@toon-protocol/rig 4.2.0 → 4.4.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 +200 -4
- package/dist/chunk-CTRP2UAQ.js +7668 -0
- package/dist/chunk-CTRP2UAQ.js.map +1 -0
- package/dist/{chunk-NCEMQUPA.js → chunk-GWA3CSUR.js} +198 -6
- package/dist/chunk-GWA3CSUR.js.map +1 -0
- package/dist/chunk-RL5XUWR4.js +3726 -0
- package/dist/chunk-RL5XUWR4.js.map +1 -0
- package/dist/chunk-U5A7MHBP.js +198 -0
- package/dist/chunk-U5A7MHBP.js.map +1 -0
- package/dist/{chunk-FT2ZEKPX.js → chunk-UE5TAFEU.js} +3 -3
- package/dist/{chunk-YZRR2UO6.js → chunk-XCLUB2CY.js} +1 -183
- package/dist/chunk-XCLUB2CY.js.map +1 -0
- package/dist/ci-serve-P4HOLAMP.js +2360 -0
- package/dist/ci-serve-P4HOLAMP.js.map +1 -0
- package/dist/cli/rig.js +10 -7844
- package/dist/cli/rig.js.map +1 -1
- package/dist/index.d.ts +754 -1
- package/dist/index.js +164 -3
- package/dist/index.js.map +1 -1
- package/dist/standalone/index.js +11 -10
- package/dist/{standalone-mode-YOFWOAL5.js → standalone-mode-R3HH2ALP.js} +10 -11
- package/dist/{standalone-mode-YOFWOAL5.js.map → standalone-mode-R3HH2ALP.js.map} +1 -1
- package/package.json +5 -4
- package/dist/chunk-CXREFXB6.js +0 -16
- package/dist/chunk-CXREFXB6.js.map +0 -1
- package/dist/chunk-LTFMPOHU.js +0 -194
- package/dist/chunk-LTFMPOHU.js.map +0 -1
- package/dist/chunk-NCEMQUPA.js.map +0 -1
- package/dist/chunk-TJPIE5X4.js +0 -1294
- package/dist/chunk-TJPIE5X4.js.map +0 -1
- package/dist/chunk-YZRR2UO6.js.map +0 -1
- /package/dist/{chunk-FT2ZEKPX.js.map → chunk-UE5TAFEU.js.map} +0 -0
package/dist/chunk-TJPIE5X4.js
DELETED
|
@@ -1,1294 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ARWEAVE_FETCH_TIMEOUT_MS,
|
|
3
|
-
ARWEAVE_GATEWAYS,
|
|
4
|
-
buildRepoAnnouncement,
|
|
5
|
-
buildRepoRefs,
|
|
6
|
-
isValidArweaveTxId
|
|
7
|
-
} from "./chunk-NCEMQUPA.js";
|
|
8
|
-
import {
|
|
9
|
-
uploadChargeFor
|
|
10
|
-
} from "./chunk-CXREFXB6.js";
|
|
11
|
-
|
|
12
|
-
// src/objects.ts
|
|
13
|
-
import { createHash } from "crypto";
|
|
14
|
-
var FREE_TIER_MAX_ITEM_BYTES = 107520;
|
|
15
|
-
var MAX_OBJECT_SIZE = FREE_TIER_MAX_ITEM_BYTES;
|
|
16
|
-
var EMPTY_BLOB_SHA = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
|
|
17
|
-
function hashGitObject(type, body) {
|
|
18
|
-
const header = Buffer.from(`${type} ${body.length}\0`);
|
|
19
|
-
const fullObject = Buffer.concat([header, body]);
|
|
20
|
-
const sha = createHash("sha1").update(fullObject).digest("hex");
|
|
21
|
-
return { sha, buffer: fullObject, body };
|
|
22
|
-
}
|
|
23
|
-
function createGitBlob(content) {
|
|
24
|
-
return hashGitObject("blob", Buffer.from(content, "utf-8"));
|
|
25
|
-
}
|
|
26
|
-
function createGitTree(entries) {
|
|
27
|
-
const sorted = [...entries].sort(
|
|
28
|
-
(a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
29
|
-
);
|
|
30
|
-
const entryBuffers = [];
|
|
31
|
-
for (const entry of sorted) {
|
|
32
|
-
const modeAndName = Buffer.from(`${entry.mode} ${entry.name}\0`);
|
|
33
|
-
const rawSha = Buffer.from(entry.sha, "hex");
|
|
34
|
-
entryBuffers.push(Buffer.concat([modeAndName, rawSha]));
|
|
35
|
-
}
|
|
36
|
-
return hashGitObject("tree", Buffer.concat(entryBuffers));
|
|
37
|
-
}
|
|
38
|
-
function createGitCommit(opts) {
|
|
39
|
-
const lines = [
|
|
40
|
-
`tree ${opts.treeSha}`,
|
|
41
|
-
...opts.parentSha ? [`parent ${opts.parentSha}`] : [],
|
|
42
|
-
`author ${opts.authorName} <${opts.authorPubkey}@nostr> ${opts.timestamp} +0000`,
|
|
43
|
-
`committer ${opts.authorName} <${opts.authorPubkey}@nostr> ${opts.timestamp} +0000`,
|
|
44
|
-
"",
|
|
45
|
-
opts.message
|
|
46
|
-
];
|
|
47
|
-
return hashGitObject("commit", Buffer.from(lines.join("\n"), "utf-8"));
|
|
48
|
-
}
|
|
49
|
-
function createGitTag(opts) {
|
|
50
|
-
const lines = [
|
|
51
|
-
`object ${opts.objectSha}`,
|
|
52
|
-
`type ${opts.objectType}`,
|
|
53
|
-
`tag ${opts.tagName}`,
|
|
54
|
-
`tagger ${opts.taggerName} <${opts.taggerPubkey}@nostr> ${opts.timestamp} +0000`,
|
|
55
|
-
"",
|
|
56
|
-
opts.message
|
|
57
|
-
];
|
|
58
|
-
return hashGitObject("tag", Buffer.from(lines.join("\n"), "utf-8"));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// src/repo-reader.ts
|
|
62
|
-
import { execFile, spawn } from "child_process";
|
|
63
|
-
import { promisify } from "util";
|
|
64
|
-
var execFileAsync = promisify(execFile);
|
|
65
|
-
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
66
|
-
var GitError = class extends Error {
|
|
67
|
-
constructor(message, exitCode, stderr) {
|
|
68
|
-
super(message);
|
|
69
|
-
this.exitCode = exitCode;
|
|
70
|
-
this.stderr = stderr;
|
|
71
|
-
this.name = "GitError";
|
|
72
|
-
}
|
|
73
|
-
exitCode;
|
|
74
|
-
stderr;
|
|
75
|
-
};
|
|
76
|
-
var FULL_SHA_RE = /^[0-9a-f]{40}$/;
|
|
77
|
-
var REV_TOKEN_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]*(?:[~^][0-9]*)*$/;
|
|
78
|
-
function isValidRevision(rev) {
|
|
79
|
-
if (rev.length === 0 || rev.length > 1024) return false;
|
|
80
|
-
if (!REV_TOKEN_RE.test(rev)) return false;
|
|
81
|
-
if (rev.includes("..")) return false;
|
|
82
|
-
if (rev.endsWith(".lock") || rev.endsWith("/") || rev.endsWith(".")) return false;
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
function assertRevision(rev, what) {
|
|
86
|
-
if (!isValidRevision(rev)) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`${what} is not a valid git revision (got ${JSON.stringify(rev)}); expected a SHA or simple refname \u2014 options/ranges are rejected`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function assertFullSha(sha, what) {
|
|
93
|
-
if (!FULL_SHA_RE.test(sha)) {
|
|
94
|
-
throw new Error(
|
|
95
|
-
`${what} is not a full 40-hex SHA-1 (got ${JSON.stringify(sha)})`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
function assertRange(range, what) {
|
|
100
|
-
const parts = range.split(/\.{2,3}/);
|
|
101
|
-
const separators = range.match(/\.{2,3}/g) ?? [];
|
|
102
|
-
const ok = parts.length <= 2 && separators.length === parts.length - 1 && parts.every((p) => isValidRevision(p));
|
|
103
|
-
if (!ok) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`${what} is not a valid revision range (got ${JSON.stringify(range)}); expected <rev>, <rev>..<rev>, or <rev>...<rev>`
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
var OBJECT_TYPES = /* @__PURE__ */ new Set(["blob", "tree", "commit", "tag"]);
|
|
110
|
-
var BatchParser = class {
|
|
111
|
-
buf = Buffer.alloc(0);
|
|
112
|
-
pending = null;
|
|
113
|
-
objects = [];
|
|
114
|
-
missing = [];
|
|
115
|
-
push(chunk) {
|
|
116
|
-
this.buf = this.buf.length === 0 ? chunk : Buffer.concat([this.buf, chunk]);
|
|
117
|
-
this.drain();
|
|
118
|
-
}
|
|
119
|
-
/** True when no partially-parsed record remains. */
|
|
120
|
-
isComplete() {
|
|
121
|
-
return this.pending === null && this.buf.length === 0;
|
|
122
|
-
}
|
|
123
|
-
drain() {
|
|
124
|
-
for (; ; ) {
|
|
125
|
-
if (this.pending) {
|
|
126
|
-
const needed = this.pending.size + 1;
|
|
127
|
-
if (this.buf.length < needed) return;
|
|
128
|
-
const body = Buffer.from(this.buf.subarray(0, this.pending.size));
|
|
129
|
-
this.objects.push({ sha: this.pending.sha, type: this.pending.type, body });
|
|
130
|
-
this.buf = this.buf.subarray(needed);
|
|
131
|
-
this.pending = null;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
const nl = this.buf.indexOf(10);
|
|
135
|
-
if (nl === -1) return;
|
|
136
|
-
const header = this.buf.subarray(0, nl).toString("utf-8");
|
|
137
|
-
this.buf = this.buf.subarray(nl + 1);
|
|
138
|
-
const [name, second, third] = header.split(" ");
|
|
139
|
-
if (name && second === "missing" && third === void 0) {
|
|
140
|
-
this.missing.push(name);
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (name && second && third !== void 0 && OBJECT_TYPES.has(second)) {
|
|
144
|
-
const size = Number.parseInt(third, 10);
|
|
145
|
-
if (Number.isSafeInteger(size) && size >= 0) {
|
|
146
|
-
this.pending = { sha: name, type: second, size };
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
throw new GitError(
|
|
151
|
-
`unexpected cat-file --batch header: ${JSON.stringify(header)}`,
|
|
152
|
-
void 0,
|
|
153
|
-
""
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
var GitRepoReader = class {
|
|
159
|
-
constructor(repoPath) {
|
|
160
|
-
this.repoPath = repoPath;
|
|
161
|
-
}
|
|
162
|
-
repoPath;
|
|
163
|
-
/** Run git with argument-array safety; resolves stdout as UTF-8. */
|
|
164
|
-
async git(args, opts = {}) {
|
|
165
|
-
try {
|
|
166
|
-
const { stdout } = await execFileAsync("git", args, {
|
|
167
|
-
cwd: this.repoPath,
|
|
168
|
-
maxBuffer: MAX_BUFFER,
|
|
169
|
-
encoding: "utf-8"
|
|
170
|
-
});
|
|
171
|
-
return { stdout, exitCode: 0 };
|
|
172
|
-
} catch (err) {
|
|
173
|
-
const e = err;
|
|
174
|
-
const exitCode = typeof e.code === "number" ? e.code : void 0;
|
|
175
|
-
if (exitCode !== void 0 && opts.allowExitCodes?.includes(exitCode)) {
|
|
176
|
-
return { stdout: e.stdout ?? "", exitCode };
|
|
177
|
-
}
|
|
178
|
-
throw new GitError(
|
|
179
|
-
`git ${args[0]} failed${exitCode !== void 0 ? ` (exit ${exitCode})` : ""}: ${(e.stderr ?? e.message ?? "").trim()}`,
|
|
180
|
-
exitCode,
|
|
181
|
-
(e.stderr ?? "").trim()
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* List all branches and tags plus the symbolic HEAD.
|
|
187
|
-
*
|
|
188
|
-
* Annotated tags report the tag object's SHA/type with the peeled target
|
|
189
|
-
* in `peeledSha`. A detached HEAD is tolerated (`head` is `undefined`).
|
|
190
|
-
*/
|
|
191
|
-
async listRefs() {
|
|
192
|
-
const format = "%(refname)%00%(objectname)%00%(objecttype)%00%(*objectname)";
|
|
193
|
-
const [refsRes, headRes] = await Promise.all([
|
|
194
|
-
this.git(["for-each-ref", `--format=${format}`, "refs/heads", "refs/tags"]),
|
|
195
|
-
// Exit 1 = detached HEAD (or unborn branch pointer oddities) — tolerated.
|
|
196
|
-
this.git(["symbolic-ref", "--quiet", "HEAD"], { allowExitCodes: [1] })
|
|
197
|
-
]);
|
|
198
|
-
const refs = [];
|
|
199
|
-
for (const line of refsRes.stdout.split("\n")) {
|
|
200
|
-
if (!line) continue;
|
|
201
|
-
const [refname, sha, objecttype, peeled] = line.split("\0");
|
|
202
|
-
if (!refname || !sha || !objecttype || !OBJECT_TYPES.has(objecttype)) {
|
|
203
|
-
throw new GitError(`unexpected for-each-ref line: ${JSON.stringify(line)}`, void 0, "");
|
|
204
|
-
}
|
|
205
|
-
refs.push({
|
|
206
|
-
refname,
|
|
207
|
-
sha,
|
|
208
|
-
type: objecttype,
|
|
209
|
-
...peeled ? { peeledSha: peeled } : {}
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
const head = headRes.exitCode === 0 ? headRes.stdout.trim() || void 0 : void 0;
|
|
213
|
-
return { head, refs };
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* SHAs of every object reachable from `want` but not from `have`
|
|
217
|
-
* (`git rev-list --objects <want…> --not <have…>`), i.e. the push delta.
|
|
218
|
-
*
|
|
219
|
-
* Haves that don't exist locally (e.g. remote tips we never fetched) are
|
|
220
|
-
* filtered out first via one `cat-file --batch-check` pass — rev-list
|
|
221
|
-
* would otherwise die on them.
|
|
222
|
-
*/
|
|
223
|
-
async objectsBetween(want, have) {
|
|
224
|
-
const objects = await this.objectsBetweenWithPaths(want, have);
|
|
225
|
-
return objects.map((o) => o.sha);
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Like {@link objectsBetween} but keeps the path each object was reached
|
|
229
|
-
* by (`rev-list --objects` emits `<sha> <path>` for blobs and non-root
|
|
230
|
-
* trees) — used by push planning to report actionable oversize errors.
|
|
231
|
-
*/
|
|
232
|
-
async objectsBetweenWithPaths(want, have) {
|
|
233
|
-
for (const w of want) assertRevision(w, "want");
|
|
234
|
-
for (const h of have) assertRevision(h, "have");
|
|
235
|
-
if (want.length === 0) return [];
|
|
236
|
-
const knownHaves = await this.filterExisting(have);
|
|
237
|
-
const args = ["rev-list", "--objects", ...want];
|
|
238
|
-
if (knownHaves.length > 0) args.push("--not", ...knownHaves);
|
|
239
|
-
args.push("--");
|
|
240
|
-
const { stdout } = await this.git(args);
|
|
241
|
-
const objects = [];
|
|
242
|
-
for (const line of stdout.split("\n")) {
|
|
243
|
-
if (!line) continue;
|
|
244
|
-
const spaceIdx = line.indexOf(" ");
|
|
245
|
-
if (spaceIdx === -1) {
|
|
246
|
-
objects.push({ sha: line });
|
|
247
|
-
} else {
|
|
248
|
-
const path = line.slice(spaceIdx + 1);
|
|
249
|
-
objects.push({
|
|
250
|
-
sha: line.slice(0, spaceIdx),
|
|
251
|
-
...path ? { path } : {}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return objects;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* List every blob (file) reachable from a ref's root tree, recursively,
|
|
259
|
-
* with the path it is served at (#368: the ar.io site manifest join key).
|
|
260
|
-
* Uses `git ls-tree -r -z` — NUL-terminated records so binary/spaced paths
|
|
261
|
-
* survive verbatim, and no path quoting to undo. Submodule (`commit`)
|
|
262
|
-
* gitlink entries and directories are excluded; only real file blobs remain.
|
|
263
|
-
*/
|
|
264
|
-
async listBlobs(rev) {
|
|
265
|
-
assertRevision(rev, "ref");
|
|
266
|
-
const { stdout } = await this.git(["ls-tree", "-r", "-z", rev, "--"]);
|
|
267
|
-
const blobs = [];
|
|
268
|
-
for (const record of stdout.split("\0")) {
|
|
269
|
-
if (!record) continue;
|
|
270
|
-
const tab = record.indexOf(" ");
|
|
271
|
-
if (tab === -1) {
|
|
272
|
-
throw new GitError(
|
|
273
|
-
`unexpected ls-tree record: ${JSON.stringify(record)}`,
|
|
274
|
-
void 0,
|
|
275
|
-
""
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
const meta = record.slice(0, tab);
|
|
279
|
-
const path = record.slice(tab + 1);
|
|
280
|
-
const [, type, sha] = meta.split(" ");
|
|
281
|
-
if (type !== "blob") continue;
|
|
282
|
-
if (!sha || !FULL_SHA_RE.test(sha)) {
|
|
283
|
-
throw new GitError(
|
|
284
|
-
`unexpected ls-tree object id: ${JSON.stringify(record)}`,
|
|
285
|
-
void 0,
|
|
286
|
-
""
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
blobs.push({ path, sha });
|
|
290
|
-
}
|
|
291
|
-
return blobs;
|
|
292
|
-
}
|
|
293
|
-
/** Run git feeding `input` on stdin; resolves collected stdout bytes. */
|
|
294
|
-
runWithStdin(args, input) {
|
|
295
|
-
return new Promise((resolve, reject) => {
|
|
296
|
-
const child = spawn("git", args, {
|
|
297
|
-
cwd: this.repoPath,
|
|
298
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
299
|
-
});
|
|
300
|
-
const out = [];
|
|
301
|
-
let stderr = "";
|
|
302
|
-
child.stdout.on("data", (chunk) => out.push(chunk));
|
|
303
|
-
child.stderr.on("data", (chunk) => {
|
|
304
|
-
stderr += chunk.toString("utf-8");
|
|
305
|
-
});
|
|
306
|
-
child.on("error", (err) => {
|
|
307
|
-
reject(new GitError(`failed to spawn git ${args[0]}: ${err.message}`, void 0, ""));
|
|
308
|
-
});
|
|
309
|
-
child.on("close", (code) => {
|
|
310
|
-
if (code !== 0) {
|
|
311
|
-
return reject(
|
|
312
|
-
new GitError(`git ${args[0]} failed (exit ${code}): ${stderr.trim()}`, code ?? void 0, stderr.trim())
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
resolve(Buffer.concat(out));
|
|
316
|
-
});
|
|
317
|
-
child.stdin.on("error", () => {
|
|
318
|
-
});
|
|
319
|
-
child.stdin.write(input);
|
|
320
|
-
child.stdin.end();
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
/** Of the given revisions, keep only those resolvable locally. */
|
|
324
|
-
async filterExisting(revs) {
|
|
325
|
-
if (revs.length === 0) return [];
|
|
326
|
-
const { missing } = await this.batchCheck(revs);
|
|
327
|
-
const missingSet = new Set(missing);
|
|
328
|
-
return revs.filter((r) => !missingSet.has(r));
|
|
329
|
-
}
|
|
330
|
-
/** One `cat-file --batch-check` pass; returns names reported missing. */
|
|
331
|
-
async batchCheck(names) {
|
|
332
|
-
const stdout = await this.runWithStdin(
|
|
333
|
-
["cat-file", "--batch-check"],
|
|
334
|
-
names.join("\n") + "\n"
|
|
335
|
-
);
|
|
336
|
-
const missing = [];
|
|
337
|
-
for (const line of stdout.toString("utf-8").split("\n")) {
|
|
338
|
-
if (line.endsWith(" missing")) missing.push(line.slice(0, -" missing".length));
|
|
339
|
-
}
|
|
340
|
-
return { missing };
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Object metadata (type + body size) for a batch of SHAs via one
|
|
344
|
-
* `cat-file --batch-check` pass — no bodies are read. Missing objects are
|
|
345
|
-
* reported, not thrown.
|
|
346
|
-
*/
|
|
347
|
-
async statObjects(shas) {
|
|
348
|
-
for (const sha of shas) assertFullSha(sha, "sha");
|
|
349
|
-
if (shas.length === 0) return { objects: [], missing: [] };
|
|
350
|
-
const stdout = await this.runWithStdin(
|
|
351
|
-
["cat-file", "--batch-check"],
|
|
352
|
-
shas.join("\n") + "\n"
|
|
353
|
-
);
|
|
354
|
-
const objects = [];
|
|
355
|
-
const missing = [];
|
|
356
|
-
for (const line of stdout.toString("utf-8").split("\n")) {
|
|
357
|
-
if (!line) continue;
|
|
358
|
-
if (line.endsWith(" missing")) {
|
|
359
|
-
missing.push(line.slice(0, -" missing".length));
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
const [sha, type, sizeStr] = line.split(" ");
|
|
363
|
-
const size = Number.parseInt(sizeStr ?? "", 10);
|
|
364
|
-
if (!sha || !type || !OBJECT_TYPES.has(type) || !Number.isSafeInteger(size) || size < 0) {
|
|
365
|
-
throw new GitError(
|
|
366
|
-
`unexpected cat-file --batch-check line: ${JSON.stringify(line)}`,
|
|
367
|
-
void 0,
|
|
368
|
-
""
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
objects.push({ sha, type, size });
|
|
372
|
-
}
|
|
373
|
-
return { objects, missing };
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Read raw object bodies via a single streaming `git cat-file --batch`
|
|
377
|
-
* child process. Bodies may be binary and are parsed size-driven across
|
|
378
|
-
* chunk boundaries. Missing objects are reported, not thrown.
|
|
379
|
-
*/
|
|
380
|
-
async readObjects(shas) {
|
|
381
|
-
for (const sha of shas) assertFullSha(sha, "sha");
|
|
382
|
-
if (shas.length === 0) return { objects: [], missing: [] };
|
|
383
|
-
const parser = new BatchParser();
|
|
384
|
-
await new Promise((resolve, reject) => {
|
|
385
|
-
const child = spawn("git", ["cat-file", "--batch"], {
|
|
386
|
-
cwd: this.repoPath,
|
|
387
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
388
|
-
});
|
|
389
|
-
let stderr = "";
|
|
390
|
-
let parseError = null;
|
|
391
|
-
child.stderr.on("data", (chunk) => {
|
|
392
|
-
stderr += chunk.toString("utf-8");
|
|
393
|
-
});
|
|
394
|
-
child.stdout.on("data", (chunk) => {
|
|
395
|
-
if (parseError) return;
|
|
396
|
-
try {
|
|
397
|
-
parser.push(chunk);
|
|
398
|
-
} catch (err) {
|
|
399
|
-
parseError = err;
|
|
400
|
-
child.kill();
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
child.on("error", (err) => {
|
|
404
|
-
reject(new GitError(`failed to spawn git cat-file: ${err.message}`, void 0, ""));
|
|
405
|
-
});
|
|
406
|
-
child.on("close", (code) => {
|
|
407
|
-
if (parseError) return reject(parseError);
|
|
408
|
-
if (code !== 0) {
|
|
409
|
-
return reject(
|
|
410
|
-
new GitError(`git cat-file --batch failed (exit ${code}): ${stderr.trim()}`, code ?? void 0, stderr.trim())
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
if (!parser.isComplete()) {
|
|
414
|
-
return reject(
|
|
415
|
-
new GitError("git cat-file --batch output ended mid-record", code ?? void 0, stderr.trim())
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
resolve();
|
|
419
|
-
});
|
|
420
|
-
child.stdin.on("error", () => {
|
|
421
|
-
});
|
|
422
|
-
child.stdin.write(shas.join("\n") + "\n");
|
|
423
|
-
child.stdin.end();
|
|
424
|
-
});
|
|
425
|
-
return { objects: parser.objects, missing: parser.missing };
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* `git merge-base --is-ancestor <a> <b>` — true when `a` is an ancestor
|
|
429
|
-
* of `b` (fast-forward check / force detection). Exit codes other than
|
|
430
|
-
* 0/1 (e.g. unknown revisions) throw.
|
|
431
|
-
*/
|
|
432
|
-
async isAncestor(a, b) {
|
|
433
|
-
assertRevision(a, "ancestor candidate");
|
|
434
|
-
assertRevision(b, "descendant candidate");
|
|
435
|
-
const { exitCode } = await this.git(
|
|
436
|
-
["merge-base", "--is-ancestor", a, b],
|
|
437
|
-
{ allowExitCodes: [1] }
|
|
438
|
-
);
|
|
439
|
-
return exitCode === 0;
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* `git format-patch --stdout <range>` — the full mbox-formatted patch
|
|
443
|
-
* series text (empty string when the range selects no commits).
|
|
444
|
-
*/
|
|
445
|
-
async formatPatch(range) {
|
|
446
|
-
assertRange(range, "range");
|
|
447
|
-
const { stdout } = await this.git(["format-patch", "--stdout", range, "--"]);
|
|
448
|
-
return stdout;
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Parent SHAs for a batch of commit SHAs via one
|
|
452
|
-
* `git rev-list --no-walk=unsorted --parents` pass. Root commits map to an
|
|
453
|
-
* empty array. Used to derive the kind:1617 `commit`/`parent-commit` tag
|
|
454
|
-
* pairs for exactly the commits a format-patch series carries.
|
|
455
|
-
*/
|
|
456
|
-
async commitParents(shas) {
|
|
457
|
-
for (const sha of shas) assertFullSha(sha, "sha");
|
|
458
|
-
if (shas.length === 0) return /* @__PURE__ */ new Map();
|
|
459
|
-
const { stdout } = await this.git([
|
|
460
|
-
"rev-list",
|
|
461
|
-
"--no-walk=unsorted",
|
|
462
|
-
"--parents",
|
|
463
|
-
...shas,
|
|
464
|
-
"--"
|
|
465
|
-
]);
|
|
466
|
-
const parents = /* @__PURE__ */ new Map();
|
|
467
|
-
for (const line of stdout.split("\n")) {
|
|
468
|
-
if (!line) continue;
|
|
469
|
-
const [sha, ...rest] = line.split(" ");
|
|
470
|
-
if (!sha || !FULL_SHA_RE.test(sha) || rest.some((p) => !FULL_SHA_RE.test(p))) {
|
|
471
|
-
throw new GitError(
|
|
472
|
-
`unexpected rev-list --parents line: ${JSON.stringify(line)}`,
|
|
473
|
-
void 0,
|
|
474
|
-
""
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
parents.set(sha, rest);
|
|
478
|
-
}
|
|
479
|
-
return parents;
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Resolve a ref/revision to a full SHA via `git rev-parse --verify`.
|
|
483
|
-
* Throws {@link GitError} when the name doesn't resolve.
|
|
484
|
-
*/
|
|
485
|
-
async resolveRef(name) {
|
|
486
|
-
assertRevision(name, "ref name");
|
|
487
|
-
const { stdout } = await this.git(["rev-parse", "--verify", "--quiet", name]);
|
|
488
|
-
const sha = stdout.trim();
|
|
489
|
-
if (!FULL_SHA_RE.test(sha)) {
|
|
490
|
-
throw new GitError(
|
|
491
|
-
`rev-parse --verify returned unexpected output for ${JSON.stringify(name)}: ${JSON.stringify(sha)}`,
|
|
492
|
-
void 0,
|
|
493
|
-
""
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
return sha;
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
// src/object-fetch.ts
|
|
501
|
-
var DEFAULT_CONCURRENCY = 8;
|
|
502
|
-
var ObjectIntegrityError = class extends Error {
|
|
503
|
-
constructor(objects) {
|
|
504
|
-
super(
|
|
505
|
-
`${objects.length} downloaded object(s) failed SHA-1 verification \u2014 the gateway content does not match the announced git SHA(s): ` + objects.map((o) => `${o.sha} (tx ${o.txId})`).join(", ") + ". Refusing to write corrupt/tampered objects."
|
|
506
|
-
);
|
|
507
|
-
this.objects = objects;
|
|
508
|
-
this.name = "ObjectIntegrityError";
|
|
509
|
-
}
|
|
510
|
-
objects;
|
|
511
|
-
};
|
|
512
|
-
var OBJECT_TYPES2 = [
|
|
513
|
-
"blob",
|
|
514
|
-
"tree",
|
|
515
|
-
"commit",
|
|
516
|
-
"tag"
|
|
517
|
-
];
|
|
518
|
-
function verifyObjectBody(expectedSha, bytes) {
|
|
519
|
-
const body = Buffer.from(bytes);
|
|
520
|
-
for (const type of OBJECT_TYPES2) {
|
|
521
|
-
if (hashGitObject(type, body).sha === expectedSha) {
|
|
522
|
-
return { sha: expectedSha, type, body };
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
return null;
|
|
526
|
-
}
|
|
527
|
-
async function fetchTxBytes(txId, options = {}) {
|
|
528
|
-
if (!isValidArweaveTxId(txId)) return null;
|
|
529
|
-
const gateways = options.gateways ?? ARWEAVE_GATEWAYS;
|
|
530
|
-
const fetchFn = options.fetchFn ?? fetch;
|
|
531
|
-
const timeoutMs = options.timeoutMs ?? ARWEAVE_FETCH_TIMEOUT_MS;
|
|
532
|
-
for (const gateway of gateways) {
|
|
533
|
-
try {
|
|
534
|
-
const response = await fetchFn(`${gateway}/${txId}`, {
|
|
535
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
536
|
-
});
|
|
537
|
-
if (!response.ok) continue;
|
|
538
|
-
return new Uint8Array(await response.arrayBuffer());
|
|
539
|
-
} catch {
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
return null;
|
|
543
|
-
}
|
|
544
|
-
async function downloadGitObjects(entries, options = {}) {
|
|
545
|
-
const queue = [...entries];
|
|
546
|
-
const total = queue.length;
|
|
547
|
-
const concurrency = Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY);
|
|
548
|
-
const objects = /* @__PURE__ */ new Map();
|
|
549
|
-
const unavailable = [];
|
|
550
|
-
const corrupt = [];
|
|
551
|
-
let done = 0;
|
|
552
|
-
const worker = async () => {
|
|
553
|
-
for (; ; ) {
|
|
554
|
-
const next = queue.shift();
|
|
555
|
-
if (!next) return;
|
|
556
|
-
const [sha, txId] = next;
|
|
557
|
-
const bytes = await fetchTxBytes(txId, options);
|
|
558
|
-
if (bytes === null) {
|
|
559
|
-
unavailable.push({ sha, txId });
|
|
560
|
-
} else {
|
|
561
|
-
const verified = verifyObjectBody(sha, bytes);
|
|
562
|
-
if (verified === null) {
|
|
563
|
-
corrupt.push({ sha, txId });
|
|
564
|
-
} else {
|
|
565
|
-
objects.set(sha, verified);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
done += 1;
|
|
569
|
-
options.onObject?.(done, total);
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
await Promise.all(
|
|
573
|
-
Array.from({ length: Math.min(concurrency, total) }, () => worker())
|
|
574
|
-
);
|
|
575
|
-
if (corrupt.length > 0) throw new ObjectIntegrityError(corrupt);
|
|
576
|
-
return { objects, unavailable };
|
|
577
|
-
}
|
|
578
|
-
var FULL_SHA_RE2 = /^[0-9a-f]{40}$/;
|
|
579
|
-
var GITLINK_MODE = "160000";
|
|
580
|
-
function bytesToHex(bytes) {
|
|
581
|
-
let hex = "";
|
|
582
|
-
for (const byte of bytes) hex += byte.toString(16).padStart(2, "0");
|
|
583
|
-
return hex;
|
|
584
|
-
}
|
|
585
|
-
function referencedShas(object) {
|
|
586
|
-
switch (object.type) {
|
|
587
|
-
case "blob":
|
|
588
|
-
return [];
|
|
589
|
-
case "tree": {
|
|
590
|
-
const refs = [];
|
|
591
|
-
const data = object.body;
|
|
592
|
-
let offset = 0;
|
|
593
|
-
while (offset < data.length) {
|
|
594
|
-
const spaceIdx = data.indexOf(32, offset);
|
|
595
|
-
if (spaceIdx === -1) break;
|
|
596
|
-
const mode = data.subarray(offset, spaceIdx).toString("utf-8");
|
|
597
|
-
const nulIdx = data.indexOf(0, spaceIdx + 1);
|
|
598
|
-
if (nulIdx === -1 || nulIdx + 21 > data.length) break;
|
|
599
|
-
const sha = bytesToHex(data.subarray(nulIdx + 1, nulIdx + 21));
|
|
600
|
-
if (mode !== GITLINK_MODE) refs.push(sha);
|
|
601
|
-
offset = nulIdx + 21;
|
|
602
|
-
}
|
|
603
|
-
return refs;
|
|
604
|
-
}
|
|
605
|
-
case "commit": {
|
|
606
|
-
const refs = [];
|
|
607
|
-
const text = object.body.toString("utf-8");
|
|
608
|
-
const headerEnd = text.indexOf("\n\n");
|
|
609
|
-
const header = headerEnd === -1 ? text : text.slice(0, headerEnd);
|
|
610
|
-
for (const line of header.split("\n")) {
|
|
611
|
-
if (line.startsWith("tree ")) refs.push(line.slice(5).trim());
|
|
612
|
-
else if (line.startsWith("parent ")) refs.push(line.slice(7).trim());
|
|
613
|
-
}
|
|
614
|
-
return refs.filter((sha) => FULL_SHA_RE2.test(sha));
|
|
615
|
-
}
|
|
616
|
-
case "tag": {
|
|
617
|
-
const text = object.body.toString("utf-8");
|
|
618
|
-
const match = /^object ([0-9a-f]{40})$/m.exec(text);
|
|
619
|
-
return match?.[1] ? [match[1]] : [];
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
function walkClosure(tips, objects, presentLocally = /* @__PURE__ */ new Set()) {
|
|
624
|
-
const reachable = /* @__PURE__ */ new Set();
|
|
625
|
-
const missing = /* @__PURE__ */ new Set();
|
|
626
|
-
const stack = [...new Set(tips)];
|
|
627
|
-
while (stack.length > 0) {
|
|
628
|
-
const sha = stack.pop();
|
|
629
|
-
if (reachable.has(sha) || missing.has(sha)) continue;
|
|
630
|
-
if (presentLocally.has(sha)) continue;
|
|
631
|
-
const object = objects.get(sha);
|
|
632
|
-
if (!object) {
|
|
633
|
-
missing.add(sha);
|
|
634
|
-
continue;
|
|
635
|
-
}
|
|
636
|
-
reachable.add(sha);
|
|
637
|
-
for (const ref of referencedShas(object)) {
|
|
638
|
-
if (!reachable.has(ref) && !missing.has(ref)) stack.push(ref);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
return { reachable, missing: [...missing] };
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// src/read-pipeline.ts
|
|
645
|
-
var MAX_PASSES = 64;
|
|
646
|
-
async function collectRepoObjects(options) {
|
|
647
|
-
const { tips, shaToTxId, resolveMissing } = options;
|
|
648
|
-
const present = options.presentLocally ?? /* @__PURE__ */ new Set();
|
|
649
|
-
const txIds = new Map(shaToTxId);
|
|
650
|
-
const resolverAsked = /* @__PURE__ */ new Set();
|
|
651
|
-
const undownloadable = /* @__PURE__ */ new Map();
|
|
652
|
-
const objects = /* @__PURE__ */ new Map();
|
|
653
|
-
const initial = [];
|
|
654
|
-
for (const [sha, txId] of txIds) {
|
|
655
|
-
if (!present.has(sha)) initial.push([sha, txId]);
|
|
656
|
-
}
|
|
657
|
-
const bulk = await downloadGitObjects(initial, options);
|
|
658
|
-
for (const [sha, object] of bulk.objects) objects.set(sha, object);
|
|
659
|
-
for (const { sha, txId } of bulk.unavailable) undownloadable.set(sha, txId);
|
|
660
|
-
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
661
|
-
const closure = walkClosure(tips, objects, present);
|
|
662
|
-
const open = closure.missing.filter((sha) => !undownloadable.has(sha));
|
|
663
|
-
if (open.length === 0) break;
|
|
664
|
-
const toResolve = open.filter(
|
|
665
|
-
(sha) => !txIds.has(sha) && !resolverAsked.has(sha)
|
|
666
|
-
);
|
|
667
|
-
for (const sha of toResolve) resolverAsked.add(sha);
|
|
668
|
-
if (toResolve.length > 0) {
|
|
669
|
-
const resolved = await resolveMissing(toResolve);
|
|
670
|
-
for (const [sha, txId] of resolved) txIds.set(sha, txId);
|
|
671
|
-
}
|
|
672
|
-
const batch = [];
|
|
673
|
-
for (const sha of open) {
|
|
674
|
-
const txId = txIds.get(sha);
|
|
675
|
-
if (txId !== void 0 && !objects.has(sha)) batch.push([sha, txId]);
|
|
676
|
-
}
|
|
677
|
-
if (batch.length === 0) break;
|
|
678
|
-
const result = await downloadGitObjects(batch, options);
|
|
679
|
-
for (const [sha, object] of result.objects) objects.set(sha, object);
|
|
680
|
-
for (const { sha, txId } of result.unavailable)
|
|
681
|
-
undownloadable.set(sha, txId);
|
|
682
|
-
if (result.objects.size === 0) break;
|
|
683
|
-
}
|
|
684
|
-
let finalClosure = walkClosure(tips, objects, present);
|
|
685
|
-
if (finalClosure.missing.includes(EMPTY_BLOB_SHA) && !present.has(EMPTY_BLOB_SHA)) {
|
|
686
|
-
objects.set(EMPTY_BLOB_SHA, {
|
|
687
|
-
sha: EMPTY_BLOB_SHA,
|
|
688
|
-
type: "blob",
|
|
689
|
-
body: Buffer.alloc(0)
|
|
690
|
-
});
|
|
691
|
-
finalClosure = walkClosure(tips, objects, present);
|
|
692
|
-
}
|
|
693
|
-
const missing = finalClosure.missing.map((sha) => ({
|
|
694
|
-
sha,
|
|
695
|
-
txId: txIds.get(sha) ?? null
|
|
696
|
-
}));
|
|
697
|
-
const reachable = finalClosure.reachable;
|
|
698
|
-
const skippedUnavailable = [...undownloadable].filter(
|
|
699
|
-
([sha]) => !reachable.has(sha) && !finalClosure.missing.includes(sha)
|
|
700
|
-
).map(([sha, txId]) => ({ sha, txId }));
|
|
701
|
-
return { objects, missing, skippedUnavailable };
|
|
702
|
-
}
|
|
703
|
-
function missingObjectsMessage(missing, context) {
|
|
704
|
-
const listed = missing.slice(0, 20).map(
|
|
705
|
-
(m) => ` ${m.sha}${m.txId ? ` (tx ${m.txId})` : " (no Arweave tx found)"}`
|
|
706
|
-
).join("\n");
|
|
707
|
-
const more = missing.length > 20 ? `
|
|
708
|
-
\u2026 and ${missing.length - 20} more` : "";
|
|
709
|
-
return `${context}: ${missing.length} required object(s) could not be downloaded:
|
|
710
|
-
${listed}${more}
|
|
711
|
-
Recently pushed objects can take 10-20 minutes to become fetchable from Arweave gateways \u2014 if this repo was just pushed, retry in a few minutes. Nothing was written.`;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
// src/materialize.ts
|
|
715
|
-
import { execFile as execFile2, spawn as spawn2 } from "child_process";
|
|
716
|
-
import { promisify as promisify2 } from "util";
|
|
717
|
-
var execFileAsync2 = promisify2(execFile2);
|
|
718
|
-
var ObjectWriteMismatchError = class extends Error {
|
|
719
|
-
constructor(expectedSha, writtenSha) {
|
|
720
|
-
super(
|
|
721
|
-
`git hash-object wrote ${writtenSha} where ${expectedSha} was expected \u2014 object content does not round-trip; aborting`
|
|
722
|
-
);
|
|
723
|
-
this.expectedSha = expectedSha;
|
|
724
|
-
this.writtenSha = writtenSha;
|
|
725
|
-
this.name = "ObjectWriteMismatchError";
|
|
726
|
-
}
|
|
727
|
-
expectedSha;
|
|
728
|
-
writtenSha;
|
|
729
|
-
};
|
|
730
|
-
var FULL_SHA_RE3 = /^[0-9a-f]{40}$/;
|
|
731
|
-
function isSafeRefname(refname) {
|
|
732
|
-
if (!refname.startsWith("refs/") || refname.length > 1024) return false;
|
|
733
|
-
if (/[\u0000-\u0020~^:?*[\\\u007f]/.test(refname)) return false;
|
|
734
|
-
if (refname.includes("..") || refname.includes("@{")) return false;
|
|
735
|
-
if (refname.endsWith("/") || refname.endsWith(".")) return false;
|
|
736
|
-
for (const part of refname.split("/")) {
|
|
737
|
-
if (part === "" || part.startsWith(".") || part.startsWith("-"))
|
|
738
|
-
return false;
|
|
739
|
-
if (part.endsWith(".lock")) return false;
|
|
740
|
-
}
|
|
741
|
-
return true;
|
|
742
|
-
}
|
|
743
|
-
function assertSafeRefname(refname) {
|
|
744
|
-
if (!isSafeRefname(refname)) {
|
|
745
|
-
throw new Error(
|
|
746
|
-
`unsafe ref name from remote state: ${JSON.stringify(refname)} \u2014 refusing`
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
function assertFullSha2(sha) {
|
|
751
|
-
if (!FULL_SHA_RE3.test(sha)) {
|
|
752
|
-
throw new Error(`not a full 40-hex SHA-1: ${JSON.stringify(sha)}`);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
async function runGit(repoPath, args) {
|
|
756
|
-
try {
|
|
757
|
-
const { stdout } = await execFileAsync2("git", args, {
|
|
758
|
-
cwd: repoPath,
|
|
759
|
-
encoding: "utf-8",
|
|
760
|
-
maxBuffer: 64 * 1024 * 1024
|
|
761
|
-
});
|
|
762
|
-
return stdout;
|
|
763
|
-
} catch (err) {
|
|
764
|
-
const e = err;
|
|
765
|
-
throw new Error(
|
|
766
|
-
`git ${args[0]} failed${typeof e.code === "number" ? ` (exit ${e.code})` : ""}: ${(e.stderr ?? e.message ?? "").trim()}`
|
|
767
|
-
);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
async function writeGitObject(repoPath, object) {
|
|
771
|
-
assertFullSha2(object.sha);
|
|
772
|
-
const written = await new Promise((resolve, reject) => {
|
|
773
|
-
const child = spawn2(
|
|
774
|
-
"git",
|
|
775
|
-
["hash-object", "-w", "--stdin", "-t", object.type],
|
|
776
|
-
{ cwd: repoPath, stdio: ["pipe", "pipe", "pipe"] }
|
|
777
|
-
);
|
|
778
|
-
let stdout = "";
|
|
779
|
-
let stderr = "";
|
|
780
|
-
child.stdout.on("data", (chunk) => {
|
|
781
|
-
stdout += chunk.toString("utf-8");
|
|
782
|
-
});
|
|
783
|
-
child.stderr.on("data", (chunk) => {
|
|
784
|
-
stderr += chunk.toString("utf-8");
|
|
785
|
-
});
|
|
786
|
-
child.on("error", (err) => {
|
|
787
|
-
reject(new Error(`failed to spawn git hash-object: ${err.message}`));
|
|
788
|
-
});
|
|
789
|
-
child.on("close", (code) => {
|
|
790
|
-
if (code !== 0) {
|
|
791
|
-
return reject(
|
|
792
|
-
new Error(`git hash-object failed (exit ${code}): ${stderr.trim()}`)
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
resolve(stdout.trim());
|
|
796
|
-
});
|
|
797
|
-
child.stdin.on("error", () => {
|
|
798
|
-
});
|
|
799
|
-
child.stdin.write(object.body);
|
|
800
|
-
child.stdin.end();
|
|
801
|
-
});
|
|
802
|
-
if (written !== object.sha) {
|
|
803
|
-
throw new ObjectWriteMismatchError(object.sha, written);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
async function writeGitObjects(repoPath, objects) {
|
|
807
|
-
let count = 0;
|
|
808
|
-
for (const object of objects) {
|
|
809
|
-
await writeGitObject(repoPath, object);
|
|
810
|
-
count += 1;
|
|
811
|
-
}
|
|
812
|
-
return count;
|
|
813
|
-
}
|
|
814
|
-
async function updateRef(repoPath, refname, sha) {
|
|
815
|
-
assertSafeRefname(refname);
|
|
816
|
-
assertFullSha2(sha);
|
|
817
|
-
await runGit(repoPath, ["update-ref", refname, sha]);
|
|
818
|
-
}
|
|
819
|
-
async function setHeadSymref(repoPath, refname) {
|
|
820
|
-
assertSafeRefname(refname);
|
|
821
|
-
await runGit(repoPath, ["symbolic-ref", "HEAD", refname]);
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// src/npub.ts
|
|
825
|
-
var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
826
|
-
function bech32Polymod(values) {
|
|
827
|
-
const GEN = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
828
|
-
let chk = 1;
|
|
829
|
-
for (const v of values) {
|
|
830
|
-
const b = chk >> 25;
|
|
831
|
-
chk = (chk & 33554431) << 5 ^ v;
|
|
832
|
-
for (let i = 0; i < 5; i++) {
|
|
833
|
-
chk ^= b >> i & 1 ? GEN[i] : 0;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
return chk;
|
|
837
|
-
}
|
|
838
|
-
function bech32HrpExpand(hrp) {
|
|
839
|
-
const ret = [];
|
|
840
|
-
for (let i = 0; i < hrp.length; i++) {
|
|
841
|
-
ret.push(hrp.charCodeAt(i) >> 5);
|
|
842
|
-
}
|
|
843
|
-
ret.push(0);
|
|
844
|
-
for (let i = 0; i < hrp.length; i++) {
|
|
845
|
-
ret.push(hrp.charCodeAt(i) & 31);
|
|
846
|
-
}
|
|
847
|
-
return ret;
|
|
848
|
-
}
|
|
849
|
-
function bech32CreateChecksum(hrp, data) {
|
|
850
|
-
const values = bech32HrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
|
|
851
|
-
const polymod = bech32Polymod(values) ^ 1;
|
|
852
|
-
const ret = [];
|
|
853
|
-
for (let i = 0; i < 6; i++) {
|
|
854
|
-
ret.push(polymod >> 5 * (5 - i) & 31);
|
|
855
|
-
}
|
|
856
|
-
return ret;
|
|
857
|
-
}
|
|
858
|
-
function convertBits(data, fromBits, toBits, pad) {
|
|
859
|
-
let acc = 0;
|
|
860
|
-
let bits = 0;
|
|
861
|
-
const ret = [];
|
|
862
|
-
const maxv = (1 << toBits) - 1;
|
|
863
|
-
for (const value of data) {
|
|
864
|
-
acc = acc << fromBits | value;
|
|
865
|
-
bits += fromBits;
|
|
866
|
-
while (bits >= toBits) {
|
|
867
|
-
bits -= toBits;
|
|
868
|
-
ret.push(acc >> bits & maxv);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
if (pad && bits > 0) {
|
|
872
|
-
ret.push(acc << toBits - bits & maxv);
|
|
873
|
-
}
|
|
874
|
-
return ret;
|
|
875
|
-
}
|
|
876
|
-
function hexToBytes(hex) {
|
|
877
|
-
const bytes = [];
|
|
878
|
-
for (let i = 0; i < hex.length; i += 2) {
|
|
879
|
-
bytes.push(parseInt(hex.slice(i, i + 2), 16));
|
|
880
|
-
}
|
|
881
|
-
return bytes;
|
|
882
|
-
}
|
|
883
|
-
function hexToNpub(hexPubkey) {
|
|
884
|
-
const hrp = "npub";
|
|
885
|
-
const bytes = hexToBytes(hexPubkey);
|
|
886
|
-
const words = convertBits(bytes, 8, 5, true);
|
|
887
|
-
const checksum = bech32CreateChecksum(hrp, words);
|
|
888
|
-
const combined = words.concat(checksum);
|
|
889
|
-
return hrp + "1" + combined.map((d) => BECH32_CHARSET[d]).join("");
|
|
890
|
-
}
|
|
891
|
-
function npubToHex(npub) {
|
|
892
|
-
const lower = npub.toLowerCase();
|
|
893
|
-
if (lower !== npub && npub.toUpperCase() !== npub) {
|
|
894
|
-
throw new Error("npub: mixed case");
|
|
895
|
-
}
|
|
896
|
-
if (!lower.startsWith("npub1")) {
|
|
897
|
-
throw new Error("npub: invalid prefix");
|
|
898
|
-
}
|
|
899
|
-
if (lower.length !== 63) {
|
|
900
|
-
throw new Error("npub: invalid length");
|
|
901
|
-
}
|
|
902
|
-
const data = [];
|
|
903
|
-
for (let i = 5; i < lower.length; i++) {
|
|
904
|
-
const idx = BECH32_CHARSET.indexOf(lower[i]);
|
|
905
|
-
if (idx === -1) throw new Error("npub: invalid character");
|
|
906
|
-
data.push(idx);
|
|
907
|
-
}
|
|
908
|
-
const hrpExpanded = bech32HrpExpand("npub");
|
|
909
|
-
if (bech32Polymod(hrpExpanded.concat(data)) !== 1) {
|
|
910
|
-
throw new Error("npub: invalid checksum");
|
|
911
|
-
}
|
|
912
|
-
const words = data.slice(0, -6);
|
|
913
|
-
const bytes = convertBits(words, 5, 8, false);
|
|
914
|
-
if (bytes.length !== 32) {
|
|
915
|
-
throw new Error("npub: invalid data length");
|
|
916
|
-
}
|
|
917
|
-
const totalBits = words.length * 5;
|
|
918
|
-
const trailingBits = totalBits - bytes.length * 8;
|
|
919
|
-
if (trailingBits > 0) {
|
|
920
|
-
const lastWord = words[words.length - 1];
|
|
921
|
-
const mask = (1 << trailingBits) - 1;
|
|
922
|
-
if ((lastWord & mask) !== 0) {
|
|
923
|
-
throw new Error("npub: non-zero padding bits");
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
return bytes.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
927
|
-
}
|
|
928
|
-
var HEX64_RE = /^[0-9a-f]{64}$/;
|
|
929
|
-
function ownerToHex(owner) {
|
|
930
|
-
if (HEX64_RE.test(owner)) return owner;
|
|
931
|
-
if (owner.startsWith("npub1")) {
|
|
932
|
-
try {
|
|
933
|
-
return npubToHex(owner);
|
|
934
|
-
} catch (err) {
|
|
935
|
-
throw new Error(
|
|
936
|
-
`invalid owner ${JSON.stringify(owner)}: ${err instanceof Error ? err.message : String(err)}`
|
|
937
|
-
);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
throw new Error(
|
|
941
|
-
`invalid owner ${JSON.stringify(owner)}: expected a 64-char lowercase hex pubkey or an npub1\u2026 string`
|
|
942
|
-
);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// src/routes.ts
|
|
946
|
-
function serializeFeeEstimate(plan) {
|
|
947
|
-
return {
|
|
948
|
-
objectCount: plan.estimate.objectCount,
|
|
949
|
-
totalObjectBytes: plan.estimate.totalObjectBytes,
|
|
950
|
-
uploadFee: plan.estimate.uploadFee.toString(),
|
|
951
|
-
eventCount: plan.estimate.eventCount,
|
|
952
|
-
eventFees: plan.estimate.eventFees.toString(),
|
|
953
|
-
totalFee: plan.estimate.totalFee.toString(),
|
|
954
|
-
...plan.estimate.skippedEmptyCount > 0 ? { skippedEmptyCount: plan.estimate.skippedEmptyCount } : {}
|
|
955
|
-
};
|
|
956
|
-
}
|
|
957
|
-
function serializePushPlan(plan) {
|
|
958
|
-
return {
|
|
959
|
-
repoId: plan.repoId,
|
|
960
|
-
refUpdates: plan.refUpdates,
|
|
961
|
-
newRefs: plan.newRefs,
|
|
962
|
-
headSymref: plan.headSymref,
|
|
963
|
-
objects: plan.objects,
|
|
964
|
-
knownShaToTxId: Object.fromEntries(plan.knownShaToTxId),
|
|
965
|
-
announceNeeded: plan.announceNeeded,
|
|
966
|
-
announcement: plan.announcement,
|
|
967
|
-
estimate: serializeFeeEstimate(plan)
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
function serializeEventReceipt(kind, receipt) {
|
|
971
|
-
return {
|
|
972
|
-
eventId: receipt.eventId,
|
|
973
|
-
feePaid: receipt.feePaid.toString(),
|
|
974
|
-
kind
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
function serializePushResult(plan, result) {
|
|
978
|
-
return {
|
|
979
|
-
repoId: plan.repoId,
|
|
980
|
-
refUpdates: plan.refUpdates,
|
|
981
|
-
uploads: result.uploads.map((u) => ({
|
|
982
|
-
sha: u.sha,
|
|
983
|
-
txId: u.txId,
|
|
984
|
-
feePaid: u.feePaid.toString(),
|
|
985
|
-
skipped: u.skipped
|
|
986
|
-
})),
|
|
987
|
-
announceReceipt: result.announceReceipt ? {
|
|
988
|
-
eventId: result.announceReceipt.eventId,
|
|
989
|
-
feePaid: result.announceReceipt.feePaid.toString()
|
|
990
|
-
} : null,
|
|
991
|
-
refsReceipt: {
|
|
992
|
-
eventId: result.refsReceipt.eventId,
|
|
993
|
-
feePaid: result.refsReceipt.feePaid.toString()
|
|
994
|
-
},
|
|
995
|
-
arweaveMap: Object.fromEntries(result.arweaveMap),
|
|
996
|
-
totalFeePaid: result.totalFeePaid.toString(),
|
|
997
|
-
estimate: serializeFeeEstimate(plan)
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
// src/push.ts
|
|
1002
|
-
var NonFastForwardError = class extends Error {
|
|
1003
|
-
constructor(refs) {
|
|
1004
|
-
super(
|
|
1005
|
-
`non-fast-forward update rejected for ${refs.map((r) => r.refname).join(", ")} \u2014 re-run with force to overwrite the remote ref(s)`
|
|
1006
|
-
);
|
|
1007
|
-
this.refs = refs;
|
|
1008
|
-
this.name = "NonFastForwardError";
|
|
1009
|
-
}
|
|
1010
|
-
refs;
|
|
1011
|
-
};
|
|
1012
|
-
var OversizeObjectsError = class extends Error {
|
|
1013
|
-
constructor(objects) {
|
|
1014
|
-
super(
|
|
1015
|
-
`${objects.length} object(s) exceed the ${MAX_OBJECT_SIZE} byte upload limit: ` + objects.map((o) => `${o.path ?? o.sha} (${o.size} bytes)`).join(", ")
|
|
1016
|
-
);
|
|
1017
|
-
this.objects = objects;
|
|
1018
|
-
this.name = "OversizeObjectsError";
|
|
1019
|
-
}
|
|
1020
|
-
objects;
|
|
1021
|
-
};
|
|
1022
|
-
async function planPush(options) {
|
|
1023
|
-
const { repoReader, remoteState, feeRates, repoId, force = false } = options;
|
|
1024
|
-
const resolveMissing = options.resolveMissing ?? remoteState.resolveMissing.bind(remoteState);
|
|
1025
|
-
const { head, refs: localRefs } = await repoReader.listRefs();
|
|
1026
|
-
const localByName = new Map(localRefs.map((r) => [r.refname, r]));
|
|
1027
|
-
let selected;
|
|
1028
|
-
if (options.refs !== void 0) {
|
|
1029
|
-
selected = options.refs.map((name) => {
|
|
1030
|
-
const ref = localByName.get(name);
|
|
1031
|
-
if (!ref) {
|
|
1032
|
-
throw new Error(
|
|
1033
|
-
`ref ${JSON.stringify(name)} does not exist locally (ref deletion is out of scope in v1)`
|
|
1034
|
-
);
|
|
1035
|
-
}
|
|
1036
|
-
return ref;
|
|
1037
|
-
});
|
|
1038
|
-
} else {
|
|
1039
|
-
selected = localRefs;
|
|
1040
|
-
}
|
|
1041
|
-
const refUpdates = [];
|
|
1042
|
-
const rejected = [];
|
|
1043
|
-
for (const ref of selected) {
|
|
1044
|
-
const remoteSha = remoteState.refs.get(ref.refname) ?? null;
|
|
1045
|
-
if (remoteSha === null) {
|
|
1046
|
-
refUpdates.push({
|
|
1047
|
-
refname: ref.refname,
|
|
1048
|
-
localSha: ref.sha,
|
|
1049
|
-
remoteSha,
|
|
1050
|
-
kind: "new"
|
|
1051
|
-
});
|
|
1052
|
-
continue;
|
|
1053
|
-
}
|
|
1054
|
-
if (remoteSha === ref.sha) {
|
|
1055
|
-
refUpdates.push({
|
|
1056
|
-
refname: ref.refname,
|
|
1057
|
-
localSha: ref.sha,
|
|
1058
|
-
remoteSha,
|
|
1059
|
-
kind: "up-to-date"
|
|
1060
|
-
});
|
|
1061
|
-
continue;
|
|
1062
|
-
}
|
|
1063
|
-
let fastForward = false;
|
|
1064
|
-
try {
|
|
1065
|
-
fastForward = await repoReader.isAncestor(remoteSha, ref.sha);
|
|
1066
|
-
} catch (err) {
|
|
1067
|
-
if (!(err instanceof GitError)) throw err;
|
|
1068
|
-
fastForward = false;
|
|
1069
|
-
}
|
|
1070
|
-
if (fastForward) {
|
|
1071
|
-
refUpdates.push({
|
|
1072
|
-
refname: ref.refname,
|
|
1073
|
-
localSha: ref.sha,
|
|
1074
|
-
remoteSha,
|
|
1075
|
-
kind: "fast-forward"
|
|
1076
|
-
});
|
|
1077
|
-
} else if (force) {
|
|
1078
|
-
refUpdates.push({
|
|
1079
|
-
refname: ref.refname,
|
|
1080
|
-
localSha: ref.sha,
|
|
1081
|
-
remoteSha,
|
|
1082
|
-
kind: "forced"
|
|
1083
|
-
});
|
|
1084
|
-
} else {
|
|
1085
|
-
rejected.push({ refname: ref.refname, localSha: ref.sha, remoteSha });
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
if (rejected.length > 0) throw new NonFastForwardError(rejected);
|
|
1089
|
-
const updates = refUpdates.filter((u) => u.kind !== "up-to-date");
|
|
1090
|
-
const wantTips = [...new Set(updates.map((u) => u.localSha))];
|
|
1091
|
-
const haveTips = [...new Set(remoteState.refs.values())];
|
|
1092
|
-
const delta = wantTips.length > 0 ? await repoReader.objectsBetweenWithPaths(wantTips, haveTips) : [];
|
|
1093
|
-
const knownShaToTxId = new Map(remoteState.shaToTxId);
|
|
1094
|
-
let candidates = delta.filter((o) => !knownShaToTxId.has(o.sha));
|
|
1095
|
-
if (candidates.length > 0) {
|
|
1096
|
-
const resolved = await resolveMissing(candidates.map((o) => o.sha));
|
|
1097
|
-
for (const [sha, txId] of resolved) knownShaToTxId.set(sha, txId);
|
|
1098
|
-
candidates = candidates.filter((o) => !knownShaToTxId.has(o.sha));
|
|
1099
|
-
}
|
|
1100
|
-
const pathBySha = new Map(candidates.map((c) => [c.sha, c.path]));
|
|
1101
|
-
const { objects: stats, missing } = await repoReader.statObjects(
|
|
1102
|
-
candidates.map((c) => c.sha)
|
|
1103
|
-
);
|
|
1104
|
-
if (missing.length > 0) {
|
|
1105
|
-
throw new Error(
|
|
1106
|
-
`objects vanished from the local repository during planning: ${missing.join(", ")}`
|
|
1107
|
-
);
|
|
1108
|
-
}
|
|
1109
|
-
const tipShas = new Set(updates.map((u) => u.localSha));
|
|
1110
|
-
const planned = [];
|
|
1111
|
-
const skippedEmptyObjects = [];
|
|
1112
|
-
for (const stat of stats) {
|
|
1113
|
-
const path = pathBySha.get(stat.sha);
|
|
1114
|
-
const object = {
|
|
1115
|
-
...stat,
|
|
1116
|
-
...path ? { path } : {},
|
|
1117
|
-
isRefTip: tipShas.has(stat.sha)
|
|
1118
|
-
};
|
|
1119
|
-
if (stat.sha === EMPTY_BLOB_SHA) skippedEmptyObjects.push(object);
|
|
1120
|
-
else planned.push(object);
|
|
1121
|
-
}
|
|
1122
|
-
const objects = [
|
|
1123
|
-
...planned.filter((o) => !o.isRefTip),
|
|
1124
|
-
...planned.filter((o) => o.isRefTip)
|
|
1125
|
-
];
|
|
1126
|
-
const newRefsMap = new Map(remoteState.refs);
|
|
1127
|
-
for (const update of updates) newRefsMap.set(update.refname, update.localSha);
|
|
1128
|
-
const headSymref = head && newRefsMap.has(head) ? head : remoteState.headSymref && newRefsMap.has(remoteState.headSymref) ? remoteState.headSymref : [...newRefsMap.keys()][0] ?? null;
|
|
1129
|
-
const newRefs = {};
|
|
1130
|
-
const headSha = headSymref ? newRefsMap.get(headSymref) : void 0;
|
|
1131
|
-
if (headSymref && headSha) newRefs[headSymref] = headSha;
|
|
1132
|
-
for (const [refname, sha] of newRefsMap) {
|
|
1133
|
-
if (refname !== headSymref) newRefs[refname] = sha;
|
|
1134
|
-
}
|
|
1135
|
-
const announceNeeded = !remoteState.announced;
|
|
1136
|
-
const totalObjectBytes = objects.reduce((sum, o) => sum + o.size, 0);
|
|
1137
|
-
const uploadFee = objects.reduce(
|
|
1138
|
-
(sum, o) => sum + uploadChargeFor(feeRates, o.size),
|
|
1139
|
-
0n
|
|
1140
|
-
);
|
|
1141
|
-
const eventCount = 1 + (announceNeeded ? 1 : 0);
|
|
1142
|
-
const eventFees = BigInt(eventCount) * feeRates.eventFee;
|
|
1143
|
-
return {
|
|
1144
|
-
repoId,
|
|
1145
|
-
refUpdates,
|
|
1146
|
-
newRefs,
|
|
1147
|
-
headSymref,
|
|
1148
|
-
objects,
|
|
1149
|
-
skippedEmptyObjects,
|
|
1150
|
-
knownShaToTxId,
|
|
1151
|
-
announceNeeded,
|
|
1152
|
-
announcement: {
|
|
1153
|
-
name: options.announcement?.name ?? repoId,
|
|
1154
|
-
description: options.announcement?.description ?? ""
|
|
1155
|
-
},
|
|
1156
|
-
estimate: {
|
|
1157
|
-
objectCount: objects.length,
|
|
1158
|
-
totalObjectBytes,
|
|
1159
|
-
uploadFee,
|
|
1160
|
-
eventCount,
|
|
1161
|
-
eventFees,
|
|
1162
|
-
totalFee: uploadFee + eventFees,
|
|
1163
|
-
skippedEmptyCount: skippedEmptyObjects.length
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
var READ_BATCH_SIZE = 100;
|
|
1168
|
-
async function executePush(options) {
|
|
1169
|
-
const { plan, publisher, remoteState, repoReader, relayUrls } = options;
|
|
1170
|
-
const merged = new Map([...remoteState.shaToTxId, ...plan.knownShaToTxId]);
|
|
1171
|
-
const resultBySha = /* @__PURE__ */ new Map();
|
|
1172
|
-
let totalFeePaid = 0n;
|
|
1173
|
-
const pending = [];
|
|
1174
|
-
for (const object of plan.objects) {
|
|
1175
|
-
const knownTxId = merged.get(object.sha);
|
|
1176
|
-
if (knownTxId !== void 0) {
|
|
1177
|
-
resultBySha.set(object.sha, {
|
|
1178
|
-
sha: object.sha,
|
|
1179
|
-
txId: knownTxId,
|
|
1180
|
-
feePaid: 0n,
|
|
1181
|
-
skipped: true
|
|
1182
|
-
});
|
|
1183
|
-
} else {
|
|
1184
|
-
pending.push(object);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
for (let i = 0; i < pending.length; i += READ_BATCH_SIZE) {
|
|
1188
|
-
const batch = pending.slice(i, i + READ_BATCH_SIZE);
|
|
1189
|
-
const { objects: read, missing } = await repoReader.readObjects(
|
|
1190
|
-
batch.map((o) => o.sha)
|
|
1191
|
-
);
|
|
1192
|
-
if (missing.length > 0) {
|
|
1193
|
-
throw new Error(
|
|
1194
|
-
`objects vanished from the local repository during push: ${missing.join(", ")}`
|
|
1195
|
-
);
|
|
1196
|
-
}
|
|
1197
|
-
const bodyBySha = new Map(read.map((r) => [r.sha, r.body]));
|
|
1198
|
-
for (const object of batch) {
|
|
1199
|
-
const body = bodyBySha.get(object.sha);
|
|
1200
|
-
if (!body) {
|
|
1201
|
-
throw new Error(
|
|
1202
|
-
`internal: cat-file returned no body for ${object.sha}`
|
|
1203
|
-
);
|
|
1204
|
-
}
|
|
1205
|
-
const receipt = await publisher.uploadGitObject({
|
|
1206
|
-
sha: object.sha,
|
|
1207
|
-
type: object.type,
|
|
1208
|
-
body,
|
|
1209
|
-
repoId: plan.repoId,
|
|
1210
|
-
// #368: the path the blob was reached by drives its Content-Type; a
|
|
1211
|
-
// non-blob object (no path) uploads as octet-stream.
|
|
1212
|
-
...object.path ? { path: object.path } : {}
|
|
1213
|
-
});
|
|
1214
|
-
merged.set(object.sha, receipt.txId);
|
|
1215
|
-
totalFeePaid += receipt.feePaid;
|
|
1216
|
-
resultBySha.set(object.sha, {
|
|
1217
|
-
sha: object.sha,
|
|
1218
|
-
txId: receipt.txId,
|
|
1219
|
-
feePaid: receipt.feePaid,
|
|
1220
|
-
skipped: false
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
let announceReceipt = null;
|
|
1225
|
-
if (plan.announceNeeded && !remoteState.announced) {
|
|
1226
|
-
const announceEvent = buildRepoAnnouncement(
|
|
1227
|
-
plan.repoId,
|
|
1228
|
-
plan.announcement.name,
|
|
1229
|
-
plan.announcement.description
|
|
1230
|
-
);
|
|
1231
|
-
announceReceipt = await publisher.publishEvent(announceEvent, relayUrls);
|
|
1232
|
-
totalFeePaid += announceReceipt.feePaid;
|
|
1233
|
-
}
|
|
1234
|
-
const refsEvent = buildRepoRefs(
|
|
1235
|
-
plan.repoId,
|
|
1236
|
-
plan.newRefs,
|
|
1237
|
-
Object.fromEntries(merged)
|
|
1238
|
-
);
|
|
1239
|
-
const refsReceipt = await publisher.publishEvent(refsEvent, relayUrls);
|
|
1240
|
-
totalFeePaid += refsReceipt.feePaid;
|
|
1241
|
-
const uploads = plan.objects.map((o) => {
|
|
1242
|
-
const step = resultBySha.get(o.sha);
|
|
1243
|
-
if (!step) {
|
|
1244
|
-
throw new Error(`internal: no upload result recorded for ${o.sha}`);
|
|
1245
|
-
}
|
|
1246
|
-
return step;
|
|
1247
|
-
});
|
|
1248
|
-
return {
|
|
1249
|
-
uploads,
|
|
1250
|
-
announceReceipt,
|
|
1251
|
-
refsReceipt,
|
|
1252
|
-
arweaveMap: merged,
|
|
1253
|
-
totalFeePaid
|
|
1254
|
-
};
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
export {
|
|
1258
|
-
MAX_OBJECT_SIZE,
|
|
1259
|
-
hashGitObject,
|
|
1260
|
-
createGitBlob,
|
|
1261
|
-
createGitTree,
|
|
1262
|
-
createGitCommit,
|
|
1263
|
-
createGitTag,
|
|
1264
|
-
GitError,
|
|
1265
|
-
GitRepoReader,
|
|
1266
|
-
DEFAULT_CONCURRENCY,
|
|
1267
|
-
ObjectIntegrityError,
|
|
1268
|
-
verifyObjectBody,
|
|
1269
|
-
fetchTxBytes,
|
|
1270
|
-
downloadGitObjects,
|
|
1271
|
-
referencedShas,
|
|
1272
|
-
walkClosure,
|
|
1273
|
-
collectRepoObjects,
|
|
1274
|
-
missingObjectsMessage,
|
|
1275
|
-
ObjectWriteMismatchError,
|
|
1276
|
-
isSafeRefname,
|
|
1277
|
-
runGit,
|
|
1278
|
-
writeGitObject,
|
|
1279
|
-
writeGitObjects,
|
|
1280
|
-
updateRef,
|
|
1281
|
-
setHeadSymref,
|
|
1282
|
-
hexToNpub,
|
|
1283
|
-
npubToHex,
|
|
1284
|
-
ownerToHex,
|
|
1285
|
-
serializeFeeEstimate,
|
|
1286
|
-
serializePushPlan,
|
|
1287
|
-
serializeEventReceipt,
|
|
1288
|
-
serializePushResult,
|
|
1289
|
-
NonFastForwardError,
|
|
1290
|
-
OversizeObjectsError,
|
|
1291
|
-
planPush,
|
|
1292
|
-
executePush
|
|
1293
|
-
};
|
|
1294
|
-
//# sourceMappingURL=chunk-TJPIE5X4.js.map
|