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/handler

v0.15.1

Published

Nwire — the operation primitive. Typed, callable, hookable value built on @nwire/hooks. defineHandler / defineError / defineResource / response factories / framework errors. Standalone; every transport (HTTP, queue, MCP, CLI) speaks the same handler shape

Downloads

5,919

Readme

@nwire/handler

The operation primitive — one typed, callable, hookable value usable from every transport (HTTP, queue, MCP, CLI, direct test).

pnpm add @nwire/handler

Why

Every backend ends up with the same shape: parse input, run a function with some deps, return a typed response or throw a typed error. Different teams wrap it in different abstractions (controllers, route handlers, command handlers, tRPC procedures, NestJS providers). They're all the same thing.

@nwire/handler is that thing, distilled: a callable, type-generic value. It's a Hook from @nwire/hooks underneath, so it gets .use() middleware, .on() listeners, .tap() observation, signal cancellation, and replay — for free.

Three rules:

  1. Ctx is generic. Whatever you pass to .run(ctx) is what the handler body destructures, fully typed.
  2. App pins ctx once. defineHandlerWith<AppExtras>() returns a factory you import everywhere; handlers never annotate ctx.
  3. No global pollution. Plugin contributions are imported types, intersected at the app boundary. Nothing is declare module-ed.

Surface

// The primitive
function defineHandler<TInputSchema, TOutput, TExtras extends object = object>(
  name: string,
  config: HandlerConfig<TInputSchema, TOutput, TExtras>,
): HandlerDefinition<TInputSchema, TOutput, TExtras>;

// The app-boundary factory — pins TExtras once
function defineHandlerWith<TExtras extends object>(): typeof defineHandler bound to TExtras;

// Type alias for declaring a handler's ctx shape inline
type Ctx<TInput, TExtras extends object = object> =
  { input: TInput; signal: AbortSignal; set: …  } & TExtras;

// Resources, errors, response factories
function defineResource(name, { schema, public, examples });
function defineError({ code, status, summary });   // callable: throw NotFound({ resourceId });
ok / created / accepted / noContent / notModified / gone

// Built-in errors
Unauthorized / Forbidden / NotFound / Conflict / Gone / BadRequest

// Type helpers
HandlerInput<H> / HandlerOutput<H> / HandlerExtras<H>

Consumer example

app/define.ts — one file, pinned once:

import { defineHandlerWith, type Ctx } from "@nwire/handler";
import type { AuthCradle } from "@nwire/auth";
import type { DbCradle } from "@nwire/drizzle";

// Plugins export type fragments; app composes its cradle:
export type AppCradle = AuthCradle &
  DbCradle & {
    config: AppConfig;
    logger: Logger;
  };

// Extras the wires put onto ctx at request time:
type AppExtras = {
  logger: Logger;
  pg: PgClient;
  user: { id: string; role: string; balance: number };
};

export const defineAction = defineHandlerWith<AppExtras>();
export type AppCtx<I = unknown> = Ctx<I, AppExtras>;

app/orders/buy-wash.action.ts — a complete handler:

import { z } from "zod";
import { defineResource, defineError, NotFound } from "@nwire/handler";
import { defineAction } from "@/define";

const Wash = defineResource("Wash", {
  schema: z.object({ id: z.string(), name: z.string(), price: z.number() }),
  public: ["id", "name", "price"],
});

const InsufficientFunds = defineError({
  code: "INSUFFICIENT_FUNDS",
  status: 402,
  summary: "balance too low for this wash",
});

export const BuyWash = defineAction("buyWash", {
  input: z.object({ washId: z.string() }),
  returns: Wash,
  errors: [NotFound, InsufficientFunds],
  handler: async ({ input, logger, pg, user, signal }) => {
    const row = await pg.query("SELECT * FROM washes WHERE id=$1", [input.washId], { signal });
    if (!row) throw NotFound({ resourceId: input.washId });
    if (user.balance < row.price) throw InsufficientFunds({ have: user.balance, need: row.price });
    logger.log(`${user.id} bought ${row.id}`);
    return row;
  },
});

// Per-handler middleware via the hook substrate
BuyWash.use(async (ctx, next) => {
  const t = performance.now();
  await next();
  ctx.logger.log(`buyWash ${(performance.now() - t).toFixed(1)}ms`);
});

wires/api.wire.ts — boot, container, request composition:

import { createContainer } from "@nwire/container";
import type { AppCradle } from "@/define";
import { BuyWash } from "@/orders/buy-wash.action";

const root = createContainer<AppCradle>();
root.register("config", { port: 3000 });
root.register("logger", () => ({ log: (s) => console.log(s) }));
root.register("db.pg", () => new PgClient(root.cradle.config.port));

const server = createServer(async (req, res) => {
  const user = await authenticate(req);

  const result = await BuyWash(JSON.parse(await readBody(req))).run({
    ctx: {
      logger: root.cradle.logger,
      pg: root.cradle["db.pg"],
      user,
    },
    signal: req.signal,
  });
  res.writeHead(201).end(JSON.stringify(result));
});

app/orders/buy-wash.test.ts — unit test, no container:

import { expect, test } from "vitest";
import { BuyWash, InsufficientFunds } from "./buy-wash.action";

test("rejects when balance < price", async () => {
  await expect(
    BuyWash({ washId: "w1" }).run({
      ctx: {
        logger: { log: () => {} },
        pg: { query: async () => ({ id: "w1", name: "Quick", price: 100 }) },
        user: { id: "u-1", role: "user", balance: 5 },
      },
    }),
  ).rejects.toMatchObject({
    code: "INSUFFICIENT_FUNDS",
    status: 402,
    context: { have: 5, need: 100 },
  });
});

The four contracts in one model

| Concern | How | | ---------------- | ------------------------------------------------------------------------------------------- | | Input validation | input: zodSchema — parsed before the handler body runs | | Output shape | returns: Resource \| ResponseSpec[] — narrows the handler's return type, drives OpenAPI | | Throws | errors: [NotFound, …] — typed throwables, drive 4xx OpenAPI bodies | | Cancellation | signal: AbortSignal on ctx — forward to fetch/pg/etc., bail via signal.throwIfAborted() |

Cancellation in practice

The signal flows through nested .run() calls automatically:

const ParentOp = defineAction("parent", {
  handler: async ({ signal }) => ChildOp().run({ signal }),
});
// caller-supplied signal → ParentOp.run({ signal }) → ChildOp.run({ signal })
// abort the caller's controller → both bail

When no signal is supplied, ctx gets a non-aborting placeholder — code can always pass ctx.signal through to fetch/pg/redis without null checks.

Errors

throw NotFound; // bare value (it IS an Error)
throw NotFound({ resourceId: input.id }); // callable, contextualised clone

Caught at the transport layer — REST → status + { code, message, context }, GraphQL → extensions.code, CLI → exit status + stderr.

Built on @nwire/hooks

Every handler is a Hook<HandlerRunCtx> underneath. .use(), .on(), .tap(), .runDetailed() all work. The user's handler function is the innermost chain step, registered at construction with Number.MIN_SAFE_INTEGER priority so every .use() you add wraps around it. Telemetry, replay, observation come along for free.

Scope

Standalone — no DI, no events, no logger, no transport opinions. App composes ctx by value; transports compose ctx at boot. Forge / HTTP / queue / MCP wires layer on top.