Skip to content

nitrogenlabs/arkhamjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ArkhamJS: A lightweight, isomorphic data state management library

npm version npm downloads Issues TypeScript

ArkhamJS makes state management predictable by using a unidirectional data flow and immutable state updates. It combines the best of Flux architecture with modern event-driven patterns to help you write applications that behave consistently across environments.

ArkhamJS stands out as a truly universal data state management library, designed to work seamlessly across all JavaScript environments. Unlike most state management solutions that focus primarily on web applications, ArkhamJS provides consistent APIs and behavior whether you're building React web apps, React Native mobile applications, or Node.js server environments. This cross-platform capability means you can use the same state management patterns and code across your entire tech stack, significantly reducing context-switching and enabling code sharing between frontend and backend systems.

πŸ“‹ Table of Contents

πŸš€ Why ArkhamJS?

Predictable State Management

ArkhamJS helps you write applications that behave consistently across different environments. By enforcing a unidirectional data flow and immutable state updates, it makes state changes predictable and traceable.

Why Developers Choose ArkhamJS

  • βœ… Predictable state updates with unidirectional data flow
  • βœ… Immutable state for consistent behavior
  • βœ… Type-safe with first-class TypeScript support
  • βœ… Minimal boilerplate compared to Redux
  • βœ… Powerful middleware system for extensibility
  • βœ… Cross-platform storage integrations
  • βœ… Efficient debugging with DevTools and logging
  • βœ… Universal compatibility across all JavaScript environments

Real-World Benefits

Challenge Traditional Solutions ArkhamJS Solution
State Predictability Complex reducers and actions Simple store patterns
Data Consistency Manual immutability Built-in immutable updates
State Debugging External tools, complex setup Integrated DevTools
State Persistence External libraries Built-in storage adapters
Type Safety Complex type definitions First-class TypeScript
Cross-Environment Different solutions per platform One consistent API

🌍 Multi-Environment Support

Universal State Management

ArkhamJS works seamlessly across all JavaScript environments with platform-specific optimizations:

React Web Applications

import { Flux } from '@nlabs/arkhamjs';
import { useFluxState, useFluxDispatch } from '@nlabs/arkhamjs-utils-react';

// Type-safe store definition
interface UserState {
  users: User[];
  currentUser: User | null;
}

const UserStore = {
  name: 'user',
  action: (type: string, data: any, state: UserState): UserState => {
    switch (type) {
      case 'ADD_USER':
        return { ...state, users: [...state.users, data] };
      case 'SET_CURRENT_USER':
        return { ...state, currentUser: data };
      default:
        return state;
    }
  }
};

// React component with hooks
const UserList = (): JSX.Element => {
  const users = useFluxState<User[]>('user.users', []);
  const dispatch = useFluxDispatch();

  const addUser = (user: User): void => {
    dispatch({ type: 'ADD_USER', user });
  };

  return (
    <div>
      {users.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
      <button onClick={() => addUser({ id: 1, name: 'John' })}>
        Add User
      </button>
    </div>
  );
};

React Native Applications

import { Flux } from '@nlabs/arkhamjs';
import { NativeStorage } from '@nlabs/arkhamjs-storage-native';

// Same store logic, different storage
Flux.init({
  name: 'my-app',
  stores: [UserStore],
  storage: NativeStorage, // AsyncStorage for React Native
  storageWait: 300
});

// React Native component
const UserProfile = (): JSX.Element => {
  const currentUser = useFluxState<User | null>('user.currentUser', null);

  return (
    <View>
      <Text>Welcome, {currentUser?.name}!</Text>
    </View>
  );
};

Node.js Applications

import { Flux } from '@nlabs/arkhamjs';
import { NodeStorage } from '@nlabs/arkhamjs-storage-node';

// Server-side state management
Flux.init({
  name: 'my-server',
  stores: [UserStore],
  storage: NodeStorage, // File system or database storage
  storageWait: 500
});

// API endpoint using ArkhamJS
const userController = {
  createUser: async (req: Request, res: Response): Promise<void> => {
    const user = req.body;

    // Dispatch action to update state
    Flux.dispatch({ type: 'ADD_USER', user });

    // State is automatically persisted
    res.json({ success: true, user });
  },

  getUsers: async (req: Request, res: Response): Promise<void> => {
    // Get state directly
    const users = Flux.getState<User[]>('user.users');
    res.json(users);
  }
};

Vanilla JavaScript

import { Flux } from '@nlabs/arkhamjs';

// Works in any JavaScript environment
Flux.init({
  name: 'my-app',
  stores: [UserStore]
});

// Event-driven updates
Flux.on('ADD_USER', (action: { type: string; user: User }) => {
  console.log('User added:', action.user);
  updateUI();
});

// Direct state access
const updateUI = (): void => {
  const users = Flux.getState<User[]>('user.users');
  renderUserList(users);
};

πŸ“¦ Packages

ArkhamJS is built as a modular ecosystem, allowing you to use only what you need:

Core Package

Middleware Ecosystem

Storage Solutions

React Integration

Developer Tools

Examples & Templates

🎯 Key Features

1. Predictable State Container

// Single source of truth
const currentState = Flux.getState();

// Predictable state updates
Flux.dispatch({ type: 'ADD_USER', user });

Why it matters: A single source of truth makes your application state predictable and easier to debug, test, and reason about.

2. Unidirectional Data Flow

// Data flows in one direction
Flux.dispatch({ type: 'ADD_USER', user });  // 1. Action dispatched
// 2. Store processes action
// 3. State is updated immutably
// 4. Subscribers are notified

Why it matters: Unidirectional data flow ensures that state changes are predictable and traceable, making debugging easier.

3. Immutable State Updates

// Immutable state updates
const UserStore = {
  name: 'user',
  action: (type, data, state) => {
    switch (type) {
      case 'ADD_USER':
        return { ...state, users: [...state.users, data] }; // New object returned
      default:
        return state;
    }
  }
};

Why it matters: Immutability guarantees that state is never modified directly, preventing hard-to-track bugs and enabling time-travel debugging.

4. Middleware Ecosystem

// Extensible middleware system
Flux.init({
  middleware: [
    loggerMiddleware,    // Logging
    devToolsMiddleware,  // DevTools integration
    persistMiddleware    // State persistence
  ]
});

Why it matters: Middleware provides a clean way to extend functionality for logging, debugging, API calls, and more without cluttering your application logic.

5. Cross-Platform Storage

// Consistent storage API across platforms
Flux.init({
  storage: BrowserStorage, // Browser
  // or: NativeStorage,    // React Native
  // or: NodeStorage       // Node.js
});

Why it matters: Built-in storage adapters provide a consistent API for persisting state across different environments.

⚑ Performance & Bundle Size

Competitive Bundle Analysis

Library Gzipped Size Minified Size Features
ArkhamJS 13.4 KB 40 KB Full-featured Flux + middleware + storage
Zustand ~3.2 KB ~8.5 KB Minimal, no middleware
Jotai ~4.1 KB ~11 KB Atomic model, no middleware
Redux Toolkit ~14 KB ~41 KB Full Redux ecosystem
MobX ~7.5 KB ~23 KB Core only
Recoil ~8.5 KB ~25 KB Facebook, atomic

ArkhamJS provides more features for similar size - you get a complete state management solution with middleware, storage, and devtools support.

Performance Optimizations

  • Tree-shaking enabled for minimal bundle size
  • Selective re-renders with state path subscriptions
  • Immutable updates prevent unnecessary re-renders
  • Event-driven updates only trigger relevant components
  • Lazy loading of middleware and storage adapters

πŸ—οΈ Architecture

Flux + Event-Driven Design

ArkhamJS combines the best of Flux architecture with modern event-driven patterns:

class FluxFramework extends EventEmitter {
  // Centralized state store (Flux)
  private state: Record<string, any> = {};

  // Event-driven updates (Pub/Sub)
  async dispatch(action: FluxAction): Promise<void> {
    // 1. Update stores (Flux pattern)
    this.updateStoresState(action.type, action);

    // 2. Emit events (Pub/Sub pattern)
    this.emit(action.type, action);
    this.emit('arkhamjs', this.state);
  }
}

State Management Patterns

Pattern ArkhamJS Redux Toolkit Zustand Jotai Valtio
Immutable Updates βœ… Full βœ… Full βœ… Partial βœ… Full ❌ Mutable
Event-Driven βœ… Native ❌ Actions ❌ Direct ❌ Atoms ❌ Proxy
Middleware Support βœ… Built-in βœ… Extensive ⚠️ Limited ❌ No ❌ No
DevTools βœ… Plugin βœ… Built-in βœ… Basic ❌ No ❌ No
Storage Integration βœ… Built-in ❌ External βœ… Plugin ❌ No ❌ No
TypeScript βœ… First-class βœ… Excellent βœ… Good βœ… Built-in βœ… Good
Multi-Platform βœ… Universal ❌ Web-only ❌ Web-only ❌ Web-only ❌ Web-only

πŸ”„ Migration Guide

From Redux

Benefits:

  • 70% less boilerplate code
  • Same familiar patterns (actions, stores, middleware)
  • Better performance with event-driven updates
  • Smaller bundle size (13.4KB vs 14KB for Redux Toolkit)
// Before: Redux
const userSlice = createSlice({
  name: 'user',
  initialState: { users: [] },
  reducers: { addUser: (state, action) => { state.users.push(action.payload); } }
});
dispatch(addUser(user));

// After: ArkhamJS
Flux.dispatch({ type: 'ADD_USER', user });

From Zustand

Benefits:

  • Built-in middleware support (logging, devtools, persistence)
  • Event-driven architecture for better reactivity
  • Familiar Flux patterns for team consistency
  • Storage integration out of the box
// Before: Zustand
const useStore = create((set) => ({
  users: [],
  addUser: (user: User) => set((state) => ({ users: [...state.users, user] }))
}));

// After: ArkhamJS
Flux.dispatch({ type: 'ADD_USER', user });

From Context API

Benefits:

  • Better performance with selective updates
  • Middleware support for debugging and persistence
  • Predictable state management with immutable updates
  • Event-driven architecture for complex interactions

πŸ“š Quick Start

Installation

npm install @nlabs/arkhamjs

Basic Setup

import { Flux } from '@nlabs/arkhamjs';

// Define your stores with TypeScript
interface UserState {
  users: User[];
}

const UserStore = {
  name: 'user',
  action: (type: string, data: any, state: UserState): UserState => {
    switch (type) {
      case 'ADD_USER':
        return { ...state, users: [...(state.users || []), data] };
      default:
        return state;
    }
  }
};

// Initialize Flux
Flux.init({
  name: 'my-app',
  stores: [UserStore],
  debug: true // Enable debugging in development
});

// Dispatch actions
Flux.dispatch({ type: 'ADD_USER', user: { name: 'John', email: 'john@example.com' } });

// Listen to events
Flux.on('ADD_USER', (action: { type: string; user: User }) => {
  console.log('User added:', action.user);
});

// Get state
const users: User[] = Flux.getState('user.users');

React Integration

import React, { useEffect, useState } from 'react';
import { Flux } from '@nlabs/arkhamjs';

const UserList = (): JSX.Element => {
  const [users, setUsers] = useState<User[]>([]);

  useEffect(() => {
    // Get initial state
    setUsers(Flux.getState<User[]>('user.users') || []);

    // Listen for updates
    const handleUserAdded = (action: { type: string; user: User }): void => {
      setUsers(Flux.getState<User[]>('user.users'));
    };

    Flux.on('ADD_USER', handleUserAdded);

    return () => {
      Flux.off('ADD_USER', handleUserAdded);
    };
  }, []);

  return (
    <div>
      {users.map(user => (
        <div key={user.email}>{user.name}</div>
      ))}
    </div>
  );
};

πŸ”§ Advanced Usage

Middleware Integration

import { Flux } from '@nlabs/arkhamjs';
import { Logger } from '@nlabs/arkhamjs-middleware-logger';
import { DevTools } from '@nlabs/arkhamjs-middleware-devtools';

Flux.init({
  name: 'my-app',
  stores: [UserStore],
  middleware: [
    Logger(), // Automatic action logging
    DevTools() // Chrome DevTools integration
  ]
});

Storage Persistence

import { Flux } from '@nlabs/arkhamjs';
import { BrowserStorage } from '@nlabs/arkhamjs-storage-browser';

Flux.init({
  name: 'my-app',
  stores: [UserStore],
  storage: BrowserStorage, // Automatic localStorage persistence
  storageWait: 300 // Debounce storage updates
});

TypeScript Support

import { Flux, FluxAction, FluxStore } from '@nlabs/arkhamjs';

interface User {
  name: string;
  email: string;
}

interface UserState {
  users: User[];
}

const UserStore: FluxStore<UserState> = {
  name: 'user',

  action: (type: string, data: any, state: UserState): UserState => {
    switch (type) {
      case 'ADD_USER':
        return { ...state, users: [...state.users, data] };
      default:
        return state;
    }
  }
};

// Type-safe action dispatching
Flux.dispatch<{ type: 'ADD_USER'; user: User }>({
  type: 'ADD_USER',
  user: { name: 'John', email: 'john@example.com' }
});

🀝 Community & Support

Getting Help

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

MIT License - see LICENSE file for details.


πŸŽ‰ Ready to Get Started?

ArkhamJS is the perfect balance of simplicity and power. Whether you're building a small startup app or a large enterprise system, ArkhamJS scales with your needs while keeping your code clean and maintainable.

Start building with ArkhamJS today and experience the future of state management! πŸš€

npm install @nlabs/arkhamjs

View Documentation β€’ Join Discord β€’ Report Issues

About

ArkhamJS - Javascript Flux Framework

Resources

Stars

Watchers

Forks

Packages

No packages published