{"id":23790045,"url":"https://github.com/blackglory/extra-fsm","last_synced_at":"2026-05-02T10:43:32.769Z","repository":{"id":60027053,"uuid":"540525779","full_name":"BlackGlory/extra-fsm","owner":"BlackGlory","description":"🌲","archived":false,"fork":false,"pushed_at":"2023-09-14T13:13:26.000Z","size":483,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-09T05:57:22.088Z","etag":null,"topics":["browser","esm","library","nodejs","npm-package","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/extra-fsm","language":"TypeScript","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/BlackGlory.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-23T16:23:39.000Z","updated_at":"2023-09-14T13:14:46.000Z","dependencies_parsed_at":"2024-06-20T23:22:11.654Z","dependency_job_id":"38fbe532-5ca1-4b06-8b89-146dc6ee4448","html_url":"https://github.com/BlackGlory/extra-fsm","commit_stats":{"total_commits":16,"total_committers":1,"mean_commits":16.0,"dds":0.0,"last_synced_commit":"eb2287f16a136f2ef91e4d2e24f0acb32bfb200d"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/BlackGlory/extra-fsm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-fsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-fsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-fsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-fsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BlackGlory","download_url":"https://codeload.github.com/BlackGlory/extra-fsm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-fsm/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269842893,"owners_count":24484107,"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","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"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":["browser","esm","library","nodejs","npm-package","typescript"],"created_at":"2025-01-01T17:18:13.297Z","updated_at":"2026-05-02T10:43:32.731Z","avatar_url":"https://github.com/BlackGlory.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# extra-fsm\n## Install\n```sh\nnpm install --save extra-fsm\n# or\nyarn add extra-fsm\n```\n\n## Usage\nThe finite state machine provided in the library is the most common table-driven state machine,\nwhere the transition rules are defined in a table,\nwhich is easy to understand.\n\nExample, a machine that can be started and stopped:\n```ts\nimport { FiniteStateMachine, transition } from 'extra-fsm'\n\nconst schema = {\n  stopped: { start: 'running' }\n, running: { stop: 'stopped' }\n}\n\n// OOP\nconst machine = new FiniteStateMachine(schema, 'stopped')\nmachine.send('start')\nconsole.log(machine.state) // running\n\n// FP\nconst newState = transition(schema, 'stopped', 'start')\nconsole.log(newState) // running\n```\n\nThe library intentionally does not add any kind of advanced transition rules,\nsuch as defining state transition events as functions.\nAdvanced features are not supported because my experience has shown that\nthey are not as good as customized specialized wrappers,\nespecially when considering extensibility.\n\nExample, a machine that is not reliable,\nhas a 50% chance of failing to start,\nbut your luck attribute affects the success rate:\n```ts\nimport { FiniteStateMachine, transition } from 'extra-fsm'\n\ninterface IGameState {\n  attributes: IAttributes\n}\n\ninterface IAttributes {\n  luck: number\n}\n\nclass UnreliableMachine {\n  static schema = {\n    stopped: { start: 'running' }\n  , running: { stop: 'stopped' }\n  }\n\n  private fsm = new FiniteStateMachine(UnreliableMachine.schema, 'stopped')\n\n  get state() {\n    return this.fsm.state\n  }\n\n  constructor(private game: IGameState) {}\n\n  start(): void {\n    assert(this.fsm.can('start'))\n\n    if (Math.random() \u003c 0.5 * (this.game.attributes.luck + 1)) {\n      this.fsm.send('start')\n    }\n  }\n\n  stop(): void {\n    this.fsm.send('stop')\n  }\n}\n\nconst game: IGameState = {\n  attributes: {\n    luck: 1\n  }\n}\n\nconst machine = new UnreliableMachine(game)\nmachine.start()\nconsole.log(machine.state) // running\n```\n\n## API\n```ts\ntype IFiniteStateMachineSchema\u003c\n  State extends string | number | symbol\n, Event extends string | number | symbol\n\u003e = Record\u003cState, Partial\u003cRecord\u003cEvent, State\u003e\u003e\u003e\n```\n\n### FiniteStateMachine\n```ts\nclass FiniteStateMachine\u003c\n  State extends string | number | symbol\n, Event extends string | number | symbol\n\u003e {\n  get [Symbol.toStringTag](): string\n  get state(): State\n\n  constructor(\n    schema: IFiniteStateMachineSchema\u003cState, Event\u003e\n  , initialState: State\n  )\n\n  matches(state: State): boolean\n  can(event: Event): boolean\n\n  /**\n   * @throws {BadEventError}\n   */\n  send(event: Event): void\n}\n```\n\n### ObservableFiniteStateMachine\n```ts\ninterface IFiniteStateMachineStateChange\u003c\n  State extends string | number | symbol\n, Event extends string | number | symbol\n\u003e {\n  event: Event\n  oldState: State\n  newState: State\n}\n\nclass ObservableFiniteStateMachine\u003c\n  State extends string | number | symbol\n, Event extends string | number | symbol\n\u003e extends FiniteStateMachine\u003cState, Event\u003e {\n  get [Symbol.toStringTag](): string\n\n  observeStateChanges(): Observable\u003cIFiniteStateMachineStateChange\u003cState, Event\u003e\u003e\n}\n```\n\n### transition\n```ts\nexport function transition\u003c\n  State extends string | number | symbol\n, Event extends string | number | symbol\n, TransitionEvent extends Event\n\u003e(\n  schema: IFiniteStateMachineSchema\u003cState, Event\u003e\n, state: State\n, event: TransitionEvent\n): State\n```\n\n### canTransition\n```ts\nfunction canTransition\u003c\n  State extends string | number | symbol\n, Event extends string | number | symbol\n\u003e(\n  schema: IFiniteStateMachineSchema\u003cState, Event\u003e\n, state: State\n, event: Event\n) =\u003e boolean\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Fextra-fsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblackglory%2Fextra-fsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Fextra-fsm/lists"}