bulletin-deploy 0.6.3 → 0.6.4

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.
@@ -6,7 +6,7 @@ import * as path from "path";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "bulletin-deploy",
9
- version: "0.6.3",
9
+ version: "0.6.4",
10
10
  private: false,
11
11
  repository: {
12
12
  type: "git",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  captureWarning,
3
3
  withSpan
4
- } from "./chunk-ECOC6TV4.js";
4
+ } from "./chunk-3J3S6JLX.js";
5
5
 
6
6
  // src/dotns.ts
7
7
  import crypto from "crypto";
@@ -51,13 +51,18 @@ var _rpcIdCounter = 0;
51
51
  async function fetchNonce(rpc, ss58Address) {
52
52
  const WS = globalThis.WebSocket ?? (await import("./wrapper-IFSKR7DG.js")).default;
53
53
  return new Promise((resolve, reject) => {
54
- const timeout = setTimeout(() => {
54
+ let done = false;
55
+ const settle = (fn, ...args) => {
56
+ if (done) return;
57
+ done = true;
58
+ clearTimeout(timer);
55
59
  try {
56
60
  ws.close();
57
61
  } catch {
58
62
  }
59
- reject(new Error(`fetchNonce timed out after 10s for ${rpc}`));
60
- }, 1e4);
63
+ fn(...args);
64
+ };
65
+ const timer = setTimeout(() => settle(reject, new Error(`fetchNonce timed out after 8s for ${rpc}`)), 8e3);
61
66
  const ws = new WS(rpc);
62
67
  const id = ++_rpcIdCounter;
63
68
  ws.onopen = () => ws.send(JSON.stringify({ jsonrpc: "2.0", id, method: "system_accountNextIndex", params: [ss58Address] }));
@@ -65,16 +70,11 @@ async function fetchNonce(rpc, ss58Address) {
65
70
  const d = typeof e.data === "string" ? e.data : e.data.toString();
66
71
  const r = JSON.parse(d);
67
72
  if (r.id === id) {
68
- clearTimeout(timeout);
69
- ws.close();
70
- r.error ? reject(new Error(r.error.message)) : resolve(r.result);
73
+ r.error ? settle(reject, new Error(r.error.message)) : settle(resolve, r.result);
71
74
  }
72
75
  };
73
- ws.onerror = () => {
74
- clearTimeout(timeout);
75
- ws.close();
76
- reject(new Error(`WebSocket to ${rpc} failed`));
77
- };
76
+ ws.onerror = () => settle(reject, new Error(`WebSocket to ${rpc} failed`));
77
+ ws.onclose = () => settle(reject, new Error(`WebSocket to ${rpc} closed before response`));
78
78
  });
79
79
  }
80
80
  var ProofOfPersonhoodStatus = {
@@ -4,7 +4,7 @@ import {
4
4
  TX_TIMEOUT_MS,
5
5
  fetchNonce,
6
6
  validateDomainLabel
7
- } from "./chunk-TITCVJJF.js";
7
+ } from "./chunk-MXMJJURQ.js";
8
8
  import {
9
9
  derivePoolAccounts,
10
10
  ensureAuthorized,
@@ -18,7 +18,7 @@ import {
18
18
  setDeployAttribute,
19
19
  withDeploySpan,
20
20
  withSpan
21
- } from "./chunk-ECOC6TV4.js";
21
+ } from "./chunk-3J3S6JLX.js";
22
22
 
23
23
  // src/deploy.ts
24
24
  import { Buffer } from "buffer";
@@ -280,8 +280,10 @@ var BULLETIN_RPC = DEFAULT_BULLETIN_RPC;
280
280
  var POOL_SIZE = DEFAULT_POOL_SIZE;
281
281
  var CHUNK_SIZE = 1 * 1024 * 1024;
282
282
  var MAX_FILE_SIZE = 8 * 1024 * 1024;
283
- var MAX_RECONNECTIONS = parseInt(process.env.BULLETIN_MAX_RECONNECTIONS ?? "1", 10);
283
+ var MAX_RECONNECTIONS = parseInt(process.env.BULLETIN_MAX_RECONNECTIONS ?? "3", 10);
284
284
  var CHUNK_TIMEOUT_MS = 3e4;
285
+ var RETRY_BASE_DELAY_MS = 2e3;
286
+ var RETRY_MAX_DELAY_MS = 15e3;
285
287
  function isConnectionError(error) {
286
288
  const msg = error?.message || String(error);
287
289
  return /heartbeat timeout|WS halt|Unable to connect/i.test(msg);
@@ -362,7 +364,7 @@ function toHashingEnum(mhCode) {
362
364
  }
363
365
  async function getProvider() {
364
366
  console.log(` Connecting to Bulletin: ${BULLETIN_RPC}`);
365
- const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider(BULLETIN_RPC)));
367
+ const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider([BULLETIN_RPC])));
366
368
  const unsafeApi = client.getUnsafeApi();
367
369
  try {
368
370
  await cryptoWaitReady();
@@ -390,7 +392,7 @@ async function getProvider() {
390
392
  }
391
393
  async function getDirectProvider(mnemonic) {
392
394
  console.log(` Connecting to Bulletin: ${BULLETIN_RPC}`);
393
- const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider(BULLETIN_RPC)));
395
+ const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider([BULLETIN_RPC])));
394
396
  const unsafeApi = client.getUnsafeApi();
395
397
  const { signer, ss58 } = deriveRootSigner(mnemonic);
396
398
  console.log(` Using direct signer: ${ss58}`);
@@ -408,39 +410,46 @@ async function getDirectProvider(mnemonic) {
408
410
  setDeployAttribute("deploy.signer.address", ss58);
409
411
  return { client, unsafeApi, signer, ss58 };
410
412
  }
413
+ var MAX_BEST_CHAIN_DROPS = 5;
411
414
  function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction", rpc, senderSS58, expectedNonce, timeoutMs } = {}) {
412
415
  const timeout = timeoutMs ?? TX_TIMEOUT_MS;
413
416
  return new Promise((resolve2, reject) => {
414
417
  let settled = false;
418
+ let sub;
419
+ let dropCount = 0;
415
420
  const settle = (fn) => (...args) => {
416
- if (!settled) {
417
- settled = true;
418
- clearTimeout(timer);
419
- try {
420
- sub.unsubscribe();
421
- } catch {
421
+ if (settled) return;
422
+ settled = true;
423
+ clearTimeout(timer);
424
+ try {
425
+ sub?.unsubscribe();
426
+ } catch {
427
+ }
428
+ fn(...args);
429
+ };
430
+ const tryNonceFallback = async () => {
431
+ if (!rpc || !senderSS58 || expectedNonce == null) return false;
432
+ try {
433
+ const currentNonce = await fetchNonce(rpc, senderSS58);
434
+ if (settled) return true;
435
+ if (currentNonce > expectedNonce) {
436
+ console.log(` ${label}: nonce advanced (${expectedNonce} -> ${currentNonce}), tx was included`);
437
+ settle(resolve2)({ value: onSuccess(), viaFallback: true });
438
+ return true;
422
439
  }
423
- fn(...args);
440
+ } catch (e) {
441
+ if (settled) return true;
442
+ console.log(` ${label}: nonce fallback failed: ${e.message?.slice(0, 80)}`);
424
443
  }
444
+ return false;
425
445
  };
426
446
  const timer = setTimeout(async () => {
427
447
  if (settled) return;
428
- if (rpc && senderSS58 && expectedNonce != null) {
429
- try {
430
- const currentNonce = await fetchNonce(rpc, senderSS58);
431
- if (currentNonce > expectedNonce) {
432
- console.log(` ${label}: subscription timed out but nonce advanced (${expectedNonce} -> ${currentNonce}), tx was included`);
433
- settle(resolve2)({ value: onSuccess(), viaFallback: true });
434
- return;
435
- }
436
- } catch (e) {
437
- console.log(` ${label}: nonce check failed: ${e.message}`);
438
- }
439
- }
448
+ if (await tryNonceFallback()) return;
440
449
  settle(reject)(new Error(`${label} timed out after ${timeout / 1e3}s waiting for block confirmation`));
441
450
  }, timeout);
442
- const sub = tx.signSubmitAndWatch(signer, txOpts).subscribe({
443
- next: (event) => {
451
+ sub = tx.signSubmitAndWatch(signer, txOpts).subscribe({
452
+ next: async (event) => {
444
453
  if (event.type === "txBestBlocksState") {
445
454
  if (event.found) {
446
455
  if (event.ok) {
@@ -449,7 +458,14 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
449
458
  settle(reject)(new Error(`${label} dispatch error`));
450
459
  }
451
460
  } else {
452
- console.log(` ${label}: tx dropped from best chain, waiting for re-inclusion...`);
461
+ dropCount++;
462
+ if (dropCount >= MAX_BEST_CHAIN_DROPS) {
463
+ console.log(` ${label}: tx dropped ${dropCount} times, checking nonce...`);
464
+ if (await tryNonceFallback()) return;
465
+ settle(reject)(new Error(`${label} tx dropped from best chain ${dropCount} times`));
466
+ } else {
467
+ console.log(` ${label}: tx dropped from best chain (${dropCount}/${MAX_BEST_CHAIN_DROPS}), waiting...`);
468
+ }
453
469
  }
454
470
  }
455
471
  },
@@ -531,13 +547,15 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
531
547
  throw new Error(`Connection lost and max reconnections (${MAX_RECONNECTIONS}) exhausted`);
532
548
  }
533
549
  reconnectionsUsed++;
550
+ const delay = Math.min(RETRY_BASE_DELAY_MS * Math.pow(2, reconnectionsUsed - 1), RETRY_MAX_DELAY_MS);
534
551
  console.log(`
535
- Connection lost, reconnecting to Bulletin (${reconnectionsUsed}/${MAX_RECONNECTIONS})...`);
552
+ Connection lost, reconnecting to Bulletin in ${(delay / 1e3).toFixed(0)}s (${reconnectionsUsed}/${MAX_RECONNECTIONS})...`);
536
553
  captureWarning("WebSocket connection lost, reconnecting", { reconnection: reconnectionsUsed, maxReconnections: MAX_RECONNECTIONS });
537
554
  try {
538
555
  client.destroy();
539
556
  } catch {
540
557
  }
558
+ await new Promise((r) => setTimeout(r, delay));
541
559
  const fresh = await reconnect();
542
560
  client = fresh.client;
543
561
  unsafeApi = fresh.unsafeApi;
@@ -589,10 +607,19 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
589
607
  captureWarning("Chunk upload failed, retrying", { chunkIndex: fail.index + 1, maxRetries: MAX_CHUNK_RETRIES, error: fail.error?.message?.slice(0, 200) });
590
608
  let retried = false;
591
609
  for (let attempt = 1; attempt <= MAX_CHUNK_RETRIES; attempt++) {
592
- console.log(` Retrying chunk ${fail.index + 1} (attempt ${attempt}/${MAX_CHUNK_RETRIES})...`);
593
- await new Promise((r) => setTimeout(r, 6e3));
594
- const freshNonce = await fetchNonce(BULLETIN_RPC, ss58);
610
+ const retryDelay = Math.min(RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1), RETRY_MAX_DELAY_MS);
611
+ console.log(` Retrying chunk ${fail.index + 1} (attempt ${attempt}/${MAX_CHUNK_RETRIES}) in ${(retryDelay / 1e3).toFixed(0)}s...`);
612
+ await new Promise((r) => setTimeout(r, retryDelay));
613
+ if (isConnectionError(fail.error) && reconnect && reconnectionsUsed < MAX_RECONNECTIONS) {
614
+ try {
615
+ await doReconnect();
616
+ } catch (reconnectErr) {
617
+ console.log(` Reconnect failed: ${reconnectErr.message?.slice(0, 80)}`);
618
+ break;
619
+ }
620
+ }
595
621
  try {
622
+ const freshNonce = await fetchNonce(BULLETIN_RPC, ss58);
596
623
  const result2 = await storeChunk(unsafeApi, signer, fail.chunkData, freshNonce, ss58);
597
624
  stored[fail.index] = result2;
598
625
  retried = true;
@@ -600,6 +627,12 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
600
627
  } catch (e) {
601
628
  captureWarning("Chunk retry failed", { chunkIndex: fail.index + 1, attempt, maxRetries: MAX_CHUNK_RETRIES, error: e.message?.slice(0, 200) });
602
629
  console.log(` Retry ${attempt} failed: ${e.message?.slice(0, 80)}`);
630
+ if (isConnectionError(e) && reconnect && reconnectionsUsed < MAX_RECONNECTIONS) {
631
+ try {
632
+ await doReconnect();
633
+ } catch {
634
+ }
635
+ }
603
636
  }
604
637
  }
605
638
  if (!retried) {
package/dist/deploy.js CHANGED
@@ -19,10 +19,10 @@ import {
19
19
  storeChunkedContent,
20
20
  storeDirectory,
21
21
  storeFile
22
- } from "./chunk-IPTNRVHY.js";
23
- import "./chunk-TITCVJJF.js";
22
+ } from "./chunk-YNBJATGK.js";
23
+ import "./chunk-MXMJJURQ.js";
24
24
  import "./chunk-AIHW2WLO.js";
25
- import "./chunk-ECOC6TV4.js";
25
+ import "./chunk-3J3S6JLX.js";
26
26
  import "./chunk-QGM4M3NI.js";
27
27
  export {
28
28
  DEFAULT_BULLETIN_RPC,
package/dist/dotns.js CHANGED
@@ -19,8 +19,8 @@ import {
19
19
  sanitizeDomainLabel,
20
20
  stripTrailingDigits,
21
21
  validateDomainLabel
22
- } from "./chunk-TITCVJJF.js";
23
- import "./chunk-ECOC6TV4.js";
22
+ } from "./chunk-MXMJJURQ.js";
23
+ import "./chunk-3J3S6JLX.js";
24
24
  import "./chunk-QGM4M3NI.js";
25
25
  export {
26
26
  CONNECTION_TIMEOUT_MS,
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  deploy
3
- } from "./chunk-IPTNRVHY.js";
3
+ } from "./chunk-YNBJATGK.js";
4
4
  import {
5
5
  DotNS
6
- } from "./chunk-TITCVJJF.js";
6
+ } from "./chunk-MXMJJURQ.js";
7
7
  import {
8
8
  bootstrapPool,
9
9
  derivePoolAccounts,
@@ -11,7 +11,7 @@ import {
11
11
  fetchPoolAuthorizations,
12
12
  selectAccount
13
13
  } from "./chunk-AIHW2WLO.js";
14
- import "./chunk-ECOC6TV4.js";
14
+ import "./chunk-3J3S6JLX.js";
15
15
  import "./chunk-QGM4M3NI.js";
16
16
  export {
17
17
  DotNS,
package/dist/telemetry.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  setDeployAttribute,
9
9
  withDeploySpan,
10
10
  withSpan
11
- } from "./chunk-ECOC6TV4.js";
11
+ } from "./chunk-3J3S6JLX.js";
12
12
  import "./chunk-QGM4M3NI.js";
13
13
  export {
14
14
  VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulletin-deploy",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",