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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@infra-blocks/emitter

v0.2.1

Published

This package offers utilities related to Node.Js' event emitters.

Readme

ts-emitter

Build Release codecov

This package contains a few type safe emitter related utilites. First, it does expose yet another freaking emitter. It also offers various types, such as the EmitterLike interface, to maximize code reuse across projects. There is also a base class that client code can implement to automatically inherit the on and once methods, properly typed.

Another emitter

Why? Because I thought that the Node.js packaged one lacked some features (async emission, for example), and the ones I'd been looking at in other NPM packages were missing just a little bit of stuff. I don't claim that this is the best, nor the fastest event emitter (although it is quite simple in its implementation, so it could be among the fastest). I do claim, however, that it is configurable as fuck.

No nonsense (Keith Peterson)

The only methods we keep from the Node.js interface are on, once and emit. We do away with the aliases (such as addListener) and pretty much every other method I've never actually had to use in my career.

The default behavior when emitting an error event without any listener registered is the same as any other fucking event: nothing happens. There. Not trying to be smart here.

Emit as a dependency injection

This emitter is designed such that the emit method can be easily switched out to change the semantics. By default, when using the base factory function, it behaves just like the Node.js one: when an event is emitted, listeners are called in sequence and their results are completely ignored (vanilla behavior before the support for handling rejections).

import { Emitter } from "@infra-blocks/emitter";

type MyEvents = {
  gogo: (gadget: string) => void;
}

const emitter = Emitter.create<MyEvents>();
emitter.on("gogo", (gadget) => console.log("gadget is: ", gadget));
emitter.on("gogo", () => console.log("was that show ever good?"));
emitter.emit("gogo", "retractablePenus");

// Prints out:
// gadget is: retractablePenus
// was that show ever good?

In addition, the default strategy also exports weird members on emit, just to showcase of what you could do yourself if you wanted to support more than one strategy for the same emitter.

// Wraps all listener results in a Promise.all and returns that.
const results = await emitter.emit.awaitAll("gogo", "retractablePenus");
// Same but for Promise.allSettled. Try it. The typing works esé.
const results = await emitter.emit.awaitAllSettled("gogo", "retractablePenus");
// They are executed one at a time, when a listener is only invoked if all
// previous listeners have already resolved.
await emitter.emit.awaitEach("gogo", "retractablePenus");

If you know you always want the awaitAll logic, for example, and don't want the weird emit attributes, you can also create an emitter that does that by default:

type MyEvents = {
  // Does something asynchronous with the poop quantity.
  poop: (quantityInLitres: number) => Promise<boolean>;
}

const emitter = Emitter.awaitingAll<MyEvents>();
// ...
const results: Array<boolean> = await emitter.emit("poop", 2.3); // My kid shits a lot. Notice how emit now returns a promise?

You can customize the behavior of emit by passing a strategy factory upon construction:

// Creates an emitter that replays events once. So a single emit results in the invocation
// of the listeners twice. I don't know why you would want that, but it is possible to do.
const emitter = Emitter.withStrategyFactory<MyEvents>((listeners) => {
  return <K extends keyof E>(event: K, ...args: Parameters<E[K]>) => {
    for (const _ of listeners.invocations(event, ...args)){
      // Forget that result already.
    }
    for (const _ of listeners.invocations(event, ...args)){
      // Run it twice.
    }
  }
})

Nuff said about that emitter.

EmitterLike interface

The EmitterLike interface is defined as such:

export type Events = {
  [key: string]: (...args: any[]) => unknown;
};

export interface EmitterLike<E extends Events> {
  on<Event extends keyof E>(event: Event, handler: E[Event]): this;
  once<Event extends keyof E>(event: Event, handler: E[Event]): this;
}

It's basically to provide a stub for classes that support event registration, but handle emission internally. The interface is automatically type safe with regards to the event type. In addition, and EmitterLikeBase class is provided if you'd like the straigtforward implementations.

Here is a simple example usage:

// MyEmitterLike class allows subscriptions to "connect", "data" and "disconnect" events.
// NOTE: this should be a "type" and not an "interface" to get string indexing for free, although
// it is also possible with an interface (that extends Record<...>).
export type MyEvents = {
  connect: () => void;
  data: (stuff: number) => Promise<void>;
  disconnect: () => void;
}

// The generics take care of the type-safety, and the emitter like boiler-plate logic
// is taken care of.
export class MyEmitterLiker extends EmitterLikeBase<MyEvents, DefaultStrategy<MyEvents, AlwaysVoid<MyEvents>>> {
  private readonly client: MyClient;

  private constructor() {
    super({ emitter: Emitter.create<MyEvents>() })
    this.client = new MyClient();
  }
  
  async callMe() {
    await this.client.connect();
    // The `emit` method is only available within the implementer with the `protected` visibility,
    // and it is type-safe with regards to the expected handler arguments.
    this.emit("connect");
    for await (const stuff of this.client.readStuff()) {
      // The same emit methods that the emitter supports are offered in the subclass.
      await this.emit.awaitEach("data", stuff);
    }
    await this.client.disconnect();
    this.emit("disconnect");
  }
}

const myEmitterLike = new MyEmitterLike();
// The subscription methods are available.
myEmitterLike.once("connect", () => console.log("connected!"));
myEmitterLike.once("disconnect", () => console.log("disconnected!"));
myEmitterLike.on("data", async (stuff) => await db.storeStuff(stuff));
await myEmitterLike.callMe();