{"id":17134353,"url":"https://github.com/brianium/fsm","last_synced_at":"2025-10-12T03:04:34.488Z","repository":{"id":146515057,"uuid":"454953733","full_name":"brianium/fsm","owner":"brianium","description":"Yet another FSM library for Clojure(Script)","archived":false,"fork":false,"pushed_at":"2022-02-04T20:33:25.000Z","size":2389,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-25T02:57:33.633Z","etag":null,"topics":["clojure","clojurescript","state-machine","state-management"],"latest_commit_sha":null,"homepage":"https://brianium.github.io/fsm/","language":"Clojure","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/brianium.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-02T22:17:11.000Z","updated_at":"2023-01-12T18:18:11.000Z","dependencies_parsed_at":"2023-04-28T22:02:04.975Z","dependency_job_id":null,"html_url":"https://github.com/brianium/fsm","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/brianium/fsm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Ffsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Ffsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Ffsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Ffsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brianium","download_url":"https://codeload.github.com/brianium/fsm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Ffsm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279010155,"owners_count":26084690,"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-10-12T02:00:06.719Z","response_time":53,"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":["clojure","clojurescript","state-machine","state-management"],"created_at":"2024-10-14T19:44:41.841Z","updated_at":"2025-10-12T03:04:34.451Z","avatar_url":"https://github.com/brianium.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"fsm.gif\"\u003e\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n \u003cstrong\u003e(F)\u003c/strong\u003eart \u003cstrong\u003e(S)\u003c/strong\u003emelling \u003cstrong\u003e(M)\u003c/strong\u003eachine\n \u003cbr /\u003e\n Lol just kidding, \u003cem\u003ebut can you imagine?\u003c/em\u003e\n\u003c/div\u003e\n\n# fsm\n\nYet another finite state machine library for Clojure(Script).\n\n[![cljdoc badge](https://cljdoc.org/badge/com.github.brianium/fsm)](https://cljdoc.org/d/com.github.brianium/fsm/CURRENT) [![Clojars Project](https://img.shields.io/clojars/v/com.github.brianium/fsm.svg)](https://clojars.org/com.github.brianium/fsm)\n\n## Table of contents\n\n- [Using fsm](#using-fsm)\n  - [create-state-machine](#create-state-machine)\n  - [transition](#transition)\n  - [add-effect](#add-effect)\n  - [StateMachine](#statemachine)\n- [Demo](https://brianium.github.io/fsm/)\n\n### Using fsm\n\nIt only supports hash maps. It works in the browser and the jvm. You make a state machine. You transition it\nto new states via events. You can register side effects. It is a great time!\n\n#### create-state-machine\n\nHere is how you describe states:\n\n```clojure\n(def states {nil       {::init ::ready}\n             ::ready   {::enable ::enabled}\n             ::enabled {::disable ::disabled\n                        ::stop    nil}\n             ::disabled {::enable ::enabled\n                         ::cancel nil}})\n```\n\nEvery key is a named state. A `nil` state can mean pre-existence. Or non-existence. Or \"not yet a thing\"-ness. \n\nYou can use this state description to make a new state machine:\n\n```clojure\n(require '[fsm.core :as fsm])\n\n(def initial-state {})\n\n(def state-machine (fsm/create-state-machine states initial-state))\n```\n\nYour state map will automatically have an `:fsm/state` key added with the named state - i.e `::ready`, `::enabled`, etc. It will also contain an `:fsm/last-event` key containing the last event.\n\nA third argument can be provided which is an `atom-fn` that is used. Defaults to `atom`\n\n```clojure\n(require '[reagent.core :as r])\n\n(def state-machine (fsm/create-state-machine states initial-state r/atom))\n```\n\n#### transition\n\nYou move a state machine from one state to the next via the `transition` function. `transition` is effectively a no-op if you try to do an invalid transition i.e you can't `::disable` an already `::disabled` state machine.\n\nThe final argument is the new state of the state machine. It is all or nothing here. No partial updates, just give me the new state. A function CAN be used if you want to modify existing state. If the last argument is omitted entirely, a `nil` payload is assumed.\n\n```clojure\n(fsm/transition state-machine ::init {:count 0})\n\n;;; You can use a function of arity 1 that returns a new state, or in this case the current state\n\n(fsm/transition state-machine ::enable fsm/current-state)\n\n;;; Or omit it to set the underlying data structure to nil\n\n(fsm/transition state-machine ::stop)\n```\n\n#### add-effect\n\nAllows you to do things in response to state changes. All effects must be named by a keyword. Side effects can be called in a few ways:\n\n```clojure\n;;; Do something whenever state changes\n(fsm/add-effect state-machine ::log (fn [sm old-state new-state] (println new-state)))\n\n;;; Do something when moving from one explicit state to another, in this case from ready to enabled\n(fsm/add-effect state-machine ::start ::ready ::enabled on-enabled)\n\n;;; Do something when the state machine moves from one of the given set to an explicit state, in this case\n;;; when the state machine reaches a nil state from the ready OR enabled state\n(fsm/add-effect state-machine ::shutdown #{::ready ::enabled} nil on-shutdown)\n```\n\n#### StateMachine\n\nThe default implementation for this library is backed by atoms. A custom implementation can be supported by\nimplementing the `StateMachine` protocol. The atom backed one may serve as a good example:\n\n```clojure\n(ns fsm.impl\n  (:require [fsm.protocols :as p]))\n\n(defrecord AtomStateMachine [states *state]\n  p/StateMachine\n  (-transition [_ event payload]\n    (let [current @*state\n          next    (get-in states [(:fsm/state current) event] ::not-found)]\n      (when-not (= ::not-found next)\n        (-\u003e\u003e payload\n             (merge {:fsm/state next})\n             (reset! *state)))))\n\n  (-add-effect [this key fn-3]\n    (add-watch *state key (fn [_ _ old new]\n                            (fn-3 this old new))))\n\n  (-current-state [_]\n    @*state))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianium%2Ffsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrianium%2Ffsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianium%2Ffsm/lists"}