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

eventemitterzero

v1.0.0

Published

The smallest possible event emitter built on native JS APIs and providing modern DX

Readme

npm version Downloads Uses TypeScript

eventemitterzero

This event emitter is the smallest possible event emitter, providing a fully typesafe API and modern features.

Here is the implementation (with type annotations removed for clarity):

export class EventEmitter{
  private eventTarget = new EventTarget();

  emit = (event, eventObject) => {
    this.eventTarget.dispatchEvent(
      new CustomEvent(event, { detail: eventObject })
    );
  };

  on = (event, listener) => {
    const wrappedListener = (e) => {
      listener(e.detail);
    };
    this.eventTarget.addEventListener(event, wrappedListener);
    return () => {
      this.eventTarget.removeEventListener(event, wrappedListener);
    };
  };
}

Benefits

  • ESM
  • Small implementation
  • Zero dependencies
  • Typescript
    • Checks that event objects match the event names.
    • Allows void event types to either 1) omit the second emit arg, or 2) pass an undefined/optional typed arg.
    • Checks that the listener function params match the event object type.
  • Uses native EventTarget and CustomEvent APIs to propagate events.
  • The on function returns a deregistration function, as opposed to having an off function. This helps avoid memory leak bugs, and overall results in cleaner code.
    • The deregistration function can safely be called multiple times.
  • Uses the event object pattern, which is preferable to using multiple event args.
  • Allows events without an event object.
  • No binding needed for function members.
  • No silly aliases or context binding features.
  • This package is built by GitHub Actions with provenance.

Usage

import { EventEmitter } from 'eventemitterzero';

// Create an event emitter instance, passing the interface describing all of its events.
const myEventEmitter = new EventEmitter<{
  eventA: IEventA;
  eventB: IEventB;
  eventC: void; // Setting `void` here means that the event has no event object/value.
}>();

// To emit an event:
myEventEmitter.emit('eventA', { ... });
myEventEmitter.emit('eventB', { ... });
myEventEmitter.emit('eventC');
myEventEmitter.emit('eventC', undefined); // this also works (typically this would be a variable instead of literal `undefined`).

// To listen for events
const unlisten = myEventEmitter.on('eventA', (eventObject) => {
  // handle eventA
});
// To clean up, call unlisten. This removes the registered event listener, avoiding memory leaks.
unlisten();

There is no once function, but you can easily achieve it like this:

const unlisten = myEventEmitter('eventA', () => {
  unlisten();
});
// Later, clean up if never called:
unlisten();

Using this pattern you can do a conditional once — only deregistering when a certain condition is the case.