jssm 5.104.2 → 5.112.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATING-jssm-viz.md +67 -0
- package/README.md +179 -882
- package/dist/deno/README.md +347 -0
- package/dist/{es6 → deno}/jssm.d.ts +773 -39
- package/dist/deno/jssm.js +1 -0
- package/{jssm_compiler.d.ts → dist/deno/jssm_compiler.d.ts} +17 -2
- package/dist/deno/jssm_constants.d.ts +37 -0
- package/dist/deno/jssm_error.d.ts +27 -0
- package/dist/deno/jssm_theme.d.ts +15 -0
- package/dist/{es6 → deno}/jssm_types.d.ts +327 -8
- package/dist/deno/jssm_util.d.ts +258 -0
- package/dist/deno/jssm_viz.d.ts +175 -0
- package/dist/deno/jssm_viz_colors.d.ts +63 -0
- package/dist/jssm.es5.cjs +1 -1
- package/dist/jssm.es5.iife.js +1 -0
- package/dist/jssm.es6.mjs +1 -1
- package/dist/jssm_viz.cjs +1 -0
- package/dist/jssm_viz.iife.cjs +1 -0
- package/dist/jssm_viz.mjs +1 -0
- package/jssm.es5.d.cts +1191 -43
- package/jssm.es6.d.ts +1191 -43
- package/jssm_viz.es5.d.cts +2341 -0
- package/jssm_viz.es6.d.ts +2341 -0
- package/package.json +73 -24
- package/.clocignore +0 -1
- package/.codeclimate.yml +0 -22
- package/.editorconfig +0 -12
- package/.eslintrc +0 -20
- package/.gitattributes +0 -6
- package/.nycrc +0 -6
- package/.travis.yml +0 -9
- package/CHANGELOG.md +0 -178
- package/dist/es6/fsl_parser.js +0 -1
- package/dist/es6/jssm.js +0 -2488
- package/dist/es6/jssm_arrow.js +0 -187
- package/dist/es6/jssm_compiler.d.ts +0 -135
- package/dist/es6/jssm_compiler.js +0 -366
- package/dist/es6/jssm_constants.d.ts +0 -5
- package/dist/es6/jssm_constants.js +0 -94
- package/dist/es6/jssm_error.d.ts +0 -8
- package/dist/es6/jssm_error.js +0 -28
- package/dist/es6/jssm_theme.d.ts +0 -4
- package/dist/es6/jssm_theme.js +0 -13
- package/dist/es6/jssm_types.js +0 -3
- package/dist/es6/jssm_util.d.ts +0 -106
- package/dist/es6/jssm_util.js +0 -180
- package/dist/es6/themes/jssm_base_stylesheet.d.ts +0 -11
- package/dist/es6/themes/jssm_base_stylesheet.js +0 -58
- package/dist/es6/themes/jssm_theme_bold.d.ts +0 -11
- package/dist/es6/themes/jssm_theme_bold.js +0 -58
- package/dist/es6/themes/jssm_theme_default.d.ts +0 -11
- package/dist/es6/themes/jssm_theme_default.js +0 -58
- package/dist/es6/themes/jssm_theme_modern.d.ts +0 -11
- package/dist/es6/themes/jssm_theme_modern.js +0 -58
- package/dist/es6/themes/jssm_theme_ocean.d.ts +0 -11
- package/dist/es6/themes/jssm_theme_ocean.js +0 -56
- package/dist/es6/themes/jssm_theme_plain.d.ts +0 -11
- package/dist/es6/themes/jssm_theme_plain.js +0 -70
- package/dist/es6/version.js +0 -2
- package/dist/jssm.es5.iife.cjs +0 -1
- package/dist/jssm.es5.iife.nonmin.cjs +0 -23180
- package/dist/jssm.es5.nonmin.cjs +0 -23175
- package/dist/jssm.es6.nonmin.cjs +0 -23144
- package/fsl_parser.d.ts +0 -6
- package/jest-dragon.config.cjs +0 -33
- package/jest-spec.config.cjs +0 -33
- package/jest-stoch.config.cjs +0 -33
- package/jest-unicode.config.cjs +0 -33
- package/jssm.d.ts +0 -1141
- package/jssm_arrow.d.ts +0 -53
- package/jssm_constants.d.ts +0 -5
- package/jssm_error.d.ts +0 -8
- package/jssm_theme.d.ts +0 -4
- package/jssm_types.d.ts +0 -378
- package/jssm_util.d.ts +0 -106
- package/rollup.config.deno.js +0 -44
- package/rollup.config.es5.js +0 -52
- package/rollup.config.es6.js +0 -55
- package/tutorial_learn_testing.md +0 -168
- package/typedoc-options.cjs +0 -68
- package/version.d.ts +0 -2
- /package/dist/{es6 → deno}/fsl_parser.d.ts +0 -0
- /package/dist/{es6 → deno}/jssm_arrow.d.ts +0 -0
- /package/dist/{es6 → deno}/version.d.ts +0 -0
package/README.md
CHANGED
|
@@ -18,35 +18,19 @@ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
* Generated for version 5.
|
|
21
|
+
* Generated for version 5.112.4 at 5/12/2026, 11:13:56 AM
|
|
22
22
|
|
|
23
23
|
-->
|
|
24
|
-
# jssm 5.
|
|
24
|
+
# jssm 5.112.4
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
[**Try the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·
|
|
27
|
+
[Documentation](https://stonecypher.github.io/jssm/docs/) ·
|
|
28
|
+
[Discord](https://discord.gg/9P95USqnMK) ·
|
|
29
|
+
[Issues](https://github.com/StoneCypher/fsl/issues)
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* 5,063 specs with 100.0% coverage.
|
|
35
|
-
* 9 fuzz tests with 12.4% coverage.
|
|
36
|
-
|
|
37
|
-
With 3,007 lines, that's about 1.7 tests per line, or 2.0 generated tests per line.
|
|
38
|
-
|
|
39
|
-
***Meet your new state machine library.***
|
|
40
|
-
|
|
41
|
-
# <a href="https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html" target="_blank" rel="noopener noreferrer">TRY THE LIVE EDITOR</a>
|
|
42
|
-
|
|
43
|
-
<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>
|
|
44
|
-
|
|
45
|
-
<a href="https://discord.gg/9P95USqnMK"></a>
|
|
46
|
-
|
|
47
|
-
<br/><br/>
|
|
48
|
-
|
|
49
|
-
Wouldn't it be nice if your TypeScript and Javascript state machines were simple and readable one-liners?
|
|
31
|
+
**Easy.** Tiny. Fast. Finite state machines as one-liner strings, for
|
|
32
|
+
TypeScript and JavaScript. Renders to PNG, SVG, and JPEG. Runs in Node,
|
|
33
|
+
browsers, and Deno. MIT licensed.
|
|
50
34
|
|
|
51
35
|
```javascript
|
|
52
36
|
import { sm } from 'jssm';
|
|
@@ -54,105 +38,29 @@ import { sm } from 'jssm';
|
|
|
54
38
|
const TrafficLight = sm`Red -> Green -> Yellow -> Red;`;
|
|
55
39
|
```
|
|
56
40
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Wouldn't it be great if they were easy to work with?
|
|
60
|
-
|
|
61
|
-
```javascript
|
|
62
|
-
const log = s => console.log(s);
|
|
63
|
-
|
|
64
|
-
log( TrafficLight.state() ); // 'Red'
|
|
65
|
-
|
|
66
|
-
Machine.transition('Green'); // true
|
|
67
|
-
log( TrafficLight.state() ); // 'Green'
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
<br/>
|
|
71
|
-
|
|
72
|
-
What if the notation supported action names easily?
|
|
73
|
-
|
|
74
|
-
```javascript
|
|
75
|
-
const TLWA = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`; // TLWA = Traffic Light With Actions
|
|
76
|
-
|
|
77
|
-
log( TLWA.state() ); // 'Red'
|
|
78
|
-
|
|
79
|
-
TLWA.action('next'); // true
|
|
80
|
-
log( TLWA.state() ); // 'Green'
|
|
81
|
-
|
|
82
|
-
TLWA.action('next'); // true
|
|
83
|
-
log( TLWA.state() ); // 'Yellow'
|
|
84
|
-
|
|
85
|
-
TLWA.action('next'); // true
|
|
86
|
-
log( TLWA.state() ); // 'Red'
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
<br/>
|
|
90
|
-
|
|
91
|
-
What if integration with the outside was straightforward?
|
|
41
|
+
That's it. Using it is equally easy:
|
|
92
42
|
|
|
93
43
|
```javascript
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
log( MTL.state() ); // 'Red'
|
|
99
|
-
|
|
100
|
-
MTL.action('next'); // true, console logs 'GO GO GO'
|
|
101
|
-
log( MTL.state() ); // 'Green'
|
|
102
|
-
|
|
103
|
-
MTL.action('next'); // true
|
|
104
|
-
log( MTL.state() ); // 'Yellow'
|
|
105
|
-
|
|
106
|
-
MTL.action('next'); // true, console logs 'STOP'
|
|
107
|
-
log( MTL.state() ); // 'Red'
|
|
44
|
+
TrafficLight.state(); // 'Red'
|
|
45
|
+
TrafficLight.go('Green'); // true
|
|
46
|
+
TrafficLight.state(); // 'Green'
|
|
108
47
|
```
|
|
109
48
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
What if the machine followed JS standards, and distinguished refusals as `false` from mistakes as `throw`n?
|
|
49
|
+
The point of a state machine is to refuse to do things that aren't correct:
|
|
113
50
|
|
|
114
51
|
```javascript
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
log( ATL.state() ); // 'Red' - uses 1st state unless told otherwise
|
|
118
|
-
ATL.transition('Yellow'); // false (Yellow isn't allowed from Red)
|
|
119
|
-
ATL.transition('Blue'); // throws (Blue isn't a state at all)
|
|
52
|
+
TrafficLight.go('Red'); // false - Green doesn't go to Red, only Yellow
|
|
53
|
+
TrafficLight.go('Blue'); // throws - Blue doesn't exist at all
|
|
120
54
|
```
|
|
121
55
|
|
|
122
|
-
|
|
56
|
+
A more involved machine, with main paths, forced paths, and per-state
|
|
57
|
+
styling, renders to:
|
|
123
58
|
|
|
124
|
-
|
|
125
|
-
only-when-forced `~>` ?
|
|
59
|
+
<img src="https://raw.githubusercontent.com/StoneCypher/jssm/main/src/assets/doc%20light%20styled.png" alt="A styled four-state traffic light"/>
|
|
126
60
|
|
|
127
61
|
```javascript
|
|
128
62
|
const TrafficLightWithOff = sm`
|
|
129
|
-
Red => Green => Yellow => Red;
|
|
130
|
-
[Red Yellow Green] ~> Off -> Red;
|
|
131
|
-
`;
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
<br/>
|
|
135
|
-
|
|
136
|
-
What if that were easy to render visually?
|
|
137
|
-
|
|
138
|
-
```javascript
|
|
139
|
-
const TrafficLightWithOff = sm`
|
|
140
|
-
Red => Green => Yellow => Red;
|
|
141
|
-
[Red Yellow Green] ~> Off -> Red;
|
|
142
|
-
`;
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
<br/>
|
|
146
|
-
|
|
147
|
-
<img src="https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/doc%20light%20unstyled.png"/>
|
|
148
|
-
|
|
149
|
-
<br/>
|
|
150
|
-
|
|
151
|
-
What if that were easy to render visually, with styling, in PNG, JPEG, or SVG?
|
|
152
|
-
|
|
153
|
-
```javascript
|
|
154
|
-
const TrafficLightWithOff = sm`
|
|
155
|
-
Red => Green => Yellow => Red;
|
|
63
|
+
Red 'next' => Green 'next' => Yellow 'next' => Red;
|
|
156
64
|
[Red Yellow Green] ~> Off -> Red;
|
|
157
65
|
|
|
158
66
|
flow: left;
|
|
@@ -161,7 +69,7 @@ const TrafficLightWithOff = sm`
|
|
|
161
69
|
state Yellow : { background-color: lightyellow; corners: rounded; };
|
|
162
70
|
state Green : { background-color: lightgreen; corners: rounded; };
|
|
163
71
|
|
|
164
|
-
state Off
|
|
72
|
+
state Off : {
|
|
165
73
|
background-color : steelblue;
|
|
166
74
|
text-color : white;
|
|
167
75
|
shape : octagon;
|
|
@@ -170,881 +78,270 @@ const TrafficLightWithOff = sm`
|
|
|
170
78
|
`;
|
|
171
79
|
```
|
|
172
80
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
<img src="https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/doc%20light%20styled.png"/>
|
|
176
|
-
|
|
177
|
-
<br/>
|
|
178
|
-
|
|
179
|
-
What if the machine was lighting fast, able to do tens of millions of transitions per second?
|
|
180
|
-
|
|
181
|
-
<img src="https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/speed%20claim.png"/>
|
|
182
|
-
|
|
183
|
-
<br/>
|
|
184
|
-
|
|
185
|
-
* What if the machine and language had [extensive 100% test coverage](https://coveralls.io/github/StoneCypher/jssm)
|
|
186
|
-
with [thousands of cases](https://github.com/StoneCypher/jssm/tree/main/src/ts/tests)?
|
|
187
|
-
* What if the machine gave extensive Typescript introspection support?
|
|
188
|
-
* What if the machine had been around and active since May 2017?
|
|
189
|
-
* What if the machine was MIT licensed, end to end?
|
|
190
|
-
|
|
191
|
-
But, above all else:
|
|
192
|
-
|
|
193
|
-
`What if it was easy?`
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
<br/><br/>
|
|
200
|
-
|
|
201
|
-
# Introducing JSSM
|
|
202
|
-
|
|
203
|
-
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.
|
|
204
|
-
|
|
205
|
-
State machines can make your code cleaner, safer, and more trustworthy.
|
|
206
|
-
|
|
207
|
-
And, with the right language, state machines can be easy and fun.
|
|
81
|
+
The same string is the runtime and the diagram. They cannot drift apart.
|
|
208
82
|
|
|
209
|
-
|
|
83
|
+
[**Try it in the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·
|
|
84
|
+
[Documentation](https://stonecypher.github.io/jssm/docs/) ·
|
|
85
|
+
[Discord](https://discord.gg/9P95USqnMK) ·
|
|
86
|
+
[Issues](https://github.com/StoneCypher/fsl/issues)
|
|
210
87
|
|
|
211
88
|
|
|
212
89
|
|
|
213
90
|
<br/>
|
|
214
91
|
|
|
215
|
-
##
|
|
92
|
+
## Install
|
|
216
93
|
|
|
217
|
-
JSSM is a Javascript state machine implementing [Finite State Language](https://fsl.tools/), with a terse DSL and a simple API.
|
|
218
|
-
100% test coverage; typed with Flowtype. MIT licensed.
|
|
219
|
-
|
|
220
|
-
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.
|
|
221
|
-
|
|
222
|
-
[Try it live!](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html)
|
|
223
|
-
|
|
224
|
-
Visualize with [jssm-viz](https://github.com/StoneCypher/jssm-viz), or at the command line with [jssm-viz-cli](https://github.com/StoneCypher/jssm-viz-cli).
|
|
225
|
-
|
|
226
|
-
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!
|
|
227
|
-
|
|
228
|
-
<div id="badge_style_hook">
|
|
229
|
-
|
|
230
|
-
[](https://github.com/StoneCypher/jssm/actions)
|
|
231
|
-
|
|
232
|
-
[]()
|
|
233
|
-
[]()
|
|
234
|
-
[]()
|
|
235
|
-
[]()
|
|
236
|
-
|
|
237
|
-
[](https://github.com/StoneCypher/jssm/blob/master/LICENSE.md)
|
|
238
|
-
[](https://github.com/StoneCypher/jssm/issues)
|
|
239
|
-
[](https://github.com/StoneCypher/jssm/issues?q=is%3Aissue+is%3Aclosed)
|
|
240
|
-
[](https://travis-ci.org/StoneCypher/jssm)
|
|
241
|
-
[](https://coveralls.io/github/StoneCypher/jssm)
|
|
242
|
-
|
|
243
|
-
[](https://www.npmjs.com/package/jssm)
|
|
244
|
-
[](https://img.shields.io/cdnjs/v/jquery.svg)
|
|
245
|
-
[](https://www.npmjs.com/package/jssm)
|
|
246
|
-
|
|
247
|
-
<img src="https://starchart.cc/StoneCypher/jssm.svg" width="50%">
|
|
248
|
-
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<br/><br/>
|
|
254
|
-
|
|
255
|
-
## TL;DR
|
|
256
|
-
Specify finite state machines with a brief syntax. Run them; they're fast. Make mistakes; they're strict. Derive
|
|
257
|
-
charts. Save and load states, and histories. Make machine factories to churn out dozens or thousands of instances.
|
|
258
|
-
Impress friends and loved ones. Cure corns and callouses.
|
|
259
|
-
|
|
260
|
-
```fsl
|
|
261
|
-
Red 'Proceed' -> Green 'Proceed' -> Yellow 'Proceed' -> Red;
|
|
262
94
|
```
|
|
263
|
-
|
|
264
|
-
This will produce the following FSM (graphed with [jssm-viz](https://github.com/StoneCypher/jssm-viz)):
|
|
265
|
-
|
|
266
|
-

|
|
267
|
-
|
|
268
|
-
You'll build an executable state machine.
|
|
269
|
-
|
|
270
|
-

|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
<br/><br/>
|
|
275
|
-
|
|
276
|
-
## Why
|
|
277
|
-
|
|
278
|
-
As usual, a valid question.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
<br/>
|
|
283
|
-
|
|
284
|
-
### Why state machines
|
|
285
|
-
|
|
286
|
-
State machines are a method of making your software better able to prevent illegal states. Similar to type systems, SQL
|
|
287
|
-
constraints, and linters, state machines are a way to teach the software to catch mistakes in ways you define, to help
|
|
288
|
-
lead to better software.
|
|
289
|
-
|
|
290
|
-
The major mechanism of a state machine is to define `states`, the `transitions` between them, and sometimes associated
|
|
291
|
-
`data` and other niceties. The minor mechanism of state machines is to attach `actions` to the transitions, such that
|
|
292
|
-
the state machine can partially run itself.
|
|
293
|
-
|
|
294
|
-

|
|
295
|
-
|
|
296
|
-
So, to look at the same traffic light as above, you'll notice some things.
|
|
297
|
-
|
|
298
|
-
1. A sufficiently smart implementation will know that it's okay for `Green` to switch to `Yellow`, but not to `Red`
|
|
299
|
-
1. A sufficiently smart implementation knows there's no such thing as `Blue`
|
|
300
|
-
1. A sufficiently smart implementation knows that when in `Green`, to be told to `Proceed` means to go to `Yellow`, but
|
|
301
|
-
when in `Yellow`, it means to go to `Red` instead
|
|
302
|
-
|
|
303
|
-
Along with other common sense things, a good state machine implementation can help eliminate large classes of error in
|
|
304
|
-
software. State machines are often applied when the stakes on having things correct are high.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
<br/>
|
|
309
|
-
|
|
310
|
-
### Why this implementation
|
|
311
|
-
|
|
312
|
-
Brevity.
|
|
313
|
-
|
|
314
|
-
High quality testing. JSSM has 100% coverage, and has partial stochastic test coverage.
|
|
315
|
-
|
|
316
|
-
Feature parity, especially around the DSL and data control.
|
|
317
|
-
|
|
318
|
-
Data integrity. JSSM allows a much stricter form of state machine than is common, with a relatively low performance
|
|
319
|
-
and storage overhead. It also offers an extremely terse domain specific language (though it does not require said DSL)
|
|
320
|
-
to produce state machines in otherwise comparatively tiny and easily read code.
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
<br/><br/>
|
|
325
|
-
|
|
326
|
-
## Quick Start
|
|
327
|
-
|
|
328
|
-
> A state machine in `JSSM` is defined in one of two ways: through the DSL, or through a datastructure.
|
|
329
|
-
|
|
330
|
-
So yeah, let's start by getting some terminology out of the way, and then we can go right back to that impenetrable
|
|
331
|
-
sentence, so that it'll make sense.
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
<br/>
|
|
336
|
-
|
|
337
|
-
### Quick Terminology
|
|
338
|
-
|
|
339
|
-
Finite state machines have been around forever, are used by everyone, and are hugely important. As a result, the
|
|
340
|
-
terminology is a mess, is in conflict, and is very poorly chosen, in accordince with everything-is-horrible law.
|
|
341
|
-
|
|
342
|
-
This section describes the terminology *as used by this library*. The author has done his best to choose a terminology
|
|
343
|
-
that matches common use and will be familiar to most. Conflicts are explained in the following section, to keep this
|
|
344
|
-
simple.
|
|
345
|
-
|
|
346
|
-
For this quick overview, we'll define six basic concepts:
|
|
347
|
-
|
|
348
|
-
1. `Finite state machine`s
|
|
349
|
-
1. `Machine`s
|
|
350
|
-
1. `State`s
|
|
351
|
-
1. `Current state`
|
|
352
|
-
1. `Transition`s
|
|
353
|
-
1. `Action`s
|
|
354
|
-
|
|
355
|
-
There's other stuff, of course, but these five are enough to wrap your head around `finite state machine`s.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
<br/>
|
|
360
|
-
|
|
361
|
-
#### Basic concepts
|
|
362
|
-
|
|
363
|
-
This is a trivial traffic light `FSM`, with three states, three transitions, and one action:
|
|
364
|
-
|
|
365
|
-
```fsl
|
|
366
|
-
Red 'Proceed' -> Green 'Proceed' -> Yellow 'Proceed' -> Red;
|
|
95
|
+
npm install jssm
|
|
367
96
|
```
|
|
368
97
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
Let's review its pieces.
|
|
372
|
-
|
|
373
|
-
* `finite state machine`s
|
|
374
|
-
* A `finite state machine` (or `FSM`) is a collection of `state`s, and rules about how you can `transition` between
|
|
375
|
-
the `state`s.
|
|
376
|
-
* We tend to refer to a design for a machine as "an `FSM`."
|
|
377
|
-
* In this example, the traffic light's structure is "a traffic light `FSM`."
|
|
378
|
-
|
|
379
|
-
* `state`s
|
|
380
|
-
* `FSM`s always have at least one `state`, and nearly always many `state`s
|
|
381
|
-
* In this example,
|
|
382
|
-
* the `state`s are **Red**, **Yellow**, and **Green**
|
|
383
|
-
* Something made from this `FSM` will only ever be one of those colors - not, say, **Blue**
|
|
384
|
-
|
|
385
|
-
* `machine`s
|
|
386
|
-
* Single instances of an `FSM` are referred to as a `machine`
|
|
387
|
-
* We might have a thousand instances of the traffic light designed above
|
|
388
|
-
* We would say "My intersection has four `machines` of the standard three color light `FSM`."
|
|
389
|
-
|
|
390
|
-
* `current state`
|
|
391
|
-
* A `machine` has a `current state`, though an `FSM` does not
|
|
392
|
-
* "This specific traffic light is currently **Red**"
|
|
393
|
-
* Traffic lights in general do not have a current color, only specific lights
|
|
394
|
-
* `FSM`s do not have a current state, only specific `machine`s
|
|
395
|
-
* A given `machine` will always have exactly one `state` - never multiple, never none
|
|
396
|
-
|
|
397
|
-
* `transitions`
|
|
398
|
-
* `FSM`s nearly always have `transition`s
|
|
399
|
-
* Transitions govern whether a `state` may be reached from another `state`
|
|
400
|
-
* This restriction is much of the value of `FSM`s
|
|
401
|
-
* In this example,
|
|
402
|
-
* the `transition`s are
|
|
403
|
-
* **Green** → **Yellow**
|
|
404
|
-
* **Yellow** → **Red**
|
|
405
|
-
* **Red** → **Green**
|
|
406
|
-
* a `machine` whose `current state` is **Green** may switch to **Yellow**, because there is an appropriate transition
|
|
407
|
-
* a `machine` whose `current state` is **Green** may not switch to **Red**, or to **Green** anew, because there is no
|
|
408
|
-
such transition
|
|
409
|
-
* A `machine` in **Yellow** which is told to `transition` to **Green** (which isn't legal) will know to refuse
|
|
410
|
-
* This makes `FSM`s an effective tool for error prevention
|
|
411
|
-
|
|
412
|
-
* `actions`
|
|
413
|
-
* Many `FSM`s have `action`s, which represent events from the outside world.
|
|
414
|
-
* In this example, there is only one action - **Proceed**
|
|
415
|
-
* The `action` **Proceed** is available from all three colors
|
|
416
|
-
* At any time we may indicate to this light to go to its next color, without
|
|
417
|
-
taking the time to know what it is.
|
|
418
|
-
* This allows `FSM`s like the light to self-manage.
|
|
419
|
-
* A `machine` in **Yellow** which is told to take the `action` **Proceed** will
|
|
420
|
-
know on its own to switch its `current state` to **Red**.
|
|
421
|
-
* This makes `FSM`s an effective tool for complexity reduction
|
|
422
|
-
|
|
423
|
-
Those six ideas in hand - `FSM`s, `state`s, `machine`s, `current state`, `transition`s, and `action`s - and you're ready
|
|
424
|
-
to move forwards.
|
|
425
|
-
|
|
426
|
-
One other quick definition - a `DSL`, or `domain specific language`, is when someone makes a language and embeds it into
|
|
427
|
-
a different language, for the purpose of attacking a specific job. When `React` uses a precompiler to embed stuff that
|
|
428
|
-
looks like HTML in Javascript, that's a DSL.
|
|
429
|
-
|
|
430
|
-
This library implements a simple language for `defining finite state machine`s inside of strings. For example, this
|
|
431
|
-
`DSL` defines that `'a -> b;'` actually means "create two states, create a transition between them, assign the first as
|
|
432
|
-
the initial state", et cetera. That micro-language is the `DSL` that we'll be referring to a lot, coming up. This
|
|
433
|
-
`DSL`'s parser's original name was `jssm-dot`, because it's a descendant-in-spirit of an older flowcharting language
|
|
434
|
-
[DOT](http://www.graphviz.org/content/dot-language), from [graphviz](graphviz.org), which is also used to make the
|
|
435
|
-
visualizations in [jssm-viz](https://github.com/StoneCypher/jssm-viz) by way of [viz-js](viz-js.com).
|
|
436
|
-
|
|
437
|
-
Enough history lesson. On with the tooling.
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
<br/>
|
|
442
|
-
|
|
443
|
-
### And now, that Quick Start we were talking about
|
|
444
|
-
|
|
445
|
-
So let's put together a trivial four-state traffic light: the three colors, plus **Off**. This will give us an
|
|
446
|
-
opportunity to go over the basic facilities in the language.
|
|
447
|
-
|
|
448
|
-
At any time, you can take the code and put it into the
|
|
449
|
-
[graph explorer](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) for an opportunity to mess with the
|
|
450
|
-
code as you see fit.
|
|
98
|
+
The package ships pure ES6, a CommonJS ES5 bundle, an IIFE for browsers,
|
|
99
|
+
and TypeScript typings. A Deno build is included. Node 10 or newer.
|
|
451
100
|
|
|
452
101
|
|
|
453
102
|
|
|
454
103
|
<br/>
|
|
455
104
|
|
|
456
|
-
|
|
105
|
+
## Visualization
|
|
457
106
|
|
|
458
|
-
|
|
107
|
+
`jssm` ships with a visualization subpath that renders state machines to
|
|
108
|
+
SVG using Graphviz (via [`@viz-js/viz`](https://www.npmjs.com/package/@viz-js/viz)).
|
|
459
109
|
|
|
460
|
-
|
|
110
|
+
```typescript
|
|
111
|
+
import { sm } from 'jssm';
|
|
112
|
+
import { fsl_to_svg_string } from 'jssm/viz';
|
|
461
113
|
|
|
462
|
-
|
|
463
|
-
Off -> Red;
|
|
114
|
+
const svg = await fsl_to_svg_string('a -> b;');
|
|
464
115
|
```
|
|
465
116
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
Off 'TurnOn' -> Red;
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
So far, our machine is simple:
|
|
473
|
-
|
|
474
|
-

|
|
117
|
+
The viz subpath is opt-in - importing only from `jssm` does not pull in
|
|
118
|
+
`@viz-js/viz`. See the Visualization doc page for browser, ESM, and IIFE
|
|
119
|
+
usage patterns.
|
|
475
120
|
|
|
476
121
|
|
|
477
122
|
|
|
478
123
|
<br/>
|
|
479
124
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
The main path of a traffic light is cycling from **Green** to **Yellow**, then to **Red**, then back again. Because
|
|
483
|
-
this is the main path, we'll mark these steps `=> main transition`s.
|
|
125
|
+
## 60-second tour
|
|
484
126
|
|
|
485
|
-
|
|
486
|
-
Off 'TurnOn' -> Red => Green => Yellow => Red;
|
|
487
|
-
```
|
|
127
|
+
**Actions** let a machine advance without the caller knowing the next state:
|
|
488
128
|
|
|
489
|
-
|
|
490
|
-
|
|
129
|
+
```javascript
|
|
130
|
+
const Light = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`;
|
|
491
131
|
|
|
492
|
-
|
|
493
|
-
|
|
132
|
+
Light.action('next'); // true
|
|
133
|
+
Light.state(); // 'Green'
|
|
134
|
+
Light.action('next'); // true
|
|
135
|
+
Light.state(); // 'Yellow'
|
|
494
136
|
```
|
|
495
137
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-

|
|
499
|
-
|
|
500
|
-
|
|
138
|
+
**Three arrow types** distinguish kinds of transition:
|
|
501
139
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
be a `~> forced transition`.
|
|
508
|
-
|
|
509
|
-
We could write
|
|
510
|
-
|
|
511
|
-
```fsl
|
|
512
|
-
Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
|
|
513
|
-
Red ~> Off;
|
|
514
|
-
Yellow ~> Off;
|
|
515
|
-
Green ~> Off;
|
|
140
|
+
```javascript
|
|
141
|
+
const Light = sm`
|
|
142
|
+
Red => Green => Yellow => Red; // => main path
|
|
143
|
+
[Red Yellow Green] ~> Off -> Red; // ~> forced, -> legal
|
|
144
|
+
`;
|
|
516
145
|
```
|
|
517
146
|
|
|
518
|
-
|
|
147
|
+
`->` is a legal transition. `=>` is a legal transition that is also part of
|
|
148
|
+
the main path. `~>` is a transition that requires `force_transition` -
|
|
149
|
+
useful for emergency stops, resets, and other rarities.
|
|
519
150
|
|
|
520
|
-
|
|
521
|
-
Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
|
|
522
|
-
[Red Yellow Green] ~> Off;
|
|
523
|
-
```
|
|
151
|
+
**Hooks** observe and gate transitions:
|
|
524
152
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
153
|
+
```javascript
|
|
154
|
+
const m = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`
|
|
155
|
+
.hook('Red', 'Green', () => console.log('GO')) // specific edge
|
|
156
|
+
.hook_entry('Red', () => console.log('STOP')) // entering a state
|
|
157
|
+
.hook_action('Yellow', 'Red', 'next', () => allowed()); // gate a specific action; return false to block
|
|
530
158
|
```
|
|
531
159
|
|
|
532
|
-
|
|
160
|
+
Pre-hooks fire before the state changes and may return `false` to refuse the
|
|
161
|
+
transition. Post-hooks fire after. Four `*_everything` hooks
|
|
162
|
+
(`hook_pre_everything`, `hook_everything`, `hook_pre_post_everything`,
|
|
163
|
+
`hook_post_everything`) bracket the entire pipeline if you want a single
|
|
164
|
+
observation point.
|
|
533
165
|
|
|
534
|
-
|
|
166
|
+
**Refusals and errors are deliberately different.** An illegal transition
|
|
167
|
+
returns `false`. An unknown state throws. Branching code can rely on the
|
|
168
|
+
distinction.
|
|
535
169
|
|
|
536
170
|
|
|
537
171
|
|
|
538
172
|
<br/>
|
|
539
173
|
|
|
540
|
-
|
|
174
|
+
## Why jssm
|
|
541
175
|
|
|
542
|
-
|
|
543
|
-
|
|
176
|
+
**The big win: most state-machine libraries make you write a gargantuan
|
|
177
|
+
JSON document, or call a builder API a few dozen times, to define a single
|
|
178
|
+
machine. jssm machines are short, readable, arrow-driven strings - so they
|
|
179
|
+
are easy to write, easy to read, easy to debug, and easy to share.**
|
|
544
180
|
|
|
545
|
-
|
|
181
|
+
That decision shows up everywhere downstream:
|
|
546
182
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
183
|
+
- **A DSL with features other state-machine libraries don't have.** Three
|
|
184
|
+
arrow types distinguish legal, main-path, and forced transitions. Array
|
|
185
|
+
notation collapses repeated edges - `[Red Yellow Green] ~> Off` replaces
|
|
186
|
+
three lines. Named actions, per-state styling, named edges, validators,
|
|
187
|
+
and live visualization all live in the same string the runtime parses.
|
|
551
188
|
|
|
189
|
+
- **Definition strings stay tiny, which makes machines easy to debug.**
|
|
190
|
+
The traffic light is one line; the [full eight-step ATM walkthrough](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/Tutorial_ATM.md)
|
|
191
|
+
is thirty. Small enough to read top-to-bottom, diff in code review,
|
|
192
|
+
paste into a bug report, or drop into the
|
|
193
|
+
[live editor](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html)
|
|
194
|
+
for a rendered diagram you can step through.
|
|
552
195
|
|
|
196
|
+
- **Fast.** Tens of millions of transitions per second on commodity
|
|
197
|
+
hardware. See [`src/buildjs/benchmark.cjs`](https://github.com/StoneCypher/jssm/blob/main/src/buildjs/benchmark.cjs)
|
|
198
|
+
or run `npm run benny` against your own machine.
|
|
553
199
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
and of `fsl`, this library's `DSL`.
|
|
560
|
-
|
|
561
|
-
We're going to improve on this [NCSU ATM diagram](https://people.engr.ncsu.edu/efg/210/s99/Notes/fsm/atm.gif) that I
|
|
562
|
-
found:
|
|
563
|
-
|
|
564
|
-

|
|
565
|
-
|
|
566
|
-
Remember, at any time, you can take the code and put it into the
|
|
567
|
-
[graph explorer](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) for an opportunity to mess with the
|
|
568
|
-
code as you see fit.
|
|
200
|
+
- **More thoroughly tested than any other JavaScript state-machine
|
|
201
|
+
library.** 5,342 tests at 100.0% line coverage
|
|
202
|
+
([report](https://coveralls.io/github/StoneCypher/jssm)), plus
|
|
203
|
+
fuzz testing via `fast-check`, with parser test data across ten natural
|
|
204
|
+
languages and Emoji.
|
|
569
205
|
|
|
570
206
|
|
|
571
207
|
|
|
572
208
|
<br/>
|
|
573
209
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
We'll start with an [empty machine](https://github.com/StoneCypher/jssm/blob/master/src/machines/atm%20quick%20start%20tutorial/1_EmptyWaiting.jssm).
|
|
210
|
+
## Documentation
|
|
577
211
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
212
|
+
- [What are state machines?](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/WhatAreStateMachines.md) - conceptual intro for newcomers
|
|
213
|
+
- [Getting started](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/GettingStarted.md) - install and use the library across Node, browser, Deno, ES5/ES6, CDN, and TypeScript
|
|
214
|
+
- [Tutorial: a four-state traffic light](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/Tutorial_TrafficLight.md) - short walkthrough that introduces the three arrow types
|
|
215
|
+
- [Tutorial: building an ATM state machine](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/Tutorial_ATM.md) - longer walkthrough that builds a real-world machine in nine incremental steps
|
|
216
|
+
- [Language reference](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/LanguageReference.md) - DSL reference for people already comfortable with state machines
|
|
217
|
+
- [Catalog of example machines](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/ExampleMachines.md) - comparison table of worked examples (light switch, traffic light, intersection, vending machine, more)
|
|
218
|
+
- [Generated API reference](https://stonecypher.github.io/jssm/docs/) - full surface, generated from the TypeScript source
|
|
583
219
|
|
|
584
220
|
|
|
585
221
|
|
|
586
222
|
<br/>
|
|
587
223
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
We'll add the ability to physically eject the user's card and reset to the empty and waiting state. Right now it'll
|
|
591
|
-
dangle around un-used at the top, but later it'll become useful.
|
|
224
|
+
## API at a glance
|
|
592
225
|
|
|
593
|
-
|
|
226
|
+
| Method | Purpose |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `` sm`...` `` | Build a machine from DSL |
|
|
229
|
+
| `.state()` | The current state |
|
|
230
|
+
| `.transition(state)` | Move to a state. Returns `false` if illegal, throws if unknown. |
|
|
231
|
+
| `.force_transition(state)` | Move to a state across a `~>` forced edge |
|
|
232
|
+
| `.action(name)` | Trigger a named action. The next state is derived from the current state. |
|
|
233
|
+
| `.valid_transition(state)` · `.valid_action(name)` | Test whether a transition or action is legal from the current state, without taking it |
|
|
234
|
+
| `.hook(from, to, fn)` | Run on a specific edge. Pre-hook; return `false` to block. |
|
|
235
|
+
| `.hook_entry(state, fn)` · `.hook_exit(state, fn)` | Run when entering or leaving a state |
|
|
236
|
+
| `.hook_action(from, to, action, fn)` | Run when a named action causes a specific edge |
|
|
237
|
+
| `.hook_pre_everything(fn)` · `.hook_everything(fn)` | Bracket the pre-hook pipeline |
|
|
238
|
+
| `.hook_pre_post_everything(fn)` · `.hook_post_everything(fn)` | Bracket the post-hook pipeline |
|
|
594
239
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-

|
|
240
|
+
The full surface - including history, validators, factories, data, and the
|
|
241
|
+
graph-introspection methods - is in the [generated API
|
|
242
|
+
docs](https://stonecypher.github.io/jssm/docs/).
|
|
601
243
|
|
|
602
244
|
|
|
603
245
|
|
|
604
246
|
<br/>
|
|
605
247
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
We'll add the ability to physically insert a card, next. You know, the, uh, thing ATMs are pretty much for.
|
|
609
|
-
|
|
610
|
-
To get this, add the path leg `EmptyWaiting 'InsertCard' -> HasCardNoAuth;`
|
|
611
|
-
|
|
612
|
-
```fsl
|
|
613
|
-
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
|
|
614
|
-
EjectCardAndReset -> EmptyWaiting;
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
Notice that the new `state`, **HasCardNoAuth**, has been rendered red. This is because it is `terminal` - there is
|
|
618
|
-
no exit from this node currently. (**EmptyAndWaiting** did not render that way because it had a transition to itself.)
|
|
619
|
-
That will change as we go back to adding more nodes. `terminal node`s are usually either mistakes or the last single
|
|
620
|
-
`state` of a given `FSM`.
|
|
248
|
+
## Status
|
|
621
249
|
|
|
622
|
-
|
|
250
|
+
In production use since May 2017. Current series is 5.x and the DSL is
|
|
251
|
+
stable; the runtime API has been additive for several years. MIT licensed
|
|
252
|
+
end to end. Test data and parser cases are included for English, German,
|
|
253
|
+
French, Spanish, Hebrew, Russian, Ukrainian, Belarusian, Bengali,
|
|
254
|
+
Portuguese, and Emoji.
|
|
623
255
|
|
|
624
256
|
|
|
625
257
|
|
|
626
258
|
<br/>
|
|
627
259
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
Next, we should have a cancel, because the ATM's <key>7</key> key is broken, and we need our card back. Cancel will
|
|
631
|
-
exit to the main menu, and return our card credential.
|
|
632
|
-
|
|
633
|
-
To that end, we add the path `HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;`
|
|
634
|
-
|
|
635
|
-
```fsl
|
|
636
|
-
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
|
|
260
|
+
## Community
|
|
637
261
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
EjectCardAndReset -> EmptyWaiting;
|
|
641
|
-
```
|
|
262
|
+
<a href="https://discord.gg/9P95USqnMK"></a>
|
|
642
263
|
|
|
643
|
-
|
|
264
|
+
Questions, design discussions, bug reports, and "what would you do for X?"
|
|
265
|
+
are all welcome on Discord. Issues that need a paper trail go in the
|
|
266
|
+
[issue tracker](https://github.com/StoneCypher/fsl/issues).
|
|
644
267
|
|
|
645
268
|
|
|
646
269
|
|
|
647
270
|
<br/>
|
|
648
271
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
Next, let's give the ability to get the password ... wrong. 😂 Because we all know that one ATM that only has the
|
|
652
|
-
wrong-PIN path, so, apparently that's a product to someone.
|
|
653
|
-
|
|
654
|
-
When they get the PIN wrong, they're prompted to try again (or to cancel.)
|
|
655
|
-
|
|
656
|
-
We'll add the path `HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;`
|
|
657
|
-
|
|
658
|
-
```fsl
|
|
659
|
-
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
|
|
660
|
-
|
|
661
|
-
HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
|
|
662
|
-
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
|
|
663
|
-
|
|
664
|
-
EjectCardAndReset -> EmptyWaiting;
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-

|
|
272
|
+
## Comparisons
|
|
668
273
|
|
|
274
|
+
A direct, head-to-head comparison with the other actively-maintained JS state
|
|
275
|
+
machine libraries - XState, Stately.js, Finity, machina.js, and others - is
|
|
276
|
+
in progress and will live in
|
|
277
|
+
[FeatureComparison.md](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/FeatureComparison.md).
|
|
669
278
|
|
|
279
|
+
A list of related projects, without commentary, is at the bottom of that
|
|
280
|
+
file.
|
|
670
281
|
|
|
671
282
|
|
|
672
283
|
|
|
673
284
|
<br/>
|
|
674
285
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
Next, let's give the ability to get the password right.
|
|
678
|
-
|
|
679
|
-
We'll add two paths. The first gets the password right: `HasCardNoAuth 'RightPIN' -> MainMenu;`
|
|
680
|
-
|
|
681
|
-
The second, from our new `state` **MainMenu**, gives people the ability to leave: `MainMenu 'ExitReturnCard' -> EjectCardAndReset;`
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
```fsl
|
|
685
|
-
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
|
|
686
|
-
|
|
687
|
-
HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
|
|
688
|
-
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
|
|
689
|
-
HasCardNoAuth 'RightPIN' -> MainMenu;
|
|
690
|
-
|
|
691
|
-
MainMenu 'ExitReturnCard' -> EjectCardAndReset;
|
|
692
|
-
|
|
693
|
-
EjectCardAndReset -> EmptyWaiting;
|
|
694
|
-
```
|
|
286
|
+
## Contributing
|
|
695
287
|
|
|
696
|
-
|
|
288
|
+
Issues and PRs are welcome. The cheapest useful contribution is a language
|
|
289
|
+
test case: open a PR with
|
|
290
|
+
[`english.json`](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/english.json)
|
|
291
|
+
translated into your language. Translating
|
|
292
|
+
[`traffic_light.fsl`](https://github.com/StoneCypher/fsl_traffic_light/blob/master/traffic_light.fsl)
|
|
293
|
+
into a separate repo and publishing it goes a step further.
|
|
697
294
|
|
|
698
295
|
|
|
699
296
|
|
|
700
297
|
<br/>
|
|
701
298
|
|
|
702
|
-
|
|
299
|
+
## Acknowledgements
|
|
703
300
|
|
|
704
|
-
|
|
301
|
+
[Michael Morgan](https://github.com/msmorgan/) has debated significant
|
|
302
|
+
sections of the notation, invented several concepts and operators, helped
|
|
303
|
+
with the parser and system nomenclature, and published the first non-author
|
|
304
|
+
`FSL` machine. [Vat Raghavan](https://github.com/MachinShin) participated
|
|
305
|
+
extensively in language design and implemented several features. [Forest
|
|
306
|
+
Belton](https://github.com/forestbelton) provided guidance, bugfixes, and
|
|
307
|
+
parser commentary. [Jordan
|
|
308
|
+
Harbrand](https://github.com/ljharb) suggested two interesting features and
|
|
309
|
+
gave strong feedback on the initial tutorial draft.
|
|
705
310
|
|
|
706
|
-
|
|
707
|
-
balance of, then you're shown a screen with the information you requested; then go back to the main menu.
|
|
311
|
+
Translation contributors:
|
|
708
312
|
|
|
709
|
-
|
|
313
|
+
- [Mykhaylo Les](https://github.com/miles91) - [Ukrainian](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/ukrainian.json), [Belarusian](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/belarussian.json), [Russian](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/russian.json)
|
|
314
|
+
- [Tanvir Islam](https://github.com/tanvirrb) - [Bengali](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/bengali.json) (also published the first non-English `FSL` machine)
|
|
315
|
+
- [Francisco Junior](https://github.com/fcojr) - [Portuguese](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/portuguese.json)
|
|
316
|
+
- [Jeff Katz](https://github.com/kraln) - [German](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/german.json)
|
|
317
|
+
- [Alex Cresswell](https://github.com/technophile77) - [Spanish](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/spanish.json)
|
|
318
|
+
- [Dvir Cohen](https://github.com/cohendvir) - [Hebrew](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/hebrew.json)
|
|
319
|
+
- [David de la Peña](https://github.com/daviddelapena) - [French](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/french.json)
|
|
710
320
|
|
|
711
|
-
|
|
712
|
-
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
|
|
713
|
-
|
|
714
|
-
HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
|
|
715
|
-
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
|
|
716
|
-
HasCardNoAuth 'RightPIN' -> MainMenu;
|
|
717
|
-
|
|
718
|
-
MainMenu 'ExitReturnCard' -> EjectCardAndReset;
|
|
719
|
-
MainMenu 'CheckBalance' -> PickAccount -> DisplayBalance -> MainMenu;
|
|
720
|
-
|
|
721
|
-
EjectCardAndReset -> EmptyWaiting;
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-

|
|
321
|
+
If your contribution is missing here, please open an issue.
|
|
725
322
|
|
|
726
323
|
|
|
727
324
|
|
|
728
325
|
<br/>
|
|
729
326
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
Let's add something difficult. Their state machine just proceeds assuming everything is okay.
|
|
733
|
-
|
|
734
|
-
To desposit money:
|
|
735
|
-
|
|
736
|
-
1. Accept physical money
|
|
737
|
-
2. If accept failed (eg door jammed,) reject physical object, go to main menu
|
|
738
|
-
3. If accept succeeded, ask human expected value
|
|
739
|
-
4. Pick an account this should go into
|
|
740
|
-
5. Contact bank. Request to credit for theoretical physical money.
|
|
741
|
-
6. Three results: yes, no, offer-after-audit.
|
|
742
|
-
7. If no, reject physical object, go to main menu.
|
|
743
|
-
8. If yes, consume physical object, tell user consumed, go to main menu
|
|
744
|
-
9. If offer-after-audit, ask human what to do
|
|
745
|
-
10. if human-yes, consume physical object, tell user consumed, go to main menu
|
|
746
|
-
11. if human-no, reject physical object, go to main menu
|
|
747
|
-
|
|
748
|
-
Writing this out in code is not only generally longer than the text form, but also error prone and hard to maintain.
|
|
749
|
-
|
|
750
|
-
... or there's the `FSM` `DSL`, which is usually as-brief-as the text, and frequently both briefer and more explicit.
|
|
751
|
-
|
|
752
|
-
* Rules 1-2: `MainMenu 'AcceptDeposit' -> TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;`
|
|
753
|
-
* Rules 3-6: `TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;`
|
|
754
|
-
* Rule 7: `BankResponse 'BankNo' -> RejectPhysicalMoney;`
|
|
755
|
-
* Rule 8: `BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;`
|
|
756
|
-
* Rules 9-10: `BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;`
|
|
757
|
-
* Rule 11: `BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney;`
|
|
758
|
-
|
|
759
|
-
Or, as a block,
|
|
760
|
-
|
|
761
|
-
```fsl
|
|
762
|
-
MainMenu 'AcceptDeposit' -> TentativeAcceptMoney;
|
|
763
|
-
|
|
764
|
-
TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;
|
|
765
|
-
TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;
|
|
766
|
-
|
|
767
|
-
BankResponse 'BankNo' -> RejectPhysicalMoney;
|
|
768
|
-
BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;
|
|
769
|
-
BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;
|
|
770
|
-
|
|
771
|
-
BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney;
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
Which leaves us with the total code
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
```fsl
|
|
778
|
-
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
|
|
779
|
-
|
|
780
|
-
HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
|
|
781
|
-
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
|
|
782
|
-
HasCardNoAuth 'RightPIN' -> MainMenu;
|
|
783
|
-
|
|
784
|
-
MainMenu 'AcceptDeposit' -> TentativeAcceptMoney;
|
|
785
|
-
MainMenu 'ExitReturnCard' -> EjectCardAndReset;
|
|
786
|
-
MainMenu 'CheckBalance' -> PickCheckBalanceAccount -> DisplayBalance -> MainMenu;
|
|
787
|
-
|
|
788
|
-
TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;
|
|
789
|
-
TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;
|
|
790
|
-
|
|
791
|
-
BankResponse 'BankNo' -> RejectPhysicalMoney;
|
|
792
|
-
BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;
|
|
793
|
-
BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;
|
|
794
|
-
|
|
795
|
-
BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney;
|
|
796
|
-
|
|
797
|
-
EjectCardAndReset -> EmptyWaiting;
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-

|
|
801
|
-
|
|
327
|
+
---
|
|
802
328
|
|
|
329
|
+
<details>
|
|
330
|
+
<summary>Stats, coverage, and badges</summary>
|
|
803
331
|
|
|
804
332
|
<br/>
|
|
805
333
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
Let's also be able to take money from the machine. After this, we'll move on, since our example is pretty squarely made
|
|
809
|
-
by now.
|
|
810
|
-
|
|
811
|
-
1. Pick a withdrawl account, or cancel to the main menu
|
|
812
|
-
2. Shown a balance, pick a withdrawl amount, or cancel to acct picker
|
|
813
|
-
3. Is the withdrawl account too high? If so go to 2
|
|
814
|
-
4. Does the machine actually have the money? If not go to 2
|
|
815
|
-
5. Otherwise confirm intent w/ human
|
|
816
|
-
6. Attempt to post the transaction.
|
|
817
|
-
7. If fail, display reason and go to 1
|
|
818
|
-
8. If succeed, dispense money and go to main menu
|
|
819
|
-
|
|
820
|
-
* Rules 1-3: `MainMenu -> PickWithdrawlAccount -> PickAmount -> AcctHasMoney? 'TooHighForAcct' -> PickWithdrawlAccount;`
|
|
821
|
-
* Rule 4: `AcctHasMoney? -> MachineHasMoney? 'MachineLowOnCash' -> PickAmount;`
|
|
822
|
-
* Rule 5: `MachineHasMoney? -> ConfirmWithdrawWithHuman 'MakeChanges' -> PickWithdrawlAmount;`
|
|
823
|
-
* Rule 6: `ConfirmWithdrawWithHuman 'PostWithdrawl' -> BankWithdrawlResponse;`
|
|
824
|
-
* Rule 7: `BankWithdrawlResponse 'WithdrawlFailure' -> WithdrawlFailureExplanation -> PickWithdrawlAccount;`
|
|
825
|
-
* Rule 8: `BankWithdrawlResponse 'WithdrawlSuccess' -> DispenseMoney -> MainMenu;`
|
|
826
|
-
|
|
827
|
-
Rule 1 canceller: `PickWithdrawlAccount 'CancelWithdrawl' -> MainMenu;`
|
|
828
|
-
Rule 2 canceller: `PickWithdrawlAmount 'SwitchAccounts' -> PickWithdrawlAccount;`
|
|
829
|
-
|
|
830
|
-
Or as a whole, we're adding
|
|
831
|
-
|
|
832
|
-
```fsl
|
|
833
|
-
MainMenu -> PickWithdrawlAccount -> PickAmount -> AcctHasMoney? 'TooHighForAcct' -> PickWithdrawlAccount;
|
|
834
|
-
AcctHasMoney? -> MachineHasMoney? 'MachineLowOnCash' -> PickAmount;
|
|
835
|
-
MachineHasMoney? -> ConfirmWithdrawWithHuman 'MakeChanges' -> PickWithdrawlAmount;
|
|
836
|
-
ConfirmWithdrawWithHuman 'PostWithdrawl' -> BankWithdrawlResponse;
|
|
837
|
-
BankWithdrawlResponse 'WithdrawlFailure' -> WithdrawlFailureExplanation -> PickWithdrawlAccount;
|
|
838
|
-
BankWithdrawlResponse 'WithdrawlSuccess' -> DispenseMoney -> MainMenu;
|
|
839
|
-
|
|
840
|
-
PickWithdrawlAccount 'CancelWithdrawl' -> MainMenu;
|
|
841
|
-
PickWithdrawlAmount 'SwitchAccounts' -> PickWithdrawlAccount;
|
|
842
|
-
```
|
|
843
|
-
|
|
844
|
-
Which leaves us with
|
|
845
|
-
|
|
846
|
-
```fsl
|
|
847
|
-
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
|
|
848
|
-
|
|
849
|
-
HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
|
|
850
|
-
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
|
|
851
|
-
HasCardNoAuth 'RightPIN' -> MainMenu;
|
|
852
|
-
|
|
853
|
-
MainMenu 'AcceptDeposit' -> TentativeAcceptMoney;
|
|
854
|
-
MainMenu 'ExitReturnCard' -> EjectCardAndReset;
|
|
855
|
-
MainMenu 'CheckBalance' -> PickCheckBalanceAccount -> DisplayBalance -> MainMenu;
|
|
856
|
-
|
|
857
|
-
TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;
|
|
858
|
-
TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;
|
|
859
|
-
|
|
860
|
-
BankResponse 'BankNo' -> RejectPhysicalMoney;
|
|
861
|
-
BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;
|
|
862
|
-
BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;
|
|
863
|
-
|
|
864
|
-
BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney;
|
|
865
|
-
|
|
866
|
-
MainMenu -> PickWithdrawlAccount -> PickAmount -> AcctHasMoney? 'TooHighForAcct' -> PickWithdrawlAccount;
|
|
867
|
-
AcctHasMoney? -> MachineHasMoney? 'MachineLowOnCash' -> PickAmount;
|
|
868
|
-
MachineHasMoney? -> ConfirmWithdrawWithHuman 'MakeChanges' -> PickWithdrawlAmount;
|
|
869
|
-
ConfirmWithdrawWithHuman 'PostWithdrawl' -> BankWithdrawlResponse;
|
|
870
|
-
BankWithdrawlResponse 'WithdrawlFailure' -> WithdrawlFailureExplanation -> PickWithdrawlAccount;
|
|
871
|
-
BankWithdrawlResponse 'WithdrawlSuccess' -> DispenseMoney -> MainMenu;
|
|
872
|
-
|
|
873
|
-
PickWithdrawlAccount 'CancelWithdrawl' -> MainMenu;
|
|
874
|
-
PickWithdrawlAmount 'SwitchAccounts' -> PickWithdrawlAccount;
|
|
875
|
-
|
|
876
|
-
EjectCardAndReset -> EmptyWaiting;
|
|
877
|
-
```
|
|
878
|
-
|
|
879
|
-

|
|
880
|
-
|
|
881
|
-
As you can see, building up even very complex state machines is actually relatively straightforward, in a short
|
|
882
|
-
amount of time.
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
<br/><br/>
|
|
887
|
-
|
|
888
|
-
## Features
|
|
889
|
-
### DSL
|
|
890
|
-
### States
|
|
891
|
-
### Transitions
|
|
892
|
-
### Cycles
|
|
893
|
-
### Stripes
|
|
894
|
-
### Named Ordered Lists
|
|
895
|
-
### Atoms
|
|
896
|
-
### Strings
|
|
897
|
-
### Arrow types
|
|
898
|
-
### Unicode representations
|
|
899
|
-
### Node declarations
|
|
900
|
-
### All the styling bullshit
|
|
901
|
-
### Named edges
|
|
902
|
-
### URL callouts
|
|
903
|
-
### The 9 or whatever directives
|
|
904
|
-
### How to publish a machine
|
|
905
|
-
#### Legal, main, and forced
|
|
906
|
-
### Validators
|
|
907
|
-
### State history
|
|
908
|
-
### Automatic visualization
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
<br/><br/>
|
|
913
|
-
|
|
914
|
-
## How to think in state machines
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
<br/><br/>
|
|
919
|
-
|
|
920
|
-
## Example Machines
|
|
921
|
-
### Door lock
|
|
922
|
-
### Traffic lights
|
|
923
|
-
#### Basic three-state
|
|
924
|
-
#### RYG, Off, Flash-red, Flash-yellow
|
|
925
|
-
#### RYG, Off, Flash-red, Flash-yellow, Green-left, Yellow-left
|
|
926
|
-
#### Heirarchal intersection
|
|
927
|
-
### [ATM](https://people.engr.ncsu.edu/efg/210/s99/Notes/fsm/atm.gif)
|
|
928
|
-
### [HTTP](https://www.w3.org/Library/User/Architecture/HTTP.gif)
|
|
929
|
-
#### Better HTTP
|
|
930
|
-
### [TCP](http://www.texample.net/media/tikz/examples/PNG/tcp-state-machine.png)
|
|
931
|
-
### Coin-op vending machine (data)
|
|
932
|
-
### Video games
|
|
933
|
-
#### Pac-man Ghost (sensors)
|
|
934
|
-
#### Weather (probabilistics)
|
|
935
|
-
#### Roguelike monster (interface satisfaction)
|
|
936
|
-
### Candy crush clone game flow (practical large use)
|
|
937
|
-
### Vegas locked 21 dealer behavior
|
|
938
|
-
### React SPA website (practical large use)
|
|
939
|
-
### [BGP](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/BGP_FSM.svg/549px-BGP_FSM.svg.png)
|
|
940
|
-
### [LibGCrypt FIPS mode FSM](https://www.gnupg.org/documentation/manuals/gcrypt/fips-fsm.png)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
<br/><br/>
|
|
945
|
-
|
|
946
|
-
## How to debug
|
|
334
|
+
***5,342 tests***, run 14,351 times.
|
|
947
335
|
|
|
336
|
+
- 5,251 specs with 100.0% coverage
|
|
337
|
+
- 91 fuzz tests with 62.2% coverage
|
|
338
|
+
- 3,460 TypeScript lines - 1.5 tests per line, 4.1 generated tests per line
|
|
948
339
|
|
|
340
|
+
[](https://github.com/StoneCypher/jssm/actions)
|
|
341
|
+
[](https://www.npmjs.com/package/jssm)
|
|
342
|
+
[](https://www.npmjs.com/package/jssm)
|
|
343
|
+
[](https://github.com/StoneCypher/jssm/blob/main/LICENSE.md)
|
|
344
|
+
[](https://coveralls.io/github/StoneCypher/jssm)
|
|
345
|
+
[](https://github.com/StoneCypher/fsl/issues)
|
|
949
346
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
## How to publish
|
|
953
|
-
It's really quite simple.
|
|
954
|
-
|
|
955
|
-
1. Make a github repository.
|
|
956
|
-
1. Put your code in a file inside, with the extension `.fsl`
|
|
957
|
-
1. Make sure your code contains a `machine_name`
|
|
958
|
-
|
|
959
|
-
Once done, your work should show up [here](https://github.com/search?utf8=%E2%9C%93&q=extension%3Afsl+machine_name&type=Code).
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
<br/><br/>
|
|
964
|
-
|
|
965
|
-
## Notation Comparison
|
|
966
|
-
### Their notations, one by one
|
|
967
|
-
### Apples to Apples - Traffic Light
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
<br/><br/>
|
|
972
|
-
|
|
973
|
-
## Other state machines
|
|
974
|
-
There are a lot of state machine impls for JS, many quite a bit more mature than this one. Here are some options:
|
|
975
|
-
|
|
976
|
-
1. [Finity](https://github.com/nickuraltsev/finity) 😮
|
|
977
|
-
1. [Stately.js](https://github.com/fschaefer/Stately.js)
|
|
978
|
-
1. [machina.js](https://github.com/ifandelse/machina.js)
|
|
979
|
-
1. [Pastafarian](https://github.com/orbitbot/pastafarian)
|
|
980
|
-
1. [Henderson](https://github.com/orbitbot/henderson)
|
|
981
|
-
1. [fsm-as-promised](https://github.com/vstirbu/fsm-as-promised)
|
|
982
|
-
1. [state-machine](https://github.com/DEADB17/state-machine)
|
|
983
|
-
1. [mood](https://github.com/bredele/mood)
|
|
984
|
-
1. [FSM Workbench](https://github.com/MatthewHepburn/FSM-Workbench)
|
|
985
|
-
1. [SimpleStateMachine](https://github.com/ccnokes/SimpleStateMachine)
|
|
986
|
-
1. shime/[micro-machine](https://github.com/shime/micro-machine)
|
|
987
|
-
1. soveran/[micromachine](https://github.com/soveran/micromachine) (ruby)
|
|
988
|
-
1. fabiospampinato/[FSM](https://github.com/fabiospampinato/FSM)
|
|
989
|
-
1. HQarroum/[FSM](https://github.com/HQarroum/Fsm)
|
|
990
|
-
1. [Finite-State-Automata](https://github.com/RolandR/Finite-State-Automata)
|
|
991
|
-
1. [finite-state-machine](https://github.com/MarkH817/finite-state-machine)
|
|
992
|
-
1. [nfm](https://github.com/ajauhri/nfm)
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
And some similar stuff:
|
|
996
|
-
1. [redux-machine](https://github.com/mheiber/redux-machine)
|
|
997
|
-
1. [ember-fsm](https://github.com/heycarsten/ember-fsm)
|
|
998
|
-
1. [State machine cat](https://github.com/sverweij/state-machine-cat)
|
|
999
|
-
1. [Workty](https://github.com/AlexLevshin/workty) 😮
|
|
1000
|
-
1. [sam-simpler](https://github.com/sladiri/sam-simpler)
|
|
1001
|
-
1. [event_chain](https://github.com/quilin/event_chain)
|
|
1002
|
-
1. [DRAKON](https://en.wikipedia.org/wiki/DRAKON)
|
|
1003
|
-
1. [Yakindu Statechart Tools](https://github.com/Yakindu/statecharts)
|
|
1004
|
-
1. [GraphViz](http://www.graphviz.org/)
|
|
1005
|
-
1. [Viz.js](https://github.com/mdaines/viz.js/), which we use
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
<br/><br/><br/>
|
|
1010
|
-
|
|
1011
|
-
# Thanks
|
|
1012
|
-
|
|
1013
|
-
JSSM and FSL have had a lot of help.
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
<br/><br/>
|
|
1018
|
-
|
|
1019
|
-
## Internationalization
|
|
1020
|
-
|
|
1021
|
-
* [Mykhaylo Les](https://github.com/miles91) provided three translation test cases ([Ukrainian](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/ukrainian.json), [Belarussian](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/belarussian.json), and [Russian](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/russian.json),) and the corresponding Traffic Light translations (also [Ukrainian](https://github.com/StoneCypher/fsl_traffic_light_ukrainian/blob/master/traffic%20light.fsl), [Belarussian](https://github.com/StoneCypher/fsl_traffic_light_belarussian/blob/master/traffic_light.fsl), and [Russian](https://github.com/StoneCypher/fsl_traffic_light_russian/blob/master/traffic%20light.fsl).)
|
|
1022
|
-
* [Tanvir Islam](https://github.com/tanvirrb) provided the [Bengali test case](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/bengali.json), translated the [Traffic Light](https://github.com/tanvirrb/fsl-traffic-light-bengali/blob/master/traffic_light.fsl) to Bengali, and published the first non-English `FSL` machine, in Bengali.
|
|
1023
|
-
* [Francisco Junior](https://github.com/fcojr) provided the [Portuguese test case](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/portuguese.json) and translated the [Traffic Light](https://github.com/StoneCypher/fsl_traffic_light_portuguese/blob/master/traffic_light.fsl) to Portuguese
|
|
1024
|
-
* [Jeff Katz](https://github.com/kraln) provided the [German test case](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/german.json).
|
|
1025
|
-
* [Alex Cresswell](https://github.com/technophile77) provdied the [Spanish test case](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/spanish.json)
|
|
1026
|
-
* [Dvir Cohen](https://github.com/cohendvir) provided the [Hebrew test case](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/hebrew.json).
|
|
1027
|
-
* [David de la Peña](https://github.com/daviddelapena) provided the [French test case](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/french.json)
|
|
1028
|
-
|
|
1029
|
-
If I've overlooked you, please let me know.
|
|
1030
|
-
|
|
1031
|
-
If you'd like to help, it's straightforward.
|
|
1032
|
-
|
|
1033
|
-
1. Easy mode: open a PR with [this file](https://github.com/StoneCypher/jssm/blob/master/src/js/tests/language_data/english.json) translated into your language
|
|
1034
|
-
1. Extra mile: create a new repo containing [this file](https://github.com/StoneCypher/fsl_traffic_light/blob/master/traffic_light.fsl) translated
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
<br/><br/>
|
|
1039
|
-
|
|
1040
|
-
## Code and Language
|
|
1041
|
-
|
|
1042
|
-
[Vat Raghavan](https://github.com/MachinShin) has participated extensively in language discussion and implemented several features.
|
|
1043
|
-
|
|
1044
|
-
[Forest Belton](https://github.com/forestbelton) has provided guidance, bugfixes, parser and language commentary.
|
|
1045
|
-
|
|
1046
|
-
[Jordan Harbrand](https://github.com/ljharb) suggested two interesting features and provided strong feedback on the initial tutorial draft.
|
|
1047
|
-
|
|
1048
|
-
The biggest thanks must go to [Michael Morgan](https://github.com/msmorgan/), who has debated significant sections of
|
|
1049
|
-
the notation, invented several concepts and operators, helped with the parser, with system nomenclature, for having published
|
|
1050
|
-
the first not-by-me `FSL` machine, for encouragement, and generally just for having been as interested as he has been.
|
|
347
|
+
</details>
|