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

@fordi-org/noodly

v0.1.0

Published

A Functional State Machine (FSM) to act as a store for reactive applications.

Readme

Noodly

Noodly is a Functional State Machine (FSM), meant to be used as a store for reactive apps.

Demo...

API

Noodly

Signature: Noodly(initialState:Object, children:Object):NoodlyInstance

This is the main function. It creates a state machine from an initial state:

import Noodly from 'noodly';

const myMachine = Noodly({
  loadState: 'new',
  data: undefined
});

initialState is an object representing your machine's initial state. children is a map of child states, e.g.,

const bill = Noodly({ loading: false });
const account = Noodly({ loading: false });
const store = Noodly({}, { bill, account });

Your NoodlyInstance exposes the following functions:

action

Signature:

instance.action(actor:Actor):Action

An Actor is either an async generator function, or an object with a single key, which is an async generator function. This latter form is provided because it is more readable.

The following are equivalent:

export const doTheThing = action(async function *doTheThing() {
  // ...
});

Or:

export const doTheThing = action({
  async *doTheThing(curState) {
    // ...
  }
});

Actions/Actors should be named like verbs. A yield from an action will update the state destructively (the new state will not contain the old state). Actors are called with no context, and arguments passed to the Action are passed into the Actor. Any errors thrown during execution of the Actor are passed into the error field of the state, and execution is halted. For more information about an Actor's lifecycle, see listen.

Example:

const bill = Noodly({
  loadStates: {},
  // Not actually necessary, but I find "knowing at the top what
  // metadata exists" is nice.
  promises: {},
  data: {},
});

export const fetchBill = action({
  async *fetchBill(accountNumber) {
    // Another call has already initiated the fetch.
    const last = bill.getState();
    if (last.promises[accountNumber]) return;
    const promise = fetch(`/api/bill/current/${accountNumber}`).then(r => r.json());
    const next = {
      loadStates: {
        ...last.loadStates,
        [accountNumber]: true,
      },
      promises: {
        ...last.promises,
        [accountNumber]: promise,
      },
      data: last.data,
    };
    yield next;
    const data = await next.promise;
    yield { 
      loadStates: {
        ...next.loadStates,
        [accountNumber]: false,
      },
      promises: next.promises,
      data: {
        ...next.data,
        [accountNumber]: data,
      }
    };
  }
});

listen

Signatures:

instance.listen(signal:String, listener:SignalHandler):Unlistener
instance.listen(listener:SignalHandler):Unlistener

signal can be:

  • 'start' - called before the Actor is called.
  • 'step' - called after each yield in the Actor.
  • 'complete' - called when the Actor exits.
  • 'error' - called when the Actor throws an error.

listener is an SignalHandler, a function with the signature:

listener(newState:Object, oldState:Object, type:String, path:Array<String>):void

  • newState is the state after the action / step has completed.
  • olsState is the state as it was before the action / step was run.
  • type is the event type ('step' or 'complete')
  • path is the path from the root store to the current state machine, split by dots.

An Unlistener is a function that can be called to stop listening for signals. Its signature is:

unlisten():void

For reactive apps, it's unlikely you'll be using listen directly, as the built-in select function handles all the necessary maintenance under the hood.

Example:

bill.listen(state => {
  if (!state.loading && state.data && !state.error) {
    updateComponentState({ bill: state.data });
  }
});

log

Signature:

instance.log(enabled:Boolean):Boolean

Enable or disable logging for this Noodly instance.

setTools

Signature:

instance.setTools(tools:Tools):void

Set the reactive tools ({ useState, useEffect } with the normal signatures) for the Noodly instance's selectors. If using Noodly reactively, this must be called before using any selectors.

Example:

const bill = Noodly({
  loading: false,
  // Not actually necessary, but I find "knowing at the top what
  // metadata exists" is nice.
  promise: undefined,
  data: undefined,
});

bill.setTools({ useState, useEffect });

select

Note: Please call setTools({ useState, useEffect }) before using any of the generated selectors.

Signature:

select(selector:SelectorFunction):HookFunction

Generates a reactive hook function that selects into the store.

Example:

export const useBalanceDue = bill.select(
  ({ data: { balanceDue } = { } }) => balanceDue
);

// ... 

export const Balance = () => {
  const balanceDue = useBalanceDue();
  return (
    <div className="balance">
      {formatAsCurrency(balanceDue)}
    </div>
  );
};

getState

Signature:

getState():Object

Gets the current state of the Noodly instance.

machine

machine is a public-safe subset of the Noodly instance; it contains only getState and listen. The consumer is responsible for exposing actions.

useError

The only built-in selector. It retrieves the error field.