git-fs-s3 0.15.3 → 0.16.0

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.
package/dist/http.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { B as fromHex, F as concat, G as sha1, I as decodeAscii, L as decodeUtf8, N as isAbsentError, P as isNotFoundError, f as readEntryHeader, h as runStep, l as readPackIndex, m as rawFs, p as readOfsDeltaOffset, q as toHex, r as isSafeFullRefName, u as encodeEntryHeader, v as packFileName, y as parsePackDirectory, z as encodeUtf8 } from "./refs-Cb_NfKWI.js";
2
- import { a as storePack, d as listLooseRefNames, i as packBaseName, n as encodePack, p as resolveLooseRefFast, r as hashGitObject, t as createSemaphore, u as listBranches } from "./semaphore-BviNoQuJ.js";
2
+ import { a as storePack, d as listLooseRefNames, i as packBaseName, n as encodePack, p as resolveLooseRefFast, r as hashGitObject, t as createSemaphore, u as listBranches } from "./semaphore-CPjn7Iqh.js";
3
3
  import git from "isomorphic-git";
4
4
  //#region src/http/pkt-line.ts
5
5
  /**
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ function createCachedStore(store, options = {}) {
35
35
  const cacheMisses = options.cacheMisses ?? false;
36
36
  const cacheLists = options.cacheLists ?? false;
37
37
  const range = store.getRange?.bind(store);
38
+ const maxRangeBytes = options.maxRangeBytes ?? 65536;
38
39
  const coalesce = options.coalesce ?? true;
39
40
  const copy = options.copy ?? true;
40
41
  const clone = (data) => copy ? data.slice() : data;
@@ -50,6 +51,33 @@ function createCachedStore(store, options = {}) {
50
51
  sizeCalculation: listEntrySize,
51
52
  ttl
52
53
  });
54
+ const rangeKeysByObject = /* @__PURE__ */ new Map();
55
+ const rangeCache = new LRUCache({
56
+ maxSize: Math.max(1, Math.ceil(maxBytes / 4)),
57
+ sizeCalculation: (value) => value.byteLength || 1,
58
+ ttl,
59
+ dispose: (_value, rangeKey) => {
60
+ const key = rangeKey.slice(0, rangeKey.indexOf("\0"));
61
+ const keys = rangeKeysByObject.get(key);
62
+ if (keys?.delete(rangeKey) && keys.size === 0) rangeKeysByObject.delete(key);
63
+ }
64
+ });
65
+ const pendingRanges = /* @__PURE__ */ new Map();
66
+ const rangePrefix = (key) => `${key}\0`;
67
+ const admitRange = (key, rangeKey, data) => {
68
+ rangeCache.set(rangeKey, clone(data), { ttl: ttlForKey?.(key) });
69
+ if (!rangeCache.has(rangeKey)) return;
70
+ const keys = rangeKeysByObject.get(key) ?? /* @__PURE__ */ new Set();
71
+ keys.add(rangeKey);
72
+ rangeKeysByObject.set(key, keys);
73
+ };
74
+ const pendingRangeObjects = () => [...pendingRanges.keys()].map((k) => k.slice(0, k.indexOf("\0")));
75
+ /** Drop every slice (cached or in flight) of the object at `key`. */
76
+ const dropRanges = (key) => {
77
+ for (const rangeKey of [...rangeKeysByObject.get(key) ?? []]) rangeCache.delete(rangeKey);
78
+ const prefix = rangePrefix(key);
79
+ for (const rangeKey of [...pendingRanges.keys()]) if (rangeKey.startsWith(prefix)) pendingRanges.delete(rangeKey);
80
+ };
53
81
  const pendingGets = /* @__PURE__ */ new Map();
54
82
  const pendingHeads = /* @__PURE__ */ new Map();
55
83
  const pendingLists = /* @__PURE__ */ new Map();
@@ -83,6 +111,7 @@ function createCachedStore(store, options = {}) {
83
111
  generation++;
84
112
  pendingGets.delete(key);
85
113
  pendingHeads.delete(key);
114
+ dropRanges(key);
86
115
  }
87
116
  function coalesced(pending, key, fn) {
88
117
  if (!coalesce) return fn();
@@ -172,9 +201,26 @@ function createCachedStore(store, options = {}) {
172
201
  return fetched;
173
202
  }));
174
203
  },
175
- ...range ? { getRange: (key, start, end) => range(key, start, end) } : {},
204
+ ...range ? { async getRange(key, start, end) {
205
+ if (end - start + 1 > maxRangeBytes) return range(key, start, end);
206
+ const rangeKey = `${rangePrefix(key)}${start}\0${end}`;
207
+ const cached = rangeCache.get(rangeKey);
208
+ if (cached !== void 0) {
209
+ onHit?.(key);
210
+ return clone(cached);
211
+ }
212
+ const data = await coalesced(pendingRanges, rangeKey, async () => {
213
+ onMiss?.(key);
214
+ const gen = generation;
215
+ const fetched = await range(key, start, end);
216
+ if (fetched !== null && unchangedSince(gen)) admitRange(key, rangeKey, fetched);
217
+ return fetched;
218
+ });
219
+ return data === null ? null : clone(data);
220
+ } } : {},
176
221
  invalidate(prefix) {
177
222
  generation++;
223
+ for (const key of [...rangeKeysByObject.keys(), ...pendingRangeObjects()]) if (key.startsWith(prefix)) dropRanges(key);
178
224
  for (const key of [...pendingGets.keys()]) if (key.startsWith(prefix)) pendingGets.delete(key);
179
225
  for (const key of [...pendingHeads.keys()]) if (key.startsWith(prefix)) pendingHeads.delete(key);
180
226
  pendingLists.clear();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["toKey","toKeyWithPrefix"],"sources":["../src/cache.ts","../src/errors.ts","../src/path.ts","../src/git-fs.ts","../src/prefix.ts","../src/retry.ts"],"sourcesContent":["import { LRUCache } from \"lru-cache\";\nimport type {\n\tListOptions,\n\tListResult,\n\tObjectStat,\n\tObjectStore,\n} from \"./types.js\";\n\nexport interface CacheOptions {\n\t/** Maximum bytes of object data held in memory. Default 50 MiB. */\n\tmaxBytes?: number;\n\t/**\n\t * Largest single entry admitted to the cache. Defaults to a tenth of\n\t * `maxBytes` so one huge packfile cannot evict the whole working set.\n\t */\n\tmaxEntryBytes?: number;\n\t/** Entry time-to-live in milliseconds. Default 60 000. */\n\tttlMs?: number;\n\t/**\n\t * Override the TTL for a specific key (get/head) or list prefix (list),\n\t * in milliseconds. Return `undefined` to fall back to `ttlMs`. Git refs\n\t * (`refs/heads/<branch>`, `HEAD`) are mutable — the same key's value\n\t * changes on every push — unlike content-addressed object keys, which\n\t * never change for a given key and are safe to cache for the full\n\t * `ttlMs`. Without this, a long `ttlMs` tuned for objects also caches\n\t * ref reads that long, so a warm process can keep serving a\n\t * pre-push ref value for the rest of that TTL even though nothing\n\t * changed *this* process's own cache (see `invalidate`) — it just never\n\t * knew to. Give ref-like keys a short override (a few seconds) instead:\n\t * a ref read is one small object, so re-reading it far more often than\n\t * `ttlMs` is cheap, and every read downstream of a fresh ref (tree,\n\t * commit, blob — all keyed by the sha it resolves to) still gets the\n\t * full-length cache/coalescing benefit.\n\t */\n\tttlForKey?: (key: string) => number | undefined;\n\t/**\n\t * Also cache \"key does not exist\" results. Loose-object probes on packed\n\t * repositories are almost always misses, so this saves many round trips —\n\t * but only enable it when a single process is the only writer, otherwise\n\t * another instance's push can be masked for up to `ttlMs`.\n\t */\n\tcacheMisses?: boolean;\n\t/**\n\t * Also cache `list()` results (directory listings and `limit: 1`\n\t * existence probes). Writes through this store keep cached listings\n\t * consistent; after writing to the backend by any other means, call\n\t * `invalidate()` with the affected prefix. Default false.\n\t */\n\tcacheLists?: boolean;\n\t/**\n\t * Collapse concurrent `get`/`head`/`list` calls for the same key into a\n\t * single backend request. Default true.\n\t */\n\tcoalesce?: boolean;\n\t/**\n\t * Return defensive copies from `get` (and copy on admit), so a caller\n\t * mutating returned bytes can't corrupt the cache. Default true. Set\n\t * false when no reader mutates results (isomorphic-git doesn't): every\n\t * hit otherwise copies the whole entry, which for packfiles is megabytes\n\t * per read.\n\t */\n\tcopy?: boolean;\n\t/** Called when a read is answered from cache. */\n\tonHit?: (key: string) => void;\n\t/** Called when a read has to go to the backing store. */\n\tonMiss?: (key: string) => void;\n}\n\n/** An {@link ObjectStore} wrapper that also supports explicit invalidation. */\nexport interface CachedObjectStore extends ObjectStore {\n\t/**\n\t * Drop every cached entry — contents, misses, and listings — whose key\n\t * falls under `prefix` (exact keys included). Call this after the backing\n\t * store was modified by something other than this wrapper.\n\t */\n\tinvalidate(prefix: string): void;\n}\n\nconst MISS = Symbol(\"miss\");\ntype CacheEntry = Uint8Array | typeof MISS;\n\ninterface ListEntry {\n\tresult: ListResult;\n\t/** The raw list prefix this entry describes. */\n\tprefix: string;\n\t/** True for `limit: 1` existence probes. */\n\tprobe: boolean;\n\t/** True when the listing came back with no objects or prefixes. */\n\tempty: boolean;\n}\n\nfunction listEntrySize(entry: ListEntry): number {\n\tlet size = entry.prefix.length + 16;\n\tfor (const o of entry.result.objects) size += o.key.length + 8;\n\tfor (const p of entry.result.prefixes) size += p.length;\n\treturn size;\n}\n\nfunction copyListResult(result: ListResult): ListResult {\n\treturn {\n\t\tobjects: result.objects.map((o) => ({ ...o })),\n\t\tprefixes: [...result.prefixes],\n\t};\n}\n\n/**\n * Wrap an {@link ObjectStore} with an in-process LRU read cache.\n *\n * Git object keys are content-addressed and therefore immutable, which makes\n * them ideal cache entries; mutable keys (refs, packed-refs) are bounded by\n * `ttlMs`. Writes and deletes through this wrapper invalidate their key and\n * any cached listings they affect — with one asymmetry: a non-empty `limit: 1`\n * probe (a \"directory exists\" answer) survives writes underneath it, because\n * adding a key below a prefix cannot make that prefix stop existing, while\n * empty probes and full listings are always dropped.\n */\nexport function createCachedStore(\n\tstore: ObjectStore,\n\toptions: CacheOptions = {},\n): CachedObjectStore {\n\tconst maxBytes = options.maxBytes ?? 50 * 1024 * 1024;\n\tconst maxEntryBytes = options.maxEntryBytes ?? Math.ceil(maxBytes / 10);\n\tconst ttl = options.ttlMs ?? 60_000;\n\tconst ttlForKey = options.ttlForKey;\n\tconst cacheMisses = options.cacheMisses ?? false;\n\tconst cacheLists = options.cacheLists ?? false;\n\tconst range = store.getRange?.bind(store);\n\tconst coalesce = options.coalesce ?? true;\n\tconst copy = options.copy ?? true;\n\tconst clone = (data: Uint8Array) => (copy ? data.slice() : data);\n\tconst onHit = options.onHit;\n\tconst onMiss = options.onMiss;\n\n\tconst cache = new LRUCache<string, CacheEntry>({\n\t\tmaxSize: maxBytes,\n\t\tsizeCalculation: (value) => (value === MISS ? 1 : value.byteLength || 1),\n\t\tttl,\n\t});\n\tconst listCache = new LRUCache<string, ListEntry>({\n\t\tmaxSize: Math.max(1, Math.ceil(maxBytes / 10)),\n\t\tsizeCalculation: listEntrySize,\n\t\tttl,\n\t});\n\n\tconst pendingGets = new Map<string, Promise<Uint8Array | null>>();\n\tconst pendingHeads = new Map<string, Promise<ObjectStat | null>>();\n\tconst pendingLists = new Map<string, Promise<ListResult>>();\n\n\t/**\n\t * Bumped by every write, delete and invalidation. A backend read that\n\t * started before one of those may finish after it carrying the old answer\n\t * (the old bytes, or \"missing\" for a key that now exists); it still\n\t * answers its own callers, but only a read that saw no write in between\n\t * may populate the cache — otherwise a stale miss could hide a freshly\n\t * written object for the whole TTL.\n\t */\n\tlet generation = 0;\n\tconst unchangedSince = (start: number) => generation === start;\n\n\tconst admit = (key: string, data: Uint8Array) => {\n\t\tif (data.byteLength <= maxEntryBytes) {\n\t\t\tcache.set(key, clone(data), { ttl: ttlForKey?.(key) });\n\t\t}\n\t};\n\n\t/** Drop list entries a write/delete at `key` may have made stale. */\n\tfunction clearStaleListEntries(key: string): void {\n\t\tfor (const [listKey, entry] of listCache.entries()) {\n\t\t\tif (!key.startsWith(entry.prefix)) continue;\n\t\t\tif (entry.probe && !entry.empty) continue;\n\t\t\tlistCache.delete(listKey);\n\t\t}\n\t\t// Readers arriving after this write must not join a listing that\n\t\t// started before it.\n\t\tfor (const listKey of pendingLists.keys()) {\n\t\t\tconst prefix = listKey.split(\"|\").slice(2).join(\"|\");\n\t\t\tif (key.startsWith(prefix)) pendingLists.delete(listKey);\n\t\t}\n\t}\n\n\t/** A write to `key` happened: no in-flight read of it may be joined or cached. */\n\tfunction noteWrite(key: string): void {\n\t\tgeneration++;\n\t\tpendingGets.delete(key);\n\t\tpendingHeads.delete(key);\n\t}\n\n\tfunction coalesced<T>(\n\t\tpending: Map<string, Promise<T>>,\n\t\tkey: string,\n\t\tfn: () => Promise<T>,\n\t): Promise<T> {\n\t\tif (!coalesce) return fn();\n\t\tconst inflight = pending.get(key);\n\t\tif (inflight !== undefined) return inflight;\n\t\tconst p = fn().finally(() => {\n\t\t\tif (pending.get(key) === p) pending.delete(key);\n\t\t});\n\t\tpending.set(key, p);\n\t\treturn p;\n\t}\n\n\treturn {\n\t\tasync get(key: string): Promise<Uint8Array | null> {\n\t\t\tconst cached = cache.get(key);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tonHit?.(key);\n\t\t\t\treturn cached === MISS ? null : clone(cached);\n\t\t\t}\n\t\t\tconst data = await coalesced(pendingGets, key, async () => {\n\t\t\t\tonMiss?.(key);\n\t\t\t\tconst start = generation;\n\t\t\t\tconst fetched = await store.get(key);\n\t\t\t\tif (unchangedSince(start)) {\n\t\t\t\t\tif (fetched !== null) {\n\t\t\t\t\t\tadmit(key, fetched);\n\t\t\t\t\t} else if (cacheMisses) {\n\t\t\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn fetched;\n\t\t\t});\n\t\t\treturn data === null ? null : clone(data);\n\t\t},\n\n\t\tasync put(key: string, data: Uint8Array): Promise<void> {\n\t\t\tnoteWrite(key);\n\t\t\ttry {\n\t\t\t\tawait store.put(key, data);\n\t\t\t} finally {\n\t\t\t\t// Even a failed put may have landed; never keep serving the\n\t\t\t\t// pre-write answer for this key.\n\t\t\t\tnoteWrite(key);\n\t\t\t\tcache.delete(key);\n\t\t\t\tclearStaleListEntries(key);\n\t\t\t}\n\t\t\tadmit(key, data);\n\t\t},\n\n\t\tasync delete(key: string): Promise<void> {\n\t\t\tnoteWrite(key);\n\t\t\ttry {\n\t\t\t\tawait store.delete(key);\n\t\t\t} finally {\n\t\t\t\tnoteWrite(key);\n\t\t\t\tcache.delete(key);\n\t\t\t\tclearStaleListEntries(key);\n\t\t\t}\n\t\t\tif (cacheMisses) {\n\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\n\t\t\t} else {\n\t\t\t\tcache.delete(key);\n\t\t\t}\n\t\t},\n\n\t\tasync head(key: string): Promise<ObjectStat | null> {\n\t\t\tconst cached = cache.get(key);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tonHit?.(key);\n\t\t\t\treturn cached === MISS ? null : { size: cached.byteLength };\n\t\t\t}\n\t\t\treturn coalesced(pendingHeads, key, async () => {\n\t\t\t\tonMiss?.(key);\n\t\t\t\tconst start = generation;\n\t\t\t\tconst stat = await store.head(key);\n\t\t\t\tif (stat === null && cacheMisses && unchangedSince(start)) {\n\t\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\n\t\t\t\t}\n\t\t\t\treturn stat;\n\t\t\t});\n\t\t},\n\n\t\tasync list(prefix: string, listOptions?: ListOptions): Promise<ListResult> {\n\t\t\tif (!cacheLists) return store.list(prefix, listOptions);\n\t\t\tconst listKey = `${listOptions?.delimiter ?? \"\"}|${listOptions?.limit ?? \"\"}|${prefix}`;\n\t\t\tconst cached = listCache.get(listKey);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tonHit?.(prefix);\n\t\t\t\treturn copyListResult(cached.result);\n\t\t\t}\n\t\t\tconst result = await coalesced(pendingLists, listKey, async () => {\n\t\t\t\tonMiss?.(prefix);\n\t\t\t\tconst start = generation;\n\t\t\t\tconst fetched = await store.list(prefix, listOptions);\n\t\t\t\tif (!unchangedSince(start)) return fetched;\n\t\t\t\tlistCache.set(\n\t\t\t\t\tlistKey,\n\t\t\t\t\t{\n\t\t\t\t\t\tresult: copyListResult(fetched),\n\t\t\t\t\t\tprefix,\n\t\t\t\t\t\tprobe: listOptions?.limit === 1,\n\t\t\t\t\t\tempty:\n\t\t\t\t\t\t\tfetched.objects.length === 0 && fetched.prefixes.length === 0,\n\t\t\t\t\t},\n\t\t\t\t\t{ ttl: ttlForKey?.(prefix) },\n\t\t\t\t);\n\t\t\t\treturn fetched;\n\t\t\t});\n\t\t\treturn copyListResult(result);\n\t\t},\n\n\t\t// Forwarded straight to the backing store: a range read is a handful of\n\t\t// bytes at a known offset, so caching one buys nothing, and bypassing the\n\t\t// cache here keeps this wrapper's read paths unchanged.\n\t\t...(range\n\t\t\t? {\n\t\t\t\t\tgetRange: (key: string, start: number, end: number) =>\n\t\t\t\t\t\trange(key, start, end),\n\t\t\t\t}\n\t\t\t: {}),\n\n\t\tinvalidate(prefix: string): void {\n\t\t\tgeneration++;\n\t\t\tfor (const key of [...pendingGets.keys()]) {\n\t\t\t\tif (key.startsWith(prefix)) pendingGets.delete(key);\n\t\t\t}\n\t\t\tfor (const key of [...pendingHeads.keys()]) {\n\t\t\t\tif (key.startsWith(prefix)) pendingHeads.delete(key);\n\t\t\t}\n\t\t\tpendingLists.clear();\n\t\t\tfor (const key of cache.keys()) {\n\t\t\t\tif (key.startsWith(prefix)) cache.delete(key);\n\t\t\t}\n\t\t\tfor (const [listKey, entry] of listCache.entries()) {\n\t\t\t\tif (\n\t\t\t\t\tentry.prefix.startsWith(prefix) ||\n\t\t\t\t\tprefix.startsWith(entry.prefix)\n\t\t\t\t) {\n\t\t\t\t\tlistCache.delete(listKey);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n","/**\n * Node-style filesystem error carrying a `code` property, which is what\n * isomorphic-git inspects to distinguish \"file not found\" from real failures.\n */\nexport class FsError extends Error {\n\treadonly code: string;\n\treadonly syscall: string;\n\treadonly path: string;\n\n\tconstructor(code: string, syscall: string, path: string) {\n\t\tsuper(`${code}: ${syscall} '${path}'`);\n\t\tthis.name = \"FsError\";\n\t\tthis.code = code;\n\t\tthis.syscall = syscall;\n\t\tthis.path = path;\n\t}\n}\n\nexport const enoent = (syscall: string, path: string) =>\n\tnew FsError(\"ENOENT\", syscall, path);\n\nexport const enotdir = (syscall: string, path: string) =>\n\tnew FsError(\"ENOTDIR\", syscall, path);\n\nexport const enotempty = (syscall: string, path: string) =>\n\tnew FsError(\"ENOTEMPTY\", syscall, path);\n\nexport const einval = (syscall: string, path: string) =>\n\tnew FsError(\"EINVAL\", syscall, path);\n\nexport const eperm = (syscall: string, path: string) =>\n\tnew FsError(\"EPERM\", syscall, path);\n","import { einval } from \"./errors.js\";\n\n/**\n * Normalize an absolute-or-relative filesystem path into a storage key\n * segment: no leading/trailing slashes, `.` segments dropped, `..` resolved.\n * A `..` that would escape the root throws EINVAL — paths handed to the fs\n * must never address keys outside the configured prefix.\n */\nexport function normalizePath(filepath: string): string {\n\tconst segments = filepath.split(\"/\");\n\tconst out: string[] = [];\n\tfor (const segment of segments) {\n\t\tif (segment === \"\" || segment === \".\") continue;\n\t\tif (segment === \"..\") {\n\t\t\tif (out.length === 0) throw einval(\"resolve\", filepath);\n\t\t\tout.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tout.push(segment);\n\t}\n\treturn out.join(\"/\");\n}\n\n/** Join a configured key prefix with a normalized path. */\nexport function toKey(prefix: string, filepath: string): string {\n\tconst normalized = normalizePath(filepath);\n\tif (prefix === \"\") return normalized;\n\treturn normalized === \"\" ? prefix : `${prefix}/${normalized}`;\n}\n","import { LRUCache } from \"lru-cache\";\nimport { enoent, enotdir, enotempty, eperm } from \"./errors.js\";\nimport { parseLooseObjectPath } from \"./git-layout.js\";\nimport { parsePackDirectory, READABLE_PACK_FILE_KINDS } from \"./pack-files.js\";\nimport { normalizePath, toKey as toKeyWithPrefix } from \"./path.js\";\nimport type {\n\tEncoding,\n\tGitdirFacts,\n\tGitFsClient,\n\tGitFsOptions,\n\tObjectStore,\n\tReadFileOptions,\n\tStat,\n\tWriteFileOptions,\n} from \"./types.js\";\n\nconst FILE_MODE = 0o100644;\nconst DIR_MODE = 0o40000;\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nfunction makeStat(type: \"file\" | \"dir\", size: number): Stat {\n\tconst epoch = new Date(0);\n\treturn {\n\t\ttype,\n\t\tmode: type === \"file\" ? FILE_MODE : DIR_MODE,\n\t\tsize,\n\t\tino: 0,\n\t\tmtimeMs: 0,\n\t\tctimeMs: 0,\n\t\tuid: 0,\n\t\tgid: 0,\n\t\tdev: 0,\n\t\tmtime: epoch,\n\t\tctime: epoch,\n\t\tisFile: () => type === \"file\",\n\t\tisDirectory: () => type === \"dir\",\n\t\tisSymbolicLink: () => false,\n\t};\n}\n\nfunction resolveEncoding(\n\toptions?: ReadFileOptions | WriteFileOptions | Encoding,\n): Encoding | undefined {\n\tif (typeof options === \"string\") return options;\n\treturn options?.encoding;\n}\n\n/**\n * The filesystem returned by {@link createGitFs}: the isomorphic-git client\n * plus git-aware maintenance hooks.\n */\nexport interface GitFs extends GitFsClient {\n\t/**\n\t * Return every file below a directory in one object-store listing. Unlike\n\t * `readdir`, this deliberately does not use a delimiter: object storage can\n\t * enumerate a ref namespace recursively without the stat-per-entry walk a\n\t * POSIX filesystem needs to distinguish files from implicit directories.\n\t * Paths are relative to `dirpath` and never include directory entries.\n\t */\n\tlistFilesRecursively(dirpath: string): Promise<string[]>;\n\t/**\n\t * Probe, with one bounded list, whether `gitdir` contains any loose\n\t * objects, and remember the answer. This is the only way a loose-object\n\t * hint is ever created; call it before full-history walks (commit logs,\n\t * reachability traversals) so fully packed repositories skip every\n\t * guaranteed-miss loose-object read. A later loose write flips the hint\n\t * back, so it cannot go stale mid-push.\n\t */\n\tdetectLooseObjects(gitdir: string): Promise<void>;\n\t/**\n\t * Warm the cache with every pack file under `gitdir` in parallel (plus\n\t * the loose-object hint) before a sequential history walk. Skipped when\n\t * the pack directory holds more than `maxPacks * 2` entries — warming\n\t * only helps when the cache budget actually fits the packs.\n\t */\n\tprefetchPacks(gitdir: string, options?: { maxPacks?: number }): Promise<void>;\n\t/**\n\t * Clear fs-level state (loose-object hints) under `pathPrefix`, and\n\t * forward to the store's `invalidate` when it has one. Call after the\n\t * backing store was modified by something other than this fs.\n\t */\n\tinvalidate(pathPrefix: string): void;\n\t/**\n\t * Declare that `gitdir` exists, so `stat` answers \"directory\" for it with\n\t * zero store calls. isomorphic-git stats the gitdir on every command\n\t * (`discoverGitdir`); in object storage that is a HEAD that always misses\n\t * plus a listing. Register once the caller has confirmed the repository\n\t * exists by other means (e.g. its database row); `invalidate` forgets it.\n\t */\n\tregisterGitdir(gitdir: string, facts?: GitdirFacts): void;\n}\n\n/**\n * Create a promise-based filesystem client for isomorphic-git backed by an\n * {@link ObjectStore}.\n *\n * Semantics:\n * - Directories are implicit, as in object storage: `mkdir` is a no-op and a\n * directory \"exists\" whenever at least one key lives under its prefix.\n * - Symbolic links are not supported (`readlink`/`symlink` throw). Bare\n * repositories never contain them.\n * - Designed for bare, server-side repositories (`git.init({bare: true})`,\n * plumbing commands, ref updates). Worktree checkouts belong on a real disk.\n */\nexport function createGitFs(\n\tstore: ObjectStore,\n\toptions: GitFsOptions = {},\n): GitFs {\n\tconst prefix = options.prefix ?? \"\";\n\tconst useLooseHints = options.looseObjectHints ?? false;\n\tconst onNote = options.onNote;\n\n\tconst toKey = (path: string): string => toKeyWithPrefix(prefix, path);\n\n\t/**\n\t * Per-gitdir \"does any loose object exist\" hint. Entries are only created\n\t * by {@link GitFs.detectLooseObjects}, so a pathological ref that merely\n\t * looks like a loose object (`refs/heads/objects/aa/…`) derives a scope\n\t * that was never registered and can never be wrongly short-circuited.\n\t */\n\tconst looseHints = new LRUCache<string, \"none\" | \"present\">({\n\t\tmax: 1024,\n\t\tttl: options.hintTtlMs ?? 3_600_000,\n\t});\n\n\t/** Gitdirs the caller vouched for via {@link GitFs.registerGitdir}. */\n\tconst registeredGitdirs = new LRUCache<string, ReadonlySet<string>>({\n\t\tmax: 4096,\n\t});\n\n\t/** Whether `path` is a top-level file its registered gitdir lacks. */\n\tfunction registeredAbsent(path: string): boolean {\n\t\tconst slash = path.lastIndexOf(\"/\");\n\t\tif (slash < 0) return false;\n\t\tconst absent = registeredGitdirs.get(path.slice(0, slash));\n\t\treturn absent?.has(path.slice(slash + 1)) ?? false;\n\t}\n\n\t/** The gitdir scope of a loose-object path, or null when it isn't one. */\n\tfunction looseScope(path: string): string | null {\n\t\treturn parseLooseObjectPath(path)?.gitdir ?? null;\n\t}\n\n\tconst pendingDetections = new Map<string, Promise<void>>();\n\n\t/**\n\t * Resolve the loose-object hint for `path`'s gitdir before answering, so a\n\t * read that runs before any explicit `detectLooseObjects` call (e.g. the\n\t * first commit read of a request) doesn't pay a doomed GET. One coalesced\n\t * listing per gitdir, cached for `hintTtlMs`.\n\t */\n\tasync function knownAbsent(path: string): Promise<boolean> {\n\t\tif (registeredAbsent(path)) return true;\n\t\tif (!useLooseHints) return false;\n\t\tconst scope = looseScope(path);\n\t\tif (scope === null) return false;\n\t\tif (!looseHints.has(scope)) {\n\t\t\tlet pending = pendingDetections.get(scope);\n\t\t\tif (!pending) {\n\t\t\t\tpending = detectLooseObjects(scope).finally(() =>\n\t\t\t\t\tpendingDetections.delete(scope),\n\t\t\t\t);\n\t\t\t\tpendingDetections.set(scope, pending);\n\t\t\t}\n\t\t\tawait pending;\n\t\t}\n\t\treturn looseHints.get(scope) === \"none\";\n\t}\n\n\tasync function isDirectory(dirKey: string): Promise<boolean> {\n\t\tconst { objects, prefixes } = await store.list(`${dirKey}/`, {\n\t\t\tlimit: 1,\n\t\t});\n\t\treturn objects.length > 0 || prefixes.length > 0;\n\t}\n\n\tasync function stat(filepath: string, syscall: string): Promise<Stat> {\n\t\tconst path = normalizePath(filepath);\n\t\tif (await knownAbsent(path)) throw enoent(syscall, filepath);\n\t\tconst k = toKey(path);\n\t\tif (k === prefix || k === \"\" || registeredGitdirs.has(path)) {\n\t\t\treturn makeStat(\"dir\", 0);\n\t\t}\n\t\tconst fileStat = await store.head(k);\n\t\tif (fileStat) return makeStat(\"file\", fileStat.size);\n\t\t// A loose-object path is always a leaf; when the object itself is\n\t\t// absent there is no point probing for a directory of the same name.\n\t\tif (useLooseHints && looseScope(path) !== null) {\n\t\t\tthrow enoent(syscall, filepath);\n\t\t}\n\t\tif (await isDirectory(k)) return makeStat(\"dir\", 0);\n\t\tthrow enoent(syscall, filepath);\n\t}\n\n\tconst range = store.getRange?.bind(store);\n\n\tconst promises: GitFsClient[\"promises\"] = {\n\t\t// Only defined when the backing store supports ranges, so a caller can\n\t\t// test for it rather than have to interpret a failure.\n\t\t...(range\n\t\t\t? {\n\t\t\t\t\tgetRange: (filepath: string, start: number, end: number) =>\n\t\t\t\t\t\trange(toKey(normalizePath(filepath)), start, end),\n\t\t\t\t}\n\t\t\t: {}),\n\n\t\tasync readFile(filepath, opts) {\n\t\t\tconst path = normalizePath(filepath);\n\t\t\tif (await knownAbsent(path)) throw enoent(\"open\", filepath);\n\t\t\tconst data = await store.get(toKey(path));\n\t\t\tif (data === null) throw enoent(\"open\", filepath);\n\t\t\treturn resolveEncoding(opts) === \"utf8\" ? textDecoder.decode(data) : data;\n\t\t},\n\n\t\tasync writeFile(filepath, data, _opts) {\n\t\t\tconst path = normalizePath(filepath);\n\t\t\tif (useLooseHints) {\n\t\t\t\tconst scope = looseScope(path);\n\t\t\t\t// Flip before the write lands so a racing read can never\n\t\t\t\t// short-circuit an object that is in the middle of arriving.\n\t\t\t\tif (scope !== null) looseHints.set(scope, \"present\");\n\t\t\t}\n\t\t\tconst bytes = typeof data === \"string\" ? textEncoder.encode(data) : data;\n\t\t\tawait store.put(toKey(path), bytes);\n\t\t},\n\n\t\tasync unlink(filepath) {\n\t\t\tconst k = toKey(normalizePath(filepath));\n\t\t\tif ((await store.head(k)) === null) throw enoent(\"unlink\", filepath);\n\t\t\tawait store.delete(k);\n\t\t},\n\n\t\tasync readdir(dirpath) {\n\t\t\tconst k = toKey(normalizePath(dirpath));\n\t\t\tconst isRoot = k === prefix || k === \"\";\n\t\t\tconst listPrefix = isRoot && k === \"\" ? \"\" : `${k}/`;\n\t\t\tconst { objects, prefixes } = await store.list(listPrefix, {\n\t\t\t\tdelimiter: \"/\",\n\t\t\t});\n\t\t\tif (objects.length === 0 && prefixes.length === 0) {\n\t\t\t\tif (!isRoot && (await store.head(k)) !== null) {\n\t\t\t\t\tthrow enotdir(\"scandir\", dirpath);\n\t\t\t\t}\n\t\t\t\tif (!isRoot) throw enoent(\"scandir\", dirpath);\n\t\t\t}\n\t\t\tconst names = objects.map((o) => o.key.slice(listPrefix.length));\n\t\t\tconst dirNames = prefixes.map((p) =>\n\t\t\t\tp.slice(listPrefix.length).replace(/\\/$/, \"\"),\n\t\t\t);\n\t\t\treturn [...names, ...dirNames].sort();\n\t\t},\n\n\t\tasync mkdir(_dirpath, _opts) {\n\t\t\t// Directories are implicit in object storage.\n\t\t},\n\n\t\tasync rmdir(dirpath) {\n\t\t\tconst k = toKey(normalizePath(dirpath));\n\t\t\tconst { objects, prefixes } = await store.list(`${k}/`, { limit: 1 });\n\t\t\tif (objects.length > 0 || prefixes.length > 0) {\n\t\t\t\tthrow enotempty(\"rmdir\", dirpath);\n\t\t\t}\n\t\t\t// Empty implicit directories don't exist; nothing to remove.\n\t\t},\n\n\t\tstat: (filepath) => stat(filepath, \"stat\"),\n\t\tlstat: (filepath) => stat(filepath, \"lstat\"),\n\n\t\tasync readlink(filepath): Promise<never> {\n\t\t\tthrow enoent(\"readlink\", filepath);\n\t\t},\n\n\t\tasync symlink(_target, filepath): Promise<never> {\n\t\t\tthrow eperm(\"symlink\", filepath);\n\t\t},\n\n\t\tasync chmod(_filepath, _mode) {\n\t\t\t// POSIX modes don't exist in object storage.\n\t\t},\n\t};\n\n\tasync function detectLooseObjects(gitdir: string): Promise<void> {\n\t\tif (!useLooseHints) return;\n\t\tconst scope = normalizePath(gitdir);\n\t\t// A live hint must win over re-detection: after a loose write flips it\n\t\t// to \"present\", re-deriving from a (possibly cached, pre-write) listing\n\t\t// could wrongly reinstate \"none\" and mask real objects.\n\t\tif (looseHints.has(scope)) return;\n\t\ttry {\n\t\t\tconst { objects } = await store.list(`${toKey(scope)}/objects/`, {\n\t\t\t\tlimit: 1,\n\t\t\t});\n\t\t\t// Loose fan-out directories (two hex digits) sort before \"info/\"\n\t\t\t// and \"pack/\", so when any loose object exists it is the first key.\n\t\t\tconst first = objects[0]?.key;\n\t\t\tconst hint =\n\t\t\t\tfirst !== undefined && parseLooseObjectPath(first) !== null\n\t\t\t\t\t? \"present\"\n\t\t\t\t\t: \"none\";\n\t\t\tlooseHints.set(scope, hint);\n\t\t\tonNote?.(`loose objects ${hint} under ${scope}`);\n\t\t} catch {\n\t\t\t// Leave unknown — reads fall back to their normal round trip.\n\t\t}\n\t}\n\n\tasync function prefetchPacks(\n\t\tgitdir: string,\n\t\tprefetchOptions?: { maxPacks?: number },\n\t): Promise<void> {\n\t\tconst maxPacks = prefetchOptions?.maxPacks ?? 30;\n\t\tconst packDir = `${normalizePath(gitdir)}/objects/pack`;\n\t\t// isomorphic-git never reads .bitmap/.rev/.keep, so fetching them is a\n\t\t// wasted GET and wasted cache space.\n\t\tconst entries = parsePackDirectory(\n\t\t\tawait promises.readdir(packDir).catch(() => []),\n\t\t)\n\t\t\t.filter((file) => READABLE_PACK_FILE_KINDS.has(file.kind))\n\t\t\t.map((file) => file.name);\n\t\tif (entries.length > maxPacks * 2) {\n\t\t\tawait detectLooseObjects(gitdir);\n\t\t\treturn;\n\t\t}\n\t\tawait Promise.all([\n\t\t\tdetectLooseObjects(gitdir),\n\t\t\t...entries.map((name) =>\n\t\t\t\tpromises.readFile(`${packDir}/${name}`).catch(() => undefined),\n\t\t\t),\n\t\t]);\n\t}\n\n\tasync function listFilesRecursively(dirpath: string): Promise<string[]> {\n\t\tconst directory = toKey(normalizePath(dirpath));\n\t\tconst listPrefix = directory === \"\" ? \"\" : `${directory}/`;\n\t\tconst { objects } = await store.list(listPrefix);\n\t\treturn objects\n\t\t\t.map((object) => object.key.slice(listPrefix.length))\n\t\t\t.filter((path) => path.length > 0)\n\t\t\t.sort();\n\t}\n\n\tfunction registerGitdir(gitdir: string, facts?: GitdirFacts): void {\n\t\tregisteredGitdirs.set(\n\t\t\tnormalizePath(gitdir),\n\t\t\tnew Set(facts?.absentFiles ?? []),\n\t\t);\n\t}\n\n\tfunction invalidate(pathPrefix: string): void {\n\t\tconst normalized = normalizePath(pathPrefix);\n\t\tfor (const dir of registeredGitdirs.keys()) {\n\t\t\tif (dir.startsWith(normalized)) registeredGitdirs.delete(dir);\n\t\t}\n\t\tfor (const scope of looseHints.keys()) {\n\t\t\tif (scope.startsWith(normalized)) looseHints.delete(scope);\n\t\t}\n\t\tconst maybe = store as ObjectStore & {\n\t\t\tinvalidate?: (prefix: string) => void;\n\t\t};\n\t\tmaybe.invalidate?.(toKey(normalized));\n\t}\n\n\treturn {\n\t\tpromises,\n\t\tlistFilesRecursively,\n\t\tdetectLooseObjects,\n\t\tprefetchPacks,\n\t\tinvalidate,\n\t\tregisterGitdir,\n\t};\n}\n","import type { ObjectStore } from \"./types.js\";\n\nconst BATCH = 20;\n\nasync function inBatches<T>(\n\titems: T[],\n\tfn: (item: T) => Promise<void>,\n): Promise<void> {\n\tfor (let i = 0; i < items.length; i += BATCH) {\n\t\tawait Promise.all(items.slice(i, i + BATCH).map(fn));\n\t}\n}\n\n/**\n * Delete every object under `prefix` (one paginated LIST, then batched\n * deletes). Returns the number of objects deleted. Callers serialise against\n * concurrent writers (e.g. a repository lock) and invalidate any cache\n * layered above `store`.\n */\nexport async function deletePrefix(\n\tstore: ObjectStore,\n\tprefix: string,\n): Promise<number> {\n\tconst { objects } = await store.list(prefix);\n\tawait inBatches(objects, (object) => store.delete(object.key));\n\treturn objects.length;\n}\n\nexport interface MovePrefixOptions {\n\t/**\n\t * Copy one object, e.g. a store's server-side `copy` (no bytes pass through\n\t * this process). Defaults to `get` + `put` on `store`.\n\t */\n\tcopy?: (fromKey: string, toKey: string) => Promise<void>;\n}\n\n/**\n * Move every object under `from` to the same relative key under `to`: copy\n * everything first, then delete the old keys only once every copy succeeded,\n * so a failed copy leaves the source untouched (a partial destination may\n * remain; call {@link deletePrefix} on it to clean up). Returns the number of\n * objects moved. `from` and `to` must not overlap.\n */\nexport async function movePrefix(\n\tstore: ObjectStore,\n\tfrom: string,\n\tto: string,\n\toptions: MovePrefixOptions = {},\n): Promise<number> {\n\tif (from.startsWith(to) || to.startsWith(from)) {\n\t\tthrow new Error(`movePrefix: prefixes overlap (${from} -> ${to})`);\n\t}\n\tconst copy =\n\t\toptions.copy ??\n\t\t(async (fromKey: string, toKey: string) => {\n\t\t\tconst data = await store.get(fromKey);\n\t\t\tif (data) await store.put(toKey, data);\n\t\t});\n\tconst { objects } = await store.list(from);\n\tawait inBatches(objects, (object) =>\n\t\tcopy(object.key, to + object.key.slice(from.length)),\n\t);\n\tawait inBatches(objects, (object) => store.delete(object.key));\n\treturn objects.length;\n}\n","import type { ObjectStore } from \"./types.js\";\n\n/** Options accepted by {@link createRetryStore}. */\nexport interface RetryOptions {\n\t/** Retries after the first attempt (total attempts = retries + 1). Default 3. */\n\tretries?: number;\n\t/** Backoff base delay in milliseconds, doubled each attempt. Default 100. */\n\tinitialDelayMs?: number;\n\t/** Upper bound for the backoff base delay. Default 5000. */\n\tmaxDelayMs?: number;\n\t/** Random jitter added to each delay, as a fraction of it. Default 0.3. */\n\tjitter?: number;\n\t/**\n\t * Decide whether an error is worth retrying. The store contract maps\n\t * \"not found\" to `null` rather than throwing, so any thrown error is a\n\t * genuine failure; the default retries network faults, throttling, and\n\t * HTTP 5xx responses.\n\t */\n\tisRetryable?: (error: unknown) => boolean;\n\t/**\n\t * Circuit breaker configuration, or `false` to disable. After `threshold`\n\t * consecutive failures the store fails fast for `resetMs`, then lets one\n\t * request probe the backend again. Defaults: 5 failures, 30 000 ms.\n\t */\n\tbreaker?: false | { threshold?: number; resetMs?: number };\n\t/** Called before each retry sleep; useful for logging/metrics. */\n\tonRetry?: (info: {\n\t\tkey: string;\n\t\top: string;\n\t\tattempt: number;\n\t\tdelayMs: number;\n\t}) => void;\n}\n\n/**\n * Thrown instead of calling the backend while the circuit breaker is open.\n * Carries `code: \"EUNAVAILABLE\"` so callers can map it to a 503.\n */\nexport class CircuitOpenError extends Error {\n\treadonly code = \"EUNAVAILABLE\";\n\n\tconstructor() {\n\t\tsuper(\"Circuit breaker is open, object store unavailable\");\n\t\tthis.name = \"CircuitOpenError\";\n\t}\n}\n\nconst RETRYABLE_NAMES = new Set([\n\t\"TimeoutError\",\n\t\"RequestTimeout\",\n\t\"RequestTimeoutException\",\n\t\"SlowDown\",\n\t\"ThrottlingException\",\n\t\"TooManyRequestsException\",\n]);\n\nconst RETRYABLE_CODES = new Set([\n\t\"ECONNRESET\",\n\t\"ECONNREFUSED\",\n\t\"EPIPE\",\n\t\"ETIMEDOUT\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"EPROTO\",\n]);\n\n/**\n * The default retry classifier: network faults, timeouts, throttling and HTTP\n * 5xx/429 (including `$metadata.httpStatusCode`-shaped errors). `fetch`\n * reports a dropped connection as a bare `TypeError`, which counts too.\n * Compose it in a custom `isRetryable`.\n */\nexport function isRetryableError(error: unknown): boolean {\n\tif (error instanceof TypeError) return true;\n\tif (typeof error !== \"object\" || error === null) return false;\n\tconst err = error as {\n\t\tname?: string;\n\t\tcode?: string;\n\t\t$metadata?: { httpStatusCode?: number };\n\t};\n\tif (err.name !== undefined && RETRYABLE_NAMES.has(err.name)) return true;\n\tif (err.code !== undefined && RETRYABLE_CODES.has(err.code)) return true;\n\tconst status = err.$metadata?.httpStatusCode;\n\treturn status !== undefined && (status >= 500 || status === 429);\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Wrap an {@link ObjectStore} with retries (exponential backoff + jitter) and\n * an optional per-instance circuit breaker.\n *\n * Place this decorator closest to the network store, underneath any cache:\n * the cache then never stores transient failures, and callers coalesced onto\n * one request share a single retried attempt.\n */\nexport function createRetryStore(\n\tstore: ObjectStore,\n\toptions: RetryOptions = {},\n): ObjectStore {\n\tconst retries = options.retries ?? 3;\n\tconst initialDelayMs = options.initialDelayMs ?? 100;\n\tconst maxDelayMs = options.maxDelayMs ?? 5000;\n\tconst jitter = options.jitter ?? 0.3;\n\tconst isRetryable = options.isRetryable ?? isRetryableError;\n\tconst breaker =\n\t\toptions.breaker === false\n\t\t\t? null\n\t\t\t: {\n\t\t\t\t\tthreshold: options.breaker?.threshold ?? 5,\n\t\t\t\t\tresetMs: options.breaker?.resetMs ?? 30_000,\n\t\t\t\t};\n\n\tlet failures = 0;\n\tlet lastFailureAt = 0;\n\tlet state: \"closed\" | \"open\" | \"half-open\" = \"closed\";\n\tlet probing = false;\n\n\t/**\n\t * Consecutive transient failures open the circuit; any success closes it\n\t * and resets the count. Non-retryable errors (a 403, a malformed request)\n\t * say nothing about the backend's health and never count. Once the reset\n\t * window passes, exactly one request probes the backend (half-open);\n\t * everything else keeps failing fast until that probe settles.\n\t */\n\tasync function guarded<T>(fn: () => Promise<T>): Promise<T> {\n\t\tif (breaker === null) return fn();\n\t\tlet isProbe = false;\n\t\tif (state !== \"closed\") {\n\t\t\tif (\n\t\t\t\tprobing ||\n\t\t\t\t(state === \"open\" && Date.now() - lastFailureAt < breaker.resetMs)\n\t\t\t) {\n\t\t\t\tthrow new CircuitOpenError();\n\t\t\t}\n\t\t\tstate = \"half-open\";\n\t\t\tprobing = true;\n\t\t\tisProbe = true;\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fn();\n\t\t\tfailures = 0;\n\t\t\tstate = \"closed\";\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tif (isRetryable(error)) {\n\t\t\t\tfailures++;\n\t\t\t\tlastFailureAt = Date.now();\n\t\t\t\tif (failures >= breaker.threshold || isProbe) state = \"open\";\n\t\t\t} else if (isProbe) {\n\t\t\t\t// The backend answered; it's up.\n\t\t\t\tfailures = 0;\n\t\t\t\tstate = \"closed\";\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tif (isProbe) probing = false;\n\t\t}\n\t}\n\n\tasync function run<T>(op: string, key: string, fn: () => Promise<T>) {\n\t\tlet lastError: unknown;\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn await guarded(fn);\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\t\t\t\tif (error instanceof CircuitOpenError) throw error;\n\t\t\t\tif (!isRetryable(error) || attempt === retries) throw error;\n\t\t\t\tconst base = Math.min(initialDelayMs * 2 ** attempt, maxDelayMs);\n\t\t\t\tconst delayMs = Math.round(base + Math.random() * base * jitter);\n\t\t\t\toptions.onRetry?.({ key, op, attempt: attempt + 1, delayMs });\n\t\t\t\tawait sleep(delayMs);\n\t\t\t}\n\t\t}\n\t\tthrow lastError;\n\t}\n\n\t// Optional methods must be forwarded explicitly: this returns a new object,\n\t// so anything not listed here is dropped — and a dropped `getRange` is\n\t// invisible, because callers that want it report \"unknown\" rather than\n\t// failing. `object-size.test.ts` asserts it survives the whole stack.\n\tconst range = store.getRange?.bind(store);\n\n\treturn {\n\t\tget: (key) => run(\"get\", key, () => store.get(key)),\n\t\tput: (key, data) => run(\"put\", key, () => store.put(key, data)),\n\t\tdelete: (key) => run(\"delete\", key, () => store.delete(key)),\n\t\thead: (key) => run(\"head\", key, () => store.head(key)),\n\t\tlist: (prefix, listOptions) =>\n\t\t\trun(\"list\", prefix, () => store.list(prefix, listOptions)),\n\t\t...(range\n\t\t\t? {\n\t\t\t\t\tgetRange: (key: string, start: number, end: number) =>\n\t\t\t\t\t\trun(\"getRange\", key, () => range(key, start, end)),\n\t\t\t\t}\n\t\t\t: {}),\n\t};\n}\n"],"mappings":";;;;;AA8EA,MAAM,OAAO,OAAO,MAAM;AAa1B,SAAS,cAAc,OAA0B;CAChD,IAAI,OAAO,MAAM,OAAO,SAAS;CACjC,KAAK,MAAM,KAAK,MAAM,OAAO,SAAS,QAAQ,EAAE,IAAI,SAAS;CAC7D,KAAK,MAAM,KAAK,MAAM,OAAO,UAAU,QAAQ,EAAE;CACjD,OAAO;AACR;AAEA,SAAS,eAAe,QAAgC;CACvD,OAAO;EACN,SAAS,OAAO,QAAQ,KAAK,OAAO,EAAE,GAAG,EAAE,EAAE;EAC7C,UAAU,CAAC,GAAG,OAAO,QAAQ;CAC9B;AACD;;;;;;;;;;;;AAaA,SAAgB,kBACf,OACA,UAAwB,CAAC,GACL;CACpB,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,gBAAgB,QAAQ,iBAAiB,KAAK,KAAK,WAAW,EAAE;CACtE,MAAM,MAAM,QAAQ,SAAS;CAC7B,MAAM,YAAY,QAAQ;CAC1B,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,QAAQ,MAAM,UAAU,KAAK,KAAK;CACxC,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,SAAS,SAAsB,OAAO,KAAK,MAAM,IAAI;CAC3D,MAAM,QAAQ,QAAQ;CACtB,MAAM,SAAS,QAAQ;CAEvB,MAAM,QAAQ,IAAI,SAA6B;EAC9C,SAAS;EACT,kBAAkB,UAAW,UAAU,OAAO,IAAI,MAAM,cAAc;EACtE;CACD,CAAC;CACD,MAAM,YAAY,IAAI,SAA4B;EACjD,SAAS,KAAK,IAAI,GAAG,KAAK,KAAK,WAAW,EAAE,CAAC;EAC7C,iBAAiB;EACjB;CACD,CAAC;CAED,MAAM,8BAAc,IAAI,IAAwC;CAChE,MAAM,+BAAe,IAAI,IAAwC;CACjE,MAAM,+BAAe,IAAI,IAAiC;;;;;;;;;CAU1D,IAAI,aAAa;CACjB,MAAM,kBAAkB,UAAkB,eAAe;CAEzD,MAAM,SAAS,KAAa,SAAqB;EAChD,IAAI,KAAK,cAAc,eACtB,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG,EAAE,KAAK,YAAY,GAAG,EAAE,CAAC;CAEvD;;CAGA,SAAS,sBAAsB,KAAmB;EACjD,KAAK,MAAM,CAAC,SAAS,UAAU,UAAU,QAAQ,GAAG;GACnD,IAAI,CAAC,IAAI,WAAW,MAAM,MAAM,GAAG;GACnC,IAAI,MAAM,SAAS,CAAC,MAAM,OAAO;GACjC,UAAU,OAAO,OAAO;EACzB;EAGA,KAAK,MAAM,WAAW,aAAa,KAAK,GAAG;GAC1C,MAAM,SAAS,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG;GACnD,IAAI,IAAI,WAAW,MAAM,GAAG,aAAa,OAAO,OAAO;EACxD;CACD;;CAGA,SAAS,UAAU,KAAmB;EACrC;EACA,YAAY,OAAO,GAAG;EACtB,aAAa,OAAO,GAAG;CACxB;CAEA,SAAS,UACR,SACA,KACA,IACa;EACb,IAAI,CAAC,UAAU,OAAO,GAAG;EACzB,MAAM,WAAW,QAAQ,IAAI,GAAG;EAChC,IAAI,aAAa,KAAA,GAAW,OAAO;EACnC,MAAM,IAAI,GAAG,CAAC,CAAC,cAAc;GAC5B,IAAI,QAAQ,IAAI,GAAG,MAAM,GAAG,QAAQ,OAAO,GAAG;EAC/C,CAAC;EACD,QAAQ,IAAI,KAAK,CAAC;EAClB,OAAO;CACR;CAEA,OAAO;EACN,MAAM,IAAI,KAAyC;GAClD,MAAM,SAAS,MAAM,IAAI,GAAG;GAC5B,IAAI,WAAW,KAAA,GAAW;IACzB,QAAQ,GAAG;IACX,OAAO,WAAW,OAAO,OAAO,MAAM,MAAM;GAC7C;GACA,MAAM,OAAO,MAAM,UAAU,aAAa,KAAK,YAAY;IAC1D,SAAS,GAAG;IACZ,MAAM,QAAQ;IACd,MAAM,UAAU,MAAM,MAAM,IAAI,GAAG;IACnC,IAAI,eAAe,KAAK,GAAG;KAC1B,IAAI,YAAY,MACf,MAAM,KAAK,OAAO;UACZ,IAAI,aACV,MAAM,IAAI,KAAK,MAAM,EAAE,KAAK,YAAY,GAAG,EAAE,CAAC;IAEhD;IACA,OAAO;GACR,CAAC;GACD,OAAO,SAAS,OAAO,OAAO,MAAM,IAAI;EACzC;EAEA,MAAM,IAAI,KAAa,MAAiC;GACvD,UAAU,GAAG;GACb,IAAI;IACH,MAAM,MAAM,IAAI,KAAK,IAAI;GAC1B,UAAU;IAGT,UAAU,GAAG;IACb,MAAM,OAAO,GAAG;IAChB,sBAAsB,GAAG;GAC1B;GACA,MAAM,KAAK,IAAI;EAChB;EAEA,MAAM,OAAO,KAA4B;GACxC,UAAU,GAAG;GACb,IAAI;IACH,MAAM,MAAM,OAAO,GAAG;GACvB,UAAU;IACT,UAAU,GAAG;IACb,MAAM,OAAO,GAAG;IAChB,sBAAsB,GAAG;GAC1B;GACA,IAAI,aACH,MAAM,IAAI,KAAK,MAAM,EAAE,KAAK,YAAY,GAAG,EAAE,CAAC;QAE9C,MAAM,OAAO,GAAG;EAElB;EAEA,MAAM,KAAK,KAAyC;GACnD,MAAM,SAAS,MAAM,IAAI,GAAG;GAC5B,IAAI,WAAW,KAAA,GAAW;IACzB,QAAQ,GAAG;IACX,OAAO,WAAW,OAAO,OAAO,EAAE,MAAM,OAAO,WAAW;GAC3D;GACA,OAAO,UAAU,cAAc,KAAK,YAAY;IAC/C,SAAS,GAAG;IACZ,MAAM,QAAQ;IACd,MAAM,OAAO,MAAM,MAAM,KAAK,GAAG;IACjC,IAAI,SAAS,QAAQ,eAAe,eAAe,KAAK,GACvD,MAAM,IAAI,KAAK,MAAM,EAAE,KAAK,YAAY,GAAG,EAAE,CAAC;IAE/C,OAAO;GACR,CAAC;EACF;EAEA,MAAM,KAAK,QAAgB,aAAgD;GAC1E,IAAI,CAAC,YAAY,OAAO,MAAM,KAAK,QAAQ,WAAW;GACtD,MAAM,UAAU,GAAG,aAAa,aAAa,GAAG,GAAG,aAAa,SAAS,GAAG,GAAG;GAC/E,MAAM,SAAS,UAAU,IAAI,OAAO;GACpC,IAAI,WAAW,KAAA,GAAW;IACzB,QAAQ,MAAM;IACd,OAAO,eAAe,OAAO,MAAM;GACpC;GAmBA,OAAO,eAAe,MAlBD,UAAU,cAAc,SAAS,YAAY;IACjE,SAAS,MAAM;IACf,MAAM,QAAQ;IACd,MAAM,UAAU,MAAM,MAAM,KAAK,QAAQ,WAAW;IACpD,IAAI,CAAC,eAAe,KAAK,GAAG,OAAO;IACnC,UAAU,IACT,SACA;KACC,QAAQ,eAAe,OAAO;KAC9B;KACA,OAAO,aAAa,UAAU;KAC9B,OACC,QAAQ,QAAQ,WAAW,KAAK,QAAQ,SAAS,WAAW;IAC9D,GACA,EAAE,KAAK,YAAY,MAAM,EAAE,CAC5B;IACA,OAAO;GACR,CAAC,CAC2B;EAC7B;EAKA,GAAI,QACD,EACA,WAAW,KAAa,OAAe,QACtC,MAAM,KAAK,OAAO,GAAG,EACvB,IACC,CAAC;EAEJ,WAAW,QAAsB;GAChC;GACA,KAAK,MAAM,OAAO,CAAC,GAAG,YAAY,KAAK,CAAC,GACvC,IAAI,IAAI,WAAW,MAAM,GAAG,YAAY,OAAO,GAAG;GAEnD,KAAK,MAAM,OAAO,CAAC,GAAG,aAAa,KAAK,CAAC,GACxC,IAAI,IAAI,WAAW,MAAM,GAAG,aAAa,OAAO,GAAG;GAEpD,aAAa,MAAM;GACnB,KAAK,MAAM,OAAO,MAAM,KAAK,GAC5B,IAAI,IAAI,WAAW,MAAM,GAAG,MAAM,OAAO,GAAG;GAE7C,KAAK,MAAM,CAAC,SAAS,UAAU,UAAU,QAAQ,GAChD,IACC,MAAM,OAAO,WAAW,MAAM,KAC9B,OAAO,WAAW,MAAM,MAAM,GAE9B,UAAU,OAAO,OAAO;EAG3B;CACD;AACD;;;;;;;ACzUA,IAAa,UAAb,cAA6B,MAAM;CAClC;CACA;CACA;CAEA,YAAY,MAAc,SAAiB,MAAc;EACxD,MAAM,GAAG,KAAK,IAAI,QAAQ,IAAI,KAAK,EAAE;EACrC,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,UAAU;EACf,KAAK,OAAO;CACb;AACD;AAEA,MAAa,UAAU,SAAiB,SACvC,IAAI,QAAQ,UAAU,SAAS,IAAI;AAEpC,MAAa,WAAW,SAAiB,SACxC,IAAI,QAAQ,WAAW,SAAS,IAAI;AAErC,MAAa,aAAa,SAAiB,SAC1C,IAAI,QAAQ,aAAa,SAAS,IAAI;AAEvC,MAAa,UAAU,SAAiB,SACvC,IAAI,QAAQ,UAAU,SAAS,IAAI;AAEpC,MAAa,SAAS,SAAiB,SACtC,IAAI,QAAQ,SAAS,SAAS,IAAI;;;;;;;;;ACvBnC,SAAgB,cAAc,UAA0B;CACvD,MAAM,WAAW,SAAS,MAAM,GAAG;CACnC,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,WAAW,UAAU;EAC/B,IAAI,YAAY,MAAM,YAAY,KAAK;EACvC,IAAI,YAAY,MAAM;GACrB,IAAI,IAAI,WAAW,GAAG,MAAM,OAAO,WAAW,QAAQ;GACtD,IAAI,IAAI;GACR;EACD;EACA,IAAI,KAAK,OAAO;CACjB;CACA,OAAO,IAAI,KAAK,GAAG;AACpB;;AAGA,SAAgB,MAAM,QAAgB,UAA0B;CAC/D,MAAM,aAAa,cAAc,QAAQ;CACzC,IAAI,WAAW,IAAI,OAAO;CAC1B,OAAO,eAAe,KAAK,SAAS,GAAG,OAAO,GAAG;AAClD;;;ACZA,MAAM,YAAY;AAClB,MAAM,WAAW;AAEjB,MAAM,cAAc,IAAI,YAAY;AACpC,MAAM,cAAc,IAAI,YAAY;AAEpC,SAAS,SAAS,MAAsB,MAAoB;CAC3D,MAAM,wBAAQ,IAAI,KAAK,CAAC;CACxB,OAAO;EACN;EACA,MAAM,SAAS,SAAS,YAAY;EACpC;EACA,KAAK;EACL,SAAS;EACT,SAAS;EACT,KAAK;EACL,KAAK;EACL,KAAK;EACL,OAAO;EACP,OAAO;EACP,cAAc,SAAS;EACvB,mBAAmB,SAAS;EAC5B,sBAAsB;CACvB;AACD;AAEA,SAAS,gBACR,SACuB;CACvB,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,OAAO,SAAS;AACjB;;;;;;;;;;;;;AA2DA,SAAgB,YACf,OACA,UAAwB,CAAC,GACjB;CACR,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,gBAAgB,QAAQ,oBAAoB;CAClD,MAAM,SAAS,QAAQ;CAEvB,MAAMA,WAAS,SAAyBC,MAAgB,QAAQ,IAAI;;;;;;;CAQpE,MAAM,aAAa,IAAI,SAAqC;EAC3D,KAAK;EACL,KAAK,QAAQ,aAAa;CAC3B,CAAC;;CAGD,MAAM,oBAAoB,IAAI,SAAsC,EACnE,KAAK,KACN,CAAC;;CAGD,SAAS,iBAAiB,MAAuB;EAChD,MAAM,QAAQ,KAAK,YAAY,GAAG;EAClC,IAAI,QAAQ,GAAG,OAAO;EAEtB,OADe,kBAAkB,IAAI,KAAK,MAAM,GAAG,KAAK,CAC5C,CAAC,EAAE,IAAI,KAAK,MAAM,QAAQ,CAAC,CAAC,KAAK;CAC9C;;CAGA,SAAS,WAAW,MAA6B;EAChD,OAAO,qBAAqB,IAAI,CAAC,EAAE,UAAU;CAC9C;CAEA,MAAM,oCAAoB,IAAI,IAA2B;;;;;;;CAQzD,eAAe,YAAY,MAAgC;EAC1D,IAAI,iBAAiB,IAAI,GAAG,OAAO;EACnC,IAAI,CAAC,eAAe,OAAO;EAC3B,MAAM,QAAQ,WAAW,IAAI;EAC7B,IAAI,UAAU,MAAM,OAAO;EAC3B,IAAI,CAAC,WAAW,IAAI,KAAK,GAAG;GAC3B,IAAI,UAAU,kBAAkB,IAAI,KAAK;GACzC,IAAI,CAAC,SAAS;IACb,UAAU,mBAAmB,KAAK,CAAC,CAAC,cACnC,kBAAkB,OAAO,KAAK,CAC/B;IACA,kBAAkB,IAAI,OAAO,OAAO;GACrC;GACA,MAAM;EACP;EACA,OAAO,WAAW,IAAI,KAAK,MAAM;CAClC;CAEA,eAAe,YAAY,QAAkC;EAC5D,MAAM,EAAE,SAAS,aAAa,MAAM,MAAM,KAAK,GAAG,OAAO,IAAI,EAC5D,OAAO,EACR,CAAC;EACD,OAAO,QAAQ,SAAS,KAAK,SAAS,SAAS;CAChD;CAEA,eAAe,KAAK,UAAkB,SAAgC;EACrE,MAAM,OAAO,cAAc,QAAQ;EACnC,IAAI,MAAM,YAAY,IAAI,GAAG,MAAM,OAAO,SAAS,QAAQ;EAC3D,MAAM,IAAID,QAAM,IAAI;EACpB,IAAI,MAAM,UAAU,MAAM,MAAM,kBAAkB,IAAI,IAAI,GACzD,OAAO,SAAS,OAAO,CAAC;EAEzB,MAAM,WAAW,MAAM,MAAM,KAAK,CAAC;EACnC,IAAI,UAAU,OAAO,SAAS,QAAQ,SAAS,IAAI;EAGnD,IAAI,iBAAiB,WAAW,IAAI,MAAM,MACzC,MAAM,OAAO,SAAS,QAAQ;EAE/B,IAAI,MAAM,YAAY,CAAC,GAAG,OAAO,SAAS,OAAO,CAAC;EAClD,MAAM,OAAO,SAAS,QAAQ;CAC/B;CAEA,MAAM,QAAQ,MAAM,UAAU,KAAK,KAAK;CAExC,MAAM,WAAoC;EAGzC,GAAI,QACD,EACA,WAAW,UAAkB,OAAe,QAC3C,MAAMA,QAAM,cAAc,QAAQ,CAAC,GAAG,OAAO,GAAG,EAClD,IACC,CAAC;EAEJ,MAAM,SAAS,UAAU,MAAM;GAC9B,MAAM,OAAO,cAAc,QAAQ;GACnC,IAAI,MAAM,YAAY,IAAI,GAAG,MAAM,OAAO,QAAQ,QAAQ;GAC1D,MAAM,OAAO,MAAM,MAAM,IAAIA,QAAM,IAAI,CAAC;GACxC,IAAI,SAAS,MAAM,MAAM,OAAO,QAAQ,QAAQ;GAChD,OAAO,gBAAgB,IAAI,MAAM,SAAS,YAAY,OAAO,IAAI,IAAI;EACtE;EAEA,MAAM,UAAU,UAAU,MAAM,OAAO;GACtC,MAAM,OAAO,cAAc,QAAQ;GACnC,IAAI,eAAe;IAClB,MAAM,QAAQ,WAAW,IAAI;IAG7B,IAAI,UAAU,MAAM,WAAW,IAAI,OAAO,SAAS;GACpD;GACA,MAAM,QAAQ,OAAO,SAAS,WAAW,YAAY,OAAO,IAAI,IAAI;GACpE,MAAM,MAAM,IAAIA,QAAM,IAAI,GAAG,KAAK;EACnC;EAEA,MAAM,OAAO,UAAU;GACtB,MAAM,IAAIA,QAAM,cAAc,QAAQ,CAAC;GACvC,IAAK,MAAM,MAAM,KAAK,CAAC,MAAO,MAAM,MAAM,OAAO,UAAU,QAAQ;GACnE,MAAM,MAAM,OAAO,CAAC;EACrB;EAEA,MAAM,QAAQ,SAAS;GACtB,MAAM,IAAIA,QAAM,cAAc,OAAO,CAAC;GACtC,MAAM,SAAS,MAAM,UAAU,MAAM;GACrC,MAAM,aAAa,UAAU,MAAM,KAAK,KAAK,GAAG,EAAE;GAClD,MAAM,EAAE,SAAS,aAAa,MAAM,MAAM,KAAK,YAAY,EAC1D,WAAW,IACZ,CAAC;GACD,IAAI,QAAQ,WAAW,KAAK,SAAS,WAAW,GAAG;IAClD,IAAI,CAAC,UAAW,MAAM,MAAM,KAAK,CAAC,MAAO,MACxC,MAAM,QAAQ,WAAW,OAAO;IAEjC,IAAI,CAAC,QAAQ,MAAM,OAAO,WAAW,OAAO;GAC7C;GACA,MAAM,QAAQ,QAAQ,KAAK,MAAM,EAAE,IAAI,MAAM,WAAW,MAAM,CAAC;GAC/D,MAAM,WAAW,SAAS,KAAK,MAC9B,EAAE,MAAM,WAAW,MAAM,CAAC,CAAC,QAAQ,OAAO,EAAE,CAC7C;GACA,OAAO,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,KAAK;EACrC;EAEA,MAAM,MAAM,UAAU,OAAO,CAE7B;EAEA,MAAM,MAAM,SAAS;GACpB,MAAM,IAAIA,QAAM,cAAc,OAAO,CAAC;GACtC,MAAM,EAAE,SAAS,aAAa,MAAM,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;GACpE,IAAI,QAAQ,SAAS,KAAK,SAAS,SAAS,GAC3C,MAAM,UAAU,SAAS,OAAO;EAGlC;EAEA,OAAO,aAAa,KAAK,UAAU,MAAM;EACzC,QAAQ,aAAa,KAAK,UAAU,OAAO;EAE3C,MAAM,SAAS,UAA0B;GACxC,MAAM,OAAO,YAAY,QAAQ;EAClC;EAEA,MAAM,QAAQ,SAAS,UAA0B;GAChD,MAAM,MAAM,WAAW,QAAQ;EAChC;EAEA,MAAM,MAAM,WAAW,OAAO,CAE9B;CACD;CAEA,eAAe,mBAAmB,QAA+B;EAChE,IAAI,CAAC,eAAe;EACpB,MAAM,QAAQ,cAAc,MAAM;EAIlC,IAAI,WAAW,IAAI,KAAK,GAAG;EAC3B,IAAI;GACH,MAAM,EAAE,YAAY,MAAM,MAAM,KAAK,GAAGA,QAAM,KAAK,EAAE,YAAY,EAChE,OAAO,EACR,CAAC;GAGD,MAAM,QAAQ,QAAQ,EAAE,EAAE;GAC1B,MAAM,OACL,UAAU,KAAA,KAAa,qBAAqB,KAAK,MAAM,OACpD,YACA;GACJ,WAAW,IAAI,OAAO,IAAI;GAC1B,SAAS,iBAAiB,KAAK,SAAS,OAAO;EAChD,QAAQ,CAER;CACD;CAEA,eAAe,cACd,QACA,iBACgB;EAChB,MAAM,WAAW,iBAAiB,YAAY;EAC9C,MAAM,UAAU,GAAG,cAAc,MAAM,EAAE;EAGzC,MAAM,UAAU,mBACf,MAAM,SAAS,QAAQ,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAC/C,CAAC,CACC,QAAQ,SAAS,yBAAyB,IAAI,KAAK,IAAI,CAAC,CAAC,CACzD,KAAK,SAAS,KAAK,IAAI;EACzB,IAAI,QAAQ,SAAS,WAAW,GAAG;GAClC,MAAM,mBAAmB,MAAM;GAC/B;EACD;EACA,MAAM,QAAQ,IAAI,CACjB,mBAAmB,MAAM,GACzB,GAAG,QAAQ,KAAK,SACf,SAAS,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC,YAAY,KAAA,CAAS,CAC9D,CACD,CAAC;CACF;CAEA,eAAe,qBAAqB,SAAoC;EACvE,MAAM,YAAYA,QAAM,cAAc,OAAO,CAAC;EAC9C,MAAM,aAAa,cAAc,KAAK,KAAK,GAAG,UAAU;EACxD,MAAM,EAAE,YAAY,MAAM,MAAM,KAAK,UAAU;EAC/C,OAAO,QACL,KAAK,WAAW,OAAO,IAAI,MAAM,WAAW,MAAM,CAAC,CAAC,CACpD,QAAQ,SAAS,KAAK,SAAS,CAAC,CAAC,CACjC,KAAK;CACR;CAEA,SAAS,eAAe,QAAgB,OAA2B;EAClE,kBAAkB,IACjB,cAAc,MAAM,GACpB,IAAI,IAAI,OAAO,eAAe,CAAC,CAAC,CACjC;CACD;CAEA,SAAS,WAAW,YAA0B;EAC7C,MAAM,aAAa,cAAc,UAAU;EAC3C,KAAK,MAAM,OAAO,kBAAkB,KAAK,GACxC,IAAI,IAAI,WAAW,UAAU,GAAG,kBAAkB,OAAO,GAAG;EAE7D,KAAK,MAAM,SAAS,WAAW,KAAK,GACnC,IAAI,MAAM,WAAW,UAAU,GAAG,WAAW,OAAO,KAAK;EAK1D,MAAM,aAAaA,QAAM,UAAU,CAAC;CACrC;CAEA,OAAO;EACN;EACA;EACA;EACA;EACA;EACA;CACD;AACD;;;AClXA,MAAM,QAAQ;AAEd,eAAe,UACd,OACA,IACgB;CAChB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,OACtC,MAAM,QAAQ,IAAI,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAErD;;;;;;;AAQA,eAAsB,aACrB,OACA,QACkB;CAClB,MAAM,EAAE,YAAY,MAAM,MAAM,KAAK,MAAM;CAC3C,MAAM,UAAU,UAAU,WAAW,MAAM,OAAO,OAAO,GAAG,CAAC;CAC7D,OAAO,QAAQ;AAChB;;;;;;;;AAiBA,eAAsB,WACrB,OACA,MACA,IACA,UAA6B,CAAC,GACZ;CAClB,IAAI,KAAK,WAAW,EAAE,KAAK,GAAG,WAAW,IAAI,GAC5C,MAAM,IAAI,MAAM,iCAAiC,KAAK,MAAM,GAAG,EAAE;CAElE,MAAM,OACL,QAAQ,SACP,OAAO,SAAiB,UAAkB;EAC1C,MAAM,OAAO,MAAM,MAAM,IAAI,OAAO;EACpC,IAAI,MAAM,MAAM,MAAM,IAAI,OAAO,IAAI;CACtC;CACD,MAAM,EAAE,YAAY,MAAM,MAAM,KAAK,IAAI;CACzC,MAAM,UAAU,UAAU,WACzB,KAAK,OAAO,KAAK,KAAK,OAAO,IAAI,MAAM,KAAK,MAAM,CAAC,CACpD;CACA,MAAM,UAAU,UAAU,WAAW,MAAM,OAAO,OAAO,GAAG,CAAC;CAC7D,OAAO,QAAQ;AAChB;;;;;;;AC1BA,IAAa,mBAAb,cAAsC,MAAM;CAC3C,OAAgB;CAEhB,cAAc;EACb,MAAM,mDAAmD;EACzD,KAAK,OAAO;CACb;AACD;AAEA,MAAM,kCAAkB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AAED,MAAM,kCAAkB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;;;AAQD,SAAgB,iBAAiB,OAAyB;CACzD,IAAI,iBAAiB,WAAW,OAAO;CACvC,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,MAAM;CAKZ,IAAI,IAAI,SAAS,KAAA,KAAa,gBAAgB,IAAI,IAAI,IAAI,GAAG,OAAO;CACpE,IAAI,IAAI,SAAS,KAAA,KAAa,gBAAgB,IAAI,IAAI,IAAI,GAAG,OAAO;CACpE,MAAM,SAAS,IAAI,WAAW;CAC9B,OAAO,WAAW,KAAA,MAAc,UAAU,OAAO,WAAW;AAC7D;AAEA,MAAM,SAAS,OAAe,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;;;;AAU9E,SAAgB,iBACf,OACA,UAAwB,CAAC,GACX;CACd,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,UACL,QAAQ,YAAY,QACjB,OACA;EACA,WAAW,QAAQ,SAAS,aAAa;EACzC,SAAS,QAAQ,SAAS,WAAW;CACtC;CAEH,IAAI,WAAW;CACf,IAAI,gBAAgB;CACpB,IAAI,QAAyC;CAC7C,IAAI,UAAU;;;;;;;;CASd,eAAe,QAAW,IAAkC;EAC3D,IAAI,YAAY,MAAM,OAAO,GAAG;EAChC,IAAI,UAAU;EACd,IAAI,UAAU,UAAU;GACvB,IACC,WACC,UAAU,UAAU,KAAK,IAAI,IAAI,gBAAgB,QAAQ,SAE1D,MAAM,IAAI,iBAAiB;GAE5B,QAAQ;GACR,UAAU;GACV,UAAU;EACX;EACA,IAAI;GACH,MAAM,SAAS,MAAM,GAAG;GACxB,WAAW;GACX,QAAQ;GACR,OAAO;EACR,SAAS,OAAO;GACf,IAAI,YAAY,KAAK,GAAG;IACvB;IACA,gBAAgB,KAAK,IAAI;IACzB,IAAI,YAAY,QAAQ,aAAa,SAAS,QAAQ;GACvD,OAAO,IAAI,SAAS;IAEnB,WAAW;IACX,QAAQ;GACT;GACA,MAAM;EACP,UAAU;GACT,IAAI,SAAS,UAAU;EACxB;CACD;CAEA,eAAe,IAAO,IAAY,KAAa,IAAsB;EACpE,IAAI;EACJ,KAAK,IAAI,UAAU,GAAG,WAAW,SAAS,WACzC,IAAI;GACH,OAAO,MAAM,QAAQ,EAAE;EACxB,SAAS,OAAO;GACf,YAAY;GACZ,IAAI,iBAAiB,kBAAkB,MAAM;GAC7C,IAAI,CAAC,YAAY,KAAK,KAAK,YAAY,SAAS,MAAM;GACtD,MAAM,OAAO,KAAK,IAAI,iBAAiB,KAAK,SAAS,UAAU;GAC/D,MAAM,UAAU,KAAK,MAAM,OAAO,KAAK,OAAO,IAAI,OAAO,MAAM;GAC/D,QAAQ,UAAU;IAAE;IAAK;IAAI,SAAS,UAAU;IAAG;GAAQ,CAAC;GAC5D,MAAM,MAAM,OAAO;EACpB;EAED,MAAM;CACP;CAMA,MAAM,QAAQ,MAAM,UAAU,KAAK,KAAK;CAExC,OAAO;EACN,MAAM,QAAQ,IAAI,OAAO,WAAW,MAAM,IAAI,GAAG,CAAC;EAClD,MAAM,KAAK,SAAS,IAAI,OAAO,WAAW,MAAM,IAAI,KAAK,IAAI,CAAC;EAC9D,SAAS,QAAQ,IAAI,UAAU,WAAW,MAAM,OAAO,GAAG,CAAC;EAC3D,OAAO,QAAQ,IAAI,QAAQ,WAAW,MAAM,KAAK,GAAG,CAAC;EACrD,OAAO,QAAQ,gBACd,IAAI,QAAQ,cAAc,MAAM,KAAK,QAAQ,WAAW,CAAC;EAC1D,GAAI,QACD,EACA,WAAW,KAAa,OAAe,QACtC,IAAI,YAAY,WAAW,MAAM,KAAK,OAAO,GAAG,CAAC,EACnD,IACC,CAAC;CACL;AACD"}
1
+ {"version":3,"file":"index.js","names":["toKey","toKeyWithPrefix"],"sources":["../src/cache.ts","../src/errors.ts","../src/path.ts","../src/git-fs.ts","../src/prefix.ts","../src/retry.ts"],"sourcesContent":["import { LRUCache } from \"lru-cache\";\nimport type {\n\tListOptions,\n\tListResult,\n\tObjectStat,\n\tObjectStore,\n} from \"./types.js\";\n\nexport interface CacheOptions {\n\t/** Maximum bytes of object data held in memory. Default 50 MiB. */\n\tmaxBytes?: number;\n\t/**\n\t * Largest single entry admitted to the cache. Defaults to a tenth of\n\t * `maxBytes` so one huge packfile cannot evict the whole working set.\n\t */\n\tmaxEntryBytes?: number;\n\t/**\n\t * Largest range read (`getRange`) admitted to the cache, in bytes. A range\n\t * read is a slice of a pack at a known offset: a header probe, or a small\n\t * object's bytes. Slices up to this size are cached; larger slices always\n\t * go to the backing store so a big blob cannot churn the cache. Range\n\t * entries have their own LRU, capped at a quarter of `maxBytes`. Default\n\t * 64 KiB; 0 disables range caching.\n\t */\n\tmaxRangeBytes?: number;\n\t/** Entry time-to-live in milliseconds. Default 60 000. */\n\tttlMs?: number;\n\t/**\n\t * Override the TTL for a specific key (get/head) or list prefix (list),\n\t * in milliseconds. Return `undefined` to fall back to `ttlMs`. Git refs\n\t * (`refs/heads/<branch>`, `HEAD`) are mutable — the same key's value\n\t * changes on every push — unlike content-addressed object keys, which\n\t * never change for a given key and are safe to cache for the full\n\t * `ttlMs`. Without this, a long `ttlMs` tuned for objects also caches\n\t * ref reads that long, so a warm process can keep serving a\n\t * pre-push ref value for the rest of that TTL even though nothing\n\t * changed *this* process's own cache (see `invalidate`) — it just never\n\t * knew to. Give ref-like keys a short override (a few seconds) instead:\n\t * a ref read is one small object, so re-reading it far more often than\n\t * `ttlMs` is cheap, and every read downstream of a fresh ref (tree,\n\t * commit, blob — all keyed by the sha it resolves to) still gets the\n\t * full-length cache/coalescing benefit.\n\t */\n\tttlForKey?: (key: string) => number | undefined;\n\t/**\n\t * Also cache \"key does not exist\" results. Loose-object probes on packed\n\t * repositories are almost always misses, so this saves many round trips —\n\t * but only enable it when a single process is the only writer, otherwise\n\t * another instance's push can be masked for up to `ttlMs`.\n\t */\n\tcacheMisses?: boolean;\n\t/**\n\t * Also cache `list()` results (directory listings and `limit: 1`\n\t * existence probes). Writes through this store keep cached listings\n\t * consistent; after writing to the backend by any other means, call\n\t * `invalidate()` with the affected prefix. Default false.\n\t */\n\tcacheLists?: boolean;\n\t/**\n\t * Collapse concurrent `get`/`head`/`list` calls for the same key into a\n\t * single backend request. Default true.\n\t */\n\tcoalesce?: boolean;\n\t/**\n\t * Return defensive copies from `get` (and copy on admit), so a caller\n\t * mutating returned bytes can't corrupt the cache. Default true. Set\n\t * false when no reader mutates results (isomorphic-git doesn't): every\n\t * hit otherwise copies the whole entry, which for packfiles is megabytes\n\t * per read.\n\t */\n\tcopy?: boolean;\n\t/** Called when a read is answered from cache. */\n\tonHit?: (key: string) => void;\n\t/** Called when a read has to go to the backing store. */\n\tonMiss?: (key: string) => void;\n}\n\n/** An {@link ObjectStore} wrapper that also supports explicit invalidation. */\nexport interface CachedObjectStore extends ObjectStore {\n\t/**\n\t * Drop every cached entry — contents, misses, and listings — whose key\n\t * falls under `prefix` (exact keys included). Call this after the backing\n\t * store was modified by something other than this wrapper.\n\t */\n\tinvalidate(prefix: string): void;\n}\n\nconst MISS = Symbol(\"miss\");\ntype CacheEntry = Uint8Array | typeof MISS;\n\ninterface ListEntry {\n\tresult: ListResult;\n\t/** The raw list prefix this entry describes. */\n\tprefix: string;\n\t/** True for `limit: 1` existence probes. */\n\tprobe: boolean;\n\t/** True when the listing came back with no objects or prefixes. */\n\tempty: boolean;\n}\n\nfunction listEntrySize(entry: ListEntry): number {\n\tlet size = entry.prefix.length + 16;\n\tfor (const o of entry.result.objects) size += o.key.length + 8;\n\tfor (const p of entry.result.prefixes) size += p.length;\n\treturn size;\n}\n\nfunction copyListResult(result: ListResult): ListResult {\n\treturn {\n\t\tobjects: result.objects.map((o) => ({ ...o })),\n\t\tprefixes: [...result.prefixes],\n\t};\n}\n\n/**\n * Wrap an {@link ObjectStore} with an in-process LRU read cache.\n *\n * Git object keys are content-addressed and therefore immutable, which makes\n * them ideal cache entries; mutable keys (refs, packed-refs) are bounded by\n * `ttlMs`. Writes and deletes through this wrapper invalidate their key and\n * any cached listings they affect — with one asymmetry: a non-empty `limit: 1`\n * probe (a \"directory exists\" answer) survives writes underneath it, because\n * adding a key below a prefix cannot make that prefix stop existing, while\n * empty probes and full listings are always dropped.\n */\nexport function createCachedStore(\n\tstore: ObjectStore,\n\toptions: CacheOptions = {},\n): CachedObjectStore {\n\tconst maxBytes = options.maxBytes ?? 50 * 1024 * 1024;\n\tconst maxEntryBytes = options.maxEntryBytes ?? Math.ceil(maxBytes / 10);\n\tconst ttl = options.ttlMs ?? 60_000;\n\tconst ttlForKey = options.ttlForKey;\n\tconst cacheMisses = options.cacheMisses ?? false;\n\tconst cacheLists = options.cacheLists ?? false;\n\tconst range = store.getRange?.bind(store);\n\tconst maxRangeBytes = options.maxRangeBytes ?? 64 * 1024;\n\tconst coalesce = options.coalesce ?? true;\n\tconst copy = options.copy ?? true;\n\tconst clone = (data: Uint8Array) => (copy ? data.slice() : data);\n\tconst onHit = options.onHit;\n\tconst onMiss = options.onMiss;\n\n\tconst cache = new LRUCache<string, CacheEntry>({\n\t\tmaxSize: maxBytes,\n\t\tsizeCalculation: (value) => (value === MISS ? 1 : value.byteLength || 1),\n\t\tttl,\n\t});\n\tconst listCache = new LRUCache<string, ListEntry>({\n\t\tmaxSize: Math.max(1, Math.ceil(maxBytes / 10)),\n\t\tsizeCalculation: listEntrySize,\n\t\tttl,\n\t});\n\n\t// Range slices, keyed `${key}\\0${start}\\0${end}`. Separate from `cache`\n\t// because a slice is not the whole object: `get`/`head` must never see it.\n\t// `rangeKeysByObject` indexes them by object key so a write to one object\n\t// drops only that object's slices; `dispose` keeps it in step with LRU\n\t// eviction and TTL expiry.\n\tconst rangeKeysByObject = new Map<string, Set<string>>();\n\tconst rangeCache = new LRUCache<string, Uint8Array>({\n\t\tmaxSize: Math.max(1, Math.ceil(maxBytes / 4)),\n\t\tsizeCalculation: (value) => value.byteLength || 1,\n\t\tttl,\n\t\tdispose: (_value, rangeKey) => {\n\t\t\tconst key = rangeKey.slice(0, rangeKey.indexOf(\"\\0\"));\n\t\t\tconst keys = rangeKeysByObject.get(key);\n\t\t\tif (keys?.delete(rangeKey) && keys.size === 0) {\n\t\t\t\trangeKeysByObject.delete(key);\n\t\t\t}\n\t\t},\n\t});\n\tconst pendingRanges = new Map<string, Promise<Uint8Array | null>>();\n\tconst rangePrefix = (key: string) => `${key}\\0`;\n\tconst admitRange = (key: string, rangeKey: string, data: Uint8Array) => {\n\t\trangeCache.set(rangeKey, clone(data), { ttl: ttlForKey?.(key) });\n\t\t// lru-cache silently rejects an entry larger than maxSize, without\n\t\t// calling `dispose`; indexing it would leak.\n\t\tif (!rangeCache.has(rangeKey)) return;\n\t\tconst keys = rangeKeysByObject.get(key) ?? new Set<string>();\n\t\tkeys.add(rangeKey);\n\t\trangeKeysByObject.set(key, keys);\n\t};\n\tconst pendingRangeObjects = () =>\n\t\t[...pendingRanges.keys()].map((k) => k.slice(0, k.indexOf(\"\\0\")));\n\t/** Drop every slice (cached or in flight) of the object at `key`. */\n\tconst dropRanges = (key: string) => {\n\t\tfor (const rangeKey of [...(rangeKeysByObject.get(key) ?? [])]) {\n\t\t\trangeCache.delete(rangeKey);\n\t\t}\n\t\tconst prefix = rangePrefix(key);\n\t\tfor (const rangeKey of [...pendingRanges.keys()]) {\n\t\t\tif (rangeKey.startsWith(prefix)) pendingRanges.delete(rangeKey);\n\t\t}\n\t};\n\n\tconst pendingGets = new Map<string, Promise<Uint8Array | null>>();\n\tconst pendingHeads = new Map<string, Promise<ObjectStat | null>>();\n\tconst pendingLists = new Map<string, Promise<ListResult>>();\n\n\t/**\n\t * Bumped by every write, delete and invalidation. A backend read that\n\t * started before one of those may finish after it carrying the old answer\n\t * (the old bytes, or \"missing\" for a key that now exists); it still\n\t * answers its own callers, but only a read that saw no write in between\n\t * may populate the cache — otherwise a stale miss could hide a freshly\n\t * written object for the whole TTL.\n\t */\n\tlet generation = 0;\n\tconst unchangedSince = (start: number) => generation === start;\n\n\tconst admit = (key: string, data: Uint8Array) => {\n\t\tif (data.byteLength <= maxEntryBytes) {\n\t\t\tcache.set(key, clone(data), { ttl: ttlForKey?.(key) });\n\t\t}\n\t};\n\n\t/** Drop list entries a write/delete at `key` may have made stale. */\n\tfunction clearStaleListEntries(key: string): void {\n\t\tfor (const [listKey, entry] of listCache.entries()) {\n\t\t\tif (!key.startsWith(entry.prefix)) continue;\n\t\t\tif (entry.probe && !entry.empty) continue;\n\t\t\tlistCache.delete(listKey);\n\t\t}\n\t\t// Readers arriving after this write must not join a listing that\n\t\t// started before it.\n\t\tfor (const listKey of pendingLists.keys()) {\n\t\t\tconst prefix = listKey.split(\"|\").slice(2).join(\"|\");\n\t\t\tif (key.startsWith(prefix)) pendingLists.delete(listKey);\n\t\t}\n\t}\n\n\t/** A write to `key` happened: no in-flight read of it may be joined or cached. */\n\tfunction noteWrite(key: string): void {\n\t\tgeneration++;\n\t\tpendingGets.delete(key);\n\t\tpendingHeads.delete(key);\n\t\tdropRanges(key);\n\t}\n\n\tfunction coalesced<T>(\n\t\tpending: Map<string, Promise<T>>,\n\t\tkey: string,\n\t\tfn: () => Promise<T>,\n\t): Promise<T> {\n\t\tif (!coalesce) return fn();\n\t\tconst inflight = pending.get(key);\n\t\tif (inflight !== undefined) return inflight;\n\t\tconst p = fn().finally(() => {\n\t\t\tif (pending.get(key) === p) pending.delete(key);\n\t\t});\n\t\tpending.set(key, p);\n\t\treturn p;\n\t}\n\n\treturn {\n\t\tasync get(key: string): Promise<Uint8Array | null> {\n\t\t\tconst cached = cache.get(key);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tonHit?.(key);\n\t\t\t\treturn cached === MISS ? null : clone(cached);\n\t\t\t}\n\t\t\tconst data = await coalesced(pendingGets, key, async () => {\n\t\t\t\tonMiss?.(key);\n\t\t\t\tconst start = generation;\n\t\t\t\tconst fetched = await store.get(key);\n\t\t\t\tif (unchangedSince(start)) {\n\t\t\t\t\tif (fetched !== null) {\n\t\t\t\t\t\tadmit(key, fetched);\n\t\t\t\t\t} else if (cacheMisses) {\n\t\t\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn fetched;\n\t\t\t});\n\t\t\treturn data === null ? null : clone(data);\n\t\t},\n\n\t\tasync put(key: string, data: Uint8Array): Promise<void> {\n\t\t\tnoteWrite(key);\n\t\t\ttry {\n\t\t\t\tawait store.put(key, data);\n\t\t\t} finally {\n\t\t\t\t// Even a failed put may have landed; never keep serving the\n\t\t\t\t// pre-write answer for this key.\n\t\t\t\tnoteWrite(key);\n\t\t\t\tcache.delete(key);\n\t\t\t\tclearStaleListEntries(key);\n\t\t\t}\n\t\t\tadmit(key, data);\n\t\t},\n\n\t\tasync delete(key: string): Promise<void> {\n\t\t\tnoteWrite(key);\n\t\t\ttry {\n\t\t\t\tawait store.delete(key);\n\t\t\t} finally {\n\t\t\t\tnoteWrite(key);\n\t\t\t\tcache.delete(key);\n\t\t\t\tclearStaleListEntries(key);\n\t\t\t}\n\t\t\tif (cacheMisses) {\n\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\n\t\t\t} else {\n\t\t\t\tcache.delete(key);\n\t\t\t}\n\t\t},\n\n\t\tasync head(key: string): Promise<ObjectStat | null> {\n\t\t\tconst cached = cache.get(key);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tonHit?.(key);\n\t\t\t\treturn cached === MISS ? null : { size: cached.byteLength };\n\t\t\t}\n\t\t\treturn coalesced(pendingHeads, key, async () => {\n\t\t\t\tonMiss?.(key);\n\t\t\t\tconst start = generation;\n\t\t\t\tconst stat = await store.head(key);\n\t\t\t\tif (stat === null && cacheMisses && unchangedSince(start)) {\n\t\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\n\t\t\t\t}\n\t\t\t\treturn stat;\n\t\t\t});\n\t\t},\n\n\t\tasync list(prefix: string, listOptions?: ListOptions): Promise<ListResult> {\n\t\t\tif (!cacheLists) return store.list(prefix, listOptions);\n\t\t\tconst listKey = `${listOptions?.delimiter ?? \"\"}|${listOptions?.limit ?? \"\"}|${prefix}`;\n\t\t\tconst cached = listCache.get(listKey);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tonHit?.(prefix);\n\t\t\t\treturn copyListResult(cached.result);\n\t\t\t}\n\t\t\tconst result = await coalesced(pendingLists, listKey, async () => {\n\t\t\t\tonMiss?.(prefix);\n\t\t\t\tconst start = generation;\n\t\t\t\tconst fetched = await store.list(prefix, listOptions);\n\t\t\t\tif (!unchangedSince(start)) return fetched;\n\t\t\t\tlistCache.set(\n\t\t\t\t\tlistKey,\n\t\t\t\t\t{\n\t\t\t\t\t\tresult: copyListResult(fetched),\n\t\t\t\t\t\tprefix,\n\t\t\t\t\t\tprobe: listOptions?.limit === 1,\n\t\t\t\t\t\tempty:\n\t\t\t\t\t\t\tfetched.objects.length === 0 && fetched.prefixes.length === 0,\n\t\t\t\t\t},\n\t\t\t\t\t{ ttl: ttlForKey?.(prefix) },\n\t\t\t\t);\n\t\t\t\treturn fetched;\n\t\t\t});\n\t\t\treturn copyListResult(result);\n\t\t},\n\n\t\t// Small slices are cached (see `maxRangeBytes`); larger ones go straight\n\t\t// to the backing store.\n\t\t...(range\n\t\t\t? {\n\t\t\t\t\tasync getRange(\n\t\t\t\t\t\tkey: string,\n\t\t\t\t\t\tstart: number,\n\t\t\t\t\t\tend: number,\n\t\t\t\t\t): Promise<Uint8Array | null> {\n\t\t\t\t\t\tif (end - start + 1 > maxRangeBytes) return range(key, start, end);\n\t\t\t\t\t\tconst rangeKey = `${rangePrefix(key)}${start}\\0${end}`;\n\t\t\t\t\t\tconst cached = rangeCache.get(rangeKey);\n\t\t\t\t\t\tif (cached !== undefined) {\n\t\t\t\t\t\t\tonHit?.(key);\n\t\t\t\t\t\t\treturn clone(cached);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst data = await coalesced(pendingRanges, rangeKey, async () => {\n\t\t\t\t\t\t\tonMiss?.(key);\n\t\t\t\t\t\t\tconst gen = generation;\n\t\t\t\t\t\t\tconst fetched = await range(key, start, end);\n\t\t\t\t\t\t\tif (fetched !== null && unchangedSince(gen)) {\n\t\t\t\t\t\t\t\tadmitRange(key, rangeKey, fetched);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn fetched;\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn data === null ? null : clone(data);\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\n\t\tinvalidate(prefix: string): void {\n\t\t\tgeneration++;\n\t\t\tfor (const key of [\n\t\t\t\t...rangeKeysByObject.keys(),\n\t\t\t\t...pendingRangeObjects(),\n\t\t\t]) {\n\t\t\t\tif (key.startsWith(prefix)) dropRanges(key);\n\t\t\t}\n\t\t\tfor (const key of [...pendingGets.keys()]) {\n\t\t\t\tif (key.startsWith(prefix)) pendingGets.delete(key);\n\t\t\t}\n\t\t\tfor (const key of [...pendingHeads.keys()]) {\n\t\t\t\tif (key.startsWith(prefix)) pendingHeads.delete(key);\n\t\t\t}\n\t\t\tpendingLists.clear();\n\t\t\tfor (const key of cache.keys()) {\n\t\t\t\tif (key.startsWith(prefix)) cache.delete(key);\n\t\t\t}\n\t\t\tfor (const [listKey, entry] of listCache.entries()) {\n\t\t\t\tif (\n\t\t\t\t\tentry.prefix.startsWith(prefix) ||\n\t\t\t\t\tprefix.startsWith(entry.prefix)\n\t\t\t\t) {\n\t\t\t\t\tlistCache.delete(listKey);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n","/**\n * Node-style filesystem error carrying a `code` property, which is what\n * isomorphic-git inspects to distinguish \"file not found\" from real failures.\n */\nexport class FsError extends Error {\n\treadonly code: string;\n\treadonly syscall: string;\n\treadonly path: string;\n\n\tconstructor(code: string, syscall: string, path: string) {\n\t\tsuper(`${code}: ${syscall} '${path}'`);\n\t\tthis.name = \"FsError\";\n\t\tthis.code = code;\n\t\tthis.syscall = syscall;\n\t\tthis.path = path;\n\t}\n}\n\nexport const enoent = (syscall: string, path: string) =>\n\tnew FsError(\"ENOENT\", syscall, path);\n\nexport const enotdir = (syscall: string, path: string) =>\n\tnew FsError(\"ENOTDIR\", syscall, path);\n\nexport const enotempty = (syscall: string, path: string) =>\n\tnew FsError(\"ENOTEMPTY\", syscall, path);\n\nexport const einval = (syscall: string, path: string) =>\n\tnew FsError(\"EINVAL\", syscall, path);\n\nexport const eperm = (syscall: string, path: string) =>\n\tnew FsError(\"EPERM\", syscall, path);\n","import { einval } from \"./errors.js\";\n\n/**\n * Normalize an absolute-or-relative filesystem path into a storage key\n * segment: no leading/trailing slashes, `.` segments dropped, `..` resolved.\n * A `..` that would escape the root throws EINVAL — paths handed to the fs\n * must never address keys outside the configured prefix.\n */\nexport function normalizePath(filepath: string): string {\n\tconst segments = filepath.split(\"/\");\n\tconst out: string[] = [];\n\tfor (const segment of segments) {\n\t\tif (segment === \"\" || segment === \".\") continue;\n\t\tif (segment === \"..\") {\n\t\t\tif (out.length === 0) throw einval(\"resolve\", filepath);\n\t\t\tout.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tout.push(segment);\n\t}\n\treturn out.join(\"/\");\n}\n\n/** Join a configured key prefix with a normalized path. */\nexport function toKey(prefix: string, filepath: string): string {\n\tconst normalized = normalizePath(filepath);\n\tif (prefix === \"\") return normalized;\n\treturn normalized === \"\" ? prefix : `${prefix}/${normalized}`;\n}\n","import { LRUCache } from \"lru-cache\";\nimport { enoent, enotdir, enotempty, eperm } from \"./errors.js\";\nimport { parseLooseObjectPath } from \"./git-layout.js\";\nimport { parsePackDirectory, READABLE_PACK_FILE_KINDS } from \"./pack-files.js\";\nimport { normalizePath, toKey as toKeyWithPrefix } from \"./path.js\";\nimport type {\n\tEncoding,\n\tGitdirFacts,\n\tGitFsClient,\n\tGitFsOptions,\n\tObjectStore,\n\tReadFileOptions,\n\tStat,\n\tWriteFileOptions,\n} from \"./types.js\";\n\nconst FILE_MODE = 0o100644;\nconst DIR_MODE = 0o40000;\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nfunction makeStat(type: \"file\" | \"dir\", size: number): Stat {\n\tconst epoch = new Date(0);\n\treturn {\n\t\ttype,\n\t\tmode: type === \"file\" ? FILE_MODE : DIR_MODE,\n\t\tsize,\n\t\tino: 0,\n\t\tmtimeMs: 0,\n\t\tctimeMs: 0,\n\t\tuid: 0,\n\t\tgid: 0,\n\t\tdev: 0,\n\t\tmtime: epoch,\n\t\tctime: epoch,\n\t\tisFile: () => type === \"file\",\n\t\tisDirectory: () => type === \"dir\",\n\t\tisSymbolicLink: () => false,\n\t};\n}\n\nfunction resolveEncoding(\n\toptions?: ReadFileOptions | WriteFileOptions | Encoding,\n): Encoding | undefined {\n\tif (typeof options === \"string\") return options;\n\treturn options?.encoding;\n}\n\n/**\n * The filesystem returned by {@link createGitFs}: the isomorphic-git client\n * plus git-aware maintenance hooks.\n */\nexport interface GitFs extends GitFsClient {\n\t/**\n\t * Return every file below a directory in one object-store listing. Unlike\n\t * `readdir`, this deliberately does not use a delimiter: object storage can\n\t * enumerate a ref namespace recursively without the stat-per-entry walk a\n\t * POSIX filesystem needs to distinguish files from implicit directories.\n\t * Paths are relative to `dirpath` and never include directory entries.\n\t */\n\tlistFilesRecursively(dirpath: string): Promise<string[]>;\n\t/**\n\t * Probe, with one bounded list, whether `gitdir` contains any loose\n\t * objects, and remember the answer. This is the only way a loose-object\n\t * hint is ever created; call it before full-history walks (commit logs,\n\t * reachability traversals) so fully packed repositories skip every\n\t * guaranteed-miss loose-object read. A later loose write flips the hint\n\t * back, so it cannot go stale mid-push.\n\t */\n\tdetectLooseObjects(gitdir: string): Promise<void>;\n\t/**\n\t * Warm the cache with every pack file under `gitdir` in parallel (plus\n\t * the loose-object hint) before a sequential history walk. Skipped when\n\t * the pack directory holds more than `maxPacks * 2` entries — warming\n\t * only helps when the cache budget actually fits the packs.\n\t */\n\tprefetchPacks(gitdir: string, options?: { maxPacks?: number }): Promise<void>;\n\t/**\n\t * Clear fs-level state (loose-object hints) under `pathPrefix`, and\n\t * forward to the store's `invalidate` when it has one. Call after the\n\t * backing store was modified by something other than this fs.\n\t */\n\tinvalidate(pathPrefix: string): void;\n\t/**\n\t * Declare that `gitdir` exists, so `stat` answers \"directory\" for it with\n\t * zero store calls. isomorphic-git stats the gitdir on every command\n\t * (`discoverGitdir`); in object storage that is a HEAD that always misses\n\t * plus a listing. Register once the caller has confirmed the repository\n\t * exists by other means (e.g. its database row); `invalidate` forgets it.\n\t */\n\tregisterGitdir(gitdir: string, facts?: GitdirFacts): void;\n}\n\n/**\n * Create a promise-based filesystem client for isomorphic-git backed by an\n * {@link ObjectStore}.\n *\n * Semantics:\n * - Directories are implicit, as in object storage: `mkdir` is a no-op and a\n * directory \"exists\" whenever at least one key lives under its prefix.\n * - Symbolic links are not supported (`readlink`/`symlink` throw). Bare\n * repositories never contain them.\n * - Designed for bare, server-side repositories (`git.init({bare: true})`,\n * plumbing commands, ref updates). Worktree checkouts belong on a real disk.\n */\nexport function createGitFs(\n\tstore: ObjectStore,\n\toptions: GitFsOptions = {},\n): GitFs {\n\tconst prefix = options.prefix ?? \"\";\n\tconst useLooseHints = options.looseObjectHints ?? false;\n\tconst onNote = options.onNote;\n\n\tconst toKey = (path: string): string => toKeyWithPrefix(prefix, path);\n\n\t/**\n\t * Per-gitdir \"does any loose object exist\" hint. Entries are only created\n\t * by {@link GitFs.detectLooseObjects}, so a pathological ref that merely\n\t * looks like a loose object (`refs/heads/objects/aa/…`) derives a scope\n\t * that was never registered and can never be wrongly short-circuited.\n\t */\n\tconst looseHints = new LRUCache<string, \"none\" | \"present\">({\n\t\tmax: 1024,\n\t\tttl: options.hintTtlMs ?? 3_600_000,\n\t});\n\n\t/** Gitdirs the caller vouched for via {@link GitFs.registerGitdir}. */\n\tconst registeredGitdirs = new LRUCache<string, ReadonlySet<string>>({\n\t\tmax: 4096,\n\t});\n\n\t/** Whether `path` is a top-level file its registered gitdir lacks. */\n\tfunction registeredAbsent(path: string): boolean {\n\t\tconst slash = path.lastIndexOf(\"/\");\n\t\tif (slash < 0) return false;\n\t\tconst absent = registeredGitdirs.get(path.slice(0, slash));\n\t\treturn absent?.has(path.slice(slash + 1)) ?? false;\n\t}\n\n\t/** The gitdir scope of a loose-object path, or null when it isn't one. */\n\tfunction looseScope(path: string): string | null {\n\t\treturn parseLooseObjectPath(path)?.gitdir ?? null;\n\t}\n\n\tconst pendingDetections = new Map<string, Promise<void>>();\n\n\t/**\n\t * Resolve the loose-object hint for `path`'s gitdir before answering, so a\n\t * read that runs before any explicit `detectLooseObjects` call (e.g. the\n\t * first commit read of a request) doesn't pay a doomed GET. One coalesced\n\t * listing per gitdir, cached for `hintTtlMs`.\n\t */\n\tasync function knownAbsent(path: string): Promise<boolean> {\n\t\tif (registeredAbsent(path)) return true;\n\t\tif (!useLooseHints) return false;\n\t\tconst scope = looseScope(path);\n\t\tif (scope === null) return false;\n\t\tif (!looseHints.has(scope)) {\n\t\t\tlet pending = pendingDetections.get(scope);\n\t\t\tif (!pending) {\n\t\t\t\tpending = detectLooseObjects(scope).finally(() =>\n\t\t\t\t\tpendingDetections.delete(scope),\n\t\t\t\t);\n\t\t\t\tpendingDetections.set(scope, pending);\n\t\t\t}\n\t\t\tawait pending;\n\t\t}\n\t\treturn looseHints.get(scope) === \"none\";\n\t}\n\n\tasync function isDirectory(dirKey: string): Promise<boolean> {\n\t\tconst { objects, prefixes } = await store.list(`${dirKey}/`, {\n\t\t\tlimit: 1,\n\t\t});\n\t\treturn objects.length > 0 || prefixes.length > 0;\n\t}\n\n\tasync function stat(filepath: string, syscall: string): Promise<Stat> {\n\t\tconst path = normalizePath(filepath);\n\t\tif (await knownAbsent(path)) throw enoent(syscall, filepath);\n\t\tconst k = toKey(path);\n\t\tif (k === prefix || k === \"\" || registeredGitdirs.has(path)) {\n\t\t\treturn makeStat(\"dir\", 0);\n\t\t}\n\t\tconst fileStat = await store.head(k);\n\t\tif (fileStat) return makeStat(\"file\", fileStat.size);\n\t\t// A loose-object path is always a leaf; when the object itself is\n\t\t// absent there is no point probing for a directory of the same name.\n\t\tif (useLooseHints && looseScope(path) !== null) {\n\t\t\tthrow enoent(syscall, filepath);\n\t\t}\n\t\tif (await isDirectory(k)) return makeStat(\"dir\", 0);\n\t\tthrow enoent(syscall, filepath);\n\t}\n\n\tconst range = store.getRange?.bind(store);\n\n\tconst promises: GitFsClient[\"promises\"] = {\n\t\t// Only defined when the backing store supports ranges, so a caller can\n\t\t// test for it rather than have to interpret a failure.\n\t\t...(range\n\t\t\t? {\n\t\t\t\t\tgetRange: (filepath: string, start: number, end: number) =>\n\t\t\t\t\t\trange(toKey(normalizePath(filepath)), start, end),\n\t\t\t\t}\n\t\t\t: {}),\n\n\t\tasync readFile(filepath, opts) {\n\t\t\tconst path = normalizePath(filepath);\n\t\t\tif (await knownAbsent(path)) throw enoent(\"open\", filepath);\n\t\t\tconst data = await store.get(toKey(path));\n\t\t\tif (data === null) throw enoent(\"open\", filepath);\n\t\t\treturn resolveEncoding(opts) === \"utf8\" ? textDecoder.decode(data) : data;\n\t\t},\n\n\t\tasync writeFile(filepath, data, _opts) {\n\t\t\tconst path = normalizePath(filepath);\n\t\t\tif (useLooseHints) {\n\t\t\t\tconst scope = looseScope(path);\n\t\t\t\t// Flip before the write lands so a racing read can never\n\t\t\t\t// short-circuit an object that is in the middle of arriving.\n\t\t\t\tif (scope !== null) looseHints.set(scope, \"present\");\n\t\t\t}\n\t\t\tconst bytes = typeof data === \"string\" ? textEncoder.encode(data) : data;\n\t\t\tawait store.put(toKey(path), bytes);\n\t\t},\n\n\t\tasync unlink(filepath) {\n\t\t\tconst k = toKey(normalizePath(filepath));\n\t\t\tif ((await store.head(k)) === null) throw enoent(\"unlink\", filepath);\n\t\t\tawait store.delete(k);\n\t\t},\n\n\t\tasync readdir(dirpath) {\n\t\t\tconst k = toKey(normalizePath(dirpath));\n\t\t\tconst isRoot = k === prefix || k === \"\";\n\t\t\tconst listPrefix = isRoot && k === \"\" ? \"\" : `${k}/`;\n\t\t\tconst { objects, prefixes } = await store.list(listPrefix, {\n\t\t\t\tdelimiter: \"/\",\n\t\t\t});\n\t\t\tif (objects.length === 0 && prefixes.length === 0) {\n\t\t\t\tif (!isRoot && (await store.head(k)) !== null) {\n\t\t\t\t\tthrow enotdir(\"scandir\", dirpath);\n\t\t\t\t}\n\t\t\t\tif (!isRoot) throw enoent(\"scandir\", dirpath);\n\t\t\t}\n\t\t\tconst names = objects.map((o) => o.key.slice(listPrefix.length));\n\t\t\tconst dirNames = prefixes.map((p) =>\n\t\t\t\tp.slice(listPrefix.length).replace(/\\/$/, \"\"),\n\t\t\t);\n\t\t\treturn [...names, ...dirNames].sort();\n\t\t},\n\n\t\tasync mkdir(_dirpath, _opts) {\n\t\t\t// Directories are implicit in object storage.\n\t\t},\n\n\t\tasync rmdir(dirpath) {\n\t\t\tconst k = toKey(normalizePath(dirpath));\n\t\t\tconst { objects, prefixes } = await store.list(`${k}/`, { limit: 1 });\n\t\t\tif (objects.length > 0 || prefixes.length > 0) {\n\t\t\t\tthrow enotempty(\"rmdir\", dirpath);\n\t\t\t}\n\t\t\t// Empty implicit directories don't exist; nothing to remove.\n\t\t},\n\n\t\tstat: (filepath) => stat(filepath, \"stat\"),\n\t\tlstat: (filepath) => stat(filepath, \"lstat\"),\n\n\t\tasync readlink(filepath): Promise<never> {\n\t\t\tthrow enoent(\"readlink\", filepath);\n\t\t},\n\n\t\tasync symlink(_target, filepath): Promise<never> {\n\t\t\tthrow eperm(\"symlink\", filepath);\n\t\t},\n\n\t\tasync chmod(_filepath, _mode) {\n\t\t\t// POSIX modes don't exist in object storage.\n\t\t},\n\t};\n\n\tasync function detectLooseObjects(gitdir: string): Promise<void> {\n\t\tif (!useLooseHints) return;\n\t\tconst scope = normalizePath(gitdir);\n\t\t// A live hint must win over re-detection: after a loose write flips it\n\t\t// to \"present\", re-deriving from a (possibly cached, pre-write) listing\n\t\t// could wrongly reinstate \"none\" and mask real objects.\n\t\tif (looseHints.has(scope)) return;\n\t\ttry {\n\t\t\tconst { objects } = await store.list(`${toKey(scope)}/objects/`, {\n\t\t\t\tlimit: 1,\n\t\t\t});\n\t\t\t// Loose fan-out directories (two hex digits) sort before \"info/\"\n\t\t\t// and \"pack/\", so when any loose object exists it is the first key.\n\t\t\tconst first = objects[0]?.key;\n\t\t\tconst hint =\n\t\t\t\tfirst !== undefined && parseLooseObjectPath(first) !== null\n\t\t\t\t\t? \"present\"\n\t\t\t\t\t: \"none\";\n\t\t\tlooseHints.set(scope, hint);\n\t\t\tonNote?.(`loose objects ${hint} under ${scope}`);\n\t\t} catch {\n\t\t\t// Leave unknown — reads fall back to their normal round trip.\n\t\t}\n\t}\n\n\tasync function prefetchPacks(\n\t\tgitdir: string,\n\t\tprefetchOptions?: { maxPacks?: number },\n\t): Promise<void> {\n\t\tconst maxPacks = prefetchOptions?.maxPacks ?? 30;\n\t\tconst packDir = `${normalizePath(gitdir)}/objects/pack`;\n\t\t// isomorphic-git never reads .bitmap/.rev/.keep, so fetching them is a\n\t\t// wasted GET and wasted cache space.\n\t\tconst entries = parsePackDirectory(\n\t\t\tawait promises.readdir(packDir).catch(() => []),\n\t\t)\n\t\t\t.filter((file) => READABLE_PACK_FILE_KINDS.has(file.kind))\n\t\t\t.map((file) => file.name);\n\t\tif (entries.length > maxPacks * 2) {\n\t\t\tawait detectLooseObjects(gitdir);\n\t\t\treturn;\n\t\t}\n\t\tawait Promise.all([\n\t\t\tdetectLooseObjects(gitdir),\n\t\t\t...entries.map((name) =>\n\t\t\t\tpromises.readFile(`${packDir}/${name}`).catch(() => undefined),\n\t\t\t),\n\t\t]);\n\t}\n\n\tasync function listFilesRecursively(dirpath: string): Promise<string[]> {\n\t\tconst directory = toKey(normalizePath(dirpath));\n\t\tconst listPrefix = directory === \"\" ? \"\" : `${directory}/`;\n\t\tconst { objects } = await store.list(listPrefix);\n\t\treturn objects\n\t\t\t.map((object) => object.key.slice(listPrefix.length))\n\t\t\t.filter((path) => path.length > 0)\n\t\t\t.sort();\n\t}\n\n\tfunction registerGitdir(gitdir: string, facts?: GitdirFacts): void {\n\t\tregisteredGitdirs.set(\n\t\t\tnormalizePath(gitdir),\n\t\t\tnew Set(facts?.absentFiles ?? []),\n\t\t);\n\t}\n\n\tfunction invalidate(pathPrefix: string): void {\n\t\tconst normalized = normalizePath(pathPrefix);\n\t\tfor (const dir of registeredGitdirs.keys()) {\n\t\t\tif (dir.startsWith(normalized)) registeredGitdirs.delete(dir);\n\t\t}\n\t\tfor (const scope of looseHints.keys()) {\n\t\t\tif (scope.startsWith(normalized)) looseHints.delete(scope);\n\t\t}\n\t\tconst maybe = store as ObjectStore & {\n\t\t\tinvalidate?: (prefix: string) => void;\n\t\t};\n\t\tmaybe.invalidate?.(toKey(normalized));\n\t}\n\n\treturn {\n\t\tpromises,\n\t\tlistFilesRecursively,\n\t\tdetectLooseObjects,\n\t\tprefetchPacks,\n\t\tinvalidate,\n\t\tregisterGitdir,\n\t};\n}\n","import type { ObjectStore } from \"./types.js\";\n\nconst BATCH = 20;\n\nasync function inBatches<T>(\n\titems: T[],\n\tfn: (item: T) => Promise<void>,\n): Promise<void> {\n\tfor (let i = 0; i < items.length; i += BATCH) {\n\t\tawait Promise.all(items.slice(i, i + BATCH).map(fn));\n\t}\n}\n\n/**\n * Delete every object under `prefix` (one paginated LIST, then batched\n * deletes). Returns the number of objects deleted. Callers serialise against\n * concurrent writers (e.g. a repository lock) and invalidate any cache\n * layered above `store`.\n */\nexport async function deletePrefix(\n\tstore: ObjectStore,\n\tprefix: string,\n): Promise<number> {\n\tconst { objects } = await store.list(prefix);\n\tawait inBatches(objects, (object) => store.delete(object.key));\n\treturn objects.length;\n}\n\nexport interface MovePrefixOptions {\n\t/**\n\t * Copy one object, e.g. a store's server-side `copy` (no bytes pass through\n\t * this process). Defaults to `get` + `put` on `store`.\n\t */\n\tcopy?: (fromKey: string, toKey: string) => Promise<void>;\n}\n\n/**\n * Move every object under `from` to the same relative key under `to`: copy\n * everything first, then delete the old keys only once every copy succeeded,\n * so a failed copy leaves the source untouched (a partial destination may\n * remain; call {@link deletePrefix} on it to clean up). Returns the number of\n * objects moved. `from` and `to` must not overlap.\n */\nexport async function movePrefix(\n\tstore: ObjectStore,\n\tfrom: string,\n\tto: string,\n\toptions: MovePrefixOptions = {},\n): Promise<number> {\n\tif (from.startsWith(to) || to.startsWith(from)) {\n\t\tthrow new Error(`movePrefix: prefixes overlap (${from} -> ${to})`);\n\t}\n\tconst copy =\n\t\toptions.copy ??\n\t\t(async (fromKey: string, toKey: string) => {\n\t\t\tconst data = await store.get(fromKey);\n\t\t\tif (data) await store.put(toKey, data);\n\t\t});\n\tconst { objects } = await store.list(from);\n\tawait inBatches(objects, (object) =>\n\t\tcopy(object.key, to + object.key.slice(from.length)),\n\t);\n\tawait inBatches(objects, (object) => store.delete(object.key));\n\treturn objects.length;\n}\n","import type { ObjectStore } from \"./types.js\";\n\n/** Options accepted by {@link createRetryStore}. */\nexport interface RetryOptions {\n\t/** Retries after the first attempt (total attempts = retries + 1). Default 3. */\n\tretries?: number;\n\t/** Backoff base delay in milliseconds, doubled each attempt. Default 100. */\n\tinitialDelayMs?: number;\n\t/** Upper bound for the backoff base delay. Default 5000. */\n\tmaxDelayMs?: number;\n\t/** Random jitter added to each delay, as a fraction of it. Default 0.3. */\n\tjitter?: number;\n\t/**\n\t * Decide whether an error is worth retrying. The store contract maps\n\t * \"not found\" to `null` rather than throwing, so any thrown error is a\n\t * genuine failure; the default retries network faults, throttling, and\n\t * HTTP 5xx responses.\n\t */\n\tisRetryable?: (error: unknown) => boolean;\n\t/**\n\t * Circuit breaker configuration, or `false` to disable. After `threshold`\n\t * consecutive failures the store fails fast for `resetMs`, then lets one\n\t * request probe the backend again. Defaults: 5 failures, 30 000 ms.\n\t */\n\tbreaker?: false | { threshold?: number; resetMs?: number };\n\t/** Called before each retry sleep; useful for logging/metrics. */\n\tonRetry?: (info: {\n\t\tkey: string;\n\t\top: string;\n\t\tattempt: number;\n\t\tdelayMs: number;\n\t}) => void;\n}\n\n/**\n * Thrown instead of calling the backend while the circuit breaker is open.\n * Carries `code: \"EUNAVAILABLE\"` so callers can map it to a 503.\n */\nexport class CircuitOpenError extends Error {\n\treadonly code = \"EUNAVAILABLE\";\n\n\tconstructor() {\n\t\tsuper(\"Circuit breaker is open, object store unavailable\");\n\t\tthis.name = \"CircuitOpenError\";\n\t}\n}\n\nconst RETRYABLE_NAMES = new Set([\n\t\"TimeoutError\",\n\t\"RequestTimeout\",\n\t\"RequestTimeoutException\",\n\t\"SlowDown\",\n\t\"ThrottlingException\",\n\t\"TooManyRequestsException\",\n]);\n\nconst RETRYABLE_CODES = new Set([\n\t\"ECONNRESET\",\n\t\"ECONNREFUSED\",\n\t\"EPIPE\",\n\t\"ETIMEDOUT\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"EPROTO\",\n]);\n\n/**\n * The default retry classifier: network faults, timeouts, throttling and HTTP\n * 5xx/429 (including `$metadata.httpStatusCode`-shaped errors). `fetch`\n * reports a dropped connection as a bare `TypeError`, which counts too.\n * Compose it in a custom `isRetryable`.\n */\nexport function isRetryableError(error: unknown): boolean {\n\tif (error instanceof TypeError) return true;\n\tif (typeof error !== \"object\" || error === null) return false;\n\tconst err = error as {\n\t\tname?: string;\n\t\tcode?: string;\n\t\t$metadata?: { httpStatusCode?: number };\n\t};\n\tif (err.name !== undefined && RETRYABLE_NAMES.has(err.name)) return true;\n\tif (err.code !== undefined && RETRYABLE_CODES.has(err.code)) return true;\n\tconst status = err.$metadata?.httpStatusCode;\n\treturn status !== undefined && (status >= 500 || status === 429);\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Wrap an {@link ObjectStore} with retries (exponential backoff + jitter) and\n * an optional per-instance circuit breaker.\n *\n * Place this decorator closest to the network store, underneath any cache:\n * the cache then never stores transient failures, and callers coalesced onto\n * one request share a single retried attempt.\n */\nexport function createRetryStore(\n\tstore: ObjectStore,\n\toptions: RetryOptions = {},\n): ObjectStore {\n\tconst retries = options.retries ?? 3;\n\tconst initialDelayMs = options.initialDelayMs ?? 100;\n\tconst maxDelayMs = options.maxDelayMs ?? 5000;\n\tconst jitter = options.jitter ?? 0.3;\n\tconst isRetryable = options.isRetryable ?? isRetryableError;\n\tconst breaker =\n\t\toptions.breaker === false\n\t\t\t? null\n\t\t\t: {\n\t\t\t\t\tthreshold: options.breaker?.threshold ?? 5,\n\t\t\t\t\tresetMs: options.breaker?.resetMs ?? 30_000,\n\t\t\t\t};\n\n\tlet failures = 0;\n\tlet lastFailureAt = 0;\n\tlet state: \"closed\" | \"open\" | \"half-open\" = \"closed\";\n\tlet probing = false;\n\n\t/**\n\t * Consecutive transient failures open the circuit; any success closes it\n\t * and resets the count. Non-retryable errors (a 403, a malformed request)\n\t * say nothing about the backend's health and never count. Once the reset\n\t * window passes, exactly one request probes the backend (half-open);\n\t * everything else keeps failing fast until that probe settles.\n\t */\n\tasync function guarded<T>(fn: () => Promise<T>): Promise<T> {\n\t\tif (breaker === null) return fn();\n\t\tlet isProbe = false;\n\t\tif (state !== \"closed\") {\n\t\t\tif (\n\t\t\t\tprobing ||\n\t\t\t\t(state === \"open\" && Date.now() - lastFailureAt < breaker.resetMs)\n\t\t\t) {\n\t\t\t\tthrow new CircuitOpenError();\n\t\t\t}\n\t\t\tstate = \"half-open\";\n\t\t\tprobing = true;\n\t\t\tisProbe = true;\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fn();\n\t\t\tfailures = 0;\n\t\t\tstate = \"closed\";\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tif (isRetryable(error)) {\n\t\t\t\tfailures++;\n\t\t\t\tlastFailureAt = Date.now();\n\t\t\t\tif (failures >= breaker.threshold || isProbe) state = \"open\";\n\t\t\t} else if (isProbe) {\n\t\t\t\t// The backend answered; it's up.\n\t\t\t\tfailures = 0;\n\t\t\t\tstate = \"closed\";\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tif (isProbe) probing = false;\n\t\t}\n\t}\n\n\tasync function run<T>(op: string, key: string, fn: () => Promise<T>) {\n\t\tlet lastError: unknown;\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn await guarded(fn);\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\t\t\t\tif (error instanceof CircuitOpenError) throw error;\n\t\t\t\tif (!isRetryable(error) || attempt === retries) throw error;\n\t\t\t\tconst base = Math.min(initialDelayMs * 2 ** attempt, maxDelayMs);\n\t\t\t\tconst delayMs = Math.round(base + Math.random() * base * jitter);\n\t\t\t\toptions.onRetry?.({ key, op, attempt: attempt + 1, delayMs });\n\t\t\t\tawait sleep(delayMs);\n\t\t\t}\n\t\t}\n\t\tthrow lastError;\n\t}\n\n\t// Optional methods must be forwarded explicitly: this returns a new object,\n\t// so anything not listed here is dropped — and a dropped `getRange` is\n\t// invisible, because callers that want it report \"unknown\" rather than\n\t// failing. `object-size.test.ts` asserts it survives the whole stack.\n\tconst range = store.getRange?.bind(store);\n\n\treturn {\n\t\tget: (key) => run(\"get\", key, () => store.get(key)),\n\t\tput: (key, data) => run(\"put\", key, () => store.put(key, data)),\n\t\tdelete: (key) => run(\"delete\", key, () => store.delete(key)),\n\t\thead: (key) => run(\"head\", key, () => store.head(key)),\n\t\tlist: (prefix, listOptions) =>\n\t\t\trun(\"list\", prefix, () => store.list(prefix, listOptions)),\n\t\t...(range\n\t\t\t? {\n\t\t\t\t\tgetRange: (key: string, start: number, end: number) =>\n\t\t\t\t\t\trun(\"getRange\", key, () => range(key, start, end)),\n\t\t\t\t}\n\t\t\t: {}),\n\t};\n}\n"],"mappings":"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"}
package/dist/ops.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { D as GitPathNotFoundError, H as inflate, T as GitInvalidRequestError, W as readBlobContent, o as isSafeTreePath, s as qualifyBranchRef } from "./refs-Cb_NfKWI.js";
2
2
  import { i as parseLooseObjectPath, t as getObjectSize } from "./object-size-BJAsnZzw.js";
3
- import { C as summarizeCommit, D as findTreeEntry, E as deleteFromTree, O as listTreeEntries, S as resolveCommit, T as runStep, _ as getCommitHistory, a as storePack, b as getTreeFromRef, c as createBranchFrom, f as listTags, g as getCommit, h as getBlob, k as upsertTree, l as deleteBranchByName, m as HISTORY_WALK_DEPTH, n as encodePack, o as assertBranchExists, p as resolveLooseRefFast, s as assertSafeBranchName, t as createSemaphore, u as listBranches, v as getCommitLog, w as resultKeyPrefixes, x as peelToCommit, y as getFileContent } from "./semaphore-BviNoQuJ.js";
3
+ import { A as upsertTree, C as resolveCommitOid, D as deleteFromTree, E as runStep, O as findTreeEntry, S as resolveCommit, T as resultKeyPrefixes, _ as getCommitHistory, a as storePack, b as getTreeFromRef, c as createBranchFrom, f as listTags, g as getCommit, h as getBlob, k as listTreeEntries, l as deleteBranchByName, m as HISTORY_WALK_DEPTH, n as encodePack, o as assertBranchExists, p as resolveLooseRefFast, s as assertSafeBranchName, t as createSemaphore, u as listBranches, v as getCommitLog, w as summarizeCommit, x as peelToCommit, y as getFileContent } from "./semaphore-CPjn7Iqh.js";
4
4
  import git from "isomorphic-git";
5
5
  import { createTwoFilesPatch } from "diff";
6
6
  //#region src/ops/packed-writes.ts
@@ -705,6 +705,6 @@ async function getSubmodules(repo, ref, hooks) {
705
705
  return parseGitmodules(new TextDecoder().decode(bytes));
706
706
  }
707
707
  //#endregion
708
- export { BANNER_WALK_DEPTH, HISTORY_WALK_DEPTH, MAX_DIFF_TEXT_BYTES, analyzeMerge, assertBranchExists, assertSafeBranchName, authorNow, commitFilesToBare, createBranchFrom, deleteBranchByName, deleteFileFromBare, deleteFromTree, fastForwardMerge, findTreeEntry, getBlob, getCommit, getCommitDiff, getCommitHistory, getCommitLog, getDiffBetweenRefs, getFileContent, getFileHistory, getLastCommitsForTree, getSubmodules, getTreeFromRef, listBranches, listTags, listTreeEntries, parseGitmodules, peelToCommit, resolveCommit, resolveLooseRefFast, resultKeyPrefixes, summarizeCommit, upsertTree, withPackedWrites, writeCommitToBare };
708
+ export { BANNER_WALK_DEPTH, HISTORY_WALK_DEPTH, MAX_DIFF_TEXT_BYTES, analyzeMerge, assertBranchExists, assertSafeBranchName, authorNow, commitFilesToBare, createBranchFrom, deleteBranchByName, deleteFileFromBare, deleteFromTree, fastForwardMerge, findTreeEntry, getBlob, getCommit, getCommitDiff, getCommitHistory, getCommitLog, getDiffBetweenRefs, getFileContent, getFileHistory, getLastCommitsForTree, getSubmodules, getTreeFromRef, listBranches, listTags, listTreeEntries, parseGitmodules, peelToCommit, resolveCommit, resolveCommitOid, resolveLooseRefFast, resultKeyPrefixes, summarizeCommit, upsertTree, withPackedWrites, writeCommitToBare };
709
709
 
710
710
  //# sourceMappingURL=ops.js.map
@@ -217,6 +217,46 @@ async function peelToCommit(repo, oid) {
217
217
  }
218
218
  throw new GitRefNotFoundError(`Tag chain at ${oid} is too deep`);
219
219
  }
220
+ const HEX_SHA_RE = /^[0-9a-f]{4,40}$/i;
221
+ /**
222
+ * The commit oid a revision names: a branch, tag, `HEAD`, a full ref, a full
223
+ * sha or an abbreviated sha (4+ hex digits). Refs win over an abbreviated sha
224
+ * of the same spelling, as in git. Throws GitRefNotFoundError when nothing
225
+ * matches or an abbreviation is ambiguous. An abbreviation scans the pack
226
+ * indexes, so resolve once and read by the returned (immutable) oid.
227
+ */
228
+ async function resolveCommitOid(repo, rev) {
229
+ const oid = await lookupCommitOid(repo, rev);
230
+ try {
231
+ await git.readCommit({
232
+ ...repo,
233
+ oid
234
+ });
235
+ } catch (err) {
236
+ if (isNotFound(err) || err?.code === "ObjectTypeError") throw new GitRefNotFoundError(`Ref ${rev} does not exist`);
237
+ throw err;
238
+ }
239
+ return oid;
240
+ }
241
+ async function lookupCommitOid(repo, rev) {
242
+ try {
243
+ return await resolveRefOid(repo, rev);
244
+ } catch (err) {
245
+ if (!(err instanceof GitRefNotFoundError) || !HEX_SHA_RE.test(rev)) throw err;
246
+ }
247
+ const sha = rev.toLowerCase();
248
+ if (sha.length === 40) return peelToCommit(repo, sha).catch(notFoundAsRef(rev));
249
+ return git.expandOid({
250
+ ...repo,
251
+ oid: sha
252
+ }).then((full) => peelToCommit(repo, full)).catch(notFoundAsRef(rev));
253
+ }
254
+ function notFoundAsRef(rev) {
255
+ return (err) => {
256
+ if (isNotFound(err) || err?.code === "AmbiguousError") throw new GitRefNotFoundError(`Ref ${rev} does not exist`);
257
+ throw err;
258
+ };
259
+ }
220
260
  /** Resolve a branch name / full ref / sha to its commit. */
221
261
  async function resolveCommit(repo, ref) {
222
262
  const oid = await resolveRefOid(repo, ref);
@@ -641,6 +681,6 @@ function createSemaphore(limit) {
641
681
  } };
642
682
  }
643
683
  //#endregion
644
- export { summarizeCommit as C, findTreeEntry as D, deleteFromTree as E, listTreeEntries as O, resolveCommit as S, runStep as T, getCommitHistory as _, storePack as a, getTreeFromRef as b, createBranchFrom as c, listLooseRefNames as d, listTags as f, getCommit as g, getBlob as h, packBaseName as i, upsertTree as k, deleteBranchByName as l, HISTORY_WALK_DEPTH as m, encodePack as n, assertBranchExists as o, resolveLooseRefFast as p, hashGitObject as r, assertSafeBranchName as s, createSemaphore as t, listBranches as u, getCommitLog as v, resultKeyPrefixes as w, peelToCommit as x, getFileContent as y };
684
+ export { upsertTree as A, resolveCommitOid as C, deleteFromTree as D, runStep as E, findTreeEntry as O, resolveCommit as S, resultKeyPrefixes as T, getCommitHistory as _, storePack as a, getTreeFromRef as b, createBranchFrom as c, listLooseRefNames as d, listTags as f, getCommit as g, getBlob as h, packBaseName as i, listTreeEntries as k, deleteBranchByName as l, HISTORY_WALK_DEPTH as m, encodePack as n, assertBranchExists as o, resolveLooseRefFast as p, hashGitObject as r, assertSafeBranchName as s, createSemaphore as t, listBranches as u, getCommitLog as v, summarizeCommit as w, peelToCommit as x, getFileContent as y };
645
685
 
646
- //# sourceMappingURL=semaphore-BviNoQuJ.js.map
686
+ //# sourceMappingURL=semaphore-CPjn7Iqh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semaphore-CPjn7Iqh.js","names":[],"sources":["../src/ops/tree.ts","../src/ops/types.ts","../src/ops/history.ts","../src/ops/branch.ts","../src/pack-write.ts","../src/semaphore.ts"],"sourcesContent":["import git from \"isomorphic-git\";\nimport { GitInvalidRequestError, GitPathNotFoundError } from \"../git-errors.js\";\nimport type { Repo } from \"./types.js\";\n\nexport interface TreeEntry {\n\tpath: string;\n\tmode: string;\n\ttype: \"blob\" | \"tree\" | \"commit\";\n\toid: string;\n\tsize?: number;\n}\n\nconst joinPath = (prefix: string, name: string) =>\n\tprefix ? `${prefix.replace(/\\/+$/, \"\")}/${name}` : name;\n\n/** File modes a blob entry can carry; anything else isn't a regular file. */\nconst BLOB_MODES = new Set([\"100644\", \"100755\", \"120000\"]);\n\n/**\n * Build/update a git tree by overlaying new blobs onto an existing tree,\n * returning the new root tree oid. `entries` maps relative paths to blob oids.\n *\n * An updated file keeps its existing mode (executable bit, symlink); a path\n * that names an existing directory, or runs through an existing file, is\n * rejected rather than silently replacing what's there.\n */\nexport async function upsertTree(\n\trepo: Repo,\n\ttreeOid: string | undefined,\n\tentries: Map<string, string>,\n): Promise<string> {\n\tconst existing = treeOid\n\t\t? (await git.readTree({ ...repo, oid: treeOid })).tree\n\t\t: [];\n\tconst byName = new Map(existing.map((e) => [e.path, e]));\n\tconst direct = new Map<string, string>();\n\tconst nested = new Map<string, Map<string, string>>();\n\tfor (const [filePath, blobOid] of entries) {\n\t\tconst slash = filePath.indexOf(\"/\");\n\t\tif (slash === -1) {\n\t\t\tdirect.set(filePath, blobOid);\n\t\t} else {\n\t\t\tconst dir = filePath.slice(0, slash);\n\t\t\tconst rest = filePath.slice(slash + 1);\n\t\t\tif (!nested.has(dir)) nested.set(dir, new Map());\n\t\t\tnested.get(dir)?.set(rest, blobOid);\n\t\t}\n\t}\n\tfor (const [name, blobOid] of direct) {\n\t\tconst current = byName.get(name);\n\t\tif (current && current.type !== \"blob\") {\n\t\t\tthrow new GitInvalidRequestError(`\"${name}\" is a directory, not a file`);\n\t\t}\n\t\tbyName.set(name, {\n\t\t\tmode: current && BLOB_MODES.has(current.mode) ? current.mode : \"100644\",\n\t\t\tpath: name,\n\t\t\toid: blobOid,\n\t\t\ttype: \"blob\",\n\t\t});\n\t}\n\t// Sibling subdirectories are independent subtree writes — no reason to\n\t// serialize them for multi-file commits touching several directories.\n\tconst nestedResults = await Promise.all(\n\t\tArray.from(nested, async ([dir, subEntries]) => {\n\t\t\tconst entry = byName.get(dir);\n\t\t\tif (entry && entry.type !== \"tree\") {\n\t\t\t\tthrow new GitInvalidRequestError(`\"${dir}\" is a file, not a directory`);\n\t\t\t}\n\t\t\tconst subtreeOid = entry?.oid;\n\t\t\tconst newOid = await upsertTree(repo, subtreeOid, subEntries);\n\t\t\treturn [dir, newOid] as const;\n\t\t}),\n\t);\n\tfor (const [dir, newOid] of nestedResults) {\n\t\tbyName.set(dir, { mode: \"040000\", path: dir, oid: newOid, type: \"tree\" });\n\t}\n\treturn git.writeTree({ ...repo, tree: Array.from(byName.values()) });\n}\n\n/**\n * Remove a file path from a tree, returning the new root tree oid. Throws\n * GitPathNotFoundError when there's no such file; directories left empty\n * are dropped, as git never stores an empty subtree.\n */\nexport async function deleteFromTree(\n\trepo: Repo,\n\ttreeOid: string,\n\tfilePath: string,\n): Promise<string> {\n\tconst result = await deleteFromSubtree(repo, treeOid, filePath, filePath);\n\treturn result ?? git.writeTree({ ...repo, tree: [] });\n}\n\n/** The new subtree oid, or null when removing the file left it empty. */\nasync function deleteFromSubtree(\n\trepo: Repo,\n\ttreeOid: string,\n\tfilePath: string,\n\tfullPath: string,\n): Promise<string | null> {\n\tconst existing = (await git.readTree({ ...repo, oid: treeOid })).tree;\n\tconst byName = new Map(existing.map((e) => [e.path, e]));\n\tconst slash = filePath.indexOf(\"/\");\n\tconst name = slash === -1 ? filePath : filePath.slice(0, slash);\n\tconst entry = byName.get(name);\n\tif (slash === -1) {\n\t\tif (entry?.type !== \"blob\") {\n\t\t\tthrow new GitPathNotFoundError(`No such file: ${fullPath}`);\n\t\t}\n\t\tbyName.delete(name);\n\t} else {\n\t\tif (entry?.type !== \"tree\") {\n\t\t\tthrow new GitPathNotFoundError(`No such file: ${fullPath}`);\n\t\t}\n\t\tconst newOid = await deleteFromSubtree(\n\t\t\trepo,\n\t\t\tentry.oid,\n\t\t\tfilePath.slice(slash + 1),\n\t\t\tfullPath,\n\t\t);\n\t\tif (newOid === null) byName.delete(name);\n\t\telse byName.set(name, { ...entry, oid: newOid });\n\t}\n\tif (byName.size === 0) return null;\n\treturn git.writeTree({ ...repo, tree: Array.from(byName.values()) });\n}\n\n/** Resolve a path inside a tree to its entry, or null when absent. */\nexport async function findTreeEntry(\n\trepo: Repo,\n\trootTreeOid: string,\n\ttreePath: string,\n): Promise<TreeEntry | null> {\n\tif (!treePath) {\n\t\treturn { path: \"\", mode: \"040000\", type: \"tree\", oid: rootTreeOid };\n\t}\n\n\tconst parts = treePath.split(\"/\").filter(Boolean);\n\tlet currentTreeOid = rootTreeOid;\n\tlet currentPath = \"\";\n\n\tfor (const [index, part] of parts.entries()) {\n\t\tconst tree = await git.readTree({ ...repo, oid: currentTreeOid });\n\t\tconst entry = tree.tree.find((candidate) => candidate.path === part);\n\n\t\tif (!entry) return null;\n\n\t\tcurrentPath = currentPath ? joinPath(currentPath, entry.path) : entry.path;\n\n\t\tif (index === parts.length - 1) {\n\t\t\treturn {\n\t\t\t\tpath: currentPath,\n\t\t\t\tmode: entry.mode,\n\t\t\t\ttype: entry.type,\n\t\t\t\toid: entry.oid,\n\t\t\t};\n\t\t}\n\n\t\tif (entry.type !== \"tree\") return null;\n\n\t\tcurrentTreeOid = entry.oid;\n\t}\n\n\treturn null;\n}\n\n/** List a tree's direct entries, with paths prefixed by `prefix`. */\nexport async function listTreeEntries(\n\trepo: Repo,\n\ttreeOid: string,\n\tprefix = \"\",\n): Promise<TreeEntry[]> {\n\tconst tree = await git.readTree({ ...repo, oid: treeOid });\n\n\treturn tree.tree.map((entry) => ({\n\t\tpath: prefix ? joinPath(prefix, entry.path) : entry.path,\n\t\tmode: entry.mode,\n\t\ttype: entry.type,\n\t\toid: entry.oid,\n\t}));\n}\n","import type git from \"isomorphic-git\";\n\n/** The fs shape isomorphic-git accepts, derived from its own signatures. */\nexport type IsoGitFs = Parameters<typeof git.readTree>[0][\"fs\"];\n\n/**\n * A repository handle: everything isomorphic-git needs to address one bare\n * repo. Create it once per request (sharing `cache` across calls is what lets\n * isomorphic-git reuse parsed pack indexes) and pass it to every op.\n */\nexport interface Repo {\n\tfs: IsoGitFs;\n\tgitdir: string;\n\t/** isomorphic-git's shared parse cache — strongly recommended per repo. */\n\tcache?: object;\n}\n\n/**\n * Key/value store for memoizing expensive walk results (commit logs, tree\n * listings, per-path history). Keys are namespaced `kind:gitdir:headSha:…`,\n * so entries self-invalidate on push (new head, new key); evict\n * {@link resultKeyPrefixes} to drop a repo's superseded entries early.\n * Values are always objects (arrays or records).\n */\nexport interface ResultCache {\n\tget<T extends object>(key: string): T | null | undefined;\n\tset(key: string, value: object): void;\n}\n\n/** Optional instrumentation hooks accepted by every op. */\nexport interface OpsHooks {\n\tresultCache?: ResultCache;\n\t/** Wrap a timed sub-step (network walk, tree listing). Default: run directly. */\n\tstep?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;\n\t/** Diagnostic sink for cache hit/miss and walk summaries. */\n\tonNote?: (message: string) => void;\n\t/**\n\t * Warm every pack (e.g. `GitFs.prefetchPacks`) before a cache-miss read,\n\t * so a sequential walk doesn't discover packs one round trip at a time.\n\t */\n\tprefetch?: () => Promise<void>;\n}\n\nexport const runStep = <T>(\n\thooks: OpsHooks | undefined,\n\tlabel: string,\n\tfn: () => Promise<T>,\n): Promise<T> => (hooks?.step ? hooks.step(label, fn) : fn());\n\n/**\n * The result-cache key prefixes holding entries for `gitdir` — evict these\n * from your {@link ResultCache} when the repo's storage was rewritten outside\n * a normal push (rename, repack).\n */\nexport function resultKeyPrefixes(gitdir: string): string[] {\n\treturn [\n\t\t`commitlog:${gitdir}:`,\n\t\t`tree:${gitdir}:`,\n\t\t`last-commits:${gitdir}:`,\n\t\t`file-history:${gitdir}:`,\n\t];\n}\n","import git from \"isomorphic-git\";\nimport {\n\tGitObjectNotFoundError,\n\tGitPathNotFoundError,\n\tGitRefNotFoundError,\n} from \"../git-errors.js\";\nimport { qualifyBranchRef } from \"../refs.js\";\nimport { findTreeEntry, listTreeEntries, type TreeEntry } from \"./tree.js\";\nimport { type OpsHooks, type Repo, runStep } from \"./types.js\";\n\n/** How far back history walks look by default (commit count). */\nexport const HISTORY_WALK_DEPTH = 400;\n\nexport interface GitIdentity {\n\tname: string;\n\temail: string;\n\ttimestamp: number;\n\ttimezoneOffset: number;\n}\n\nexport interface CommitInfo {\n\toid: string;\n\tcommit: {\n\t\tmessage: string;\n\t\ttree: string;\n\t\tparent: string[];\n\t\tauthor: GitIdentity;\n\t\tcommitter: GitIdentity;\n\t};\n\tpayload: string;\n}\n\n/** The display summary of a commit (history lists, \"last commit\" columns). */\nexport interface CommitSummary {\n\tsha: string;\n\tmessage: string;\n\tauthorName: string;\n\tauthorEmail: string;\n\t/** ISO 8601 author date. */\n\tcreatedAt: string;\n}\n\nexport function summarizeCommit(commit: CommitInfo): CommitSummary {\n\treturn {\n\t\tsha: commit.oid,\n\t\tmessage: commit.commit.message.trim(),\n\t\tauthorName: commit.commit.author.name,\n\t\tauthorEmail: commit.commit.author.email,\n\t\tcreatedAt: new Date(commit.commit.author.timestamp * 1000).toISOString(),\n\t};\n}\n\nconst isNotFound = (err: unknown) =>\n\t(err as { code?: string } | null)?.code === \"NotFoundError\";\n\n/**\n * A ref that doesn't resolve is an expected condition (empty repo, unborn\n * branch) each caller handles. An object missing *underneath* a resolved ref\n * means storage is inconsistent — raised as GitObjectNotFoundError so it is\n * never mistaken for \"empty\".\n */\nfunction wrapMissingObject<T>(\n\tpromise: Promise<T>,\n\tcontext: string,\n): Promise<T> {\n\treturn promise.catch((err: unknown) => {\n\t\tif (isNotFound(err)) {\n\t\t\tthrow new GitObjectNotFoundError(\n\t\t\t\t`Git data for ${context} is missing from storage`,\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t});\n}\n\n/** The commit a branch name, full ref or sha points at; GitRefNotFoundError if none. */\n/**\n * A short name tries `refs/heads/<name>` first and then `refs/tags/<name>`,\n * so a branch wins over a same-named tag. Annotated tags are peeled to the\n * commit they point at, so every read accepts a tag wherever it accepts a\n * branch.\n */\nasync function resolveRefOid(repo: Repo, ref: string): Promise<string> {\n\tconst qualified = qualifyBranchRef(ref);\n\tconst candidates =\n\t\tqualified === `refs/heads/${ref}`\n\t\t\t? [qualified, `refs/tags/${ref}`]\n\t\t\t: [qualified];\n\tfor (const candidate of candidates) {\n\t\ttry {\n\t\t\treturn await peelToCommit(\n\t\t\t\trepo,\n\t\t\t\tawait git.resolveRef({ ...repo, ref: candidate }),\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tif (!isNotFound(err)) throw err;\n\t\t}\n\t}\n\tthrow new GitRefNotFoundError(`Ref ${ref} does not exist`);\n}\n\n/** Follow annotated tags (possibly nested) down to the object they name. */\nexport async function peelToCommit(repo: Repo, oid: string): Promise<string> {\n\tlet current = oid;\n\tfor (let depth = 0; depth < 10; depth++) {\n\t\tconst { type, object } = await git.readObject({\n\t\t\t...repo,\n\t\t\toid: current,\n\t\t\tformat: \"parsed\",\n\t\t});\n\t\tif (type !== \"tag\") return current;\n\t\tcurrent = (object as { object: string }).object;\n\t}\n\tthrow new GitRefNotFoundError(`Tag chain at ${oid} is too deep`);\n}\n\nconst HEX_SHA_RE = /^[0-9a-f]{4,40}$/i;\n\n/**\n * The commit oid a revision names: a branch, tag, `HEAD`, a full ref, a full\n * sha or an abbreviated sha (4+ hex digits). Refs win over an abbreviated sha\n * of the same spelling, as in git. Throws GitRefNotFoundError when nothing\n * matches or an abbreviation is ambiguous. An abbreviation scans the pack\n * indexes, so resolve once and read by the returned (immutable) oid.\n */\nexport async function resolveCommitOid(\n\trepo: Repo,\n\trev: string,\n): Promise<string> {\n\tconst oid = await lookupCommitOid(repo, rev);\n\t// A sha (or abbreviation) may name a tree or blob; only a commit counts.\n\t// The parse lands in the repo cache, so the caller's read of it is free.\n\ttry {\n\t\tawait git.readCommit({ ...repo, oid });\n\t} catch (err) {\n\t\tif (\n\t\t\tisNotFound(err) ||\n\t\t\t(err as { code?: string })?.code === \"ObjectTypeError\"\n\t\t) {\n\t\t\tthrow new GitRefNotFoundError(`Ref ${rev} does not exist`);\n\t\t}\n\t\tthrow err;\n\t}\n\treturn oid;\n}\n\nasync function lookupCommitOid(repo: Repo, rev: string): Promise<string> {\n\ttry {\n\t\treturn await resolveRefOid(repo, rev);\n\t} catch (err) {\n\t\tif (!(err instanceof GitRefNotFoundError) || !HEX_SHA_RE.test(rev)) {\n\t\t\tthrow err;\n\t\t}\n\t}\n\t// Refs are case-sensitive, shas are not: fall back to the lowercase sha.\n\tconst sha = rev.toLowerCase();\n\tif (sha.length === 40)\n\t\treturn peelToCommit(repo, sha).catch(notFoundAsRef(rev));\n\treturn git\n\t\t.expandOid({ ...repo, oid: sha })\n\t\t.then((full) => peelToCommit(repo, full))\n\t\t.catch(notFoundAsRef(rev));\n}\n\nfunction notFoundAsRef(rev: string) {\n\treturn (err: unknown): never => {\n\t\tif (\n\t\t\tisNotFound(err) ||\n\t\t\t(err as { code?: string } | null)?.code === \"AmbiguousError\"\n\t\t) {\n\t\t\tthrow new GitRefNotFoundError(`Ref ${rev} does not exist`);\n\t\t}\n\t\tthrow err;\n\t};\n}\n\n/** Resolve a branch name / full ref / sha to its commit. */\nexport async function resolveCommit(repo: Repo, ref: string) {\n\tconst oid = await resolveRefOid(repo, ref);\n\tconst { commit } = await wrapMissingObject(\n\t\tgit.readCommit({ ...repo, oid }),\n\t\t`${repo.gitdir} commit ${oid}`,\n\t);\n\treturn { oid, commit };\n}\n\n/** Read a blob's bytes by oid. */\nexport async function getBlob(repo: Repo, sha: string): Promise<Uint8Array> {\n\tconst { blob } = await wrapMissingObject(\n\t\tgit.readBlob({ ...repo, oid: sha }),\n\t\t`${repo.gitdir} blob ${sha}`,\n\t);\n\treturn blob;\n}\n\n/** Read a file's bytes at a ref. Throws GitPathNotFoundError when absent. */\nexport async function getFileContent(\n\trepo: Repo,\n\tfilePath: string,\n\tref: string,\n\thooks?: OpsHooks,\n): Promise<Uint8Array> {\n\t// A commit, a tree per path segment, then the blob: warm every pack first\n\t// rather than discovering them one round trip at a time.\n\tif (hooks?.prefetch) await runStep(hooks, \"prefetch\", hooks.prefetch);\n\tconst { commit } = await resolveCommit(repo, ref);\n\tconst context = `${repo.gitdir}@${ref}:${filePath}`;\n\tconst entry = await wrapMissingObject(\n\t\tfindTreeEntry(repo, commit.tree, filePath),\n\t\tcontext,\n\t);\n\tif (entry?.type !== \"blob\") {\n\t\tthrow new GitPathNotFoundError(`File not found: ${filePath}`);\n\t}\n\tconst { blob } = await wrapMissingObject(\n\t\tgit.readBlob({ ...repo, oid: entry.oid }),\n\t\tcontext,\n\t);\n\treturn blob;\n}\n\n/** Read one commit by sha. */\nexport async function getCommit(repo: Repo, sha: string): Promise<CommitInfo> {\n\tconst result = await wrapMissingObject(\n\t\tgit.readCommit({ ...repo, oid: sha }),\n\t\t`${repo.gitdir} commit ${sha}`,\n\t);\n\treturn { oid: result.oid, commit: result.commit, payload: result.payload };\n}\n\nexport interface CommitLogOptions {\n\tref: string;\n\tdepth?: number;\n\t/** The ref's commit, when the caller already resolved it (saves a lookup). */\n\tknownHeadSha?: string;\n}\n\n/**\n * The commit chain from a ref, newest first; [] when the ref doesn't exist.\n * Walking is sequential and round-trip-bound on object storage, so the\n * deepest walk seen per head is memoized in `hooks.resultCache` and sliced\n * for shallower requests.\n */\nexport async function getCommitLog(\n\trepo: Repo,\n\toptions: CommitLogOptions,\n\thooks?: OpsHooks,\n): Promise<CommitInfo[]> {\n\tconst { ref } = options;\n\tconst depth = options.depth ?? 50;\n\tlet headSha = options.knownHeadSha;\n\tif (headSha === undefined) {\n\t\ttry {\n\t\t\theadSha = await resolveRefOid(repo, ref);\n\t\t} catch (err) {\n\t\t\tif (err instanceof GitRefNotFoundError) return [];\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tconst cacheKey = `commitlog:${repo.gitdir}:${headSha}`;\n\tconst cached = hooks?.resultCache?.get<CommitInfo[]>(cacheKey);\n\tconst fullyWalked = cached?.at(-1)?.commit.parent.length === 0;\n\tif (cached && (cached.length >= depth || fullyWalked)) {\n\t\thooks?.onNote?.(`getCommitLog: cache hit ${cacheKey} (depth=${depth})`);\n\t\treturn cached.slice(0, depth);\n\t}\n\thooks?.onNote?.(`getCommitLog: cache miss ${cacheKey} (depth=${depth})`);\n\tif (hooks?.prefetch) await runStep(hooks, \"prefetch\", hooks.prefetch);\n\n\tconst log = await wrapMissingObject(\n\t\trunStep(hooks, `git.log ${ref} depth=${depth}`, () =>\n\t\t\tgit.log({ ...repo, ref: headSha, depth }),\n\t\t),\n\t\t`${repo.gitdir}@${ref} history`,\n\t);\n\tconst result = log.map(({ oid, commit, payload }) => ({\n\t\toid,\n\t\tcommit,\n\t\tpayload: payload ?? \"\",\n\t}));\n\tif (!cached || result.length > cached.length) {\n\t\thooks?.resultCache?.set(cacheKey, result);\n\t}\n\treturn result;\n}\n\n/**\n * List a directory at the tip of a ref. Throws GitRefNotFoundError when the\n * ref doesn't resolve and GitPathNotFoundError when `treePath` doesn't exist.\n * Memoized per head sha.\n */\nexport async function getTreeFromRef(\n\trepo: Repo,\n\toptions: { ref: string; treePath?: string },\n\thooks?: OpsHooks,\n): Promise<TreeEntry[]> {\n\tconst { ref } = options;\n\tconst treePath = options.treePath ?? \"\";\n\tconst { commit, oid: headSha } = await resolveCommit(repo, ref);\n\n\tconst cacheKey = `tree:${repo.gitdir}:${headSha}:${treePath}`;\n\tconst cached = hooks?.resultCache?.get<TreeEntry[]>(cacheKey);\n\tif (cached) {\n\t\thooks?.onNote?.(`getTreeFromRef: cache hit ${cacheKey}`);\n\t\treturn cached;\n\t}\n\thooks?.onNote?.(`getTreeFromRef: cache miss ${cacheKey}`);\n\tif (hooks?.prefetch) await runStep(hooks, \"prefetch\", hooks.prefetch);\n\n\tconst context = `${repo.gitdir}@${ref}:${treePath || \"/\"}`;\n\tconst dir = treePath\n\t\t? await wrapMissingObject(\n\t\t\t\trunStep(hooks, `findTreeEntry ${treePath}`, () =>\n\t\t\t\t\tfindTreeEntry(repo, commit.tree, treePath),\n\t\t\t\t),\n\t\t\t\tcontext,\n\t\t\t)\n\t\t: { path: \"\", type: \"tree\" as const, oid: commit.tree };\n\tif (!dir) {\n\t\tthrow new GitPathNotFoundError(\n\t\t\t`Path \"${treePath}\" does not exist at ${ref}`,\n\t\t);\n\t}\n\tconst result =\n\t\tdir.type === \"tree\"\n\t\t\t? await wrapMissingObject(\n\t\t\t\t\trunStep(hooks, `listTreeEntries ${treePath || \"/\"}`, () =>\n\t\t\t\t\t\tlistTreeEntries(repo, dir.oid, dir.path),\n\t\t\t\t\t),\n\t\t\t\t\tcontext,\n\t\t\t\t)\n\t\t\t: [];\n\thooks?.resultCache?.set(cacheKey, result);\n\treturn result;\n}\n\n/**\n * A page of commit history from a ref tip; [] when the ref doesn't exist.\n * Pages are slices of {@link getCommitLog}'s memoized walk.\n */\nexport async function getCommitHistory(\n\trepo: Repo,\n\toptions: { ref: string; limit?: number; skip?: number },\n\thooks?: OpsHooks,\n): Promise<CommitInfo[]> {\n\tconst limit = options.limit ?? 50;\n\tconst skip = options.skip ?? 0;\n\tconst log = await getCommitLog(\n\t\trepo,\n\t\t{ ref: options.ref, depth: limit + skip },\n\t\thooks,\n\t);\n\treturn log.slice(skip, skip + limit);\n}\n","import git from \"isomorphic-git\";\nimport {\n\tGitConflictError,\n\tGitInvalidRequestError,\n\tisAbsentError,\n} from \"../git-errors.js\";\nimport { isSafeBranchName } from \"../refs.js\";\nimport { peelToCommit } from \"./history.js\";\nimport type { Repo } from \"./types.js\";\n\nexport interface Branch {\n\tname: string;\n\tcommit: string;\n\tisDefault: boolean;\n}\n\nexport interface Tag {\n\tname: string;\n\t/** The commit the tag names (annotated tags peeled). */\n\tcommit: string;\n\t/** The tag object's own oid, for annotated tags; absent for lightweight ones. */\n\ttagObject?: string;\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\ntype PromisesFs = {\n\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n};\n\n// Repo.fs is typed as isomorphic-git's own fs union (promise- or\n// callback-based) since that's what git.readTree's signature allows, but\n// every fs this package actually constructs (git-fs.ts) is promise-based —\n// this just keeps the callback-style branch honest instead of crashing on\n// it. Shared by every loose-ref fast path below instead of each repeating\n// the same cast + presence check.\nfunction getPromisesFs(fs: Repo[\"fs\"]): PromisesFs | null {\n\tconst promises = (fs as { promises?: PromisesFs }).promises;\n\treturn promises ?? null;\n}\n\nfunction decodeFileContent(content: string | Uint8Array): string {\n\treturn typeof content === \"string\"\n\t\t? content\n\t\t: new TextDecoder().decode(content);\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\treturn listLooseRefNames(repo, \"refs/heads\");\n}\n\n/**\n * Loose ref names under `dir` (e.g. `refs/tags`) in one recursive listing,\n * or `null` when the fs can't list recursively (use isomorphic-git's walk).\n */\nexport async function listLooseRefNames(\n\trepo: Repo,\n\tdir: \"refs/heads\" | \"refs/tags\",\n): Promise<string[] | null> {\n\tif (!hasRecursiveFileLister(repo.fs)) return null;\n\tconst names = await repo.fs.listFilesRecursively(`${repo.gitdir}/${dir}`);\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 = getPromisesFs(repo.fs);\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 decodeFileContent(content).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 (error) {\n\t\tif (isAbsentError(error)) return new Map();\n\t\tthrow error;\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 = getPromisesFs(repo.fs);\n\tif (!promisesFs) return null;\n\ttry {\n\t\tconst content = await promisesFs.readFile(`${repo.gitdir}/HEAD`, \"utf8\");\n\t\tconst head = decodeFileContent(content).trim();\n\t\tconst match = /^ref: refs\\/heads\\/(.+)$/.exec(head);\n\t\treturn match?.[1] && isSafeBranchName(match[1]) ? match[1] : null;\n\t} catch (error) {\n\t\t// No HEAD file is \"no branch\"; a failed read is not (#93).\n\t\tif (isAbsentError(error)) return null;\n\t\tthrow error;\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\tconst promisesFs = getPromisesFs(repo.fs);\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 = decodeFileContent(content).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((error: unknown) => {\n\t\t\t\t\t\t\tif (isAbsentError(error)) return null;\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t})\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: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tassertSafeBranchName(startPoint);\n\tconst object = await resolveLooseRefFast(repo, `refs/heads/${startPoint}`);\n\ttry {\n\t\tawait git.branch({ ...repo, ref: name, checkout: false, object });\n\t} catch (err) {\n\t\tif ((err as { code?: string }).code === \"AlreadyExistsError\") {\n\t\t\tthrow new GitConflictError(`Branch ${name} already exists`);\n\t\t}\n\t\tthrow err;\n\t}\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\t// Same rule as receive-pack: deleting the branch HEAD names leaves every\n\t// clone without a default branch.\n\tconst current =\n\t\t(await getCurrentLooseBranch(repo)) ??\n\t\t(await git\n\t\t\t.currentBranch({ ...repo, fullname: false })\n\t\t\t.catch((error: unknown) => {\n\t\t\t\t// A failed HEAD read must not skip this guard: isomorphic-git's\n\t\t\t\t// deleteBranch would then detach HEAD instead of refusing (#93).\n\t\t\t\tif (isAbsentError(error)) return null;\n\t\t\t\tthrow error;\n\t\t\t}));\n\tif (current === name) {\n\t\tthrow new GitInvalidRequestError(\n\t\t\t`Cannot delete ${name}: it is the default branch`,\n\t\t);\n\t}\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/** All tags with the commits they name, sorted by name; [] when there are none. */\nexport async function listTags(repo: Repo): Promise<Tag[]> {\n\tconst names = (await listLooseRefNames(repo, \"refs/tags\")) ?? [];\n\tconst all = new Set([\n\t\t...names,\n\t\t...(await git.listTags(repo).catch((error: unknown) => {\n\t\t\tif (isAbsentError(error)) return [] as string[];\n\t\t\tthrow error;\n\t\t})),\n\t]);\n\tconst tags = await Promise.all(\n\t\t[...all].filter(isSafeBranchName).map(async (name): Promise<Tag | null> => {\n\t\t\ttry {\n\t\t\t\tconst oid = await resolveLooseRefFast(repo, `refs/tags/${name}`);\n\t\t\t\tconst commit = await peelToCommit(repo, oid);\n\t\t\t\treturn commit === oid\n\t\t\t\t\t? { name, commit }\n\t\t\t\t\t: { name, commit, tagObject: oid };\n\t\t\t} catch (error) {\n\t\t\t\t// A tag deleted since the listing, or naming a missing object, is\n\t\t\t\t// not listed; a failed read fails the listing instead of\n\t\t\t\t// silently shortening it (#93).\n\t\t\t\tif (isAbsentError(error)) return null;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}),\n\t);\n\treturn tags\n\t\t.filter((tag): tag is Tag => tag !== null)\n\t\t.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));\n}\n","/**\n * Writing packfiles: serialize whole (never deltified) objects into a pack,\n * and store a pack plus its index in a repository. Shared by repack (the\n * loose-object fallback) and packed writes (ops/packed-writes.ts).\n *\n * Edge-compatible: Web Crypto SHA-1, CompressionStream, Uint8Array only.\n */\nimport git from \"isomorphic-git\";\nimport {\n\tconcat,\n\tdeflate,\n\tencodeUtf8,\n\tfromHex,\n\tsha1,\n\ttoHex,\n} from \"./edge-utils.js\";\nimport { rawFs } from \"./http/types.js\";\nimport type { Repo } from \"./ops/types.js\";\nimport { packFileName } from \"./pack-files.js\";\n\nexport type PackObjectType = \"commit\" | \"tree\" | \"blob\" | \"tag\";\n\n// Git pack object type bits (bits 6-4 of the header's first byte).\nconst PACK_OBJECT_TYPE_BITS: Record<PackObjectType, number> = {\n\tcommit: 0b0010000,\n\ttree: 0b0100000,\n\tblob: 0b0110000,\n\ttag: 0b1000000,\n};\n\n// First byte: continuation bit | 3-bit type | low 4 bits of length; the rest\n// of the length follows 7 bits at a time, 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\tlet rest = Math.floor(length / 16);\n\twhile (more) {\n\t\tmore = rest > 0b01111111;\n\t\tbytes.push((more ? 0b10000000 : 0) | (rest & 0b01111111));\n\t\trest = Math.floor(rest / 128);\n\t}\n\treturn new Uint8Array(bytes);\n}\n\n/** Git's object id: sha1(\"<type> <byte length>\\0<content>\"). */\nexport function hashGitObject(\n\ttype: string,\n\tcontent: Uint8Array,\n): Promise<string> {\n\treturn sha1(concat(encodeUtf8(`${type} ${content.length}\\0`), content));\n}\n\nexport interface PackObject {\n\ttype: PackObjectType;\n\tcontent: Uint8Array<ArrayBuffer>;\n}\n\n/** A version-2 pack holding `objects` as full entries, in order. */\nexport async function encodePack(\n\tobjects: readonly PackObject[],\n): Promise<Uint8Array<ArrayBuffer>> {\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);\n\tview.setUint32(8, objects.length, false);\n\n\tconst chunks: Uint8Array[] = [header];\n\tconst BATCH_SIZE = 100;\n\tfor (let i = 0; i < objects.length; i += BATCH_SIZE) {\n\t\tconst encoded = await Promise.all(\n\t\t\tobjects\n\t\t\t\t.slice(i, i + BATCH_SIZE)\n\t\t\t\t.map(async (object) =>\n\t\t\t\t\tconcat(\n\t\t\t\t\t\tencodePackObjectHeader(object.type, object.content.length),\n\t\t\t\t\t\tawait deflate(object.content),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t);\n\t\tchunks.push(...encoded);\n\t}\n\t// The trailer is the SHA-1 of every preceding byte.\n\tconst body = concat(...chunks);\n\treturn concat(body, fromHex(await sha1(body)));\n}\n\n/**\n * A pack's conventional base name, `pack-<checksum>`: the trailer SHA-1 is a\n * function of the content, so two writers can never pick the same name for\n * different packs, and rewriting the same pack is idempotent.\n */\nexport function packBaseName(pack: Uint8Array): string {\n\treturn `pack-${toHex(pack.subarray(pack.length - 20))}`;\n}\n\n/**\n * Store `pack` in `repo` and index it (isomorphic-git's `indexPack`, which\n * re-hashes every object). The `.idx` is written last: a pack only becomes\n * visible to readers once its index exists. Returns the base name and the\n * oids the index lists.\n */\nexport async function storePack(\n\trepo: Repo,\n\tpack: Uint8Array,\n): Promise<{ base: string; oids: string[] }> {\n\tconst fsp = rawFs(repo);\n\tconst packDir = `${repo.gitdir}/objects/pack`;\n\tconst base = packBaseName(pack);\n\tawait fsp.mkdir(packDir, { recursive: true });\n\tawait fsp.writeFile(`${packDir}/${packFileName(base, \"pack\")}`, pack);\n\tconst { oids } = await git.indexPack({\n\t\t...repo,\n\t\tdir: packDir,\n\t\tfilepath: packFileName(base, \"pack\"),\n\t});\n\treturn { base, oids };\n}\n","/**\n * A FIFO semaphore for bounding in-flight I/O.\n *\n * An unbounded `Promise.all` over a list whose size the caller (or an\n * attacker) controls — a git tree with tens of thousands of blob entries, say,\n * each costing one object-store read — issues that many concurrent requests\n * from a single push or clone. Sockets and memory then scale with the input,\n * and S3-compatible providers rate-limit per account, so one hostile push\n * degrades every other tenant. Bounding the *in-flight* count caps resource\n * use at `limit` no matter how wide the input is, while still letting reads\n * overlap up to that limit instead of running strictly sequentially.\n */\nexport interface Semaphore {\n\trun<T>(fn: () => Promise<T>): Promise<T>;\n}\n\n/**\n * Create a FIFO semaphore admitting at most `limit` concurrent `run`\n * executions. Waiters are queued as promises and released in call order — no\n * polling or timers — so a caller blocked on a full semaphore is woken the\n * instant a permit frees, not on the next tick of a poll loop.\n */\nexport function createSemaphore(limit: number): Semaphore {\n\tif (!Number.isInteger(limit) || limit < 1) {\n\t\tthrow new Error(\n\t\t\t`createSemaphore: limit must be an integer >= 1, got ${limit}`,\n\t\t);\n\t}\n\n\tlet active = 0;\n\tconst waiting: Array<() => void> = [];\n\n\t/**\n\t * Hand the permit to the next waiter instead of dropping it, so `active`\n\t * only decreases when nothing is queued and never exceeds `limit`.\n\t */\n\tfunction release(): void {\n\t\tconst next = waiting.shift();\n\t\tif (next) next();\n\t\telse active--;\n\t}\n\n\tfunction acquire(): Promise<void> {\n\t\tif (active < limit) {\n\t\t\tactive++;\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\treturn new Promise<void>((resolve) => {\n\t\t\twaiting.push(resolve);\n\t\t});\n\t}\n\n\treturn {\n\t\trun<T>(fn: () => Promise<T>): Promise<T> {\n\t\t\treturn acquire().then(async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await fn();\n\t\t\t\t} finally {\n\t\t\t\t\trelease();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -7,6 +7,15 @@ export interface CacheOptions {
7
7
  * `maxBytes` so one huge packfile cannot evict the whole working set.
8
8
  */
9
9
  maxEntryBytes?: number;
10
+ /**
11
+ * Largest range read (`getRange`) admitted to the cache, in bytes. A range
12
+ * read is a slice of a pack at a known offset: a header probe, or a small
13
+ * object's bytes. Slices up to this size are cached; larger slices always
14
+ * go to the backing store so a big blob cannot churn the cache. Range
15
+ * entries have their own LRU, capped at a quarter of `maxBytes`. Default
16
+ * 64 KiB; 0 disables range caching.
17
+ */
18
+ maxRangeBytes?: number;
10
19
  /** Entry time-to-live in milliseconds. Default 60 000. */
11
20
  ttlMs?: number;
12
21
  /**
@@ -31,6 +31,14 @@ export interface CommitSummary {
31
31
  export declare function summarizeCommit(commit: CommitInfo): CommitSummary;
32
32
  /** Follow annotated tags (possibly nested) down to the object they name. */
33
33
  export declare function peelToCommit(repo: Repo, oid: string): Promise<string>;
34
+ /**
35
+ * The commit oid a revision names: a branch, tag, `HEAD`, a full ref, a full
36
+ * sha or an abbreviated sha (4+ hex digits). Refs win over an abbreviated sha
37
+ * of the same spelling, as in git. Throws GitRefNotFoundError when nothing
38
+ * matches or an abbreviation is ambiguous. An abbreviation scans the pack
39
+ * indexes, so resolve once and read by the returned (immutable) oid.
40
+ */
41
+ export declare function resolveCommitOid(repo: Repo, rev: string): Promise<string>;
34
42
  /** Resolve a branch name / full ref / sha to its commit. */
35
43
  export declare function resolveCommit(repo: Repo, ref: string): Promise<{
36
44
  oid: string;
@@ -2,7 +2,7 @@ export { assertBranchExists, assertSafeBranchName, type Branch, createBranchFrom
2
2
  export { authorNow, type CommitAuthor, commitFilesToBare, deleteFileFromBare, writeCommitToBare, } from "./commit.js";
3
3
  export { type DiffEntry, type DiffFile, type DiffResult, getCommitDiff, getDiffBetweenRefs, MAX_DIFF_TEXT_BYTES, } from "./diff.js";
4
4
  export { BANNER_WALK_DEPTH, type FileHistoryResult, getFileHistory, } from "./file-history.js";
5
- export { type CommitInfo, type CommitLogOptions, type CommitSummary, type GitIdentity, getBlob, getCommit, getCommitHistory, getCommitLog, getFileContent, getTreeFromRef, HISTORY_WALK_DEPTH, peelToCommit, resolveCommit, summarizeCommit, } from "./history.js";
5
+ export { type CommitInfo, type CommitLogOptions, type CommitSummary, type GitIdentity, getBlob, getCommit, getCommitHistory, getCommitLog, getFileContent, getTreeFromRef, HISTORY_WALK_DEPTH, peelToCommit, resolveCommit, resolveCommitOid, summarizeCommit, } from "./history.js";
6
6
  export { getLastCommitsForTree } from "./last-commit.js";
7
7
  export { analyzeMerge, fastForwardMerge, type MergeAnalysis } from "./merge.js";
8
8
  export { withPackedWrites } from "./packed-writes.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-fs-s3",
3
- "version": "0.15.3",
3
+ "version": "0.16.0",
4
4
  "description": "isomorphic-git filesystem backend for S3-compatible object storage (AWS S3, Cloudflare R2, MinIO) — run git servers without a disk",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"semaphore-BviNoQuJ.js","names":[],"sources":["../src/ops/tree.ts","../src/ops/types.ts","../src/ops/history.ts","../src/ops/branch.ts","../src/pack-write.ts","../src/semaphore.ts"],"sourcesContent":["import git from \"isomorphic-git\";\nimport { GitInvalidRequestError, GitPathNotFoundError } from \"../git-errors.js\";\nimport type { Repo } from \"./types.js\";\n\nexport interface TreeEntry {\n\tpath: string;\n\tmode: string;\n\ttype: \"blob\" | \"tree\" | \"commit\";\n\toid: string;\n\tsize?: number;\n}\n\nconst joinPath = (prefix: string, name: string) =>\n\tprefix ? `${prefix.replace(/\\/+$/, \"\")}/${name}` : name;\n\n/** File modes a blob entry can carry; anything else isn't a regular file. */\nconst BLOB_MODES = new Set([\"100644\", \"100755\", \"120000\"]);\n\n/**\n * Build/update a git tree by overlaying new blobs onto an existing tree,\n * returning the new root tree oid. `entries` maps relative paths to blob oids.\n *\n * An updated file keeps its existing mode (executable bit, symlink); a path\n * that names an existing directory, or runs through an existing file, is\n * rejected rather than silently replacing what's there.\n */\nexport async function upsertTree(\n\trepo: Repo,\n\ttreeOid: string | undefined,\n\tentries: Map<string, string>,\n): Promise<string> {\n\tconst existing = treeOid\n\t\t? (await git.readTree({ ...repo, oid: treeOid })).tree\n\t\t: [];\n\tconst byName = new Map(existing.map((e) => [e.path, e]));\n\tconst direct = new Map<string, string>();\n\tconst nested = new Map<string, Map<string, string>>();\n\tfor (const [filePath, blobOid] of entries) {\n\t\tconst slash = filePath.indexOf(\"/\");\n\t\tif (slash === -1) {\n\t\t\tdirect.set(filePath, blobOid);\n\t\t} else {\n\t\t\tconst dir = filePath.slice(0, slash);\n\t\t\tconst rest = filePath.slice(slash + 1);\n\t\t\tif (!nested.has(dir)) nested.set(dir, new Map());\n\t\t\tnested.get(dir)?.set(rest, blobOid);\n\t\t}\n\t}\n\tfor (const [name, blobOid] of direct) {\n\t\tconst current = byName.get(name);\n\t\tif (current && current.type !== \"blob\") {\n\t\t\tthrow new GitInvalidRequestError(`\"${name}\" is a directory, not a file`);\n\t\t}\n\t\tbyName.set(name, {\n\t\t\tmode: current && BLOB_MODES.has(current.mode) ? current.mode : \"100644\",\n\t\t\tpath: name,\n\t\t\toid: blobOid,\n\t\t\ttype: \"blob\",\n\t\t});\n\t}\n\t// Sibling subdirectories are independent subtree writes — no reason to\n\t// serialize them for multi-file commits touching several directories.\n\tconst nestedResults = await Promise.all(\n\t\tArray.from(nested, async ([dir, subEntries]) => {\n\t\t\tconst entry = byName.get(dir);\n\t\t\tif (entry && entry.type !== \"tree\") {\n\t\t\t\tthrow new GitInvalidRequestError(`\"${dir}\" is a file, not a directory`);\n\t\t\t}\n\t\t\tconst subtreeOid = entry?.oid;\n\t\t\tconst newOid = await upsertTree(repo, subtreeOid, subEntries);\n\t\t\treturn [dir, newOid] as const;\n\t\t}),\n\t);\n\tfor (const [dir, newOid] of nestedResults) {\n\t\tbyName.set(dir, { mode: \"040000\", path: dir, oid: newOid, type: \"tree\" });\n\t}\n\treturn git.writeTree({ ...repo, tree: Array.from(byName.values()) });\n}\n\n/**\n * Remove a file path from a tree, returning the new root tree oid. Throws\n * GitPathNotFoundError when there's no such file; directories left empty\n * are dropped, as git never stores an empty subtree.\n */\nexport async function deleteFromTree(\n\trepo: Repo,\n\ttreeOid: string,\n\tfilePath: string,\n): Promise<string> {\n\tconst result = await deleteFromSubtree(repo, treeOid, filePath, filePath);\n\treturn result ?? git.writeTree({ ...repo, tree: [] });\n}\n\n/** The new subtree oid, or null when removing the file left it empty. */\nasync function deleteFromSubtree(\n\trepo: Repo,\n\ttreeOid: string,\n\tfilePath: string,\n\tfullPath: string,\n): Promise<string | null> {\n\tconst existing = (await git.readTree({ ...repo, oid: treeOid })).tree;\n\tconst byName = new Map(existing.map((e) => [e.path, e]));\n\tconst slash = filePath.indexOf(\"/\");\n\tconst name = slash === -1 ? filePath : filePath.slice(0, slash);\n\tconst entry = byName.get(name);\n\tif (slash === -1) {\n\t\tif (entry?.type !== \"blob\") {\n\t\t\tthrow new GitPathNotFoundError(`No such file: ${fullPath}`);\n\t\t}\n\t\tbyName.delete(name);\n\t} else {\n\t\tif (entry?.type !== \"tree\") {\n\t\t\tthrow new GitPathNotFoundError(`No such file: ${fullPath}`);\n\t\t}\n\t\tconst newOid = await deleteFromSubtree(\n\t\t\trepo,\n\t\t\tentry.oid,\n\t\t\tfilePath.slice(slash + 1),\n\t\t\tfullPath,\n\t\t);\n\t\tif (newOid === null) byName.delete(name);\n\t\telse byName.set(name, { ...entry, oid: newOid });\n\t}\n\tif (byName.size === 0) return null;\n\treturn git.writeTree({ ...repo, tree: Array.from(byName.values()) });\n}\n\n/** Resolve a path inside a tree to its entry, or null when absent. */\nexport async function findTreeEntry(\n\trepo: Repo,\n\trootTreeOid: string,\n\ttreePath: string,\n): Promise<TreeEntry | null> {\n\tif (!treePath) {\n\t\treturn { path: \"\", mode: \"040000\", type: \"tree\", oid: rootTreeOid };\n\t}\n\n\tconst parts = treePath.split(\"/\").filter(Boolean);\n\tlet currentTreeOid = rootTreeOid;\n\tlet currentPath = \"\";\n\n\tfor (const [index, part] of parts.entries()) {\n\t\tconst tree = await git.readTree({ ...repo, oid: currentTreeOid });\n\t\tconst entry = tree.tree.find((candidate) => candidate.path === part);\n\n\t\tif (!entry) return null;\n\n\t\tcurrentPath = currentPath ? joinPath(currentPath, entry.path) : entry.path;\n\n\t\tif (index === parts.length - 1) {\n\t\t\treturn {\n\t\t\t\tpath: currentPath,\n\t\t\t\tmode: entry.mode,\n\t\t\t\ttype: entry.type,\n\t\t\t\toid: entry.oid,\n\t\t\t};\n\t\t}\n\n\t\tif (entry.type !== \"tree\") return null;\n\n\t\tcurrentTreeOid = entry.oid;\n\t}\n\n\treturn null;\n}\n\n/** List a tree's direct entries, with paths prefixed by `prefix`. */\nexport async function listTreeEntries(\n\trepo: Repo,\n\ttreeOid: string,\n\tprefix = \"\",\n): Promise<TreeEntry[]> {\n\tconst tree = await git.readTree({ ...repo, oid: treeOid });\n\n\treturn tree.tree.map((entry) => ({\n\t\tpath: prefix ? joinPath(prefix, entry.path) : entry.path,\n\t\tmode: entry.mode,\n\t\ttype: entry.type,\n\t\toid: entry.oid,\n\t}));\n}\n","import type git from \"isomorphic-git\";\n\n/** The fs shape isomorphic-git accepts, derived from its own signatures. */\nexport type IsoGitFs = Parameters<typeof git.readTree>[0][\"fs\"];\n\n/**\n * A repository handle: everything isomorphic-git needs to address one bare\n * repo. Create it once per request (sharing `cache` across calls is what lets\n * isomorphic-git reuse parsed pack indexes) and pass it to every op.\n */\nexport interface Repo {\n\tfs: IsoGitFs;\n\tgitdir: string;\n\t/** isomorphic-git's shared parse cache — strongly recommended per repo. */\n\tcache?: object;\n}\n\n/**\n * Key/value store for memoizing expensive walk results (commit logs, tree\n * listings, per-path history). Keys are namespaced `kind:gitdir:headSha:…`,\n * so entries self-invalidate on push (new head, new key); evict\n * {@link resultKeyPrefixes} to drop a repo's superseded entries early.\n * Values are always objects (arrays or records).\n */\nexport interface ResultCache {\n\tget<T extends object>(key: string): T | null | undefined;\n\tset(key: string, value: object): void;\n}\n\n/** Optional instrumentation hooks accepted by every op. */\nexport interface OpsHooks {\n\tresultCache?: ResultCache;\n\t/** Wrap a timed sub-step (network walk, tree listing). Default: run directly. */\n\tstep?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;\n\t/** Diagnostic sink for cache hit/miss and walk summaries. */\n\tonNote?: (message: string) => void;\n\t/**\n\t * Warm every pack (e.g. `GitFs.prefetchPacks`) before a cache-miss read,\n\t * so a sequential walk doesn't discover packs one round trip at a time.\n\t */\n\tprefetch?: () => Promise<void>;\n}\n\nexport const runStep = <T>(\n\thooks: OpsHooks | undefined,\n\tlabel: string,\n\tfn: () => Promise<T>,\n): Promise<T> => (hooks?.step ? hooks.step(label, fn) : fn());\n\n/**\n * The result-cache key prefixes holding entries for `gitdir` — evict these\n * from your {@link ResultCache} when the repo's storage was rewritten outside\n * a normal push (rename, repack).\n */\nexport function resultKeyPrefixes(gitdir: string): string[] {\n\treturn [\n\t\t`commitlog:${gitdir}:`,\n\t\t`tree:${gitdir}:`,\n\t\t`last-commits:${gitdir}:`,\n\t\t`file-history:${gitdir}:`,\n\t];\n}\n","import git from \"isomorphic-git\";\nimport {\n\tGitObjectNotFoundError,\n\tGitPathNotFoundError,\n\tGitRefNotFoundError,\n} from \"../git-errors.js\";\nimport { qualifyBranchRef } from \"../refs.js\";\nimport { findTreeEntry, listTreeEntries, type TreeEntry } from \"./tree.js\";\nimport { type OpsHooks, type Repo, runStep } from \"./types.js\";\n\n/** How far back history walks look by default (commit count). */\nexport const HISTORY_WALK_DEPTH = 400;\n\nexport interface GitIdentity {\n\tname: string;\n\temail: string;\n\ttimestamp: number;\n\ttimezoneOffset: number;\n}\n\nexport interface CommitInfo {\n\toid: string;\n\tcommit: {\n\t\tmessage: string;\n\t\ttree: string;\n\t\tparent: string[];\n\t\tauthor: GitIdentity;\n\t\tcommitter: GitIdentity;\n\t};\n\tpayload: string;\n}\n\n/** The display summary of a commit (history lists, \"last commit\" columns). */\nexport interface CommitSummary {\n\tsha: string;\n\tmessage: string;\n\tauthorName: string;\n\tauthorEmail: string;\n\t/** ISO 8601 author date. */\n\tcreatedAt: string;\n}\n\nexport function summarizeCommit(commit: CommitInfo): CommitSummary {\n\treturn {\n\t\tsha: commit.oid,\n\t\tmessage: commit.commit.message.trim(),\n\t\tauthorName: commit.commit.author.name,\n\t\tauthorEmail: commit.commit.author.email,\n\t\tcreatedAt: new Date(commit.commit.author.timestamp * 1000).toISOString(),\n\t};\n}\n\nconst isNotFound = (err: unknown) =>\n\t(err as { code?: string } | null)?.code === \"NotFoundError\";\n\n/**\n * A ref that doesn't resolve is an expected condition (empty repo, unborn\n * branch) each caller handles. An object missing *underneath* a resolved ref\n * means storage is inconsistent — raised as GitObjectNotFoundError so it is\n * never mistaken for \"empty\".\n */\nfunction wrapMissingObject<T>(\n\tpromise: Promise<T>,\n\tcontext: string,\n): Promise<T> {\n\treturn promise.catch((err: unknown) => {\n\t\tif (isNotFound(err)) {\n\t\t\tthrow new GitObjectNotFoundError(\n\t\t\t\t`Git data for ${context} is missing from storage`,\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t});\n}\n\n/** The commit a branch name, full ref or sha points at; GitRefNotFoundError if none. */\n/**\n * A short name tries `refs/heads/<name>` first and then `refs/tags/<name>`,\n * so a branch wins over a same-named tag. Annotated tags are peeled to the\n * commit they point at, so every read accepts a tag wherever it accepts a\n * branch.\n */\nasync function resolveRefOid(repo: Repo, ref: string): Promise<string> {\n\tconst qualified = qualifyBranchRef(ref);\n\tconst candidates =\n\t\tqualified === `refs/heads/${ref}`\n\t\t\t? [qualified, `refs/tags/${ref}`]\n\t\t\t: [qualified];\n\tfor (const candidate of candidates) {\n\t\ttry {\n\t\t\treturn await peelToCommit(\n\t\t\t\trepo,\n\t\t\t\tawait git.resolveRef({ ...repo, ref: candidate }),\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tif (!isNotFound(err)) throw err;\n\t\t}\n\t}\n\tthrow new GitRefNotFoundError(`Ref ${ref} does not exist`);\n}\n\n/** Follow annotated tags (possibly nested) down to the object they name. */\nexport async function peelToCommit(repo: Repo, oid: string): Promise<string> {\n\tlet current = oid;\n\tfor (let depth = 0; depth < 10; depth++) {\n\t\tconst { type, object } = await git.readObject({\n\t\t\t...repo,\n\t\t\toid: current,\n\t\t\tformat: \"parsed\",\n\t\t});\n\t\tif (type !== \"tag\") return current;\n\t\tcurrent = (object as { object: string }).object;\n\t}\n\tthrow new GitRefNotFoundError(`Tag chain at ${oid} is too deep`);\n}\n\n/** Resolve a branch name / full ref / sha to its commit. */\nexport async function resolveCommit(repo: Repo, ref: string) {\n\tconst oid = await resolveRefOid(repo, ref);\n\tconst { commit } = await wrapMissingObject(\n\t\tgit.readCommit({ ...repo, oid }),\n\t\t`${repo.gitdir} commit ${oid}`,\n\t);\n\treturn { oid, commit };\n}\n\n/** Read a blob's bytes by oid. */\nexport async function getBlob(repo: Repo, sha: string): Promise<Uint8Array> {\n\tconst { blob } = await wrapMissingObject(\n\t\tgit.readBlob({ ...repo, oid: sha }),\n\t\t`${repo.gitdir} blob ${sha}`,\n\t);\n\treturn blob;\n}\n\n/** Read a file's bytes at a ref. Throws GitPathNotFoundError when absent. */\nexport async function getFileContent(\n\trepo: Repo,\n\tfilePath: string,\n\tref: string,\n\thooks?: OpsHooks,\n): Promise<Uint8Array> {\n\t// A commit, a tree per path segment, then the blob: warm every pack first\n\t// rather than discovering them one round trip at a time.\n\tif (hooks?.prefetch) await runStep(hooks, \"prefetch\", hooks.prefetch);\n\tconst { commit } = await resolveCommit(repo, ref);\n\tconst context = `${repo.gitdir}@${ref}:${filePath}`;\n\tconst entry = await wrapMissingObject(\n\t\tfindTreeEntry(repo, commit.tree, filePath),\n\t\tcontext,\n\t);\n\tif (entry?.type !== \"blob\") {\n\t\tthrow new GitPathNotFoundError(`File not found: ${filePath}`);\n\t}\n\tconst { blob } = await wrapMissingObject(\n\t\tgit.readBlob({ ...repo, oid: entry.oid }),\n\t\tcontext,\n\t);\n\treturn blob;\n}\n\n/** Read one commit by sha. */\nexport async function getCommit(repo: Repo, sha: string): Promise<CommitInfo> {\n\tconst result = await wrapMissingObject(\n\t\tgit.readCommit({ ...repo, oid: sha }),\n\t\t`${repo.gitdir} commit ${sha}`,\n\t);\n\treturn { oid: result.oid, commit: result.commit, payload: result.payload };\n}\n\nexport interface CommitLogOptions {\n\tref: string;\n\tdepth?: number;\n\t/** The ref's commit, when the caller already resolved it (saves a lookup). */\n\tknownHeadSha?: string;\n}\n\n/**\n * The commit chain from a ref, newest first; [] when the ref doesn't exist.\n * Walking is sequential and round-trip-bound on object storage, so the\n * deepest walk seen per head is memoized in `hooks.resultCache` and sliced\n * for shallower requests.\n */\nexport async function getCommitLog(\n\trepo: Repo,\n\toptions: CommitLogOptions,\n\thooks?: OpsHooks,\n): Promise<CommitInfo[]> {\n\tconst { ref } = options;\n\tconst depth = options.depth ?? 50;\n\tlet headSha = options.knownHeadSha;\n\tif (headSha === undefined) {\n\t\ttry {\n\t\t\theadSha = await resolveRefOid(repo, ref);\n\t\t} catch (err) {\n\t\t\tif (err instanceof GitRefNotFoundError) return [];\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tconst cacheKey = `commitlog:${repo.gitdir}:${headSha}`;\n\tconst cached = hooks?.resultCache?.get<CommitInfo[]>(cacheKey);\n\tconst fullyWalked = cached?.at(-1)?.commit.parent.length === 0;\n\tif (cached && (cached.length >= depth || fullyWalked)) {\n\t\thooks?.onNote?.(`getCommitLog: cache hit ${cacheKey} (depth=${depth})`);\n\t\treturn cached.slice(0, depth);\n\t}\n\thooks?.onNote?.(`getCommitLog: cache miss ${cacheKey} (depth=${depth})`);\n\tif (hooks?.prefetch) await runStep(hooks, \"prefetch\", hooks.prefetch);\n\n\tconst log = await wrapMissingObject(\n\t\trunStep(hooks, `git.log ${ref} depth=${depth}`, () =>\n\t\t\tgit.log({ ...repo, ref: headSha, depth }),\n\t\t),\n\t\t`${repo.gitdir}@${ref} history`,\n\t);\n\tconst result = log.map(({ oid, commit, payload }) => ({\n\t\toid,\n\t\tcommit,\n\t\tpayload: payload ?? \"\",\n\t}));\n\tif (!cached || result.length > cached.length) {\n\t\thooks?.resultCache?.set(cacheKey, result);\n\t}\n\treturn result;\n}\n\n/**\n * List a directory at the tip of a ref. Throws GitRefNotFoundError when the\n * ref doesn't resolve and GitPathNotFoundError when `treePath` doesn't exist.\n * Memoized per head sha.\n */\nexport async function getTreeFromRef(\n\trepo: Repo,\n\toptions: { ref: string; treePath?: string },\n\thooks?: OpsHooks,\n): Promise<TreeEntry[]> {\n\tconst { ref } = options;\n\tconst treePath = options.treePath ?? \"\";\n\tconst { commit, oid: headSha } = await resolveCommit(repo, ref);\n\n\tconst cacheKey = `tree:${repo.gitdir}:${headSha}:${treePath}`;\n\tconst cached = hooks?.resultCache?.get<TreeEntry[]>(cacheKey);\n\tif (cached) {\n\t\thooks?.onNote?.(`getTreeFromRef: cache hit ${cacheKey}`);\n\t\treturn cached;\n\t}\n\thooks?.onNote?.(`getTreeFromRef: cache miss ${cacheKey}`);\n\tif (hooks?.prefetch) await runStep(hooks, \"prefetch\", hooks.prefetch);\n\n\tconst context = `${repo.gitdir}@${ref}:${treePath || \"/\"}`;\n\tconst dir = treePath\n\t\t? await wrapMissingObject(\n\t\t\t\trunStep(hooks, `findTreeEntry ${treePath}`, () =>\n\t\t\t\t\tfindTreeEntry(repo, commit.tree, treePath),\n\t\t\t\t),\n\t\t\t\tcontext,\n\t\t\t)\n\t\t: { path: \"\", type: \"tree\" as const, oid: commit.tree };\n\tif (!dir) {\n\t\tthrow new GitPathNotFoundError(\n\t\t\t`Path \"${treePath}\" does not exist at ${ref}`,\n\t\t);\n\t}\n\tconst result =\n\t\tdir.type === \"tree\"\n\t\t\t? await wrapMissingObject(\n\t\t\t\t\trunStep(hooks, `listTreeEntries ${treePath || \"/\"}`, () =>\n\t\t\t\t\t\tlistTreeEntries(repo, dir.oid, dir.path),\n\t\t\t\t\t),\n\t\t\t\t\tcontext,\n\t\t\t\t)\n\t\t\t: [];\n\thooks?.resultCache?.set(cacheKey, result);\n\treturn result;\n}\n\n/**\n * A page of commit history from a ref tip; [] when the ref doesn't exist.\n * Pages are slices of {@link getCommitLog}'s memoized walk.\n */\nexport async function getCommitHistory(\n\trepo: Repo,\n\toptions: { ref: string; limit?: number; skip?: number },\n\thooks?: OpsHooks,\n): Promise<CommitInfo[]> {\n\tconst limit = options.limit ?? 50;\n\tconst skip = options.skip ?? 0;\n\tconst log = await getCommitLog(\n\t\trepo,\n\t\t{ ref: options.ref, depth: limit + skip },\n\t\thooks,\n\t);\n\treturn log.slice(skip, skip + limit);\n}\n","import git from \"isomorphic-git\";\nimport {\n\tGitConflictError,\n\tGitInvalidRequestError,\n\tisAbsentError,\n} from \"../git-errors.js\";\nimport { isSafeBranchName } from \"../refs.js\";\nimport { peelToCommit } from \"./history.js\";\nimport type { Repo } from \"./types.js\";\n\nexport interface Branch {\n\tname: string;\n\tcommit: string;\n\tisDefault: boolean;\n}\n\nexport interface Tag {\n\tname: string;\n\t/** The commit the tag names (annotated tags peeled). */\n\tcommit: string;\n\t/** The tag object's own oid, for annotated tags; absent for lightweight ones. */\n\ttagObject?: string;\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\ntype PromisesFs = {\n\treadFile(path: string, encoding: \"utf8\"): Promise<string | Uint8Array>;\n};\n\n// Repo.fs is typed as isomorphic-git's own fs union (promise- or\n// callback-based) since that's what git.readTree's signature allows, but\n// every fs this package actually constructs (git-fs.ts) is promise-based —\n// this just keeps the callback-style branch honest instead of crashing on\n// it. Shared by every loose-ref fast path below instead of each repeating\n// the same cast + presence check.\nfunction getPromisesFs(fs: Repo[\"fs\"]): PromisesFs | null {\n\tconst promises = (fs as { promises?: PromisesFs }).promises;\n\treturn promises ?? null;\n}\n\nfunction decodeFileContent(content: string | Uint8Array): string {\n\treturn typeof content === \"string\"\n\t\t? content\n\t\t: new TextDecoder().decode(content);\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\treturn listLooseRefNames(repo, \"refs/heads\");\n}\n\n/**\n * Loose ref names under `dir` (e.g. `refs/tags`) in one recursive listing,\n * or `null` when the fs can't list recursively (use isomorphic-git's walk).\n */\nexport async function listLooseRefNames(\n\trepo: Repo,\n\tdir: \"refs/heads\" | \"refs/tags\",\n): Promise<string[] | null> {\n\tif (!hasRecursiveFileLister(repo.fs)) return null;\n\tconst names = await repo.fs.listFilesRecursively(`${repo.gitdir}/${dir}`);\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 = getPromisesFs(repo.fs);\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 decodeFileContent(content).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 (error) {\n\t\tif (isAbsentError(error)) return new Map();\n\t\tthrow error;\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 = getPromisesFs(repo.fs);\n\tif (!promisesFs) return null;\n\ttry {\n\t\tconst content = await promisesFs.readFile(`${repo.gitdir}/HEAD`, \"utf8\");\n\t\tconst head = decodeFileContent(content).trim();\n\t\tconst match = /^ref: refs\\/heads\\/(.+)$/.exec(head);\n\t\treturn match?.[1] && isSafeBranchName(match[1]) ? match[1] : null;\n\t} catch (error) {\n\t\t// No HEAD file is \"no branch\"; a failed read is not (#93).\n\t\tif (isAbsentError(error)) return null;\n\t\tthrow error;\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\tconst promisesFs = getPromisesFs(repo.fs);\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 = decodeFileContent(content).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((error: unknown) => {\n\t\t\t\t\t\t\tif (isAbsentError(error)) return null;\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t})\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: string,\n): Promise<void> {\n\tassertSafeBranchName(name);\n\tassertSafeBranchName(startPoint);\n\tconst object = await resolveLooseRefFast(repo, `refs/heads/${startPoint}`);\n\ttry {\n\t\tawait git.branch({ ...repo, ref: name, checkout: false, object });\n\t} catch (err) {\n\t\tif ((err as { code?: string }).code === \"AlreadyExistsError\") {\n\t\t\tthrow new GitConflictError(`Branch ${name} already exists`);\n\t\t}\n\t\tthrow err;\n\t}\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\t// Same rule as receive-pack: deleting the branch HEAD names leaves every\n\t// clone without a default branch.\n\tconst current =\n\t\t(await getCurrentLooseBranch(repo)) ??\n\t\t(await git\n\t\t\t.currentBranch({ ...repo, fullname: false })\n\t\t\t.catch((error: unknown) => {\n\t\t\t\t// A failed HEAD read must not skip this guard: isomorphic-git's\n\t\t\t\t// deleteBranch would then detach HEAD instead of refusing (#93).\n\t\t\t\tif (isAbsentError(error)) return null;\n\t\t\t\tthrow error;\n\t\t\t}));\n\tif (current === name) {\n\t\tthrow new GitInvalidRequestError(\n\t\t\t`Cannot delete ${name}: it is the default branch`,\n\t\t);\n\t}\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/** All tags with the commits they name, sorted by name; [] when there are none. */\nexport async function listTags(repo: Repo): Promise<Tag[]> {\n\tconst names = (await listLooseRefNames(repo, \"refs/tags\")) ?? [];\n\tconst all = new Set([\n\t\t...names,\n\t\t...(await git.listTags(repo).catch((error: unknown) => {\n\t\t\tif (isAbsentError(error)) return [] as string[];\n\t\t\tthrow error;\n\t\t})),\n\t]);\n\tconst tags = await Promise.all(\n\t\t[...all].filter(isSafeBranchName).map(async (name): Promise<Tag | null> => {\n\t\t\ttry {\n\t\t\t\tconst oid = await resolveLooseRefFast(repo, `refs/tags/${name}`);\n\t\t\t\tconst commit = await peelToCommit(repo, oid);\n\t\t\t\treturn commit === oid\n\t\t\t\t\t? { name, commit }\n\t\t\t\t\t: { name, commit, tagObject: oid };\n\t\t\t} catch (error) {\n\t\t\t\t// A tag deleted since the listing, or naming a missing object, is\n\t\t\t\t// not listed; a failed read fails the listing instead of\n\t\t\t\t// silently shortening it (#93).\n\t\t\t\tif (isAbsentError(error)) return null;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}),\n\t);\n\treturn tags\n\t\t.filter((tag): tag is Tag => tag !== null)\n\t\t.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));\n}\n","/**\n * Writing packfiles: serialize whole (never deltified) objects into a pack,\n * and store a pack plus its index in a repository. Shared by repack (the\n * loose-object fallback) and packed writes (ops/packed-writes.ts).\n *\n * Edge-compatible: Web Crypto SHA-1, CompressionStream, Uint8Array only.\n */\nimport git from \"isomorphic-git\";\nimport {\n\tconcat,\n\tdeflate,\n\tencodeUtf8,\n\tfromHex,\n\tsha1,\n\ttoHex,\n} from \"./edge-utils.js\";\nimport { rawFs } from \"./http/types.js\";\nimport type { Repo } from \"./ops/types.js\";\nimport { packFileName } from \"./pack-files.js\";\n\nexport type PackObjectType = \"commit\" | \"tree\" | \"blob\" | \"tag\";\n\n// Git pack object type bits (bits 6-4 of the header's first byte).\nconst PACK_OBJECT_TYPE_BITS: Record<PackObjectType, number> = {\n\tcommit: 0b0010000,\n\ttree: 0b0100000,\n\tblob: 0b0110000,\n\ttag: 0b1000000,\n};\n\n// First byte: continuation bit | 3-bit type | low 4 bits of length; the rest\n// of the length follows 7 bits at a time, 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\tlet rest = Math.floor(length / 16);\n\twhile (more) {\n\t\tmore = rest > 0b01111111;\n\t\tbytes.push((more ? 0b10000000 : 0) | (rest & 0b01111111));\n\t\trest = Math.floor(rest / 128);\n\t}\n\treturn new Uint8Array(bytes);\n}\n\n/** Git's object id: sha1(\"<type> <byte length>\\0<content>\"). */\nexport function hashGitObject(\n\ttype: string,\n\tcontent: Uint8Array,\n): Promise<string> {\n\treturn sha1(concat(encodeUtf8(`${type} ${content.length}\\0`), content));\n}\n\nexport interface PackObject {\n\ttype: PackObjectType;\n\tcontent: Uint8Array<ArrayBuffer>;\n}\n\n/** A version-2 pack holding `objects` as full entries, in order. */\nexport async function encodePack(\n\tobjects: readonly PackObject[],\n): Promise<Uint8Array<ArrayBuffer>> {\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);\n\tview.setUint32(8, objects.length, false);\n\n\tconst chunks: Uint8Array[] = [header];\n\tconst BATCH_SIZE = 100;\n\tfor (let i = 0; i < objects.length; i += BATCH_SIZE) {\n\t\tconst encoded = await Promise.all(\n\t\t\tobjects\n\t\t\t\t.slice(i, i + BATCH_SIZE)\n\t\t\t\t.map(async (object) =>\n\t\t\t\t\tconcat(\n\t\t\t\t\t\tencodePackObjectHeader(object.type, object.content.length),\n\t\t\t\t\t\tawait deflate(object.content),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t);\n\t\tchunks.push(...encoded);\n\t}\n\t// The trailer is the SHA-1 of every preceding byte.\n\tconst body = concat(...chunks);\n\treturn concat(body, fromHex(await sha1(body)));\n}\n\n/**\n * A pack's conventional base name, `pack-<checksum>`: the trailer SHA-1 is a\n * function of the content, so two writers can never pick the same name for\n * different packs, and rewriting the same pack is idempotent.\n */\nexport function packBaseName(pack: Uint8Array): string {\n\treturn `pack-${toHex(pack.subarray(pack.length - 20))}`;\n}\n\n/**\n * Store `pack` in `repo` and index it (isomorphic-git's `indexPack`, which\n * re-hashes every object). The `.idx` is written last: a pack only becomes\n * visible to readers once its index exists. Returns the base name and the\n * oids the index lists.\n */\nexport async function storePack(\n\trepo: Repo,\n\tpack: Uint8Array,\n): Promise<{ base: string; oids: string[] }> {\n\tconst fsp = rawFs(repo);\n\tconst packDir = `${repo.gitdir}/objects/pack`;\n\tconst base = packBaseName(pack);\n\tawait fsp.mkdir(packDir, { recursive: true });\n\tawait fsp.writeFile(`${packDir}/${packFileName(base, \"pack\")}`, pack);\n\tconst { oids } = await git.indexPack({\n\t\t...repo,\n\t\tdir: packDir,\n\t\tfilepath: packFileName(base, \"pack\"),\n\t});\n\treturn { base, oids };\n}\n","/**\n * A FIFO semaphore for bounding in-flight I/O.\n *\n * An unbounded `Promise.all` over a list whose size the caller (or an\n * attacker) controls — a git tree with tens of thousands of blob entries, say,\n * each costing one object-store read — issues that many concurrent requests\n * from a single push or clone. Sockets and memory then scale with the input,\n * and S3-compatible providers rate-limit per account, so one hostile push\n * degrades every other tenant. Bounding the *in-flight* count caps resource\n * use at `limit` no matter how wide the input is, while still letting reads\n * overlap up to that limit instead of running strictly sequentially.\n */\nexport interface Semaphore {\n\trun<T>(fn: () => Promise<T>): Promise<T>;\n}\n\n/**\n * Create a FIFO semaphore admitting at most `limit` concurrent `run`\n * executions. Waiters are queued as promises and released in call order — no\n * polling or timers — so a caller blocked on a full semaphore is woken the\n * instant a permit frees, not on the next tick of a poll loop.\n */\nexport function createSemaphore(limit: number): Semaphore {\n\tif (!Number.isInteger(limit) || limit < 1) {\n\t\tthrow new Error(\n\t\t\t`createSemaphore: limit must be an integer >= 1, got ${limit}`,\n\t\t);\n\t}\n\n\tlet active = 0;\n\tconst waiting: Array<() => void> = [];\n\n\t/**\n\t * Hand the permit to the next waiter instead of dropping it, so `active`\n\t * only decreases when nothing is queued and never exceeds `limit`.\n\t */\n\tfunction release(): void {\n\t\tconst next = waiting.shift();\n\t\tif (next) next();\n\t\telse active--;\n\t}\n\n\tfunction acquire(): Promise<void> {\n\t\tif (active < limit) {\n\t\t\tactive++;\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\treturn new Promise<void>((resolve) => {\n\t\t\twaiting.push(resolve);\n\t\t});\n\t}\n\n\treturn {\n\t\trun<T>(fn: () => Promise<T>): Promise<T> {\n\t\t\treturn acquire().then(async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await fn();\n\t\t\t\t} finally {\n\t\t\t\t\trelease();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t};\n}\n"],"mappings":"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"}