just-git 1.4.2 → 1.5.1

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.
@@ -126,6 +126,20 @@ interface Storage {
126
126
  * The adapter uses this for compare-and-swap with symref resolution.
127
127
  */
128
128
  atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => MaybeAsync<T>): MaybeAsync<T>;
129
+ /**
130
+ * Record a fork relationship. `targetId` becomes a fork of `sourceId`.
131
+ * The adapter layer handles ref copying and root resolution.
132
+ */
133
+ forkRepo?(sourceId: string, targetId: string): MaybeAsync<void>;
134
+ /**
135
+ * Get the parent (root) repo ID for a fork, or `null` if the repo
136
+ * is not a fork.
137
+ */
138
+ getForkParent?(repoId: string): MaybeAsync<string | null>;
139
+ /**
140
+ * List all direct fork IDs of a repo.
141
+ */
142
+ listForks?(repoId: string): MaybeAsync<string[]>;
129
143
  }
130
144
 
131
145
  /** Options for {@link gcRepo}. */
@@ -336,6 +350,47 @@ interface GitServerConfig<A = Auth> {
336
350
  /** Delta window size (default 10). Smaller = faster, worse compression ratio. */
337
351
  deltaWindow?: number;
338
352
  };
353
+ /**
354
+ * Safety limits for incoming receive-pack requests.
355
+ *
356
+ * These bounds are enforced on buffered HTTP bodies and on pack ingestion
357
+ * for both HTTP and SSH push paths.
358
+ */
359
+ receiveLimits?: {
360
+ /** Maximum compressed/raw HTTP request body size in bytes. */
361
+ maxRequestBytes?: number;
362
+ /**
363
+ * Maximum decompressed HTTP request body size in bytes.
364
+ *
365
+ * Guards against gzip decompression bombs (`Content-Encoding: gzip`).
366
+ * Only applies to HTTP — SSH has no application-layer compression,
367
+ * so `maxRequestBytes` alone is sufficient there.
368
+ */
369
+ maxInflatedBytes?: number;
370
+ /** Maximum pack payload size in bytes. */
371
+ maxPackBytes?: number;
372
+ /** Maximum number of objects declared by a received pack. */
373
+ maxPackObjects?: number;
374
+ };
375
+ /**
376
+ * Safety limits for incoming upload-pack (fetch/clone) requests.
377
+ *
378
+ * Upload-pack request bodies contain only pkt-line want/have lists,
379
+ * so they are much smaller than receive-pack bodies. Defaults are
380
+ * tighter than receiveLimits (10 MB raw, 20 MB inflated).
381
+ */
382
+ fetchLimits?: {
383
+ /** Maximum compressed/raw HTTP request body size in bytes. */
384
+ maxRequestBytes?: number;
385
+ /**
386
+ * Maximum decompressed HTTP request body size in bytes.
387
+ *
388
+ * Guards against gzip decompression bombs (`Content-Encoding: gzip`).
389
+ * Only applies to HTTP — SSH has no application-layer compression,
390
+ * so `maxRequestBytes` alone is sufficient there.
391
+ */
392
+ maxInflatedBytes?: number;
393
+ };
339
394
  /**
340
395
  * Called when the server catches an unhandled error.
341
396
  *
@@ -474,8 +529,24 @@ interface GitServer<A = Auth> {
474
529
  repo(id: string): Promise<GitRepo | null>;
475
530
  /** Get a repo by ID, or throw if it doesn't exist. */
476
531
  requireRepo(id: string): Promise<GitRepo>;
477
- /** Delete a repo and all its data. */
532
+ /** Delete a repo and all its data. Throws if the repo has active forks. */
478
533
  deleteRepo(id: string): Promise<void>;
534
+ /**
535
+ * Fork an existing repo. Copies refs from the source repo to the
536
+ * target. The fork shares the source's object pool — object reads
537
+ * fall through to the root repo's partition, while writes go to
538
+ * the fork's own partition.
539
+ *
540
+ * Forking a fork resolves to the root: `forkRepo("fork-of-A", "B")`
541
+ * records B as a fork of A's root, not of the intermediate fork.
542
+ *
543
+ * Requires a storage backend that implements fork methods.
544
+ *
545
+ * @throws If the source doesn't exist, the target already exists,
546
+ * the storage backend doesn't support forks, or the server is
547
+ * shutting down.
548
+ */
549
+ forkRepo(sourceId: string, targetId: string, options?: CreateRepoOptions): Promise<GitRepo>;
479
550
  /**
480
551
  * Remove unreachable objects from a repo's storage.
481
552
  *
@@ -873,37 +944,52 @@ interface UploadPackOptions {
873
944
  /** Delta window size (default 10). Ignored when noDelta is true. */
874
945
  deltaWindow?: number;
875
946
  }
947
+ interface AuthorizedFetchSet {
948
+ allowedRefHashes: Map<string, string>;
949
+ allowedWantHashes: Set<string>;
950
+ }
951
+ interface ReceivePackLimitOptions {
952
+ maxPackBytes?: number;
953
+ maxPackObjects?: number;
954
+ }
876
955
  /**
877
956
  * Handle a `POST /git-upload-pack` request.
878
957
  *
879
958
  * Returns `Uint8Array` for buffered responses (cache hits, deltified packs)
880
959
  * or `ReadableStream<Uint8Array>` for streaming no-delta responses.
881
960
  */
882
- declare function handleUploadPack(repo: GitRepo, requestBody: Uint8Array, options?: UploadPackOptions): Promise<Uint8Array | ReadableStream<Uint8Array>>;
961
+ declare function handleUploadPack(repo: GitRepo, requestBody: Uint8Array, options?: UploadPackOptions & {
962
+ authorizedFetchSet?: AuthorizedFetchSet;
963
+ }): Promise<Uint8Array | ReadableStream<Uint8Array> | Rejection>;
883
964
  interface ReceivePackResult {
884
965
  updates: RefUpdate[];
885
966
  unpackOk: boolean;
886
967
  capabilities: string[];
887
968
  /** Whether the request body contained a valid pkt-line flush packet. */
888
969
  sawFlush: boolean;
970
+ /** Hashes of objects ingested from the pack (for rollback on hook rejection). Only populated when the object store supports deferred ingestion (server-backed stores). Undefined for VFS-backed stores. */
971
+ ingestedHashes?: string[];
889
972
  }
890
973
  /**
891
974
  * Ingest a receive-pack request: parse commands, ingest the packfile,
892
975
  * and compute enriched RefUpdate objects. Does NOT apply ref updates —
893
976
  * call `applyReceivePack` to run hooks and apply refs.
977
+ *
978
+ * Objects are persisted immediately (needed by `buildRefUpdates` for
979
+ * ancestry checks). If hooks later reject the push, `applyReceivePack`
980
+ * rolls back the ingested objects.
894
981
  */
895
- declare function ingestReceivePack(repo: GitRepo, requestBody: Uint8Array): Promise<ReceivePackResult>;
982
+ declare function ingestReceivePack(repo: GitRepo, requestBody: Uint8Array, limits?: ReceivePackLimitOptions): Promise<ReceivePackResult>;
896
983
  /**
897
984
  * Streaming variant of `ingestReceivePack`. Accepts pre-parsed push
898
- * commands and a raw pack byte stream. Uses `readPackStreaming`
899
- * `ingestPackStream` so pack bytes are consumed incrementally without
900
- * buffering the entire pack in memory.
985
+ * commands and a raw pack byte stream. Uses `readPackStreaming` for
986
+ * incremental consumption.
901
987
  *
902
988
  * The HTTP handler continues using `ingestReceivePack` (runtime buffers
903
989
  * POST bodies anyway). The SSH handler calls this directly after parsing
904
990
  * pkt-line commands.
905
991
  */
906
- declare function ingestReceivePackFromStream(repo: GitRepo, commands: PushCommand[], capabilities: string[], packStream: AsyncIterable<Uint8Array>, sawFlush?: boolean): Promise<ReceivePackResult>;
992
+ declare function ingestReceivePackFromStream(repo: GitRepo, commands: PushCommand[], capabilities: string[], packStream: AsyncIterable<Uint8Array>, sawFlush?: boolean, limits?: ReceivePackLimitOptions): Promise<ReceivePackResult>;
907
993
  /**
908
994
  * Apply ref updates with CAS protection only — no hooks.
909
995
  *
@@ -949,7 +1035,9 @@ declare function handleLsRefs<A>(repo: GitRepo, repoId: string, args: string[],
949
1035
  * enumeration and pack building via the shared pipeline, then
950
1036
  * builds a v2 section-based response.
951
1037
  */
952
- declare function handleV2Fetch(repo: GitRepo, args: string[], options?: UploadPackOptions): Promise<Uint8Array | ReadableStream<Uint8Array>>;
1038
+ declare function handleV2Fetch(repo: GitRepo, args: string[], options?: UploadPackOptions & {
1039
+ authorizedFetchSet?: AuthorizedFetchSet;
1040
+ }): Promise<Uint8Array | ReadableStream<Uint8Array> | Rejection>;
953
1041
 
954
1042
  /**
955
1043
  * In-memory storage backend with multi-repo support.
@@ -967,6 +1055,7 @@ declare class MemoryStorage implements Storage {
967
1055
  private repos;
968
1056
  private objects;
969
1057
  private refs;
1058
+ private forks;
970
1059
  hasRepo(repoId: string): boolean;
971
1060
  insertRepo(repoId: string): void;
972
1061
  deleteRepo(repoId: string): void;
@@ -986,6 +1075,9 @@ declare class MemoryStorage implements Storage {
986
1075
  removeRef(repoId: string, name: string): void;
987
1076
  listRefs(repoId: string, prefix?: string): RawRefEntry[];
988
1077
  atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => T): T;
1078
+ forkRepo(sourceId: string, targetId: string): void;
1079
+ getForkParent(repoId: string): string | null;
1080
+ listForks(repoId: string): string[];
989
1081
  /** List all created repo IDs. Convenience for tests and debugging. */
990
1082
  repoIds(): string[];
991
1083
  private getObjMap;
@@ -1037,6 +1129,9 @@ declare class BunSqliteStorage implements Storage {
1037
1129
  removeRef(repoId: string, name: string): void;
1038
1130
  listRefs(repoId: string, prefix?: string): RawRefEntry[];
1039
1131
  atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => T): T;
1132
+ forkRepo(sourceId: string, targetId: string): void;
1133
+ getForkParent(repoId: string): string | null;
1134
+ listForks(repoId: string): string[];
1040
1135
  }
1041
1136
 
1042
1137
  /** Minimal prepared statement interface matching `better-sqlite3`. */
@@ -1084,6 +1179,9 @@ declare class BetterSqlite3Storage implements Storage {
1084
1179
  removeRef(repoId: string, name: string): void;
1085
1180
  listRefs(repoId: string, prefix?: string): RawRefEntry[];
1086
1181
  atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => T): T;
1182
+ forkRepo(sourceId: string, targetId: string): void;
1183
+ getForkParent(repoId: string): string | null;
1184
+ listForks(repoId: string): string[];
1087
1185
  }
1088
1186
 
1089
1187
  /** Minimal pool interface matching the `pg` package's `Pool` class. */
@@ -1134,6 +1232,9 @@ declare class PgStorage implements Storage {
1134
1232
  removeRef(repoId: string, name: string): Promise<void>;
1135
1233
  listRefs(repoId: string, prefix?: string): Promise<RawRefEntry[]>;
1136
1234
  atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => Promise<T> | T): Promise<T>;
1235
+ forkRepo(sourceId: string, targetId: string): Promise<void>;
1236
+ getForkParent(repoId: string): Promise<string | null>;
1237
+ listForks(repoId: string): Promise<string[]>;
1137
1238
  }
1138
1239
 
1139
1240
  export { type AdvertiseRefsEvent, type AdvertiseResult, type ApplyReceivePackOptions, type Auth, type AuthProvider, type BetterSqlite3Database, BetterSqlite3Storage, type BunSqliteDatabase, BunSqliteStorage, type CreateRepoOptions, type GcOptions, type GcResult, GitRepo, type GitServer, type GitServerConfig, type MaybeAsync, MemoryStorage, type NodeHttpRequest, type NodeHttpResponse, type PgPool, PgStorage, type PostReceiveEvent, type PreReceiveEvent, type PushCommand, RawObject, type RawRefEntry, type ReceivePackResult, Ref, type RefAdvertisement, type RefOps, type RefResult, type RefUpdate, type RefUpdateRequest, type RefUpdateResult, Rejection, type ServerHooks, type ServerPolicy, type SshChannel, type SshSessionInfo, type Storage, type UpdateEvent, type V2CommandRequest, type V2FetchRequest, type V2FetchResponseOptions, type V2LsRefsRef, advertiseRefsWithHooks, applyCasRefUpdates, applyReceivePack, buildRefAdvertisementBytes, buildRefListBytes, buildRefListPktLines, buildV2CapabilityAdvertisement, buildV2CapabilityAdvertisementBytes, buildV2FetchResponse, buildV2LsRefsResponse, collectRefs, composeHooks, createServer, handleLsRefs, handleUploadPack, handleV2Fetch, ingestReceivePack, ingestReceivePackFromStream, parseV2CommandRequest, parseV2FetchArgs, resolveRefUpdates };