@parity/product-deploy 0.7.28-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +233 -0
- package/assets/environments.json +313 -0
- package/bin/bulletin-bootstrap +84 -0
- package/bin/bulletin-deploy +429 -0
- package/dist/bug-report.d.ts +29 -0
- package/dist/bug-report.js +27 -0
- package/dist/chunk-2VAUMZB2.js +284 -0
- package/dist/chunk-43HLT335.js +232 -0
- package/dist/chunk-5VZQ2KSU.js +231 -0
- package/dist/chunk-ADNBLFDP.js +225 -0
- package/dist/chunk-BMAEWZYV.js +24 -0
- package/dist/chunk-C2TS5MER.js +64 -0
- package/dist/chunk-DNXH4QTI.js +2336 -0
- package/dist/chunk-FZWJV5AD.js +231 -0
- package/dist/chunk-GZD2UFLR.js +8 -0
- package/dist/chunk-HOTQDYHD.js +219 -0
- package/dist/chunk-IDYGYIMH.js +207 -0
- package/dist/chunk-KHVTYIIX.js +146 -0
- package/dist/chunk-KJH2T5TQ.js +172 -0
- package/dist/chunk-KOSF5FDO.js +49 -0
- package/dist/chunk-LZJMVPYW.js +156 -0
- package/dist/chunk-MFTODIIT.js +725 -0
- package/dist/chunk-MMAZFJDG.js +91 -0
- package/dist/chunk-NF2FL4ZO.js +164 -0
- package/dist/chunk-OITUIM2E.js +524 -0
- package/dist/chunk-P6CHOMN3.js +2368 -0
- package/dist/chunk-QMYW3D6E.js +316 -0
- package/dist/chunk-QTZNULSH.js +185 -0
- package/dist/chunk-RI3ZLNPN.js +71 -0
- package/dist/chunk-S7EM5VMW.js +108 -0
- package/dist/chunk-T7EEVWNU.js +32 -0
- package/dist/chunk-UPWEOGLQ.js +37 -0
- package/dist/chunk-ZOC4GITL.js +13 -0
- package/dist/chunk-ZYVGHDMU.js +117 -0
- package/dist/chunk-probe.d.ts +37 -0
- package/dist/chunk-probe.js +18 -0
- package/dist/chunker.d.ts +8 -0
- package/dist/chunker.js +10 -0
- package/dist/deploy.d.ts +299 -0
- package/dist/deploy.js +96 -0
- package/dist/dotns.d.ts +506 -0
- package/dist/dotns.js +101 -0
- package/dist/environments.d.ts +104 -0
- package/dist/environments.js +23 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +8 -0
- package/dist/gh-pages-mirror.d.ts +76 -0
- package/dist/gh-pages-mirror.js +30 -0
- package/dist/incremental-stats.d.ts +69 -0
- package/dist/incremental-stats.js +10 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +146 -0
- package/dist/manifest/byte-budget.d.ts +46 -0
- package/dist/manifest/byte-budget.js +14 -0
- package/dist/manifest/config-load.d.ts +36 -0
- package/dist/manifest/config-load.js +10 -0
- package/dist/manifest/publish.d.ts +54 -0
- package/dist/manifest/publish.js +23 -0
- package/dist/manifest/schema.d.ts +29 -0
- package/dist/manifest/schema.js +10 -0
- package/dist/manifest/types.d.ts +90 -0
- package/dist/manifest/types.js +6 -0
- package/dist/manifest-embed.d.ts +18 -0
- package/dist/manifest-embed.js +9 -0
- package/dist/manifest-fetch.d.ts +32 -0
- package/dist/manifest-fetch.js +21 -0
- package/dist/manifest-roundtrip.d.ts +15 -0
- package/dist/manifest-roundtrip.js +55 -0
- package/dist/manifest.d.ts +44 -0
- package/dist/manifest.js +20 -0
- package/dist/memory-report.d.ts +95 -0
- package/dist/memory-report.js +17 -0
- package/dist/merkle.d.ts +50 -0
- package/dist/merkle.js +33 -0
- package/dist/personhood/bind-paid-alias.d.ts +43 -0
- package/dist/personhood/bind-paid-alias.js +10 -0
- package/dist/personhood/bind-personal-id.d.ts +55 -0
- package/dist/personhood/bind-personal-id.js +12 -0
- package/dist/personhood/bootstrap.d.ts +85 -0
- package/dist/personhood/bootstrap.js +245 -0
- package/dist/personhood/claim-pgas.d.ts +61 -0
- package/dist/personhood/claim-pgas.js +12 -0
- package/dist/personhood/constants.d.ts +23 -0
- package/dist/personhood/constants.js +22 -0
- package/dist/personhood/encoding.d.ts +49 -0
- package/dist/personhood/encoding.js +24 -0
- package/dist/personhood/hashing.d.ts +4 -0
- package/dist/personhood/hashing.js +8 -0
- package/dist/personhood/member-key.d.ts +12 -0
- package/dist/personhood/member-key.js +10 -0
- package/dist/personhood/people-client.d.ts +14 -0
- package/dist/personhood/people-client.js +48 -0
- package/dist/personhood/reprove.d.ts +43 -0
- package/dist/personhood/reprove.js +225 -0
- package/dist/pool.d.ts +51 -0
- package/dist/pool.js +30 -0
- package/dist/run-state.d.ts +22 -0
- package/dist/run-state.js +20 -0
- package/dist/telemetry.d.ts +56 -0
- package/dist/telemetry.js +71 -0
- package/dist/version-check.d.ts +38 -0
- package/dist/version-check.js +30 -0
- package/docs/bootstrap.md +49 -0
- package/docs/e2e-bootstrap.md +154 -0
- package/docs/telemetry.md +62 -0
- package/docs/testing.md +44 -0
- package/package.json +82 -0
- package/tools/release-retry-wrapper.mjs +74 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// src/incremental-stats.ts
|
|
2
|
+
var SECONDS_PER_PROBE_SKIP = 3.5;
|
|
3
|
+
function countByReason(probe, reason) {
|
|
4
|
+
return probe.filter((r) => r.present === null && r.failureReason === reason).length;
|
|
5
|
+
}
|
|
6
|
+
function computeStats(input) {
|
|
7
|
+
const present = input.probeResults.filter((r) => r.present === true);
|
|
8
|
+
const failed = input.probeResults.filter((r) => r.present === null);
|
|
9
|
+
const recycled = present.filter((r) => input.prevChunks[r.cid] == null);
|
|
10
|
+
return {
|
|
11
|
+
manifestSource: input.manifestSource,
|
|
12
|
+
manifestFetchAttempts: input.manifestFetchAttempts,
|
|
13
|
+
manifestBytes: input.manifestBytes ?? 0,
|
|
14
|
+
framework: input.framework,
|
|
15
|
+
filesTotal: input.filesTotal,
|
|
16
|
+
filesStable: input.filesStable,
|
|
17
|
+
filesVolatile: input.filesVolatile,
|
|
18
|
+
probedTotal: input.probeResults.length,
|
|
19
|
+
probePresent: present.length,
|
|
20
|
+
probeAbsent: input.probeResults.filter((r) => r.present === false).length,
|
|
21
|
+
probeFailed: failed.length,
|
|
22
|
+
probeFailedRpc: countByReason(input.probeResults, "rpc_error"),
|
|
23
|
+
probeFailedDecode: countByReason(input.probeResults, "decode_error"),
|
|
24
|
+
probeFailedMetadata: countByReason(input.probeResults, "metadata_error"),
|
|
25
|
+
recycledCids: recycled.length,
|
|
26
|
+
retentionPeriodBlocks: input.retentionPeriodBlocks,
|
|
27
|
+
bytesProbePresent: input.bytesProbePresent,
|
|
28
|
+
bytesProbeAbsent: input.bytesProbeAbsent ?? 0,
|
|
29
|
+
bytesSkipped: input.bytesSkipped,
|
|
30
|
+
bytesUploaded: input.bytesUploaded,
|
|
31
|
+
chunksTotal: input.chunksTotal,
|
|
32
|
+
chunksUploaded: input.chunksUploaded,
|
|
33
|
+
chunksSkipped: input.chunksSkipped,
|
|
34
|
+
carBytes: input.carBytes,
|
|
35
|
+
section0Bytes: input.sectionSizes.section0,
|
|
36
|
+
section1Bytes: input.sectionSizes.section1,
|
|
37
|
+
section2Bytes: input.sectionSizes.section2,
|
|
38
|
+
estimatedSecondsSaved: Math.round(SECONDS_PER_PROBE_SKIP * present.length),
|
|
39
|
+
tier2VerifiedCount: input.tier2VerifiedCount,
|
|
40
|
+
tier2InconclusiveCount: input.tier2InconclusiveCount,
|
|
41
|
+
tier2FallbackCount: input.tier2FallbackCount
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function telemetryAttributes(s) {
|
|
45
|
+
const hitRate = s.filesTotal === 0 ? 0 : s.filesStable / s.filesTotal;
|
|
46
|
+
return {
|
|
47
|
+
"deploy.cache.manifest_source": s.manifestSource,
|
|
48
|
+
"deploy.cache.manifest_fetch_attempts": String(s.manifestFetchAttempts),
|
|
49
|
+
"deploy.cache.manifest_bytes": String(s.manifestBytes),
|
|
50
|
+
"deploy.cache.framework": s.framework ?? "",
|
|
51
|
+
"deploy.cache.hit_rate": String(Math.round(hitRate * 1e3) / 1e3),
|
|
52
|
+
"deploy.cache.files_total": String(s.filesTotal),
|
|
53
|
+
"deploy.cache.files_stable": String(s.filesStable),
|
|
54
|
+
"deploy.cache.files_volatile": String(s.filesVolatile),
|
|
55
|
+
"deploy.cache.probed_total": String(s.probedTotal),
|
|
56
|
+
"deploy.cache.probe_present": String(s.probePresent),
|
|
57
|
+
"deploy.cache.probe_absent": String(s.probeAbsent),
|
|
58
|
+
"deploy.cache.probe_failed": String(s.probeFailed),
|
|
59
|
+
"deploy.cache.probe_failed_rpc": String(s.probeFailedRpc),
|
|
60
|
+
"deploy.cache.probe_failed_decode": String(s.probeFailedDecode),
|
|
61
|
+
"deploy.cache.probe_failed_metadata": String(s.probeFailedMetadata),
|
|
62
|
+
"deploy.cache.recycled_cids": String(s.recycledCids),
|
|
63
|
+
"deploy.cache.retention_period_blocks": String(s.retentionPeriodBlocks),
|
|
64
|
+
"deploy.cache.chunks_total": String(s.chunksTotal),
|
|
65
|
+
"deploy.cache.chunks_uploaded": String(s.chunksUploaded),
|
|
66
|
+
"deploy.cache.chunks_skipped": String(s.chunksSkipped),
|
|
67
|
+
"deploy.cache.bytes_probe_present": String(s.bytesProbePresent),
|
|
68
|
+
"deploy.cache.bytes_probe_absent": String(s.bytesProbeAbsent),
|
|
69
|
+
"deploy.cache.bytes_skipped": String(s.bytesSkipped),
|
|
70
|
+
"deploy.cache.bytes_uploaded": String(s.bytesUploaded),
|
|
71
|
+
"deploy.cache.car_bytes": String(s.carBytes),
|
|
72
|
+
"deploy.cache.section0_bytes": String(s.section0Bytes),
|
|
73
|
+
"deploy.cache.section1_bytes": String(s.section1Bytes),
|
|
74
|
+
"deploy.cache.section2_bytes": String(s.section2Bytes),
|
|
75
|
+
"deploy.cache.estimated_seconds_saved": String(s.estimatedSecondsSaved),
|
|
76
|
+
"deploy.cache.tier2_fallback": String(s.tier2FallbackCount),
|
|
77
|
+
"deploy.cache.tier2_verified": String(s.tier2VerifiedCount),
|
|
78
|
+
"deploy.cache.tier2_inconclusive": String(s.tier2InconclusiveCount)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function fmtMb(bytes) {
|
|
82
|
+
return (bytes / 1e6).toFixed(1);
|
|
83
|
+
}
|
|
84
|
+
function fmtKb(bytes) {
|
|
85
|
+
return (bytes / 1e3).toFixed(1);
|
|
86
|
+
}
|
|
87
|
+
function renderSummary(s) {
|
|
88
|
+
const lines = [];
|
|
89
|
+
const attemptsWord = s.manifestFetchAttempts === 1 ? "attempt" : "attempts";
|
|
90
|
+
if (s.manifestSource === "heuristic_fallback") {
|
|
91
|
+
lines.push(` \u26A0 Previous manifest fetch failed after ${s.manifestFetchAttempts} ${attemptsWord} (gateway timeout).`);
|
|
92
|
+
lines.push(` Using heuristic classification \u2014 hit rate may be lower this run.`);
|
|
93
|
+
lines.push(` Subsequent deploys recover automatically.`);
|
|
94
|
+
lines.push("");
|
|
95
|
+
}
|
|
96
|
+
lines.push(`Cache:`);
|
|
97
|
+
if (s.manifestSource === "none") {
|
|
98
|
+
lines.push(` Manifest: first deploy (no previous manifest)`);
|
|
99
|
+
} else if (s.manifestSource === "embedded") {
|
|
100
|
+
const sizeStr = s.manifestBytes > 0 ? `, ${fmtKb(s.manifestBytes)} KB Range hit` : "";
|
|
101
|
+
lines.push(` Manifest: embedded (${s.manifestFetchAttempts} ${attemptsWord}${sizeStr})`);
|
|
102
|
+
} else {
|
|
103
|
+
lines.push(` Manifest: heuristic_fallback (${s.manifestFetchAttempts} ${attemptsWord})`);
|
|
104
|
+
}
|
|
105
|
+
if (s.filesTotal > 0 && s.manifestSource !== "none") {
|
|
106
|
+
const pct = s.filesTotal === 0 ? 0 : Math.round(s.filesStable / s.filesTotal * 100);
|
|
107
|
+
const heuristicNote = s.manifestSource === "heuristic_fallback" ? " (heuristic estimate)" : "";
|
|
108
|
+
lines.push(` Files: ${s.filesStable} unchanged, ${s.filesVolatile} changed (${pct} % stable)${heuristicNote}`);
|
|
109
|
+
}
|
|
110
|
+
if (s.probedTotal > 0) {
|
|
111
|
+
let probeFailedStr = "";
|
|
112
|
+
if (s.probeFailed > 0) {
|
|
113
|
+
const reasons = [];
|
|
114
|
+
if (s.probeFailedRpc > 0) reasons.push("rpc_error");
|
|
115
|
+
if (s.probeFailedDecode > 0) reasons.push("decode_error");
|
|
116
|
+
if (s.probeFailedMetadata > 0) reasons.push("metadata_error");
|
|
117
|
+
probeFailedStr = `, ${s.probeFailed} probe-failed (${reasons.join(", ")})`;
|
|
118
|
+
}
|
|
119
|
+
lines.push(` Probed: ${s.probedTotal} chunks \u2192 ${s.probePresent} on chain, ${s.probeAbsent} absent${probeFailedStr}`);
|
|
120
|
+
}
|
|
121
|
+
if (s.recycledCids > 0 && s.manifestSource === "embedded") {
|
|
122
|
+
lines.push(` Recycled: ${s.recycledCids} CIDs found on-chain that weren't in the previous manifest`);
|
|
123
|
+
}
|
|
124
|
+
if (s.tier2FallbackCount > 0) {
|
|
125
|
+
const inconclusiveStr = s.tier2InconclusiveCount > 0 ? `, ${s.tier2InconclusiveCount} inconclusive` : "";
|
|
126
|
+
lines.push(` Verify: ${s.tier2VerifiedCount}/${s.tier2FallbackCount} via-fallback chunks confirmed on chain${inconclusiveStr}`);
|
|
127
|
+
}
|
|
128
|
+
lines.push(` CAR sections: manifest ${fmtKb(s.section0Bytes)} KB \xB7 stable ${fmtMb(s.section1Bytes)} MB \xB7 volatile ${fmtMb(s.section2Bytes)} MB`);
|
|
129
|
+
if (s.chunksUploaded > 0) {
|
|
130
|
+
if (s.bytesSkipped > 0) {
|
|
131
|
+
lines.push(` Upload: ${fmtMb(s.bytesUploaded)} MB across ${s.chunksUploaded} chunks (vs ${fmtMb(s.carBytes)} MB if full deploy)`);
|
|
132
|
+
} else {
|
|
133
|
+
lines.push(` Upload: ${fmtMb(s.bytesUploaded)} MB across ${s.chunksUploaded} chunks`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (s.estimatedSecondsSaved > 0 || s.bytesSkipped > 0) {
|
|
137
|
+
lines.push(` Saved: ~${s.estimatedSecondsSaved} s and ${fmtMb(s.bytesSkipped)} MB`);
|
|
138
|
+
}
|
|
139
|
+
return lines.join("\n");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export {
|
|
143
|
+
computeStats,
|
|
144
|
+
telemetryAttributes,
|
|
145
|
+
renderSummary
|
|
146
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// src/run-state.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
|
|
6
|
+
// package.json
|
|
7
|
+
var package_default = {
|
|
8
|
+
name: "@parity/product-deploy",
|
|
9
|
+
version: "0.7.28-rc.0",
|
|
10
|
+
private: false,
|
|
11
|
+
repository: {
|
|
12
|
+
type: "git",
|
|
13
|
+
url: "https://github.com/paritytech/bulletin-deploy.git"
|
|
14
|
+
},
|
|
15
|
+
publishConfig: {
|
|
16
|
+
registry: "https://registry.npmjs.org",
|
|
17
|
+
access: "public"
|
|
18
|
+
},
|
|
19
|
+
type: "module",
|
|
20
|
+
main: "./dist/index.js",
|
|
21
|
+
types: "./dist/index.d.ts",
|
|
22
|
+
bin: {
|
|
23
|
+
"bulletin-deploy": "./bin/bulletin-deploy",
|
|
24
|
+
"bulletin-bootstrap": "./bin/bulletin-bootstrap"
|
|
25
|
+
},
|
|
26
|
+
exports: {
|
|
27
|
+
".": {
|
|
28
|
+
types: "./dist/index.d.ts",
|
|
29
|
+
import: "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./deploy": {
|
|
32
|
+
types: "./dist/deploy.d.ts",
|
|
33
|
+
import: "./dist/deploy.js"
|
|
34
|
+
},
|
|
35
|
+
"./manifest-roundtrip": {
|
|
36
|
+
types: "./dist/manifest-roundtrip.d.ts",
|
|
37
|
+
import: "./dist/manifest-roundtrip.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
files: [
|
|
41
|
+
"dist",
|
|
42
|
+
"bin",
|
|
43
|
+
"docs",
|
|
44
|
+
"assets",
|
|
45
|
+
"tools/release-retry-wrapper.mjs"
|
|
46
|
+
],
|
|
47
|
+
scripts: {
|
|
48
|
+
build: "tsup 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/reprove.ts src/personhood/bind-personal-id.ts src/personhood/claim-pgas.ts src/personhood/bind-paid-alias.ts src/personhood/bootstrap.ts src/manifest/types.ts src/manifest/schema.ts src/manifest/byte-budget.ts src/manifest/config-load.ts src/manifest/publish.ts --format esm --dts --clean --target node22",
|
|
49
|
+
"refresh-environments": "node scripts/refresh-environments.mjs",
|
|
50
|
+
"check:watched-dependencies": "node tools/check-watched-dependencies.mjs",
|
|
51
|
+
prepare: "npm run build",
|
|
52
|
+
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/watched-dependencies.test.js test/chunk-sharing-report.test.js test/product-manifest.test.js",
|
|
53
|
+
"test:e2e": "npm run build && node --test test/e2e.test.js",
|
|
54
|
+
"test:e2e:smoke": "bash scripts/e2e-pass.sh smoke",
|
|
55
|
+
"test:e2e:pr": "bash scripts/e2e-pass.sh pr",
|
|
56
|
+
"test:e2e:nightly": "bash scripts/e2e-pass.sh nightly",
|
|
57
|
+
benchmark: "npm run build && node benchmark.js"
|
|
58
|
+
},
|
|
59
|
+
dependencies: {
|
|
60
|
+
"@ipld/car": "^5.4.3",
|
|
61
|
+
"@ipld/dag-pb": "^4.1.3",
|
|
62
|
+
"@noble/hashes": "^1.7.2",
|
|
63
|
+
"@polkadot-api/metadata-builders": "^0.14.2",
|
|
64
|
+
"@polkadot-api/substrate-bindings": "^0.20.2",
|
|
65
|
+
"@polkadot-labs/hdkd": "^0.0.28",
|
|
66
|
+
"@polkadot-labs/hdkd-helpers": "^0.0.30",
|
|
67
|
+
"@polkadot/keyring": "^14.0.3",
|
|
68
|
+
"@polkadot/util-crypto": "^14.0.3",
|
|
69
|
+
"@sentry/node": "^9.14.0",
|
|
70
|
+
"ipfs-unixfs": "^11.2.0",
|
|
71
|
+
"ipfs-unixfs-importer": "^16.1.4",
|
|
72
|
+
jiti: "^2.4.2",
|
|
73
|
+
multiformats: "^13.4.1",
|
|
74
|
+
"polkadot-api": "^2.1.3",
|
|
75
|
+
verifiablejs: "^1.2.0",
|
|
76
|
+
viem: "^2.30.5"
|
|
77
|
+
},
|
|
78
|
+
devDependencies: {
|
|
79
|
+
"@types/node": "^22.0.0",
|
|
80
|
+
tsup: "^8.5.0",
|
|
81
|
+
typescript: "^5.9.3",
|
|
82
|
+
ws: "^8.20.1"
|
|
83
|
+
},
|
|
84
|
+
minimumVersion: "0.5.6",
|
|
85
|
+
engines: {
|
|
86
|
+
node: ">=22"
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/run-state.ts
|
|
91
|
+
var VERSION = package_default.version;
|
|
92
|
+
function resolveStateDir() {
|
|
93
|
+
if (process.platform === "darwin") {
|
|
94
|
+
return path.join(os.homedir(), "Library", "Application Support", "bulletin-deploy");
|
|
95
|
+
}
|
|
96
|
+
if (process.platform === "win32") {
|
|
97
|
+
const base2 = process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local");
|
|
98
|
+
return path.join(base2, "bulletin-deploy");
|
|
99
|
+
}
|
|
100
|
+
const base = process.env.XDG_STATE_HOME && process.env.XDG_STATE_HOME.length > 0 ? process.env.XDG_STATE_HOME : path.join(os.homedir(), ".local", "state");
|
|
101
|
+
return path.join(base, "bulletin-deploy");
|
|
102
|
+
}
|
|
103
|
+
function stateFilePath() {
|
|
104
|
+
return path.join(resolveStateDir(), "last-run.json");
|
|
105
|
+
}
|
|
106
|
+
function loadRunState() {
|
|
107
|
+
try {
|
|
108
|
+
const raw = fs.readFileSync(stateFilePath(), "utf-8");
|
|
109
|
+
const parsed = JSON.parse(raw);
|
|
110
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
111
|
+
return parsed;
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function writeRunState(patch) {
|
|
117
|
+
try {
|
|
118
|
+
const dir = resolveStateDir();
|
|
119
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
120
|
+
const file = stateFilePath();
|
|
121
|
+
let existing = {};
|
|
122
|
+
try {
|
|
123
|
+
const raw = fs.readFileSync(file, "utf-8");
|
|
124
|
+
const parsed = JSON.parse(raw);
|
|
125
|
+
if (parsed && typeof parsed === "object") existing = parsed;
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
const merged = { ...existing, ...patch };
|
|
129
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
130
|
+
fs.writeFileSync(tmp, JSON.stringify(merged), { encoding: "utf-8" });
|
|
131
|
+
fs.renameSync(tmp, file);
|
|
132
|
+
} catch {
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function isPidAlive(pid) {
|
|
136
|
+
try {
|
|
137
|
+
process.kill(pid, 0);
|
|
138
|
+
return true;
|
|
139
|
+
} catch (err) {
|
|
140
|
+
const code = err.code;
|
|
141
|
+
if (code === "EPERM") return true;
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function shouldSkipStaleWarning(prev) {
|
|
146
|
+
if (prev.pid && isPidAlive(prev.pid)) return true;
|
|
147
|
+
if (prev.toolVersion !== VERSION) return true;
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
function probablyOomRssMb(override) {
|
|
151
|
+
if (typeof override === "number" && Number.isFinite(override)) return override;
|
|
152
|
+
const env = process.env.BULLETIN_DEPLOY_OOM_HINT_RSS_MB;
|
|
153
|
+
const parsed = env != null ? Number(env) : NaN;
|
|
154
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
155
|
+
return 1800;
|
|
156
|
+
}
|
|
157
|
+
function shouldShowOomHint(prev) {
|
|
158
|
+
if (prev.lastPeakRssMb == null) return false;
|
|
159
|
+
return prev.lastPeakRssMb >= probablyOomRssMb();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export {
|
|
163
|
+
package_default,
|
|
164
|
+
VERSION,
|
|
165
|
+
resolveStateDir,
|
|
166
|
+
stateFilePath,
|
|
167
|
+
loadRunState,
|
|
168
|
+
writeRunState,
|
|
169
|
+
shouldSkipStaleWarning,
|
|
170
|
+
probablyOomRssMb,
|
|
171
|
+
shouldShowOomHint
|
|
172
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MANIFEST_DIR,
|
|
3
|
+
MANIFEST_PATH
|
|
4
|
+
} from "./chunk-S7EM5VMW.js";
|
|
5
|
+
|
|
6
|
+
// src/manifest-embed.ts
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
function writeAtomic(filePath, body) {
|
|
10
|
+
const tmp = `${filePath}.tmp`;
|
|
11
|
+
fs.writeFileSync(tmp, body);
|
|
12
|
+
fs.renameSync(tmp, filePath);
|
|
13
|
+
}
|
|
14
|
+
function ensureDir(dirPath) {
|
|
15
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
function writeEmbeddedManifestPlaceholder(buildDir, data) {
|
|
18
|
+
ensureDir(path.join(buildDir, MANIFEST_DIR));
|
|
19
|
+
const payload = {
|
|
20
|
+
version: data.version,
|
|
21
|
+
previous_contenthash: data.previousContenthash,
|
|
22
|
+
deployed_at: data.deployedAt,
|
|
23
|
+
framework: data.framework,
|
|
24
|
+
files: {},
|
|
25
|
+
stableBlockOrder: [],
|
|
26
|
+
blocks: [],
|
|
27
|
+
chunks: {}
|
|
28
|
+
};
|
|
29
|
+
writeAtomic(path.join(buildDir, MANIFEST_PATH), JSON.stringify(payload, null, 2));
|
|
30
|
+
}
|
|
31
|
+
function finaliseEmbeddedManifest(buildDir, data) {
|
|
32
|
+
ensureDir(path.join(buildDir, MANIFEST_DIR));
|
|
33
|
+
const payload = {
|
|
34
|
+
version: data.version,
|
|
35
|
+
previous_contenthash: data.previousContenthash,
|
|
36
|
+
deployed_at: data.deployedAt,
|
|
37
|
+
framework: data.framework,
|
|
38
|
+
files: data.files,
|
|
39
|
+
stableBlockOrder: data.stableBlockOrder,
|
|
40
|
+
blocks: data.blocks,
|
|
41
|
+
chunks: data.chunks
|
|
42
|
+
};
|
|
43
|
+
writeAtomic(path.join(buildDir, MANIFEST_PATH), JSON.stringify(payload, null, 2));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
writeEmbeddedManifestPlaceholder,
|
|
48
|
+
finaliseEmbeddedManifest
|
|
49
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// src/manifest/schema.ts
|
|
2
|
+
var ICON_FORMATS = ["jpeg", "png"];
|
|
3
|
+
var KIND_APP = "app";
|
|
4
|
+
var KIND_WIDGET = "widget";
|
|
5
|
+
var KIND_WORKER = "worker";
|
|
6
|
+
var EXECUTABLE_KINDS = [KIND_APP, KIND_WIDGET, KIND_WORKER];
|
|
7
|
+
var LABEL = String.raw`(?!-)[a-z0-9-]{1,63}(?<!-)`;
|
|
8
|
+
var DOMAIN_RE = new RegExp(`^${LABEL}(\\.${LABEL})*\\.dot$`, "i");
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function isNonEmptyString(value) {
|
|
13
|
+
return typeof value === "string" && value.length > 0;
|
|
14
|
+
}
|
|
15
|
+
function isAppVersion(value) {
|
|
16
|
+
if (!Array.isArray(value)) return false;
|
|
17
|
+
if (value.length !== 3 && value.length !== 4) return false;
|
|
18
|
+
if (!value.slice(0, 3).every((n) => typeof n === "number" && Number.isFinite(n) && n >= 0)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (value.length === 4 && typeof value[3] !== "string") return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function validateWidgetFields(input, p) {
|
|
25
|
+
const errors = [];
|
|
26
|
+
if ("description" in input && input.description !== void 0 && typeof input.description !== "string") {
|
|
27
|
+
errors.push(`${p}description must be a string when present`);
|
|
28
|
+
}
|
|
29
|
+
if (!isPlainObject(input.dimensions)) {
|
|
30
|
+
errors.push(`${p}dimensions must be an object`);
|
|
31
|
+
return errors;
|
|
32
|
+
}
|
|
33
|
+
const dims = input.dimensions;
|
|
34
|
+
if (!Array.isArray(dims.height) || dims.height.length === 0 || !dims.height.every((h) => typeof h === "number" && Number.isInteger(h) && h > 0)) {
|
|
35
|
+
errors.push(`${p}dimensions.height must be a non-empty array of positive integers`);
|
|
36
|
+
}
|
|
37
|
+
if ("width" in dims && dims.width !== void 0 && !(typeof dims.width === "number" && Number.isInteger(dims.width) && dims.width > 0)) {
|
|
38
|
+
errors.push(`${p}dimensions.width must be a positive integer when present`);
|
|
39
|
+
}
|
|
40
|
+
return errors;
|
|
41
|
+
}
|
|
42
|
+
function validateWorkerFields(input, p) {
|
|
43
|
+
const errors = [];
|
|
44
|
+
if (!isNonEmptyString(input.entrypoint)) {
|
|
45
|
+
errors.push(`${p}entrypoint must be a non-empty string`);
|
|
46
|
+
} else if (input.entrypoint.startsWith("/") || input.entrypoint.split("/").includes("..")) {
|
|
47
|
+
errors.push(`${p}entrypoint must be a relative path with no '..' segments`);
|
|
48
|
+
}
|
|
49
|
+
if (!isPlainObject(input.includes)) {
|
|
50
|
+
errors.push(`${p}includes must be an object`);
|
|
51
|
+
return errors;
|
|
52
|
+
}
|
|
53
|
+
const inc = input.includes;
|
|
54
|
+
if (typeof inc.chat !== "boolean") errors.push(`${p}includes.chat must be a boolean`);
|
|
55
|
+
if (typeof inc.pocket !== "boolean") errors.push(`${p}includes.pocket must be a boolean`);
|
|
56
|
+
if (inc.chat === false && inc.pocket === false) {
|
|
57
|
+
errors.push(`${p}includes must have at least one of chat / pocket = true`);
|
|
58
|
+
}
|
|
59
|
+
return errors;
|
|
60
|
+
}
|
|
61
|
+
function validateRootManifest(input) {
|
|
62
|
+
const errors = [];
|
|
63
|
+
if (!isPlainObject(input)) {
|
|
64
|
+
return { ok: false, errors: ["root manifest must be an object"] };
|
|
65
|
+
}
|
|
66
|
+
if (input.$v !== 1) errors.push(`root manifest $v must be 1 (got ${JSON.stringify(input.$v)})`);
|
|
67
|
+
if (!isNonEmptyString(input.displayName)) errors.push("root manifest displayName must be a non-empty string");
|
|
68
|
+
if (typeof input.description !== "string") errors.push("root manifest description must be a string");
|
|
69
|
+
if (!isPlainObject(input.icon)) {
|
|
70
|
+
errors.push("root manifest icon must be an object");
|
|
71
|
+
} else {
|
|
72
|
+
if (!isNonEmptyString(input.icon.cid)) errors.push("root manifest icon.cid must be a non-empty string");
|
|
73
|
+
if (!ICON_FORMATS.includes(input.icon.format)) {
|
|
74
|
+
errors.push(`root manifest icon.format must be one of ${ICON_FORMATS.join(", ")} (got ${JSON.stringify(input.icon.format)})`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return errors.length === 0 ? { ok: true, value: input } : { ok: false, errors };
|
|
78
|
+
}
|
|
79
|
+
function validateExecutableManifest(input) {
|
|
80
|
+
const errors = [];
|
|
81
|
+
if (!isPlainObject(input)) {
|
|
82
|
+
return { ok: false, errors: ["executable manifest must be an object"] };
|
|
83
|
+
}
|
|
84
|
+
if (input.$v !== 1) errors.push(`executable manifest $v must be 1 (got ${JSON.stringify(input.$v)})`);
|
|
85
|
+
if (!isAppVersion(input.appVersion)) {
|
|
86
|
+
errors.push("executable manifest appVersion must be [major, minor, patch] or [major, minor, patch, build]");
|
|
87
|
+
}
|
|
88
|
+
const kind = input.kind;
|
|
89
|
+
const p = "executable manifest ";
|
|
90
|
+
if (kind === KIND_APP) {
|
|
91
|
+
} else if (kind === KIND_WIDGET) {
|
|
92
|
+
errors.push(...validateWidgetFields(input, p));
|
|
93
|
+
} else if (kind === KIND_WORKER) {
|
|
94
|
+
errors.push(...validateWorkerFields(input, p));
|
|
95
|
+
} else {
|
|
96
|
+
errors.push(`${p}kind must be one of ${EXECUTABLE_KINDS.join(", ")} (got ${JSON.stringify(kind)})`);
|
|
97
|
+
}
|
|
98
|
+
return errors.length === 0 ? { ok: true, value: input } : { ok: false, errors };
|
|
99
|
+
}
|
|
100
|
+
function validateProductConfig(input) {
|
|
101
|
+
const errors = [];
|
|
102
|
+
if (!isPlainObject(input)) {
|
|
103
|
+
return { ok: false, errors: ["product config must be an object (did you forget `export default`?)"] };
|
|
104
|
+
}
|
|
105
|
+
if (!isNonEmptyString(input.domain) || !DOMAIN_RE.test(input.domain)) {
|
|
106
|
+
errors.push("product config domain must be a non-empty dotNS name ending in .dot");
|
|
107
|
+
}
|
|
108
|
+
if (!isNonEmptyString(input.displayName)) errors.push("product config displayName must be a non-empty string");
|
|
109
|
+
if (typeof input.description !== "string") errors.push("product config description must be a string");
|
|
110
|
+
if (!isPlainObject(input.icon)) {
|
|
111
|
+
errors.push("product config icon must be an object");
|
|
112
|
+
} else {
|
|
113
|
+
if (!isNonEmptyString(input.icon.path)) errors.push("product config icon.path must be a non-empty string");
|
|
114
|
+
if (!ICON_FORMATS.includes(input.icon.format)) {
|
|
115
|
+
errors.push(`product config icon.format must be one of ${ICON_FORMATS.join(", ")}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!Array.isArray(input.executables) || input.executables.length === 0) {
|
|
119
|
+
errors.push("product config executables must be a non-empty array");
|
|
120
|
+
} else {
|
|
121
|
+
const seenKinds = /* @__PURE__ */ new Set();
|
|
122
|
+
input.executables.forEach((exec, index) => {
|
|
123
|
+
errors.push(...validateExecutableConfig(exec, index));
|
|
124
|
+
if (isPlainObject(exec) && typeof exec.kind === "string") {
|
|
125
|
+
if (seenKinds.has(exec.kind)) errors.push(`executables[${index}]: duplicate kind '${exec.kind}'`);
|
|
126
|
+
seenKinds.add(exec.kind);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return errors.length === 0 ? { ok: true, value: input } : { ok: false, errors };
|
|
131
|
+
}
|
|
132
|
+
function validateExecutableConfig(input, index) {
|
|
133
|
+
const p = `executables[${index}].`;
|
|
134
|
+
if (!isPlainObject(input)) return [`executables[${index}] must be an object`];
|
|
135
|
+
const errors = [];
|
|
136
|
+
if (!isNonEmptyString(input.path)) errors.push(`${p}path must be a non-empty string`);
|
|
137
|
+
if (!isAppVersion(input.appVersion)) {
|
|
138
|
+
errors.push(`${p}appVersion must be [major, minor, patch] or [major, minor, patch, build]`);
|
|
139
|
+
}
|
|
140
|
+
const kind = input.kind;
|
|
141
|
+
if (kind === KIND_APP) {
|
|
142
|
+
} else if (kind === KIND_WIDGET) {
|
|
143
|
+
errors.push(...validateWidgetFields(input, p));
|
|
144
|
+
} else if (kind === KIND_WORKER) {
|
|
145
|
+
errors.push(...validateWorkerFields(input, p));
|
|
146
|
+
} else {
|
|
147
|
+
errors.push(`${p}kind must be one of ${EXECUTABLE_KINDS.join(", ")} (got ${JSON.stringify(kind)})`);
|
|
148
|
+
}
|
|
149
|
+
return errors;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export {
|
|
153
|
+
validateRootManifest,
|
|
154
|
+
validateExecutableManifest,
|
|
155
|
+
validateProductConfig
|
|
156
|
+
};
|