https://github.com/khraksmamtsov/ts-fsm
Finit state machine writen in typescript
https://github.com/khraksmamtsov/ts-fsm
lifecycle lifecycle-management state-machine state-management typescript
Last synced: 9 months ago
JSON representation
Finit state machine writen in typescript
- Host: GitHub
- URL: https://github.com/khraksmamtsov/ts-fsm
- Owner: KhraksMamtsov
- License: mit
- Created: 2018-06-02T10:34:03.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2018-06-18T06:42:39.000Z (about 8 years ago)
- Last Synced: 2025-01-10T11:52:04.197Z (over 1 year ago)
- Topics: lifecycle, lifecycle-management, state-machine, state-management, typescript
- Language: TypeScript
- Homepage:
- Size: 853 KB
- Stars: 22
- Watchers: 1
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: code-of-conduct.md
Awesome Lists containing this project
README


[](https://travis-ci.org/KhraksMamtsov/ts-fsm)
[](https://coveralls.io/github/KhraksMamtsov/ts-fsm?branch=master)
[](https://bundlephobia.com/result?p=ts-fsm)
[](https://bundlephobia.com/result?p=ts-fsm)

# 🤖 Finite state machine written in typescript
❄️ ↔️ 🌊 ↔️ ♨️
Advanced control of entities' lifecycle.
```cmd
npm install ts-fsm
yarn add ts-fsm
```
## Simple usage
```typescript
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.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.
```typescript
new StateMachine(
STATE.SOLID,
states,
transitions
);
new StateMachine(
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
```typescript
const increaseEntropy = ({ transport }: { transport: IObject }) => {
transport["entropy"] = transport["entropy"] === undefined ? 0 : transport["entropy"] + 1;
};
new StateMachine(
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.
```typescript
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.
```typescript
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.
```typescript
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.
```typescript
new StateMachine(
STATE.SOLID,
states,
transitions,
{
onError(error) {
someAwesomeLogger(error);
makeSomeAction();
throw CustomError("Custom Error!");
}
}
);
```