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

@inventbuild/supamachine

v0.4.1

Published

Deterministic auth state machine for Supabase apps

Readme

Supamachine

A deterministic authentication state machine for Supabase web and mobile apps.

This is an early-stage library designed to make authentication easier and way less error-prone, especially as your app grows in complexity.

Overview

Does this kind of auth code look familiar to you?

const {
  data: { subscription },
} = supabase.auth.onAuthStateChange((event, session) => {
  if (event === "INITIAL_SESSION" || event === "TOKEN_REFRESHED") {
    setLoading(false);
  }
  if (session) {
    setSession(session);
  } else {
    setSession(null);
  }
  ...and so much more of this
});

THERE'S GOT TO BE A BETTER WAY!

Supamachine models auth as an explicit state machine with clear states (CHECKING_SESSION, AUTHENTICATING, SIGNED_OUT, CONTEXT_LOADING, INITIALIZING, AUTH_READY, plus error states) and allows you to derive custom app states via mapState.

Real-World Benefits

When I moved a client project from my original AuthContext to Supamachine, Supamachine turned ~300 lines of lifecycle orchestration (session management, auth state changes, post-login flow, navigation decisions) into ~50 lines of configuration (loadContext, initializeApp, mapState).

Usage

pnpm add @inventbuild/supamachine

Basic setup

import {
  SupamachineProvider,
  useSupamachine,
  AuthStateStatus,
} from "@inventbuild/supamachine";

type MyContext = { userData: { name: string } };
type MyAppState = { status: "MAIN_APP"; session: Session; context: MyContext };

return (
  <SupamachineProvider<MyContext, MyAppState>
    supabase={supabase}
    loadContext={async (session) => {
      const { data } = await supabase
        .from("profiles")
        .select("*")
        .eq("id", session.user.id)
        .single();
      return { userData: data };
    }}
    mapState={(snapshot) => ({
      status: "MAIN_APP",
      session: snapshot.session,
      context: snapshot.context!,
    })}
  >
    <App />
  </SupamachineProvider>
);

function App() {
  const { state, updateContext } = useSupamachine<MyContext, MyAppState>();

  switch (state.status) {
    case AuthStateStatus.CHECKING_SESSION:
    case AuthStateStatus.CONTEXT_LOADING:
      return <Loading />;
    case AuthStateStatus.SIGNED_OUT:
      return <Login />;
    case "MAIN_APP":
      return <Home session={state.session} />;
    default:
      return <Loading />;
  }
}

Provider API

  • supabase (required) – Supabase client instance
  • loadContext(session) – Optional. Fetches app context (e.g. user profile) after auth
  • initializeApp({ session, context }) – Optional. Side effects after context is loaded (e.g. set avatar)
  • mapState(snapshot) – Optional. Maps the internal AUTH_READY state to your custom app states
  • actions – Optional. Auth actions (signIn, signOut, etc.) to expose via useSupamachine(). Merged with a default signOut so you always have actions.signOut() available.
  • options – Optional. logLevel, getSessionTimeoutMs, loadContextTimeoutMs, initializeAppTimeoutMs, authenticatingTimeoutMs

actions

This is an optional convenience for your imperative Supabase auth methods, like signInWith... and signOut, etc. Pass your auth actions to the provider; they're exposed via useSupamachine().actions. Since Supamachine responds to Supabase events, you don't need to use updateContext. A default signOut is included since it's simple (but can be overriden). Example usage:

<SupamachineProvider
  supabase={supabase}
  actions={{
    signOut: () => supabase.auth.signOut(),
    signInWithOtp: (email) => supabase.auth.signInWithOtp({ email }),
    signInWithGoogle: () => { /* platform-specific */ },
  }}
>
const { state, actions } = useSupamachine();
actions.signOut();
actions.signInWithOtp("[email protected]");

If you omit actions, you still get actions.signOut() from the default.

updateContext

Use updateContext to imperatively update context and trigger a re-run of mapState:

const { updateContext } = useSupamachine();
updateContext((current) => ({
  ...current,
  userData: { ...current.userData, onboardingComplete: true },
}));

refreshContext

refreshContext(session) re-runs loadContext with a new session, updates context and session in place, re-runs mapState, and emits—without leaving AUTH_READY. The adapter uses it automatically for USER_UPDATED so metadata changes (e.g. from updateUser) don't trigger a full reload. Exposed via useSupamachine() if you need to call it manually.

beginAuth / cancelAuth

beginAuth() moves the machine into the AUTHENTICATING state for flows you control manually (for example, showing an up-front OAuth chooser or interstitial before Supabase kicks in). cancelAuth() lets you abandon a long-running or failed auth attempt and return to a safe state. Both are exposed via useSupamachine() and are mainly useful for advanced flows; most apps can rely on Supabase auth events alone.

Philosophy

Handling auth in your app is all about states. Supamachine explicitly defines every possible state (CHECKING_SESSION, SIGNED_OUT, CONTEXT_LOADING, INITIALIZING, AUTH_READY, plus error states) and lets you extend with custom states via mapState. By capturing all states and transitions, edge cases are handled deterministically.

More resources

  • examples/react-simple.tsx – minimal setup using loadContext and core states
  • examples/react-custom.tsx – custom app states derived via mapState
  • examples/react-complex.tsx – subscription-gated flow using initializeApp, updateContext, and actions
  • STATE_MACHINE.md – auto-generated state machine diagram and transition table
  • changelog.md – release notes for each published version