jssm 5.145.0 → 5.145.2

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.
@@ -0,0 +1,461 @@
1
+ <!--
2
+
3
+ I8, 8 ,8I 88
4
+ `8b d8b d8' ""
5
+ "8, ,8"8, ,8"
6
+ Y8 8P Y8 8P ,adPPYYba, 8b,dPPYba, 8b,dPPYba, 88 8b,dPPYba, ,adPPYb,d8
7
+ `8b d8' `8b d8' "" `Y8 88P' "Y8 88P' `"8a 88 88P' `"8a a8" `Y88
8
+ `8a a8' `8a a8' ,adPPPPP88 88 88 88 88 88 88 8b 88
9
+ `8a8' `8a8' 88, ,88 88 88 88 88 88 88 "8a, ,d88
10
+ `8' `8' `"8bbdP"Y8 88 88 88 88 88 88 `"YbbdP"Y8
11
+ aa, ,88
12
+ "Y8bbdP"
13
+
14
+ This file is generated. If you edit it, the edits ***will be lost***.
15
+ ----------------------
16
+
17
+ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
18
+
19
+
20
+
21
+ * Generated for version 5.145.2 at 6/22/2026, 8:17:04 PM
22
+
23
+ -->
24
+ # jssm 5.145.2
25
+
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)
30
+
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.
34
+
35
+ ```javascript
36
+ import { sm } from 'jssm';
37
+
38
+ const TrafficLight = sm`Red -> Green -> Yellow -> Red;`;
39
+ ```
40
+
41
+ That's it. Using it is equally easy:
42
+
43
+ ```javascript
44
+ TrafficLight.state(); // 'Red'
45
+ TrafficLight.go('Green'); // true
46
+ TrafficLight.state(); // 'Green'
47
+ ```
48
+
49
+ The point of a state machine is to refuse to do things that aren't correct:
50
+
51
+ ```javascript
52
+ TrafficLight.go('Red'); // false - Green doesn't go to Red, only Yellow
53
+ TrafficLight.go('Blue'); // throws - Blue doesn't exist at all
54
+ ```
55
+
56
+ A more involved machine, with main paths, forced paths, and per-state
57
+ styling, renders to:
58
+
59
+ <img src="https://raw.githubusercontent.com/StoneCypher/jssm/main/src/assets/doc%20light%20styled.png" alt="A styled four-state traffic light"/>
60
+
61
+ ```javascript
62
+ const TrafficLightWithOff = sm`
63
+ Red 'next' => Green 'next' => Yellow 'next' => Red;
64
+ [Red Yellow Green] ~> Off -> Red;
65
+
66
+ flow: left;
67
+
68
+ state Red : { background-color: pink; corners: rounded; };
69
+ state Yellow : { background-color: lightyellow; corners: rounded; };
70
+ state Green : { background-color: lightgreen; corners: rounded; };
71
+
72
+ state Off : {
73
+ background-color : steelblue;
74
+ text-color : white;
75
+ shape : octagon;
76
+ linestyle : dashed;
77
+ };
78
+ `;
79
+ ```
80
+
81
+ The same string is the runtime and the diagram. They cannot drift apart.
82
+
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)
87
+
88
+
89
+
90
+ <br/>
91
+
92
+ ## Install
93
+
94
+ ```
95
+ npm install jssm
96
+ ```
97
+
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.
100
+
101
+
102
+
103
+ <br/>
104
+
105
+ ## Visualization
106
+
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)).
109
+
110
+ ```typescript
111
+ import { sm } from 'jssm';
112
+ import { fsl_to_svg_string } from 'jssm/viz';
113
+
114
+ const svg = await fsl_to_svg_string('a -> b;');
115
+ ```
116
+
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.
120
+
121
+
122
+
123
+ <br/>
124
+
125
+ ## Command-line interface
126
+
127
+ `jssm` ships a CLI for rendering FSL machines to images and other formats.
128
+
129
+ ### Installation
130
+
131
+ ```sh
132
+ npm install -g jssm
133
+ ```
134
+
135
+ This installs three binaries: `fsl` (the dispatcher), `jssm` (alias for `fsl`), and `fsl-render` (the render plugin).
136
+
137
+ ### Render
138
+
139
+ Render a single machine to SVG (default):
140
+
141
+ ```sh
142
+ fsl render machine.fsl
143
+ # → machine.svg next to input
144
+ ```
145
+
146
+ Specify a format:
147
+
148
+ ```sh
149
+ fsl render machine.fsl --target=png --width=800
150
+ fsl render machine.fsl --target=dot --stdout > machine.dot
151
+ ```
152
+
153
+ Render multiple machines:
154
+
155
+ ```sh
156
+ fsl render *.fsl --target=svg --out-dir=./diagrams
157
+ ```
158
+
159
+ Pipe FSL via stdin:
160
+
161
+ ```sh
162
+ cat machine.fsl | fsl render --target=dot | dot -Tpng > out.png
163
+ ```
164
+
165
+ ### Plugin architecture
166
+
167
+ Every `fsl-<name>` executable on PATH is dispatched when you run `fsl <name>`. Third-party plugins follow the same contract as first-party `fsl-render`. See `notes/superpowers/specs/2026-05-12-fsl-cli-design.md` for the contract.
168
+
169
+ ### Library API
170
+
171
+ The same render functions are available programmatically:
172
+
173
+ ```js
174
+ import { render, renderSet } from 'jssm/cli';
175
+
176
+ const result = await render(fslText, { target: 'svg' });
177
+ if (result.kind === 'text') console.log(result.content);
178
+ ```
179
+
180
+
181
+
182
+ <br/>
183
+
184
+ ## Web Components
185
+
186
+ `jssm` ships Lit-based web components for use in plain HTML or as a base for framework wrappers.
187
+
188
+ CDN one-liner (with an import map for `@viz-js/viz`):
189
+
190
+ ```html
191
+ <script type="module" src="https://cdn.jsdelivr.net/npm/jssm/dist/cdn/viz.js"></script>
192
+ <fsl-viz fsl="Off -> On -> Off;"></fsl-viz>
193
+ ```
194
+
195
+ npm one-liner:
196
+
197
+ ```ts
198
+ import 'jssm/wc/viz/define';
199
+ // then use <fsl-viz fsl="..."> anywhere; <jssm-viz> is an accepted alias
200
+ ```
201
+
202
+ Full documentation: [src/doc_md/WebComponents.md](src/doc_md/WebComponents.md).
203
+
204
+
205
+
206
+ <br/>
207
+
208
+ ## 60-second tour
209
+
210
+ **Actions** let a machine advance without the caller knowing the next state:
211
+
212
+ ```javascript
213
+ const Light = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`;
214
+
215
+ Light.action('next'); // true
216
+ Light.state(); // 'Green'
217
+ Light.action('next'); // true
218
+ Light.state(); // 'Yellow'
219
+ ```
220
+
221
+ **Three arrow types** distinguish kinds of transition:
222
+
223
+ ```javascript
224
+ const Light = sm`
225
+ Red => Green => Yellow => Red; // => main path
226
+ [Red Yellow Green] ~> Off -> Red; // ~> forced, -> legal
227
+ `;
228
+ ```
229
+
230
+ `->` is a legal transition. `=>` is a legal transition that is also part of
231
+ the main path. `~>` is a transition that requires `force_transition` -
232
+ useful for emergency stops, resets, and other rarities.
233
+
234
+ **Hooks** observe and gate transitions:
235
+
236
+ ```javascript
237
+ const m = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`
238
+ .hook('Red', 'Green', () => console.log('GO')) // specific edge
239
+ .hook_entry('Red', () => console.log('STOP')) // entering a state
240
+ .hook_action('Yellow', 'Red', 'next', () => allowed()); // gate a specific action; return false to block
241
+ ```
242
+
243
+ Pre-hooks fire before the state changes and may return `false` to refuse the
244
+ transition. Post-hooks fire after. Four `*_everything` hooks
245
+ (`hook_pre_everything`, `hook_everything`, `hook_pre_post_everything`,
246
+ `hook_post_everything`) bracket the entire pipeline if you want a single
247
+ observation point.
248
+
249
+ **Refusals and errors are deliberately different.** An illegal transition
250
+ returns `false`. An unknown state throws. Branching code can rely on the
251
+ distinction.
252
+
253
+ **Overlapping state groups** let a state belong to several groups at once -
254
+ something a strict hierarchy can't express. A group is declared with `&`,
255
+ and the same `&name` then drives transitions, shared metadata, boundary
256
+ hooks, and runtime queries:
257
+
258
+ ```javascript
259
+ const req = sm`
260
+ &InProgress : [connecting sending receiving];
261
+ &Receiving : [receiving draining];
262
+
263
+ idle 'send' -> connecting 'open' -> sending 'reply' -> receiving;
264
+ receiving 'eof' -> draining 'done' -> idle;
265
+
266
+ &InProgress 'abort' -> idle; // a transition from every group member
267
+ on enter &Receiving do 'log_rx'; // boundary hook fires crossing in
268
+ `;
269
+
270
+ req.action('send');
271
+ req.isIn('InProgress'); // true - connecting is in &InProgress
272
+ req.groupsOf('receiving'); // Set { 'InProgress', 'Receiving' } - overlap
273
+ ```
274
+
275
+ `receiving` is in **both** groups simultaneously - it is in-progress *and*
276
+ receiving. When two groups disagree about the same action, a CSS-like
277
+ cascade decides: state-specific edges win, then the innermost (nearest)
278
+ group, then the later-declared one. Groups render as nested Graphviz
279
+ clusters, or as bracketed chips on the node label where memberships
280
+ genuinely overlap.
281
+
282
+ See the cookbook's overlapping-groups recipes for fuller worked examples.
283
+
284
+
285
+
286
+ <br/>
287
+
288
+ ## Why jssm
289
+
290
+ **The big win: most state-machine libraries make you write a gargantuan
291
+ JSON document, or call a builder API a few dozen times, to define a single
292
+ machine. jssm machines are short, readable, arrow-driven strings - so they
293
+ are easy to write, easy to read, easy to debug, and easy to share.**
294
+
295
+ That decision shows up everywhere downstream:
296
+
297
+ - **A DSL with features other state-machine libraries don't have.** Three
298
+ arrow types distinguish legal, main-path, and forced transitions. Array
299
+ notation collapses repeated edges - `[Red Yellow Green] ~> Off` replaces
300
+ three lines. Named actions, per-state styling, named edges, validators,
301
+ and live visualization all live in the same string the runtime parses.
302
+
303
+ - **Definition strings stay tiny, which makes machines easy to debug.**
304
+ 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)
305
+ is thirty. Small enough to read top-to-bottom, diff in code review,
306
+ paste into a bug report, or drop into the
307
+ [live editor](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html)
308
+ for a rendered diagram you can step through.
309
+
310
+ - **Fast.** Tens of millions of transitions per second on commodity
311
+ hardware. See [`src/buildjs/benchmark.cjs`](https://github.com/StoneCypher/jssm/blob/main/src/buildjs/benchmark.cjs)
312
+ or run `npm run benny` against your own machine.
313
+
314
+ - **More thoroughly tested than any other JavaScript state-machine
315
+ library.** 7,389 tests at 100.0% line coverage
316
+ ([report](https://coveralls.io/github/StoneCypher/jssm)), plus
317
+ fuzz testing via `fast-check`, with parser test data across ten natural
318
+ languages and Emoji.
319
+
320
+
321
+
322
+ <br/>
323
+
324
+ ## Documentation
325
+
326
+ - [What are state machines?](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/WhatAreStateMachines.md) - conceptual intro for newcomers
327
+ - [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
328
+ - [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
329
+ - [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
330
+ - [Language reference](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/LanguageReference.md) - DSL reference for people already comfortable with state machines
331
+ - [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)
332
+ - [Generated API reference](https://stonecypher.github.io/jssm/docs/) - full surface, generated from the TypeScript source
333
+
334
+
335
+
336
+ <br/>
337
+
338
+ ## API at a glance
339
+
340
+ | Method | Purpose |
341
+ |---|---|
342
+ | `` sm`...` `` | Build a machine from DSL |
343
+ | `.state()` | The current state |
344
+ | `.transition(state)` | Move to a state. Returns `false` if illegal, throws if unknown. |
345
+ | `.force_transition(state)` | Move to a state across a `~>` forced edge |
346
+ | `.action(name)` | Trigger a named action. The next state is derived from the current state. |
347
+ | `.valid_transition(state)` · `.valid_action(name)` | Test whether a transition or action is legal from the current state, without taking it |
348
+ | `.hook(from, to, fn)` | Run on a specific edge. Pre-hook; return `false` to block. |
349
+ | `.hook_entry(state, fn)` · `.hook_exit(state, fn)` | Run when entering or leaving a state |
350
+ | `.hook_action(from, to, action, fn)` | Run when a named action causes a specific edge |
351
+ | `.hook_pre_everything(fn)` · `.hook_everything(fn)` | Bracket the pre-hook pipeline |
352
+ | `.hook_pre_post_everything(fn)` · `.hook_post_everything(fn)` | Bracket the post-hook pipeline |
353
+
354
+ The full surface - including history, validators, factories, data, and the
355
+ graph-introspection methods - is in the [generated API
356
+ docs](https://stonecypher.github.io/jssm/docs/).
357
+
358
+
359
+
360
+ <br/>
361
+
362
+ ## Status
363
+
364
+ In production use since May 2017. Current series is 5.x and the DSL is
365
+ stable; the runtime API has been additive for several years. MIT licensed
366
+ end to end. Test data and parser cases are included for English, German,
367
+ French, Spanish, Hebrew, Russian, Ukrainian, Belarusian, Bengali,
368
+ Portuguese, and Emoji.
369
+
370
+
371
+
372
+ <br/>
373
+
374
+ ## Community
375
+
376
+ <a href="https://discord.gg/9P95USqnMK">![Discord community](https://discordapp.com/api/guilds/899910109642235924/widget.png?style=banner1)</a>
377
+
378
+ Questions, design discussions, bug reports, and "what would you do for X?"
379
+ are all welcome on Discord. Issues that need a paper trail go in the
380
+ [issue tracker](https://github.com/StoneCypher/fsl/issues).
381
+
382
+
383
+
384
+ <br/>
385
+
386
+ ## Comparisons
387
+
388
+ A direct, head-to-head comparison with the other actively-maintained JS state
389
+ machine libraries - XState, Stately.js, Finity, machina.js, and others - is
390
+ in progress and will live in
391
+ [FeatureComparison.md](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/FeatureComparison.md).
392
+
393
+ A list of related projects, without commentary, is at the bottom of that
394
+ file.
395
+
396
+
397
+
398
+ <br/>
399
+
400
+ ## Contributing
401
+
402
+ Issues and PRs are welcome. The cheapest useful contribution is a language
403
+ test case: open a PR with
404
+ [`english.json`](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/english.json)
405
+ translated into your language. Translating
406
+ [`traffic_light.fsl`](https://github.com/StoneCypher/fsl_traffic_light/blob/master/traffic_light.fsl)
407
+ into a separate repo and publishing it goes a step further.
408
+
409
+
410
+
411
+ <br/>
412
+
413
+ ## Acknowledgements
414
+
415
+ [Michael Morgan](https://github.com/msmorgan/) has debated significant
416
+ sections of the notation, invented several concepts and operators, helped
417
+ with the parser and system nomenclature, and published the first non-author
418
+ `FSL` machine. [Vat Raghavan](https://github.com/MachinShin) participated
419
+ extensively in language design and implemented several features. [Forest
420
+ Belton](https://github.com/forestbelton) provided guidance, bugfixes, and
421
+ parser commentary. [Jordan
422
+ Harbrand](https://github.com/ljharb) suggested two interesting features and
423
+ gave strong feedback on the initial tutorial draft.
424
+
425
+ Translation contributors:
426
+
427
+ - [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)
428
+ - [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)
429
+ - [Francisco Junior](https://github.com/fcojr) - [Portuguese](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/portuguese.json)
430
+ - [Jeff Katz](https://github.com/kraln) - [German](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/german.json)
431
+ - [Alex Cresswell](https://github.com/technophile77) - [Spanish](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/spanish.json)
432
+ - [Dvir Cohen](https://github.com/cohendvir) - [Hebrew](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/hebrew.json)
433
+ - [David de la Peña](https://github.com/daviddelapena) - [French](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/french.json)
434
+
435
+ If your contribution is missing here, please open an issue.
436
+
437
+
438
+
439
+ <br/>
440
+
441
+ ---
442
+
443
+ <details>
444
+ <summary>Stats, coverage, and badges</summary>
445
+
446
+ <br/>
447
+
448
+ ***7,389 tests***, run 82,431 times.
449
+
450
+ - 6,631 specs with 100.0% coverage
451
+ - 758 fuzz tests with 73.7% coverage
452
+ - 6,962 TypeScript lines - 1.1 tests per line, 11.8 generated tests per line
453
+
454
+ [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
455
+ [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
456
+ [![NPM downloads](https://img.shields.io/npm/dt/jssm.svg)](https://www.npmjs.com/package/jssm)
457
+ [![License](https://img.shields.io/npm/l/jssm.svg)](https://github.com/StoneCypher/jssm/blob/main/LICENSE.md)
458
+ [![Coveralls status](https://img.shields.io/coveralls/StoneCypher/jssm.svg)](https://coveralls.io/github/StoneCypher/jssm)
459
+ [![Open issues](https://img.shields.io/github/issues/StoneCypher/fsl.svg)](https://github.com/StoneCypher/fsl/issues)
460
+
461
+ </details>