Files
2026-05-27 23:29:53 +01:00

3.3 KiB

Spec delta: api-router-hono

ADDED Requirements

Requirement: HonoRouter pipeline facet satisfies the host's PipelineRouter contract

The package SHALL extend HonoRouter with a second, additive facet that produces values structurally assignable to @suaveplan/api-host's PipelineRoute interface. The facet SHALL expose:

  • registerPipelineRoute(reg: HonoPipelineRegistration): void — stores (plugin, method, path, handler, options?, statusMap?).
  • unregisterPipelineRoute(method: string, path: string): void — no-op when absent.
  • unregisterPipelinePlugin(plugin: string): void — removes every entry owned by plugin (hot-reload teardown).
  • listPipelineRoutes(): readonly HonoPipelineRoute[] — snapshot for admin tooling.
  • matchPipeline(method: string, url: string): HonoPipelineRoute | null — resolves (method, url) to a HonoPipelineRoute carrying plugin, path, options, handler, params, statusMap.

The facet SHALL preserve the existing abstract Router surface verbatim. Registering the same (method, path-shape) under a DIFFERENT plugin SHALL throw ROUTER_HONO.PIPELINE_ROUTE_COLLISION. Re-registering the same (plugin, method, path) SHALL overwrite. Parameter names SHALL be erased in the collision key so :id and :other clash.

Scenario: facet satisfies the host's PipelineRouter conformance suite

  • WHEN runPipelineRouterConformance (from @suaveplan/api-host/testing) is invoked with a factory that returns { router: { match: (m, u) => r.matchPipeline(m, u) }, register: (reg) => r.registerPipelineRoute(reg) }
  • THEN every harness case passes (register → match round-trip, 404, statusMap propagation, plugin ownership across reload, param parsing, collision detection)

Scenario: collision detection throws ROUTER_HONO.PIPELINE_ROUTE_COLLISION

  • WHEN two plugins register the same (method, path-shape) on one HonoRouter
  • THEN the second call throws RouterHonoError with code === "ROUTER_HONO.PIPELINE_ROUTE_COLLISION"

Scenario: hot-reload teardown removes plugin-owned entries

  • WHEN unregisterPipelinePlugin("billing") is called after billing registered two pipeline routes
  • THEN subsequent matchPipeline(...) calls against either route return null AND listPipelineRoutes() no longer contains a "billing"-owned entry

Verified by: src/pipeline/pipeline.test.ts AND src/pipeline/pipeline.conformance.test.ts AND src/adapter/adapter.test.ts::"pipeline facet — register/match/list/unregister/unregisterPlugin"


MODIFIED Requirements

Requirement: ROUTER_HONO error-code registry

The package SHALL expose ROUTER_HONO_CODES (defineCodes("ROUTER_HONO", [...])) and a RouterHonoError extending SuaveplanError. The registry MUST contain HONO_INTERNAL_ERROR, INVALID_HONO_PEER, AND PIPELINE_ROUTE_COLLISION. Every code MUST be thrown by at least one production path in this package.

Scenario: registry shape

  • WHEN Object.keys(ROUTER_HONO_CODES) is inspected
  • THEN the keys are exactly ["HONO_INTERNAL_ERROR", "INVALID_HONO_PEER", "PIPELINE_ROUTE_COLLISION"] AND each value is ROUTER_HONO.<name>

Verified by: src/error-codes/error-codes.test.ts::"enumerates the three declared codes" AND src/error-codes/error-codes.test.ts::"namespaces each entry under ROUTER_HONO.*"