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.
- πͺΆ 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
npm install statecoreconst { 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();Creates a new StateCore instance.
const store = createStatecore();
const storeWithState = createStatecore({ users: [], loading: false });Retrieves the current state value.
const currentState = store.statecoreGetState();Updates state and automatically notifies all observers.
store.statecoreSetState({ users: [...users, newUser], loading: false });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();Removes a specific observer function.
const myObserver = (eventName, newState) => console.log(newState);
store.statecoreAddObserver(myObserver);
store.statecoreRemoveObserver(myObserver);Returns array of all registered observers.
const observers = store.statecoreGetAllObservers();
console.log(`Active observers: ${observers ? observers.length : 0}`);Manually notify all observers with custom events.
store.statecoreNotifyAllObservers('CUSTOM_EVENT', { message: 'Hello World' });Destroys the instance, notifies observers, and cleans up resources.
store.statecoreDestroy();Check if the instance has been destroyed.
if (!store.statecoreIsDestroyed()) {
store.statecoreSetState(newState);
}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);
}
});StateCore provides two overloads for static instance management:
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 instancesAdvanced 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); // trueUse 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')); // falseEnhanced 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);
});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);
}
}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;
}
});// 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');
}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.
}
});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'
});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;
}StateCore works seamlessly across different JavaScript environments:
<script src="statecore.js"></script>
<script>
const store = window.statecore.createStatecore({ count: 0 });
</script>import { createStatecore } from 'statecore';
const store = createStatecore({ count: 0 });const { createStatecore } = require('statecore');
const store = createStatecore({ count: 0 });define(['statecore'], function(statecore) {
const store = statecore.createStatecore({ count: 0 });
});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();
}// β
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);class SafeStore extends StatecoreClass {
updateState(newData) {
if (!this.statecoreIsDestroyed()) {
this.statecoreSetState({ ...this.statecoreGetState(), ...newData });
}
}
}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');store.statecoreAddObserver((eventName, error, originalArgs) => {
if (eventName === STATECORE_EVENT__OBSERVER_ERROR) {
// Log to monitoring service
console.error('StateCore observer error:', error);
// Continue app operation
}
});store.statecoreNotifyAllObservers('USER_LOGIN_SUCCESS', user);
store.statecoreNotifyAllObservers('NETWORK_ERROR', error);
store.statecoreNotifyAllObservers('DATA_SYNC_COMPLETE', syncResult);// 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
}Run the test suite:
# Run tests
node test.js
# Or if you have npm scripts set up
npm test// 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();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;
});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;
}
}Contributions are welcome! Please feel free to submit issues and pull requests.
- Clone the repository
- Run tests:
node test.js - Make your changes
- Ensure tests pass
- Submit a pull request
MIT License - see LICENSE file for details.
MrZenW
- Website: MrZenW.com
- Email: MrZenW@gmail.com
- GitHub: github.com/MrZenW
Thanks to all contributors and users who have helped make StateCore better!