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.
Files changed (84) hide show
  1. package/MIGRATING-jssm-viz.md +67 -0
  2. package/README.md +179 -882
  3. package/dist/deno/README.md +347 -0
  4. package/dist/{es6 → deno}/jssm.d.ts +773 -39
  5. package/dist/deno/jssm.js +1 -0
  6. package/{jssm_compiler.d.ts → dist/deno/jssm_compiler.d.ts} +17 -2
  7. package/dist/deno/jssm_constants.d.ts +37 -0
  8. package/dist/deno/jssm_error.d.ts +27 -0
  9. package/dist/deno/jssm_theme.d.ts +15 -0
  10. package/dist/{es6 → deno}/jssm_types.d.ts +327 -8
  11. package/dist/deno/jssm_util.d.ts +258 -0
  12. package/dist/deno/jssm_viz.d.ts +175 -0
  13. package/dist/deno/jssm_viz_colors.d.ts +63 -0
  14. package/dist/jssm.es5.cjs +1 -1
  15. package/dist/jssm.es5.iife.js +1 -0
  16. package/dist/jssm.es6.mjs +1 -1
  17. package/dist/jssm_viz.cjs +1 -0
  18. package/dist/jssm_viz.iife.cjs +1 -0
  19. package/dist/jssm_viz.mjs +1 -0
  20. package/jssm.es5.d.cts +1191 -43
  21. package/jssm.es6.d.ts +1191 -43
  22. package/jssm_viz.es5.d.cts +2341 -0
  23. package/jssm_viz.es6.d.ts +2341 -0
  24. package/package.json +73 -24
  25. package/.clocignore +0 -1
  26. package/.codeclimate.yml +0 -22
  27. package/.editorconfig +0 -12
  28. package/.eslintrc +0 -20
  29. package/.gitattributes +0 -6
  30. package/.nycrc +0 -6
  31. package/.travis.yml +0 -9
  32. package/CHANGELOG.md +0 -178
  33. package/dist/es6/fsl_parser.js +0 -1
  34. package/dist/es6/jssm.js +0 -2488
  35. package/dist/es6/jssm_arrow.js +0 -187
  36. package/dist/es6/jssm_compiler.d.ts +0 -135
  37. package/dist/es6/jssm_compiler.js +0 -366
  38. package/dist/es6/jssm_constants.d.ts +0 -5
  39. package/dist/es6/jssm_constants.js +0 -94
  40. package/dist/es6/jssm_error.d.ts +0 -8
  41. package/dist/es6/jssm_error.js +0 -28
  42. package/dist/es6/jssm_theme.d.ts +0 -4
  43. package/dist/es6/jssm_theme.js +0 -13
  44. package/dist/es6/jssm_types.js +0 -3
  45. package/dist/es6/jssm_util.d.ts +0 -106
  46. package/dist/es6/jssm_util.js +0 -180
  47. package/dist/es6/themes/jssm_base_stylesheet.d.ts +0 -11
  48. package/dist/es6/themes/jssm_base_stylesheet.js +0 -58
  49. package/dist/es6/themes/jssm_theme_bold.d.ts +0 -11
  50. package/dist/es6/themes/jssm_theme_bold.js +0 -58
  51. package/dist/es6/themes/jssm_theme_default.d.ts +0 -11
  52. package/dist/es6/themes/jssm_theme_default.js +0 -58
  53. package/dist/es6/themes/jssm_theme_modern.d.ts +0 -11
  54. package/dist/es6/themes/jssm_theme_modern.js +0 -58
  55. package/dist/es6/themes/jssm_theme_ocean.d.ts +0 -11
  56. package/dist/es6/themes/jssm_theme_ocean.js +0 -56
  57. package/dist/es6/themes/jssm_theme_plain.d.ts +0 -11
  58. package/dist/es6/themes/jssm_theme_plain.js +0 -70
  59. package/dist/es6/version.js +0 -2
  60. package/dist/jssm.es5.iife.cjs +0 -1
  61. package/dist/jssm.es5.iife.nonmin.cjs +0 -23180
  62. package/dist/jssm.es5.nonmin.cjs +0 -23175
  63. package/dist/jssm.es6.nonmin.cjs +0 -23144
  64. package/fsl_parser.d.ts +0 -6
  65. package/jest-dragon.config.cjs +0 -33
  66. package/jest-spec.config.cjs +0 -33
  67. package/jest-stoch.config.cjs +0 -33
  68. package/jest-unicode.config.cjs +0 -33
  69. package/jssm.d.ts +0 -1141
  70. package/jssm_arrow.d.ts +0 -53
  71. package/jssm_constants.d.ts +0 -5
  72. package/jssm_error.d.ts +0 -8
  73. package/jssm_theme.d.ts +0 -4
  74. package/jssm_types.d.ts +0 -378
  75. package/jssm_util.d.ts +0 -106
  76. package/rollup.config.deno.js +0 -44
  77. package/rollup.config.es5.js +0 -52
  78. package/rollup.config.es6.js +0 -55
  79. package/tutorial_learn_testing.md +0 -168
  80. package/typedoc-options.cjs +0 -68
  81. package/version.d.ts +0 -2
  82. /package/dist/{es6 → deno}/fsl_parser.d.ts +0 -0
  83. /package/dist/{es6 → deno}/jssm_arrow.d.ts +0 -0
  84. /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.104.2 at 9/2/2025, 6:38:27 AM
21
+ * Generated for version 5.112.4 at 5/12/2026, 11:13:56 AM
22
22
 
23
23
  -->
24
- # jssm 5.104.2
24
+ # jssm 5.112.4
25
25
 
26
- Easy. Small. Fast. TS, es6, es5. Node, Browser. 100% coverage. Property
27
- tests. Fuzz tests. Language tests for a dozen languages and emoji. Easy to
28
- share online. Easy to embed.
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
- Readable, useful state machines as one-liner strings.
31
-
32
- ***5,072 tests***, run 5,963 times.
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">![Discord community](https://discordapp.com/api/guilds/899910109642235924/widget.png?style=banner1)</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
- <br/>
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
- const MTL = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;` // MTL = More Traffic Lights
95
- .hook('Red', 'Green', () => log('GO GO GO') ) // node will jump the gun when you hit return, though
96
- .hook_entry('Red', () => log('STOP') ); // so put it on one line in node
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
- <br/>
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
- const ATL = sm`Red -> Green -> Yellow -> Red;`; // ATL = Another Traffic Light
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
- <br/>
56
+ A more involved machine, with main paths, forced paths, and per-state
57
+ styling, renders to:
123
58
 
124
- What if there were easy convenience notations for lists, and for designating main-path `=>` vs available path `->` vs
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
- <br/>
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
- <a href="https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html" target="_blank" rel="noopener noreferrer">TRY THE LIVE EDITOR</a>
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
- ## What is JSSM?
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
- [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
231
-
232
- [![GitHub forks](https://img.shields.io/github/forks/StoneCypher/jssm.svg?style=social&label=Fork%20JSSM)]()
233
- [![GitHub watchers](https://img.shields.io/github/watchers/StoneCypher/jssm.svg?style=social&label=Watch%20JSSM)]()
234
- [![GitHub stars](https://img.shields.io/github/stars/StoneCypher/jssm.svg?style=social&label=JSSM%20Stars)]()
235
- [![GitHub followers](https://img.shields.io/github/followers/StoneCypher.svg?style=social&label=Follow%20StoneCypher)]()
236
-
237
- [![License](https://img.shields.io/npm/l/jssm.svg)](https://github.com/StoneCypher/jssm/blob/master/LICENSE.md)
238
- [![Open issues](https://img.shields.io/github/issues/StoneCypher/jssm.svg)](https://github.com/StoneCypher/jssm/issues)
239
- [![Closed issues](https://img.shields.io/github/issues-closed/StoneCypher/jssm.svg)](https://github.com/StoneCypher/jssm/issues?q=is%3Aissue+is%3Aclosed)
240
- [![Travis status](https://img.shields.io/travis/StoneCypher/jssm.svg)](https://travis-ci.org/StoneCypher/jssm)
241
- [![Coveralls status](https://img.shields.io/coveralls/StoneCypher/jssm.svg)](https://coveralls.io/github/StoneCypher/jssm)
242
-
243
- [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
244
- [![CDNjs version](https://img.shields.io/cdnjs/v/jquery.svg)](https://img.shields.io/cdnjs/v/jquery.svg)
245
- [![NPM downloads](https://img.shields.io/npm/dt/jssm.svg)](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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/ryg%20proceed.png)
267
-
268
- You'll build an executable state machine.
269
-
270
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/ryg%20traffic%20light%20console%20screenshot.png)
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/ryg%20proceed.png)
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/ryg%20proceed.png)
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** &rarr; **Yellow**
404
- * **Yellow** &rarr; **Red**
405
- * **Red** &rarr; **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
- #### 0: Lights always have an off state
105
+ ## Visualization
457
106
 
458
- Our light will start in the **Off** `state`, with the ability to switch to the **Red** `state`.
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
- Since that's a normal, not-notable thing, we'll just make it a regular `-> legal transition`.
110
+ ```typescript
111
+ import { sm } from 'jssm';
112
+ import { fsl_to_svg_string } from 'jssm/viz';
461
113
 
462
- ```fsl
463
- Off -> Red;
114
+ const svg = await fsl_to_svg_string('a -> b;');
464
115
  ```
465
116
 
466
- We will give that `transition` an `action`, and call it **TurnOn**.
467
-
468
- ```fsl
469
- Off 'TurnOn' -> Red;
470
- ```
471
-
472
- So far, our machine is simple:
473
-
474
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/traffic%20light%20quick%20start%20tutorial/Off%20To%20Red.png)
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
- #### 1: Traffic lights have a three-color cycle
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
- ```fsl
486
- Off 'TurnOn' -> Red => Green => Yellow => Red;
487
- ```
127
+ **Actions** let a machine advance without the caller knowing the next state:
488
128
 
489
- We will give those all the same action name, **Proceed**, indicating "next color" without needing to know what we're
490
- currently on.
129
+ ```javascript
130
+ const Light = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`;
491
131
 
492
- ```fsl
493
- Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
132
+ Light.action('next'); // true
133
+ Light.state(); // 'Green'
134
+ Light.action('next'); // true
135
+ Light.state(); // 'Yellow'
494
136
  ```
495
137
 
496
- Machine's still pretty simple:
497
-
498
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/traffic%20light%20quick%20start%20tutorial/Off%20To%20RGY.png)
499
-
500
-
138
+ **Three arrow types** distinguish kinds of transition:
501
139
 
502
- <br/>
503
-
504
- #### 2: Traffic lights can be shut down
505
-
506
- We'd also like to be able to turn this light back off. Because that's expected to be a rarity, we'll require that it
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
- But that takes a lot of space even with this short list, so, instead we'll use the array notation
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
- ```fsl
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
- And we'd like those all to have the action **TurnOff**, so
526
-
527
- ```fsl
528
- Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
529
- [Red Yellow Green] 'TurnOff' ~> Off;
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
- Machine's still not too bad:
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/traffic%20light%20quick%20start%20tutorial/Off%20To%20From%20RGY.png)
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
- ### Let's actually use the traffic light
174
+ ## Why jssm
541
175
 
542
- That's actually the bulk of the language. There are other little add-ons here and there, but, primarily you now know
543
- how to write a state machine.
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
- Let's load it and use it! 😀
181
+ That decision shows up everywhere downstream:
546
182
 
547
- #### loading into node
548
- #### loading into html
549
- #### jssm-viz
550
- #### redistribution on npm
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
- <br/>
555
-
556
- ### An introduction to machine design
557
-
558
- Let's make a `state machine` for ATMs. In the process, we will use a lot of core concepts of `finite state machine`s
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/ncsu%20atm%20diagram.gif)
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
- #### 0: Empty machine
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
- ```fsl
579
- EmptyWaiting 'Wait' -> EmptyWaiting;
580
- ```
581
-
582
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/0_EmptyWaiting.png)
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
- #### 1: Should be able to eject cards
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
- This is expressed as the path `EjectCardAndReset -> EmptyWaiting;`
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
- ```fsl
596
- EmptyWaiting 'Wait' -> EmptyWaiting;
597
- EjectCardAndReset -> EmptyWaiting;
598
- ```
599
-
600
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/1_EjectCard.png)
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
- #### 2: Should be able to insert cards
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/2_InsertCard.png)
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
- #### 3: Should be able to cancel and recover the card
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
- HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
639
-
640
- EjectCardAndReset -> EmptyWaiting;
641
- ```
262
+ <a href="https://discord.gg/9P95USqnMK">![Discord community](https://discordapp.com/api/guilds/899910109642235924/widget.png?style=banner1)</a>
642
263
 
643
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/3_ReturnCard.png)
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
- #### 4: Can give the wrong PIN
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/4_WrongPin.png)
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
- #### 5: Can give the correct PIN
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/5_RightPin.png)
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
- #### 6: Can check balance from main menu
299
+ ## Acknowledgements
703
300
 
704
- Hooray, now we're getting somewhere.
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
- Let's add the ability to check your balance. First pick that from the main menu, then pick which account to see the
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
- That's `MainMenu 'CheckBalance' -> PickAccount -> DisplayBalance -> MainMenu;`.
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
- ```fsl
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/6_CanCheckBalance.png)
321
+ If your contribution is missing here, please open an issue.
725
322
 
726
323
 
727
324
 
728
325
  <br/>
729
326
 
730
- #### 7: Can deposit money from main menu
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/7_CanDepositMoney.png)
801
-
327
+ ---
802
328
 
329
+ <details>
330
+ <summary>Stats, coverage, and badges</summary>
803
331
 
804
332
  <br/>
805
333
 
806
- #### 8: Can withdraw money from main menu
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
- ![](https://raw.githubusercontent.com/StoneCypher/jssm/master/src/assets/atm%20quick%20start%20tutorial/8_CanWithdrawMoney.png)
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
+ [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
341
+ [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
342
+ [![NPM downloads](https://img.shields.io/npm/dt/jssm.svg)](https://www.npmjs.com/package/jssm)
343
+ [![License](https://img.shields.io/npm/l/jssm.svg)](https://github.com/StoneCypher/jssm/blob/main/LICENSE.md)
344
+ [![Coveralls status](https://img.shields.io/coveralls/StoneCypher/jssm.svg)](https://coveralls.io/github/StoneCypher/jssm)
345
+ [![Open issues](https://img.shields.io/github/issues/StoneCypher/fsl.svg)](https://github.com/StoneCypher/fsl/issues)
949
346
 
950
- <br/><br/>
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>