# `middleware` _(no submodules)_ ## Purpose The `middleware` module defines the onion-model `Middleware` contract and the `composeMiddleware` builder. Every adapter consumes the same shape so middleware authored against the abstract contract drops into every router implementation (Hono, in-memory, the worker-bridge router) unchanged. The contract is intentionally narrow: a middleware receives the request and a `next()` continuation, and must return a response. There is no `ctx` mutation handle, no implicit state — middleware threads explicit values via wrapping responses and inspecting the request shape. ## Features - `Middleware` — type generic over the request body and response body shapes so handler-level types flow through. - `composeMiddleware([…])` — builds a single composed middleware from a list; order is preserved. - `next()` enforcement: a middleware that calls `next()` twice in a row has its dispatch rejected. (Multiple sequential `next()` calls across *different* middlewares are correct; only same-middleware repeats fail.) - Synchronous return values are accepted (wrapped in `Promise.resolve`). ## Usage: Basic ```ts import { composeMiddleware } from "@suaveplan/api-router/middleware"; const log = composeMiddleware([ async (req, next) => { const res = await next(); return res; // pass-through with hook points }, ]); ``` ## Usage: Advanced ```ts import { composeMiddleware, type Middleware } from "@suaveplan/api-router/middleware"; const auth: Middleware = async (req, next) => { if (req.headers.get("authorization") === null) { return { status: 401, headers: {}, body: { code: "UNAUTH" }, streaming: false }; } return next(); }; const trace: Middleware = async (req, next) => { req.span.setAttribute("path", req.url); const res = await next(); req.span.setAttribute("status", res.status); return res; }; const stack = composeMiddleware([trace, auth]); // Execution: trace → auth → terminal → auth → trace ``` ## API Reference - `Middleware` — `(req, next) => RouterResponse | Promise`. - `composeMiddleware(list)` — returns a single composed `Middleware`. - `PipelineStep` — literal union of every per-request pipeline step name. - `PLUGIN_INSERTION_STEP` — the constant `"post-validation"`; the single insertion point plugin middleware is allowed. - `assertPluginInsertionStep(step)` — throws `ROUTER.MIDDLEWARE_INSERTION_DENIED` if `step` isn't the allowed boundary. ## Implementation Notes `composeMiddleware` uses the textbook indexed-dispatch pattern: each call to `next()` advances an internal index, and double-`next()` from the same middleware is detected by comparing the new index to the recorded high-watermark. The detection rejects the inner Promise rather than throwing synchronously so the error propagates through the caller's existing `.catch` / `try/await` machinery. Synchronous middlewares are supported — the return value is wrapped in `Promise.resolve` so callers don't have to choose between `async` and plain functions. The terminal continuation is invoked with no arguments and must produce the response; adapters wire this to the handler call.