muddle/core/cache.ts
2026-10-23 09:12:49 -04:00

69 lines
1.8 KiB
TypeScript

import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import { join } from "node:path";
export interface Cache {
get(key: string): Promise<Uint8Array | undefined>;
put(
key: string,
value: Uint8Array,
options?: { ttl?: number },
): Promise<void>;
delete(key: string): Promise<void>;
}
export interface CloudflareCacheLike {
match(request: Request): Promise<Response | undefined>;
put(request: Request, response: Response): Promise<void>;
delete(request: Request): Promise<boolean>;
}
function cacheKey(key: string): string {
return encodeURIComponent(key);
}
export function filesystemCache(dir: string): Cache {
return {
async get(key) {
const path = join(dir, cacheKey(key));
if (!existsSync(path)) return undefined;
return new Uint8Array(await readFile(path));
},
async put(key, value) {
await mkdir(dir, { recursive: true });
const path = join(dir, cacheKey(key));
const staged = `${path}.next`;
await writeFile(staged, value);
await rename(staged, path);
},
async delete(key) {
const path = join(dir, cacheKey(key));
if (existsSync(path)) await rename(path, `${path}.deleted`);
},
};
}
export function cloudflareCache(
cache: CloudflareCacheLike,
origin = "https://muddle.cache",
): Cache {
const request = (key: string) =>
new Request(new URL(`/${encodeURIComponent(key)}`, origin).toString());
return {
async get(key) {
const response = await cache.match(request(key));
return response
? new Uint8Array(await response.arrayBuffer())
: undefined;
},
async put(key, value, options) {
const headers = options?.ttl
? { "cache-control": `max-age=${options.ttl}` }
: undefined;
await cache.put(request(key), new Response(value, { headers }));
},
async delete(key) {
await cache.delete(request(key));
},
};
}