3.3 KiB
secrets-like — SecretsLike re-export and required-secret enforcement
(no submodules)
Purpose
This module is the runtime entry point for the abstract Secrets contract. The canonical SecretsLike interface — get(name), require(name), list() — lives in @suaveplan/api-types/secrets so type-only consumers (the manifest validator, the plugin host signature) can depend on it without pulling api-secrets's runtime. We re-export it here so plugin authors importing from @suaveplan/api-secrets get one canonical entry. The runtime in this file is the host-side helper pair checkRequiredSecrets and enforceRequiredSecrets — both walk a manifest's secrets[] to assert every required: true descriptor resolves before the plugin transitions from installing → installed.
The helpers are split into a returning form and a throwing form so callers can integrate the check into whatever lifecycle plumbing they prefer — discriminated-result for hosts that aggregate failures, exception for hosts that bail fast.
Features
- Re-export of the canonical
SecretsLikefrom@suaveplan/api-types. SecretsListLike— minimal structural shape (list(): readonly string[]) accepted by the helpers.checkRequiredSecrets— pure, returns{ ok: true } | { ok: false; missing }.enforceRequiredSecrets— throwsSecretsError(SECRETS.SECRET_REQUIRED_MISSING_AT_START)listing every missing name oncontext.missing.
Usage: Basic
import { enforceRequiredSecrets } from "@suaveplan/api-secrets/secrets-like";
await enforceRequiredSecrets(ctx.secrets, manifest.secrets);
// throws SECRETS.SECRET_REQUIRED_MISSING_AT_START if any required name absent
Usage: Advanced
Aggregate missing secrets across multiple plugins during a batch install:
import { checkRequiredSecrets } from "@suaveplan/api-secrets/secrets-like";
interface InstallPlan { plugin: string; missing: readonly string[] }
const plans: InstallPlan[] = [];
for (const { name, secretsView, descriptors } of installCandidates) {
const result = checkRequiredSecrets(secretsView, descriptors);
if (!result.ok) plans.push({ plugin: name, missing: result.missing });
}
if (plans.length > 0) {
// present an operator-facing report rather than failing the first plugin
reportToAdminConsole(plans);
}
API Reference
type SecretsLike— re-exported from@suaveplan/api-types/secrets.interface SecretsListLike—{ list(): readonly string[] }.function checkRequiredSecrets(secrets, descriptors): RequiredSecretsCheck.function enforceRequiredSecrets(secrets, descriptors): void— throws on failure.
Implementation Notes
The helpers accept SecretsListLike rather than the full SecretsLike so tests can pass a { list: () => [...] } literal without implementing every method. Required-secret enforcement deliberately uses list() rather than calling require() for every entry — list() is documented as cheap (often O(1) over an in-memory map), while require may hit a network or a secret-manager daemon. The throwing form attaches the full missing-names array to error.context.missing, which the @suaveplan/error wire-format adapter renders into the JSON envelope so admin UIs can show every missing name in one shot rather than playing whack-a-mole.