Files
WRNexusJS/packages/viewer/src/source.ts
T

69 lines
2.2 KiB
TypeScript

import type { ResolveOptions, ViewerSource } from "./types.ts";
export function safeSourceUrl(src: string): string | undefined {
try {
const url = new URL(src, typeof location === "undefined" ? "http://localhost" : location.href);
return ["http:", "https:", "blob:", "data:"].includes(url.protocol) ? url.href : undefined;
} catch {
return undefined;
}
}
/** The source's own http(s) URL, when it has one that an element can load. */
export function directUrl(source: ViewerSource): string | undefined {
if (source.blob || !source.src) return undefined;
const url = safeSourceUrl(source.src);
return url && /^https?:/i.test(url) ? url : undefined;
}
export async function readBounded(
response: Response,
cap: number,
prefixOnly = false,
): Promise<Uint8Array> {
if (!response.ok || !response.body) throw new Error("File could not be read");
const reader = response.body.getReader();
const parts: Uint8Array[] = [];
let length = 0;
try {
while (true) {
const next = await reader.read();
if (next.done) break;
if (!prefixOnly && length + next.value.length > cap)
throw new Error("File exceeds preview limit");
const part = next.value.subarray(0, cap - length);
parts.push(part);
length += part.length;
if (prefixOnly && length >= cap) break;
}
} finally {
await reader.cancel().catch(() => {});
reader.releaseLock();
}
const bytes = new Uint8Array(length);
let offset = 0;
for (const part of parts) {
bytes.set(part, offset);
offset += part.length;
}
return bytes;
}
export async function readSource(
source: ViewerSource,
cap: number,
options: ResolveOptions = {},
): Promise<Blob> {
if (source.blob) {
if (source.blob.size > cap) throw new Error("File exceeds preview limit");
return source.blob;
}
const url = source.src && safeSourceUrl(source.src);
if (!url) throw new Error("No readable file source");
const response = await (options.fetch ?? fetch)(url, { signal: options.signal });
const bytes = await readBounded(response, cap);
return new Blob([bytes as Uint8Array<ArrayBuffer>], {
type: response.headers.get("content-type") ?? source.mime ?? "",
});
}