UNPKG

jw-gate

Version:

Creates a "gate" with "locks." When all locks are open, the gate is open, useful for dealing with semi-random async events.

296 lines (222 loc) 8.58 kB
# jw-gate A reactive coordination library that manages multiple conditions through an event-driven "Gate" with independent "locks". Perfect for two key scenarios: 1. **UI State Management** - Track multiple async operations and show progress as each completes 2. **Dynamic Condition Monitoring** - React to conditions that change over time (IoT, sensors, resources) Unlike promise-based coordination, jw-gate lets you respond immediately to individual condition changes rather than waiting for everything to complete at once. ## Why jw-gate? **For UI Progress Tracking**: `Promise.all()` only tells you when everything is done - you can't show individual completions. jw-gate lets you update your UI as each operation finishes. **For Dynamic Conditions**: Traditional promises are one-shot, but real-world conditions change over time. jw-gate provides reactive coordination that responds immediately when conditions shift. ## Features - 🎯 Track multiple async operations with individual progress updates - 🚪 Reactive coordination of changing conditions over time - 🔄 Event-driven architecture - respond immediately to state changes - ⏱️ Automatic timeout support for temporary conditions - 🔍 Comprehensive state monitoring and introspection - 🛡️ Error handling through events (no crashes) - 🌐 Works in both Node.js and browser environments - 📦 Zero dependencies ## Installation ```bash npm install jw-gate ``` ## Quick Start ### UI Progress Tracking ```javascript const Gate = require('jw-gate'); // Track multiple file uploads const uploadGate = new Gate(['file1', 'file2', 'file3'], true); // Update UI when all files are complete uploadGate.on('unlocked', () => { updateUI('All uploads complete! 🎉'); }); // Start uploads and update progress individually uploadFile1().then(() => { updateUI('File 1 complete ✓'); uploadGate.setLock('file1', false); }); uploadFile2().then(() => { updateUI('File 2 complete ✓'); uploadGate.setLock('file2', false); }); uploadFile3().then(() => { updateUI('File 3 complete ✓'); uploadGate.setLock('file3', false); }); ``` ### Dynamic Condition Monitoring ```javascript // Monitor changing conditions const systemGate = new Gate(['network', 'power', 'sensors'], true); // React immediately when all conditions are met systemGate.on('unlocked', () => { console.log('System ready - starting operation'); startOperation(); }); systemGate.on('locked', () => { console.log('Conditions changed - pausing operation'); pauseOperation(); }); // Conditions change independently over time networkMonitor.on('connected', () => systemGate.setLock('network', false)); networkMonitor.on('disconnected', () => systemGate.setLock('network', true)); powerMonitor.on('stable', () => systemGate.setLock('power', false)); powerMonitor.on('unstable', () => systemGate.setLock('power', true)); ``` ## Common Examples ### UI Progress Tracking #### File Upload Dashboard ```javascript const uploadGate = new Gate(['validation', 'upload', 'processing'], true); // Show progress as each step completes uploadGate.on('unlocked', () => { showMessage('Upload complete! File is ready to use.'); enableDownloadButton(); }); // Validate file validateFile(file).then(valid => { if (valid) { showProgress('Validation complete ✓'); uploadGate.setLock('validation', false); } }); // Upload to server uploadToServer(file).then(() => { showProgress('Upload complete ✓'); uploadGate.setLock('upload', false); }); // Server processing processOnServer(file).then(() => { showProgress('Processing complete ✓'); uploadGate.setLock('processing', false); }); ``` #### Multi-Step Form Validation ```javascript const formGate = new Gate(['email', 'password', 'terms'], true); formGate.on('unlocked', () => { enableSubmitButton(); showMessage('Form ready for submission'); }); formGate.on('locked', () => { disableSubmitButton(); }); emailField.on('validated', () => { showCheckmark('email'); formGate.setLock('email', false); }); passwordField.on('validated', () => { showCheckmark('password'); formGate.setLock('password', false); }); termsCheckbox.on('checked', () => { showCheckmark('terms'); formGate.setLock('terms', false); }); ``` ### Dynamic Condition Monitoring #### Smart Device Control ```javascript const deviceGate = new Gate(['safety', 'network', 'power'], true); deviceGate.on('unlocked', () => { console.log('Device activated - all systems go'); device.start(); statusLight.setGreen(); }); deviceGate.on('locked', () => { console.log('Safety conditions changed - device stopped'); device.emergencyStop(); statusLight.setRed(); }); // Sensors update conditions independently safetySystem.on('safe', () => deviceGate.setLock('safety', false)); safetySystem.on('unsafe', () => deviceGate.setLock('safety', true)); networkMonitor.on('connected', () => deviceGate.setLock('network', false)); networkMonitor.on('disconnected', () => deviceGate.setLock('network', true)); powerMonitor.on('stable', () => deviceGate.setLock('power', false)); powerMonitor.on('fluctuation', () => deviceGate.setLock('power', true)); ``` #### Resource-Aware Processing ```javascript const processingGate = new Gate(['cpu', 'memory', 'disk'], true); processingGate.on('unlocked', () => { console.log('Resources available - starting batch job'); startBatchProcessing(); }); processingGate.on('locked', () => { console.log('Resource constraints - pausing batch job'); pauseBatchProcessing(); }); // Resource monitors update conditions cpuMonitor.on('available', () => processingGate.setLock('cpu', false)); cpuMonitor.on('busy', () => processingGate.setLock('cpu', true)); memoryMonitor.on('sufficient', () => processingGate.setLock('memory', false)); memoryMonitor.on('low', () => processingGate.setLock('memory', true)); diskMonitor.on('space', () => processingGate.setLock('disk', false)); diskMonitor.on('full', () => processingGate.setLock('disk', true)); ``` ## API Reference ### Constructor ```javascript new Gate(lockNames, initialState = false) ``` - `lockNames`: Array of strings representing lock names - `initialState`: Boolean indicating initial state of locks (default: false = unlocked) ### Event Management ```javascript gate.on(event, callback) // Register event listener gate.off(event, callback) // Remove event listener ``` **Events:** - `'locked'`: Emitted when gate becomes locked (one or more locks engaged) - `'unlocked'`: Emitted when gate becomes unlocked (all locks disengaged) - `'error'`: Emitted when an error occurs ### Lock Management ```javascript gate.setLock(lockName, state) // Set lock state (true = locked) gate.setLockWithTimeout(lock, state, ms) // Set lock with automatic timeout gate.resetAll(state) // Set all locks to same state ``` ### State Inspection ```javascript gate.getState() // Get complete state object gate.isUnlocked() // Check if gate is currently unlocked gate.getLockedCount() // Count of currently locked locks gate.getTotalLocks() // Total number of locks ``` ## State Object The `getState()` method returns: ```javascript { state: 'locked' | 'unlocked', locks: { [lockName: string]: boolean // true = locked, false = unlocked }, isLocked: boolean } ``` ## Error Handling All errors are emitted as events rather than thrown, preventing crashes: ```javascript gate.on('error', (errorMessage) => { console.error('Gate error:', errorMessage); // Handle error appropriately }); ``` ## Browser Support Works in both Node.js and browser environments: ```html <script src="jw-gate.js"></script> <script> const gate = new Gate(['condition1', 'condition2']); // Use normally </script> ``` ## When NOT to Use jw-gate - **Simple one-time coordination**: Use `Promise.all()` if you don't need individual progress updates - **Single async operation**: Use `async/await` instead - **Static boolean logic**: Use regular conditionals instead jw-gate excels when you need **immediate feedback on individual condition changes** or **reactive coordination of changing conditions**. ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## License MIT