it was weird. i pressed delete on a subfolder, i think one of the pages.off folders that i was using. and then, suddenly, nvim on windows 7 decided to delete every file in the directory. they weren't shred off the space time continuum, but just marked deleted. i had to pay $80 to get access to a software that could see them. bleh! just seeing all my work, a little over a week, was pretty heart shattering. but i remembered that long ago, a close friend said i could call them whenever i was feeling sad. i finally took them up on that offer. the first time i've ever called someone for emotional support. but it's ok. i got it back. and the site framework is better than ever. i'm gonna commit and push more often. the repo is private anyways.
99 lines
2.4 KiB
TypeScript
99 lines
2.4 KiB
TypeScript
interface Loaded {
|
|
map: BuiltAssetMap;
|
|
buf: Buffer;
|
|
}
|
|
let assets: Loaded | null = null;
|
|
|
|
export type StaticPageId = string;
|
|
|
|
export async function reload() {
|
|
const [map, buf] = await Promise.all([
|
|
fs.readFile(".clover/static.json", "utf8"),
|
|
fs.readFile(".clover/static.blob"),
|
|
]);
|
|
assets = {
|
|
map: JSON.parse(map),
|
|
buf,
|
|
};
|
|
}
|
|
|
|
export async function reloadSync() {
|
|
const map = fs.readFileSync(".clover/static.json", "utf8");
|
|
const buf = fs.readFileSync(".clover/static.blob");
|
|
assets = {
|
|
map: JSON.parse(map),
|
|
buf,
|
|
};
|
|
}
|
|
|
|
export async function assetMiddleware(c: Context, next: Next) {
|
|
if (!assets) await reload();
|
|
const asset = assets!.map[c.req.path];
|
|
if (asset) {
|
|
return assetInner(c, asset, 200);
|
|
}
|
|
return next();
|
|
}
|
|
|
|
export async function serveAsset(
|
|
c: Context,
|
|
id: StaticPageId,
|
|
status: StatusCode,
|
|
) {
|
|
assets ?? await reload();
|
|
return assetInner(c, assets!.map[id], status);
|
|
}
|
|
|
|
export function hasAsset(id: string) {
|
|
if (!assets) reloadSync();
|
|
return assets!.map[id] !== undefined;
|
|
}
|
|
|
|
export function etagMatches(etag: string, ifNoneMatch: string) {
|
|
return ifNoneMatch === etag || ifNoneMatch.split(/,\s*/).indexOf(etag) > -1;
|
|
}
|
|
|
|
function subarrayAsset([start, end]: View) {
|
|
return assets!.buf.subarray(start, end);
|
|
}
|
|
|
|
function assetInner(c: Context, asset: BuiltAsset, status: StatusCode) {
|
|
const ifnonematch = c.req.header("If-None-Match");
|
|
if (ifnonematch) {
|
|
const etag = asset.headers.ETag;
|
|
if (etagMatches(etag, ifnonematch)) {
|
|
c.res = new Response(null, {
|
|
status: 304,
|
|
statusText: "Not Modified",
|
|
headers: {
|
|
ETag: etag,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
const acceptEncoding = c.req.header("Accept-Encoding") ?? "";
|
|
let body;
|
|
let headers = asset.headers;
|
|
if (acceptEncoding.includes("zstd") && asset.zstd) {
|
|
body = subarrayAsset(asset.zstd);
|
|
headers = {
|
|
...asset.headers,
|
|
"Content-Encoding": "zstd",
|
|
};
|
|
} else if (acceptEncoding.includes("gzip") && asset.gzip) {
|
|
body = subarrayAsset(asset.gzip);
|
|
headers = {
|
|
...asset.headers,
|
|
"Content-Encoding": "gzip",
|
|
};
|
|
} else {
|
|
body = subarrayAsset(asset.raw);
|
|
}
|
|
c.res = new Response(body, { headers, status });
|
|
}
|
|
|
|
import * as fs from "./fs.ts";
|
|
import type { Context, Next } from "hono";
|
|
import type { StatusCode } from "hono/utils/http-status";
|
|
import type { BuiltAsset, BuiltAssetMap, View } from "./incremental.ts";
|