{"id":15628370,"url":"https://github.com/stonecypher/jssm","last_synced_at":"2026-06-09T00:01:42.731Z","repository":{"id":20760182,"uuid":"90109662","full_name":"StoneCypher/jssm","owner":"StoneCypher","description":"Fast, easy Javascript finite state machines with visualizations; enjoy a one liner FSM instead of pages.  MIT; Typescripted; 100% test coverage.  Implements the FSL language.","archived":false,"fork":false,"pushed_at":"2026-05-27T19:09:27.000Z","size":42668,"stargazers_count":367,"open_issues_count":20,"forks_count":24,"subscribers_count":7,"default_branch":"main","last_synced_at":"2026-05-27T21:07:17.148Z","etag":null,"topics":["dsl","finite-state-machine","flowtype","fsm","fsm-library","graphviz","javascript","javascript-library","js","machine","mealy","mealy-machine","mit-license","moore","moore-machine","state","state-machine","tested","typed","typed-js"],"latest_commit_sha":null,"homepage":"http://fsl.tools/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StoneCypher.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.long.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2017-05-03T04:58:01.000Z","updated_at":"2026-05-27T19:06:51.000Z","dependencies_parsed_at":"2023-02-18T12:03:53.789Z","dependency_job_id":"d0a1d0a6-6b7f-4fa8-b307-eb260c647ea3","html_url":"https://github.com/StoneCypher/jssm","commit_stats":{"total_commits":937,"total_committers":11,"mean_commits":85.18181818181819,"dds":0.04482390608324438,"last_synced_commit":"31dfb9c1f14c6de3cf9ba7773273164c675567c4"},"previous_names":[],"tags_count":246,"template":false,"template_full_name":null,"purl":"pkg:github/StoneCypher/jssm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StoneCypher%2Fjssm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StoneCypher%2Fjssm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StoneCypher%2Fjssm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StoneCypher%2Fjssm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StoneCypher","download_url":"https://codeload.github.com/StoneCypher/jssm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StoneCypher%2Fjssm/sbom","scorecard":{"id":134610,"data":{"date":"2025-08-04","repo":{"name":"github.com/StoneCypher/jssm","commit":"03dda20b684b8614caa993ac796f95d6bfabd74e"},"scorecard":{"version":"v5.2.1-28-gc1d103a9","commit":"c1d103a9bb9f635ec7260bf9aa0699466fa4be0e"},"score":3.8,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/nodejs.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 1/17 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#branch-protection"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:71: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:169: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:172: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:245: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:50: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:53: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:86: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:89: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:128: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:130: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/nodejs.yml:139: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:151: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:154: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:194: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:203: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:212: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:264: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/StoneCypher/jssm/nodejs.yml/main?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:77","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:95","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:135","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:23","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:41","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:160","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:178","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:59","Info:   0 out of  21 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   8 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":10,"reason":"project is fuzzed","details":["Info: TypeScriptPropertyBasedTesting integration found: src/ts/tests/arbitraries.ts:2","Info: TypeScriptPropertyBasedTesting integration found: src/ts/tests/kitchen_sink_dragon.maximal.ts:5","Info: TypeScriptPropertyBasedTesting integration found: src/ts/tests/seq.stoch.ts:3","Info: TypeScriptPropertyBasedTesting integration found: src/ts/tests/arbitraries.ts:2","Info: TypeScriptPropertyBasedTesting integration found: src/ts/tests/kitchen_sink_dragon.maximal.ts:5","Info: TypeScriptPropertyBasedTesting integration found: src/ts/tests/seq.stoch.ts:3"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#fuzzing"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T06:09:37.089Z","repository_id":20760182,"created_at":"2025-08-16T06:09:37.090Z","updated_at":"2025-08-16T06:09:37.090Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33597808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dsl","finite-state-machine","flowtype","fsm","fsm-library","graphviz","javascript","javascript-library","js","machine","mealy","mealy-machine","mit-license","moore","moore-machine","state","state-machine","tested","typed","typed-js"],"created_at":"2024-10-03T10:22:15.420Z","updated_at":"2026-06-09T00:01:42.719Z","avatar_url":"https://github.com/StoneCypher.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n\nI8,        8        ,8I                                     88\n`8b       d8b       d8'                                     \"\"\n \"8,     ,8\"8,     ,8\"\n  Y8     8P Y8     8P  ,adPPYYba,  8b,dPPYba,  8b,dPPYba,   88  8b,dPPYba,    ,adPPYb,d8\n  `8b   d8' `8b   d8'  \"\"     `Y8  88P'   \"Y8  88P'   `\"8a  88  88P'   `\"8a  a8\"    `Y88\n   `8a a8'   `8a a8'   ,adPPPPP88  88          88       88  88  88       88  8b       88\n    `8a8'     `8a8'    88,    ,88  88          88       88  88  88       88  \"8a,   ,d88\n     `8'       `8'     `\"8bbdP\"Y8  88          88       88  88  88       88   `\"YbbdP\"Y8\n                                                                              aa,    ,88\n                                                                               \"Y8bbdP\"\n\nThis file is generated.  If you edit it, the edits ***will be lost***.\n----------------------\n\nPlease edit the file it's derived from, instead: `./src/md/readme_base.md`\n\n\n\n* Generated for version 5.143.0 at 6/8/2026, 3:15:18 PM\n\n--\u003e\n# jssm 5.143.0\n\n[**Try the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·\n[Documentation](https://stonecypher.github.io/jssm/docs/) ·\n[Discord](https://discord.gg/9P95USqnMK) ·\n[Issues](https://github.com/StoneCypher/fsl/issues)\n\n**Easy.**  Tiny.  Fast.  Finite state machines as one-liner strings, for\nTypeScript and JavaScript.  Renders to PNG, SVG, and JPEG.  Runs in Node,\nbrowsers, and Deno.  MIT licensed.\n\n```javascript\nimport { sm } from 'jssm';\n\nconst TrafficLight = sm`Red -\u003e Green -\u003e Yellow -\u003e Red;`;\n```\n\nThat's it.  Using it is equally easy:\n\n```javascript\nTrafficLight.state();      // 'Red'\nTrafficLight.go('Green');  // true\nTrafficLight.state();      // 'Green'\n```\n\nThe point of a state machine is to refuse to do things that aren't correct:\n\n```javascript\nTrafficLight.go('Red');    // false  - Green doesn't go to Red, only Yellow\nTrafficLight.go('Blue');   // throws - Blue doesn't exist at all\n```\n\nA more involved machine, with main paths, forced paths, and per-state\nstyling, renders to:\n\n\u003cimg src=\"https://raw.githubusercontent.com/StoneCypher/jssm/main/src/assets/doc%20light%20styled.png\" alt=\"A styled four-state traffic light\"/\u003e\n\n```javascript\nconst TrafficLightWithOff = sm`\n  Red 'next' =\u003e Green 'next' =\u003e Yellow 'next' =\u003e Red;\n  [Red Yellow Green] ~\u003e Off -\u003e Red;\n\n  flow: left;\n\n  state Red    : { background-color: pink;        corners: rounded; };\n  state Yellow : { background-color: lightyellow; corners: rounded; };\n  state Green  : { background-color: lightgreen;  corners: rounded; };\n\n  state Off    : {\n    background-color : steelblue;\n    text-color       : white;\n    shape            : octagon;\n    linestyle        : dashed;\n  };\n`;\n```\n\nThe same string is the runtime and the diagram.  They cannot drift apart.\n\n[**Try it in the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·\n[Documentation](https://stonecypher.github.io/jssm/docs/) ·\n[Discord](https://discord.gg/9P95USqnMK) ·\n[Issues](https://github.com/StoneCypher/fsl/issues)\n\n\n\n\u003cbr/\u003e\n\n## Install\n\n```\nnpm install jssm\n```\n\nThe package ships pure ES6, a CommonJS ES5 bundle, an IIFE for browsers,\nand TypeScript typings.  A Deno build is included.  Node 10 or newer.\n\n\n\n\u003cbr/\u003e\n\n## Visualization\n\n`jssm` ships with a visualization subpath that renders state machines to\nSVG using Graphviz (via [`@viz-js/viz`](https://www.npmjs.com/package/@viz-js/viz)).\n\n```typescript\nimport { sm }                  from 'jssm';\nimport { fsl_to_svg_string }   from 'jssm/viz';\n\nconst svg = await fsl_to_svg_string('a -\u003e b;');\n```\n\nThe viz subpath is opt-in - importing only from `jssm` does not pull in\n`@viz-js/viz`. See the Visualization doc page for browser, ESM, and IIFE\nusage patterns.\n\n\n\n\u003cbr/\u003e\n\n## Command-line interface\n\n`jssm` ships a CLI for rendering FSL machines to images and other formats.\n\n### Installation\n\n```sh\nnpm install -g jssm\n```\n\nThis installs three binaries: `fsl` (the dispatcher), `jssm` (alias for `fsl`), and `fsl-render` (the render plugin).\n\n### Render\n\nRender a single machine to SVG (default):\n\n```sh\nfsl render machine.fsl\n# → machine.svg next to input\n```\n\nSpecify a format:\n\n```sh\nfsl render machine.fsl --target=png --width=800\nfsl render machine.fsl --target=dot --stdout \u003e machine.dot\n```\n\nRender multiple machines:\n\n```sh\nfsl render *.fsl --target=svg --out-dir=./diagrams\n```\n\nPipe FSL via stdin:\n\n```sh\ncat machine.fsl | fsl render --target=dot | dot -Tpng \u003e out.png\n```\n\n### Plugin architecture\n\nEvery `fsl-\u003cname\u003e` executable on PATH is dispatched when you run `fsl \u003cname\u003e`. 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.\n\n### Library API\n\nThe same render functions are available programmatically:\n\n```js\nimport { render, renderSet } from 'jssm/cli';\n\nconst result = await render(fslText, { target: 'svg' });\nif (result.kind === 'text') console.log(result.content);\n```\n\n\n\n\u003cbr/\u003e\n\n## Web Components\n\n`jssm` ships Lit-based web components for use in plain HTML or as a base for framework wrappers.\n\nCDN one-liner (with an import map for `@viz-js/viz`):\n\n```html\n\u003cscript type=\"module\" src=\"https://cdn.jsdelivr.net/npm/jssm/dist/cdn/viz.js\"\u003e\u003c/script\u003e\n\u003cfsl-viz fsl=\"Off -\u003e On -\u003e Off;\"\u003e\u003c/fsl-viz\u003e\n```\n\nnpm one-liner:\n\n```ts\nimport 'jssm/wc/viz/define';\n// then use \u003cfsl-viz fsl=\"...\"\u003e anywhere; \u003cjssm-viz\u003e is an accepted alias\n```\n\nFull documentation: [src/doc_md/WebComponents.md](src/doc_md/WebComponents.md).\n\n\n\n\u003cbr/\u003e\n\n## 60-second tour\n\n**Actions** let a machine advance without the caller knowing the next state:\n\n```javascript\nconst Light = sm`Red 'next' -\u003e Green 'next' -\u003e Yellow 'next' -\u003e Red;`;\n\nLight.action('next');  // true\nLight.state();         // 'Green'\nLight.action('next');  // true\nLight.state();         // 'Yellow'\n```\n\n**Three arrow types** distinguish kinds of transition:\n\n```javascript\nconst Light = sm`\n  Red =\u003e Green =\u003e Yellow =\u003e Red;     // =\u003e main path\n  [Red Yellow Green] ~\u003e Off -\u003e Red;  // ~\u003e forced, -\u003e legal\n`;\n```\n\n`-\u003e` is a legal transition.  `=\u003e` is a legal transition that is also part of\nthe main path.  `~\u003e` is a transition that requires `force_transition` -\nuseful for emergency stops, resets, and other rarities.\n\n**Hooks** observe and gate transitions:\n\n```javascript\nconst m = sm`Red 'next' -\u003e Green 'next' -\u003e Yellow 'next' -\u003e Red;`\n  .hook('Red', 'Green', () =\u003e console.log('GO'))            // specific edge\n  .hook_entry('Red', () =\u003e console.log('STOP'))             // entering a state\n  .hook_action('Yellow', 'Red', 'next', () =\u003e allowed());   // gate a specific action; return false to block\n```\n\nPre-hooks fire before the state changes and may return `false` to refuse the\ntransition.  Post-hooks fire after.  Four `*_everything` hooks\n(`hook_pre_everything`, `hook_everything`, `hook_pre_post_everything`,\n`hook_post_everything`) bracket the entire pipeline if you want a single\nobservation point.\n\n**Refusals and errors are deliberately different.**  An illegal transition\nreturns `false`.  An unknown state throws.  Branching code can rely on the\ndistinction.\n\n\n\n\u003cbr/\u003e\n\n## Why jssm\n\n**The big win: most state-machine libraries make you write a gargantuan\nJSON document, or call a builder API a few dozen times, to define a single\nmachine.  jssm machines are short, readable, arrow-driven strings - so they\nare easy to write, easy to read, easy to debug, and easy to share.**\n\nThat decision shows up everywhere downstream:\n\n- **A DSL with features other state-machine libraries don't have.**  Three\n  arrow types distinguish legal, main-path, and forced transitions.  Array\n  notation collapses repeated edges - `[Red Yellow Green] ~\u003e Off` replaces\n  three lines.  Named actions, per-state styling, named edges, validators,\n  and live visualization all live in the same string the runtime parses.\n\n- **Definition strings stay tiny, which makes machines easy to debug.**\n  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)\n  is thirty.  Small enough to read top-to-bottom, diff in code review,\n  paste into a bug report, or drop into the\n  [live editor](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html)\n  for a rendered diagram you can step through.\n\n- **Fast.**  Tens of millions of transitions per second on commodity\n  hardware.  See [`src/buildjs/benchmark.cjs`](https://github.com/StoneCypher/jssm/blob/main/src/buildjs/benchmark.cjs)\n  or run `npm run benny` against your own machine.\n\n- **More thoroughly tested than any other JavaScript state-machine\n  library.**  6,726 tests at 100.0% line coverage\n  ([report](https://coveralls.io/github/StoneCypher/jssm)), plus\n  fuzz testing via `fast-check`, with parser test data across ten natural\n  languages and Emoji.\n\n\n\n\u003cbr/\u003e\n\n## Documentation\n\n- [What are state machines?](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/WhatAreStateMachines.md) - conceptual intro for newcomers\n- [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\n- [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\n- [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\n- [Language reference](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/LanguageReference.md) - DSL reference for people already comfortable with state machines\n- [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)\n- [Generated API reference](https://stonecypher.github.io/jssm/docs/) - full surface, generated from the TypeScript source\n\n\n\n\u003cbr/\u003e\n\n## API at a glance\n\n| Method | Purpose |\n|---|---|\n| `` sm`...` `` | Build a machine from DSL |\n| `.state()` | The current state |\n| `.transition(state)` | Move to a state. Returns `false` if illegal, throws if unknown. |\n| `.force_transition(state)` | Move to a state across a `~\u003e` forced edge |\n| `.action(name)` | Trigger a named action. The next state is derived from the current state. |\n| `.valid_transition(state)` · `.valid_action(name)` | Test whether a transition or action is legal from the current state, without taking it |\n| `.hook(from, to, fn)` | Run on a specific edge. Pre-hook; return `false` to block. |\n| `.hook_entry(state, fn)` · `.hook_exit(state, fn)` | Run when entering or leaving a state |\n| `.hook_action(from, to, action, fn)` | Run when a named action causes a specific edge |\n| `.hook_pre_everything(fn)` · `.hook_everything(fn)` | Bracket the pre-hook pipeline |\n| `.hook_pre_post_everything(fn)` · `.hook_post_everything(fn)` | Bracket the post-hook pipeline |\n\nThe full surface - including history, validators, factories, data, and the\ngraph-introspection methods - is in the [generated API\ndocs](https://stonecypher.github.io/jssm/docs/).\n\n\n\n\u003cbr/\u003e\n\n## Status\n\nIn production use since May 2017.  Current series is 5.x and the DSL is\nstable; the runtime API has been additive for several years.  MIT licensed\nend to end.  Test data and parser cases are included for English, German,\nFrench, Spanish, Hebrew, Russian, Ukrainian, Belarusian, Bengali,\nPortuguese, and Emoji.\n\n\n\n\u003cbr/\u003e\n\n## Community\n\n\u003ca href=\"https://discord.gg/9P95USqnMK\"\u003e![Discord community](https://discordapp.com/api/guilds/899910109642235924/widget.png?style=banner1)\u003c/a\u003e\n\nQuestions, design discussions, bug reports, and \"what would you do for X?\"\nare all welcome on Discord.  Issues that need a paper trail go in the\n[issue tracker](https://github.com/StoneCypher/fsl/issues).\n\n\n\n\u003cbr/\u003e\n\n## Comparisons\n\nA direct, head-to-head comparison with the other actively-maintained JS state\nmachine libraries - XState, Stately.js, Finity, machina.js, and others - is\nin progress and will live in\n[FeatureComparison.md](https://github.com/StoneCypher/jssm/blob/main/src/doc_md/FeatureComparison.md).\n\nA list of related projects, without commentary, is at the bottom of that\nfile.\n\n\n\n\u003cbr/\u003e\n\n## Contributing\n\nIssues and PRs are welcome.  The cheapest useful contribution is a language\ntest case: open a PR with\n[`english.json`](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/english.json)\ntranslated into your language.  Translating\n[`traffic_light.fsl`](https://github.com/StoneCypher/fsl_traffic_light/blob/master/traffic_light.fsl)\ninto a separate repo and publishing it goes a step further.\n\n\n\n\u003cbr/\u003e\n\n## Acknowledgements\n\n[Michael Morgan](https://github.com/msmorgan/) has debated significant\nsections of the notation, invented several concepts and operators, helped\nwith the parser and system nomenclature, and published the first non-author\n`FSL` machine.  [Vat Raghavan](https://github.com/MachinShin) participated\nextensively in language design and implemented several features.  [Forest\nBelton](https://github.com/forestbelton) provided guidance, bugfixes, and\nparser commentary.  [Jordan\nHarbrand](https://github.com/ljharb) suggested two interesting features and\ngave strong feedback on the initial tutorial draft.\n\nTranslation contributors:\n\n- [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)\n- [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)\n- [Francisco Junior](https://github.com/fcojr) - [Portuguese](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/portuguese.json)\n- [Jeff Katz](https://github.com/kraln) - [German](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/german.json)\n- [Alex Cresswell](https://github.com/technophile77) - [Spanish](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/spanish.json)\n- [Dvir Cohen](https://github.com/cohendvir) - [Hebrew](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/hebrew.json)\n- [David de la Peña](https://github.com/daviddelapena) - [French](https://github.com/StoneCypher/jssm/blob/main/src/ts/tests/language_data/french.json)\n\nIf your contribution is missing here, please open an issue.\n\n\n\n\u003cbr/\u003e\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003eStats, coverage, and badges\u003c/summary\u003e\n\n\u003cbr/\u003e\n\n***6,726 tests***, run 58,800 times.\n\n- 6,200 specs with 100.0% coverage\n- 526 fuzz tests with 3.3% coverage\n- 5,776 TypeScript lines - 1.2 tests per line, 10.2 generated tests per line\n\n[![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)\n[![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)\n[![NPM downloads](https://img.shields.io/npm/dt/jssm.svg)](https://www.npmjs.com/package/jssm)\n[![License](https://img.shields.io/npm/l/jssm.svg)](https://github.com/StoneCypher/jssm/blob/main/LICENSE.md)\n[![Coveralls status](https://img.shields.io/coveralls/StoneCypher/jssm.svg)](https://coveralls.io/github/StoneCypher/jssm)\n[![Open issues](https://img.shields.io/github/issues/StoneCypher/fsl.svg)](https://github.com/StoneCypher/fsl/issues)\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstonecypher%2Fjssm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstonecypher%2Fjssm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstonecypher%2Fjssm/lists"}