first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
# error-codes
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `@suaveplan/seed` package centralises every failure mode it can produce
behind a single registry built with `defineCodes("SEED", [...])` from
`@suaveplan/error`. The registry is the stable contract callers depend on:
code strings are documented, frozen at module load, and never renamed
without an explicit migration. This module exports the `SEED_CODES`
registry, the derived `SeedErrorCode` type, and the `SeedError` class that
every public failure path raises.
`SeedError` extends `SuaveplanError<SeedErrorCode>` so `instanceof SeedError`
narrows `code` to the exact union of `SEED.*` strings. It adds optional
`seedName` and `dependency` discriminants that diagnose which seed (or
which missing dependency) triggered the error, while leaving the base
`context` map free for caller-supplied structured data. The class threads
`cause` straight through `SuaveplanError`, so store-layer exceptions
wrapped by the runner remain inspectable through `error.cause`.
## Features
- Frozen `SEED_CODES` registry with seven namespaced codes
- `SeedErrorCode` literal-union type derived from the registry
- `SeedError` class extending `SuaveplanError<SeedErrorCode>`
- Optional `seedName` and `dependency` diagnostic discriminants
- Full `cause`-chain preservation via `SuaveplanError`
- Compatible with the OTel span-event adapter shipped by `@suaveplan/error`
## Usage: Basic
```ts
import { SEED_CODES, SeedError } from "@suaveplan/seed";
if (registry.has(seed.name)) {
throw new SeedError(SEED_CODES.DUPLICATE_NAME, `seed "${seed.name}" already registered`, {
seedName: seed.name,
});
}
```
## Usage: Advanced
```ts
import { SEED_CODES, SeedError } from "@suaveplan/seed";
import { SuaveplanError } from "@suaveplan/error";
try {
await store.insert(seed.table, batch);
} catch (cause) {
throw new SeedError(SEED_CODES.STORE_ERROR, `store rejected batch for ${seed.table}`, {
cause: cause instanceof Error ? cause : undefined,
seedName: seed.name,
context: { table: seed.table, batchSize: batch.length },
});
}
// Downstream:
catch (err) {
if (err instanceof SeedError && err.code === SEED_CODES.STORE_ERROR) {
logger.error("seed.store.failure", {
seedName: err.seedName,
rootMessage: err.cause instanceof Error ? err.cause.message : String(err.cause),
});
}
}
```
## API Reference
### `SEED_CODES`
A frozen object literal of seven namespaced codes:
| Property | Value |
| ----------------------------- | ---------------------------------- |
| `DUPLICATE_NAME` | `"SEED.DUPLICATE_NAME"` |
| `UNREGISTERED_DEPENDENCY` | `"SEED.UNREGISTERED_DEPENDENCY"` |
| `CIRCULAR_DEPENDENCY` | `"SEED.CIRCULAR_DEPENDENCY"` |
| `DEPENDENCY_DEPTH_EXCEEDED` | `"SEED.DEPENDENCY_DEPTH_EXCEEDED"` |
| `INVALID_CONFIG` | `"SEED.INVALID_CONFIG"` |
| `STORE_ERROR` | `"SEED.STORE_ERROR"` |
| `ABORTED` | `"SEED.ABORTED"` |
### `type SeedErrorCode`
The literal union of every value in `SEED_CODES`.
### `class SeedError extends SuaveplanError<SeedErrorCode>`
| Member | Type |
| ------------------ | ---------------------------------------------------------------------- |
| `code` | `SeedErrorCode` (narrowed) |
| `seedName?` | `string` |
| `dependency?` | `string` |
| `cause?` | `unknown` (inherited) |
| `timestamp` | `number` (inherited; `Date.now()` at construction) |
| `constructor(...)` | `(code, message, options?: SeedErrorOptions)` |
`SeedErrorOptions` extends `SuaveplanErrorOptions` (`cause`, `context`,
`clock`, `traceId`) with the additional optional `seedName` and
`dependency` fields.
## Implementation Notes
`defineCodes` returns a `Object.freeze`d record with `null` prototype, so
the registry cannot be mutated, reassigned, or extended at runtime — any
attempt fails in strict mode (which TypeScript modules enforce). The
literal type machinery preserves the exact `${Namespace}.${Reason}`
string template across every consumer, so `switch (err.code)` enjoys
exhaustive narrowing without runtime cost.
`SeedError`'s constructor passes `message` straight through to
`SuaveplanError`'s `messageKey` parameter. The base class resolves the
key via the i18n resolver if one has been registered with
`setMessageResolver`, falling back to the raw string otherwise — so this
package works without i18n wiring and lights up automatically when
`@suaveplan/i18n` is present.
Optional `seedName` / `dependency` fields are stored on the error instance
directly (not on `context`) so they survive structured-cloning and
serialisation via the `toJSON` helper from `@suaveplan/error`. Consumers
that programmatically dispatch on a missing dependency can read
`err.dependency` without first parsing `err.context`.