3.8 KiB
error-codes
(no submodules)
Purpose
The error-codes module owns the stable CAMPAIGNS_API.* namespace used by
every failure path inside @suaveplan/api-plugin-campaigns. Consumers
branch on the string code value (rather than instanceof checks against
private subclasses) so the namespace doubles as both an internal vocabulary
and a public-API contract surface.
This is the foundation module for the chunked migration. Chunk A registers
only the codes that the foundation surface can actually throw; Chunks B
(repositories) and C (routes) extend the same defineCodes call rather than
introducing parallel namespaces. The intentional minimalism keeps coverage
at 100% across the package boundary at every chunk milestone.
Features
CAMPAIGNS_API_CODES— frozendefineCodesregistry mapping reason names to"CAMPAIGNS_API.<REASON>"string values.CampaignsApiError— concrete subclass ofSuaveplanErrorwith an explicit public constructor delegating tosuper, plus a typedcampaignsApiCodefield that narrows toCampaignsApiErrorCode.CampaignsApiErrorCode— discriminated string-literal union of every registered code.
Usage: Basic
import {
CAMPAIGNS_API_CODES,
CampaignsApiError,
} from "@suaveplan/api-plugin-campaigns";
try {
createCampaignsPlugin({ routePrefix: 42 as never });
} catch (err) {
if (
err instanceof CampaignsApiError &&
err.code === CAMPAIGNS_API_CODES.INVALID_OPTIONS
) {
// The ZodError is attached as `cause`.
console.error(err.cause);
}
}
Usage: Advanced
import {
CAMPAIGNS_API_CODES,
CampaignsApiError,
} from "@suaveplan/api-plugin-campaigns";
function rethrowAsCampaigns(cause: unknown): never {
throw new CampaignsApiError(
CAMPAIGNS_API_CODES.INTERNAL_ERROR,
"unhandled internal failure inside campaigns plugin",
{
cause: cause instanceof Error ? cause : new Error(String(cause)),
context: { statusCode: 500 },
}
);
}
API Reference
CAMPAIGNS_API_CODES— frozen record with one key per registered reason. Chunk-A keys:INVALID_OPTIONS,INTERNAL_ERROR. Chunk-B additions:CAMPAIGN_NOT_FOUND,SUPPRESSION_NOT_FOUND,INVALID_INPUT,DB_UNAVAILABLE,DUPLICATE_SUPPRESSION,TENANT_REQUIRED. Chunk-C additions:INVALID_STATE_TRANSITION,INVALID_UNSUBSCRIBE_TOKEN,ROUTE_PREFIX_COLLISION,WEBHOOK_REJECTED,SERVICE_NOT_AVAILABLE. Every value is the literal string"CAMPAIGNS_API.<KEY>".class CampaignsApiError extends SuaveplanError<CampaignsApiErrorCode>new CampaignsApiError(code, message, options?)error.code— the namespaced code string.error.campaignsApiCode— same ascode, typed narrowly.error.cause/error.context— inherited fromSuaveplanError.
type CampaignsApiErrorCode— string-literal union over every value ofCAMPAIGNS_API_CODES(currently eight codes spanning Chunks A and B).
Implementation Notes
The constructor is public rather than protected because consumers
(test suites and integration scaffolds) construct the error directly
without subclassing. defineCodes returns a frozen Object.create(null),
so Object.isFrozen(CAMPAIGNS_API_CODES) === true is an invariant the
test suite enforces — accidentally swapping it for a plain object literal
would silently weaken the API contract and tests would catch it.
The Chunk-A registration deliberately excludes codes that Chunks B and C will need. Adding unthrown codes here now would either depress branch coverage or force placeholder tests that exercise dead code paths; incremental registration aligned with each chunk's actual throw sites keeps the verification gate honest.