just-git 1.0.2 → 1.1.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 +50 -60
- package/dist/hooks-jTfqkfs1.d.ts +480 -0
- package/dist/index.d.ts +22 -407
- package/dist/index.js +398 -398
- package/dist/repo/index.d.ts +261 -0
- package/dist/repo/index.js +18 -0
- package/dist/server/index.d.ts +284 -0
- package/dist/server/index.js +42 -0
- package/package.json +9 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { f as GitRepo, X as Rejection } from '../hooks-jTfqkfs1.js';
|
|
2
|
+
|
|
3
|
+
interface GitServerConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Resolve an incoming request path to a repository.
|
|
6
|
+
*
|
|
7
|
+
* Return values:
|
|
8
|
+
* - `GitRepo` — use this repo for the request
|
|
9
|
+
* - `null` — respond with 404
|
|
10
|
+
* - `Response` — send this response as-is (useful for 401/403 with
|
|
11
|
+
* custom headers like `WWW-Authenticate`)
|
|
12
|
+
*/
|
|
13
|
+
resolveRepo: (repoPath: string, request: Request) => GitRepo | Response | null | Promise<GitRepo | Response | null>;
|
|
14
|
+
/** Server-side hooks. All optional. */
|
|
15
|
+
hooks?: ServerHooks;
|
|
16
|
+
/** Base path prefix to strip from URLs (e.g. "/git"). */
|
|
17
|
+
basePath?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Cache generated packfiles for identical full-clone requests.
|
|
20
|
+
*
|
|
21
|
+
* When enabled, the server caches the computed pack data for each
|
|
22
|
+
* (repo, wants) pair where no `have` lines are sent. Subsequent
|
|
23
|
+
* clones of the same ref state are served from cache, skipping
|
|
24
|
+
* object enumeration, delta computation, and compression.
|
|
25
|
+
*
|
|
26
|
+
* Set to `false` to disable. Default: enabled with 256 MB limit.
|
|
27
|
+
*/
|
|
28
|
+
packCache?: false | {
|
|
29
|
+
maxBytes?: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Control delta compression and streaming for upload-pack responses.
|
|
33
|
+
*/
|
|
34
|
+
packOptions?: {
|
|
35
|
+
/** Skip delta compression entirely. Larger packs, much faster generation. */
|
|
36
|
+
noDelta?: boolean;
|
|
37
|
+
/** Delta window size (default 10). Smaller = faster, worse compression ratio. */
|
|
38
|
+
deltaWindow?: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
interface GitServer {
|
|
42
|
+
/** Standard fetch-API handler: (Request) => Response */
|
|
43
|
+
fetch: (request: Request) => Promise<Response>;
|
|
44
|
+
}
|
|
45
|
+
interface ServerHooks {
|
|
46
|
+
/**
|
|
47
|
+
* Called after objects are unpacked but before any refs update.
|
|
48
|
+
* Receives ALL ref updates as a batch. Return a Rejection to abort
|
|
49
|
+
* the entire push. Auth, branch protection, and repo-wide policy
|
|
50
|
+
* belong here.
|
|
51
|
+
*/
|
|
52
|
+
preReceive?: (event: PreReceiveEvent) => void | Rejection | Promise<void | Rejection>;
|
|
53
|
+
/**
|
|
54
|
+
* Called per-ref, after preReceive passes.
|
|
55
|
+
* Return a Rejection to block this specific ref update while
|
|
56
|
+
* allowing others. Per-branch rules belong here.
|
|
57
|
+
*/
|
|
58
|
+
update?: (event: UpdateEvent) => void | Rejection | Promise<void | Rejection>;
|
|
59
|
+
/**
|
|
60
|
+
* Called after all ref updates succeed. Cannot reject.
|
|
61
|
+
* CI triggers, webhooks, notifications belong here.
|
|
62
|
+
*/
|
|
63
|
+
postReceive?: (event: PostReceiveEvent) => void | Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Called when a client wants to fetch or push (during ref advertisement).
|
|
66
|
+
* Return a filtered ref list to hide branches, or void to advertise all.
|
|
67
|
+
*/
|
|
68
|
+
advertiseRefs?: (event: AdvertiseRefsEvent) => RefAdvertisement[] | void | Promise<RefAdvertisement[] | void>;
|
|
69
|
+
}
|
|
70
|
+
interface RefUpdate {
|
|
71
|
+
ref: string;
|
|
72
|
+
oldHash: string | null;
|
|
73
|
+
newHash: string;
|
|
74
|
+
isFF: boolean;
|
|
75
|
+
isCreate: boolean;
|
|
76
|
+
isDelete: boolean;
|
|
77
|
+
}
|
|
78
|
+
interface PreReceiveEvent {
|
|
79
|
+
repo: GitRepo;
|
|
80
|
+
repoPath: string;
|
|
81
|
+
updates: readonly RefUpdate[];
|
|
82
|
+
request: Request;
|
|
83
|
+
}
|
|
84
|
+
interface UpdateEvent {
|
|
85
|
+
repo: GitRepo;
|
|
86
|
+
repoPath: string;
|
|
87
|
+
update: RefUpdate;
|
|
88
|
+
request: Request;
|
|
89
|
+
}
|
|
90
|
+
interface PostReceiveEvent {
|
|
91
|
+
repo: GitRepo;
|
|
92
|
+
repoPath: string;
|
|
93
|
+
updates: readonly RefUpdate[];
|
|
94
|
+
request: Request;
|
|
95
|
+
}
|
|
96
|
+
interface AdvertiseRefsEvent {
|
|
97
|
+
repo: GitRepo;
|
|
98
|
+
repoPath: string;
|
|
99
|
+
refs: RefAdvertisement[];
|
|
100
|
+
service: "git-upload-pack" | "git-receive-pack";
|
|
101
|
+
request: Request;
|
|
102
|
+
}
|
|
103
|
+
interface RefAdvertisement {
|
|
104
|
+
name: string;
|
|
105
|
+
hash: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Framework-agnostic Git Smart HTTP request handler.
|
|
110
|
+
*
|
|
111
|
+
* Uses web-standard Request/Response, works with Bun.serve, Hono,
|
|
112
|
+
* Cloudflare Workers, or any framework that speaks fetch API.
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create a Git Smart HTTP server handler.
|
|
117
|
+
*
|
|
118
|
+
* ```ts
|
|
119
|
+
* const server = createGitServer({
|
|
120
|
+
* resolveRepo: async (repoPath, request) => storage.repo(repoPath),
|
|
121
|
+
* });
|
|
122
|
+
* Bun.serve({ fetch: server.fetch });
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function createGitServer(config: GitServerConfig): GitServer;
|
|
126
|
+
/**
|
|
127
|
+
* Compose multiple hook sets into a single `ServerHooks` object.
|
|
128
|
+
*
|
|
129
|
+
* - **Pre-hooks** (`preReceive`, `update`): run in order, short-circuit
|
|
130
|
+
* on the first `Rejection`.
|
|
131
|
+
* - **Post-hooks** (`postReceive`): run all in order. Each is individually
|
|
132
|
+
* try/caught so one failure doesn't prevent the rest from running.
|
|
133
|
+
* - **Filter hooks** (`advertiseRefs`): chain — each hook receives the
|
|
134
|
+
* refs returned by the previous one. Returning void passes through
|
|
135
|
+
* unchanged.
|
|
136
|
+
*/
|
|
137
|
+
declare function composeHooks(...hookSets: (ServerHooks | undefined)[]): ServerHooks;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* High-level server operations for Git Smart HTTP.
|
|
141
|
+
*
|
|
142
|
+
* Each operation accepts a `GitRepo` (ObjectStore + RefStore)
|
|
143
|
+
* and returns protocol-level results. The handler layer is
|
|
144
|
+
* responsible for hook invocation and ref application.
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
interface PackCacheEntry {
|
|
148
|
+
packData: Uint8Array;
|
|
149
|
+
objectCount: number;
|
|
150
|
+
deltaCount: number;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Bounded LRU-ish cache for generated packfiles.
|
|
154
|
+
*
|
|
155
|
+
* Keyed on `(repoPath, sorted wants)` — only caches full clones
|
|
156
|
+
* (requests with no `have` lines). Incremental fetches always
|
|
157
|
+
* compute fresh packs.
|
|
158
|
+
*
|
|
159
|
+
* Entries are automatically invalidated when refs change: since the
|
|
160
|
+
* cache key includes the exact want hashes, a ref update changes
|
|
161
|
+
* the want set on the next client request, producing a cache miss.
|
|
162
|
+
*/
|
|
163
|
+
declare class PackCache {
|
|
164
|
+
private entries;
|
|
165
|
+
private currentBytes;
|
|
166
|
+
private maxBytes;
|
|
167
|
+
private hits;
|
|
168
|
+
private misses;
|
|
169
|
+
constructor(maxBytes?: number);
|
|
170
|
+
/** Build a cache key. Returns null for requests with haves (not cacheable). */
|
|
171
|
+
static key(repoPath: string, wants: string[], haves: string[]): string | null;
|
|
172
|
+
get(key: string): PackCacheEntry | undefined;
|
|
173
|
+
set(key: string, entry: PackCacheEntry): void;
|
|
174
|
+
get stats(): {
|
|
175
|
+
entries: number;
|
|
176
|
+
bytes: number;
|
|
177
|
+
hits: number;
|
|
178
|
+
misses: number;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
interface RefsData {
|
|
182
|
+
refs: RefAdvertisement[];
|
|
183
|
+
headTarget?: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Collect the structured ref list from a repo (no wire encoding).
|
|
187
|
+
* The handler can pass this through an advertiseRefs hook to filter,
|
|
188
|
+
* then call `buildRefAdvertisementBytes` to produce the wire format.
|
|
189
|
+
*/
|
|
190
|
+
declare function collectRefs(repo: GitRepo): Promise<RefsData>;
|
|
191
|
+
/**
|
|
192
|
+
* Build the wire-format ref advertisement from a (possibly filtered) ref list.
|
|
193
|
+
*/
|
|
194
|
+
declare function buildRefAdvertisementBytes(refs: RefAdvertisement[], service: "git-upload-pack" | "git-receive-pack", headTarget?: string): Uint8Array;
|
|
195
|
+
interface UploadPackOptions {
|
|
196
|
+
/** Pack cache instance. When provided, full clones (no haves) are cached. */
|
|
197
|
+
cache?: PackCache;
|
|
198
|
+
/** Repo path used as part of the cache key. Required when cache is set. */
|
|
199
|
+
cacheKey?: string;
|
|
200
|
+
/** Skip delta compression — faster pack generation, larger output. */
|
|
201
|
+
noDelta?: boolean;
|
|
202
|
+
/** Delta window size (default 10). Ignored when noDelta is true. */
|
|
203
|
+
deltaWindow?: number;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Handle a `POST /git-upload-pack` request.
|
|
207
|
+
*
|
|
208
|
+
* Returns `Uint8Array` for buffered responses (cache hits, deltified packs)
|
|
209
|
+
* or `ReadableStream<Uint8Array>` for streaming no-delta responses.
|
|
210
|
+
*/
|
|
211
|
+
declare function handleUploadPack(repo: GitRepo, requestBody: Uint8Array, options?: UploadPackOptions): Promise<Uint8Array | ReadableStream<Uint8Array>>;
|
|
212
|
+
interface ReceivePackResult {
|
|
213
|
+
updates: RefUpdate[];
|
|
214
|
+
unpackOk: boolean;
|
|
215
|
+
capabilities: string[];
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Ingest a receive-pack request: parse commands, ingest the packfile,
|
|
219
|
+
* and compute enriched RefUpdate objects. Does NOT apply ref updates —
|
|
220
|
+
* the handler runs hooks first, then applies surviving updates.
|
|
221
|
+
*/
|
|
222
|
+
declare function ingestReceivePack(repo: GitRepo, requestBody: Uint8Array): Promise<ReceivePackResult>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Opinionated hook presets built on top of the minimal ServerHooks interface.
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
interface StandardHooksConfig {
|
|
229
|
+
/** Branches that cannot be force-pushed to or deleted. */
|
|
230
|
+
protectedBranches?: string[];
|
|
231
|
+
/** Reject all non-fast-forward pushes globally. */
|
|
232
|
+
denyNonFastForward?: boolean;
|
|
233
|
+
/** Reject all ref deletions globally. */
|
|
234
|
+
denyDeletes?: boolean;
|
|
235
|
+
/** Return false to reject the entire push (e.g. check Authorization header). */
|
|
236
|
+
authorize?: (request: Request) => boolean | Promise<boolean>;
|
|
237
|
+
/** Called after refs are updated. */
|
|
238
|
+
onPush?: (event: PostReceiveEvent) => void | Promise<void>;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Build a standard set of server hooks from a simple config.
|
|
242
|
+
*
|
|
243
|
+
* Covers the most common policies (branch protection, fast-forward
|
|
244
|
+
* enforcement, authorization, post-push callbacks) so users don't
|
|
245
|
+
* have to wire hooks manually for typical setups.
|
|
246
|
+
*/
|
|
247
|
+
declare function createStandardHooks(config: StandardHooksConfig): ServerHooks;
|
|
248
|
+
|
|
249
|
+
interface SqliteStatement {
|
|
250
|
+
run(...params: any[]): void;
|
|
251
|
+
get(...params: any[]): any;
|
|
252
|
+
all(...params: any[]): any[];
|
|
253
|
+
}
|
|
254
|
+
interface SqliteDatabase {
|
|
255
|
+
run(sql: string): void;
|
|
256
|
+
prepare(sql: string): SqliteStatement;
|
|
257
|
+
transaction<F extends (...args: any[]) => any>(fn: F): (...args: Parameters<F>) => ReturnType<F>;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* SQLite-backed git storage with multi-repo support.
|
|
261
|
+
*
|
|
262
|
+
* Creates and manages `git_objects` and `git_refs` tables in the
|
|
263
|
+
* provided database. Multiple repos are partitioned by `repo_id`.
|
|
264
|
+
*
|
|
265
|
+
* ```ts
|
|
266
|
+
* const db = new Database("repos.sqlite");
|
|
267
|
+
* const storage = new SqliteStorage(db);
|
|
268
|
+
* const server = createGitServer({
|
|
269
|
+
* resolve: async (repoPath) => storage.repo(repoPath),
|
|
270
|
+
* });
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
declare class SqliteStorage {
|
|
274
|
+
private db;
|
|
275
|
+
private stmts;
|
|
276
|
+
private ingestTx;
|
|
277
|
+
constructor(db: SqliteDatabase);
|
|
278
|
+
/** Get a `GitRepo` scoped to a specific repo. */
|
|
279
|
+
repo(repoId: string): GitRepo;
|
|
280
|
+
/** Delete all objects and refs for a repo. */
|
|
281
|
+
deleteRepo(repoId: string): void;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export { type AdvertiseRefsEvent, type GitServer, type GitServerConfig, PackCache, type PostReceiveEvent, type PreReceiveEvent, type ReceivePackResult, type RefAdvertisement, type RefUpdate, type RefsData, Rejection, type ServerHooks, type SqliteDatabase, type SqliteStatement, SqliteStorage, type StandardHooksConfig, type UpdateEvent, type UploadPackOptions, buildRefAdvertisementBytes, collectRefs, composeHooks, createGitServer, createStandardHooks, handleUploadPack, ingestReceivePack };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
var B=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});function S(n){return n!=null&&typeof n=="object"&&"reject"in n&&n.reject===!0}var K="0000000000000000000000000000000000000000",be=(()=>{let n=new Array(256);for(let e=0;e<256;e++)n[e]=(e>>4).toString(16)+(e&15).toString(16);return n})();function N(n){let e="";for(let t=0;t<20;t++)e+=be[n[t]];return e}function ye(n,e){let t="";for(let o=0;o<20;o++)t+=be[n[e+o]];return t}function W(n){let e=new Uint8Array(20);for(let t=0;t<20;t++)e[t]=parseInt(n.slice(t*2,t*2+2),16);return e}var Xe=new TextEncoder;function Y(n){return typeof n=="string"?Xe.encode(n):n}function Ze(){if(typeof globalThis.Bun<"u")return()=>{let n=new Bun.CryptoHasher("sha1"),e={update(t){return n.update(Y(t)),e},hex:()=>Promise.resolve(n.digest("hex"))};return e};try{let n=B(["node","crypto"].join(":"));if(typeof n.createHash=="function")return()=>{let e=n.createHash("sha1"),t={update(o){return e.update(Y(o)),t},hex:()=>Promise.resolve(e.digest("hex"))};return t}}catch{}if(typeof globalThis.crypto?.subtle?.digest=="function")return()=>{let n=[],e={update(t){return n.push(Y(t)),e},async hex(){let t=0;for(let i of n)t+=i.byteLength;let o=new Uint8Array(t),r=0;for(let i of n)o.set(i,r),r+=i.byteLength;let a=await crypto.subtle.digest("SHA-1",o);return N(new Uint8Array(a))}};return e};throw new Error("No SHA-1 implementation available. Requires Bun, Node.js, Deno, or a browser with Web Crypto.")}var xe=Ze(),$=xe;async function me(n){return xe().update(n).hex()}var Je=new Set(["tree","commit","tag"]),_=class{map=new Map;currentBytes=0;maxBytes;constructor(e=16*1024*1024){this.maxBytes=e}get(e){return this.map.get(e)}set(e,t){if(!Je.has(t.type))return;let o=t.content.byteLength;if(!(o>this.maxBytes/2)&&!this.map.has(e)){for(;this.currentBytes+o>this.maxBytes&&this.map.size>0;){let r=this.map.keys().next().value;this.currentBytes-=this.map.get(r).content.byteLength,this.map.delete(r)}this.map.set(e,t),this.currentBytes+=o}}get size(){return this.map.size}get bytes(){return this.currentBytes}clear(){this.map.clear(),this.currentBytes=0}};function Qe(){try{let n=B(["node","zlib"].join(":"));if(typeof n.deflateSync=="function"&&typeof n.inflateSync=="function"){let e;try{let t=n.inflateSync(n.deflateSync(Buffer.from("x")),{info:!0});t?.engine&&typeof t.engine.bytesWritten=="number"&&(e=o=>{let r=n.inflateSync(o,{info:!0});return Promise.resolve({result:new Uint8Array(r.buffer),bytesConsumed:r.engine.bytesWritten})})}catch{}return{deflate:t=>Promise.resolve(new Uint8Array(n.deflateSync(t))),inflate:t=>Promise.resolve(new Uint8Array(n.inflateSync(t))),inflateWithConsumed:e}}}catch{}if(typeof globalThis.CompressionStream=="function"&&typeof globalThis.DecompressionStream=="function")return{async deflate(n){let e=new CompressionStream("deflate"),t=e.writable.getWriter();return t.write(n),t.close(),new Uint8Array(await new Response(e.readable).arrayBuffer())},async inflate(n){let e=new DecompressionStream("deflate"),t=e.writable.getWriter();return t.write(n),t.close(),new Uint8Array(await new Response(e.readable).arrayBuffer())}};throw new Error("No zlib implementation available. Requires Bun, Node.js, Deno, or a browser with CompressionStream.")}var V=Qe(),G=V.deflate,X=V.inflate,Z=V.inflateWithConsumed;var M=1346454347,ee=2,Re=1,ke=2,Pe=3,Oe=4,U=6,te=7,et={[Re]:"commit",[ke]:"tree",[Pe]:"blob",[Oe]:"tag"},je={commit:Re,tree:ke,blob:Pe,tag:Oe};async function ve(n,e){let t=new DataView(n.buffer,n.byteOffset,n.byteLength),o=t.getUint32(0);if(o!==M)throw new Error(`Invalid pack signature: 0x${o.toString(16)} (expected 0x${M.toString(16)})`);let r=t.getUint32(4);if(r!==ee)throw new Error(`Unsupported pack version: ${r}`);let a=t.getUint32(8),i=[],s=12;for(let f=0;f<a;f++){let d=await tt(n,s);i.push(d),s=d.nextOffset}return(await nt(i,e)).map((f,d)=>({...f,offset:i[d].headerOffset,nextOffset:i[d].nextOffset}))}async function tt(n,e){let t=e,o=n[e++],r=o>>4&7,a=o&15,i=4;for(;o&128;)o=n[e++],a|=(o&127)<<i,i+=7;let s,c;if(r===U){let u=n[e++];for(s=u&127;u&128;)s+=1,u=n[e++],s=(s<<7)+(u&127);s=t-s}else r===te&&(c=ye(n,e),e+=20);let{result:f,bytesConsumed:d}=await at(n,e,a);return{headerOffset:t,typeNum:r,inflated:f,baseOffset:s,baseHash:c,nextOffset:e+d}}async function nt(n,e){let t=new Map;for(let a=0;a<n.length;a++)t.set(n[a].headerOffset,a);let o=new Array(n.length).fill(null);async function r(a){let i=o[a];if(i)return i;let s=n[a];if(s.typeNum!==U&&s.typeNum!==te){let l=et[s.typeNum];if(!l)throw new Error(`Unknown object type: ${s.typeNum}`);let h={type:l,content:s.inflated,hash:await J(l,s.inflated)};return o[a]=h,h}if(s.typeNum===U){let l=t.get(s.baseOffset);if(l===void 0)throw new Error(`OFS_DELTA base not found at offset ${s.baseOffset}`);let h=await r(l),b=ge(h.content,s.inflated),p={type:h.type,content:b,hash:await J(h.type,b)};return o[a]=p,p}let c=await rt(n,o,s.baseHash,r),f;if(c!==void 0)f=await r(c);else if(e){let l=await e(s.baseHash);l&&(f=l)}if(!f)throw new Error(`REF_DELTA base not found for hash ${s.baseHash}`);let d=ge(f.content,s.inflated),u={type:f.type,content:d,hash:await J(f.type,d)};return o[a]=u,u}for(let a=0;a<n.length;a++)await r(a);return o}async function rt(n,e,t,o){for(let r=0;r<e.length;r++)if(e[r]?.hash===t)return r;for(let r=0;r<n.length;r++){let a=n[r];if(a.typeNum!==U&&a.typeNum!==te&&(await o(r)).hash===t)return r}}function ge(n,e){let t=0,{value:o,newPos:r}=we(e,t);if(t=r,o!==n.byteLength)throw new Error(`Delta base size mismatch: expected ${o}, got ${n.byteLength}`);let{value:a,newPos:i}=we(e,t);t=i;let s=new Uint8Array(a),c=0;for(;t<e.byteLength;){let f=e[t++];if(f&128){let d=0,u=0;f&1&&(d=e[t++]),f&2&&(d|=e[t++]<<8),f&4&&(d|=e[t++]<<16),f&8&&(d|=e[t++]<<24),f&16&&(u=e[t++]),f&32&&(u|=e[t++]<<8),f&64&&(u|=e[t++]<<16),u===0&&(u=65536),s.set(n.subarray(d,d+u),c),c+=u}else if(f>0)s.set(e.subarray(t,t+f),c),c+=f,t+=f;else throw new Error("Unexpected delta opcode 0x00 (reserved)")}if(c!==a)throw new Error(`Delta produced ${c} bytes, expected ${a}`);return s}async function Ie(n){let e=[],t=new Map,o=new Uint8Array(12),r=new DataView(o.buffer);r.setUint32(0,M),r.setUint32(4,ee),r.setUint32(8,n.length),e.push(o);let a=12,i=[];for(let s of n){let c=a;t.set(s.hash,a);let f=s.delta&&s.deltaBaseHash?t.get(s.deltaBaseHash):void 0;if(s.delta&&f!==void 0){let d=Q(U,s.delta.byteLength),u=st(a-f),l=await G(s.delta);e.push(d,u,l),a+=d.byteLength+u.byteLength+l.byteLength}else{let d=je[s.type],u=Q(d,s.content.byteLength),l=await G(s.content);e.push(u,l),a+=u.byteLength+l.byteLength}i.push({hash:s.hash,offset:c,nextOffset:a})}return{data:await ot(e),entries:i}}async function*Ae(n,e){let t=$(),o=new Uint8Array(12),r=new DataView(o.buffer);r.setUint32(0,M),r.setUint32(4,ee),r.setUint32(8,n),t.update(o),yield o;for await(let a of e){let i=je[a.type],s=Q(i,a.content.byteLength),c=await G(a.content);t.update(s),t.update(c),yield it(s,c)}yield W(await t.hex())}async function ot(n){let e=0;for(let i of n)e+=i.byteLength;e+=20;let t=new Uint8Array(e),o=0;for(let i of n)t.set(i,o),o+=i.byteLength;let r=$();r.update(t.subarray(0,o));let a=await r.hex();return t.set(W(a),o),t}function st(n){let e=[];e.push(n&127);let t=n>>>7;for(;t>0;)e.unshift(128|--t&127),t>>>=7;return new Uint8Array(e)}function Q(n,e){let t=[],o=n<<4|e&15;for(e>>=4;e>0;)t.push(o|128),o=e&127,e>>=7;return t.push(o),new Uint8Array(t)}function we(n,e){let t=0,o=0,r;do r=n[e++],t|=(r&127)<<o,o+=7;while(r&128);return{value:t,newPos:e}}async function at(n,e,t){let o=n.subarray(e);if(Z){let{result:s,bytesConsumed:c}=await Z(o);if(s.byteLength!==t)throw new Error(`Inflate size mismatch: got ${s.byteLength}, expected ${t}`);return{result:s,bytesConsumed:c}}let r=await X(o);if(r.byteLength!==t)throw new Error(`Inflate size mismatch: got ${r.byteLength}, expected ${t}`);let a=2,i=o.byteLength;for(;a<i;){let s=a+i>>>1;try{(await X(o.subarray(0,s))).byteLength===t?i=s:a=s+1}catch{a=s+1}}return{result:r,bytesConsumed:a}}function it(n,e){let t=new Uint8Array(n.byteLength+e.byteLength);return t.set(n,0),t.set(e,n.byteLength),t}var ct=new TextEncoder;async function J(n,e){let t=ct.encode(`${n} ${e.byteLength}\0`),o=$();return o.update(t),o.update(e),o.hex()}var ft=new TextEncoder,qt=new TextDecoder;function Ee(n,e){let t=ft.encode(`${n} ${e.byteLength}\0`),o=new Uint8Array(t.byteLength+e.byteLength);return o.set(t),o.set(e,t.byteLength),o}function T(n){let e=n.indexOf("<"),t=n.indexOf(">");if(e===-1||t===-1)throw new Error(`Malformed identity line: "${n}"`);let o=n.slice(0,e).trimEnd(),r=n.slice(e+1,t),a=n.slice(t+2),[i="0",s="+0000"]=a.split(" "),c=parseInt(i,10);return{name:o,email:r,timestamp:c,timezone:s}}var Vt=new TextEncoder,lt=new TextDecoder;function C(n){let e=lt.decode(n),t=e.indexOf(`
|
|
2
|
+
|
|
3
|
+
`),o=t===-1?e:e.slice(0,t),r=t===-1?"":e.slice(t+2),a="",i=[],s,c;for(let f of o.split(`
|
|
4
|
+
`)){let d=f.indexOf(" ");if(d===-1)continue;let u=f.slice(0,d),l=f.slice(d+1);switch(u){case"tree":a=l;break;case"parent":i.push(l);break;case"author":s=T(l);break;case"committer":c=T(l);break}}if(!a)throw new Error("Commit missing tree field");if(!s)throw new Error("Commit missing author field");if(!c)throw new Error("Commit missing committer field");return{type:"commit",tree:a,parents:i,author:s,committer:c,message:r}}var Jt=new TextEncoder,pt=new TextDecoder;function P(n){let e=pt.decode(n),t=e.indexOf(`
|
|
5
|
+
|
|
6
|
+
`),o=t===-1?e:e.slice(0,t),r=t===-1?"":e.slice(t+2),a="",i="commit",s="",c;for(let f of o.split(`
|
|
7
|
+
`)){let d=f.indexOf(" ");if(d===-1)continue;let u=f.slice(0,d),l=f.slice(d+1);switch(u){case"object":a=l;break;case"type":i=l;break;case"tag":s=l;break;case"tagger":c=T(l);break}}if(!a)throw new Error("Tag missing object field");if(!s)throw new Error("Tag missing tag name field");if(!c)throw new Error("Tag missing tagger field");return{type:"tag",object:a,objectType:i,name:s,tagger:c,message:r}}async function H(n,e){return n.objectStore.read(e)}async function Se(n,e){return n.objectStore.exists(e)}var tn=new TextDecoder;async function Ue(n,e){let t=await H(n,e);if(t.type!=="commit")throw new Error(`Expected commit object for ${e}, got ${t.type}`);return C(t.content)}var on=new TextEncoder,Te=new TextDecoder;function ne(n){let e=[],t=0;for(;t<n.byteLength;){let o=n.indexOf(32,t);if(o===-1)break;let r=Te.decode(n.subarray(t,o)),a=n.indexOf(0,o+1);if(a===-1)break;let i=Te.decode(n.subarray(o+1,a)),s=n.subarray(a+1,a+21),c=N(s),f=r.padStart(6,"0");e.push({mode:f,name:i,hash:c}),t=a+21}return{type:"tree",entries:e}}async function Ce(n,e,t){if(e===t)return!0;let o=new Set,r=[t],a=0;for(;a<r.length;){let i=r[a++];if(i===e)return!0;if(o.has(i))continue;o.add(i);let s=await Ue(n,i);for(let c of s.parents)o.has(c)||r.push(c)}return!1}var q=new Uint32Array([0,2874782929,1454598562,4260027763,104818581,2909197124,1351355959,4225088230,209637162,2804382715,1523426952,4053013081,172075199,2702711918,1559413021,4155209164,419274324,3013880453,1313798134,3843506983,516094401,3046853904,1215402083,3811058866,344150398,3218793903,1110456540,3916008461,314324715,3118826042,1141858121,4015451032,838548648,2594343033,1732793610,3423723995,935615293,2627596268,1635201695,3392046670,1032188802,2531018579,1798740512,3228511985,1002083351,2430804166,1829371317,3327150436,688300796,2187269677,2142620510,3572268943,792315241,2220913080,2039131339,3537049626,628649430,2384582919,1942684788,3633492133,591858243,2283716242,1978950625,3735934768,1677097296,3366964609,893718770,2652677155,1707679429,3465587220,863663975,2552480694,1871230586,3302032043,960225240,2455923465,1773654511,3270403390,1057273933,2489126044,2064377604,3495056341,767069862,2262907511,2100592785,3597481024,730327347,2162056674,2004166702,3693911295,566641036,2325738845,1900695483,3658742634,670639641,2359333576,1376601592,4183093545,79572058,2951190667,1412571757,4285241020,42027983,2849570590,1584630482,4113186307,146858864,2744735649,1481436487,4078262678,251626725,2779131956,1257298860,3785977725,474198542,3071936223,1288682553,3885369576,444388763,2972016970,1183716486,3990331479,272465188,3143944693,1085371155,3957901250,369236657,3176902240,1823819377,3354194592,973483987,2438961922,1787437540,3251868981,1010387014,2539715735,1623951707,3415358858,913891577,2636207144,1727327950,3450690079,809994092,2602447805,1950930981,3742461172,586920327,2309096790,1920450480,3643673441,616879634,2409456323,2016810767,3547309022,780500653,2245839484,2114547866,3578840139,683284792,2212736489,1565424345,4128755208,201134971,2695145386,1534139724,4029196701,230847726,2795225151,1362146803,4201185570,126082129,2899994752,1460654694,4233520823,29146052,2867138325,1169417357,4008333404,318803247,3092855294,1133282072,3906287561,356510394,3194379883,1238182823,3801390966,528368133,3022517972,1341279282,3836475619,423699856,2987954497,256065313,2753203184,1508923011,4071219794,159144116,2720394341,1607414038,4103505351,54390795,2825143514,1435408809,4275514744,84055966,2925208399,1404173884,4175973101,398369141,3169260964,1091422423,3931405318,293717728,3134747185,1194504002,3966442387,465595999,2962872974,1299384317,3861558060,503253450,3064380699,1263296616,3759527097,1052246921,2514597720,1745578539,3276988154,948397084,2480853197,1848905150,3312302447,851889315,2577365106,1685431553,3475771856,888777526,2678071271,1649066644,3373495877,642096605,2367432972,1895232639,3685695662,544930376,2334346905,1992922090,3717211963,708530935,2170742310,2089302869,3620835204,738473314,2271052211,2058837184,3522095121]),ut=new Uint32Array([0,2125799437,1446245579,679924934,121527623,2039184714,1359849868,800622977,243055246,1892260483,1481592389,653946440,155379657,2012727236,1601245954,566506255,486110492,1649169681,1254785495,880779738,466036827,1701972054,1307892880,859962525,310759314,1815146399,1152793433,973282132,364614357,1796125400,1133012510,1027425811,972220984,1195887157,1874927347,292830974,1053603711,1081710450,1761559476,373973945,932073654,1228632251,1639818365,520651888,816844273,1308962300,1719925050,406247735,621518628,1539227433,1933077487,227363810,573948515,1551950446,1946564264,179508901,729228714,1438778791,2101662049,66187628,743012589,1392269536,2054851622,80718891,1944441968,223409277,634790075,1533214902,1960544567,175024442,585661948,1549622769,2107207422,53235443,732600885,1428142648,2057778105,68293556,747947890,1377953663,1864147308,296321377,959403431,1201305002,1747123243,379051046,1041303776,1084500205,1633688546,534068207,928247593,1239863076,1717454501,418080424,812495470,1322816099,1243037256,883039813,472173187,1653729934,1294557967,865878786,454727620,1706014665,1147897030,987669707,307785741,1827491840,1129699713,1038154124,359017802,1808993607,1458457428,677079897,14532511,2120785810,1372591635,795162142,132375256,2035729109,1486025178,640143831,245433617,1880368412,1605151901,555322512,161437782,1999272027,1284643889,841432124,446818554,1679083767,1269580150,890855803,497006013,1663735216,1122936511,1012631218,350048884,1785229945,1171323896,996531189,333647667,1834364734,1349216557,786319648,106470886,2028846571,1465201770,702551143,23511201,2144592044,1578619811,547550126,136587112,1989216101,1495895780,664579817,253393455,1907317282,1969288713,198563332,592642754,1575363279,1918806862,216763203,611147653,1524137864,2065452167,94989450,758102092,1402640449,2082607552,43463117,705815819,1420084486,1771668245,388801304,1068136414,1092573139,1856495186,269680223,949234329,1176570516,1743075739,424679830,836160848,1331948893,1624990940,510543057,921210903,1214099482,1064814657,1103292492,1766079626,401677447,944346374,1190966539,1853514189,282018240,822833871,1337873090,1731757572,428713481,909455240,1216352133,1611062083,515111758,596557149,1564187984,1975339414,185100699,615571482,1510326295,1921193169,204879068,770836435,1397172190,2076308248,91542293,718035604,1417247385,2097131103,38440530,109388409,2016412276,1354159794,772011711,29065022,2131648307,1468566517,691907576,150575351,1984739578,1590324284,545213489,264750512,1903355325,1509175675,658575734,444356453,1690924904,1280287662,855278499,490867234,1677142575,1265762025,902094564,335605227,1790299622,1110645024,1015429421,322875564,1837863073,1158497383,1001940074]),F=64,ht=4,re=4096,He=65536,bt=127;function yt(n){let e=n.byteLength;if(e===0)return null;let t=(e-1)/16|0;if(t===0)return null;let o=t>>>2;o<16&&(o=16),o=mt(o);let r=o-1,a=new Uint32Array(o),i=[],s=-1>>>0;for(let l=t*16-16;l>=0;l-=16){let h=0;for(let b=1;b<=16;b++)h=(h<<8|n[l+b])^q[h>>>23];if(h=h>>>0,h===s)i[i.length-1].ptr=l+16;else{s=h;let b=h&r;i.push({ptr:l+16,val:h}),a[b]++}}let c=new Map;for(let l of i){let h=l.val&r,b=c.get(h);b||(b=[],c.set(h,b)),b.push(l)}for(let[l,h]of c){if(h.length<=F)continue;let b=[],p=h.length/F;for(let y=0;y<F;y++)b.push(h[Math.floor(y*p)]);c.set(l,b),a[l]=F}let f=new Uint32Array(o+1),d=[],u=0;for(let l=0;l<o;l++){f[l]=u;let h=c.get(l);if(h){for(let b of h)d.push(b);u+=h.length}}return f[o]=u,{src:n,hashMask:r,buckets:f,entries:d}}function xt(n,e,t){if(!n||e.byteLength===0)return null;let o=n.src.byteLength,r=e.byteLength,a=n.src,i=new Uint8Array(Math.max(r,1024)),s=0;function c(p){for(;s+p>i.length;){let y=new Uint8Array(i.length*2);y.set(i),i=y}}s=De(i,s,o),s=De(i,s,r),c(18),s++;let f=0,d=0,u=Math.min(16,r);for(let p=0;p<u;p++)i[s++]=e[p],f=(f<<8|e[p])^q[f>>>23],d++;f=f>>>0;let l=u,h=0,b=0;for(;l<r;){if(b<re){f^=ut[e[l-16]],f=((f<<8|e[l])^q[f>>>23])>>>0;let p=f&n.hashMask,y=n.buckets[p],x=n.buckets[p+1];for(let R=y;R<x;R++){let m=n.entries[R];if(m.val!==f)continue;let g=m.ptr,k=a.byteLength-g,E=r-l;if(k>E&&(k=E),k<=b)break;let j=0;for(;j<k&&a[g+j]===e[l+j];)j++;if(j>b&&(b=j,h=g,b>=re))break}}if(b<ht)c(2),d===0&&s++,i[s++]=e[l++],d++,d===bt&&(i[s-d-1]=d,d=0),b=0;else{if(d>0){for(;h>0&&a[h-1]===e[l-1];)if(b++,h--,l--,s--,!(--d>0)){s--,d=-1;break}d>0&&(i[s-d-1]=d),d=0}let p=b>He?b-He:0;b-=p,c(8);let y=s++,x=128;if(h&255&&(i[s++]=h&255,x|=1),h&65280&&(i[s++]=h>>>8&255,x|=2),h&16711680&&(i[s++]=h>>>16&255,x|=4),h&4278190080&&(i[s++]=h>>>24&255,x|=8),b&255&&(i[s++]=b&255,x|=16),b&65280&&(i[s++]=b>>>8&255,x|=32),i[y]=x,l+=b,h+=b,b=p,h>4294967295&&(b=0),b<re){f=0;for(let R=-16;R<0;R++)f=(f<<8|e[l+R])^q[f>>>23];f=f>>>0}}if(t&&s>t)return null}return d>0&&(i[s-d-1]=d),t&&s>t?null:i.subarray(0,s)}var Le={blob:0,tree:1,commit:2,tag:3};function Be(n,e){let t=e?.window??10,o=e?.depth??50,r=n.slice().sort((f,d)=>{let u=Le[f.type]??99,l=Le[d.type]??99;return u!==l?u-l:d.content.byteLength-f.content.byteLength}),a=new Map,i=[],s=[],c=[];for(let f=0;f<r.length;f++){let d=r[f],u={hash:d.hash,type:d.type,content:d.content,depth:0},l=null,h=null,b=Math.max(0,f-t);for(let p=f-1;p>=b;p--){let y=r[p];if(y.type!==d.type||(a.get(y.hash)?.depth??0)>=o||d.content.byteLength<y.content.byteLength>>>5)continue;let m=s[p-b]??null;if(!m)continue;let g=l?l.byteLength:d.content.byteLength/2|0;if(g<16)continue;let k=xt(m,d.content,g);k&&(!l||k.byteLength<l.byteLength)&&(l=k,h=y.hash)}if(l&&h){u.delta=l,u.deltaBase=h;let p=a.get(h);u.depth=(p?.depth??0)+1}a.set(d.hash,u),i.push(u),s.length>=t&&(s.shift(),c.shift()),s.push(yt(d.content)),c.push(d.hash)}return i}function mt(n){let e=n;return e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,e|=e>>16,e++,e<16?16:e}function De(n,e,t){let o=t;do{let r=o&127;o>>>=7,o>0&&(r|=128),n[e++]=r}while(o>0);return e}async function oe(n,e,t){let o=new Set;for(let i of t)await L(n,i,o);let r=[],a=new Set;for(let i of e)await D(n,i,o,a,r);return{count:r.length,objects:gt(r)}}async function Ne(n,e,t){let{count:o,objects:r}=await oe(n,e,t);return{count:o,objects:wt(n,r)}}async function*gt(n){for(let e of n)yield e}async function*wt(n,e){for await(let t of e){let o=await H(n,t.hash);yield{hash:t.hash,type:t.type,content:o.content}}}async function We(n){let e=[];for await(let t of n.objects)e.push(t);return e}async function L(n,e,t){if(t.has(e)||(t.add(e),!await Se(n,e)))return;let o=await H(n,e);switch(o.type){case"commit":{let r=C(o.content);await L(n,r.tree,t);for(let a of r.parents)await L(n,a,t);break}case"tree":{let r=ne(o.content);for(let a of r.entries)await L(n,a.hash,t);break}case"tag":{let r=P(o.content);await L(n,r.object,t);break}case"blob":break}}async function D(n,e,t,o,r){if(o.has(e)||t.has(e))return;o.add(e);let a=await H(n,e);switch(r.push({hash:e,type:a.type}),a.type){case"commit":{let i=C(a.content);await D(n,i.tree,t,o,r);for(let s of i.parents)await D(n,s,t,o,r);break}case"tree":{let i=ne(a.content);for(let s of i.entries)await D(n,s.hash,t,o,r);break}case"tag":{let i=P(a.content);await D(n,i.object,t,o,r);break}case"blob":break}}var Rt=new TextEncoder,_e=new TextDecoder,$e=65520,kt=new Uint8Array([48,48,48,48]);function w(n){let e=typeof n=="string"?Rt.encode(n):n,t=4+e.byteLength;if(t>$e)throw new Error(`pkt-line too long: ${t} bytes (max ${$e})`);let o=t.toString(16).padStart(4,"0"),r=new Uint8Array(t);return r[0]=o.charCodeAt(0),r[1]=o.charCodeAt(1),r[2]=o.charCodeAt(2),r[3]=o.charCodeAt(3),r.set(e,4),r}function O(){return kt.slice()}function v(...n){let e=0;for(let r of n)e+=r.byteLength;let t=new Uint8Array(e),o=0;for(let r of n)t.set(r,o),o+=r.byteLength;return t}function Ge(n){let e=[],t=0;for(;t<n.byteLength;){if(t+4>n.byteLength)throw new Error("Truncated pkt-line header");let o=_e.decode(n.subarray(t,t+4)),r=parseInt(o,16);if(Number.isNaN(r))throw new Error(`Invalid pkt-line length: ${o}`);if(r===0){e.push({type:"flush"}),t+=4;continue}if(r<4)throw new Error(`Invalid pkt-line length: ${r}`);if(t+r>n.byteLength)throw new Error(`Truncated pkt-line: need ${r} bytes at offset ${t}, have ${n.byteLength-t}`);e.push({type:"data",data:n.subarray(t+4,t+r)}),t+=r}return e}function Me(n){if(n.type==="flush")return"";let e=_e.decode(n.data);return e.endsWith(`
|
|
8
|
+
`)?e.slice(0,-1):e}var Fe=65514;function qe(n,e,t,o){let r=[];r.push(w(`# service=${e}
|
|
9
|
+
`)),r.push(O());let a=[...t];o&&a.push(`symref=HEAD:${o}`),a.push("agent=just-git/1.0");let i=a.join(" ");if(n.length===0)r.push(w(`0000000000000000000000000000000000000000 capabilities^{}\0${i}
|
|
10
|
+
`));else for(let s=0;s<n.length;s++){let c=n[s];s===0?r.push(w(`${c.hash} ${c.name}\0${i}
|
|
11
|
+
`)):r.push(w(`${c.hash} ${c.name}
|
|
12
|
+
`))}return r.push(O()),v(...r)}function ze(n){let e=Ge(n),t=[],o=[],r=[];for(let a of e){if(a.type==="flush")continue;let i=Me(a);if(i.startsWith("want ")){let s=i.slice(5);if(t.length===0){let c=s.indexOf(" ");c!==-1?(t.push(s.slice(0,c)),r=s.slice(c+1).split(" ").filter(Boolean)):t.push(s)}else t.push(s)}else i.startsWith("have ")&&o.push(i.slice(5))}return{wants:t,haves:o,capabilities:r}}function I(n,e,t){let o=[];if(t&&t.length>0){for(let a of t)o.push(w(`ACK ${a} common
|
|
13
|
+
`));let r=t[t.length-1];o.push(w(`ACK ${r} ready
|
|
14
|
+
`)),o.push(w(`ACK ${r}
|
|
15
|
+
`))}else o.push(w(`NAK
|
|
16
|
+
`));if(e){let r=0;for(;r<n.byteLength;){let a=Math.min(Fe,n.byteLength-r);o.push(se(1,n.subarray(r,r+a))),r+=a}o.push(O())}else{let r=new Uint8Array(n.byteLength);r.set(n),o.push(r)}return v(...o)}async function*Ke(n,e,t){let o=[];if(t&&t.length>0){for(let a of t)o.push(w(`ACK ${a} common
|
|
17
|
+
`));let r=t[t.length-1];o.push(w(`ACK ${r} ready
|
|
18
|
+
`)),o.push(w(`ACK ${r}
|
|
19
|
+
`))}else o.push(w(`NAK
|
|
20
|
+
`));if(yield v(...o),e){for await(let r of n){let a=0;for(;a<r.byteLength;){let i=Math.min(Fe,r.byteLength-a);yield se(1,r.subarray(a,a+i)),a+=i}}yield O()}else for await(let r of n)yield r}function Ye(n){let e=new TextDecoder,t=[],o=[],r=0;for(;r<n.byteLength&&!(r+4>n.byteLength);){let i=e.decode(n.subarray(r,r+4)),s=parseInt(i,16);if(Number.isNaN(s))break;if(s===0){r+=4;break}if(s<4||r+s>n.byteLength)break;let c=n.subarray(r+4,r+s);r+=s;let f=c.indexOf(0),d;if(f!==-1)d=e.decode(c.subarray(0,f)),o=e.decode(c.subarray(f+1)).replace(/\n$/,"").split(" ").filter(Boolean);else{let l=e.decode(c);d=l.endsWith(`
|
|
21
|
+
`)?l.slice(0,-1):l}let u=d.split(" ");u.length>=3&&t.push({oldHash:u[0],newHash:u[1],refName:u[2]})}let a=r<n.byteLength?n.subarray(r):new Uint8Array(0);return{commands:t,packData:a,capabilities:o}}function z(n,e,t){let o=[];o.push(w(n?`unpack ok
|
|
22
|
+
`:`unpack error
|
|
23
|
+
`));for(let a of e)a.ok?o.push(w(`ok ${a.name}
|
|
24
|
+
`)):o.push(w(`ng ${a.name} ${a.error??"failed"}
|
|
25
|
+
`));o.push(O());let r=v(...o);if(t){let a=[];return a.push(se(1,r)),a.push(O()),v(...a)}return r}function se(n,e){let t=new Uint8Array(1+e.byteLength);return t[0]=n,t.set(e,1),w(t)}var A=class{entries=new Map;currentBytes=0;maxBytes;hits=0;misses=0;constructor(e=256*1024*1024){this.maxBytes=e}static key(e,t,o){if(o.length>0)return null;let r=t.slice().sort();return`${e}\0${r.join(",")}`}get(e){let t=this.entries.get(e);return t?this.hits++:this.misses++,t}set(e,t){if(this.entries.has(e))return;let o=t.packData.byteLength;if(!(o>this.maxBytes)){for(;this.currentBytes+o>this.maxBytes&&this.entries.size>0;){let r=this.entries.keys().next().value;this.currentBytes-=this.entries.get(r).packData.byteLength,this.entries.delete(r)}this.entries.set(e,t),this.currentBytes+=o}}get stats(){return{entries:this.entries.size,bytes:this.currentBytes,hits:this.hits,misses:this.misses}}},Pt=["multi_ack_detailed","no-done","side-band-64k","ofs-delta","include-tag"],Ot=["report-status","side-band-64k","ofs-delta","delete-refs"];async function ae(n){let e=await n.refStore.listRefs("refs"),t=await n.refStore.readRef("HEAD"),o=[],r=null,a;if(t)if(t.type==="symbolic"){a=t.target;let i=await n.refStore.readRef(t.target);i?.type==="direct"&&(r=i.hash)}else r=t.hash;r&&o.push({name:"HEAD",hash:r});for(let i of e)if(o.push({name:i.name,hash:i.hash}),i.name.startsWith("refs/tags/"))try{let s=await n.objectStore.read(i.hash);if(s.type==="tag"){let c=P(s.content);o.push({name:`${i.name}^{}`,hash:c.object})}}catch{}return{refs:o,headTarget:a}}function ie(n,e,t){return qe(n,e,e==="git-upload-pack"?Pt:Ot,t)}async function ce(n,e,t){let{wants:o,haves:r,capabilities:a}=ze(e);if(o.length===0)return I(new Uint8Array(0),!1);let i=a.includes("multi_ack_detailed"),s=a.includes("side-band-64k"),c;if(i&&r.length>0){c=[];for(let d of r)await n.objectStore.exists(d)&&c.push(d);c.length===0&&(c=void 0)}let f=t?.cache&&t.cacheKey?A.key(t.cacheKey,o,r):null;if(f&&t?.cache){let d=t.cache.get(f);if(d)return I(d.packData,s,c)}return t?.noDelta?jt(n,o,r,a,s,c):vt(n,o,r,a,s,c,t,f)}async function jt(n,e,t,o,r,a){let{count:i,objects:s}=await oe(n,e,t);if(i===0){let p=I(new Uint8Array(0),r,a);return new ReadableStream({start(y){y.enqueue(p),y.close()}})}let c=[];for await(let p of s)c.push(p);let f=new Set(c.map(p=>p.hash)),d=[];if(o.includes("include-tag")){let p=await n.refStore.listRefs("refs/tags");for(let y of p)if(!f.has(y.hash))try{let x=await n.objectStore.read(y.hash);if(x.type==="tag"){let R=P(x.content);f.has(R.object)&&d.push({hash:y.hash,type:"tag",content:x.content})}}catch{}}let u=c.length+d.length;async function*l(){for(let p of c){let y=await n.objectStore.read(p.hash);yield{type:y.type,content:y.content}}for(let p of d)yield{type:p.type,content:p.content}}let h=Ae(u,l()),b=Ke(h,r,a);return new ReadableStream({async pull(p){let{value:y,done:x}=await b.next();x?p.close():p.enqueue(y)}})}async function vt(n,e,t,o,r,a,i,s){let c=await Ne(n,e,t);if(c.count===0)return I(new Uint8Array(0),r,a);let f=await We(c),d=new Set(f.map(p=>p.hash));if(o.includes("include-tag")){let p=await n.refStore.listRefs("refs/tags");for(let y of p)if(!d.has(y.hash))try{let x=await n.objectStore.read(y.hash);if(x.type==="tag"){let R=P(x.content);d.has(R.object)&&(f.push({hash:y.hash,type:"tag",content:x.content}),d.add(y.hash))}}catch{}}let u=i?.deltaWindow?{window:i.deltaWindow}:void 0,l=Be(f,u),h=l.map(p=>({hash:p.hash,type:p.type,content:p.content,delta:p.delta,deltaBaseHash:p.deltaBase})),{data:b}=await Ie(h);if(s&&i?.cache){let p=l.filter(y=>y.delta).length;i.cache.set(s,{packData:b,objectCount:f.length,deltaCount:p})}return I(b,r,a)}async function fe(n,e){let{commands:t,packData:o,capabilities:r}=Ye(e),a=!0;if(o.byteLength>0)try{await n.objectStore.ingestPack(o)}catch{a=!1}let i=[];for(let s of t){let c=s.oldHash===K,f=s.newHash===K,d=!1;if(!c&&!f&&a)try{d=await Ce(n,s.oldHash,s.newHash)}catch{}i.push({ref:s.refName,oldHash:c?null:s.oldHash,newHash:s.newHash,isFF:d,isCreate:c,isDelete:f})}return{updates:i,unpackOk:a,capabilities:r}}function It(n){let{resolveRepo:e,hooks:t,basePath:o}=n,r=n.packCache===!1?void 0:new A(n.packCache?.maxBytes);return{async fetch(a){try{let i=new URL(a.url),s=decodeURIComponent(i.pathname);if(o){let c=o.replace(/\/+$/,"");if(!s.startsWith(c))return new Response("Not Found",{status:404});s=s.slice(c.length)}if(s.startsWith("/")||(s=`/${s}`),s.endsWith("/info/refs")&&a.method==="GET"){let c=i.searchParams.get("service");if(c!=="git-upload-pack"&&c!=="git-receive-pack")return new Response("Unsupported service",{status:403});let f=de(s,"/info/refs"),d=await e(f,a);if(d instanceof Response)return d;if(!d)return new Response("Not Found",{status:404});let u=d,{refs:l,headTarget:h}=await ae(u),b=l;if(t?.advertiseRefs){let y=await t.advertiseRefs({repo:u,repoPath:f,refs:l,service:c,request:a});y&&(b=y)}let p=ie(b,c,h);return new Response(p,{headers:{"Content-Type":`application/x-${c}-advertisement`,"Cache-Control":"no-cache"}})}if(s.endsWith("/git-upload-pack")&&a.method==="POST"){let c=de(s,"/git-upload-pack"),f=await e(c,a);if(f instanceof Response)return f;if(!f)return new Response("Not Found",{status:404});let d=f,u=await Ve(a),l=await ce(d,u,{cache:r,cacheKey:c,noDelta:n.packOptions?.noDelta,deltaWindow:n.packOptions?.deltaWindow});return new Response(l,{headers:{"Content-Type":"application/x-git-upload-pack-result"}})}if(s.endsWith("/git-receive-pack")&&a.method==="POST"){let c=de(s,"/git-receive-pack"),f=await e(c,a);if(f instanceof Response)return f;if(!f)return new Response("Not Found",{status:404});let d=f,u=await Ve(a),{updates:l,unpackOk:h,capabilities:b}=await fe(d,u),p=b.includes("side-band-64k"),y=b.includes("report-status");if(!h){if(y){let m=l.map(g=>({name:g.ref,ok:!1,error:"unpack failed"}));return new Response(z(!1,m,p),{headers:{"Content-Type":"application/x-git-receive-pack-result"}})}return new Response(new Uint8Array(0),{headers:{"Content-Type":"application/x-git-receive-pack-result"}})}if(t?.preReceive){let m=await t.preReceive({repo:d,repoPath:c,updates:l,request:a});if(S(m)){if(y){let g=m.message??"pre-receive hook declined",k=l.map(E=>({name:E.ref,ok:!1,error:g}));return new Response(z(!0,k,p),{headers:{"Content-Type":"application/x-git-receive-pack-result"}})}return new Response(new Uint8Array(0),{headers:{"Content-Type":"application/x-git-receive-pack-result"}})}}let x=[],R=[];for(let m of l){if(t?.update){let g=await t.update({repo:d,repoPath:c,update:m,request:a});if(S(g)){x.push({ref:m.ref,ok:!1,error:g.message??"update hook declined"});continue}}try{let g=m.isCreate?null:m.oldHash,k=m.isDelete?null:{type:"direct",hash:m.newHash};if(!await d.refStore.compareAndSwapRef(m.ref,g,k)){x.push({ref:m.ref,ok:!1,error:"failed to lock"});continue}x.push({ref:m.ref,ok:!0}),R.push(m)}catch(g){x.push({ref:m.ref,ok:!1,error:g instanceof Error?g.message:String(g)})}}if(t?.postReceive&&R.length>0)try{await t.postReceive({repo:d,repoPath:c,updates:R,request:a})}catch{}if(y){let m=x.map(g=>({name:g.ref,ok:g.ok,error:g.error}));return new Response(z(!0,m,p),{headers:{"Content-Type":"application/x-git-receive-pack-result"}})}return new Response(new Uint8Array(0),{headers:{"Content-Type":"application/x-git-receive-pack-result"}})}return new Response("Not Found",{status:404})}catch(i){return console.error(" [server] Internal error:",i),new Response("Internal Server Error",{status:500})}}}}function de(n,e){let t=n.slice(0,-e.length);return t.startsWith("/")&&(t=t.slice(1)),t}async function Ve(n){let e=new Uint8Array(await n.arrayBuffer()),t=n.headers.get("content-encoding");if(t==="gzip"||t==="x-gzip"){let o=new DecompressionStream("gzip"),r=o.writable.getWriter();return r.write(e),r.close(),new Uint8Array(await new Response(o.readable).arrayBuffer())}return e}function At(...n){let e=n.filter(s=>s!=null);if(e.length===0)return{};if(e.length===1)return e[0];let t={},o=e.filter(s=>s.preReceive).map(s=>s.preReceive);o.length>0&&(t.preReceive=async s=>{for(let c of o){let f=await c(s);if(S(f))return f}});let r=e.filter(s=>s.update).map(s=>s.update);r.length>0&&(t.update=async s=>{for(let c of r){let f=await c(s);if(S(f))return f}});let a=e.filter(s=>s.postReceive).map(s=>s.postReceive);a.length>0&&(t.postReceive=async s=>{for(let c of a)try{await c(s)}catch{}});let i=e.filter(s=>s.advertiseRefs).map(s=>s.advertiseRefs);return i.length>0&&(t.advertiseRefs=async s=>{let c=s.refs;for(let f of i){let d=await f({...s,refs:c});d&&(c=d)}return c}),t}function Et(n){let{protectedBranches:e=[],denyNonFastForward:t=!1,denyDeletes:o=!1,authorize:r,onPush:a}=n,i=new Set(e.map(c=>c.startsWith("refs/")?c:`refs/heads/${c}`)),s={};return(r||i.size>0)&&(s.preReceive=async c=>{if(r&&!await r(c.request))return{reject:!0,message:"unauthorized"};for(let f of c.updates)if(i.has(f.ref)){if(f.isDelete)return{reject:!0,message:`cannot delete protected branch ${f.ref}`};if(!f.isCreate&&!f.isFF)return{reject:!0,message:`non-fast-forward push to protected branch ${f.ref}`}}}),(t||o)&&(s.update=async c=>{if(o&&c.update.isDelete)return{reject:!0,message:"ref deletion denied"};if(t&&!c.update.isCreate&&!c.update.isDelete&&!c.update.isFF)return{reject:!0,message:"non-fast-forward"}}),a&&(s.postReceive=a),s}var St=`
|
|
26
|
+
CREATE TABLE IF NOT EXISTS git_objects (
|
|
27
|
+
repo_id TEXT NOT NULL,
|
|
28
|
+
hash TEXT NOT NULL,
|
|
29
|
+
type TEXT NOT NULL,
|
|
30
|
+
content BLOB NOT NULL,
|
|
31
|
+
PRIMARY KEY (repo_id, hash)
|
|
32
|
+
) WITHOUT ROWID;
|
|
33
|
+
|
|
34
|
+
CREATE TABLE IF NOT EXISTS git_refs (
|
|
35
|
+
repo_id TEXT NOT NULL,
|
|
36
|
+
name TEXT NOT NULL,
|
|
37
|
+
type TEXT NOT NULL CHECK(type IN ('direct', 'symbolic')),
|
|
38
|
+
hash TEXT,
|
|
39
|
+
target TEXT,
|
|
40
|
+
PRIMARY KEY (repo_id, name)
|
|
41
|
+
) WITHOUT ROWID;
|
|
42
|
+
`;function Ut(n){return{objInsert:n.prepare("INSERT OR IGNORE INTO git_objects (repo_id, hash, type, content) VALUES (?, ?, ?, ?)"),objRead:n.prepare("SELECT type, content FROM git_objects WHERE repo_id = ? AND hash = ?"),objExists:n.prepare("SELECT 1 FROM git_objects WHERE repo_id = ? AND hash = ? LIMIT 1"),objPrefix:n.prepare("SELECT hash FROM git_objects WHERE repo_id = ? AND hash GLOB ?"),objDeleteAll:n.prepare("DELETE FROM git_objects WHERE repo_id = ?"),refRead:n.prepare("SELECT type, hash, target FROM git_refs WHERE repo_id = ? AND name = ?"),refWrite:n.prepare("INSERT OR REPLACE INTO git_refs (repo_id, name, type, hash, target) VALUES (?, ?, ?, ?, ?)"),refDelete:n.prepare("DELETE FROM git_refs WHERE repo_id = ? AND name = ?"),refList:n.prepare("SELECT name, type, hash, target FROM git_refs WHERE repo_id = ? AND name GLOB ?"),refListAll:n.prepare("SELECT name, type, hash, target FROM git_refs WHERE repo_id = ?"),refDeleteAll:n.prepare("DELETE FROM git_refs WHERE repo_id = ?")}}var le=class{db;stmts;ingestTx;constructor(e){this.db=e,e.run(St),this.stmts=Ut(e),this.ingestTx=e.transaction(t=>{for(let o of t)this.stmts.objInsert.run(o.repoId,o.hash,o.type,o.content)})}repo(e){return{objectStore:new pe(this.stmts,this.ingestTx,e),refStore:new ue(this.stmts,this.db,e)}}deleteRepo(e){this.stmts.objDeleteAll.run(e),this.stmts.refDeleteAll.run(e)}},pe=class{constructor(e,t,o){this.stmts=e;this.ingestTx=t;this.repoId=o;this.cache=new _}cache;async write(e,t){let o=Ee(e,t),r=await me(o);return this.stmts.objInsert.run(this.repoId,r,e,t),r}async read(e){let t=this.cache.get(e);if(t)return t;let o=this.stmts.objRead.get(this.repoId,e);if(!o)throw new Error(`object ${e} not found`);let r={type:o.type,content:new Uint8Array(o.content)};return this.cache.set(e,r),r}async exists(e){return this.stmts.objExists.get(this.repoId,e)!==null}async ingestPack(e){if(e.byteLength<32||new DataView(e.buffer,e.byteOffset,e.byteLength).getUint32(8)===0)return 0;let r=await ve(e,async i=>{let s=this.stmts.objRead.get(this.repoId,i);return s?{type:s.type,content:new Uint8Array(s.content)}:null}),a=r.map(i=>({repoId:this.repoId,hash:i.hash,type:i.type,content:i.content}));return this.ingestTx(a),r.length}async findByPrefix(e){return e.length<4?[]:this.stmts.objPrefix.all(this.repoId,`${e}*`).map(o=>o.hash)}},ue=class{constructor(e,t,o){this.stmts=e;this.repoId=o;let r=e,a=o;this.casTx=t.transaction((i,s,c)=>{let f=r.refRead.get(a,i),d=null;if(f&&(f.type==="direct"?d=f.hash:f.type==="symbolic"&&f.target&&(d=he(r,a,f.target))),s===null){if(f!==null)return!1}else if(d!==s)return!1;return c===null?r.refDelete.run(a,i):c.type==="symbolic"?r.refWrite.run(a,i,"symbolic",null,c.target):r.refWrite.run(a,i,"direct",c.hash,null),!0})}casTx;async readRef(e){let t=this.stmts.refRead.get(this.repoId,e);return t?t.type==="symbolic"?{type:"symbolic",target:t.target}:{type:"direct",hash:t.hash}:null}async writeRef(e,t){t.type==="symbolic"?this.stmts.refWrite.run(this.repoId,e,"symbolic",null,t.target):this.stmts.refWrite.run(this.repoId,e,"direct",t.hash,null)}async deleteRef(e){this.stmts.refDelete.run(this.repoId,e)}async compareAndSwapRef(e,t,o){return this.casTx(e,t,o)}async listRefs(e){let t;e?t=this.stmts.refList.all(this.repoId,`${e}*`):t=this.stmts.refListAll.all(this.repoId);let o=[];for(let r of t)if(r.type==="direct"&&r.hash)o.push({name:r.name,hash:r.hash});else if(r.type==="symbolic"&&r.target){let a=he(this.stmts,this.repoId,r.target);a&&o.push({name:r.name,hash:a})}return o}};function he(n,e,t,o=0){if(o>10)return null;let r=n.refRead.get(e,t);return r?r.type==="direct"?r.hash:r.type==="symbolic"&&r.target?he(n,e,r.target,o+1):null:null}export{A as PackCache,le as SqliteStorage,ie as buildRefAdvertisementBytes,ae as collectRefs,At as composeHooks,It as createGitServer,Et as createStandardHooks,ce as handleUploadPack,fe as ingestReceivePack};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "just-git",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Pure TypeScript git implementation for virtual command line environments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -28,6 +28,14 @@
|
|
|
28
28
|
".": {
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./server": {
|
|
33
|
+
"types": "./dist/server/index.d.ts",
|
|
34
|
+
"import": "./dist/server/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./repo": {
|
|
37
|
+
"types": "./dist/repo/index.d.ts",
|
|
38
|
+
"import": "./dist/repo/index.js"
|
|
31
39
|
}
|
|
32
40
|
},
|
|
33
41
|
"scripts": {
|