{"id":13724994,"url":"https://github.com/jdubray/sam-fsm","last_synced_at":"2026-01-17T09:41:52.445Z","repository":{"id":65492538,"uuid":"346858537","full_name":"jdubray/sam-fsm","owner":"jdubray","description":"npm sam-fsm","archived":false,"fork":false,"pushed_at":"2021-04-11T04:16:39.000Z","size":947,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-26T09:58:28.026Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jdubray.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-11T22:36:05.000Z","updated_at":"2024-02-17T22:35:38.000Z","dependencies_parsed_at":"2023-01-26T01:45:56.523Z","dependency_job_id":null,"html_url":"https://github.com/jdubray/sam-fsm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jdubray/sam-fsm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdubray%2Fsam-fsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdubray%2Fsam-fsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdubray%2Fsam-fsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdubray%2Fsam-fsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jdubray","download_url":"https://codeload.github.com/jdubray/sam-fsm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdubray%2Fsam-fsm/sbom","scorecard":{"id":512904,"data":{"date":"2025-08-11","repo":{"name":"github.com/jdubray/sam-fsm","commit":"2aca9f00669af72b79933d0fa6872671653175cf"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"24 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-2j2x-2gpw-g8fm","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T01:03:10.032Z","repository_id":65492538,"created_at":"2025-08-20T01:03:10.032Z","updated_at":"2025-08-20T01:03:10.032Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28505565,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T06:57:29.758Z","status":"ssl_error","status_checked_at":"2026-01-17T06:56:03.931Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-08-03T01:02:09.139Z","updated_at":"2026-01-17T09:41:52.406Z","avatar_url":"https://github.com/jdubray.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# an FSM library \n\n`sam-fsm` is a companion libray to the [sam-pattern](https://www.npmjs.com/package/sam-pattern). It provides a simple finite state machine implementation on top of the [SAM Pattern](http://sam.js.org) (which is itself a more robust state machine structure based on TLA+). `sam-fsm` supports deterministic and non deterministic state machines. Several FSMs can run concurrently in the same SAM instance, making it really easy do build sophisticated applications with complex state management needs. The two libraries combined enable you to use control states when they make sense and not needing any when the control states would be irrelevant to the application state mutations. It is just too cumbersome to specify a control state for all the actions, or a global state machine for your entire application state. `sam-fsm` + `sam-pattern` solves that problem.\n\n## Table of Contents\n- [Installation](#installation)        \n  - [Node.js](#nodejs)        \n  - [Browsers](#browsers)        \n  - [Getting started](#getting-started)           \n- [Library](#library)        \n  - [Constructor](#constructor)        \n    - [Parameters](#parameters)   \n  - [Integration with SAM](#integration-with-sam)\n    - [Next-Action predicates](#next-action-predicates)\n    - [Transition guards](#transition-guards)\n    - [Composite State](#composite-state)\n    - [Exception Handling](#exception-handling)  \n  - [Alternative specification formats](#alternative-specification-format)  \n  - [State Diagram](#state-diagram)\n- [Code samples](#code-samples)        \n- [Support](#support)   \n- [Change Log](#change-log)    \n- [Copyright and license](#copyright-and-license)\n\n## Installation\n\n### Node.js\nThe library is available on [npm](https://www.npmjs.com/package/sam-fsm). To install it, type:\n\n```sh\n$ npm install --save sam-fsm\n```\n\n```javascript\nconst { fsm } = require('sam-fsm')\n\nconst simpleFsm = fsm({\n  pc0: 'START_STATE',\n  actions: {\n    DO_SOMETHING: ['END_STATE']\n  },\n  states: {\n    START_STATE: {\n      transitions: ['DO_SOMETHING']\n    }\n  },\n  deterministic: true,\n  enforceAllowedActions: true\n})\n\n```\n\n### Browsers\nYou can also use it within the browser; install via npm and use the ./dist/fsm.js file. For example:\n\n```html\n\u003cscript src=\"./node_modules/sam-fsm/dist/fsm.js\"\u003e\u003c/script\u003e\n\n// or\n\n\u003cscript src=\"https://unpkg.com/sam-fsm\"\u003e\u003c/script\u003e\n```\n\n```javascript\nconst { fsm } = tpFSM\n\nconst simpleFsm = fsm({\n  pc0: 'START_STATE',\n  actions: {\n    DO_SOMETHING: ['END_STATE']\n  },\n  states: {\n    START_STATE: {\n      transitions: ['DO_SOMETHING']\n    },\n    END_STATE: {\n      transitions: []\n    }\n  },\n  deterministic: true,\n  enforceAllowedActions: true\n})\n\n```\n\n### Getting started\n\nThe FSM descriptor specifies:\n- actions and their possible resuling states (more than one if not deterministic)\n- states and their respective (allowed) actions to transition from\n- the initial value of the state (`pc0`)\n- whether the state machine is deterministic or not\n- whether allowed transition need to be enforced\n- an optional SAM component name, when you want the FSM to be deployed in that component's local state\n\nDeterministic FSMs will mutate the `pc` variable for you. Non deterministic FSMs expect that you will provide one or more acceptors that mutate the `pc` variable with the current control state value.\n\nPlease note that `pc` is used commonly in TLA+ as the control state variable name and is itself in reference to [John Von Neumann's](https://en.wikipedia.org/wiki/Program_counter) `program counter` (also called `instruction pointer` in x86 architectures).\n\nThe `sam-fsm` library enables the interleaving between one or more FSM and a regular SAM state machine making it easier to use FSM semantics when they make sense without compromising the robust structure of a TLA+ based state machine. Of course, `sam-fsm` also supports FSM-only state machines, simply using SAM as the underlying implementation. \n\nThe descriptor support both action and event semantics since actions are full-fledged SAM actions:\n```javascript\n  actions: {\n    CALL_API: ['called'],\n    ON_SUCCESS: ['succeeded'],\n    ON_ERROR: ['failed']\n  },\n  states: {\n    called: {\n      transitions: ['ON_SUCCESS', 'ON_ERROR']\n    },\n    succeeded: {\n      transitions: ['...']\n    },\n    failed: {\n      transitions: ['CALL_API']\n    }\n  }\n```\n\nLet's take a look at the example of a clock:\n\n```javascript\nconst {\n  SAM, first, last, api, createInstance, doNotRender, utils: { E }, events\n} = require('sam-pattern')\n\nconst { fsm } = require('sam-fsm')\n\n// Instantiate clock fsm\nconst clock = fsm({\n  pc0: 'TOCKED',\n  actions: {\n    TICK: ['TICKED'],\n    TOCK: ['TOCKED']\n  },\n  states: {\n    TICKED: {\n      transitions: ['TOCK']\n    },\n    TOCKED: {\n      transitions: ['TICK']\n    }\n  },\n  deterministic: true,\n  enforceAllowedTransitions: true\n})\n\n// Create a new SAM instance\nconst FSMTest = createInstance({ instanceName: 'FSMTest' })\n\n// add fsm to SAM instance\nconst intents = FSMTest({\n  initialState: clock.initialState({}),\n  component: {\n    actions: [\n      // Labeled SAM actions\n      ['TICK', () =\u003e ({ tick: true, tock: false })],\n      [ 'TOCK', () =\u003e ({ tock: true, tick: false })]\n    ],\n    acceptors: clock.acceptors,\n    reactors: clock.stateMachine\n  },\n  render: state =\u003e {\n    console.log(state.pc)\n  }\n}).intents\n\nconst [tick, tock] = intents\n\ntick()\ntock()\n```\n\nHere is the new [Rocket Launcher example](https://codepen.io/sam-pattern/pen/XWNGNBy)\n\n## Library\n\n### Constructor\n- `fsm`                   : Instantiates a new fsm \n\n#### Parameters\n- `pc0`                   : initial state \n- `actions`               : an object where the keys are the action labels and the values the array of possible resulting states (one state only for deterministic state machines)\n- `states`                : an object where the keys are the state labels and the values are allowed transitions from the corresponding state (as an array of action lables). States may optionally include `next-actions` that can be added to the next-action-predicate (nap) of a SAM instance\n- `transitions`           : an alternative way to define the FSM specification (please see section on [Transitions](##alternative-specification-format))\n- `composite`             : expresses that the current state machine is a composite state of another state machine\n- `deterministic`         : a boolean value, `true` if the FSM is deterministic\n- `enforceAllowedActions` : a boolean value, when `true` the acceptors will validate that a valid action is used to transition away from a state\n- `pc`                    : a string that is used to rename the `pc` variable, `{ pc: 'status' }` will use `model.status` as the control state variable.\n- `componentName`         : an optional SAM component name that will deploy the FSM in the SAM component local state tree\n- `blockUnexpectedActions`: when true, uses the SAM allowedActions implementation to block unexpected actions. When several FSMs are running the collection of allowed actions is the sum of all expected actions.\n\n### Integration with SAM\n\nStart by creating a SAM instance as usual:\n```\nconst SAMFSM = createInstance({ instanceName: 'SAMFSM' })\n```\n\n`sam-fsm` provides five integration points: `initialState`, `addAction`, `event`, `acceptors` and the `stateMachine` reactor.\n\nAssuming your myFsm as the `sam-fsm` instance name:\n\n```javascript\nconst intents = SAMFSM({\n      initialState: myFsm.initialState(yourRegularSAMInitialState), // adds FSM specific hooks\n      component: {\n        actions: [\n          action1, // a sam action, unrelated to the sam-fsm instance\n          action2, // another regular sam action\n          ['ACTION3', action3], // a labeled SAM action\n          ['ACTION4', action4, mySecondFSM], // a labeled SAM action associated to a specific fsm\n          myFsm.addAction(action4, 'ACTION_5') // another way to create a labeled SAM action\n          myFsm.event('ON_SUCCESS') // creates a SAM action that publishes an event\n        ],\n        acceptors: [\n          ...myFsm.acceptors, // the control state acceptors\n          acceptor1, // \n          acceptor2\n        ],\n        reactors: [\n          ...myFsm.stateMachine, // the sam-fsm \n          reactor1,  // a sam reactor, unrelated to the sam-fsm instance\n          reactor2   // another regular reactor\n        ]\n      },\n      render: state =\u003e { console.log(state) }\n  })\n```\n\nFSM instance methods:\n\n`initialState`    : wraps the SAM instance's intial state with the FSM internal variables (such as `pc`)\n\n`addAction`       : wraps regular SAM actions\n\n`event`           : instantiates a SAM action that publishes an event (the action presents the event label value as a proposal)\n\n`acceptors`       : returns the fsm acceptors (as an array)\n\n`stateMachine`    : returns the fsm reactor (as an array of 1 element)\n\n`naps`            : returns the fsm next-action-predicates as a single, flat, array\n\nFrom that point on, everything else is similar to a regular SAM instance, you can add additional acceptors, reactors (before or after the fsm ones) and naps as well.\n\n`sam-fsm` supports SAM components and their local state. Several FSMs can be deployed in the same SAM instance, as long as you use a different `pc` variable but they can share actions!\n\n#### Next-Action predicates\n\nNAPs can be defined inline, in the state machine specification:\n\n```javascript\n...\nstates: {\n    ticking: {\n      transitions: ['TICK','LAUNCH','ABORT'],\n      naps: {\n        {\n          condition: ({ counter }) =\u003e counter \u003e 0,\n          nextAction: (state) =\u003e setTimeout(_tick, 1000)\n        },{\n          condition: ({ counter }) =\u003e counter === 0,\n          nextAction: (state) =\u003e setTimeout(_launch, 100)\n        }\n      }\n    },\n...\n}\n```\n\nA NAP includes a condition and the next action as a function of the application state. The condition would be evaluated only when the control state is equal to its parent state.\n\nThe predicate triggers only when the state machine is in the given state (e.g. `ticking`)\n\nIntents need to be wired manually due to the interdependency it creates between the fsm and the SAM instance.\n\n#### Transition guards\n\nThe library supports transition guards which can be added to specific state transitions:\n\n```javascript\nconst clock = fsm({\n        pc: 'status',\n        pc0: 'TOCKED',\n        actions: {\n          TICK_GUARDED: ['TICKED'],\n          TOCK_GUARDED: ['TOCKED']\n        },\n        states: {\n          TICKED: {\n            transitions: ['TOCK_GUARDED'],\n            guards: [{\n              action: 'TOCK_GUARDED',\n              // once the counter reaches 5, TICK_GUARDED and TOCK_GUARDED\n              // are no longer allowed\n              condition: ({ counter }) =\u003e counter \u003c 5\n            }]\n          },\n          TOCKED: {\n            transitions: ['TICK_GUARDED'],\n            guards: [{\n              // The action name can be ommitted, in which case the first element of the transition\n              // array will be used\n              // action: 'TICK_GUARDED',\n              condition: ({ counter }) =\u003e counter \u003c 5\n            }]\n          }\n        },\n        deterministic: true,\n        lax:false,\n        enforceAllowedTransitions: true,\n        blockUnexpectedActions: true\n      })\n```\n\nAs their name suggests the transition will only be possible while the condition is true. In the case above, the transition will be disallowed once the counter value is greater or equal to 5. The last transition allowed would increment the counter value to 5 and from that point on, the clock won't be able to `tick` or `tock`.\n\n#### Composite State\n\nSince a SAM instance can run multiple state machines, the `sam-fsm` library also supports the concept of `composite state` when a state machine can only accept actions when another state machine is in a particular state. The composite state fsm can also be specified to execute automatic actions on the parent fsm on specific states (success, failure,...)\n\nThe composite descriptor specifies the parent fsm composite state label (`COMPOSITE_STATE` in the snippet below). That state value acts as a global guard of the composite fsm. The composite fsm will start in the pc0 state each time the parent transitions to the composite state.\n\nConversely, the composite fsm can specify automatic actions on the parent fsm as composite transitions. On a given state (for instance `END`, the composite fsm will automatically execute the specified parent action and pass the proposal parameters (in addition to its own state)).\n\nWhen the parent and/or composite fsm use their local states, the same logic applies.\n\n```javascript\nconst parentFSM = fsm({ \n  pc: 'parentStatus', \n  states: {\n    // Specify the parent state machine as usual including\n    // the composite state\n    COMPOSITE_STATE: { ... }\n  },\n  ... \n})\n\nconst compositeStateFSM = fsm({ \n        ...\n        // a composite fsm is specified as a regular fsm\n        // add a composite property that includes a\n        // composite descriptor\n        composite: {\n          // a reference to the parentFSM\n          of: parentFSM,\n          // specify the parent composite state label, \n          // where it can be found (pc) and an optional\n          // component name\n          onState: { pc: 'parentStatus', label: 'COMPOSITE_STATE', component: 'optionalParentComponentName' },\n          transitions: [\n            // on reaching one ore more end states\n            // trigger an intent and construct the proposal\n            // from this list of properties from the model\n            { onState: 'END', action: intentToTrigger, proposal: ['counter'] }\n          ]\n        }\n        ...\n})\n\n// the SAM instance needs to be specified with labeled actions that also \n// include a reference to their respective fsm (parent, child1, child2, ...)\nconst intents = SAMFSM({\n      ...\n      component: {\n        actions: [\n          ['ACTION1', action1, parentFSM],\n          ['ACTION2', action2, parentFSM],\n          ['ACTION3', action3, compositeStateFSM],\n        ],\n      ...\n})\n```\n\n#### Exception Handling\n\nExceptions are reported as SAM exceptions which can be accessed via these four SAM methods:\n- `hasError`  \n- `error`\n- `errorMessage`\n- `clearError`\n\nFor instance:\n\n```javascript\nrender: (state) =\u003e {\n  if (state.hasError()) {\n    console.log(state.errorMessage())\n    state.clearError()\n  } \n}\n```\n\n### Alternative specification format\n\nSome people prefer defining their FSM as a series of transitions. `sam-fsm` supports the following format:\n\n```javascript\nconst transitions = [{\n    from: 'ready', to: 'started', on: 'START'\n  },{\n    from: 'started', to: 'ticking', on: 'TICK'\n  },{\n    from: 'ticking', to: 'ticking', on: 'TICK'\n  },{\n    from: 'ticking', to: 'aborted', on: 'ABORT'\n  },{\n    from: 'ticking', to: 'launched', on: 'LAUNCH'\n  },{\n    from: 'aborted', to: 'ready', on: 'RESET'\n  },{\n    from: 'launched', to: 'ready', on: 'RESET'\n  }]\n\nconst rocketLauncherStyle1 = fsm({ \n  pc0: 'ready', \n  transitions, \n  deterministic: true \n})\n\n// or state - action - state\nconst stateActionState = {\n        ready: {\n          START: \"started\"\n        },\n        started: {\n          TICK: \"ticking\"\n        },\n        ticking: {\n          TICK: \"ticking\",\n          ABORT: \"aborted\",\n          LAUNCH: \"launched\"\n        },\n        aborted: {\n          RESET: \"ready\"\n        },\n        launched: {\n          RESET: \"ready\"\n        }\n      }\n\nconst rocketLauncherStyle2 = fsm({ \n  pc0: 'ready', \n  transitions: stateActionState, \n  deterministic: true \n})\n\n```\nYou can also use the `fsm.actionsAndStatesFor` class method to translate transitions into states and actions (the transition style is detected automatically):\n\n```javascript\nconst { pc0, states, actions } = fsm.actionsAndStatesFor(transitions)\n\n// and then as usual\nconst rocketLauncherFSM = fsm({ pc0, states, actions })\n```\n\nThe function uses the first `from` state as the start state (`pc0`) and adds `deterministic` and `enforceAllowedTransitions` properties. You can, of course, add reactors as necessary. These styles do not support NAPs.\n\n### State Diagram\n\nThe fsm comes with a [graphViz formated](https://edotor.net/) state diagram. Here is [another editor](http://magjac.com/graphviz-visual-editor/)\n\n```javascript\nconst clock = fsm({ ... })\n\nconsole.log(clock.stateDiagram)\n\n// should yield\ndigraph fsm_diagram {\nrankdir=LR;\nsize=\"8,5\"\nREADY [shape = circle margin=0 fixedsize=true width=0.33 fontcolor=black style=filled color=black label=\"\\n\\n\\nREADY\"]\nEND [shape = doublecircle margin=0 style=filled fontcolor=white color=black]\nnode [shape = Mrecord];\nREADY -\u003e TICKED [label = \"START\"];\nTICKED -\u003e TOCKED [label = \"TOCK\"];\nTOCKED -\u003e TICKED [label = \"TICK\"];\nTOCKED -\u003e END [label = \"STOP\\n counter \u003e 5\"];\n}\n```\n\nThe diagram generator is capable of displaying conditions (see unit tests for an example):\n\nThe fsm is also capable of creating a representation of the runtime state diagram:\n```\nclock.runtimeStateDiagram()\n```\n\u003cimg src=\"graphviz.png\"\n     style=\"width: 300px\" /\u003e\n\n## Code samples\n\n[Rocket Launcher](https://codepen.io/sam-pattern/pen/XWNGNBy)\n\n[sam-fsm without the sam-pattern library](https://codepen.io/sam-pattern/pen/abBejoV)\n\nPlease see [the unit tests](https://github.com/jdubray/sam-fsm/tree/master/test) for additional code samples\n\n## Support\n\nPlease post your questions/comments on the [SAM-pattern forum](https://gitter.im/jdubray/sam)\n\n## Change Log\n- 0.9.24  RC2 `sam-fsm' is ready!\n- 0.9.23  Adds indexed action to the runtime state diagrams\n- 0.9.20  Adds support for runtime state diagrams\n- 0.9.19  Adds support for composite state machine\n- 0.9.17  Adds GraphViz state diagram\n- 0.9.15  Adds tests for labeled SAM actions\n- 0.9.12  Minifies the lib  (3.4kB)\n- 0.9.11  Fixes minor defect, adds sample without `sam-pattern` library\n- 0.9.10  RC1 `sam-fsm` is feature complete!\n- 0.9.9   Adds support for SAM allowedActions mechanism (blocking unexpected actions)\n          *** Breaking change *** the `send` instance method has been renamed `event`  \n- 0.9.8   Adds support for transitions in the constructor (in addition to actions/states)\n- 0.9.7   Adds support for localstate, new unit tests and cleans up doc and code sample\n- 0.9.2   Adds `actionsAndStatesFor` and `flattenTransitions` to transform transitions into states and actions\n- 0.9.1   Adds next-action-predicate in the fsm specification\n- 0.8.9   Ready for community review\n\n## Copyright and license\nCode and documentation copyright 2021 Jean-Jacques Dubray. Code released under the ISC license. Docs released under Creative Commons.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdubray%2Fsam-fsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjdubray%2Fsam-fsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdubray%2Fsam-fsm/lists"}