105 lines · 4.1 KB
import { concatBytes, FLUSH_PKT, packetText, parseAllPackets, pktLine } from "./pktline";

const PRIVATE_PREFIX = "refs/gitfor/";
const ZERO_SHA = "0".repeat(40);

/** Whether `name` is a ref clients must never see: base commits bridged into a fork for its pull requests. */
export function isPrivateRef(name: string): boolean {
  return name.startsWith(PRIVATE_PREFIX);
}

function refsOf(lines: string[]): Map<string, string> {
  const refs = new Map<string, string>();
  for (const line of lines) {
    const [sha, name] = (line.split("\0")[0] ?? "").split(" ");
    if (sha && name) refs.set(name, sha);
  }
  return refs;
}

/** Points `symref=HEAD:...` in a v0 capability list at the configured default branch. */
function withDefaultHead(caps: string, branchRef: string): string {
  const others = caps.split(" ").filter((cap) => cap !== "" && !cap.startsWith("symref=HEAD:"));
  return [...others, `symref=HEAD:${branchRef}`].join(" ");
}

/**
 * Rewrites a v0 upload-pack advertisement:
 * - removes `refs/gitfor/*`. Capabilities ride on the first ref line, so they move to the next kept ref
 *   (or to a `capabilities^{}` placeholder when none is left);
 * - points HEAD at `defaultBranch`, because Artifacts' own HEAD never follows the repository setting.
 */
export function filterAdvertisement(body: Uint8Array, defaultBranch?: string): Uint8Array<ArrayBuffer> {
  const packets = parseAllPackets(body);
  const texts = packets.map((packet) => (packet.kind === "data" ? (packetText(packet) ?? "") : ""));
  const refs = refsOf(texts.filter((line) => line && !line.startsWith("#")));
  const branchRef = defaultBranch ? `refs/heads/${defaultBranch}` : null;
  const headSha = branchRef ? refs.get(branchRef) : undefined;
  const out: Uint8Array[] = [];
  let pendingCaps: string | null = null;
  let sawRefs = false;