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,148 @@
# `errors`
Stable error class hierarchy and code registry for
`@suaveplan/third-party-elevenlabs`. Every non-2xx HTTP response and every
transport-level failure maps to one of 17 typed `ElevenLabsApiError`
subclasses under namespace `THIRD_PARTY_ELEVENLABS.*`.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The ElevenLabs API surface is large and surfaces failures across HTTP status
codes, malformed JSON bodies, transport timeouts, and WebSocket protocol
errors. Without a stable error hierarchy, every consumer of this client
would have to learn the upstream's quirks. This module defines the
canonical 17-code surface so callers can branch on `error.code` or
`error instanceof RateLimitExceededError` without touching the wire.
The mapping is total and disjoint over the `[400, 600)` HTTP range, every
documented network failure mode, every JSON / Zod parse failure, and the
two WebSocket failure modes (handshake / protocol). Every error preserves
the original cause via the cause-chain on `SuaveplanError`.
## Features
- 17 error codes registered via `defineCodes('THIRD_PARTY_ELEVENLABS', ...)`.
- 17 typed subclasses, one per code, with sensible default messages.
- Convenience options surface common transport context:
- `RateLimitExceededError({ retryAfterMs })` — retries planning.
- `RequestValidationFailedError({ body })` — preserved 422 body.
- `WebSocketConnectionFailedError({ code })` — close code.
- `NetworkTimeoutError({ timeoutMs })` — observed limit.
- Every HTTP error accepts `{ endpoint, statusCode, requestId }`.
- Caller-supplied `context` is shallow-merged on top of derived context so
adapter packages can layer their own attribution (e.g. `tenantId`).
## Usage: Basic Example
```ts
import {
ElevenLabsApiError,
RateLimitExceededError,
} from "@suaveplan/third-party-elevenlabs/errors/errors";
try {
await client.fetchJson(req, schema);
} catch (err) {
if (err instanceof RateLimitExceededError) {
console.log(`retry after ${err.context?.retryAfterMs ?? "unknown"} ms`);
}
}
```
## Usage: Advanced Example
Branch on every documented failure mode and re-throw with adapter context:
```ts
import {
ElevenLabsApiError,
ELEVENLABS_CODES,
} from "@suaveplan/third-party-elevenlabs/errors/errors";
function handleFailure(err: unknown): never {
if (!(err instanceof ElevenLabsApiError)) throw err;
switch (err.code) {
case ELEVENLABS_CODES["AUTH.UNAUTHORIZED"]:
case ELEVENLABS_CODES["AUTH.FORBIDDEN"]:
throw new ProviderNotConfiguredError({ cause: err });
case ELEVENLABS_CODES["RATE_LIMIT.EXCEEDED"]:
case ELEVENLABS_CODES["RATE_LIMIT.QUOTA_EXHAUSTED"]:
throw new ProviderRateLimitedError({
retryAfterMs: err.context?.retryAfterMs as number | undefined,
cause: err,
});
default:
throw new TtsSynthesisFailedError({ cause: err });
}
}
```
## API Reference
- `ELEVENLABS_CODES` — frozen record `{ "<reason>":
"THIRD_PARTY_ELEVENLABS.<reason>" }`.
- `ElevenLabsErrorCode` — TypeScript union of every code value.
- `ElevenLabsApiError` — abstract base extending `SuaveplanError`.
Auth + request errors:
- `UnauthorizedError({ endpoint?, statusCode?, requestId?, ...base })` —
`AUTH.UNAUTHORIZED` (HTTP 401).
- `ForbiddenError(...)` — `AUTH.FORBIDDEN` (HTTP 403).
- `RequestInvalidError(...)` — `REQUEST.INVALID` (other 4xx).
- `RequestValidationFailedError({ body?, ...request })` —
`REQUEST.VALIDATION_FAILED` (HTTP 422); body preserved verbatim.
- `RequestNotFoundError(...)` — `REQUEST.NOT_FOUND` (HTTP 404).
Rate limit:
- `RateLimitExceededError({ retryAfterMs?, ...request })` —
`RATE_LIMIT.EXCEEDED` (HTTP 429).
- `RateLimitQuotaExhaustedError(...)` — `RATE_LIMIT.QUOTA_EXHAUSTED`.
Server:
- `ServerTransientError(...)` — `SERVER.TRANSIENT` (retryable 5xx).
- `ServerUnavailableError(...)` — `SERVER.UNAVAILABLE` (non-retryable / circuit-open).
Network:
- `NetworkTimeoutError({ endpoint?, timeoutMs?, ...base })` —
`NETWORK.TIMEOUT`.
- `NetworkAbortedError({ endpoint?, ...base })` — `NETWORK.ABORTED`.
- `NetworkUnreachableError({ endpoint?, ...base })` — `NETWORK.UNREACHABLE`.
Response / stream:
- `ResponseMalformedError({ endpoint?, bodyPreview?, ...base })` —
`RESPONSE.MALFORMED`.
- `ResponseUnsupportedFormatError({ contentType?, ...base })` —
`RESPONSE.UNSUPPORTED_FORMAT`.
- `StreamInterruptedError(...)` — `STREAM.INTERRUPTED`.
WebSocket:
- `WebSocketConnectionFailedError({ code?, ...base })` —
`WEBSOCKET.CONNECTION_FAILED`.
- `WebSocketProtocolError({ code?, ...base })` — `WEBSOCKET.PROTOCOL`.
Every constructor accepts `{ message?, cause?, context?, traceId? }`.
## Implementation Notes
- The 17 codes partition `[400, 600)` HTTP statuses plus three networking
modes plus three response / stream modes plus two WebSocket modes. The
client never throws a generic `ElevenLabsApiError` — every thrown error
is one of the typed subclasses.
- `RequestValidationFailedError.body` should be the **parsed** JSON body
(truncated to a reasonable bound by the client — currently 4 KB; see
`client.ts`). Never put raw network buffers here.
- `bodyPreview` on `ResponseMalformedError` is a short string (≤ 256
bytes) suitable for logs. Adapters should not re-serialise the full body
into context.
- All subclasses default to a stable English message. Localisation flows
through `MessageResolver` on `@suaveplan/error`.