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

node-state-machine

v0.1.4

Published

State machine for NodeJS.

Downloads

11

Readme

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