Package Exports
- @germankuber/xstate-builders
Readme
๐ฏ XState Builders
A complete fluent builder pattern library for creating XState v5+ state machines with full TypeScript support. Build complex state machines using an intuitive, type-safe API that makes XState more approachable and maintainable.
โจ Features
- ๐๏ธ Fluent Builder Pattern: Intuitive, chainable API for constructing state machines
- ๐ฏ Full TypeScript Support: Complete type safety with intelligent autocompletion
- ๐ท๏ธ Advanced State Features: Tags, meta, descriptions, outputs, and more
- โฐ Delayed Transitions: Built-in support for time-based transitions
- ๐ Always Transitions: Automatic state transitions with guards
- ๐ฆ Multi-Format Builds: CommonJS, ES Modules, and UMD distributions
- ๐งช Comprehensive Tests: 400+ tests with 79% branch coverage
- ๐ Live Examples: Interactive React examples included
๐ Quick Start
Installation
npm install @xstate/builders xstateBasic Usage
import {
GenericMachineBuilder,
GenericStateBuilder,
GenericStatesBuilder,
GenericActionsBuilder
} from '@xstate/builders';
// Create actions
const actions = GenericActionsBuilder.create()
.withAssignAction('startLoading', { isLoading: true })
.withAssignAction('completeLoading', { isLoading: false, result: 'success' })
.build();
// Create states with advanced features
const idleState = GenericStateBuilder.create()
.withTag('ready')
.withDescription('Waiting for user action')
.withTransition('START', 'loading', ['startLoading'])
.build();
const loadingState = GenericStateBuilder.create()
.withTags('loading', 'visible')
.withAfter(
GenericDelayedTransitionsBuilder.create()
.afterWithActions(2000, ['completeLoading'], 'success')
.build()
)
.build();
const successState = GenericStateBuilder.create()
.asFinalStateWithOutput({ status: 'completed', timestamp: Date.now() })
.withTag('success')
.build();
// Build the complete machine
const machine = GenericMachineBuilder.create()
.withId('dataProcessor')
.withInitial('idle')
.withContext({ isLoading: false, result: null })
.withStates(
GenericStatesBuilder.create()
.withState('idle', idleState)
.withState('loading', loadingState)
.withState('success', successState)
.build()
)
.withActions(actions)
.build();React Integration
import { useMachine } from '@xstate/react';
function MyComponent() {
const [state, send] = useMachine(machine);
return (
<div>
<p>Current state: {state.value}</p>
<p>Tags: {Array.from(state.tags).join(', ')}</p>
{state.context.isLoading && <div>Loading...</div>}
<button
onClick={() => send({ type: 'START' })}
disabled={!state.can({ type: 'START' })}
>
Start Process
</button>
</div>
);
}๐ Advanced Features
State Tags & Meta
const state = GenericStateBuilder.create()
.withTags('loading', 'visible', 'critical')
.withMeta(
GenericMetaBuilder.create()
.withComponent('LoadingSpinner')
.withTimeout(5000)
.build()
)
.withDescription('Shows loading indicator while processing')
.build();Delayed Transitions
const state = GenericStateBuilder.create()
.withAfter(
GenericDelayedTransitionsBuilder.create()
.after(3000, 'timeout') // Simple timeout
.afterWithActions(1000, ['showWarning'], 'warning') // With actions
.afterWithGuard(5000, 'isStillWaiting', 'giveUp') // With guard
.build()
)
.build();Final States with Output
const finalState = GenericStateBuilder.create()
.asFinalStateWithOutput(
GenericOutputBuilder.create()
.withStatus('completed')
.withResult('Processing finished successfully')
.withTimestamp()
.build()
)
.build();Always Transitions
const state = GenericStateBuilder.create()
.withAlways('processing', 'hasDataReady') // Conditional auto-transition
.build();๐ ๏ธ Development
Development Scripts
# Development with hot reload
npm run dev # Build library in watch mode
npm run example:dev # Start example app (port 3001)
# Building
npm run build # Build library (CJS, ESM, UMD)
npm run example:build # Build example app
# Testing
npm test # Run tests in watch mode
npm run test:ci # Run tests with coverage
npm run type-check # TypeScript type checkingProject Structure
@xstate/builders/
โโโ src/
โ โโโ lib/ # Library entry point
โ โ โโโ index.ts
โ โโโ xstate-builders/ # Builder implementations
โ โ โโโ MachineBuilder.ts
โ โ โโโ StateBuilder.ts
โ โ โโโ ActionsBuilder.ts
โ โ โโโ ...
โ โโโ __tests__/ # Test files
โโโ examples/ # Example React app
โ โโโ src/
โ โ โโโ App.tsx # Live examples
โ โโโ package.json # References parent via file:..
โโโ dist/ # Built library files
โ โโโ index.js # CommonJS
โ โโโ index.esm.js # ES Modules
โ โโโ index.umd.js # UMD
โ โโโ index.d.ts # TypeScript definitions
โโโ package.json # Main package configurationBuilder Classes Available
| Builder | Purpose | Key Methods |
|---|---|---|
GenericMachineBuilder |
Complete state machines | withId(), withStates(), withActions() |
GenericStateBuilder |
Individual states | withTag(), withMeta(), withAfter(), withAlways() |
GenericStatesBuilder |
State collections | withState(), build() |
GenericActionsBuilder |
Actions | withAssignAction(), withSendAction(), withSpawnChildAction() |
GenericDelayedTransitionsBuilder |
Time-based transitions | after(), afterWithActions(), afterWithGuard() |
GenericOutputBuilder |
Final state outputs | withStatus(), withResult(), withTimestamp() |
GenericMetaBuilder |
State metadata | withComponent(), withTimeout(), withLevel() |
๐ฆ Multi-Format Distribution
The library is distributed in multiple formats for maximum compatibility:
- CommonJS (
dist/index.js): For Node.js and older bundlers - ES Modules (
dist/index.esm.js): For modern bundlers and browsers - UMD (
dist/index.umd.js): For direct browser usage - TypeScript (
dist/index.d.ts): Complete type definitions
๐ฏ Why Use XState Builders?
Before (Raw XState):
const machine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
tags: ['off'],
meta: { color: 'red' },
on: {
TOGGLE: {
target: 'active',
actions: ['setActive']
}
}
},
active: {
tags: ['on'],
meta: { color: 'green' },
after: {
5000: 'inactive'
},
on: {
TOGGLE: {
target: 'inactive',
actions: ['setInactive']
}
}
}
}
});After (XState Builders):
const machine = GenericMachineBuilder.create()
.withId('toggle')
.withInitial('inactive')
.withStates(
GenericStatesBuilder.create()
.withState('inactive',
GenericStateBuilder.create()
.withTag('off')
.withMeta({ color: 'red' })
.withTransition('TOGGLE', 'active', ['setActive'])
.build()
)
.withState('active',
GenericStateBuilder.create()
.withTag('on')
.withMeta({ color: 'green' })
.withAfter(
GenericDelayedTransitionsBuilder.create()
.after(5000, 'inactive')
.build()
)
.withTransition('TOGGLE', 'inactive', ['setInactive'])
.build()
)
.build()
)
.build();Benefits:
- ๐ง More Readable: Self-documenting, fluent API
- ๐ฏ Type Safe: Catch errors at compile time
- ๐ฆ Reusable: Share builder configurations
- ๐งฉ Composable: Mix and match builders
- ๐ก๏ธ Reliable: Comprehensive test coverage
๐ Test Coverage
The library maintains high test coverage across all builders:
Test Suites: 16 passed
Tests: 425 passed
Coverage: 79% branches, 55% statements๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
๐ License
MIT License - see the LICENSE file for details.
๐ Links
Built with โค๏ธ for the XState community