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.
- package/README.md +23 -17
- package/dist/{chunk-3BDDEGAP.js → chunk-2JP4MS6H.js} +7 -3
- package/dist/chunk-2JP4MS6H.js.map +1 -0
- package/dist/{chunk-YQFNY6PG.js → chunk-FIFWOMPM.js} +1 -1
- package/dist/chunk-FIFWOMPM.js.map +1 -0
- package/dist/chunk-RMAOPWHJ.js +38 -0
- package/dist/chunk-RMAOPWHJ.js.map +1 -0
- package/dist/http.js +257 -81
- package/dist/http.js.map +1 -1
- package/dist/index.js +115 -18
- package/dist/index.js.map +1 -1
- package/dist/ops.js +2 -2
- package/dist/types/cache.d.ts +81 -0
- package/dist/types/edge-utils.d.ts +43 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/git-errors.d.ts +70 -0
- package/dist/types/git-fs.d.ts +60 -0
- package/dist/types/git-layout.d.ts +52 -0
- package/dist/types/http/index.d.ts +7 -0
- package/dist/types/http/info-refs.d.ts +27 -0
- package/dist/types/http/pack-reuse.d.ts +26 -0
- package/dist/types/http/pkt-line.d.ts +14 -0
- package/dist/types/http/reachability.d.ts +36 -0
- package/dist/types/http/receive-pack.d.ts +54 -0
- package/dist/types/http/repack.d.ts +24 -0
- package/dist/types/http/types.d.ts +40 -0
- package/dist/types/http/upload-pack.d.ts +21 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/ops/branch.d.ts +38 -0
- package/dist/types/ops/commit.d.ts +45 -0
- package/dist/types/ops/diff.d.ts +24 -0
- package/dist/types/ops/file-history.d.ts +41 -0
- package/dist/types/ops/history.d.ts +85 -0
- package/dist/types/ops/index.d.ts +9 -0
- package/dist/types/ops/last-commit.d.ts +23 -0
- package/dist/types/ops/merge.d.ts +28 -0
- package/dist/types/ops/tree.d.ts +19 -0
- package/dist/{types-BHoHOaQt.d.ts → types/ops/types.d.ts} +7 -9
- package/dist/types/pack-files.d.ts +28 -0
- package/dist/types/path.d.ts +9 -0
- package/dist/types/refs.d.ts +54 -0
- package/dist/types/retry.d.ts +52 -0
- package/dist/types/stores/memory.d.ts +16 -0
- package/dist/{s3.d.ts → types/stores/s3.d.ts} +4 -7
- package/dist/{types-QgIkUR_q.d.cts → types/types.d.ts} +22 -23
- package/package.json +19 -23
- package/dist/chunk-3BDDEGAP.js.map +0 -1
- package/dist/chunk-YQFNY6PG.js.map +0 -1
- package/dist/http.cjs +0 -793
- package/dist/http.cjs.map +0 -1
- package/dist/http.d.cts +0 -206
- package/dist/http.d.ts +0 -206
- package/dist/index.cjs +0 -815
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -381
- package/dist/index.d.ts +0 -381
- package/dist/ops.cjs +0 -1057
- package/dist/ops.cjs.map +0 -1
- package/dist/ops.d.cts +0 -302
- package/dist/ops.d.ts +0 -302
- package/dist/s3.cjs +0 -123
- package/dist/s3.cjs.map +0 -1
- package/dist/s3.d.cts +0 -36
- package/dist/types-BHoHOaQt.d.cts +0 -53
- 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
|