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 🙏

© 2025 – Pkg Stats / Ryan Hefner

micro-fsm

v0.2.5

Published

A tiny finite state machine library

Downloads

26

Readme

micro-fsm

A minimalistic yet powerful Finite State Machine (FSM) library for JavaScript/TypeScript projects. Only 227 bytes.

A Finite State Machine (FSM) is a computational model that represents a system as a set of distinct states and the transitions between those states. The system can only be in one state at a time, and it transitions from one state to another based on specific inputs or events. FSMs are useful for modeling systems with well-defined behaviors, such as user interfaces, communication protocols, or game logic. They help in designing, implementing, and reasoning about the behavior of such systems.

Features

  • Type-safe transitions: Leverages TypeScript's advanced type system to infer valid states and transitions, enabling compile-time checks.
  • Minimalistic API: Provides a simple and intuitive interface for defining and interacting with state machines.
  • Customizable: Allows for custom logic execution during state transitions through optional functions.
  • Lightweight: Minimal footprint, only 227 bytes minimized. No dependencies.

Installation

npm install micro-fsm

Example

import fsm from "micro-fsm";

const machine = fsm("stopped", {
  start: { from: ["stopped", "paused"], to: "started" },
  stop: { from: ["started", "paused"], to: "stopped" },
  pause: { from: ["started"], to: "paused" },
  resume: { from: ["paused"], to: "started" },
  log: {
    fn: (message: string) => console.log(`[${machine.current}] ${message}`),
  },
});

// The type of `machine` is automatically inferred by TypeScript. The following
// assertion shows the inferred type.
machine satisfies {
  current: "stopped" | "paused" | "started";
  prev: "stopped" | "paused" | "started";
  can(eventName: "start" | "stop" | "pause" | "resume" | "log"): boolean;

  start(): void;
  stop(): void;
  pause(): void;
  resume(): void;
  log(message: string): void;
};

// Starting from 'stopped'
machine.start(); // State changes to 'started'

// Pausing from 'started'
machine.pause(); // State changes to 'paused'

// Resuming from 'paused'
machine.resume(); // State changes to 'started'

// Stopping from 'started'
machine.stop(); // State changes to 'stopped'

// Logging state from 'stopped'
machine.log("Hello, world"); // Logs '[stopped] Hello, world'

Usage

The machine

  • machine.current: This property holds the current state of the FSM.
  • machine.prev: This property stores the previous state before the last transition.
  • machine.can(eventName): This method checks if a given event can be executed from the current state. It returns true if the transition is allowed according to the FSM's configuration, and false otherwise.
  • machine.<transition>(): Perform the transition. Throws if not allowed. If the event's config has a custom function fn, that function is called as part of the transition.

Setting up the FSM

The FSM is initialized with fsm(initialState, config), where initialState is the starting state of the machine, and config is an object defining the states, transitions, and optional actions.

Each key in the config object represents an event name, and its value is an object specifying the conditions for the transition.

  • from: An array listing the states from which the transition can occur. It's optional; if omitted, the transition can happen from any state.

  • to: Specifies the target state of the transition. It's also optional; omitting it means the event doesn't change the state but can still execute logic via fn.

  • fn: A function that executes when the event occurs. This allows for custom logic during transitions, such as logging, validation, or triggering side effects. Like from and to, it's optional.

Advanced state transitions

The FSM allows for defining transitions with custom logic through the fn property. This can be leveraged for various purposes such as validation, logging, or triggering side effects during transitions.

Example: Validation during transition

Imagine you have a state machine for a simple workflow system with states like draft, review, and published. You might want to validate certain conditions before transitioning from review to published.

Only synchronous checks are supported.

import fsm from "micro-fsm";

const workflowMachine = fsm("draft", {
  submit: { from: ["draft"], to: "review" },
  publish: {
    from: ["review"],
    to: "published",
    fn: () => {
      // Custom validation logic here
      if (!someValidationCondition()) {
        throw new Error("Cannot publish due to validation failure.");
      }
    },
  },
});

workflowMachine.submit(); // Moves from 'draft' to 'review'
try {
  workflowMachine.publish(); // Attempts to move from 'review' to 'published'
} catch (error) {
  console.error(error.message); // Handles validation failure
}

API

The library exports a single function that creates a Finite State Machine.

export default function fsm<
  TState extends string,
  TConfig extends Config<TState, Function>,
>(initialState: TState, config: TConfig): FiniteStateMachine<TState, TConfig>;

interface Config<TState extends string> {
  [eventName: string]: EventConfig<TState>;
}

interface EventConfig<TState extends string> {
  from?: readonly TState[];
  to?: TState;
  fn?: Function;
}

type FiniteStateMachine<
  TState extends string,
  TConfig extends Config<TState, Function>,
> = FiniteStateMachineBase<TState, keyof TConfig>;

interface FiniteStateMachineBase<
  TState extends string,
  TEventName extends string,
> {
  readonly current: TState;
  readonly prev: TState;
  can(eventName: TEventName): boolean;
}

type GetEventsFromConfig<T extends Config<string>> = {
  [K in keyof T]: T[K] extends { fn: infer F } ? F : () => void;
};

Note: The actual TypeScript types differ from the API above, in order to infer all types, in order to not have to specify the complex type parameters.