// Shared blob download: fetches an endpoint and saves the response as a file. // Works whether or not the response sets Content-Disposition (some exports // don't), falling back to the given name. Throws on a non-OK response so callers // can surface the failure with a toast. export async function downloadFile(endpoint: string, fallbackName: string): Promise { const res = await fetch(endpoint, { credentials: 'include' }); if (!res.ok) { let data: { message?: string; error?: string } = {}; try { data = await res.json(); } catch { /* ignore */ } throw new Error(data.message || data.error || `HTTP ${res.status}`); } const disposition = res.headers.get('Content-Disposition'); const match = disposition?.match(/filename="?([^"]+)"?/i); const name = match?.[1] || fallbackName; const blob = await res.blob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = name; a.click(); URL.revokeObjectURL(url); }