{"id":13527079,"url":"https://github.com/choojs/nanostate","last_synced_at":"2025-04-01T09:31:03.137Z","repository":{"id":47037008,"uuid":"112034064","full_name":"choojs/nanostate","owner":"choojs","description":"🚦- Small Finite State Machines","archived":false,"fork":false,"pushed_at":"2022-05-22T04:15:15.000Z","size":26,"stargazers_count":172,"open_issues_count":3,"forks_count":21,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-02T13:52:23.081Z","etag":null,"topics":["finite-state-machine","fsm","state-machine"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/choojs.png","metadata":{"files":{"readme":"README.md","changelog":null,"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-11-25T20:59:09.000Z","updated_at":"2025-02-08T11:07:17.000Z","dependencies_parsed_at":"2022-07-22T02:02:24.394Z","dependency_job_id":null,"html_url":"https://github.com/choojs/nanostate","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choojs%2Fnanostate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choojs%2Fnanostate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choojs%2Fnanostate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choojs%2Fnanostate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/choojs","download_url":"https://codeload.github.com/choojs/nanostate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246615942,"owners_count":20806031,"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":["finite-state-machine","fsm","state-machine"],"created_at":"2024-08-01T06:01:40.507Z","updated_at":"2025-04-01T09:31:02.753Z","avatar_url":"https://github.com/choojs.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Libraries"],"sub_categories":["JavaScript"],"readme":"# nanostate\n[![npm version][2]][3] [![build status][4]][5]\n[![downloads][8]][9] [![js-standard-style][10]][11]\n\nSmall Finite State Machines. Great data structure to make code more readable,\nmaintainable and easier to debug.\n\n## Usage\n```js\nvar nanostate = require('nanostate')\n\nvar machine = nanostate('green', {\n  green: { timer: 'yellow' },\n  yellow: { timer: 'red' },\n  red: { timer: 'green' }\n})\n\nmachine.emit('timer')\nconsole.log(machine.state)\n// =\u003e 'yellow'\n\nmachine.emit('timer')\nconsole.log(machine.state)\n// =\u003e 'red'\n\nmachine.emit('timer')\nconsole.log(machine.state)\n// =\u003e 'green'\n```\n\n## Hierarchical\nLet's implement a traffic light that flashes red whenever there's a power\noutage. Instead of adding a `powerOutage` event to each normal state, we\nintroduce a hierarchy which allows any normal state to emit the `powerOutage`\nevent to change the state to `flashingRed`.\n```js\nvar nanostate = require('nanostate')\n\nvar machine = nanostate('green', {\n  green: { timer: 'yellow' },\n  yellow: { timer: 'red' },\n  red: { timer: 'green' }\n})\n\nmachine.event('powerOutage', nanostate('flashingRed', {\n  flashingRed: { powerRestored: 'green' }\n}))\n\nmachine.emit('timer')\nconsole.log(machine.state)\n// =\u003e 'yellow'\n\nmachine.emit('powerOutage')\nconsole.log(machine.state)\n// =\u003e 'flashingRed'\n\nmachine.emit('powerRestored')\nconsole.log(machine.state)\n// =\u003e 'green'\n```\n\n## History (to be implemented)\nImplementers note: keep track of the last state a machine was in before exiting\nto the next machine. That way if `'$history'` is called, it can be merged into\nthe previous machine.\n\nTODO: figure out how it works if machines are combined in a non-linear fashion.\n\n```js\nvar nanostate = require('nanostate')\n\nvar machine = nanostate('cash', {\n  cash: { check: 'check' },\n  check: { cash: 'cash' }\n})\n\nmachine.join('next', nanostate('review', {\n  review: { previous: '$history' }\n}))\n```\n\n## Parallel\nSometimes there's multiple parallel states that need\nexpressing; `nanostate.parallel` helps with that. For example when editing\ntext, a particular piece of text might be `bold`, `italic` and `underlined` at\nthe same time. The trick is that all of these states operate in parallel\n```js\nvar nanostate = require('nanostate')\n\nvar machine = nanostate.parallel({\n  bold: nanostate('off', {\n    on: { 'toggle': 'off' },\n    off: { 'toggle': 'on' },\n  }),\n  underline: nanostate('off', {\n    on: { 'toggle': 'off' },\n    off: { 'toggle': 'on' },\n  }),\n  italics: nanostate('off', {\n    on: { 'toggle': 'off' },\n    off: { 'toggle': 'on' },\n  }),\n  list: nanostate('none', {\n    none: { bullets: 'bullets', numbers: 'numbers' },\n    bullets: { none: 'none', numbers: 'numbers' },\n    numbers: { bullets: 'bullets', none: 'none' }\n  })\n})\n\nmachine.emit('bold:toggle')\nconsole.log(machine.state)\n// =\u003e {\n//   bold: 'on',\n//   italics: 'off',\n//   underline: 'off',\n//   list: 'none'\n// }\n```\n\n## Nanocomponent\nUsage in combination with\n[nanocomponent](https://github.com/choojs/nanocomponent) to create stateful UI\ncomponents.\n```js\nvar Nanocomponent = require('nanocomponent')\nvar nanostate = require('nanostate')\n\nmodule.exports = class Component extends Nanocomponent {\n  constructor (name, state, emit) {\n    super(name, state, emit)\n\n    this.state = {\n      data: {},\n      input: ''\n    }\n\n    this.machine = nanostate('idle', {\n      idle: { click: 'loading' },\n      loading: { resolve: 'data', reject: 'error' },\n      data: { click: 'loading' },\n      error: { click: 'loading' }\n    })\n\n    this.machine.on('loading', () =\u003e this.searchRepositories())\n  }\n\n  createElement () {\n    var buttonText = {\n      idle: 'Fetch Github',\n      loading: 'Loading…',\n      error: 'Github fail. Retry?',\n      data: 'Fetch Again?'\n    }[this.machine.state]\n\n    return html`\n      \u003cdiv\u003e\n        \u003cinput\n          type=\"text\"\n          value=${this.state.input}\n          onChange=${e =\u003e (this.state.input = e.target.value) \u0026\u0026 this.rerender()}\n        \u003e\n        \u003cbutton\n          onClick=${() =\u003e this.machine.emit('click')}\n          disabled=${this.machine.state === 'loading'}\n        \u003e\n          ${buttonText}\n        \u003c/button\u003e\n        ${data \u0026\u0026 html`\u003cdiv\u003e${JSON.stringify(data, null, 2)}\u003c/div\u003e`}\n        ${this.machine.state === 'error' \u0026\u0026 html`\u003ch1\u003eError\u003c/h1\u003e`}\n      \u003c/div\u003e\n    `\n  }\n\n  searchRepositories () {\n    fetch(`${ROOT_URL}/${this.state.input}`)\n      .then(res =\u003e res.json())\n      .then(res =\u003e {\n         this.state.data = res.data\n         this.machine.emit('resolve')\n       })\n      .catch(err =\u003e this.machine.emit('reject'))\n  }\n}\n```\n\n## API\n### `machine = nanostate(initialState, transitions)`\nCreate a new instance of Nanostate. Takes the name of the initial state, and a\nmapping of states and their corresponding transitions. A state mapping is\ndefined as `{ 1: { 2: 3 }}`, where `1` is the state's name, `2` is an event\nname it accepts, and `3` is the new state after the event has been emitted.\n\n### `machine.emit(event)`\nMove from the current state to a new state. Will throw if an invalid command is\npassed.\n\n### `machine.on(state, cb)`\nTrigger a callback when a certain state is entered. Useful to trigger side\neffects upon state change.\n\n### `state = machine.state`\nReturn the current state.\n\n### `machine.event(eventName, machine)` (to be implemented)\nAdd another machine to the transition graph. The first argument is an event\nname, which can be transitioned to from all other states in the graph.\n\n### `machine = nanostate.parallel(machines)` (to be implemented)\nCombine several state machines into a single machine. Takes an object of state\nmachines, where the key is used to prefix the events for the state machine.\n\nSay we have two state machine: `'foo'` and `'bar'`. `'foo'` has an event called\n`'beep'`. When combined through `.parallel()`, the event on the combined\nmachine would be `'foo:beep'`.\n\n## Installation\n```sh\n$ npm install nanostate\n```\n\n## See Also\n- [Infinitely Better UIs With Finite Automata (video)](https://www.youtube.com/watch?v=VU1NKX6Qkxc\u0026feature=youtu.be)\n- [yoshuawuyts/fsm-event](https://github.com/yoshuawuyts/fsm-event)\n- [davidkpiano/xstate](https://github.com/davidkpiano/xstate/)\n\n## License\n[Apache-2.0](./LICENSE)\n\n[0]: https://img.shields.io/badge/stability-experimental-orange.svg?style=flat-square\n[1]: https://nodejs.org/api/documentation.html#documentation_stability_index\n[2]: https://img.shields.io/npm/v/nanostate.svg?style=flat-square\n[3]: https://npmjs.org/package/nanostate\n[4]: https://img.shields.io/travis/choojs/nanostate/master.svg?style=flat-square\n[5]: https://travis-ci.org/choojs/nanostate\n[6]: https://img.shields.io/codecov/c/github/choojs/nanostate/master.svg?style=flat-square\n[7]: https://codecov.io/github/choojs/nanostate\n[8]: http://img.shields.io/npm/dm/nanostate.svg?style=flat-square\n[9]: https://npmjs.org/package/nanostate\n[10]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square\n[11]: https://github.com/feross/standard\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchoojs%2Fnanostate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchoojs%2Fnanostate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchoojs%2Fnanostate/lists"}