Jakmar is a finite state machine for Javascript.
If you want to see Jakmar in action, please consult the following:
- Game of life with Jakmar and React
- Workflow visualization with Jakmar and Vue (and d3)
For Node.js, just include the library:
var jakmar = require('jakmar')
First, build a MachineDefinition:
var definition = jakmar.create('foo-def') // foo-def is an identifier
Once it's created, start by adding states:
definition
.state('opened') // a state is just defined by a string
.state('closed')
And some transitions:
definition
.transition('open', 'closed', 'opened') // 'open' transition, from the 'closed' state to the 'opened' state
.transition('close', 'opened', 'closed') // 'close' transition, from the 'opened' state to the 'closed' state
Then just build the definition to get your stateful instance:
var stateful = definition.build('opened') // opened will be the initial state
Your stateful instance has been enriched with methods defined by transitions. You can also verify the current state using the state
property:
console.log(stateful.state) // opened
stateful.close()
console.log(stateful.state) // closed
stateful.open()
console.log(stateful.state) // opened
The create
method creates and returns a new instance of a MachineDefinition
with id
as an identifier. The following options
are valid (and all are optional):
errorOnInvalidTransition
: will throw an Error if a transition tries to be applied but the stateful object is not in an expected state. Default istrue
.errorOnUnknownState
: will throw an Error if a transition tries to be applied to or from a state that does not exist. Default istrue
.
machineDefinition = jakmar.create('fooDef', {errorOnInvalidTransition: false, errorOnUnknownState: false})
The get
method will return the MachineDefinition
with id
as an identifier. If there is no machine definition found, returns undefined
.
machineDefinition = jakmar.get('fooDef')
The reset
method will remove all stored MachineDefinition
s from Jakmar.
jakmar.reset()
Register a new state with stateId
as an identifier to the machineDefinition
. Returns this
for chained calls.
machineDefinition.state('opened')
Register an array of new states with stateIds
being an Array of identifier for the machineDefinition
. Returns this
for chained calls.
machineDefinition.states(['opened', 'closed'])
Alternate way of registering new states, using any number of string, each a stateId
for the machineDefinition
. Returns this
for chained calls.
machineDefinition.states('opened', 'closed')
Register a new transition with transitionId
as an identifier to the machineDefinition
. The transition will change the stateful object from the state defined by fromStateId
to the state defined by toStateId
. Returns this
for chained calls.
machineDefinition.transition('open', 'closed', 'opened')
Transform the target
into a statefulObject
with the current machineDefinition
states and transitions applied to it. target
is optional ; a new object is created if missing. The stateful object initial state is mandatory and defined by the initialState
. Returns a statefulObject
.
var statefulObject = machineDefinition.build('opened', {})
Expose the registered states of the machineDefinition
. Returns an object with key / value pairs, key being the state ids and the value being the State
objects. A State
object only has an id
at the moment.
var states = machineDefinition.getStates()
console.log(states)
// {
// opened: { id: 'opened' },
// closed: { id: 'closed' }
// }
Expose the registered transitions of the machineDefinition
. Returns an Arrat of Transition
objects. A Transition
object has two properties: the transition id
and an applicableStates
object of key / value pairs, where the key is the applicable fromState
and the value is the corresponding toState
for that transition.
machineDefinition.transition('toggle', 'opened', 'closed')
machineDefinition.transition('toggle', 'closed', 'opened')
var transitions = machineDefinition.getTransitions()
console.log(transitions)
// [
// { id: 'toggle', applicableStates : { opened: 'closed', closed: 'opened' } }
// ]
Register a function that will be called every time the statefulObject
enter a new state. It is called after the onExitFn
, once the new state has been applied. The onEnterFn
should be a function accepting one argument, being the id
of state that has just been entered. Returns this
for chained calls.
var onEnter = function(stateId) {
console.log('Entering', stateId)
}
machineDefinition.onEnterFn(onEnter)
var statefulObject = machineDefinition.build('opened')
statefulObject.close()
// Entering closed
Register a function that will be called every time the statefulObject
exits a state. It is called before the onEnterFn
, once the new state has been applied. The onExitFn
should be a function accepting one argument, being the id
of state that will be exited. Returns this
for chained calls.
var onExit = function(stateId) {
console.log('Exiting', stateId)
}
machineDefinition.onExitFn(onExit)
var statefulObject = machineDefinition.build('opened')
statefulObject.close()
// Exiting opened
Return the current state id of the statefulObject
.
var statefulObject = machineDefinition.build('opened')
statefulObject.state // 'opened'
Function that gets called every time the statefulObject
changes state. It gets called with three arguments: the transition
id that trigerred the change of state, the fromState
id and the toState
id. It gets called ```after the state has changed.
var statefulObject = machineDefinition.build('opened')
statefulObject.stateChange = function(transitionId, fromStateId, toStateId) {
console.log('Going from', fromStateId, 'to', toStateId, 'because of transition', transitionId)
}
statefulObject.open()
// Going from opened to closed because of transition open
Apply a registered transition
to the statefulObject
. Returns true
id the transitions was applied, throws an Error
if the transition can't be applied (because the statefulObject
is not in a state where the transition
can be applied) or returns false
if the errorOnInvalidTransition
option is set to false.
statefulObject.state // closed
statefulObject.open() // returns true
statefulObject.state // opened
statefulObject.open() // throws an Error
- Add a minified version