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

@mjstahl/stated

v6.2.0

Published

Simply put state management for your JavaScript application

Downloads

34

Readme

Build Status

Stated

Simply put state management for your JavaScript application. Pushdown automata (FSM with history) in less than 1KB.

Installation

$ npm install --save @mjstahl/stated
// as a factory function
const stated = require('@mjstahl/stated')

// as a class expression
const { Stated } = require('@mjstahl/stated')

API

stated(states: Object[, persistent: Boolean]) -> Stated

new Stated(states: Object[, persistent: Boolean]) -> Stated

To create an instance of Stated pass a 'states' object. A valid states object must have an, at least, a single state. initial states can be set after instantiation by assign a valid state to the initial property.

By default each Stated object is not persistent. If you wish to store each state change pass 'true' as the second argument to the constructor.

const h20 = stated({
  initial: 'water',
  water: {
    // actions must reference existing states
    FROZEN: 'ice',
    BOILED: 'steam',
    // Number, Array, Function, Object, etc
    value: '60F'
  },
  ice: {
    BOILED: 'steam',
    WARMED: 'water',
    value: '32F'
  },
  steam: {
    COOLED: 'water',
    FROZEN: 'ice',
    value: {
      temp: '212F'
    }
  }
})

h20.state //-> 'water'
h20.value //-> '60F'

<stated>.to(action: String[, updateValue: Any]) -> Stated

Transition from the current state to a new state. If called with a second argument. The value of the new state will be updated with the value. If value is an Object, the current value and updateValue will be merged. If the updateValue is not an Object, value will be replaced with updateValue.

  h20.to(h20.actions.FROZEN)

  h20.state //-> 'ice'
  h20.value //-> '32F'

  h20.to(h20.actions.BOILED, { state: 'gas' })

  h20.value //-> { state: 'gas', temp: '212F' }

<stated>.reset() -> Stated

Set the Stated object's state to initial state.

  h20.reset()

  h20.state //-> 'water'
  h20.value //-> '60F'

<stated>.actions -> Object

Return an object with actions as properties and associated values. Provided to avoid typos when traversing states. For example:

h20.to(h20.actions.BOILED)
h20.actions

//-> { 'FROZEN': 'FROZEN', 'COOLED': 'COOLED' }

actions only includes actions related to the current state. So attempting to call to with an invalid action will cause a runtime error as that action does not exist on <stated>.actions.

For a state that does not have any actions all the states of the Stated object will available from actions.

<stated>.state -> String

Return the name of the Stated's current state.

  h20.state

  //-> 'steam'

<stated>.value -> Any

Returns the value of the current state if one exists; returns undefined if not.

  h20.to(h20.actions.WARMED)
  h20.value

  //-> '60F'

<stated>.via -> Object

Return an object with actions as properties and the state they point to as values.

h20.to(h20.actions.FROZEN)
h20.via

//-> { BOILED: 'steam', WARMED: 'water' }

Guards

Each state may have a canEnter and canLeave function that is executed when attempting to leave one state and enter another. Each function takes one argument, the Stated object, for use during its execution.

When a transition is occurring (via the on method), if there is a canLeave function on the current state, it is executed. If the canLeave function returns true and the future state has a canEnter function, the canEnter function is executed. If the canEnter function of the future state returns true, the Stated object is transitioned successfully.

If either the canLeave or canEnter functions exist and return false, the transition does not occur.

.canLeave(self: Stated) -> Boolean

.canEnter(self: Stated) -> Boolean

Transition Functions

Each state can have an onLeave and onEnter function that is executed when leaving one state, and having entered another. This are useful for performing side-effects.

.onLeave(self: Stated) -> Void

Executed when a transition is to occur but before the current state has been replaced with the next state.

const h20 = stated({
  initial: 'water',
  water: {
    FROZEN: 'ice',
    value: '60F'
    onLeave: () => console.log('Winter is Coming!!!')
  },
  ice: {
    WARMED: 'water',
    value: '32F'
  },
})

h20.to(h20.actions.FROZEN)

//-> 'Winter is Coming!!!'

.onEnter(self: Stated) -> Void

Executed after the state and values have been updated, history has recorded (if persistent), and 'transition' event has been emitted.

const h20 = stated({
  initial: 'water',
  water: {
    FROZEN: 'ice',
    value: '60F'
    onLeave: () => console.log('Winter is Coming!!!')
  },
  ice: {
    WARMED: 'water',
    value: '32F'
    onEnter: () => console.log('Winter is Here!!!')
  },
})

h20.to(h20.actions.FROZEN)

//-> 'Winter is Coming!!!'
//-> 'Winter is Here!!!'

Events

Each stated object is also an EventEmitter. When the Stated object transitions from one state to another all callbacks passed to the onTransition function are evaluated with the Stated object passed the only argument to the callback. onTransition returns a function that unsubscribes when executed.

<stated>.onTransition(callback: Function) -> unsubscribe: Function

  const unbind = h20.onTransition(({ state, value }) => {
    console.log(state) //-> 'water'
    console.log(value) //-> '60F'
  })
  h20.reset()

  // when you are finished listening
  unbind()

Persistence

Each Stated object is can be persistent and store each state change, allowing the user to undo and redo states. Use can turn persistence on and off with the persistent property. You can make Stated object persistent by default by passing true as the second argument to the constructor.

<stated>.persistent -> Boolean

new Stated({... states, true})

Toggle whether the Stated object will store each state change.

h20.persistant //-> false, Stated objects are not persistent by default

// To turn on persistence, simply set 'persistent' to 'true'
h20.persistent = true

<stated>.undo() -> Stated

Return to a previous state. If the current state is the initial state and undo is called, the Stated object will be returned without performing a state transition (in the initial state).

  h20.to(h20.actions.FROZEN)
  h20.state //-> 'ice'

  h20.undo()
  h20.state //-> 'water'

<stated>.redo() -> Stated

Re-apply a later state. If the current state is the top of the stack of states and redo is called, the Stated object will be returned without performing a state transition (the current state will remain equal to the state at the top of the stack).

  h20.to(h20.actions.FROZEN)
  h20.state //-> 'ice'

  h20.undo()
  h20.state //-> 'water'

  h20.redo()
  h20.state //-> 'ice'