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

wide-fsm

v0.1.2

Published

Finite State Machine using tagged discriminated unions for state/events.

Downloads

164

Readme

wide-fsm

docs

WideFSM is a strongly typed implementation of a finite state machine that uses discriminated unions for state and events. What this means is you can pass arbitrary data in objects to your FSM and have it store more than just a state tag.

:warning: Because WideFSM uses functions to transition from one state to another, and because if statements exist, it has the potential to get pretty hairy if you have lots of conditionals between transitions.

Turnstile

Here's an example of the classic turnstile finite state machine. Except this turnstile is smart, it allows you to insert more than one coin and tracks its own balance, something that wouldn't be possible with a regular finite state machine.

Example

// The turnstile can either be locked or unlocked
type TurnstileState = {
  tag: 'locked',
} | {
  tag: 'unlocked',
  balance: number,
}
// You can interact with the turnstile by inserting a coin
// or by walking through it.
type TurnstileEvents = {
  tag: 'insert_coins',
  numberOfCoinsInserted: number,
} | {
  tag: 'walk_through',
}

// This defines all of the transitions of the finite state machine.
const transitions = {
  locked: {
    insert_coins: (prev, event) => ({ tag: 'unlocked', balance: event.numberOfCoinsInserted }),
  },
  unlocked: {
    insert_coins: (prev, event) => ({ tag: 'unlocked', balance: prev.balance + event.numberOfCoinsInserted }),
    walk_through: (prev, event) => {
      const nextBalance = prev.balance - 1
      if (nextBalance === 0) return { tag: 'locked'}
      else return { tag: 'unlocked', balance: nextBalance }
    }
  }
} satisifes TransitionLookup<TurnstileState, TurnstileEvents>;

function main() {
  const turnstileFsm = new Fsm<TurnstileState, TurnstileEvents, typeof transitions>({ tag: 'locked' }, transitions)

  // Oh a turnstile.  What happens if I try to walk through it?
  let canIWalkThrough = turnstileFsm.canDispatch('walk_through')
  console.log(canIWalkThrough) // false

  // I must need to insert some coins.
  turnstileFsm.dispatch({ tag: 'insert_coins', numberOfCoinsInserted: 2 })

  // Lets try again... Looks like it is open now.
  canIWalkThrough = turnstileFsm.canDispatch('walk_through')
  console.log(canIWalkThrough) // true

  // I will walk through, and so will my friend...
  turnstileFsm.dispatch({ tag: 'walk_through' })
  turnstileFsm.dispatch({ tag: 'walk_through' })

  try {
    turnstileFsm.dispatch({ tag: 'walk_through' })
  } catch (error) {
    console.log('Ouch! It stopped moving.  The balance must be empty.')
  }
} 

About

I wrote this to help create input controls with many states (i.e. click, drag, long press, click and drag when key is pressed) where it ended up being really practical if I could store some extra data along with my state (i.e. when the mouse is dragging I want to know the point where it started dragging as well as its current position).

The Fsm class is generic, not only over the state/events that you pass in, but also over the TransitionLookup object. This means that transitions handlers and that the onTransition(...) callbacks will have their arguments typed.

The down-side is that it's a little verbose, having to seperate the transitions into their own object that satisifes TransitionLookup<States, Events>.

Contributing

Install dependencies, make your change, run npm run test and then npm run docs. Let me know if you can find a way to avoid having to define the transitions argument seperately :)

Acknowledgements / Similar

Thanks typescript-fsm for another great FSM implementation in typescript.