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

@supercat1337/event-emitter

v2.0.1

Published

Lightweight typed event emitter with Symbol support, one-time listeners, and zero dependencies. Works in Node.js and browsers.

Readme

@supercat1337/event-emitter 🐈⚡

A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript with advanced capabilities and industry-leading type safety.


Features

  • Dual Implementation – Choose between lightweight EventEmitterLite or full-featured EventEmitter.
  • First-class TypeScript – Deep generic support for event names and argument validation.
  • Promise-based Waiting – Native waitForEvent and waitForAnyEvent with built-in timeout support.
  • Lifecycle Tracking – Monitor when events gain or lose listeners (onHasEventListeners, onNoEventListeners).
  • Centralized Error Handling – Intercept and handle listener errors globally via onListenerError.
  • Memory-Efficient – Automatic cleanup of unused event keys and dedicated destroy() lifecycle.
  • Immutable Emission – Listener arrays are snapshotted during emission, making it safe to modify listeners inside callbacks.
  • Modern ES2022+ – Leverages native private fields and optimized logic.

Installation

npm install @supercat1337/event-emitter

Quick Start

Using the full-featured EventEmitter

import { EventEmitter } from '@supercat1337/event-emitter';

// Define your event types
type AppEvents = 'user:created' | 'user:deleted' | 'notification:sent';

const emitter = new EventEmitter<AppEvents>();

// Subscribe with an auto-generated unsubscribe function
const unsubscribe = emitter.on('user:created', (userData) => {
    console.log('User created:', userData);
});

// Emit events
emitter.emit('user:created', { id: 1, name: 'John' });

// Clean up
unsubscribe();

Using the lightweight EventEmitterLite

For performance-critical paths where you only need core on/off/emit logic:

import { EventEmitterLite } from '@supercat1337/event-emitter';

const lite = new EventEmitterLite();
lite.on('data', (msg) => console.log(msg));
lite.emit('data', 'Hello, World!');

API Reference

Core Classes

EventEmitterLite<Events>

The core implementation focused on performance. Use this when you don't need async waiting or lifecycle hooks.

EventEmitter<Events>

Extends EventEmitterLite with the full suite of advanced features: async promises, tracking hooks, and instance destruction.

Common Properties

| Property | Type | Description | |---------------|-----------|--------------------------------------------------------------------------------------------| | logErrors | boolean | If true, errors in listeners are logged to the console. (Default: true)Even when false, errors can still be caught via onListenerError. | | isDestroyed | boolean | (EventEmitter only) Returns true if the instance has been destroyed. |

Common Methods

| Method | Description | |---------------------------------|---------------------------------------------------------------------------------------| | on(event, listener) | Subscribes to an event. Returns an unsubscribe() function. | | once(event, listener) | Subscribes for a single invocation, then auto-removes. | | off(event, listener) | Removes a specific listener. Alias for removeListener. | | emit(event, ...args) | Triggers all listeners for the event with provided arguments. | | removeListener(event, listener) | Same as off. |

Advanced Methods (EventEmitter only)

| Method | Description | |-------------------------------------------------|-------------------------------------------------------------------------------------------------------------| | waitForEvent(event, [maxWaitMs]) | Returns a Promise<boolean>. Resolves true when the event fires. If maxWaitMs is reached, resolves false. | | waitForAnyEvent(events, [maxWaitMs]) | Waits for the first occurring event from an array of event names. | | clear() | Removes all listeners from all events. | | clearEventListeners(event) | Removes all listeners for a specific event. | | destroy() | Completely wipes the instance. Clears all listeners and prevents further emissions. | | onHasEventListeners(callback) | Invoked when any event gains its first listener. Receives the event name. | | onNoEventListeners(callback) | Invoked when any event loses its last listener. Receives the event name. | | onListenerError(callback) | Invoked when any listener throws an error. Receives (error, eventName, ...args). |

Example: lifecycle tracking

emitter.onHasEventListeners((event) => {
    console.log(`First listener added for ${event}`);
});

emitter.onNoEventListeners((event) => {
    console.log(`Last listener removed for ${event}`);
});

emitter.on('foo', () => {}); // logs: First listener added for foo
emitter.off('foo', () => {}); // logs: Last listener removed for foo

Example: error handling

emitter.onListenerError((error, event, ...args) => {
    console.error(`Error in ${event}:`, error);
    myLoggingService.report(error);
});

emitter.on('crash', () => { throw new Error('boom'); });
emitter.emit('crash'); // error is caught, passed to the callback, and (if logErrors=true) also logged.

TypeScript Usage

1. Simple string union

Good for events that don't pass complex data.

type MyEvents = 'start' | 'stop' | 'tick';
const emitter = new EventEmitter<MyEvents>();
emitter.emit('start'); // OK
emitter.emit('unknown'); // Type error

2. Full type safety (recommended)

Define a record mapping event names to argument tuples. This gives you autocompletion and argument validation.

type MyEvents = {
    'user:updated': [id: number, name: string];
    'ping': []; // no arguments
};

const emitter = new EventEmitter<MyEvents>();

// Listener gets correct argument types
emitter.on('user:updated', (id, name) => {
    console.log(id, name);
});

// Emit is type-checked
emitter.emit('user:updated', 1, 'Alice'); // ✅
emitter.emit('user:updated', '1');        // ❌ Type error

3. Using keyof with a separate type map

If you prefer to keep event names as a union but still want argument types, combine a union with a mapped type:

type EventNames = 'start' | 'stop' | 'tick';
type EventMap = {
    [K in EventNames]: K extends 'tick' ? [counter: number] : [];
};

const emitter = new EventEmitter<EventMap>();
emitter.on('tick', (counter) => console.log(counter)); // counter is number

Error Handling

By default, all listener errors are caught to prevent the emitter from crashing.

  • If logErrors is true (default), errors are printed to console.error.
  • Even with logErrors: false, you can still intercept errors globally using onListenerError for custom logging or reporting.

Performance Notes

  • Snapshotted iteration: Listener arrays are copied before emission. If a listener calls off() on itself during emission, the current cycle continues safely without skipping elements.
  • Zero dependencies: Ultra-small bundle size.
  • Memory management: Event keys are deleted from the internal store when the last listener is removed, reducing memory usage over time.

Browser Support

| Feature | Support | |------------|-----------------| | ES2022+ | Modern browsers | | TypeScript | 4.0+ | | Node.js | 14+ |


License

MIT © supercat1337