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

handlery

v0.2.7

Published

Better event handling for ALL emitters

Readme

handlery (like emittery but for 'handle') is a super clean and easy way to handle any kind of events with class-based handlers (and decorators!). It works with all kinds of event emitters (emittery, nodejs' EventEmitter...) out of the box! Just look at it:

@register()
class UserHandler extends EventHandler {
  @on('user.add')
  public handleAddUser(user: Events['user.add'], ctx: HandlerContext) {
    ctx.emitter.emit('user.add.response', { ... })
    // ...
  }
}

It requires typescript decorators support (modern, ECMA decorators. Not Decorators.Legacy etc.) And an event emitter like emittery or node's built-in EventEmitter.

Installation

It's on npm!

npm install handlery
# or
pnpm add handlery
# or
yarn add handlery

Usage

(Check out our docs)

First, you need some event emitter. handlery works with emittery, the nodejs event emitter and many more. Let's use emittery as an example.

First, define some events. Event IDs can be strings, numbers or symbols

export type AppEvents = {
  'user.add': { name: string };
  'user.remove': { id: string };
};

const EMITTERY = new Emittery<AppEvents>();

then, import the right adapter from handlery/adapters, in this case emitteryAdapter and pass your emittery instance. Pass the converted emitter to handlery

const emitter = emitteryAdapter(EMITTERY);
const { on, subscribe, EventHandler } = handlery(emitter);

The returned Handlery type has the functions on, subscribe and a class EventHandler that can be used to create your handler classes:

@subscribe()
export class UserHandler extends EventHandler {
  @on('user.add')
  public handleAddUser(data: AppEvents['user.add']) {
    console.log('User created:', data.name);
  }

  @on('user.remove')
  public handleRemoveUser(data: AppEvents['user.remove']) {
    console.log('User removed with ID:', data.id);
  }
}

The listener functions are created right away, and start listening instantly due to the subscribe decorator on the class. If you leave that subscribe decorator away, you can always register and subscribe the class later with

UserHandler.register();
// or
UserHandler.subscribe();

The difference between those is that register creates an instance of the class and subscribe actually starts listening to events. If you use the @subscribe() decorator, the handler class will be registered and subscribed right away. Of course, you can also call UserHandler.unsubscribe() later on.

Different emitter adapters

The emitteryHandler function I showed you above is a wrapper around the emittery adapter, which can be imported from handlery/adapters. You could write the same thing as

import handlery from 'handlery';
import { emitteryAdapter } from 'handlery/adapters';

const EMITTERY = new Emittery<AppEvents>();

const emitter = emitteryAdapter(EMITTERY);
const { on, EventHandler } = handlery(emitter);

The purpose of the adapters is to take any form of event emitter and turn it into a simpler, handlery-compatible version. This is done to support all kinds of emitter typings, and different function signatures. For example, emittery's on method can take an abort signal as part of an options parameter, returns an unsubscribe function and can be async. The nodejs EventEmitter on only has two arguments, returns the EventEmitter and is always synchronous.

To ensure this library is 'emitter-agnostic', we ensure a single, easy-to-use format.

The node EventEmitter adapter

Node's EventEmitter is special in that it passes multiple parameters to it's handlers/emitters. Where in emittery you would do something like

emitter.emit('myEvent', { myData1: 42, myDataTwo: 'test' });

In node, you would pass the data as an argument list. Because of that, handlers receive arguments as an array!

class MyHandler extends EventHandler {
  @on('myEvent')
  public handleTestEvent2(data: [number, string]) {
    console.log('Handled myEvent:', data);
  }
}

The good thing is that handlery is able to infer all types from the EventEmitter. So if you typed that, you're good!

type Events = {
  testEvent1: [string];
  testEvent2: [number, string];
};

const eventEmitter = new EventEmitter<Events>();
const adapter = nodeAdapter(eventEmitter);
const { on, EventHandler } = handlery(adapter);

// `on` and `EventHandler` know that the `data` prop is of type `[string]` or `[string, number]`.
class TestHandler extends EventHandler {
  @on('testEvent1')
  public handleTestEvent1(data: [string]) {
    console.log('Handled testEvent1:', data);
  }

  @on('testEvent2')
  public handleTestEvent2(data: [number, string]) {
    console.log('Handled testEvent2:', data);
  }
}