@osvec/nostate
v0.1.1
Published
A state machine library for typescript.
Downloads
4
Readme
NoState
A state machine library for typescript.
Install
npm
npm install @osvec/nostateyarn
yarn add @osvec/nostateExample
Suppose you are implementing a player module, the state diagram may look like this.

Then, your code will be as follows, these actions are performed only in the appropriate state.
import { Event, StateMachine } from "@osvec/nostate";
// define state
type State = "idle" | "playing" | "paused" | "released";
export interface StateChangedCallback<S> {
(preState: S, targetState: S): void;
}
export interface FireFailedCallback<S> {
(currentState: S, eventName: string): void;
}
export class Player {
private stateMachine: StateMachine<State>;
private stateChangedCallback?: StateChangedCallback<State>;
private fireFailedCallback?: FireFailedCallback<State>;
// define event
private startEvent: Event<string>;
private stopEvent: Event;
private pauseEvent: Event;
private resumeEvent: Event;
private releaseEvent: Event;
constructor() {
this.stateMachine = new StateMachine<State>();
this.startEvent = {
name: "start",
action: this.realStart,
};
this.stopEvent = {
name: "stop",
action: this.realStop,
};
this.pauseEvent = {
name: "pause",
action: this.realPause,
};
this.resumeEvent = {
name: "resume",
action: this.realResume,
};
this.releaseEvent = {
name: "release",
action: this.realRelease,
};
// config transitions and initial state
this.stateMachine.configure({
initialState: "idle",
transitions: [
{ source: "idle", event: this.startEvent, target: "playing" },
{ source: "idle", event: this.releaseEvent, target: "released" },
{ source: "playing", event: this.pauseEvent, target: "paused" },
{ source: "playing", event: this.stopEvent, target: "idle" },
{ source: "playing", event: this.releaseEvent, target: "released" },
{ source: "paused", event: this.resumeEvent, target: "playing" },
{ source: "paused", event: this.stopEvent, target: "idle" },
{ source: "paused", event: this.releaseEvent, target: "released" },
],
});
this.stateMachine.on("stateChanged", (preState, targetState) => {
console.log(`on state changed ${preState} -> ${targetState}`);
this.stateChangedCallback?.(preState, targetState);
});
this.stateMachine.on("fireFailed", (currentState, event) => {
console.log(`can't call ${event} at state ${currentState}`);
this.fireFailedCallback?.(currentState, event);
});
}
start(album: string) {
this.stateMachine.fire(this.startEvent, album);
}
stop() {
this.stateMachine.fire(this.stopEvent);
}
pause() {
this.stateMachine.fire(this.pauseEvent);
}
resume() {
this.stateMachine.fire(this.resumeEvent);
}
release() {
this.stateMachine.fire(this.releaseEvent);
}
getState() {
return this.stateMachine.getCurrentState();
}
on(type: "stateChanged", callback: StateChangedCallback<State>): void;
on(type: "fireFailed", callback: FireFailedCallback<State>): void;
on(type: "stateChanged" | "fireFailed", callback: StateChangedCallback<State> | FireFailedCallback<State>) {
if (type === "stateChanged") {
this.stateChangedCallback = callback as StateChangedCallback<State>;
} else if (type === "fireFailed") {
this.fireFailedCallback = callback as FireFailedCallback<State>;
}
}
private realStart(album: string) {
console.log(`real start: ${album}`);
}
private realStop() {
console.log(`real stop`);
}
private realPause() {
console.log(`real pause`);
}
private realResume() {
console.log(`real resume`);
}
private realRelease() {
console.log(`real release`);
}
}
