84 lines
3.3 KiB
Markdown
84 lines
3.3 KiB
Markdown
# `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.
|