import { concatBytes, FLUSH_PKT, packetText, parseAllPackets, pktLine } from "./pktline";
import { isValidRefName } from "./writer";
const ZERO_SHA = "0000000000000000000000000000000000000000";
const PEELED_SUFFIX = "^{}";
const PACK_MAGIC = [0x50, 0x41, 0x43, 0x4b];
const MAX_PREAMBLE_BYTES = 64 * 1024;
const decoder = new TextDecoder();
export type AdvertisedRef = { name: string; sha: string };
export type RefCommand = { name: string; sha: string; oldSha?: string };
const SHA_PATTERN = /^[0-9a-f]{40}$/;
/** A branch or tag whose name passes `git check-ref-format`, so a remote cannot smuggle odd names in. */
function isSyncableRef(name: string): boolean {
const prefix = ["refs/heads/", "refs/tags/"].find((candidate) => name.startsWith(candidate));
return prefix !== undefined && isValidRefName(name.slice(prefix.length));
}
/** Every branch and tag in a v0 upload-pack advertisement, using the tag object (not the peeled commit) for annotated tags. */
export function parseSyncableRefs(body: Uint8Array, includeAll = false, includePeeled = false): AdvertisedRef[] {
const refs: AdvertisedRef[] = [];
for (const packet of parseAllPackets(body)) {
const line = packetText(packet);
if (line === null || line.startsWith("#")) continue;
const [refPart] = line.split("\0");
const [sha, name] = refPart.split(" ");
if (!sha || !name || !SHA_PATTERN.test(sha) || sha === ZERO_SHA) continue;
if (name.endsWith(PEELED_SUFFIX) && !(includeAll && includePeeled)) continue;
if (includeAll || isSyncableRef(name)) refs.push({ name, sha });
}
return refs;
}
/**
* A stateless v0 upload-pack request. No side-band and no thin-pack, so the response is a plain,
* self-contained packfile that receive-pack will accept verbatim.
*/
export function uploadPackRequest(wants: string[], haves: string[]): Uint8Array<ArrayBuffer> {