@nwire/hooks
v0.7.1
Published
Nwire — the universal dispatch primitive. One hook() accepts both .use() (sequential koa-compose chain) and .on() (parallel listener) attachments. Built on emittery + a ~30 LOC composer. Standalone, in-process only, ~100 LOC of surface.
Readme
@nwire/hooks
Universal dispatch primitive. One
hook()accepts both.use()(sequential chain) and.on()(parallel listener) attachments.
What it is
Every middleware, lifecycle phase, event subscription, and plugin attachment in Nwire is built from one primitive: a named hook that runs a koa-compose chain first, then a parallel listener fan-out via Promise.allSettled. Built on emittery (~3KB) plus a ~30 LOC composer. No other Nwire package required.
Install
pnpm add @nwire/hooksWithin nwire-app
For developers using this package as part of the Nwire stack. You usually never call hook() yourself — @nwire/app lifecycle, @nwire/forge events, and every plugin's middleware all build on it. Touch this package when authoring a plugin that needs custom hooks.
import { createHooks, hook } from "@nwire/hooks";
const lifecycle = createHooks({
boot: hook<EndpointCtx>("endpoint.boot"),
ready: hook<EndpointCtx>("endpoint.ready"),
shutdown: hook<EndpointCtx>("endpoint.shutdown"),
});
// Apply a plugin object — same hook can take chains AND listeners:
lifecycle.use({
boot: async (ctx, next) => {
logger.info("booting…");
await next();
},
shutdown: { on: (ctx) => logger.info("shut down", ctx) },
});The contract — the matrix
| Question | Behavior |
| ------------------------------------------------ | ------------------------------------------- |
| .on() runs if chain short-circuits (no throw)? | Yes — listeners observe outcome |
| .on() runs if chain throws? | Yes — ctx.error is set first |
| Can listeners mutate ctx? | No — .on() types ctx as Readonly<Ctx> |
| Listeners awaited by .run()? | Yes — parallel, Promise.allSettled |
| Listener throws → fails chain? | No — collected, routed to onListenerError |
| Listener ordering | Unordered (parallel) |
| Chain ordering | Insertion order (koa-compose) |
Escape hatch: hook(name, { strictListeners: true }) re-throws the first listener error instead of routing it through onListenerError.
API
hook<Ctx>(name, options?)— create one hook.Hook<Ctx>.use(fn)/.on(fn)/.off(fn)/.run(ctx).createHooks(record)— typed bundle with.hooks+.use(plugin).compose(fns)/pipe(...fns)/withTimeout(ms, fn)/withRetry(opts, fn)— composition helpers.
