Jssm
Fast, easy Javascript finite state machines with visualizations; enjoy a one liner FSM instead of pages. MIT; Typescripted; 100% test coverage. Implements the FSL language.
Install / Use
/learn @StoneCypher/JssmREADME
jssm 5.105.0
Easy. Small. Fast. TS, es6, es5. Node, Browser. 100% coverage. Property tests. Fuzz tests. Language tests for a dozen languages and emoji. Easy to share online. Easy to embed.
Readable, useful state machines as one-liner strings.
5,084 tests, run 5,975 times.
- 5,075 specs with 99.9% coverage.
- 9 fuzz tests with 12.3% coverage.
With 3,028 lines, that's about 1.7 tests per line, or 2.0 generated tests per line.
Meet your new state machine library.
<a href="https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html" target="_blank" rel="noopener noreferrer">TRY THE LIVE EDITOR</a>
<a href="https://discord.gg/9P95USqnMK">Discord community</a> - <a href="https://stonecypher.github.io/jssm/docs/">Documentation</a> - <a href="https://github.com/StoneCypher/fsl/issues">Issue tracker</a> - <a href="https://github.com/StoneCypher/jssm/actions">CI build history</a>
<a href="https://discord.gg/9P95USqnMK">
</a>
<br/><br/>
Wouldn't it be nice if your TypeScript and Javascript state machines were simple and readable one-liners?
import { sm } from 'jssm';
const TrafficLight = sm`Red -> Green -> Yellow -> Red;`;
<br/>
Wouldn't it be great if they were easy to work with?
const log = s => console.log(s);
log( TrafficLight.state() ); // 'Red'
Machine.transition('Green'); // true
log( TrafficLight.state() ); // 'Green'
<br/>
What if the notation supported action names easily?
const TLWA = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`; // TLWA = Traffic Light With Actions
log( TLWA.state() ); // 'Red'
TLWA.action('next'); // true
log( TLWA.state() ); // 'Green'
TLWA.action('next'); // true
log( TLWA.state() ); // 'Yellow'
TLWA.action('next'); // true
log( TLWA.state() ); // 'Red'
<br/>
What if integration with the outside was straightforward?
const MTL = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;` // MTL = More Traffic Lights
.hook('Red', 'Green', () => log('GO GO GO') ) // node will jump the gun when you hit return, though
.hook_entry('Red', () => log('STOP') ); // so put it on one line in node
log( MTL.state() ); // 'Red'
MTL.action('next'); // true, console logs 'GO GO GO'
log( MTL.state() ); // 'Green'
MTL.action('next'); // true
log( MTL.state() ); // 'Yellow'
MTL.action('next'); // true, console logs 'STOP'
log( MTL.state() ); // 'Red'
<br/>
What if the machine followed JS standards, and distinguished refusals as false from mistakes as thrown?
const ATL = sm`Red -> Green -> Yellow -> Red;`; // ATL = Another Traffic Light
log( ATL.state() ); // 'Red' - uses 1st state unless told otherwise
ATL.transition('Yellow'); // false (Yellow isn't allowed from Red)
ATL.transition('Blue'); // throws (Blue isn't a state at all)
<br/>
What if there were easy convenience notations for lists, and for designating main-path => vs available path -> vs
only-when-forced ~> ?
const TrafficLightWithOff = sm`
Red => Green => Yellow => Red;
[Red Yellow Green] ~> Off -> Red;
`;
<br/>
What if that were easy to render visually?
const TrafficLightWithOff = sm`
Red => Green => Yellow => Red;
[Red Yellow Green] ~> Off -> Red;
`;
<br/>
<img src="https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/doc%20light%20unstyled.png"/>
<br/>
What if that were easy to render visually, with styling, in PNG, JPEG, or SVG?
const TrafficLightWithOff = sm`
Red => Green => Yellow => Red;
[Red Yellow Green] ~> Off -> Red;
flow: left;
state Red : { background-color: pink; corners: rounded; };
state Yellow : { background-color: lightyellow; corners: rounded; };
state Green : { background-color: lightgreen; corners: rounded; };
state Off : {
background-color : steelblue;
text-color : white;
shape : octagon;
linestyle : dashed;
};
`;
<br/>
<img src="https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/doc%20light%20styled.png"/>
<br/>
What if the machine was lighting fast, able to do tens of millions of transitions per second?
<img src="https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/speed%20claim.png"/> <br/>- What if the machine and language had extensive 100% test coverage with thousands of cases?
- What if the machine gave extensive Typescript introspection support?
- What if the machine had been around and active since May 2017?
- What if the machine was MIT licensed, end to end?
But, above all else:
What if it was easy?
<br/><br/>
Introducing JSSM
Meet JSSM: the <b><u>J</u></b>ava<b><u>s</u></b>cript <b><u>S</u></b>tate <b><u>M</u></b>achine.
State machines can make your code cleaner, safer, and more trustworthy.
And, with the right language, state machines can be easy and fun.
<a href="https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html" target="_blank" rel="noopener noreferrer">TRY THE LIVE EDITOR</a>
<br/>What is JSSM?
JSSM is a Javascript state machine implementing Finite State Language, with a terse DSL and a simple API. 100% test coverage; typed with Flowtype. MIT licensed.
The NPM package includes pure es6, a cjs es5 bundle, and .d.ts typings. The repository includes the original typescript, the bundle, the es6, documentation, tests, tutorials, and so on.
Visualize with jssm-viz, or at the command line with jssm-viz-cli.
Language test cases for Belorussian, English, German, Hebrew, Italian, Russian, Spanish, Ukrainian, and Emoji. Please help to make sure that your language is well handled!
<div id="badge_style_hook"> <img src="https://starchart.cc/StoneCypher/jssm.svg" width="50%"> </div><br/><br/>
TL;DR
Specify finite state machines with a brief syntax. Run them; they're fast. Make mistakes; they're strict. Derive charts. Save and load states, and histories. Make machine factories to churn out dozens or thousands of instances. Impress friends and loved ones. Cure corns and callouses.
Red 'Proceed' -> Green 'Proceed' -> Yellow 'Proceed' -> Red;
This will produce the following FSM (graphed with jssm-viz):

You'll build an executable state machine.

<br/><br/>
Why
As usual, a valid question.
<br/>Why state machines
State machines are a method of making your software better able to prevent illegal states. Similar to type systems, SQL constraints, and linters, state machines are a way to teach the software to catch mistakes in ways you define, to help lead to better software.
The major mechanism of a state machine is to define states, the transitions between them, a
