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
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Build a packfile by copying already-compressed entries out of existing
3
+ * packs ("pack reuse"), instead of inflating and re-deflating every object.
4
+ *
5
+ * Entries keep their stored form, deltas included, so the output is as
6
+ * compact as the packs it came from. `OFS_DELTA` entries are rewritten as
7
+ * `REF_DELTA` (base named by oid), since offsets don't survive the move into
8
+ * a new pack; delta bases are always included and written before the deltas
9
+ * that use them. Formats: gitformat-pack(5).
10
+ *
11
+ * Edge-compatible: Web Crypto SHA-1 and Uint8Array only.
12
+ */
13
+ import type { Repo } from "../ops/types.js";
14
+ /** An object's position in a pack, as listed by its `.idx`. */
15
+ export interface PackIndexEntry {
16
+ readonly oid: string;
17
+ readonly offset: number;
18
+ }
19
+ /** Entries of a version-2 `.idx`; `null` for any other format. */
20
+ export declare function readPackIndex(idx: Uint8Array): PackIndexEntry[] | null;
21
+ /**
22
+ * A pack holding at least `oids`, built from stored entries, or `null` when
23
+ * any of them (or a delta base they need) isn't in a readable pack — e.g. a
24
+ * loose object — so the caller should build the pack another way.
25
+ */
26
+ export declare function buildPackFromExistingPacks(repo: Repo, oids: readonly string[]): Promise<Uint8Array | null>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Git pkt-line framing (smart HTTP protocol wire format).
3
+ *
4
+ * Edge-compatible: uses Uint8Array instead of Buffer.
5
+ */
6
+ /** Flush packet: exactly four zero bytes. */
7
+ export declare const FLUSH: Uint8Array<ArrayBuffer>;
8
+ /** Frame a UTF-8 string as one pkt-line. */
9
+ export declare function pktLine(data: string): Uint8Array<ArrayBuffer>;
10
+ /** Frame raw bytes as one pkt-line. */
11
+ export declare function pktLineBuffer(body: Uint8Array): Uint8Array<ArrayBuffer>;
12
+ /** Decode pkt-lines to strings; `null` marks a flush-pkt. Stops on garbage. */
13
+ export declare function parsePktLines(buf: Uint8Array): Array<string | null>;
14
+ export declare function sideBandPackfile(packData: Uint8Array): Uint8Array<ArrayBuffer>;
@@ -0,0 +1,36 @@
1
+ import type { Repo } from "../ops/types.js";
2
+ import type { HttpHooks } from "./types.js";
3
+ export interface ReachabilityResult {
4
+ oids: string[];
5
+ /**
6
+ * False if any object in the graph couldn't be read — repack uses this
7
+ * (not a raw object-count comparison) to decide whether it's safe to
8
+ * delete old packs: counts alone can't distinguish "everything read fine"
9
+ * from "some objects silently failed".
10
+ */
11
+ complete: boolean;
12
+ }
13
+ /**
14
+ * Walk the object graph from `startOids`, returning every reachable oid.
15
+ * Concurrent traversal paths are deduplicated promise-per-oid; a missing
16
+ * object is retried once, then reported through `hooks.onWarn` and
17
+ * reflected in `complete: false`.
18
+ *
19
+ * `haveOids` bounds the walk: any oid in this set is assumed already
20
+ * verified (it was reachable from a ref that existed before this call) and
21
+ * is recorded in `seen` without being read or descended into. Without a
22
+ * boundary, every call re-walks the *entire* history from scratch — for
23
+ * receive-pack validation (see receive-pack.ts) that means every push pays
24
+ * for reading every object in the repo, not just the ones it introduced,
25
+ * and one transient read failure anywhere in that whole history rejects an
26
+ * otherwise-valid push.
27
+ *
28
+ * Commits, trees and tags are read once each (parsed). Blobs have no
29
+ * children, so `verifyBlobs: false` records a blob from its tree entry
30
+ * without reading (and inflating) it — for callers that read every blob
31
+ * themselves afterwards anyway, like upload-pack's `packObjects`. Leave it
32
+ * true wherever the walk *is* the existence check (receive-pack, repack).
33
+ */
34
+ export declare function collectReachableOids(repo: Repo, startOids: string[], hooks?: HttpHooks, haveOids?: ReadonlySet<string> | readonly string[], options?: {
35
+ verifyBlobs?: boolean;
36
+ }): Promise<ReachabilityResult>;
@@ -0,0 +1,54 @@
1
+ import type { Repo } from "../ops/types.js";
2
+ import { type RepackOptions } from "./repack.js";
3
+ import { type GitHttpResult, type HttpHooks } from "./types.js";
4
+ export interface RefUpdateCommand {
5
+ oldOid: string;
6
+ newOid: string;
7
+ refName: string;
8
+ }
9
+ export interface RefUpdateResult {
10
+ refName: string;
11
+ ok: boolean;
12
+ reason?: string;
13
+ }
14
+ /** Split a receive-pack body: pkt-line ref commands, flush, then raw PACK. */
15
+ export declare function parseReceivePackBody(body: Uint8Array): {
16
+ refUpdates: RefUpdateCommand[];
17
+ packData: Uint8Array;
18
+ };
19
+ /** Initialize the repo when its HEAD doesn't exist yet (first push). */
20
+ export declare function ensureRepoInitialized(repo: Repo, defaultBranch?: string): Promise<void>;
21
+ /** Write the incoming PACK into objects/pack/ and index it. */
22
+ export declare function indexIncomingPack(repo: Repo, packData: Uint8Array, hooks?: HttpHooks): Promise<string[]>;
23
+ /**
24
+ * Apply ref updates, enforcing compare-and-swap against each command's
25
+ * claimed oldOid. Every client-supplied refName is validated before any
26
+ * filesystem call reads or writes through it.
27
+ */
28
+ export declare function applyRefUpdates(repo: Repo, refUpdates: RefUpdateCommand[], hooks?: HttpHooks): Promise<RefUpdateResult[]>;
29
+ /** The report-status response body for a set of ref-update results. */
30
+ export declare function receivePackResponse(results: RefUpdateResult[]): GitHttpResult;
31
+ export interface ApplyReceivePackOptions {
32
+ defaultBranch?: string;
33
+ /**
34
+ * Repack tuning, or `false` to skip repacking entirely. The returned
35
+ * `stalePackPaths` are gitdir-relative pack files the repack deleted from
36
+ * this repo — after syncing to any secondary storage, delete them there
37
+ * too.
38
+ */
39
+ repack?: RepackOptions | false;
40
+ }
41
+ /**
42
+ * The storage half of a push against `repo`: initialize if needed, index the
43
+ * incoming pack, CAS-apply ref updates, then repack when enough packs have
44
+ * accumulated. Serialize concurrent pushes to the same repo externally (a
45
+ * per-repo lock); build the HTTP response afterwards with
46
+ * {@link receivePackResponse}.
47
+ */
48
+ export declare function applyReceivePack(repo: Repo, parsed: {
49
+ refUpdates: RefUpdateCommand[];
50
+ packData: Uint8Array;
51
+ }, options?: ApplyReceivePackOptions, hooks?: HttpHooks): Promise<{
52
+ results: RefUpdateResult[];
53
+ stalePackPaths: string[];
54
+ }>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Pack consolidation: merge all packs into one, reusing stored (possibly
3
+ * deltified) entries when every object is packed.
4
+ *
5
+ * Edge-compatible: uses Web Crypto (SHA-1), CompressionStream (deflate),
6
+ * and Uint8Array throughout — no node:crypto, node:zlib, or Buffer.
7
+ */
8
+ import type { Repo } from "../ops/types.js";
9
+ import { type HttpHooks } from "./types.js";
10
+ /**
11
+ * Default repack threshold. Consolidating is O(total repo object count) —
12
+ * so paying it on every push makes push latency grow with repo size forever.
13
+ */
14
+ export declare const REPACK_PACK_COUNT_THRESHOLD = 4;
15
+ export interface RepackOptions {
16
+ /** Skip repacking below this many accumulated packs. Default 4. */
17
+ threshold?: number;
18
+ }
19
+ /**
20
+ * Consolidate all pack files into one, returning the gitdir-relative paths
21
+ * of the old .pack/.idx files it removed — after syncing the new pack to any
22
+ * secondary storage, delete those same paths there too.
23
+ */
24
+ export declare function repackRepository(repo: Repo, options?: RepackOptions, hooks?: HttpHooks): Promise<string[]>;
@@ -0,0 +1,40 @@
1
+ import type { Repo } from "../ops/types.js";
2
+ /**
3
+ * Transport-agnostic HTTP response the handlers produce.
4
+ *
5
+ * `body` is pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,
6
+ * whose default type argument isn't consistent across TypeScript versions)
7
+ * so it's always assignable to Fetch API `BodyInit` regardless of which
8
+ * TypeScript/lib version a consumer compiles against — every producer here
9
+ * (`concat`, `pktLine`, `TextEncoder.encode`) is ArrayBuffer-backed already.
10
+ */
11
+ export interface GitHttpResult {
12
+ status: number;
13
+ headers: Record<string, string>;
14
+ body: Uint8Array<ArrayBuffer>;
15
+ }
16
+ /** Optional instrumentation hooks shared by the /http handlers. */
17
+ export interface HttpHooks {
18
+ /** Wrap a timed sub-step. Default: run directly. */
19
+ step?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;
20
+ /** Non-fatal problem sink (missing objects, failed repacks). */
21
+ onWarn?: (message: string, error?: unknown) => void;
22
+ }
23
+ export declare const runStep: <T>(hooks: HttpHooks | undefined, label: string, fn: () => Promise<T>) => Promise<T>;
24
+ /**
25
+ * The raw promise-fs surface some /http paths need beyond isomorphic-git's
26
+ * plumbing (writing an incoming pack file, listing/deleting pack files).
27
+ * Both `node:fs` and this package's `GitFs` satisfy it via `.promises`.
28
+ */
29
+ export interface RawFsPromises {
30
+ readFile(path: string): Promise<Uint8Array | string>;
31
+ writeFile(path: string, data: Uint8Array | string): Promise<void>;
32
+ unlink(path: string): Promise<void>;
33
+ readdir(path: string): Promise<string[]>;
34
+ mkdir(path: string, options?: {
35
+ recursive?: boolean;
36
+ }): Promise<unknown>;
37
+ stat(path: string): Promise<unknown>;
38
+ }
39
+ /** Duck-type the `.promises` surface off a repo's fs. */
40
+ export declare function rawFs(repo: Repo): RawFsPromises;
@@ -0,0 +1,21 @@
1
+ import type { Repo } from "../ops/types.js";
2
+ import { type GitHttpResult, type HttpHooks } from "./types.js";
3
+ export interface UploadPackOptions {
4
+ /**
5
+ * Called once before the reachability walk — wire loose-object detection
6
+ * (`GitFs.detectLooseObjects`) here so a fully packed repo's walk doesn't
7
+ * pay a doomed loose-object probe per object.
8
+ */
9
+ beforeWalk?: () => Promise<void>;
10
+ }
11
+ /**
12
+ * `POST …/git-upload-pack` — serve a clone/fetch. Authorization is the
13
+ * caller's job. Negotiation is client-driven (no multi_ack advertised):
14
+ * "have" batches without "done" get a bare NAK; the final batch gets the
15
+ * packfile, side-band-64k framed.
16
+ *
17
+ * Fast path: a fresh clone (no haves) of a repo consolidated down to a
18
+ * single pack serves that pack's bytes directly — skipping the O(objects)
19
+ * traversal + repack entirely.
20
+ */
21
+ export declare function handleUploadPack(repo: Repo, body: Uint8Array, options?: UploadPackOptions, hooks?: HttpHooks): Promise<GitHttpResult>;
@@ -0,0 +1,11 @@
1
+ export { type CachedObjectStore, type CacheOptions, createCachedStore, } from "./cache.js";
2
+ export { concat, decodeAscii, decodeUtf8, deflate, encodeUtf8, fromHex, hasNullByte, readBlobContent, sha1, toBase64, toHex, } from "./edge-utils.js";
3
+ export { FsError } from "./errors.js";
4
+ export { formatErrorResponse, GitAuthenticationError, GitAuthorizationError, GitConflictError, GitError, GitInvalidRequestError, GitObjectNotFoundError, GitPathNotFoundError, GitProtocolError, GitRateLimitError, GitRefNotFoundError, GitRepositoryNotFoundError, type MergeConflictDetail, } from "./git-errors.js";
5
+ export { createGitFs, type GitFs } from "./git-fs.js";
6
+ export { classifyGitdirPath, type GitdirEntry, isMutableGitdirEntry, parseLooseObjectPath, } from "./git-layout.js";
7
+ export { PACK_FILE_KINDS, type PackFileKind, type PackFileName, packFileName, parsePackDirectory, parsePackFileName, READABLE_PACK_FILE_KINDS, } from "./pack-files.js";
8
+ export { isFullSha, isSafeBranchName, isSafeFullRefName, isSafeRefName, isSafeRepoPath, qualifyBranchRef, } from "./refs.js";
9
+ export { CircuitOpenError, createRetryStore, type RetryOptions, } from "./retry.js";
10
+ export { MemoryObjectStore } from "./stores/memory.js";
11
+ export type { GitdirFacts, GitFsClient, GitFsOptions, ListOptions, ListResult, ObjectStat, ObjectStore, Stat, } from "./types.js";
@@ -0,0 +1,38 @@
1
+ import type { Repo } from "./types.js";
2
+ export interface Branch {
3
+ name: string;
4
+ commit: string;
5
+ isDefault: boolean;
6
+ }
7
+ /**
8
+ * Defense in depth for every branch-name argument below: `git.deleteBranch`
9
+ * and raw resolveRef/writeRef reads don't validate ref names internally the
10
+ * way `git.branch` does (see refs.ts) — guard at the point the primitives are
11
+ * actually called, not just at an API boundary far above.
12
+ */
13
+ export declare function assertSafeBranchName(name: string): void;
14
+ /**
15
+ * Loose ref names under `dir` (e.g. `refs/tags`) in one recursive listing,
16
+ * or `null` when the fs can't list recursively (use isomorphic-git's walk).
17
+ */
18
+ export declare function listLooseRefNames(repo: Repo, dir: "refs/heads" | "refs/tags"): Promise<string[] | null>;
19
+ /**
20
+ * Resolve a fully-qualified ref's oid with one read instead of isomorphic-
21
+ * git's own `resolveRef`, which does a stat *then* a read for a loose ref
22
+ * (two round trips against object storage) — wasteful specifically here,
23
+ * where the caller already knows the ref exists (it came from a directory
24
+ * listing moments earlier). Reads the loose file directly and falls back to
25
+ * `git.resolveRef` only when that doesn't pan out (packed-refs, or anything
26
+ * else the plain-loose-file assumption doesn't cover), so correctness for
27
+ * those cases is unchanged — this only removes the redundant round trip on
28
+ * the common path.
29
+ */
30
+ export declare function resolveLooseRefFast(repo: Repo, ref: string): Promise<string>;
31
+ /** All branches with their tip commits; [] for an empty repository. */
32
+ export declare function listBranches(repo: Repo): Promise<Branch[]>;
33
+ /** Create `name` pointing at the tip of `startPoint` (no checkout). */
34
+ export declare function createBranchFrom(repo: Repo, name: string, startPoint?: string): Promise<void>;
35
+ /** Delete a branch ref (validated — deleteBranch has no internal ref check). */
36
+ export declare function deleteBranchByName(repo: Repo, name: string): Promise<void>;
37
+ /** Throws (NotFoundError) unless the branch resolves. */
38
+ export declare function assertBranchExists(repo: Repo, name: string): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import type { Repo } from "./types.js";
2
+ export interface CommitAuthor {
3
+ name: string;
4
+ email: string;
5
+ timestamp: number;
6
+ timezoneOffset: number;
7
+ }
8
+ /** An author stamped with the current time. */
9
+ export declare function authorNow(name: string, email: string): CommitAuthor;
10
+ /**
11
+ * Write a commit directly to a bare repository — no worktree, no checkout.
12
+ * `buildTree` receives the parent commit's tree oid (undefined on an empty
13
+ * repo / unborn branch) and returns the new root tree oid; this function
14
+ * writes the commit object and force-updates `refs/heads/<branch>`.
15
+ *
16
+ * Serialize concurrent writers externally (a per-repo lock): the
17
+ * resolve-ref → write-ref sequence is not atomic on object storage.
18
+ */
19
+ export declare function writeCommitToBare(repo: Repo, options: {
20
+ branch: string;
21
+ message: string;
22
+ author: CommitAuthor;
23
+ buildTree: (parentTreeOid: string | undefined) => Promise<string>;
24
+ }): Promise<string>;
25
+ /**
26
+ * Commit a set of files onto a branch, straight to the bare repo. Each blob
27
+ * is written to its own content-addressed key — no shared state between
28
+ * files, so they're written in parallel.
29
+ */
30
+ export declare function commitFilesToBare(repo: Repo, options: {
31
+ branch: string;
32
+ message: string;
33
+ author: CommitAuthor;
34
+ files: Array<{
35
+ path: string;
36
+ content: string | Uint8Array;
37
+ }>;
38
+ }): Promise<string>;
39
+ /** Commit the removal of one file from a branch, straight to the bare repo. */
40
+ export declare function deleteFileFromBare(repo: Repo, options: {
41
+ branch: string;
42
+ filePath: string;
43
+ message: string;
44
+ author: CommitAuthor;
45
+ }): Promise<string>;
@@ -0,0 +1,24 @@
1
+ import type { Repo } from "./types.js";
2
+ export interface DiffFile {
3
+ path: string;
4
+ status: "added" | "modified" | "deleted" | "renamed";
5
+ additions: number;
6
+ deletions: number;
7
+ patch: string;
8
+ oldPath?: string;
9
+ isBinary?: boolean;
10
+ oldContent?: string;
11
+ newContent?: string;
12
+ oldSize?: number;
13
+ newSize?: number;
14
+ }
15
+ export interface DiffResult {
16
+ files: DiffFile[];
17
+ totalAdditions: number;
18
+ totalDeletions: number;
19
+ totalFiles: number;
20
+ }
21
+ /** The diff a single commit introduced (against its first parent). */
22
+ export declare function getCommitDiff(repo: Repo, commitSha: string): Promise<DiffResult>;
23
+ /** The diff between two refs (base -> compare). */
24
+ export declare function getDiffBetweenRefs(repo: Repo, baseRef: string, compareRef: string): Promise<DiffResult>;
@@ -0,0 +1,41 @@
1
+ import type { OpsHooks, Repo } from "./types.js";
2
+ export interface FileHistoryEntry {
3
+ sha: string;
4
+ message: string;
5
+ authorName: string;
6
+ authorEmail: string;
7
+ createdAt: string;
8
+ }
9
+ export interface FileHistoryResult {
10
+ entries: FileHistoryEntry[];
11
+ /**
12
+ * True when the walk hit its depth budget (or the requested `limit`)
13
+ * before exhausting the branch's full commit chain — there may be older
14
+ * commits touching this file that a deeper walk would surface.
15
+ */
16
+ truncated: boolean;
17
+ }
18
+ /**
19
+ * Default walk bound for a caller that actually wants deep history (a file's
20
+ * "History" tab). Walking the full chain is round-trip-bound on object
21
+ * storage, so cap how far back a single request will look.
22
+ */
23
+ export declare const HISTORY_WALK_DEPTH = 400;
24
+ /**
25
+ * Much shallower default for a "latest commit touching this file" banner that
26
+ * only displays `entries[0]` — trades "always finds the true last-touching
27
+ * commit" for "finds it if it's reasonably recent", the right call for a
28
+ * banner with a full History view a click away.
29
+ */
30
+ export declare const BANNER_WALK_DEPTH = 60;
31
+ /**
32
+ * All commits (newest first) that changed a single file's blob oid, walking
33
+ * the first-parent chain — same approach as getLastCommitsForTree but for one
34
+ * path and collecting every match instead of stopping at the first.
35
+ */
36
+ export declare function getFileHistory(repo: Repo, options: {
37
+ ref: string;
38
+ filePath: string;
39
+ limit?: number;
40
+ maxDepth?: number;
41
+ }, hooks?: OpsHooks): Promise<FileHistoryResult>;
@@ -0,0 +1,85 @@
1
+ import { type TreeEntry } from "./tree.js";
2
+ import { type OpsHooks, type Repo } from "./types.js";
3
+ export interface CommitInfo {
4
+ oid: string;
5
+ commit: {
6
+ message: string;
7
+ tree: string;
8
+ parent: string[];
9
+ author: {
10
+ name: string;
11
+ email: string;
12
+ timestamp: number;
13
+ timezoneOffset: number;
14
+ };
15
+ committer: {
16
+ name: string;
17
+ email: string;
18
+ timestamp: number;
19
+ timezoneOffset: number;
20
+ };
21
+ };
22
+ payload: string;
23
+ }
24
+ /**
25
+ * Resolve a branch name / full ref / sha to its commit. The ref not
26
+ * resolving (unborn branch, genuinely empty repo) and the ref resolving but
27
+ * its commit object being unreadable (storage inconsistency — see
28
+ * `wrapMissingObject` above) are different failures with different meanings,
29
+ * so only the first is left as a raw isomorphic-git NotFoundError for
30
+ * callers to treat as "empty"; the second is wrapped into
31
+ * GitObjectNotFoundError specifically so it can't be mistaken for the first
32
+ * by an `isNotFound`-style check downstream (see getTreeFromRef/getCommitLog).
33
+ */
34
+ export declare function resolveCommit(repo: Repo, ref: string): Promise<{
35
+ oid: string;
36
+ commit: import("isomorphic-git").CommitObject;
37
+ }>;
38
+ /** Read a blob's bytes by oid. */
39
+ export declare function getBlob(repo: Repo, sha: string): Promise<Uint8Array>;
40
+ /** Read a file's bytes at a ref. Throws GitPathNotFoundError when absent. */
41
+ export declare function getFileContent(repo: Repo, filePath: string, ref?: string, hooks?: OpsHooks): Promise<Uint8Array>;
42
+ /** Read one commit by sha. */
43
+ export declare function getCommit(repo: Repo, sha: string): Promise<CommitInfo>;
44
+ export interface CommitLogOptions {
45
+ ref?: string;
46
+ depth?: number;
47
+ /**
48
+ * Pass the head sha when the caller already resolved `ref` — resolveRef
49
+ * tries several candidate paths in sequence and misses the first few every
50
+ * time for a normal branch name, which is pure waste when the sha is known.
51
+ */
52
+ knownHeadSha?: string;
53
+ }
54
+ /**
55
+ * The commit chain from a ref, newest first. Walking is inherently sequential
56
+ * (each commit's oid is only discoverable by reading its child first) and
57
+ * network-round-trip-bound against object storage, so the deepest walk seen
58
+ * per head is memoized in `hooks.resultCache` and sliced for shallower or
59
+ * repeated requests — don't bypass this by calling `git.log` directly.
60
+ */
61
+ export declare function getCommitLog(repo: Repo, options?: CommitLogOptions, hooks?: OpsHooks): Promise<CommitInfo[]>;
62
+ /** A file at a ref, decoded for display: utf8 text or base64 when binary. */
63
+ export declare function getFileFromRef(repo: Repo, filePath: string, ref: string, hooks?: OpsHooks): Promise<{
64
+ content: string;
65
+ size: number;
66
+ isBinary: boolean;
67
+ }>;
68
+ /**
69
+ * List a directory at the tip of a ref. Returns [] for an empty repo/unborn
70
+ * branch; throws GitPathNotFoundError when `treePath` doesn't exist. Results
71
+ * are memoized per head sha (auto-invalidates on push).
72
+ */
73
+ export declare function getTreeFromRef(repo: Repo, options?: {
74
+ ref?: string;
75
+ treePath?: string;
76
+ }, hooks?: OpsHooks): Promise<TreeEntry[]>;
77
+ /**
78
+ * A page of commit history from a branch tip. Memoized per head sha; builds
79
+ * on {@link getCommitLog}'s walk cache for the underlying chain.
80
+ */
81
+ export declare function getCommitHistory(repo: Repo, options: {
82
+ ref: string;
83
+ limit?: number;
84
+ skip?: number;
85
+ }, hooks?: OpsHooks): Promise<CommitInfo[]>;
@@ -0,0 +1,9 @@
1
+ export { assertBranchExists, assertSafeBranchName, type Branch, createBranchFrom, deleteBranchByName, listBranches, resolveLooseRefFast, } from "./branch.js";
2
+ export { authorNow, type CommitAuthor, commitFilesToBare, deleteFileFromBare, writeCommitToBare, } from "./commit.js";
3
+ export { type DiffFile, type DiffResult, getCommitDiff, getDiffBetweenRefs, } from "./diff.js";
4
+ export { BANNER_WALK_DEPTH, type FileHistoryEntry, type FileHistoryResult, getFileHistory, HISTORY_WALK_DEPTH, } from "./file-history.js";
5
+ export { type CommitInfo, type CommitLogOptions, getBlob, getCommit, getCommitHistory, getCommitLog, getFileContent, getFileFromRef, getTreeFromRef, resolveCommit, } from "./history.js";
6
+ export { getLastCommitsForTree, type LastCommitInfo, } from "./last-commit.js";
7
+ export { analyzeMerge, fastForwardMerge, type MergeAnalysis } from "./merge.js";
8
+ export { deleteFromTree, findTreeEntry, listTreeEntries, type TreeEntry, upsertTree, } from "./tree.js";
9
+ export { type IsoGitFs, type OpsHooks, type Repo, type ResultCache, resultKeyPrefixes, } from "./types.js";
@@ -0,0 +1,23 @@
1
+ import type { OpsHooks, Repo } from "./types.js";
2
+ export interface LastCommitInfo {
3
+ sha: string;
4
+ message: string;
5
+ authorName: string;
6
+ authorEmail: string;
7
+ createdAt: string;
8
+ }
9
+ /**
10
+ * For each direct child of `treePath` (at the tip of `ref`), find the most
11
+ * recent commit that changed it — the tree view's "last commit" column. Walks
12
+ * history newest-to-oldest, comparing the directory's tree oid
13
+ * commit-to-commit and only descending one level to diff child oids when
14
+ * something under the directory actually changed. Preserve the two-phase
15
+ * structure (parallel prefetch, then sequential resolve) when touching this —
16
+ * the "which entries are still unresolved" state must advance
17
+ * commit-by-commit.
18
+ */
19
+ export declare function getLastCommitsForTree(repo: Repo, options: {
20
+ ref: string;
21
+ treePath?: string;
22
+ depth?: number;
23
+ }, hooks?: OpsHooks): Promise<Record<string, LastCommitInfo>>;
@@ -0,0 +1,28 @@
1
+ import type { Repo } from "./types.js";
2
+ export interface MergeAnalysis {
3
+ canMerge: boolean;
4
+ hasConflicts: boolean;
5
+ conflictingFiles: string[];
6
+ fastForward: boolean;
7
+ }
8
+ /**
9
+ * Cheap pre-merge check: do both branches exist, and is this a fast-forward?
10
+ * `canMerge`/`fastForward` are the only fields this actually determines —
11
+ * `hasConflicts`/`conflictingFiles` are NOT a real content-conflict check
12
+ * (isomorphic-git's `git.merge` doesn't expose a dry-run), they only ever
13
+ * reflect "one of the branches couldn't be resolved" (`canMerge: false`).
14
+ * Real merge conflicts are only discoverable by actually attempting the
15
+ * merge.
16
+ */
17
+ export declare function analyzeMerge(repo: Repo, sourceBranch: string, targetBranch: string): Promise<MergeAnalysis>;
18
+ /**
19
+ * Attempt a fast-forward merge directly against the bare repo: when source is
20
+ * a descendant of target, just move the target ref — no worktree, no new
21
+ * commit. Returns null when the merge is not a fast-forward (callers fall
22
+ * back to a real three-way merge, which needs a worktree). Serialize with a
23
+ * per-repo lock: resolve → writeRef is not atomic.
24
+ */
25
+ export declare function fastForwardMerge(repo: Repo, sourceBranch: string, targetBranch: string): Promise<{
26
+ success: true;
27
+ commitSha: string;
28
+ } | null>;
@@ -0,0 +1,19 @@
1
+ import type { Repo } from "./types.js";
2
+ export interface TreeEntry {
3
+ path: string;
4
+ mode: string;
5
+ type: "blob" | "tree";
6
+ oid: string;
7
+ size?: number;
8
+ }
9
+ /**
10
+ * Build/update a git tree by overlaying new blobs onto an existing tree,
11
+ * returning the new root tree oid. `entries` maps relative paths to blob oids.
12
+ */
13
+ export declare function upsertTree(repo: Repo, treeOid: string | undefined, entries: Map<string, string>): Promise<string>;
14
+ /** Remove a file path from a tree, returning the new root tree oid. */
15
+ export declare function deleteFromTree(repo: Repo, treeOid: string, filePath: string): Promise<string>;
16
+ /** Resolve a path inside a tree to its entry, or null when absent. */
17
+ export declare function findTreeEntry(repo: Repo, rootTreeOid: string, treePath: string): Promise<TreeEntry | null>;
18
+ /** List a tree's direct entries, with paths prefixed by `prefix`. */
19
+ export declare function listTreeEntries(repo: Repo, treeOid: string, prefix?: string): Promise<TreeEntry[]>;
@@ -1,13 +1,12 @@
1
- import git__default from 'isomorphic-git';
2
-
1
+ import type git from "isomorphic-git";
3
2
  /** The fs shape isomorphic-git accepts, derived from its own signatures. */
4
- type IsoGitFs = Parameters<typeof git__default.readTree>[0]["fs"];
3
+ export type IsoGitFs = Parameters<typeof git.readTree>[0]["fs"];
5
4
  /**
6
5
  * A repository handle: everything isomorphic-git needs to address one bare
7
6
  * repo. Create it once per request (sharing `cache` across calls is what lets
8
7
  * isomorphic-git reuse parsed pack indexes) and pass it to every op.
9
8
  */
10
- interface Repo {
9
+ export interface Repo {
11
10
  fs: IsoGitFs;
12
11
  gitdir: string;
13
12
  /** isomorphic-git's shared parse cache — strongly recommended per repo. */
@@ -20,12 +19,12 @@ interface Repo {
20
19
  * under {@link resultKeyPrefixes} after rewriting a repo's storage out of
21
20
  * band, or stale walks leak until your store's own eviction.
22
21
  */
23
- interface ResultCache {
22
+ export interface ResultCache {
24
23
  get<T>(key: string): T | null | undefined;
25
24
  set(key: string, value: unknown): void;
26
25
  }
27
26
  /** Optional instrumentation and tuning hooks accepted by every op. */
28
- interface OpsHooks {
27
+ export interface OpsHooks {
29
28
  resultCache?: ResultCache;
30
29
  /** Wrap a timed sub-step (network walk, tree listing). Default: run directly. */
31
30
  step?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;
@@ -43,11 +42,10 @@ interface OpsHooks {
43
42
  /** Minimum walk depth before `prefetch` fires for history walks. Default 5. */
44
43
  prefetchMinDepth?: number;
45
44
  }
45
+ export declare const runStep: <T>(hooks: OpsHooks | undefined, label: string, fn: () => Promise<T>) => Promise<T>;
46
46
  /**
47
47
  * The result-cache key prefixes holding entries for `gitdir` — evict these
48
48
  * from your {@link ResultCache} when the repo's storage was rewritten outside
49
49
  * a normal push (bulk sync, rename, repack cleanup).
50
50
  */
51
- declare function resultKeyPrefixes(gitdir: string): string[];
52
-
53
- export { type IsoGitFs as I, type OpsHooks as O, type Repo as R, type ResultCache as a, resultKeyPrefixes as r };
51
+ export declare function resultKeyPrefixes(gitdir: string): string[];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Typed view of a git `objects/pack` directory listing.
3
+ *
4
+ * Every pack is a set of sibling files sharing one base name
5
+ * (`pack-<hash>.pack`, `.idx`, and optionally `.keep`/`.bitmap`/`.rev`).
6
+ * Parsing names once into {@link PackFileName} lets callers group, filter and
7
+ * rename by kind instead of re-matching filename strings at each call site.
8
+ *
9
+ * Edge-compatible: no node: imports.
10
+ */
11
+ /** Every file kind git writes into `objects/pack`. */
12
+ export declare const PACK_FILE_KINDS: readonly ["pack", "idx", "keep", "bitmap", "rev"];
13
+ export type PackFileKind = (typeof PACK_FILE_KINDS)[number];
14
+ /** Kinds isomorphic-git actually reads; the rest are git-CLI accelerators. */
15
+ export declare const READABLE_PACK_FILE_KINDS: ReadonlySet<PackFileKind>;
16
+ export interface PackFileName {
17
+ /** Shared base of a pack's sibling files, e.g. `pack-1a2b…`. */
18
+ readonly base: string;
19
+ readonly kind: PackFileKind;
20
+ /** The file name as listed, e.g. `pack-1a2b….idx`. */
21
+ readonly name: string;
22
+ }
23
+ /** Parse a pack-directory entry; `null` for anything git didn't put there. */
24
+ export declare function parsePackFileName(name: string): PackFileName | null;
25
+ /** File name of the `kind` sibling of the pack with this base name. */
26
+ export declare function packFileName(base: string, kind: PackFileKind): string;
27
+ /** Parse a directory listing, dropping entries that aren't pack files. */
28
+ export declare function parsePackDirectory(entries: readonly string[]): PackFileName[];
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Normalize an absolute-or-relative filesystem path into a storage key
3
+ * segment: no leading/trailing slashes, `.` segments dropped, `..` resolved.
4
+ * A `..` that would escape the root throws EINVAL — paths handed to the fs
5
+ * must never address keys outside the configured prefix.
6
+ */
7
+ export declare function normalizePath(filepath: string): string;
8
+ /** Join a configured key prefix with a normalized path. */
9
+ export declare function toKey(prefix: string, filepath: string): string;