bulletin-deploy 0.19.3 → 0.19.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.
Files changed (69) hide show
  1. package/assets/environments.json +5 -5
  2. package/bin/bulletin-deploy +10 -1
  3. package/dist/auth/index.js +7 -7
  4. package/dist/auth/vendor/index.js +2 -2
  5. package/dist/auth-config.js +5 -5
  6. package/dist/bug-report.js +5 -4
  7. package/dist/chain-health.js +4 -3
  8. package/dist/{chunk-PSYA6E5M.js → chunk-2P4EZOFF.js} +1 -1
  9. package/dist/{chunk-NA3SXVY6.js → chunk-3NR3T7US.js} +22 -12
  10. package/dist/{chunk-6FQJLLEF.js → chunk-6J4WRFRL.js} +1 -1
  11. package/dist/{chunk-LPTKBQII.js → chunk-7DDOVCV5.js} +1 -1
  12. package/dist/{chunk-I5PNPQZO.js → chunk-ANX3RZAS.js} +38 -10
  13. package/dist/{chunk-IP6ABIIC.js → chunk-BFK5UCUJ.js} +29 -4
  14. package/dist/{chunk-H4OZ6PTA.js → chunk-C437RTTU.js} +8 -8
  15. package/dist/{chunk-VBIFKNUS.js → chunk-CCIBF55X.js} +1 -1
  16. package/dist/{chunk-IW3X2MJF.js → chunk-D3YME2EG.js} +30 -1
  17. package/dist/{chunk-6MSHWNJT.js → chunk-D6BE3RAU.js} +138 -54
  18. package/dist/{chunk-3MAIF2SF.js → chunk-F24S4EV5.js} +49 -7
  19. package/dist/{chunk-WPC2PUCV.js → chunk-H44LXDQJ.js} +43 -8
  20. package/dist/{chunk-5FLTDWWP.js → chunk-H44NO63C.js} +1 -1
  21. package/dist/{chunk-3ACNAWUD.js → chunk-JTH3ISLR.js} +3 -3
  22. package/dist/{chunk-NKDGBZBP.js → chunk-O56KLG7Z.js} +2 -2
  23. package/dist/{chunk-KTKFHROI.js → chunk-OONQPYHM.js} +4 -4
  24. package/dist/{chunk-SNFGJOR2.js → chunk-R3BXRRKE.js} +1 -1
  25. package/dist/{chunk-ZOC4GITL.js → chunk-SH2LG52Z.js} +5 -1
  26. package/dist/{chunk-ABMNC34K.js → chunk-U2QO5G35.js} +2 -2
  27. package/dist/{chunk-QU5GJF6U.js → chunk-UFP2XWZ4.js} +3 -3
  28. package/dist/chunk-ZG3HAH5A.js +18 -0
  29. package/dist/{chunk-RJDHC56V.js → chunk-ZZIOS4RG.js} +53 -16
  30. package/dist/chunk-probe.js +4 -3
  31. package/dist/commands/login.js +24 -23
  32. package/dist/commands/logout.js +7 -7
  33. package/dist/commands/transfer.js +7 -6
  34. package/dist/commands/whoami.js +5 -5
  35. package/dist/deploy-actors.js +10 -9
  36. package/dist/deploy.d.ts +45 -18
  37. package/dist/deploy.js +30 -20
  38. package/dist/dotns.d.ts +56 -2
  39. package/dist/dotns.js +9 -6
  40. package/dist/environments.d.ts +7 -1
  41. package/dist/environments.js +2 -2
  42. package/dist/errors.d.ts +5 -1
  43. package/dist/errors.js +7 -3
  44. package/dist/incremental-stats.d.ts +14 -0
  45. package/dist/incremental-stats.js +1 -1
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.js +23 -22
  48. package/dist/manifest/config-load.js +3 -3
  49. package/dist/manifest/publish.d.ts +1 -0
  50. package/dist/manifest/publish.js +23 -22
  51. package/dist/manifest/schema.js +1 -1
  52. package/dist/manifest-embed.js +2 -2
  53. package/dist/manifest-fetch.js +2 -2
  54. package/dist/manifest-roundtrip.js +2 -2
  55. package/dist/manifest.d.ts +14 -1
  56. package/dist/manifest.js +5 -1
  57. package/dist/memory-report.js +3 -2
  58. package/dist/merkle.js +20 -19
  59. package/dist/mnemonic.d.ts +35 -0
  60. package/dist/mnemonic.js +8 -0
  61. package/dist/personhood/bootstrap.js +7 -6
  62. package/dist/personhood/people-client.js +7 -6
  63. package/dist/run-state.js +1 -1
  64. package/dist/sss-allowance-cache.js +6 -6
  65. package/dist/storage-signer.js +20 -19
  66. package/dist/telemetry.d.ts +2 -1
  67. package/dist/telemetry.js +5 -2
  68. package/dist/version-check.js +4 -3
  69. package/package.json +4 -4
@@ -1,53 +1,54 @@
1
+ import {
2
+ preflightSssAllowance
3
+ } from "./chunk-6J4WRFRL.js";
4
+ import {
5
+ statementSigningAccount
6
+ } from "./chunk-GRPLHUYC.js";
1
7
  import {
2
8
  subnameNestingLevels
3
9
  } from "./chunk-HNJWIY55.js";
4
10
  import {
5
11
  deriveProductSigner
6
12
  } from "./chunk-M5AKL3FL.js";
7
- import {
8
- preflightSssAllowance
9
- } from "./chunk-6FQJLLEF.js";
10
- import {
11
- statementSigningAccount
12
- } from "./chunk-GRPLHUYC.js";
13
13
  import {
14
14
  computeStats,
15
15
  renderSummary,
16
16
  telemetryAttributes
17
- } from "./chunk-IW3X2MJF.js";
17
+ } from "./chunk-D3YME2EG.js";
18
18
  import {
19
19
  finaliseEmbeddedManifest,
20
20
  writeEmbeddedManifestPlaceholder
21
- } from "./chunk-LPTKBQII.js";
21
+ } from "./chunk-7DDOVCV5.js";
22
22
  import {
23
23
  extractManifestFromCar,
24
24
  fetchPreviousManifest,
25
25
  writePersistentLocalManifest
26
- } from "./chunk-3MAIF2SF.js";
26
+ } from "./chunk-F24S4EV5.js";
27
27
  import {
28
+ CONTENT_HASH_RE,
28
29
  MANIFEST_PATH,
29
30
  MANIFEST_VERSION,
30
31
  classifyFile,
31
32
  parseManifest
32
- } from "./chunk-WPC2PUCV.js";
33
+ } from "./chunk-H44LXDQJ.js";
33
34
  import {
34
35
  setDeployContext
35
- } from "./chunk-QU5GJF6U.js";
36
+ } from "./chunk-UFP2XWZ4.js";
36
37
  import {
37
38
  getBestBlockNumber,
38
39
  probeChunks,
39
40
  probeFinalityGap
40
- } from "./chunk-PSYA6E5M.js";
41
+ } from "./chunk-2P4EZOFF.js";
41
42
  import {
42
43
  packSection
43
44
  } from "./chunk-C2TS5MER.js";
44
45
  import {
45
46
  resolveStorageSigner
46
- } from "./chunk-3ACNAWUD.js";
47
+ } from "./chunk-JTH3ISLR.js";
47
48
  import {
48
49
  createSlotAccountSigner,
49
50
  requestResourceAllocation
50
- } from "./chunk-5FLTDWWP.js";
51
+ } from "./chunk-H44NO63C.js";
51
52
  import {
52
53
  DEFAULT_TLD,
53
54
  DotNS,
@@ -59,7 +60,11 @@ import {
59
60
  parseDomainName,
60
61
  popStatusName,
61
62
  verifyNonceAdvanced
62
- } from "./chunk-RJDHC56V.js";
63
+ } from "./chunk-ZZIOS4RG.js";
64
+ import {
65
+ mnemonicConflictNotice,
66
+ resolveEffectiveMnemonic
67
+ } from "./chunk-ZG3HAH5A.js";
63
68
  import {
64
69
  derivePoolAccounts,
65
70
  detectTestnet,
@@ -74,7 +79,7 @@ import {
74
79
  STALE_SESSION_MESSAGE,
75
80
  getPeopleChainEndpoints,
76
81
  hasPersistedSession
77
- } from "./chunk-ABMNC34K.js";
82
+ } from "./chunk-U2QO5G35.js";
78
83
  import {
79
84
  CLI_NAME
80
85
  } from "./chunk-TSPERKUS.js";
@@ -84,13 +89,10 @@ import {
84
89
  getPopSelfServeConfig,
85
90
  loadEnvironments,
86
91
  resolveEndpoints
87
- } from "./chunk-NA3SXVY6.js";
88
- import {
89
- NonRetryableError
90
- } from "./chunk-ZOC4GITL.js";
92
+ } from "./chunk-3NR3T7US.js";
91
93
  import {
92
94
  ChainHealthSampler
93
- } from "./chunk-VBIFKNUS.js";
95
+ } from "./chunk-CCIBF55X.js";
94
96
  import {
95
97
  VERSION,
96
98
  captureWarning,
@@ -104,7 +106,10 @@ import {
104
106
  truncateAddress,
105
107
  withDeploySpan,
106
108
  withSpan
107
- } from "./chunk-IP6ABIIC.js";
109
+ } from "./chunk-BFK5UCUJ.js";
110
+ import {
111
+ NonRetryableError
112
+ } from "./chunk-SH2LG52Z.js";
108
113
 
109
114
  // src/merkle.ts
110
115
  import * as fs2 from "fs";
@@ -664,12 +669,17 @@ async function getSignerProvider(signer, ss58) {
664
669
  }
665
670
  function __selectStorageProviderModeForTest(options) {
666
671
  if (options.storageSigner && options.storageSignerAddress) return "storageSigner";
667
- if (options.signer && options.signerAddress) return "signer";
672
+ if (options.signer && options.signerAddress && !options.sessionSigner) return "signer";
668
673
  if (options.mnemonic) return "direct";
669
674
  return "pool";
670
675
  }
671
- function resolveEffectiveMnemonic(opts) {
672
- return opts.flagMnemonic ?? opts.envMnemonic ?? opts.envDotnsMnemonic;
676
+ function deployActorsToSignerOptions(actors) {
677
+ return {
678
+ signer: actors.worker.signer,
679
+ signerAddress: actors.worker.address,
680
+ ...actors.recipientH160 ? { transferTo: actors.recipientH160 } : {},
681
+ sessionSigner: actors.worker.source === "session"
682
+ };
673
683
  }
674
684
  function resolveEnvId(opts) {
675
685
  return opts.flagEnv ?? opts.envVar;
@@ -724,7 +734,8 @@ function assertPoolFallbackAllowed(network, reason) {
724
734
  );
725
735
  }
726
736
  function selectStorageReconnect(options) {
727
- if (options.storageSigner && options.storageSignerAddress) {
737
+ const mode = __selectStorageProviderModeForTest(options);
738
+ if (mode === "storageSigner") {
728
739
  let useSlot = true;
729
740
  return async () => {
730
741
  if (!useSlot) return getProvider();
@@ -745,9 +756,9 @@ function selectStorageReconnect(options) {
745
756
  }
746
757
  };
747
758
  }
748
- if (options.signer && options.signerAddress)
759
+ if (mode === "signer")
749
760
  return () => getSignerProvider(options.signer, options.signerAddress);
750
- if (options.mnemonic)
761
+ if (mode === "direct")
751
762
  return () => getDirectProvider(options.mnemonic, options.derivationPath);
752
763
  return () => getProvider();
753
764
  }
@@ -1517,7 +1528,7 @@ async function readPreviousContenthashSafe(dotns, domainName) {
1517
1528
  return null;
1518
1529
  }
1519
1530
  }
1520
- function buildFilesMap(buildDir, fileCids = /* @__PURE__ */ new Map()) {
1531
+ function buildFilesMap(buildDir, fileCids = /* @__PURE__ */ new Map(), framework = null) {
1521
1532
  const map = {};
1522
1533
  function walk(dir, prefix = "") {
1523
1534
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -1534,7 +1545,7 @@ function buildFilesMap(buildDir, fileCids = /* @__PURE__ */ new Map()) {
1534
1545
  size = fs.statSync(abs).size;
1535
1546
  } catch {
1536
1547
  }
1537
- const type = classifyFile(rel, { fileCid: fileCid || void 0 });
1548
+ const type = classifyFile(rel, { fileCid: fileCid || void 0, framework });
1538
1549
  map[rel] = { cid: fileCid, type, size };
1539
1550
  }
1540
1551
  }
@@ -1542,6 +1553,40 @@ function buildFilesMap(buildDir, fileCids = /* @__PURE__ */ new Map()) {
1542
1553
  walk(buildDir);
1543
1554
  return map;
1544
1555
  }
1556
+ function computeClassificationPrecision(fileCids, filesMap, prevManifest, sampleCap = 10) {
1557
+ let filesStableGroundTruth = 0;
1558
+ let filesStablePacked = 0;
1559
+ let heuristicAgreeStable = 0;
1560
+ let heuristicAgreeVolatile = 0;
1561
+ let heuristicFalseVolatile = 0;
1562
+ let heuristicFalseStable = 0;
1563
+ const sampleSeen = /* @__PURE__ */ new Set();
1564
+ const unhashedStableSample = [];
1565
+ for (const [p, cid] of fileCids.entries()) {
1566
+ if (p === MANIFEST_PATH) continue;
1567
+ const guess = filesMap[p]?.type ?? "volatile";
1568
+ if (guess === "stable") filesStablePacked++;
1569
+ const truth = classifyFile(p, { prevManifest, fileCid: cid });
1570
+ if (truth === "stable") filesStableGroundTruth++;
1571
+ const prevEntry = prevManifest?.files[p];
1572
+ if (!prevEntry || !cid) continue;
1573
+ if (truth === "stable" && guess === "stable") {
1574
+ heuristicAgreeStable++;
1575
+ } else if (truth === "volatile" && guess === "volatile") {
1576
+ heuristicAgreeVolatile++;
1577
+ } else if (truth === "stable" && guess === "volatile") {
1578
+ heuristicFalseVolatile++;
1579
+ const basename3 = p.split("/").pop() ?? p;
1580
+ if (unhashedStableSample.length < sampleCap && !sampleSeen.has(basename3)) {
1581
+ sampleSeen.add(basename3);
1582
+ unhashedStableSample.push(basename3);
1583
+ }
1584
+ } else {
1585
+ heuristicFalseStable++;
1586
+ }
1587
+ }
1588
+ return { filesStableGroundTruth, filesStablePacked, heuristicAgreeStable, heuristicAgreeVolatile, heuristicFalseVolatile, heuristicFalseStable, unhashedStableSample };
1589
+ }
1545
1590
  async function readRetentionPeriodBlocks(unsafeApi) {
1546
1591
  try {
1547
1592
  const rp = await unsafeApi.query.TransactionStorage.RetentionPeriod.getValue();
@@ -1550,10 +1595,37 @@ async function readRetentionPeriodBlocks(unsafeApi) {
1550
1595
  return 0;
1551
1596
  }
1552
1597
  }
1598
+ var VITE_ASSET_HASH_RE = CONTENT_HASH_RE;
1599
+ function detectBuildMarkers(directoryPath) {
1600
+ const markers = [];
1601
+ try {
1602
+ const manifestPath = path.join(directoryPath, "manifest.json");
1603
+ const polkavmBinPath = path.join(directoryPath, "app.polkavm");
1604
+ if (fs.existsSync(manifestPath) && fs.existsSync(polkavmBinPath)) {
1605
+ const obj = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
1606
+ if (obj && typeof obj === "object" && obj.runtime && obj.runtime.kind === "polkavm") {
1607
+ markers.push("polkavm-app");
1608
+ }
1609
+ }
1610
+ } catch {
1611
+ }
1612
+ if (fs.existsSync(path.join(directoryPath, "_next", "static"))) markers.push("next");
1613
+ if (fs.existsSync(path.join(directoryPath, "_nuxt"))) markers.push("nuxt");
1614
+ const assetsDir = path.join(directoryPath, "assets");
1615
+ if (fs.existsSync(assetsDir)) {
1616
+ try {
1617
+ const entries = fs.readdirSync(assetsDir);
1618
+ if (entries.some((name) => VITE_ASSET_HASH_RE.test(name))) markers.push("vite");
1619
+ } catch {
1620
+ }
1621
+ }
1622
+ return markers;
1623
+ }
1624
+ function frameworkFromMarkers(markers) {
1625
+ return markers.length === 1 ? markers[0] : null;
1626
+ }
1553
1627
  function detectFramework(directoryPath) {
1554
- if (fs.existsSync(path.join(directoryPath, "_next"))) return "next";
1555
- if (fs.existsSync(path.join(directoryPath, "assets"))) return "vite";
1556
- return null;
1628
+ return frameworkFromMarkers(detectBuildMarkers(directoryPath));
1557
1629
  }
1558
1630
  var SIZE_WARN_BYTES = 50 * 1024 * 1024;
1559
1631
  var SIZE_ABORT_BYTES = 500 * 1024 * 1024;
@@ -1615,6 +1687,8 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1615
1687
  const prevManifest = fetched.source === "embedded" ? fetched.manifest : null;
1616
1688
  console.log(` Manifest fetch: ${fetched.source}${fetched.source !== "none" ? ` (${fetched.attempts} attempt${fetched.attempts === 1 ? "" : "s"})` : ""}`);
1617
1689
  applyManifestFetchAttributes(fetched);
1690
+ const buildMarkers = detectBuildMarkers(directoryPath);
1691
+ const framework = frameworkFromMarkers(buildMarkers);
1618
1692
  const deployedAt = opts.reproducibleSource ? resolveReproducibleTimestamp(opts.reproducibleSource) : (/* @__PURE__ */ new Date()).toISOString();
1619
1693
  writeEmbeddedManifestPlaceholder(directoryPath, {
1620
1694
  version: MANIFEST_VERSION,
@@ -1634,7 +1708,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1634
1708
  useKubo = hasIPFS();
1635
1709
  }
1636
1710
  const phaseA = await withSpan("deploy.merkleize", `1a. merkleize (${useKubo ? "kubo" : "js"}, stable)`, { "deploy.directory": dirBasename, "deploy.merkle": useKubo ? "kubo" : "js" }, async () => {
1637
- const r = await merkleizeWithStableOrder(directoryPath, prevManifest?.stableBlockOrder, { useKubo, phase: "Phase A" });
1711
+ const r = await merkleizeWithStableOrder(directoryPath, prevManifest?.stableBlockOrder, { useKubo, phase: "Phase A", classifyFn: (p) => classifyFile(p, { framework }) });
1638
1712
  sampleMemory("merkleize_end");
1639
1713
  return r;
1640
1714
  });
@@ -1722,7 +1796,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1722
1796
  setDeployAttribute("deploy.probe.failed", probeFailedCount);
1723
1797
  setDeployAttribute("deploy.phase_a.chunks_uploaded", probeAbsent);
1724
1798
  const phaseAKnownPresent = new Set(phaseAUploadCids);
1725
- const filesMap = buildFilesMap(directoryPath, phaseA.fileCids);
1799
+ const filesMap = buildFilesMap(directoryPath, phaseA.fileCids, framework);
1726
1800
  const blocksList = [...phaseA.blocks.keys()];
1727
1801
  const chunksMap = {};
1728
1802
  for (let i = 0; i < phaseA.section1ChunkCids.length; i++) {
@@ -1743,7 +1817,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1743
1817
  version: MANIFEST_VERSION,
1744
1818
  previousContenthash: prevContenthash,
1745
1819
  deployedAt,
1746
- framework: detectFramework(directoryPath),
1820
+ framework,
1747
1821
  files: filesMap,
1748
1822
  stableBlockOrder: phaseA.stableOrder,
1749
1823
  blocks: blocksList,
@@ -1753,7 +1827,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1753
1827
  carChunksA.length = 0;
1754
1828
  phaseA.carBytes = new Uint8Array(0);
1755
1829
  const phaseB = await withSpan("deploy.merkleize", "1c. merkleize (js, finalise)", { "deploy.directory": dirBasename }, async () => {
1756
- const r = await merkleizeWithStableOrder(directoryPath, phaseA.stableOrder, { useKubo, phase: "Phase B" });
1830
+ const r = await merkleizeWithStableOrder(directoryPath, phaseA.stableOrder, { useKubo, phase: "Phase B", classifyFn: (p) => classifyFile(p, { framework }) });
1757
1831
  sampleMemory("merkleize_finalise_end");
1758
1832
  return r;
1759
1833
  });
@@ -1940,12 +2014,8 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1940
2014
  preWarmGateway(newlyUploadedCids, [gateway]);
1941
2015
  }
1942
2016
  const retentionPeriodBlocks = await readRetentionPeriodBlocks(provider.unsafeApi);
1943
- const framework = detectFramework(directoryPath);
1944
- const filesStableCount = [...phaseA.fileCids.entries()].filter(([p, cid]) => {
1945
- if (p === MANIFEST_PATH) return false;
1946
- return classifyFile(p, { prevManifest, fileCid: cid, framework }) === "stable";
1947
- }).length;
1948
2017
  const filesTotalCount = phaseA.fileCids.size - (phaseA.fileCids.has(MANIFEST_PATH) ? 1 : 0);
2018
+ const precision = computeClassificationPrecision(phaseA.fileCids, filesMap, prevManifest);
1949
2019
  const probeResultsForStats = carChunkCidsA.flatMap((cid) => {
1950
2020
  if (trustedCidsA.has(cid)) return [{ cid, present: true, block: 0, index: 0 }];
1951
2021
  if (!phaseASkipProbeResults.has(cid)) return [];
@@ -1973,8 +2043,15 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1973
2043
  manifestBytes: fetched.source === "embedded" ? fetched.bytesDownloaded ?? 0 : 0,
1974
2044
  framework,
1975
2045
  filesTotal: filesTotalCount,
1976
- filesStable: filesStableCount,
1977
- filesVolatile: filesTotalCount - filesStableCount,
2046
+ filesStable: precision.filesStableGroundTruth,
2047
+ filesVolatile: filesTotalCount - precision.filesStableGroundTruth,
2048
+ filesStablePacked: precision.filesStablePacked,
2049
+ heuristicAgreeStable: precision.heuristicAgreeStable,
2050
+ heuristicAgreeVolatile: precision.heuristicAgreeVolatile,
2051
+ heuristicFalseVolatile: precision.heuristicFalseVolatile,
2052
+ heuristicFalseStable: precision.heuristicFalseStable,
2053
+ buildMarkers,
2054
+ unhashedStableSample: precision.unhashedStableSample,
1978
2055
  probeResults: probeResultsForStats,
1979
2056
  prevChunks: prevManifest?.chunks ?? {},
1980
2057
  retentionPeriodBlocks,
@@ -2017,6 +2094,10 @@ function resolveProductSigner(options) {
2017
2094
  if (options.signer || options.signerAddress || options.suri || options.derivationPath) {
2018
2095
  throw new NonRetryableError("--product-name derives the signer itself; it cannot be combined with --suri, --derivation-path, or an external signer.");
2019
2096
  }
2097
+ if (!options.mnemonic) {
2098
+ const notice = mnemonicConflictNotice({ envMnemonic: process.env.MNEMONIC, envDotnsMnemonic: process.env.DOTNS_MNEMONIC });
2099
+ if (notice) console.error(notice);
2100
+ }
2020
2101
  const productMnemonic = resolveEffectiveMnemonic({
2021
2102
  flagMnemonic: options.mnemonic,
2022
2103
  envMnemonic: process.env.MNEMONIC,
@@ -2154,6 +2235,7 @@ async function deploy(content, domainName = null, options = {}) {
2154
2235
  let envAssetHub;
2155
2236
  let envSource;
2156
2237
  let envUserFilePath;
2238
+ let envUserFileKeys;
2157
2239
  let envNetwork;
2158
2240
  let envName;
2159
2241
  let envIpfs;
@@ -2173,12 +2255,13 @@ async function deploy(content, domainName = null, options = {}) {
2173
2255
  envAssetHub = options.assetHubEndpoints;
2174
2256
  } else {
2175
2257
  try {
2176
- const { doc, source, userFilePath } = await loadEnvironments();
2258
+ const { doc, source, userFilePath, userFileContractKeys } = await loadEnvironments();
2177
2259
  const resolved = resolveEndpoints(doc, envId);
2178
2260
  envBulletin = resolved.bulletin;
2179
2261
  envAssetHub = options.assetHubEndpoints ?? resolved.assetHub;
2180
2262
  envSource = source;
2181
2263
  envUserFilePath = userFilePath;
2264
+ envUserFileKeys = userFileContractKeys?.[envId];
2182
2265
  envNetwork = resolved.network ?? "unknown";
2183
2266
  envName = resolved.envName;
2184
2267
  envIpfs = resolved.ipfs;
@@ -2199,7 +2282,7 @@ async function deploy(content, domainName = null, options = {}) {
2199
2282
  captureWarning(`environments load failed: ${e?.message ?? e}`);
2200
2283
  }
2201
2284
  }
2202
- const contractSources = describeContractSources(envContracts, options.contracts, envSource, envUserFilePath, envId);
2285
+ const contractSources = describeContractSources(envContracts, options.contracts, envSource, envUserFilePath, envId, envUserFileKeys);
2203
2286
  if (options.contracts && Object.keys(options.contracts).length > 0) {
2204
2287
  envContracts = { ...envContracts, ...options.contracts };
2205
2288
  }
@@ -2236,12 +2319,7 @@ async function deploy(content, domainName = null, options = {}) {
2236
2319
  isTestnet: isTestnetEnv,
2237
2320
  sessionPresent: hasSession
2238
2321
  });
2239
- options = {
2240
- ...options,
2241
- signer: actors.worker.signer,
2242
- signerAddress: actors.worker.address,
2243
- ...actors.recipientH160 ? { transferTo: actors.recipientH160 } : {}
2244
- };
2322
+ options = { ...options, ...deployActorsToSignerOptions(actors) };
2245
2323
  sessionCleanup = actors.worker.destroy.bind(actors.worker);
2246
2324
  if (actors.worker.source === "session") resolvedUserSession = actors.worker;
2247
2325
  if (actors.recipientH160) {
@@ -2973,7 +3051,11 @@ async function buildOrderedCar(options) {
2973
3051
  newStable.sort(
2974
3052
  (a, b) => b.size - a.size || (a.fileCid < b.fileCid ? -1 : a.fileCid > b.fileCid ? 1 : 0)
2975
3053
  );
2976
- for (const f of newStable) section1Files.push(f);
3054
+ for (const f of newStable) {
3055
+ if (placed.has(f.fileCid)) continue;
3056
+ section1Files.push(f);
3057
+ placed.add(f.fileCid);
3058
+ }
2977
3059
  volatileFiles.sort(
2978
3060
  (a, b) => b.size - a.size || (a.fileCid < b.fileCid ? -1 : a.fileCid > b.fileCid ? 1 : 0)
2979
3061
  );
@@ -3148,7 +3230,7 @@ export {
3148
3230
  ENCRYPT_PBKDF2_ITERATIONS,
3149
3231
  encryptContent,
3150
3232
  __selectStorageProviderModeForTest,
3151
- resolveEffectiveMnemonic,
3233
+ deployActorsToSignerOptions,
3152
3234
  resolveEnvId,
3153
3235
  shouldPublishManifest,
3154
3236
  chooseSignerInput,
@@ -3176,6 +3258,8 @@ export {
3176
3258
  computeStorageCid,
3177
3259
  storeDirectory,
3178
3260
  buildFilesMap,
3261
+ computeClassificationPrecision,
3262
+ detectBuildMarkers,
3179
3263
  detectFramework,
3180
3264
  checkDeploySize,
3181
3265
  resolveReproducibleTimestamp,
@@ -2,7 +2,7 @@ import {
2
2
  MANIFEST_DIR,
3
3
  MANIFEST_FILENAME,
4
4
  parseManifest
5
- } from "./chunk-WPC2PUCV.js";
5
+ } from "./chunk-H44LXDQJ.js";
6
6
 
7
7
  // src/manifest-fetch.ts
8
8
  import * as fs from "fs";
@@ -267,6 +267,16 @@ async function fetchManifestFromChain(client, rootCid, timeoutMs) {
267
267
  return null;
268
268
  }
269
269
  }
270
+ function manifestFetchAttributes(params) {
271
+ return {
272
+ "manifest.fetch.gateway": params.gateway,
273
+ "manifest.fetch.cid": params.cid,
274
+ "manifest.fetch.outcome": params.outcome,
275
+ "manifest.fetch.attempts": String(params.attempts),
276
+ "manifest.fetch.bytes": String(params.bytes),
277
+ "manifest.fetch.elapsed_ms": String(params.elapsedMs)
278
+ };
279
+ }
270
280
  async function fetchPreviousManifest(prevContenthash, options = {}) {
271
281
  if (prevContenthash === null) return { source: "none" };
272
282
  const local = readPersistentLocalManifest(options.domain, prevContenthash);
@@ -302,6 +312,31 @@ async function fetchPreviousManifest(prevContenthash, options = {}) {
302
312
  }
303
313
  }
304
314
  const gatewayList = (options.gateways ?? (options.gateway ? [options.gateway] : [])).map((g) => g.replace(/\/$/, ""));
315
+ if (gatewayList.length === 0) {
316
+ const cid = prevContenthash.slice(0, 12);
317
+ Sentry.startSpan(
318
+ {
319
+ op: "manifest.fetch",
320
+ name: `manifest fetch ${cid} (no gateway)`,
321
+ attributes: manifestFetchAttributes({
322
+ gateway: "",
323
+ cid,
324
+ outcome: "no_gateway_configured",
325
+ attempts: 0,
326
+ bytes: 0,
327
+ elapsedMs: 0
328
+ })
329
+ },
330
+ () => {
331
+ }
332
+ );
333
+ return {
334
+ source: "heuristic_fallback",
335
+ reason: "no gateway configured",
336
+ attempts: 0,
337
+ bytesDownloaded: 0
338
+ };
339
+ }
305
340
  const budget = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
306
341
  const start = Date.now();
307
342
  let lastReason = "unknown";
@@ -311,22 +346,29 @@ async function fetchPreviousManifest(prevContenthash, options = {}) {
311
346
  const gateway = gatewayRaw.replace(/\/ipfs\/?$/, "");
312
347
  const url = `${gateway}/ipfs/${prevContenthash}`;
313
348
  const gatewayStart = Date.now();
349
+ const cid = prevContenthash.slice(0, 12);
314
350
  const tierResult = await Sentry.startSpan(
315
351
  {
316
352
  op: "manifest.fetch",
317
- name: `manifest fetch ${prevContenthash.slice(0, 12)}`,
353
+ name: `manifest fetch ${cid}`,
318
354
  attributes: {
319
355
  "manifest.fetch.gateway": gateway,
320
- "manifest.fetch.cid": prevContenthash.slice(0, 12),
356
+ "manifest.fetch.cid": cid,
321
357
  "manifest.fetch.budget_ms": String(budget)
322
358
  }
323
359
  },
324
360
  async (span) => {
325
361
  const result = await fetchAcrossTiers(url, budget, start);
326
- span.setAttribute("manifest.fetch.outcome", result.outcome);
327
- span.setAttribute("manifest.fetch.attempts", String(result.attempts));
328
- span.setAttribute("manifest.fetch.bytes", String(result.bytesDownloaded));
329
- span.setAttribute("manifest.fetch.elapsed_ms", String(Date.now() - gatewayStart));
362
+ span.setAttributes(
363
+ manifestFetchAttributes({
364
+ gateway,
365
+ cid,
366
+ outcome: result.outcome,
367
+ attempts: result.attempts,
368
+ bytes: result.bytesDownloaded,
369
+ elapsedMs: Date.now() - gatewayStart
370
+ })
371
+ );
330
372
  return result;
331
373
  }
332
374
  );
@@ -24,21 +24,54 @@ var STABLE_EXTENSIONS = /* @__PURE__ */ new Set([
24
24
  "ogg",
25
25
  "pdf"
26
26
  ]);
27
- var CONTENT_HASH_RE = /[-.](?:[a-f0-9]{6,16}|[A-Za-z0-9_-]{6,16})\.[a-zA-Z0-9]+$/;
27
+ var CONTENT_HASH_RE = /[-.](?:[a-f0-9]{6,32}|[A-Za-z0-9_-]{6,20})\.[a-zA-Z0-9]+$/;
28
+ var NUXT_HASH_RE = /(?:^|\/)[A-Za-z0-9_-]{8,20}\.[a-zA-Z0-9]+$/;
29
+ var FRAMEWORK_RULES = {
30
+ // Next.js self-hosting docs: static assets under _next/static/ carry an
31
+ // immutable Cache-Control because "these immutable files contain a
32
+ // SHA-hash in the file name" — but _buildManifest.js and _ssgManifest.js
33
+ // sit under a build-id directory and carry no hash of their own. Their
34
+ // path is unique only while the build id is (Next keeps it constant when
35
+ // `deploymentId` is set), so with a fixed build id they are a stable path
36
+ // with mutable content — the C2 expensive error — unless excluded.
37
+ next: {
38
+ stablePrefixes: ["_next/static/"],
39
+ volatileExact: ["_buildManifest.js", "_ssgManifest.js"]
40
+ },
41
+ // Nuxt's app manifest writes _nuxt/builds/latest.json — re-read on every
42
+ // page load to detect a new deployment, so it has a fixed filename and
43
+ // changes content every build. A blanket `_nuxt/` stablePrefix would put
44
+ // it in section 1, so the rule is scoped to the hash shape instead of the
45
+ // prefix. _nuxt/builds/meta/<buildId>.json IS stable: that name is unique
46
+ // per build, so its content never changes under it.
47
+ nuxt: { scopedHashRe: [{ prefix: "_nuxt/", re: NUXT_HASH_RE }] },
48
+ // A PolkaVM app bundle. Nothing here is content-addressed, so the rules
49
+ // are about what the bundle means, not what its filenames look like: the
50
+ // payload directories never change, the executable and its manifest
51
+ // change every release. Measured on polkavm-app-kit: one deploy shipped
52
+ // 52.17 MB volatile against 3.73 MB stable, another 17.65 MB across 8
53
+ // files at 99.9% volatile. `game/` and `LICENSES/` are generic enough to
54
+ // be someone else's mutable content, so they are earned entirely by the
55
+ // detection marker (a parsed manifest.json field) being specific.
56
+ "polkavm-app": {
57
+ stablePrefixes: ["game/", "LICENSES/"],
58
+ volatileExact: ["app.polkavm", "manifest.json"]
59
+ }
60
+ };
28
61
  function isVolatilePath(p) {
29
62
  return p.startsWith(`${MANIFEST_DIR}/`) || p === MANIFEST_DIR;
30
63
  }
31
64
  function classifyFileHeuristic(filePath, framework) {
32
65
  if (isVolatilePath(filePath)) return "volatile";
33
- if (CONTENT_HASH_RE.test(filePath)) return "stable";
66
+ const rules = framework ? FRAMEWORK_RULES[framework] : void 0;
67
+ if (rules) {
68
+ if (rules.volatileExact && rules.volatileExact.includes(filePath.split("/").pop() ?? filePath)) return "volatile";
69
+ if (rules.stablePrefixes?.some((prefix) => filePath.startsWith(prefix))) return "stable";
70
+ if (rules.scopedHashRe?.some(({ prefix, re }) => filePath.startsWith(prefix) && re.test(filePath))) return "stable";
71
+ }
34
72
  const ext = filePath.split(".").pop()?.toLowerCase();
35
73
  if (ext && STABLE_EXTENSIONS.has(ext)) return "stable";
36
- if (framework === "next") {
37
- if (filePath.startsWith("_next/static/")) return "stable";
38
- }
39
- if (framework === "vite") {
40
- if (filePath.startsWith("assets/") && CONTENT_HASH_RE.test(filePath)) return "stable";
41
- }
74
+ if (CONTENT_HASH_RE.test(filePath)) return "stable";
42
75
  return "volatile";
43
76
  }
44
77
  function classifyFile(filePath, ctx = {}) {
@@ -101,6 +134,8 @@ export {
101
134
  MANIFEST_DIR,
102
135
  MANIFEST_FILENAME,
103
136
  MANIFEST_PATH,
137
+ CONTENT_HASH_RE,
138
+ FRAMEWORK_RULES,
104
139
  isVolatilePath,
105
140
  classifyFileHeuristic,
106
141
  classifyFile,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-TSPERKUS.js";
4
4
  import {
5
5
  NonRetryableError
6
- } from "./chunk-ZOC4GITL.js";
6
+ } from "./chunk-SH2LG52Z.js";
7
7
 
8
8
  // src/auth/vendor/auth.ts
9
9
  import { readdir, unlink } from "fs/promises";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  BULLETIN_RESOURCE,
3
3
  resolveSigner
4
- } from "./chunk-5FLTDWWP.js";
4
+ } from "./chunk-H44NO63C.js";
5
5
  import {
6
6
  DEFAULT_MNEMONIC
7
- } from "./chunk-RJDHC56V.js";
7
+ } from "./chunk-ZZIOS4RG.js";
8
8
  import {
9
9
  DOT_PRODUCT_ID
10
- } from "./chunk-ABMNC34K.js";
10
+ } from "./chunk-U2QO5G35.js";
11
11
 
12
12
  // src/deploy-actors.ts
13
13
  var DEFAULT_WORKER_SURI = DEFAULT_MNEMONIC;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  validateProductConfig
3
- } from "./chunk-I5PNPQZO.js";
3
+ } from "./chunk-ANX3RZAS.js";
4
4
  import {
5
5
  NonRetryableError
6
- } from "./chunk-ZOC4GITL.js";
6
+ } from "./chunk-SH2LG52Z.js";
7
7
 
8
8
  // src/manifest/config-load.ts
9
9
  import * as fs from "fs/promises";
@@ -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.19.3",
9
+ version: "0.19.4",
10
10
  private: false,
11
11
  repository: {
12
12
  type: "git",
@@ -52,11 +52,11 @@ var package_default = {
52
52
  "tools/verify-baked-dsn.mjs"
53
53
  ],
54
54
  scripts: {
55
- build: "tsup src/suppress-localstorage-warning.ts src/index.ts src/deploy.ts src/dotns.ts src/dotns-protocol.ts src/pool.ts src/telemetry.ts src/chain-health.ts src/memory-report.ts src/merkle.ts src/version-check.ts src/bug-report.ts src/run-state.ts src/environments.ts src/errors.ts src/subname-depth.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/ring-proof.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/compose.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 src/product-account.ts --format esm --dts --clean --target node22",
55
+ build: "tsup src/suppress-localstorage-warning.ts src/index.ts src/deploy.ts src/dotns.ts src/mnemonic.ts src/dotns-protocol.ts src/pool.ts src/telemetry.ts src/chain-health.ts src/memory-report.ts src/merkle.ts src/version-check.ts src/bug-report.ts src/run-state.ts src/environments.ts src/errors.ts src/subname-depth.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/ring-proof.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/compose.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 src/product-account.ts --format esm --dts --clean --target node22",
56
56
  "refresh-environments": "node scripts/refresh-environments.mjs",
57
57
  postinstall: "patch-package || true",
58
58
  prepare: "npm run build",
59
- test: "npm run build && node --test test/test.js test/cli-help.test.js test/helpers/e2e-helpers.test.js test/helpers/e2e-exit-guard.test.js test/environments.test.js test/refresh-environments.test.js test/chunk-sharing-report.test.js test/dotns-deployments-parser.test.js test/dotns-token-id.test.js test/dotns-subnode-id.test.js test/dotns-protocol.test.js test/product-manifest.test.js test/manifest-trusted-products.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/chunk-probe-timeout.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 test/product-account.test.js test/post-release-monitor-skill.test.js test/test-release-retry-wrapper.js test/reprove-alias-tool.test.js test/bootstrap-env.test.js test/check-chain-identity.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/helpers/e2e-exit-guard.test.js test/environments.test.js test/refresh-environments.test.js test/chunk-sharing-report.test.js test/car-section1-dedup.test.js test/dotns-deployments-parser.test.js test/dotns-token-id.test.js test/dotns-subnode-id.test.js test/dotns-protocol.test.js test/product-manifest.test.js test/manifest-trusted-products.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/chunk-probe-timeout.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 test/product-account.test.js test/post-release-monitor-skill.test.js test/test-release-retry-wrapper.js test/reprove-alias-tool.test.js test/bootstrap-env.test.js test/check-chain-identity.test.js && npm run test:vendor",
60
60
  "test:e2e": "npm run build && node --test test/e2e.test.js",
61
61
  "test:e2e:smoke": "bash scripts/e2e-pass.sh smoke",
62
62
  "test:e2e:pr": "bash scripts/e2e-pass.sh pr",
@@ -94,7 +94,7 @@ var package_default = {
94
94
  "@types/ws": "^8.18.1",
95
95
  tsup: "^8.5.0",
96
96
  typescript: "^5.9.3",
97
- vitest: "^4.1.0"
97
+ vitest: "^4.1.11"
98
98
  },
99
99
  overrides: {
100
100
  "@polkadot-api/json-rpc-provider": "^0.2.0"
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-IP6ABIIC.js";
3
+ } from "./chunk-BFK5UCUJ.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";