Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
# DatabaseError / MigrationError
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`DatabaseError` is the structured error class for all database failures in `@suaveplan/db`. It extends `SuaveplanError` from `@suaveplan/error`, ensuring every database error carries a stable, machine-readable `DB.*` code alongside a human-readable message and optional cause chain. `MigrationError` is a specialisation that records which named migration failed.
Using `SuaveplanError` as the base means consumers can perform type-safe `instanceof` checks, extract OTel-compatible codes from `error.code`, and inspect cause chains consistently across the entire Suaveplan project.
## Features
- Extends `SuaveplanError<DbCode>` — participates in the project-wide error hierarchy
- Stable `DB.*` codes registered via `defineCodes` — never change between versions
- `cause` threading — wraps lower-level driver errors without losing the original stack
- `MigrationError.migration` field — names the exact migration file that failed
- `name` property set to the concrete class name for easy identification in logs
## Usage: Basic Example
```ts
import { DatabaseError, MigrationError, DB_CODES } from "@suaveplan/db";
// Throw a connection failure
throw new DatabaseError(
DB_CODES.CONNECTION_FAILED,
"Could not reach postgres://localhost:5432/app"
);
// Throw a migration failure
throw new MigrationError("003_add_index", "duplicate column name: email");
```
## Usage: Advanced Example
```ts
import { DatabaseError, DB_CODES } from "@suaveplan/db";
import { SuaveplanError } from "@suaveplan/error";
async function runQuery(sql: string): Promise<unknown[]> {
try {
return await driver.query(sql);
} catch (cause) {
throw new DatabaseError(
DB_CODES.QUERY_FAILED,
`Query failed: ${cause instanceof Error ? cause.message : String(cause)}`,
{ cause: cause instanceof Error ? cause : undefined }
);
}
}
// Narrowing in catch blocks
try {
await runQuery("SELECT * FROM users");
} catch (err) {
if (err instanceof DatabaseError) {
console.error(err.code, err.message); // "DB.QUERY_FAILED Query failed: ..."
}
}
```
## API Reference
### `DatabaseError`
```ts
class DatabaseError extends SuaveplanError<DbCode> {
constructor(code: DbCode, message: string, options?: SuaveplanErrorOptions): void
}
```
- **`code`** (`DbCode`) — one of `DB.CONNECTION_FAILED`, `DB.NOT_CONNECTED`, `DB.QUERY_FAILED`, `DB.TRANSACTION_FAILED`, `DB.MIGRATION_FAILED`
- **`message`** (`string`) — human-readable description
- **`options.cause`** (`Error | undefined`) — lower-level error that triggered this one
### `MigrationError`
```ts
class MigrationError extends DatabaseError {
readonly migration: string;
constructor(migration: string, detail: string, options?: SuaveplanErrorOptions): void
}
```
- **`migration`** (`string`) — the migration name (e.g. `"001_init"`)
- **`detail`** (`string`) — description of what went wrong
- Message format: `"Migration '<migration>' failed: <detail>"`
- Code is always `DB.MIGRATION_FAILED`
### `DB_CODES`
```ts
const DB_CODES: {
readonly CONNECTION_FAILED: "DB.CONNECTION_FAILED";
readonly NOT_CONNECTED: "DB.NOT_CONNECTED";
readonly QUERY_FAILED: "DB.QUERY_FAILED";
readonly TRANSACTION_FAILED:"DB.TRANSACTION_FAILED";
readonly MIGRATION_FAILED: "DB.MIGRATION_FAILED";
}
```
## Implementation Notes
Both classes pass the `message` string directly to `SuaveplanError` as a literal message key rather than an i18n key, so no message-resolver configuration is required. This keeps `@suaveplan/db` dependency-free of any i18n infrastructure. If localisation is needed in a future version, the message key can be replaced with an i18n key and the `context` option used for interpolation.
`MigrationError` explicitly sets `this.name = "MigrationError"` in the constructor because `SuaveplanError` sets `this.name = new.target.name` — the explicit assignment in the subclass constructor ensures the name is accurate when the class is transpiled or minified.