/** * What a failed provider call proves about the side effect: * - "rejected": the provider answered and definitively did not perform it. * - "unknown": the request may have been applied (5xx/transport loss on a * non-idempotent request) — never resend without a provider dedupe key. */ export type DeliveryCertainty = "rejected" | "unknown"; export class ConnectorError extends Error { constructor( message: string, readonly provider: string, override readonly cause?: unknown, ) { super(message); this.name = "ConnectorError"; } } export class UnknownProviderError extends ConnectorError { constructor(providerId: string) { super(`no connector registered for provider "${providerId}"`, providerId); this.name = "UnknownProviderError"; } } export class AuthError extends ConnectorError { constructor(message: string, provider: string, readonly retryable: boolean) { super(message, provider); this.name = "AuthError"; } } export class RateLimitError extends ConnectorError { /** A 429 is a refusal before processing, so nothing was delivered. */ readonly delivery: DeliveryCertainty = "rejected"; constructor( provider: string, readonly retryAfterMs?: number, readonly status = 429, ) { super(`rate limited by ${provider}`, provider); this.name = "RateLimitError"; } } /** The provider answered with a non-2xx status that is a definite refusal (4xx). */ export class ProviderRejectedError extends ConnectorError { readonly delivery: DeliveryCertainty = "rejected"; constructor( message: string, provider: string, readonly status: number, readonly retryAfterMs?: number, ) { // cause keeps the `{ status }` shape connectors already inspect. super(message, provider, { status }); this.name = "ProviderRejectedError"; } } /** * 5xx answer the SDK client did not retry (or stopped retrying). For * non-idempotent requests delivery is "unknown": the server may have applied * the write before failing. */ export class ProviderServerError extends ConnectorError { constructor( provider: string, readonly status: number, readonly method: string, readonly delivery: DeliveryCertainty, readonly retryAfterMs?: number, ) { super(`${provider}: HTTP ${status} on ${method}`, provider, { status }); this.name = "ProviderServerError"; } } /** fetch itself failed (socket loss, DNS, abort) on a non-idempotent request. */ export class ProviderTransportError extends ConnectorError { readonly delivery: DeliveryCertainty = "unknown"; constructor( provider: string, readonly method: string, cause: unknown, ) { super(`${provider}: transport failure on ${method}`, provider, cause); this.name = "ProviderTransportError"; } } /** Delivery certainty carried by an SDK error, if the error type records one. */ export function deliveryCertaintyOf(error: unknown): DeliveryCertainty | undefined { if (error instanceof AuthError) return "rejected"; if ( error instanceof RateLimitError || error instanceof ProviderRejectedError || error instanceof ProviderServerError || error instanceof ProviderTransportError ) { return error.delivery; } return undefined; } export class CapabilityNotSupportedError extends ConnectorError { constructor(provider: string, capability: string) { super(`${provider} does not support "${capability}"`, provider); this.name = "CapabilityNotSupportedError"; } }