git-fs-s3 0.4.0 → 0.5.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.
Files changed (62) hide show
  1. package/README.md +23 -17
  2. package/dist/{chunk-Z4656PGE.js → chunk-2JP4MS6H.js} +2 -2
  3. package/dist/{chunk-YQFNY6PG.js → chunk-FIFWOMPM.js} +1 -1
  4. package/dist/chunk-FIFWOMPM.js.map +1 -0
  5. package/dist/http.js +3 -3
  6. package/dist/http.js.map +1 -1
  7. package/dist/index.js +2 -3
  8. package/dist/index.js.map +1 -1
  9. package/dist/ops.js +2 -2
  10. package/dist/types/cache.d.ts +81 -0
  11. package/dist/types/edge-utils.d.ts +43 -0
  12. package/dist/types/errors.d.ts +16 -0
  13. package/dist/types/git-errors.d.ts +70 -0
  14. package/dist/types/git-fs.d.ts +60 -0
  15. package/dist/types/git-layout.d.ts +52 -0
  16. package/dist/types/http/index.d.ts +7 -0
  17. package/dist/types/http/info-refs.d.ts +27 -0
  18. package/dist/types/http/pack-reuse.d.ts +26 -0
  19. package/dist/types/http/pkt-line.d.ts +14 -0
  20. package/dist/types/http/reachability.d.ts +36 -0
  21. package/dist/types/http/receive-pack.d.ts +54 -0
  22. package/dist/types/http/repack.d.ts +24 -0
  23. package/dist/types/http/types.d.ts +40 -0
  24. package/dist/types/http/upload-pack.d.ts +21 -0
  25. package/dist/types/index.d.ts +11 -0
  26. package/dist/types/ops/branch.d.ts +38 -0
  27. package/dist/types/ops/commit.d.ts +45 -0
  28. package/dist/types/ops/diff.d.ts +24 -0
  29. package/dist/types/ops/file-history.d.ts +41 -0
  30. package/dist/types/ops/history.d.ts +85 -0
  31. package/dist/types/ops/index.d.ts +9 -0
  32. package/dist/types/ops/last-commit.d.ts +23 -0
  33. package/dist/types/ops/merge.d.ts +28 -0
  34. package/dist/types/ops/tree.d.ts +19 -0
  35. package/dist/{types-BHoHOaQt.d.cts → types/ops/types.d.ts} +7 -9
  36. package/dist/types/pack-files.d.ts +28 -0
  37. package/dist/types/path.d.ts +9 -0
  38. package/dist/types/refs.d.ts +54 -0
  39. package/dist/types/retry.d.ts +52 -0
  40. package/dist/types/stores/memory.d.ts +16 -0
  41. package/dist/{s3.d.ts → types/stores/s3.d.ts} +4 -7
  42. package/dist/{types-dbHzZ4ed.d.ts → types/types.d.ts} +11 -22
  43. package/package.json +18 -22
  44. package/dist/chunk-YQFNY6PG.js.map +0 -1
  45. package/dist/http.cjs +0 -1063
  46. package/dist/http.cjs.map +0 -1
  47. package/dist/http.d.cts +0 -215
  48. package/dist/http.d.ts +0 -215
  49. package/dist/index.cjs +0 -944
  50. package/dist/index.cjs.map +0 -1
  51. package/dist/index.d.cts +0 -479
  52. package/dist/index.d.ts +0 -479
  53. package/dist/ops.cjs +0 -1060
  54. package/dist/ops.cjs.map +0 -1
  55. package/dist/ops.d.cts +0 -302
  56. package/dist/ops.d.ts +0 -302
  57. package/dist/s3.cjs +0 -123
  58. package/dist/s3.cjs.map +0 -1
  59. package/dist/s3.d.cts +0 -36
  60. package/dist/types-BHoHOaQt.d.ts +0 -53
  61. package/dist/types-dbHzZ4ed.d.cts +0 -131
  62. /package/dist/{chunk-Z4656PGE.js.map → chunk-2JP4MS6H.js.map} +0 -0
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cache.ts","../src/errors.ts","../src/git-fs.ts","../src/git-layout.ts","../src/path.ts","../src/retry.ts","../src/stores/memory.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 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\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}\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(() => pending.delete(key));\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 fetched = await store.get(key);\n\t\t\t\tif (fetched !== null) {\n\t\t\t\t\tadmit(key, fetched);\n\t\t\t\t} else if (cacheMisses) {\n\t\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\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\tawait store.put(key, data);\n\t\t\tadmit(key, data);\n\t\t\tif (data.byteLength > maxEntryBytes) cache.delete(key);\n\t\t\tclearStaleListEntries(key);\n\t\t},\n\n\t\tasync delete(key: string): Promise<void> {\n\t\t\tawait store.delete(key);\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\tclearStaleListEntries(key);\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 stat = await store.head(key);\n\t\t\t\tif (stat === null && cacheMisses) {\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 fetched = await store.list(prefix, listOptions);\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\tinvalidate(prefix: string): void {\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 eisdir = (syscall: string, path: string) =>\n\tnew FsError(\"EISDIR\", 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 { 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 structurallyAbsent = options.isStructurallyAbsent;\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 (structurallyAbsent?.(path) || 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 promises: GitFsClient[\"promises\"] = {\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","/**\n * Typed classification of paths inside a git directory, so callers can\n * reason about \"a ref\", \"a loose object\" or \"the pack listing\" as values\n * instead of matching path strings against patterns.\n *\n * Edge-compatible: no node: imports.\n */\n\nimport { type PackFileName, parsePackFileName } from \"./pack-files.js\";\n\n/** What a gitdir-relative path refers to. Listing prefixes end in `/`. */\nexport type GitdirEntry =\n\t| { readonly kind: \"head\" }\n\t| { readonly kind: \"config\" }\n\t/** A ref file (`refs/heads/main`) or a refs listing (`refs/heads/`). */\n\t| { readonly kind: \"refs\"; readonly path: string }\n\t/** The `objects/` listing. */\n\t| { readonly kind: \"objects-dir\" }\n\t/** The `objects/pack/` listing. */\n\t| { readonly kind: \"pack-dir\" }\n\t| { readonly kind: \"pack-file\"; readonly file: PackFileName }\n\t| { readonly kind: \"loose-object\"; readonly oid: string }\n\t| { readonly kind: \"other\" };\n\nconst HEX = new Set(\"0123456789abcdef\");\n\nfunction isHex(value: string, length: number): boolean {\n\tif (value.length !== length) return false;\n\tfor (const char of value) if (!HEX.has(char)) return false;\n\treturn true;\n}\n\n/** Classify a path relative to a gitdir (no leading slash). */\nexport function classifyGitdirPath(path: string): GitdirEntry {\n\tconst segments = path.split(\"/\");\n\tconst [first, second, third, ...rest] = segments;\n\tif (segments.length === 1) {\n\t\tif (first === \"HEAD\") return { kind: \"head\" };\n\t\tif (first === \"config\") return { kind: \"config\" };\n\t\tif (first === \"refs\") return { kind: \"refs\", path };\n\t\treturn { kind: \"other\" };\n\t}\n\tif (first === \"refs\") return { kind: \"refs\", path };\n\tif (first !== \"objects\" || second === undefined) return { kind: \"other\" };\n\tif (segments.length === 2 && second === \"\") return { kind: \"objects-dir\" };\n\tif (second === \"pack\" && third !== undefined && rest.length === 0) {\n\t\tif (third === \"\") return { kind: \"pack-dir\" };\n\t\tconst file = parsePackFileName(third);\n\t\treturn file ? { kind: \"pack-file\", file } : { kind: \"other\" };\n\t}\n\tif (\n\t\tthird !== undefined &&\n\t\trest.length === 0 &&\n\t\tisHex(second, 2) &&\n\t\tisHex(third, 38)\n\t) {\n\t\treturn { kind: \"loose-object\", oid: second + third };\n\t}\n\treturn { kind: \"other\" };\n}\n\n/**\n * Whether reads of this entry can change between pushes: refs, `HEAD`, and\n * the object/pack listings. Everything else is content-addressed (objects)\n * or written once (config, a given pack file name).\n */\nexport function isMutableGitdirEntry(entry: GitdirEntry): boolean {\n\tswitch (entry.kind) {\n\t\tcase \"head\":\n\t\tcase \"refs\":\n\t\tcase \"objects-dir\":\n\t\tcase \"pack-dir\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * A loose-object path anywhere in the fs, split into its gitdir and oid —\n * for callers that don't know where the gitdir boundary is: a loose object\n * is always `<gitdir>/objects/<2 hex>/<38 hex>`.\n */\nexport function parseLooseObjectPath(\n\tpath: string,\n): { readonly gitdir: string; readonly oid: string } | null {\n\tconst segments = path.split(\"/\");\n\tif (segments.length < 3) return null;\n\tconst gitdirSegments = segments.slice(0, -3);\n\tconst entry = classifyGitdirPath(segments.slice(-3).join(\"/\"));\n\tif (entry.kind !== \"loose-object\") return null;\n\treturn { gitdir: gitdirSegments.join(\"/\"), oid: entry.oid };\n}\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 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\nfunction defaultIsRetryable(error: unknown): boolean {\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 ?? defaultIsRetryable;\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\n\tasync function guarded<T>(fn: () => Promise<T>): Promise<T> {\n\t\tif (breaker === null) return fn();\n\t\tif (state === \"open\") {\n\t\t\tif (Date.now() - lastFailureAt < breaker.resetMs) {\n\t\t\t\tthrow new CircuitOpenError();\n\t\t\t}\n\t\t\tstate = \"half-open\";\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fn();\n\t\t\tif (state === \"half-open\") {\n\t\t\t\tstate = \"closed\";\n\t\t\t\tfailures = 0;\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tfailures++;\n\t\t\tlastFailureAt = Date.now();\n\t\t\tif (failures >= breaker.threshold) state = \"open\";\n\t\t\tthrow error;\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\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};\n}\n","import type {\n\tListOptions,\n\tListResult,\n\tObjectStat,\n\tObjectStore,\n} from \"../types.js\";\n\n/**\n * In-memory {@link ObjectStore}. Useful for tests, examples, and ephemeral\n * repositories; also the reference implementation for the list/delimiter\n * semantics other stores must match.\n */\nexport class MemoryObjectStore implements ObjectStore {\n\tprivate readonly objects = new Map<string, Uint8Array>();\n\n\tasync get(key: string): Promise<Uint8Array | null> {\n\t\tconst data = this.objects.get(key);\n\t\treturn data ? data.slice() : null;\n\t}\n\n\tasync put(key: string, data: Uint8Array): Promise<void> {\n\t\tthis.objects.set(key, data.slice());\n\t}\n\n\tasync delete(key: string): Promise<void> {\n\t\tthis.objects.delete(key);\n\t}\n\n\tasync head(key: string): Promise<ObjectStat | null> {\n\t\tconst data = this.objects.get(key);\n\t\treturn data ? { size: data.byteLength } : null;\n\t}\n\n\tasync list(prefix: string, options?: ListOptions): Promise<ListResult> {\n\t\tconst delimiter = options?.delimiter;\n\t\tconst limit = options?.limit ?? Number.POSITIVE_INFINITY;\n\t\tconst objects: ListResult[\"objects\"] = [];\n\t\tconst prefixes = new Set<string>();\n\n\t\tfor (const [key, data] of this.objects) {\n\t\t\tif (!key.startsWith(prefix)) continue;\n\t\t\tconst rest = key.slice(prefix.length);\n\t\t\tif (delimiter !== undefined) {\n\t\t\t\tconst idx = rest.indexOf(delimiter);\n\t\t\t\tif (idx !== -1) {\n\t\t\t\t\tprefixes.add(prefix + rest.slice(0, idx + delimiter.length));\n\t\t\t\t} else {\n\t\t\t\t\tobjects.push({ key, size: data.byteLength });\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tobjects.push({ key, size: data.byteLength });\n\t\t\t}\n\t\t\tif (objects.length + prefixes.size >= limit) break;\n\t\t}\n\n\t\treturn { objects, prefixes: [...prefixes] };\n\t}\n\n\t/** Number of stored objects (test convenience, not part of ObjectStore). */\n\tget size(): number {\n\t\treturn this.objects.size;\n\t}\n}\n"],"mappings":"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","names":["LRUCache","toKey","LRUCache"]}
1
+ {"version":3,"sources":["../src/cache.ts","../src/errors.ts","../src/git-fs.ts","../src/git-layout.ts","../src/path.ts","../src/retry.ts","../src/stores/memory.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 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\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}\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(() => pending.delete(key));\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 fetched = await store.get(key);\n\t\t\t\tif (fetched !== null) {\n\t\t\t\t\tadmit(key, fetched);\n\t\t\t\t} else if (cacheMisses) {\n\t\t\t\t\tcache.set(key, MISS, { ttl: ttlForKey?.(key) });\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\tawait store.put(key, data);\n\t\t\tadmit(key, data);\n\t\t\tif (data.byteLength > maxEntryBytes) cache.delete(key);\n\t\t\tclearStaleListEntries(key);\n\t\t},\n\n\t\tasync delete(key: string): Promise<void> {\n\t\t\tawait store.delete(key);\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\tclearStaleListEntries(key);\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 stat = await store.head(key);\n\t\t\t\tif (stat === null && cacheMisses) {\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 fetched = await store.list(prefix, listOptions);\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\tinvalidate(prefix: string): void {\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 eisdir = (syscall: string, path: string) =>\n\tnew FsError(\"EISDIR\", 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 { 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 promises: GitFsClient[\"promises\"] = {\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","/**\n * Typed classification of paths inside a git directory, so callers can\n * reason about \"a ref\", \"a loose object\" or \"the pack listing\" as values\n * instead of matching path strings against patterns.\n *\n * Edge-compatible: no node: imports.\n */\n\nimport { type PackFileName, parsePackFileName } from \"./pack-files.js\";\n\n/** What a gitdir-relative path refers to. Listing prefixes end in `/`. */\nexport type GitdirEntry =\n\t| { readonly kind: \"head\" }\n\t| { readonly kind: \"config\" }\n\t/** A ref file (`refs/heads/main`) or a refs listing (`refs/heads/`). */\n\t| { readonly kind: \"refs\"; readonly path: string }\n\t/** The `objects/` listing. */\n\t| { readonly kind: \"objects-dir\" }\n\t/** The `objects/pack/` listing. */\n\t| { readonly kind: \"pack-dir\" }\n\t| { readonly kind: \"pack-file\"; readonly file: PackFileName }\n\t| { readonly kind: \"loose-object\"; readonly oid: string }\n\t| { readonly kind: \"other\" };\n\nconst HEX = new Set(\"0123456789abcdef\");\n\nfunction isHex(value: string, length: number): boolean {\n\tif (value.length !== length) return false;\n\tfor (const char of value) if (!HEX.has(char)) return false;\n\treturn true;\n}\n\n/** Classify a path relative to a gitdir (no leading slash). */\nexport function classifyGitdirPath(path: string): GitdirEntry {\n\tconst segments = path.split(\"/\");\n\tconst [first, second, third, ...rest] = segments;\n\tif (segments.length === 1) {\n\t\tif (first === \"HEAD\") return { kind: \"head\" };\n\t\tif (first === \"config\") return { kind: \"config\" };\n\t\tif (first === \"refs\") return { kind: \"refs\", path };\n\t\treturn { kind: \"other\" };\n\t}\n\tif (first === \"refs\") return { kind: \"refs\", path };\n\tif (first !== \"objects\" || second === undefined) return { kind: \"other\" };\n\tif (segments.length === 2 && second === \"\") return { kind: \"objects-dir\" };\n\tif (second === \"pack\" && third !== undefined && rest.length === 0) {\n\t\tif (third === \"\") return { kind: \"pack-dir\" };\n\t\tconst file = parsePackFileName(third);\n\t\treturn file ? { kind: \"pack-file\", file } : { kind: \"other\" };\n\t}\n\tif (\n\t\tthird !== undefined &&\n\t\trest.length === 0 &&\n\t\tisHex(second, 2) &&\n\t\tisHex(third, 38)\n\t) {\n\t\treturn { kind: \"loose-object\", oid: second + third };\n\t}\n\treturn { kind: \"other\" };\n}\n\n/**\n * Whether reads of this entry can change between pushes: refs, `HEAD`, and\n * the object/pack listings. Everything else is content-addressed (objects)\n * or written once (config, a given pack file name).\n */\nexport function isMutableGitdirEntry(entry: GitdirEntry): boolean {\n\tswitch (entry.kind) {\n\t\tcase \"head\":\n\t\tcase \"refs\":\n\t\tcase \"objects-dir\":\n\t\tcase \"pack-dir\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * A loose-object path anywhere in the fs, split into its gitdir and oid —\n * for callers that don't know where the gitdir boundary is: a loose object\n * is always `<gitdir>/objects/<2 hex>/<38 hex>`.\n */\nexport function parseLooseObjectPath(\n\tpath: string,\n): { readonly gitdir: string; readonly oid: string } | null {\n\tconst segments = path.split(\"/\");\n\tif (segments.length < 3) return null;\n\tconst gitdirSegments = segments.slice(0, -3);\n\tconst entry = classifyGitdirPath(segments.slice(-3).join(\"/\"));\n\tif (entry.kind !== \"loose-object\") return null;\n\treturn { gitdir: gitdirSegments.join(\"/\"), oid: entry.oid };\n}\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 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\nfunction defaultIsRetryable(error: unknown): boolean {\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 ?? defaultIsRetryable;\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\n\tasync function guarded<T>(fn: () => Promise<T>): Promise<T> {\n\t\tif (breaker === null) return fn();\n\t\tif (state === \"open\") {\n\t\t\tif (Date.now() - lastFailureAt < breaker.resetMs) {\n\t\t\t\tthrow new CircuitOpenError();\n\t\t\t}\n\t\t\tstate = \"half-open\";\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fn();\n\t\t\tif (state === \"half-open\") {\n\t\t\t\tstate = \"closed\";\n\t\t\t\tfailures = 0;\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tfailures++;\n\t\t\tlastFailureAt = Date.now();\n\t\t\tif (failures >= breaker.threshold) state = \"open\";\n\t\t\tthrow error;\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\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};\n}\n","import type {\n\tListOptions,\n\tListResult,\n\tObjectStat,\n\tObjectStore,\n} from \"../types.js\";\n\n/**\n * In-memory {@link ObjectStore}. Useful for tests, examples, and ephemeral\n * repositories; also the reference implementation for the list/delimiter\n * semantics other stores must match.\n */\nexport class MemoryObjectStore implements ObjectStore {\n\tprivate readonly objects = new Map<string, Uint8Array>();\n\n\tasync get(key: string): Promise<Uint8Array | null> {\n\t\tconst data = this.objects.get(key);\n\t\treturn data ? data.slice() : null;\n\t}\n\n\tasync put(key: string, data: Uint8Array): Promise<void> {\n\t\tthis.objects.set(key, data.slice());\n\t}\n\n\tasync delete(key: string): Promise<void> {\n\t\tthis.objects.delete(key);\n\t}\n\n\tasync head(key: string): Promise<ObjectStat | null> {\n\t\tconst data = this.objects.get(key);\n\t\treturn data ? { size: data.byteLength } : null;\n\t}\n\n\tasync list(prefix: string, options?: ListOptions): Promise<ListResult> {\n\t\tconst delimiter = options?.delimiter;\n\t\tconst limit = options?.limit ?? Number.POSITIVE_INFINITY;\n\t\tconst objects: ListResult[\"objects\"] = [];\n\t\tconst prefixes = new Set<string>();\n\n\t\tfor (const [key, data] of this.objects) {\n\t\t\tif (!key.startsWith(prefix)) continue;\n\t\t\tconst rest = key.slice(prefix.length);\n\t\t\tif (delimiter !== undefined) {\n\t\t\t\tconst idx = rest.indexOf(delimiter);\n\t\t\t\tif (idx !== -1) {\n\t\t\t\t\tprefixes.add(prefix + rest.slice(0, idx + delimiter.length));\n\t\t\t\t} else {\n\t\t\t\t\tobjects.push({ key, size: data.byteLength });\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tobjects.push({ key, size: data.byteLength });\n\t\t\t}\n\t\t\tif (objects.length + prefixes.size >= limit) break;\n\t\t}\n\n\t\treturn { objects, prefixes: [...prefixes] };\n\t}\n\n\t/** Number of stored objects (test convenience, not part of ObjectStore). */\n\tget size(): number {\n\t\treturn this.objects.size;\n\t}\n}\n"],"mappings":"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","names":["LRUCache","toKey","LRUCache"]}
package/dist/ops.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  deleteBranchByName,
6
6
  listBranches,
7
7
  resolveLooseRefFast
8
- } from "./chunk-Z4656PGE.js";
8
+ } from "./chunk-2JP4MS6H.js";
9
9
  import {
10
10
  GitObjectNotFoundError,
11
11
  GitPathNotFoundError,
@@ -14,7 +14,7 @@ import {
14
14
  qualifyBranchRef,
15
15
  readBlobContent,
16
16
  toBase64
17
- } from "./chunk-YQFNY6PG.js";
17
+ } from "./chunk-FIFWOMPM.js";
18
18
 
19
19
  // src/ops/commit.ts
20
20
  import git2 from "isomorphic-git";
@@ -0,0 +1,81 @@
1
+ import type { ObjectStore } from "./types.js";
2
+ export interface CacheOptions {
3
+ /** Maximum bytes of object data held in memory. Default 50 MiB. */
4
+ maxBytes?: number;
5
+ /**
6
+ * Largest single entry admitted to the cache. Defaults to a tenth of
7
+ * `maxBytes` so one huge packfile cannot evict the whole working set.
8
+ */
9
+ maxEntryBytes?: number;
10
+ /** Entry time-to-live in milliseconds. Default 60 000. */
11
+ ttlMs?: number;
12
+ /**
13
+ * Override the TTL for a specific key (get/head) or list prefix (list),
14
+ * in milliseconds. Return `undefined` to fall back to `ttlMs`. Git refs
15
+ * (`refs/heads/<branch>`, `HEAD`) are mutable — the same key's value
16
+ * changes on every push — unlike content-addressed object keys, which
17
+ * never change for a given key and are safe to cache for the full
18
+ * `ttlMs`. Without this, a long `ttlMs` tuned for objects also caches
19
+ * ref reads that long, so a warm process can keep serving a
20
+ * pre-push ref value for the rest of that TTL even though nothing
21
+ * changed *this* process's own cache (see `invalidate`) — it just never
22
+ * knew to. Give ref-like keys a short override (a few seconds) instead:
23
+ * a ref read is one small object, so re-reading it far more often than
24
+ * `ttlMs` is cheap, and every read downstream of a fresh ref (tree,
25
+ * commit, blob — all keyed by the sha it resolves to) still gets the
26
+ * full-length cache/coalescing benefit.
27
+ */
28
+ ttlForKey?: (key: string) => number | undefined;
29
+ /**
30
+ * Also cache "key does not exist" results. Loose-object probes on packed
31
+ * repositories are almost always misses, so this saves many round trips —
32
+ * but only enable it when a single process is the only writer, otherwise
33
+ * another instance's push can be masked for up to `ttlMs`.
34
+ */
35
+ cacheMisses?: boolean;
36
+ /**
37
+ * Also cache `list()` results (directory listings and `limit: 1`
38
+ * existence probes). Writes through this store keep cached listings
39
+ * consistent; after writing to the backend by any other means, call
40
+ * `invalidate()` with the affected prefix. Default false.
41
+ */
42
+ cacheLists?: boolean;
43
+ /**
44
+ * Collapse concurrent `get`/`head`/`list` calls for the same key into a
45
+ * single backend request. Default true.
46
+ */
47
+ coalesce?: boolean;
48
+ /**
49
+ * Return defensive copies from `get` (and copy on admit), so a caller
50
+ * mutating returned bytes can't corrupt the cache. Default true. Set
51
+ * false when no reader mutates results (isomorphic-git doesn't): every
52
+ * hit otherwise copies the whole entry, which for packfiles is megabytes
53
+ * per read.
54
+ */
55
+ copy?: boolean;
56
+ /** Called when a read is answered from cache. */
57
+ onHit?: (key: string) => void;
58
+ /** Called when a read has to go to the backing store. */
59
+ onMiss?: (key: string) => void;
60
+ }
61
+ /** An {@link ObjectStore} wrapper that also supports explicit invalidation. */
62
+ export interface CachedObjectStore extends ObjectStore {
63
+ /**
64
+ * Drop every cached entry — contents, misses, and listings — whose key
65
+ * falls under `prefix` (exact keys included). Call this after the backing
66
+ * store was modified by something other than this wrapper.
67
+ */
68
+ invalidate(prefix: string): void;
69
+ }
70
+ /**
71
+ * Wrap an {@link ObjectStore} with an in-process LRU read cache.
72
+ *
73
+ * Git object keys are content-addressed and therefore immutable, which makes
74
+ * them ideal cache entries; mutable keys (refs, packed-refs) are bounded by
75
+ * `ttlMs`. Writes and deletes through this wrapper invalidate their key and
76
+ * any cached listings they affect — with one asymmetry: a non-empty `limit: 1`
77
+ * probe (a "directory exists" answer) survives writes underneath it, because
78
+ * adding a key below a prefix cannot make that prefix stop existing, while
79
+ * empty probes and full listings are always dropped.
80
+ */
81
+ export declare function createCachedStore(store: ObjectStore, options?: CacheOptions): CachedObjectStore;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Edge-compatible utilities replacing node:crypto, node:zlib, and Buffer.
3
+ *
4
+ * Every function here uses only Web APIs (SubtleCrypto, CompressionStream,
5
+ * TextEncoder/TextDecoder) — no Node built-ins. They work on Cloudflare
6
+ * Workers, Vercel Edge, Deno Deploy, and Node >= 18.
7
+ */
8
+ /**
9
+ * Encode a UTF-8 string to bytes.
10
+ *
11
+ * Return type pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,
12
+ * whose default type argument differs across TypeScript versions) so it's
13
+ * always assignable to Fetch API `BodyInit` regardless of a consumer's own
14
+ * TypeScript/lib version.
15
+ */
16
+ export declare function encodeUtf8(data: string): Uint8Array<ArrayBuffer>;
17
+ /** Decode bytes as UTF-8. */
18
+ export declare function decodeUtf8(data: Uint8Array): string;
19
+ /** Decode bytes as ASCII. */
20
+ export declare function decodeAscii(data: Uint8Array): string;
21
+ /** Concatenate any number of Uint8Arrays into one. */
22
+ export declare function concat(...parts: Uint8Array[]): Uint8Array<ArrayBuffer>;
23
+ /** Uint8Array → lowercase hex string. */
24
+ export declare function toHex(data: Uint8Array): string;
25
+ /** Uint8Array → base64 string. */
26
+ export declare function toBase64(data: Uint8Array): string;
27
+ /** Hex string → Uint8Array. */
28
+ export declare function fromHex(hex: string): Uint8Array<ArrayBuffer>;
29
+ /** SHA-1 hash via Web Crypto API. Returns a hex string. */
30
+ export declare function sha1(data: Uint8Array<ArrayBuffer> | string): Promise<string>;
31
+ /** Deflate (zlib) via the CompressionStream Web API. */
32
+ export declare function deflate(data: Uint8Array<ArrayBuffer>): Promise<Uint8Array<ArrayBuffer>>;
33
+ /** Check if a Uint8Array contains a null byte. */
34
+ export declare function hasNullByte(data: Uint8Array): boolean;
35
+ /**
36
+ * Read a blob as text or binary metadata — the edge-compatible replacement
37
+ * for the `Buffer.from(blob)` pattern used throughout diff.ts and history.ts.
38
+ */
39
+ export declare function readBlobContent(blob: Uint8Array): {
40
+ isBinary: boolean;
41
+ text: string;
42
+ bytes: Uint8Array;
43
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Node-style filesystem error carrying a `code` property, which is what
3
+ * isomorphic-git inspects to distinguish "file not found" from real failures.
4
+ */
5
+ export declare class FsError extends Error {
6
+ readonly code: string;
7
+ readonly syscall: string;
8
+ readonly path: string;
9
+ constructor(code: string, syscall: string, path: string);
10
+ }
11
+ export declare const enoent: (syscall: string, path: string) => FsError;
12
+ export declare const enotdir: (syscall: string, path: string) => FsError;
13
+ export declare const eisdir: (syscall: string, path: string) => FsError;
14
+ export declare const enotempty: (syscall: string, path: string) => FsError;
15
+ export declare const einval: (syscall: string, path: string) => FsError;
16
+ export declare const eperm: (syscall: string, path: string) => FsError;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Git-server error types carrying an HTTP status and a retryability flag, so
3
+ * transport layers can map internal failures to responses without inspecting
4
+ * messages. Extend {@link GitError} for app-specific cases (storage backends,
5
+ * quota, …) and {@link formatErrorResponse} keeps working for them.
6
+ */
7
+ export declare class GitError extends Error {
8
+ statusCode: number;
9
+ retryable: boolean;
10
+ constructor(message: string, statusCode?: number, retryable?: boolean);
11
+ toJSON(): Record<string, unknown>;
12
+ }
13
+ /** A file/directory path not found within a tree (404). */
14
+ export declare class GitPathNotFoundError extends GitError {
15
+ constructor(message: string);
16
+ }
17
+ /** A git object not found (404). */
18
+ export declare class GitObjectNotFoundError extends GitError {
19
+ constructor(message: string);
20
+ }
21
+ /** A ref (branch/tag) not found (404). */
22
+ export declare class GitRefNotFoundError extends GitError {
23
+ constructor(message: string);
24
+ }
25
+ /** The repository itself not found (404). */
26
+ export declare class GitRepositoryNotFoundError extends GitError {
27
+ constructor(message: string);
28
+ }
29
+ export interface MergeConflictDetail {
30
+ file: string;
31
+ baseLines?: string[];
32
+ sourceLines?: string[];
33
+ targetLines?: string[];
34
+ }
35
+ /** A merge conflict (409), carrying per-file conflict detail. */
36
+ export declare class GitConflictError extends GitError {
37
+ conflicts: MergeConflictDetail[];
38
+ constructor(message: string, conflicts?: MergeConflictDetail[]);
39
+ toJSON(): Record<string, unknown>;
40
+ }
41
+ /** Authentication failed (401). */
42
+ export declare class GitAuthenticationError extends GitError {
43
+ constructor(message: string);
44
+ }
45
+ /** Authorization failed (403). */
46
+ export declare class GitAuthorizationError extends GitError {
47
+ constructor(message: string);
48
+ }
49
+ /** Too many failed attempts (429). */
50
+ export declare class GitRateLimitError extends GitError {
51
+ constructor(message: string);
52
+ }
53
+ /** Malformed request (400). */
54
+ export declare class GitInvalidRequestError extends GitError {
55
+ constructor(message: string);
56
+ }
57
+ /** Git wire-protocol violation (400). */
58
+ export declare class GitProtocolError extends GitError {
59
+ constructor(message: string);
60
+ }
61
+ /**
62
+ * Map any error to an HTTP response shape. 401s carry the WWW-Authenticate
63
+ * header git clients need before they will prompt for credentials. Non-GitError
64
+ * failures are masked as opaque 500s — internal messages don't leak.
65
+ */
66
+ export declare function formatErrorResponse(error: unknown): {
67
+ status: number;
68
+ body: Record<string, unknown>;
69
+ headers?: Record<string, string>;
70
+ };
@@ -0,0 +1,60 @@
1
+ import type { GitdirFacts, GitFsClient, GitFsOptions, ObjectStore } from "./types.js";
2
+ /**
3
+ * The filesystem returned by {@link createGitFs}: the isomorphic-git client
4
+ * plus git-aware maintenance hooks.
5
+ */
6
+ export interface GitFs extends GitFsClient {
7
+ /**
8
+ * Return every file below a directory in one object-store listing. Unlike
9
+ * `readdir`, this deliberately does not use a delimiter: object storage can
10
+ * enumerate a ref namespace recursively without the stat-per-entry walk a
11
+ * POSIX filesystem needs to distinguish files from implicit directories.
12
+ * Paths are relative to `dirpath` and never include directory entries.
13
+ */
14
+ listFilesRecursively(dirpath: string): Promise<string[]>;
15
+ /**
16
+ * Probe, with one bounded list, whether `gitdir` contains any loose
17
+ * objects, and remember the answer. This is the only way a loose-object
18
+ * hint is ever created; call it before full-history walks (commit logs,
19
+ * reachability traversals) so fully packed repositories skip every
20
+ * guaranteed-miss loose-object read. A later loose write flips the hint
21
+ * back, so it cannot go stale mid-push.
22
+ */
23
+ detectLooseObjects(gitdir: string): Promise<void>;
24
+ /**
25
+ * Warm the cache with every pack file under `gitdir` in parallel (plus
26
+ * the loose-object hint) before a sequential history walk. Skipped when
27
+ * the pack directory holds more than `maxPacks * 2` entries — warming
28
+ * only helps when the cache budget actually fits the packs.
29
+ */
30
+ prefetchPacks(gitdir: string, options?: {
31
+ maxPacks?: number;
32
+ }): Promise<void>;
33
+ /**
34
+ * Clear fs-level state (loose-object hints) under `pathPrefix`, and
35
+ * forward to the store's `invalidate` when it has one. Call after the
36
+ * backing store was modified by something other than this fs.
37
+ */
38
+ invalidate(pathPrefix: string): void;
39
+ /**
40
+ * Declare that `gitdir` exists, so `stat` answers "directory" for it with
41
+ * zero store calls. isomorphic-git stats the gitdir on every command
42
+ * (`discoverGitdir`); in object storage that is a HEAD that always misses
43
+ * plus a listing. Register once the caller has confirmed the repository
44
+ * exists by other means (e.g. its database row); `invalidate` forgets it.
45
+ */
46
+ registerGitdir(gitdir: string, facts?: GitdirFacts): void;
47
+ }
48
+ /**
49
+ * Create a promise-based filesystem client for isomorphic-git backed by an
50
+ * {@link ObjectStore}.
51
+ *
52
+ * Semantics:
53
+ * - Directories are implicit, as in object storage: `mkdir` is a no-op and a
54
+ * directory "exists" whenever at least one key lives under its prefix.
55
+ * - Symbolic links are not supported (`readlink`/`symlink` throw). Bare
56
+ * repositories never contain them.
57
+ * - Designed for bare, server-side repositories (`git.init({bare: true})`,
58
+ * plumbing commands, ref updates). Worktree checkouts belong on a real disk.
59
+ */
60
+ export declare function createGitFs(store: ObjectStore, options?: GitFsOptions): GitFs;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Typed classification of paths inside a git directory, so callers can
3
+ * reason about "a ref", "a loose object" or "the pack listing" as values
4
+ * instead of matching path strings against patterns.
5
+ *
6
+ * Edge-compatible: no node: imports.
7
+ */
8
+ import { type PackFileName } from "./pack-files.js";
9
+ /** What a gitdir-relative path refers to. Listing prefixes end in `/`. */
10
+ export type GitdirEntry = {
11
+ readonly kind: "head";
12
+ } | {
13
+ readonly kind: "config";
14
+ }
15
+ /** A ref file (`refs/heads/main`) or a refs listing (`refs/heads/`). */
16
+ | {
17
+ readonly kind: "refs";
18
+ readonly path: string;
19
+ }
20
+ /** The `objects/` listing. */
21
+ | {
22
+ readonly kind: "objects-dir";
23
+ }
24
+ /** The `objects/pack/` listing. */
25
+ | {
26
+ readonly kind: "pack-dir";
27
+ } | {
28
+ readonly kind: "pack-file";
29
+ readonly file: PackFileName;
30
+ } | {
31
+ readonly kind: "loose-object";
32
+ readonly oid: string;
33
+ } | {
34
+ readonly kind: "other";
35
+ };
36
+ /** Classify a path relative to a gitdir (no leading slash). */
37
+ export declare function classifyGitdirPath(path: string): GitdirEntry;
38
+ /**
39
+ * Whether reads of this entry can change between pushes: refs, `HEAD`, and
40
+ * the object/pack listings. Everything else is content-addressed (objects)
41
+ * or written once (config, a given pack file name).
42
+ */
43
+ export declare function isMutableGitdirEntry(entry: GitdirEntry): boolean;
44
+ /**
45
+ * A loose-object path anywhere in the fs, split into its gitdir and oid —
46
+ * for callers that don't know where the gitdir boundary is: a loose object
47
+ * is always `<gitdir>/objects/<2 hex>/<38 hex>`.
48
+ */
49
+ export declare function parseLooseObjectPath(path: string): {
50
+ readonly gitdir: string;
51
+ readonly oid: string;
52
+ } | null;
@@ -0,0 +1,7 @@
1
+ export { type GitService, handleInfoRefs, type InfoRefsOptions, listAllRefs, } from "./info-refs.js";
2
+ export { FLUSH, parsePktLines, pktLine, pktLineBuffer, sideBandPackfile, } from "./pkt-line.js";
3
+ export { collectReachableOids, type ReachabilityResult, } from "./reachability.js";
4
+ export { type ApplyReceivePackOptions, applyReceivePack, applyRefUpdates, ensureRepoInitialized, indexIncomingPack, parseReceivePackBody, type RefUpdateCommand, type RefUpdateResult, receivePackResponse, } from "./receive-pack.js";
5
+ export { REPACK_PACK_COUNT_THRESHOLD, type RepackOptions, repackRepository, } from "./repack.js";
6
+ export { type GitHttpResult, type HttpHooks, type RawFsPromises, rawFs, } from "./types.js";
7
+ export { handleUploadPack, type UploadPackOptions } from "./upload-pack.js";
@@ -0,0 +1,27 @@
1
+ import type { Repo } from "../ops/types.js";
2
+ import { type GitHttpResult, type HttpHooks } from "./types.js";
3
+ export type GitService = "git-upload-pack" | "git-receive-pack";
4
+ /** All refs with resolved oids, plus the symref HEAD should advertise. */
5
+ export declare function listAllRefs(repo: Repo, defaultBranch?: string): Promise<{
6
+ refs: {
7
+ name: string;
8
+ oid: string;
9
+ }[];
10
+ headSymref: string;
11
+ }>;
12
+ export interface InfoRefsOptions {
13
+ service: GitService;
14
+ defaultBranch?: string;
15
+ /** Advertised in the agent capability. Default "git-fs-s3". */
16
+ agent?: string;
17
+ }
18
+ /**
19
+ * `GET …/info/refs?service=…` — the ref advertisement. Authentication and
20
+ * authorization are the caller's job before invoking this.
21
+ *
22
+ * Capabilities advertised match what the sibling handlers implement:
23
+ * side-band-64k (honored by handleUploadPack's response framing — clients
24
+ * like isomorphic-git unconditionally expect it), tip/reachable sha1 wants,
25
+ * delete-refs and report-status for pushes.
26
+ */
27
+ export declare function handleInfoRefs(repo: Repo, options: InfoRefsOptions, hooks?: HttpHooks): Promise<GitHttpResult>;