3.7 KiB
@suaveplan/api-host/empty-secrets
(no submodules)
Purpose
EmptySecrets is the no-op default SecretsLike implementation the
host injects into every PluginContext / RequestContext when an
operator constructs a HostAdapters bag without a secrets slot. It
preserves the foundation-fix-gap-3 design intent that plugin authors
can always destructure ctx.secrets without a defensive null guard —
the failure mode is an explicit, debuggable SECRETS.SECRET_NOT_FOUND
throw rather than a TypeError from a missing field.
In production a real adapter (e.g. createEnvSecrets from
@suaveplan/api-secrets-env, a Vault-backed adapter, or an HSM bridge)
replaces this default. EmptySecrets is exclusively the
"nothing configured" fall-through.
Features
get(name)resolves toundefinedfor every key.require(name)throwsSECRETS.SECRET_NOT_FOUNDcarrying the requested key oncontext.nameand a sentinelcontext.source = "host.default.empty-secrets"for correlation.list()returns the frozen emptyreadonly string[].- Zero state —
EmptySecretsinstances are interchangeable; the host constructs one percreateHostcall so OTel attribute attachment remains traceable per host instance. - Stable error identity — every throw is a
SecretsErrorso callers caninstanceof-narrow without importing the abstract base.
Usage: Basic
import { EmptySecrets } from "@suaveplan/api-host/empty-secrets";
const secrets = new EmptySecrets();
const maybe = await secrets.get("STRIPE_SECRET_KEY");
// maybe === undefined — `EmptySecrets` never resolves a value.
Usage: Advanced
A custom host that injects EmptySecrets only in development mode so
production deploys fail fast when the real adapter wiring is missing:
import { createHost } from "@suaveplan/api-host/host";
import type { HostAdapters } from "@suaveplan/api-host/host";
import { EmptySecrets } from "@suaveplan/api-host/empty-secrets";
import { createEnvSecrets } from "@suaveplan/api-secrets-env";
function buildSecrets(mode: "production" | "development") {
if (mode === "production") {
return createEnvSecrets({ allowedKeys: ["STRIPE_SECRET_KEY"] });
}
return new EmptySecrets();
}
const adapters: HostAdapters = {
/* ...other slots... */
secrets: buildSecrets(process.env["NODE_ENV"] as never),
} as never;
const host = await createHost({ adapters, plugins: [], mode: "development" });
API Reference
class EmptySecrets implements SecretsLike
get(name: string): Promise<string | undefined>— alwaysundefined.require(name: string): Promise<string>— always throwsSecretsErrorwith codeSECRETS.SECRET_NOT_FOUNDandcontext: { name, source: "host.default.empty-secrets" }.list(): readonly string[]— frozen empty array, stable across calls.
Implementation Notes
EmptySecrets is deliberately the simplest possible SecretsLike
implementation — every method is constant-time and allocation-free
(the frozen empty array is module-scoped and shared). The class lives
inside @suaveplan/api-host rather than @suaveplan/api-secrets
because the policy decision "what does the host inject when no adapter
is provided" is a host-orchestration concern, not a secrets-domain
concern. Sibling adapters (api-secrets-env, hypothetical
api-secrets-vault) stay focused on concrete sources.
Error codes are sourced from @suaveplan/api-secrets's
SECRETS_CODES registry so the throw uses the canonical
namespace string (SECRETS.SECRET_NOT_FOUND) every other secrets
source uses. This keeps audit / OTel log dashboards consistent
regardless of which source surfaced the failure.