import { concatBytes, FLUSH_PKT, packetText, parseAllPackets, pktLine } from "./pktline";
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 };
/** 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): 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 === ZERO_SHA || name.endsWith(PEELED_SUFFIX)) continue;
if (name.startsWith("refs/heads/") || name.startsWith("refs/tags/")) 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> {
const lines: Uint8Array[] = wants.map((sha, index) =>
pktLine(index === 0 ? `want ${sha} ofs-delta no-progress agent=gitfor\n` : `want ${sha}\n`),
);
lines.push(FLUSH_PKT);
for (const sha of haves) lines.push(pktLine(`have ${sha}\n`));
lines.push(pktLine("done\n"));
return concatBytes(lines);
}
function packStart(buffer: Uint8Array): number {
for (let index = 0; index + PACK_MAGIC.length <= buffer.byteLength; index++) {