Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
# `testing/conformance`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `testing/conformance` module exports `runRouterConformance(factory)` —
the canonical conformance harness every concrete router adapter MUST pass.
Stages 4-7 deliver adapters (`api-router-hono`, `api-router-in-memory`, the
worker-bridge router); each pins to this harness so behaviour stays
identical across the matrix.
The harness exercises every required surface: verb registration, mount
rewriting, middleware chain ordering, route-collision rejection, param
parsing, blue-green slot lifecycle, and error propagation. Adapters that
add their own behaviours layer those tests on top — never instead of — the
harness suite.
## Features
- `RouterConformanceFactory` — `{ router: () => Router, slot: (plugin, handler) => BlueGreenSlot, label: string }`.
- One `describe` block per factory invocation, labelled with `factory.label`.
- Eight required `it` cases — verb declarations, mount, middleware
ordering, collision rejection, param parse, slot lifecycle, flip-with-no-shadow,
error propagation.
- Reuses `composeMiddleware` and the reference `Request`/`Response` so
adapters get the canonical chain semantics for free.
## Usage: Basic
```ts
import { runRouterConformance } from "@suaveplan/api-router/testing";
import { Router } from "@suaveplan/api-router/router";
import { InMemoryBlueGreenSlot } from "@suaveplan/api-router/slot";
runRouterConformance({
label: "InMemory",
router: () => new Router({ plugin: "test" }),
slot: (plugin, handler) => new InMemoryBlueGreenSlot(plugin, handler),
});
```
## Usage: Advanced
```ts
// Inside an adapter package (e.g. api-router-hono):
import { runRouterConformance } from "@suaveplan/api-router/testing";
import { HonoRouter } from "./hono-router.js";
import { InMemoryBlueGreenSlot } from "@suaveplan/api-router/slot";
// Adapter-specific tests
describe("HonoRouter — adapter-specific", () => {
/* … hono-only assertions … */
});
// Then run the canonical suite
runRouterConformance({
label: "Hono",
router: () => new HonoRouter({ plugin: "test" }),
slot: (plugin, handler) => new InMemoryBlueGreenSlot(plugin, handler),
});
```
## API Reference
- `runRouterConformance(factory: RouterConformanceFactory): void` — drops
a fully-formed `describe` block into the host test file. The block
contains eight `it` cases covering every required surface.
- `RouterConformanceFactory` — `{ router, slot, label }`.
## Implementation Notes
The harness imports `@suaveplan/testing/runner` so the same source file
runs under both Vitest and `bun:test` (per the project's runner
abstraction). Each `it` carries the standard `{ timeout: 5000 }` envelope
so any deadlock in an adapter's implementation surfaces as a clean
timeout failure rather than an indefinitely-hanging test run.
The `slot` factory parameter exists so adapters that have their own
`BlueGreenSlot` implementation (e.g. a worker-RPC-backed one) can wire it
into the same lifecycle tests. Adapters without a custom slot
implementation pass `InMemoryBlueGreenSlot` from this package — the
reference impl is intentionally complete enough to be used directly in
production-equivalent test scenarios.
The harness deliberately uses synthetic `RouterRequest` objects built
with `MapHeaders` — the conformance contract is router-level, not
adapter-intake-level. Adapters that want to exercise their wire intake
(parsing, body decoding) add those tests separately.

View File

@@ -0,0 +1,115 @@
# `pipeline-router-conformance` — adapter test harness
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`runPipelineRouterConformance(factory)` is the canonical contract suite
every concrete `PipelineRouter` adapter MUST pass. It is the
complement of `runRouterConformance` from `@suaveplan/api-router/testing`
— that harness covers the abstract `Router` surface; this one covers the
host-pipeline surface that `@suaveplan/api-host`'s `RequestPipeline`
actually consumes at runtime. The harness lives in `@suaveplan/api-router`
because the `PipelineRouter` contract itself lives in
`@suaveplan/api-types/pipeline-router` — the zero-runtime contract
package every adapter and the host orchestration core depend on. The
harness shipping from api-router (rather than from api-host) keeps every
router adapter (Hono, in-memory, Stage-7 engines) free of a build-graph
edge on the orchestration core; api-host re-exports the harness from
`./testing` for back-compat.
## Features
- **Factory-driven** — the harness asks the adapter for a fresh
`(router, register)` pair on every test, so cross-test state can never
leak.
- **Reload support** — an optional `reload()` hook on the factory lets
the harness verify plugin ownership survives a hot-reload cycle.
- **Six required cases** — register → match, 404, statusMap propagation,
plugin ownership across reload, param parsing, collision detection.
- **No runtime peer-dep** — the harness imports only
`@suaveplan/testing/runner` plus the contract's structural types, so
any adapter (Hono, in-memory, worker bridge) can wire it in without
pulling extra Hono / Bun runtime peer.
- **Back-compat aliased** — re-exported from
`@suaveplan/api-host/testing` so existing adapter wiring continues to
work without code changes.
## Usage: Basic
```ts
import { runPipelineRouterConformance } from "@suaveplan/api-router/testing/pipeline-router-conformance";
import { HonoRouter } from "@suaveplan/api-router-hono/adapter";
runPipelineRouterConformance({
label: "HonoRouter pipeline facet",
build: () => {
const r = new HonoRouter();
return {
router: { match: (method, url) => r.matchPipeline(method, url) },
register: (reg) => r.registerPipelineRoute(reg),
};
},
});
```
## Usage: Advanced
```ts
import { runPipelineRouterConformance } from "@suaveplan/api-router/testing/pipeline-router-conformance";
import { HonoRouter } from "@suaveplan/api-router-hono/adapter";
// Verify the reload-cycle path — the harness replays registrations
// against a fresh router and asserts plugin ownership survives.
runPipelineRouterConformance({
label: "HonoRouter pipeline facet w/ reload",
build: () => {
let r = new HonoRouter();
const replay: Array<Parameters<typeof r.registerPipelineRoute>[0]> = [];
return {
router: {
// Bind `match` to the CURRENT `r` so the call goes through
// the post-reload instance every time.
match: (method, url) => r.matchPipeline(method, url),
},
register: (reg) => {
r.registerPipelineRoute(reg);
replay.push(reg);
},
reload: () => {
r = new HonoRouter();
for (const reg of replay) r.registerPipelineRoute(reg);
},
};
},
});
```
## API Reference
- `interface PipelineRouterConformanceFactory` —
- `label: string`
- `build(): { router: PipelineRouter; register: (reg: PipelineRouteRegistration) => void; reload?: () => void }`
- `interface PipelineRouteRegistration` — `{ plugin, method, path, handler, options?, statusMap? }`.
- `function runPipelineRouterConformance(factory): void` — declares one
`describe()` block with six required `it()` cases.
## Implementation Notes
The harness deliberately uses the structural `PipelineRouter` interface
exported from `@suaveplan/api-types/pipeline-router` — adapters do not need to
instance-check, and bag types like a "router with `registerPipelineRoute`"
never appear in the contract. This keeps the harness portable across
radically different adapter shapes (Hono, radix, worker-bridge).
The reload-cycle assertion is gated behind `factory.reload?.()` — adapters
that have no concept of reload (e.g. a purely-static in-memory table)
still pass the suite by omitting the hook. The harness then asserts the
match against the same single-build router, which already covers the
plugin-ownership invariant for static cases.
All `it()` cases declare an explicit `{ timeout: 5000 }` per
testing rules. No `vi.mock`/`jest.mock` or other mock primitives are used
— the harness drives a real adapter through real registrations.