@parity/product-deploy 0.7.28-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +233 -0
  3. package/assets/environments.json +313 -0
  4. package/bin/bulletin-bootstrap +84 -0
  5. package/bin/bulletin-deploy +429 -0
  6. package/dist/bug-report.d.ts +29 -0
  7. package/dist/bug-report.js +27 -0
  8. package/dist/chunk-2VAUMZB2.js +284 -0
  9. package/dist/chunk-43HLT335.js +232 -0
  10. package/dist/chunk-5VZQ2KSU.js +231 -0
  11. package/dist/chunk-ADNBLFDP.js +225 -0
  12. package/dist/chunk-BMAEWZYV.js +24 -0
  13. package/dist/chunk-C2TS5MER.js +64 -0
  14. package/dist/chunk-DNXH4QTI.js +2336 -0
  15. package/dist/chunk-FZWJV5AD.js +231 -0
  16. package/dist/chunk-GZD2UFLR.js +8 -0
  17. package/dist/chunk-HOTQDYHD.js +219 -0
  18. package/dist/chunk-IDYGYIMH.js +207 -0
  19. package/dist/chunk-KHVTYIIX.js +146 -0
  20. package/dist/chunk-KJH2T5TQ.js +172 -0
  21. package/dist/chunk-KOSF5FDO.js +49 -0
  22. package/dist/chunk-LZJMVPYW.js +156 -0
  23. package/dist/chunk-MFTODIIT.js +725 -0
  24. package/dist/chunk-MMAZFJDG.js +91 -0
  25. package/dist/chunk-NF2FL4ZO.js +164 -0
  26. package/dist/chunk-OITUIM2E.js +524 -0
  27. package/dist/chunk-P6CHOMN3.js +2368 -0
  28. package/dist/chunk-QMYW3D6E.js +316 -0
  29. package/dist/chunk-QTZNULSH.js +185 -0
  30. package/dist/chunk-RI3ZLNPN.js +71 -0
  31. package/dist/chunk-S7EM5VMW.js +108 -0
  32. package/dist/chunk-T7EEVWNU.js +32 -0
  33. package/dist/chunk-UPWEOGLQ.js +37 -0
  34. package/dist/chunk-ZOC4GITL.js +13 -0
  35. package/dist/chunk-ZYVGHDMU.js +117 -0
  36. package/dist/chunk-probe.d.ts +37 -0
  37. package/dist/chunk-probe.js +18 -0
  38. package/dist/chunker.d.ts +8 -0
  39. package/dist/chunker.js +10 -0
  40. package/dist/deploy.d.ts +299 -0
  41. package/dist/deploy.js +96 -0
  42. package/dist/dotns.d.ts +506 -0
  43. package/dist/dotns.js +101 -0
  44. package/dist/environments.d.ts +104 -0
  45. package/dist/environments.js +23 -0
  46. package/dist/errors.d.ts +6 -0
  47. package/dist/errors.js +8 -0
  48. package/dist/gh-pages-mirror.d.ts +76 -0
  49. package/dist/gh-pages-mirror.js +30 -0
  50. package/dist/incremental-stats.d.ts +69 -0
  51. package/dist/incremental-stats.js +10 -0
  52. package/dist/index.d.ts +23 -0
  53. package/dist/index.js +146 -0
  54. package/dist/manifest/byte-budget.d.ts +46 -0
  55. package/dist/manifest/byte-budget.js +14 -0
  56. package/dist/manifest/config-load.d.ts +36 -0
  57. package/dist/manifest/config-load.js +10 -0
  58. package/dist/manifest/publish.d.ts +54 -0
  59. package/dist/manifest/publish.js +23 -0
  60. package/dist/manifest/schema.d.ts +29 -0
  61. package/dist/manifest/schema.js +10 -0
  62. package/dist/manifest/types.d.ts +90 -0
  63. package/dist/manifest/types.js +6 -0
  64. package/dist/manifest-embed.d.ts +18 -0
  65. package/dist/manifest-embed.js +9 -0
  66. package/dist/manifest-fetch.d.ts +32 -0
  67. package/dist/manifest-fetch.js +21 -0
  68. package/dist/manifest-roundtrip.d.ts +15 -0
  69. package/dist/manifest-roundtrip.js +55 -0
  70. package/dist/manifest.d.ts +44 -0
  71. package/dist/manifest.js +20 -0
  72. package/dist/memory-report.d.ts +95 -0
  73. package/dist/memory-report.js +17 -0
  74. package/dist/merkle.d.ts +50 -0
  75. package/dist/merkle.js +33 -0
  76. package/dist/personhood/bind-paid-alias.d.ts +43 -0
  77. package/dist/personhood/bind-paid-alias.js +10 -0
  78. package/dist/personhood/bind-personal-id.d.ts +55 -0
  79. package/dist/personhood/bind-personal-id.js +12 -0
  80. package/dist/personhood/bootstrap.d.ts +85 -0
  81. package/dist/personhood/bootstrap.js +245 -0
  82. package/dist/personhood/claim-pgas.d.ts +61 -0
  83. package/dist/personhood/claim-pgas.js +12 -0
  84. package/dist/personhood/constants.d.ts +23 -0
  85. package/dist/personhood/constants.js +22 -0
  86. package/dist/personhood/encoding.d.ts +49 -0
  87. package/dist/personhood/encoding.js +24 -0
  88. package/dist/personhood/hashing.d.ts +4 -0
  89. package/dist/personhood/hashing.js +8 -0
  90. package/dist/personhood/member-key.d.ts +12 -0
  91. package/dist/personhood/member-key.js +10 -0
  92. package/dist/personhood/people-client.d.ts +14 -0
  93. package/dist/personhood/people-client.js +48 -0
  94. package/dist/personhood/reprove.d.ts +43 -0
  95. package/dist/personhood/reprove.js +225 -0
  96. package/dist/pool.d.ts +51 -0
  97. package/dist/pool.js +30 -0
  98. package/dist/run-state.d.ts +22 -0
  99. package/dist/run-state.js +20 -0
  100. package/dist/telemetry.d.ts +56 -0
  101. package/dist/telemetry.js +71 -0
  102. package/dist/version-check.d.ts +38 -0
  103. package/dist/version-check.js +30 -0
  104. package/docs/bootstrap.md +49 -0
  105. package/docs/e2e-bootstrap.md +154 -0
  106. package/docs/telemetry.md +62 -0
  107. package/docs/testing.md +44 -0
  108. package/package.json +82 -0
  109. package/tools/release-retry-wrapper.mjs +74 -0
@@ -0,0 +1,231 @@
1
+ import {
2
+ MANIFEST_DIR,
3
+ MANIFEST_FILENAME,
4
+ parseManifest
5
+ } from "./chunk-S7EM5VMW.js";
6
+
7
+ // src/manifest-fetch.ts
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import * as os from "os";
11
+ import { CarReader } from "@ipld/car/reader";
12
+ import * as dagPB from "@ipld/dag-pb";
13
+ import { CID } from "multiformats/cid";
14
+ var DEFAULT_TIMEOUT_MS = 3e4;
15
+ var SIDECAR_FILENAME = ".last_deploy_cid";
16
+ var RANGE_TIERS = [
17
+ "bytes=0-4095",
18
+ "bytes=0-65535",
19
+ "bytes=0-1048575",
20
+ void 0
21
+ // full body
22
+ ];
23
+ function getCacheDir() {
24
+ const override = process.env.BULLETIN_DEPLOY_CACHE_DIR;
25
+ if (override) return path.join(override, "manifests");
26
+ if (process.platform === "win32") return null;
27
+ const xdg = process.env.XDG_CACHE_HOME;
28
+ if (xdg) return path.join(xdg, "bulletin-deploy", "manifests");
29
+ return path.join(os.homedir(), ".cache", "bulletin-deploy", "manifests");
30
+ }
31
+ function readPersistentLocalManifest(domain, prevContenthash) {
32
+ if (!domain) return null;
33
+ const cacheDir = getCacheDir();
34
+ if (!cacheDir) return null;
35
+ const cidPath = path.join(cacheDir, `${domain}.cid`);
36
+ const manifestPath = path.join(cacheDir, `${domain}.json`);
37
+ let storedCid;
38
+ try {
39
+ storedCid = fs.readFileSync(cidPath, "utf8").trim();
40
+ } catch {
41
+ return null;
42
+ }
43
+ if (storedCid !== prevContenthash) return null;
44
+ let text;
45
+ try {
46
+ text = fs.readFileSync(manifestPath, "utf8");
47
+ } catch {
48
+ return null;
49
+ }
50
+ const parsed = parseManifest(text);
51
+ if (!parsed.ok) return null;
52
+ return {
53
+ source: "embedded",
54
+ manifest: parsed.manifest,
55
+ attempts: 0,
56
+ bytesDownloaded: text.length
57
+ };
58
+ }
59
+ function writePersistentLocalManifest(domain, storageCid, manifestJson) {
60
+ const cacheDir = getCacheDir();
61
+ if (!cacheDir) return;
62
+ try {
63
+ fs.mkdirSync(cacheDir, { recursive: true });
64
+ const cidPath = path.join(cacheDir, `${domain}.cid`);
65
+ const manifestPath = path.join(cacheDir, `${domain}.json`);
66
+ const cidTmp = `${cidPath}.${process.pid}.tmp`;
67
+ const manifestTmp = `${manifestPath}.${process.pid}.tmp`;
68
+ fs.writeFileSync(cidTmp, storageCid);
69
+ fs.renameSync(cidTmp, cidPath);
70
+ fs.writeFileSync(manifestTmp, manifestJson);
71
+ fs.renameSync(manifestTmp, manifestPath);
72
+ } catch {
73
+ }
74
+ }
75
+ async function fetchAcrossTiers(url, budget, start) {
76
+ let lastReason = "unknown";
77
+ let attempts = 0;
78
+ let bytesDownloaded = 0;
79
+ for (let tier = 0; tier < RANGE_TIERS.length; tier++) {
80
+ if (Date.now() - start > budget) {
81
+ return { outcome: "retryable", reason: `budget exceeded: ${lastReason}`, attempts, bytesDownloaded };
82
+ }
83
+ attempts++;
84
+ const headers = {};
85
+ if (RANGE_TIERS[tier] !== void 0) headers.Range = RANGE_TIERS[tier];
86
+ let res;
87
+ try {
88
+ const ctrl = new AbortController();
89
+ const remaining = budget - (Date.now() - start);
90
+ const timer = setTimeout(() => ctrl.abort(), Math.max(100, remaining));
91
+ try {
92
+ res = await fetch(url, { headers, signal: ctrl.signal });
93
+ } finally {
94
+ clearTimeout(timer);
95
+ }
96
+ } catch (e) {
97
+ lastReason = `network error: ${e?.message ?? e}`;
98
+ continue;
99
+ }
100
+ if (res.status === 404) return { outcome: "404", attempts, bytesDownloaded };
101
+ if (res.status !== 200 && res.status !== 206) {
102
+ lastReason = `gateway HTTP ${res.status}`;
103
+ continue;
104
+ }
105
+ const isFullBody = res.status === 200;
106
+ let carBytes;
107
+ try {
108
+ const buf = await res.arrayBuffer();
109
+ carBytes = new Uint8Array(buf);
110
+ bytesDownloaded = carBytes.length;
111
+ } catch (e) {
112
+ if (isFullBody) return { outcome: "parse_error", reason: `body read error: ${e?.message ?? e}`, attempts, bytesDownloaded };
113
+ lastReason = `body read error: ${e?.message ?? e}`;
114
+ continue;
115
+ }
116
+ let manifestBytes;
117
+ try {
118
+ manifestBytes = await extractManifestFromCar(carBytes);
119
+ } catch (e) {
120
+ const msg = String(e?.message ?? e);
121
+ if (isFullBody) return { outcome: "parse_error", reason: `CAR parse error: ${msg}`, attempts, bytesDownloaded };
122
+ lastReason = `truncated at tier ${tier}: ${msg}`;
123
+ continue;
124
+ }
125
+ if (!manifestBytes) {
126
+ if (isFullBody) return { outcome: "parse_error", reason: "no .bulletin-deploy/manifest.json in deployed DAG", attempts, bytesDownloaded };
127
+ lastReason = `manifest not in slice tier ${tier}`;
128
+ continue;
129
+ }
130
+ const text = new TextDecoder().decode(manifestBytes);
131
+ const parsed = parseManifest(text);
132
+ if (parsed.ok) return { outcome: "success", manifest: parsed.manifest, attempts, bytesDownloaded };
133
+ return { outcome: "parse_error", reason: parsed.error, attempts, bytesDownloaded };
134
+ }
135
+ return { outcome: "retryable", reason: `tiers exhausted: ${lastReason}`, attempts, bytesDownloaded };
136
+ }
137
+ async function fetchPreviousManifest(prevContenthash, options = {}) {
138
+ if (prevContenthash === null) return { source: "none" };
139
+ const local = readPersistentLocalManifest(options.domain, prevContenthash);
140
+ if (local) return local;
141
+ const gatewayList = (options.gateways ?? (options.gateway ? [options.gateway] : [])).map((g) => g.replace(/\/$/, ""));
142
+ const budget = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
143
+ const start = Date.now();
144
+ let lastReason = "unknown";
145
+ let totalAttempts = 0;
146
+ let bytesDownloaded = 0;
147
+ for (const gatewayRaw of gatewayList) {
148
+ const gateway = gatewayRaw.replace(/\/ipfs\/?$/, "");
149
+ const url = `${gateway}/ipfs/${prevContenthash}`;
150
+ const tierResult = await fetchAcrossTiers(url, budget, start);
151
+ if (tierResult.outcome === "success") {
152
+ return {
153
+ source: "embedded",
154
+ manifest: tierResult.manifest,
155
+ attempts: totalAttempts + tierResult.attempts,
156
+ bytesDownloaded: bytesDownloaded + tierResult.bytesDownloaded
157
+ };
158
+ }
159
+ if (tierResult.outcome === "404" || tierResult.outcome === "parse_error") {
160
+ return {
161
+ source: "heuristic_fallback",
162
+ reason: tierResult.outcome === "404" ? "gateway 404" : tierResult.reason,
163
+ attempts: totalAttempts + tierResult.attempts,
164
+ bytesDownloaded: bytesDownloaded + tierResult.bytesDownloaded
165
+ };
166
+ }
167
+ lastReason = tierResult.reason;
168
+ totalAttempts += tierResult.attempts;
169
+ bytesDownloaded += tierResult.bytesDownloaded;
170
+ }
171
+ return { source: "heuristic_fallback", reason: `all gateways exhausted: ${lastReason}`, attempts: totalAttempts, bytesDownloaded };
172
+ }
173
+ async function extractManifestFromCar(carBytes) {
174
+ const reader = await CarReader.fromBytes(carBytes);
175
+ const roots = await reader.getRoots();
176
+ if (roots.length === 0) return null;
177
+ const blocks = /* @__PURE__ */ new Map();
178
+ for await (const { cid, bytes } of reader.blocks()) {
179
+ blocks.set(cid.toString(), bytes);
180
+ }
181
+ return walkDagToManifest(blocks, roots[0].toString());
182
+ }
183
+ async function walkDagToManifest(blocks, rootCid) {
184
+ const rootBytes = blocks.get(rootCid);
185
+ if (!rootBytes) return null;
186
+ const rootNode = dagPB.decode(rootBytes);
187
+ const bdLink = (rootNode.Links ?? []).find((l) => l.Name === MANIFEST_DIR);
188
+ if (!bdLink) return null;
189
+ const bdBytes = blocks.get(bdLink.Hash.toString());
190
+ if (!bdBytes) return null;
191
+ const bdNode = dagPB.decode(bdBytes);
192
+ const manLink = (bdNode.Links ?? []).find((l) => l.Name === MANIFEST_FILENAME);
193
+ if (!manLink) return null;
194
+ const manCidStr = manLink.Hash.toString();
195
+ const manCid = CID.parse(manCidStr);
196
+ const manBytes = blocks.get(manCidStr);
197
+ if (!manBytes) return null;
198
+ if (manCid.code === 85) {
199
+ return manBytes;
200
+ }
201
+ if (manCid.code === 112) {
202
+ const node = dagPB.decode(manBytes);
203
+ const parts = [];
204
+ let total = 0;
205
+ for (const link of node.Links ?? []) {
206
+ const leafBytes = blocks.get(link.Hash.toString());
207
+ if (!leafBytes) return null;
208
+ parts.push(leafBytes);
209
+ total += leafBytes.length;
210
+ }
211
+ const out = new Uint8Array(total);
212
+ let pos = 0;
213
+ for (const part of parts) {
214
+ out.set(part, pos);
215
+ pos += part.length;
216
+ }
217
+ return out;
218
+ }
219
+ return null;
220
+ }
221
+
222
+ export {
223
+ DEFAULT_TIMEOUT_MS,
224
+ SIDECAR_FILENAME,
225
+ getCacheDir,
226
+ readPersistentLocalManifest,
227
+ writePersistentLocalManifest,
228
+ fetchPreviousManifest,
229
+ extractManifestFromCar,
230
+ walkDagToManifest
231
+ };
@@ -0,0 +1,8 @@
1
+ // src/manifest/types.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+
6
+ export {
7
+ defineConfig
8
+ };
@@ -0,0 +1,219 @@
1
+ // src/gh-pages-mirror.ts
2
+ import { execSync } from "child_process";
3
+ import * as fs from "fs";
4
+ import * as os from "os";
5
+ import * as path from "path";
6
+ var GH_PAGES_MIRROR_MAX_BYTES = 100 * 1024 * 1024;
7
+ var GH_PAGES_MIRROR_DIR = "bulletin";
8
+ var GH_PAGES_MIRROR_BRANCH = "gh-pages";
9
+ var MirrorSkipped = class extends Error {
10
+ constructor(reason) {
11
+ super(reason);
12
+ this.name = "MirrorSkipped";
13
+ }
14
+ };
15
+ async function pollMirrorFreshness(mirrorUrl2, expectedCid, opts = {}) {
16
+ const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1e3;
17
+ const intervalMs = opts.intervalMs ?? 1e4;
18
+ const fetchFn = opts.fetchFn ?? fetch;
19
+ const manifestUrl = mirrorUrl2.replace(/\.car$/, ".json");
20
+ const started = Date.now();
21
+ const deadline = started + timeoutMs;
22
+ let attempts = 0;
23
+ let lastCid = null;
24
+ let lastStatus = 0;
25
+ while (Date.now() < deadline) {
26
+ attempts++;
27
+ try {
28
+ const res = await fetchFn(manifestUrl, { redirect: "follow", cache: "no-store" });
29
+ lastStatus = res.status;
30
+ if (res.status === 200) {
31
+ const m = await res.json();
32
+ if (m.cid === expectedCid) {
33
+ return { verified: true, attempts, durationMs: Date.now() - started, lastCid: m.cid, lastStatus };
34
+ }
35
+ lastCid = m.cid ?? null;
36
+ }
37
+ } catch {
38
+ }
39
+ if (Date.now() + intervalMs >= deadline) break;
40
+ await new Promise((r) => setTimeout(r, intervalMs));
41
+ }
42
+ return { verified: false, attempts, durationMs: Date.now() - started, lastCid, lastStatus };
43
+ }
44
+ function parseGitRemoteUrl(url) {
45
+ const trimmed = url.trim();
46
+ const ssh = trimmed.match(/^git@[^:]+:([^/]+)\/(.+?)(?:\.git)?$/);
47
+ if (ssh) return { owner: ssh[1], repo: ssh[2] };
48
+ const https = trimmed.match(/^https?:\/\/(?:[^@]*@)?[^/]+\/([^/]+)\/(.+?)(?:\.git)?$/);
49
+ if (https) return { owner: https[1], repo: https[2] };
50
+ return null;
51
+ }
52
+ function resolveOwnerRepo(repoPath) {
53
+ const envRepo = process.env.GITHUB_REPOSITORY;
54
+ if (envRepo && envRepo.includes("/")) {
55
+ const [owner, repo] = envRepo.split("/");
56
+ if (owner && repo) return { owner, repo };
57
+ }
58
+ try {
59
+ const url = execSync("git config --get remote.origin.url", {
60
+ cwd: repoPath,
61
+ encoding: "utf-8",
62
+ stdio: ["ignore", "pipe", "ignore"]
63
+ }).trim();
64
+ return parseGitRemoteUrl(url);
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ function resolveSourceCommit(repoPath) {
70
+ if (process.env.GITHUB_SHA) return process.env.GITHUB_SHA;
71
+ try {
72
+ return execSync("git rev-parse HEAD", {
73
+ cwd: repoPath,
74
+ encoding: "utf-8",
75
+ stdio: ["ignore", "pipe", "ignore"]
76
+ }).trim();
77
+ } catch {
78
+ return void 0;
79
+ }
80
+ }
81
+ function normalizeDomainFilename(domain) {
82
+ const label = domain.endsWith(".dot") ? domain.slice(0, -4) : domain;
83
+ if (!/^[a-z0-9-]+$/.test(label)) {
84
+ throw new Error(`Invalid domain label for mirror filename: ${JSON.stringify(domain)}`);
85
+ }
86
+ return `${label}.dot`;
87
+ }
88
+ function mirrorUrl(owner, repo, domainFilename) {
89
+ return `https://${owner}.github.io/${repo}/${GH_PAGES_MIRROR_DIR}/${domainFilename}.car`;
90
+ }
91
+ function buildManifest(input) {
92
+ return {
93
+ domain: normalizeDomainFilename(input.domain),
94
+ cid: input.cid,
95
+ toolVersion: input.toolVersion,
96
+ deployedAt: input.deployedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
97
+ encrypted: input.encrypted,
98
+ bulletinRpc: input.bulletinRpc,
99
+ sourceRepo: input.sourceRepo,
100
+ sourceCommit: input.sourceCommit
101
+ };
102
+ }
103
+ function runGit(args, cwd, extraEnv = {}) {
104
+ return execSync(`git ${args.join(" ")}`, {
105
+ cwd,
106
+ encoding: "utf-8",
107
+ stdio: ["ignore", "pipe", "pipe"],
108
+ env: { ...process.env, ...extraEnv }
109
+ }).trim();
110
+ }
111
+ var MIRROR_BOT_GIT_OVERRIDES = [
112
+ "-c",
113
+ "user.email=bulletin-deploy@noreply.github.com",
114
+ "-c",
115
+ "user.name=bulletin-deploy",
116
+ "-c",
117
+ "commit.gpgsign=false"
118
+ ];
119
+ function pushRemoteUrl(owner, repo, token) {
120
+ const authedOwner = token ? `x-access-token:${token}@github.com` : "github.com";
121
+ return `https://${authedOwner}/${owner}/${repo}.git`;
122
+ }
123
+ async function mirrorToGitHubPages(input) {
124
+ const repoPath = input.repoPath ?? process.cwd();
125
+ const ownerRepo = resolveOwnerRepo(repoPath);
126
+ if (!ownerRepo) {
127
+ throw new MirrorSkipped("no GitHub repo detected (GITHUB_REPOSITORY unset and no github.com remote)");
128
+ }
129
+ if (input.carBytes.length > GH_PAGES_MIRROR_MAX_BYTES) {
130
+ const mb = (input.carBytes.length / 1024 / 1024).toFixed(1);
131
+ throw new MirrorSkipped(`CAR is ${mb} MB, exceeds GitHub's 100 MB single-file soft limit. Pages can't host this CAR \u2014 the on-chain deploy still succeeds and hosts will fall back to Bulletin.`);
132
+ }
133
+ const domainFilename = normalizeDomainFilename(input.domain);
134
+ const { owner, repo } = ownerRepo;
135
+ const sourceCommit = input.sourceCommit ?? resolveSourceCommit(repoPath);
136
+ const sourceRepo = input.sourceRepo ?? `${owner}/${repo}`;
137
+ const manifest = buildManifest({ ...input, sourceCommit, sourceRepo });
138
+ const workTree = fs.mkdtempSync(path.join(os.tmpdir(), "bulletin-mirror-"));
139
+ const token = input.githubToken ?? process.env.GITHUB_TOKEN;
140
+ try {
141
+ let branchExists = false;
142
+ try {
143
+ execSync(`git ls-remote --exit-code --heads origin ${GH_PAGES_MIRROR_BRANCH}`, {
144
+ cwd: repoPath,
145
+ stdio: ["ignore", "ignore", "ignore"]
146
+ });
147
+ branchExists = true;
148
+ } catch {
149
+ branchExists = false;
150
+ }
151
+ if (branchExists) {
152
+ runGit(["fetch", "origin", GH_PAGES_MIRROR_BRANCH, "--depth=1"], repoPath);
153
+ runGit(["worktree", "add", "--detach", workTree, `origin/${GH_PAGES_MIRROR_BRANCH}`], repoPath);
154
+ } else {
155
+ runGit(["worktree", "add", "--detach", workTree, "HEAD"], repoPath);
156
+ runGit(["checkout", "--orphan", GH_PAGES_MIRROR_BRANCH], workTree);
157
+ runGit(["rm", "-rf", "--quiet", "."], workTree);
158
+ }
159
+ const mirrorDir = path.join(workTree, GH_PAGES_MIRROR_DIR);
160
+ fs.mkdirSync(mirrorDir, { recursive: true });
161
+ const carPath = path.join(mirrorDir, `${domainFilename}.car`);
162
+ const manifestPath = path.join(mirrorDir, `${domainFilename}.json`);
163
+ fs.writeFileSync(carPath, input.carBytes);
164
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
165
+ runGit([...MIRROR_BOT_GIT_OVERRIDES, "add", GH_PAGES_MIRROR_DIR], workTree);
166
+ const status = runGit(["status", "--porcelain"], workTree);
167
+ if (status.length === 0) {
168
+ return {
169
+ url: mirrorUrl(owner, repo, domainFilename),
170
+ owner,
171
+ repo,
172
+ carPath: path.posix.join(GH_PAGES_MIRROR_DIR, `${domainFilename}.car`),
173
+ manifestPath: path.posix.join(GH_PAGES_MIRROR_DIR, `${domainFilename}.json`)
174
+ };
175
+ }
176
+ runGit(
177
+ [
178
+ ...MIRROR_BOT_GIT_OVERRIDES,
179
+ "commit",
180
+ "-m",
181
+ `"mirror(bulletin): ${domainFilename} @ ${input.cid.slice(0, 12)}"`
182
+ ],
183
+ workTree
184
+ );
185
+ runGit(["push", pushRemoteUrl(owner, repo, token), `HEAD:${GH_PAGES_MIRROR_BRANCH}`], workTree);
186
+ return {
187
+ url: mirrorUrl(owner, repo, domainFilename),
188
+ owner,
189
+ repo,
190
+ carPath: path.posix.join(GH_PAGES_MIRROR_DIR, `${domainFilename}.car`),
191
+ manifestPath: path.posix.join(GH_PAGES_MIRROR_DIR, `${domainFilename}.json`)
192
+ };
193
+ } finally {
194
+ try {
195
+ runGit(["worktree", "remove", "--force", workTree], repoPath);
196
+ } catch {
197
+ }
198
+ try {
199
+ fs.rmSync(workTree, { recursive: true, force: true });
200
+ } catch {
201
+ }
202
+ }
203
+ }
204
+
205
+ export {
206
+ GH_PAGES_MIRROR_MAX_BYTES,
207
+ GH_PAGES_MIRROR_DIR,
208
+ GH_PAGES_MIRROR_BRANCH,
209
+ MirrorSkipped,
210
+ pollMirrorFreshness,
211
+ parseGitRemoteUrl,
212
+ resolveOwnerRepo,
213
+ resolveSourceCommit,
214
+ normalizeDomainFilename,
215
+ mirrorUrl,
216
+ buildManifest,
217
+ MIRROR_BOT_GIT_OVERRIDES,
218
+ mirrorToGitHubPages
219
+ };
@@ -0,0 +1,207 @@
1
+ import {
2
+ BANDERSNATCH_SIGNATURE_BYTES
3
+ } from "./chunk-T7EEVWNU.js";
4
+ import {
5
+ buildImplicationMessage,
6
+ buildV5GeneralExtrinsic,
7
+ bytesToHex,
8
+ hexToBytes,
9
+ readExtensionOrder,
10
+ toHex
11
+ } from "./chunk-ZYVGHDMU.js";
12
+
13
+ // src/personhood/bind-personal-id.ts
14
+ import { Enum } from "polkadot-api";
15
+ var AS_PERSON_IDENTIFIER = "AsPerson";
16
+ var VERIFY_MULTI_SIGNATURE_IDENTIFIER = "VerifyMultiSignature";
17
+ var IMPLICATION_EXCLUDE = /* @__PURE__ */ new Set([
18
+ "VerifyMultiSignature",
19
+ AS_PERSON_IDENTIFIER,
20
+ "AuthorizeCall",
21
+ "StorageWeightReclaim"
22
+ ]);
23
+ var PersonalIdBindingError = class extends Error {
24
+ dispatchError;
25
+ kind;
26
+ constructor(message, options = {}) {
27
+ super(message, options);
28
+ this.name = "PersonalIdBindingError";
29
+ this.kind = options.kind ?? "unknown";
30
+ this.dispatchError = options.dispatchError;
31
+ }
32
+ };
33
+ function buildAsPersonExtensionValue(signature, personalId) {
34
+ return Enum("AsPersonalIdentityWithProof", [bytesToHex(signature), personalId]);
35
+ }
36
+ var bindPersonalIdToAccount = async ({
37
+ typedApi,
38
+ client,
39
+ personalId,
40
+ account,
41
+ signMember,
42
+ callValidAt,
43
+ progress
44
+ }) => {
45
+ const api = typedApi;
46
+ if (typeof api.tx?.People?.set_personal_id_account !== "function") {
47
+ throw new PersonalIdBindingError(
48
+ "typedApi does not expose People.set_personal_id_account \u2014 wrong chain or stale descriptors",
49
+ { kind: "client_error" }
50
+ );
51
+ }
52
+ const block = callValidAt ?? await api.query.System.Number.getValue();
53
+ const innerTx = api.tx.People.set_personal_id_account({
54
+ account,
55
+ call_valid_at: block
56
+ });
57
+ const callBytes = await innerTx.getEncodedData();
58
+ const passEmpty = await capturePass({
59
+ innerTx,
60
+ asPersonValue: new Uint8Array()
61
+ });
62
+ const msgHash = buildImplicationMessage(
63
+ callBytes,
64
+ passEmpty.extensions,
65
+ IMPLICATION_EXCLUDE
66
+ );
67
+ const signature = await Promise.resolve(signMember(msgHash));
68
+ if (signature.length !== BANDERSNATCH_SIGNATURE_BYTES) {
69
+ throw new PersonalIdBindingError(
70
+ `Bandersnatch signature must be ${BANDERSNATCH_SIGNATURE_BYTES} bytes, got ${signature.length}`,
71
+ { kind: "client_error" }
72
+ );
73
+ }
74
+ const asPersonProof = Enum("AsPersonalIdentityWithProof", [bytesToHex(signature), personalId]);
75
+ const passProof = await capturePass({
76
+ innerTx,
77
+ asPersonValue: asPersonProof
78
+ });
79
+ const extrinsicBytes = buildV5GeneralExtrinsic(callBytes, passProof.extensions);
80
+ const extrinsicHex = toHex(extrinsicBytes);
81
+ const blockHash = await submitExtrinsic(client, extrinsicHex, progress);
82
+ return { extrinsicHex, blockHash };
83
+ };
84
+ var capturePass = async ({
85
+ innerTx,
86
+ asPersonValue
87
+ }) => {
88
+ let captured = null;
89
+ const sentinel = new Error("__personhood_capture_sentinel__");
90
+ const signer = {
91
+ publicKey: new Uint8Array(32),
92
+ signTx: async (callData, signedExtensions, metadata) => {
93
+ const order = await readExtensionOrder(metadata);
94
+ const byIdentifier = {};
95
+ for (const id of order) {
96
+ const ext = signedExtensions[id];
97
+ if (!ext) {
98
+ throw new PersonalIdBindingError(
99
+ `papi did not produce signed extension '${id}'`
100
+ );
101
+ }
102
+ byIdentifier[id] = {
103
+ value: ext.value,
104
+ additionalSigned: ext.additionalSigned
105
+ };
106
+ }
107
+ captured = { callData, extensions: { order, byIdentifier } };
108
+ throw sentinel;
109
+ },
110
+ signBytes: async () => new Uint8Array(64)
111
+ };
112
+ try {
113
+ await innerTx.sign(signer, {
114
+ customSignedExtensions: {
115
+ [AS_PERSON_IDENTIFIER]: {
116
+ value: asPersonValue,
117
+ additionalSigned: new Uint8Array()
118
+ },
119
+ [VERIFY_MULTI_SIGNATURE_IDENTIFIER]: {
120
+ value: Enum("Disabled")
121
+ },
122
+ RestrictOrigins: {
123
+ value: new Uint8Array([1]),
124
+ additionalSigned: new Uint8Array()
125
+ }
126
+ }
127
+ });
128
+ } catch (err) {
129
+ if (err !== sentinel) throw err;
130
+ }
131
+ if (!captured) {
132
+ throw new PersonalIdBindingError(
133
+ "extension capture failed \u2014 papi never invoked signTx",
134
+ { kind: "client_error" }
135
+ );
136
+ }
137
+ return captured;
138
+ };
139
+ var submitExtrinsic = (client, extrinsicHex, progress) => {
140
+ return new Promise((resolve, reject) => {
141
+ let settled = false;
142
+ const fail = (err) => {
143
+ if (settled) return;
144
+ settled = true;
145
+ reject(err);
146
+ };
147
+ const succeed = (blockHash) => {
148
+ if (settled) return;
149
+ settled = true;
150
+ resolve(blockHash);
151
+ };
152
+ client.submitAndWatch(hexToBytes(extrinsicHex)).subscribe({
153
+ next: (event) => {
154
+ if (settled) return;
155
+ if (event.type === "broadcasted") {
156
+ progress?.onBroadcasted?.();
157
+ return;
158
+ }
159
+ if (event.type === "txBestBlocksState" && event.found) {
160
+ if (event.ok === false) {
161
+ fail(
162
+ new PersonalIdBindingError(
163
+ "set_personal_id_account dispatched but failed in-block",
164
+ {
165
+ kind: "dispatch_error",
166
+ dispatchError: event.dispatchError
167
+ }
168
+ )
169
+ );
170
+ return;
171
+ }
172
+ progress?.onBestBlock?.(event.block.hash);
173
+ return;
174
+ }
175
+ if (event.type === "finalized") {
176
+ if (event.ok === false) {
177
+ fail(
178
+ new PersonalIdBindingError(
179
+ "set_personal_id_account dispatch failed at finalization",
180
+ {
181
+ kind: "dispatch_error",
182
+ dispatchError: event.dispatchError
183
+ }
184
+ )
185
+ );
186
+ return;
187
+ }
188
+ succeed(event.block.hash);
189
+ }
190
+ },
191
+ error: (err) => {
192
+ fail(
193
+ err instanceof PersonalIdBindingError ? err : new PersonalIdBindingError(
194
+ err instanceof Error ? `RPC rejected extrinsic: ${err.message}` : "RPC error during submitAndWatch",
195
+ { cause: err, kind: "rpc_error" }
196
+ )
197
+ );
198
+ }
199
+ });
200
+ });
201
+ };
202
+
203
+ export {
204
+ PersonalIdBindingError,
205
+ buildAsPersonExtensionValue,
206
+ bindPersonalIdToAccount
207
+ };