A TypeScript-first statechart runtime and state-management platform. Framework-agnostic, serializable, and deterministic — built for production apps and tooling alike.
| Package | Description |
|---|---|
@stategraph/core |
Pure runtime: machines, actors, guards, actions, effects |
@stategraph/react |
React 18+ adapter (useActor, useSelector, useSend) |
@stategraph/dom |
Headless DOM adapter (mountActor, bindEvent, onSnapshot) |
@stategraph/inspect |
Trace recording, serialization, devtools bridge, and replay |
@stategraph/testing |
Test-plan generation, effect mocks, adapter conformance suite |
@stategraph/model-check |
Static structural analysis for machine IR |
| Package | Description |
|---|---|
@stategraph/react |
Vue 3 adapter |
@stategraph/angular |
Angular adapter |
@stategraph/solid |
SolidJS adapter |
@stategraph/svelte |
Svelte adapter |
@stategraph/scxml |
SCXML import/export |
@stategraph/migrate-xstate |
XState → StateGraph codemod tooling |
import { assign, fromPromise, setup, createActor } from "@stategraph/core";
const machine = setup({
guards: {
isValid: ({ context }) => context.value.trim().length > 0,
},
actions: {
setValue: assign(({ event }) => ({ value: event.value })),
},
effects: {
submit: fromPromise(({ input }) => fetch(input.url, { method: "POST" })),
},
}).createMachine({
id: "form",
initial: "idle",
context: { value: "" },
states: {
idle: {
on: {
CHANGE: { actions: ["setValue"] },
SUBMIT: { target: "submitting", guard: "isValid" },
},
},
submitting: {
invoke: { src: "submit", input: ({ context }) => ({ url: "/api" }), onDone: "success" },
},
success: { type: "final" },
},
});
const actor = createActor(machine);
actor.start();
actor.send({ type: "CHANGE", value: "hello" });
actor.send({ type: "SUBMIT" });
console.log(actor.getSnapshot().value); // "submitting"| Path | Description | Dev command |
|---|---|---|
apps/playground |
Live demos: counter, toggle, 3-second timer | pnpm dev:playground |
apps/docs |
Single-page reference docs for all 10 guide topics | pnpm dev:docs |
examples/react-modal |
React modal with closed ↔ open lifecycle machine |
pnpm dev:modal |
examples/react-form |
React form with guarded submit, async effect, error/retry | pnpm dev:form |
examples/dom-player |
Parallel playback + volume machine with DOM event binding | pnpm dev:player |
- Run-to-completion: one external event fully resolves (all internal events drained) before the next starts.
- Determinism: same machine + same event sequence + same effect results = same snapshot sequence, always.
- Immutability: context is never mutated in place.
assign()produces a new partial object. - Serializable definition: guards, actions, and effects are registered as string references — the machine definition is pure JSON-serializable data.
- Effects scheduled after commit: side effects are dispatched after the transition step completes, never inline.
pnpm build # Build all packages
pnpm test # Run all tests
pnpm lint # Lint all packages
pnpm check-types # Type-check all packages
pnpm dev # Start all dev servers (packages + apps)
pnpm dev:playground # Start playground app only
pnpm dev:docs # Start docs app only
pnpm dev:modal # Start react-modal example only
pnpm dev:form # Start react-form example only
pnpm dev:player # Start dom-player example onlyapps/
playground/ Vite app — interactive machine demos
docs/ Vite app — reference documentation
devtools/ Vite app — devtools UI (stub)
packages/
core/ Runtime (zero dependencies)
react/ React 18+ adapter
dom/ Headless DOM adapter
inspect/ Trace + devtools
testing/ Test utilities
model-check/ Static analysis
config-eslint/ Shared ESLint config
config-typescript/ Shared TypeScript configs
config-vitest/ Shared Vitest config
examples/
react-modal/ Modal lifecycle example
react-form/ Form with async effect example
dom-player/ Parallel state DOM player example
docs/
decisions/ Architecture Decision Records (ADR-001 – ADR-009)
Nine ADRs govern all non-trivial design choices. See docs/decisions/ for the full index.
Key decisions:
- ADR-001 —
setup()+.createMachine()dual-call pattern; no executable functions in the definition object. - ADR-004 — Snapshot shape with
value,configuration,context,children,pendingEffects. - ADR-007 — Lock-step
0.x.yversioning; only 6 packages published in MVP. - ADR-008 — Dual ESM + CJS output via tsup for all published packages.
- All packages: TypeScript strict mode (
strict,noUncheckedIndexedAccess,exactOptionalPropertyTypes). @stategraph/coremust have zero framework dependencies, zero browser globals, zero test-framework imports.- Import only from package barrels (
src/index.ts). No deep imports between packages. - Adapter packages may only depend on
@stategraph/coreand their framework peer dependency. - Run
pnpm lint && pnpm check-types && pnpm testbefore opening a PR.