import { env } from "cloudflare:workers";
import { MAX_IN_LIST, all, chunks, first, placeholders, run } from "../db";
import { HttpError } from "../errors";
import { adjustStorage } from "./quota";
import type { RepoRow } from "~/lib/types";
/** Forks read LFS objects from their parents; bound the chain so lookups stay O(1) in D1 calls. */
const MAX_FORK_DEPTH = 3;
const PURGE_PAGES = 10;
const R2_LIST_LIMIT = 1000;
type ObjectRow = { repo_id: string; size: number };
export function objectKey(repoId: string, oid: string): string {
return `lfs/${repoId}/${oid}`;
}
async function repoChain(repo: Pick<RepoRow, "id" | "fork_of_id">): Promise<string[]> {
const chain = [repo.id];
let parent = repo.fork_of_id;
while (parent && chain.length < MAX_FORK_DEPTH) {
chain.push(parent);
const row = await first<{ fork_of_id: string | null }>("SELECT fork_of_id FROM repos WHERE id = ?", parent);
parent = row?.fork_of_id ?? null;
}
return chain;
}
/** Finds which repo in the fork chain stores the object, preferring the repo itself. */
export async function findObject(repo: Pick<RepoRow, "id" | "fork_of_id">, oid: string): Promise<ObjectRow | null> {
const own = await first<ObjectRow>(
"SELECT repo_id, size FROM lfs_objects WHERE repo_id = ? AND oid = ?",
repo.id,
oid,
);
if (own || !repo.fork_of_id) return own;
const chain = (await repoChain(repo)).slice(1);
const rows = await all<ObjectRow>(
`SELECT repo_id, size FROM lfs_objects WHERE oid = ? AND repo_id IN (${placeholders(chain.length)})`,
oid,