96 lines · 4.0 KB
import { first } from "../db";
import { notFoundError } from "../errors";
import { hasPermission, loadRepo, repoPermission } from "../repos";
import { scopedRepoPermission } from "../token-scope";
import type { RequestCredential } from "../token-scope";
import { diffTreesCached } from "./diff";
import { compareCommits } from "./history";
import { assertVisibleRevision } from "./reachable";
import { listRefs, resolveRevision } from "./refs";
import type { ResolvedRevision } from "./refs";
import { withStore } from "./store";
import type { Comparison } from "./history";
import type { DiffResult, RepoRow, Viewer } from "~/lib/types";

export type BranchComparison = {
  base: ResolvedRevision;
  head: ResolvedRevision;
  headRepo: RepoRow;
  comparison: Comparison;
  diff: DiffResult;
  baseBranches: string[];
  headBranches: string[];
};

/** Who is comparing: a signed-in viewer (full access) or a request credential whose token scope also applies. */
export type CompareAccess = Viewer | null | RequestCredential;

function isCredential(access: CompareAccess): access is RequestCredential {
  return access !== null && "grant" in access;
}

async function canReadHead(repo: RepoRow, access: CompareAccess): Promise<boolean> {
  if (isCredential(access)) return hasPermission(await scopedRepoPermission(repo, access, "contents"), "read");
  return (await repoPermission(repo, access)) !== "none";
}

/**
 * Resolves an `owner:branch` head to a fork the caller can read; bare names stay in the base repository. Every
 * refusal is the same not-found, so the response never tells whether an unreadable private fork exists.
 */