cache.ts

1.9 KB · raw

1import type { Database } from "bun:sqlite";2import { type Bucket, BucketWriteError, KEPR_PREFIX } from "../bucket/reader.ts";3import { filesAt, objectBytes, stripCount } from "../mirror/queries.ts";4import { buildTarball } from "./tar.ts";56const tarballsOf = (repo: string) => `${KEPR_PREFIX}tarballs/${repo}/`;78/**9 * A save's tarball, kept in the bucket under Kepr's own folder (June, 2026-09-30) as10 * `.kepr/tarballs/<repo>/<generation>/<id>.tar.gz` once first requested. The generation is how11 * many strip events the mirror holds, so once a strip lands no older file is ever looked up again,12 * even if Kepr stops before the sync's cleanup runs. Without a write key nothing is kept, and each13 * request builds it again.14 */15export async function cachedTarball(16	bucket: Bucket,17	db: Database,18	repo: string,19	saveId: string,20): Promise<Uint8Array> {21	const stored = await bucket.get(`${tarballsOf(repo)}${stripCount(db)}/${saveId}.tar.gz`);22	if (stored) return stored;23	// Read again after the await: the generation and the build below run in one synchronous go, so24	// a strip landing meanwhile can't file new contents under the old generation or the reverse.25	const key = `${tarballsOf(repo)}${stripCount(db)}/${saveId}.tar.gz`;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	try {33		await bucket.put(key, tarball, "application/gzip");34	} catch (error) {35		if (!(error instanceof BucketWriteError)) throw error;36	}37	return tarball;38}3940/** Deletes a repo's kept tarballs: after a strip, or from the console. They rebuild on request. */41export async function dropTarballs(bucket: Bucket, repo: string): Promise<void> {42	for (const key of await bucket.list(tarballsOf(repo))) await bucket.remove(key);43}