Files
skills/docs/api-stack/api/api-auth-provider/src/path-ownership/path-ownership.md
2026-05-29 08:20:56 +01:00

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

  • 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

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): 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.