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

@bernierllc/event-emitter

v1.0.5

Published

Type-safe event bus for inter-package communication

Downloads

426

Readme

@bernierllc/event-emitter

Type-safe event bus for inter-package communication with support for wildcard listeners, async handlers, and comprehensive error handling.

Installation

npm install @bernierllc/event-emitter

Features

  • 🎯 Type-safe event handling - Full TypeScript support with generics
  • 🌟 Wildcard listeners - Subscribe to all events with *
  • Async/await support - Handles both sync and async event handlers
  • 🛡️ Error handling - Configurable error capture and propagation
  • 🔄 One-time subscriptions - once() support for single-use listeners
  • 📊 Observable - Track listener counts and event names
  • 🚀 High performance - Parallel execution of async handlers
  • 🔌 Zero dependencies - Lightweight core with optional logger integration

Usage

Basic Event Subscription and Publishing

import { createEventEmitter } from '@bernierllc/event-emitter';

// Create event emitter
const events = createEventEmitter();

// Subscribe to events
const subscription = events.on('user.created', (data) => {
  console.log('User created:', data);
});

// Publish events
await events.emit('user.created', {
  userId: '123',
  email: '[email protected]'
});

// Unsubscribe
subscription.unsubscribe();

Type-Safe Events

interface UserCreatedEvent {
  userId: string;
  email: string;
  name: string;
}

// Type-safe subscription
events.on<UserCreatedEvent>('user.created', (data) => {
  // data is typed as UserCreatedEvent
  console.log(data.userId, data.email, data.name);
});

// Type-safe emission
await events.emit<UserCreatedEvent>('user.created', {
  userId: '123',
  email: '[email protected]',
  name: 'John Doe'
});

One-Time Subscriptions

// Subscribe once (automatically unsubscribes after first event)
events.once('app.ready', () => {
  console.log('App is ready!');
});

await events.emit('app.ready', {});
await events.emit('app.ready', {}); // Handler won't be called

Wildcard Listeners

// Listen to all events
events.on('*', (data) => {
  console.log('Event received:', data);
});

// Both specific and wildcard listeners will be called
await events.emit('user.created', { userId: '123' });
await events.emit('order.placed', { orderId: '456' });

Async Event Handlers

// Async handler
events.on('user.created', async (data) => {
  await sendWelcomeEmail(data.email);
  await createUserProfile(data.userId);
});

// emit() waits for all handlers to complete
await events.emit('user.created', {
  userId: '123',
  email: '[email protected]'
});

Error Handling

// With error capture (default)
const safeEmitter = createEventEmitter({ captureErrors: true });

safeEmitter.on('process', () => {
  throw new Error('Processing failed');
});

// Errors are captured and logged, other handlers continue
await safeEmitter.emit('process', {});

// Without error capture
const strictEmitter = createEventEmitter({ captureErrors: false });

strictEmitter.on('process', () => {
  throw new Error('Processing failed');
});

// Errors are thrown
await strictEmitter.emit('process', {}); // Throws error

Max Listeners

// Limit listeners per event
const limitedEmitter = createEventEmitter({ maxListeners: 10 });

// Add 10 listeners
for (let i = 0; i < 10; i++) {
  limitedEmitter.on('test', () => console.log(i));
}

// This will throw
limitedEmitter.on('test', () => console.log('too many')); // Error!

Managing Listeners

// Get listener count
const count = events.listenerCount('user.created');
console.log(`${count} listeners for user.created`);

// Get all event names
const eventNames = events.eventNames();
console.log('Active events:', eventNames);

// Remove all listeners for specific event
events.removeAllListeners('user.created');

// Remove all listeners for all events
events.removeAllListeners();

Configuration

const events = createEventEmitter({
  // Maximum listeners per event (0 = unlimited)
  maxListeners: 100,

  // Log events (requires @bernierllc/logger)
  logEvents: true,

  // Capture and suppress handler errors
  captureErrors: true
});

Environment Variables

EVENT_EMITTER_MAX_LISTENERS=100
EVENT_EMITTER_LOG_EVENTS=true
EVENT_EMITTER_CAPTURE_ERRORS=true

API Reference

EventEmitter

Main class for event management.

Methods

  • on<T>(eventType: string, handler: EventHandler<T>): EventSubscription - Subscribe to event
  • once<T>(eventType: string, handler: EventHandler<T>): EventSubscription - Subscribe once
  • off(subscription: EventSubscription): void - Unsubscribe from event
  • emit<T>(eventType: string, data: T): Promise<void> - Publish event
  • removeAllListeners(eventType?: string): void - Remove all listeners
  • listenerCount(eventType: string): number - Get listener count
  • eventNames(): string[] - Get all event names

EventSubscription

Subscription object returned by on() and once().

interface EventSubscription {
  id: string;                    // Unique subscription ID
  eventType: string;             // Event type
  handler: EventHandler;         // Handler function
  once: boolean;                 // One-time subscription?
  unsubscribe: () => void;       // Unsubscribe function
}

EventHandler<T>

Event handler function type.

type EventHandler<T = any> = (data: T) => void | Promise<void>;

createEventEmitter(options?: EventEmitterOptions): EventEmitter

Factory function for creating event emitters.

Integration Status

Logger Integration

Status: Optional (recommended for event tracking)

Justification: While this package can function without logging, it's highly recommended to integrate @bernierllc/logger for event tracking and error monitoring. Event emitters often need to log event emissions, listener registrations, and errors to help with debugging and monitoring event-driven workflows. However, the package is designed to work without logging for environments where logging isn't available.

Pattern: Optional integration - package works without logger, but logger enhances event tracking capabilities.

Example Integration:

import { Logger } from '@bernierllc/logger';
import { EventEmitter } from '@bernierllc/event-emitter';

const logger = new Logger({ service: 'event-emitter' });
const emitter = new EventEmitter({ logger });

emitter.on('error', (error) => {
  logger.error('Event emitter error', error);
});

NeverHub Integration

Status: Optional (recommended for distributed event bus)

Justification: While this package can function without NeverHub, it's highly recommended to integrate @bernierllc/neverhub-adapter for distributed event bus capabilities. Event emitters often need to coordinate events across services, and NeverHub provides a service mesh for publishing events that other services can subscribe to. However, the package is designed to work without NeverHub for simpler use cases.

Pattern: Optional integration - package works without NeverHub, but NeverHub enhances distributed event capabilities.

Example Integration:

import { NeverHubAdapter } from '@bernierllc/neverhub-adapter';
import { EventEmitter } from '@bernierllc/event-emitter';

const neverhub = new NeverHubAdapter({ service: 'event-emitter' });
const emitter = new EventEmitter();

// Bridge local events to NeverHub
emitter.on('user:created', async (data) => {
  await neverhub.publish('user.created', data);
});

Docs-Suite Integration

Status: Ready

Format: TypeDoc-compatible JSDoc comments are included throughout the source code. All public APIs are documented with examples and type information.

Use Cases

Inter-Package Communication

// In cache-manager package
events.emit('cache.invalidated', { key: 'user:123' });

// In data-sync package
events.on('cache.invalidated', async (data) => {
  await refreshData(data.key);
});

State Management

// State changes
events.on('state.changed', (newState) => {
  console.log('State updated:', newState);
});

await events.emit('state.changed', { status: 'active' });

Task Lifecycle Events

// Task queue events
events.on('task.started', (task) => console.log('Started:', task.id));
events.on('task.completed', (task) => console.log('Completed:', task.id));
events.on('task.failed', (task) => console.log('Failed:', task.id));

Workflow Orchestration

// Multi-step workflow
events.on('workflow.step.completed', async (data) => {
  if (data.nextStep) {
    await events.emit('workflow.step.start', { step: data.nextStep });
  }
});

Performance

  • Parallel Execution: Async handlers execute in parallel via Promise.all()
  • Memory Efficient: Automatic cleanup of empty listener sets
  • Fast Lookups: O(1) listener retrieval using Map data structure
  • Zero Dependencies: Minimal bundle size (~2KB gzipped)

Testing

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run tests in watch mode
npm run test:watch

See Also

License

Copyright (c) 2025 Bernier LLC. All rights reserved.