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

@lacspace/events

v1.0.0

Published

A fully type-safe event emitter / tiny pub-sub for Node and the browser — declare an events map and get compile-time-checked on/emit, error-isolated dispatch, once, waitFor and wildcard listeners. Zero-dependency, isomorphic, tree-shakeable.

Readme

@lacspace/events

A fully type-safe event emitter / tiny pub-sub for Node and the browser. Declare an events map once and every on/emit/waitFor is checked at compile time — no string typos, no any payloads. Zero dependencies, isomorphic, tree-shakeable.

Think mitt / nanoevents / eventemitter3, but strongly typed and with a few ergonomics: error-isolated dispatch, once, waitFor, wildcard listeners, and an unsubscribe handle from every subscription.

npm i @lacspace/events

Quick start

import { createEmitter } from "@lacspace/events";

type Events = {
  login: { userId: string };
  logout: void;
  message: string;
};

const bus = createEmitter<Events>();

const off = bus.on("login", ({ userId }) => console.log("hi", userId));
bus.emit("login", { userId: "u1" }); // ✓ typed
// bus.emit("login", { user: "u1" }); // ✗ compile error — wrong payload shape
// bus.emit("logn", ...);             // ✗ compile error — unknown event

off(); // unsubscribe

The event name and payload type flow automatically: on("login", h) infers h's argument as { userId: string }, and emit("login", p) requires p to match exactly.

Error isolation

A handler that throws never stops the others — every listener for an event runs. Route thrown errors to an optional onError:

const bus = createEmitter<Events>({
  onError: (err, event) => console.error(`listener for ${String(event)} threw`, err),
});

Without onError, listener errors are swallowed so one bad handler can never break emit.

Safe to mutate during emit

Subscribing or unsubscribing from inside a handler is fine. emit iterates over a snapshot taken before dispatch, so a listener added mid-emit won't fire for the in-flight event, and a removed one won't be called after removal.

once & waitFor

bus.once("login", (u) => console.log("first login", u)); // fires exactly once

const { userId } = await bus.waitFor("login"); // promise resolves on next login

Wildcard listeners

const off = bus.onAny((event, payload) => log(String(event), payload));
// …
off();

API

| Export | What | |---|---| | createEmitter<E>(options?) | Build an Emitter<E>. options.onError?(error, event) catches listener throws. | | emitter.on(event, handler) | Subscribe. Returns an Unsubscribe function. | | emitter.once(event, handler) | Subscribe for one dispatch, then auto-remove. Returns Unsubscribe. | | emitter.off(event, handler?) | Remove one handler, or all handlers for event if omitted. | | emitter.emit(event, payload) | Dispatch to every listener (error-isolated, snapshot-safe). | | emitter.listenerCount(event?) | Count for one event, or the grand total if omitted. | | emitter.removeAllListeners(event?) | Clear one event, or everything (incl. wildcard). | | emitter.waitFor(event) | Promise that resolves with the next payload. | | emitter.onAny(handler) / offAny(handler) | Wildcard listener over every event. onAny returns Unsubscribe. |

Types: EventMap, Emitter<E>, EmitterOptions<E>, Handler<T>, WildcardHandler<E>, Unsubscribe.

Use void (or undefined) as an event's payload type for events that carry no data.

Why

  • Type-safe — the events map is the single source of truth; typos and wrong payloads are compile errors.
  • Zero dependencies — nothing to audit, tiny install, tree-shakeable.
  • Isomorphic — identical API in Node, the browser, edge runtimes and workers.
  • Robust dispatch — errors are isolated per listener; mutating listeners during emit is safe.

Licence

Lacspace Free Licence v1.0 — see LICENSE. Part of the @lacspace developer platform.