Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
# `error-codes`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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` — frozen `defineCodes` registry mapping reason names
to `"CAMPAIGNS_API.<REASON>"` string values.
- `CampaignsApiError` — concrete subclass of `SuaveplanError` with an
explicit public constructor delegating to `super`, plus a typed
`campaignsApiCode` field that narrows to `CampaignsApiErrorCode`.
- `CampaignsApiErrorCode` — discriminated string-literal union of every
registered code.
## Usage: Basic
```ts
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
```ts
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 as `code`, typed narrowly.
- `error.cause` / `error.context` — inherited from `SuaveplanError`.
- `type CampaignsApiErrorCode` — string-literal union over every value of
`CAMPAIGNS_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.