e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
57 lines
1.4 KiB
TypeScript
57 lines
1.4 KiB
TypeScript
const SANDBOX_FETCH_HEADERS = {
|
|
Accept: "application/zip, application/octet-stream, */*",
|
|
// Blaxel preview hosts intermittently reset Node's default fetch path.
|
|
"User-Agent": "curl/8.7.1",
|
|
};
|
|
|
|
const MAX_ATTEMPTS = 3;
|
|
const RETRY_DELAY_MS = 300;
|
|
|
|
function isRetryableFetchError(error: unknown) {
|
|
if (!(error instanceof Error)) return false;
|
|
const cause = error.cause as { code?: string } | undefined;
|
|
const code = cause?.code ?? error.message;
|
|
return (
|
|
code === "ECONNRESET" ||
|
|
code === "ETIMEDOUT" ||
|
|
code === "ECONNREFUSED" ||
|
|
code === "fetch failed"
|
|
);
|
|
}
|
|
|
|
function sleep(ms: number) {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
function mergeHeaders(headers?: HeadersInit): Headers {
|
|
const merged = new Headers(SANDBOX_FETCH_HEADERS);
|
|
if (!headers) return merged;
|
|
new Headers(headers).forEach((value, key) => merged.set(key, value));
|
|
return merged;
|
|
}
|
|
|
|
export async function fetchSandboxResource(
|
|
url: string | URL,
|
|
init?: RequestInit,
|
|
): Promise<Response> {
|
|
let lastError: unknown;
|
|
|
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
|
|
try {
|
|
return await fetch(url, {
|
|
...init,
|
|
cache: "no-store",
|
|
headers: mergeHeaders(init?.headers),
|
|
});
|
|
} catch (error) {
|
|
lastError = error;
|
|
if (!isRetryableFetchError(error) || attempt === MAX_ATTEMPTS) {
|
|
throw error;
|
|
}
|
|
await sleep(RETRY_DELAY_MS * attempt);
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|