git-fs-s3 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,118 @@
1
+ import {
2
+ GitInvalidRequestError,
3
+ isSafeBranchName
4
+ } from "./chunk-YQFNY6PG.js";
5
+
6
+ // src/ops/branch.ts
7
+ import git from "isomorphic-git";
8
+ function assertSafeBranchName(name) {
9
+ if (!isSafeBranchName(name)) {
10
+ throw new GitInvalidRequestError(`Invalid branch name: ${name}`);
11
+ }
12
+ }
13
+ var FULL_SHA_RE = /^[0-9a-f]{40}$/i;
14
+ function hasRecursiveFileLister(fs) {
15
+ return typeof fs === "object" && fs !== null && "listFilesRecursively" in fs && typeof fs.listFilesRecursively === "function";
16
+ }
17
+ async function listLooseBranches(repo) {
18
+ if (!hasRecursiveFileLister(repo.fs)) return null;
19
+ const names = await repo.fs.listFilesRecursively(`${repo.gitdir}/refs/heads`);
20
+ return names.filter(isSafeBranchName);
21
+ }
22
+ async function readPackedBranches(repo) {
23
+ if (!hasRecursiveFileLister(repo.fs)) return /* @__PURE__ */ new Map();
24
+ const promisesFs = repo.fs.promises;
25
+ if (!promisesFs) return /* @__PURE__ */ new Map();
26
+ try {
27
+ const content = await promisesFs.readFile(
28
+ `${repo.gitdir}/packed-refs`,
29
+ "utf8"
30
+ );
31
+ const refs = /* @__PURE__ */ new Map();
32
+ for (const line of (typeof content === "string" ? content : new TextDecoder().decode(content)).split("\n")) {
33
+ const match = /^([0-9a-f]{40}) refs\/heads\/(.+)$/i.exec(line);
34
+ if (match?.[1] && match[2] && isSafeBranchName(match[2])) {
35
+ refs.set(match[2], match[1]);
36
+ }
37
+ }
38
+ return refs;
39
+ } catch {
40
+ return /* @__PURE__ */ new Map();
41
+ }
42
+ }
43
+ async function getCurrentLooseBranch(repo) {
44
+ if (!hasRecursiveFileLister(repo.fs)) return null;
45
+ const promisesFs = repo.fs.promises;
46
+ if (!promisesFs) return null;
47
+ try {
48
+ const content = await promisesFs.readFile(`${repo.gitdir}/HEAD`, "utf8");
49
+ const head = (typeof content === "string" ? content : new TextDecoder().decode(content)).trim();
50
+ const match = /^ref: refs\/heads\/(.+)$/.exec(head);
51
+ return match?.[1] && isSafeBranchName(match[1]) ? match[1] : null;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+ async function resolveLooseRefFast(repo, ref) {
57
+ const promisesFs = repo.fs.promises;
58
+ if (promisesFs) {
59
+ try {
60
+ const content = await promisesFs.readFile(
61
+ `${repo.gitdir}/${ref}`,
62
+ "utf8"
63
+ );
64
+ const oid = (typeof content === "string" ? content : new TextDecoder().decode(content)).trim();
65
+ if (FULL_SHA_RE.test(oid)) return oid;
66
+ } catch {
67
+ }
68
+ }
69
+ return git.resolveRef({ ...repo, ref });
70
+ }
71
+ async function listBranches(repo) {
72
+ try {
73
+ const [looseBranches, looseCurrentBranch, packedBranches] = await Promise.all([
74
+ listLooseBranches(repo),
75
+ getCurrentLooseBranch(repo),
76
+ readPackedBranches(repo)
77
+ ]);
78
+ const branches = looseBranches === null ? await git.listBranches(repo) : [.../* @__PURE__ */ new Set([...looseBranches, ...packedBranches.keys()])].sort();
79
+ const currentBranch = looseBranches === null ? await git.currentBranch({ ...repo, fullname: false }).catch(() => null) : looseCurrentBranch;
80
+ return Promise.all(
81
+ branches.map(async (branch) => {
82
+ const packedCommit = packedBranches.get(branch);
83
+ return {
84
+ name: branch,
85
+ commit: packedCommit && !looseBranches?.includes(branch) ? packedCommit : await resolveLooseRefFast(repo, `refs/heads/${branch}`),
86
+ isDefault: branch === currentBranch
87
+ };
88
+ })
89
+ );
90
+ } catch (err) {
91
+ if (err.code === "NotFoundError") return [];
92
+ throw err;
93
+ }
94
+ }
95
+ async function createBranchFrom(repo, name, startPoint = "main") {
96
+ assertSafeBranchName(name);
97
+ assertSafeBranchName(startPoint);
98
+ const object = await resolveLooseRefFast(repo, `refs/heads/${startPoint}`);
99
+ await git.branch({ ...repo, ref: name, checkout: false, object });
100
+ }
101
+ async function deleteBranchByName(repo, name) {
102
+ assertSafeBranchName(name);
103
+ await git.deleteBranch({ ...repo, ref: name });
104
+ }
105
+ async function assertBranchExists(repo, name) {
106
+ assertSafeBranchName(name);
107
+ await resolveLooseRefFast(repo, `refs/heads/${name}`);
108
+ }
109
+
110
+ export {
111
+ assertSafeBranchName,
112
+ resolveLooseRefFast,
113
+ listBranches,
114
+ createBranchFrom,
115
+ deleteBranchByName,
116
+ assertBranchExists
117
+ };
118
+ //# sourceMappingURL=chunk-H7IGQRIA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ops/branch.ts"],"sourcesContent":["import git from \"isomorphic-git\";\nimport { GitInvalidRequestError } from \"../git-errors.js\";\nimport { isSafeBranchName } from \"../refs.js\";\nimport type { Repo } from \"./types.js\";\n\nexport interface Branch {\n\tname: string;\n\tcommit: string;\n\tisDefault: boolean;\n}\n\n/**\n * Defense in depth for every branch-name argument below: `git.deleteBranch`\n * and raw resolveRef/writeRef reads don't validate ref names internally the\n * way `git.branch` does (see refs.ts) — guard at the point the primitives are\n * actually called, not just at an API boundary far above.\n */\nexport function assertSafeBranchName(name: string): void {\n\tif (!isSafeBranchName(name)) {\n\t\tthrow new GitInvalidRequestError(`Invalid branch name: ${name}`);\n\t}\n}\n\nconst FULL_SHA_RE = /^[0-9a-f]{40}$/i;\n\ntype RecursiveFileLister = {\n\tlistFilesRecursively(dirpath: string): Promise<string[]>;\n};\n\nfunction hasRecursiveFileLister(\n\tfs: Repo[\"fs\"],\n): fs is Repo[\"fs\"] & RecursiveFileLister {\n\treturn (\n\t\ttypeof fs === \"object\" &&\n\t\tfs !== null &&\n\t\t\"listFilesRecursively\" in fs &&\n\t\ttypeof (fs as Partial<RecursiveFileLister>).listFilesRecursively ===\n\t\t\t\"function\"\n\t);\n}\n\n/**\n * Lists loose branch names without isomorphic-git's recursive readdir/stat\n * traversal. Object stores already return only leaf keys in a recursive LIST,\n * so this is one request regardless of branch-name nesting. Ordinary\n * filesystems retain isomorphic-git's implementation and packed refs remain\n * supported there.\n */\nasync function listLooseBranches(repo: Repo): Promise<string[] | null> {\n\tif (!hasRecursiveFileLister(repo.fs)) return null;\n\tconst names = await repo.fs.listFilesRecursively(`${repo.gitdir}/refs/heads`);\n\treturn names.filter(isSafeBranchName);\n}\n\n/** Read packed branch refs so the object-store fast path preserves Git semantics. */\nasync function readPackedBranches(repo: Repo): Promise<Map<string, string>> {\n\tif (!hasRecursiveFileLister(repo.fs)) return new Map();\n\tconst promisesFs = (\n\t\trepo.fs as {\n\t\t\tpromises?: {\n\t\t\t\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n\t\t\t};\n\t\t}\n\t).promises;\n\tif (!promisesFs) return new Map();\n\ttry {\n\t\tconst content = await promisesFs.readFile(\n\t\t\t`${repo.gitdir}/packed-refs`,\n\t\t\t\"utf8\",\n\t\t);\n\t\tconst refs = new Map<string, string>();\n\t\tfor (const line of (typeof content === \"string\"\n\t\t\t? content\n\t\t\t: new TextDecoder().decode(content)\n\t\t).split(\"\\n\")) {\n\t\t\tconst match = /^([0-9a-f]{40}) refs\\/heads\\/(.+)$/i.exec(line);\n\t\t\tif (match?.[1] && match[2] && isSafeBranchName(match[2])) {\n\t\t\t\trefs.set(match[2], match[1]);\n\t\t\t}\n\t\t}\n\t\treturn refs;\n\t} catch {\n\t\treturn new Map();\n\t}\n}\n\n/** Read the symbolic HEAD directly when the object-store fs is available. */\nasync function getCurrentLooseBranch(repo: Repo): Promise<string | null> {\n\tif (!hasRecursiveFileLister(repo.fs)) return null;\n\tconst promisesFs = (\n\t\trepo.fs as {\n\t\t\tpromises?: {\n\t\t\t\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n\t\t\t};\n\t\t}\n\t).promises;\n\tif (!promisesFs) return null;\n\ttry {\n\t\tconst content = await promisesFs.readFile(`${repo.gitdir}/HEAD`, \"utf8\");\n\t\tconst head = (\n\t\t\ttypeof content === \"string\" ? content : new TextDecoder().decode(content)\n\t\t).trim();\n\t\tconst match = /^ref: refs\\/heads\\/(.+)$/.exec(head);\n\t\treturn match?.[1] && isSafeBranchName(match[1]) ? match[1] : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Resolve a fully-qualified ref's oid with one read instead of isomorphic-\n * git's own `resolveRef`, which does a stat *then* a read for a loose ref\n * (two round trips against object storage) — wasteful specifically here,\n * where the caller already knows the ref exists (it came from a directory\n * listing moments earlier). Reads the loose file directly and falls back to\n * `git.resolveRef` only when that doesn't pan out (packed-refs, or anything\n * else the plain-loose-file assumption doesn't cover), so correctness for\n * those cases is unchanged — this only removes the redundant round trip on\n * the common path.\n */\nexport async function resolveLooseRefFast(\n\trepo: Repo,\n\tref: string,\n): Promise<string> {\n\t// Repo.fs is typed as isomorphic-git's own fs union (promise- or\n\t// callback-based) since that's what git.readTree's signature allows, but\n\t// every fs this package actually constructs (git-fs.ts) is promise-based\n\t// — the runtime check just keeps the callback-style branch honest instead\n\t// of crashing on it.\n\tconst promisesFs = (\n\t\trepo.fs as {\n\t\t\tpromises?: {\n\t\t\t\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n\t\t\t};\n\t\t}\n\t).promises;\n\tif (promisesFs) {\n\t\ttry {\n\t\t\tconst content = await promisesFs.readFile(\n\t\t\t\t`${repo.gitdir}/${ref}`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tconst oid = (\n\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t? content\n\t\t\t\t\t: new TextDecoder().decode(content)\n\t\t\t).trim();\n\t\t\tif (FULL_SHA_RE.test(oid)) return oid;\n\t\t} catch {\n\t\t\t// Not a loose file (packed-refs, or genuinely absent) — fall through.\n\t\t}\n\t}\n\treturn git.resolveRef({ ...repo, ref });\n}\n\n/** All branches with their tip commits; [] for an empty repository. */\nexport async function listBranches(repo: Repo): Promise<Branch[]> {\n\ttry {\n\t\tconst [looseBranches, looseCurrentBranch, packedBranches] =\n\t\t\tawait Promise.all([\n\t\t\t\tlistLooseBranches(repo),\n\t\t\t\tgetCurrentLooseBranch(repo),\n\t\t\t\treadPackedBranches(repo),\n\t\t\t]);\n\t\tconst branches =\n\t\t\tlooseBranches === null\n\t\t\t\t? await git.listBranches(repo)\n\t\t\t\t: [...new Set([...looseBranches, ...packedBranches.keys()])].sort();\n\t\tconst currentBranch =\n\t\t\tlooseBranches === null\n\t\t\t\t? await git\n\t\t\t\t\t\t.currentBranch({ ...repo, fullname: false })\n\t\t\t\t\t\t.catch(() => null)\n\t\t\t\t: looseCurrentBranch;\n\n\t\treturn Promise.all(\n\t\t\tbranches.map(async (branch) => {\n\t\t\t\tconst packedCommit = packedBranches.get(branch);\n\t\t\t\treturn {\n\t\t\t\t\tname: branch,\n\t\t\t\t\tcommit:\n\t\t\t\t\t\tpackedCommit && !looseBranches?.includes(branch)\n\t\t\t\t\t\t\t? packedCommit\n\t\t\t\t\t\t\t: await resolveLooseRefFast(repo, `refs/heads/${branch}`),\n\t\t\t\t\tisDefault: branch === currentBranch,\n\t\t\t\t};\n\t\t\t}),\n\t\t);\n\t} catch (err: unknown) {\n\t\tif ((err as { code?: string }).code === \"NotFoundError\") return [];\n\t\tthrow err;\n\t}\n}\n\n/** Create `name` pointing at the tip of `startPoint` (no checkout). */\nexport async function createBranchFrom(\n\trepo: Repo,\n\tname: string,\n\tstartPoint = \"main\",\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tassertSafeBranchName(startPoint);\n\tconst object = await resolveLooseRefFast(repo, `refs/heads/${startPoint}`);\n\tawait git.branch({ ...repo, ref: name, checkout: false, object });\n}\n\n/** Delete a branch ref (validated — deleteBranch has no internal ref check). */\nexport async function deleteBranchByName(\n\trepo: Repo,\n\tname: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tawait git.deleteBranch({ ...repo, ref: name });\n}\n\n/** Throws (NotFoundError) unless the branch resolves. */\nexport async function assertBranchExists(\n\trepo: Repo,\n\tname: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tawait resolveLooseRefFast(repo, `refs/heads/${name}`);\n}\n"],"mappings":"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","names":[]}
package/dist/http.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/http/index.ts","../src/http/info-refs.ts","../src/edge-utils.ts","../src/ops/branch.ts","../src/refs.ts","../src/http/pkt-line.ts","../src/http/types.ts","../src/http/reachability.ts","../src/http/receive-pack.ts","../src/http/repack.ts","../src/http/upload-pack.ts"],"sourcesContent":["export {\n\ttype GitService,\n\thandleInfoRefs,\n\ttype InfoRefsOptions,\n\tlistAllRefs,\n} from \"./info-refs.js\";\nexport {\n\tFLUSH,\n\tparsePktLines,\n\tpktLine,\n\tpktLineBuffer,\n\tsideBandPackfile,\n} from \"./pkt-line.js\";\nexport {\n\tcollectReachableOids,\n\ttype ReachabilityResult,\n} from \"./reachability.js\";\nexport {\n\ttype ApplyReceivePackOptions,\n\tapplyReceivePack,\n\tapplyRefUpdates,\n\tensureRepoInitialized,\n\tindexIncomingPack,\n\tparseReceivePackBody,\n\ttype RefUpdateCommand,\n\ttype RefUpdateResult,\n\treceivePackResponse,\n} from \"./receive-pack.js\";\nexport {\n\tREPACK_PACK_COUNT_THRESHOLD,\n\ttype RepackOptions,\n\trepackRepository,\n} from \"./repack.js\";\nexport {\n\ttype GitHttpResult,\n\ttype HttpHooks,\n\ttype RawFsPromises,\n\trawFs,\n} from \"./types.js\";\nexport { handleUploadPack, type UploadPackOptions } from \"./upload-pack.js\";\n","import git from \"isomorphic-git\";\nimport { concat } from \"../edge-utils.js\";\nimport { resolveLooseRefFast } from \"../ops/branch.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { FLUSH, pktLine } from \"./pkt-line.js\";\nimport { type GitHttpResult, type HttpHooks, runStep } from \"./types.js\";\n\nexport type GitService = \"git-upload-pack\" | \"git-receive-pack\";\n\n/** All refs with resolved oids, plus the symref HEAD should advertise. */\nexport async function listAllRefs(repo: Repo, defaultBranch = \"main\") {\n\t// Fetch branch/tag lists and HEAD's symref target in parallel\n\tconst [branches, tags, headSymref] = await Promise.all([\n\t\tgit.listBranches(repo),\n\t\tgit.listTags(repo),\n\t\t// Wrap with Promise.resolve so a mock/stub returning undefined doesn't crash .then()\n\t\tPromise.resolve(git.currentBranch({ ...repo, fullname: true }))\n\t\t\t.then((cb) => cb ?? `refs/heads/${defaultBranch}`)\n\t\t\t.catch(() => `refs/heads/${defaultBranch}`),\n\t]);\n\n\t// Resolve all branch and tag oids in parallel\n\tconst [branchRefs, tagRefs] = await Promise.all([\n\t\tPromise.all(\n\t\t\tbranches.map(async (branch) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst oid = await resolveLooseRefFast(repo, `refs/heads/${branch}`);\n\t\t\t\t\treturn { name: `refs/heads/${branch}`, oid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}),\n\t\t),\n\t\tPromise.all(\n\t\t\ttags.map(async (tag) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst oid = await resolveLooseRefFast(repo, `refs/tags/${tag}`);\n\t\t\t\t\treturn { name: `refs/tags/${tag}`, oid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}),\n\t\t),\n\t]);\n\n\t// HEAD in a bare repo is almost always a symref to a branch whose oid\n\t// branchRefs already resolved above with one direct GET each — reuse it\n\t// instead of a second, separate resolveRef(\"HEAD\") call. isomorphic-git's\n\t// ref resolution runs its full multi-candidate expansion (refs/%s,\n\t// refs/tags/%s, refs/heads/%s, refs/remotes/%s, refs/remotes/%s/HEAD)\n\t// even when the symref target is already fully qualified, which used to\n\t// cost several guaranteed-404 R2 reads on every clone/fetch. Falls back\n\t// to the slow-but-correct path only when that lookup misses: detached\n\t// HEAD (points at a raw sha, not a symref) or a symref to a branch that\n\t// doesn't exist yet (freshly initialized, still-empty repo).\n\tconst headOid =\n\t\tbranchRefs.find((r) => r?.name === headSymref)?.oid ??\n\t\t(await git.resolveRef({ ...repo, ref: \"HEAD\" }).catch(() => null));\n\n\tconst refs: Array<{ name: string; oid: string }> = [];\n\tif (headOid) refs.push({ name: \"HEAD\", oid: headOid });\n\tfor (const r of branchRefs) if (r) refs.push(r);\n\tfor (const r of tagRefs) if (r) refs.push(r);\n\n\treturn { refs, headSymref };\n}\n\nexport interface InfoRefsOptions {\n\tservice: GitService;\n\tdefaultBranch?: string;\n\t/** Advertised in the agent capability. Default \"git-fs-s3\". */\n\tagent?: string;\n}\n\n/**\n * `GET …/info/refs?service=…` — the ref advertisement. Authentication and\n * authorization are the caller's job before invoking this.\n *\n * Capabilities advertised match what the sibling handlers implement:\n * side-band-64k (honored by handleUploadPack's response framing — clients\n * like isomorphic-git unconditionally expect it), tip/reachable sha1 wants,\n * delete-refs and report-status for pushes.\n */\nexport async function handleInfoRefs(\n\trepo: Repo,\n\toptions: InfoRefsOptions,\n\thooks?: HttpHooks,\n): Promise<GitHttpResult> {\n\tconst { service } = options;\n\tconst agent = options.agent ?? \"git-fs-s3\";\n\tconst { refs, headSymref } = await runStep(hooks, \"listAllRefs\", () =>\n\t\tlistAllRefs(repo, options.defaultBranch ?? \"main\"),\n\t);\n\n\tconst isUpload = service === \"git-upload-pack\";\n\tconst caps = isUpload\n\t\t? `no-progress side-band-64k symref=HEAD:${headSymref} allow-tip-sha1-in-want allow-reachable-sha1-in-want agent=${agent}`\n\t\t: `delete-refs report-status no-done agent=${agent}`;\n\n\tconst parts: Uint8Array[] = [pktLine(`# service=${service}\\n`), FLUSH];\n\n\tif (refs.length === 0) {\n\t\t// Empty repo: git needs this exact sentinel\n\t\tparts.push(\n\t\t\tpktLine(\n\t\t\t\t`0000000000000000000000000000000000000000 capabilities^{}\\0${caps}\\n`,\n\t\t\t),\n\t\t);\n\t} else {\n\t\tlet first = true;\n\t\tfor (const { name, oid } of refs) {\n\t\t\tparts.push(\n\t\t\t\tpktLine(first ? `${oid} ${name}\\0${caps}\\n` : `${oid} ${name}\\n`),\n\t\t\t);\n\t\t\tfirst = false;\n\t\t}\n\t}\n\tparts.push(FLUSH);\n\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": `application/x-${service}-advertisement`,\n\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t},\n\t\tbody: concat(...parts),\n\t};\n}\n","/**\n * Edge-compatible utilities replacing node:crypto, node:zlib, and Buffer.\n *\n * Every function here uses only Web APIs (SubtleCrypto, CompressionStream,\n * TextEncoder/TextDecoder) — no Node built-ins. They work on Cloudflare\n * Workers, Vercel Edge, Deno Deploy, and Node >= 18.\n */\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\n// ---------------------------------------------------------------------------\n// Text\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a UTF-8 string to bytes.\n *\n * Return type pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,\n * whose default type argument differs across TypeScript versions) so it's\n * always assignable to Fetch API `BodyInit` regardless of a consumer's own\n * TypeScript/lib version.\n */\nexport function encodeUtf8(data: string): Uint8Array<ArrayBuffer> {\n\treturn textEncoder.encode(data);\n}\n\n/** Decode bytes as UTF-8. */\nexport function decodeUtf8(data: Uint8Array): string {\n\treturn textDecoder.decode(data);\n}\n\n/** Decode bytes as ASCII. */\nexport function decodeAscii(data: Uint8Array): string {\n\tlet s = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\ts += String.fromCharCode(data[i] as number);\n\treturn s;\n}\n\n// ---------------------------------------------------------------------------\n// Array manipulation\n// ---------------------------------------------------------------------------\n\n/** Concatenate any number of Uint8Arrays into one. */\nexport function concat(...parts: Uint8Array[]): Uint8Array<ArrayBuffer> {\n\tlet total = 0;\n\tfor (const p of parts) total += p.length;\n\tconst out = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const p of parts) {\n\t\tout.set(p, offset);\n\t\toffset += p.length;\n\t}\n\treturn out;\n}\n\n// ---------------------------------------------------------------------------\n// Encoding\n// ---------------------------------------------------------------------------\n\n/** Uint8Array → lowercase hex string. */\nexport function toHex(data: Uint8Array): string {\n\tlet hex = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\thex += (data[i] as number).toString(16).padStart(2, \"0\");\n\treturn hex;\n}\n\n/** Uint8Array → base64 string. */\nexport function toBase64(data: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\tbinary += String.fromCharCode(data[i] as number);\n\treturn btoa(binary);\n}\n\n/** Hex string → Uint8Array. */\nexport function fromHex(hex: string): Uint8Array<ArrayBuffer> {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n\t}\n\treturn bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Crypto\n// ---------------------------------------------------------------------------\n\n/** SHA-1 hash via Web Crypto API. Returns a hex string. */\nexport async function sha1(data: Uint8Array | string): Promise<string> {\n\tconst bytes = typeof data === \"string\" ? encodeUtf8(data) : data;\n\tconst hash = await globalThis.crypto.subtle.digest(\"SHA-1\", bytes);\n\treturn toHex(new Uint8Array(hash));\n}\n\n// ---------------------------------------------------------------------------\n// Compression\n// ---------------------------------------------------------------------------\n\n/**\n * Deflate compress via the CompressionStream Web API.\n * Falls back to throwing if CompressionStream is unavailable (very old runtimes).\n */\nexport async function deflate(\n\tdata: Uint8Array,\n): Promise<Uint8Array<ArrayBuffer>> {\n\tconst stream = new Blob([data])\n\t\t.stream()\n\t\t.pipeThrough(new CompressionStream(\"deflate\"));\n\treturn new Uint8Array(await new Response(stream).arrayBuffer());\n}\n\n// ---------------------------------------------------------------------------\n// Binary detection (replaces Buffer.includes(0) pattern)\n// ---------------------------------------------------------------------------\n\n/** Check if a Uint8Array contains a null byte. */\nexport function hasNullByte(data: Uint8Array): boolean {\n\treturn data.includes(0);\n}\n\n/**\n * Read a blob as text or binary metadata — the edge-compatible replacement\n * for the `Buffer.from(blob)` pattern used throughout diff.ts and history.ts.\n */\nexport function readBlobContent(blob: Uint8Array): {\n\tisBinary: boolean;\n\ttext: string;\n\tbytes: Uint8Array;\n} {\n\tconst isBinary = hasNullByte(blob);\n\treturn {\n\t\tisBinary,\n\t\ttext: isBinary ? \"\" : decodeUtf8(blob),\n\t\tbytes: blob,\n\t};\n}\n","import git from \"isomorphic-git\";\nimport { GitInvalidRequestError } from \"../git-errors.js\";\nimport { isSafeBranchName } from \"../refs.js\";\nimport type { Repo } from \"./types.js\";\n\nexport interface Branch {\n\tname: string;\n\tcommit: string;\n\tisDefault: boolean;\n}\n\n/**\n * Defense in depth for every branch-name argument below: `git.deleteBranch`\n * and raw resolveRef/writeRef reads don't validate ref names internally the\n * way `git.branch` does (see refs.ts) — guard at the point the primitives are\n * actually called, not just at an API boundary far above.\n */\nexport function assertSafeBranchName(name: string): void {\n\tif (!isSafeBranchName(name)) {\n\t\tthrow new GitInvalidRequestError(`Invalid branch name: ${name}`);\n\t}\n}\n\nconst FULL_SHA_RE = /^[0-9a-f]{40}$/i;\n\n/**\n * Resolve a fully-qualified ref's oid with one read instead of isomorphic-\n * git's own `resolveRef`, which does a stat *then* a read for a loose ref\n * (two round trips against object storage) — wasteful specifically here,\n * where the caller already knows the ref exists (it came from a directory\n * listing moments earlier). Reads the loose file directly and falls back to\n * `git.resolveRef` only when that doesn't pan out (packed-refs, or anything\n * else the plain-loose-file assumption doesn't cover), so correctness for\n * those cases is unchanged — this only removes the redundant round trip on\n * the common path.\n */\nexport async function resolveLooseRefFast(\n\trepo: Repo,\n\tref: string,\n): Promise<string> {\n\t// Repo.fs is typed as isomorphic-git's own fs union (promise- or\n\t// callback-based) since that's what git.readTree's signature allows, but\n\t// every fs this package actually constructs (git-fs.ts) is promise-based\n\t// — the runtime check just keeps the callback-style branch honest instead\n\t// of crashing on it.\n\tconst promisesFs = (\n\t\trepo.fs as {\n\t\t\tpromises?: {\n\t\t\t\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n\t\t\t};\n\t\t}\n\t).promises;\n\tif (promisesFs) {\n\t\ttry {\n\t\t\tconst content = await promisesFs.readFile(\n\t\t\t\t`${repo.gitdir}/${ref}`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tconst oid = (\n\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t? content\n\t\t\t\t\t: new TextDecoder().decode(content)\n\t\t\t).trim();\n\t\t\tif (FULL_SHA_RE.test(oid)) return oid;\n\t\t} catch {\n\t\t\t// Not a loose file (packed-refs, or genuinely absent) — fall through.\n\t\t}\n\t}\n\treturn git.resolveRef({ ...repo, ref });\n}\n\n/** All branches with their tip commits; [] for an empty repository. */\nexport async function listBranches(repo: Repo): Promise<Branch[]> {\n\ttry {\n\t\tconst [branches, currentBranch] = await Promise.all([\n\t\t\tgit.listBranches(repo),\n\t\t\tgit.currentBranch({ ...repo, fullname: false }).catch(() => null),\n\t\t]);\n\n\t\treturn Promise.all(\n\t\t\tbranches.map(async (branch) => ({\n\t\t\t\tname: branch,\n\t\t\t\tcommit: await resolveLooseRefFast(repo, `refs/heads/${branch}`),\n\t\t\t\tisDefault: branch === currentBranch,\n\t\t\t})),\n\t\t);\n\t} catch (err: unknown) {\n\t\tif ((err as { code?: string }).code === \"NotFoundError\") return [];\n\t\tthrow err;\n\t}\n}\n\n/** Create `name` pointing at the tip of `startPoint` (no checkout). */\nexport async function createBranchFrom(\n\trepo: Repo,\n\tname: string,\n\tstartPoint = \"main\",\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tassertSafeBranchName(startPoint);\n\tconst object = await resolveLooseRefFast(repo, `refs/heads/${startPoint}`);\n\tawait git.branch({ ...repo, ref: name, checkout: false, object });\n}\n\n/** Delete a branch ref (validated — deleteBranch has no internal ref check). */\nexport async function deleteBranchByName(\n\trepo: Repo,\n\tname: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tawait git.deleteBranch({ ...repo, ref: name });\n}\n\n/** Throws (NotFoundError) unless the branch resolves. */\nexport async function assertBranchExists(\n\trepo: Repo,\n\tname: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tawait resolveLooseRefFast(repo, `refs/heads/${name}`);\n}\n","/**\n * Git ref-name validation, mirroring isomorphic-git's own internal `isValidRef`\n * character-class rules (the check `git.branch` and top-level `git.writeRef`\n * run before touching disk).\n *\n * Several of isomorphic-git's OTHER ref-touching primitives — `git.commit`,\n * `git.merge`, `git.deleteBranch`, and top-level `git.resolveRef`/\n * `git.deleteRef` — do NOT run this check internally: they resolve straight\n * through `fs.write`/`fs.rm(join(gitdir, ref))` with no jail to the gitdir.\n * On a shared-storage server (many repos under one prefix or base directory),\n * every branch/ref name that originates from request input must be validated\n * against these predicates before it reaches any of those primitives —\n * otherwise a `\"../\"`-laden name lets a caller with write access to any single\n * repo read, corrupt, or delete another repo's ref/object files.\n */\n\nconst BAD_REF_COMPONENT =\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: control chars are exactly what git's own ref-name rules reject — this needs to match the same range.\n\t/(^|[/.])([/.]|$)|^@$|@\\{|[\\x00-\\x20\\x7f~^:?*[\\\\]|\\.lock(\\/|$)/;\n\nconst FULL_SHA_RE = /^[0-9a-f]{40}$/i;\n\n/** Validates a fully-qualified ref (must start with refs/heads/ or refs/tags/). */\nexport function isSafeFullRefName(ref: string): boolean {\n\tif (!ref.startsWith(\"refs/heads/\") && !ref.startsWith(\"refs/tags/\")) {\n\t\treturn false;\n\t}\n\treturn !BAD_REF_COMPONENT.test(ref);\n}\n\n/**\n * Validates a bare branch name (no refs/ prefix). Rejects anything that looks\n * like a full ref path — a name of `\"refs/heads/x\"` would otherwise sail\n * through unprefixed at call sites that build `refs/heads/${name}` themselves\n * (doubling the prefix into something that still resolves), or be used as-is\n * at call sites that pass a name already containing `\"refs/\"` straight\n * through. Also rejects 40-hex SHA-shaped values so a stored branch name can\n * never be ambiguous with a commit SHA at write time; use\n * {@link isSafeRefName} on read paths that accept both shapes.\n */\nexport function isSafeBranchName(name: string): boolean {\n\tif (!name || name.startsWith(\"refs/\") || name === \"HEAD\") return false;\n\tif (FULL_SHA_RE.test(name)) return false;\n\treturn !BAD_REF_COMPONENT.test(name);\n}\n\n/** True for a full 40-hex-char commit SHA — the shape {@link isSafeBranchName} deliberately rejects. */\nexport function isFullSha(value: string): boolean {\n\treturn FULL_SHA_RE.test(value);\n}\n\n/**\n * Validates a \"ref\" field that may name either a branch or a commit SHA it's\n * pinned to — the shape read-path route params take (permalinks, raw links).\n * Both shapes still go through the traversal check.\n */\nexport function isSafeRefName(value: string): boolean {\n\treturn isSafeBranchName(value) || isFullSha(value);\n}\n\n/**\n * Validates a repo-relative file path from request input: relative, no `..`\n * segments, no `.git/` prefix, no null bytes. Use this anywhere a path\n * segment comes straight off a URL or form field rather than re-deriving the\n * checks ad hoc.\n */\nexport function isSafeRepoPath(p: string): boolean {\n\tif (p.startsWith(\"/\")) return false;\n\tif (p.split(\"/\").some((segment) => segment === \"..\")) return false;\n\tif (/^\\.git(\\/|$)/i.test(p)) return false;\n\tif (p.includes(\"\\0\")) return false;\n\treturn true;\n}\n\n/**\n * Qualify a bare branch name to `refs/heads/<name>` before handing it to\n * isomorphic-git. `resolveRef`/`expand` try several candidate paths in\n * sequence for a bare name — `ref`, `refs/ref`, `refs/tags/ref`,\n * `refs/heads/ref`, … — missing (and, against object storage, paying a real\n * round trip for) the first three every time. For a branch-only ref model,\n * skip straight to the winner. Left untouched: already-qualified refs,\n * `\"HEAD\"` (its own first candidate, already optimal), and 40-hex oids\n * (resolved locally by isomorphic-git with no I/O at all).\n */\nexport function qualifyBranchRef(ref: string): string {\n\tif (ref.startsWith(\"refs/\") || ref === \"HEAD\" || FULL_SHA_RE.test(ref)) {\n\t\treturn ref;\n\t}\n\treturn `refs/heads/${ref}`;\n}\n","/**\n * Git pkt-line framing (smart HTTP protocol wire format).\n *\n * Edge-compatible: uses Uint8Array instead of Buffer.\n */\n\nimport { concat, decodeAscii, decodeUtf8, encodeUtf8 } from \"../edge-utils.js\";\n\n/** Flush packet: exactly four zero bytes. */\nexport const FLUSH: Uint8Array<ArrayBuffer> = new Uint8Array([\n\t0x30, 0x30, 0x30, 0x30,\n]); // \"0000\"\n\n/** Frame a UTF-8 string as one pkt-line. */\nexport function pktLine(data: string): Uint8Array<ArrayBuffer> {\n\tconst body = encodeUtf8(data);\n\tconst len = (body.length + 4).toString(16).padStart(4, \"0\");\n\treturn concat(encodeUtf8(len), body);\n}\n\n/** Frame raw bytes as one pkt-line. */\nexport function pktLineBuffer(body: Uint8Array): Uint8Array<ArrayBuffer> {\n\tconst len = (body.length + 4).toString(16).padStart(4, \"0\");\n\treturn concat(encodeUtf8(len), body);\n}\n\n/** Decode pkt-lines to strings; `null` marks a flush-pkt. Stops on garbage. */\nexport function parsePktLines(buf: Uint8Array): Array<string | null> {\n\tconst lines: Array<string | null> = [];\n\tlet pos = 0;\n\twhile (pos + 4 <= buf.length) {\n\t\tconst len = Number.parseInt(decodeAscii(buf.subarray(pos, pos + 4)), 16);\n\t\tif (len === 0) {\n\t\t\tlines.push(null);\n\t\t\tpos += 4;\n\t\t} else if (len >= 4) {\n\t\t\tlines.push(decodeUtf8(buf.subarray(pos + 4, pos + len)));\n\t\t\tpos += len;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn lines;\n}\n\n/**\n * Per the git protocol, once side-band-64k has been negotiated, packfile\n * bytes in the upload-pack response must be chunked into pkt-lines each\n * prefixed with a control byte (0x01 = packfile data), terminated by a\n * flush-pkt. Without this, clients that don't special-case \"no side-band\" —\n * e.g. isomorphic-git's GitSideBand.demux, which always treats the response\n * as side-band-framed — misparse the raw packfile bytes as bogus pkt-line\n * length headers and spin forever. (Native `git` tolerates a raw unframed\n * stream when side-band isn't negotiated, so this only surfaces with\n * isomorphic-git as the HTTP client.)\n */\nconst SIDE_BAND_MAX_CHUNK = 65515;\n\nexport function sideBandPackfile(\n\tpackData: Uint8Array,\n): Uint8Array<ArrayBuffer> {\n\tconst parts: Uint8Array[] = [];\n\tfor (\n\t\tlet offset = 0;\n\t\toffset < packData.length;\n\t\toffset += SIDE_BAND_MAX_CHUNK\n\t) {\n\t\tconst chunk = packData.subarray(offset, offset + SIDE_BAND_MAX_CHUNK);\n\t\tparts.push(pktLineBuffer(concat(new Uint8Array([1]), chunk)));\n\t}\n\tparts.push(FLUSH);\n\treturn concat(...parts);\n}\n","import type { Repo } from \"../ops/types.js\";\n\n/**\n * Transport-agnostic HTTP response the handlers produce.\n *\n * `body` is pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,\n * whose default type argument isn't consistent across TypeScript versions)\n * so it's always assignable to Fetch API `BodyInit` regardless of which\n * TypeScript/lib version a consumer compiles against — every producer here\n * (`concat`, `pktLine`, `TextEncoder.encode`) is ArrayBuffer-backed already.\n */\nexport interface GitHttpResult {\n\tstatus: number;\n\theaders: Record<string, string>;\n\tbody: Uint8Array<ArrayBuffer>;\n}\n\n/** Optional instrumentation hooks shared by the /http handlers. */\nexport interface HttpHooks {\n\t/** Wrap a timed sub-step. Default: run directly. */\n\tstep?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;\n\t/** Non-fatal problem sink (missing objects, failed repacks). */\n\tonWarn?: (message: string, error?: unknown) => void;\n}\n\nexport const runStep = <T>(\n\thooks: HttpHooks | undefined,\n\tlabel: string,\n\tfn: () => Promise<T>,\n): Promise<T> => (hooks?.step ? hooks.step(label, fn) : fn());\n\n/**\n * The raw promise-fs surface some /http paths need beyond isomorphic-git's\n * plumbing (writing an incoming pack file, listing/deleting pack files).\n * Both `node:fs` and this package's `GitFs` satisfy it via `.promises`.\n */\nexport interface RawFsPromises {\n\treadFile(path: string): Promise<Uint8Array | string>;\n\twriteFile(path: string, data: Uint8Array | string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\treaddir(path: string): Promise<string[]>;\n\tmkdir(path: string, options?: { recursive?: boolean }): Promise<unknown>;\n\tstat(path: string): Promise<unknown>;\n}\n\n/** Duck-type the `.promises` surface off a repo's fs. */\nexport function rawFs(repo: Repo): RawFsPromises {\n\tconst fs = repo.fs as { promises?: RawFsPromises };\n\tif (!fs?.promises) {\n\t\tthrow new TypeError(\n\t\t\t\"This operation needs a promise fs (`fs.promises`) — node:fs and createGitFs both provide one\",\n\t\t);\n\t}\n\treturn fs.promises;\n}\n","import git from \"isomorphic-git\";\nimport type { Repo } from \"../ops/types.js\";\nimport type { HttpHooks } from \"./types.js\";\n\nexport interface ReachabilityResult {\n\toids: string[];\n\t/**\n\t * False if any object in the graph couldn't be read — repack uses this\n\t * (not a raw object-count comparison) to decide whether it's safe to\n\t * delete old packs: counts alone can't distinguish \"everything read fine\"\n\t * from \"some objects silently failed\".\n\t */\n\tcomplete: boolean;\n}\n\nconst delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * A read landing in the gap between a repack's new consolidated pack being\n * uploaded and the stale-pack cleanup finishing can transiently miss an\n * object that is not actually lost — it exists in the new pack the whole\n * time; the reader's cached pack listing was just taken mid-transition. One\n * retry after a short delay is enough to observe the consistent listing.\n */\nconst MISSING_OBJECT_RETRY_DELAY_MS = 200;\n\n/**\n * Walk the full object graph from `startOids`, returning every reachable\n * oid. Concurrent traversal paths are deduplicated promise-per-oid; a\n * missing object is retried once, then reported through `hooks.onWarn` and\n * reflected in `complete: false`.\n */\nexport async function collectReachableOids(\n\trepo: Repo,\n\tstartOids: string[],\n\thooks?: HttpHooks,\n): Promise<ReachabilityResult> {\n\tconst seen = new Set<string>();\n\tlet complete = true;\n\tconst promises = new Map<string, Promise<void>>();\n\n\tasync function readAndVisitChildren(oid: string): Promise<void> {\n\t\tconst obj = await git.readObject({ ...repo, oid });\n\t\t// Add to seen only after a successful read so failed reads are excluded\n\t\t// from any pack built from this set.\n\t\tseen.add(oid);\n\t\tlet children: string[] = [];\n\t\tif (obj.type === \"commit\") {\n\t\t\tconst { commit } = await git.readCommit({ ...repo, oid });\n\t\t\tchildren = [commit.tree, ...commit.parent];\n\t\t} else if (obj.type === \"tree\") {\n\t\t\tconst { tree } = await git.readTree({ ...repo, oid });\n\t\t\tchildren = tree.map((e) => e.oid);\n\t\t} else if (obj.type === \"tag\") {\n\t\t\tconst { tag } = await git.readTag({ ...repo, oid });\n\t\t\tchildren = [tag.object];\n\t\t}\n\t\tawait Promise.all(children.map(visit));\n\t}\n\n\tfunction visit(oid: string): Promise<void> {\n\t\tconst existing = promises.get(oid);\n\t\tif (existing) return existing;\n\n\t\tconst p = (async () => {\n\t\t\ttry {\n\t\t\t\tawait readAndVisitChildren(oid);\n\t\t\t} catch {\n\t\t\t\ttry {\n\t\t\t\t\tawait delay(MISSING_OBJECT_RETRY_DELAY_MS);\n\t\t\t\t\tawait readAndVisitChildren(oid);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\thooks?.onWarn?.(`missing object ${oid}`, err);\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\tpromises.set(oid, p);\n\t\treturn p;\n\t}\n\n\tawait Promise.all(startOids.map(visit));\n\treturn { oids: Array.from(seen), complete };\n}\n","import git from \"isomorphic-git\";\nimport { concat, decodeAscii, decodeUtf8 } from \"../edge-utils.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { isSafeFullRefName } from \"../refs.js\";\nimport { FLUSH, pktLine } from \"./pkt-line.js\";\nimport { type RepackOptions, repackRepository } from \"./repack.js\";\nimport { type GitHttpResult, type HttpHooks, rawFs, runStep } from \"./types.js\";\n\nconst ZERO_OID = \"0\".repeat(40);\n\nexport interface RefUpdateCommand {\n\toldOid: string;\n\tnewOid: string;\n\trefName: string;\n}\n\nexport interface RefUpdateResult {\n\trefName: string;\n\tok: boolean;\n\treason?: string;\n}\n\n/** Split a receive-pack body: pkt-line ref commands, flush, then raw PACK. */\nexport function parseReceivePackBody(body: Uint8Array): {\n\trefUpdates: RefUpdateCommand[];\n\tpackData: Uint8Array;\n} {\n\tconst refUpdates: RefUpdateCommand[] = [];\n\tlet pos = 0;\n\twhile (pos + 4 <= body.length) {\n\t\tconst len = Number.parseInt(decodeAscii(body.subarray(pos, pos + 4)), 16);\n\t\tif (len === 0) {\n\t\t\tpos += 4;\n\t\t\tbreak; // flush = end of ref update commands\n\t\t}\n\t\tif (len < 4) break;\n\t\t// Strip NUL-separated capabilities from the first command line\n\t\tconst line = decodeUtf8(body.subarray(pos + 4, pos + len))\n\t\t\t.replace(/\\n$/, \"\")\n\t\t\t.split(\"\\0\")[0];\n\t\tpos += len;\n\t\tconst parts = (line ?? \"\").split(\" \");\n\t\tconst [oldOid, newOid, refName] = parts;\n\t\tif (oldOid && newOid && refName) {\n\t\t\trefUpdates.push({ oldOid, newOid, refName });\n\t\t}\n\t}\n\treturn { refUpdates, packData: body.subarray(pos) };\n}\n\n/** Initialize the repo when its HEAD doesn't exist yet (first push). */\nexport async function ensureRepoInitialized(\n\trepo: Repo,\n\tdefaultBranch = \"main\",\n): Promise<void> {\n\tconst fsp = rawFs(repo);\n\ttry {\n\t\tawait fsp.stat(`${repo.gitdir}/HEAD`);\n\t} catch {\n\t\tawait fsp.mkdir(repo.gitdir, { recursive: true }).catch(() => {});\n\t\tawait git.init({ ...repo, dir: repo.gitdir, defaultBranch, bare: true });\n\t}\n}\n\n/** Write the incoming PACK into objects/pack/ and index it. */\nexport async function indexIncomingPack(\n\trepo: Repo,\n\tpackData: Uint8Array,\n\thooks?: HttpHooks,\n): Promise<void> {\n\tif (packData.length < 4) return;\n\tawait runStep(hooks, \"write + indexPack incoming pack\", async () => {\n\t\tconst fsp = rawFs(repo);\n\t\tconst packDir = `${repo.gitdir}/objects/pack`;\n\t\tawait fsp.mkdir(packDir, { recursive: true });\n\n\t\tconst packName = `recv-${Date.now()}`;\n\t\tawait fsp.writeFile(`${packDir}/${packName}.pack`, packData);\n\n\t\tawait git.indexPack({\n\t\t\t...repo,\n\t\t\tdir: packDir,\n\t\t\tfilepath: `${packName}.pack`,\n\t\t});\n\t});\n}\n\n/**\n * Apply ref updates, enforcing compare-and-swap against each command's\n * claimed oldOid. Every client-supplied refName is validated before any\n * filesystem call reads or writes through it.\n */\nexport async function applyRefUpdates(\n\trepo: Repo,\n\trefUpdates: RefUpdateCommand[],\n\thooks?: HttpHooks,\n): Promise<RefUpdateResult[]> {\n\treturn runStep(hooks, \"apply ref updates\", () =>\n\t\tPromise.all(\n\t\t\trefUpdates.map(async ({ oldOid, newOid, refName }) => {\n\t\t\t\tif (!isSafeFullRefName(refName)) {\n\t\t\t\t\treturn { refName, ok: false, reason: \"invalid ref name\" };\n\t\t\t\t}\n\n\t\t\t\tconst currentOid = await git\n\t\t\t\t\t.resolveRef({ ...repo, ref: refName })\n\t\t\t\t\t.catch(() => ZERO_OID);\n\n\t\t\t\tif (currentOid !== oldOid) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\trefName,\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\treason: \"non-fast-forward, ref updated by another push\",\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif (newOid === ZERO_OID) {\n\t\t\t\t\tawait git.deleteRef({ ...repo, ref: refName }).catch(() => {});\n\t\t\t\t} else {\n\t\t\t\t\tawait git.writeRef({\n\t\t\t\t\t\t...repo,\n\t\t\t\t\t\tref: refName,\n\t\t\t\t\t\tvalue: newOid,\n\t\t\t\t\t\tforce: true,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { refName, ok: true };\n\t\t\t}),\n\t\t),\n\t);\n}\n\n/** The report-status response body for a set of ref-update results. */\nexport function receivePackResponse(results: RefUpdateResult[]): GitHttpResult {\n\tconst responseBody = concat(\n\t\tpktLine(\"unpack ok\\n\"),\n\t\t...results.map(({ refName, ok, reason }) =>\n\t\t\tpktLine(ok ? `ok ${refName}\\n` : `ng ${refName} ${reason}\\n`),\n\t\t),\n\t\tFLUSH,\n\t);\n\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-git-receive-pack-result\",\n\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t},\n\t\tbody: responseBody,\n\t};\n}\n\nexport interface ApplyReceivePackOptions {\n\tdefaultBranch?: string;\n\t/**\n\t * Repack tuning, or `false` to skip repacking entirely. The returned\n\t * `stalePackPaths` are gitdir-relative pack files the repack deleted from\n\t * this repo — after syncing to any secondary storage, delete them there\n\t * too.\n\t */\n\trepack?: RepackOptions | false;\n}\n\n/**\n * The storage half of a push against `repo`: initialize if needed, index the\n * incoming pack, CAS-apply ref updates, then repack when enough packs have\n * accumulated. Serialize concurrent pushes to the same repo externally (a\n * per-repo lock); build the HTTP response afterwards with\n * {@link receivePackResponse}.\n */\nexport async function applyReceivePack(\n\trepo: Repo,\n\tparsed: { refUpdates: RefUpdateCommand[]; packData: Uint8Array },\n\toptions?: ApplyReceivePackOptions,\n\thooks?: HttpHooks,\n): Promise<{ results: RefUpdateResult[]; stalePackPaths: string[] }> {\n\tawait ensureRepoInitialized(repo, options?.defaultBranch ?? \"main\");\n\tawait indexIncomingPack(repo, parsed.packData, hooks);\n\tconst results = await applyRefUpdates(repo, parsed.refUpdates, hooks);\n\tconst repackOptions = options?.repack;\n\tconst stalePackPaths =\n\t\trepackOptions === false\n\t\t\t? []\n\t\t\t: await runStep(hooks, \"repack\", () =>\n\t\t\t\t\trepackRepository(repo, repackOptions, hooks),\n\t\t\t\t);\n\treturn { results, stalePackPaths };\n}\n","/**\n * Pack consolidation: merge all packs into one verified, non-deltified pack.\n *\n * Edge-compatible: uses Web Crypto (SHA-1), CompressionStream (deflate),\n * and Uint8Array throughout — no node:crypto, node:zlib, or Buffer.\n */\n\nimport git from \"isomorphic-git\";\nimport { concat, deflate, encodeUtf8, sha1 } from \"../edge-utils.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { collectReachableOids } from \"./reachability.js\";\nimport { type HttpHooks, rawFs } from \"./types.js\";\n\ntype PackObjectType = \"commit\" | \"tree\" | \"blob\" | \"tag\";\n\n// Git pack object type bits (bits 6-4 of the header's first byte) — same\n// constants real git and isomorphic-git's own (de)serializers use.\nconst PACK_OBJECT_TYPE_BITS: Record<PackObjectType, number> = {\n\tcommit: 0b0010000,\n\ttree: 0b0100000,\n\tblob: 0b0110000,\n\ttag: 0b1000000,\n};\n\n// Git's pack object header: first byte packs (continuation bit | 3-bit type |\n// low 4 bits of length); any remaining length is emitted 7 bits at a time,\n// each with its own continuation bit, little-endian.\nfunction encodePackObjectHeader(\n\ttype: PackObjectType,\n\tlength: number,\n): Uint8Array {\n\tconst bytes: number[] = [];\n\tlet more = length > 0b1111;\n\tbytes.push(\n\t\t(more ? 0b10000000 : 0) | PACK_OBJECT_TYPE_BITS[type] | (length & 0b1111),\n\t);\n\tlength >>>= 4;\n\twhile (more) {\n\t\tmore = length > 0b01111111;\n\t\tbytes.push((more ? 0b10000000 : 0) | (length & 0b01111111));\n\t\tlength >>>= 7;\n\t}\n\treturn new Uint8Array(bytes);\n}\n\n// Git's object hash: sha1(\"<type> <byte length>\\0<content>\") — matches\n// isomorphic-git's internal GitObject.wrap + shasum, computed independently\n// here rather than trusted from isomorphic-git's own read path.\nasync function hashGitObject(\n\ttype: string,\n\tcontent: Uint8Array,\n): Promise<string> {\n\tconst prefix = encodeUtf8(`${type} ${content.length}\\0`);\n\treturn sha1(concat(prefix, content));\n}\n\ntype VerifiedObject = { type: PackObjectType; content: Uint8Array };\n\n/**\n * Read every reachable object and independently re-derive its oid from the\n * bytes isomorphic-git handed back, instead of trusting the oid it was asked\n * for. isomorphic-git's *packed*-object read path never verifies the\n * resolved content's SHA-1 against the requested oid — only the\n * loose-object branch does.\n */\nasync function readAndVerifyObjects(\n\trepo: Repo,\n\toids: string[],\n): Promise<Map<string, VerifiedObject>> {\n\tconst objects = new Map<string, VerifiedObject>();\n\tconst BATCH_SIZE = 100;\n\tfor (let i = 0; i < oids.length; i += BATCH_SIZE) {\n\t\tconst batch = oids.slice(i, i + BATCH_SIZE);\n\t\tconst entries = await Promise.all(\n\t\t\tbatch.map(async (oid) => {\n\t\t\t\tconst { type, object } = await git.readObject({\n\t\t\t\t\t...repo,\n\t\t\t\t\toid,\n\t\t\t\t\tformat: \"content\",\n\t\t\t\t});\n\t\t\t\tconst content = object as unknown as Uint8Array;\n\t\t\t\tconst actualOid = await hashGitObject(type, content);\n\t\t\t\tif (actualOid !== oid) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`repack: object ${oid} failed independent SHA-1 verification ` +\n\t\t\t\t\t\t\t`(recomputed ${actualOid}) — refusing to trust this read, aborting repack`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn { oid, type: type as PackObjectType, content };\n\t\t\t}),\n\t\t);\n\t\tfor (const { oid, type, content } of entries) {\n\t\t\tobjects.set(oid, { type, content });\n\t\t}\n\t}\n\treturn objects;\n}\n\n/**\n * Serialize verified objects into a pack containing only full (never\n * deltified) entries, in the given oid order.\n */\nasync function buildVerifiedPack(\n\toids: string[],\n\tobjects: Map<string, VerifiedObject>,\n): Promise<Uint8Array> {\n\t// PACK header: \"PACK\" + version(2) + count, all big-endian uint32\n\tconst header = new Uint8Array(12);\n\tconst view = new DataView(header.buffer);\n\theader.set(encodeUtf8(\"PACK\"), 0);\n\tview.setUint32(4, 2, false); // version 2\n\tview.setUint32(8, oids.length, false); // object count\n\n\tconst chunks: Uint8Array[] = [header];\n\n\tconst BATCH_SIZE = 100;\n\tfor (let i = 0; i < oids.length; i += BATCH_SIZE) {\n\t\tconst batch = oids.slice(i, i + BATCH_SIZE);\n\t\tconst encoded = await Promise.all(\n\t\t\tbatch.map(async (oid) => {\n\t\t\t\tconst entry = objects.get(oid);\n\t\t\t\tif (!entry) {\n\t\t\t\t\tthrow new Error(`repack: missing verified object for ${oid}`);\n\t\t\t\t}\n\t\t\t\tconst objHeader = encodePackObjectHeader(\n\t\t\t\t\tentry.type,\n\t\t\t\t\tentry.content.length,\n\t\t\t\t);\n\t\t\t\tconst compressed = await deflate(entry.content);\n\t\t\t\treturn concat(objHeader, compressed);\n\t\t\t}),\n\t\t);\n\t\tchunks.push(...encoded);\n\t}\n\n\t// Git's pack trailer is the SHA-1 of every preceding byte (header +\n\t// every object entry) as a single digest — NOT an incremental/chained\n\t// hash. Web Crypto's subtle.digest has no streaming mode, so unlike\n\t// Node's crypto.createHash this can't be folded into the loop above;\n\t// everything is already buffered in `chunks`, so hash it in one pass.\n\tconst packBody = concat(...chunks);\n\tconst trailer = fromHex(await sha1(packBody));\n\treturn concat(packBody, trailer);\n}\n\n/** Hex string → Uint8Array. */\nfunction fromHex(hex: string): Uint8Array {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n\t}\n\treturn bytes;\n}\n\n/**\n * Default repack threshold. Consolidating is O(total repo object count) —\n * so paying it on every push makes push latency grow with repo size forever.\n */\nexport const REPACK_PACK_COUNT_THRESHOLD = 4;\n\nasync function countPacks(repo: Repo): Promise<number> {\n\ttry {\n\t\tconst entries = await rawFs(repo).readdir(`${repo.gitdir}/objects/pack`);\n\t\treturn entries.filter((f) => f.endsWith(\".pack\")).length;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\nexport interface RepackOptions {\n\t/** Skip repacking below this many accumulated packs. Default 4. */\n\tthreshold?: number;\n}\n\n/**\n * Consolidate all pack files into one, returning the gitdir-relative paths\n * of the old .pack/.idx files it removed — after syncing the new pack to any\n * secondary storage, delete those same paths there too.\n */\nexport async function repackRepository(\n\trepo: Repo,\n\toptions?: RepackOptions,\n\thooks?: HttpHooks,\n): Promise<string[]> {\n\tconst threshold = options?.threshold ?? REPACK_PACK_COUNT_THRESHOLD;\n\tconst fsp = rawFs(repo);\n\ttry {\n\t\tif ((await countPacks(repo)) < threshold) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst [branches, tags] = await Promise.all([\n\t\t\tgit.listBranches(repo),\n\t\t\tgit.listTags(repo),\n\t\t]);\n\t\tconst refNames = [\n\t\t\t...branches.map((b) => `refs/heads/${b}`),\n\t\t\t...tags.map((t) => `refs/tags/${t}`),\n\t\t];\n\t\tconst tipOids = (\n\t\t\tawait Promise.all(\n\t\t\t\trefNames.map((ref) =>\n\t\t\t\t\tgit.resolveRef({ ...repo, ref }).catch(() => null),\n\t\t\t\t),\n\t\t\t)\n\t\t).filter((oid): oid is string => oid !== null);\n\t\tif (tipOids.length === 0) return [];\n\n\t\tconst { oids, complete } = await collectReachableOids(repo, tipOids, hooks);\n\t\tif (!complete || oids.length === 0) return [];\n\n\t\tconst objects = await readAndVerifyObjects(repo, oids);\n\t\tconst packBuffer = await buildVerifiedPack(oids, objects);\n\n\t\tconst packDir = `${repo.gitdir}/objects/pack`;\n\t\tawait fsp.mkdir(packDir, { recursive: true });\n\t\tconst newBase = `pack-${Date.now()}`;\n\t\tconst newPackFile = `${newBase}.pack`;\n\t\tconst newIdxFile = `${newBase}.idx`;\n\t\tawait fsp.writeFile(`${packDir}/${newPackFile}`, packBuffer);\n\n\t\tconst { oids: indexedOids } = await git.indexPack({\n\t\t\t...repo,\n\t\t\tdir: packDir,\n\t\t\tfilepath: newPackFile,\n\t\t});\n\n\t\tconst expected = new Set(oids);\n\t\tconst indexed = new Set(indexedOids);\n\t\tif (\n\t\t\tindexed.size !== expected.size ||\n\t\t\toids.some((oid) => !indexed.has(oid))\n\t\t) {\n\t\t\tawait fsp.unlink(`${packDir}/${newPackFile}`).catch(() => {});\n\t\t\tawait fsp.unlink(`${packDir}/${newIdxFile}`).catch(() => {});\n\t\t\tthrow new Error(\n\t\t\t\t\"repack: indexed pack's oid set didn't match the verified reachable set — aborting\",\n\t\t\t);\n\t\t}\n\n\t\tconst allEntries: string[] = await fsp.readdir(packDir).catch(() => []);\n\n\t\tconst staleFiles = allEntries.filter(\n\t\t\t(f) =>\n\t\t\t\tf !== newPackFile &&\n\t\t\t\tf !== newIdxFile &&\n\t\t\t\t(f.endsWith(\".pack\") || f.endsWith(\".idx\") || f.endsWith(\".keep\")),\n\t\t);\n\n\t\tawait Promise.all(\n\t\t\tstaleFiles.map((f) => fsp.unlink(`${packDir}/${f}`).catch(() => {})),\n\t\t);\n\n\t\treturn staleFiles.map((f) => `objects/pack/${f}`);\n\t} catch (err) {\n\t\thooks?.onWarn?.(\"repack failed (non-fatal)\", err);\n\t\treturn [];\n\t}\n}\n","import git from \"isomorphic-git\";\nimport { concat } from \"../edge-utils.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { parsePktLines, pktLine, sideBandPackfile } from \"./pkt-line.js\";\nimport { collectReachableOids } from \"./reachability.js\";\nimport { type GitHttpResult, type HttpHooks, rawFs, runStep } from \"./types.js\";\n\nconst ZERO_OID = \"0\".repeat(40);\n\nexport interface UploadPackOptions {\n\t/**\n\t * Called once before the reachability walk — wire loose-object detection\n\t * (`GitFs.detectLooseObjects`) here so a fully packed repo's walk doesn't\n\t * pay a doomed loose-object probe per object.\n\t */\n\tbeforeWalk?: () => Promise<void>;\n}\n\n/**\n * `POST …/git-upload-pack` — serve a clone/fetch. Authorization is the\n * caller's job. Negotiation is client-driven (no multi_ack advertised):\n * \"have\" batches without \"done\" get a bare NAK; the final batch gets the\n * packfile, side-band-64k framed.\n *\n * Fast path: a fresh clone (no haves) of a repo consolidated down to a\n * single pack serves that pack's bytes directly — skipping the O(objects)\n * traversal + repack entirely.\n */\nexport async function handleUploadPack(\n\trepo: Repo,\n\tbody: Uint8Array,\n\toptions?: UploadPackOptions,\n\thooks?: HttpHooks,\n): Promise<GitHttpResult> {\n\tconst lines = parsePktLines(body);\n\n\tconst wants: string[] = [];\n\tconst haves: string[] = [];\n\tlet done = false;\n\tfor (const line of lines) {\n\t\tif (!line) continue;\n\t\tif (line.startsWith(\"want \")) {\n\t\t\twants.push(line.slice(5, 45));\n\t\t}\n\t\tif (line.startsWith(\"have \")) {\n\t\t\tconst sha = line.slice(5, 45);\n\t\t\tif (sha !== ZERO_OID) {\n\t\t\t\thaves.push(sha);\n\t\t\t}\n\t\t}\n\t\tif (line.startsWith(\"done\")) {\n\t\t\tdone = true;\n\t\t}\n\t}\n\n\tif (wants.length === 0) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\theaders: { \"Content-Type\": \"application/x-git-upload-pack-result\" },\n\t\t\tbody: concat(pktLine(\"NAK\\n\")),\n\t\t};\n\t}\n\n\tif (haves.length > 0 && !done) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\theaders: { \"Content-Type\": \"application/x-git-upload-pack-result\" },\n\t\t\tbody: concat(pktLine(\"NAK\\n\")),\n\t\t};\n\t}\n\n\t// Fresh clone = no haves, so all objects are needed. When the repo is down\n\t// to a single pack, serve it directly and skip the traversal + pack build.\n\tif (haves.length === 0) {\n\t\tconst packDirPath = `${repo.gitdir}/objects/pack`;\n\t\tconst entries = await runStep(hooks, \"readdir objects/pack\", () =>\n\t\t\trawFs(repo)\n\t\t\t\t.readdir(packDirPath)\n\t\t\t\t.catch(() => [] as string[]),\n\t\t);\n\t\tconst packNames = entries.filter((f) => f.endsWith(\".pack\"));\n\t\tconst packName = packNames[0];\n\t\tif (packNames.length === 1 && packName !== undefined) {\n\t\t\tconst packData = await runStep(\n\t\t\t\thooks,\n\t\t\t\t\"read consolidated pack (fast path)\",\n\t\t\t\t() => rawFs(repo).readFile(`${packDirPath}/${packName}`),\n\t\t\t);\n\t\t\tconst bytes =\n\t\t\t\tpackData instanceof Uint8Array\n\t\t\t\t\t? packData\n\t\t\t\t\t: new TextEncoder().encode(packData as string);\n\t\t\treturn {\n\t\t\t\tstatus: 200,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/x-git-upload-pack-result\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t\tbody: concat(pktLine(\"NAK\\n\"), sideBandPackfile(bytes)),\n\t\t\t};\n\t\t}\n\t}\n\n\tif (options?.beforeWalk) {\n\t\tawait runStep(hooks, \"beforeWalk\", options.beforeWalk);\n\t}\n\n\tconst { oids: wantOids } = await runStep(\n\t\thooks,\n\t\t\"collectReachableOids(wants)\",\n\t\t() => collectReachableOids(repo, wants, hooks),\n\t);\n\tlet oids = wantOids;\n\n\tif (haves.length > 0) {\n\t\tconst { oids: haveOids } = await runStep(\n\t\t\thooks,\n\t\t\t\"collectReachableOids(haves)\",\n\t\t\t() => collectReachableOids(repo, haves, hooks),\n\t\t);\n\t\tconst haveSet = new Set(haveOids);\n\t\toids = oids.filter((oid) => !haveSet.has(oid));\n\t}\n\n\tconst { packfile } = await runStep(\n\t\thooks,\n\t\t`packObjects (${oids.length} oids)`,\n\t\t() => git.packObjects({ ...repo, oids }),\n\t);\n\n\tconst packBytes =\n\t\tpackfile instanceof Uint8Array ? packfile : new Uint8Array(packfile ?? []);\n\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-git-upload-pack-result\",\n\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t},\n\t\tbody: concat(pktLine(\"NAK\\n\"), sideBandPackfile(packBytes)),\n\t};\n}\n"],"mappings":"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","names":["import_isomorphic_git","git","git","import_isomorphic_git","git","import_isomorphic_git","import_isomorphic_git","git","git","import_isomorphic_git","ZERO_OID","git"]}
1
+ {"version":3,"sources":["../src/http/index.ts","../src/http/info-refs.ts","../src/edge-utils.ts","../src/ops/branch.ts","../src/refs.ts","../src/http/pkt-line.ts","../src/http/types.ts","../src/http/reachability.ts","../src/http/receive-pack.ts","../src/http/repack.ts","../src/http/upload-pack.ts"],"sourcesContent":["export {\n\ttype GitService,\n\thandleInfoRefs,\n\ttype InfoRefsOptions,\n\tlistAllRefs,\n} from \"./info-refs.js\";\nexport {\n\tFLUSH,\n\tparsePktLines,\n\tpktLine,\n\tpktLineBuffer,\n\tsideBandPackfile,\n} from \"./pkt-line.js\";\nexport {\n\tcollectReachableOids,\n\ttype ReachabilityResult,\n} from \"./reachability.js\";\nexport {\n\ttype ApplyReceivePackOptions,\n\tapplyReceivePack,\n\tapplyRefUpdates,\n\tensureRepoInitialized,\n\tindexIncomingPack,\n\tparseReceivePackBody,\n\ttype RefUpdateCommand,\n\ttype RefUpdateResult,\n\treceivePackResponse,\n} from \"./receive-pack.js\";\nexport {\n\tREPACK_PACK_COUNT_THRESHOLD,\n\ttype RepackOptions,\n\trepackRepository,\n} from \"./repack.js\";\nexport {\n\ttype GitHttpResult,\n\ttype HttpHooks,\n\ttype RawFsPromises,\n\trawFs,\n} from \"./types.js\";\nexport { handleUploadPack, type UploadPackOptions } from \"./upload-pack.js\";\n","import git from \"isomorphic-git\";\nimport { concat } from \"../edge-utils.js\";\nimport { resolveLooseRefFast } from \"../ops/branch.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { FLUSH, pktLine } from \"./pkt-line.js\";\nimport { type GitHttpResult, type HttpHooks, runStep } from \"./types.js\";\n\nexport type GitService = \"git-upload-pack\" | \"git-receive-pack\";\n\n/** All refs with resolved oids, plus the symref HEAD should advertise. */\nexport async function listAllRefs(repo: Repo, defaultBranch = \"main\") {\n\t// Fetch branch/tag lists and HEAD's symref target in parallel\n\tconst [branches, tags, headSymref] = await Promise.all([\n\t\tgit.listBranches(repo),\n\t\tgit.listTags(repo),\n\t\t// Wrap with Promise.resolve so a mock/stub returning undefined doesn't crash .then()\n\t\tPromise.resolve(git.currentBranch({ ...repo, fullname: true }))\n\t\t\t.then((cb) => cb ?? `refs/heads/${defaultBranch}`)\n\t\t\t.catch(() => `refs/heads/${defaultBranch}`),\n\t]);\n\n\t// Resolve all branch and tag oids in parallel\n\tconst [branchRefs, tagRefs] = await Promise.all([\n\t\tPromise.all(\n\t\t\tbranches.map(async (branch) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst oid = await resolveLooseRefFast(repo, `refs/heads/${branch}`);\n\t\t\t\t\treturn { name: `refs/heads/${branch}`, oid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}),\n\t\t),\n\t\tPromise.all(\n\t\t\ttags.map(async (tag) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst oid = await resolveLooseRefFast(repo, `refs/tags/${tag}`);\n\t\t\t\t\treturn { name: `refs/tags/${tag}`, oid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}),\n\t\t),\n\t]);\n\n\t// HEAD in a bare repo is almost always a symref to a branch whose oid\n\t// branchRefs already resolved above with one direct GET each — reuse it\n\t// instead of a second, separate resolveRef(\"HEAD\") call. isomorphic-git's\n\t// ref resolution runs its full multi-candidate expansion (refs/%s,\n\t// refs/tags/%s, refs/heads/%s, refs/remotes/%s, refs/remotes/%s/HEAD)\n\t// even when the symref target is already fully qualified, which used to\n\t// cost several guaranteed-404 R2 reads on every clone/fetch. Falls back\n\t// to the slow-but-correct path only when that lookup misses: detached\n\t// HEAD (points at a raw sha, not a symref) or a symref to a branch that\n\t// doesn't exist yet (freshly initialized, still-empty repo).\n\tconst headOid =\n\t\tbranchRefs.find((r) => r?.name === headSymref)?.oid ??\n\t\t(await git.resolveRef({ ...repo, ref: \"HEAD\" }).catch(() => null));\n\n\tconst refs: Array<{ name: string; oid: string }> = [];\n\tif (headOid) refs.push({ name: \"HEAD\", oid: headOid });\n\tfor (const r of branchRefs) if (r) refs.push(r);\n\tfor (const r of tagRefs) if (r) refs.push(r);\n\n\treturn { refs, headSymref };\n}\n\nexport interface InfoRefsOptions {\n\tservice: GitService;\n\tdefaultBranch?: string;\n\t/** Advertised in the agent capability. Default \"git-fs-s3\". */\n\tagent?: string;\n}\n\n/**\n * `GET …/info/refs?service=…` — the ref advertisement. Authentication and\n * authorization are the caller's job before invoking this.\n *\n * Capabilities advertised match what the sibling handlers implement:\n * side-band-64k (honored by handleUploadPack's response framing — clients\n * like isomorphic-git unconditionally expect it), tip/reachable sha1 wants,\n * delete-refs and report-status for pushes.\n */\nexport async function handleInfoRefs(\n\trepo: Repo,\n\toptions: InfoRefsOptions,\n\thooks?: HttpHooks,\n): Promise<GitHttpResult> {\n\tconst { service } = options;\n\tconst agent = options.agent ?? \"git-fs-s3\";\n\tconst { refs, headSymref } = await runStep(hooks, \"listAllRefs\", () =>\n\t\tlistAllRefs(repo, options.defaultBranch ?? \"main\"),\n\t);\n\n\tconst isUpload = service === \"git-upload-pack\";\n\tconst caps = isUpload\n\t\t? `no-progress side-band-64k symref=HEAD:${headSymref} allow-tip-sha1-in-want allow-reachable-sha1-in-want agent=${agent}`\n\t\t: `delete-refs report-status no-done agent=${agent}`;\n\n\tconst parts: Uint8Array[] = [pktLine(`# service=${service}\\n`), FLUSH];\n\n\tif (refs.length === 0) {\n\t\t// Empty repo: git needs this exact sentinel\n\t\tparts.push(\n\t\t\tpktLine(\n\t\t\t\t`0000000000000000000000000000000000000000 capabilities^{}\\0${caps}\\n`,\n\t\t\t),\n\t\t);\n\t} else {\n\t\tlet first = true;\n\t\tfor (const { name, oid } of refs) {\n\t\t\tparts.push(\n\t\t\t\tpktLine(first ? `${oid} ${name}\\0${caps}\\n` : `${oid} ${name}\\n`),\n\t\t\t);\n\t\t\tfirst = false;\n\t\t}\n\t}\n\tparts.push(FLUSH);\n\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": `application/x-${service}-advertisement`,\n\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t},\n\t\tbody: concat(...parts),\n\t};\n}\n","/**\n * Edge-compatible utilities replacing node:crypto, node:zlib, and Buffer.\n *\n * Every function here uses only Web APIs (SubtleCrypto, CompressionStream,\n * TextEncoder/TextDecoder) — no Node built-ins. They work on Cloudflare\n * Workers, Vercel Edge, Deno Deploy, and Node >= 18.\n */\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\n// ---------------------------------------------------------------------------\n// Text\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a UTF-8 string to bytes.\n *\n * Return type pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,\n * whose default type argument differs across TypeScript versions) so it's\n * always assignable to Fetch API `BodyInit` regardless of a consumer's own\n * TypeScript/lib version.\n */\nexport function encodeUtf8(data: string): Uint8Array<ArrayBuffer> {\n\treturn textEncoder.encode(data);\n}\n\n/** Decode bytes as UTF-8. */\nexport function decodeUtf8(data: Uint8Array): string {\n\treturn textDecoder.decode(data);\n}\n\n/** Decode bytes as ASCII. */\nexport function decodeAscii(data: Uint8Array): string {\n\tlet s = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\ts += String.fromCharCode(data[i] as number);\n\treturn s;\n}\n\n// ---------------------------------------------------------------------------\n// Array manipulation\n// ---------------------------------------------------------------------------\n\n/** Concatenate any number of Uint8Arrays into one. */\nexport function concat(...parts: Uint8Array[]): Uint8Array<ArrayBuffer> {\n\tlet total = 0;\n\tfor (const p of parts) total += p.length;\n\tconst out = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const p of parts) {\n\t\tout.set(p, offset);\n\t\toffset += p.length;\n\t}\n\treturn out;\n}\n\n// ---------------------------------------------------------------------------\n// Encoding\n// ---------------------------------------------------------------------------\n\n/** Uint8Array → lowercase hex string. */\nexport function toHex(data: Uint8Array): string {\n\tlet hex = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\thex += (data[i] as number).toString(16).padStart(2, \"0\");\n\treturn hex;\n}\n\n/** Uint8Array → base64 string. */\nexport function toBase64(data: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\tbinary += String.fromCharCode(data[i] as number);\n\treturn btoa(binary);\n}\n\n/** Hex string → Uint8Array. */\nexport function fromHex(hex: string): Uint8Array<ArrayBuffer> {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n\t}\n\treturn bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Crypto\n// ---------------------------------------------------------------------------\n\n/** SHA-1 hash via Web Crypto API. Returns a hex string. */\nexport async function sha1(data: Uint8Array | string): Promise<string> {\n\tconst bytes = typeof data === \"string\" ? encodeUtf8(data) : data;\n\tconst hash = await globalThis.crypto.subtle.digest(\"SHA-1\", bytes);\n\treturn toHex(new Uint8Array(hash));\n}\n\n// ---------------------------------------------------------------------------\n// Compression\n// ---------------------------------------------------------------------------\n\n/**\n * Deflate compress via the CompressionStream Web API.\n * Falls back to throwing if CompressionStream is unavailable (very old runtimes).\n */\nexport async function deflate(\n\tdata: Uint8Array,\n): Promise<Uint8Array<ArrayBuffer>> {\n\tconst stream = new Blob([data])\n\t\t.stream()\n\t\t.pipeThrough(new CompressionStream(\"deflate\"));\n\treturn new Uint8Array(await new Response(stream).arrayBuffer());\n}\n\n// ---------------------------------------------------------------------------\n// Binary detection (replaces Buffer.includes(0) pattern)\n// ---------------------------------------------------------------------------\n\n/** Check if a Uint8Array contains a null byte. */\nexport function hasNullByte(data: Uint8Array): boolean {\n\treturn data.includes(0);\n}\n\n/**\n * Read a blob as text or binary metadata — the edge-compatible replacement\n * for the `Buffer.from(blob)` pattern used throughout diff.ts and history.ts.\n */\nexport function readBlobContent(blob: Uint8Array): {\n\tisBinary: boolean;\n\ttext: string;\n\tbytes: Uint8Array;\n} {\n\tconst isBinary = hasNullByte(blob);\n\treturn {\n\t\tisBinary,\n\t\ttext: isBinary ? \"\" : decodeUtf8(blob),\n\t\tbytes: blob,\n\t};\n}\n","import git from \"isomorphic-git\";\nimport { GitInvalidRequestError } from \"../git-errors.js\";\nimport { isSafeBranchName } from \"../refs.js\";\nimport type { Repo } from \"./types.js\";\n\nexport interface Branch {\n\tname: string;\n\tcommit: string;\n\tisDefault: boolean;\n}\n\n/**\n * Defense in depth for every branch-name argument below: `git.deleteBranch`\n * and raw resolveRef/writeRef reads don't validate ref names internally the\n * way `git.branch` does (see refs.ts) — guard at the point the primitives are\n * actually called, not just at an API boundary far above.\n */\nexport function assertSafeBranchName(name: string): void {\n\tif (!isSafeBranchName(name)) {\n\t\tthrow new GitInvalidRequestError(`Invalid branch name: ${name}`);\n\t}\n}\n\nconst FULL_SHA_RE = /^[0-9a-f]{40}$/i;\n\ntype RecursiveFileLister = {\n\tlistFilesRecursively(dirpath: string): Promise<string[]>;\n};\n\nfunction hasRecursiveFileLister(\n\tfs: Repo[\"fs\"],\n): fs is Repo[\"fs\"] & RecursiveFileLister {\n\treturn (\n\t\ttypeof fs === \"object\" &&\n\t\tfs !== null &&\n\t\t\"listFilesRecursively\" in fs &&\n\t\ttypeof (fs as Partial<RecursiveFileLister>).listFilesRecursively ===\n\t\t\t\"function\"\n\t);\n}\n\n/**\n * Lists loose branch names without isomorphic-git's recursive readdir/stat\n * traversal. Object stores already return only leaf keys in a recursive LIST,\n * so this is one request regardless of branch-name nesting. Ordinary\n * filesystems retain isomorphic-git's implementation and packed refs remain\n * supported there.\n */\nasync function listLooseBranches(repo: Repo): Promise<string[] | null> {\n\tif (!hasRecursiveFileLister(repo.fs)) return null;\n\tconst names = await repo.fs.listFilesRecursively(`${repo.gitdir}/refs/heads`);\n\treturn names.filter(isSafeBranchName);\n}\n\n/** Read packed branch refs so the object-store fast path preserves Git semantics. */\nasync function readPackedBranches(repo: Repo): Promise<Map<string, string>> {\n\tif (!hasRecursiveFileLister(repo.fs)) return new Map();\n\tconst promisesFs = (\n\t\trepo.fs as {\n\t\t\tpromises?: {\n\t\t\t\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n\t\t\t};\n\t\t}\n\t).promises;\n\tif (!promisesFs) return new Map();\n\ttry {\n\t\tconst content = await promisesFs.readFile(\n\t\t\t`${repo.gitdir}/packed-refs`,\n\t\t\t\"utf8\",\n\t\t);\n\t\tconst refs = new Map<string, string>();\n\t\tfor (const line of (typeof content === \"string\"\n\t\t\t? content\n\t\t\t: new TextDecoder().decode(content)\n\t\t).split(\"\\n\")) {\n\t\t\tconst match = /^([0-9a-f]{40}) refs\\/heads\\/(.+)$/i.exec(line);\n\t\t\tif (match?.[1] && match[2] && isSafeBranchName(match[2])) {\n\t\t\t\trefs.set(match[2], match[1]);\n\t\t\t}\n\t\t}\n\t\treturn refs;\n\t} catch {\n\t\treturn new Map();\n\t}\n}\n\n/** Read the symbolic HEAD directly when the object-store fs is available. */\nasync function getCurrentLooseBranch(repo: Repo): Promise<string | null> {\n\tif (!hasRecursiveFileLister(repo.fs)) return null;\n\tconst promisesFs = (\n\t\trepo.fs as {\n\t\t\tpromises?: {\n\t\t\t\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n\t\t\t};\n\t\t}\n\t).promises;\n\tif (!promisesFs) return null;\n\ttry {\n\t\tconst content = await promisesFs.readFile(`${repo.gitdir}/HEAD`, \"utf8\");\n\t\tconst head = (\n\t\t\ttypeof content === \"string\" ? content : new TextDecoder().decode(content)\n\t\t).trim();\n\t\tconst match = /^ref: refs\\/heads\\/(.+)$/.exec(head);\n\t\treturn match?.[1] && isSafeBranchName(match[1]) ? match[1] : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Resolve a fully-qualified ref's oid with one read instead of isomorphic-\n * git's own `resolveRef`, which does a stat *then* a read for a loose ref\n * (two round trips against object storage) — wasteful specifically here,\n * where the caller already knows the ref exists (it came from a directory\n * listing moments earlier). Reads the loose file directly and falls back to\n * `git.resolveRef` only when that doesn't pan out (packed-refs, or anything\n * else the plain-loose-file assumption doesn't cover), so correctness for\n * those cases is unchanged — this only removes the redundant round trip on\n * the common path.\n */\nexport async function resolveLooseRefFast(\n\trepo: Repo,\n\tref: string,\n): Promise<string> {\n\t// Repo.fs is typed as isomorphic-git's own fs union (promise- or\n\t// callback-based) since that's what git.readTree's signature allows, but\n\t// every fs this package actually constructs (git-fs.ts) is promise-based\n\t// — the runtime check just keeps the callback-style branch honest instead\n\t// of crashing on it.\n\tconst promisesFs = (\n\t\trepo.fs as {\n\t\t\tpromises?: {\n\t\t\t\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n\t\t\t};\n\t\t}\n\t).promises;\n\tif (promisesFs) {\n\t\ttry {\n\t\t\tconst content = await promisesFs.readFile(\n\t\t\t\t`${repo.gitdir}/${ref}`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tconst oid = (\n\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t? content\n\t\t\t\t\t: new TextDecoder().decode(content)\n\t\t\t).trim();\n\t\t\tif (FULL_SHA_RE.test(oid)) return oid;\n\t\t} catch {\n\t\t\t// Not a loose file (packed-refs, or genuinely absent) — fall through.\n\t\t}\n\t}\n\treturn git.resolveRef({ ...repo, ref });\n}\n\n/** All branches with their tip commits; [] for an empty repository. */\nexport async function listBranches(repo: Repo): Promise<Branch[]> {\n\ttry {\n\t\tconst [looseBranches, looseCurrentBranch, packedBranches] =\n\t\t\tawait Promise.all([\n\t\t\t\tlistLooseBranches(repo),\n\t\t\t\tgetCurrentLooseBranch(repo),\n\t\t\t\treadPackedBranches(repo),\n\t\t\t]);\n\t\tconst branches =\n\t\t\tlooseBranches === null\n\t\t\t\t? await git.listBranches(repo)\n\t\t\t\t: [...new Set([...looseBranches, ...packedBranches.keys()])].sort();\n\t\tconst currentBranch =\n\t\t\tlooseBranches === null\n\t\t\t\t? await git\n\t\t\t\t\t\t.currentBranch({ ...repo, fullname: false })\n\t\t\t\t\t\t.catch(() => null)\n\t\t\t\t: looseCurrentBranch;\n\n\t\treturn Promise.all(\n\t\t\tbranches.map(async (branch) => {\n\t\t\t\tconst packedCommit = packedBranches.get(branch);\n\t\t\t\treturn {\n\t\t\t\t\tname: branch,\n\t\t\t\t\tcommit:\n\t\t\t\t\t\tpackedCommit && !looseBranches?.includes(branch)\n\t\t\t\t\t\t\t? packedCommit\n\t\t\t\t\t\t\t: await resolveLooseRefFast(repo, `refs/heads/${branch}`),\n\t\t\t\t\tisDefault: branch === currentBranch,\n\t\t\t\t};\n\t\t\t}),\n\t\t);\n\t} catch (err: unknown) {\n\t\tif ((err as { code?: string }).code === \"NotFoundError\") return [];\n\t\tthrow err;\n\t}\n}\n\n/** Create `name` pointing at the tip of `startPoint` (no checkout). */\nexport async function createBranchFrom(\n\trepo: Repo,\n\tname: string,\n\tstartPoint = \"main\",\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tassertSafeBranchName(startPoint);\n\tconst object = await resolveLooseRefFast(repo, `refs/heads/${startPoint}`);\n\tawait git.branch({ ...repo, ref: name, checkout: false, object });\n}\n\n/** Delete a branch ref (validated — deleteBranch has no internal ref check). */\nexport async function deleteBranchByName(\n\trepo: Repo,\n\tname: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tawait git.deleteBranch({ ...repo, ref: name });\n}\n\n/** Throws (NotFoundError) unless the branch resolves. */\nexport async function assertBranchExists(\n\trepo: Repo,\n\tname: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tawait resolveLooseRefFast(repo, `refs/heads/${name}`);\n}\n","/**\n * Git ref-name validation, mirroring isomorphic-git's own internal `isValidRef`\n * character-class rules (the check `git.branch` and top-level `git.writeRef`\n * run before touching disk).\n *\n * Several of isomorphic-git's OTHER ref-touching primitives — `git.commit`,\n * `git.merge`, `git.deleteBranch`, and top-level `git.resolveRef`/\n * `git.deleteRef` — do NOT run this check internally: they resolve straight\n * through `fs.write`/`fs.rm(join(gitdir, ref))` with no jail to the gitdir.\n * On a shared-storage server (many repos under one prefix or base directory),\n * every branch/ref name that originates from request input must be validated\n * against these predicates before it reaches any of those primitives —\n * otherwise a `\"../\"`-laden name lets a caller with write access to any single\n * repo read, corrupt, or delete another repo's ref/object files.\n */\n\nconst BAD_REF_COMPONENT =\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: control chars are exactly what git's own ref-name rules reject — this needs to match the same range.\n\t/(^|[/.])([/.]|$)|^@$|@\\{|[\\x00-\\x20\\x7f~^:?*[\\\\]|\\.lock(\\/|$)/;\n\nconst FULL_SHA_RE = /^[0-9a-f]{40}$/i;\n\n/** Validates a fully-qualified ref (must start with refs/heads/ or refs/tags/). */\nexport function isSafeFullRefName(ref: string): boolean {\n\tif (!ref.startsWith(\"refs/heads/\") && !ref.startsWith(\"refs/tags/\")) {\n\t\treturn false;\n\t}\n\treturn !BAD_REF_COMPONENT.test(ref);\n}\n\n/**\n * Validates a bare branch name (no refs/ prefix). Rejects anything that looks\n * like a full ref path — a name of `\"refs/heads/x\"` would otherwise sail\n * through unprefixed at call sites that build `refs/heads/${name}` themselves\n * (doubling the prefix into something that still resolves), or be used as-is\n * at call sites that pass a name already containing `\"refs/\"` straight\n * through. Also rejects 40-hex SHA-shaped values so a stored branch name can\n * never be ambiguous with a commit SHA at write time; use\n * {@link isSafeRefName} on read paths that accept both shapes.\n */\nexport function isSafeBranchName(name: string): boolean {\n\tif (!name || name.startsWith(\"refs/\") || name === \"HEAD\") return false;\n\tif (FULL_SHA_RE.test(name)) return false;\n\treturn !BAD_REF_COMPONENT.test(name);\n}\n\n/** True for a full 40-hex-char commit SHA — the shape {@link isSafeBranchName} deliberately rejects. */\nexport function isFullSha(value: string): boolean {\n\treturn FULL_SHA_RE.test(value);\n}\n\n/**\n * Validates a \"ref\" field that may name either a branch or a commit SHA it's\n * pinned to — the shape read-path route params take (permalinks, raw links).\n * Both shapes still go through the traversal check.\n */\nexport function isSafeRefName(value: string): boolean {\n\treturn isSafeBranchName(value) || isFullSha(value);\n}\n\n/**\n * Validates a repo-relative file path from request input: relative, no `..`\n * segments, no `.git/` prefix, no null bytes. Use this anywhere a path\n * segment comes straight off a URL or form field rather than re-deriving the\n * checks ad hoc.\n */\nexport function isSafeRepoPath(p: string): boolean {\n\tif (p.startsWith(\"/\")) return false;\n\tif (p.split(\"/\").some((segment) => segment === \"..\")) return false;\n\tif (/^\\.git(\\/|$)/i.test(p)) return false;\n\tif (p.includes(\"\\0\")) return false;\n\treturn true;\n}\n\n/**\n * Qualify a bare branch name to `refs/heads/<name>` before handing it to\n * isomorphic-git. `resolveRef`/`expand` try several candidate paths in\n * sequence for a bare name — `ref`, `refs/ref`, `refs/tags/ref`,\n * `refs/heads/ref`, … — missing (and, against object storage, paying a real\n * round trip for) the first three every time. For a branch-only ref model,\n * skip straight to the winner. Left untouched: already-qualified refs,\n * `\"HEAD\"` (its own first candidate, already optimal), and 40-hex oids\n * (resolved locally by isomorphic-git with no I/O at all).\n */\nexport function qualifyBranchRef(ref: string): string {\n\tif (ref.startsWith(\"refs/\") || ref === \"HEAD\" || FULL_SHA_RE.test(ref)) {\n\t\treturn ref;\n\t}\n\treturn `refs/heads/${ref}`;\n}\n","/**\n * Git pkt-line framing (smart HTTP protocol wire format).\n *\n * Edge-compatible: uses Uint8Array instead of Buffer.\n */\n\nimport { concat, decodeAscii, decodeUtf8, encodeUtf8 } from \"../edge-utils.js\";\n\n/** Flush packet: exactly four zero bytes. */\nexport const FLUSH: Uint8Array<ArrayBuffer> = new Uint8Array([\n\t0x30, 0x30, 0x30, 0x30,\n]); // \"0000\"\n\n/** Frame a UTF-8 string as one pkt-line. */\nexport function pktLine(data: string): Uint8Array<ArrayBuffer> {\n\tconst body = encodeUtf8(data);\n\tconst len = (body.length + 4).toString(16).padStart(4, \"0\");\n\treturn concat(encodeUtf8(len), body);\n}\n\n/** Frame raw bytes as one pkt-line. */\nexport function pktLineBuffer(body: Uint8Array): Uint8Array<ArrayBuffer> {\n\tconst len = (body.length + 4).toString(16).padStart(4, \"0\");\n\treturn concat(encodeUtf8(len), body);\n}\n\n/** Decode pkt-lines to strings; `null` marks a flush-pkt. Stops on garbage. */\nexport function parsePktLines(buf: Uint8Array): Array<string | null> {\n\tconst lines: Array<string | null> = [];\n\tlet pos = 0;\n\twhile (pos + 4 <= buf.length) {\n\t\tconst len = Number.parseInt(decodeAscii(buf.subarray(pos, pos + 4)), 16);\n\t\tif (len === 0) {\n\t\t\tlines.push(null);\n\t\t\tpos += 4;\n\t\t} else if (len >= 4) {\n\t\t\tlines.push(decodeUtf8(buf.subarray(pos + 4, pos + len)));\n\t\t\tpos += len;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn lines;\n}\n\n/**\n * Per the git protocol, once side-band-64k has been negotiated, packfile\n * bytes in the upload-pack response must be chunked into pkt-lines each\n * prefixed with a control byte (0x01 = packfile data), terminated by a\n * flush-pkt. Without this, clients that don't special-case \"no side-band\" —\n * e.g. isomorphic-git's GitSideBand.demux, which always treats the response\n * as side-band-framed — misparse the raw packfile bytes as bogus pkt-line\n * length headers and spin forever. (Native `git` tolerates a raw unframed\n * stream when side-band isn't negotiated, so this only surfaces with\n * isomorphic-git as the HTTP client.)\n */\nconst SIDE_BAND_MAX_CHUNK = 65515;\n\nexport function sideBandPackfile(\n\tpackData: Uint8Array,\n): Uint8Array<ArrayBuffer> {\n\tconst parts: Uint8Array[] = [];\n\tfor (\n\t\tlet offset = 0;\n\t\toffset < packData.length;\n\t\toffset += SIDE_BAND_MAX_CHUNK\n\t) {\n\t\tconst chunk = packData.subarray(offset, offset + SIDE_BAND_MAX_CHUNK);\n\t\tparts.push(pktLineBuffer(concat(new Uint8Array([1]), chunk)));\n\t}\n\tparts.push(FLUSH);\n\treturn concat(...parts);\n}\n","import type { Repo } from \"../ops/types.js\";\n\n/**\n * Transport-agnostic HTTP response the handlers produce.\n *\n * `body` is pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,\n * whose default type argument isn't consistent across TypeScript versions)\n * so it's always assignable to Fetch API `BodyInit` regardless of which\n * TypeScript/lib version a consumer compiles against — every producer here\n * (`concat`, `pktLine`, `TextEncoder.encode`) is ArrayBuffer-backed already.\n */\nexport interface GitHttpResult {\n\tstatus: number;\n\theaders: Record<string, string>;\n\tbody: Uint8Array<ArrayBuffer>;\n}\n\n/** Optional instrumentation hooks shared by the /http handlers. */\nexport interface HttpHooks {\n\t/** Wrap a timed sub-step. Default: run directly. */\n\tstep?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;\n\t/** Non-fatal problem sink (missing objects, failed repacks). */\n\tonWarn?: (message: string, error?: unknown) => void;\n}\n\nexport const runStep = <T>(\n\thooks: HttpHooks | undefined,\n\tlabel: string,\n\tfn: () => Promise<T>,\n): Promise<T> => (hooks?.step ? hooks.step(label, fn) : fn());\n\n/**\n * The raw promise-fs surface some /http paths need beyond isomorphic-git's\n * plumbing (writing an incoming pack file, listing/deleting pack files).\n * Both `node:fs` and this package's `GitFs` satisfy it via `.promises`.\n */\nexport interface RawFsPromises {\n\treadFile(path: string): Promise<Uint8Array | string>;\n\twriteFile(path: string, data: Uint8Array | string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\treaddir(path: string): Promise<string[]>;\n\tmkdir(path: string, options?: { recursive?: boolean }): Promise<unknown>;\n\tstat(path: string): Promise<unknown>;\n}\n\n/** Duck-type the `.promises` surface off a repo's fs. */\nexport function rawFs(repo: Repo): RawFsPromises {\n\tconst fs = repo.fs as { promises?: RawFsPromises };\n\tif (!fs?.promises) {\n\t\tthrow new TypeError(\n\t\t\t\"This operation needs a promise fs (`fs.promises`) — node:fs and createGitFs both provide one\",\n\t\t);\n\t}\n\treturn fs.promises;\n}\n","import git from \"isomorphic-git\";\nimport type { Repo } from \"../ops/types.js\";\nimport type { HttpHooks } from \"./types.js\";\n\nexport interface ReachabilityResult {\n\toids: string[];\n\t/**\n\t * False if any object in the graph couldn't be read — repack uses this\n\t * (not a raw object-count comparison) to decide whether it's safe to\n\t * delete old packs: counts alone can't distinguish \"everything read fine\"\n\t * from \"some objects silently failed\".\n\t */\n\tcomplete: boolean;\n}\n\nconst delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * A read landing in the gap between a repack's new consolidated pack being\n * uploaded and the stale-pack cleanup finishing can transiently miss an\n * object that is not actually lost — it exists in the new pack the whole\n * time; the reader's cached pack listing was just taken mid-transition. One\n * retry after a short delay is enough to observe the consistent listing.\n */\nconst MISSING_OBJECT_RETRY_DELAY_MS = 200;\n\n/**\n * Walk the full object graph from `startOids`, returning every reachable\n * oid. Concurrent traversal paths are deduplicated promise-per-oid; a\n * missing object is retried once, then reported through `hooks.onWarn` and\n * reflected in `complete: false`.\n */\nexport async function collectReachableOids(\n\trepo: Repo,\n\tstartOids: string[],\n\thooks?: HttpHooks,\n): Promise<ReachabilityResult> {\n\tconst seen = new Set<string>();\n\tlet complete = true;\n\tconst promises = new Map<string, Promise<void>>();\n\n\tasync function readAndVisitChildren(oid: string): Promise<void> {\n\t\tconst obj = await git.readObject({ ...repo, oid });\n\t\t// Add to seen only after a successful read so failed reads are excluded\n\t\t// from any pack built from this set.\n\t\tseen.add(oid);\n\t\tlet children: string[] = [];\n\t\tif (obj.type === \"commit\") {\n\t\t\tconst { commit } = await git.readCommit({ ...repo, oid });\n\t\t\tchildren = [commit.tree, ...commit.parent];\n\t\t} else if (obj.type === \"tree\") {\n\t\t\tconst { tree } = await git.readTree({ ...repo, oid });\n\t\t\tchildren = tree.map((e) => e.oid);\n\t\t} else if (obj.type === \"tag\") {\n\t\t\tconst { tag } = await git.readTag({ ...repo, oid });\n\t\t\tchildren = [tag.object];\n\t\t}\n\t\tawait Promise.all(children.map(visit));\n\t}\n\n\tfunction visit(oid: string): Promise<void> {\n\t\tconst existing = promises.get(oid);\n\t\tif (existing) return existing;\n\n\t\tconst p = (async () => {\n\t\t\ttry {\n\t\t\t\tawait readAndVisitChildren(oid);\n\t\t\t} catch {\n\t\t\t\ttry {\n\t\t\t\t\tawait delay(MISSING_OBJECT_RETRY_DELAY_MS);\n\t\t\t\t\tawait readAndVisitChildren(oid);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\thooks?.onWarn?.(`missing object ${oid}`, err);\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\tpromises.set(oid, p);\n\t\treturn p;\n\t}\n\n\tawait Promise.all(startOids.map(visit));\n\treturn { oids: Array.from(seen), complete };\n}\n","import git from \"isomorphic-git\";\nimport { concat, decodeAscii, decodeUtf8 } from \"../edge-utils.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { isSafeFullRefName } from \"../refs.js\";\nimport { FLUSH, pktLine } from \"./pkt-line.js\";\nimport { type RepackOptions, repackRepository } from \"./repack.js\";\nimport { type GitHttpResult, type HttpHooks, rawFs, runStep } from \"./types.js\";\n\nconst ZERO_OID = \"0\".repeat(40);\n\nexport interface RefUpdateCommand {\n\toldOid: string;\n\tnewOid: string;\n\trefName: string;\n}\n\nexport interface RefUpdateResult {\n\trefName: string;\n\tok: boolean;\n\treason?: string;\n}\n\n/** Split a receive-pack body: pkt-line ref commands, flush, then raw PACK. */\nexport function parseReceivePackBody(body: Uint8Array): {\n\trefUpdates: RefUpdateCommand[];\n\tpackData: Uint8Array;\n} {\n\tconst refUpdates: RefUpdateCommand[] = [];\n\tlet pos = 0;\n\twhile (pos + 4 <= body.length) {\n\t\tconst len = Number.parseInt(decodeAscii(body.subarray(pos, pos + 4)), 16);\n\t\tif (len === 0) {\n\t\t\tpos += 4;\n\t\t\tbreak; // flush = end of ref update commands\n\t\t}\n\t\tif (len < 4) break;\n\t\t// Strip NUL-separated capabilities from the first command line\n\t\tconst line = decodeUtf8(body.subarray(pos + 4, pos + len))\n\t\t\t.replace(/\\n$/, \"\")\n\t\t\t.split(\"\\0\")[0];\n\t\tpos += len;\n\t\tconst parts = (line ?? \"\").split(\" \");\n\t\tconst [oldOid, newOid, refName] = parts;\n\t\tif (oldOid && newOid && refName) {\n\t\t\trefUpdates.push({ oldOid, newOid, refName });\n\t\t}\n\t}\n\treturn { refUpdates, packData: body.subarray(pos) };\n}\n\n/** Initialize the repo when its HEAD doesn't exist yet (first push). */\nexport async function ensureRepoInitialized(\n\trepo: Repo,\n\tdefaultBranch = \"main\",\n): Promise<void> {\n\tconst fsp = rawFs(repo);\n\ttry {\n\t\tawait fsp.stat(`${repo.gitdir}/HEAD`);\n\t} catch {\n\t\tawait fsp.mkdir(repo.gitdir, { recursive: true }).catch(() => {});\n\t\tawait git.init({ ...repo, dir: repo.gitdir, defaultBranch, bare: true });\n\t}\n}\n\n/** Write the incoming PACK into objects/pack/ and index it. */\nexport async function indexIncomingPack(\n\trepo: Repo,\n\tpackData: Uint8Array,\n\thooks?: HttpHooks,\n): Promise<void> {\n\tif (packData.length < 4) return;\n\tawait runStep(hooks, \"write + indexPack incoming pack\", async () => {\n\t\tconst fsp = rawFs(repo);\n\t\tconst packDir = `${repo.gitdir}/objects/pack`;\n\t\tawait fsp.mkdir(packDir, { recursive: true });\n\n\t\tconst packName = `recv-${Date.now()}`;\n\t\tawait fsp.writeFile(`${packDir}/${packName}.pack`, packData);\n\n\t\tawait git.indexPack({\n\t\t\t...repo,\n\t\t\tdir: packDir,\n\t\t\tfilepath: `${packName}.pack`,\n\t\t});\n\t});\n}\n\n/**\n * Apply ref updates, enforcing compare-and-swap against each command's\n * claimed oldOid. Every client-supplied refName is validated before any\n * filesystem call reads or writes through it.\n */\nexport async function applyRefUpdates(\n\trepo: Repo,\n\trefUpdates: RefUpdateCommand[],\n\thooks?: HttpHooks,\n): Promise<RefUpdateResult[]> {\n\treturn runStep(hooks, \"apply ref updates\", () =>\n\t\tPromise.all(\n\t\t\trefUpdates.map(async ({ oldOid, newOid, refName }) => {\n\t\t\t\tif (!isSafeFullRefName(refName)) {\n\t\t\t\t\treturn { refName, ok: false, reason: \"invalid ref name\" };\n\t\t\t\t}\n\n\t\t\t\tconst currentOid = await git\n\t\t\t\t\t.resolveRef({ ...repo, ref: refName })\n\t\t\t\t\t.catch(() => ZERO_OID);\n\n\t\t\t\tif (currentOid !== oldOid) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\trefName,\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\treason: \"non-fast-forward, ref updated by another push\",\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif (newOid === ZERO_OID) {\n\t\t\t\t\tawait git.deleteRef({ ...repo, ref: refName }).catch(() => {});\n\t\t\t\t} else {\n\t\t\t\t\tawait git.writeRef({\n\t\t\t\t\t\t...repo,\n\t\t\t\t\t\tref: refName,\n\t\t\t\t\t\tvalue: newOid,\n\t\t\t\t\t\tforce: true,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { refName, ok: true };\n\t\t\t}),\n\t\t),\n\t);\n}\n\n/** The report-status response body for a set of ref-update results. */\nexport function receivePackResponse(results: RefUpdateResult[]): GitHttpResult {\n\tconst responseBody = concat(\n\t\tpktLine(\"unpack ok\\n\"),\n\t\t...results.map(({ refName, ok, reason }) =>\n\t\t\tpktLine(ok ? `ok ${refName}\\n` : `ng ${refName} ${reason}\\n`),\n\t\t),\n\t\tFLUSH,\n\t);\n\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-git-receive-pack-result\",\n\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t},\n\t\tbody: responseBody,\n\t};\n}\n\nexport interface ApplyReceivePackOptions {\n\tdefaultBranch?: string;\n\t/**\n\t * Repack tuning, or `false` to skip repacking entirely. The returned\n\t * `stalePackPaths` are gitdir-relative pack files the repack deleted from\n\t * this repo — after syncing to any secondary storage, delete them there\n\t * too.\n\t */\n\trepack?: RepackOptions | false;\n}\n\n/**\n * The storage half of a push against `repo`: initialize if needed, index the\n * incoming pack, CAS-apply ref updates, then repack when enough packs have\n * accumulated. Serialize concurrent pushes to the same repo externally (a\n * per-repo lock); build the HTTP response afterwards with\n * {@link receivePackResponse}.\n */\nexport async function applyReceivePack(\n\trepo: Repo,\n\tparsed: { refUpdates: RefUpdateCommand[]; packData: Uint8Array },\n\toptions?: ApplyReceivePackOptions,\n\thooks?: HttpHooks,\n): Promise<{ results: RefUpdateResult[]; stalePackPaths: string[] }> {\n\tawait ensureRepoInitialized(repo, options?.defaultBranch ?? \"main\");\n\tawait indexIncomingPack(repo, parsed.packData, hooks);\n\tconst results = await applyRefUpdates(repo, parsed.refUpdates, hooks);\n\tconst repackOptions = options?.repack;\n\tconst stalePackPaths =\n\t\trepackOptions === false\n\t\t\t? []\n\t\t\t: await runStep(hooks, \"repack\", () =>\n\t\t\t\t\trepackRepository(repo, repackOptions, hooks),\n\t\t\t\t);\n\treturn { results, stalePackPaths };\n}\n","/**\n * Pack consolidation: merge all packs into one verified, non-deltified pack.\n *\n * Edge-compatible: uses Web Crypto (SHA-1), CompressionStream (deflate),\n * and Uint8Array throughout — no node:crypto, node:zlib, or Buffer.\n */\n\nimport git from \"isomorphic-git\";\nimport { concat, deflate, encodeUtf8, sha1 } from \"../edge-utils.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { collectReachableOids } from \"./reachability.js\";\nimport { type HttpHooks, rawFs } from \"./types.js\";\n\ntype PackObjectType = \"commit\" | \"tree\" | \"blob\" | \"tag\";\n\n// Git pack object type bits (bits 6-4 of the header's first byte) — same\n// constants real git and isomorphic-git's own (de)serializers use.\nconst PACK_OBJECT_TYPE_BITS: Record<PackObjectType, number> = {\n\tcommit: 0b0010000,\n\ttree: 0b0100000,\n\tblob: 0b0110000,\n\ttag: 0b1000000,\n};\n\n// Git's pack object header: first byte packs (continuation bit | 3-bit type |\n// low 4 bits of length); any remaining length is emitted 7 bits at a time,\n// each with its own continuation bit, little-endian.\nfunction encodePackObjectHeader(\n\ttype: PackObjectType,\n\tlength: number,\n): Uint8Array {\n\tconst bytes: number[] = [];\n\tlet more = length > 0b1111;\n\tbytes.push(\n\t\t(more ? 0b10000000 : 0) | PACK_OBJECT_TYPE_BITS[type] | (length & 0b1111),\n\t);\n\tlength >>>= 4;\n\twhile (more) {\n\t\tmore = length > 0b01111111;\n\t\tbytes.push((more ? 0b10000000 : 0) | (length & 0b01111111));\n\t\tlength >>>= 7;\n\t}\n\treturn new Uint8Array(bytes);\n}\n\n// Git's object hash: sha1(\"<type> <byte length>\\0<content>\") — matches\n// isomorphic-git's internal GitObject.wrap + shasum, computed independently\n// here rather than trusted from isomorphic-git's own read path.\nasync function hashGitObject(\n\ttype: string,\n\tcontent: Uint8Array,\n): Promise<string> {\n\tconst prefix = encodeUtf8(`${type} ${content.length}\\0`);\n\treturn sha1(concat(prefix, content));\n}\n\ntype VerifiedObject = { type: PackObjectType; content: Uint8Array };\n\n/**\n * Read every reachable object and independently re-derive its oid from the\n * bytes isomorphic-git handed back, instead of trusting the oid it was asked\n * for. isomorphic-git's *packed*-object read path never verifies the\n * resolved content's SHA-1 against the requested oid — only the\n * loose-object branch does.\n */\nasync function readAndVerifyObjects(\n\trepo: Repo,\n\toids: string[],\n): Promise<Map<string, VerifiedObject>> {\n\tconst objects = new Map<string, VerifiedObject>();\n\tconst BATCH_SIZE = 100;\n\tfor (let i = 0; i < oids.length; i += BATCH_SIZE) {\n\t\tconst batch = oids.slice(i, i + BATCH_SIZE);\n\t\tconst entries = await Promise.all(\n\t\t\tbatch.map(async (oid) => {\n\t\t\t\tconst { type, object } = await git.readObject({\n\t\t\t\t\t...repo,\n\t\t\t\t\toid,\n\t\t\t\t\tformat: \"content\",\n\t\t\t\t});\n\t\t\t\tconst content = object as unknown as Uint8Array;\n\t\t\t\tconst actualOid = await hashGitObject(type, content);\n\t\t\t\tif (actualOid !== oid) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`repack: object ${oid} failed independent SHA-1 verification ` +\n\t\t\t\t\t\t\t`(recomputed ${actualOid}) — refusing to trust this read, aborting repack`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn { oid, type: type as PackObjectType, content };\n\t\t\t}),\n\t\t);\n\t\tfor (const { oid, type, content } of entries) {\n\t\t\tobjects.set(oid, { type, content });\n\t\t}\n\t}\n\treturn objects;\n}\n\n/**\n * Serialize verified objects into a pack containing only full (never\n * deltified) entries, in the given oid order.\n */\nasync function buildVerifiedPack(\n\toids: string[],\n\tobjects: Map<string, VerifiedObject>,\n): Promise<Uint8Array> {\n\t// PACK header: \"PACK\" + version(2) + count, all big-endian uint32\n\tconst header = new Uint8Array(12);\n\tconst view = new DataView(header.buffer);\n\theader.set(encodeUtf8(\"PACK\"), 0);\n\tview.setUint32(4, 2, false); // version 2\n\tview.setUint32(8, oids.length, false); // object count\n\n\tconst chunks: Uint8Array[] = [header];\n\n\tconst BATCH_SIZE = 100;\n\tfor (let i = 0; i < oids.length; i += BATCH_SIZE) {\n\t\tconst batch = oids.slice(i, i + BATCH_SIZE);\n\t\tconst encoded = await Promise.all(\n\t\t\tbatch.map(async (oid) => {\n\t\t\t\tconst entry = objects.get(oid);\n\t\t\t\tif (!entry) {\n\t\t\t\t\tthrow new Error(`repack: missing verified object for ${oid}`);\n\t\t\t\t}\n\t\t\t\tconst objHeader = encodePackObjectHeader(\n\t\t\t\t\tentry.type,\n\t\t\t\t\tentry.content.length,\n\t\t\t\t);\n\t\t\t\tconst compressed = await deflate(entry.content);\n\t\t\t\treturn concat(objHeader, compressed);\n\t\t\t}),\n\t\t);\n\t\tchunks.push(...encoded);\n\t}\n\n\t// Git's pack trailer is the SHA-1 of every preceding byte (header +\n\t// every object entry) as a single digest — NOT an incremental/chained\n\t// hash. Web Crypto's subtle.digest has no streaming mode, so unlike\n\t// Node's crypto.createHash this can't be folded into the loop above;\n\t// everything is already buffered in `chunks`, so hash it in one pass.\n\tconst packBody = concat(...chunks);\n\tconst trailer = fromHex(await sha1(packBody));\n\treturn concat(packBody, trailer);\n}\n\n/** Hex string → Uint8Array. */\nfunction fromHex(hex: string): Uint8Array {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n\t}\n\treturn bytes;\n}\n\n/**\n * Default repack threshold. Consolidating is O(total repo object count) —\n * so paying it on every push makes push latency grow with repo size forever.\n */\nexport const REPACK_PACK_COUNT_THRESHOLD = 4;\n\nasync function countPacks(repo: Repo): Promise<number> {\n\ttry {\n\t\tconst entries = await rawFs(repo).readdir(`${repo.gitdir}/objects/pack`);\n\t\treturn entries.filter((f) => f.endsWith(\".pack\")).length;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\nexport interface RepackOptions {\n\t/** Skip repacking below this many accumulated packs. Default 4. */\n\tthreshold?: number;\n}\n\n/**\n * Consolidate all pack files into one, returning the gitdir-relative paths\n * of the old .pack/.idx files it removed — after syncing the new pack to any\n * secondary storage, delete those same paths there too.\n */\nexport async function repackRepository(\n\trepo: Repo,\n\toptions?: RepackOptions,\n\thooks?: HttpHooks,\n): Promise<string[]> {\n\tconst threshold = options?.threshold ?? REPACK_PACK_COUNT_THRESHOLD;\n\tconst fsp = rawFs(repo);\n\ttry {\n\t\tif ((await countPacks(repo)) < threshold) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst [branches, tags] = await Promise.all([\n\t\t\tgit.listBranches(repo),\n\t\t\tgit.listTags(repo),\n\t\t]);\n\t\tconst refNames = [\n\t\t\t...branches.map((b) => `refs/heads/${b}`),\n\t\t\t...tags.map((t) => `refs/tags/${t}`),\n\t\t];\n\t\tconst tipOids = (\n\t\t\tawait Promise.all(\n\t\t\t\trefNames.map((ref) =>\n\t\t\t\t\tgit.resolveRef({ ...repo, ref }).catch(() => null),\n\t\t\t\t),\n\t\t\t)\n\t\t).filter((oid): oid is string => oid !== null);\n\t\tif (tipOids.length === 0) return [];\n\n\t\tconst { oids, complete } = await collectReachableOids(repo, tipOids, hooks);\n\t\tif (!complete || oids.length === 0) return [];\n\n\t\tconst objects = await readAndVerifyObjects(repo, oids);\n\t\tconst packBuffer = await buildVerifiedPack(oids, objects);\n\n\t\tconst packDir = `${repo.gitdir}/objects/pack`;\n\t\tawait fsp.mkdir(packDir, { recursive: true });\n\t\tconst newBase = `pack-${Date.now()}`;\n\t\tconst newPackFile = `${newBase}.pack`;\n\t\tconst newIdxFile = `${newBase}.idx`;\n\t\tawait fsp.writeFile(`${packDir}/${newPackFile}`, packBuffer);\n\n\t\tconst { oids: indexedOids } = await git.indexPack({\n\t\t\t...repo,\n\t\t\tdir: packDir,\n\t\t\tfilepath: newPackFile,\n\t\t});\n\n\t\tconst expected = new Set(oids);\n\t\tconst indexed = new Set(indexedOids);\n\t\tif (\n\t\t\tindexed.size !== expected.size ||\n\t\t\toids.some((oid) => !indexed.has(oid))\n\t\t) {\n\t\t\tawait fsp.unlink(`${packDir}/${newPackFile}`).catch(() => {});\n\t\t\tawait fsp.unlink(`${packDir}/${newIdxFile}`).catch(() => {});\n\t\t\tthrow new Error(\n\t\t\t\t\"repack: indexed pack's oid set didn't match the verified reachable set — aborting\",\n\t\t\t);\n\t\t}\n\n\t\tconst allEntries: string[] = await fsp.readdir(packDir).catch(() => []);\n\n\t\tconst staleFiles = allEntries.filter(\n\t\t\t(f) =>\n\t\t\t\tf !== newPackFile &&\n\t\t\t\tf !== newIdxFile &&\n\t\t\t\t(f.endsWith(\".pack\") || f.endsWith(\".idx\") || f.endsWith(\".keep\")),\n\t\t);\n\n\t\tawait Promise.all(\n\t\t\tstaleFiles.map((f) => fsp.unlink(`${packDir}/${f}`).catch(() => {})),\n\t\t);\n\n\t\treturn staleFiles.map((f) => `objects/pack/${f}`);\n\t} catch (err) {\n\t\thooks?.onWarn?.(\"repack failed (non-fatal)\", err);\n\t\treturn [];\n\t}\n}\n","import git from \"isomorphic-git\";\nimport { concat } from \"../edge-utils.js\";\nimport type { Repo } from \"../ops/types.js\";\nimport { parsePktLines, pktLine, sideBandPackfile } from \"./pkt-line.js\";\nimport { collectReachableOids } from \"./reachability.js\";\nimport { type GitHttpResult, type HttpHooks, rawFs, runStep } from \"./types.js\";\n\nconst ZERO_OID = \"0\".repeat(40);\n\nexport interface UploadPackOptions {\n\t/**\n\t * Called once before the reachability walk — wire loose-object detection\n\t * (`GitFs.detectLooseObjects`) here so a fully packed repo's walk doesn't\n\t * pay a doomed loose-object probe per object.\n\t */\n\tbeforeWalk?: () => Promise<void>;\n}\n\n/**\n * `POST …/git-upload-pack` — serve a clone/fetch. Authorization is the\n * caller's job. Negotiation is client-driven (no multi_ack advertised):\n * \"have\" batches without \"done\" get a bare NAK; the final batch gets the\n * packfile, side-band-64k framed.\n *\n * Fast path: a fresh clone (no haves) of a repo consolidated down to a\n * single pack serves that pack's bytes directly — skipping the O(objects)\n * traversal + repack entirely.\n */\nexport async function handleUploadPack(\n\trepo: Repo,\n\tbody: Uint8Array,\n\toptions?: UploadPackOptions,\n\thooks?: HttpHooks,\n): Promise<GitHttpResult> {\n\tconst lines = parsePktLines(body);\n\n\tconst wants: string[] = [];\n\tconst haves: string[] = [];\n\tlet done = false;\n\tfor (const line of lines) {\n\t\tif (!line) continue;\n\t\tif (line.startsWith(\"want \")) {\n\t\t\twants.push(line.slice(5, 45));\n\t\t}\n\t\tif (line.startsWith(\"have \")) {\n\t\t\tconst sha = line.slice(5, 45);\n\t\t\tif (sha !== ZERO_OID) {\n\t\t\t\thaves.push(sha);\n\t\t\t}\n\t\t}\n\t\tif (line.startsWith(\"done\")) {\n\t\t\tdone = true;\n\t\t}\n\t}\n\n\tif (wants.length === 0) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\theaders: { \"Content-Type\": \"application/x-git-upload-pack-result\" },\n\t\t\tbody: concat(pktLine(\"NAK\\n\")),\n\t\t};\n\t}\n\n\tif (haves.length > 0 && !done) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\theaders: { \"Content-Type\": \"application/x-git-upload-pack-result\" },\n\t\t\tbody: concat(pktLine(\"NAK\\n\")),\n\t\t};\n\t}\n\n\t// Fresh clone = no haves, so all objects are needed. When the repo is down\n\t// to a single pack, serve it directly and skip the traversal + pack build.\n\tif (haves.length === 0) {\n\t\tconst packDirPath = `${repo.gitdir}/objects/pack`;\n\t\tconst entries = await runStep(hooks, \"readdir objects/pack\", () =>\n\t\t\trawFs(repo)\n\t\t\t\t.readdir(packDirPath)\n\t\t\t\t.catch(() => [] as string[]),\n\t\t);\n\t\tconst packNames = entries.filter((f) => f.endsWith(\".pack\"));\n\t\tconst packName = packNames[0];\n\t\tif (packNames.length === 1 && packName !== undefined) {\n\t\t\tconst packData = await runStep(\n\t\t\t\thooks,\n\t\t\t\t\"read consolidated pack (fast path)\",\n\t\t\t\t() => rawFs(repo).readFile(`${packDirPath}/${packName}`),\n\t\t\t);\n\t\t\tconst bytes =\n\t\t\t\tpackData instanceof Uint8Array\n\t\t\t\t\t? packData\n\t\t\t\t\t: new TextEncoder().encode(packData as string);\n\t\t\treturn {\n\t\t\t\tstatus: 200,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/x-git-upload-pack-result\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t\tbody: concat(pktLine(\"NAK\\n\"), sideBandPackfile(bytes)),\n\t\t\t};\n\t\t}\n\t}\n\n\tif (options?.beforeWalk) {\n\t\tawait runStep(hooks, \"beforeWalk\", options.beforeWalk);\n\t}\n\n\tconst { oids: wantOids } = await runStep(\n\t\thooks,\n\t\t\"collectReachableOids(wants)\",\n\t\t() => collectReachableOids(repo, wants, hooks),\n\t);\n\tlet oids = wantOids;\n\n\tif (haves.length > 0) {\n\t\tconst { oids: haveOids } = await runStep(\n\t\t\thooks,\n\t\t\t\"collectReachableOids(haves)\",\n\t\t\t() => collectReachableOids(repo, haves, hooks),\n\t\t);\n\t\tconst haveSet = new Set(haveOids);\n\t\toids = oids.filter((oid) => !haveSet.has(oid));\n\t}\n\n\tconst { packfile } = await runStep(\n\t\thooks,\n\t\t`packObjects (${oids.length} oids)`,\n\t\t() => git.packObjects({ ...repo, oids }),\n\t);\n\n\tconst packBytes =\n\t\tpackfile instanceof Uint8Array ? packfile : new Uint8Array(packfile ?? []);\n\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-git-upload-pack-result\",\n\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t},\n\t\tbody: concat(pktLine(\"NAK\\n\"), sideBandPackfile(packBytes)),\n\t};\n}\n"],"mappings":"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","names":["import_isomorphic_git","git","git","import_isomorphic_git","git","import_isomorphic_git","import_isomorphic_git","git","git","import_isomorphic_git","ZERO_OID","git"]}
package/dist/http.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveLooseRefFast
3
- } from "./chunk-2BMKLGNT.js";
3
+ } from "./chunk-H7IGQRIA.js";
4
4
  import {
5
5
  concat,
6
6
  decodeAscii,
package/dist/index.cjs CHANGED
@@ -575,6 +575,12 @@ function createGitFs(store, options = {}) {
575
575
  )
576
576
  ]);
577
577
  }
578
+ async function listFilesRecursively(dirpath) {
579
+ const directory = toKey2(normalizePath(dirpath));
580
+ const listPrefix = directory === "" ? "" : `${directory}/`;
581
+ const { objects } = await store.list(listPrefix);
582
+ return objects.map((object) => object.key.slice(listPrefix.length)).filter((path) => path.length > 0).sort();
583
+ }
578
584
  function invalidate(pathPrefix) {
579
585
  const normalized = normalizePath(pathPrefix);
580
586
  for (const scope of looseHints.keys()) {
@@ -583,7 +589,13 @@ function createGitFs(store, options = {}) {
583
589
  const maybe = store;
584
590
  maybe.invalidate?.(toKey2(normalized));
585
591
  }
586
- return { promises, detectLooseObjects, prefetchPacks, invalidate };
592
+ return {
593
+ promises,
594
+ listFilesRecursively,
595
+ detectLooseObjects,
596
+ prefetchPacks,
597
+ invalidate
598
+ };
587
599
  }
588
600
 
589
601
  // src/refs.ts