npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@peter.naydenov/fsm

v5.2.0

Published

Finite State Machine

Downloads

12

Readme

FSM (@peter.naydenov/fsm)

version license

What's new in version 5.x.x

Description

Finite state machine(FSM) is an abstract machine that can be in exactly one of a finite number of states at any given time. The FSM can change from one state to another in response to some external inputs(actions). The change from state to another is called a transition. An FSM definition includes:

const fsmDefinition = {
          init  : 'none'  // initial fsm state
        , behavior : [     
                    // [ fromState,  action   , nextState, transitionFx, chaining ]
                       [ 'none'   , 'activate', 'active' , 'switchON'   ] // transition condition
                    ,  [ 'active' , 'stop'    , 'none'   , 'switchOFF'  ] // transition condition
                 ]
        , stateData : { 
                        // ... all stateData flags are here
                      }
                    }
    }
  • init: string. Initial state of the FSM;
  • stateData: Container for all stateData required by the FSM.
  • behavior: array of transition conditions. Describe relation among fsm state, action and transition;
  • transitionCondition: Array [fromState action nextState transitionFx];
  • fromState: string. State as starting point for transition;
  • action: string. External input signal. Transition could happen only when fromState and action are described in transitionCondition record;
  • nextState: string. The next FSM state if transition become successful;
  • transitionFx: string. The function name that will be executed on 'state/action' conditions. Transition should return transitionResult object where parameter "success" will represent transition success. Result of transition will be evaluated by FSM. Positive response will change actual state to 'nextState' from transitionCondition record. Negative result will keep actual state;
  • chaining(optional): Array [ positive, negative ]. Optional element of transition condition. It's a mechanism to trigger next action based on the result of the transition. If transition result is positive, FSM will trigger action from positive position. If transition result is negative, FSM will trigger action from negative position. If only one position will use chaining, set the other as 'false'.
    // ... transition conditions
    , [ 'ready', 'activate', 'active', 'switchOFF', [ false, 'generator' ] ]
    // Chaining will trigger 'generator' action on negative transition result and will do nothing on positive result.
    // ... transition conditions

Transitions are functions and they are provided to FSM as functional library - javascript object with functions.

const transitionLibrary = {
          switchOn  : function () {
                        // ...code 
                      }
        , switchOff : function () {
                        // ...code
                      }
    }

It's simple and clean way to represent system behaviour but practice shows that is not enough. This implementation was extended to cover some aditional program needs like:

  • Transition function could contain asynchronous code;
  • Chain-actions in transaction conditions(Optional);
  • Chain-actions are possible on positive and negative transition-end;
  • Export fsm state as an object: externalState;
  • Import externalState;
  • Prevent simultaneous updates;
  • Fsm-updates cache mechanism and execution in a row;

Implement Fsm

Install module by:

npm i @peter.naydenov/fsm

Add in your script:

import Fsm from '@peter.naydenov/fsm'
 // init new fsm:
 const fsm = new Fsm ( machine, lib )

... where machine is fsm-description and lib is a transition-library.

If your project is commonJS, use a dynamic import function or use version v.3.0.0 or older.

Fsm Description

Fsm description is an object that define fsm business logic. Every fsm description should contain init and behavior properties.:

const machine = {
        init : 'stopped'
      , behavior: [
                //    [  state   , action    , nextState, functionName, chainAction(optional) ]
                      [ 'stopped', 'activate', 'active', 'fnActivate' ] // transition condition
                    , [ 'active' , 'stop'    , 'stoped', 'fnStop'     ] 
                ]
      , stateData : { greeting: 'hi' }
      /** Parameter 'stateData' is not a required parameter. It's a data storage space.
       *  Storaged data is available in every transition function by using a function 'extractList'. 
       *  Only a predefined state-data-names can be saved in 'stateData'
       * /
      }

Property 'init' is the initial state of the system. Property 'behavior' describes how system reacts. Every row(transition condition) contains 5 elements:

  1. Current active state;
  2. Action;
  3. Next state;
  4. Transition function that should be applied;
  5. Chain action
[ 'stopped', 'activate', 'active', 'fnActivate' ]
// Read this transition condition like:
/**
 *  When state is 'stopped' and action 'activate' comes, transition 'fnActivate' will be executed. On success state will become 'active'. No chain-actions are defined.
 */

Fsm Transition Library

Object that will contain all transition functions:

 const lib = {
       fnActicate () { 
           // ... code to activate
       }
     , fnStop () {
          // ... code to stop
       }
 }

Fsm Transition Function

Fsm transition function is a function, member of transition library. Transition functions is kind of middleware. Every function receives this arguments:

const lib = {
 fnActivate ( {task, state, dependencies, extractList}, data ) {
      // function code is here...
      task.done ( end )   // end is a transitionResult {}
   }
}
  • task - askForPromise object; AskForPromise documentation. Represents asynchronous transition execution. Finish by providing to task object, the transition result object. task.done( transitionResult );
  • state - string. Current state of the FSM;
  • dependencies - object. Contain all external dependencies. Set fsm dependencies by fsm.setDependencies();
  • extractList - function. Extract data from FSM stateData;
  • data - any(Prefered: object). External data provided from fsm.update( action, data);

Execution of transition function will end on task.done(end) where end is an transitionResult object.

transitionResult

Only one of the params in this object is required:

 { 
     success : true   // Required. Boolean. Transition success;
   , response : {}    // Optional. Object. update answer response;
   , stateData : {}   // Optional. Object. 'stateData' if there is stateData changes; 
  }

FSM Methods

   setDependencies     : 'Insert all external dependencies'
 , getDependencies     : 'Returns all external dependencies'
 , update              : 'Trigger an action'
 , importState         : 'Import externalState object.'
 , exportState         : 'Export state and stateData as a single object (externalState)'
 , reset               : 'Revert state and stateData to initial values described during initialization'
 , ignoreCachedUpdates : 'Ignore all cached updates'
 , getState            : 'Returns actual state'
 , extractList         : 'Extract list of state segments or properties'

fsm.setDependencies ()

Set dependencies for FSM. Dependency object will be provided to every transition function. With dependency injection code will stay testable. Method could be called multiple times. All dependencies will be merged in one object.

 const 
      moment = require ( 'moment' )
    , fsm = new Fsm ()
    , deps = {
              scrollTo : window.scrollTo
            , moment 
        }
    ;

  fsm.setDependencies ( deps )     
  • deps: object. Object with external dependencies;
  • Method returns: void;

fsm.getDependencies ()

Returns all external dependencies.

 const 
      moment = require ( 'moment' )
    , fsm = new Fsm ()
    , deps = {
              scrollTo : window.scrollTo
            , moment 
        }
    ;

  fsm.setDependencies ( deps )     
  const allDeps = fsm.getDependencies ()     

fsm.update ()

Provide actions to FSM. If conditions 'state/action' exist in description table, FSM will react.

  • If action name does not exist in behavior table, FSM will ignore the action;
  • If action name exist in behavior table but current state is different, FSM will ignore the action;
  • If transition function is not provided, FSM will ignore the action;
 fsm.update ( action, altData )
    .then ( r => {
             // ...do something with the response
        })
  • action(required): string. The action name.
  • altData(optional): any. Additional data provided to the transition;
  • Method returns: Promise. Returned value is equal to transitionResult.response;

fsm.getState ()

Will return current current FSM state.

 let currentState = fsm.getState ()
  • Method returns: string. Current FSM state;

fsm.exportState ()

Export state and stateData as a single object (externalState). Export state to:

  • Keep fsm history record;
  • Possible point of recovery;
  • Synchronize fsm states accross the network in large distributed systems;
  • Debuging purposes;
   const externalState = fsm.exportState ();
   /**
    *  externalState = {
    *                     state : 'actualState'
    *                   , stateData : {
    *                                   ... all stateData flags are here
    *                                }
    *               }
    * */
  • Method returns: externalState object

fsm.importState ()

Put fsm in specific state by importing externalState object described in 'exportState()' method. Use method to:

  • Recover previous state of the fsm;

  • Testing specific situations by moving fsm directly in required state;

  • Synchronize fsm states accross the network;

  • externalState: externalState object

  • Method returns: void

fsm.reset ()

Returns initial values for state and stateData.

  fsm.reset ()
  • Method returns: void;

fsm.ignoreCachedUpdates ()

Ignore all cached updates.

const fsm = new Fsm ( description, transitions );
fsm.update ( 'start' ).then ( x =>  fsm.ignoreCachedUpdates () ) 
/** 
 *  Update with action 'start' will block all upcoming updates. Subsequent updates
 *  will wait by using cache mechanism.
 *  Method 'ignoreCachedUpdates' will remove updates from cache and will close their promises with 'reject' and
 *  error message.
 *  Use reject function to customize 'canceled updates' behaviour
 */
fsm.update ( 'move' )
    .then ( r => {   // resolve function
                result = fsm.exportState ()
                expect ( result.state ).to.be.equal ( 'initial' )
              }
            , r => {   // reject function
                //---> 'r' will contain error message produced by fsm.
                //  r == "Action 'move' was ignored"
            })
  • Method returns: void;

Example

Example represents controller for system that require electricity. On fsm.update('start') controller will try to activate standard electricity source. On fail will trigger chain-action generator and will try to activate alternative energy source. On success state will become altSrc(alternativeSource). When standart electricity source is available we can inform the system by calling fsm.update('electricity') and on success state will become active. Switch off the system any time by calling fsm.update('stop').

const 
        lib   = {  
                    switchON ( task, dependencies, stateData, dt ) {
                            task.done ({ success : false })
                        }
                    , altOn ( task ) {
                            task.done ({ success: true })
                        }
                    , switchOFF ( task ) {
                            task.done ({ success: true })
                        }
                    , primarySource ( task ) {
                            task.done ({ success: true })
                        }
            }
    , machine = {
                      init  :  'none'
                    , behavior : [
                                [ 'none'   , 'start'      , 'active' , 'switchON', [ false, 'generator']  ]
                              , [ 'none'   , 'generator'  , 'altSrc' , 'altOn'         ]
                              , [ 'active' , 'stop'       , 'none'   , 'switchOFF'     ]
                              , [ 'altSrc' , 'stop'       , 'none'   , 'switchOFF'     ]
                              , [ 'altSrc' , 'electricity' , 'active', 'primarySource' ]
                            ]
                    , stateData : {}
                }
    ;
const fsm = new Fsm ( machine, lib );
fsm.update ( 'activate' )
    .then ( () => {
                const r = fsm.getState ();
                expect ( r ).to.be.equal ( 'altSrc' )
            })

External Links

Credits

'@peter.naydenov/fsm' was created by Peter Naydenov.

License

'@peter.naydenov/fsm' is released under the MIT License.