first commit
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# VaultSyncError
|
||||
|
||||
## Purpose
|
||||
|
||||
`VaultSyncError` is the single concrete error class thrown from every
|
||||
production path inside `@suaveplan/vault-sync`. It extends the foundational
|
||||
`SuaveplanError` from `@suaveplan/error` and parameterises the `code` field
|
||||
on the literal union `VaultSyncCode` so consumers can switch on `err.code`
|
||||
with full TypeScript exhaustiveness. A unique brand symbol is attached at
|
||||
construction so cross-realm or duck-typed impersonators are rejected by the
|
||||
`VaultSyncError.is(value)` guard.
|
||||
|
||||
## Features
|
||||
|
||||
- Extends `SuaveplanError<VaultSyncCode>` for narrowed `code` checks
|
||||
- Brand symbol prevents prototype-chain impersonation across realms
|
||||
- `VaultSyncError.is(value)` runs both `instanceof` and brand verification
|
||||
- Carries structured `context`, `cause`, `traceId`, and `timestamp` from the base
|
||||
- Pairs with the `VAULT_SYNC_CODES` registry so every code in the union is reachable
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { VaultSyncError } from "@suaveplan/vault-sync";
|
||||
import { VAULT_SYNC_CODES } from "@suaveplan/vault-sync/errors";
|
||||
|
||||
throw new VaultSyncError(
|
||||
VAULT_SYNC_CODES["MERGE.STRATEGY_UNKNOWN"],
|
||||
"error.vault-sync.merge.strategy-unknown",
|
||||
{ context: { strategy: "smart-merge" } }
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { VaultSyncError } from "@suaveplan/vault-sync";
|
||||
import { VAULT_SYNC_CODES } from "@suaveplan/vault-sync/errors";
|
||||
|
||||
async function safeFlush(input: () => Promise<void>): Promise<{ ok: true } | { ok: false; code: string }> {
|
||||
try {
|
||||
await input();
|
||||
return { ok: true };
|
||||
} catch (err) {
|
||||
// Defence-in-depth: instanceof can break across worker boundaries.
|
||||
if (VaultSyncError.is(err)) {
|
||||
return { ok: false, code: err.code };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await safeFlush(async () => {
|
||||
throw new VaultSyncError(
|
||||
VAULT_SYNC_CODES["QUEUE.DEPTH_EXCEEDED"],
|
||||
"error.vault-sync.queue.depth-exceeded",
|
||||
{ context: { depth: 10_001, limit: 10_000 } }
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class VaultSyncError extends SuaveplanError<VaultSyncCode>` — concrete
|
||||
error type for every failure inside `vault-sync`.
|
||||
- `constructor(code: VaultSyncCode, messageKey: string, options?: SuaveplanErrorOptions)`
|
||||
— `messageKey` is forwarded to the i18n resolver. `options.context` may
|
||||
carry safe-by-construction fields (no key bytes, no plaintext, no item IDs).
|
||||
- `static is(value: unknown): value is VaultSyncError` — narrow guard that
|
||||
verifies both `instanceof` and the unique brand symbol attached at
|
||||
construction. Use whenever errors might cross `iframe`, `Worker`, or
|
||||
serialization boundaries.
|
||||
- Inherited fields: `code`, `message`, `context`, `cause`, `timestamp`,
|
||||
`traceId`. See `@suaveplan/error` for full semantics.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The brand symbol is module-private — a third party cannot construct a
|
||||
duck-typed value that satisfies `VaultSyncError.is`. Subclasses are
|
||||
discouraged: if a more specific error is needed, attach metadata via
|
||||
`options.context` rather than introducing a new class. Centralising on a
|
||||
single throwable simplifies dependent-package error handling, reduces
|
||||
`instanceof` chains, and keeps the public API surface minimal. All thrown
|
||||
errors flow through this class — no plain `Error`, no anonymous throws.
|
||||
Reference in New Issue
Block a user