bulletin-deploy 0.13.2-rc.1 → 0.13.2-rc.3

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 (43) hide show
  1. package/bin/bulletin-deploy +71 -22
  2. package/dist/auth-config.js +3 -3
  3. package/dist/bug-report.js +4 -4
  4. package/dist/{chunk-PFVSY3YT.js → chunk-6IQHSWSZ.js} +1 -1
  5. package/dist/{chunk-H3D57D5J.js → chunk-ARFQU4A6.js} +4 -3
  6. package/dist/{chunk-Z6RQSZNM.js → chunk-G3KVQUCG.js} +70 -1
  7. package/dist/{chunk-LLNSFXHF.js → chunk-P2TGLW2B.js} +1 -1
  8. package/dist/{chunk-6FP4PGF5.js → chunk-P5WPSCTS.js} +28 -11
  9. package/dist/{chunk-FH7SSPPT.js → chunk-PWMX6QIS.js} +12 -8
  10. package/dist/{chunk-CHE5D3OO.js → chunk-S4D4ZCQF.js} +3 -3
  11. package/dist/{chunk-NNRJLXFB.js → chunk-SBXZ4GDM.js} +1 -1
  12. package/dist/{chunk-HMJN4XJD.js → chunk-SJEB4RKY.js} +39 -9
  13. package/dist/{chunk-KCZSMYLV.js → chunk-TNFSFFA6.js} +1 -1
  14. package/dist/{chunk-362JEZKM.js → chunk-U2SZ3ZRI.js} +29 -6
  15. package/dist/{chunk-5N5HND6Y.js → chunk-XIVCZUZK.js} +2 -2
  16. package/dist/chunk-probe.js +3 -3
  17. package/dist/commands/login.js +10 -10
  18. package/dist/commands/logout.js +4 -4
  19. package/dist/commands/transfer.js +3 -3
  20. package/dist/commands/whoami.js +3 -3
  21. package/dist/deploy-actors.js +5 -5
  22. package/dist/deploy.d.ts +80 -1
  23. package/dist/deploy.js +24 -12
  24. package/dist/dotns.d.ts +52 -1
  25. package/dist/dotns.js +15 -3
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +17 -15
  28. package/dist/manifest/config-load.d.ts +9 -1
  29. package/dist/manifest/config-load.js +3 -1
  30. package/dist/manifest/publish.d.ts +6 -2
  31. package/dist/manifest/publish.js +11 -11
  32. package/dist/memory-report.js +2 -2
  33. package/dist/merkle.js +10 -10
  34. package/dist/personhood/bootstrap.js +10 -10
  35. package/dist/personhood/people-client.js +3 -3
  36. package/dist/run-state.js +1 -1
  37. package/dist/sss-allowance-cache.js +4 -4
  38. package/dist/storage-signer.js +10 -10
  39. package/dist/telemetry.d.ts +3 -2
  40. package/dist/telemetry.js +4 -2
  41. package/dist/version-check.js +3 -3
  42. package/package.json +4 -3
  43. package/tools/verify-baked-dsn.mjs +85 -0
@@ -6,7 +6,7 @@
6
6
  // SDK modules that trigger the "@parity/product-sdk-logger" localStorage warning.
7
7
  import "../dist/suppress-localstorage-warning.js";
8
8
 
9
- import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY, isConnectionError, isBenignTeardownError, unpublish } from "../dist/deploy.js";
9
+ import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY, isConnectionError, isBenignTeardownError, unpublish, resolveEffectiveMnemonic, resolveEnvId, shouldPublishManifest, validateNoManifestFlags } from "../dist/deploy.js";
10
10
  import { VERSION, setDeployAttribute, captureWarning, closeTelemetry, setRunStateActive, markRelaunchOomHintShown } from "../dist/telemetry.js";
11
11
  import { handleFailedDeploy, handlePreflightVersionCheck, fetchVersionInfo, preReleaseWarning, checkNodeVersion } from "../dist/version-check.js";
12
12
  import { setDeployContext, installLogCapture, buildCliFlagsSummary } from "../dist/bug-report.js";
@@ -56,6 +56,7 @@ for (let i = 0; i < args.length; i++) {
56
56
  else if (args[i] === "--input-car") { flags.inputCar = args[++i]; }
57
57
  else if (args[i] === "--tag") { flags.tag = args[++i]; }
58
58
  else if (args[i] === "--config") { flags.config = args[++i]; }
59
+ else if (args[i] === "--no-manifest" || args[i] === "--content-only") { flags.noManifest = true; }
59
60
  else if (args[i] === "--gh-pages-mirror") { flags.ghPagesMirror = true; }
60
61
  else if (args[i] === "--allow-large-deploy") { flags.allowLargeDeploy = true; }
61
62
  else if (args[i] === "--reproducible") { flags.reproducibleSource = "commit"; }
@@ -78,6 +79,34 @@ if (flags.publish && flags.unpublish) {
78
79
  process.exit(1);
79
80
  }
80
81
 
82
+ // #1163: --no-manifest (--content-only) skips manifest publishing even when a
83
+ // bulletin-deploy.config.* is discoverable; --publish depends on those
84
+ // manifest records (Publisher listing → Browse), so the combination is
85
+ // rejected up front rather than silently publishing a domain with no manifest.
86
+ const noManifestErr = validateNoManifestFlags({ noManifest: !!flags.noManifest, publish: !!flags.publish });
87
+ if (noManifestErr) {
88
+ console.error(noManifestErr);
89
+ process.exit(1);
90
+ }
91
+
92
+ // #1107: the mnemonic actually used to sign — --mnemonic flag > MNEMONIC env
93
+ // var > DOTNS_MNEMONIC env var. Used everywhere deploy/publish/unpublish need
94
+ // a signer identity (existence guards below intentionally keep checking
95
+ // flags.mnemonic / process.env.MNEMONIC directly — only the VALUE forwarded
96
+ // to options.mnemonic changes here).
97
+ const effectiveMnemonic = resolveEffectiveMnemonic({
98
+ flagMnemonic: flags.mnemonic,
99
+ envMnemonic: process.env.MNEMONIC,
100
+ envDotnsMnemonic: process.env.DOTNS_MNEMONIC,
101
+ });
102
+
103
+ // #1165: the env id actually targeted this session — --env flag >
104
+ // BULLETIN_DEPLOY_ENV env var. `undefined` here still means "let
105
+ // deploy()/the DEFAULT_ENV_ID fallback below decide" — this only resolves
106
+ // the flag/env-var precedence so a session default doesn't require
107
+ // repeating --env on every invocation.
108
+ const effectiveEnvId = resolveEnvId({ flagEnv: flags.env, envVar: process.env.BULLETIN_DEPLOY_ENV });
109
+
81
110
  // --environment-file: propagate to the env var so all internal loadEnvironments()
82
111
  // call sites (deploy.ts, manifest/publish.ts, personhood) honor the override.
83
112
  if (flags.environmentFile) {
@@ -119,10 +148,10 @@ if (flags.unpublish) {
119
148
  }
120
149
  try {
121
150
  const result = await unpublish(domain, {
122
- mnemonic: flags.mnemonic,
151
+ mnemonic: effectiveMnemonic,
123
152
  derivationPath: flags.derivationPath,
124
153
  rpc: flags.rpc,
125
- env: flags.env,
154
+ env: effectiveEnvId,
126
155
  });
127
156
  console.log(`Domain: ${result.domainName}`);
128
157
  console.log(`Status: ${result.status}`);
@@ -148,7 +177,7 @@ function installSsoTeardownGuards() {
148
177
  {
149
178
  const sub = positional[0];
150
179
  if (sub === "login" || sub === "logout" || sub === "whoami") {
151
- const envId = flags.env ?? DEFAULT_ENV_ID;
180
+ const envId = effectiveEnvId ?? DEFAULT_ENV_ID;
152
181
  installSsoTeardownGuards();
153
182
  try {
154
183
  const capSub = sub.charAt(0).toUpperCase() + sub.slice(1);
@@ -167,7 +196,7 @@ function installSsoTeardownGuards() {
167
196
  // (or --to). Stand-alone handover + recovery for a deploy whose transfer step
168
197
  // failed. Uses the SSO stack to resolve the session recipient.
169
198
  if (positional[0] === "transfer") {
170
- const envId = flags.env ?? DEFAULT_ENV_ID;
199
+ const envId = effectiveEnvId ?? DEFAULT_ENV_ID;
171
200
  installSsoTeardownGuards();
172
201
  try {
173
202
  const { runTransfer } = await import("../dist/commands/transfer.js");
@@ -237,6 +266,9 @@ Options:
237
266
  --env <id> Target environment from environments.json (default: paseo-next-v2).
238
267
  Drives both the bulletin RPC and the asset-hub RPC used
239
268
  by DotNS. See --list-environments for valid ids.
269
+ Precedence: --env > BULLETIN_DEPLOY_ENV env var > paseo-next-v2.
270
+ Set BULLETIN_DEPLOY_ENV to pick a session default without
271
+ repeating --env on every invocation.
240
272
  --environment-file <path>
241
273
  Path to a JSON file deep-merged over the bundled
242
274
  environments.json. Override only the fields you need
@@ -274,6 +306,11 @@ Options:
274
306
  <build-dir> looking for bulletin-deploy.config.{ts,js,mjs}). When a
275
307
  config is found, deploy ALSO writes the manifest + executable text
276
308
  records on <domain> and its app/widget/worker subnames.
309
+ --no-manifest, --content-only
310
+ Skip manifest publishing entirely, even when a
311
+ bulletin-deploy.config.* is discoverable — plain content
312
+ deploy (storage + root contenthash), same as having no
313
+ config at all. Mutually exclusive with --publish.
277
314
  --gh-pages-mirror After deploy, push the CAR to the current repo's gh-pages branch
278
315
  at bulletin/<domain>.dot.car (opt-in; also set GH_PAGES_MIRROR=1)
279
316
  --publish After deploy, list <domain> in the on-chain Publisher
@@ -445,7 +482,7 @@ try {
445
482
  rpc: effectiveRpc,
446
483
  repo: process.env.GITHUB_REPOSITORY,
447
484
  branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME,
448
- signerMode: flags.mnemonic ? "direct" : "pool",
485
+ signerMode: effectiveMnemonic ? "direct" : "pool",
449
486
  deployTag,
450
487
  cliFlags: buildCliFlagsSummary(flags),
451
488
  ci,
@@ -460,24 +497,30 @@ try {
460
497
  // + its referenced icon/executable files BEFORE any storage or chain work, so
461
498
  // config errors fail fast up front instead of after merkleize + upload. No-op
462
499
  // (returns null) for contenthash-only deploys with no config.
463
- try {
464
- const { preflightProductConfig } = await import("../dist/index.js");
465
- // config-load resolves a relative cwd internally, so no node:path here
466
- // (it's only dynamically imported later, in the publish block).
467
- await preflightProductConfig(
468
- flags.config ? { path: flags.config } : { cwd: buildDir, walkUp: true },
469
- );
470
- } catch (err) {
471
- console.error(`Error: ${err?.message ?? err}`);
472
- process.exit(err instanceof NonRetryableError ? EXIT_CODE_NO_RETRY : 1);
500
+ //
501
+ // #1163: --no-manifest (--content-only) skips this entirely — the deploy is
502
+ // content-only regardless of any discoverable config, so there's nothing to
503
+ // preflight.
504
+ if (!flags.noManifest) {
505
+ try {
506
+ const { preflightProductConfig } = await import("../dist/index.js");
507
+ // config-load resolves a relative cwd internally, so no node:path here
508
+ // (it's only dynamically imported later, in the publish block).
509
+ await preflightProductConfig(
510
+ flags.config ? { path: flags.config } : { cwd: buildDir, walkUp: true },
511
+ );
512
+ } catch (err) {
513
+ console.error(`Error: ${err?.message ?? err}`);
514
+ process.exit(err instanceof NonRetryableError ? EXIT_CODE_NO_RETRY : 1);
515
+ }
473
516
  }
474
517
 
475
518
  const result = await deploy(buildDir, domain, {
476
- mnemonic: flags.mnemonic,
519
+ mnemonic: effectiveMnemonic,
477
520
  derivationPath: flags.derivationPath,
478
521
  suri: flags.suri,
479
522
  rpc: flags.rpc,
480
- env: flags.env,
523
+ env: effectiveEnvId,
481
524
  poolSize: flags.poolSize,
482
525
  password: flags.password,
483
526
  contracts: flags.contracts,
@@ -542,6 +585,12 @@ try {
542
585
  // Walks up from <build-dir> (or honours --config) looking for
543
586
  // bulletin-deploy.config.{ts,js,mjs}. When found, writes the root + per-
544
587
  // executable text records, otherwise the legacy flow returns unchanged.
588
+ //
589
+ // #1163: --no-manifest (--content-only) skips manifest publishing even when
590
+ // a config is present. shouldPublishManifest() is the single source of
591
+ // truth for that decision (config discovery still runs either way, so the
592
+ // "config found but --no-manifest set" case is exercised for real, not just
593
+ // in unit tests).
545
594
  {
546
595
  const { tryLoadProductConfig, publishManifest } = await import("../dist/index.js");
547
596
  const path = await import("node:path");
@@ -549,15 +598,15 @@ try {
549
598
  const loaded = await tryLoadProductConfig(
550
599
  flags.config ? { path: flags.config } : { cwd: buildDirAbs, walkUp: true },
551
600
  );
552
- if (loaded) {
601
+ if (shouldPublishManifest({ configFound: !!loaded, noManifest: !!flags.noManifest })) {
553
602
  try {
554
603
  await publishManifest({
555
604
  loaded,
556
605
  domain,
557
606
  buildDirCid: { absPath: buildDirAbs, cid: result.cid },
558
- env: flags.env,
607
+ env: effectiveEnvId,
559
608
  rpc: flags.rpc,
560
- mnemonic: flags.mnemonic,
609
+ mnemonic: effectiveMnemonic,
561
610
  derivationPath: flags.derivationPath,
562
611
  });
563
612
  } catch (err) {
@@ -565,7 +614,7 @@ try {
565
614
  process.exit(err instanceof NonRetryableError ? EXIT_CODE_NO_RETRY : 1);
566
615
  }
567
616
  }
568
- // No config → the contenthash-only deploy above is the whole job; nothing else to do.
617
+ // No config (or --no-manifest) → the contenthash-only deploy above is the whole job; nothing else to do.
569
618
  }
570
619
 
571
620
  if (!flags.help && !flags.version) {
@@ -9,10 +9,10 @@ import {
9
9
  getPeopleChainEndpoints,
10
10
  hasPersistedSession,
11
11
  resolveBulletinEndpoints
12
- } from "./chunk-KCZSMYLV.js";
12
+ } from "./chunk-TNFSFFA6.js";
13
13
  import "./chunk-TSPERKUS.js";
14
- import "./chunk-6FP4PGF5.js";
15
- import "./chunk-H3D57D5J.js";
14
+ import "./chunk-P5WPSCTS.js";
15
+ import "./chunk-ARFQU4A6.js";
16
16
  import "./chunk-3ZEMX4WD.js";
17
17
  import "./chunk-ZOC4GITL.js";
18
18
  export {
@@ -10,10 +10,10 @@ import {
10
10
  offerBugReport,
11
11
  scrubSecrets,
12
12
  setDeployContext
13
- } from "./chunk-CHE5D3OO.js";
14
- import "./chunk-PFVSY3YT.js";
15
- import "./chunk-6FP4PGF5.js";
16
- import "./chunk-H3D57D5J.js";
13
+ } from "./chunk-S4D4ZCQF.js";
14
+ import "./chunk-6IQHSWSZ.js";
15
+ import "./chunk-P5WPSCTS.js";
16
+ import "./chunk-ARFQU4A6.js";
17
17
  export {
18
18
  buildCliFlagsSummary,
19
19
  buildLabels,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-6FP4PGF5.js";
3
+ } from "./chunk-P5WPSCTS.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";
@@ -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.13.2-rc.1",
9
+ version: "0.13.2-rc.3",
10
10
  private: false,
11
11
  repository: {
12
12
  type: "git",
@@ -48,14 +48,15 @@ var package_default = {
48
48
  "assets",
49
49
  "patches",
50
50
  "DEPLOYMENT.md",
51
- "tools/release-retry-wrapper.mjs"
51
+ "tools/release-retry-wrapper.mjs",
52
+ "tools/verify-baked-dsn.mjs"
52
53
  ],
53
54
  scripts: {
54
55
  build: "tsup src/suppress-localstorage-warning.ts src/index.ts src/deploy.ts src/dotns.ts src/pool.ts src/telemetry.ts src/memory-report.ts src/merkle.ts src/gh-pages-mirror.ts src/version-check.ts src/bug-report.ts src/run-state.ts src/environments.ts src/errors.ts src/manifest.ts src/chunk-probe.ts src/manifest-embed.ts src/manifest-fetch.ts src/manifest-roundtrip.ts src/incremental-stats.ts src/chunker.ts src/personhood/encoding.ts src/personhood/hashing.ts src/personhood/constants.ts src/personhood/member-key.ts src/personhood/people-client.ts src/personhood/proof-validity.ts src/personhood/reprove.ts src/personhood/bind-personal-id.ts src/personhood/claim-pgas.ts src/personhood/bind-paid-alias.ts src/personhood/bootstrap.ts src/personhood/chain-prereqs.ts src/manifest/types.ts src/manifest/schema.ts src/manifest/byte-budget.ts src/manifest/config-load.ts src/manifest/publish.ts src/auth/index.ts src/auth/vendor/index.ts src/auth/vendor/ui/index.ts src/auth-config.ts src/commands/login.ts src/commands/logout.ts src/commands/whoami.ts src/commands/transfer.ts src/storage-signer.ts src/spinner.ts src/sss-allowance.ts src/sss-allowance-cache.ts src/deploy-actors.ts --format esm --dts --clean --target node22",
55
56
  "refresh-environments": "node scripts/refresh-environments.mjs",
56
57
  postinstall: "patch-package || true",
57
58
  prepare: "npm run build",
58
- test: "npm run build && node --test test/test.js test/cli-help.test.js test/helpers/e2e-helpers.test.js test/environments.test.js test/refresh-environments.test.js test/chunk-sharing-report.test.js test/product-manifest.test.js test/cache-savings-totals.test.js test/error-pattern-signature.test.js test/exit-codes.test.js test/probe-env-health.test.js test/e2e-chain-calls.test.js test/auth-config.test.js test/whoami.test.js test/login.test.js test/logout.test.js test/auth-resolve.test.js test/storage-signer.test.js test/spinner.test.js test/sss-allowance.test.js test/sss-allowance-cache.test.js test/dotns-transfer.test.js test/deploy-actors.test.js test/transfer-command.test.js test/dotns-register-fee.test.js test/deploy-label-ordering.test.js test/benign-teardown.test.js && npm run test:vendor",
59
+ test: "npm run build && node --test test/test.js test/cli-help.test.js test/helpers/e2e-helpers.test.js test/environments.test.js test/refresh-environments.test.js test/chunk-sharing-report.test.js test/product-manifest.test.js test/cache-savings-totals.test.js test/error-pattern-signature.test.js test/exit-codes.test.js test/probe-env-health.test.js test/e2e-chain-calls.test.js test/auth-config.test.js test/whoami.test.js test/login.test.js test/logout.test.js test/auth-resolve.test.js test/storage-signer.test.js test/spinner.test.js test/sss-allowance.test.js test/sss-allowance-cache.test.js test/dotns-transfer.test.js test/deploy-actors.test.js test/transfer-command.test.js test/dotns-register-fee.test.js test/deploy-label-ordering.test.js test/benign-teardown.test.js test/verify-baked-dsn.test.js && npm run test:vendor",
59
60
  "test:e2e": "npm run build && node --test test/e2e.test.js",
60
61
  "test:e2e:smoke": "bash scripts/e2e-pass.sh smoke",
61
62
  "test:e2e:pr": "bash scripts/e2e-pass.sh pr",
@@ -11,7 +11,7 @@ import {
11
11
  setDeploySentryTag,
12
12
  truncateAddress,
13
13
  withSpan
14
- } from "./chunk-6FP4PGF5.js";
14
+ } from "./chunk-P5WPSCTS.js";
15
15
  import {
16
16
  validateContractAddresses
17
17
  } from "./chunk-3ZEMX4WD.js";
@@ -63,6 +63,10 @@ var TX_KIND_HASH = "hash";
63
63
  var TX_KIND_NONCE_ADVANCED = "nonce-advanced";
64
64
  var TX_KIND_BEST_BLOCK = "best-block";
65
65
  var ATTR_TX_RESOLUTION_KIND = "deploy.dotns.tx_resolution_kind";
66
+ var TX_KIND_SKIPPED = "skipped";
67
+ function shouldSkipTextWrite(current, target) {
68
+ return current === target;
69
+ }
66
70
  var ONE_PAS = 10000000000n;
67
71
  var FEE_FLOOR_OWNED = ONE_PAS / 100n;
68
72
  var FEE_FLOOR_REGISTER = ONE_PAS / 10n;
@@ -274,6 +278,42 @@ async function verifyNonceAdvanced(endpoints, ss58Address, originalNonce) {
274
278
  }
275
279
  return { advanced: false };
276
280
  }
281
+ var NONCE_CONTENTION_BACKOFF_BASE_MS = 250;
282
+ var NONCE_CONTENTION_BACKOFF_MAX_MS = 2e3;
283
+ function nonceContentionBackoffMs(attempt, rand = Math.random) {
284
+ const base = Math.min(NONCE_CONTENTION_BACKOFF_BASE_MS * 2 ** (attempt - 1), NONCE_CONTENTION_BACKOFF_MAX_MS);
285
+ const jitter = (rand() * 2 - 1) * NONCE_CONTENTION_BACKOFF_BASE_MS;
286
+ return Math.max(0, Math.round(base + jitter));
287
+ }
288
+ var DOTNS_NONCE_CONTENTION_MAX_ATTEMPTS = 5;
289
+ function isNonceContentionAmbiguous(err) {
290
+ const msg = err instanceof Error ? err.message : String(err);
291
+ return msg.startsWith("nonce-advance fallback:");
292
+ }
293
+ async function reacquireNonceOnContention(resubmit, nonceFallback, label, opts = {}) {
294
+ const fetchNonceFn = opts.fetchNonce ?? fetchNonce;
295
+ const sleepFn = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
296
+ const backoffFn = opts.backoffMs ?? nonceContentionBackoffMs;
297
+ const maxAttempts = opts.maxAttempts ?? DOTNS_NONCE_CONTENTION_MAX_ATTEMPTS;
298
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
299
+ setDeployAttribute("deploy.dotns.nonce_contention_retries", attempt);
300
+ const ms = backoffFn(attempt);
301
+ console.log(` ${label}: nonce contention (attempt ${attempt}/${maxAttempts}) \u2014 re-acquiring nonce and resubmitting in ${ms}ms\u2026`);
302
+ await sleepFn(ms);
303
+ try {
304
+ nonceFallback.expectedNonce = await fetchNonceFn(nonceFallback.rpcs, nonceFallback.senderSS58);
305
+ } catch {
306
+ }
307
+ try {
308
+ return await resubmit();
309
+ } catch (e) {
310
+ if (!isNonceContentionAmbiguous(e)) throw e;
311
+ }
312
+ }
313
+ throw new NonRetryableError(
314
+ `DotNS write could not acquire a nonce slot after ${maxAttempts} attempts \u2014 the shared signer (${nonceFallback.senderSS58}) is under nonce contention; pass your own --mnemonic for an isolated nonce space.`
315
+ );
316
+ }
277
317
  var ProofOfPersonhoodStatus = {
278
318
  NoStatus: 0,
279
319
  ProofOfPersonhoodLite: 1,
@@ -875,6 +915,19 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
875
915
  filter.flush();
876
916
  throw fastFail;
877
917
  }
918
+ if (opts.nonceFallback && isNonceContentionAmbiguous(e)) {
919
+ try {
920
+ return await reacquireNonceOnContention(
921
+ () => this.signAndSubmitExtrinsic(buildExtrinsic(), signer, filter.callback, opts),
922
+ opts.nonceFallback,
923
+ label,
924
+ { fetchNonce: opts.fetchNonce, sleep: opts.sleep, backoffMs: opts.nonceContentionBackoffMs }
925
+ );
926
+ } catch (contentionErr) {
927
+ filter.flush();
928
+ throw contentionErr;
929
+ }
930
+ }
878
931
  const decision = classifyTxRetryDecision(e);
879
932
  const msg = e?.message ?? String(e);
880
933
  if (!shouldRetryTxAttempt(attempt, DOTNS_TX_MAX_ATTEMPTS, decision)) {
@@ -1982,6 +2035,16 @@ var DotNS = class {
1982
2035
  this.ensureConnected();
1983
2036
  console.log(` Setting text[${key}]: ${value}`);
1984
2037
  const node = namehash(`${domainName}.dot`);
2038
+ try {
2039
+ const current = await this.getTextRecord(domainName, key);
2040
+ if (shouldSkipTextWrite(current, value)) {
2041
+ console.log(` text[${key}] already set \u2014 skipping tx`);
2042
+ setDeployAttribute("deploy.dotns.text_unchanged", "true");
2043
+ return { value, txHash: TX_KIND_SKIPPED };
2044
+ }
2045
+ } catch (_) {
2046
+ }
2047
+ setDeployAttribute("deploy.dotns.text_unchanged", "false");
1985
2048
  const MAX_VERIFY_CHAIN_SECONDS = VERIFY_EFFECT_CHAIN_SECONDS;
1986
2049
  const TEXT_POLL_INTERVAL_MS = 2e3;
1987
2050
  const verifyEffect = async () => {
@@ -2899,6 +2962,8 @@ export {
2899
2962
  TX_KIND_NONCE_ADVANCED,
2900
2963
  TX_KIND_BEST_BLOCK,
2901
2964
  ATTR_TX_RESOLUTION_KIND,
2965
+ TX_KIND_SKIPPED,
2966
+ shouldSkipTextWrite,
2902
2967
  MINIMUM_REGISTER_STORAGE_DEPOSIT,
2903
2968
  registerDepositWei,
2904
2969
  bufferedWeiToNative,
@@ -2936,6 +3001,10 @@ export {
2936
3001
  DEFAULT_MNEMONIC,
2937
3002
  fetchNonce,
2938
3003
  verifyNonceAdvanced,
3004
+ nonceContentionBackoffMs,
3005
+ DOTNS_NONCE_CONTENTION_MAX_ATTEMPTS,
3006
+ isNonceContentionAmbiguous,
3007
+ reacquireNonceOnContention,
2939
3008
  ProofOfPersonhoodStatus,
2940
3009
  PUBLISHER_ABI,
2941
3010
  PublisherNotSupportedError,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  captureWarning
3
- } from "./chunk-6FP4PGF5.js";
3
+ } from "./chunk-P5WPSCTS.js";
4
4
 
5
5
  // src/chunk-probe.ts
6
6
  import { Twox128, Blake2128Concat, decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  package_default,
3
3
  writeRunState
4
- } from "./chunk-H3D57D5J.js";
4
+ } from "./chunk-ARFQU4A6.js";
5
5
 
6
6
  // src/memory-report.ts
7
7
  import * as fs2 from "fs";
@@ -134,7 +134,7 @@ function initTelemetry() {
134
134
  if (process.env.BULLETIN_DEPLOY_USE_AMBIENT_SENTRY === "1") {
135
135
  return;
136
136
  }
137
- const dsn = process.env.SENTRY_DSN || (true ? "" : "");
137
+ const dsn = process.env.SENTRY_DSN || (true ? "https://e021c025d79c4c3ade2862a11f13c40b@o4511059872841728.ingest.de.sentry.io/4511093597405264" : "");
138
138
  if (!dsn) return;
139
139
  Sentry.init({
140
140
  dsn,
@@ -334,13 +334,13 @@ function computeDeployOutcome(errorCategory, isSad, sadReason) {
334
334
  return "clean";
335
335
  }
336
336
  var ERROR_KIND_RULES = [
337
- [/Contract reverted|Contract execution would revert|revert(?:ed|ing)?\s*\(flags=[0-9]+\)/i, "contract-revert"],
337
+ [/Contract reverted|Contract execution would revert|revert(?:ed|ing)?\s*\(flags=[0-9]+\)|"type"\s*:\s*"ContractReverted"/i, "contract-revert"],
338
338
  [/timed out after \d+s waiting for block|Transaction not included after \d+s|Transaction did not settle within/i, "chain-timeout"],
339
339
  [/\bstale\b.*nonce|nonce.*\bstale\b|"type"\s*:\s*"(?:Future|Stale)"|Invalid::Future|tx rejected by pool/i, "nonce-stale"],
340
340
  [/heartbeat timeout|WS halt|Unable to connect|ChainHead disjointed|websocket.*closed|socket closed|disconnect/i, "connection"],
341
341
  [/requires ProofOfPersonhood(?:Full|Lite|Light),\s*but this signer is NoStatus/i, "naming.pop_required"],
342
342
  [/requires NoStatus,\s*but this signer is ProofOfPersonhood/i, "naming.nostatus_required"],
343
- [/Cannot decode zero data.*with ABI parameters/i, "naming.contract_unavailable"],
343
+ [/Cannot decode zero data.*with ABI parameters/i, "dotns.abi_decode_empty"],
344
344
  // Issue #1060: contractCall's own empty-`0x`-data guard (src/dotns.ts, #729) throws
345
345
  // an actionable wrapper instead of letting the raw viem message above reach a
346
346
  // caller. Same failure family (a DotNS contract read came back empty) — classify
@@ -368,6 +368,13 @@ function classifyErrorKind(msg) {
368
368
  }
369
369
  return "unknown";
370
370
  }
371
+ function classifyErrorForSpan(msg) {
372
+ return {
373
+ kind: classifyErrorKind(msg),
374
+ message: sanitizeErrorMessage(msg),
375
+ pattern: analyseErrorPattern(msg)
376
+ };
377
+ }
371
378
  function sanitizeErrorMessage(msg) {
372
379
  return scrubPaths(msg.slice(0, 500));
373
380
  }
@@ -398,10 +405,11 @@ async function withSpan(op, description, attributes, fn) {
398
405
  return await fn();
399
406
  } catch (error) {
400
407
  const msg = error.message ?? String(error);
408
+ const { kind, message, pattern } = classifyErrorForSpan(msg);
401
409
  span.setAttribute("error.message", msg);
402
- span.setAttribute("deploy.error_kind", classifyErrorKind(msg));
403
- span.setAttribute("deploy.error_message", sanitizeErrorMessage(msg));
404
- span.setAttribute("deploy.error_pattern_signature", analyseErrorPattern(msg));
410
+ span.setAttribute("deploy.error_kind", kind);
411
+ span.setAttribute("deploy.error_message", message);
412
+ span.setAttribute("deploy.error_pattern_signature", pattern);
405
413
  span.setStatus({ code: 2, message: "internal_error" });
406
414
  throw error;
407
415
  }
@@ -496,12 +504,9 @@ async function withDeploySpan(domain, fn) {
496
504
  } catch (error) {
497
505
  const msg = error.message ?? String(error);
498
506
  span.setAttribute("deploy.status", "error");
499
- span.setAttribute("deploy.error", msg.slice(0, 500));
507
+ setDeployErrorOnSpan(span, msg);
500
508
  const errorCategory = classifyDeployError(msg);
501
509
  span.setAttribute("deploy.error_category", errorCategory);
502
- span.setAttribute("deploy.error_kind", classifyErrorKind(msg));
503
- span.setAttribute("deploy.error_message", sanitizeErrorMessage(msg));
504
- span.setAttribute("deploy.error_pattern_signature", analyseErrorPattern(msg));
505
510
  currentErrorCategory = errorCategory;
506
511
  const isExpected = isExpectedError(msg);
507
512
  span.setAttribute("deploy.expected", isExpected ? "true" : "false");
@@ -578,6 +583,17 @@ function setDeployAttribute(key, value) {
578
583
  if (!deployRootSpan) return;
579
584
  deployRootSpan.setAttribute(key, value);
580
585
  }
586
+ function setDeployErrorOnSpan(span, msg) {
587
+ const { kind, message, pattern } = classifyErrorForSpan(msg);
588
+ span.setAttribute("deploy.error", msg.slice(0, 500));
589
+ span.setAttribute("deploy.error_kind", kind);
590
+ span.setAttribute("deploy.error_message", message);
591
+ span.setAttribute("deploy.error_pattern_signature", pattern);
592
+ }
593
+ function setDeployError(msg) {
594
+ if (!deployRootSpan) return;
595
+ setDeployErrorOnSpan(deployRootSpan, msg);
596
+ }
581
597
  function __setDeployRootSpanForTest(span) {
582
598
  deployRootSpan = span;
583
599
  }
@@ -774,6 +790,7 @@ export {
774
790
  withDeploySpan,
775
791
  setDeployReportContext,
776
792
  setDeployAttribute,
793
+ setDeployError,
777
794
  __setDeployRootSpanForTest,
778
795
  __setSentryForTest,
779
796
  getCurrentSentryTraceId,
@@ -4,14 +4,17 @@ import {
4
4
  import {
5
5
  BLAKE2B_256_MULTIHASH_CODE,
6
6
  encodeContenthash,
7
+ resolveBulletinEndpoints,
7
8
  resolveDotnsConnectOptions,
9
+ setBulletinEndpoints,
8
10
  storeDirectory,
9
11
  storeFile
10
- } from "./chunk-HMJN4XJD.js";
12
+ } from "./chunk-SJEB4RKY.js";
11
13
  import {
12
14
  DotNS
13
- } from "./chunk-Z6RQSZNM.js";
15
+ } from "./chunk-G3KVQUCG.js";
14
16
  import {
17
+ DEFAULT_ENV_ID,
15
18
  getPopSelfServeConfig,
16
19
  loadEnvironments,
17
20
  resolveEndpoints
@@ -38,6 +41,11 @@ async function publishManifest(opts) {
38
41
  );
39
42
  }
40
43
  const configDir = path.dirname(sourcePath);
44
+ const envId = opts.env ?? DEFAULT_ENV_ID;
45
+ const { doc } = await loadEnvironments();
46
+ const resolved = resolveEndpoints(doc, envId);
47
+ const popSelfServe = getPopSelfServeConfig(doc, envId);
48
+ setBulletinEndpoints(resolveBulletinEndpoints(resolved.bulletin, opts.rpc));
41
49
  const iconAbs = path.resolve(configDir, config.icon.path);
42
50
  const iconBytes = await readFileOrThrow(iconAbs, "icon");
43
51
  console.log(`
@@ -59,7 +67,7 @@ Manifest publish \u2014 ${config.domain}`);
59
67
  console.log(` Executable [${exec.kind}] CID: ${storageCid}`);
60
68
  executableCids[exec.kind] = storageCid;
61
69
  }
62
- const dotns = await connectDotNS(opts);
70
+ const dotns = await connectDotNS(opts, resolved, popSelfServe, envId);
63
71
  try {
64
72
  const baseLabel = stripDotSuffix(config.domain);
65
73
  await dotns.ensureContentResolver(baseLabel);
@@ -104,11 +112,7 @@ async function readFileOrThrow(p, label) {
104
112
  throw new NonRetryableError(`Cannot read ${label} at ${p}: ${err.message}`);
105
113
  }
106
114
  }
107
- async function connectDotNS(opts) {
108
- const envId = opts.env ?? "paseo-next-v2";
109
- const { doc } = await loadEnvironments();
110
- const resolved = resolveEndpoints(doc, envId);
111
- const popSelfServe = getPopSelfServeConfig(doc, envId);
115
+ async function connectDotNS(opts, resolved, popSelfServe, envId) {
112
116
  const deployOptsShim = {
113
117
  mnemonic: opts.mnemonic,
114
118
  derivationPath: opts.derivationPath
@@ -2,15 +2,15 @@ import {
2
2
  classifyErrorArea,
3
3
  isInteractive,
4
4
  promptYesNo
5
- } from "./chunk-PFVSY3YT.js";
5
+ } from "./chunk-6IQHSWSZ.js";
6
6
  import {
7
7
  VERSION,
8
8
  getCurrentSentryTraceId,
9
9
  resolveIssueRepoSlug
10
- } from "./chunk-6FP4PGF5.js";
10
+ } from "./chunk-P5WPSCTS.js";
11
11
  import {
12
12
  package_default
13
- } from "./chunk-H3D57D5J.js";
13
+ } from "./chunk-ARFQU4A6.js";
14
14
 
15
15
  // src/bug-report.ts
16
16
  import { execSync, execFileSync } from "child_process";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GRPLHUYC.js";
4
4
  import {
5
5
  DOT_DAPP_ID
6
- } from "./chunk-KCZSMYLV.js";
6
+ } from "./chunk-TNFSFFA6.js";
7
7
 
8
8
  // src/sss-allowance-cache.ts
9
9
  import { mkdir, readFile, writeFile, unlink } from "fs/promises";