1import { decodeFilePreviewText, FILE_TEXT_PREVIEW_MAX_BYTES } from "@t3tools/shared/filePreview";
2
3/** Consume only a bounded prefix, even when a host ignores the requested HTTP range. */
4export async function readFilePreviewResponse(
5 response: Pick<Response, "ok" | "body">,
6 signal: AbortSignal,
7) {
8 if (!response.ok) {
9 // A streamed error body holds the connection open until GC otherwise.
10 void response.body?.cancel().catch(() => undefined);
11 throw new Error("The file could not be loaded. Reconnect and try again.");
12 }
13 if (signal.aborted) throw new Error("Preview cancelled.");
14 const limit = FILE_TEXT_PREVIEW_MAX_BYTES + 1;
15 const reader = response.body?.getReader();
16 if (!reader) throw new Error("Streaming file previews are unavailable in this runtime.");
17 const cancel = () => {
18 void reader.cancel().catch(() => undefined);
19 };
20 signal.addEventListener("abort", cancel, { once: true });
21 const chunks: Uint8Array[] = [];
22 let length = 0;
23 try {
24 while (length < limit) {
25 if (signal.aborted) throw new Error("Preview cancelled.");
26 const next = await reader.read();
27 if (next.done) break;
28 const chunk = next.value.subarray(0, limit - length);
29 chunks.push(chunk);
30 length += chunk.length;
31 }
32 } finally {
33 signal.removeEventListener("abort", cancel);
34 await reader.cancel();
35 }
36 if (signal.aborted) throw new Error("Preview cancelled.");
37 const bytes = new Uint8Array(length);
38 let offset = 0;
39 for (const chunk of chunks) {
40 bytes.set(chunk, offset);
41 offset += chunk.length;
42 }
43 return decodeFilePreviewText(bytes);
44}