git-fs-s3 0.3.5

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.
@@ -0,0 +1,373 @@
1
+ import { O as ObjectStore, G as GitFsClient, a as GitFsOptions, b as ObjectStat, L as ListOptions, c as ListResult } from './types-QgIkUR_q.js';
2
+ export { S as Stat } from './types-QgIkUR_q.js';
3
+
4
+ interface CacheOptions {
5
+ /** Maximum bytes of object data held in memory. Default 50 MiB. */
6
+ maxBytes?: number;
7
+ /**
8
+ * Largest single entry admitted to the cache. Defaults to a tenth of
9
+ * `maxBytes` so one huge packfile cannot evict the whole working set.
10
+ */
11
+ maxEntryBytes?: number;
12
+ /** Entry time-to-live in milliseconds. Default 60 000. */
13
+ ttlMs?: number;
14
+ /**
15
+ * Override the TTL for a specific key (get/head) or list prefix (list),
16
+ * in milliseconds. Return `undefined` to fall back to `ttlMs`. Git refs
17
+ * (`refs/heads/<branch>`, `HEAD`) are mutable — the same key's value
18
+ * changes on every push — unlike content-addressed object keys, which
19
+ * never change for a given key and are safe to cache for the full
20
+ * `ttlMs`. Without this, a long `ttlMs` tuned for objects also caches
21
+ * ref reads that long, so a warm process can keep serving a
22
+ * pre-push ref value for the rest of that TTL even though nothing
23
+ * changed *this* process's own cache (see `invalidate`) — it just never
24
+ * knew to. Give ref-like keys a short override (a few seconds) instead:
25
+ * a ref read is one small object, so re-reading it far more often than
26
+ * `ttlMs` is cheap, and every read downstream of a fresh ref (tree,
27
+ * commit, blob — all keyed by the sha it resolves to) still gets the
28
+ * full-length cache/coalescing benefit.
29
+ */
30
+ ttlForKey?: (key: string) => number | undefined;
31
+ /**
32
+ * Also cache "key does not exist" results. Loose-object probes on packed
33
+ * repositories are almost always misses, so this saves many round trips —
34
+ * but only enable it when a single process is the only writer, otherwise
35
+ * another instance's push can be masked for up to `ttlMs`.
36
+ */
37
+ cacheMisses?: boolean;
38
+ /**
39
+ * Also cache `list()` results (directory listings and `limit: 1`
40
+ * existence probes). Writes through this store keep cached listings
41
+ * consistent; after writing to the backend by any other means, call
42
+ * `invalidate()` with the affected prefix. Default false.
43
+ */
44
+ cacheLists?: boolean;
45
+ /**
46
+ * Collapse concurrent `get`/`head`/`list` calls for the same key into a
47
+ * single backend request. Default true.
48
+ */
49
+ coalesce?: boolean;
50
+ /** Called when a read is answered from cache. */
51
+ onHit?: (key: string) => void;
52
+ /** Called when a read has to go to the backing store. */
53
+ onMiss?: (key: string) => void;
54
+ }
55
+ /** An {@link ObjectStore} wrapper that also supports explicit invalidation. */
56
+ interface CachedObjectStore extends ObjectStore {
57
+ /**
58
+ * Drop every cached entry — contents, misses, and listings — whose key
59
+ * falls under `prefix` (exact keys included). Call this after the backing
60
+ * store was modified by something other than this wrapper.
61
+ */
62
+ invalidate(prefix: string): void;
63
+ }
64
+ /**
65
+ * Wrap an {@link ObjectStore} with an in-process LRU read cache.
66
+ *
67
+ * Git object keys are content-addressed and therefore immutable, which makes
68
+ * them ideal cache entries; mutable keys (refs, packed-refs) are bounded by
69
+ * `ttlMs`. Writes and deletes through this wrapper invalidate their key and
70
+ * any cached listings they affect — with one asymmetry: a non-empty `limit: 1`
71
+ * probe (a "directory exists" answer) survives writes underneath it, because
72
+ * adding a key below a prefix cannot make that prefix stop existing, while
73
+ * empty probes and full listings are always dropped.
74
+ */
75
+ declare function createCachedStore(store: ObjectStore, options?: CacheOptions): CachedObjectStore;
76
+
77
+ /**
78
+ * Edge-compatible utilities replacing node:crypto, node:zlib, and Buffer.
79
+ *
80
+ * Every function here uses only Web APIs (SubtleCrypto, CompressionStream,
81
+ * TextEncoder/TextDecoder) — no Node built-ins. They work on Cloudflare
82
+ * Workers, Vercel Edge, Deno Deploy, and Node >= 18.
83
+ */
84
+ /**
85
+ * Encode a UTF-8 string to bytes.
86
+ *
87
+ * Return type pinned to `Uint8Array<ArrayBuffer>` (not the bare `Uint8Array`,
88
+ * whose default type argument differs across TypeScript versions) so it's
89
+ * always assignable to Fetch API `BodyInit` regardless of a consumer's own
90
+ * TypeScript/lib version.
91
+ */
92
+ declare function encodeUtf8(data: string): Uint8Array<ArrayBuffer>;
93
+ /** Decode bytes as UTF-8. */
94
+ declare function decodeUtf8(data: Uint8Array): string;
95
+ /** Decode bytes as ASCII. */
96
+ declare function decodeAscii(data: Uint8Array): string;
97
+ /** Concatenate any number of Uint8Arrays into one. */
98
+ declare function concat(...parts: Uint8Array[]): Uint8Array<ArrayBuffer>;
99
+ /** Uint8Array → lowercase hex string. */
100
+ declare function toHex(data: Uint8Array): string;
101
+ /** Uint8Array → base64 string. */
102
+ declare function toBase64(data: Uint8Array): string;
103
+ /** Hex string → Uint8Array. */
104
+ declare function fromHex(hex: string): Uint8Array<ArrayBuffer>;
105
+ /** SHA-1 hash via Web Crypto API. Returns a hex string. */
106
+ declare function sha1(data: Uint8Array | string): Promise<string>;
107
+ /**
108
+ * Deflate compress via the CompressionStream Web API.
109
+ * Falls back to throwing if CompressionStream is unavailable (very old runtimes).
110
+ */
111
+ declare function deflate(data: Uint8Array): Promise<Uint8Array<ArrayBuffer>>;
112
+ /** Check if a Uint8Array contains a null byte. */
113
+ declare function hasNullByte(data: Uint8Array): boolean;
114
+ /**
115
+ * Read a blob as text or binary metadata — the edge-compatible replacement
116
+ * for the `Buffer.from(blob)` pattern used throughout diff.ts and history.ts.
117
+ */
118
+ declare function readBlobContent(blob: Uint8Array): {
119
+ isBinary: boolean;
120
+ text: string;
121
+ bytes: Uint8Array;
122
+ };
123
+
124
+ /**
125
+ * Node-style filesystem error carrying a `code` property, which is what
126
+ * isomorphic-git inspects to distinguish "file not found" from real failures.
127
+ */
128
+ declare class FsError extends Error {
129
+ readonly code: string;
130
+ readonly syscall: string;
131
+ readonly path: string;
132
+ constructor(code: string, syscall: string, path: string);
133
+ }
134
+
135
+ /**
136
+ * Git-server error types carrying an HTTP status and a retryability flag, so
137
+ * transport layers can map internal failures to responses without inspecting
138
+ * messages. Extend {@link GitError} for app-specific cases (storage backends,
139
+ * quota, …) and {@link formatErrorResponse} keeps working for them.
140
+ */
141
+ declare class GitError extends Error {
142
+ statusCode: number;
143
+ retryable: boolean;
144
+ constructor(message: string, statusCode?: number, retryable?: boolean);
145
+ toJSON(): Record<string, unknown>;
146
+ }
147
+ /** A file/directory path not found within a tree (404). */
148
+ declare class GitPathNotFoundError extends GitError {
149
+ constructor(message: string);
150
+ }
151
+ /** A git object not found (404). */
152
+ declare class GitObjectNotFoundError extends GitError {
153
+ constructor(message: string);
154
+ }
155
+ /** A ref (branch/tag) not found (404). */
156
+ declare class GitRefNotFoundError extends GitError {
157
+ constructor(message: string);
158
+ }
159
+ /** The repository itself not found (404). */
160
+ declare class GitRepositoryNotFoundError extends GitError {
161
+ constructor(message: string);
162
+ }
163
+ interface MergeConflictDetail {
164
+ file: string;
165
+ baseLines?: string[];
166
+ sourceLines?: string[];
167
+ targetLines?: string[];
168
+ }
169
+ /** A merge conflict (409), carrying per-file conflict detail. */
170
+ declare class GitConflictError extends GitError {
171
+ conflicts: MergeConflictDetail[];
172
+ constructor(message: string, conflicts?: MergeConflictDetail[]);
173
+ toJSON(): Record<string, unknown>;
174
+ }
175
+ /** Authentication failed (401). */
176
+ declare class GitAuthenticationError extends GitError {
177
+ constructor(message: string);
178
+ }
179
+ /** Authorization failed (403). */
180
+ declare class GitAuthorizationError extends GitError {
181
+ constructor(message: string);
182
+ }
183
+ /** Too many failed attempts (429). */
184
+ declare class GitRateLimitError extends GitError {
185
+ constructor(message: string);
186
+ }
187
+ /** Malformed request (400). */
188
+ declare class GitInvalidRequestError extends GitError {
189
+ constructor(message: string);
190
+ }
191
+ /** Git wire-protocol violation (400). */
192
+ declare class GitProtocolError extends GitError {
193
+ constructor(message: string);
194
+ }
195
+ /**
196
+ * Map any error to an HTTP response shape. 401s carry the WWW-Authenticate
197
+ * header git clients need before they will prompt for credentials. Non-GitError
198
+ * failures are masked as opaque 500s — internal messages don't leak.
199
+ */
200
+ declare function formatErrorResponse(error: unknown): {
201
+ status: number;
202
+ body: Record<string, unknown>;
203
+ headers?: Record<string, string>;
204
+ };
205
+
206
+ /**
207
+ * The filesystem returned by {@link createGitFs}: the isomorphic-git client
208
+ * plus git-aware maintenance hooks.
209
+ */
210
+ interface GitFs extends GitFsClient {
211
+ /**
212
+ * Probe, with one bounded list, whether `gitdir` contains any loose
213
+ * objects, and remember the answer. This is the only way a loose-object
214
+ * hint is ever created; call it before full-history walks (commit logs,
215
+ * reachability traversals) so fully packed repositories skip every
216
+ * guaranteed-miss loose-object read. A later loose write flips the hint
217
+ * back, so it cannot go stale mid-push.
218
+ */
219
+ detectLooseObjects(gitdir: string): Promise<void>;
220
+ /**
221
+ * Warm the cache with every pack file under `gitdir` in parallel (plus
222
+ * the loose-object hint) before a sequential history walk. Skipped when
223
+ * the pack directory holds more than `maxPacks * 2` entries — warming
224
+ * only helps when the cache budget actually fits the packs.
225
+ */
226
+ prefetchPacks(gitdir: string, options?: {
227
+ maxPacks?: number;
228
+ }): Promise<void>;
229
+ /**
230
+ * Clear fs-level state (loose-object hints) under `pathPrefix`, and
231
+ * forward to the store's `invalidate` when it has one. Call after the
232
+ * backing store was modified by something other than this fs.
233
+ */
234
+ invalidate(pathPrefix: string): void;
235
+ }
236
+ /**
237
+ * Create a promise-based filesystem client for isomorphic-git backed by an
238
+ * {@link ObjectStore}.
239
+ *
240
+ * Semantics:
241
+ * - Directories are implicit, as in object storage: `mkdir` is a no-op and a
242
+ * directory "exists" whenever at least one key lives under its prefix.
243
+ * - Symbolic links are not supported (`readlink`/`symlink` throw). Bare
244
+ * repositories never contain them.
245
+ * - Designed for bare, server-side repositories (`git.init({bare: true})`,
246
+ * plumbing commands, ref updates). Worktree checkouts belong on a real disk.
247
+ */
248
+ declare function createGitFs(store: ObjectStore, options?: GitFsOptions): GitFs;
249
+
250
+ /**
251
+ * Git ref-name validation, mirroring isomorphic-git's own internal `isValidRef`
252
+ * character-class rules (the check `git.branch` and top-level `git.writeRef`
253
+ * run before touching disk).
254
+ *
255
+ * Several of isomorphic-git's OTHER ref-touching primitives — `git.commit`,
256
+ * `git.merge`, `git.deleteBranch`, and top-level `git.resolveRef`/
257
+ * `git.deleteRef` — do NOT run this check internally: they resolve straight
258
+ * through `fs.write`/`fs.rm(join(gitdir, ref))` with no jail to the gitdir.
259
+ * On a shared-storage server (many repos under one prefix or base directory),
260
+ * every branch/ref name that originates from request input must be validated
261
+ * against these predicates before it reaches any of those primitives —
262
+ * otherwise a `"../"`-laden name lets a caller with write access to any single
263
+ * repo read, corrupt, or delete another repo's ref/object files.
264
+ */
265
+ /** Validates a fully-qualified ref (must start with refs/heads/ or refs/tags/). */
266
+ declare function isSafeFullRefName(ref: string): boolean;
267
+ /**
268
+ * Validates a bare branch name (no refs/ prefix). Rejects anything that looks
269
+ * like a full ref path — a name of `"refs/heads/x"` would otherwise sail
270
+ * through unprefixed at call sites that build `refs/heads/${name}` themselves
271
+ * (doubling the prefix into something that still resolves), or be used as-is
272
+ * at call sites that pass a name already containing `"refs/"` straight
273
+ * through. Also rejects 40-hex SHA-shaped values so a stored branch name can
274
+ * never be ambiguous with a commit SHA at write time; use
275
+ * {@link isSafeRefName} on read paths that accept both shapes.
276
+ */
277
+ declare function isSafeBranchName(name: string): boolean;
278
+ /** True for a full 40-hex-char commit SHA — the shape {@link isSafeBranchName} deliberately rejects. */
279
+ declare function isFullSha(value: string): boolean;
280
+ /**
281
+ * Validates a "ref" field that may name either a branch or a commit SHA it's
282
+ * pinned to — the shape read-path route params take (permalinks, raw links).
283
+ * Both shapes still go through the traversal check.
284
+ */
285
+ declare function isSafeRefName(value: string): boolean;
286
+ /**
287
+ * Validates a repo-relative file path from request input: relative, no `..`
288
+ * segments, no `.git/` prefix, no null bytes. Use this anywhere a path
289
+ * segment comes straight off a URL or form field rather than re-deriving the
290
+ * checks ad hoc.
291
+ */
292
+ declare function isSafeRepoPath(p: string): boolean;
293
+ /**
294
+ * Qualify a bare branch name to `refs/heads/<name>` before handing it to
295
+ * isomorphic-git. `resolveRef`/`expand` try several candidate paths in
296
+ * sequence for a bare name — `ref`, `refs/ref`, `refs/tags/ref`,
297
+ * `refs/heads/ref`, … — missing (and, against object storage, paying a real
298
+ * round trip for) the first three every time. For a branch-only ref model,
299
+ * skip straight to the winner. Left untouched: already-qualified refs,
300
+ * `"HEAD"` (its own first candidate, already optimal), and 40-hex oids
301
+ * (resolved locally by isomorphic-git with no I/O at all).
302
+ */
303
+ declare function qualifyBranchRef(ref: string): string;
304
+
305
+ /** Options accepted by {@link createRetryStore}. */
306
+ interface RetryOptions {
307
+ /** Retries after the first attempt (total attempts = retries + 1). Default 3. */
308
+ retries?: number;
309
+ /** Backoff base delay in milliseconds, doubled each attempt. Default 100. */
310
+ initialDelayMs?: number;
311
+ /** Upper bound for the backoff base delay. Default 5000. */
312
+ maxDelayMs?: number;
313
+ /** Random jitter added to each delay, as a fraction of it. Default 0.3. */
314
+ jitter?: number;
315
+ /**
316
+ * Decide whether an error is worth retrying. The store contract maps
317
+ * "not found" to `null` rather than throwing, so any thrown error is a
318
+ * genuine failure; the default retries network faults, throttling, and
319
+ * HTTP 5xx responses.
320
+ */
321
+ isRetryable?: (error: unknown) => boolean;
322
+ /**
323
+ * Circuit breaker configuration, or `false` to disable. After `threshold`
324
+ * consecutive failures the store fails fast for `resetMs`, then lets one
325
+ * request probe the backend again. Defaults: 5 failures, 30 000 ms.
326
+ */
327
+ breaker?: false | {
328
+ threshold?: number;
329
+ resetMs?: number;
330
+ };
331
+ /** Called before each retry sleep; useful for logging/metrics. */
332
+ onRetry?: (info: {
333
+ key: string;
334
+ op: string;
335
+ attempt: number;
336
+ delayMs: number;
337
+ }) => void;
338
+ }
339
+ /**
340
+ * Thrown instead of calling the backend while the circuit breaker is open.
341
+ * Carries `code: "EUNAVAILABLE"` so callers can map it to a 503.
342
+ */
343
+ declare class CircuitOpenError extends Error {
344
+ readonly code = "EUNAVAILABLE";
345
+ constructor();
346
+ }
347
+ /**
348
+ * Wrap an {@link ObjectStore} with retries (exponential backoff + jitter) and
349
+ * an optional per-instance circuit breaker.
350
+ *
351
+ * Place this decorator closest to the network store, underneath any cache:
352
+ * the cache then never stores transient failures, and callers coalesced onto
353
+ * one request share a single retried attempt.
354
+ */
355
+ declare function createRetryStore(store: ObjectStore, options?: RetryOptions): ObjectStore;
356
+
357
+ /**
358
+ * In-memory {@link ObjectStore}. Useful for tests, examples, and ephemeral
359
+ * repositories; also the reference implementation for the list/delimiter
360
+ * semantics other stores must match.
361
+ */
362
+ declare class MemoryObjectStore implements ObjectStore {
363
+ private readonly objects;
364
+ get(key: string): Promise<Uint8Array | null>;
365
+ put(key: string, data: Uint8Array): Promise<void>;
366
+ delete(key: string): Promise<void>;
367
+ head(key: string): Promise<ObjectStat | null>;
368
+ list(prefix: string, options?: ListOptions): Promise<ListResult>;
369
+ /** Number of stored objects (test convenience, not part of ObjectStore). */
370
+ get size(): number;
371
+ }
372
+
373
+ export { type CacheOptions, type CachedObjectStore, CircuitOpenError, FsError, GitAuthenticationError, GitAuthorizationError, GitConflictError, GitError, type GitFs, GitFsClient, GitFsOptions, GitInvalidRequestError, GitObjectNotFoundError, GitPathNotFoundError, GitProtocolError, GitRateLimitError, GitRefNotFoundError, GitRepositoryNotFoundError, ListOptions, ListResult, MemoryObjectStore, type MergeConflictDetail, ObjectStat, ObjectStore, type RetryOptions, concat, createCachedStore, createGitFs, createRetryStore, decodeAscii, decodeUtf8, deflate, encodeUtf8, formatErrorResponse, fromHex, hasNullByte, isFullSha, isSafeBranchName, isSafeFullRefName, isSafeRefName, isSafeRepoPath, qualifyBranchRef, readBlobContent, sha1, toBase64, toHex };