Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
"format:check": "oxfmt --check package.json .vscode .eslintrc.cjs .github *.md .devcontainer .oxfmtrc.json",
"check-deps": "tsx scripts/check-deps.ts"
},
"lint-staged": {
"**/*.{cjs,ts}": [
"oxfmt"
]
},
"devDependencies": {
"@types/node": "^22.14.1",
"@typescript-eslint/eslint-plugin": "^7.2.0",
Expand Down
29 changes: 24 additions & 5 deletions packages/hub/src/utils/WebBlob.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,32 @@ describe("WebBlob", () => {
let size: number;
let contentType: string;

const fetchWithContentLength: typeof fetch = async (input, init) => {
if ((init as RequestInit | undefined)?.method === "HEAD") {
const response = await fetch(input, init);
const headers = new Headers(response.headers);
headers.set("content-length", String(size));
headers.set("accept-ranges", "bytes");
return new Response(null, { status: response.status, headers });
}
return fetch(input, init);
};

beforeAll(async () => {
const response = await fetch(resourceUrl, { method: "HEAD" });
size = Number(response.headers.get("content-length"));
// Use a GET request so we can derive size from the actual response body —
// content-length from HEAD is not CORS-exposed in browsers.
const response = await fetch(resourceUrl);
fullText = await response.text();
contentType = response.headers.get("content-type") || "";
fullText = await (await fetch(resourceUrl)).text();
size = new TextEncoder().encode(fullText).byteLength;
});

it("should create a WebBlob with a slice on the entire resource", async () => {
const webBlob = await WebBlob.create(resourceUrl, { cacheBelow: 0, accessToken: undefined });
const webBlob = await WebBlob.create(resourceUrl, {
cacheBelow: 0,
accessToken: undefined,
fetch: fetchWithContentLength,
});

expect(webBlob).toMatchObject({
url: resourceUrl,
Expand Down Expand Up @@ -75,7 +92,9 @@ describe("WebBlob", () => {
it("should create a slice on the file", async () => {
const expectedText = fullText.slice(10, 20);

const slice = (await WebBlob.create(resourceUrl, { cacheBelow: 0, accessToken: undefined })).slice(10, 20);
const slice = (
await WebBlob.create(resourceUrl, { cacheBelow: 0, accessToken: undefined, fetch: fetchWithContentLength })
).slice(10, 20);

expect(slice).toMatchObject({
url: resourceUrl,
Expand Down
8 changes: 7 additions & 1 deletion packages/hub/src/utils/WebBlob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ export class WebBlob extends Blob {
}),
});

const size = Number(response.headers.get("content-length"));
const contentLength = response.headers.get("content-length");

if (contentLength === null || !Number.isFinite(Number(contentLength))) {
throw new Error(`Invalid Content-Length: ${contentLength}, for url ${url}`);
}
Comment thread
coyotte508 marked this conversation as resolved.

const size = Number(contentLength);
const contentType = response.headers.get("content-type") || "";
const supportRange = response.headers.get("accept-ranges") === "bytes";

Expand Down
Loading