3.8 KiB
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'sInMemoryBlueGreenSlotfor the same hot-reload primitive Stage-4 ships.
Usage: Basic
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:
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.Routerconstructor(options?: { plugin?: string })- Inherits
get/post/put/patch/delete/head/options/all,match,mount,collidesWith,use,chain. registerPipelineRoute(reg: InMemoryPipelineRegistration): voidunregisterPipelineRoute(method: string, path: string): voidunregisterPipelinePlugin(plugin: string): voidlistPipelineRoutes(): 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.