@hyperfrontend/state-machineView on npm →Architecture →

@hyperfrontend/state-machine

Lightweight, functional state management library with Redux-inspired actions/reducers, async operation orchestration, and lifecycle-aware component abstractions for predictable application state.

What is @hyperfrontend/state-machine?

@hyperfrontend/state-machine is a composable state management framework that combines Redux-style action/reducer patterns with specialized abstractions for asynchronous operations and component lifecycles. It provides a minimal Store implementation with subscribe/dispatch APIs, a pre-built reducer for process state (start, pause, cancel, success, fail), and higher-level abstractions like AsyncOperation for automatic state transitions and LifecycleAwareComponent for initialization/activation workflows.

The library emphasizes explicit state modeling through a core State interface (inProgress, success, fail, halt) with derived state computation (notStarted, done, paused, cancelled, retrying, restarting). The event system dispatches actions through the store and notifies listeners when derived states activate, enabling decoupled state observation. Modular exports (/actions, /reducer, /store, /async-operation, /lifecycle-aware-component) allow selective imports for tree-shaking and custom composition.

Key Features

  • Redux-inspired architecture - Central store with immutable state, action dispatching, and pure reducers
  • Pre-built process reducer - Ready-to-use reducer for START/PAUSE/CANCEL/SUCCESS/FAIL action types
  • Derived state computation - Automatic computation of notStarted, done, paused, cancelled, retrying, restarting from core state
  • Event-driven notifications - Observer pattern with event handlers triggered when specific derived states activate
  • Async operation wrapper - AsyncOperation class that automatically dispatches actions for promise lifecycles
  • Coordinated async processes - CoordinatedAsyncProcess for managing multiple async operations with startAll/cancelAll/pauseAll
  • Lifecycle-aware components - Abstract class with initializing/ready/starting/stopping/active state tracking and callbacks
  • Modular exports - Tree-shakeable secondary entry points for actions, reducers, selectors, events, and async operations
  • Zero external dependencies - Self-contained implementation with no third-party runtime dependencies

Architecture Highlights

The library uses a functional core with imperative shell pattern. The rootReducer is a pure function mapping (state, action) → new state using a handler lookup table. The Store class wraps the reducer with subscription management using a Set<Listener> for efficient add/remove operations. Derived state computation happens through selector functions that transform core state into boolean flags, with the Events class comparing previous/current derived states to trigger event handlers only when specific flags activate. The LifecycleAwareComponent uses protected setter methods (setInitializing, setReady, etc.) that invoke callback stacks only when state actually changes, preventing duplicate notifications. All state updates are immutable using object spread ({ ...state, inProgress: true }).

For a detailed technical deep dive, see ARCHITECTURE.md.

Why Use @hyperfrontend/state-machine?

Predictable Async Operation State Without Boilerplate

Managing async operation states (loading, success, error) typically requires manual flag updates, error handling, and state synchronization across components. The AsyncOperation class wraps any async function and automatically dispatches start/success/fail actions, updating store state without repetitive try-catch blocks or flag management. Event handlers trigger on state transitions (notStarted→inProgress, inProgress→done) rather than requiring manual status checks. For data fetching, form submissions, or long-running computations, this eliminates state management bugs where flags aren't reset or errors aren't caught, reducing async-related defects by centralizing state logic.

Lifecycle Management for Initialization-Heavy Components

Services, connections, or components with initialization → ready → active lifecycles require tracking multiple boolean flags and coordinating callbacks. The LifecycleAwareComponent abstract class provides a standardized pattern with five lifecycle states (initializing, ready, starting, stopping, active) and automatic callback invocation when states change. The callback stack pattern (callStack) ensures handlers execute in registration order and prevents duplicate notifications when state doesn't actually change. For WebSocket connections, database pools, or audio/video managers with multi-stage initialization, this eliminates race conditions and state inconsistencies that occur with ad-hoc flag management.

Coordinated Multi-Process Orchestration

Applications with parallel async operations (bulk uploads, multi-step wizards, resource preloading) need orchestration for starting all processes, cancelling in-flight operations, or pausing work. CoordinatedAsyncProcess manages multiple AsyncOperation instances with startAll(), cancelAll(), and pauseAll() methods that operate on all registered processes atomically. This is critical for batch operations where partial completion is unacceptable (transaction-like behavior) or resource management where all operations must pause on throttling events. Trading platforms with multiple real-time data streams or ETL pipelines with parallel extract/transform jobs use this pattern to prevent resource exhaustion and ensure consistent operational state.

Lightweight Alternative to Full Redux Stack

Redux provides powerful state management but introduces significant complexity (action creators, middleware, selector libraries, DevTools integration) for small to medium applications. This library distills Redux's core concepts (store, actions, reducers, subscriptions) into ~200 lines of code with a focused API for process state management. The pre-built reducer handles 90% of async operation state without custom reducers or action types. For applications that need predictable state patterns but don't require Redux's ecosystem (time-travel debugging, middleware composition, community plugins), this provides sufficient structure with minimal learning curve and bundle size.

Type-Safe State Management with Explicit Contracts

Dynamic state management systems (global objects, event buses) suffer from unclear contracts and runtime errors from missing or misshapen state. This library enforces explicit State and Action interfaces with TypeScript, making state shape and allowed actions visible at development time. The Handlers type maps action types to handler functions with specific action payloads, preventing action type typos. Derived state computation through typed selectors (StateDeriver, StateStatusDeriver) ensures consistent state interpretation. For teams maintaining large codebases or onboarding new developers, type-enforced contracts prevent entire classes of state management bugs that plague loosely-typed systems.

Installation

npm install @hyperfrontend/state-machine

Quick Start

Basic Store Usage

import { Store } from '@hyperfrontend/state-machine/store'
import { start, success, fail } from '@hyperfrontend/state-machine/actions'

// Create store
const store = new Store()

// Subscribe to state changes
const unsubscribe = store.subscribe((state, action) => {
  console.log('State changed:', state, 'Action:', action.type)
})

// Dispatch actions
store.dispatch(start())
console.log(store.getState()) // { inProgress: true, success: false, fail: false, halt: false }

store.dispatch(success())
console.log(store.getState()) // { inProgress: false, success: true, fail: false, halt: false }

// Cleanup
unsubscribe()

Async Operation with Automatic State Management

import { AsyncOperation } from '@hyperfrontend/state-machine/async-operation'

// Define async process
const fetchUserData = async () => {
  const response = await fetch('/api/user')
  if (!response.ok) throw new Error('Failed to fetch')
  return response.json()
}

// Wrap with AsyncOperation
const operation = new AsyncOperation(fetchUserData)

// Listen to lifecycle events
operation.on('inProgress', (event, current, previous) => {
  console.log('Fetching user data...')
})

operation.on('successful', (event, current, previous) => {
  console.log('User data fetched successfully!')
})

operation.on('failed', (event, current, previous) => {
  console.error('Failed to fetch user data')
})

// Start operation (automatically dispatches start/success/fail)
await operation.start()

Lifecycle-Aware Component

import { LifecycleAwareComponent } from '@hyperfrontend/state-machine/lifecycle-aware-component'

class DatabaseConnection extends LifecycleAwareComponent {
  private connection: any = null

  protected init = async () => {
    this.setInitializing(true)
    this.connection = await createDatabaseConnection()
    this.setInitializing(false)
    this.setReady(true)
    return 'success'
  }

  public start = async () => {
    if (!this.ready) await this.init()
    this.setStarting(true)
    await this.connection.connect()
    this.setStarting(false)
    this.setActive(true)
    return 'success'
  }

  public stop = async () => {
    this.setStopping(true)
    await this.connection.disconnect()
    this.setStopping(false)
    this.setActive(false)
    return 'success'
  }
}

// Usage
const db = new DatabaseConnection()

db.onReadyStatusChange((ready) => {
  console.log(ready ? 'Database ready' : 'Database not ready')
})

db.onActiveStatusChange((active) => {
  console.log(active ? 'Database connected' : 'Database disconnected')
})

await db.start() // Triggers init → ready → starting → active
await db.stop() // Triggers stopping → inactive

Coordinated Async Operations

import { CoordinatedAsyncProcess } from '@hyperfrontend/state-machine/coordinated-async-operation'

const coordinator = new CoordinatedAsyncProcess()

coordinator
  .registerProcess(async () => {
    await loadImages()
  })
  .registerProcess(async () => {
    await loadStyles()
  })
  .registerProcess(async () => {
    await loadScripts()
  })

// Start all processes in parallel
await coordinator.startAll()

// Or cancel all if needed
coordinator.cancelAll()

API Overview

Core Modules

Store Management:

  • Store - Central state container with dispatch/subscribe/getState
  • rootReducer - Pre-built reducer for process state (START/PAUSE/CANCEL/SUCCESS/FAIL)

Actions:

  • start(...args) - Dispatch START action
  • pause(...args) - Dispatch PAUSE action
  • cancel(...args) - Dispatch CANCEL action
  • success(...args) - Dispatch SUCCESS action
  • fail(error) - Dispatch FAIL action

State Types:

  • State - Core state shape: { inProgress, success, fail, halt }
  • DerivedState - Computed state: { notStarted, inProgress, done, successful, failed, retrying, restarting, paused, cancelled }
  • Action - Base action type with type property
  • Event - Event names for derived state transitions

Async Operations:

  • AsyncOperation - Wraps async functions with automatic action dispatching
  • CoordinatedAsyncProcess - Manages multiple async operations
  • AsyncProcess - Type for async functions: (...args: any[]) => Promise<void>

Lifecycle Components:

  • LifecycleAwareComponent - Abstract class with lifecycle state tracking
  • Lifecycle properties: initializing, ready, starting, stopping, active
  • Lifecycle callbacks: onInitializingStatusChange, onReadyStatusChange, onStartStatusChange, onStopStatusChange, onActiveStatusChange

Events:

  • Events - Event dispatcher with derived state change detection
  • Event types: notStarted, inProgress, done, successful, failed, retrying, restarting, paused, cancelled

Selectors:

  • Functions for computing derived state from core state
  • State status derivers for boolean flag computation

Modular Exports

  • @hyperfrontend/state-machine/actions - Action creators
  • @hyperfrontend/state-machine/store - Store implementation
  • @hyperfrontend/state-machine/reducer - Root reducer
  • @hyperfrontend/state-machine/state - State utilities and initial state
  • @hyperfrontend/state-machine/selectors - State selectors
  • @hyperfrontend/state-machine/events - Event system
  • @hyperfrontend/state-machine/async-operation - Async operation wrapper
  • @hyperfrontend/state-machine/coordinated-async-operation - Multi-process coordination
  • @hyperfrontend/state-machine/lifecycle-aware-component - Lifecycle component base class
  • @hyperfrontend/state-machine/models - TypeScript types and interfaces

Compatibility

Platform Support
Browser
Node.js
Web Workers
Deno, Bun, Cloudflare Workers

Output Formats

Format File Tree-Shakeable
ESM index.esm.js
CJS index.cjs.js
IIFE bundle/index.iife.min.js
UMD bundle/index.umd.min.js

Bundle size: < 1 KB (minified, self-contained)

CDN Usage

<!-- unpkg -->
<script src="https://unpkg.com/@hyperfrontend/state-machine"></script>

<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@hyperfrontend/state-machine"></script>

<script>
  const { createStore, createReducer } = HyperfrontendStateMachine
</script>

Global variable: HyperfrontendStateMachine

Part of hyperfrontend

This library is part of the hyperfrontend monorepo.

📖 Full documentation

License

MIT

API Reference

View:
Organized by entry point

Module Structure

|

9 modules · 30 total exports

Related