const MAX_ERROR_CHARS = 300; export function normalizeBaseUrl(baseUrl: string | undefined, fallback: string): string { const raw = baseUrl?.trim() || fallback; return raw.replace(/\/+$/, ""); } export async function fetchWithTimeout( url: string, init: RequestInit, timeoutMs: number, fetchFn: typeof fetch, ): Promise { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), Math.max(1, timeoutMs)); try { return await fetchFn(url, { ...init, signal: controller.signal }); } finally { clearTimeout(timer); } } export async function readErrorResponse(res: Response): Promise { try { const text = await res.text(); const collapsed = text.replace(/\s+/g, " ").trim(); if (!collapsed) return undefined; if (collapsed.length <= MAX_ERROR_CHARS) return collapsed; return `${collapsed.slice(0, MAX_ERROR_CHARS)}…`; } catch { return undefined; } }