/**
* useViewerFetch — tiny wrapper around `fetch` for the rich viewers.
*
* The PreviewModal already builds and pushes `authHeaders` + the
* configured `credentials` mode into every viewer. The viewers need
* binary access (ArrayBuffer for utif/ag-psd/pdfjs, Blob for EPUB,
* text for CSV/Mermaid/ipynb), and they all want to honour the same
* auth strategy.
*
* Each helper uses the URL the modal hands down (`previewUrl(path)`)
* and merges in the same headers + credentials so a CSRF/Bearer host
* doesn't reject the request when the viewer fetches the file body
* directly (instead of letting the browser do it via `
` etc).
*/
export interface ViewerFetchOptions {
url: string;
headers?: Record;
credentials?: RequestCredentials;
signal?: AbortSignal;
}
async function fetchOk(opts: ViewerFetchOptions): Promise {
const res = await fetch(opts.url, {
headers: opts.headers ?? {},
credentials: opts.credentials ?? 'same-origin',
signal: opts.signal,
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(
`${res.status} ${res.statusText}${text ? ' — ' + text.slice(0, 200) : ''}`,
);
}
return res;
}
export async function fetchViewerArrayBuffer(
opts: ViewerFetchOptions,
): Promise {
const res = await fetchOk(opts);
return res.arrayBuffer();
}
export async function fetchViewerBlob(
opts: ViewerFetchOptions,
): Promise<{ blob: Blob; objectUrl: string; mime: string }> {
const res = await fetchOk(opts);
const blob = await res.blob();
const mime = blob.type || res.headers.get('content-type') || '';
return { blob, objectUrl: URL.createObjectURL(blob), mime };
}
export async function fetchViewerText(
opts: ViewerFetchOptions,
): Promise {
const res = await fetchOk(opts);
return res.text();
}