bulletin-deploy 0.6.16 → 0.7.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.
@@ -1,38 +1,40 @@
1
1
  import {
2
- DEFAULT_MNEMONIC,
3
2
  DotNS,
4
3
  TX_TIMEOUT_MS,
5
4
  fetchNonce,
6
5
  popStatusName,
7
6
  validateDomainLabel
8
- } from "./chunk-3C7PWPPG.js";
7
+ } from "./chunk-OCOCVZQV.js";
9
8
  import {
10
9
  MirrorSkipped,
11
- mirrorToGitHubPages
12
- } from "./chunk-UZVOH3HB.js";
13
- import {
14
- merkleizeJS
15
- } from "./chunk-QILGABSF.js";
16
- import {
17
- derivePoolAccounts,
18
- detectTestnet,
19
- ensureAuthorized,
20
- fetchPoolAuthorizations,
21
- selectAccount,
22
- topUpBy
23
- } from "./chunk-JHNW2EKY.js";
10
+ mirrorToGitHubPages,
11
+ pollMirrorFreshness
12
+ } from "./chunk-2Q2WSKFD.js";
24
13
  import {
25
14
  VERSION,
26
15
  captureWarning,
27
16
  initTelemetry,
28
17
  resolveRunner,
29
18
  resolveRunnerType,
19
+ sampleMemory,
30
20
  setDeployAttribute,
21
+ setDeployReportContext,
31
22
  setDeploySentryTag,
32
23
  truncateAddress,
33
24
  withDeploySpan,
34
25
  withSpan
35
- } from "./chunk-LF3XAUCI.js";
26
+ } from "./chunk-Q42TQHNL.js";
27
+ import {
28
+ merkleizeJS
29
+ } from "./chunk-B7GUYYAN.js";
30
+ import {
31
+ derivePoolAccounts,
32
+ detectTestnet,
33
+ ensureAuthorized,
34
+ fetchPoolAuthorizations,
35
+ selectAccount,
36
+ topUpBy
37
+ } from "./chunk-JHNW2EKY.js";
36
38
 
37
39
  // src/deploy.ts
38
40
  import { Buffer } from "buffer";
@@ -52,262 +54,9 @@ import { base58btc } from "multiformats/bases/base58";
52
54
  import * as dagPB from "@ipld/dag-pb";
53
55
  import { UnixFS } from "ipfs-unixfs";
54
56
  import { cryptoWaitReady } from "@polkadot/util-crypto";
55
- import { createCdm } from "@dotdm/cdm";
56
57
  import { getPolkadotSigner } from "polkadot-api/signer";
57
58
  import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
58
59
  import { mnemonicToEntropy, entropyToMiniSecret, ss58Address } from "@polkadot-labs/hdkd-helpers";
59
-
60
- // cdm.json
61
- var cdm_default = {
62
- targets: {
63
- acc2c3b5e912b762: {
64
- "asset-hub": "wss://asset-hub-paseo-rpc.n.dwellir.com",
65
- bulletin: "https://paseo-ipfs.polkadot.io/ipfs"
66
- }
67
- },
68
- dependencies: {
69
- acc2c3b5e912b762: {
70
- "@example/playground-registry": "latest"
71
- }
72
- },
73
- contracts: {
74
- acc2c3b5e912b762: {
75
- "@example/playground-registry": {
76
- version: 6,
77
- address: "0x279585Cb8E8971e34520A3ebbda3E0C4D77C3d97",
78
- abi: [
79
- {
80
- type: "constructor",
81
- inputs: [],
82
- stateMutability: "nonpayable"
83
- },
84
- {
85
- type: "function",
86
- name: "publish",
87
- inputs: [
88
- {
89
- name: "domain",
90
- type: "string"
91
- },
92
- {
93
- name: "metadata_uri",
94
- type: "string"
95
- }
96
- ],
97
- outputs: [],
98
- stateMutability: "nonpayable"
99
- },
100
- {
101
- type: "function",
102
- name: "unpublish",
103
- inputs: [
104
- {
105
- name: "domain",
106
- type: "string"
107
- }
108
- ],
109
- outputs: [],
110
- stateMutability: "nonpayable"
111
- },
112
- {
113
- type: "function",
114
- name: "rateApp",
115
- inputs: [
116
- {
117
- name: "domain",
118
- type: "string"
119
- },
120
- {
121
- name: "rating",
122
- type: "uint8"
123
- },
124
- {
125
- name: "comment_uri",
126
- type: "string"
127
- }
128
- ],
129
- outputs: [],
130
- stateMutability: "nonpayable"
131
- },
132
- {
133
- type: "function",
134
- name: "removeRating",
135
- inputs: [
136
- {
137
- name: "domain",
138
- type: "string"
139
- },
140
- {
141
- name: "reviewer",
142
- type: "address"
143
- }
144
- ],
145
- outputs: [],
146
- stateMutability: "nonpayable"
147
- },
148
- {
149
- type: "function",
150
- name: "getContextId",
151
- inputs: [],
152
- outputs: [
153
- {
154
- name: "",
155
- type: "bytes32"
156
- }
157
- ],
158
- stateMutability: "view"
159
- },
160
- {
161
- type: "function",
162
- name: "getAppCount",
163
- inputs: [],
164
- outputs: [
165
- {
166
- name: "",
167
- type: "uint32"
168
- }
169
- ],
170
- stateMutability: "view"
171
- },
172
- {
173
- type: "function",
174
- name: "getDomainAt",
175
- inputs: [
176
- {
177
- name: "index",
178
- type: "uint32"
179
- }
180
- ],
181
- outputs: [
182
- {
183
- name: "",
184
- type: "tuple",
185
- components: [
186
- {
187
- name: "isSome",
188
- type: "bool"
189
- },
190
- {
191
- name: "value",
192
- type: "string"
193
- }
194
- ]
195
- }
196
- ],
197
- stateMutability: "view"
198
- },
199
- {
200
- type: "function",
201
- name: "getOwnerAppCount",
202
- inputs: [
203
- {
204
- name: "owner",
205
- type: "address"
206
- }
207
- ],
208
- outputs: [
209
- {
210
- name: "",
211
- type: "uint32"
212
- }
213
- ],
214
- stateMutability: "view"
215
- },
216
- {
217
- type: "function",
218
- name: "getOwnerDomainAt",
219
- inputs: [
220
- {
221
- name: "owner",
222
- type: "address"
223
- },
224
- {
225
- name: "index",
226
- type: "uint32"
227
- }
228
- ],
229
- outputs: [
230
- {
231
- name: "",
232
- type: "tuple",
233
- components: [
234
- {
235
- name: "isSome",
236
- type: "bool"
237
- },
238
- {
239
- name: "value",
240
- type: "string"
241
- }
242
- ]
243
- }
244
- ],
245
- stateMutability: "view"
246
- },
247
- {
248
- type: "function",
249
- name: "getSudo",
250
- inputs: [],
251
- outputs: [
252
- {
253
- name: "",
254
- type: "address"
255
- }
256
- ],
257
- stateMutability: "view"
258
- },
259
- {
260
- type: "function",
261
- name: "getMetadataUri",
262
- inputs: [
263
- {
264
- name: "domain",
265
- type: "string"
266
- }
267
- ],
268
- outputs: [
269
- {
270
- name: "",
271
- type: "tuple",
272
- components: [
273
- {
274
- name: "isSome",
275
- type: "bool"
276
- },
277
- {
278
- name: "value",
279
- type: "string"
280
- }
281
- ]
282
- }
283
- ],
284
- stateMutability: "view"
285
- },
286
- {
287
- type: "function",
288
- name: "getOwner",
289
- inputs: [
290
- {
291
- name: "domain",
292
- type: "string"
293
- }
294
- ],
295
- outputs: [
296
- {
297
- name: "",
298
- type: "address"
299
- }
300
- ],
301
- stateMutability: "view"
302
- }
303
- ],
304
- metadataCid: "bafk2bzaceck7veaix4ttzyd6bmwlssgycrrlgilpat2c272nczzlrgnqy6fze"
305
- }
306
- }
307
- }
308
- };
309
-
310
- // src/deploy.ts
311
60
  var NonRetryableError = class extends Error {
312
61
  constructor(message) {
313
62
  super(message);
@@ -337,17 +86,6 @@ function isConnectionError(error) {
337
86
  return /heartbeat timeout|WS halt|Unable to connect/i.test(msg);
338
87
  }
339
88
  var CID_CONFIG = { version: 1, codec: 85, hashCode: 18, hashLength: 32 };
340
- function getGitRemoteUrl() {
341
- try {
342
- const raw = execSync("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
343
- if (raw.startsWith("git@")) {
344
- return raw.replace(/^git@([^:]+):/, "https://$1/").replace(/\.git$/, "");
345
- }
346
- return raw.replace(/\.git$/, "");
347
- } catch {
348
- return null;
349
- }
350
- }
351
89
  function deriveRootSigner(mnemonic, path2 = "") {
352
90
  const entropy = mnemonicToEntropy(mnemonic);
353
91
  const miniSecret = entropyToMiniSecret(entropy);
@@ -356,11 +94,6 @@ function deriveRootSigner(mnemonic, path2 = "") {
356
94
  const signer = getPolkadotSigner(keyPair.publicKey, "Sr25519", keyPair.sign);
357
95
  return { signer, ss58: ss58Address(keyPair.publicKey) };
358
96
  }
359
- function getRegistrySigner(explicitMnemonic, derivationPath) {
360
- const mnemonic = explicitMnemonic || process.env.DOTNS_MNEMONIC || process.env.MNEMONIC || DEFAULT_MNEMONIC;
361
- const { signer, ss58 } = deriveRootSigner(mnemonic, derivationPath ?? "");
362
- return { signer, origin: ss58 };
363
- }
364
97
  function createCID(data, codec = CID_CONFIG.codec, hashCode = CID_CONFIG.hashCode) {
365
98
  let hash;
366
99
  if (hashCode === 45600) hash = blake2b(data, { dkLen: CID_CONFIG.hashLength });
@@ -431,7 +164,7 @@ async function getProvider() {
431
164
  console.log(` Using pool account ${selected.index}: ${selected.address}`);
432
165
  setDeployAttribute("deploy.signer.mode", "pool");
433
166
  setDeployAttribute("deploy.pool.account", truncateAddress(selected.address));
434
- setDeployAttribute("deploy.pool.index", selected.index);
167
+ setDeployAttribute("deploy.pool.index", String(selected.index));
435
168
  return { client, unsafeApi, signer: selected.signer, ss58: selected.address };
436
169
  } catch (e) {
437
170
  client.destroy();
@@ -617,6 +350,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
617
350
  console.log(`
618
351
  Connection lost, reconnecting to Bulletin in ${(delay / 1e3).toFixed(0)}s (${reconnectionsUsed}/${MAX_RECONNECTIONS})...`);
619
352
  captureWarning("WebSocket connection lost, reconnecting", { reconnection: reconnectionsUsed, maxReconnections: MAX_RECONNECTIONS });
353
+ sampleMemory(`reconnect_${reconnectionsUsed}_before`);
620
354
  try {
621
355
  client.destroy();
622
356
  } catch {
@@ -628,6 +362,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
628
362
  signer = fresh.signer;
629
363
  ss58 = fresh.ss58;
630
364
  ownsClient = true;
365
+ sampleMemory(`reconnect_${reconnectionsUsed}_after`);
631
366
  }
632
367
  try {
633
368
  let startNonce = await fetchNonce(BULLETIN_RPC, ss58);
@@ -797,7 +532,7 @@ function chunk(data, size = CHUNK_SIZE) {
797
532
  let offset = 0;
798
533
  while (offset < data.length) {
799
534
  const end = Math.min(offset + size, data.length);
800
- chunks.push(new Uint8Array(data.subarray(offset, end)));
535
+ chunks.push(data.subarray(offset, end));
801
536
  offset = end;
802
537
  }
803
538
  return chunks;
@@ -826,33 +561,74 @@ async function merkleize(directoryPath, outputCarPath) {
826
561
  console.log(` CAR: ${(size / 1024 / 1024).toFixed(2)} MB`);
827
562
  return { carPath: outputCarPath, cid };
828
563
  }
829
- async function storeDirectory(directoryPath, provider = {}, password, jsMerkle) {
564
+ function computeStorageCid(chunks) {
565
+ const hashCode = 18;
566
+ const chunkInfo = chunks.map((c) => ({
567
+ cid: createCID(c, CID_CONFIG.codec, hashCode),
568
+ len: c.length
569
+ }));
570
+ const fileData = new UnixFS({ type: "file", blockSizes: chunkInfo.map((c) => BigInt(c.len)) });
571
+ const dagNode = dagPB.prepare({ Data: fileData.marshal(), Links: chunkInfo.map((c) => ({ Name: "", Tsize: c.len, Hash: c.cid })) });
572
+ const dagBytes = dagPB.encode(dagNode);
573
+ return createCID(dagBytes, 112, hashCode).toString();
574
+ }
575
+ async function storeDirectory(directoryPath, providerOrOptions = {}, password, jsMerkle) {
576
+ const opts = providerOrOptions && ("provider" in providerOrOptions || "onCarReady" in providerOrOptions || "password" in providerOrOptions || "jsMerkle" in providerOrOptions) ? providerOrOptions : { provider: providerOrOptions, password, jsMerkle };
577
+ const provider = opts.provider ?? {};
578
+ password = opts.password;
579
+ jsMerkle = opts.jsMerkle;
830
580
  let carContent;
831
581
  let ipfsCid;
832
582
  const dirBasename = path.basename(directoryPath);
583
+ sampleMemory("storage_start");
833
584
  if (jsMerkle) {
834
585
  const result = await withSpan("deploy.merkleize", "1a. merkleize (js)", { "deploy.directory": dirBasename }, async () => {
835
- return merkleizeJS(directoryPath);
586
+ const r = await merkleizeJS(directoryPath);
587
+ sampleMemory("merkleize_end");
588
+ return r;
836
589
  });
837
590
  carContent = result.carBytes;
838
591
  ipfsCid = result.cid;
839
592
  } else {
840
593
  const carPath = path.join(path.dirname(directoryPath), `${path.basename(directoryPath)}.car`);
841
594
  const { cid } = await withSpan("deploy.merkleize", "1a. merkleize", { "deploy.directory": dirBasename }, async () => {
842
- return merkleize(directoryPath, carPath);
595
+ const r = await merkleize(directoryPath, carPath);
596
+ sampleMemory("merkleize_end");
597
+ return r;
843
598
  });
844
599
  ipfsCid = cid;
845
- carContent = new Uint8Array(fs.readFileSync(carPath));
600
+ carContent = fs.readFileSync(carPath);
846
601
  }
847
602
  if (password) {
848
603
  console.log(` Encrypting CAR file...`);
849
604
  carContent = await encryptContent(carContent, password);
850
605
  console.log(` Encrypted: ${(carContent.length / 1024 / 1024).toFixed(2)} MB`);
851
606
  }
607
+ const dumpPath = process.env.BULLETIN_DEPLOY_DUMP_CAR ?? path.join(path.dirname(directoryPath), `${path.basename(directoryPath)}.bulletin.car`);
608
+ fs.writeFileSync(dumpPath, carContent);
609
+ console.log(` Pre-upload CAR saved to ${dumpPath} (${(carContent.length / 1024 / 1024).toFixed(2)} MB)`);
852
610
  const carChunks = chunk(carContent, CHUNK_SIZE);
853
- const storageCid = await withSpan("deploy.chunk-upload", "1b. chunk-upload", { "deploy.chunks.total": carChunks.length, "deploy.car.bytes": carContent.length }, async () => {
854
- return storeChunkedContent(carChunks, provider);
611
+ const predictedStorageCid = computeStorageCid(carChunks);
612
+ if (opts.onCarReady) await opts.onCarReady(carContent, predictedStorageCid);
613
+ setDeployReportContext({
614
+ jsMerkle: Boolean(jsMerkle),
615
+ chunkCount: carChunks.length,
616
+ carBytes: carContent.length,
617
+ outputDir: path.dirname(directoryPath)
855
618
  });
619
+ const carMb = String(Math.round(carContent.length / 1024 / 1024 * 100) / 100);
620
+ const storageCid = await withSpan("deploy.chunk-upload", "1b. chunk-upload", { "deploy.chunks.total": String(carChunks.length), "deploy.car.bytes": String(carContent.length), "deploy.car.mb": carMb }, async () => {
621
+ sampleMemory("chunk_upload_start");
622
+ const r = await storeChunkedContent(carChunks, provider);
623
+ sampleMemory("chunk_upload_end");
624
+ return r;
625
+ });
626
+ if (storageCid !== predictedStorageCid) {
627
+ captureWarning("computeStorageCid drift vs storeChunkedContent", {
628
+ predicted: predictedStorageCid,
629
+ uploaded: storageCid
630
+ });
631
+ }
856
632
  return { storageCid, ipfsCid, carBytes: carContent };
857
633
  }
858
634
  async function estimateUploadBytes(content) {
@@ -896,7 +672,7 @@ async function deploy(content, domainName = null, options = {}) {
896
672
  }
897
673
  let cid;
898
674
  let ipfsCid;
899
- let mirrorCarBytes;
675
+ let mirrorPromise = Promise.resolve(null);
900
676
  console.log("\n" + "=".repeat(60));
901
677
  console.log(`DEPLOYING TO TESTNET v${VERSION}`);
902
678
  console.log("=".repeat(60));
@@ -997,10 +773,25 @@ async function deploy(content, domainName = null, options = {}) {
997
773
  console.log(`
998
774
  Mode: Directory`);
999
775
  console.log(` Path: ${contentPath}`);
1000
- const dirResult = await storeDirectory(contentPath, providerWithReconnect, options.password, options.jsMerkle);
1001
- cid = dirResult.storageCid;
1002
- ipfsCid = dirResult.ipfsCid;
1003
- mirrorCarBytes = dirResult.carBytes;
776
+ const { storageCid: sCid, ipfsCid: iCid } = await storeDirectory(contentPath, {
777
+ provider: providerWithReconnect,
778
+ password: options.password,
779
+ jsMerkle: options.jsMerkle,
780
+ onCarReady: (carBytes, predictedCid) => {
781
+ if (options.ghPagesMirror) {
782
+ mirrorPromise = mirrorToGitHubPages({
783
+ domain: name,
784
+ carBytes,
785
+ cid: predictedCid,
786
+ toolVersion: VERSION,
787
+ bulletinRpc: options.rpc ?? process.env.BULLETIN_RPC ?? DEFAULT_BULLETIN_RPC,
788
+ encrypted: Boolean(options.password)
789
+ }).catch((err) => err instanceof Error ? err : new Error(String(err)));
790
+ }
791
+ }
792
+ });
793
+ cid = sCid;
794
+ ipfsCid = iCid;
1004
795
  } else {
1005
796
  console.log(`
1006
797
  Mode: File`);
@@ -1064,74 +855,40 @@ async function deploy(content, domainName = null, options = {}) {
1064
855
  });
1065
856
  if (options.ghPagesMirror) {
1066
857
  console.log("\n" + "=".repeat(60));
1067
- console.log("GitHub Pages mirror");
1068
- console.log("=".repeat(60));
1069
- if (!mirrorCarBytes) {
1070
- console.log(" Skipped: --gh-pages-mirror only supports directory deploys (no CAR captured for this content type).");
1071
- } else {
1072
- await withSpan("deploy.gh-pages-mirror", "3b. gh-pages-mirror", { "deploy.domain": name }, async () => {
1073
- try {
1074
- const mirror = await mirrorToGitHubPages({
1075
- domain: name,
1076
- carBytes: mirrorCarBytes,
1077
- cid,
1078
- toolVersion: VERSION,
1079
- bulletinRpc: options.rpc ?? process.env.BULLETIN_RPC ?? DEFAULT_BULLETIN_RPC,
1080
- encrypted: Boolean(options.password)
1081
- });
1082
- console.log(` Mirror: ${mirror.url}`);
1083
- console.log(` Manifest: https://${mirror.owner}.github.io/${mirror.repo}/${mirror.manifestPath}`);
1084
- setDeployAttribute("deploy.gh_pages_url", mirror.url);
1085
- } catch (err) {
1086
- if (err instanceof MirrorSkipped) {
1087
- console.log(` Skipped: ${err.message}`);
1088
- } else {
1089
- const msg = err instanceof Error ? err.message : String(err);
1090
- console.log(` Failed (non-fatal): ${msg}`);
1091
- captureWarning("gh-pages mirror failed", { error: msg.slice(0, 200) });
1092
- }
1093
- }
1094
- });
1095
- }
1096
- }
1097
- if (options.playground) {
1098
- console.log("\n" + "=".repeat(60));
1099
- console.log("Playground Registry");
858
+ console.log("Final checks");
1100
859
  console.log("=".repeat(60));
1101
- await withSpan("deploy.registry", "3. registry", { "deploy.domain": name }, async () => {
1102
- const repoUrl = getGitRemoteUrl();
1103
- const metadata = {};
1104
- if (repoUrl) metadata.repository = repoUrl;
1105
- console.log(`
1106
- Metadata: ${JSON.stringify(metadata)}`);
1107
- const metadataBytes = new Uint8Array(Buffer.from(JSON.stringify(metadata), "utf-8"));
1108
- console.log(` Uploading metadata to Bulletin...`);
1109
- const metadataCid = await storeFile(metadataBytes, provider);
1110
- console.log(` Metadata CID: ${metadataCid}`);
1111
- const { signer, origin } = options.signer && options.signerAddress ? { signer: options.signer, origin: options.signerAddress } : getRegistrySigner(options.mnemonic, options.derivationPath);
1112
- console.log(` Publishing to registry as ${origin}...`);
1113
- const MAX_REGISTRY_RETRIES = 3;
1114
- for (let attempt = 1; attempt <= MAX_REGISTRY_RETRIES; attempt++) {
1115
- const cdm = createCdm(cdm_default, { defaultSigner: signer, defaultOrigin: origin });
1116
- try {
1117
- const registry = cdm.getContract("@example/playground-registry");
1118
- const result = await registry.publish.tx(`${name}.dot`, metadataCid);
1119
- if (!result.ok) throw new Error("Registry publish transaction failed");
1120
- console.log(` Tx: ${result.txHash}`);
1121
- console.log(` Registered ${name}.dot in playground registry!`);
1122
- break;
1123
- } catch (e) {
1124
- if (attempt < MAX_REGISTRY_RETRIES) {
1125
- captureWarning("Registry publish failed, retrying", { attempt, maxRetries: MAX_REGISTRY_RETRIES, error: e.message?.slice(0, 200) });
1126
- console.log(` Attempt ${attempt} failed: ${e.message?.slice(0, 80)}`);
1127
- console.log(` Retrying in 6s...`);
1128
- await new Promise((r) => setTimeout(r, 6e3));
1129
- continue;
1130
- }
1131
- throw e;
1132
- } finally {
1133
- cdm.destroy();
1134
- }
860
+ await withSpan("deploy.gh-pages-mirror", "4. gh-pages-mirror", { "deploy.domain": name }, async () => {
861
+ const mirror = await mirrorPromise;
862
+ if (mirror === null) {
863
+ console.log(" GitHub Pages mirror: skipped (only directory deploys produce a CAR suitable for mirroring).");
864
+ return;
865
+ }
866
+ if (mirror instanceof MirrorSkipped) {
867
+ console.log(` GitHub Pages mirror: skipped \u2014 ${mirror.message}`);
868
+ return;
869
+ }
870
+ if (mirror instanceof Error) {
871
+ console.log(` GitHub Pages mirror: failed (non-fatal) \u2014 ${mirror.message}`);
872
+ captureWarning("gh-pages mirror failed", { error: mirror.message.slice(0, 200) });
873
+ return;
874
+ }
875
+ console.log(` Mirror: ${mirror.url}`);
876
+ console.log(` Manifest: https://${mirror.owner}.github.io/${mirror.repo}/${mirror.manifestPath}`);
877
+ setDeployAttribute("deploy.gh_pages_url", mirror.url);
878
+ process.stdout.write(" Verifying Pages serves this deploy's CAR... ");
879
+ const freshness = await pollMirrorFreshness(mirror.url, cid, { timeoutMs: 3 * 60 * 1e3, intervalMs: 1e4 });
880
+ if (freshness.verified) {
881
+ console.log(`ok (${freshness.attempts} attempt${freshness.attempts === 1 ? "" : "s"}, ${(freshness.durationMs / 1e3).toFixed(0)}s).`);
882
+ } else {
883
+ console.log(`timed out.`);
884
+ console.log(` GitHub Pages last served cid=${freshness.lastCid ?? "n/a"} (expected ${cid}); it should catch up shortly. Non-fatal.`);
885
+ captureWarning("gh-pages mirror freshness poll timed out", {
886
+ url: mirror.url,
887
+ expectedCid: cid,
888
+ lastCid: freshness.lastCid ?? "n/a",
889
+ durationMs: freshness.durationMs,
890
+ attempts: freshness.attempts
891
+ });
1135
892
  }
1136
893
  });
1137
894
  }
@@ -1171,6 +928,7 @@ export {
1171
928
  chunk,
1172
929
  hasIPFS,
1173
930
  merkleize,
931
+ computeStorageCid,
1174
932
  storeDirectory,
1175
933
  estimateUploadBytes,
1176
934
  deploy
@@ -1,10 +1,10 @@
1
- import {
2
- isTestnetSpecName
3
- } from "./chunk-JHNW2EKY.js";
4
1
  import {
5
2
  captureWarning,
6
3
  withSpan
7
- } from "./chunk-LF3XAUCI.js";
4
+ } from "./chunk-Q42TQHNL.js";
5
+ import {
6
+ isTestnetSpecName
7
+ } from "./chunk-JHNW2EKY.js";
8
8
 
9
9
  // src/dotns.ts
10
10
  import crypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-LF3XAUCI.js";
3
+ } from "./chunk-Q42TQHNL.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";