3.8 KiB
factory/create-static-principal-resolver — fixed-Principal resolver
(no submodules)
Purpose
Constructs a SessionResolver from @suaveplan/api-auth that returns the
configured Principal verbatim on every request — no token decoding, no
JWKS, no I/O. This is the workhorse adapter for plugin integration tests,
the api-plugin-example fixture, and any environment where the goal is to
exercise the downstream pipeline (RBAC gate, tenant resolver chain reading
principal.tenantId, tenant-scoped DB) with a deterministic identity carried
in config.
The factory consumes options previously validated by the options module so
construction is total: every misuse maps to either
AUTH_STATIC_PRINCIPAL.INVALID_OPTIONS or AUTH_STATIC_PRINCIPAL.INVALID_PRINCIPAL
and surfaces synchronously at construction time. The resolver itself never
throws at request time.
Features
- Defensive clone on every
resolve()— callers cannot mutate one returned Principal and observe the change on subsequent calls. applyTo(req)predicate for arbitrary "return null for this request" rules.nullForPathssuffix-glob list (/webhooks/*) and exact-match patterns for the most common public-route test setup.- Idempotent no-op
dispose()matching theSessionResolvercontract. - Returned objects are frozen (top-level +
scopes+claims) to make attempted mutations fail loudly in strict mode.
Usage: Basic
import { createStaticPrincipalResolver } from "@suaveplan/api-auth-static-principal/resolver";
const resolver = createStaticPrincipalResolver({
principal: {
id: "u1",
scopes: ["billing:write"],
tenantId: "acme",
claims: { sub: "u1" },
},
});
const principal = await resolver.resolve(
new Request("https://example.test/api/billing/invoices")
);
// principal.id === "u1"
Usage: Advanced
Combine applyTo and nullForPaths to model a mixed-route test surface:
import { createStaticPrincipalResolver } from "@suaveplan/api-auth-static-principal/resolver";
const resolver = createStaticPrincipalResolver({
principal: {
id: "u1",
scopes: ["billing:write", "billing:read"],
tenantId: "acme",
claims: { sub: "u1" },
},
nullForPaths: ["/webhooks/*", "/healthz"],
applyTo: (req) => !req.headers.has("x-skip-auth"),
});
// /healthz and /webhooks/stripe resolve to null.
// Requests with the `x-skip-auth` header resolve to null.
// Everything else returns the configured Principal.
API Reference
| Symbol | Kind | Description |
|---|---|---|
createStaticPrincipalResolver(o) |
function | Returns a SessionResolver carrying o.principal on every match. |
Implementation Notes
The clone strategy is: deep-freeze a single template at construction, then
clone-from-template per resolve(). The per-call clone is unavoidable —
otherwise two callers each freezing the returned object would freeze each
other's state. The per-call clone copies the top level plus scopes (array
spread) and claims (object spread); deeper structures inside claims are
not cloned. This matches the Principal contract: claims values are
unknown, so the package can't responsibly deep-clone them. Callers passing
mutable nested claims values are responsible for not mutating them in-place.
pathMatches accepts only exact-match and a single suffix glob (/foo/*)
on purpose — richer matching belongs in applyTo. The trailing slash is
preserved when stripping the * so /foo/* matches /foo/x but not
/foox. The pattern dialect is documented on the spec and exercised by the
test file.