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,127 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: api-auth-provider (api-auth-provider)
**Package**: `@suaveplan/api-auth-provider`
**Status**: PROPOSED
> Every requirement carries a `**Verified by:**` line.
## ADDED Requirements
### Requirement: AuthProviderAdapter contract
The package SHALL export `AuthProviderAdapter` interface: `pathPrefix: string` (default `/auth`), `routes(router: Router, ctx: PluginContext): void` to register provider routes, `lifecycle: { install, start, stop, uninstall }` hooks matching the generic plugin lifecycle. The adapter MUST be generic over `Caps extends CapabilityMap`.
#### Scenario: every documented field present
- **WHEN** `expectTypeOf<AuthProviderAdapter>()` is checked
- **THEN** `pathPrefix`, `routes`, and the four lifecycle hooks are declared
**Verified by:** `src/provider/provider.spec-conformance.test.ts::"AuthProviderAdapter declares pathPrefix routes lifecycle"`
---
### Requirement: `import z from "zod"` everywhere
Every Zod import MUST use the default form.
#### Scenario: no named import of z
- **WHEN** the test greps `import z` from `"zod"`
- **THEN** zero matches
**Verified by:** `tests/zod-import-form.test.ts::"every src file uses default-import z"`
---
### Requirement: Canonical provider route surface
The package SHALL export `PROVIDER_ROUTE_SURFACE` listing the canonical paths a provider OWNS (relative to `pathPrefix`): `/register`, `/login`, `/logout`, `/refresh`, `/oauth/:provider/start`, `/oauth/:provider/callback`, `/verify-email/:token`, `/forgot-password`, `/reset-password`. Providers MAY implement a subset; the host does not require every path.
#### Scenario: surface enumerates the documented paths
- **WHEN** the test inspects `PROVIDER_ROUTE_SURFACE`
- **THEN** every documented path is listed
**Verified by:** `src/route-surface/route-surface.test.ts::"canonical surface lists every documented path"`
---
### Requirement: Path-ownership registry
The package SHALL export `PathOwnership` — a runtime registry. `register(provider)` claims `provider.pathPrefix`. A second `register(provider2)` with a different provider MUST throw `AUTH_PROVIDER.MULTIPLE_PROVIDERS_REGISTERED`. Re-registering the SAME provider instance MUST be a no-op (idempotent).
#### Scenario: double registration with different provider rejected
- **WHEN** provider A is registered AND provider B is registered
- **THEN** the second call throws `AUTH_PROVIDER.MULTIPLE_PROVIDERS_REGISTERED`
#### Scenario: re-registering same provider is idempotent
- **WHEN** provider A is registered twice
- **THEN** the second call returns without throwing
**Verified by:** `src/path-ownership/path-ownership.test.ts::"double registration rejected"` AND `src/path-ownership/path-ownership.test.ts::"re-registering same provider is idempotent"`
---
### Requirement: Collision detector
The package SHALL export `detectAuthPathCollisions({ provider, plugins }): CollisionReport`. A plugin route whose prefix overlaps with `provider.pathPrefix` AND is not in `publicRoutes` MUST be reported with code `AUTH_PROVIDER.PATH_COLLISION` AND `details.plugin` AND `details.path`. Plugin routes outside the auth prefix MUST NOT be reported.
#### Scenario: overlap reported
- **WHEN** provider owns `/auth` AND plugin declares route `/auth/custom`
- **THEN** the report includes `{ code: "AUTH_PROVIDER.PATH_COLLISION", details: { plugin: ..., path: "/auth/custom" } }`
#### Scenario: non-overlap not reported
- **WHEN** provider owns `/auth` AND plugin declares route `/billing/invoices`
- **THEN** the report is empty
#### Scenario: configurable prefix shifts collision domain
- **WHEN** provider's `pathPrefix === "/myauth"` AND plugin declares route `/auth/x`
- **THEN** the report is empty AND a plugin route `/myauth/x` would be reported
**Verified by:** `src/collision-detector/collision-detector.test.ts::"overlap with auth prefix reported"` AND `src/collision-detector/collision-detector.test.ts::"non-overlap not reported"` AND `src/collision-detector/collision-detector.test.ts::"configurable prefix shifts collision domain"`
---
### Requirement: CapabilityId augmentation contributes "auth.provider"
The package SHALL augment the `CapabilityId` union to include `"auth.provider"`.
#### Scenario: auth.provider assignable to CapabilityId
- **WHEN** `expectTypeOf<"auth.provider">().toMatchTypeOf<CapabilityId>()` is checked
- **THEN** the assertion passes
**Verified by:** `src/capability-id/capability-id.spec-conformance.test.ts::"auth.provider is a valid CapabilityId after augmentation"`
---
### Requirement: Error codes registry frozen
The package SHALL export `AUTH_PROVIDER_CODES` containing exactly: `PATH_COLLISION`, `MULTIPLE_PROVIDERS_REGISTERED`, `PROVIDER_LIFECYCLE_FAILED`, `OAUTH_CALLBACK_INVALID_STATE`.
#### Scenario: every code reachable
- **WHEN** the test enumerates `AUTH_PROVIDER_CODES`
- **THEN** every code has a production throw site
**Verified by:** `src/error-codes/error-codes.spec-conformance.test.ts::"every AUTH_PROVIDER_CODES entry thrown by production path"`
---
### Requirement: Conformance harness for providers
The package SHALL export `runAuthProviderConformance({ factory, label })`. Every concrete provider in Stages 6/8 MUST pass it.
#### Scenario: harness covers path ownership, route surface, lifecycle, collision
- **WHEN** the harness runs against a stub provider
- **THEN** test categories exist for each documented behavior
**Verified by:** `src/testing/conformance.test.ts::"harness covers path ownership, route surface, lifecycle, collision"`