3.5 KiB
@suaveplan/api-router-in-memory — In-memory Router for tests
Package: @suaveplan/api-router-in-memory
Status: PROPOSED
Type: new-package
Stage: 5 (Testing kit + conformance suites) — see umbrella openspec/changes/api-plugin-foundation/
Why
Per design doc §13.2, the host testing-utility kit (@suaveplan/api-host/testing) ships REAL, in-memory implementations of every capability — production-grade adapters that happen to keep state in process memory, not test doubles. api-router-in-memory is the router slot of that kit: a Router that dispatches via a fetch-shaped function call rather than a real HTTP listener. Tests run with zero network setup, deterministic timing, and the full pipeline (auth → tenant → validation → handler → response validation → telemetry close) exercised in-process.
The package is also a load-bearing reference: every adapter in Stage 4 runs the same runRouterConformance suite (§13.4); this adapter is the canonical in-memory pass — if it fails the suite, the suite itself is suspect.
What changes
Net-new package depending on @suaveplan/api-router (contract + ./testing conformance), @suaveplan/api-types, @suaveplan/error.
- ADDED:
createInMemoryRouter(opts)— direct-surface factory returning aRouterwhosedispatch(request)runs the full pipeline synchronously-ish (Promise-returning) without binding a socket. - ADDED:
host.fetch(method, path, init?)integration — theRouterexposes afetch-shaped entry point compatible with thecreateTestHosttest ergonomics shown in §13.2. - ADDED:
InMemoryRouterOptions— Zod schema (basePath?: string,trackRequests?: boolean(default true — record every request for assertion)). - ADDED:
ROUTER_IN_MEMORY_*error codes (ROUTER_IN_MEMORY_NO_MATCH,ROUTER_IN_MEMORY_DUPLICATE_ROUTE,ROUTER_IN_MEMORY_INVALID_OPTIONS). - ADDED: Test-recording surface —
router.requestsreturns an array of every request that landed (for assertions);router.reset()clears the history. - ADDED: Registration in
@suaveplan/api-host/testing's adapter table as the defaultrouteradapter when callers passrouter: "in-memory".
Out of scope
- HTTP listener — by design.
- Worker-proxy surface — Stage 5 adapters are direct-only; the conformance suite that lives in
api-router/testingis also run againstapi-router-hono(which DOES ship both surfaces) so the proxy invariants are still pinned. - Streaming over network — the in-memory router supports
ReadableStreambody/response but no socket-level flushing semantics.
Source mapping
Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.
| Requirement | Design doc section |
|---|---|
| Real in-memory impl, not a double | §13.2 |
host.fetch integration |
§13.2 |
| Request recording for assertions | §13.2 (implicit via host.events.published pattern) |
Registered in api-host/testing adapter table |
§15 Stage 5 |
Verification approach
src/factory/create-in-memory-router.test.ts— register routes, dispatch, recording, reset.src/factory/create-in-memory-router.spec-conformance.test.ts—runRouterConformance("in-memory-direct", ...).src/fetch-shape/fetch-shape.test.ts—host.fetch("POST", "/x", { body })returnsResponse-shaped result with status / headers / body.tests/integration/integration.test.ts— booted bycreateTestHost's default adapter table; end-to-end pipeline executed.
Migration impact
None — additive only.