{"version":3,"file":"errors.cjs","names":[],"sources":["../../src/http/errors.ts"],"sourcesContent":["import type { ApiError } from \"./types\";\n\n/**\n * Error thrown by {@link createApiClient} / {@link uploadWithProgress} on a\n * non-2xx response. Mirrors the Tempest FastAPI SDK error envelope\n * (`{ detail, code, details.request_id }`) so callers get a typed `code` and a\n * `requestId` for log correlation, while still being a real `Error` (stack\n * trace, `instanceof Error`).\n *\n * @example\n * try {\n *     await api.post(\"/users\", { body });\n * } catch (err) {\n *     if (isApiError(err) && err.code === \"EMAIL_TAKEN\") {\n *         showFieldError(\"email\", err.detail);\n *     }\n * }\n */\nexport class TempestApiError extends Error implements ApiError {\n    readonly status: number;\n    readonly detail: string;\n    readonly code?: string;\n    readonly requestId?: string;\n    readonly fields?: Record<string, string>;\n    readonly body?: unknown;\n\n    constructor(init: ApiError) {\n        super(init.detail);\n        this.name = \"TempestApiError\";\n        this.status = init.status;\n        this.detail = init.detail;\n        this.code = init.code;\n        this.requestId = init.requestId;\n        this.fields = init.fields;\n        this.body = init.body;\n    }\n}\n\n/**\n * Type guard for the {@link ApiError} shape. Matches both {@link TempestApiError}\n * instances and plain objects carrying `status` + `detail`.\n *\n * @param error - The unknown value (typically a caught error).\n * @returns Whether `error` conforms to the `ApiError` contract.\n */\nexport function isApiError(error: unknown): error is ApiError {\n    return (\n        typeof error === \"object\" &&\n        error !== null &&\n        typeof (error as ApiError).status === \"number\" &&\n        typeof (error as ApiError).detail === \"string\"\n    );\n}\n\n/**\n * Location prefixes FastAPI puts at the head of a validation error's `loc`,\n * naming the part of the request rather than the field. Dropped from the\n * rendered path, so `[\"body\", \"email\"]` reads as `email`.\n */\nconst LOC_ROOTS: ReadonlySet<string> = new Set([\"body\", \"query\", \"path\", \"header\", \"cookie\"]);\n\n/**\n * Render a FastAPI validation error's `loc` tuple as a dotted field path.\n *\n * @param loc - The raw `loc` value from one validation error entry.\n * @returns The dotted path (`\"items.0.price\"`), or undefined when `loc` carries\n *     nothing addressable.\n */\nfunction formatLoc(loc: unknown): string | undefined {\n    if (!Array.isArray(loc)) return undefined;\n    const parts = loc\n        .filter(\n            (part): part is string | number => typeof part === \"string\" || typeof part === \"number\",\n        )\n        .filter((part, index) => !(index === 0 && LOC_ROOTS.has(String(part))));\n    return parts.length > 0 ? parts.join(\".\") : undefined;\n}\n\n/**\n * How deep {@link normalizeDetail} follows a nested `detail` before giving up.\n *\n * A real envelope needs two or three levels: the list, an entry, the entry's\n * own `detail`. The cap exists because the body is untrusted input arriving on\n * the error path — a response nesting `{\"detail\":{\"detail\":…}}` twenty thousand\n * deep (a 220 KB body) overflowed the stack, and a `RangeError` thrown while\n * *building* the error is worse than the error: the caller's `catch` stops\n * receiving a `TempestApiError`, so `isApiError` is false, `describeApiError`\n * has nothing to read and the `401` handling never runs.\n */\nconst MAX_DETAIL_DEPTH = 4;\n\n/**\n * Pull field-level messages out of a validation `detail` **list**.\n *\n * FastAPI's `422` body is `detail: [{ loc, msg, type }]`, which is exactly what\n * a form needs and exactly what the flattened `detail` string destroys. Only the\n * top level is read: a validation error names one field per entry, and following\n * nesting here would invent paths the backend never sent.\n *\n * This is one of two ways a body names a field — see {@link collectFields}, which\n * is the entry point and falls back to the singular keys when the list names\n * nothing addressable.\n *\n * @param raw - The `detail` value from the error body.\n * @returns Field path to message, or undefined when the body is not a\n *     validation list (or carries no entry naming a field).\n */\nfunction collectListFields(raw: unknown): Record<string, string> | undefined {\n    if (!Array.isArray(raw)) return undefined;\n\n    const fields: Record<string, string> = {};\n    for (const entry of raw) {\n        if (typeof entry !== \"object\" || entry === null) continue;\n        const record = entry as Record<string, unknown>;\n        const field = formatLoc(record.loc);\n        if (field === undefined || field in fields) continue;\n        const message = normalizeDetail(record.msg) ?? normalizeDetail(record.message);\n        if (message === undefined) continue;\n        fields[field] = message;\n    }\n\n    return Object.keys(fields).length > 0 ? fields : undefined;\n}\n\n/**\n * Narrow a value to a plain object — a record, and not an array.\n *\n * Arrays are excluded because `typeof [] === \"object\"` while a list means\n * something else entirely here: FastAPI's `detail` list is read by\n * {@link collectListFields}, and reading `.field` off it would only ever be\n * undefined.\n *\n * @param value - The candidate value from the error body.\n * @returns The value as a record, or undefined when it is not a plain object.\n */\nfunction plainRecord(value: unknown): Record<string, unknown> | undefined {\n    return typeof value === \"object\" && value !== null && !Array.isArray(value)\n        ? (value as Record<string, unknown>)\n        : undefined;\n}\n\n/**\n * Read the field a **singular** error envelope names.\n *\n * A backend built on `tempest-fastapi-sdk` never answers with FastAPI's `detail`\n * list once it owns the handler: it names the guilty field in a key beside the\n * message. Three shapes are seen in the wild, and they are read inner-out:\n *\n * 1. `detail.field` — the field sits in the same object as the message it\n *    describes (`{ detail: { detail: \"Cidade não encontrada…\", field: \"city\" } }`),\n *    so it is the least ambiguous claim about which message belongs to which input.\n * 2. `field` at the top level — a flattened `RequestValidationError`\n *    (`{ detail: \"Value error, … for field 'phone' in 'body'\", field: \"phone\" }`)\n *    makes the same claim one level out.\n * 3. `details.field` — `details` is the envelope's free-form context bag, not a\n *    validation channel, and its `field` may be about something no input on screen\n *    carries (an unknown sort column, say). It answers last for exactly that reason.\n *\n * `location` (`\"body -> phone\"`) is deliberately not parsed: it renders the same\n * path `field` already names, no observed envelope sends it without `field`, and\n * splitting an arrow-separated string would invent a path the backend never sent.\n *\n * @param body - The parsed error body, or null when it was not an object.\n * @returns The field name, or undefined when none of the three keys carried a\n *     non-empty string — anything else is not usable as a key on `fields`.\n */\nfunction namedField(body: Record<string, unknown> | null): string | undefined {\n    if (body === null) return undefined;\n    const candidates: readonly unknown[] = [\n        plainRecord(body.detail)?.field,\n        body.field,\n        plainRecord(body.details)?.field,\n    ];\n    for (const candidate of candidates) {\n        if (typeof candidate === \"string\" && candidate !== \"\") return candidate;\n    }\n    return undefined;\n}\n\n/**\n * Drop the machine-readable tail a `tempest-fastapi-sdk` backend appends.\n *\n * That backend flattens a field error into\n * `\"CPF ou CNPJ inválido for field 'cpf_cnpj' in 'body'\"` — a finished sentence\n * in the app's language with an English clause glued to the end. The clause\n * carries nothing new: the same two values arrive as `field` and `location` on\n * the envelope, which is where `ApiError.fields` reads them from. Left in, it\n * reaches the user, and every consuming app grew its own regex to shave it off.\n *\n * The trim only fires when the field the tail names is the field the envelope\n * resolved to. That is what keeps it from deleting text it cannot account for:\n * a tail naming some other field is either a different envelope shape or a\n * sentence that genuinely reads that way, and both are left alone.\n *\n * @param message - The readable message, or undefined when the body had none.\n * @param field - The field name {@link namedField} resolved, if any.\n * @returns The message without the tail, or the message unchanged.\n */\nfunction trimFieldSuffix(\n    message: string | undefined,\n    field: string | undefined,\n): string | undefined {\n    if (message === undefined || field === undefined) return message;\n    const escaped = field.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n    const trimmed = message\n        .replace(new RegExp(`\\\\s+for field '${escaped}'(?:\\\\s+in\\\\s+'[^']*')?\\\\.?\\\\s*$`), \"\")\n        .trim();\n    return trimmed === \"\" ? message : trimmed;\n}\n\n/**\n * Index the body's field-level messages, whichever envelope carried them.\n *\n * The single entry point behind `ApiError.fields`, and it applies two rules the\n * tests pin:\n *\n * - FastAPI's `detail` list wins whenever it names at least one addressable\n *   field. A plain FastAPI app still answers a schema-level `422` with that list\n *   even on a `tempest-fastapi-sdk` backend, so it stays authoritative.\n * - A named field with no readable message produces nothing. The only string\n *   left at that point is the synthetic `Erro <status>`, and `{ phone: \"Erro 422\" }`\n *   on an input is noise rather than an error message.\n *\n * @param body - The parsed error body, or null when it was not an object.\n * @param message - The readable message the same body produced, before the\n *     synthetic fallback — the string that also becomes `ApiError.detail`.\n * @returns Field name to message, or undefined when nothing named a field.\n */\nfunction collectFields(\n    body: Record<string, unknown> | null,\n    message: string | undefined,\n): Record<string, string> | undefined {\n    const listed = collectListFields(body?.detail);\n    if (listed !== undefined) return listed;\n    const field = namedField(body);\n    if (field === undefined || message === undefined) return undefined;\n    return { [field]: message };\n}\n\n/**\n * Collapse a backend `detail` of any shape into a single readable line.\n *\n * FastAPI answers a `422` with `detail` as a **list** of\n * `{ loc, msg, type }` entries, not a string. Passing that through `String()`\n * yields `\"[object Object]\"` — an error message that tells the user nothing and\n * hides which field failed. Each entry becomes `\"<field>: <msg>\"` and the\n * entries are joined with `\"; \"`; a nested object is read through its\n * `msg`/`message`/`detail` string.\n *\n * @param raw - The `detail` (or `message`) value from the error body.\n * @param depth - Current nesting level. Past {@link MAX_DETAIL_DEPTH} the value\n *     is treated as unreadable instead of followed further.\n * @returns The rendered message, or undefined when nothing readable is there —\n *     letting the caller fall back to the synthetic `Erro <status>`.\n */\nfunction normalizeDetail(raw: unknown, depth: number = 0): string | undefined {\n    if (raw === null || raw === undefined) return undefined;\n    if (typeof raw === \"string\") return raw === \"\" ? undefined : raw;\n    if (typeof raw === \"number\" || typeof raw === \"boolean\") return String(raw);\n    if (depth >= MAX_DETAIL_DEPTH) return undefined;\n\n    if (Array.isArray(raw)) {\n        const lines = raw\n            .map((entry) => {\n                const message = normalizeDetail(entry, depth + 1);\n                if (message === undefined) return undefined;\n                const field =\n                    typeof entry === \"object\" && entry !== null\n                        ? formatLoc((entry as Record<string, unknown>).loc)\n                        : undefined;\n                return field === undefined ? message : `${field}: ${message}`;\n            })\n            .filter((line): line is string => line !== undefined);\n        return lines.length > 0 ? lines.join(\"; \") : undefined;\n    }\n\n    if (typeof raw === \"object\") {\n        const entry = raw as Record<string, unknown>;\n        return (\n            normalizeDetail(entry.msg, depth + 1) ??\n            normalizeDetail(entry.message, depth + 1) ??\n            normalizeDetail(entry.detail, depth + 1)\n        );\n    }\n\n    return undefined;\n}\n\n/**\n * Statuses worth a second attempt, as a set for the sub-500 cases.\n *\n * A network failure (status `0`), a request timeout, a too-early replay, and a\n * rate limit — which usually carries the `Retry-After` the backoff honours.\n * Everything else below 500 is the server refusing on purpose.\n */\nconst RETRIABLE_STATUSES: ReadonlySet<number> = new Set([0, 408, 425, 429]);\n\n/**\n * Whether an HTTP status describes a condition a replay can plausibly fix.\n *\n * The single owner of that decision. It used to be spelled out in three places —\n * the client's own policy, the react-query default and the bare `retry()` helper\n * — and they had already drifted: the query default was missing `425`, so the\n * same `425 Too Early` was replayed through `createApiClient({ retry: true })`\n * and not replayed through `useQuery`. Same app, same error, two behaviours, and\n * no test caught it because each file asserted against its own copy.\n *\n * Deliberately about the status and nothing else. Whether a *non*-API error is\n * worth replaying, and whether the request's method may be replayed at all, are\n * the caller's calls: {@link createApiClient} refuses a non-idempotent method,\n * while a bare `retry()` has no method to inspect.\n *\n * @example\n * await api.get(\"/report\", {\n *     retry: { shouldRetry: (error) => isApiError(error) && isRetriableStatus(error.status) },\n * });\n *\n * @param status - The HTTP status, where `0` means the request never landed.\n * @returns Whether a retry is worth attempting.\n */\nexport function isRetriableStatus(status: number): boolean {\n    return RETRIABLE_STATUSES.has(status) || status >= 500;\n}\n\n/**\n * Detail text synthesised when a response body carries none.\n *\n * Exported because {@link describeApiError} has to recognise it: a detail the\n * server never sent says strictly less than the caller's own fallback, so the\n * funnel drops it. Comparing against a copied literal would silently stop\n * matching the day this sentence is reworded — no type error, no failing test.\n *\n * @param status - The HTTP status of the error.\n * @returns The synthetic detail for that status.\n */\nexport function syntheticDetail(status: number): string {\n    return `Erro ${status}`;\n}\n\n/**\n * Parse an error body + response into the Tempest {@link ApiError} envelope.\n *\n * Reads `detail`/`message`, the programmatic `code`, and the correlation id\n * from `details.request_id` (falling back to the `X-Request-ID` header, then\n * the id the client sent).\n *\n * A `422` from FastAPI carries `detail` as a list of `{ loc, msg, type }`\n * entries, so it is flattened to `\"<field>: <msg>; <field>: <msg>\"` instead of\n * being stringified into `\"[object Object]\"`, and the same entries are indexed\n * on `fields` (`{ email: \"Field required\" }`) for a form to consume without\n * parsing that line back apart. The untouched body stays on `body`.\n *\n * A backend that owns its handlers — every `tempest-fastapi-sdk` app — sends no\n * such list, and names the field in a key instead:\n *\n * ```json\n * { \"detail\": \"Value error, … for field 'phone' in 'body'\", \"field\": \"phone\" }\n * { \"detail\": { \"detail\": \"Cidade não encontrada…\", \"field\": \"city\" },\n *   \"code\": \"VALIDATION_ERROR\", \"details\": { \"field\": \"city\" } }\n * ```\n *\n * Those are indexed too, keyed by the field the backend named and valued with the\n * same sentence that becomes `detail`. Precedence is the list first, then\n * `detail.field`, `field`, `details.field` — see {@link collectFields}.\n *\n * A flattened `detail` from the list is developer-facing: it carries the\n * backend's field paths and the validator's own wording. `describeApiError` knows\n * not to show it to a person when `fields` is set — which now also covers a\n * business error that named a field, whose `detail` was a finished sentence. That\n * sentence is not lost: it is on `fields`, attached to the input that failed.\n *\n * @param status - HTTP status code.\n * @param body - The parsed error body (object, string, or null).\n * @param headers - The response headers (for the `X-Request-ID` fallback).\n * @param sentRequestId - The id the client sent on the request, if any.\n * @returns A fully-populated `ApiError`.\n *\n * @tempest-limits param-count — the arguments are the response as it arrives\n * (`status`, `body`, `headers`) plus the id the request was sent with, and they are\n * passed at exactly three places, all of them a client's response path\n * (`createApiClient`, `uploadWithProgress`, `createResumableUpload`). Wrapping them\n * in an options object would name each argument twice at every call site to say\n * nothing new.\n */\nexport function buildApiError(\n    status: number,\n    body: unknown,\n    headers?: Headers | { get(name: string): string | null },\n    sentRequestId?: string,\n): ApiError {\n    const obj =\n        typeof body === \"object\" && body !== null ? (body as Record<string, unknown>) : null;\n    const message = trimFieldSuffix(\n        normalizeDetail(obj?.detail) ?? normalizeDetail(obj?.message),\n        namedField(obj),\n    );\n    const detail = message ?? syntheticDetail(status);\n    const code = typeof obj?.code === \"string\" ? obj.code : undefined;\n    const details =\n        typeof obj?.details === \"object\" && obj.details !== null\n            ? (obj.details as Record<string, unknown>)\n            : null;\n    const requestId =\n        (typeof details?.request_id === \"string\" ? details.request_id : undefined) ??\n        headers?.get(\"X-Request-ID\") ??\n        sentRequestId ??\n        undefined;\n\n    return {\n        status,\n        detail,\n        code,\n        requestId: requestId ?? undefined,\n        retryAfter: parseRetryAfter(headers?.get(\"Retry-After\")),\n        fields: collectFields(obj, message),\n        body,\n    };\n}\n\n/**\n * Parse a `Retry-After` header into seconds. Accepts a delta-seconds integer\n * (`\"120\"`) or an HTTP-date (`\"Wed, 21 Oct 2015 07:28:00 GMT\"`).\n *\n * @param value - The raw header value, or null.\n * @returns The delay in seconds (>= 0), or undefined when absent/unparseable.\n */\nexport function parseRetryAfter(value: string | null | undefined): number | undefined {\n    if (!value) return undefined;\n    const trimmed = value.trim();\n    if (/^\\d+$/.test(trimmed)) return Number(trimmed);\n    const when = Date.parse(trimmed);\n    if (Number.isNaN(when)) return undefined;\n    return Math.max(0, Math.round((when - Date.now()) / 1000));\n}\n"],"mappings":"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"}