{"id":15400436,"url":"https://github.com/marco-ippolito/fiume","last_synced_at":"2025-04-05T15:09:09.429Z","repository":{"id":206660654,"uuid":"717400902","full_name":"marco-ippolito/fiume","owner":"marco-ippolito","description":"zero-dependency, lightweight finite state machine  in Typescript","archived":false,"fork":false,"pushed_at":"2025-03-24T12:42:13.000Z","size":1247,"stargazers_count":70,"open_issues_count":0,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-28T17:52:33.296Z","etag":null,"topics":["finite-state-machine","fsm","javascript","typescript"],"latest_commit_sha":null,"homepage":"https://marcoippolito.dev","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/marco-ippolito.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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}},"created_at":"2023-11-11T11:34:01.000Z","updated_at":"2025-03-24T12:42:15.000Z","dependencies_parsed_at":"2023-11-13T12:45:50.956Z","dependency_job_id":"ff019fdd-599a-4a29-bd17-d6f6786ad9a6","html_url":"https://github.com/marco-ippolito/fiume","commit_stats":{"total_commits":258,"total_committers":6,"mean_commits":43.0,"dds":0.3682170542635659,"last_synced_commit":"6a2bed5772d9138142f9641a8fd080eedb28d821"},"previous_names":["marco-ippolito/fsm","marco-ippolito/fiume"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marco-ippolito%2Ffiume","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marco-ippolito%2Ffiume/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marco-ippolito%2Ffiume/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marco-ippolito%2Ffiume/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marco-ippolito","download_url":"https://codeload.github.com/marco-ippolito/fiume/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247353746,"owners_count":20925329,"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","javascript","typescript"],"created_at":"2024-10-01T15:53:54.870Z","updated_at":"2025-04-05T15:09:09.409Z","avatar_url":"https://github.com/marco-ippolito.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fiume 🏞️\n\n[![npm version](https://img.shields.io/npm/v/fiume)](https://www.npmjs.com/package/fiume)\n[![build status](https://img.shields.io/github/actions/workflow/status/marco-ippolito/fiume/ci.yml)](https://github.com/marco-ippolito/fiume/actions)\n[![biome](https://img.shields.io/badge/code%20style-biome-brightgreen.svg?style=flat)](https://biomejs.dev/)\n![npm bundle size](https://img.shields.io/bundlephobia/minzip/fiume?label=Bundle%20Size\u0026link=https://bundlephobia.com/package/fiume@latest)\n\n**Fiume** is a zero-dependency, simple, and flexible state machine\nlibrary written in TypeScript.\nIt supports _Deterministic_ and partially _Non-Deterministic_\nstate machines.\nIt is compatible with all JavaScript runtimes and is designed to manage\nthe flow of a system through various states.\nThis library provides a lightweight and intuitive way to define states,\ntransitions, and hooks for state entry, exit, and transition events.\n\nUnlike other libraries, **Fiume** does not require hardcoding state transitions.\nInstead, you can write the transition logic inside the `transitionTo` function.\n\n## Docs\n\nYou can find documentation and examples at [fiume.dev](https://fiume.marcoippolito.dev).\n\n## Installation\n\n```bash\nnpm install fiume\n```\n\n## Usage\n\n```ts\nimport { StateMachine, State } from \"fiume\";\n\n// Define a simple ON-OFF machine\nconst states: Array\u003cState\u003e = [\n  {\n    id: \"OFF\",\n    initial: true,\n    transitionGuard: ({ event }) =\u003e event === 'button clicked',\n    transitionTo: () =\u003e \"ON\",\n  },\n  {\n    id: \"ON\",\n    transitionGuard: ({ event }) =\u003e event === 'button clicked',\n    transitionTo: () =\u003e \"OFF\",\n  },\n];\n\n// Create a state machine instance\nconst machine = StateMachine.from(states);\n\n// Start the state machine\nawait machine.start();\nconsole.log(machine.currentStateId); // OFF\n\n// Trigger a transition by sending an event\nawait machine.send('button clicked');\nconsole.log(machine.currentStateId); // ON\n\n// Trigger another transition\nawait machine.send('button clicked');\nconsole.log(machine.currentStateId); // OFF\n\n// Trigger another transition\nawait machine.send('wrong event'); // wrong event wont trigger the transition\nconsole.log(machine.currentStateId); // OFF\n\n```\n\nWith `autoTransition` set to `true`, the machine does not wait\nfor the `send` method to trigger the transition to the next state:\n\n```ts\nimport { StateMachine, State } from \"fiume\";\n\nconst states: Array\u003cState\u003e = [\n  {\n    id: \"OFF\",\n    initial: true,\n    autoTransition: true,\n    transitionTo: () =\u003e \"ON\",\n  },\n  {\n    id: \"ON\",\n    final: true,\n  },\n];\n\nconst machine = StateMachine.from(states);\nawait machine.start();\nconsole.log(machine.currentStateId); // ON\n\n```\n\nYou can define custom hooks `onEntry`, `onExit` and `onFinal`:\n\n```ts\n\nconst states: Array\u003cState\u003e = [\n  {\n    id: \"OFF\",\n    initial: true,\n    transitionTo: async ({ context, event, sharedData }) =\u003e \"ON\",\n    onEntry: async ({ context, event, sharedData }) =\u003e console.log(event.name, event.value),\n    onExit: async ({ context, event, sharedData }) =\u003e console.log(event.name, event.value),\n  },\n  {\n    id: \"ON\",\n    final: true,\n    transitionTo: ({ context, event, sharedData }) =\u003e \"OFF\",\n    onEntry: async ({ context, event, sharedData }) =\u003e console.log(event.name, event.value),\n    onExit: ({ context, event, sharedData }) =\u003e console.log(event.name, event.value),\n    onFinal: ({ context, event, sharedData }) =\u003e console.log(event.name, event.value),\n  },\n];\n\ntype MyContext = { foo: string, bar: string }\ntype MyEvent = { name: string, value: number }\n\nconst machine = StateMachine.from\u003cMyContext, MyEvent\u003e(\n  states,\n  { context: { foo: 'foo', bar: 'bar' }}\n);\n\n// Start the state machine\nawait machine.start();\n\nawait machine.send({ name: 'foo', value: 1 });\nmachine.currentStateId; // ON\nawait machine.send({ name: 'foo', value: 2 });\nmachine.currentStateId; // OFF\n\n```\n\nYou can also `subscribe` to state transitions:\n\n```ts\n\nconst states: Array\u003cState\u003e = [\n  {\n    id: \"ONE\",\n    initial: true,\n    transitionTo: () =\u003e \"TWO\",\n  },\n  {\n    id: \"TWO\",\n    transitionTo: () =\u003e \"THREE\",\n  },\n  { id: \"THREE\", final: true },\n];\n\nconst machine = StateMachine.from(states);\nawait machine.start();\n\n// Subscribe to state transitions\nconst subId = machine.subscribe(\n  ({ context, currentStateId }) =\u003e console.log(currentStateId)\n); // ONE, TWO\nconsole.log(machine.currentStateId); // ONE\nawait machine.send();\nconsole.log(machine.currentStateId); // TWO\n\n// Unsubscribe the previous subscription\nmachine.unsubscribe(subId);\n\nawait machine.send();\nconsole.log(machine.currentStateId); // THREE\n\n```\n\n## API\n\n### StateMachine\n\n#### Constructor\n\n- `StateMachine.from`: A static function that returns a\nnew instance of the state machine. It takes the following parameters:\n  - `states`: An array of `State` objects representing the states of the state machine.\n  - `options` (optional): Configuration options for the state machine:\n    - `id` (string): The id of the machine,\n    - `context`: User-defined object.\n\n    \u003e Don't add in `context` objects that cannot be copied,\n    like database connections, sockets, emitter, request, etc. Instead use `sharedData`!\n\n    - `sharedData`: User-defined object.\n\n    \u003e Use `sharedData` to store database connection, sockets, request/response, etc.\n\nExample:\n\n```ts\nimport { StateMachine } from \"fiume\";\nconst machine = StateMachine.from(states, options);\n\n```\n\n- `StateMachine.fromSnapshot`:  A static function that returns a new instance\nof the state machine from an existing snapshot. It takes the following parameters:\n  - `snapshot`: The snapshot object produced by `machine.createSnapshot()`.\n  - `states`: An array of `State` objects representing the states of the state machine.\n  - `sharedData` (optional): User-defined object.\n\nExample:\n\n```ts\nimport { StateMachine } from \"fiume\";\nconst machine = StateMachine.from(states, options);\nawait machine.start();\nconst snapshot = machine.createSnapshot();\nconst refromSnapshot = StateMachine.fromSnapshot(snapshot, states);\n\n```\n\n#### Public Methods\n\n- `start` (async): Initiates the state machine and\n  triggers the execution of the initial state.\n\n- `send` (async): Send events to states that are not `autoTransition`.\nIf current state has `autoTransition: false`,\ncalling the `send` function is required to move to next state.\nIf the machine is in a final state and  `isFinished` set to `true`,\nusing `send` will reject.\n\n- `createSnapshot`: Returns a snapshot of the current machine\n  with the following properties:\n  - snapshotId (string): Id of the current snapshot.\n  - machineId: (string): Id of the machine.\n  - stateId: (string): Id of the current state when snapshot is taken.\n  - context: (TContext):  User defined context\n\n  \u003e`sharedData` will not be snapshotted!\n\n- `subscribe`: You can register a callback that will be invoked\non every state transition between the `onEntry` and `onExit` hooks.\nThe callback returns the `subscriptionId` and receives `context`,\n`event`, `sharedData`, and `currentStateId`.\n\n- `unsubscribe`: Remove the subscription with the given `subscriptionId`.\n\n#### Public properties\n\n- `id` string: The id of the machine,\nif not supplied in the constructor, will be a randomUUID.\n\n- `currentStateId` string: The id of current state of the machine.\n\n- `isFinished` boolean: True if the machine has finished in a final state.\n\n- `context`: User defined context.\n\n- `sharedData` (TSharedData): User defined data shared with the state machine.\n\n\u003e Do not add in `context`, objects that cannot be copied,\nlike database connections, `EventEmitter`, `Request`,\n`Socket`, use `sharedData` instead!\n\n### State\n\nRepresents a state in the state machine.\n\n- `id`: (required) Unique identifier for the state.\n- `transitionTo` (optional): Function or AsyncFunction that defines the\ntransition logic to move to another state, must return the id of the next state.\n- `autoTransition` (optional): Boolean, if `true` the machine will transition to\nthe next state without waiting for an event. If set to `true`\nis not possibile to use `transitionGuard`.\n- `onEntry` (optional): Hook called when entering the state.\n- `onExit` (optional): Hook called when exiting the state.\n- `onFinal` (optional): Hook called when execution has ended in final state.\n- `initial` (optional): Boolean indicating whether the state is the initial state,\nthere can only be one initial state.\n- `final` (optional): Boolean indicating whether the state is a final state.\n- `transitionGuard` (optional): Function or AsyncFunction takes as input\na user event and defines whether or not transition to the next state\n\n## License\n\nThis library is licensed under the [Apache 2.0 License](LICENSE).\nFeel free to use, modify, and distribute it as needed. Contributions are welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarco-ippolito%2Ffiume","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarco-ippolito%2Ffiume","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarco-ippolito%2Ffiume/lists"}