38 lines
3.2 KiB
Markdown
38 lines
3.2 KiB
Markdown
# Change: enterprise-error
|
|
|
|
## Why
|
|
|
|
`@suaveplan/error` is the **foundation error substrate** — every other package in the monorepo extends `SuaveplanError` and uses its stable code registry. On the genesis branch it does not yet exist. It must land **before** any other Layer 1 package (events, crypto, bitpacker, timing, datetime, serialize, mime, env, opentelemetry) because each of them declares its own error subclass against this base.
|
|
|
|
Per the project rule for foundation packages (`memory: foundation-100-coverage`), this ships at **100%** coverage (statements / branches / functions / lines) — not the 95% floor. Anything less puts a long-term ceiling on every consumer.
|
|
|
|
## What Changes
|
|
|
|
### New package: `packages/universal/error`
|
|
|
|
Ships a self-contained, zero-dependency (no `@suaveplan/*`, no runtime browser/Node/React APIs) TypeScript library:
|
|
|
|
- **`SuaveplanError`** abstract base class with `{ code, message, cause?, context?, timestamp, traceId? }` shape
|
|
- **Stable code registry** — namespaced `PACKAGE.DOMAIN.REASON` strings, typed as a const record; each package adds its own leaf registry that extends this one without renumbering
|
|
- **Typed error discriminants** — generics propagate the code union through `instanceof` checks so consumers get exhaustive switch coverage without casts
|
|
- **Cause-chain traversal** — `walkCauses(err)` generator, cycle-safe, depth-bounded
|
|
- **Wire format** — Zod schema for JSON and MessagePack (MessagePack encoder ships as a pure function, no external `msgpack` dep; binary format is documented and versioned)
|
|
- **Redaction** — `@sensitive` tag on context fields, applied at the serialization boundary; redacted fields become `"[REDACTED]"` in the wire payload but remain accessible on the in-memory instance
|
|
- **Injectable clock** — `createError({ clock })` takes an optional clock so timestamps are deterministic under test; the module-level default uses `Date.now()` but is never touched at import time
|
|
- **OTel span event adapter** — `toSpanEvent(err)` returns the payload an OTel span recorder expects (`exception.type`, `exception.message`, `exception.stacktrace`, all attribute values serialized)
|
|
- **i18n-ready** — every user-visible string is keyed, never literal; the default locale is pass-through (returns the key)
|
|
|
|
### Why this stays foundation-pure
|
|
|
|
The enterprise brief calls for `@suaveplan/schema-registry`, `@suaveplan/logger`, `@suaveplan/i18n`, and `@suaveplan/opentelemetry` integration. Those are **adapters**, not dependencies — this package exposes the hooks (`toSpanEvent`, i18n-key strings, Zod schemas, logger-compatible `toJSON`) but does not import any of them. Wiring happens in the dependent packages.
|
|
|
|
### Coverage gate
|
|
|
|
Vitest thresholds set to 100/100/100/100 from day one. No `istanbul ignore` pragmas (empirically stripped by the esbuild transform). Dead defensive branches are either removed or upgraded to `as` assertions with invariant comments.
|
|
|
|
## Impact
|
|
|
|
- Unblocks every other Layer 1 package (events, crypto, bitpacker, timing, datetime, serialize, mime, env, opentelemetry) which extend `SuaveplanError`
|
|
- Adds one new spec capability: `error`
|
|
- No existing code paths affected (genesis branch has no consumers yet)
|