git-fs-s3 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -17
- package/dist/{chunk-YQFNY6PG.js → chunk-FIFWOMPM.js} +1 -1
- package/dist/chunk-FIFWOMPM.js.map +1 -0
- package/dist/{chunk-Z4656PGE.js → chunk-XNSZTA4Y.js} +3 -3
- package/dist/{chunk-Z4656PGE.js.map → chunk-XNSZTA4Y.js.map} +1 -1
- package/dist/http.js +8 -8
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/ops.js +15 -18
- package/dist/ops.js.map +1 -1
- package/dist/types/cache.d.ts +81 -0
- package/dist/types/edge-utils.d.ts +43 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/git-errors.d.ts +70 -0
- package/dist/types/git-fs.d.ts +60 -0
- package/dist/types/git-layout.d.ts +52 -0
- package/dist/types/http/index.d.ts +7 -0
- package/dist/types/http/info-refs.d.ts +28 -0
- package/dist/types/http/pack-reuse.d.ts +26 -0
- package/dist/types/http/pkt-line.d.ts +14 -0
- package/dist/types/http/reachability.d.ts +36 -0
- package/dist/types/http/receive-pack.d.ts +55 -0
- package/dist/types/http/repack.d.ts +24 -0
- package/dist/types/http/types.d.ts +40 -0
- package/dist/types/http/upload-pack.d.ts +21 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/ops/branch.d.ts +38 -0
- package/dist/types/ops/commit.d.ts +45 -0
- package/dist/types/ops/diff.d.ts +24 -0
- package/dist/types/ops/file-history.d.ts +41 -0
- package/dist/types/ops/history.d.ts +86 -0
- package/dist/types/ops/index.d.ts +9 -0
- package/dist/types/ops/last-commit.d.ts +23 -0
- package/dist/types/ops/merge.d.ts +28 -0
- package/dist/types/ops/tree.d.ts +19 -0
- package/dist/{types-BHoHOaQt.d.cts → types/ops/types.d.ts} +7 -9
- package/dist/types/pack-files.d.ts +28 -0
- package/dist/types/path.d.ts +9 -0
- package/dist/types/refs.d.ts +54 -0
- package/dist/types/retry.d.ts +52 -0
- package/dist/types/stores/memory.d.ts +16 -0
- package/dist/{s3.d.ts → types/stores/s3.d.ts} +4 -7
- package/dist/{types-dbHzZ4ed.d.ts → types/types.d.ts} +11 -22
- package/package.json +18 -22
- package/dist/chunk-YQFNY6PG.js.map +0 -1
- package/dist/http.cjs +0 -1063
- package/dist/http.cjs.map +0 -1
- package/dist/http.d.cts +0 -215
- package/dist/http.d.ts +0 -215
- package/dist/index.cjs +0 -944
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -479
- package/dist/index.d.ts +0 -479
- package/dist/ops.cjs +0 -1060
- package/dist/ops.cjs.map +0 -1
- package/dist/ops.d.cts +0 -302
- package/dist/ops.d.ts +0 -302
- package/dist/s3.cjs +0 -123
- package/dist/s3.cjs.map +0 -1
- package/dist/s3.d.cts +0 -36
- package/dist/types-BHoHOaQt.d.ts +0 -53
- package/dist/types-dbHzZ4ed.d.cts +0 -131
|
@@ -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,28 @@
|
|
|
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
|
+
/** Advertised as HEAD when HEAD itself doesn't name an existing branch. */
|
|
15
|
+
defaultBranch: string;
|
|
16
|
+
/** Advertised in the agent capability. Default "git-fs-s3". */
|
|
17
|
+
agent?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* `GET …/info/refs?service=…` — the ref advertisement. Authentication and
|
|
21
|
+
* authorization are the caller's job before invoking this.
|
|
22
|
+
*
|
|
23
|
+
* Capabilities advertised match what the sibling handlers implement:
|
|
24
|
+
* side-band-64k (honored by handleUploadPack's response framing — clients
|
|
25
|
+
* like isomorphic-git unconditionally expect it), tip/reachable sha1 wants,
|
|
26
|
+
* delete-refs and report-status for pushes.
|
|
27
|
+
*/
|
|
28
|
+
export declare function handleInfoRefs(repo: Repo, options: InfoRefsOptions, hooks?: HttpHooks): Promise<GitHttpResult>;
|
|
@@ -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,55 @@
|
|
|
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
|
+
/** HEAD's branch when a push initializes an empty repository. */
|
|
33
|
+
defaultBranch: string;
|
|
34
|
+
/**
|
|
35
|
+
* Repack tuning, or `false` to skip repacking entirely. The returned
|
|
36
|
+
* `stalePackPaths` are gitdir-relative pack files the repack deleted from
|
|
37
|
+
* this repo — after syncing to any secondary storage, delete them there
|
|
38
|
+
* too.
|
|
39
|
+
*/
|
|
40
|
+
repack?: RepackOptions | false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The storage half of a push against `repo`: initialize if needed, index the
|
|
44
|
+
* incoming pack, CAS-apply ref updates, then repack when enough packs have
|
|
45
|
+
* accumulated. Serialize concurrent pushes to the same repo externally (a
|
|
46
|
+
* per-repo lock); build the HTTP response afterwards with
|
|
47
|
+
* {@link receivePackResponse}.
|
|
48
|
+
*/
|
|
49
|
+
export declare function applyReceivePack(repo: Repo, parsed: {
|
|
50
|
+
refUpdates: RefUpdateCommand[];
|
|
51
|
+
packData: Uint8Array;
|
|
52
|
+
}, options: ApplyReceivePackOptions, hooks?: HttpHooks): Promise<{
|
|
53
|
+
results: RefUpdateResult[];
|
|
54
|
+
stalePackPaths: string[];
|
|
55
|
+
}>;
|
|
@@ -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,86 @@
|
|
|
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. Throws GitRefNotFoundError when the
|
|
70
|
+
* ref doesn't resolve (including an empty repo's unborn branch) and
|
|
71
|
+
* GitPathNotFoundError when `treePath` doesn't exist. Results are memoized per
|
|
72
|
+
* head sha (auto-invalidates on push).
|
|
73
|
+
*/
|
|
74
|
+
export declare function getTreeFromRef(repo: Repo, options: {
|
|
75
|
+
ref: string;
|
|
76
|
+
treePath?: string;
|
|
77
|
+
}, hooks?: OpsHooks): Promise<TreeEntry[]>;
|
|
78
|
+
/**
|
|
79
|
+
* A page of commit history from a branch tip. Memoized per head sha; builds
|
|
80
|
+
* on {@link getCommitLog}'s walk cache for the underlying chain.
|
|
81
|
+
*/
|
|
82
|
+
export declare function getCommitHistory(repo: Repo, options: {
|
|
83
|
+
ref: string;
|
|
84
|
+
limit?: number;
|
|
85
|
+
skip?: number;
|
|
86
|
+
}, 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[]>;
|