git-fs-s3 0.3.13 → 0.5.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.
Files changed (65) hide show
  1. package/README.md +23 -17
  2. package/dist/{chunk-3BDDEGAP.js → chunk-2JP4MS6H.js} +7 -3
  3. package/dist/chunk-2JP4MS6H.js.map +1 -0
  4. package/dist/{chunk-YQFNY6PG.js → chunk-FIFWOMPM.js} +1 -1
  5. package/dist/chunk-FIFWOMPM.js.map +1 -0
  6. package/dist/chunk-RMAOPWHJ.js +38 -0
  7. package/dist/chunk-RMAOPWHJ.js.map +1 -0
  8. package/dist/http.js +257 -81
  9. package/dist/http.js.map +1 -1
  10. package/dist/index.js +115 -18
  11. package/dist/index.js.map +1 -1
  12. package/dist/ops.js +2 -2
  13. package/dist/types/cache.d.ts +81 -0
  14. package/dist/types/edge-utils.d.ts +43 -0
  15. package/dist/types/errors.d.ts +16 -0
  16. package/dist/types/git-errors.d.ts +70 -0
  17. package/dist/types/git-fs.d.ts +60 -0
  18. package/dist/types/git-layout.d.ts +52 -0
  19. package/dist/types/http/index.d.ts +7 -0
  20. package/dist/types/http/info-refs.d.ts +27 -0
  21. package/dist/types/http/pack-reuse.d.ts +26 -0
  22. package/dist/types/http/pkt-line.d.ts +14 -0
  23. package/dist/types/http/reachability.d.ts +36 -0
  24. package/dist/types/http/receive-pack.d.ts +54 -0
  25. package/dist/types/http/repack.d.ts +24 -0
  26. package/dist/types/http/types.d.ts +40 -0
  27. package/dist/types/http/upload-pack.d.ts +21 -0
  28. package/dist/types/index.d.ts +11 -0
  29. package/dist/types/ops/branch.d.ts +38 -0
  30. package/dist/types/ops/commit.d.ts +45 -0
  31. package/dist/types/ops/diff.d.ts +24 -0
  32. package/dist/types/ops/file-history.d.ts +41 -0
  33. package/dist/types/ops/history.d.ts +85 -0
  34. package/dist/types/ops/index.d.ts +9 -0
  35. package/dist/types/ops/last-commit.d.ts +23 -0
  36. package/dist/types/ops/merge.d.ts +28 -0
  37. package/dist/types/ops/tree.d.ts +19 -0
  38. package/dist/{types-BHoHOaQt.d.ts → types/ops/types.d.ts} +7 -9
  39. package/dist/types/pack-files.d.ts +28 -0
  40. package/dist/types/path.d.ts +9 -0
  41. package/dist/types/refs.d.ts +54 -0
  42. package/dist/types/retry.d.ts +52 -0
  43. package/dist/types/stores/memory.d.ts +16 -0
  44. package/dist/{s3.d.ts → types/stores/s3.d.ts} +4 -7
  45. package/dist/{types-QgIkUR_q.d.cts → types/types.d.ts} +22 -23
  46. package/package.json +19 -23
  47. package/dist/chunk-3BDDEGAP.js.map +0 -1
  48. package/dist/chunk-YQFNY6PG.js.map +0 -1
  49. package/dist/http.cjs +0 -793
  50. package/dist/http.cjs.map +0 -1
  51. package/dist/http.d.cts +0 -206
  52. package/dist/http.d.ts +0 -206
  53. package/dist/index.cjs +0 -815
  54. package/dist/index.cjs.map +0 -1
  55. package/dist/index.d.cts +0 -381
  56. package/dist/index.d.ts +0 -381
  57. package/dist/ops.cjs +0 -1057
  58. package/dist/ops.cjs.map +0 -1
  59. package/dist/ops.d.cts +0 -302
  60. package/dist/ops.d.ts +0 -302
  61. package/dist/s3.cjs +0 -123
  62. package/dist/s3.cjs.map +0 -1
  63. package/dist/s3.d.cts +0 -36
  64. package/dist/types-BHoHOaQt.d.cts +0 -53
  65. package/dist/types-QgIkUR_q.d.ts +0 -121
package/dist/http.cjs DELETED
@@ -1,793 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/http/index.ts
31
- var http_exports = {};
32
- __export(http_exports, {
33
- FLUSH: () => FLUSH,
34
- REPACK_PACK_COUNT_THRESHOLD: () => REPACK_PACK_COUNT_THRESHOLD,
35
- applyReceivePack: () => applyReceivePack,
36
- applyRefUpdates: () => applyRefUpdates,
37
- collectReachableOids: () => collectReachableOids,
38
- ensureRepoInitialized: () => ensureRepoInitialized,
39
- handleInfoRefs: () => handleInfoRefs,
40
- handleUploadPack: () => handleUploadPack,
41
- indexIncomingPack: () => indexIncomingPack,
42
- listAllRefs: () => listAllRefs,
43
- parsePktLines: () => parsePktLines,
44
- parseReceivePackBody: () => parseReceivePackBody,
45
- pktLine: () => pktLine,
46
- pktLineBuffer: () => pktLineBuffer,
47
- rawFs: () => rawFs,
48
- receivePackResponse: () => receivePackResponse,
49
- repackRepository: () => repackRepository,
50
- sideBandPackfile: () => sideBandPackfile
51
- });
52
- module.exports = __toCommonJS(http_exports);
53
-
54
- // src/http/info-refs.ts
55
- var import_isomorphic_git2 = __toESM(require("isomorphic-git"), 1);
56
-
57
- // src/edge-utils.ts
58
- var textEncoder = new TextEncoder();
59
- var textDecoder = new TextDecoder();
60
- function encodeUtf8(data) {
61
- return textEncoder.encode(data);
62
- }
63
- function decodeUtf8(data) {
64
- return textDecoder.decode(data);
65
- }
66
- function decodeAscii(data) {
67
- let s = "";
68
- for (let i = 0; i < data.length; i++)
69
- s += String.fromCharCode(data[i]);
70
- return s;
71
- }
72
- function concat(...parts) {
73
- let total = 0;
74
- for (const p of parts) total += p.length;
75
- const out = new Uint8Array(total);
76
- let offset = 0;
77
- for (const p of parts) {
78
- out.set(p, offset);
79
- offset += p.length;
80
- }
81
- return out;
82
- }
83
- function toHex(data) {
84
- let hex = "";
85
- for (let i = 0; i < data.length; i++)
86
- hex += data[i].toString(16).padStart(2, "0");
87
- return hex;
88
- }
89
- function fromHex(hex) {
90
- const bytes = new Uint8Array(hex.length / 2);
91
- for (let i = 0; i < bytes.length; i++) {
92
- bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
93
- }
94
- return bytes;
95
- }
96
- async function sha1(data) {
97
- const bytes = typeof data === "string" ? encodeUtf8(data) : data;
98
- const hash = await globalThis.crypto.subtle.digest("SHA-1", bytes);
99
- return toHex(new Uint8Array(hash));
100
- }
101
- async function deflate(data) {
102
- const stream = new Blob([data]).stream().pipeThrough(new CompressionStream("deflate"));
103
- return new Uint8Array(await new Response(stream).arrayBuffer());
104
- }
105
-
106
- // src/ops/branch.ts
107
- var import_isomorphic_git = __toESM(require("isomorphic-git"), 1);
108
-
109
- // src/refs.ts
110
- var BAD_REF_COMPONENT = (
111
- // biome-ignore lint/suspicious/noControlCharactersInRegex: control chars are exactly what git's own ref-name rules reject — this needs to match the same range.
112
- /(^|[/.])([/.]|$)|^@$|@\{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/
113
- );
114
- function isSafeFullRefName(ref) {
115
- if (!ref.startsWith("refs/heads/") && !ref.startsWith("refs/tags/")) {
116
- return false;
117
- }
118
- return !BAD_REF_COMPONENT.test(ref);
119
- }
120
-
121
- // src/ops/branch.ts
122
- var FULL_SHA_RE = /^[0-9a-f]{40}$/i;
123
- function getPromisesFs(fs) {
124
- const promises = fs.promises;
125
- return promises ?? null;
126
- }
127
- function decodeFileContent(content) {
128
- return typeof content === "string" ? content : new TextDecoder().decode(content);
129
- }
130
- async function resolveLooseRefFast(repo, ref) {
131
- const promisesFs = getPromisesFs(repo.fs);
132
- if (promisesFs) {
133
- try {
134
- const content = await promisesFs.readFile(
135
- `${repo.gitdir}/${ref}`,
136
- "utf8"
137
- );
138
- const oid = decodeFileContent(content).trim();
139
- if (FULL_SHA_RE.test(oid)) return oid;
140
- } catch {
141
- }
142
- }
143
- return import_isomorphic_git.default.resolveRef({ ...repo, ref });
144
- }
145
-
146
- // src/http/pkt-line.ts
147
- var FLUSH = new Uint8Array([
148
- 48,
149
- 48,
150
- 48,
151
- 48
152
- ]);
153
- function pktLine(data) {
154
- const body = encodeUtf8(data);
155
- const len = (body.length + 4).toString(16).padStart(4, "0");
156
- return concat(encodeUtf8(len), body);
157
- }
158
- function pktLineBuffer(body) {
159
- const len = (body.length + 4).toString(16).padStart(4, "0");
160
- return concat(encodeUtf8(len), body);
161
- }
162
- function parsePktLines(buf) {
163
- const lines = [];
164
- let pos = 0;
165
- while (pos + 4 <= buf.length) {
166
- const len = Number.parseInt(decodeAscii(buf.subarray(pos, pos + 4)), 16);
167
- if (len === 0) {
168
- lines.push(null);
169
- pos += 4;
170
- } else if (len >= 4) {
171
- lines.push(decodeUtf8(buf.subarray(pos + 4, pos + len)));
172
- pos += len;
173
- } else {
174
- break;
175
- }
176
- }
177
- return lines;
178
- }
179
- var SIDE_BAND_MAX_CHUNK = 65515;
180
- function sideBandPackfile(packData) {
181
- const parts = [];
182
- for (let offset = 0; offset < packData.length; offset += SIDE_BAND_MAX_CHUNK) {
183
- const chunk = packData.subarray(offset, offset + SIDE_BAND_MAX_CHUNK);
184
- parts.push(pktLineBuffer(concat(new Uint8Array([1]), chunk)));
185
- }
186
- parts.push(FLUSH);
187
- return concat(...parts);
188
- }
189
-
190
- // src/http/types.ts
191
- var runStep = (hooks, label, fn) => hooks?.step ? hooks.step(label, fn) : fn();
192
- function rawFs(repo) {
193
- const fs = repo.fs;
194
- if (!fs?.promises) {
195
- throw new TypeError(
196
- "This operation needs a promise fs (`fs.promises`) \u2014 node:fs and createGitFs both provide one"
197
- );
198
- }
199
- return fs.promises;
200
- }
201
-
202
- // src/http/info-refs.ts
203
- async function listAllRefs(repo, defaultBranch = "main") {
204
- const [branches, tags, headSymref] = await Promise.all([
205
- import_isomorphic_git2.default.listBranches(repo),
206
- import_isomorphic_git2.default.listTags(repo),
207
- // Wrap with Promise.resolve so a mock/stub returning undefined doesn't crash .then()
208
- Promise.resolve(import_isomorphic_git2.default.currentBranch({ ...repo, fullname: true })).then((cb) => cb ?? `refs/heads/${defaultBranch}`).catch(() => `refs/heads/${defaultBranch}`)
209
- ]);
210
- const [branchRefs, tagRefs] = await Promise.all([
211
- Promise.all(
212
- branches.map(async (branch) => {
213
- try {
214
- const oid = await resolveLooseRefFast(repo, `refs/heads/${branch}`);
215
- return { name: `refs/heads/${branch}`, oid };
216
- } catch {
217
- return null;
218
- }
219
- })
220
- ),
221
- Promise.all(
222
- tags.map(async (tag) => {
223
- try {
224
- const oid = await resolveLooseRefFast(repo, `refs/tags/${tag}`);
225
- return { name: `refs/tags/${tag}`, oid };
226
- } catch {
227
- return null;
228
- }
229
- })
230
- )
231
- ]);
232
- const headOid = branchRefs.find((r) => r?.name === headSymref)?.oid ?? await import_isomorphic_git2.default.resolveRef({ ...repo, ref: "HEAD" }).catch(() => null);
233
- const refs = [];
234
- if (headOid) refs.push({ name: "HEAD", oid: headOid });
235
- for (const r of branchRefs) if (r) refs.push(r);
236
- for (const r of tagRefs) if (r) refs.push(r);
237
- return { refs, headSymref };
238
- }
239
- async function handleInfoRefs(repo, options, hooks) {
240
- const { service } = options;
241
- const agent = options.agent ?? "git-fs-s3";
242
- const { refs, headSymref } = await runStep(
243
- hooks,
244
- "listAllRefs",
245
- () => listAllRefs(repo, options.defaultBranch ?? "main")
246
- );
247
- const isUpload = service === "git-upload-pack";
248
- const caps = isUpload ? `no-progress side-band-64k symref=HEAD:${headSymref} allow-tip-sha1-in-want allow-reachable-sha1-in-want agent=${agent}` : `delete-refs report-status no-done agent=${agent}`;
249
- const parts = [pktLine(`# service=${service}
250
- `), FLUSH];
251
- if (refs.length === 0) {
252
- parts.push(
253
- pktLine(
254
- `0000000000000000000000000000000000000000 capabilities^{}\0${caps}
255
- `
256
- )
257
- );
258
- } else {
259
- let first = true;
260
- for (const { name, oid } of refs) {
261
- parts.push(
262
- pktLine(first ? `${oid} ${name}\0${caps}
263
- ` : `${oid} ${name}
264
- `)
265
- );
266
- first = false;
267
- }
268
- }
269
- parts.push(FLUSH);
270
- return {
271
- status: 200,
272
- headers: {
273
- "Content-Type": `application/x-${service}-advertisement`,
274
- "Cache-Control": "no-cache"
275
- },
276
- body: concat(...parts)
277
- };
278
- }
279
-
280
- // src/http/reachability.ts
281
- var import_isomorphic_git3 = __toESM(require("isomorphic-git"), 1);
282
- var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
283
- var MISSING_OBJECT_RETRY_DELAY_MS = 200;
284
- async function collectReachableOids(repo, startOids, hooks, haveOids) {
285
- const have = haveOids instanceof Set ? haveOids : new Set(haveOids ?? []);
286
- const seen = /* @__PURE__ */ new Set();
287
- let complete = true;
288
- const promises = /* @__PURE__ */ new Map();
289
- async function readAndVisitChildren(oid) {
290
- const obj = await import_isomorphic_git3.default.readObject({ ...repo, oid });
291
- seen.add(oid);
292
- let children = [];
293
- if (obj.type === "commit") {
294
- const { commit } = await import_isomorphic_git3.default.readCommit({ ...repo, oid });
295
- children = [commit.tree, ...commit.parent];
296
- } else if (obj.type === "tree") {
297
- const { tree } = await import_isomorphic_git3.default.readTree({ ...repo, oid });
298
- children = tree.map((e) => e.oid);
299
- } else if (obj.type === "tag") {
300
- const { tag } = await import_isomorphic_git3.default.readTag({ ...repo, oid });
301
- children = [tag.object];
302
- }
303
- await Promise.all(children.map(visit));
304
- }
305
- function visit(oid) {
306
- const existing = promises.get(oid);
307
- if (existing) return existing;
308
- if (have.has(oid)) {
309
- seen.add(oid);
310
- const p2 = Promise.resolve();
311
- promises.set(oid, p2);
312
- return p2;
313
- }
314
- const p = (async () => {
315
- try {
316
- await readAndVisitChildren(oid);
317
- } catch {
318
- try {
319
- await delay(MISSING_OBJECT_RETRY_DELAY_MS);
320
- await readAndVisitChildren(oid);
321
- } catch (err) {
322
- complete = false;
323
- hooks?.onWarn?.(`missing object ${oid}`, err);
324
- }
325
- }
326
- })();
327
- promises.set(oid, p);
328
- return p;
329
- }
330
- await Promise.all(startOids.map(visit));
331
- return { oids: Array.from(seen), complete };
332
- }
333
-
334
- // src/http/receive-pack.ts
335
- var import_isomorphic_git5 = __toESM(require("isomorphic-git"), 1);
336
-
337
- // src/http/repack.ts
338
- var import_isomorphic_git4 = __toESM(require("isomorphic-git"), 1);
339
- var PACK_OBJECT_TYPE_BITS = {
340
- commit: 16,
341
- tree: 32,
342
- blob: 48,
343
- tag: 64
344
- };
345
- function encodePackObjectHeader(type, length) {
346
- const bytes = [];
347
- let more = length > 15;
348
- bytes.push(
349
- (more ? 128 : 0) | PACK_OBJECT_TYPE_BITS[type] | length & 15
350
- );
351
- length >>>= 4;
352
- while (more) {
353
- more = length > 127;
354
- bytes.push((more ? 128 : 0) | length & 127);
355
- length >>>= 7;
356
- }
357
- return new Uint8Array(bytes);
358
- }
359
- async function hashGitObject(type, content) {
360
- const prefix = encodeUtf8(`${type} ${content.length}\0`);
361
- return sha1(concat(prefix, content));
362
- }
363
- async function readAndVerifyObjects(repo, oids) {
364
- const objects = /* @__PURE__ */ new Map();
365
- const BATCH_SIZE = 100;
366
- for (let i = 0; i < oids.length; i += BATCH_SIZE) {
367
- const batch = oids.slice(i, i + BATCH_SIZE);
368
- const entries = await Promise.all(
369
- batch.map(async (oid) => {
370
- const { type, object } = await import_isomorphic_git4.default.readObject({
371
- ...repo,
372
- oid,
373
- format: "content"
374
- });
375
- const content = object;
376
- const actualOid = await hashGitObject(type, content);
377
- if (actualOid !== oid) {
378
- throw new Error(
379
- `repack: object ${oid} failed independent SHA-1 verification (recomputed ${actualOid}) \u2014 refusing to trust this read, aborting repack`
380
- );
381
- }
382
- return { oid, type, content };
383
- })
384
- );
385
- for (const { oid, type, content } of entries) {
386
- objects.set(oid, { type, content });
387
- }
388
- }
389
- return objects;
390
- }
391
- async function buildVerifiedPack(oids, objects) {
392
- const header = new Uint8Array(12);
393
- const view = new DataView(header.buffer);
394
- header.set(encodeUtf8("PACK"), 0);
395
- view.setUint32(4, 2, false);
396
- view.setUint32(8, oids.length, false);
397
- const chunks = [header];
398
- const BATCH_SIZE = 100;
399
- for (let i = 0; i < oids.length; i += BATCH_SIZE) {
400
- const batch = oids.slice(i, i + BATCH_SIZE);
401
- const encoded = await Promise.all(
402
- batch.map(async (oid) => {
403
- const entry = objects.get(oid);
404
- if (!entry) {
405
- throw new Error(`repack: missing verified object for ${oid}`);
406
- }
407
- const objHeader = encodePackObjectHeader(
408
- entry.type,
409
- entry.content.length
410
- );
411
- const compressed = await deflate(entry.content);
412
- return concat(objHeader, compressed);
413
- })
414
- );
415
- chunks.push(...encoded);
416
- }
417
- const packBody = concat(...chunks);
418
- const trailer = fromHex(await sha1(packBody));
419
- return concat(packBody, trailer);
420
- }
421
- var REPACK_PACK_COUNT_THRESHOLD = 4;
422
- async function countPacks(repo) {
423
- try {
424
- const entries = await rawFs(repo).readdir(`${repo.gitdir}/objects/pack`);
425
- return entries.filter((f) => f.endsWith(".pack")).length;
426
- } catch {
427
- return 0;
428
- }
429
- }
430
- async function repackRepository(repo, options, hooks) {
431
- const threshold = options?.threshold ?? REPACK_PACK_COUNT_THRESHOLD;
432
- const fsp = rawFs(repo);
433
- try {
434
- if (await countPacks(repo) < threshold) {
435
- return [];
436
- }
437
- const [branches, tags] = await Promise.all([
438
- import_isomorphic_git4.default.listBranches(repo),
439
- import_isomorphic_git4.default.listTags(repo)
440
- ]);
441
- const refNames = [
442
- ...branches.map((b) => `refs/heads/${b}`),
443
- ...tags.map((t) => `refs/tags/${t}`)
444
- ];
445
- const tipOids = (await Promise.all(
446
- refNames.map(
447
- (ref) => import_isomorphic_git4.default.resolveRef({ ...repo, ref }).catch(() => null)
448
- )
449
- )).filter((oid) => oid !== null);
450
- if (tipOids.length === 0) return [];
451
- const { oids, complete } = await collectReachableOids(repo, tipOids, hooks);
452
- if (!complete || oids.length === 0) return [];
453
- const objects = await readAndVerifyObjects(repo, oids);
454
- const packBuffer = await buildVerifiedPack(oids, objects);
455
- const packDir = `${repo.gitdir}/objects/pack`;
456
- await fsp.mkdir(packDir, { recursive: true });
457
- const newBase = `pack-${Date.now()}`;
458
- const newPackFile = `${newBase}.pack`;
459
- const newIdxFile = `${newBase}.idx`;
460
- await fsp.writeFile(`${packDir}/${newPackFile}`, packBuffer);
461
- const { oids: indexedOids } = await import_isomorphic_git4.default.indexPack({
462
- ...repo,
463
- dir: packDir,
464
- filepath: newPackFile
465
- });
466
- const expected = new Set(oids);
467
- const indexed = new Set(indexedOids);
468
- if (indexed.size !== expected.size || oids.some((oid) => !indexed.has(oid))) {
469
- await fsp.unlink(`${packDir}/${newPackFile}`).catch(() => {
470
- });
471
- await fsp.unlink(`${packDir}/${newIdxFile}`).catch(() => {
472
- });
473
- throw new Error(
474
- "repack: indexed pack's oid set didn't match the verified reachable set \u2014 aborting"
475
- );
476
- }
477
- const allEntries = await fsp.readdir(packDir).catch(() => []);
478
- const staleFiles = allEntries.filter(
479
- (f) => f !== newPackFile && f !== newIdxFile && (f.endsWith(".pack") || f.endsWith(".idx") || f.endsWith(".keep"))
480
- );
481
- await Promise.all(
482
- staleFiles.map((f) => fsp.unlink(`${packDir}/${f}`).catch(() => {
483
- }))
484
- );
485
- return staleFiles.map((f) => `objects/pack/${f}`);
486
- } catch (err) {
487
- hooks?.onWarn?.("repack failed (non-fatal)", err);
488
- return [];
489
- }
490
- }
491
-
492
- // src/http/receive-pack.ts
493
- var ZERO_OID = "0".repeat(40);
494
- function parseReceivePackBody(body) {
495
- const refUpdates = [];
496
- let pos = 0;
497
- while (pos + 4 <= body.length) {
498
- const len = Number.parseInt(decodeAscii(body.subarray(pos, pos + 4)), 16);
499
- if (len === 0) {
500
- pos += 4;
501
- break;
502
- }
503
- if (len < 4) break;
504
- const line = decodeUtf8(body.subarray(pos + 4, pos + len)).replace(/\n$/, "").split("\0")[0];
505
- pos += len;
506
- const parts = (line ?? "").split(" ");
507
- const [oldOid, newOid, refName] = parts;
508
- if (oldOid && newOid && refName) {
509
- refUpdates.push({ oldOid, newOid, refName });
510
- }
511
- }
512
- return { refUpdates, packData: body.subarray(pos) };
513
- }
514
- async function ensureRepoInitialized(repo, defaultBranch = "main") {
515
- const fsp = rawFs(repo);
516
- try {
517
- await fsp.stat(`${repo.gitdir}/HEAD`);
518
- } catch {
519
- await fsp.mkdir(repo.gitdir, { recursive: true }).catch(() => {
520
- });
521
- await import_isomorphic_git5.default.init({ ...repo, dir: repo.gitdir, defaultBranch, bare: true });
522
- }
523
- }
524
- async function indexIncomingPack(repo, packData, hooks) {
525
- if (packData.length < 4) return [];
526
- const packName = await runStep(
527
- hooks,
528
- "write + indexPack incoming pack",
529
- async () => {
530
- const fsp = rawFs(repo);
531
- const packDir = `${repo.gitdir}/objects/pack`;
532
- await fsp.mkdir(packDir, { recursive: true });
533
- const packName2 = `recv-${Date.now()}`;
534
- await fsp.writeFile(`${packDir}/${packName2}.pack`, packData);
535
- await import_isomorphic_git5.default.indexPack({
536
- ...repo,
537
- dir: packDir,
538
- filepath: `${packName2}.pack`
539
- });
540
- return packName2;
541
- }
542
- );
543
- return [
544
- `${repo.gitdir}/objects/pack/${packName}.pack`,
545
- `${repo.gitdir}/objects/pack/${packName}.idx`
546
- ];
547
- }
548
- async function checkRefCas(repo, refName, oldOid) {
549
- if (!isSafeFullRefName(refName)) {
550
- return { refName, ok: false, reason: "invalid ref name" };
551
- }
552
- const currentOid = await import_isomorphic_git5.default.resolveRef({ ...repo, ref: refName }).catch(() => ZERO_OID);
553
- return currentOid === oldOid ? null : {
554
- refName,
555
- ok: false,
556
- reason: "non-fast-forward, ref updated by another push"
557
- };
558
- }
559
- async function collectExistingRefTips(repo) {
560
- const [branches, tags] = await Promise.all([
561
- import_isomorphic_git5.default.listBranches(repo),
562
- import_isomorphic_git5.default.listTags(repo)
563
- ]);
564
- const refs = [
565
- ...branches.map((b) => `refs/heads/${b}`),
566
- ...tags.map((t) => `refs/tags/${t}`)
567
- ];
568
- const oids = await Promise.all(
569
- refs.map((ref) => import_isomorphic_git5.default.resolveRef({ ...repo, ref }).catch(() => null))
570
- );
571
- return new Set(oids.filter((oid) => oid !== null));
572
- }
573
- async function validateRefUpdates(repo, refUpdates) {
574
- return Promise.all(
575
- refUpdates.map(
576
- async ({ oldOid, refName }) => await checkRefCas(repo, refName, oldOid) ?? { refName, ok: true }
577
- )
578
- );
579
- }
580
- async function applyRefUpdates(repo, refUpdates, hooks) {
581
- return runStep(
582
- hooks,
583
- "apply ref updates",
584
- () => Promise.all(
585
- refUpdates.map(async ({ oldOid, newOid, refName }) => {
586
- const failure = await checkRefCas(repo, refName, oldOid);
587
- if (failure) return failure;
588
- if (newOid === ZERO_OID) {
589
- await import_isomorphic_git5.default.deleteRef({ ...repo, ref: refName }).catch(() => {
590
- });
591
- } else {
592
- await import_isomorphic_git5.default.writeRef({
593
- ...repo,
594
- ref: refName,
595
- value: newOid,
596
- force: true
597
- });
598
- }
599
- return { refName, ok: true };
600
- })
601
- )
602
- );
603
- }
604
- function receivePackResponse(results) {
605
- const responseBody = concat(
606
- pktLine("unpack ok\n"),
607
- ...results.map(
608
- ({ refName, ok, reason }) => pktLine(ok ? `ok ${refName}
609
- ` : `ng ${refName} ${reason}
610
- `)
611
- ),
612
- FLUSH
613
- );
614
- return {
615
- status: 200,
616
- headers: {
617
- "Content-Type": "application/x-git-receive-pack-result",
618
- "Cache-Control": "no-cache"
619
- },
620
- body: responseBody
621
- };
622
- }
623
- async function applyReceivePack(repo, parsed, options, hooks) {
624
- await ensureRepoInitialized(repo, options?.defaultBranch ?? "main");
625
- const preflight = await validateRefUpdates(repo, parsed.refUpdates);
626
- const acceptedUpdates = parsed.refUpdates.filter(
627
- (_, index) => preflight[index]?.ok
628
- );
629
- const incomingPackPaths = await indexIncomingPack(
630
- repo,
631
- acceptedUpdates.some((update) => update.newOid !== ZERO_OID) ? parsed.packData : new Uint8Array(),
632
- hooks
633
- );
634
- const newTips = acceptedUpdates.filter((update) => update.newOid !== ZERO_OID).map((update) => update.newOid);
635
- if (newTips.length > 0) {
636
- const haveOids = await collectExistingRefTips(repo);
637
- for (const update of acceptedUpdates) {
638
- if (update.oldOid !== ZERO_OID) haveOids.add(update.oldOid);
639
- }
640
- const { complete } = await collectReachableOids(
641
- repo,
642
- newTips,
643
- hooks,
644
- haveOids
645
- );
646
- if (!complete) {
647
- await Promise.all(
648
- incomingPackPaths.map(
649
- (filepath) => rawFs(repo).unlink(filepath).catch(() => {
650
- })
651
- )
652
- );
653
- return {
654
- results: preflight.map(
655
- (result) => result.ok ? { ...result, ok: false, reason: "incomplete object graph" } : result
656
- ),
657
- stalePackPaths: []
658
- };
659
- }
660
- }
661
- const applied = await applyRefUpdates(repo, acceptedUpdates, hooks);
662
- const results = preflight.map(
663
- (result, index) => result.ok ? applied.find(
664
- (entry) => entry.refName === parsed.refUpdates[index]?.refName
665
- ) ?? result : result
666
- );
667
- const repackOptions = options?.repack;
668
- const stalePackPaths = repackOptions === false || !results.some((result) => result.ok) ? [] : await runStep(
669
- hooks,
670
- "repack",
671
- () => repackRepository(repo, repackOptions, hooks)
672
- );
673
- return { results, stalePackPaths };
674
- }
675
-
676
- // src/http/upload-pack.ts
677
- var import_isomorphic_git6 = __toESM(require("isomorphic-git"), 1);
678
- var ZERO_OID2 = "0".repeat(40);
679
- async function handleUploadPack(repo, body, options, hooks) {
680
- const lines = parsePktLines(body);
681
- const wants = [];
682
- const haves = [];
683
- let done = false;
684
- for (const line of lines) {
685
- if (!line) continue;
686
- if (line.startsWith("want ")) {
687
- wants.push(line.slice(5, 45));
688
- }
689
- if (line.startsWith("have ")) {
690
- const sha = line.slice(5, 45);
691
- if (sha !== ZERO_OID2) {
692
- haves.push(sha);
693
- }
694
- }
695
- if (line.startsWith("done")) {
696
- done = true;
697
- }
698
- }
699
- if (wants.length === 0) {
700
- return {
701
- status: 200,
702
- headers: { "Content-Type": "application/x-git-upload-pack-result" },
703
- body: concat(pktLine("NAK\n"))
704
- };
705
- }
706
- if (haves.length > 0 && !done) {
707
- return {
708
- status: 200,
709
- headers: { "Content-Type": "application/x-git-upload-pack-result" },
710
- body: concat(pktLine("NAK\n"))
711
- };
712
- }
713
- if (haves.length === 0) {
714
- const packDirPath = `${repo.gitdir}/objects/pack`;
715
- const entries = await runStep(
716
- hooks,
717
- "readdir objects/pack",
718
- () => rawFs(repo).readdir(packDirPath).catch(() => [])
719
- );
720
- const packNames = entries.filter((f) => f.endsWith(".pack"));
721
- const packName = packNames[0];
722
- if (packNames.length === 1 && packName !== void 0) {
723
- const packData = await runStep(
724
- hooks,
725
- "read consolidated pack (fast path)",
726
- () => rawFs(repo).readFile(`${packDirPath}/${packName}`)
727
- );
728
- const bytes = packData instanceof Uint8Array ? packData : new TextEncoder().encode(packData);
729
- return {
730
- status: 200,
731
- headers: {
732
- "Content-Type": "application/x-git-upload-pack-result",
733
- "Cache-Control": "no-cache"
734
- },
735
- body: concat(pktLine("NAK\n"), sideBandPackfile(bytes))
736
- };
737
- }
738
- }
739
- if (options?.beforeWalk) {
740
- await runStep(hooks, "beforeWalk", options.beforeWalk);
741
- }
742
- const { oids: wantOids } = await runStep(
743
- hooks,
744
- "collectReachableOids(wants)",
745
- () => collectReachableOids(repo, wants, hooks)
746
- );
747
- let oids = wantOids;
748
- if (haves.length > 0) {
749
- const { oids: haveOids } = await runStep(
750
- hooks,
751
- "collectReachableOids(haves)",
752
- () => collectReachableOids(repo, haves, hooks)
753
- );
754
- const haveSet = new Set(haveOids);
755
- oids = oids.filter((oid) => !haveSet.has(oid));
756
- }
757
- const { packfile } = await runStep(
758
- hooks,
759
- `packObjects (${oids.length} oids)`,
760
- () => import_isomorphic_git6.default.packObjects({ ...repo, oids })
761
- );
762
- const packBytes = packfile instanceof Uint8Array ? packfile : new Uint8Array(packfile ?? []);
763
- return {
764
- status: 200,
765
- headers: {
766
- "Content-Type": "application/x-git-upload-pack-result",
767
- "Cache-Control": "no-cache"
768
- },
769
- body: concat(pktLine("NAK\n"), sideBandPackfile(packBytes))
770
- };
771
- }
772
- // Annotate the CommonJS export names for ESM import in node:
773
- 0 && (module.exports = {
774
- FLUSH,
775
- REPACK_PACK_COUNT_THRESHOLD,
776
- applyReceivePack,
777
- applyRefUpdates,
778
- collectReachableOids,
779
- ensureRepoInitialized,
780
- handleInfoRefs,
781
- handleUploadPack,
782
- indexIncomingPack,
783
- listAllRefs,
784
- parsePktLines,
785
- parseReceivePackBody,
786
- pktLine,
787
- pktLineBuffer,
788
- rawFs,
789
- receivePackResponse,
790
- repackRepository,
791
- sideBandPackfile
792
- });
793
- //# sourceMappingURL=http.cjs.map