{"id":19314193,"url":"https://github.com/davidchin/switchhub","last_synced_at":"2025-04-22T16:31:41.318Z","repository":{"id":65510407,"uuid":"78327027","full_name":"davidchin/switchhub","owner":"davidchin","description":"An undoable finite state machine for JavaScript and TypeScript","archived":false,"fork":false,"pushed_at":"2017-02-09T23:17:32.000Z","size":39,"stargazers_count":5,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-02T01:13:56.253Z","etag":null,"topics":["state-machine","state-management","state-transition","undo-redo"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidchin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-08T06:57:03.000Z","updated_at":"2023-03-08T04:26:34.000Z","dependencies_parsed_at":"2023-01-26T17:55:12.813Z","dependency_job_id":null,"html_url":"https://github.com/davidchin/switchhub","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidchin%2Fswitchhub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidchin%2Fswitchhub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidchin%2Fswitchhub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidchin%2Fswitchhub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidchin","download_url":"https://codeload.github.com/davidchin/switchhub/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250277200,"owners_count":21404007,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["state-machine","state-management","state-transition","undo-redo"],"created_at":"2024-11-10T00:42:32.229Z","updated_at":"2025-04-22T16:31:41.063Z","avatar_url":"https://github.com/davidchin.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SwitchHub\n\nSwitchHub is a finite state machine written for JavaScript. It can help you manage the state of an object declaratively. It can determine and transition to its future state by looking at its current state and your actions. Also, it can undo and redo transitions.\n\n[![Build Status](https://travis-ci.org/davidchin/switchhub.svg?branch=master)](https://travis-ci.org/davidchin/switchhub)\n\n## Installation\n\nYou can use npm to install this library.\n\n```\nnpm install --save switchhub\n```\n\n## Usage\n\n### Add transitions\n\nTo create an instance and register a list of states and events.\n\n```js\nimport { StateMachine } from 'switchhub';\n\nconst stateMachine = StateMachine.create('inactive');\n\nstateMachine.addEvent('activate', [\n    { from: 'inactive', to: 'active' },\n]);\n\nstateMachine.addEvent('deactivate', [\n    { from: 'active', to: 'inactive' },\n    { from: 'paused', to: 'inactive' },\n]);\n\nstateMachine.addEvent('pause', [\n    { from: 'active', to: 'paused' },\n]);\n\nstateMachine.addEvent('resume', [\n    { from: 'paused', to: 'active' },\n]);\n```\n\nYou can also add a transition without an event.\n\n```js\nstateMachine.addTransition({ from: 'inactive', to: 'active' });\n```\n\nIf you want to make a transition conditional, you can add a `condition` property to it.\n\n```js\nlet canPause = false;\n\nstateMachine.addEvent('pause', [\n    { from: 'active', to: 'paused', condition: () =\u003e canPause }, // `condition` function should return true or false\n]);\n```\n\n### Transition to states\n\nTo transition to a new state by triggering an event.\n\n```js\n// Given the initial state is 'inactive'\nstateMachine.triggerEvent('activate');\n// stateMachine.getState() === 'active'\n\nstateMachine.triggerEvent('pause');\n// stateMachine.getState() === 'paused'\n\nstateMachine.triggerEvent('deactivate');\n// stateMachine.getState() === 'inactive'\n```\n\nYou can also directly transition to a new state, if it is related to the current state.\n\n```js\n// Given the current state is 'inactive', you can transition to 'active'\nstateMachine.transition('active');\n// stateMachine.getState() === 'active'\n\n// Given the current state is 'paused', you cannot transition to 'inactive'\nstateMachine.transition('inactive');\n// stateMachine.getState() !== 'inactive'\n```\n\nYou can optionally pass meta data to subscribers when you transition to a new state.\n\n```js\nstateMachine.subscribe(transition) =\u003e {\n    // transition.data.message === 'Hello world'\n});\n\nstateMachine.triggerEvent('activate', { message: 'Hello world' });\n```\n\n### Undo transitions\n\nYou can undo / redo a transition if it is marked as undoable.\n\n```js\nstateMachine.addEvent('activate', [\n    { from: 'inactive', to: 'active', undoable: true },\n]);\n\nstateMachine.triggerEvent('activate');\nstateMachine.undoTransition();\nstateMachine.redoTransition();\n```\n\nWhen passing data to `triggerEvent` or `transition`, make sure it is immutable. Otherwise, when you redo a transition, your subscribers might not get the same value.\n\n### Subscribe to changes\n\nYou can subscribe to state changes.\n\n```js\nstateMachine.subscribe(transition =\u003e {\n    // transition.event === 'activate'\n    // transition.from === 'inactive'\n    // transition.to === 'active'\n});\n\nstateMachine.subscribe(transition =\u003e {\n    // You can have more than one subscriber\n});\n\nstateMachine.transition('activate');\n```\n\n### Remove transitions\n\nTo remove an event, transition or subscriber.\n\n```js\nstateMachine.removeEvent('pause');\nstateMachine.removeTransition({ from: 'active', to: 'paused' });\nstateMachine.unsubscribe(subscriber);\n```\n\n### Manage object state\n\nThere are different ways you can use the state machine to manage the state of an object. Here's one example.\n\n```js\nclass Device {\n    constructor() {\n        this._stateMachine = StateMachine.create('inactive');\n\n        // Configure your state machine here... i.e.:\n        this._stateMachine.addEvent('activate', [\n            { from: 'inactive', to: 'active' },\n        ]);\n\n        this._stateMachine.subscribe(this.handleChange.bind(this));\n    }\n\n    getState() {\n        return this._stateMachine.getState();\n    }\n\n    activate() {\n        this._stateMachine.triggerEvent('activate');\n    }\n\n    handleChange(transition) {\n        // Do more things after a successful transition\n    }\n}\n```\n\n## Development\n\nTo build distribution files, please run\n\n```\nnpm run build\n```\n\nTo run tests, please run\n\n```\nnpm test\n```\n\nTo see a test coverage report, please run\n\n```\nnpm run coverage\n```\n\nTo lint your code, please run\n\n```\nnpm run lint\n```\n\n## Contribution\n\nIf you like to contribute, please make a pull request explaining your changes. If you want to make a suggestion or file a bug report, please create a GitHub issue.\n\n## License\n\nISC\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidchin%2Fswitchhub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidchin%2Fswitchhub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidchin%2Fswitchhub/lists"}