61 lines
1.6 KiB
TypeScript
61 lines
1.6 KiB
TypeScript
export interface StaticAssets {
|
|
files: Record<string, Blob>;
|
|
indexHtml: Blob;
|
|
}
|
|
|
|
const CONTENT_TYPES: Record<string, string> = {
|
|
".css": "text/css; charset=utf-8",
|
|
".html": "text/html; charset=utf-8",
|
|
".js": "text/javascript; charset=utf-8",
|
|
".json": "application/json; charset=utf-8",
|
|
".mjs": "text/javascript; charset=utf-8",
|
|
".png": "image/png",
|
|
".svg": "image/svg+xml",
|
|
".woff": "font/woff",
|
|
".woff2": "font/woff2",
|
|
};
|
|
|
|
export function contentTypeFor(path: string): string {
|
|
const dot = path.lastIndexOf(".");
|
|
if (dot === -1) return "application/octet-stream";
|
|
const ext = path.slice(dot);
|
|
return CONTENT_TYPES[ext] ?? "application/octet-stream";
|
|
}
|
|
|
|
export function hasFileExtension(path: string): boolean {
|
|
const lastSlash = path.lastIndexOf("/");
|
|
const segment = lastSlash === -1 ? path : path.slice(lastSlash + 1);
|
|
return segment.includes(".");
|
|
}
|
|
|
|
export function htmlResponse(html: Blob): Response {
|
|
return new Response(html, {
|
|
headers: {
|
|
"Cache-Control": "no-cache",
|
|
"Content-Type": "text/html; charset=utf-8",
|
|
},
|
|
});
|
|
}
|
|
|
|
export function serveStaticAsset(pathname: string, assets: StaticAssets): Response {
|
|
if (pathname === "/") {
|
|
return htmlResponse(assets.indexHtml);
|
|
}
|
|
|
|
const file = assets.files[pathname];
|
|
if (file) {
|
|
return new Response(file, {
|
|
headers: {
|
|
"Cache-Control": "public, max-age=31536000, immutable",
|
|
"Content-Type": contentTypeFor(pathname),
|
|
},
|
|
});
|
|
}
|
|
|
|
if (hasFileExtension(pathname)) {
|
|
return new Response("Not found", { status: 404 });
|
|
}
|
|
|
|
return htmlResponse(assets.indexHtml);
|
|
}
|