@toon-protocol/client-mcp 0.13.1 → 0.14.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/dist/app/index.html +13 -13
- package/dist/{chunk-YVLSWU46.js → chunk-JCQENRQK.js} +917 -879
- package/dist/chunk-JCQENRQK.js.map +1 -0
- package/dist/daemon.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-YVLSWU46.js.map +0 -1
|
@@ -367,23 +367,54 @@ var STATUS_APPLIED_KIND = 1631;
|
|
|
367
367
|
var STATUS_CLOSED_KIND = 1632;
|
|
368
368
|
var STATUS_DRAFT_KIND = 1633;
|
|
369
369
|
|
|
370
|
-
// ../../node_modules/.pnpm/@toon-protocol+core@
|
|
370
|
+
// ../../node_modules/.pnpm/@toon-protocol+core@2.0.1_typescript@5.9.3/node_modules/@toon-protocol/core/dist/nip34/index.js
|
|
371
371
|
var REPOSITORY_ANNOUNCEMENT_KIND2 = 30617;
|
|
372
372
|
var PATCH_KIND = 1617;
|
|
373
373
|
var ISSUE_KIND = 1621;
|
|
374
374
|
|
|
375
|
-
// ../rig/dist/chunk-
|
|
375
|
+
// ../rig/dist/chunk-3HRFDH7H.js
|
|
376
|
+
import { decode as decodeToon } from "@toon-format/toon";
|
|
376
377
|
var REPOSITORY_STATE_KIND = 30618;
|
|
377
378
|
var COMMENT_KIND = 1622;
|
|
378
|
-
|
|
379
|
+
var MAINTAINERS_TAG = "maintainers";
|
|
380
|
+
var HEX64 = /^[0-9a-f]{64}$/;
|
|
381
|
+
function parseMaintainers(tags) {
|
|
382
|
+
const out = [];
|
|
383
|
+
const seen = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const tag of tags) {
|
|
385
|
+
if (tag[0] !== MAINTAINERS_TAG) continue;
|
|
386
|
+
for (const value of tag.slice(1)) {
|
|
387
|
+
const hex = value.toLowerCase();
|
|
388
|
+
if (HEX64.test(hex) && !seen.has(hex)) {
|
|
389
|
+
seen.add(hex);
|
|
390
|
+
out.push(hex);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return out;
|
|
395
|
+
}
|
|
396
|
+
function buildRepoAnnouncement(repoId, name, description, maintainers = []) {
|
|
397
|
+
const tags = [
|
|
398
|
+
["d", repoId],
|
|
399
|
+
["name", name],
|
|
400
|
+
["description", description]
|
|
401
|
+
];
|
|
402
|
+
const declared = [];
|
|
403
|
+
const seen = /* @__PURE__ */ new Set();
|
|
404
|
+
for (const value of maintainers) {
|
|
405
|
+
const hex = value.toLowerCase();
|
|
406
|
+
if (HEX64.test(hex) && !seen.has(hex)) {
|
|
407
|
+
seen.add(hex);
|
|
408
|
+
declared.push(hex);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (declared.length > 0) {
|
|
412
|
+
tags.push([MAINTAINERS_TAG, ...declared]);
|
|
413
|
+
}
|
|
379
414
|
return {
|
|
380
415
|
kind: REPOSITORY_ANNOUNCEMENT_KIND2,
|
|
381
416
|
content: "",
|
|
382
|
-
tags
|
|
383
|
-
["d", repoId],
|
|
384
|
-
["name", name],
|
|
385
|
-
["description", description]
|
|
386
|
-
],
|
|
417
|
+
tags,
|
|
387
418
|
created_at: Math.floor(Date.now() / 1e3)
|
|
388
419
|
};
|
|
389
420
|
}
|
|
@@ -432,12 +463,15 @@ function buildComment(repoOwnerPubkey, repoId, issueOrPrEventId, authorPubkey, b
|
|
|
432
463
|
created_at: Math.floor(Date.now() / 1e3)
|
|
433
464
|
};
|
|
434
465
|
}
|
|
435
|
-
function buildPatch(repoOwnerPubkey, repoId, title, commits, branchTag, content = "") {
|
|
466
|
+
function buildPatch(repoOwnerPubkey, repoId, title, commits, branchTag, content = "", description) {
|
|
436
467
|
const tags = [
|
|
437
468
|
["a", `${REPOSITORY_ANNOUNCEMENT_KIND2}:${repoOwnerPubkey}:${repoId}`],
|
|
438
469
|
["p", repoOwnerPubkey],
|
|
439
470
|
["subject", title]
|
|
440
471
|
];
|
|
472
|
+
if (description !== void 0 && description !== "") {
|
|
473
|
+
tags.push(["description", description]);
|
|
474
|
+
}
|
|
441
475
|
for (const commit of commits) {
|
|
442
476
|
tags.push(["commit", commit.sha]);
|
|
443
477
|
tags.push(["parent-commit", commit.parentSha]);
|
|
@@ -464,966 +498,968 @@ function buildStatus(targetEventId, statusKind, targetPubkey) {
|
|
|
464
498
|
created_at: Math.floor(Date.now() / 1e3)
|
|
465
499
|
};
|
|
466
500
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
var
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
import { promisify } from "util";
|
|
474
|
-
var execFileAsync = promisify(execFile);
|
|
475
|
-
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
476
|
-
var GitError = class extends Error {
|
|
477
|
-
constructor(message, exitCode, stderr) {
|
|
478
|
-
super(message);
|
|
479
|
-
this.exitCode = exitCode;
|
|
480
|
-
this.stderr = stderr;
|
|
481
|
-
this.name = "GitError";
|
|
482
|
-
}
|
|
483
|
-
exitCode;
|
|
484
|
-
stderr;
|
|
485
|
-
};
|
|
486
|
-
var FULL_SHA_RE = /^[0-9a-f]{40}$/;
|
|
487
|
-
var REV_TOKEN_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]*(?:[~^][0-9]*)*$/;
|
|
488
|
-
function isValidRevision(rev) {
|
|
489
|
-
if (rev.length === 0 || rev.length > 1024) return false;
|
|
490
|
-
if (!REV_TOKEN_RE.test(rev)) return false;
|
|
491
|
-
if (rev.includes("..")) return false;
|
|
492
|
-
if (rev.endsWith(".lock") || rev.endsWith("/") || rev.endsWith(".")) return false;
|
|
493
|
-
return true;
|
|
501
|
+
var ARWEAVE_FETCH_TIMEOUT_MS = 15e3;
|
|
502
|
+
var ARWEAVE_GRAPHQL_URL = "https://arweave.net/graphql";
|
|
503
|
+
var SHA_CACHE_MAX_SIZE = 1e4;
|
|
504
|
+
var shaToTxIdCache = /* @__PURE__ */ new Map();
|
|
505
|
+
function isValidGitSha(sha) {
|
|
506
|
+
return /^[0-9a-f]{40}$/i.test(sha);
|
|
494
507
|
}
|
|
495
|
-
function
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
508
|
+
function sanitizeGraphQLValue(value) {
|
|
509
|
+
return value.replace(/["\\\n\r\u0000-\u001f`]/g, "");
|
|
510
|
+
}
|
|
511
|
+
function shaCacheKey(sha, repo) {
|
|
512
|
+
return `${sha}:${repo}`;
|
|
513
|
+
}
|
|
514
|
+
function seedShaCache(mappings) {
|
|
515
|
+
const entries = mappings instanceof Map ? mappings.entries() : mappings;
|
|
516
|
+
for (const [key2, txId] of entries) {
|
|
517
|
+
if (shaToTxIdCache.size >= SHA_CACHE_MAX_SIZE) {
|
|
518
|
+
const firstKey = shaToTxIdCache.keys().next().value;
|
|
519
|
+
if (firstKey !== void 0) {
|
|
520
|
+
shaToTxIdCache.delete(firstKey);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
shaToTxIdCache.set(key2, txId);
|
|
500
524
|
}
|
|
501
525
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
526
|
+
var ARWEAVE_TX_ID_RE = /^[a-zA-Z0-9_-]{43}$/;
|
|
527
|
+
function isValidArweaveTxId(txId) {
|
|
528
|
+
return ARWEAVE_TX_ID_RE.test(txId);
|
|
529
|
+
}
|
|
530
|
+
async function resolveGitSha(sha, repo) {
|
|
531
|
+
if (!isValidGitSha(sha)) {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
const cacheKey = shaCacheKey(sha, repo);
|
|
535
|
+
const cached = shaToTxIdCache.get(cacheKey);
|
|
536
|
+
if (cached !== void 0) {
|
|
537
|
+
return cached;
|
|
538
|
+
}
|
|
539
|
+
const safeSha = sanitizeGraphQLValue(sha);
|
|
540
|
+
const safeRepo = sanitizeGraphQLValue(repo);
|
|
541
|
+
const query = `query {
|
|
542
|
+
transactions(tags: [
|
|
543
|
+
{ name: "Git-SHA", values: ["${safeSha}"] },
|
|
544
|
+
{ name: "Repo", values: ["${safeRepo}"] }
|
|
545
|
+
]) {
|
|
546
|
+
edges { node { id } }
|
|
547
|
+
}
|
|
548
|
+
}`;
|
|
549
|
+
try {
|
|
550
|
+
const response = await fetch(ARWEAVE_GRAPHQL_URL, {
|
|
551
|
+
method: "POST",
|
|
552
|
+
headers: { "Content-Type": "application/json" },
|
|
553
|
+
body: JSON.stringify({ query }),
|
|
554
|
+
signal: AbortSignal.timeout(ARWEAVE_FETCH_TIMEOUT_MS)
|
|
555
|
+
});
|
|
556
|
+
if (!response.ok) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
const json = await response.json();
|
|
560
|
+
const edges = json.data?.transactions?.edges;
|
|
561
|
+
if (!edges || edges.length === 0) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
const txId = edges[0]?.node?.id;
|
|
565
|
+
if (!txId || !isValidArweaveTxId(txId)) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
if (shaToTxIdCache.size >= SHA_CACHE_MAX_SIZE) {
|
|
569
|
+
const firstKey = shaToTxIdCache.keys().next().value;
|
|
570
|
+
if (firstKey !== void 0) {
|
|
571
|
+
shaToTxIdCache.delete(firstKey);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
shaToTxIdCache.set(cacheKey, txId);
|
|
575
|
+
return txId;
|
|
576
|
+
} catch {
|
|
577
|
+
return null;
|
|
507
578
|
}
|
|
508
579
|
}
|
|
509
|
-
function
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
580
|
+
function isValidRelayUrl(url) {
|
|
581
|
+
return /^wss?:\/\//i.test(url);
|
|
582
|
+
}
|
|
583
|
+
var WS_OPEN = 1;
|
|
584
|
+
function defaultWebSocketFactory2(url) {
|
|
585
|
+
const ctor = globalThis.WebSocket;
|
|
586
|
+
if (!ctor) {
|
|
514
587
|
throw new Error(
|
|
515
|
-
|
|
588
|
+
"No global WebSocket constructor (Node >= 22 required) \u2014 pass webSocketFactory"
|
|
516
589
|
);
|
|
517
590
|
}
|
|
591
|
+
return new ctor(url);
|
|
518
592
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
pending = null;
|
|
523
|
-
objects = [];
|
|
524
|
-
missing = [];
|
|
525
|
-
push(chunk) {
|
|
526
|
-
this.buf = this.buf.length === 0 ? chunk : Buffer.concat([this.buf, chunk]);
|
|
527
|
-
this.drain();
|
|
593
|
+
function decodeEventPayload(payload) {
|
|
594
|
+
if (payload !== null && typeof payload === "object") {
|
|
595
|
+
return payload;
|
|
528
596
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
return this.pending === null && this.buf.length === 0;
|
|
597
|
+
if (typeof payload !== "string") {
|
|
598
|
+
return null;
|
|
532
599
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
600
|
+
try {
|
|
601
|
+
const parsed = JSON.parse(payload);
|
|
602
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
603
|
+
return parsed;
|
|
604
|
+
}
|
|
605
|
+
} catch {
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
return decodeToon(payload);
|
|
609
|
+
} catch {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function queryRelay(relayUrl, filter, timeoutMs, webSocketFactory) {
|
|
614
|
+
return new Promise((resolve2, reject) => {
|
|
615
|
+
const events = [];
|
|
616
|
+
const subId = `git-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
617
|
+
let ws;
|
|
618
|
+
let timeoutHandle;
|
|
619
|
+
let settled = false;
|
|
620
|
+
const settle = (outcome, error) => {
|
|
621
|
+
if (settled) return;
|
|
622
|
+
settled = true;
|
|
623
|
+
clearTimeout(timeoutHandle);
|
|
624
|
+
try {
|
|
625
|
+
if (ws.readyState === WS_OPEN) {
|
|
626
|
+
ws.send(JSON.stringify(["CLOSE", subId]));
|
|
558
627
|
}
|
|
628
|
+
ws.close();
|
|
629
|
+
} catch {
|
|
559
630
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
631
|
+
if (outcome === "reject") {
|
|
632
|
+
reject(error);
|
|
633
|
+
} else {
|
|
634
|
+
resolve2(events);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
if (!isValidRelayUrl(relayUrl)) {
|
|
638
|
+
reject(
|
|
639
|
+
new Error(
|
|
640
|
+
`Invalid relay URL protocol (must be ws:// or wss://): ${relayUrl}`
|
|
641
|
+
)
|
|
564
642
|
);
|
|
643
|
+
return;
|
|
565
644
|
}
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
var GitRepoReader = class {
|
|
569
|
-
constructor(repoPath) {
|
|
570
|
-
this.repoPath = repoPath;
|
|
571
|
-
}
|
|
572
|
-
repoPath;
|
|
573
|
-
/** Run git with argument-array safety; resolves stdout as UTF-8. */
|
|
574
|
-
async git(args, opts = {}) {
|
|
575
645
|
try {
|
|
576
|
-
|
|
577
|
-
cwd: this.repoPath,
|
|
578
|
-
maxBuffer: MAX_BUFFER,
|
|
579
|
-
encoding: "utf-8"
|
|
580
|
-
});
|
|
581
|
-
return { stdout, exitCode: 0 };
|
|
646
|
+
ws = webSocketFactory(relayUrl);
|
|
582
647
|
} catch (err) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
if (exitCode !== void 0 && opts.allowExitCodes?.includes(exitCode)) {
|
|
586
|
-
return { stdout: e.stdout ?? "", exitCode };
|
|
587
|
-
}
|
|
588
|
-
throw new GitError(
|
|
589
|
-
`git ${args[0]} failed${exitCode !== void 0 ? ` (exit ${exitCode})` : ""}: ${(e.stderr ?? e.message ?? "").trim()}`,
|
|
590
|
-
exitCode,
|
|
591
|
-
(e.stderr ?? "").trim()
|
|
592
|
-
);
|
|
648
|
+
reject(new Error(`Failed to connect to relay ${relayUrl}: ${String(err)}`));
|
|
649
|
+
return;
|
|
593
650
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
if (!refname || !sha || !objecttype || !OBJECT_TYPES.has(objecttype)) {
|
|
613
|
-
throw new GitError(`unexpected for-each-ref line: ${JSON.stringify(line)}`, void 0, "");
|
|
651
|
+
timeoutHandle = setTimeout(() => {
|
|
652
|
+
settle("resolve");
|
|
653
|
+
}, timeoutMs);
|
|
654
|
+
ws.addEventListener("open", () => {
|
|
655
|
+
ws.send(JSON.stringify(["REQ", subId, filter]));
|
|
656
|
+
});
|
|
657
|
+
ws.addEventListener("message", (msgEvent) => {
|
|
658
|
+
try {
|
|
659
|
+
const msg = JSON.parse(String(msgEvent.data));
|
|
660
|
+
if (!Array.isArray(msg) || msg.length < 2) return;
|
|
661
|
+
const msgType = msg[0];
|
|
662
|
+
if (msgType === "EVENT" && msg[1] === subId && msg[2] !== void 0) {
|
|
663
|
+
const event = decodeEventPayload(msg[2]);
|
|
664
|
+
if (event) events.push(event);
|
|
665
|
+
} else if (msgType === "EOSE" && msg[1] === subId) {
|
|
666
|
+
settle("resolve");
|
|
667
|
+
}
|
|
668
|
+
} catch {
|
|
614
669
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
670
|
+
});
|
|
671
|
+
ws.addEventListener("error", (event) => {
|
|
672
|
+
const detail = typeof event === "object" && event !== null && "message" in event ? String(event.message) : "unknown";
|
|
673
|
+
settle(
|
|
674
|
+
"reject",
|
|
675
|
+
new Error(`WebSocket error connecting to ${relayUrl}: ${detail}`)
|
|
676
|
+
);
|
|
677
|
+
});
|
|
678
|
+
ws.addEventListener("close", () => {
|
|
679
|
+
settle("resolve");
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
function latestReplaceable(events) {
|
|
684
|
+
let winner = null;
|
|
685
|
+
for (const event of events) {
|
|
686
|
+
if (winner === null || event.created_at > winner.created_at || event.created_at === winner.created_at && event.id < winner.id) {
|
|
687
|
+
winner = event;
|
|
621
688
|
}
|
|
622
|
-
const head = headRes.exitCode === 0 ? headRes.stdout.trim() || void 0 : void 0;
|
|
623
|
-
return { head, refs };
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* SHAs of every object reachable from `want` but not from `have`
|
|
627
|
-
* (`git rev-list --objects <want…> --not <have…>`), i.e. the push delta.
|
|
628
|
-
*
|
|
629
|
-
* Haves that don't exist locally (e.g. remote tips we never fetched) are
|
|
630
|
-
* filtered out first via one `cat-file --batch-check` pass — rev-list
|
|
631
|
-
* would otherwise die on them.
|
|
632
|
-
*/
|
|
633
|
-
async objectsBetween(want, have) {
|
|
634
|
-
const objects = await this.objectsBetweenWithPaths(want, have);
|
|
635
|
-
return objects.map((o) => o.sha);
|
|
636
689
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (spaceIdx === -1) {
|
|
656
|
-
objects.push({ sha: line });
|
|
657
|
-
} else {
|
|
658
|
-
const path = line.slice(spaceIdx + 1);
|
|
659
|
-
objects.push({
|
|
660
|
-
sha: line.slice(0, spaceIdx),
|
|
661
|
-
...path ? { path } : {}
|
|
662
|
-
});
|
|
690
|
+
return winner;
|
|
691
|
+
}
|
|
692
|
+
function getTagValue(tags, name) {
|
|
693
|
+
const tag = tags.find((t) => t[0] === name);
|
|
694
|
+
return tag?.[1];
|
|
695
|
+
}
|
|
696
|
+
var MAX_REFS_PER_EVENT = 1e3;
|
|
697
|
+
var SYMREF_PREFIX = "ref: ";
|
|
698
|
+
function parseRefsEvent(event) {
|
|
699
|
+
const refs = /* @__PURE__ */ new Map();
|
|
700
|
+
const shaToTxId = /* @__PURE__ */ new Map();
|
|
701
|
+
let headSymref = null;
|
|
702
|
+
for (const tag of event.tags) {
|
|
703
|
+
const [tagName, v1, v2] = tag;
|
|
704
|
+
if (tagName === "r" && v1 && v2) {
|
|
705
|
+
if (v1 === "HEAD" && v2.startsWith(SYMREF_PREFIX)) {
|
|
706
|
+
headSymref = v2.slice(SYMREF_PREFIX.length);
|
|
707
|
+
continue;
|
|
663
708
|
}
|
|
709
|
+
if (refs.size >= MAX_REFS_PER_EVENT) continue;
|
|
710
|
+
refs.set(v1, v2);
|
|
711
|
+
} else if (tagName === "HEAD" && v1?.startsWith(SYMREF_PREFIX)) {
|
|
712
|
+
headSymref = v1.slice(SYMREF_PREFIX.length);
|
|
713
|
+
} else if (tagName === "arweave" && v1 && v2) {
|
|
714
|
+
shaToTxId.set(v1, v2);
|
|
664
715
|
}
|
|
665
|
-
return objects;
|
|
666
716
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
child.on("error", (err) => {
|
|
681
|
-
reject(new GitError(`failed to spawn git ${args[0]}: ${err.message}`, void 0, ""));
|
|
682
|
-
});
|
|
683
|
-
child.on("close", (code) => {
|
|
684
|
-
if (code !== 0) {
|
|
685
|
-
return reject(
|
|
686
|
-
new GitError(`git ${args[0]} failed (exit ${code}): ${stderr.trim()}`, code ?? void 0, stderr.trim())
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
resolve2(Buffer.concat(out));
|
|
690
|
-
});
|
|
691
|
-
child.stdin.on("error", () => {
|
|
692
|
-
});
|
|
693
|
-
child.stdin.write(input);
|
|
694
|
-
child.stdin.end();
|
|
695
|
-
});
|
|
717
|
+
return { refs, headSymref, shaToTxId };
|
|
718
|
+
}
|
|
719
|
+
async function fetchRemoteState(options) {
|
|
720
|
+
const {
|
|
721
|
+
relayUrls,
|
|
722
|
+
ownerPubkey,
|
|
723
|
+
repoId,
|
|
724
|
+
timeoutMs = 1e4,
|
|
725
|
+
resolveSha = resolveGitSha,
|
|
726
|
+
webSocketFactory = defaultWebSocketFactory2
|
|
727
|
+
} = options;
|
|
728
|
+
if (relayUrls.length === 0) {
|
|
729
|
+
throw new Error("fetchRemoteState: relayUrls must not be empty");
|
|
696
730
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (revs.length === 0) return [];
|
|
700
|
-
const { missing } = await this.batchCheck(revs);
|
|
701
|
-
const missingSet = new Set(missing);
|
|
702
|
-
return revs.filter((r) => !missingSet.has(r));
|
|
731
|
+
if (!ownerPubkey) {
|
|
732
|
+
throw new Error("fetchRemoteState: ownerPubkey is required");
|
|
703
733
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
const stdout = await this.runWithStdin(
|
|
707
|
-
["cat-file", "--batch-check"],
|
|
708
|
-
names.join("\n") + "\n"
|
|
709
|
-
);
|
|
710
|
-
const missing = [];
|
|
711
|
-
for (const line of stdout.toString("utf-8").split("\n")) {
|
|
712
|
-
if (line.endsWith(" missing")) missing.push(line.slice(0, -" missing".length));
|
|
713
|
-
}
|
|
714
|
-
return { missing };
|
|
734
|
+
if (!repoId) {
|
|
735
|
+
throw new Error("fetchRemoteState: repoId is required");
|
|
715
736
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
)
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
const [sha, type, sizeStr] = line.split(" ");
|
|
737
|
-
const size = Number.parseInt(sizeStr ?? "", 10);
|
|
738
|
-
if (!sha || !type || !OBJECT_TYPES.has(type) || !Number.isSafeInteger(size) || size < 0) {
|
|
739
|
-
throw new GitError(
|
|
740
|
-
`unexpected cat-file --batch-check line: ${JSON.stringify(line)}`,
|
|
741
|
-
void 0,
|
|
742
|
-
""
|
|
743
|
-
);
|
|
737
|
+
const filter = {
|
|
738
|
+
kinds: [REPOSITORY_ANNOUNCEMENT_KIND2, REPOSITORY_STATE_KIND],
|
|
739
|
+
authors: [ownerPubkey],
|
|
740
|
+
"#d": [repoId]
|
|
741
|
+
};
|
|
742
|
+
const results = await Promise.allSettled(
|
|
743
|
+
relayUrls.map((url) => queryRelay(url, filter, timeoutMs, webSocketFactory))
|
|
744
|
+
);
|
|
745
|
+
const failures = [];
|
|
746
|
+
const byId = /* @__PURE__ */ new Map();
|
|
747
|
+
for (const result of results) {
|
|
748
|
+
if (result.status === "rejected") {
|
|
749
|
+
failures.push(String(result.reason?.message ?? result.reason));
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
for (const event of result.value) {
|
|
753
|
+
if (event.pubkey !== ownerPubkey) continue;
|
|
754
|
+
if (getTagValue(event.tags, "d") !== repoId) continue;
|
|
755
|
+
if (typeof event.id === "string" && !byId.has(event.id)) {
|
|
756
|
+
byId.set(event.id, event);
|
|
744
757
|
}
|
|
745
|
-
objects.push({ sha, type, size });
|
|
746
758
|
}
|
|
747
|
-
return { objects, missing };
|
|
748
759
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
* chunk boundaries. Missing objects are reported, not thrown.
|
|
753
|
-
*/
|
|
754
|
-
async readObjects(shas) {
|
|
755
|
-
for (const sha of shas) assertFullSha(sha, "sha");
|
|
756
|
-
if (shas.length === 0) return { objects: [], missing: [] };
|
|
757
|
-
const parser = new BatchParser();
|
|
758
|
-
await new Promise((resolve2, reject) => {
|
|
759
|
-
const child = spawn("git", ["cat-file", "--batch"], {
|
|
760
|
-
cwd: this.repoPath,
|
|
761
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
762
|
-
});
|
|
763
|
-
let stderr = "";
|
|
764
|
-
let parseError = null;
|
|
765
|
-
child.stderr.on("data", (chunk) => {
|
|
766
|
-
stderr += chunk.toString("utf-8");
|
|
767
|
-
});
|
|
768
|
-
child.stdout.on("data", (chunk) => {
|
|
769
|
-
if (parseError) return;
|
|
770
|
-
try {
|
|
771
|
-
parser.push(chunk);
|
|
772
|
-
} catch (err) {
|
|
773
|
-
parseError = err;
|
|
774
|
-
child.kill();
|
|
775
|
-
}
|
|
776
|
-
});
|
|
777
|
-
child.on("error", (err) => {
|
|
778
|
-
reject(new GitError(`failed to spawn git cat-file: ${err.message}`, void 0, ""));
|
|
779
|
-
});
|
|
780
|
-
child.on("close", (code) => {
|
|
781
|
-
if (parseError) return reject(parseError);
|
|
782
|
-
if (code !== 0) {
|
|
783
|
-
return reject(
|
|
784
|
-
new GitError(`git cat-file --batch failed (exit ${code}): ${stderr.trim()}`, code ?? void 0, stderr.trim())
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
if (!parser.isComplete()) {
|
|
788
|
-
return reject(
|
|
789
|
-
new GitError("git cat-file --batch output ended mid-record", code ?? void 0, stderr.trim())
|
|
790
|
-
);
|
|
791
|
-
}
|
|
792
|
-
resolve2();
|
|
793
|
-
});
|
|
794
|
-
child.stdin.on("error", () => {
|
|
795
|
-
});
|
|
796
|
-
child.stdin.write(shas.join("\n") + "\n");
|
|
797
|
-
child.stdin.end();
|
|
798
|
-
});
|
|
799
|
-
return { objects: parser.objects, missing: parser.missing };
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* `git merge-base --is-ancestor <a> <b>` — true when `a` is an ancestor
|
|
803
|
-
* of `b` (fast-forward check / force detection). Exit codes other than
|
|
804
|
-
* 0/1 (e.g. unknown revisions) throw.
|
|
805
|
-
*/
|
|
806
|
-
async isAncestor(a, b) {
|
|
807
|
-
assertRevision(a, "ancestor candidate");
|
|
808
|
-
assertRevision(b, "descendant candidate");
|
|
809
|
-
const { exitCode } = await this.git(
|
|
810
|
-
["merge-base", "--is-ancestor", a, b],
|
|
811
|
-
{ allowExitCodes: [1] }
|
|
760
|
+
if (failures.length === relayUrls.length) {
|
|
761
|
+
throw new Error(
|
|
762
|
+
`fetchRemoteState: all ${relayUrls.length} relay(s) failed: ${failures.join("; ")}`
|
|
812
763
|
);
|
|
813
|
-
return exitCode === 0;
|
|
814
764
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
765
|
+
const events = [...byId.values()];
|
|
766
|
+
const refsEvent = latestReplaceable(
|
|
767
|
+
events.filter((e) => e.kind === REPOSITORY_STATE_KIND)
|
|
768
|
+
);
|
|
769
|
+
const announceEvent = latestReplaceable(
|
|
770
|
+
events.filter((e) => e.kind === REPOSITORY_ANNOUNCEMENT_KIND2)
|
|
771
|
+
);
|
|
772
|
+
const { refs, headSymref, shaToTxId } = refsEvent ? parseRefsEvent(refsEvent) : {
|
|
773
|
+
refs: /* @__PURE__ */ new Map(),
|
|
774
|
+
headSymref: null,
|
|
775
|
+
shaToTxId: /* @__PURE__ */ new Map()
|
|
776
|
+
};
|
|
777
|
+
if (shaToTxId.size > 0) {
|
|
778
|
+
seedShaCache(
|
|
779
|
+
[...shaToTxId].map(
|
|
780
|
+
([sha, txId]) => [shaCacheKey(sha, repoId), txId]
|
|
781
|
+
)
|
|
782
|
+
);
|
|
823
783
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
for (const sha of shas) assertFullSha(sha, "sha");
|
|
832
|
-
if (shas.length === 0) return /* @__PURE__ */ new Map();
|
|
833
|
-
const { stdout } = await this.git([
|
|
834
|
-
"rev-list",
|
|
835
|
-
"--no-walk=unsorted",
|
|
836
|
-
"--parents",
|
|
837
|
-
...shas,
|
|
838
|
-
"--"
|
|
839
|
-
]);
|
|
840
|
-
const parents = /* @__PURE__ */ new Map();
|
|
841
|
-
for (const line of stdout.split("\n")) {
|
|
842
|
-
if (!line) continue;
|
|
843
|
-
const [sha, ...rest] = line.split(" ");
|
|
844
|
-
if (!sha || !FULL_SHA_RE.test(sha) || rest.some((p) => !FULL_SHA_RE.test(p))) {
|
|
845
|
-
throw new GitError(
|
|
846
|
-
`unexpected rev-list --parents line: ${JSON.stringify(line)}`,
|
|
847
|
-
void 0,
|
|
848
|
-
""
|
|
849
|
-
);
|
|
784
|
+
const name = announceEvent ? getTagValue(announceEvent.tags, "name") ?? null : null;
|
|
785
|
+
const description = announceEvent ? getTagValue(announceEvent.tags, "description") ?? announceEvent.content : null;
|
|
786
|
+
const relays = [];
|
|
787
|
+
if (announceEvent) {
|
|
788
|
+
for (const tag of announceEvent.tags) {
|
|
789
|
+
if (tag[0] === "relays") {
|
|
790
|
+
relays.push(...tag.slice(1).filter((url) => url.length > 0));
|
|
850
791
|
}
|
|
851
|
-
parents.set(sha, rest);
|
|
852
792
|
}
|
|
853
|
-
return parents;
|
|
854
793
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
void 0,
|
|
867
|
-
""
|
|
868
|
-
);
|
|
794
|
+
const maintainers = announceEvent ? parseMaintainers(announceEvent.tags) : [];
|
|
795
|
+
const resolveMissing = async (shas) => {
|
|
796
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
797
|
+
const missing = [];
|
|
798
|
+
for (const sha of new Set(shas)) {
|
|
799
|
+
const known = shaToTxId.get(sha);
|
|
800
|
+
if (known !== void 0) {
|
|
801
|
+
resolved.set(sha, known);
|
|
802
|
+
} else {
|
|
803
|
+
missing.push(sha);
|
|
804
|
+
}
|
|
869
805
|
}
|
|
870
|
-
|
|
806
|
+
const lookups = await Promise.all(
|
|
807
|
+
missing.map(
|
|
808
|
+
async (sha) => [sha, await resolveSha(sha, repoId)]
|
|
809
|
+
)
|
|
810
|
+
);
|
|
811
|
+
for (const [sha, txId] of lookups) {
|
|
812
|
+
if (txId) resolved.set(sha, txId);
|
|
813
|
+
}
|
|
814
|
+
return resolved;
|
|
815
|
+
};
|
|
816
|
+
return {
|
|
817
|
+
announced: announceEvent !== null,
|
|
818
|
+
refs,
|
|
819
|
+
headSymref,
|
|
820
|
+
shaToTxId,
|
|
821
|
+
refsEvent,
|
|
822
|
+
announceEvent,
|
|
823
|
+
name,
|
|
824
|
+
description,
|
|
825
|
+
relays,
|
|
826
|
+
maintainers,
|
|
827
|
+
resolveMissing
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// ../rig/dist/chunk-X2CZPPDM.js
|
|
832
|
+
var MAX_OBJECT_SIZE = 95 * 1024;
|
|
833
|
+
|
|
834
|
+
// ../rig/dist/chunk-W2SDL2PE.js
|
|
835
|
+
import { execFile, spawn } from "child_process";
|
|
836
|
+
import { promisify } from "util";
|
|
837
|
+
import { execFile as execFile2, spawn as spawn2 } from "child_process";
|
|
838
|
+
import { promisify as promisify2 } from "util";
|
|
839
|
+
var execFileAsync = promisify(execFile);
|
|
840
|
+
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
841
|
+
var GitError = class extends Error {
|
|
842
|
+
constructor(message, exitCode, stderr) {
|
|
843
|
+
super(message);
|
|
844
|
+
this.exitCode = exitCode;
|
|
845
|
+
this.stderr = stderr;
|
|
846
|
+
this.name = "GitError";
|
|
871
847
|
}
|
|
848
|
+
exitCode;
|
|
849
|
+
stderr;
|
|
872
850
|
};
|
|
873
|
-
var
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
851
|
+
var FULL_SHA_RE = /^[0-9a-f]{40}$/;
|
|
852
|
+
var REV_TOKEN_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]*(?:[~^][0-9]*)*$/;
|
|
853
|
+
function isValidRevision(rev) {
|
|
854
|
+
if (rev.length === 0 || rev.length > 1024) return false;
|
|
855
|
+
if (!REV_TOKEN_RE.test(rev)) return false;
|
|
856
|
+
if (rev.includes("..")) return false;
|
|
857
|
+
if (rev.endsWith(".lock") || rev.endsWith("/") || rev.endsWith(".")) return false;
|
|
858
|
+
return true;
|
|
859
|
+
}
|
|
860
|
+
function assertRevision(rev, what) {
|
|
861
|
+
if (!isValidRevision(rev)) {
|
|
862
|
+
throw new Error(
|
|
863
|
+
`${what} is not a valid git revision (got ${JSON.stringify(rev)}); expected a SHA or simple refname \u2014 options/ranges are rejected`
|
|
877
864
|
);
|
|
878
|
-
this.refs = refs;
|
|
879
|
-
this.name = "NonFastForwardError";
|
|
880
865
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
`${objects.length} object(s) exceed the ${MAX_OBJECT_SIZE} byte upload limit: ` + objects.map((o) => `${o.path ?? o.sha} (${o.size} bytes)`).join(", ")
|
|
866
|
+
}
|
|
867
|
+
function assertFullSha(sha, what) {
|
|
868
|
+
if (!FULL_SHA_RE.test(sha)) {
|
|
869
|
+
throw new Error(
|
|
870
|
+
`${what} is not a full 40-hex SHA-1 (got ${JSON.stringify(sha)})`
|
|
887
871
|
);
|
|
888
|
-
this.objects = objects;
|
|
889
|
-
this.name = "OversizeObjectsError";
|
|
890
872
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
const
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
selected = options.refs.map((name) => {
|
|
901
|
-
const ref = localByName.get(name);
|
|
902
|
-
if (!ref) {
|
|
903
|
-
throw new Error(
|
|
904
|
-
`ref ${JSON.stringify(name)} does not exist locally (ref deletion is out of scope in v1)`
|
|
905
|
-
);
|
|
906
|
-
}
|
|
907
|
-
return ref;
|
|
908
|
-
});
|
|
909
|
-
} else {
|
|
910
|
-
selected = localRefs;
|
|
873
|
+
}
|
|
874
|
+
function assertRange(range, what) {
|
|
875
|
+
const parts = range.split(/\.{2,3}/);
|
|
876
|
+
const separators = range.match(/\.{2,3}/g) ?? [];
|
|
877
|
+
const ok = parts.length <= 2 && separators.length === parts.length - 1 && parts.every((p) => isValidRevision(p));
|
|
878
|
+
if (!ok) {
|
|
879
|
+
throw new Error(
|
|
880
|
+
`${what} is not a valid revision range (got ${JSON.stringify(range)}); expected <rev>, <rev>..<rev>, or <rev>...<rev>`
|
|
881
|
+
);
|
|
911
882
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
let fastForward = false;
|
|
925
|
-
try {
|
|
926
|
-
fastForward = await repoReader.isAncestor(remoteSha, ref.sha);
|
|
927
|
-
} catch (err) {
|
|
928
|
-
if (!(err instanceof GitError)) throw err;
|
|
929
|
-
fastForward = false;
|
|
930
|
-
}
|
|
931
|
-
if (fastForward) {
|
|
932
|
-
refUpdates.push({ refname: ref.refname, localSha: ref.sha, remoteSha, kind: "fast-forward" });
|
|
933
|
-
} else if (force) {
|
|
934
|
-
refUpdates.push({ refname: ref.refname, localSha: ref.sha, remoteSha, kind: "forced" });
|
|
935
|
-
} else {
|
|
936
|
-
rejected.push({ refname: ref.refname, localSha: ref.sha, remoteSha });
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
if (rejected.length > 0) throw new NonFastForwardError(rejected);
|
|
940
|
-
const updates = refUpdates.filter((u) => u.kind !== "up-to-date");
|
|
941
|
-
const wantTips = [...new Set(updates.map((u) => u.localSha))];
|
|
942
|
-
const haveTips = [...new Set(remoteState.refs.values())];
|
|
943
|
-
const delta = wantTips.length > 0 ? await repoReader.objectsBetweenWithPaths(wantTips, haveTips) : [];
|
|
944
|
-
const knownShaToTxId = new Map(remoteState.shaToTxId);
|
|
945
|
-
let candidates = delta.filter((o) => !knownShaToTxId.has(o.sha));
|
|
946
|
-
if (candidates.length > 0) {
|
|
947
|
-
const resolved = await resolveMissing(candidates.map((o) => o.sha));
|
|
948
|
-
for (const [sha, txId] of resolved) knownShaToTxId.set(sha, txId);
|
|
949
|
-
candidates = candidates.filter((o) => !knownShaToTxId.has(o.sha));
|
|
883
|
+
}
|
|
884
|
+
var OBJECT_TYPES = /* @__PURE__ */ new Set(["blob", "tree", "commit", "tag"]);
|
|
885
|
+
var BatchParser = class {
|
|
886
|
+
buf = Buffer.alloc(0);
|
|
887
|
+
pending = null;
|
|
888
|
+
objects = [];
|
|
889
|
+
missing = [];
|
|
890
|
+
push(chunk) {
|
|
891
|
+
this.buf = this.buf.length === 0 ? chunk : Buffer.concat([this.buf, chunk]);
|
|
892
|
+
this.drain();
|
|
950
893
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
);
|
|
955
|
-
if (missing.length > 0) {
|
|
956
|
-
throw new Error(
|
|
957
|
-
`objects vanished from the local repository during planning: ${missing.join(", ")}`
|
|
958
|
-
);
|
|
894
|
+
/** True when no partially-parsed record remains. */
|
|
895
|
+
isComplete() {
|
|
896
|
+
return this.pending === null && this.buf.length === 0;
|
|
959
897
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
898
|
+
drain() {
|
|
899
|
+
for (; ; ) {
|
|
900
|
+
if (this.pending) {
|
|
901
|
+
const needed = this.pending.size + 1;
|
|
902
|
+
if (this.buf.length < needed) return;
|
|
903
|
+
const body = Buffer.from(this.buf.subarray(0, this.pending.size));
|
|
904
|
+
this.objects.push({ sha: this.pending.sha, type: this.pending.type, body });
|
|
905
|
+
this.buf = this.buf.subarray(needed);
|
|
906
|
+
this.pending = null;
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
const nl = this.buf.indexOf(10);
|
|
910
|
+
if (nl === -1) return;
|
|
911
|
+
const header = this.buf.subarray(0, nl).toString("utf-8");
|
|
912
|
+
this.buf = this.buf.subarray(nl + 1);
|
|
913
|
+
const [name, second, third] = header.split(" ");
|
|
914
|
+
if (name && second === "missing" && third === void 0) {
|
|
915
|
+
this.missing.push(name);
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
if (name && second && third !== void 0 && OBJECT_TYPES.has(second)) {
|
|
919
|
+
const size = Number.parseInt(third, 10);
|
|
920
|
+
if (Number.isSafeInteger(size) && size >= 0) {
|
|
921
|
+
this.pending = { sha: name, type: second, size };
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
throw new GitError(
|
|
926
|
+
`unexpected cat-file --batch header: ${JSON.stringify(header)}`,
|
|
927
|
+
void 0,
|
|
928
|
+
""
|
|
929
|
+
);
|
|
965
930
|
}
|
|
966
931
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
return { ...stat2, ...path ? { path } : {}, isRefTip: tipShas.has(stat2.sha) };
|
|
972
|
-
});
|
|
973
|
-
const objects = [
|
|
974
|
-
...planned.filter((o) => !o.isRefTip),
|
|
975
|
-
...planned.filter((o) => o.isRefTip)
|
|
976
|
-
];
|
|
977
|
-
const newRefsMap = new Map(remoteState.refs);
|
|
978
|
-
for (const update of updates) newRefsMap.set(update.refname, update.localSha);
|
|
979
|
-
const headSymref = head && newRefsMap.has(head) ? head : remoteState.headSymref && newRefsMap.has(remoteState.headSymref) ? remoteState.headSymref : [...newRefsMap.keys()][0] ?? null;
|
|
980
|
-
const newRefs = {};
|
|
981
|
-
const headSha = headSymref ? newRefsMap.get(headSymref) : void 0;
|
|
982
|
-
if (headSymref && headSha) newRefs[headSymref] = headSha;
|
|
983
|
-
for (const [refname, sha] of newRefsMap) {
|
|
984
|
-
if (refname !== headSymref) newRefs[refname] = sha;
|
|
932
|
+
};
|
|
933
|
+
var GitRepoReader = class {
|
|
934
|
+
constructor(repoPath) {
|
|
935
|
+
this.repoPath = repoPath;
|
|
985
936
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
newRefs,
|
|
995
|
-
headSymref,
|
|
996
|
-
objects,
|
|
997
|
-
knownShaToTxId,
|
|
998
|
-
announceNeeded,
|
|
999
|
-
announcement: {
|
|
1000
|
-
name: options.announcement?.name ?? repoId,
|
|
1001
|
-
description: options.announcement?.description ?? ""
|
|
1002
|
-
},
|
|
1003
|
-
estimate: {
|
|
1004
|
-
objectCount: objects.length,
|
|
1005
|
-
totalObjectBytes,
|
|
1006
|
-
uploadFee,
|
|
1007
|
-
eventCount,
|
|
1008
|
-
eventFees,
|
|
1009
|
-
totalFee: uploadFee + eventFees
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
|
-
}
|
|
1013
|
-
var READ_BATCH_SIZE = 100;
|
|
1014
|
-
async function executePush(options) {
|
|
1015
|
-
const { plan, publisher, remoteState, repoReader, relayUrls } = options;
|
|
1016
|
-
const merged = new Map([...remoteState.shaToTxId, ...plan.knownShaToTxId]);
|
|
1017
|
-
const resultBySha = /* @__PURE__ */ new Map();
|
|
1018
|
-
let totalFeePaid = 0n;
|
|
1019
|
-
const pending = [];
|
|
1020
|
-
for (const object of plan.objects) {
|
|
1021
|
-
const knownTxId = merged.get(object.sha);
|
|
1022
|
-
if (knownTxId !== void 0) {
|
|
1023
|
-
resultBySha.set(object.sha, {
|
|
1024
|
-
sha: object.sha,
|
|
1025
|
-
txId: knownTxId,
|
|
1026
|
-
feePaid: 0n,
|
|
1027
|
-
skipped: true
|
|
937
|
+
repoPath;
|
|
938
|
+
/** Run git with argument-array safety; resolves stdout as UTF-8. */
|
|
939
|
+
async git(args, opts = {}) {
|
|
940
|
+
try {
|
|
941
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
942
|
+
cwd: this.repoPath,
|
|
943
|
+
maxBuffer: MAX_BUFFER,
|
|
944
|
+
encoding: "utf-8"
|
|
1028
945
|
});
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
`objects vanished from the local repository during push: ${missing.join(", ")}`
|
|
946
|
+
return { stdout, exitCode: 0 };
|
|
947
|
+
} catch (err) {
|
|
948
|
+
const e = err;
|
|
949
|
+
const exitCode = typeof e.code === "number" ? e.code : void 0;
|
|
950
|
+
if (exitCode !== void 0 && opts.allowExitCodes?.includes(exitCode)) {
|
|
951
|
+
return { stdout: e.stdout ?? "", exitCode };
|
|
952
|
+
}
|
|
953
|
+
throw new GitError(
|
|
954
|
+
`git ${args[0]} failed${exitCode !== void 0 ? ` (exit ${exitCode})` : ""}: ${(e.stderr ?? e.message ?? "").trim()}`,
|
|
955
|
+
exitCode,
|
|
956
|
+
(e.stderr ?? "").trim()
|
|
1041
957
|
);
|
|
1042
958
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* List all branches and tags plus the symbolic HEAD.
|
|
962
|
+
*
|
|
963
|
+
* Annotated tags report the tag object's SHA/type with the peeled target
|
|
964
|
+
* in `peeledSha`. A detached HEAD is tolerated (`head` is `undefined`).
|
|
965
|
+
*/
|
|
966
|
+
async listRefs() {
|
|
967
|
+
const format = "%(refname)%00%(objectname)%00%(objecttype)%00%(*objectname)";
|
|
968
|
+
const [refsRes, headRes] = await Promise.all([
|
|
969
|
+
this.git(["for-each-ref", `--format=${format}`, "refs/heads", "refs/tags"]),
|
|
970
|
+
// Exit 1 = detached HEAD (or unborn branch pointer oddities) — tolerated.
|
|
971
|
+
this.git(["symbolic-ref", "--quiet", "HEAD"], { allowExitCodes: [1] })
|
|
972
|
+
]);
|
|
973
|
+
const refs = [];
|
|
974
|
+
for (const line of refsRes.stdout.split("\n")) {
|
|
975
|
+
if (!line) continue;
|
|
976
|
+
const [refname, sha, objecttype, peeled] = line.split("\0");
|
|
977
|
+
if (!refname || !sha || !objecttype || !OBJECT_TYPES.has(objecttype)) {
|
|
978
|
+
throw new GitError(`unexpected for-each-ref line: ${JSON.stringify(line)}`, void 0, "");
|
|
1050
979
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
});
|
|
1057
|
-
merged.set(object.sha, receipt.txId);
|
|
1058
|
-
totalFeePaid += receipt.feePaid;
|
|
1059
|
-
resultBySha.set(object.sha, {
|
|
1060
|
-
sha: object.sha,
|
|
1061
|
-
txId: receipt.txId,
|
|
1062
|
-
feePaid: receipt.feePaid,
|
|
1063
|
-
skipped: false
|
|
980
|
+
refs.push({
|
|
981
|
+
refname,
|
|
982
|
+
sha,
|
|
983
|
+
type: objecttype,
|
|
984
|
+
...peeled ? { peeledSha: peeled } : {}
|
|
1064
985
|
});
|
|
1065
986
|
}
|
|
987
|
+
const head = headRes.exitCode === 0 ? headRes.stdout.trim() || void 0 : void 0;
|
|
988
|
+
return { head, refs };
|
|
1066
989
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
990
|
+
/**
|
|
991
|
+
* SHAs of every object reachable from `want` but not from `have`
|
|
992
|
+
* (`git rev-list --objects <want…> --not <have…>`), i.e. the push delta.
|
|
993
|
+
*
|
|
994
|
+
* Haves that don't exist locally (e.g. remote tips we never fetched) are
|
|
995
|
+
* filtered out first via one `cat-file --batch-check` pass — rev-list
|
|
996
|
+
* would otherwise die on them.
|
|
997
|
+
*/
|
|
998
|
+
async objectsBetween(want, have) {
|
|
999
|
+
const objects = await this.objectsBetweenWithPaths(want, have);
|
|
1000
|
+
return objects.map((o) => o.sha);
|
|
1076
1001
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
var ARWEAVE_GRAPHQL_URL = "https://arweave.net/graphql";
|
|
1104
|
-
var SHA_CACHE_MAX_SIZE = 1e4;
|
|
1105
|
-
var shaToTxIdCache = /* @__PURE__ */ new Map();
|
|
1106
|
-
function isValidGitSha(sha) {
|
|
1107
|
-
return /^[0-9a-f]{40}$/i.test(sha);
|
|
1108
|
-
}
|
|
1109
|
-
function sanitizeGraphQLValue(value) {
|
|
1110
|
-
return value.replace(/["\\\n\r\u0000-\u001f`]/g, "");
|
|
1111
|
-
}
|
|
1112
|
-
function shaCacheKey(sha, repo) {
|
|
1113
|
-
return `${sha}:${repo}`;
|
|
1114
|
-
}
|
|
1115
|
-
function seedShaCache(mappings) {
|
|
1116
|
-
const entries = mappings instanceof Map ? mappings.entries() : mappings;
|
|
1117
|
-
for (const [key2, txId] of entries) {
|
|
1118
|
-
if (shaToTxIdCache.size >= SHA_CACHE_MAX_SIZE) {
|
|
1119
|
-
const firstKey = shaToTxIdCache.keys().next().value;
|
|
1120
|
-
if (firstKey !== void 0) {
|
|
1121
|
-
shaToTxIdCache.delete(firstKey);
|
|
1002
|
+
/**
|
|
1003
|
+
* Like {@link objectsBetween} but keeps the path each object was reached
|
|
1004
|
+
* by (`rev-list --objects` emits `<sha> <path>` for blobs and non-root
|
|
1005
|
+
* trees) — used by push planning to report actionable oversize errors.
|
|
1006
|
+
*/
|
|
1007
|
+
async objectsBetweenWithPaths(want, have) {
|
|
1008
|
+
for (const w of want) assertRevision(w, "want");
|
|
1009
|
+
for (const h of have) assertRevision(h, "have");
|
|
1010
|
+
if (want.length === 0) return [];
|
|
1011
|
+
const knownHaves = await this.filterExisting(have);
|
|
1012
|
+
const args = ["rev-list", "--objects", ...want];
|
|
1013
|
+
if (knownHaves.length > 0) args.push("--not", ...knownHaves);
|
|
1014
|
+
args.push("--");
|
|
1015
|
+
const { stdout } = await this.git(args);
|
|
1016
|
+
const objects = [];
|
|
1017
|
+
for (const line of stdout.split("\n")) {
|
|
1018
|
+
if (!line) continue;
|
|
1019
|
+
const spaceIdx = line.indexOf(" ");
|
|
1020
|
+
if (spaceIdx === -1) {
|
|
1021
|
+
objects.push({ sha: line });
|
|
1022
|
+
} else {
|
|
1023
|
+
const path = line.slice(spaceIdx + 1);
|
|
1024
|
+
objects.push({
|
|
1025
|
+
sha: line.slice(0, spaceIdx),
|
|
1026
|
+
...path ? { path } : {}
|
|
1027
|
+
});
|
|
1122
1028
|
}
|
|
1123
1029
|
}
|
|
1124
|
-
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
var ARWEAVE_TX_ID_RE = /^[a-zA-Z0-9_-]{43}$/;
|
|
1128
|
-
function isValidArweaveTxId(txId) {
|
|
1129
|
-
return ARWEAVE_TX_ID_RE.test(txId);
|
|
1130
|
-
}
|
|
1131
|
-
async function resolveGitSha(sha, repo) {
|
|
1132
|
-
if (!isValidGitSha(sha)) {
|
|
1133
|
-
return null;
|
|
1134
|
-
}
|
|
1135
|
-
const cacheKey = shaCacheKey(sha, repo);
|
|
1136
|
-
const cached = shaToTxIdCache.get(cacheKey);
|
|
1137
|
-
if (cached !== void 0) {
|
|
1138
|
-
return cached;
|
|
1139
|
-
}
|
|
1140
|
-
const safeSha = sanitizeGraphQLValue(sha);
|
|
1141
|
-
const safeRepo = sanitizeGraphQLValue(repo);
|
|
1142
|
-
const query = `query {
|
|
1143
|
-
transactions(tags: [
|
|
1144
|
-
{ name: "Git-SHA", values: ["${safeSha}"] },
|
|
1145
|
-
{ name: "Repo", values: ["${safeRepo}"] }
|
|
1146
|
-
]) {
|
|
1147
|
-
edges { node { id } }
|
|
1030
|
+
return objects;
|
|
1148
1031
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1032
|
+
/** Run git feeding `input` on stdin; resolves collected stdout bytes. */
|
|
1033
|
+
runWithStdin(args, input) {
|
|
1034
|
+
return new Promise((resolve2, reject) => {
|
|
1035
|
+
const child = spawn("git", args, {
|
|
1036
|
+
cwd: this.repoPath,
|
|
1037
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1038
|
+
});
|
|
1039
|
+
const out = [];
|
|
1040
|
+
let stderr = "";
|
|
1041
|
+
child.stdout.on("data", (chunk) => out.push(chunk));
|
|
1042
|
+
child.stderr.on("data", (chunk) => {
|
|
1043
|
+
stderr += chunk.toString("utf-8");
|
|
1044
|
+
});
|
|
1045
|
+
child.on("error", (err) => {
|
|
1046
|
+
reject(new GitError(`failed to spawn git ${args[0]}: ${err.message}`, void 0, ""));
|
|
1047
|
+
});
|
|
1048
|
+
child.on("close", (code) => {
|
|
1049
|
+
if (code !== 0) {
|
|
1050
|
+
return reject(
|
|
1051
|
+
new GitError(`git ${args[0]} failed (exit ${code}): ${stderr.trim()}`, code ?? void 0, stderr.trim())
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
resolve2(Buffer.concat(out));
|
|
1055
|
+
});
|
|
1056
|
+
child.stdin.on("error", () => {
|
|
1057
|
+
});
|
|
1058
|
+
child.stdin.write(input);
|
|
1059
|
+
child.stdin.end();
|
|
1156
1060
|
});
|
|
1157
|
-
if (!response.ok) {
|
|
1158
|
-
return null;
|
|
1159
|
-
}
|
|
1160
|
-
const json = await response.json();
|
|
1161
|
-
const edges = json.data?.transactions?.edges;
|
|
1162
|
-
if (!edges || edges.length === 0) {
|
|
1163
|
-
return null;
|
|
1164
|
-
}
|
|
1165
|
-
const txId = edges[0]?.node?.id;
|
|
1166
|
-
if (!txId || !isValidArweaveTxId(txId)) {
|
|
1167
|
-
return null;
|
|
1168
|
-
}
|
|
1169
|
-
if (shaToTxIdCache.size >= SHA_CACHE_MAX_SIZE) {
|
|
1170
|
-
const firstKey = shaToTxIdCache.keys().next().value;
|
|
1171
|
-
if (firstKey !== void 0) {
|
|
1172
|
-
shaToTxIdCache.delete(firstKey);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
shaToTxIdCache.set(cacheKey, txId);
|
|
1176
|
-
return txId;
|
|
1177
|
-
} catch {
|
|
1178
|
-
return null;
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
function isValidRelayUrl(url) {
|
|
1182
|
-
return /^wss?:\/\//i.test(url);
|
|
1183
|
-
}
|
|
1184
|
-
var WS_OPEN = 1;
|
|
1185
|
-
function defaultWebSocketFactory2(url) {
|
|
1186
|
-
const ctor = globalThis.WebSocket;
|
|
1187
|
-
if (!ctor) {
|
|
1188
|
-
throw new Error(
|
|
1189
|
-
"No global WebSocket constructor (Node >= 22 required) \u2014 pass webSocketFactory"
|
|
1190
|
-
);
|
|
1191
1061
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
if (typeof payload !== "string") {
|
|
1199
|
-
return null;
|
|
1062
|
+
/** Of the given revisions, keep only those resolvable locally. */
|
|
1063
|
+
async filterExisting(revs) {
|
|
1064
|
+
if (revs.length === 0) return [];
|
|
1065
|
+
const { missing } = await this.batchCheck(revs);
|
|
1066
|
+
const missingSet = new Set(missing);
|
|
1067
|
+
return revs.filter((r) => !missingSet.has(r));
|
|
1200
1068
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1069
|
+
/** One `cat-file --batch-check` pass; returns names reported missing. */
|
|
1070
|
+
async batchCheck(names) {
|
|
1071
|
+
const stdout = await this.runWithStdin(
|
|
1072
|
+
["cat-file", "--batch-check"],
|
|
1073
|
+
names.join("\n") + "\n"
|
|
1074
|
+
);
|
|
1075
|
+
const missing = [];
|
|
1076
|
+
for (const line of stdout.toString("utf-8").split("\n")) {
|
|
1077
|
+
if (line.endsWith(" missing")) missing.push(line.slice(0, -" missing".length));
|
|
1205
1078
|
}
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1208
|
-
try {
|
|
1209
|
-
return decodeToon(payload);
|
|
1210
|
-
} catch {
|
|
1211
|
-
return null;
|
|
1079
|
+
return { missing };
|
|
1212
1080
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1081
|
+
/**
|
|
1082
|
+
* Object metadata (type + body size) for a batch of SHAs via one
|
|
1083
|
+
* `cat-file --batch-check` pass — no bodies are read. Missing objects are
|
|
1084
|
+
* reported, not thrown.
|
|
1085
|
+
*/
|
|
1086
|
+
async statObjects(shas) {
|
|
1087
|
+
for (const sha of shas) assertFullSha(sha, "sha");
|
|
1088
|
+
if (shas.length === 0) return { objects: [], missing: [] };
|
|
1089
|
+
const stdout = await this.runWithStdin(
|
|
1090
|
+
["cat-file", "--batch-check"],
|
|
1091
|
+
shas.join("\n") + "\n"
|
|
1092
|
+
);
|
|
1093
|
+
const objects = [];
|
|
1094
|
+
const missing = [];
|
|
1095
|
+
for (const line of stdout.toString("utf-8").split("\n")) {
|
|
1096
|
+
if (!line) continue;
|
|
1097
|
+
if (line.endsWith(" missing")) {
|
|
1098
|
+
missing.push(line.slice(0, -" missing".length));
|
|
1099
|
+
continue;
|
|
1231
1100
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1101
|
+
const [sha, type, sizeStr] = line.split(" ");
|
|
1102
|
+
const size = Number.parseInt(sizeStr ?? "", 10);
|
|
1103
|
+
if (!sha || !type || !OBJECT_TYPES.has(type) || !Number.isSafeInteger(size) || size < 0) {
|
|
1104
|
+
throw new GitError(
|
|
1105
|
+
`unexpected cat-file --batch-check line: ${JSON.stringify(line)}`,
|
|
1106
|
+
void 0,
|
|
1107
|
+
""
|
|
1108
|
+
);
|
|
1236
1109
|
}
|
|
1237
|
-
|
|
1238
|
-
if (!isValidRelayUrl(relayUrl)) {
|
|
1239
|
-
reject(
|
|
1240
|
-
new Error(
|
|
1241
|
-
`Invalid relay URL protocol (must be ws:// or wss://): ${relayUrl}`
|
|
1242
|
-
)
|
|
1243
|
-
);
|
|
1244
|
-
return;
|
|
1245
|
-
}
|
|
1246
|
-
try {
|
|
1247
|
-
ws = webSocketFactory(relayUrl);
|
|
1248
|
-
} catch (err) {
|
|
1249
|
-
reject(new Error(`Failed to connect to relay ${relayUrl}: ${String(err)}`));
|
|
1250
|
-
return;
|
|
1110
|
+
objects.push({ sha, type, size });
|
|
1251
1111
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1112
|
+
return { objects, missing };
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Read raw object bodies via a single streaming `git cat-file --batch`
|
|
1116
|
+
* child process. Bodies may be binary and are parsed size-driven across
|
|
1117
|
+
* chunk boundaries. Missing objects are reported, not thrown.
|
|
1118
|
+
*/
|
|
1119
|
+
async readObjects(shas) {
|
|
1120
|
+
for (const sha of shas) assertFullSha(sha, "sha");
|
|
1121
|
+
if (shas.length === 0) return { objects: [], missing: [] };
|
|
1122
|
+
const parser = new BatchParser();
|
|
1123
|
+
await new Promise((resolve2, reject) => {
|
|
1124
|
+
const child = spawn("git", ["cat-file", "--batch"], {
|
|
1125
|
+
cwd: this.repoPath,
|
|
1126
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1127
|
+
});
|
|
1128
|
+
let stderr = "";
|
|
1129
|
+
let parseError = null;
|
|
1130
|
+
child.stderr.on("data", (chunk) => {
|
|
1131
|
+
stderr += chunk.toString("utf-8");
|
|
1132
|
+
});
|
|
1133
|
+
child.stdout.on("data", (chunk) => {
|
|
1134
|
+
if (parseError) return;
|
|
1135
|
+
try {
|
|
1136
|
+
parser.push(chunk);
|
|
1137
|
+
} catch (err) {
|
|
1138
|
+
parseError = err;
|
|
1139
|
+
child.kill();
|
|
1268
1140
|
}
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1141
|
+
});
|
|
1142
|
+
child.on("error", (err) => {
|
|
1143
|
+
reject(new GitError(`failed to spawn git cat-file: ${err.message}`, void 0, ""));
|
|
1144
|
+
});
|
|
1145
|
+
child.on("close", (code) => {
|
|
1146
|
+
if (parseError) return reject(parseError);
|
|
1147
|
+
if (code !== 0) {
|
|
1148
|
+
return reject(
|
|
1149
|
+
new GitError(`git cat-file --batch failed (exit ${code}): ${stderr.trim()}`, code ?? void 0, stderr.trim())
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
if (!parser.isComplete()) {
|
|
1153
|
+
return reject(
|
|
1154
|
+
new GitError("git cat-file --batch output ended mid-record", code ?? void 0, stderr.trim())
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
resolve2();
|
|
1158
|
+
});
|
|
1159
|
+
child.stdin.on("error", () => {
|
|
1160
|
+
});
|
|
1161
|
+
child.stdin.write(shas.join("\n") + "\n");
|
|
1162
|
+
child.stdin.end();
|
|
1281
1163
|
});
|
|
1282
|
-
|
|
1283
|
-
}
|
|
1284
|
-
function latestReplaceable(events) {
|
|
1285
|
-
let winner = null;
|
|
1286
|
-
for (const event of events) {
|
|
1287
|
-
if (winner === null || event.created_at > winner.created_at || event.created_at === winner.created_at && event.id < winner.id) {
|
|
1288
|
-
winner = event;
|
|
1289
|
-
}
|
|
1164
|
+
return { objects: parser.objects, missing: parser.missing };
|
|
1290
1165
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1166
|
+
/**
|
|
1167
|
+
* `git merge-base --is-ancestor <a> <b>` — true when `a` is an ancestor
|
|
1168
|
+
* of `b` (fast-forward check / force detection). Exit codes other than
|
|
1169
|
+
* 0/1 (e.g. unknown revisions) throw.
|
|
1170
|
+
*/
|
|
1171
|
+
async isAncestor(a, b) {
|
|
1172
|
+
assertRevision(a, "ancestor candidate");
|
|
1173
|
+
assertRevision(b, "descendant candidate");
|
|
1174
|
+
const { exitCode } = await this.git(
|
|
1175
|
+
["merge-base", "--is-ancestor", a, b],
|
|
1176
|
+
{ allowExitCodes: [1] }
|
|
1177
|
+
);
|
|
1178
|
+
return exitCode === 0;
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* `git format-patch --stdout <range>` — the full mbox-formatted patch
|
|
1182
|
+
* series text (empty string when the range selects no commits).
|
|
1183
|
+
*/
|
|
1184
|
+
async formatPatch(range) {
|
|
1185
|
+
assertRange(range, "range");
|
|
1186
|
+
const { stdout } = await this.git(["format-patch", "--stdout", range, "--"]);
|
|
1187
|
+
return stdout;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Parent SHAs for a batch of commit SHAs via one
|
|
1191
|
+
* `git rev-list --no-walk=unsorted --parents` pass. Root commits map to an
|
|
1192
|
+
* empty array. Used to derive the kind:1617 `commit`/`parent-commit` tag
|
|
1193
|
+
* pairs for exactly the commits a format-patch series carries.
|
|
1194
|
+
*/
|
|
1195
|
+
async commitParents(shas) {
|
|
1196
|
+
for (const sha of shas) assertFullSha(sha, "sha");
|
|
1197
|
+
if (shas.length === 0) return /* @__PURE__ */ new Map();
|
|
1198
|
+
const { stdout } = await this.git([
|
|
1199
|
+
"rev-list",
|
|
1200
|
+
"--no-walk=unsorted",
|
|
1201
|
+
"--parents",
|
|
1202
|
+
...shas,
|
|
1203
|
+
"--"
|
|
1204
|
+
]);
|
|
1205
|
+
const parents = /* @__PURE__ */ new Map();
|
|
1206
|
+
for (const line of stdout.split("\n")) {
|
|
1207
|
+
if (!line) continue;
|
|
1208
|
+
const [sha, ...rest] = line.split(" ");
|
|
1209
|
+
if (!sha || !FULL_SHA_RE.test(sha) || rest.some((p) => !FULL_SHA_RE.test(p))) {
|
|
1210
|
+
throw new GitError(
|
|
1211
|
+
`unexpected rev-list --parents line: ${JSON.stringify(line)}`,
|
|
1212
|
+
void 0,
|
|
1213
|
+
""
|
|
1214
|
+
);
|
|
1309
1215
|
}
|
|
1310
|
-
|
|
1311
|
-
refs.set(v1, v2);
|
|
1312
|
-
} else if (tagName === "HEAD" && v1?.startsWith(SYMREF_PREFIX)) {
|
|
1313
|
-
headSymref = v1.slice(SYMREF_PREFIX.length);
|
|
1314
|
-
} else if (tagName === "arweave" && v1 && v2) {
|
|
1315
|
-
shaToTxId.set(v1, v2);
|
|
1216
|
+
parents.set(sha, rest);
|
|
1316
1217
|
}
|
|
1218
|
+
return parents;
|
|
1317
1219
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1220
|
+
/**
|
|
1221
|
+
* Resolve a ref/revision to a full SHA via `git rev-parse --verify`.
|
|
1222
|
+
* Throws {@link GitError} when the name doesn't resolve.
|
|
1223
|
+
*/
|
|
1224
|
+
async resolveRef(name) {
|
|
1225
|
+
assertRevision(name, "ref name");
|
|
1226
|
+
const { stdout } = await this.git(["rev-parse", "--verify", "--quiet", name]);
|
|
1227
|
+
const sha = stdout.trim();
|
|
1228
|
+
if (!FULL_SHA_RE.test(sha)) {
|
|
1229
|
+
throw new GitError(
|
|
1230
|
+
`rev-parse --verify returned unexpected output for ${JSON.stringify(name)}: ${JSON.stringify(sha)}`,
|
|
1231
|
+
void 0,
|
|
1232
|
+
""
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
return sha;
|
|
1331
1236
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1237
|
+
};
|
|
1238
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
1239
|
+
var NonFastForwardError = class extends Error {
|
|
1240
|
+
constructor(refs) {
|
|
1241
|
+
super(
|
|
1242
|
+
`non-fast-forward update rejected for ${refs.map((r) => r.refname).join(", ")} \u2014 re-run with force to overwrite the remote ref(s)`
|
|
1243
|
+
);
|
|
1244
|
+
this.refs = refs;
|
|
1245
|
+
this.name = "NonFastForwardError";
|
|
1334
1246
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1247
|
+
refs;
|
|
1248
|
+
};
|
|
1249
|
+
var OversizeObjectsError = class extends Error {
|
|
1250
|
+
constructor(objects) {
|
|
1251
|
+
super(
|
|
1252
|
+
`${objects.length} object(s) exceed the ${MAX_OBJECT_SIZE} byte upload limit: ` + objects.map((o) => `${o.path ?? o.sha} (${o.size} bytes)`).join(", ")
|
|
1253
|
+
);
|
|
1254
|
+
this.objects = objects;
|
|
1255
|
+
this.name = "OversizeObjectsError";
|
|
1256
|
+
}
|
|
1257
|
+
objects;
|
|
1258
|
+
};
|
|
1259
|
+
async function planPush(options) {
|
|
1260
|
+
const { repoReader, remoteState, feeRates, repoId, force = false } = options;
|
|
1261
|
+
const resolveMissing = options.resolveMissing ?? remoteState.resolveMissing.bind(remoteState);
|
|
1262
|
+
const { head, refs: localRefs } = await repoReader.listRefs();
|
|
1263
|
+
const localByName = new Map(localRefs.map((r) => [r.refname, r]));
|
|
1264
|
+
let selected;
|
|
1265
|
+
if (options.refs !== void 0) {
|
|
1266
|
+
selected = options.refs.map((name) => {
|
|
1267
|
+
const ref = localByName.get(name);
|
|
1268
|
+
if (!ref) {
|
|
1269
|
+
throw new Error(
|
|
1270
|
+
`ref ${JSON.stringify(name)} does not exist locally (ref deletion is out of scope in v1)`
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
return ref;
|
|
1274
|
+
});
|
|
1275
|
+
} else {
|
|
1276
|
+
selected = localRefs;
|
|
1337
1277
|
}
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
relayUrls.map((url) => queryRelay(url, filter, timeoutMs, webSocketFactory))
|
|
1345
|
-
);
|
|
1346
|
-
const failures = [];
|
|
1347
|
-
const byId = /* @__PURE__ */ new Map();
|
|
1348
|
-
for (const result of results) {
|
|
1349
|
-
if (result.status === "rejected") {
|
|
1350
|
-
failures.push(String(result.reason?.message ?? result.reason));
|
|
1278
|
+
const refUpdates = [];
|
|
1279
|
+
const rejected = [];
|
|
1280
|
+
for (const ref of selected) {
|
|
1281
|
+
const remoteSha = remoteState.refs.get(ref.refname) ?? null;
|
|
1282
|
+
if (remoteSha === null) {
|
|
1283
|
+
refUpdates.push({ refname: ref.refname, localSha: ref.sha, remoteSha, kind: "new" });
|
|
1351
1284
|
continue;
|
|
1352
1285
|
}
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1286
|
+
if (remoteSha === ref.sha) {
|
|
1287
|
+
refUpdates.push({ refname: ref.refname, localSha: ref.sha, remoteSha, kind: "up-to-date" });
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
let fastForward = false;
|
|
1291
|
+
try {
|
|
1292
|
+
fastForward = await repoReader.isAncestor(remoteSha, ref.sha);
|
|
1293
|
+
} catch (err) {
|
|
1294
|
+
if (!(err instanceof GitError)) throw err;
|
|
1295
|
+
fastForward = false;
|
|
1296
|
+
}
|
|
1297
|
+
if (fastForward) {
|
|
1298
|
+
refUpdates.push({ refname: ref.refname, localSha: ref.sha, remoteSha, kind: "fast-forward" });
|
|
1299
|
+
} else if (force) {
|
|
1300
|
+
refUpdates.push({ refname: ref.refname, localSha: ref.sha, remoteSha, kind: "forced" });
|
|
1301
|
+
} else {
|
|
1302
|
+
rejected.push({ refname: ref.refname, localSha: ref.sha, remoteSha });
|
|
1359
1303
|
}
|
|
1360
1304
|
}
|
|
1361
|
-
if (
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1305
|
+
if (rejected.length > 0) throw new NonFastForwardError(rejected);
|
|
1306
|
+
const updates = refUpdates.filter((u) => u.kind !== "up-to-date");
|
|
1307
|
+
const wantTips = [...new Set(updates.map((u) => u.localSha))];
|
|
1308
|
+
const haveTips = [...new Set(remoteState.refs.values())];
|
|
1309
|
+
const delta = wantTips.length > 0 ? await repoReader.objectsBetweenWithPaths(wantTips, haveTips) : [];
|
|
1310
|
+
const knownShaToTxId = new Map(remoteState.shaToTxId);
|
|
1311
|
+
let candidates = delta.filter((o) => !knownShaToTxId.has(o.sha));
|
|
1312
|
+
if (candidates.length > 0) {
|
|
1313
|
+
const resolved = await resolveMissing(candidates.map((o) => o.sha));
|
|
1314
|
+
for (const [sha, txId] of resolved) knownShaToTxId.set(sha, txId);
|
|
1315
|
+
candidates = candidates.filter((o) => !knownShaToTxId.has(o.sha));
|
|
1365
1316
|
}
|
|
1366
|
-
const
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
);
|
|
1370
|
-
const announceEvent = latestReplaceable(
|
|
1371
|
-
events.filter((e) => e.kind === REPOSITORY_ANNOUNCEMENT_KIND2)
|
|
1317
|
+
const pathBySha = new Map(candidates.map((c) => [c.sha, c.path]));
|
|
1318
|
+
const { objects: stats, missing } = await repoReader.statObjects(
|
|
1319
|
+
candidates.map((c) => c.sha)
|
|
1372
1320
|
);
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
shaToTxId: /* @__PURE__ */ new Map()
|
|
1377
|
-
};
|
|
1378
|
-
if (shaToTxId.size > 0) {
|
|
1379
|
-
seedShaCache(
|
|
1380
|
-
[...shaToTxId].map(
|
|
1381
|
-
([sha, txId]) => [shaCacheKey(sha, repoId), txId]
|
|
1382
|
-
)
|
|
1321
|
+
if (missing.length > 0) {
|
|
1322
|
+
throw new Error(
|
|
1323
|
+
`objects vanished from the local repository during planning: ${missing.join(", ")}`
|
|
1383
1324
|
);
|
|
1384
1325
|
}
|
|
1385
|
-
const
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
if (tag[0] === "relays") {
|
|
1391
|
-
relays.push(...tag.slice(1).filter((url) => url.length > 0));
|
|
1392
|
-
}
|
|
1326
|
+
const oversize = [];
|
|
1327
|
+
for (const stat2 of stats) {
|
|
1328
|
+
if (stat2.size > MAX_OBJECT_SIZE) {
|
|
1329
|
+
const path = pathBySha.get(stat2.sha);
|
|
1330
|
+
oversize.push({ ...stat2, ...path ? { path } : {} });
|
|
1393
1331
|
}
|
|
1394
1332
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1333
|
+
if (oversize.length > 0) throw new OversizeObjectsError(oversize);
|
|
1334
|
+
const tipShas = new Set(updates.map((u) => u.localSha));
|
|
1335
|
+
const planned = stats.map((stat2) => {
|
|
1336
|
+
const path = pathBySha.get(stat2.sha);
|
|
1337
|
+
return { ...stat2, ...path ? { path } : {}, isRefTip: tipShas.has(stat2.sha) };
|
|
1338
|
+
});
|
|
1339
|
+
const objects = [
|
|
1340
|
+
...planned.filter((o) => !o.isRefTip),
|
|
1341
|
+
...planned.filter((o) => o.isRefTip)
|
|
1342
|
+
];
|
|
1343
|
+
const newRefsMap = new Map(remoteState.refs);
|
|
1344
|
+
for (const update of updates) newRefsMap.set(update.refname, update.localSha);
|
|
1345
|
+
const headSymref = head && newRefsMap.has(head) ? head : remoteState.headSymref && newRefsMap.has(remoteState.headSymref) ? remoteState.headSymref : [...newRefsMap.keys()][0] ?? null;
|
|
1346
|
+
const newRefs = {};
|
|
1347
|
+
const headSha = headSymref ? newRefsMap.get(headSymref) : void 0;
|
|
1348
|
+
if (headSymref && headSha) newRefs[headSymref] = headSha;
|
|
1349
|
+
for (const [refname, sha] of newRefsMap) {
|
|
1350
|
+
if (refname !== headSymref) newRefs[refname] = sha;
|
|
1351
|
+
}
|
|
1352
|
+
const announceNeeded = !remoteState.announced;
|
|
1353
|
+
const totalObjectBytes = objects.reduce((sum, o) => sum + o.size, 0);
|
|
1354
|
+
const uploadFee = BigInt(totalObjectBytes) * feeRates.uploadFeePerByte;
|
|
1355
|
+
const eventCount = 1 + (announceNeeded ? 1 : 0);
|
|
1356
|
+
const eventFees = BigInt(eventCount) * feeRates.eventFee;
|
|
1357
|
+
return {
|
|
1358
|
+
repoId,
|
|
1359
|
+
refUpdates,
|
|
1360
|
+
newRefs,
|
|
1361
|
+
headSymref,
|
|
1362
|
+
objects,
|
|
1363
|
+
knownShaToTxId,
|
|
1364
|
+
announceNeeded,
|
|
1365
|
+
announcement: {
|
|
1366
|
+
name: options.announcement?.name ?? repoId,
|
|
1367
|
+
description: options.announcement?.description ?? ""
|
|
1368
|
+
},
|
|
1369
|
+
estimate: {
|
|
1370
|
+
objectCount: objects.length,
|
|
1371
|
+
totalObjectBytes,
|
|
1372
|
+
uploadFee,
|
|
1373
|
+
eventCount,
|
|
1374
|
+
eventFees,
|
|
1375
|
+
totalFee: uploadFee + eventFees
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
var READ_BATCH_SIZE = 100;
|
|
1380
|
+
async function executePush(options) {
|
|
1381
|
+
const { plan, publisher, remoteState, repoReader, relayUrls } = options;
|
|
1382
|
+
const merged = new Map([...remoteState.shaToTxId, ...plan.knownShaToTxId]);
|
|
1383
|
+
const resultBySha = /* @__PURE__ */ new Map();
|
|
1384
|
+
let totalFeePaid = 0n;
|
|
1385
|
+
const pending = [];
|
|
1386
|
+
for (const object of plan.objects) {
|
|
1387
|
+
const knownTxId = merged.get(object.sha);
|
|
1388
|
+
if (knownTxId !== void 0) {
|
|
1389
|
+
resultBySha.set(object.sha, {
|
|
1390
|
+
sha: object.sha,
|
|
1391
|
+
txId: knownTxId,
|
|
1392
|
+
feePaid: 0n,
|
|
1393
|
+
skipped: true
|
|
1394
|
+
});
|
|
1395
|
+
} else {
|
|
1396
|
+
pending.push(object);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
for (let i = 0; i < pending.length; i += READ_BATCH_SIZE) {
|
|
1400
|
+
const batch = pending.slice(i, i + READ_BATCH_SIZE);
|
|
1401
|
+
const { objects: read, missing } = await repoReader.readObjects(
|
|
1402
|
+
batch.map((o) => o.sha)
|
|
1403
|
+
);
|
|
1404
|
+
if (missing.length > 0) {
|
|
1405
|
+
throw new Error(
|
|
1406
|
+
`objects vanished from the local repository during push: ${missing.join(", ")}`
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
const bodyBySha = new Map(read.map((r) => [r.sha, r.body]));
|
|
1410
|
+
for (const object of batch) {
|
|
1411
|
+
const body = bodyBySha.get(object.sha);
|
|
1412
|
+
if (!body) {
|
|
1413
|
+
throw new Error(
|
|
1414
|
+
`internal: cat-file returned no body for ${object.sha}`
|
|
1415
|
+
);
|
|
1404
1416
|
}
|
|
1417
|
+
const receipt = await publisher.uploadGitObject({
|
|
1418
|
+
sha: object.sha,
|
|
1419
|
+
type: object.type,
|
|
1420
|
+
body,
|
|
1421
|
+
repoId: plan.repoId
|
|
1422
|
+
});
|
|
1423
|
+
merged.set(object.sha, receipt.txId);
|
|
1424
|
+
totalFeePaid += receipt.feePaid;
|
|
1425
|
+
resultBySha.set(object.sha, {
|
|
1426
|
+
sha: object.sha,
|
|
1427
|
+
txId: receipt.txId,
|
|
1428
|
+
feePaid: receipt.feePaid,
|
|
1429
|
+
skipped: false
|
|
1430
|
+
});
|
|
1405
1431
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1432
|
+
}
|
|
1433
|
+
let announceReceipt = null;
|
|
1434
|
+
if (plan.announceNeeded && !remoteState.announced) {
|
|
1435
|
+
const announceEvent = buildRepoAnnouncement(
|
|
1436
|
+
plan.repoId,
|
|
1437
|
+
plan.announcement.name,
|
|
1438
|
+
plan.announcement.description
|
|
1410
1439
|
);
|
|
1411
|
-
|
|
1412
|
-
|
|
1440
|
+
announceReceipt = await publisher.publishEvent(announceEvent, relayUrls);
|
|
1441
|
+
totalFeePaid += announceReceipt.feePaid;
|
|
1442
|
+
}
|
|
1443
|
+
const refsEvent = buildRepoRefs(
|
|
1444
|
+
plan.repoId,
|
|
1445
|
+
plan.newRefs,
|
|
1446
|
+
Object.fromEntries(merged)
|
|
1447
|
+
);
|
|
1448
|
+
const refsReceipt = await publisher.publishEvent(refsEvent, relayUrls);
|
|
1449
|
+
totalFeePaid += refsReceipt.feePaid;
|
|
1450
|
+
const uploads = plan.objects.map((o) => {
|
|
1451
|
+
const step = resultBySha.get(o.sha);
|
|
1452
|
+
if (!step) {
|
|
1453
|
+
throw new Error(`internal: no upload result recorded for ${o.sha}`);
|
|
1413
1454
|
}
|
|
1414
|
-
return
|
|
1415
|
-
};
|
|
1455
|
+
return step;
|
|
1456
|
+
});
|
|
1416
1457
|
return {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
announceEvent,
|
|
1423
|
-
name,
|
|
1424
|
-
description,
|
|
1425
|
-
relays,
|
|
1426
|
-
resolveMissing
|
|
1458
|
+
uploads,
|
|
1459
|
+
announceReceipt,
|
|
1460
|
+
refsReceipt,
|
|
1461
|
+
arweaveMap: merged,
|
|
1462
|
+
totalFeePaid
|
|
1427
1463
|
};
|
|
1428
1464
|
}
|
|
1429
1465
|
|
|
@@ -3672,7 +3708,9 @@ var ClientRunner = class {
|
|
|
3672
3708
|
req.title,
|
|
3673
3709
|
req.commits ?? [],
|
|
3674
3710
|
req.branch,
|
|
3675
|
-
content
|
|
3711
|
+
content,
|
|
3712
|
+
// PR body → `description` tag; never the content (git am safety, #280).
|
|
3713
|
+
typeof req.description === "string" && req.description !== "" ? req.description : void 0
|
|
3676
3714
|
);
|
|
3677
3715
|
return this.gitPublishSigned(event);
|
|
3678
3716
|
}
|
|
@@ -4322,4 +4360,4 @@ export {
|
|
|
4322
4360
|
PublishRejectedError,
|
|
4323
4361
|
registerRoutes
|
|
4324
4362
|
};
|
|
4325
|
-
//# sourceMappingURL=chunk-
|
|
4363
|
+
//# sourceMappingURL=chunk-JCQENRQK.js.map
|