Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user