import { env } from "cloudflare:workers";
import { MAX_IN_LIST, all, batch, chunks, first, placeholders, run, stmt } from "../db";
import { HttpError } from "../errors";
import { randomId } from "../crypto";
import { hasPermission, loadRepoById, repoPermission } from "../repos";
import type { RepoRow, Viewer } 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;
/** Unreferenced blobs survive this long, so an upload racing the sweeper never loses its data. */
const ORPHAN_GRACE_MS = 60 * 60 * 1000;
const SWEEP_BATCH = 100;
type ObjectRow = { repo_id: string; size: number };
type ChainRepo = Pick<RepoRow, "id" | "fork_of_id" | "owner_id" | "visibility">;
/**
* Whether readers of `repo` may see objects stored in the private `ancestor`: only for a private fork,
* and only while the fork's owner (the account whose collaborators can read it) can still read the
* ancestor, so a removed collaborator's fork stops seeing new objects.
*/
async function ownerCanRead(repo: ChainRepo, ancestorId: string): Promise<boolean> {
if (repo.visibility === "public") return false;
const ancestor = await loadRepoById(ancestorId);
if (!ancestor) return false;
if (ancestor.owner_id === repo.owner_id) return true;
const owner: Viewer = {
id: repo.owner_id,
name: "",
displayName: null,
email: "",
isAdmin: false,
emailVerified: true,
};
return hasPermission(await repoPermission(ancestor, owner), "read");
}
/**
* The repo plus the ancestors whose objects its readers may see: public ancestors, or private ones the