bulletin-deploy 0.7.27 → 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/README.md +10 -0
- package/assets/environments.json +2 -1
- package/bin/bulletin-deploy +136 -7
- package/dist/bug-report.js +4 -4
- package/dist/{chunk-I7YT2IAO.js → chunk-2DFF7JON.js} +5 -2
- package/dist/{chunk-FW4HEUMH.js → chunk-FLSEVRJH.js} +2 -2
- package/dist/chunk-GZD2UFLR.js +8 -0
- package/dist/{chunk-AXIJLJZK.js → chunk-H7KX6PV6.js} +1 -1
- package/dist/{chunk-6TIWKKDS.js → chunk-ITK6QMTD.js} +4 -3
- package/dist/chunk-LZJMVPYW.js +156 -0
- package/dist/{chunk-JIKWTM5P.js → chunk-MEPT7ICD.js} +66 -8
- package/dist/chunk-MMAZFJDG.js +91 -0
- package/dist/{chunk-MGU5I7H5.js → chunk-OITUIM2E.js} +2 -1
- package/dist/chunk-QH3KQZT7.js +164 -0
- package/dist/chunk-RI3ZLNPN.js +71 -0
- package/dist/{chunk-TILLNN33.js → chunk-TFG4YBCU.js} +334 -29
- package/dist/{chunk-W4R7HNRX.js → chunk-X6YLSXNW.js} +1 -1
- package/dist/chunk-probe.js +3 -3
- package/dist/deploy.d.ts +23 -5
- package/dist/deploy.js +12 -10
- package/dist/dotns.d.ts +123 -1
- package/dist/dotns.js +12 -4
- package/dist/environments.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +42 -8
- 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/memory-report.js +2 -2
- package/dist/merkle.js +8 -8
- package/dist/personhood/bind-paid-alias.js +2 -2
- package/dist/personhood/bind-personal-id.js +2 -2
- package/dist/personhood/bootstrap.js +12 -12
- package/dist/personhood/claim-pgas.js +2 -2
- package/dist/personhood/member-key.js +2 -2
- package/dist/personhood/people-client.js +4 -4
- package/dist/personhood/reprove.js +5 -5
- package/dist/run-state.js +1 -1
- package/dist/telemetry.js +2 -2
- package/dist/version-check.js +3 -3
- package/package.json +4 -3
- package/dist/{chunk-74ETPOKH.js → chunk-2VAUMZB2.js} +5 -5
- package/dist/{chunk-QHOZEY5X.js → chunk-5VZQ2KSU.js} +7 -7
- package/dist/{chunk-EJ5TNGAY.js → chunk-BMAEWZYV.js} +3 -3
- package/dist/{chunk-A5IQ5MKO.js → chunk-IDYGYIMH.js} +3 -3
package/README.md
CHANGED
|
@@ -215,6 +215,16 @@ await deploy("./dist", "my-app00.dot", { jsMerkle: true });
|
|
|
215
215
|
| `IPFS CLI not installed` | Install Kubo or switch to `--js-merkle`. |
|
|
216
216
|
| Previous deploy did not exit cleanly / OOM hint | Retry with a larger Node heap, for example `NODE_OPTIONS='--max-old-space-size=8192'`. |
|
|
217
217
|
|
|
218
|
+
## Contributing
|
|
219
|
+
|
|
220
|
+
New to the codebase? Start with **[ONBOARDING.md](ONBOARDING.md)** — it covers install, the mental model, the first task, and the working conventions for this repo (worktree-per-branch, squash-merge policy, never-delete-tests, where the per-directory rules live).
|
|
221
|
+
|
|
222
|
+
The team uses Claude Code as a primary tool. The repo ships team-shared Claude configuration: `.claude/skills/` (project-specific commands like `/e2e-local`, `/dotns-diagnose`, `/sentry-query`), `.claude/settings.json` (Bash allowlist), and per-directory `CLAUDE.md` files in `src/`, `sentry/`, `test/`, `tools/` that load on demand. Running `claude` inside this repo picks all of that up automatically.
|
|
223
|
+
|
|
224
|
+
Already have Claude Code installed? Clone, `npm install`, open `ONBOARDING.md`. New to Claude Code itself? The same doc covers install.
|
|
225
|
+
|
|
226
|
+
For maintainers and engineers familiar with the release flow, the canonical procedures live in the root [`CLAUDE.md`](CLAUDE.md): change workflow, dual-stage RC → stable release, post-release Sentry monitoring, and the squash-merge convention that satisfies branch protection without per-commit GPG signing.
|
|
227
|
+
|
|
218
228
|
## More Docs
|
|
219
229
|
|
|
220
230
|
- [Bootstrap and operator setup](docs/bootstrap.md)
|
package/assets/environments.json
CHANGED
|
@@ -80,7 +80,8 @@
|
|
|
80
80
|
"POP_RULES": "0x2002C1c15b88632Ad01c7770f6EbE1Ca05c8472E",
|
|
81
81
|
"STORE_FACTORY": "0x0DE5De70d61cc6b44B45d6595afDe8dB9b55bc31",
|
|
82
82
|
"LABEL_STORE_BEACON": "0xD033F7Ada687E8BC776928AB239505F9f0479Ce7",
|
|
83
|
-
"USER_STORE_BEACON": "0x7eD9b7D137Fa535965048F93b3B0248fEd2fcd32"
|
|
83
|
+
"USER_STORE_BEACON": "0x7eD9b7D137Fa535965048F93b3B0248fEd2fcd32",
|
|
84
|
+
"PUBLISHER": "0x1307fc02d308f879a16b1ae3a49b4927aed53649"
|
|
84
85
|
}
|
|
85
86
|
},
|
|
86
87
|
{
|
package/bin/bulletin-deploy
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY, isConnectionError } from "../dist/deploy.js";
|
|
3
|
+
import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY, isConnectionError, unpublish } from "../dist/deploy.js";
|
|
4
4
|
import { VERSION, setDeployAttribute, captureWarning, closeTelemetry, setRunStateActive, markRelaunchOomHintShown } from "../dist/telemetry.js";
|
|
5
5
|
import { handleFailedDeploy, handlePreflightVersionCheck, fetchVersionInfo, preReleaseWarning, checkNodeVersion } from "../dist/version-check.js";
|
|
6
6
|
import { setDeployContext, installLogCapture, buildCliFlagsSummary } from "../dist/bug-report.js";
|
|
@@ -36,19 +36,26 @@ for (let i = 0; i < args.length; i++) {
|
|
|
36
36
|
else if (args[i] === "--js-merkle") { flags.jsMerkle = true; }
|
|
37
37
|
else if (args[i] === "--input-car") { flags.inputCar = args[++i]; }
|
|
38
38
|
else if (args[i] === "--tag") { flags.tag = args[++i]; }
|
|
39
|
-
else if (args[i] === "--
|
|
40
|
-
else if (args[i] === "--description") { flags.description = args[++i]; }
|
|
39
|
+
else if (args[i] === "--config") { flags.config = args[++i]; }
|
|
41
40
|
else if (args[i] === "--gh-pages-mirror") { flags.ghPagesMirror = true; }
|
|
42
41
|
else if (args[i] === "--allow-large-deploy") { flags.allowLargeDeploy = true; }
|
|
43
42
|
else if (args[i] === "--reproducible") { flags.reproducibleSource = "commit"; }
|
|
44
43
|
else if (args[i].startsWith("--reproducible=")) { flags.reproducibleSource = args[i].slice("--reproducible=".length); }
|
|
45
44
|
else if (args[i] === "--dump-car") { flags.dumpCar = true; }
|
|
46
45
|
else if (args[i].startsWith("--dump-car=")) { flags.dumpCar = args[i].slice("--dump-car=".length); }
|
|
46
|
+
else if (args[i] === "--publish") { flags.publish = true; }
|
|
47
|
+
else if (args[i] === "--unpublish") { flags.unpublish = true; }
|
|
48
|
+
else if (args[i] === "--fail-on-publish-error") { flags.failOnPublishError = true; }
|
|
47
49
|
else if (args[i] === "--version" || args[i] === "-V") { flags.version = true; }
|
|
48
50
|
else if (args[i] === "--help" || args[i] === "-h") { flags.help = true; }
|
|
49
51
|
else { positional.push(args[i]); }
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
if (flags.publish && flags.unpublish) {
|
|
55
|
+
console.error("Error: --publish and --unpublish are mutually exclusive.");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
if (flags.version) {
|
|
53
60
|
console.log(`bulletin-deploy v${VERSION}`);
|
|
54
61
|
process.exit(0);
|
|
@@ -72,7 +79,74 @@ if (flags.listEnvironments) {
|
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
|
|
82
|
+
if (flags.unpublish) {
|
|
83
|
+
if (!flags.mnemonic && !process.env.MNEMONIC) {
|
|
84
|
+
console.error("Error: --unpublish requires --mnemonic (or MNEMONIC env var).");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const [domain] = positional;
|
|
88
|
+
if (!domain) {
|
|
89
|
+
console.error("Error: --unpublish requires a domain (e.g. my-app.dot)");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const result = await unpublish(domain, {
|
|
94
|
+
mnemonic: flags.mnemonic,
|
|
95
|
+
derivationPath: flags.derivationPath,
|
|
96
|
+
rpc: flags.rpc,
|
|
97
|
+
env: flags.env,
|
|
98
|
+
});
|
|
99
|
+
console.log(`Domain: ${result.domainName}`);
|
|
100
|
+
console.log(`Status: ${result.status}`);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
console.error(`Unpublish failed:`, e?.message ?? e);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
75
107
|
|
|
108
|
+
// `product` subcommand. Only `validate` is wired today. `publish` and `resolve` arrive in later phases.
|
|
109
|
+
if (positional[0] === "product") {
|
|
110
|
+
const verb = positional[1];
|
|
111
|
+
if (verb === "validate") {
|
|
112
|
+
try {
|
|
113
|
+
const { loadProductConfig, pessimisticSizePreflight, getTextRecordBudgetBytes } =
|
|
114
|
+
await import("../dist/index.js");
|
|
115
|
+
const explicitPath = positional[2];
|
|
116
|
+
const { config, sourcePath } = explicitPath
|
|
117
|
+
? await loadProductConfig({ path: explicitPath })
|
|
118
|
+
: await loadProductConfig();
|
|
119
|
+
console.log(`✓ Loaded ${sourcePath}`);
|
|
120
|
+
console.log(`✓ Schema ${config.domain} (${config.executables.length} executable${config.executables.length === 1 ? "" : "s"})`);
|
|
121
|
+
const budget = getTextRecordBudgetBytes();
|
|
122
|
+
const report = pessimisticSizePreflight(config, budget);
|
|
123
|
+
for (const check of report.checks) {
|
|
124
|
+
const tag = check.ok ? "✓" : "✗";
|
|
125
|
+
console.log(`${tag} ${check.key.padEnd(40)} ${check.bytes} B / ${check.budget} B`);
|
|
126
|
+
}
|
|
127
|
+
if (!report.ok) {
|
|
128
|
+
console.error("Pessimistic size preflight failed — at least one manifest exceeds the dotNS text-record budget.");
|
|
129
|
+
process.exit(EXIT_CODE_NO_RETRY);
|
|
130
|
+
}
|
|
131
|
+
console.log("Validate complete.");
|
|
132
|
+
process.exit(0);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.error(`Error: ${err?.message ?? err}`);
|
|
135
|
+
process.exit(err instanceof NonRetryableError ? EXIT_CODE_NO_RETRY : 1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (verb === "publish" || verb === "resolve") {
|
|
139
|
+
console.error(`'product ${verb}' is not implemented yet (planned for a later phase).`);
|
|
140
|
+
process.exit(2);
|
|
141
|
+
}
|
|
142
|
+
console.error(`Unknown product subcommand: ${verb ?? "(none)"}. Try: bulletin-deploy product validate [<config-path>]`);
|
|
143
|
+
process.exit(2);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (flags.publish && !flags.mnemonic && !process.env.MNEMONIC) {
|
|
147
|
+
console.error("Error: --publish requires --mnemonic (or MNEMONIC env var).");
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
76
150
|
|
|
77
151
|
if (flags.help || positional.length === 0) {
|
|
78
152
|
console.log(`bulletin-deploy v${VERSION}
|
|
@@ -97,10 +171,23 @@ Options:
|
|
|
97
171
|
--dump-car[=<path>] Save the pre-upload CAR file to disk. Default path: <buildDir>.bulletin.car.
|
|
98
172
|
Override path with =<path>. Also settable via BULLETIN_DEPLOY_DUMP_CAR env var.
|
|
99
173
|
--tag "..." Label deploy in telemetry (or set DEPLOY_TAG env var); see Telemetry in README
|
|
100
|
-
--
|
|
101
|
-
|
|
174
|
+
--config <path> Explicit path to a bulletin-deploy.config.ts (default: walk up from
|
|
175
|
+
<build-dir> looking for bulletin-deploy.config.{ts,js,mjs}). When a
|
|
176
|
+
config is found, deploy ALSO writes the manifest + executable text
|
|
177
|
+
records on <domain> and its app/widget/worker subnames.
|
|
102
178
|
--gh-pages-mirror After deploy, push the CAR to the current repo's gh-pages branch
|
|
103
179
|
at bulletin/<domain>.dot.car (opt-in; also set GH_PAGES_MIRROR=1)
|
|
180
|
+
--publish After deploy, list <domain> in the on-chain Publisher
|
|
181
|
+
registry (Publisher.publish). Only takes effect on envs
|
|
182
|
+
with a deployed Publisher (currently: paseo-next-v2).
|
|
183
|
+
Requires --mnemonic (the signer must own the label).
|
|
184
|
+
--unpublish Standalone mode: removes <domain> from the Publisher
|
|
185
|
+
registry (Publisher.unpublish). Skips the deploy.
|
|
186
|
+
Usage: bulletin-deploy --unpublish <domain.dot>
|
|
187
|
+
Requires --mnemonic. Mutually exclusive with --publish.
|
|
188
|
+
--fail-on-publish-error
|
|
189
|
+
Exit non-zero if --publish fails after a successful deploy.
|
|
190
|
+
Default: non-fatal (warning logged, exit 0).
|
|
104
191
|
--version Show version
|
|
105
192
|
--help Show this help`);
|
|
106
193
|
process.exit(0);
|
|
@@ -269,11 +356,11 @@ try {
|
|
|
269
356
|
inputCar: flags.inputCar,
|
|
270
357
|
tag: flags.tag,
|
|
271
358
|
ghPagesMirror: flags.ghPagesMirror,
|
|
272
|
-
name: flags.name,
|
|
273
|
-
description: flags.description,
|
|
274
359
|
allowLargeDeploy: flags.allowLargeDeploy,
|
|
275
360
|
reproducibleSource: flags.reproducibleSource,
|
|
276
361
|
dumpCar: flags.dumpCar,
|
|
362
|
+
publish: flags.publish,
|
|
363
|
+
failOnPublishError: flags.failOnPublishError,
|
|
277
364
|
});
|
|
278
365
|
|
|
279
366
|
const output = process.env.GITHUB_OUTPUT;
|
|
@@ -285,6 +372,48 @@ try {
|
|
|
285
372
|
console.log(`CID: ${result.cid}`);
|
|
286
373
|
console.log(`Domain: ${result.domainName}`);
|
|
287
374
|
|
|
375
|
+
// Opt-in manifest publish on top of the legacy contenthash deploy.
|
|
376
|
+
//
|
|
377
|
+
// Walks up from <build-dir> (or honours --config) looking for
|
|
378
|
+
// bulletin-deploy.config.{ts,js,mjs}. When found, writes the root + per-
|
|
379
|
+
// executable text records, otherwise the legacy flow returns unchanged.
|
|
380
|
+
{
|
|
381
|
+
const { tryLoadProductConfig, publishManifest } = await import("../dist/index.js");
|
|
382
|
+
const path = await import("node:path");
|
|
383
|
+
const buildDirAbs = path.resolve(buildDir);
|
|
384
|
+
const loaded = await tryLoadProductConfig(
|
|
385
|
+
flags.config ? { path: flags.config } : { cwd: buildDirAbs, walkUp: true },
|
|
386
|
+
);
|
|
387
|
+
if (loaded) {
|
|
388
|
+
try {
|
|
389
|
+
await publishManifest({
|
|
390
|
+
loaded,
|
|
391
|
+
domain,
|
|
392
|
+
buildDirCid: { absPath: buildDirAbs, cid: result.cid },
|
|
393
|
+
env: flags.env,
|
|
394
|
+
rpc: flags.rpc,
|
|
395
|
+
mnemonic: flags.mnemonic,
|
|
396
|
+
derivationPath: flags.derivationPath,
|
|
397
|
+
});
|
|
398
|
+
} catch (err) {
|
|
399
|
+
console.error(`Manifest publish failed: ${err?.message ?? err}`);
|
|
400
|
+
process.exit(err instanceof NonRetryableError ? EXIT_CODE_NO_RETRY : 1);
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
const where = flags.config
|
|
404
|
+
? `--config ${flags.config}`
|
|
405
|
+
: `bulletin-deploy.config.{ts,js,mjs} via walking up from ${buildDirAbs}`;
|
|
406
|
+
console.log("");
|
|
407
|
+
console.log(`⚠ No bulletin-deploy.config.ts found (${where}).`);
|
|
408
|
+
console.log(` ${domain} was published as legacy contenthash only.`);
|
|
409
|
+
console.log(" Add a bulletin-deploy.config.ts to enable the product manifest:");
|
|
410
|
+
console.log(" • product icon, displayName, description on the base name");
|
|
411
|
+
console.log(" • per-modality subnames (app.<id>.dot, widget.<id>.dot, worker.<id>.dot)");
|
|
412
|
+
console.log(" • each modality’s archive CID on its subname contenthash");
|
|
413
|
+
console.log(" See: https://github.com/paritytech/triangle-js-sdks (Product Manifest RFC)");
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
288
417
|
if (!flags.help && !flags.version) {
|
|
289
418
|
try { writeRunState({ status: "succeeded", endedAt: Date.now() }); } catch {}
|
|
290
419
|
}
|
package/dist/bug-report.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
offerBugReport,
|
|
10
10
|
scrubSecrets,
|
|
11
11
|
setDeployContext
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-2DFF7JON.js";
|
|
13
|
+
import "./chunk-H7KX6PV6.js";
|
|
14
|
+
import "./chunk-FLSEVRJH.js";
|
|
15
|
+
import "./chunk-ITK6QMTD.js";
|
|
16
16
|
export {
|
|
17
17
|
buildCliFlagsSummary,
|
|
18
18
|
buildLabels,
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
classifyErrorArea,
|
|
3
3
|
isInteractive,
|
|
4
4
|
promptYesNo
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-H7KX6PV6.js";
|
|
6
6
|
import {
|
|
7
7
|
VERSION,
|
|
8
8
|
getCurrentSentryTraceId
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-FLSEVRJH.js";
|
|
10
10
|
|
|
11
11
|
// src/bug-report.ts
|
|
12
12
|
import { execSync, execFileSync } from "child_process";
|
|
@@ -76,6 +76,9 @@ function buildCliFlagsSummary(flags) {
|
|
|
76
76
|
const parts = [];
|
|
77
77
|
if (flags.jsMerkle) parts.push("--js-merkle");
|
|
78
78
|
if (flags.ghPagesMirror) parts.push("--gh-pages-mirror");
|
|
79
|
+
if (flags.publish) parts.push("--publish");
|
|
80
|
+
if (flags.unpublish) parts.push("--unpublish");
|
|
81
|
+
if (flags.failOnPublishError) parts.push("--fail-on-publish-error");
|
|
79
82
|
if (flags.poolSize != null) parts.push(`--pool-size ${String(flags.poolSize)}`);
|
|
80
83
|
if (typeof flags.tag === "string" && flags.tag) parts.push(`--tag ${flags.tag}`);
|
|
81
84
|
if (flags.mnemonic) parts.push("--mnemonic <set>");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
package_default,
|
|
3
3
|
writeRunState
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ITK6QMTD.js";
|
|
5
5
|
|
|
6
6
|
// src/memory-report.ts
|
|
7
7
|
import * as fs2 from "fs";
|
|
@@ -299,7 +299,7 @@ function computeDeployOutcome(errorCategory, isSad, sadReason) {
|
|
|
299
299
|
var ERROR_KIND_RULES = [
|
|
300
300
|
[/Contract reverted|Contract execution would revert|revert(?:ed|ing)?\s*\(flags=[0-9]+\)/i, "contract-revert"],
|
|
301
301
|
[/timed out after \d+s waiting for block|Transaction not included after \d+s|Transaction did not settle within/i, "chain-timeout"],
|
|
302
|
-
[/\bstale\b.*nonce|nonce.*\bstale\b|"type"\s*:\s*"Future"|Invalid::Future|tx rejected by pool/i, "nonce-stale"],
|
|
302
|
+
[/\bstale\b.*nonce|nonce.*\bstale\b|"type"\s*:\s*"(?:Future|Stale)"|Invalid::Future|tx rejected by pool/i, "nonce-stale"],
|
|
303
303
|
[/heartbeat timeout|WS halt|Unable to connect|ChainHead disjointed|websocket.*closed|socket closed|disconnect/i, "connection"],
|
|
304
304
|
[/requires ProofOfPersonhoodFull,\s*but this signer is NoStatus/i, "naming.pop_required"],
|
|
305
305
|
[/Domain\s+\S+\.dot\s+is already owned by\s+0x[a-fA-F0-9]+/i, "naming.already_owned"],
|
|
@@ -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.7.
|
|
9
|
+
version: "0.7.28-rc.0",
|
|
10
10
|
private: false,
|
|
11
11
|
repository: {
|
|
12
12
|
type: "git",
|
|
@@ -45,11 +45,11 @@ var package_default = {
|
|
|
45
45
|
"tools/release-retry-wrapper.mjs"
|
|
46
46
|
],
|
|
47
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 --format esm --dts --clean --target node22",
|
|
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
49
|
"refresh-environments": "node scripts/refresh-environments.mjs",
|
|
50
50
|
"check:watched-dependencies": "node tools/check-watched-dependencies.mjs",
|
|
51
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",
|
|
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
53
|
"test:e2e": "npm run build && node --test test/e2e.test.js",
|
|
54
54
|
"test:e2e:smoke": "bash scripts/e2e-pass.sh smoke",
|
|
55
55
|
"test:e2e:pr": "bash scripts/e2e-pass.sh pr",
|
|
@@ -69,6 +69,7 @@ var package_default = {
|
|
|
69
69
|
"@sentry/node": "^9.14.0",
|
|
70
70
|
"ipfs-unixfs": "^11.2.0",
|
|
71
71
|
"ipfs-unixfs-importer": "^16.1.4",
|
|
72
|
+
jiti: "^2.4.2",
|
|
72
73
|
multiformats: "^13.4.1",
|
|
73
74
|
"polkadot-api": "^2.1.3",
|
|
74
75
|
verifiablejs: "^1.2.0",
|
|
@@ -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
|
+
};
|
|
@@ -20,21 +20,22 @@ import {
|
|
|
20
20
|
} from "./chunk-S7EM5VMW.js";
|
|
21
21
|
import {
|
|
22
22
|
setDeployContext
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-2DFF7JON.js";
|
|
24
24
|
import {
|
|
25
25
|
probeChunks
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-X6YLSXNW.js";
|
|
27
27
|
import {
|
|
28
28
|
packSection
|
|
29
29
|
} from "./chunk-C2TS5MER.js";
|
|
30
30
|
import {
|
|
31
31
|
DotNS,
|
|
32
|
+
PublisherNotSupportedError,
|
|
32
33
|
TX_TIMEOUT_MS,
|
|
33
34
|
fetchNonce,
|
|
34
35
|
parseDomainName,
|
|
35
36
|
popStatusName,
|
|
36
37
|
verifyNonceAdvanced
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-TFG4YBCU.js";
|
|
38
39
|
import {
|
|
39
40
|
derivePoolAccounts,
|
|
40
41
|
detectTestnet,
|
|
@@ -56,13 +57,13 @@ import {
|
|
|
56
57
|
truncateAddress,
|
|
57
58
|
withDeploySpan,
|
|
58
59
|
withSpan
|
|
59
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-FLSEVRJH.js";
|
|
60
61
|
import {
|
|
61
62
|
DEFAULT_ENV_ID,
|
|
62
63
|
getPopSelfServeConfig,
|
|
63
64
|
loadEnvironments,
|
|
64
65
|
resolveEndpoints
|
|
65
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-OITUIM2E.js";
|
|
66
67
|
import {
|
|
67
68
|
NonRetryableError
|
|
68
69
|
} from "./chunk-ZOC4GITL.js";
|
|
@@ -1482,6 +1483,62 @@ function assertSubdomainOwnerMatchesSigner(result, signerEvmAddress, sublabel, p
|
|
|
1482
1483
|
);
|
|
1483
1484
|
}
|
|
1484
1485
|
}
|
|
1486
|
+
async function publish(dotns, parsed, failOnError) {
|
|
1487
|
+
console.log("\n" + "=".repeat(60));
|
|
1488
|
+
console.log("Publish");
|
|
1489
|
+
console.log("=".repeat(60));
|
|
1490
|
+
if (parsed.isSubdomain) {
|
|
1491
|
+
console.log(` Subdomains are not supported by the Publisher registry \u2014 skipping.`);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
try {
|
|
1495
|
+
const result = await dotns.publishLabel(parsed.label);
|
|
1496
|
+
setDeployAttribute("deploy.publish.status", result.status);
|
|
1497
|
+
if (result.txHash) setDeployAttribute("deploy.publish.tx", result.txHash);
|
|
1498
|
+
console.log(` Status: ${result.status}`);
|
|
1499
|
+
} catch (e) {
|
|
1500
|
+
if (e instanceof PublisherNotSupportedError) {
|
|
1501
|
+
console.log(` Skipped: ${e.message}`);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
setDeployAttribute("deploy.publish.status", "failed");
|
|
1505
|
+
if (failOnError) throw e;
|
|
1506
|
+
const msg = e?.message ?? String(e);
|
|
1507
|
+
console.error(` Warning: publish failed (non-fatal): ${msg}`);
|
|
1508
|
+
captureWarning("publish failed", { error: msg.slice(0, 200) });
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
async function unpublish(domainName, options = {}) {
|
|
1512
|
+
const envId = options.env ?? DEFAULT_ENV_ID;
|
|
1513
|
+
const { doc } = await loadEnvironments();
|
|
1514
|
+
const resolved = resolveEndpoints(doc, envId);
|
|
1515
|
+
const popSelfServe = getPopSelfServeConfig(doc, envId);
|
|
1516
|
+
const parsed = parseDomainName(domainName);
|
|
1517
|
+
if (parsed.isSubdomain) {
|
|
1518
|
+
throw new Error(`Subdomains are not supported by the Publisher registry. To unpublish ${parsed.parentLabel}.dot (which controls ${domainName}), pass that label directly.`);
|
|
1519
|
+
}
|
|
1520
|
+
const label = parsed.label;
|
|
1521
|
+
const dotns = new DotNS();
|
|
1522
|
+
try {
|
|
1523
|
+
await dotns.connect(resolveDotnsConnectOptions(
|
|
1524
|
+
{ mnemonic: options.mnemonic, derivationPath: options.derivationPath },
|
|
1525
|
+
resolved.assetHub,
|
|
1526
|
+
resolved.autoAccountMapping,
|
|
1527
|
+
resolved.contracts,
|
|
1528
|
+
resolved.nativeToEthRatio,
|
|
1529
|
+
envId,
|
|
1530
|
+
popSelfServe,
|
|
1531
|
+
resolved.registerStorageDeposit
|
|
1532
|
+
));
|
|
1533
|
+
const result = await dotns.unpublishLabel(label);
|
|
1534
|
+
return { domainName: `${label}.dot`, status: result.status, txHash: result.txHash };
|
|
1535
|
+
} finally {
|
|
1536
|
+
try {
|
|
1537
|
+
dotns.disconnect();
|
|
1538
|
+
} catch {
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1485
1542
|
async function deploy(content, domainName = null, options = {}) {
|
|
1486
1543
|
const envId = options.env ?? DEFAULT_ENV_ID;
|
|
1487
1544
|
let envBulletin = [DEFAULT_BULLETIN_RPC];
|
|
@@ -1824,9 +1881,9 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
1824
1881
|
}
|
|
1825
1882
|
const contenthashHex = `0x${encodeContenthash(cid)}`;
|
|
1826
1883
|
await dotns.setContenthash(name, contenthashHex);
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1884
|
+
if (options.publish && parsed) {
|
|
1885
|
+
await publish(dotns, parsed, options.failOnPublishError);
|
|
1886
|
+
}
|
|
1830
1887
|
dotns.disconnect();
|
|
1831
1888
|
});
|
|
1832
1889
|
if (options.ghPagesMirror) {
|
|
@@ -2306,5 +2363,6 @@ export {
|
|
|
2306
2363
|
resolveDotnsConnectOptions,
|
|
2307
2364
|
estimateUploadBytes,
|
|
2308
2365
|
assertSubdomainOwnerMatchesSigner,
|
|
2366
|
+
unpublish,
|
|
2309
2367
|
deploy
|
|
2310
2368
|
};
|