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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@escapace/fsm

v5.0.0

Published

Type-safe finite state machine library for TypeScript.

Readme

@escapace/fsm

Type-safe finite state machine library for TypeScript.

Features

  • Type-safe state machine definition and execution
  • Conditional transitions with predicates
  • Context management with reducers
  • State change subscriptions
  • High-frequency transition performance (7-17x faster than @xstate/fsm)

Installation

pnpm add @escapace/fsm

Example

import { stateMachine, interpret } from '@escapace/fsm'

// Define coin values and state types
type Coin = 5 | 10 | 25 | 50
enum State {
  Locked = 'LOCKED',
  Unlocked = 'UNLOCKED',
}
enum Action {
  Coin = 'COIN',
  Push = 'PUSH',
}

// Create a turnstile that requires 50 cents to unlock
const machine = stateMachine()
  .state(State.Locked) // Define possible states
  .state(State.Unlocked)
  .initial(State.Locked) // Set starting state
  .action<Action.Coin, { coin: Coin }>(Action.Coin) // Define action with payload type
  .action(Action.Push) // Define action without payload
  .context<{ total: number }>({ total: 0 }) // Set context type and initial value
  .transition(
    State.Locked, // From locked state
    [
      Action.Coin,
      (
        context,
        action, // On coin insert, with predicate
      ) => context.total + action.payload.coin >= 50,
    ],
    State.Unlocked, // Go to unlocked state
    (context, action) => {
      // Run this reducer
      context.total += action.payload.coin
      return context
    },
  )
  .transition(
    State.Locked, // Fallback transition when not enough coins
    Action.Coin,
    State.Locked,
    (context, action) => {
      // Add coin to total
      context.total += action.payload.coin
      return context
    },
  )
  .transition(State.Unlocked, Action.Coin, State.Unlocked) // Stay unlocked on coin insert
  .transition(
    [State.Locked, State.Unlocked], // Push always locks
    Action.Push,
    State.Locked,
    (context) => {
      // Reset total on push
      context.total = 0
      return context
    },
  )

// Create and use the state machine
const turnstile = interpret(machine)

console.log(turnstile.state) // 'LOCKED'

turnstile.do(Action.Coin, { coin: 25 }) // Insert 25 cents
console.log(turnstile.state) // 'LOCKED'

turnstile.do(Action.Coin, { coin: 25 }) // Insert another 25 cents (total 50)
console.log(turnstile.state) // 'UNLOCKED'

turnstile.do(Action.Push) // Push through turnstile
console.log(turnstile.state) // 'LOCKED'

Performance

Benchmark results from 1,000,000 state transitions show escapace-fsm runs 2-2.5x slower than a hand-coded state machine using basic JavaScript constructs—variables, conditionals, and direct property access without library abstractions, type checking, or validation. This slowdown represents the cost of state machine abstraction layer. Relative to @xstate/fsm, escapace-fsm processes transitions 7x faster at median, 9x faster at p95, and 17x faster at p99. The overhead becomes measurable only in tight loops processing millions of transitions. For typical application usage—handling user interactions, coordinating async operations, managing UI state—the overhead is negligible.

API

stateMachine()

Creates a new state machine builder.

⚠️ Important: The builder pattern uses mutation for performance optimization. Each method call modifies the internal state directly rather than creating new objects. This means the builder is not immutable.

Methods

  • .state(name) - Define a state
  • .initial(state) - Set initial state
  • .action<Type, Payload>(name) - Define an action type
  • .context<Type>(initialValue) - Set context type and initial value
  • .transition(source, action, target, reducer?) - Define state transition

interpret(machine)

Creates an executable state machine instance.

Properties

  • .state - Current state (readonly)
  • .context - Current context (readonly)

Methods

  • .do(action, payload?) - Dispatch an action, returns boolean indicating success
  • .subscribe(callback) - Subscribe to state changes

Action Dispatch Return Values

The .do() method returns a boolean that indicates whether the action successfully triggered a state transition:

Returns true when:

  • A valid transition exists for the current state + action combination
  • All transition predicates (if any) evaluate to true
  • The state transition executes successfully

Returns false when:

  • No transition is defined for the current state + action combination
  • All transition predicates fail (return false)

This return value enables precise control flow based on whether state changes actually occurred.

const machine = stateMachine()
  .state('idle')
  .state('working')
  .initial('idle')
  .action('start')
  .action('stop')
  .transition('idle', 'start', 'working')
// Note: no 'stop' transition from 'idle'

const service = interpret(machine)

const started = service.do('start') // true - transition succeeds
console.log(service.state) // 'working'

const stopped = service.do('stop') // false - no transition defined
console.log(service.state) // still 'working'