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
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
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
|
|
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;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git ref-name validation, mirroring isomorphic-git's own internal `isValidRef`
|
|
3
|
+
* character-class rules (the check `git.branch` and top-level `git.writeRef`
|
|
4
|
+
* run before touching disk).
|
|
5
|
+
*
|
|
6
|
+
* Several of isomorphic-git's OTHER ref-touching primitives — `git.commit`,
|
|
7
|
+
* `git.merge`, `git.deleteBranch`, and top-level `git.resolveRef`/
|
|
8
|
+
* `git.deleteRef` — do NOT run this check internally: they resolve straight
|
|
9
|
+
* through `fs.write`/`fs.rm(join(gitdir, ref))` with no jail to the gitdir.
|
|
10
|
+
* On a shared-storage server (many repos under one prefix or base directory),
|
|
11
|
+
* every branch/ref name that originates from request input must be validated
|
|
12
|
+
* against these predicates before it reaches any of those primitives —
|
|
13
|
+
* otherwise a `"../"`-laden name lets a caller with write access to any single
|
|
14
|
+
* repo read, corrupt, or delete another repo's ref/object files.
|
|
15
|
+
*/
|
|
16
|
+
/** Validates a fully-qualified ref (must start with refs/heads/ or refs/tags/). */
|
|
17
|
+
export declare function isSafeFullRefName(ref: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Validates a bare branch name (no refs/ prefix). Rejects anything that looks
|
|
20
|
+
* like a full ref path — a name of `"refs/heads/x"` would otherwise sail
|
|
21
|
+
* through unprefixed at call sites that build `refs/heads/${name}` themselves
|
|
22
|
+
* (doubling the prefix into something that still resolves), or be used as-is
|
|
23
|
+
* at call sites that pass a name already containing `"refs/"` straight
|
|
24
|
+
* through. Also rejects 40-hex SHA-shaped values so a stored branch name can
|
|
25
|
+
* never be ambiguous with a commit SHA at write time; use
|
|
26
|
+
* {@link isSafeRefName} on read paths that accept both shapes.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isSafeBranchName(name: string): boolean;
|
|
29
|
+
/** True for a full 40-hex-char commit SHA — the shape {@link isSafeBranchName} deliberately rejects. */
|
|
30
|
+
export declare function isFullSha(value: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Validates a "ref" field that may name either a branch or a commit SHA it's
|
|
33
|
+
* pinned to — the shape read-path route params take (permalinks, raw links).
|
|
34
|
+
* Both shapes still go through the traversal check.
|
|
35
|
+
*/
|
|
36
|
+
export declare function isSafeRefName(value: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Validates a repo-relative file path from request input: relative, no `..`
|
|
39
|
+
* segments, no `.git/` prefix, no null bytes. Use this anywhere a path
|
|
40
|
+
* segment comes straight off a URL or form field rather than re-deriving the
|
|
41
|
+
* checks ad hoc.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isSafeRepoPath(p: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Qualify a bare branch name to `refs/heads/<name>` before handing it to
|
|
46
|
+
* isomorphic-git. `resolveRef`/`expand` try several candidate paths in
|
|
47
|
+
* sequence for a bare name — `ref`, `refs/ref`, `refs/tags/ref`,
|
|
48
|
+
* `refs/heads/ref`, … — missing (and, against object storage, paying a real
|
|
49
|
+
* round trip for) the first three every time. For a branch-only ref model,
|
|
50
|
+
* skip straight to the winner. Left untouched: already-qualified refs,
|
|
51
|
+
* `"HEAD"` (its own first candidate, already optimal), and 40-hex oids
|
|
52
|
+
* (resolved locally by isomorphic-git with no I/O at all).
|
|
53
|
+
*/
|
|
54
|
+
export declare function qualifyBranchRef(ref: string): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ObjectStore } from "./types.js";
|
|
2
|
+
/** Options accepted by {@link createRetryStore}. */
|
|
3
|
+
export interface RetryOptions {
|
|
4
|
+
/** Retries after the first attempt (total attempts = retries + 1). Default 3. */
|
|
5
|
+
retries?: number;
|
|
6
|
+
/** Backoff base delay in milliseconds, doubled each attempt. Default 100. */
|
|
7
|
+
initialDelayMs?: number;
|
|
8
|
+
/** Upper bound for the backoff base delay. Default 5000. */
|
|
9
|
+
maxDelayMs?: number;
|
|
10
|
+
/** Random jitter added to each delay, as a fraction of it. Default 0.3. */
|
|
11
|
+
jitter?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Decide whether an error is worth retrying. The store contract maps
|
|
14
|
+
* "not found" to `null` rather than throwing, so any thrown error is a
|
|
15
|
+
* genuine failure; the default retries network faults, throttling, and
|
|
16
|
+
* HTTP 5xx responses.
|
|
17
|
+
*/
|
|
18
|
+
isRetryable?: (error: unknown) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Circuit breaker configuration, or `false` to disable. After `threshold`
|
|
21
|
+
* consecutive failures the store fails fast for `resetMs`, then lets one
|
|
22
|
+
* request probe the backend again. Defaults: 5 failures, 30 000 ms.
|
|
23
|
+
*/
|
|
24
|
+
breaker?: false | {
|
|
25
|
+
threshold?: number;
|
|
26
|
+
resetMs?: number;
|
|
27
|
+
};
|
|
28
|
+
/** Called before each retry sleep; useful for logging/metrics. */
|
|
29
|
+
onRetry?: (info: {
|
|
30
|
+
key: string;
|
|
31
|
+
op: string;
|
|
32
|
+
attempt: number;
|
|
33
|
+
delayMs: number;
|
|
34
|
+
}) => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Thrown instead of calling the backend while the circuit breaker is open.
|
|
38
|
+
* Carries `code: "EUNAVAILABLE"` so callers can map it to a 503.
|
|
39
|
+
*/
|
|
40
|
+
export declare class CircuitOpenError extends Error {
|
|
41
|
+
readonly code = "EUNAVAILABLE";
|
|
42
|
+
constructor();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Wrap an {@link ObjectStore} with retries (exponential backoff + jitter) and
|
|
46
|
+
* an optional per-instance circuit breaker.
|
|
47
|
+
*
|
|
48
|
+
* Place this decorator closest to the network store, underneath any cache:
|
|
49
|
+
* the cache then never stores transient failures, and callers coalesced onto
|
|
50
|
+
* one request share a single retried attempt.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createRetryStore(store: ObjectStore, options?: RetryOptions): ObjectStore;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ListOptions, ListResult, ObjectStat, ObjectStore } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory {@link ObjectStore}. Useful for tests, examples, and ephemeral
|
|
4
|
+
* repositories; also the reference implementation for the list/delimiter
|
|
5
|
+
* semantics other stores must match.
|
|
6
|
+
*/
|
|
7
|
+
export declare class MemoryObjectStore implements ObjectStore {
|
|
8
|
+
private readonly objects;
|
|
9
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
10
|
+
put(key: string, data: Uint8Array): Promise<void>;
|
|
11
|
+
delete(key: string): Promise<void>;
|
|
12
|
+
head(key: string): Promise<ObjectStat | null>;
|
|
13
|
+
list(prefix: string, options?: ListOptions): Promise<ListResult>;
|
|
14
|
+
/** Number of stored objects (test convenience, not part of ObjectStore). */
|
|
15
|
+
get size(): number;
|
|
16
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { S3Client } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
interface S3ObjectStoreOptions {
|
|
1
|
+
import { type S3Client } from "@aws-sdk/client-s3";
|
|
2
|
+
import type { ListOptions, ListResult, ObjectStat, ObjectStore } from "../types.js";
|
|
3
|
+
export interface S3ObjectStoreOptions {
|
|
5
4
|
/** A configured S3Client. Works with AWS S3, Cloudflare R2, MinIO, Backblaze B2, … */
|
|
6
5
|
client: S3Client;
|
|
7
6
|
bucket: string;
|
|
@@ -19,7 +18,7 @@ interface S3ObjectStoreOptions {
|
|
|
19
18
|
* (peer dependency). One instance per bucket/prefix; reuse a single
|
|
20
19
|
* `S3Client` across stores so HTTP connections are pooled.
|
|
21
20
|
*/
|
|
22
|
-
declare class S3ObjectStore implements ObjectStore {
|
|
21
|
+
export declare class S3ObjectStore implements ObjectStore {
|
|
23
22
|
private readonly client;
|
|
24
23
|
private readonly bucket;
|
|
25
24
|
private readonly prefix;
|
|
@@ -32,5 +31,3 @@ declare class S3ObjectStore implements ObjectStore {
|
|
|
32
31
|
head(key: string): Promise<ObjectStat | null>;
|
|
33
32
|
list(prefix: string, options?: ListOptions): Promise<ListResult>;
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
export { S3ObjectStore, type S3ObjectStoreOptions };
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* implementations ship with the package: {@link ../stores/memory.MemoryObjectStore}
|
|
6
6
|
* and {@link ../stores/s3.S3ObjectStore}.
|
|
7
7
|
*/
|
|
8
|
-
interface ObjectStore {
|
|
8
|
+
export interface ObjectStore {
|
|
9
9
|
/** Return the object's bytes, or `null` if the key does not exist. */
|
|
10
10
|
get(key: string): Promise<Uint8Array | null>;
|
|
11
11
|
/** Create or overwrite the object at `key`. */
|
|
@@ -24,14 +24,14 @@ interface ObjectStore {
|
|
|
24
24
|
*/
|
|
25
25
|
list(prefix: string, options?: ListOptions): Promise<ListResult>;
|
|
26
26
|
}
|
|
27
|
-
interface ObjectStat {
|
|
27
|
+
export interface ObjectStat {
|
|
28
28
|
size: number;
|
|
29
29
|
}
|
|
30
|
-
interface ListOptions {
|
|
30
|
+
export interface ListOptions {
|
|
31
31
|
delimiter?: string;
|
|
32
32
|
limit?: number;
|
|
33
33
|
}
|
|
34
|
-
interface ListResult {
|
|
34
|
+
export interface ListResult {
|
|
35
35
|
objects: {
|
|
36
36
|
key: string;
|
|
37
37
|
size: number;
|
|
@@ -39,7 +39,7 @@ interface ListResult {
|
|
|
39
39
|
prefixes: string[];
|
|
40
40
|
}
|
|
41
41
|
/** What a caller knows about a gitdir it registers with `registerGitdir`. */
|
|
42
|
-
interface GitdirFacts {
|
|
42
|
+
export interface GitdirFacts {
|
|
43
43
|
/**
|
|
44
44
|
* Top-level gitdir files this backend never writes, such as
|
|
45
45
|
* `packed-refs` or `shallow`; reading or statting them answers ENOENT
|
|
@@ -48,7 +48,7 @@ interface GitdirFacts {
|
|
|
48
48
|
absentFiles?: readonly string[];
|
|
49
49
|
}
|
|
50
50
|
/** Options accepted by {@link ../git-fs.createGitFs}. */
|
|
51
|
-
interface GitFsOptions {
|
|
51
|
+
export interface GitFsOptions {
|
|
52
52
|
/**
|
|
53
53
|
* Key prefix all paths are stored under, e.g. `"repos/alice/blog"`.
|
|
54
54
|
* Defaults to `""` (paths map directly to keys).
|
|
@@ -63,25 +63,16 @@ interface GitFsOptions {
|
|
|
63
63
|
* Default false.
|
|
64
64
|
*/
|
|
65
65
|
looseObjectHints?: boolean;
|
|
66
|
-
/**
|
|
67
|
-
* Paths (relative to the fs, before `prefix` is applied) that are known
|
|
68
|
-
* never to exist; `readFile`/`stat` answer ENOENT for them with zero
|
|
69
|
-
* store calls. Useful for files git probes on every operation but this
|
|
70
|
-
* backend never writes, such as `packed-refs` or `shallow` under a known
|
|
71
|
-
* gitdir layout. Match precisely — a plain suffix check is wrong, since
|
|
72
|
-
* e.g. `refs/heads/packed-refs` is a legal branch ref.
|
|
73
|
-
*/
|
|
74
|
-
isStructurallyAbsent?: (path: string) => boolean;
|
|
75
66
|
/** Time-to-live for loose-object hints in milliseconds. Default 3 600 000. */
|
|
76
67
|
hintTtlMs?: number;
|
|
77
68
|
/** Diagnostic sink for notable fs events (hint detection results). */
|
|
78
69
|
onNote?: (message: string) => void;
|
|
79
70
|
}
|
|
80
|
-
type Encoding = "utf8";
|
|
81
|
-
interface ReadFileOptions {
|
|
71
|
+
export type Encoding = "utf8";
|
|
72
|
+
export interface ReadFileOptions {
|
|
82
73
|
encoding?: Encoding;
|
|
83
74
|
}
|
|
84
|
-
interface WriteFileOptions {
|
|
75
|
+
export interface WriteFileOptions {
|
|
85
76
|
encoding?: Encoding;
|
|
86
77
|
mode?: number;
|
|
87
78
|
}
|
|
@@ -89,7 +80,7 @@ interface WriteFileOptions {
|
|
|
89
80
|
* The stat shape isomorphic-git expects. Object storage has no inodes,
|
|
90
81
|
* owners, or modification times, so numeric fields are zero.
|
|
91
82
|
*/
|
|
92
|
-
interface Stat {
|
|
83
|
+
export interface Stat {
|
|
93
84
|
type: "file" | "dir";
|
|
94
85
|
mode: number;
|
|
95
86
|
size: number;
|
|
@@ -110,7 +101,7 @@ interface Stat {
|
|
|
110
101
|
* option. Declared locally so the package has no dependency on
|
|
111
102
|
* isomorphic-git itself.
|
|
112
103
|
*/
|
|
113
|
-
interface GitFsClient {
|
|
104
|
+
export interface GitFsClient {
|
|
114
105
|
promises: {
|
|
115
106
|
readFile(filepath: string, options?: ReadFileOptions | Encoding): Promise<Uint8Array | string>;
|
|
116
107
|
writeFile(filepath: string, data: Uint8Array | string, options?: WriteFileOptions | Encoding): Promise<void>;
|
|
@@ -127,5 +118,3 @@ interface GitFsClient {
|
|
|
127
118
|
chmod(filepath: string, mode: number): Promise<void>;
|
|
128
119
|
};
|
|
129
120
|
}
|
|
130
|
-
|
|
131
|
-
export type { GitFsClient as G, ListOptions as L, ObjectStore as O, Stat as S, GitdirFacts as a, GitFsOptions as b, ObjectStat as c, ListResult as d };
|
package/package.json
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-fs-s3",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "isomorphic-git filesystem backend for S3-compatible object storage (AWS S3, Cloudflare R2, MinIO) — run git servers without a disk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.js"
|
|
10
|
-
"require": "./dist/index.cjs"
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
11
10
|
},
|
|
12
11
|
"./s3": {
|
|
13
|
-
"types": "./dist/s3.d.ts",
|
|
14
|
-
"import": "./dist/s3.js"
|
|
15
|
-
"require": "./dist/s3.cjs"
|
|
12
|
+
"types": "./dist/types/stores/s3.d.ts",
|
|
13
|
+
"import": "./dist/s3.js"
|
|
16
14
|
},
|
|
17
15
|
"./ops": {
|
|
18
|
-
"types": "./dist/ops.d.ts",
|
|
19
|
-
"import": "./dist/ops.js"
|
|
20
|
-
"require": "./dist/ops.cjs"
|
|
16
|
+
"types": "./dist/types/ops/index.d.ts",
|
|
17
|
+
"import": "./dist/ops.js"
|
|
21
18
|
},
|
|
22
19
|
"./http": {
|
|
23
|
-
"types": "./dist/http.d.ts",
|
|
24
|
-
"import": "./dist/http.js"
|
|
25
|
-
"require": "./dist/http.cjs"
|
|
20
|
+
"types": "./dist/types/http/index.d.ts",
|
|
21
|
+
"import": "./dist/http.js"
|
|
26
22
|
}
|
|
27
23
|
},
|
|
28
24
|
"files": [
|
|
@@ -30,7 +26,7 @@
|
|
|
30
26
|
],
|
|
31
27
|
"sideEffects": false,
|
|
32
28
|
"scripts": {
|
|
33
|
-
"build": "tsup",
|
|
29
|
+
"build": "tsup && tsc -p tsconfig.build.json",
|
|
34
30
|
"test": "vitest run",
|
|
35
31
|
"test:watch": "vitest",
|
|
36
32
|
"coverage": "vitest run --coverage",
|
|
@@ -66,10 +62,10 @@
|
|
|
66
62
|
"access": "public"
|
|
67
63
|
},
|
|
68
64
|
"engines": {
|
|
69
|
-
"node": ">=
|
|
65
|
+
"node": ">=22"
|
|
70
66
|
},
|
|
71
67
|
"dependencies": {
|
|
72
|
-
"lru-cache": "^11.5.
|
|
68
|
+
"lru-cache": "^11.5.3"
|
|
73
69
|
},
|
|
74
70
|
"peerDependencies": {
|
|
75
71
|
"@aws-sdk/client-s3": ">=3.0.0",
|
|
@@ -88,13 +84,13 @@
|
|
|
88
84
|
}
|
|
89
85
|
},
|
|
90
86
|
"devDependencies": {
|
|
91
|
-
"@aws-sdk/client-s3": "^3.
|
|
92
|
-
"@biomejs/biome": "^2.5.
|
|
93
|
-
"@types/node": "^
|
|
94
|
-
"isomorphic-git": "^1.
|
|
87
|
+
"@aws-sdk/client-s3": "^3.1149.0",
|
|
88
|
+
"@biomejs/biome": "^2.5.15",
|
|
89
|
+
"@types/node": "^26.6.5",
|
|
90
|
+
"isomorphic-git": "^1.43.3",
|
|
95
91
|
"tsup": "^8.5.1",
|
|
96
|
-
"typescript": "^
|
|
97
|
-
"vitest": "^5.0.
|
|
92
|
+
"typescript": "^7.0.2",
|
|
93
|
+
"vitest": "^5.0.3",
|
|
98
94
|
"diff": "^9.0.0"
|
|
99
95
|
},
|
|
100
96
|
"allowScripts": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/edge-utils.ts","../src/git-errors.ts","../src/refs.ts"],"sourcesContent":["/**\n * Edge-compatible utilities replacing node:crypto, node:zlib, and Buffer.\n *\n * Every function here uses only Web APIs (SubtleCrypto, CompressionStream,\n * TextEncoder/TextDecoder) — no Node built-ins. They work on Cloudflare\n * Workers, Vercel Edge, Deno Deploy, and Node >= 18.\n */\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\n// ---------------------------------------------------------------------------\n// Text\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a UTF-8 string to bytes.\n *\n * Return type pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,\n * whose default type argument differs across TypeScript versions) so it's\n * always assignable to Fetch API `BodyInit` regardless of a consumer's own\n * TypeScript/lib version.\n */\nexport function encodeUtf8(data: string): Uint8Array<ArrayBuffer> {\n\treturn textEncoder.encode(data);\n}\n\n/** Decode bytes as UTF-8. */\nexport function decodeUtf8(data: Uint8Array): string {\n\treturn textDecoder.decode(data);\n}\n\n/** Decode bytes as ASCII. */\nexport function decodeAscii(data: Uint8Array): string {\n\tlet s = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\ts += String.fromCharCode(data[i] as number);\n\treturn s;\n}\n\n// ---------------------------------------------------------------------------\n// Array manipulation\n// ---------------------------------------------------------------------------\n\n/** Concatenate any number of Uint8Arrays into one. */\nexport function concat(...parts: Uint8Array[]): Uint8Array<ArrayBuffer> {\n\tlet total = 0;\n\tfor (const p of parts) total += p.length;\n\tconst out = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const p of parts) {\n\t\tout.set(p, offset);\n\t\toffset += p.length;\n\t}\n\treturn out;\n}\n\n// ---------------------------------------------------------------------------\n// Encoding\n// ---------------------------------------------------------------------------\n\n/** Uint8Array → lowercase hex string. */\nexport function toHex(data: Uint8Array): string {\n\tlet hex = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\thex += (data[i] as number).toString(16).padStart(2, \"0\");\n\treturn hex;\n}\n\n/** Uint8Array → base64 string. */\nexport function toBase64(data: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (let i = 0; i < data.length; i++)\n\t\tbinary += String.fromCharCode(data[i] as number);\n\treturn btoa(binary);\n}\n\n/** Hex string → Uint8Array. */\nexport function fromHex(hex: string): Uint8Array<ArrayBuffer> {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n\t}\n\treturn bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Crypto\n// ---------------------------------------------------------------------------\n\n/** SHA-1 hash via Web Crypto API. Returns a hex string. */\nexport async function sha1(data: Uint8Array | string): Promise<string> {\n\tconst bytes = typeof data === \"string\" ? encodeUtf8(data) : data;\n\tconst hash = await globalThis.crypto.subtle.digest(\"SHA-1\", bytes);\n\treturn toHex(new Uint8Array(hash));\n}\n\n// ---------------------------------------------------------------------------\n// Compression\n// ---------------------------------------------------------------------------\n\n/**\n * Deflate compress via the CompressionStream Web API.\n * Falls back to throwing if CompressionStream is unavailable (very old runtimes).\n */\nexport async function deflate(\n\tdata: Uint8Array,\n): Promise<Uint8Array<ArrayBuffer>> {\n\tconst stream = new Blob([data])\n\t\t.stream()\n\t\t.pipeThrough(new CompressionStream(\"deflate\"));\n\treturn new Uint8Array(await new Response(stream).arrayBuffer());\n}\n\n// ---------------------------------------------------------------------------\n// Binary detection (replaces Buffer.includes(0) pattern)\n// ---------------------------------------------------------------------------\n\n/** Check if a Uint8Array contains a null byte. */\nexport function hasNullByte(data: Uint8Array): boolean {\n\treturn data.includes(0);\n}\n\n/**\n * Read a blob as text or binary metadata — the edge-compatible replacement\n * for the `Buffer.from(blob)` pattern used throughout diff.ts and history.ts.\n */\nexport function readBlobContent(blob: Uint8Array): {\n\tisBinary: boolean;\n\ttext: string;\n\tbytes: Uint8Array;\n} {\n\tconst isBinary = hasNullByte(blob);\n\treturn {\n\t\tisBinary,\n\t\ttext: isBinary ? \"\" : decodeUtf8(blob),\n\t\tbytes: blob,\n\t};\n}\n","/**\n * Git-server error types carrying an HTTP status and a retryability flag, so\n * transport layers can map internal failures to responses without inspecting\n * messages. Extend {@link GitError} for app-specific cases (storage backends,\n * quota, …) and {@link formatErrorResponse} keeps working for them.\n */\nexport class GitError extends Error {\n\tstatusCode: number;\n\tretryable: boolean;\n\n\tconstructor(message: string, statusCode = 500, retryable = false) {\n\t\tsuper(message);\n\t\tthis.name = this.constructor.name;\n\t\tthis.statusCode = statusCode;\n\t\tthis.retryable = retryable;\n\t\tError.captureStackTrace?.(this, this.constructor);\n\t}\n\n\ttoJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\terror: this.name,\n\t\t\tmessage: this.message,\n\t\t\tstatusCode: this.statusCode,\n\t\t\tretryable: this.retryable,\n\t\t};\n\t}\n}\n\n/** A file/directory path not found within a tree (404). */\nexport class GitPathNotFoundError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 404, false);\n\t}\n}\n\n/** A git object not found (404). */\nexport class GitObjectNotFoundError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 404, false);\n\t}\n}\n\n/** A ref (branch/tag) not found (404). */\nexport class GitRefNotFoundError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 404, false);\n\t}\n}\n\n/** The repository itself not found (404). */\nexport class GitRepositoryNotFoundError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 404, false);\n\t}\n}\n\nexport interface MergeConflictDetail {\n\tfile: string;\n\tbaseLines?: string[];\n\tsourceLines?: string[];\n\ttargetLines?: string[];\n}\n\n/** A merge conflict (409), carrying per-file conflict detail. */\nexport class GitConflictError extends GitError {\n\tconflicts: MergeConflictDetail[];\n\n\tconstructor(message: string, conflicts: MergeConflictDetail[] = []) {\n\t\tsuper(message, 409, false);\n\t\tthis.conflicts = conflicts;\n\t}\n\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn { ...super.toJSON(), conflicts: this.conflicts };\n\t}\n}\n\n/** Authentication failed (401). */\nexport class GitAuthenticationError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 401, false);\n\t}\n}\n\n/** Authorization failed (403). */\nexport class GitAuthorizationError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 403, false);\n\t}\n}\n\n/** Too many failed attempts (429). */\nexport class GitRateLimitError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 429, false);\n\t}\n}\n\n/** Malformed request (400). */\nexport class GitInvalidRequestError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 400, false);\n\t}\n}\n\n/** Git wire-protocol violation (400). */\nexport class GitProtocolError extends GitError {\n\tconstructor(message: string) {\n\t\tsuper(message, 400, false);\n\t}\n}\n\n/**\n * Map any error to an HTTP response shape. 401s carry the WWW-Authenticate\n * header git clients need before they will prompt for credentials. Non-GitError\n * failures are masked as opaque 500s — internal messages don't leak.\n */\nexport function formatErrorResponse(error: unknown): {\n\tstatus: number;\n\tbody: Record<string, unknown>;\n\theaders?: Record<string, string>;\n} {\n\tif (error instanceof GitError) {\n\t\treturn {\n\t\t\tstatus: error.statusCode,\n\t\t\tbody: error.toJSON(),\n\t\t\theaders:\n\t\t\t\terror.statusCode === 401\n\t\t\t\t\t? { \"WWW-Authenticate\": 'Basic realm=\"Git Repository\"' }\n\t\t\t\t\t: undefined,\n\t\t};\n\t}\n\n\tif (error instanceof Error) {\n\t\treturn {\n\t\t\tstatus: 500,\n\t\t\tbody: {\n\t\t\t\terror: \"InternalServerError\",\n\t\t\t\tmessage: \"An internal error occurred\",\n\t\t\t\tretryable: true,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tstatus: 500,\n\t\tbody: {\n\t\t\terror: \"UnknownError\",\n\t\t\tmessage: \"An unknown error occurred\",\n\t\t\tretryable: true,\n\t\t},\n\t};\n}\n","/**\n * Git ref-name validation, mirroring isomorphic-git's own internal `isValidRef`\n * character-class rules (the check `git.branch` and top-level `git.writeRef`\n * run before touching disk).\n *\n * Several of isomorphic-git's OTHER ref-touching primitives — `git.commit`,\n * `git.merge`, `git.deleteBranch`, and top-level `git.resolveRef`/\n * `git.deleteRef` — do NOT run this check internally: they resolve straight\n * through `fs.write`/`fs.rm(join(gitdir, ref))` with no jail to the gitdir.\n * On a shared-storage server (many repos under one prefix or base directory),\n * every branch/ref name that originates from request input must be validated\n * against these predicates before it reaches any of those primitives —\n * otherwise a `\"../\"`-laden name lets a caller with write access to any single\n * repo read, corrupt, or delete another repo's ref/object files.\n */\n\nconst BAD_REF_COMPONENT =\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: control chars are exactly what git's own ref-name rules reject — this needs to match the same range.\n\t/(^|[/.])([/.]|$)|^@$|@\\{|[\\x00-\\x20\\x7f~^:?*[\\\\]|\\.lock(\\/|$)/;\n\nconst FULL_SHA_RE = /^[0-9a-f]{40}$/i;\n\n/** Validates a fully-qualified ref (must start with refs/heads/ or refs/tags/). */\nexport function isSafeFullRefName(ref: string): boolean {\n\tif (!ref.startsWith(\"refs/heads/\") && !ref.startsWith(\"refs/tags/\")) {\n\t\treturn false;\n\t}\n\treturn !BAD_REF_COMPONENT.test(ref);\n}\n\n/**\n * Validates a bare branch name (no refs/ prefix). Rejects anything that looks\n * like a full ref path — a name of `\"refs/heads/x\"` would otherwise sail\n * through unprefixed at call sites that build `refs/heads/${name}` themselves\n * (doubling the prefix into something that still resolves), or be used as-is\n * at call sites that pass a name already containing `\"refs/\"` straight\n * through. Also rejects 40-hex SHA-shaped values so a stored branch name can\n * never be ambiguous with a commit SHA at write time; use\n * {@link isSafeRefName} on read paths that accept both shapes.\n */\nexport function isSafeBranchName(name: string): boolean {\n\tif (!name || name.startsWith(\"refs/\") || name === \"HEAD\") return false;\n\tif (FULL_SHA_RE.test(name)) return false;\n\treturn !BAD_REF_COMPONENT.test(name);\n}\n\n/** True for a full 40-hex-char commit SHA — the shape {@link isSafeBranchName} deliberately rejects. */\nexport function isFullSha(value: string): boolean {\n\treturn FULL_SHA_RE.test(value);\n}\n\n/**\n * Validates a \"ref\" field that may name either a branch or a commit SHA it's\n * pinned to — the shape read-path route params take (permalinks, raw links).\n * Both shapes still go through the traversal check.\n */\nexport function isSafeRefName(value: string): boolean {\n\treturn isSafeBranchName(value) || isFullSha(value);\n}\n\n/**\n * Validates a repo-relative file path from request input: relative, no `..`\n * segments, no `.git/` prefix, no null bytes. Use this anywhere a path\n * segment comes straight off a URL or form field rather than re-deriving the\n * checks ad hoc.\n */\nexport function isSafeRepoPath(p: string): boolean {\n\tif (p.startsWith(\"/\")) return false;\n\tif (p.split(\"/\").some((segment) => segment === \"..\")) return false;\n\tif (/^\\.git(\\/|$)/i.test(p)) return false;\n\tif (p.includes(\"\\0\")) return false;\n\treturn true;\n}\n\n/**\n * Qualify a bare branch name to `refs/heads/<name>` before handing it to\n * isomorphic-git. `resolveRef`/`expand` try several candidate paths in\n * sequence for a bare name — `ref`, `refs/ref`, `refs/tags/ref`,\n * `refs/heads/ref`, … — missing (and, against object storage, paying a real\n * round trip for) the first three every time. For a branch-only ref model,\n * skip straight to the winner. Left untouched: already-qualified refs,\n * `\"HEAD\"` (its own first candidate, already optimal), and 40-hex oids\n * (resolved locally by isomorphic-git with no I/O at all).\n */\nexport function qualifyBranchRef(ref: string): string {\n\tif (ref.startsWith(\"refs/\") || ref === \"HEAD\" || FULL_SHA_RE.test(ref)) {\n\t\treturn ref;\n\t}\n\treturn `refs/heads/${ref}`;\n}\n"],"mappings":"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","names":[]}
|