Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
# `resolver` — `createAnonymousResolver`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Factory for the trivial `SessionResolver` that always returns `null`. This is
the canonical tail-of-chain resolver every public-API host configures: it
makes the "no auth" choice explicit in `host.json` and lets the rest of the
host (RBAC gate, error envelope, OpenAPI emission) operate uniformly because
`principal` is `null` not `undefined`. The factory shape exists — rather than
exposing a constant — so future option flags (e.g. `logAnonymousAccess`) can
be threaded in without a breaking surface change.
## Features
- Always resolves `null` — no header inspection, no network I/O, no state.
- Idempotent `dispose()` no-op (resolver holds no resources).
- Strict zod options schema rejects non-object input and unknown keys with
`AUTH_ANON.INVALID_OPTIONS`.
- Returned type is `SessionResolver` from `@suaveplan/api-auth/resolver` so
it composes directly into `SessionResolverChain`.
## Usage: Basic
```ts
import { createAnonymousResolver } from "@suaveplan/api-auth-anonymous/resolver";
const resolver = createAnonymousResolver();
// Inside a request pipeline:
const principal = await resolver.resolve(request); // → null
await resolver.dispose(); // safe to call again
```
## Usage: Advanced
Compose as the tail of a resolver chain so a JWT-typed resolver runs first
and the anonymous resolver supplies the `null` short-circuit for unrecognised
requests on public routes:
```ts
import { SessionResolverChain } from "@suaveplan/api-auth";
import { createAnonymousResolver } from "@suaveplan/api-auth-anonymous/resolver";
const chain = new SessionResolverChain([
jwtResolver, // recognises Bearer tokens, else returns null
apiKeyResolver, // recognises x-api-key, else returns null
createAnonymousResolver(), // tail: always null
]);
const principal = await chain.resolve(request);
// principal is now `Principal | null`. The host applies its `publicRoutes`
// decision: a non-public route 401s; a public route proceeds with null.
```
## API Reference
| Symbol | Kind | Description |
|------------------------------|----------|-------------------------------------------------------|
| `createAnonymousResolver` | function | `(opts?: AnonymousResolverOptions) => SessionResolver`. |
| `AnonymousResolverOptions` | type | Reserved-for-future-flags options bag. |
## Implementation Notes
- The resolver returns `Promise<null>` literally rather than the wider
`Promise<Principal | null>` so type narrowing in tests can use
`expectTypeOf<...>().toEqualTypeOf<Promise<null>>()`. The chain widens it
back to `Principal | null` at the boundary.
- `dispose()` is a `Promise<void>` not just any promise — the conformance
harness asserts the resolved value is `undefined` so this is load-bearing.
- The factory is the public surface (not a const) because future flags must
be addable without a breaking change to call sites. Even today, passing
`{}` is the canonical call.
- No OTel instrumentation is emitted from this resolver. Anonymous-access
observability lives at the host layer (per-request span tag) — adding
counters here would force a foundation-tier package to depend on
`@suaveplan/opentelemetry`, which is undesirable for a hot path.