Skip to content

statecore/statecore

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

62 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

StateCore

License: MIT Version

A lightweight, performant, and type-safe state management library based on the observer pattern.
StateCore helps you build reactive, event-driven applications with minimal overhead and maximum flexibility.


✨ Features

  • πŸͺΆ Lightweight: Zero dependencies, minimal footprint (~3KB minified)
  • πŸ”„ Observable: Built-in observer system for reactive state updates
  • πŸ›‘οΈ Type Safe: Full TypeScript definitions included
  • 🌐 Universal: Works in browsers, Node.js, and all major bundlers
  • πŸ’ͺ Error Resilient: Observer errors won't break your application
  • πŸ”„ Lifecycle Aware: Safe cleanup and destroy mechanisms
  • 🎯 Flexible: Function-based API + Class-based API with static instance management
  • ⚑ High Performance: Optimized observer notification system

πŸ“¦ Installation

npm install statecore

πŸš€ Quick Start

const { createStatecore, STATECORE_EVENT__STATE_CHANGE } = require('statecore');

// Create a state instance
const store = createStatecore({ count: 0, user: null });

// Add an observer to watch state changes
const unsubscribe = store.statecoreAddObserver((eventName, newState, oldState) => {
  if (eventName === STATECORE_EVENT__STATE_CHANGE) {
    console.log('State changed:', { from: oldState, to: newState });
  }
});

// Update state - observers will be notified automatically
store.statecoreSetState({ count: 1, user: { name: 'John' } });

// Clean up when done
unsubscribe();
store.statecoreDestroy();

πŸ“– Complete API Reference

πŸ—οΈ Core Factory

createStatecore(initialState?)

Creates a new StateCore instance.

const store = createStatecore();
const storeWithState = createStatecore({ users: [], loading: false });

πŸ“Š State Management

statecoreGetState()

Retrieves the current state value.

const currentState = store.statecoreGetState();

statecoreSetState(newState)

Updates state and automatically notifies all observers.

store.statecoreSetState({ users: [...users, newUser], loading: false });

πŸ‘οΈ Observer Management

statecoreAddObserver(observer)

Adds an observer function that gets called on state changes.

Observer Signature: (eventName, newState, oldState, ...customArgs) => void

const removeObserver = store.statecoreAddObserver((eventName, newState, oldState) => {
  switch (eventName) {
    case STATECORE_EVENT__STATE_CHANGE:
      console.log('State updated:', newState);
      break;
    case STATECORE_EVENT__DESTROY:
      console.log('Store destroyed');
      break;
  }
});

// Remove observer when no longer needed
removeObserver();

statecoreRemoveObserver(observer)

Removes a specific observer function.

const myObserver = (eventName, newState) => console.log(newState);
store.statecoreAddObserver(myObserver);
store.statecoreRemoveObserver(myObserver);

statecoreGetAllObservers()

Returns array of all registered observers.

const observers = store.statecoreGetAllObservers();
console.log(`Active observers: ${observers ? observers.length : 0}`);

πŸ”” Event System

statecoreNotifyAllObservers(eventName, ...args)

Manually notify all observers with custom events.

store.statecoreNotifyAllObservers('CUSTOM_EVENT', { message: 'Hello World' });

♻️ Lifecycle Management

statecoreDestroy()

Destroys the instance, notifies observers, and cleans up resources.

store.statecoreDestroy();

statecoreIsDestroyed()

Check if the instance has been destroyed.

if (!store.statecoreIsDestroyed()) {
  store.statecoreSetState(newState);
}

πŸ—οΈ Class-Based Usage

StateCore provides a powerful class-based API for more complex applications:

const { StatecoreClass, STATECORE_EVENT__STATE_CHANGE } = require('statecore');

class UserStore extends StatecoreClass {
  constructor() {
    super({ users: [], loading: false, error: null });
  }

  async loadUsers() {
    this.statecoreSetState({ 
      ...this.statecoreGetState(), 
      loading: true, 
      error: null 
    });
    
    try {
      const users = await fetchUsers();
      this.statecoreSetState({ 
        users, 
        loading: false, 
        error: null 
      });
    } catch (error) {
      this.statecoreSetState({ 
        ...this.statecoreGetState(),
        loading: false, 
        error: error.message 
      });
    }
  }

  addUser(user) {
    const currentState = this.statecoreGetState();
    this.statecoreSetState({
      ...currentState,
      users: [...currentState.users, user]
    });
  }
}

const userStore = new UserStore();
userStore.statecoreAddObserver((eventName, newState) => {
  if (eventName === STATECORE_EVENT__STATE_CHANGE) {
    console.log('Users updated:', newState.users);
  }
});

🏭 Static Instance Management

StateCore provides two overloads for static instance management:

StatecoreClass.statecoreClassStaticGrabInstance(instanceName, initialState?)

Creates or retrieves a singleton instance by name. Always returns an instance (creates if doesn't exist).

// Get or create singleton instance - always returns an instance
const store1 = UserStore.statecoreClassStaticGrabInstance('main', { users: [] });
const store2 = UserStore.statecoreClassStaticGrabInstance('main'); // Same instance as store1

// Different instances for different names
const adminStore = UserStore.statecoreClassStaticGrabInstance('admin');
const guestStore = UserStore.statecoreClassStaticGrabInstance('guest');

console.log(store1 === store2); // true - same instance
console.log(store1 === adminStore); // false - different instances

StatecoreClass.statecoreClassStaticGrabInstance(isGrab, instanceName, initialState?)

Advanced instance management with conditional creation.

// Check if instance exists without creating (isGrab = false)
const existing = UserStore.statecoreClassStaticGrabInstance(false, 'main');
console.log('Instance exists:', existing !== null);

// Create or get instance (isGrab = true)
const store = UserStore.statecoreClassStaticGrabInstance(true, 'main', { users: [] });

// Later, just check existence again
const check = UserStore.statecoreClassStaticGrabInstance(false, 'main');
console.log('Now exists:', check !== null); // true

Use Cases for Static Instance Management:

// Application-wide stores
class AppStateStore extends StatecoreClass {
  static getGlobalInstance() {
    return this.statecoreClassStaticGrabInstance('global', {
      user: null,
      theme: 'light',
      notifications: []
    });
  }
}

// Multi-tenant applications
class TenantStore extends StatecoreClass {
  static getForTenant(tenantId) {
    return this.statecoreClassStaticGrabInstance(`tenant-${tenantId}`, {
      tenantId,
      data: {},
      permissions: []
    });
  }
  
  static hasTenantStore(tenantId) {
    return this.statecoreClassStaticGrabInstance(false, `tenant-${tenantId}`) !== null;
  }
}

// Usage
const appStore = AppStateStore.getGlobalInstance();
const tenant1Store = TenantStore.getForTenant('tenant1');
const tenant2Store = TenantStore.getForTenant('tenant2');

console.log(TenantStore.hasTenantStore('tenant1')); // true
console.log(TenantStore.hasTenantStore('tenant3')); // false

🎯 Event Helper Methods

statecoreClassAddEventObserver(...args, observer)

Enhanced observer registration with event filtering capabilities.

class AppStore extends StatecoreClass {
  constructor() {
    super({ theme: 'light', notifications: [] });
  }
}

const app = new AppStore();

// Add observer with event filtering
app.statecoreClassAddEventObserver('THEME_CHANGE', (eventName, ...args) => {
  console.log('Theme changed:', args);
});

statecoreClassNotifyAllEventObservers(eventName, ...args)

Notify observers with custom events from class context.

class NotificationStore extends StatecoreClass {
  addNotification(message) {
    const notifications = [...this.statecoreGetState().notifications, message];
    this.statecoreSetState({ notifications });
    
    // Custom event notification
    this.statecoreClassNotifyAllEventObservers('NOTIFICATION_ADDED', message);
  }
}

🏷️ Built-in Events and Constants

StateCore provides several constants for version checking and event handling:

const { 
  STATECORE_VERSION,
  STATECORE_EVENT__STATE_CHANGE,
  STATECORE_EVENT__DESTROY,
  STATECORE_EVENT__OBSERVER_ERROR 
} = require('statecore');

console.log('StateCore version:', STATECORE_VERSION); // "3.0.1"

store.statecoreAddObserver((eventName, ...args) => {
  switch (eventName) {
    case STATECORE_EVENT__STATE_CHANGE:
      console.log('State changed:', args[0], 'from:', args[1]);
      break;
    case STATECORE_EVENT__DESTROY:
      console.log('Store destroyed');
      break;
    case STATECORE_EVENT__OBSERVER_ERROR:
      console.log('Observer error:', args[0]);
      break;
  }
});

Version Checking

// Check if you're using a compatible version
const [major, minor, patch] = STATECORE_VERSION.split('.').map(Number);

if (major >= 3) {
  console.log('Using StateCore v3+, all features available');
} else if (major === 2 && minor >= 2) {
  console.log('Using StateCore v2.2+, most features available');
} else {
  console.warn('Consider upgrading StateCore for latest features');
}

πŸ›‘οΈ Error Handling

StateCore gracefully handles observer errors without breaking the application:

store.statecoreAddObserver(() => {
  throw new Error('Broken observer');
});

store.statecoreAddObserver((eventName, newState) => {
  console.log('This still works:', newState);
});

// Both observers are called, error is logged but doesn't stop execution
store.statecoreSetState({ value: 123 });

Observer errors trigger STATECORE_EVENT__OBSERVER_ERROR events:

store.statecoreAddObserver((eventName, error, originalArgs) => {
  if (eventName === STATECORE_EVENT__OBSERVER_ERROR) {
    console.error('Observer failed:', error.message);
    // Handle error reporting, logging, etc.
  }
});

πŸ“ TypeScript Support

StateCore includes comprehensive TypeScript definitions:

import { 
  createStatecore, 
  StatecoreClass,
  StatecoreObserver,
  Statecore
} from 'statecore';

interface AppState {
  user: { id: number; name: string } | null;
  isAuthenticated: boolean;
  theme: 'light' | 'dark';
}

const store: Statecore = createStatecore<AppState>({
  user: null,
  isAuthenticated: false,
  theme: 'light'
});

const observer: StatecoreObserver = (eventName, newState: AppState, oldState: AppState) => {
  console.log('State changed:', { eventName, newState, oldState });
};

store.statecoreAddObserver(observer);

// Type-safe state updates
store.statecoreSetState({
  user: { id: 1, name: 'John Doe' },
  isAuthenticated: true,
  theme: 'dark'
});

TypeScript Class Usage

class UserStore extends StatecoreClass {
  constructor() {
    super({ users: [], loading: false } as UserState);
  }

  getUsers(): User[] {
    return this.statecoreGetState().users;
  }

  async loadUsers(): Promise<void> {
    this.statecoreSetState({ ...this.statecoreGetState(), loading: true });
    // ... implementation
  }

  // Type-safe static instance management
  static getGlobalStore(): UserStore {
    return this.statecoreClassStaticGrabInstance('global', { users: [], loading: false });
  }

  static hasGlobalStore(): boolean {
    return this.statecoreClassStaticGrabInstance(false, 'global') !== null;
  }
}

interface UserState {
  users: User[];
  loading: boolean;
}

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

🌐 Environment Support

StateCore works seamlessly across different JavaScript environments:

Browser (Global)

<script src="statecore.js"></script>
<script>
  const store = window.statecore.createStatecore({ count: 0 });
</script>

ES Modules

import { createStatecore } from 'statecore';
const store = createStatecore({ count: 0 });

CommonJS (Node.js)

const { createStatecore } = require('statecore');
const store = createStatecore({ count: 0 });

AMD (RequireJS)

define(['statecore'], function(statecore) {
  const store = statecore.createStatecore({ count: 0 });
});

βœ… Best Practices

1. Always Clean Up Observers

const unsubscribe = store.statecoreAddObserver(observer);

// In React useEffect cleanup
useEffect(() => {
  const unsubscribe = store.statecoreAddObserver(observer);
  return unsubscribe; // Cleanup on unmount
}, []);

// In Vue beforeDestroy
beforeDestroy() {
  this.unsubscribe();
}

2. Use Immutable State Updates

// βœ… Good: Create new state object
store.statecoreSetState({ 
  ...store.statecoreGetState(), 
  newProperty: 'value',
  nested: { ...store.statecoreGetState().nested, updated: true }
});

// ❌ Avoid: Mutating existing state
const state = store.statecoreGetState();
state.newProperty = 'value'; // Don't do this
store.statecoreSetState(state);

3. Check Destruction Before Operations

class SafeStore extends StatecoreClass {
  updateState(newData) {
    if (!this.statecoreIsDestroyed()) {
      this.statecoreSetState({ ...this.statecoreGetState(), ...newData });
    }
  }
}

4. Use Static Instance Management Wisely

class ConfigStore extends StatecoreClass {
  // Always ensure instance exists
  static getInstance() {
    return this.statecoreClassStaticGrabInstance('config', { 
      apiUrl: 'https://api.example.com',
      timeout: 5000 
    });
  }
  
  // Check before accessing
  static hasInstance() {
    return this.statecoreClassStaticGrabInstance(false, 'config') !== null;
  }
  
  // Environment-specific instances
  static getForEnv(env) {
    return this.statecoreClassStaticGrabInstance(`config-${env}`, {
      apiUrl: env === 'production' ? 'https://api.prod.com' : 'https://api.dev.com'
    });
  }
}

// Usage
const config = ConfigStore.getInstance();
const prodConfig = ConfigStore.getForEnv('production');
const devConfig = ConfigStore.getForEnv('development');

5. Handle Observer Errors Gracefully

store.statecoreAddObserver((eventName, error, originalArgs) => {
  if (eventName === STATECORE_EVENT__OBSERVER_ERROR) {
    // Log to monitoring service
    console.error('StateCore observer error:', error);
    // Continue app operation
  }
});

6. Use Descriptive Event Names

store.statecoreNotifyAllObservers('USER_LOGIN_SUCCESS', user);
store.statecoreNotifyAllObservers('NETWORK_ERROR', error);
store.statecoreNotifyAllObservers('DATA_SYNC_COMPLETE', syncResult);

7. Version-aware Development

// Check version compatibility in your application
const { STATECORE_VERSION } = require('statecore');

function checkCompatibility() {
  const [major] = STATECORE_VERSION.split('.').map(Number);
  if (major < 3) {
    console.warn('This application requires StateCore v3.0+');
    return false;
  }
  return true;
}

if (checkCompatibility()) {
  // Initialize your stores
}

πŸ§ͺ Testing

Run the test suite:

# Run tests
node test.js

# Or if you have npm scripts set up
npm test

Testing Your StateCore Integration

// test-example.js
const assert = require('assert');
const { createStatecore, StatecoreClass, STATECORE_EVENT__STATE_CHANGE } = require('statecore');

function testStateCore() {
  const store = createStatecore({ count: 0 });
  
  let observerCalled = false;
  let receivedState = null;
  
  const unsubscribe = store.statecoreAddObserver((eventName, newState) => {
    if (eventName === STATECORE_EVENT__STATE_CHANGE) {
      observerCalled = true;
      receivedState = newState;
    }
  });
  
  store.statecoreSetState({ count: 1 });
  
  assert.equal(observerCalled, true);
  assert.deepEqual(receivedState, { count: 1 });
  
  unsubscribe();
  store.statecoreDestroy();
  
  console.log('βœ… All tests passed!');
}

function testStaticInstances() {
  class TestStore extends StatecoreClass {}
  
  // Test instance creation
  const store1 = TestStore.statecoreClassStaticGrabInstance('test');
  const store2 = TestStore.statecoreClassStaticGrabInstance('test');
  
  assert.equal(store1 === store2, true, 'Should return same instance');
  
  // Test conditional creation
  const nonExistent = TestStore.statecoreClassStaticGrabInstance(false, 'nonexistent');
  assert.equal(nonExistent, null, 'Should return null for non-existent instance');
  
  console.log('βœ… Static instance tests passed!');
}

testStateCore();
testStaticInstances();

πŸ”§ Advanced Usage Patterns

Middleware Pattern

class MiddlewareStore extends StatecoreClass {
  constructor() {
    super({ data: null });
    this.middleware = [];
  }

  addMiddleware(fn) {
    this.middleware.push(fn);
  }

  setState(newState) {
    let processedState = newState;
    
    // Apply middleware
    for (const middleware of this.middleware) {
      processedState = middleware(processedState, this.statecoreGetState());
    }
    
    this.statecoreSetState(processedState);
  }
}

// Usage
const store = new MiddlewareStore();
store.addMiddleware((newState, oldState) => {
  console.log('Middleware: state changing from', oldState, 'to', newState);
  return newState;
});

Computed Properties Pattern

class ComputedStore extends StatecoreClass {
  constructor() {
    super({ 
      users: [],
      filter: 'all' 
    });
  }

  get filteredUsers() {
    const { users, filter } = this.statecoreGetState();
    if (filter === 'active') return users.filter(u => u.active);
    if (filter === 'inactive') return users.filter(u => !u.active);
    return users;
  }

  get userCount() {
    return this.filteredUsers.length;
  }
}

🀝 Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

Development Setup

  1. Clone the repository
  2. Run tests: node test.js
  3. Make your changes
  4. Ensure tests pass
  5. Submit a pull request

πŸ“„ License

MIT License - see LICENSE file for details.


πŸ‘€ Author

MrZenW


πŸ™ Acknowledgments

Thanks to all contributors and users who have helped make StateCore better!

About

A state-observable library.

Resources

License

Stars

Watchers

Forks

Packages

No packages published