Effect-native, schema-first, completely type-safe state machines and statecharts, inspired by XState.
The goal of
effect-machineis to become a core effect module.It originates from the following PR.
import { Machine } from "@typeonce/effect-machine"
import { Effect, Schema } from "effect"
const States = Machine.states({
Locked: {},
Unlocked: {}
})
const Events = Machine.events(
Schema.TaggedUnion({
Coin: {},
Push: {}
})
)
const Turnstile = Machine.make({
id: "Turnstile",
states: States.states,
events: Events,
initial: (to) => to.Locked()
}).handle({
Locked: {
on: { Coin: (to) => to.full.Unlocked() }
},
Unlocked: {
on: { Push: (to) => to.full.Locked() }
}
})
const program = Effect.gen(function*() {
const ref = yield* Machine.start(Turnstile)
yield* ref.send(Events.Coin())
})State and event schemas define the protocol. The handler tree defines the statechart, and the result runs as an Effect-managed machine.
The workspace publishes three packages at the same version:
@typeonce/effect-machinecontains the machine runtime, testing modules, and documentation.@typeonce/effect-machine-devtoolscontains the publishable local machine visualizer and CLI.@typeonce/oxlint-plugin-effect-machinechecks Effect Machine models for common structural mistakes.
Install matching versions so the runtime, devtools, and lint rules stay aligned.
XState is the project's main inspiration and reference for statechart semantics and API coverage.
Direct API comparisons exposed gaps in Effect Machine, and benchmarks against XState drove many runtime performance improvements. effect-machine is not a direct XState replacement.