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

effect-machine

v0.27.0

Published

Type-safe state machines for [Effect](https://effect.website).

Readme

effect-machine

Type-safe state machines for Effect.

Effect Machine gives one actor a schema-first state model, a typed event mailbox, scoped Effect work, typed input and output, supervision, persistence hooks, inspection, and framework-neutral Atom integration.

Use it when a feature has several valid states, invalid transitions, state-owned async work, timeouts, cancellation, actor coordination, or UI views that need precise subscriptions.

Install

bun add effect-machine effect

effect is a required peer dependency.

Imports

import { Event, Machine, State } from "effect-machine";
import * as ActorAtom from "effect-machine/atom";
import { EntityMachine, toEntity } from "effect-machine/cluster";

Use effect-machine for local machines and actors. Use effect-machine/atom for React, Solid, or another Effect Atom binding. Use effect-machine/cluster for distributed entity machines.

First machine

States and events are Effect schemas.

import { Effect, Schema } from "effect";
import { Event, Machine, State } from "effect-machine";

const DownloadState = State({
  Idle: {},
  Downloading: { url: Schema.String },
  Done: { url: Schema.String, bytes: Schema.Finite },
  Failed: { url: Schema.String, message: Schema.String },
});

const DownloadEvent = Event({
  Start: { url: Schema.String },
  Completed: { bytes: Schema.Finite },
  Failed: { message: Schema.String },
});

const downloadMachine = Machine.make({
  state: DownloadState,
  event: DownloadEvent,
  initial: DownloadState.Idle,
})
  .on(DownloadState.Idle, DownloadEvent.Start, ({ event }) =>
    DownloadState.Downloading({ url: event.url }),
  )
  .on(DownloadState.Downloading, DownloadEvent.Completed, ({ state, event }) =>
    DownloadState.Done.with(state, { bytes: event.bytes }),
  )
  .on(DownloadState.Downloading, DownloadEvent.Failed, ({ state, event }) =>
    DownloadState.Failed.with(state, { message: event.message }),
  )
  .final(DownloadState.Done, ({ state }) => state.bytes)
  .final(DownloadState.Failed, () => 0);

const program = Effect.scoped(
  Machine.scoped(
    Effect.gen(function* () {
      const actor = yield* Machine.spawn(downloadMachine);
      yield* actor.start;
      yield* actor.send(DownloadEvent.Start({ url: "/report.pdf" }));
      yield* actor.send(DownloadEvent.Completed({ bytes: 1024 }));
      return yield* actor.awaitOutput;
    }),
  ),
);

An empty variant is a value such as DownloadState.Idle. A non-empty variant is a constructor such as DownloadState.Downloading({ url }).

State.with(source, fields) copies matching fields into the target variant. It prevents manual context spreading across different states.

Effect is the composition layer

Effect Machine does not add an action queue or a second context system.

| Work | API | | ------------------------------------- | -------------------------------------------- | | Unconditional state change | .on, .reenter, or .immediate | | Conditional state change | .when, .reenterWhen, or .immediateWhen | | Work that produces a completion event | .task | | State-owned stream or resource | .spawn | | Actor-owned stream or resource | .background | | Autonomous machine sequence | Machine.run with Effect.flatMap | | Interactive multi-phase flow | Parent machine with child actors | | Lazy child requested by consumers | ActorHost in a parent state scope |

Effect requirements remain in R. A machine cannot start until the application provides every required service. Effectful transition handlers must have never in their error channel. Convert expected failures to states or events.

Machine-lifetime backgrounds can read self.state and self.latestTransition. These are the actor-owned subscription refs. They stay stable across supervision generations. Use SubscriptionRef.get or SubscriptionRef.changes for Effect workflows. Use self.client for synchronous host callbacks that need send, getSnapshot, or subscribe.

Read the Effect model and async work ownership.

Guards and stable state

Register ordered candidates for one state and event. The first passing guard wins. An unguarded candidate is the fallback.

machine
  .when(
    State.Checking,
    Event.Continue,
    function hasStock({ state }) {
      return state.stock > 0;
    },
    () => State.Accepted,
  )
  .on(State.Checking, Event.Continue, () => State.Rejected)
  .immediate(State.Accepted, ({ state }) => State.Ready.with(state));

The predicate can return a Boolean or Effect<boolean, never, R>. Its requirements flow into the machine type. actor.can(event) evaluates the same predicate with the actor's captured context. ActorAtom.can(actor, event) exposes the result to React and Solid. The inspector uses the predicate function name.

Immediate transitions run until the state is stable. Subscribers see only the stable state. The runtime stops an accidental eventless loop after 100 edges.

Effect services and tasks

class Api extends Context.Service<
  Api,
  { readonly load: (id: string) => Effect.Effect<Data, ApiError> }
>()("app/Api") {}

machine.task(State.Loading, ({ state }) => Effect.flatMap(Api, (api) => api.load(state.id)), {
  name: "load-data",
  onSuccess: (data) => Event.Loaded({ data }),
  onFailure: (error) => Event.LoadFailed({ message: String(error) }),
});

const actor = yield * Machine.spawn(machine).pipe(Effect.provide(ApiLive));
yield * actor.start;

The actor captures the Effect context during allocation. It keeps those services when it starts later.

onFailure receives the typed Effect error. A defect does not enter onFailure. It stops the actor or starts supervision.

Input, output, and composition

const checkoutMachine = Machine.make({
  state: CheckoutState,
  event: CheckoutEvent,
  initial: (input: CheckoutInput) => CheckoutState.Reviewing(input),
}).final(CheckoutState.Done, ({ state }) => ({ receiptId: state.receiptId }));

const program = Machine.run(cartMachine).pipe(
  Effect.flatMap((cart) => Machine.run(checkoutMachine, { input: cart })),
);

Machine.run starts one actor, waits for output, and always stops it. Interruption releases actor resources. The final actor state remains available when you use Machine.spawn and retain the actor reference.

Use a parent machine when a UI must route between phases, keep shared values, or support back navigation. Read Actors and systems.

ActorRef

| Member | Use | | --------------------------- | -------------------------------------------------- | | start | Start a direct actor | | send(event) | Queue an event | | call(event) | Process an event and return transition information | | ask(event) | Return a typed reply from an Event.reply event | | waitFor(state) | Wait for a state constructor or predicate | | sendAndWait(event, state) | Send and wait for a state | | snapshot | Read the current state as an Effect | | awaitFinal | Wait for the final state | | awaitOutput | Wait for typed output | | awaitExit | Wait for Final, Stopped, or Defect | | drain | Process queued events and stop | | subscribe | Observe state with a host callback | | client | Use the actor outside Effect | | system | Access named actors | | children | Read direct child actors |

Machine.spawn returns an unstarted actor. system.spawn starts the actor.

Use actor.client in a JavaScript callback or application that does not run inside Effect. client.can(event) returns a Promise and supports Effect predicates. client.canSync(event) supports Boolean predicates only. React and Solid should use Actor Atoms.

Atom, React, and Solid

import * as ActorAtom from "effect-machine/atom";

const stateAtom = ActorAtom.make(actor);
const countAtom = ActorAtom.select(stateAtom, (state) => state.count);

The selected Atom stays writable. Writes send machine events. A selector publishes only when its selected value changes.

The React example uses useAtomSuspense and Motion. The Solid example uses useAtomResource, Suspense, and solid-transition-group. Both include performance tests. Both retain exit-animation data in the terminal machine state.

Read Atom and UI integration and browse all examples.

Persistence, supervision, and inspection

  • Recovery resolves state during actor startup.
  • Durability saves committed transitions.
  • Supervision restarts defects within an Effect Schedule budget.
  • Inspection reports events, named transition operations, transitions, named guards, tasks, Effects, errors, stops, and actor generations.
  • Typed inspect spawn options observe one actor.
  • actor.system.inspect lets a late tool observe all actors in one system.

Read Persistence and supervision and Inspection.

Testing

Use simulate or createTestHarness for transition paths. Spawn a real actor for tasks, services, resources, persistence, supervision, inspection, and actor topology.

const result = yield * simulate(machine, events, { input });
yield * assertPath(machine, events, ["Idle", "Loading", "Done"]);
yield * assertNeverReaches(machine, events, "Failed");

Read Testing.

XState migration

The migration guide covers context, assign, actions, invoked promise and callback actors, root routers, actor registries, selectors, inspection, persistence, and exit animation values. Its patterns come from a large XState kiosk application.

Cluster entities

Use effect-machine/cluster to expose a machine through Effect Cluster. It supports typed send, ask, state reads, state watches, input adapters, snapshot persistence, and journal persistence.

Read Cluster entities.

Examples

The examples directory is a Bun workspace.

bun run examples:gate
bun run example:react
bun run example:solid

The example matrix links every pattern to executable code.

Documentation

License

MIT