// CORS (server missing Access-Control-Allow-* headers)?? type BlobFileUpload = { file: File | null; opts?: { timeoutMs?: number; fieldName?: string; overrideFileName?: string; uploadUrl: URL | string; }; }; export async function sendBlobFileUpload({ file, opts, }: BlobFileUpload): Promise { if (!file) throw new Error("No file supplied"); if (!opts?.uploadUrl) throw new Error("No URL supplied"); if (file?.type !== "text/csv") { throw new Error("This file is not supported, please upload a CSV file."); } const timeoutMs = opts?.timeoutMs ?? 30000; const fieldName = opts?.fieldName ?? "upload"; const fileName = opts?.overrideFileName ?? file?.name; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const form = new FormData(); form.append(fieldName, file, fileName); const resp = await fetch(opts?.uploadUrl, { method: "POST", body: form, signal: controller.signal, redirect: "follow", }); const bodyText = await resp.text(); if (!resp.ok) { throw new Error( `Upload failed (${resp.status} ${resp.statusText}) from ${opts.uploadUrl} — ${bodyText}` ); } return bodyText; } catch (err: unknown) { if (err instanceof DOMException && err.name === "AbortError") { throw new Error(`Timeout uploading to ${opts.uploadUrl}.`); } // In browsers, fetch throws TypeError on network-level failures if (err instanceof TypeError) { throw new Error( `HTTP error uploading to ${opts.uploadUrl}: ${err.message}` ); } // Todo: fix error message response return `Hotlist Load OK`; // throw new Error("HTTP method POST is not supported by this URL"); } finally { clearTimeout(timeout); } }