Files
suaveplan/packages/api-stack/api/api-auth-anonymous/src/resolver/resolver.md
2026-05-27 23:29:53 +01:00

3.4 KiB

resolver — createAnonymousResolver

(no submodules)

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

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:

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.