‚̧Nanobots Producing Megastructures
    Wondering what‚Äôs next for npm?Check out our public roadmap! ¬Ľ

    node-state-machine

    0.1.4¬†‚Äʬ†Public¬†‚Äʬ†Published

    Powerful finite state machine module that is very easy to use. From basic finite state machines to acceptance, locks and middleware. Install the module with npm.

    $ npm install --save node-state-machine
    

    Basics

    import { Machine } from 'node-state-machine';
     
    // We will start with a basic example. Lets create a machine
    // with three states: 'A', 'B' and 'C'. The first state will
    // always be the initial state. The machine will take care of
    // duplicate states itself. So new Machine('A', 'B', 'C', 'B')
    // only has three states.
    let machine = new Machine('A','B','C');
     
    // Now lets make some simple transitions. For example, this first
    // one says "if we process 'b' and are in state 'A', go to state
    // 'B'". The last one uses the '*' wildcard. It is called when
    // there is no other transition found.
    machine.from('A').to('B').on('b');
    machine.from('B').to('C').on('c');
    machine.from('C').to('C').on('a');
    machine.from('C').to('A').on('*');
     
    // Finalize the machine. From now on we can't add transitions
    // anymore and we can start using the machine!
    machine.finalize();
     
    machine.current(); // 'A'
    machine.process('b'); // machine.current() == 'B';
    machine.process('c'); // machine.current() == 'C';
    machine.process('d'); // machine.current() == 'A';
    machine.current(); // 'A'

    Transitions

    import { Machine } from 'node-state-machine';
     
    // We will create the exact same machine as the example before.
    let machine = new Machine('A', 'B', 'C');
     
    // With the same transitions...
    machine.from('A').to('B').on('b');
    machine.from('B').to('C').on('c');
     
    // However, we can define more complex triggers for certain
    // transitions if needed. Note that we define the same trigger
    // here as the .on('a') like in the example above! However, we can
    // write whatever we want in this lambda function.
    machine.from('C').to('C').when(v => v == a);
     
    // We can use .default() instead of .on('*'). Use whichever you like!
    machine.from('C').to('A').default();
     
    // Finalize the machine. From now on we can't add transitions
    // and can start using the machine!
    machine.finalize();
     
    machine.current(); // 'A'
    machine.process('b'); // machine.current() == 'B';
    machine.process('c'); // machine.current() == 'C';
    machine.process('d'); // machine.current() == 'A';
    machine.current(); // 'A'

    Callbacks

    // Again, we will use the same machine.
    import { Machine } from 'node-state-machine';
    let machine = new Machine('A', 'B', 'C');
    machine.from('A').to('B').on('b');
    machine.from('B').to('C').on('c');
    machine.from('C').to('C').on('a');
    machine.from('C').to('A').on('*');
    machine.finalize();
     
    // But now, we use the callback function of the machine!
    // Note that we can define (or change) it after the machine
    // is finalized! This is because it doesn't change anything
    // about the internal state of the machine.
    machine.onChange((oldState, newState, trigger) => {
      console.log(`Went from ${oldState} to ${newState} via ${trigger}!`);
    });
     
    // We can also chain the process functions.
    machine.process('b').process('c').process('d');
     
    // The logs are now as follows:
    // Went from A to B via b!
    // Went from B to C via c!
    // Went from C to A via d!

    Acceptance

    // Again, we will use the same machine.
    import { Machine } from 'node-state-machine';
    let machine = new Machine('A', 'B', 'C');
    machine.from('A').to('B').on('b');
    machine.from('B').to('C').on('c');
    machine.from('C').to('C').on('a');
    machine.from('C').to('A').on('*');
     
    // But now we define accepting states! These are states
    // that define whether or not the input is accepted.
    machine.accepts('A', 'C');
     
    machine.finalize();
    machine.process('b').process('c').process('d');
     
    // Now accept is true because the current state belongs
    // to one of the accepting states!
    let accept = machine.accepted();

    Locks & Async

    // Same code as above...
    machine.finalize();
     
    // When the machine is locked, it won't change its internal
    // state! This can be used when waiting for async functions.
    // After these transitions, the current state is 'A'.
    machine.process('b');
    machine.process('c');
    machine.lock();
    machine.process('d');
    machine.unlock();
    machine.process('b')

    Middlewares

    // Again, we will use the same machine.
    import { Machine } from 'node-state-machine';
    let machine = new Machine('A', 'B', 'C');
     
    // The use of middlewares is very useful in some cases. A middleware
    // is a function that handles the trigger of a transition. For
    // example, every time we go from 'B' to 'C', we would like to add
    // the trigger value to a global value.
    let output = '';
    let printMiddleware = (v) => { output += v; }
     
    // We create the same machine as above, and append the middleware to
    // the transition from 'B' to 'C'.
    machine.from('A').to('B').on('b');
    machine.from('B').to('C').middleware(printMiddleware).on('c');
    machine.from('C').to('C').on('a');
    machine.from('C').to('A').on('*');
     
    machine.finalize();
    machine.process('b');
    machine.process('c');
    machine.process('d');
    machine.process('b');
    machine.process('c');
     
    // Now output == 'cc' !
    // Note: You can also use multiple middlewares: .middlewares(m1, m2, m3).on('a');

    Extra

    // A little script that shows some extra functions that you can use.
     
    // Process all the values at once. Returns true or false on whether or not the
    // machine accepts the input. This function does the same as this code snippet:
    // machine.process('a').process('b').process('c');
    // let accept = machine.accepted();
    let accept = machine.bulkProcess(['a', 'b', 'c']);
     
    // Sets all default transitions to the same state. ex A > A.
    machine.setDefaults();
     
    // Check if the created machine is deterministic for the given alphabet.
    machine.isDeterministic(['a', 'b']);
     
    // Checking the finalized state.
    machine.finalized(); // false;
    machine.finalize();
    machine.finalized(); // true;
     
    // Checking the locked state.
    machine.lock();
    machine.locked(); // true;
    machine.unlock();
    machine.locked(); // false;
     
    // Returns the current state of the machine.
    machine.current();
    // Returns the initial state of the machine.
    machine.initial();
    // Returns the number of states of the machine.
    machine.numberOfStates();
     
    // Resets the machine to its initial state.
    machine.reset();

    Development

    Feel free to contribute some cool features. The following ideas will also be implemented in the future, but feel free to do it yourself if you want to. I will also add eslint and add all the development processes to gulp. To contribute, clone the repo and run npm install. Make sure gulp is installed globally.

    $ npm install         # Install dependencies
    $ npm install -g gulp # Make sure you have gulp
    $ gulp                # This will run flow & babel
    $ npm test            # To run the tests
    
    
    // Future Code Features
     
    // Return a new machine which is the minimal DFA of this machine. This
    // depends on isDeterministic(alphabet: Array<string>).
    machine.toMinimal(alphabet: Array<string>);
     
    // Build a machine based on a regex.
    machine.fromRegex(regex: string);

    License

    MIT - Jensen Bernard

    Install

    npm i node-state-machine

    DownloadsWeekly Downloads

    1

    Version

    0.1.4

    License

    MIT

    Last publish

    Collaborators

    • avatar