Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

26 linesCodeBlameRaw
1/** Only built public assets can be read from the release bucket. */
2export function archivedAssetKey(pathname: string): string | null {
3 if (!/^\/assets\/[a-zA-Z0-9_.~-]+\.(?:js|css|woff2?|ttf|otf|png|jpe?g|gif|svg|webp|ico|wasm)$/.test(pathname)) return null;
4 return `web-assets${pathname}`;
5}
6
7/** Current assets are served by Cloudflare before the Worker; misses land here. */
8export async function serveArchivedAsset(request: Request, bucket: Pick<R2Bucket, "get"> | undefined): Promise<Response> {
9 const key = archivedAssetKey(new URL(request.url).pathname);
10 if (!key || !["GET", "HEAD"].includes(request.method)) return new Response("Not found", { status: 404, headers: { "cache-control": "no-store" } });
11 try {
12 if (!bucket) throw new Error("Asset archive is not configured");
13 const object = await bucket.get(key);
14 if (!object) return new Response("Not found", { status: 404, headers: { "cache-control": "no-store" } });
15 const headers = new Headers();
16 object.writeHttpMetadata(headers);
17 headers.set("etag", object.httpEtag);
18 headers.set("cache-control", "public, max-age=31536000, immutable");
19 headers.set("x-content-type-options", "nosniff");
20 if (request.headers.get("if-none-match") === object.httpEtag) return new Response(null, { status: 304, headers });
21 headers.set("content-length", String(object.size));
22 return new Response(request.method === "HEAD" ? null : object.body, { headers });
23 } catch {
24 return new Response("Asset temporarily unavailable", { status: 503, headers: { "cache-control": "no-store", "retry-after": "1" } });
25 }
26}