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

@joshdcuneo/tsm

v0.1.4

Published

TSM is a type first finite state machine library inspired by [xstate](https://github.com/davidkpiano/xstate) and [robot](https://github.com/matthewp/robot). The API aims to strike a balance between small bundle size, ergonomics and type safety.

Downloads

12

Readme

TSM

TSM is a type first finite state machine library inspired by xstate and robot. The API aims to strike a balance between small bundle size, ergonomics and type safety.

Why TSM?

  • If you want a robust, feature rich state machine use xstate
  • If you want a light-weight, ergonomic state machine and don't mind sacrificing a little type safety use robot
  • For something in between, maybe try TSM.

Example

type State = 'idle' | 'counting' | 'maxedOut';
type Event = { type: 'inc' } | { type: 'dec' } | { type: 'reset' };
type Context = number;

const {
  machine,
  state,
  transition,
  reducer,
  context,
  guard,
  immediate,
  action,
} = components<State, Event, Context>();

const inc = transition(
  'inc',
  'counting',
  reducer(ctx => ctx + 1)
);
const maxedOut = transition(
  'inc',
  'maxedOut',
  guard(ctx => ctx === 5)
);
const dec = transition(
  'dec',
  'counting',
  reducer(ctx => ctx - 1),
  guard(ctx => ctx > 0)
);
const reset = transition(
  'reset',
  'idle',
  reducer(() => 0)
);
const restart = immediate(
  'idle',
  reducer(() => 0),
  action(() => console.log('hit the limit!'))
);

const counterMachineConfig = machine(
  state('idle', inc),
  state('counting', maxedOut, inc, dec, reset),
  state('maxedOut', restart),
  context(0)
);

const counterMachine = interpret(counterMachineConfig)
counterMachine.subscribe((newMachine, event) => {
  console.log('Machine entered a new state')
  console.log(newMachine)
  console.log(event)
})

counterMachine.matches('idle') === true
counterMachine.send('inc')
counterMachine.matches('counting') === true
counterMachine.context() === 1

API

TSM exports two functions each returning an object of related functions.

components

The components function is a type helper. It accepts three type variables that define the possibles states, events and context of the machine. It returns the interface to build your state machine.


/**
 * A helper type that links all the components to the generic types of the machine
 */
export interface Components<S, E extends MachineEvent, C extends unknown> {
  /**
   * Create a machine
   * @param args the possible states and initial context
   * @returns the machine configuration
   */
  machine: (
    ...args: (State<S, E, C> | Invoke<S, E, C> | Context<C>)[]
  ) => Machine<S, E, C>;
  
  /**
   * Create a state
   * 
   * @param name of the state
   * @param args the possible transitions out of the state
   * @returns a state configuration
   */
  state: (
    name: S,
    ...args: (Transition<S, E, C> | Immediate<S, E, C>)[]
  ) => State<S, E, C>;

  /**
   * Create a state which awaits a promise and then triggers a transition
   * @param name of the state
   * @param fn returning a promise
   * @param args the possible transitions out of the state
   * @returns an invoke state configuration
   */
  invoke: (
    name: S,
    fn: Invokable<E, C>,
    ...args: Transition<S, E, C>[]
  ) => Invoke<S, E, C>;

  /**
   * Create the context of the machine
   * @param value of the context
   * @returns the initial context for the machine
   */
  context: (value: C) => Context<C>;

  /**
   * Create transition from one state to another, triggered by an event
   * @param name the name of the event which triggers the submission
   * @param state to transition to
   * @param args the actions, guards and reducers for the transition
   * @returns the transition configuration
   */
  transition: (
    name: E['type'],
    state: S,
    ...args: (Reducer<E, C> | Action<E, C> | Guard<C>)[]
  ) => Transition<S, E, C>;

  /**
   * Create transition from one state to another, triggered when the state is entered
   * @param state to transition to
   * @param args the actions, guards and reducers for the transition
   * @returns the immediate transition configuration
   */
  immediate: (
    state: S,
    ...args: (Reducer<E, C> | Action<E, C> | Guard<C>)[]
  ) => Immediate<S, E, C>;

  /**
   * Create a reducer that is called during a transition to update the context
   * @param fn to receive and update the context
   * @returns the reducer configuration
   */
  reducer: (fn: ReducerFn<E, C>) => Reducer<E, C>;

  /**
   * Create a guard that is called before a transition to see if it should occur
   * @param fn called with the context and returning true if the transition should proceed
   * @returns the guard configuration
   */
  guard: (fn: GuardFn<C>) => Guard<C>;

  /**
   * Create an action that is called during a transition to cause a side effect
   * @param fn called with the context to trigger a side effect on transition
   * @returns the action configuration
   */
  action: (fn: ActionFn<E, C>) => Action<E, C>;
}

interpret

The interpret function takes a machine and returns a service to allow interacting with the machine. Machines are immutable so as the service receives events it interprets them and creates a machine in the next state. Each time a new machine is created all the subscribers are notified of the new machine state and the event that triggered the change.

The service interface also exposes some helper functions to make working with the state and context of the machine easier.

/**
 * A service is returned when interpret is called with a machine configuration
 * The service receives the events, immutably updates the machines and notifies subscribers
 */
export interface Service<S, E extends MachineEvent, C> {
  /**
   * The current machine
   */
  machine: Machine<S, E, C>;

  /**
   * The current subscribers to the service
   */
  subscribers: SubscriberFn<S, E, C>[];

  /**
   * Dispatch events to the service to trigger machine transitions
   *
   * It would be nice to provide a version of this function that works like
   * redux dispatch or send in xstate and robot. However its not possible to make it type safe.
   *
   * @param event the event to trigger
   */
  send: (event: E) => void;

  /**
   * Subscribe to changes in the machine
   * @param subscriber a function called with the machine and event on changes
   */
  subscribe: (subscriber: SubscriberFn<S, E, C>) => void;

  /**
   * Check if the current state name matches a string or is in an array of strings
   * @param states
   * @returns a boolean to indicate if the state matches
   */
  matches: (states: S | S[]) => boolean;

  /**
   * Get the context of the machine if it has one
   * @returns the context
   */
  context: () => C | undefined;
}