just-git 1.3.2 → 1.3.4
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/dist/index.js +1 -1
- package/dist/repo/index.js +7 -7
- package/dist/server/index.d.ts +145 -54
- package/dist/server/index.js +39 -38
- package/package.json +7 -1
package/dist/server/index.d.ts
CHANGED
|
@@ -87,6 +87,13 @@ interface Storage {
|
|
|
87
87
|
* `prefix` is at least 4 hex characters.
|
|
88
88
|
*/
|
|
89
89
|
findObjectsByPrefix(repoId: string, prefix: string): MaybeAsync<string[]>;
|
|
90
|
+
/** Return all object hashes stored for a repo. */
|
|
91
|
+
listObjectHashes(repoId: string): MaybeAsync<string[]>;
|
|
92
|
+
/**
|
|
93
|
+
* Delete specific objects by hash.
|
|
94
|
+
* Returns the number of objects actually deleted.
|
|
95
|
+
*/
|
|
96
|
+
deleteObjects(repoId: string, hashes: ReadonlyArray<string>): MaybeAsync<number>;
|
|
90
97
|
/**
|
|
91
98
|
* Read a single ref. Returns the stored {@link Ref} value (direct hash
|
|
92
99
|
* or symbolic pointer) without following symrefs — the adapter handles
|
|
@@ -112,6 +119,21 @@ interface Storage {
|
|
|
112
119
|
atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => MaybeAsync<T>): MaybeAsync<T>;
|
|
113
120
|
}
|
|
114
121
|
|
|
122
|
+
/** Options for {@link gcRepo}. */
|
|
123
|
+
interface GcOptions {
|
|
124
|
+
/** Report what would be deleted without actually deleting. Default: false. */
|
|
125
|
+
dryRun?: boolean;
|
|
126
|
+
}
|
|
127
|
+
/** Result of a {@link gcRepo} call. */
|
|
128
|
+
interface GcResult {
|
|
129
|
+
/** Number of unreachable objects deleted (or that would be deleted in dry-run mode). */
|
|
130
|
+
deleted: number;
|
|
131
|
+
/** Number of reachable objects retained. */
|
|
132
|
+
retained: number;
|
|
133
|
+
/** True if GC was aborted because refs changed during the walk (concurrent modification detected). */
|
|
134
|
+
aborted?: boolean;
|
|
135
|
+
}
|
|
136
|
+
|
|
115
137
|
/**
|
|
116
138
|
* Default session type, produced by the built-in session builder when
|
|
117
139
|
* no custom `session` config is provided to `createServer`.
|
|
@@ -130,10 +152,14 @@ interface Session {
|
|
|
130
152
|
* User-provided session builder that transforms raw transport input
|
|
131
153
|
* into a typed session object threaded through all hooks.
|
|
132
154
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
155
|
+
* Both properties are optional — provide only the transports you use.
|
|
156
|
+
* TypeScript infers `S` from whichever builders are present.
|
|
157
|
+
*
|
|
158
|
+
* If a transport is used at runtime but its builder is missing, the
|
|
159
|
+
* server returns an error (HTTP 501 / SSH exit 128).
|
|
135
160
|
*
|
|
136
161
|
* ```ts
|
|
162
|
+
* // HTTP-only — no need to provide ssh
|
|
137
163
|
* const server = createServer({
|
|
138
164
|
* storage: new BunSqliteStorage(db),
|
|
139
165
|
* session: {
|
|
@@ -141,10 +167,6 @@ interface Session {
|
|
|
141
167
|
* userId: parseJwt(req).sub,
|
|
142
168
|
* roles: parseJwt(req).roles,
|
|
143
169
|
* }),
|
|
144
|
-
* ssh: (info) => ({
|
|
145
|
-
* userId: info.username ?? "anonymous",
|
|
146
|
-
* roles: (info.metadata?.roles as string[]) ?? [],
|
|
147
|
-
* }),
|
|
148
170
|
* },
|
|
149
171
|
* hooks: {
|
|
150
172
|
* preReceive: ({ session }) => {
|
|
@@ -163,10 +185,17 @@ interface SessionBuilder<S> {
|
|
|
163
185
|
* Return `S` to proceed, or return a `Response` to short-circuit
|
|
164
186
|
* the request (e.g. 401 with `WWW-Authenticate` header). This is
|
|
165
187
|
* the primary mechanism for HTTP auth — no separate middleware needed.
|
|
188
|
+
*
|
|
189
|
+
* When omitted, HTTP requests receive a 501 response.
|
|
190
|
+
*/
|
|
191
|
+
http?: (request: Request) => S | Response | Promise<S | Response>;
|
|
192
|
+
/**
|
|
193
|
+
* Build a session from SSH session info.
|
|
194
|
+
*
|
|
195
|
+
* When omitted, SSH sessions receive exit code 128 with a
|
|
196
|
+
* diagnostic message.
|
|
166
197
|
*/
|
|
167
|
-
|
|
168
|
-
/** Build a session from SSH session info. */
|
|
169
|
-
ssh: (info: SshSessionInfo) => S | Promise<S>;
|
|
198
|
+
ssh?: (info: SshSessionInfo) => S | Promise<S>;
|
|
170
199
|
}
|
|
171
200
|
/** Information about the SSH session passed to `handleSession`. */
|
|
172
201
|
interface SshSessionInfo {
|
|
@@ -264,11 +293,12 @@ interface GitServerConfig<S = Session> {
|
|
|
264
293
|
*/
|
|
265
294
|
policy?: ServerPolicy;
|
|
266
295
|
/**
|
|
267
|
-
* Custom session builder.
|
|
268
|
-
*
|
|
269
|
-
*
|
|
296
|
+
* Custom session builder. Provide `http`, `ssh`, or both —
|
|
297
|
+
* the server calls whichever is present for that transport.
|
|
298
|
+
* If a transport is used but its builder is missing, the server
|
|
299
|
+
* returns an error (HTTP 501 / SSH exit 128).
|
|
270
300
|
*
|
|
271
|
-
* When omitted, the built-in `Session` type is used.
|
|
301
|
+
* When omitted entirely, the built-in `Session` type is used.
|
|
272
302
|
*/
|
|
273
303
|
session?: SessionBuilder<S>;
|
|
274
304
|
/** Base path prefix to strip from HTTP URLs (e.g. "/git"). */
|
|
@@ -304,7 +334,29 @@ interface GitServerConfig<S = Session> {
|
|
|
304
334
|
*/
|
|
305
335
|
onError?: false | ((err: unknown, session?: S) => void);
|
|
306
336
|
}
|
|
307
|
-
|
|
337
|
+
/**
|
|
338
|
+
* A ref update request for {@link GitServer.updateRefs}.
|
|
339
|
+
*
|
|
340
|
+
* In-process equivalent of a push command — updates a ref with CAS
|
|
341
|
+
* protection and hook enforcement, without transport overhead.
|
|
342
|
+
*/
|
|
343
|
+
interface RefUpdateRequest {
|
|
344
|
+
/** Full ref name (e.g. `"refs/heads/main"`, `"refs/tags/v1.0"`). */
|
|
345
|
+
ref: string;
|
|
346
|
+
/** New commit hash, or `null` to delete the ref. */
|
|
347
|
+
newHash: string | null;
|
|
348
|
+
/**
|
|
349
|
+
* Expected current hash for compare-and-swap.
|
|
350
|
+
*
|
|
351
|
+
* - `undefined` (default) — the server reads the current ref state
|
|
352
|
+
* automatically. Still CAS-protected against concurrent updates.
|
|
353
|
+
* - `null` — assert the ref does not exist (create-only).
|
|
354
|
+
* - `string` — explicit CAS: the update fails if the ref's current
|
|
355
|
+
* hash doesn't match.
|
|
356
|
+
*/
|
|
357
|
+
oldHash?: string | null;
|
|
358
|
+
}
|
|
359
|
+
interface GitServer<S = Session> {
|
|
308
360
|
/** Standard fetch-API handler for HTTP: (Request) => Response */
|
|
309
361
|
fetch(request: Request): Promise<Response>;
|
|
310
362
|
/**
|
|
@@ -340,6 +392,32 @@ interface GitServer {
|
|
|
340
392
|
* ```
|
|
341
393
|
*/
|
|
342
394
|
handleSession(command: string, channel: SshChannel, session?: SshSessionInfo): Promise<number>;
|
|
395
|
+
/**
|
|
396
|
+
* Update refs in-process with hook enforcement and CAS protection.
|
|
397
|
+
*
|
|
398
|
+
* Equivalent to a push, but without transport overhead — no pack
|
|
399
|
+
* negotiation, no object transfer. Objects must already exist in
|
|
400
|
+
* the repo's object store (e.g. via `createCommit` from `just-git/repo`).
|
|
401
|
+
*
|
|
402
|
+
* Runs the full hook lifecycle: `preReceive` → per-ref `update` →
|
|
403
|
+
* CAS application → `postReceive`. Returns per-ref results.
|
|
404
|
+
*
|
|
405
|
+
* ```ts
|
|
406
|
+
* import { createCommit, writeBlob, writeTree } from "just-git/repo";
|
|
407
|
+
*
|
|
408
|
+
* const repo = await server.repo("my-repo");
|
|
409
|
+
* const blob = await writeBlob(repo, "content");
|
|
410
|
+
* const tree = await writeTree(repo, [{ name: "file.txt", hash: blob }]);
|
|
411
|
+
* const commit = await createCommit(repo, { tree, parents: [head], ... });
|
|
412
|
+
*
|
|
413
|
+
* await server.updateRefs("my-repo", [
|
|
414
|
+
* { ref: "refs/heads/auto-fix", newHash: commit },
|
|
415
|
+
* ]);
|
|
416
|
+
* ```
|
|
417
|
+
*
|
|
418
|
+
* @throws If the repo does not exist or the server is shutting down.
|
|
419
|
+
*/
|
|
420
|
+
updateRefs(repoId: string, refs: RefUpdateRequest[], session?: S): Promise<RefUpdateResult>;
|
|
343
421
|
/**
|
|
344
422
|
* Node.js `http.createServer` compatible handler.
|
|
345
423
|
*
|
|
@@ -355,6 +433,19 @@ interface GitServer {
|
|
|
355
433
|
repo(id: string): Promise<GitRepo | null>;
|
|
356
434
|
/** Delete a repo and all its data. */
|
|
357
435
|
deleteRepo(id: string): Promise<void>;
|
|
436
|
+
/**
|
|
437
|
+
* Remove unreachable objects from a repo's storage.
|
|
438
|
+
*
|
|
439
|
+
* Walks all objects reachable from the repo's refs, compares against
|
|
440
|
+
* the full set of stored objects, and deletes the difference.
|
|
441
|
+
*
|
|
442
|
+
* If refs change during the walk (e.g. a concurrent push completes),
|
|
443
|
+
* GC aborts and returns `{ aborted: true }` to prevent deleting
|
|
444
|
+
* newly-reachable objects. Callers can retry.
|
|
445
|
+
*
|
|
446
|
+
* @throws If the repo does not exist or the server is shutting down.
|
|
447
|
+
*/
|
|
448
|
+
gc(repoId: string, options?: GcOptions): Promise<GcResult>;
|
|
358
449
|
/**
|
|
359
450
|
* Graceful shutdown. After calling, new HTTP requests receive 503
|
|
360
451
|
* and new SSH sessions get exit 128. Resolves when all in-flight
|
|
@@ -470,6 +561,17 @@ interface RefAdvertisement {
|
|
|
470
561
|
name: string;
|
|
471
562
|
hash: string;
|
|
472
563
|
}
|
|
564
|
+
/** Per-ref result from a push or {@link GitServer.updateRefs} call. */
|
|
565
|
+
interface RefResult {
|
|
566
|
+
ref: string;
|
|
567
|
+
ok: boolean;
|
|
568
|
+
error?: string;
|
|
569
|
+
}
|
|
570
|
+
/** Result of a push or {@link GitServer.updateRefs} call. */
|
|
571
|
+
interface RefUpdateResult {
|
|
572
|
+
refResults: RefResult[];
|
|
573
|
+
applied: RefUpdate[];
|
|
574
|
+
}
|
|
473
575
|
|
|
474
576
|
/**
|
|
475
577
|
* Unified Git server: Smart HTTP + SSH session handling.
|
|
@@ -508,7 +610,7 @@ interface RefAdvertisement {
|
|
|
508
610
|
* server.handleSession(command, channel, { username });
|
|
509
611
|
* ```
|
|
510
612
|
*/
|
|
511
|
-
declare function createServer<S = Session>(config: GitServerConfig<S>): GitServer
|
|
613
|
+
declare function createServer<S = Session>(config: GitServerConfig<S>): GitServer<S>;
|
|
512
614
|
/**
|
|
513
615
|
* Compose multiple hook sets into a single `ServerHooks` object.
|
|
514
616
|
*
|
|
@@ -674,15 +776,6 @@ interface ApplyReceivePackOptions<S = unknown> {
|
|
|
674
776
|
/** Session info threaded through to hooks. */
|
|
675
777
|
session?: S;
|
|
676
778
|
}
|
|
677
|
-
interface RefResult {
|
|
678
|
-
ref: string;
|
|
679
|
-
ok: boolean;
|
|
680
|
-
error?: string;
|
|
681
|
-
}
|
|
682
|
-
interface ApplyReceivePackResult {
|
|
683
|
-
refResults: RefResult[];
|
|
684
|
-
applied: RefUpdate[];
|
|
685
|
-
}
|
|
686
779
|
/**
|
|
687
780
|
* Run the full receive-pack lifecycle: preReceive hook, per-ref update
|
|
688
781
|
* hook with ref format validation, CAS ref application, and postReceive
|
|
@@ -692,7 +785,14 @@ interface ApplyReceivePackResult {
|
|
|
692
785
|
* Does NOT handle unpack failures — the caller should check
|
|
693
786
|
* `ingestResult.unpackOk` and short-circuit before calling this.
|
|
694
787
|
*/
|
|
695
|
-
declare function applyReceivePack<S = unknown>(options: ApplyReceivePackOptions<S>): Promise<
|
|
788
|
+
declare function applyReceivePack<S = unknown>(options: ApplyReceivePackOptions<S>): Promise<RefUpdateResult>;
|
|
789
|
+
/**
|
|
790
|
+
* Resolve `RefUpdateRequest[]` into fully computed `RefUpdate[]`.
|
|
791
|
+
*
|
|
792
|
+
* Reads current ref state when `oldHash` is not provided, and computes
|
|
793
|
+
* `isFF`/`isCreate`/`isDelete` for each entry.
|
|
794
|
+
*/
|
|
795
|
+
declare function resolveRefUpdates(repo: GitRepo, requests: RefUpdateRequest[]): Promise<RefUpdate[]>;
|
|
696
796
|
|
|
697
797
|
/**
|
|
698
798
|
* In-memory storage backend with multi-repo support.
|
|
@@ -723,6 +823,8 @@ declare class MemoryStorage implements Storage {
|
|
|
723
823
|
}>): void;
|
|
724
824
|
hasObject(repoId: string, hash: string): boolean;
|
|
725
825
|
findObjectsByPrefix(repoId: string, prefix: string): string[];
|
|
826
|
+
listObjectHashes(repoId: string): string[];
|
|
827
|
+
deleteObjects(repoId: string, hashes: ReadonlyArray<string>): number;
|
|
726
828
|
getRef(repoId: string, name: string): Ref | null;
|
|
727
829
|
putRef(repoId: string, name: string, ref: Ref): void;
|
|
728
830
|
removeRef(repoId: string, name: string): void;
|
|
@@ -757,6 +859,7 @@ declare class BunSqliteStorage implements Storage {
|
|
|
757
859
|
private db;
|
|
758
860
|
private stmts;
|
|
759
861
|
private batchInsertTx;
|
|
862
|
+
private batchDeleteTx;
|
|
760
863
|
constructor(db: BunSqliteDatabase);
|
|
761
864
|
hasRepo(repoId: string): boolean;
|
|
762
865
|
insertRepo(repoId: string): void;
|
|
@@ -770,6 +873,8 @@ declare class BunSqliteStorage implements Storage {
|
|
|
770
873
|
}>): void;
|
|
771
874
|
hasObject(repoId: string, hash: string): boolean;
|
|
772
875
|
findObjectsByPrefix(repoId: string, prefix: string): string[];
|
|
876
|
+
listObjectHashes(repoId: string): string[];
|
|
877
|
+
deleteObjects(repoId: string, hashes: ReadonlyArray<string>): number;
|
|
773
878
|
getRef(repoId: string, name: string): Ref | null;
|
|
774
879
|
putRef(repoId: string, name: string, ref: Ref): void;
|
|
775
880
|
removeRef(repoId: string, name: string): void;
|
|
@@ -801,6 +906,7 @@ declare class BetterSqlite3Storage implements Storage {
|
|
|
801
906
|
private db;
|
|
802
907
|
private stmts;
|
|
803
908
|
private batchInsertTx;
|
|
909
|
+
private batchDeleteTx;
|
|
804
910
|
constructor(db: BetterSqlite3Database);
|
|
805
911
|
hasRepo(repoId: string): boolean;
|
|
806
912
|
insertRepo(repoId: string): void;
|
|
@@ -814,6 +920,8 @@ declare class BetterSqlite3Storage implements Storage {
|
|
|
814
920
|
}>): void;
|
|
815
921
|
hasObject(repoId: string, hash: string): boolean;
|
|
816
922
|
findObjectsByPrefix(repoId: string, prefix: string): string[];
|
|
923
|
+
listObjectHashes(repoId: string): string[];
|
|
924
|
+
deleteObjects(repoId: string, hashes: ReadonlyArray<string>): number;
|
|
817
925
|
getRef(repoId: string, name: string): Ref | null;
|
|
818
926
|
putRef(repoId: string, name: string, ref: Ref): void;
|
|
819
927
|
removeRef(repoId: string, name: string): void;
|
|
@@ -821,54 +929,35 @@ declare class BetterSqlite3Storage implements Storage {
|
|
|
821
929
|
atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => T): T;
|
|
822
930
|
}
|
|
823
931
|
|
|
824
|
-
/** Minimal database interface for PostgreSQL. Use {@link wrapPgPool} to adapt a `pg` Pool. */
|
|
825
|
-
interface PgDatabase {
|
|
826
|
-
query<T = any>(text: string, values?: any[]): Promise<{
|
|
827
|
-
rows: T[];
|
|
828
|
-
}>;
|
|
829
|
-
transaction<R>(fn: (tx: PgDatabase) => Promise<R>): Promise<R>;
|
|
830
|
-
}
|
|
831
932
|
/** Minimal pool interface matching the `pg` package's `Pool` class. */
|
|
832
933
|
interface PgPool {
|
|
833
|
-
query(text: string, values?: any[]): Promise<{
|
|
834
|
-
rows:
|
|
934
|
+
query<T = any>(text: string, values?: any[]): Promise<{
|
|
935
|
+
rows: T[];
|
|
835
936
|
}>;
|
|
836
937
|
connect(): Promise<PgPoolClient>;
|
|
837
938
|
}
|
|
838
|
-
/** Minimal pool client interface matching the `pg` package's `PoolClient`. */
|
|
839
939
|
interface PgPoolClient {
|
|
840
|
-
query(text: string, values?: any[]): Promise<{
|
|
841
|
-
rows:
|
|
940
|
+
query<T = any>(text: string, values?: any[]): Promise<{
|
|
941
|
+
rows: T[];
|
|
842
942
|
}>;
|
|
843
943
|
release(): void;
|
|
844
944
|
}
|
|
845
945
|
/**
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
* Handles `BEGIN`/`COMMIT`/`ROLLBACK` and client release automatically.
|
|
849
|
-
*
|
|
850
|
-
* ```ts
|
|
851
|
-
* import { Pool } from "pg";
|
|
852
|
-
* const pool = new Pool({ connectionString: "..." });
|
|
853
|
-
* const db = wrapPgPool(pool);
|
|
854
|
-
* ```
|
|
855
|
-
*/
|
|
856
|
-
declare function wrapPgPool(pool: PgPool): PgDatabase;
|
|
857
|
-
/**
|
|
858
|
-
* PostgreSQL-backed storage.
|
|
946
|
+
* PostgreSQL-backed storage. Accepts a `pg`-style pool directly.
|
|
859
947
|
*
|
|
860
948
|
* Use the static `create` factory (schema setup is async):
|
|
861
949
|
*
|
|
862
950
|
* ```ts
|
|
863
951
|
* import { Pool } from "pg";
|
|
864
952
|
* const pool = new Pool({ connectionString: "..." });
|
|
865
|
-
* const storage = await PgStorage.create(
|
|
953
|
+
* const storage = await PgStorage.create(pool);
|
|
866
954
|
* ```
|
|
867
955
|
*/
|
|
868
956
|
declare class PgStorage implements Storage {
|
|
869
|
-
private
|
|
957
|
+
private pool;
|
|
870
958
|
private constructor();
|
|
871
|
-
static create(
|
|
959
|
+
static create(pool: PgPool): Promise<PgStorage>;
|
|
960
|
+
private transaction;
|
|
872
961
|
hasRepo(repoId: string): Promise<boolean>;
|
|
873
962
|
insertRepo(repoId: string): Promise<void>;
|
|
874
963
|
deleteRepo(repoId: string): Promise<void>;
|
|
@@ -881,6 +970,8 @@ declare class PgStorage implements Storage {
|
|
|
881
970
|
}>): Promise<void>;
|
|
882
971
|
hasObject(repoId: string, hash: string): Promise<boolean>;
|
|
883
972
|
findObjectsByPrefix(repoId: string, prefix: string): Promise<string[]>;
|
|
973
|
+
listObjectHashes(repoId: string): Promise<string[]>;
|
|
974
|
+
deleteObjects(repoId: string, hashes: ReadonlyArray<string>): Promise<number>;
|
|
884
975
|
getRef(repoId: string, name: string): Promise<Ref | null>;
|
|
885
976
|
putRef(repoId: string, name: string, ref: Ref): Promise<void>;
|
|
886
977
|
removeRef(repoId: string, name: string): Promise<void>;
|
|
@@ -888,4 +979,4 @@ declare class PgStorage implements Storage {
|
|
|
888
979
|
atomicRefUpdate<T>(repoId: string, fn: (ops: RefOps) => Promise<T> | T): Promise<T>;
|
|
889
980
|
}
|
|
890
981
|
|
|
891
|
-
export { type AdvertiseRefsEvent, type AdvertiseResult, type ApplyReceivePackOptions, type
|
|
982
|
+
export { type AdvertiseRefsEvent, type AdvertiseResult, type ApplyReceivePackOptions, 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 Session, type SessionBuilder, type SshChannel, type SshSessionInfo, type Storage, type UpdateEvent, advertiseRefsWithHooks, applyReceivePack, buildRefAdvertisementBytes, buildRefListBytes, buildRefListPktLines, collectRefs, composeHooks, createServer, handleUploadPack, ingestReceivePack, ingestReceivePackFromStream, resolveRefUpdates };
|