# `adapter` — `InMemoryRouter` _(no submodules)_ ## Purpose `InMemoryRouter` is The in-memory router adapter for `@suaveplan/api`. It implements both the abstract `Router` contract from `@suaveplan/api-router` (verb registration, collision detection, mount/prefix rewriting, param parsing) AND the `PipelineRouter` contract from `@suaveplan/api-router` (route metadata sidecar with plugin ownership, status-map propagation, and hot-reload teardown). Designed as a real, production-grade adapter that happens to keep its state in process memory — not a test double. The class passes both `runRouterConformance` and `runPipelineRouterConformance` end-to-end. ## Features - Single concrete class satisfying two contracts (`Router` + `PipelineRouter`). - Zero network I/O — `match()`/`matchPipeline()` resolve from in-memory maps. - Plugin-isolation invariant: cross-plugin collisions throw `ROUTER_IN_MEMORY.ROUTE_COLLISION`; same-plugin re-registration overwrites (the hot-reload path). - `unregisterPipelinePlugin(plugin)` clears every entry owned by a plugin in one call — wired by the host's reload driver. - Compatible with `@suaveplan/api-router-in-memory/blue-green-slot`'s `InMemoryBlueGreenSlot` for the same hot-reload primitive Stage-4 ships. ## Usage: Basic ```ts import { createInMemoryRouter } from "@suaveplan/api-router-in-memory/adapter"; const router = createInMemoryRouter({ plugin: "billing" }); router.get("/invoices/:id", () => null); const match = router.match("GET", "/invoices/i_42"); // match.params.id === "i_42" ``` ## Usage: Advanced Wire the pipeline facet behind the host's `PipelineRouter` contract, then exercise it through the host's request pipeline: ```ts import { createInMemoryRouter } from "@suaveplan/api-router-in-memory/adapter"; const router = createInMemoryRouter(); router.registerPipelineRoute({ plugin: "billing", method: "POST", path: "/invoices", handler: async (ctx) => ({ ok: true }), options: { auth: "required", runtimeValidation: true }, statusMap: { "BILLING.INSUFFICIENT_FUNDS": 402 }, }); // The host's request pipeline consumes this as a PipelineRouter: const route = router.matchPipeline("POST", "http://h.local/invoices"); // route.plugin === "billing"; route.statusMap["BILLING.INSUFFICIENT_FUNDS"] === 402 ``` ## API Reference - `class InMemoryRouter extends @suaveplan/api-router.Router` - `constructor(options?: { plugin?: string })` - Inherits `get/post/put/patch/delete/head/options/all`, `match`, `mount`, `collidesWith`, `use`, `chain`. - `registerPipelineRoute(reg: InMemoryPipelineRegistration): void` - `unregisterPipelineRoute(method: string, path: string): void` - `unregisterPipelinePlugin(plugin: string): void` - `listPipelineRoutes(): readonly InMemoryPipelineRoute[]` - `matchPipeline(method: string, url: string): InMemoryPipelineRoute | null` - `createInMemoryRouter(options?): InMemoryRouter` — factory returning the concrete type so callers can reach the pipeline-facet methods without a cast. ## Implementation Notes Verb-method registration is inherited unchanged from `@suaveplan/api-router.Router` — there is no Hono mirroring to perform, so the parent class's behaviour is the production behaviour. The pipeline facet is a separate `Map`-backed table because the host stores extra per-route metadata (`statusMap`, plugin ownership) the abstract router does not need; keeping them parallel mirrors the `HonoRouter` layering and makes the conformance suites apply identically. `createInMemoryRouter` returns the concrete `InMemoryRouter` type (not the abstract `Router`) so the pipeline-facet methods are reachable directly — this is the small, deliberate divergence from `createHonoRouter`, which hides the concrete class behind the abstract interface.