3.3 KiB
path-ownership — single-provider registry
(no submodules)
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
PathOwnershipclass withregister,release,current,hasProvider.- Injectable
clock— defaults toDate.now, used for theclaimedAttimestamp 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 structuredcontextpayload (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
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:
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): PathOwnershipClaimrelease(): voidcurrent(): ProviderClaim | nullhasProvider(): boolean
interface PathOwnershipOptions { clock? }type OwnershipClock = () => numberinterface 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.