first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
# `path-ownership` — single-provider registry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`path-ownership` is the runtime registry the host consults to enforce the
"exactly one auth provider per deployment" invariant from design §2.3. A
provider claims its `pathPrefix` at install time; any subsequent registration
with a different provider id throws `MULTIPLE_PROVIDERS_REGISTERED`. The
registry is deliberately not a module-global singleton — each host instance
constructs its own `PathOwnership`, which keeps multi-tenant test runners
and hot-reload scenarios deterministic.
## Features
- `PathOwnership` class with `register`, `release`, `current`, `hasProvider`.
- Injectable `clock` — defaults to `Date.now`, used for the `claimedAt`
timestamp on the recorded claim.
- Idempotent registration — re-registering the same instance, or a different
instance with the same provider id, returns the existing claim.
- Strict rejection of a different provider id with
`AUTH_PROVIDER.MULTIPLE_PROVIDERS_REGISTERED`, including a structured
`context` payload (`incomingProviderId`, `incumbentProviderId`,
`prefix`) suitable for diagnostic logs.
- `release()` drops the current claim — used by host shutdown and the
blue-green hot-reload sequence to swap providers atomically.
## Usage: Basic
```ts
import { PathOwnership } from "@suaveplan/api-auth-provider/path-ownership";
const ownership = new PathOwnership();
ownership.register(myProvider);
```
## Usage: Advanced
Tests inject a deterministic clock so the recorded `claimedAt` is stable
across runs:
```ts
import { PathOwnership } from "@suaveplan/api-auth-provider/path-ownership";
const ownership = new PathOwnership({ clock: () => 1_000_000 });
const claim = ownership.register(myProvider);
// claim.claimedAt === 1_000_000
// Hot-reload: release before swapping in the new provider.
ownership.release();
ownership.register(replacementProvider);
```
## API Reference
- `class PathOwnership` — runtime registry.
- `constructor(options?: PathOwnershipOptions)`
- `register(provider): PathOwnershipClaim`
- `release(): void`
- `current(): ProviderClaim | null`
- `hasProvider(): boolean`
- `interface PathOwnershipOptions { clock? }`
- `type OwnershipClock = () => number`
- `interface PathOwnershipClaim { providerId; prefix; claimedAt }`
- `interface ProviderClaim<Provider> { provider; claim }`
## Implementation Notes
The registry stores both the `AuthProviderAdapter` instance and a flat
`PathOwnershipClaim` record. Two layers exist because consumers fall into
two camps: hosts that need the live provider for lifecycle dispatch, and
diagnostic tooling that only needs the metadata. Idempotency is decided by
identity first, id second — so a host that constructs a fresh provider
instance per hot-reload still cannot accidentally register two providers
with the same id. The `MULTIPLE_PROVIDERS_REGISTERED` error carries enough
context for operators to diagnose double-installation in a misconfigured
multi-process deployment without consulting the host logs. The class uses a
private field rather than a module-level variable so multiple registries can
coexist in the same process — essential for runner-agnostic test suites and
multi-tenant in-process deployments.