All Projects → tangkhaiphuong → stateless

tangkhaiphuong / stateless

Licence: MIT license
Finite State Machine porting from Stateless C#

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to stateless

simple-state-machine
A simple Java state machine for Spring Boot projects
Stars: ✭ 25 (+0%)
Mutual labels:  fsm, state-machine, finite-state-machine
Django Fsm
Django friendly finite state machine support
Stars: ✭ 1,898 (+7492%)
Mutual labels:  fsm, state-machine, finite-state-machine
UnityHFSM
A simple yet powerful class based hierarchical finite state machine for Unity3D
Stars: ✭ 243 (+872%)
Mutual labels:  fsm, state-machine, finite-state-machine
xstate
State machines and statecharts for the modern web.
Stars: ✭ 21,286 (+85044%)
Mutual labels:  fsm, state-machine, finite-state-machine
Fluent State Machine
Fluent API for creating state machines in C#
Stars: ✭ 195 (+680%)
Mutual labels:  fsm, state-machine, finite-state-machine
pastafarian
A tiny event-based finite state machine
Stars: ✭ 20 (-20%)
Mutual labels:  fsm, state-machine, finite-state-machine
Microwf
A simple finite state machine (FSM) with workflow character where you define your workflows in code.
Stars: ✭ 122 (+388%)
Mutual labels:  fsm, workflow-engine, finite-state-machine
use-state-machine
Use Finite State Machines with React Hooks
Stars: ✭ 28 (+12%)
Mutual labels:  fsm, state-machine, finite-state-machine
Jstate
Advanced state machines in Java.
Stars: ✭ 84 (+236%)
Mutual labels:  fsm, state-machine, finite-state-machine
Fsm As Promised
A finite state machine library using ES6 promises
Stars: ✭ 446 (+1684%)
Mutual labels:  fsm, state-machine, finite-state-machine
FiniteStateMachine
This project is a finite state machine designed to be used in games.
Stars: ✭ 45 (+80%)
Mutual labels:  fsm, state-machine, finite-state-machine
Nanostate
🚦- Small Finite State Machines
Stars: ✭ 151 (+504%)
Mutual labels:  fsm, state-machine, finite-state-machine
statemachine-go
🚦 Declarative Finite-State Machines in Go
Stars: ✭ 47 (+88%)
Mutual labels:  fsm, state-machine, finite-state-machine
Statecharts.github.io
There is no state but what we make. Feel free to pitch in.
Stars: ✭ 265 (+960%)
Mutual labels:  fsm, state-machine, finite-state-machine
Finity
A finite state machine library for Node.js and the browser with a friendly configuration DSL.
Stars: ✭ 88 (+252%)
Mutual labels:  fsm, state-machine, finite-state-machine
Afsm
C++14 Finite State Machine library
Stars: ✭ 113 (+352%)
Mutual labels:  fsm, state-machine, finite-state-machine
Squirrel
squirrel-foundation is a State Machine library, which provided a lightweight, easy use, type safe and programmable state machine implementation for Java.
Stars: ✭ 1,789 (+7056%)
Mutual labels:  fsm, state-machine
Qpcpp
QP/C++ real-time embedded framework/RTOS for embedded systems based on active objects (actors) and hierarchical state machines
Stars: ✭ 124 (+396%)
Mutual labels:  fsm, state-machine
go-sm
A finite-state machine library for the Go programming language
Stars: ✭ 14 (-44%)
Mutual labels:  state-machine, finite-state-machine
Hfsm2
High-Performance Hierarchical Finite State Machine Framework
Stars: ✭ 134 (+436%)
Mutual labels:  fsm, state-machine

Create state machines and lightweight state machine-based workflows directly in JavaScript/TypeScript code:

import { StateMachine } from 'stateless';

const phoneCall = new StateMachine<State, Trigger>(State.OffHook);

phoneCall.configure(State.OffHook)
    .permit(Trigger.CallDialled, State.Ringing);
	
phoneCall.configure(State.Ringing)
    .permit(Trigger.CallConnected, State.Connected);
 
phoneCall.configure(State.Connected)
    .onEntry(() => this.startCallTimer())
    .onExit(() => this.stopCallTimer())
    .permit(Trigger.LeftMessage, State.OffHook)
    .permit(Trigger.PlacedOnHold, State.OnHold);

// ...

await phoneCall.fire(Trigger.CallDialled);
assert.equal(State.Ringing, phoneCall.State);

This project, as well as the example above, was poring from Stateless C#.

Features

Most standard state machine constructs are supported:

  • Context support for state machine configure and state context instance. (see: dotnet-state-machine/stateless#232)
  • Generic support for states and triggers of any TypeScript type (numbers, strings, enums, etc.)
  • Hierarchical states
  • Entry/exit events for states
  • Guard clauses to support conditional transitions
  • Introspection

Some useful extensions are also provided:

  • Ability to store state externally (for example, in a property tracked)
  • Parameterised triggers
  • Reentrant states
  • Export to DOT graph

State Machine Configure and State Context instance.

Some situations need reuse state machine configure for optimation. This means state configuration only declare one-time binding with context. Then create many contexts without re-configure again. It helps for the bulk of actions such as HTTP request or business workflow which performance strictly.

import { StateMachine } from 'stateless/context';

class PhoneCall {

    public static StateMachine  = new StateMachine<State, Trigger, PhoneCall>();

    public static initialize() {
        stateMachine.configure(State.OffHook)
            .permit(Trigger.CallDialled, State.Ringing);
            
        stateMachine.configure(State.Ringing)
            .permit(Trigger.CallConnected, State.Connected);
        
        stateMachine.configure(State.Connected)
            .onEntry((phonecall) => phonecall.startCallTimer())
            .onExit((phonecall) => phonecall.stopCallTimer())
            .permit(Trigger.LeftMessage, State.OffHook)
            .permit(Trigger.PlacedOnHold, State.OnHold);
    }
// ...
}
PhoneCall.initialize();

// Create many state context instance without re-configure.
const phoneCall1 = stateMachine.createStateContext(new PhoneCall(), State.OffHook);
const phoneCall2 = stateMachine.createStateContext(new PhoneCall(), State.OffHook);

await phoneCall1.fire(Trigger.CallDialled);
await phoneCall2.fire(Trigger.CallDialled);

Hierarchical States

In the example below, the OnHold state is a substate of the Connected state. This means that an OnHold call is still connected.

phoneCall.configure(State.OnHold)
    .substateOf(State.Connected)
    .permit(Trigger.TakenOffHold, State.Connected)
    .permit(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed);

In addition to the StateMachine.state property, which will report the precise current state, an isInState(State) method is provided. isInState(State) will take substates into account, so that if the example above was in the OnHold state, isInState(State.connected) would also evaluate to true.

Entry/Exit Events

In the example, the startCallTimer() method will be executed when a call is connected. The stopCallTimer() will be executed when call completes (by either hanging up or hurling the phone against the wall.)

The call can move between the Connected and OnHold states without the startCallTimer() and stopCallTimer() methods being called repeatedly because the OnHold state is a substate of the Connected state.

Entry/Exit event handlers can be supplied with a parameter of type Transition that describes the trigger, source and destination states.

External State Storage

Stateless is designed to be embedded in various application models. For example, some ORMs place requirements upon where mapped data may be stored, and UI frameworks often require state to be stored in special "bindable" properties. To this end, the StateMachine constructor can accept function arguments that will be used to read and write the state values:

const stateMachine = new StateMachine<State, Trigger>({
    accessor: () => this.myState,
    mutator: state => this.myState = state});

In this example the state machine will use the myState object for state storage.

Another example can be found in the JsonExample solution, located in the example folder.

Introspection

The state machine can provide a list of the triggers than can be successfully fired within the current state via the StateMachine.permittedTriggers property.

Guard Clauses

The state machine will choose between multiple transitions based on guard clauses, e.g.:

phoneCall.configure(State.OffHook)
    .permitIf(Trigger.CallDialled, State.Ringing, () => this.isValidNumber)
    .permitIf(Trigger.CallDialled, State.Beeping, () => !this.isValidNumber);

Guard clauses within a state must be mutually exclusive (multiple guard clauses cannot be valid at the same time.) Substates can override transitions by respecifying them, however substates cannot disallow transitions that are allowed by the superstate.

The guard clauses will be evaluated whenever a trigger is fired. Guards should therefor be made side effect free.

Trigger parameters can be used to dynamically select the destination state using the permitDynamic() configuration method.

Ignored Transitions and Reentrant States

Firing a trigger that does not have an allowed transition associated with it will cause an exception to be thrown.

To ignore triggers within certain states, use the ignore(TTrigger) directive:

phoneCall.configure(State.Connected)
    .ignore(Trigger.CallDialled);

Alternatively, a state can be marked reentrant so its entry and exit events will fire even when transitioning from/to itself:

stateMachine.configure(State.Assigned)
    .permitReentry(Trigger.Assigned)
    .onEntry(() => this.sendEmailToAssignee());

By default, triggers must be ignored explicitly. To override Stateless's default behaviour of throwing an exception when an unhandled trigger is fired, configure the state machine using the onUnhandledTrigger method:

stateMachine.onUnhandledTrigger((state, trigger) => { });

Export to DOT graph

It can be useful to visualize state machines on runtime. With this approach the code is the authoritative source and state diagrams are by-products which are always up to date.

phoneCall.configure(State.OffHook)
    .permitIf(Trigger.CallDialled, State.Ringing, isValidNumber);
    
const graph = UmlDotGraph.format(phoneCall.getInfo());

It can support to display current state of state machines.

const graph = UmlDotGraph.format(phoneCall.getInfo(), true);

The UmlDotGraph.format() method returns a string representation of the state machine in the DOT graph language, e.g.:

digraph {
  OffHook -> Ringing [label="CallDialled [isValidNumber]"];
}

This can then be rendered by tools that support the DOT graph language, such as the dot command line tool from graphviz.org or viz.js. See http://www.webgraphviz.com for instant gratification. Command line example: dot -T pdf -o phoneCall.pdf phoneCall.dot to generate a PDF file.

Async triggers

Note: This library support Promise so Async triggers is support well at core of library.

Building

Stateless runs on JavaScript/TypeScript and practically all modern.

Project Goals

This page is an almost-complete description of Stateless, and its explicit aim is to remain minimal.

Please use the issue tracker or the if you'd like to report problems or discuss features.

(Why the name? Stateless implements the set of rules regarding state transitions, but, at least when the delegate version of the constructor is used, doesn't maintain any internal state itself.)

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].