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,92 @@
# `provider` — `AuthProviderAdapter` contract + path-prefix validation
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `provider` module is the type-level + thin-runtime heart of
`@suaveplan/api-auth-provider`. It declares the abstract `AuthProviderAdapter`
interface every concrete account-lifecycle provider (Better-Auth, the rolled
reference, future Cognito/Auth0 bridges) structurally implements, and ships
the Zod-validated `authPathPrefix` constructor that asserts a deployment's
configured prefix is a legal segment-aligned mount point. By splitting the
contract from any concrete provider we keep this package optional: a
hosted-IdP deployment installs the session resolver (`api-auth`) only and
never pulls in a provider implementation.
## Features
- `AuthProviderAdapter<Caps>` interface with `id`, configurable `pathPrefix`,
`routes(router, ctx)` registration callback, and the four-hook
`AuthProviderLifecycle` quartet (`install`/`start`/`stop`/`uninstall`).
- `AuthPathPrefix` opaque brand — protects against accidental string
substitution at the install boundary.
- `AuthPathPrefixSchema` — a Zod schema enforcing leading slash, no trailing
slash (except `/`), no path parameters, no whitespace.
- `authPathPrefix(value)` constructor — returns the branded value or throws
`AUTH_PROVIDER.PROVIDER_LIFECYCLE_FAILED` with a structured `reason`.
- `isAuthPathPrefix(value)` predicate — non-throwing variant for tests.
- `DEFAULT_AUTH_PATH_PREFIX` constant (`"/auth"`).
## Usage: Basic
```ts
import {
authPathPrefix,
DEFAULT_AUTH_PATH_PREFIX,
type AuthProviderAdapter,
} from "@suaveplan/api-auth-provider/provider";
const myProvider: AuthProviderAdapter = {
id: "rolled",
pathPrefix: authPathPrefix(DEFAULT_AUTH_PATH_PREFIX),
routes(router) {
router.post("/login", { auth: null }, async () => new Response());
},
lifecycle: {
install: async () => {},
start: async () => {},
stop: async () => {},
uninstall: async () => {},
},
};
```
## Usage: Advanced
A deployment that mounts its provider under a non-standard prefix MUST run
the input through `authPathPrefix` so the value is validated before the host
records the claim in the path-ownership registry.
```ts
import { authPathPrefix } from "@suaveplan/api-auth-provider/provider";
const prefix = authPathPrefix(process.env.AUTH_PREFIX ?? "/auth/v2");
// Throws AUTH_PROVIDER.PROVIDER_LIFECYCLE_FAILED on input like
// "/auth/v2/" or "/auth/:tenant".
```
## API Reference
- `interface AuthProviderAdapter<Caps>` — provider contract.
- `interface AuthProviderLifecycle<Caps>` — install/start/stop/uninstall.
- `type AuthPathPrefix` — branded prefix string.
- `const AuthPathPrefixSchema: ZodType` — validation schema.
- `function authPathPrefix(value: string): AuthPathPrefix` — throwing
constructor.
- `function isAuthPathPrefix(value: string): boolean` — predicate.
- `const DEFAULT_AUTH_PATH_PREFIX = "/auth"` — host default.
## Implementation Notes
The brand is purely a TypeScript construct (`unique symbol`) — at runtime
`authPathPrefix("/auth")` returns the string unchanged. The schema's order of
refinements is significant: the leading-slash check fires first so callers
see the most actionable error message. Whitespace is rejected before the
parameter check because shell-supplied configuration is the typical source of
both classes of error. The constructor surfaces failures as
`PROVIDER_LIFECYCLE_FAILED` (not a hypothetical `INVALID_PREFIX`) because the
install lifecycle is the only legitimate call site — keeping the code small
makes the error registry tighter and the metadata table denser.