import { ApiError } from './api' /** Per-field validation messages: a 422 body's `{ errors: { field: [msg] } }`. */ export type FieldErrors = Record function isStringArray(value: unknown): value is string[] { return Array.isArray(value) && value.every((item) => typeof item === 'string') } /** * Extract the `{ errors: { field: [...] } }` map from a 422 ApiError body. * Returns null for anything that isn't a well-formed 422 field-error response * (wrong status, non-ApiError, non-JSON body, or a malformed errors shape) so * the caller can fall back to a generic error message. */ export function parseFieldErrors(error: unknown): FieldErrors | null { if (!(error instanceof ApiError) || error.status !== 422) { return null } let parsed: unknown try { parsed = JSON.parse(error.body) } catch { return null } if ( typeof parsed !== 'object' || parsed === null || !('errors' in parsed) || typeof parsed.errors !== 'object' || parsed.errors === null ) { return null } // Only accept the contract shape: every field maps to an array of strings. const entries = Object.entries(parsed.errors as Record) if (!entries.every(([, messages]) => isStringArray(messages))) { return null } return parsed.errors as FieldErrors }