cache.ts

1.7 KB · raw

1import type { Database } from "bun:sqlite";2import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";3import { join } from "node:path";4import { filesAt, objectBytes, stripCount } from "../mirror/queries.ts";5import { buildTarball } from "./tar.ts";67/**8 * A save's tarball, built on first request into `<tarballDir>/<repo>/<generation>/<id>.tar.gz`. The9 * generation is how many strip events the mirror holds, so once a strip lands no older file is ever10 * looked up again, even if Kepr stops before the sync's cleanup runs. Built and written in one11 * synchronous run, so a strip can't land mid-build either.12 */13export function cachedTarball(14	tarballDir: string,15	db: Database,16	repo: string,17	saveId: string,18): Uint8Array<ArrayBuffer> {19	const dir = join(tarballDir, repo, String(stripCount(db)));20	const path = join(dir, `${saveId}.tar.gz`);21	try {22		return readFileSync(path);23	} catch (error) {24		if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;25	}26	const files = filesAt(db, saveId).map((file) => {27		const bytes = objectBytes(db, file.hash);28		if (!bytes) throw new Error(`${repo}: object ${file.hash} is missing from the mirror`);29		return { path: file.path, bytes, executable: file.executable };30	});31	const tarball = buildTarball(`${repo}-${saveId.slice(0, 8)}`, files);32	mkdirSync(dir, { recursive: true });33	writeFileSync(`${path}.tmp`, tarball);34	renameSync(`${path}.tmp`, path);35	return tarball;36}3738/** Clears a repo's cached tarballs after a strip; cleanup only, since the new generation never reads them. */39export function dropTarballs(tarballDir: string, repo: string): void {40	rmSync(join(tarballDir, repo), { recursive: true, force: true });41}