@context-action/core
v0.8.4
Published
Type-safe action pipeline management library for JavaScript/TypeScript
Maintainers
Readme
@context-action/core
Type-safe action pipeline management library for JavaScript/TypeScript applications with advanced filtering, performance optimizations, and React integration support.
Installation
npm install @context-action/core
# or
pnpm install @context-action/coreQuick Start
import { ActionRegister } from '@context-action/core';
// Define your action types
interface MyActions {
increment: void;
setCount: number;
updateUser: { id: string; name: string };
}
// Create action register
const actions = new ActionRegister<MyActions>({
name: 'MyApp',
registry: { debug: true }
});
// Register handlers with priorities
actions.register('increment', () => {
console.log('Increment called');
}, { priority: 10 });
actions.register('setCount', (count) => {
console.log(`Setting count to: ${count}`);
}, { priority: 5 });
// Dispatch actions
await actions.dispatch('increment');
await actions.dispatch('setCount', 42);Memory Management
// Configure handler limits for memory safety (v0.4.1+)
const actions = new ActionRegister<MyActions>({
registry: {
maxHandlersPerAction: 1000 // Default: 1000, prevents memory issues
// maxHandlersPerAction: 5000 // Higher limit for complex applications
// maxHandlersPerAction: Infinity // Disable limit (use with caution)
}
});
// Use cases for different limits:
// - 1000 (default): Most applications
// - 5000-10000: Large enterprise applications
// - Infinity: Only for controlled environments with trusted code🚀 New Features (v0.4.0+)
Advanced Filtering System
Filter handlers by priority, ID, or custom conditions:
// Filter by priority range
await actions.dispatch('updateUser', userData, {
filter: {
priority: { min: 10, max: 50 } // Only handlers with priority 10-50
}
});
// Filter by specific handler IDs
await actions.dispatch('processData', data, {
filter: {
handlerIds: ['validation', 'logging'], // Only these handlers
excludeHandlerIds: ['analytics'] // Exclude analytics handler
}
});
// Custom filtering logic
await actions.dispatch('secureAction', data, {
filter: {
custom: (config) => config.blocking === true // Only blocking handlers
}
});
// Combined filtering
await actions.dispatch('complexAction', data, {
filter: {
priority: { min: 20 },
excludeHandlerIds: ['debug'],
custom: (config) => !config.id.includes('test')
}
});Enhanced Handler Configuration
actions.register('myAction', handler, {
priority: 10,
id: 'my-handler',
blocking: true,
once: false,
debounce: 300,
throttle: 1000,
replaceExisting: true // 🆕 Replace handler with same ID (great for React HMR)
});Immediate Execution & Queue Control
// Bypass queue for immediate execution
await actions.dispatch('urgentAction', data, {
immediate: true
});
// Queue with custom priority
await actions.dispatch('backgroundTask', data, {
queuePriority: 5
});
// Execution timeout
await actions.dispatch('timedAction', data, {
timeout: 5000
});Result Collection with Strategies
const result = await actions.dispatchWithResult('processData', data, {
result: {
collect: true,
strategy: 'all', // 'first' | 'last' | 'all' | 'merge' | 'custom'
maxResults: 10,
includeErrors: true
}
});
console.log('Results:', result.results);
console.log('Execution time:', result.execution.duration);
console.log('Success:', result.success);React Integration Helpers
import {
useActionHandler,
ReactDevUtils
} from '@context-action/core';
// React hook pattern
function MyComponent() {
const registry = useActionRegister();
// Auto-cleanup on unmount, HMR support
const handlerConfig = useActionHandler(
registry,
'userAction',
async (payload) => {
// Handler logic
},
{ priority: 10 },
[] // dependencies
);
// Direct registry dispatch with error handling
const handleDispatch = useCallback(async (action, payload) => {
try {
await registry.dispatch(action, payload);
} catch (error) {
console.error(`Failed to dispatch ${action}:`, error);
}
}, [registry]);
}
// Development utilities
ReactDevUtils.enableDebugMode();
const stats = ReactDevUtils.getStats(registry);Core Features
🎯 Type-Safe Action Pipeline
- Full TypeScript support with compile-time type checking
- Priority-based execution with configurable handler ordering
- Pipeline control - abort, modify payloads, conditional execution
- Multiple execution modes - sequential, parallel, race
⚡ Performance & Memory Optimizations
- Cached environment checks for better performance
- Optimized handler ID generation without random numbers
- Smart array filtering - only copies when needed
- Automatic memory cleanup with idle handler cleanup
- Optional concurrency queues for thread safety
🔧 Advanced Configuration
const registry = new ActionRegister<MyActions>({
name: 'MyApp',
registry: {
debug: true,
autoCleanup: true,
defaultExecutionMode: 'sequential',
useConcurrencyQueue: true,
errorHandler: (error, context) => {
console.error('Unhandled action error:', error);
}
}
});🎛️ Pipeline Controller
Full control over pipeline execution:
actions.register('validate', (data, controller) => {
// Abort pipeline
if (!data.isValid) {
controller.abort('Validation failed');
return;
}
// Modify payload for next handlers
controller.modifyPayload(data => ({
...data,
validated: true,
timestamp: Date.now()
}));
// Jump to high-priority handlers
if (data.urgent) {
controller.jumpToPriority(100);
}
// Set result for collection
controller.setResult({ validation: 'passed' });
// Early return with result
if (data.fastPath) {
controller.return({ fastPath: true });
}
});Advanced Usage
Action Guard (Debounce/Throttle)
// Built-in debounce/throttle support
actions.register('searchUsers', searchHandler, {
debounce: 300 // Wait 300ms after last call
});
actions.register('scrollHandler', updateUI, {
throttle: 100 // Max once per 100ms
});
// Via dispatch options
await actions.dispatch('search', query, {
debounce: 500
});Execution Modes
// Set execution mode per action
actions.setActionExecutionMode('logEvent', 'parallel');
actions.setActionExecutionMode('fetchData', 'race');
// Override via dispatch options
await actions.dispatch('processFiles', files, {
executionMode: 'parallel'
});Error Handling & Recovery
actions.register('riskyOperation', async (data, controller) => {
try {
const result = await riskyAPI(data);
return result;
} catch (error) {
if (error.retryable) {
// Let other handlers try
return undefined;
} else {
// Abort pipeline for critical errors
controller.abort(`Critical error: ${error.message}`);
}
}
});
// With retry configuration
await actions.dispatch('apiCall', data, {
retryOnError: {
maxAttempts: 3,
delay: 1000
}
});Statistics & Monitoring
// Registry information
const info = actions.getRegistryInfo();
console.log(`Total actions: ${info.totalActions}`);
console.log(`Total handlers: ${info.totalHandlers}`);
// Action-specific statistics
const stats = actions.getActionStats('updateUser');
if (stats) {
console.log(`Handler count: ${stats.handlerCount}`);
console.log(`Success rate: ${stats.executionStats?.successRate}%`);
console.log(`Average duration: ${stats.executionStats?.averageDuration}ms`);
}
// Clear statistics
actions.clearExecutionStats();Cleanup & Resource Management
// Explicit cleanup when done
const registry = new ActionRegister({ name: 'MyApp' });
// Use the registry...
// Clean up all resources
registry.destroy(); // Cleans up pipelines, guards, queues, statsAPI Reference
ActionRegister
Registration Methods
register<K>(action, handler, config?)- Register action handlerclearAction(action)- Remove all handlers for actionclearAll()- Remove all handlers
Dispatch Methods
dispatch<K>(action, payload?, options?)- Dispatch actiondispatchWithResult<K>(action, payload?, options?)- Dispatch with detailed results
Information Methods
getHandlerCount(action)- Get handler count for actionhasHandlers(action)- Check if action has handlersgetRegisteredActions()- Get all registered action namesgetRegistryInfo()- Get comprehensive registry informationgetActionStats(action)- Get detailed action statistics
Execution Mode Methods
setActionExecutionMode(action, mode)- Set execution mode for actiongetActionExecutionMode(action)- Get execution mode for actionremoveActionExecutionMode(action)- Reset to default execution mode
Utility Methods
getName()- Get registry nameisDebugEnabled()- Check if debug mode is enableddestroy()- Clean up all resources
Configuration Interfaces
interface HandlerConfig {
priority?: number; // Handler priority (higher = first)
id?: string; // Unique handler identifier
blocking?: boolean; // Wait for async completion
once?: boolean; // Remove after first execution
debounce?: number; // Debounce delay in ms
throttle?: number; // Throttle delay in ms
replaceExisting?: boolean; // Replace handler with same ID
}
interface DispatchOptions {
debounce?: number;
throttle?: number;
executionMode?: 'sequential' | 'parallel' | 'race';
signal?: AbortSignal;
immediate?: boolean; // Bypass queue
queuePriority?: number; // Queue priority
timeout?: number; // Execution timeout
retryOnError?: {
maxAttempts: number;
delay: number;
};
filter?: {
handlerIds?: string[];
excludeHandlerIds?: string[];
priority?: { min?: number; max?: number };
custom?: (config: HandlerConfig) => boolean;
};
result?: {
strategy?: 'first' | 'last' | 'all' | 'merge' | 'custom';
merger?: <R>(results: R[]) => R;
collect?: boolean;
maxResults?: number;
includeErrors?: boolean;
};
}TypeScript Support
Full type safety with excellent IntelliSense support:
interface AppActions {
// Void actions
reset: void;
logout: void;
// Actions with payloads
setUser: { id: string; name: string; email: string };
updatePreferences: { theme: 'light' | 'dark'; language: string };
// Union type payloads
navigate: { route: string } | { url: URL };
}
const actions = new ActionRegister<AppActions>();
// ✅ Type-safe - all good
await actions.dispatch('reset');
await actions.dispatch('setUser', { id: '1', name: 'John', email: '[email protected]' });
// ❌ TypeScript errors
await actions.dispatch('setUser'); // Missing required payload
await actions.dispatch('setUser', { id: '1' }); // Missing required fields
await actions.dispatch('invalidAction'); // Unknown actionMigration from v0.3.x
Most existing code works without changes. New features are opt-in:
// v0.3.x code - still works
const actions = new ActionRegister();
actions.register('myAction', handler);
await actions.dispatch('myAction', payload);
// v0.4.x - new features available
actions.register('myAction', handler, {
replaceExisting: true // New option
});
await actions.dispatch('myAction', payload, {
filter: { priority: { min: 10 } } // New filtering
});
// Clean up when done (recommended)
actions.destroy();Performance Tips
- Use handler IDs for better debugging and filtering
- Enable replaceExisting for React components to prevent duplicates
- Use immediate: false (default) to benefit from queue optimizations
- Call destroy() when registry is no longer needed
- Use priority filtering instead of excludeHandlerIds for better performance
- Cache ActionRegister instances - don't create new ones frequently
License
Apache-2.0
