npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jw-gate

v2.0.0

Published

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

Downloads

12

Readme

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

npm install jw-gate

Quick Start

UI Progress Tracking

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

// 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

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

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

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

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

new Gate(lockNames, initialState = false)
  • lockNames: Array of strings representing lock names
  • initialState: Boolean indicating initial state of locks (default: false = unlocked)

Event Management

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

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

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:

{
  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:

gate.on('error', (errorMessage) => {
  console.error('Gate error:', errorMessage);
  // Handle error appropriately
});

Browser Support

Works in both Node.js and browser environments:

<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