2.0 KiB
Host Reserved-Prefix Contract
Package: @suaveplan/api-host
Status: PROPOSED
Type: capability-extension
Why
Stage 6.3 (api-auth-better-auth) needs to refuse mounting under host-reserved path prefixes (/_host, /internal, /graphql, etc.) and surface AUTH_BA_ROUTE_PREFIX_COLLISION. There is no canonical list — api-host uses /internal/livez and /internal/readyz internally, and Stage 7 will add /graphql, but no contract exists for a downstream adapter to query "is this prefix reserved?". Hard-coding the list inside each adapter would drift the moment any host route changes.
This change adds the reserved-prefix contract to @suaveplan/api-host:
HOST_RESERVED_PREFIXES— frozenReadonlyArray<string>of all prefixes the host (and its first-party engines) reserve.isReservedPrefix(prefix: string): boolean— predicate consumers call before mounting.assertPrefixAvailable(prefix, codeFn)— throws via the consumer-supplied code when reserved../reserved-prefixsubpath export so adapters import without pulling the full host bundle.
What changes
- New feature folder
packages/api-stack/api/api-host/src/reserved-prefix/withreserved-prefix.ts,reserved-prefix.types.ts,reserved-prefix.md,reserved-prefix.test.ts,index.ts. package.jsongains./reserved-prefixsubpath export.- Capability spec gains the ADDED requirements below.
- No breaking changes; pure addition.
Reserved list (initial)
/_host, /internal, /admin (when admin-api mounted), /graphql (when GraphQL engine mounted), /ws (when WS engine mounted), /sse (when SSE engine mounted). Engine-conditional entries are returned only when the engine slot is filled in the registry; the static list returned by HOST_RESERVED_PREFIXES is the always-reserved subset.
Out of scope
- Dynamic engine-conditional resolution beyond the static list. A follow-up (
api-host-reserved-prefix-dynamic) will land when Stage 7 ships GraphQL/WS/SSE engines.