Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fn-machine

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fn-machine - npm Package Compare versions

Comparing version 0.0.10 to 0.0.11

3

package.json
{
"name": "fn-machine",
"version": "0.0.10",
"version": "0.0.11",
"description": "a tiny functional state machine",
"main": "index",
"module":"index",
"directories": {

@@ -7,0 +8,0 @@ "example": "example"

@@ -1,8 +0,74 @@

### fn-machine
# fn-machine
A tiny, functional, state machine utility
#### install
`npm install fn-machine`
### install
`npm install --save fn-machine`
#### usage
see [example](https://github.com/jrobinson01/fn-machine/blob/master/example/index.html)
### usage
fn-machine consists of 3 functions. The first two are used to define a machine:
`machine([State], 'initialState', initialContextObj, stateChangeCallback, loggerFn)`
`state('name', transitionsObj, enterFunction, exitFunction)`
The third function is what would traditionally be called a `send()` function. This function is returned whenever `machine(...)` is called.
#### Setting up a machine
```javascript
// import the setup functions
import {machine, state} from 'fn-machine';
// initial context object
const initialContext = {
loading: false,
users: []
}
function loadUsers() {
// simulate a network request
setTimeout(() => {
// once the request completes, we can call `myMachine` (the 'send' function).
myMachine('loaded', {users:['foo', 'bar']})
}, 1000);
}
// initialize a machine
const myMachine = machine([
state('initial', {
// each method on this object represents a transition for this particular state.
loadData: (detail, context) => {
// a transition method should return the new state, as well as the optional context.
// here we return {state:'loadingData'} to signify we want the state to now be 'loadingData', and
// that the context.loading property should be true.
return {
state:'loadingData',
context: {...context, ...{loading: true}}
}
}
}),
state('loadingData', {
loaded: (detail, context) => {
return {
state: 'loadedData',
context: {...context, ...detail, ...{loading: false}}
}
}
}, context => {// call loadUsers when this state is entered
loadUsers();
}),
state('loadedData', {}) // 'loaded' is an empty state. There are no transitions.
], 'initial', initialContext, newState => {
console.log('myMachine state changed:', newState.state, newState.context);
}, console.log);// pass an optional logger function
```
As you can see in the `loadUsers()` function above, we invoke the third function provided by fn-machine, which is the send function. The send function takes a string as the first parameter, which is the name of a transition we'd like to invoke, and optionally a `detail` object, which might contain some data we want the machine to work with.
#### More examples
There is an [example](https://github.com/jrobinson01/fn-machine/blob/master/example/index.html) in this repo, or you can play around with this [codepen](https://codepen.io/johnrobinson/pen/rNBPodV?editors=1001) that shows a basic integration with [LitElement](https://github.com/Polymer/lit-element).
#### Contributing
Yes! PR's are welcome. Tests are written in mocha. Run with `npm run test` or `yarn test`. Typechecking is provided by typescript via JSDoc annotations.

@@ -10,5 +10,6 @@ /** @typedef {import('./fn-state').CurrentState} CurrentState */

* @param {function(CurrentState)=} changeCb
* @param {function(any)=} loggerFn
* @return {function(string, Object=):CurrentState?}
*/
export default function machine(states, initialState, initialContext, changeCb = function(state){}) {
export default function machine(states, initialState, initialContext, changeCb = function(){}, loggerFn = function(){}) {
// store current state (name) and context

@@ -20,4 +21,6 @@ let current = initialState;

return function send(event, detail) {
loggerFn(`sent '${event}'`);
// if no event, return the current state
if (!event) {
loggerFn(`no event. returning currentState`);
return currentState;

@@ -35,5 +38,10 @@ }

if (transition) {
const newState = states.find(s => s.name === next.state);
if (!newState) {
// throw if next state is undefined
// throw new Error(`the transition '${event}' of current state '${current}', returned a non-existant desired state '${next.state}'.`);
throw `the transition '${event}' of current state '${current}', returned a non-existant desired state '${next.state}'.`;
}
// if the current state has an exit function, run it.
active.exit && active.exit();
const newState = states.find(s => s.name === next.state);
// if the new state has an enter function, run it as well.

@@ -46,3 +54,6 @@ newState.enter && newState.enter(next.context || context);

// call callback with the latest state.
loggerFn(`state changed to '${next.state}'`);
changeCb(next);
} else {
loggerFn(`state '${current}' does not handle event '${event}'.`);
}

@@ -52,2 +63,3 @@

}
loggerFn('could not find active state');
return currentState;

@@ -54,0 +66,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc