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

@nwire/hooks

v0.15.1

Published

Nwire — the universal dispatch primitive. One hook() accepts both .use() (sequential koa-compose chain) and .on() (parallel listener) attachments. Lazy-memoized chain compose, per-step observation tap, AsyncLocalStorage-linked run topology. In-process onl

Readme

@nwire/hooks

The universal dispatch primitive. One hook() accepts both a sequential chain (.use) and parallel listeners (.on); every run is observable end-to-end and cancellable via AbortSignal.

pnpm add @nwire/hooks

Why

Every framework reinvents the same three patterns: middleware chains, event listeners, lifecycle hooks. They all need ordering, cancellation, tracing, and replay. @nwire/hooks is the one substrate the rest of Nwire builds on — handlers, transports, plugin lifecycle, framework events all use the same primitive.

About 100 lines of surface; sub-microsecond per-step overhead; standalone.

Surface

import { hook } from "@nwire/hooks";

const onRequest = hook<RequestCtx>("app.onRequest");

onRequest.use(fn, { priority?, name? });    // chain middleware — sequential, can short-circuit
onRequest.on(fn, { when?, priority? });     // listener — parallel, observes final ctx
onRequest.off(fn);                          // detach
onRequest.tap(observer);                    // subscribe to per-step observations

await onRequest.run(ctx, { signal? });      // run chain + listeners
await onRequest.runDetailed(ctx, opts);     // same + return full step trace

Every ctx that flows through a hook gets two fields injected (non-enumerable, so structuredClone walks past them):

interface BaseHookCtx {
  readonly signal: AbortSignal; // caller-supplied or non-aborting placeholder
  set<K, V>(key: K, value: V): asserts this is this & { [P in K]: V };
}

signal is the cooperative cancellation contract — every async step can forward it (fetch(url, { signal }), pg.query(…, { signal })) and react via signal.throwIfAborted().

.set() is the typed widening primitive — middleware contributes a new field, the TS assertion narrows ctx for downstream steps.

Consumer example

import { hook } from "@nwire/hooks";

type ReqCtx = { url: string; method: string; status?: number };
const onRequest = hook<ReqCtx>("app.onRequest");

// Logger — plain async fn, wraps + measures
onRequest.use(async (ctx, next) => {
  const t = performance.now();
  console.log(`→ ${ctx.method} ${ctx.url}`);
  await next();
  console.log(`← ${ctx.status} (${(performance.now() - t).toFixed(1)}ms)`);
});

// Auth — contributes via typed .set, short-circuits on rejection
onRequest.use(
  async (ctx, next) => {
    const token = readToken(ctx.url);
    if (!token) {
      ctx.set("status", 401); // short-circuit (no next())
      return;
    }
    ctx.set("userId", await verify(token)); // ctx widened to include userId
    await next();
  },
  { priority: 100 },
);

// Metrics — listener, never mutates
onRequest.on((ctx) => metrics.tick(`http.${ctx.method}.${ctx.status}`));

await onRequest.run({ url: "/api/orders", method: "GET" });

Cancellation in practice

const ac = new AbortController();
setTimeout(() => ac.abort(new Error("user cancelled")), 5000);

await onRequest.run(ctx, { signal: ac.signal });
// inside any step: signal.throwIfAborted() bails;
// the abort propagates through nested .run() calls too

Combine with timeouts:

const timeout = AbortSignal.timeout(2000);
await onRequest.run(ctx, { signal: AbortSignal.any([userSignal, timeout]) });

Observability

.tap(observer) emits a StepObservation per chain/listener phase (start / end / error) with timing, runId, parentRunId. runDetailed() returns the full trace + outcome. Used by @nwire/scan to emit .nwire/hooks.json and by Studio for live tap streams.

Used by

@nwire/handler is built on hook<HandlerRunCtx>(). @nwire/forge runtime, framework-event bus, plugin lifecycle, and every transport in Nwire share the same substrate.

Scope

Standalone, in-process. Cross-process dispatch lives in @nwire/bus. Use this for middleware chains, lifecycle hooks, and pub-sub-within-process.