Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
# Capability Spec Delta: api-host — reserved-prefix contract
**Package**: `@suaveplan/api-host`
**Status**: PROPOSED
## ADDED Requirements
### Requirement: Publishes the canonical reserved-prefix list
`@suaveplan/api-host` SHALL export `HOST_RESERVED_PREFIXES` as a frozen `ReadonlyArray<string>` of every URL path prefix the host (and any first-party engine that has shipped) reserves. The list MUST be normalised (lowercase, leading-slash, no trailing slash) and deduped. Downstream adapters that mount their own routes MUST consult this list (directly or via `isReservedPrefix`) before mounting and refuse to install on collision.
#### Scenario: list contains every always-reserved host route
- **WHEN** an adapter reads `HOST_RESERVED_PREFIXES`
- **THEN** it contains `/_host` and `/internal` (always-reserved); `/admin`, `/graphql`, `/ws`, `/sse` are included once their respective host modules ship
#### Scenario: list is frozen
- **WHEN** consumer code attempts `HOST_RESERVED_PREFIXES.push("/foo")`
- **THEN** the assignment throws `TypeError`
**Verified by:** `src/reserved-prefix/reserved-prefix.test.ts::"HOST_RESERVED_PREFIXES contains always-reserved prefixes"` AND `src/reserved-prefix/reserved-prefix.test.ts::"HOST_RESERVED_PREFIXES is frozen"`
---
### Requirement: Exposes a predicate + assertion helper
The package SHALL export `isReservedPrefix(prefix: string): boolean` and `assertPrefixAvailable(prefix: string, codeFactory: (collision: string) => Error): void`. The predicate MUST treat input prefixes case-insensitively after normalisation and MUST match both exact prefixes (`/internal` reserves `/internal`) and child paths (`/internal/livez` is also reserved). The assertion MUST throw the error produced by `codeFactory(collision)` when reserved, returning `void` otherwise — letting each adapter surface its own code namespace (`AUTH_BA_*`, `RBAC_*`, …) without coupling.
#### Scenario: predicate matches exact + child paths
- **WHEN** `isReservedPrefix("/internal")` and `isReservedPrefix("/internal/livez")` are called
- **THEN** both return `true`
#### Scenario: predicate normalises case and trailing slash
- **WHEN** `isReservedPrefix("/Internal/")` is called
- **THEN** it returns `true`
#### Scenario: assertion throws the factory-supplied error on collision
- **WHEN** `assertPrefixAvailable("/_host", () => new MyError("X"))` is called
- **THEN** the call throws `MyError("X")`
#### Scenario: assertion is a no-op when free
- **WHEN** `assertPrefixAvailable("/auth", factory)` is called and `/auth` is not in the list
- **THEN** the call returns `void` and `factory` is NOT invoked
**Verified by:** `src/reserved-prefix/reserved-prefix.test.ts::"isReservedPrefix matches exact and child paths"` AND `src/reserved-prefix/reserved-prefix.test.ts::"isReservedPrefix normalises case and trailing slash"` AND `src/reserved-prefix/reserved-prefix.test.ts::"assertPrefixAvailable throws supplied error on collision"` AND `src/reserved-prefix/reserved-prefix.test.ts::"assertPrefixAvailable is no-op when prefix is free"`
---
### Requirement: Published via `./reserved-prefix` subpath
The contract MUST be importable from `@suaveplan/api-host/reserved-prefix` so adapters can pull only the predicate surface without bundling the full host. The subpath MUST forward types and runtime export of the same set documented in `HOST_RESERVED_PREFIXES`, `isReservedPrefix`, `assertPrefixAvailable`, `normalisePrefix`.
#### Scenario: subpath import works in TypeScript and at runtime
- **WHEN** `import { isReservedPrefix } from "@suaveplan/api-host/reserved-prefix"` is used
- **THEN** the symbol resolves AND TypeScript types it as `(prefix: string) => boolean`
**Verified by:** `src/reserved-prefix/reserved-prefix.test.ts::"subpath export resolves and types are forwarded"`