pubm 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -453,7 +453,7 @@ __export(base_exports, {
453
453
  scrollDown: () => scrollDown,
454
454
  scrollUp: () => scrollUp
455
455
  });
456
- import process6 from "node:process";
456
+ import process4 from "node:process";
457
457
  var ESC, OSC, BEL, SEP, isTerminalApp, isWindows3, cwdFunction, cursorTo, cursorMove, cursorUp, cursorDown, cursorForward, cursorBackward, cursorLeft, cursorSavePosition, cursorRestorePosition, cursorGetPosition, cursorNextLine, cursorPrevLine, cursorHide, cursorShow, eraseLines, eraseEndLine, eraseStartLine, eraseLine, eraseDown, eraseUp, eraseScreen, scrollUp, scrollDown, clearScreen, clearTerminal, enterAlternativeScreen, exitAlternativeScreen, beep, link, image, iTerm;
458
458
  var init_base = __esm({
459
459
  "node_modules/.pnpm/ansi-escapes@7.0.0/node_modules/ansi-escapes/base.js"() {
@@ -463,11 +463,11 @@ var init_base = __esm({
463
463
  OSC = "\x1B]";
464
464
  BEL = "\x07";
465
465
  SEP = ";";
466
- isTerminalApp = !isBrowser && process6.env.TERM_PROGRAM === "Apple_Terminal";
467
- isWindows3 = !isBrowser && process6.platform === "win32";
466
+ isTerminalApp = !isBrowser && process4.env.TERM_PROGRAM === "Apple_Terminal";
467
+ isWindows3 = !isBrowser && process4.platform === "win32";
468
468
  cwdFunction = isBrowser ? () => {
469
469
  throw new Error("`process.cwd()` only works in Node.js, not the browser.");
470
- } : process6.cwd;
470
+ } : process4.cwd;
471
471
  cursorTo = (x, y) => {
472
472
  if (typeof x !== "number") {
473
473
  throw new TypeError("The `x` argument is required");
@@ -708,7 +708,7 @@ var init_signals = __esm({
708
708
  });
709
709
 
710
710
  // node_modules/.pnpm/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.js
711
- var processOk, kExitEmitter, global, ObjectDefineProperty, Emitter, SignalExitBase, signalExitWrap, SignalExitFallback, _hupSig, _emitter, _process, _originalProcessEmit, _originalProcessReallyExit, _sigListeners, _loaded, _SignalExit_instances, processReallyExit_fn, processEmit_fn, SignalExit, process7, onExit, load, unload;
711
+ var processOk, kExitEmitter, global, ObjectDefineProperty, Emitter, SignalExitBase, signalExitWrap, SignalExitFallback, _hupSig, _emitter, _process, _originalProcessEmit, _originalProcessReallyExit, _sigListeners, _loaded, _SignalExit_instances, processReallyExit_fn, processEmit_fn, SignalExit, process5, onExit, load, unload;
712
712
  var init_mjs = __esm({
713
713
  "node_modules/.pnpm/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.js"() {
714
714
  "use strict";
@@ -801,7 +801,7 @@ var init_mjs = __esm({
801
801
  // "SIGHUP" throws an `ENOSYS` error on Windows,
802
802
  // so use a supported signal instead
803
803
  /* c8 ignore start */
804
- __privateAdd(this, _hupSig, process7.platform === "win32" ? "SIGINT" : "SIGHUP");
804
+ __privateAdd(this, _hupSig, process5.platform === "win32" ? "SIGINT" : "SIGHUP");
805
805
  /* c8 ignore stop */
806
806
  __privateAdd(this, _emitter, new Emitter());
807
807
  __privateAdd(this, _process);
@@ -918,7 +918,7 @@ var init_mjs = __esm({
918
918
  return og.call(__privateGet(this, _process), ev, ...args);
919
919
  }
920
920
  };
921
- process7 = globalThis.process;
921
+ process5 = globalThis.process;
922
922
  ({
923
923
  onExit: (
924
924
  /**
@@ -952,19 +952,19 @@ var init_mjs = __esm({
952
952
  */
953
953
  unload
954
954
  )
955
- } = signalExitWrap(processOk(process7) ? new SignalExit(process7) : new SignalExitFallback()));
955
+ } = signalExitWrap(processOk(process5) ? new SignalExit(process5) : new SignalExitFallback()));
956
956
  }
957
957
  });
958
958
 
959
959
  // node_modules/.pnpm/restore-cursor@5.1.0/node_modules/restore-cursor/index.js
960
- import process8 from "node:process";
960
+ import process6 from "node:process";
961
961
  var terminal, restoreCursor, restore_cursor_default;
962
962
  var init_restore_cursor = __esm({
963
963
  "node_modules/.pnpm/restore-cursor@5.1.0/node_modules/restore-cursor/index.js"() {
964
964
  "use strict";
965
965
  init_onetime();
966
966
  init_mjs();
967
- terminal = process8.stderr.isTTY ? process8.stderr : process8.stdout.isTTY ? process8.stdout : void 0;
967
+ terminal = process6.stderr.isTTY ? process6.stderr : process6.stdout.isTTY ? process6.stdout : void 0;
968
968
  restoreCursor = terminal ? onetime_default(() => {
969
969
  onExit(() => {
970
970
  terminal.write("\x1B[?25h");
@@ -976,7 +976,7 @@ var init_restore_cursor = __esm({
976
976
  });
977
977
 
978
978
  // node_modules/.pnpm/cli-cursor@5.0.0/node_modules/cli-cursor/index.js
979
- import process9 from "node:process";
979
+ import process7 from "node:process";
980
980
  var isHidden, cliCursor, cli_cursor_default;
981
981
  var init_cli_cursor = __esm({
982
982
  "node_modules/.pnpm/cli-cursor@5.0.0/node_modules/cli-cursor/index.js"() {
@@ -984,14 +984,14 @@ var init_cli_cursor = __esm({
984
984
  init_restore_cursor();
985
985
  isHidden = false;
986
986
  cliCursor = {};
987
- cliCursor.show = (writableStream = process9.stderr) => {
987
+ cliCursor.show = (writableStream = process7.stderr) => {
988
988
  if (!writableStream.isTTY) {
989
989
  return;
990
990
  }
991
991
  isHidden = false;
992
992
  writableStream.write("\x1B[?25h");
993
993
  };
994
- cliCursor.hide = (writableStream = process9.stderr) => {
994
+ cliCursor.hide = (writableStream = process7.stderr) => {
995
995
  if (!writableStream.isTTY) {
996
996
  return;
997
997
  }
@@ -1667,7 +1667,7 @@ __export(log_update_exports, {
1667
1667
  default: () => log_update_default,
1668
1668
  logUpdateStderr: () => logUpdateStderr
1669
1669
  });
1670
- import process10 from "node:process";
1670
+ import process8 from "node:process";
1671
1671
  function createLogUpdate(stream, { showCursor = false } = {}) {
1672
1672
  let previousLineCount = 0;
1673
1673
  let previousWidth = getWidth(stream);
@@ -1721,9 +1721,9 @@ var init_log_update = __esm({
1721
1721
  const toRemove = Math.max(0, lines.length - terminalHeight);
1722
1722
  return toRemove ? sliceAnsi(text, stripAnsi(lines.slice(0, toRemove).join("\n")).length + 1) : text;
1723
1723
  };
1724
- logUpdate = createLogUpdate(process10.stdout);
1724
+ logUpdate = createLogUpdate(process8.stdout);
1725
1725
  log_update_default = logUpdate;
1726
- logUpdateStderr = createLogUpdate(process10.stderr);
1726
+ logUpdateStderr = createLogUpdate(process8.stderr);
1727
1727
  }
1728
1728
  });
1729
1729
 
@@ -2225,178 +2225,6 @@ function registerPreCommand(cli2) {
2225
2225
  });
2226
2226
  }
2227
2227
 
2228
- // src/commands/snapshot.ts
2229
- function registerSnapshotCommand(cli2) {
2230
- cli2.command("snapshot [tag]", "Create a snapshot release").action(async (tag) => {
2231
- console.log(`pubm snapshot ${tag ?? ""} \u2014 coming in next phase`);
2232
- });
2233
- }
2234
-
2235
- // src/changeset/status.ts
2236
- import process5 from "node:process";
2237
-
2238
- // src/changeset/bump-utils.ts
2239
- var BUMP_ORDER = {
2240
- patch: 0,
2241
- minor: 1,
2242
- major: 2
2243
- };
2244
- function maxBump(a2, b) {
2245
- return BUMP_ORDER[a2] >= BUMP_ORDER[b] ? a2 : b;
2246
- }
2247
-
2248
- // src/changeset/reader.ts
2249
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
2250
- import path5 from "node:path";
2251
- import process4 from "node:process";
2252
-
2253
- // src/changeset/parser.ts
2254
- import { parse as parseYaml } from "yaml";
2255
- var VALID_BUMP_TYPES = /* @__PURE__ */ new Set(["patch", "minor", "major"]);
2256
- function parseChangeset(content, fileName) {
2257
- const frontmatterRegex = /^---\n([\s\S]*?)---/;
2258
- const match = content.match(frontmatterRegex);
2259
- if (!match) {
2260
- throw new Error(
2261
- `Invalid changeset format in "${fileName}": missing frontmatter`
2262
- );
2263
- }
2264
- const yamlContent = match[1];
2265
- const body = content.slice(match[0].length).trim();
2266
- const parsed = parseYaml(yamlContent);
2267
- const releases = [];
2268
- if (parsed) {
2269
- for (const [name, type] of Object.entries(parsed)) {
2270
- if (!VALID_BUMP_TYPES.has(type)) {
2271
- throw new Error(
2272
- `Invalid bump type "${type}" for package "${name}" in "${fileName}". Expected: patch, minor, or major.`
2273
- );
2274
- }
2275
- releases.push({ name, type });
2276
- }
2277
- }
2278
- const id = fileName.replace(/\.md$/, "");
2279
- return {
2280
- id,
2281
- summary: body,
2282
- releases
2283
- };
2284
- }
2285
-
2286
- // src/changeset/reader.ts
2287
- function readChangesets(cwd = process4.cwd()) {
2288
- const changesetsDir = path5.join(cwd, ".pubm", "changesets");
2289
- if (!existsSync4(changesetsDir)) {
2290
- return [];
2291
- }
2292
- const files = readdirSync2(changesetsDir);
2293
- const changesets = [];
2294
- for (const file of files) {
2295
- if (!file.endsWith(".md") || file === "README.md") {
2296
- continue;
2297
- }
2298
- const filePath = path5.join(changesetsDir, file);
2299
- const content = readFileSync2(filePath, "utf-8");
2300
- changesets.push(parseChangeset(content, file));
2301
- }
2302
- return changesets;
2303
- }
2304
-
2305
- // src/changeset/status.ts
2306
- function getStatus(cwd = process5.cwd()) {
2307
- const changesets = readChangesets(cwd);
2308
- const packages = /* @__PURE__ */ new Map();
2309
- for (const changeset of changesets) {
2310
- for (const release of changeset.releases) {
2311
- const existing = packages.get(release.name);
2312
- if (existing) {
2313
- existing.bumpType = maxBump(existing.bumpType, release.type);
2314
- existing.changesetCount += 1;
2315
- existing.summaries.push(changeset.summary);
2316
- } else {
2317
- packages.set(release.name, {
2318
- bumpType: release.type,
2319
- changesetCount: 1,
2320
- summaries: [changeset.summary]
2321
- });
2322
- }
2323
- }
2324
- }
2325
- return {
2326
- packages,
2327
- changesets,
2328
- hasChangesets: changesets.length > 0
2329
- };
2330
- }
2331
-
2332
- // src/commands/status.ts
2333
- function registerStatusCommand(cli2) {
2334
- cli2.command("status", "Show pending changeset status").option("--verbose", "Show full changeset contents").option("--since <ref>", "Only check changesets since git ref").action(async (options) => {
2335
- const status = getStatus();
2336
- if (!status.hasChangesets) {
2337
- if (options.since) {
2338
- console.log("No changesets found.");
2339
- process.exit(1);
2340
- }
2341
- console.log("No pending changesets.");
2342
- return;
2343
- }
2344
- console.log("Pending changesets:");
2345
- for (const [name, info] of status.packages) {
2346
- console.log(
2347
- ` ${name}: ${info.bumpType} (${info.changesetCount} changeset${info.changesetCount > 1 ? "s" : ""})`
2348
- );
2349
- if (options.verbose) {
2350
- for (const summary of info.summaries) {
2351
- console.log(` - ${summary}`);
2352
- }
2353
- }
2354
- }
2355
- });
2356
- }
2357
-
2358
- // src/commands/update.ts
2359
- import { UpdateKit } from "update-kit";
2360
- function registerUpdateCommand(cli2) {
2361
- cli2.command("update", "Update pubm to the latest version").action(async () => {
2362
- const kit = await UpdateKit.create({
2363
- sources: [{ type: "npm", packageName: "pubm" }],
2364
- delegateMode: "execute"
2365
- });
2366
- const result = await kit.autoUpdate({
2367
- onProgress: (p) => {
2368
- if (p.phase === "downloading" && p.totalBytes) {
2369
- const pct = Math.round(p.bytesDownloaded / p.totalBytes * 100);
2370
- process.stderr.write(`\rDownloading... ${pct}%`);
2371
- } else {
2372
- console.error(`${p.phase}...`);
2373
- }
2374
- }
2375
- });
2376
- switch (result.kind) {
2377
- case "success":
2378
- console.log(
2379
- `Updated from ${result.fromVersion} to ${result.toVersion}`
2380
- );
2381
- break;
2382
- case "needs-restart":
2383
- console.log(result.message);
2384
- break;
2385
- case "failed":
2386
- console.error(`Update failed: ${result.error.message}`);
2387
- process.exitCode = 1;
2388
- break;
2389
- }
2390
- });
2391
- }
2392
-
2393
- // src/commands/version-cmd.ts
2394
- function registerVersionCommand(cli2) {
2395
- cli2.command("version", "Consume changesets and bump versions").action(async () => {
2396
- console.log("pubm version \u2014 coming in next phase");
2397
- });
2398
- }
2399
-
2400
2228
  // node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.mjs
2401
2229
  var import_index = __toESM(require_eventemitter3(), 1);
2402
2230
  var eventemitter3_default = import_index.default;
@@ -4865,56 +4693,435 @@ function consoleError(error) {
4865
4693
  `);
4866
4694
  }
4867
4695
 
4868
- // src/git.ts
4869
- import semver from "semver";
4696
+ // src/tasks/preflight.ts
4697
+ import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
4870
4698
  import { exec as exec2 } from "tinyexec";
4871
- var GitError = class extends AbstractError {
4699
+
4700
+ // src/utils/db.ts
4701
+ import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
4702
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync4 } from "node:fs";
4703
+ import path5 from "node:path";
4704
+ var a = "aes-256-cbc";
4705
+ var n = statSync(import.meta.dirname);
4706
+ var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
4707
+ var l = createHash("md5").update(k).digest();
4708
+ function e(e2, f) {
4709
+ const c = createCipheriv(a, createHash("sha-256").update(f).digest(), l);
4710
+ return c.update(e2, "utf8", "hex") + c.final("hex");
4711
+ }
4712
+ function d(g, h) {
4713
+ const d2 = createDecipheriv(a, createHash("sha-256").update(h).digest(), l);
4714
+ return d2.update(g, "hex", "utf8") + d2.final("utf8");
4715
+ }
4716
+ var Db = class {
4872
4717
  constructor() {
4873
- super(...arguments);
4874
- __publicField(this, "name", "Git Error");
4875
- }
4876
- };
4877
- var Git = class {
4878
- async git(args) {
4879
- const { stdout } = await exec2("git", args, { throwOnError: true });
4880
- return stdout;
4718
+ __publicField(this, "path", path5.resolve(import.meta.dirname, ".pubm"));
4719
+ try {
4720
+ if (!statSync(this.path).isDirectory()) {
4721
+ mkdirSync5(this.path);
4722
+ }
4723
+ } catch {
4724
+ try {
4725
+ mkdirSync5(this.path);
4726
+ } catch (error) {
4727
+ throw new Error(
4728
+ `Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
4729
+ );
4730
+ }
4731
+ }
4881
4732
  }
4882
- async userName() {
4733
+ set(field, value) {
4883
4734
  try {
4884
- return (await this.git(["config", "--get", "user.name"])).trim();
4735
+ writeFileSync4(
4736
+ path5.resolve(
4737
+ this.path,
4738
+ Buffer.from(e(field, field)).toString("base64")
4739
+ ),
4740
+ Buffer.from(e(`${value}`, field)),
4741
+ { encoding: "binary" }
4742
+ );
4885
4743
  } catch (error) {
4886
- throw new GitError("Failed to run `git config --get user.name`", {
4887
- cause: error
4888
- });
4744
+ throw new Error(
4745
+ `Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
4746
+ );
4889
4747
  }
4890
4748
  }
4891
- async latestTag() {
4749
+ get(field) {
4750
+ const filePath = path5.resolve(
4751
+ this.path,
4752
+ Buffer.from(e(field, field)).toString("base64")
4753
+ );
4754
+ let raw;
4892
4755
  try {
4893
- return (await this.git(["describe", "--tags", "--abbrev=0"])).trim();
4756
+ raw = readFileSync2(filePath);
4894
4757
  } catch {
4895
4758
  return null;
4896
4759
  }
4897
- }
4898
- async tags() {
4899
- try {
4900
- return (await this.git(["tag", "-l"])).trim().split("\n").sort(semver.compareIdentifiers);
4901
- } catch (error) {
4902
- throw new GitError("Failed to run `git tag -l`", {
4903
- cause: error
4904
- });
4905
- }
4906
- }
4907
- async previousTag(tag) {
4908
4760
  try {
4909
- const tags = await this.tags();
4910
- const strip = (t) => t.replace(/^v/, "");
4911
- return tags.at(tags.findIndex((t) => strip(t) === strip(tag)) - 1) ?? null;
4761
+ return d(Buffer.from(raw).toString(), field);
4912
4762
  } catch {
4763
+ console.warn(
4764
+ `Stored token for '${field}' appears corrupted. It will be re-requested.`
4765
+ );
4913
4766
  return null;
4914
4767
  }
4915
4768
  }
4916
- async dryFetch() {
4917
- try {
4769
+ };
4770
+
4771
+ // src/utils/token.ts
4772
+ var TOKEN_CONFIG = {
4773
+ npm: {
4774
+ envVar: "NODE_AUTH_TOKEN",
4775
+ dbKey: "npm-token",
4776
+ ghSecretName: "NODE_AUTH_TOKEN",
4777
+ promptLabel: "npm access token"
4778
+ },
4779
+ jsr: {
4780
+ envVar: "JSR_TOKEN",
4781
+ dbKey: "jsr-token",
4782
+ ghSecretName: "JSR_TOKEN",
4783
+ promptLabel: "jsr API token"
4784
+ },
4785
+ crates: {
4786
+ envVar: "CARGO_REGISTRY_TOKEN",
4787
+ dbKey: "cargo-token",
4788
+ ghSecretName: "CARGO_REGISTRY_TOKEN",
4789
+ promptLabel: "crates.io API token"
4790
+ }
4791
+ };
4792
+ function loadTokensFromDb(registries) {
4793
+ const db = new Db();
4794
+ const tokens = {};
4795
+ for (const registry of registries) {
4796
+ const config = TOKEN_CONFIG[registry];
4797
+ if (!config) continue;
4798
+ const token = db.get(config.dbKey);
4799
+ if (token) tokens[registry] = token;
4800
+ }
4801
+ return tokens;
4802
+ }
4803
+ function injectTokensToEnv(tokens) {
4804
+ const originals = {};
4805
+ for (const [registry, token] of Object.entries(tokens)) {
4806
+ const config = TOKEN_CONFIG[registry];
4807
+ if (!config) continue;
4808
+ originals[config.envVar] = process.env[config.envVar];
4809
+ process.env[config.envVar] = token;
4810
+ }
4811
+ return () => {
4812
+ for (const [envVar, original] of Object.entries(originals)) {
4813
+ if (original === void 0) {
4814
+ delete process.env[envVar];
4815
+ } else {
4816
+ process.env[envVar] = original;
4817
+ }
4818
+ }
4819
+ };
4820
+ }
4821
+
4822
+ // src/tasks/preflight.ts
4823
+ var PreflightError = class extends AbstractError {
4824
+ constructor() {
4825
+ super(...arguments);
4826
+ __publicField(this, "name", "Preflight Error");
4827
+ }
4828
+ };
4829
+ async function collectTokens(registries, task) {
4830
+ const existing = loadTokensFromDb(registries);
4831
+ const tokens = { ...existing };
4832
+ for (const registry of registries) {
4833
+ const config = TOKEN_CONFIG[registry];
4834
+ if (!config || tokens[registry]) continue;
4835
+ task.output = `Enter ${config.promptLabel}`;
4836
+ const token = await task.prompt(ListrEnquirerPromptAdapter).run({
4837
+ type: "password",
4838
+ message: `Enter ${config.promptLabel}`
4839
+ });
4840
+ tokens[registry] = token;
4841
+ new Db().set(config.dbKey, token);
4842
+ }
4843
+ return tokens;
4844
+ }
4845
+ async function syncGhSecrets(tokens) {
4846
+ for (const [registry, token] of Object.entries(tokens)) {
4847
+ const config = TOKEN_CONFIG[registry];
4848
+ if (!config) continue;
4849
+ await exec2("gh", ["secret", "set", config.ghSecretName], {
4850
+ throwOnError: true,
4851
+ nodeOptions: { input: token }
4852
+ });
4853
+ }
4854
+ }
4855
+ async function promptGhSecretsSync(tokens, task) {
4856
+ const shouldSync = await task.prompt(ListrEnquirerPromptAdapter).run({
4857
+ type: "toggle",
4858
+ message: "Sync tokens to GitHub Secrets?",
4859
+ enabled: "Yes",
4860
+ disabled: "No"
4861
+ });
4862
+ if (shouldSync) {
4863
+ task.output = "Syncing tokens to GitHub Secrets...";
4864
+ try {
4865
+ await syncGhSecrets(tokens);
4866
+ task.output = "Tokens synced to GitHub Secrets.";
4867
+ } catch (error) {
4868
+ throw new PreflightError(
4869
+ "Failed to sync tokens to GitHub Secrets. Ensure `gh` CLI is installed and authenticated (`gh auth login`).",
4870
+ { cause: error }
4871
+ );
4872
+ }
4873
+ }
4874
+ }
4875
+
4876
+ // src/commands/secrets.ts
4877
+ function registerSecretsCommand(cli2) {
4878
+ cli2.command("secrets sync", "Sync stored tokens to GitHub Secrets").option("--registry <...registries>", "Filter to specific registries", {
4879
+ // biome-ignore lint/suspicious/noExplicitAny: CAC option type mismatch
4880
+ type: String
4881
+ }).action(async (options) => {
4882
+ try {
4883
+ const registries = options.registry ? options.registry.split(",") : ["npm", "jsr", "crates"];
4884
+ const tokens = loadTokensFromDb(registries);
4885
+ if (Object.keys(tokens).length === 0) {
4886
+ console.log(
4887
+ "No stored tokens found. Run `pubm --preflight` first to save tokens."
4888
+ );
4889
+ return;
4890
+ }
4891
+ console.log(
4892
+ `Syncing ${Object.keys(tokens).length} token(s) to GitHub Secrets...`
4893
+ );
4894
+ await syncGhSecrets(tokens);
4895
+ console.log("Done! Tokens synced to GitHub Secrets.");
4896
+ } catch (e2) {
4897
+ consoleError(e2);
4898
+ process.exitCode = 1;
4899
+ }
4900
+ });
4901
+ }
4902
+
4903
+ // src/commands/snapshot.ts
4904
+ function registerSnapshotCommand(cli2) {
4905
+ cli2.command("snapshot [tag]", "Create a snapshot release").action(async (tag) => {
4906
+ console.log(`pubm snapshot ${tag ?? ""} \u2014 coming in next phase`);
4907
+ });
4908
+ }
4909
+
4910
+ // src/changeset/status.ts
4911
+ import process10 from "node:process";
4912
+
4913
+ // src/changeset/bump-utils.ts
4914
+ var BUMP_ORDER = {
4915
+ patch: 0,
4916
+ minor: 1,
4917
+ major: 2
4918
+ };
4919
+ function maxBump(a2, b) {
4920
+ return BUMP_ORDER[a2] >= BUMP_ORDER[b] ? a2 : b;
4921
+ }
4922
+
4923
+ // src/changeset/reader.ts
4924
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
4925
+ import path6 from "node:path";
4926
+ import process9 from "node:process";
4927
+
4928
+ // src/changeset/parser.ts
4929
+ import { parse as parseYaml } from "yaml";
4930
+ var VALID_BUMP_TYPES = /* @__PURE__ */ new Set(["patch", "minor", "major"]);
4931
+ function parseChangeset(content, fileName) {
4932
+ const frontmatterRegex = /^---\n([\s\S]*?)---/;
4933
+ const match = content.match(frontmatterRegex);
4934
+ if (!match) {
4935
+ throw new Error(
4936
+ `Invalid changeset format in "${fileName}": missing frontmatter`
4937
+ );
4938
+ }
4939
+ const yamlContent = match[1];
4940
+ const body = content.slice(match[0].length).trim();
4941
+ const parsed = parseYaml(yamlContent);
4942
+ const releases = [];
4943
+ if (parsed) {
4944
+ for (const [name, type] of Object.entries(parsed)) {
4945
+ if (!VALID_BUMP_TYPES.has(type)) {
4946
+ throw new Error(
4947
+ `Invalid bump type "${type}" for package "${name}" in "${fileName}". Expected: patch, minor, or major.`
4948
+ );
4949
+ }
4950
+ releases.push({ name, type });
4951
+ }
4952
+ }
4953
+ const id = fileName.replace(/\.md$/, "");
4954
+ return {
4955
+ id,
4956
+ summary: body,
4957
+ releases
4958
+ };
4959
+ }
4960
+
4961
+ // src/changeset/reader.ts
4962
+ function readChangesets(cwd = process9.cwd()) {
4963
+ const changesetsDir = path6.join(cwd, ".pubm", "changesets");
4964
+ if (!existsSync4(changesetsDir)) {
4965
+ return [];
4966
+ }
4967
+ const files = readdirSync2(changesetsDir);
4968
+ const changesets = [];
4969
+ for (const file of files) {
4970
+ if (!file.endsWith(".md") || file === "README.md") {
4971
+ continue;
4972
+ }
4973
+ const filePath = path6.join(changesetsDir, file);
4974
+ const content = readFileSync3(filePath, "utf-8");
4975
+ changesets.push(parseChangeset(content, file));
4976
+ }
4977
+ return changesets;
4978
+ }
4979
+
4980
+ // src/changeset/status.ts
4981
+ function getStatus(cwd = process10.cwd()) {
4982
+ const changesets = readChangesets(cwd);
4983
+ const packages = /* @__PURE__ */ new Map();
4984
+ for (const changeset of changesets) {
4985
+ for (const release of changeset.releases) {
4986
+ const existing = packages.get(release.name);
4987
+ if (existing) {
4988
+ existing.bumpType = maxBump(existing.bumpType, release.type);
4989
+ existing.changesetCount += 1;
4990
+ existing.summaries.push(changeset.summary);
4991
+ } else {
4992
+ packages.set(release.name, {
4993
+ bumpType: release.type,
4994
+ changesetCount: 1,
4995
+ summaries: [changeset.summary]
4996
+ });
4997
+ }
4998
+ }
4999
+ }
5000
+ return {
5001
+ packages,
5002
+ changesets,
5003
+ hasChangesets: changesets.length > 0
5004
+ };
5005
+ }
5006
+
5007
+ // src/commands/status.ts
5008
+ function registerStatusCommand(cli2) {
5009
+ cli2.command("status", "Show pending changeset status").option("--verbose", "Show full changeset contents").option("--since <ref>", "Only check changesets since git ref").action(async (options) => {
5010
+ const status = getStatus();
5011
+ if (!status.hasChangesets) {
5012
+ if (options.since) {
5013
+ console.log("No changesets found.");
5014
+ process.exit(1);
5015
+ }
5016
+ console.log("No pending changesets.");
5017
+ return;
5018
+ }
5019
+ console.log("Pending changesets:");
5020
+ for (const [name, info] of status.packages) {
5021
+ console.log(
5022
+ ` ${name}: ${info.bumpType} (${info.changesetCount} changeset${info.changesetCount > 1 ? "s" : ""})`
5023
+ );
5024
+ if (options.verbose) {
5025
+ for (const summary of info.summaries) {
5026
+ console.log(` - ${summary}`);
5027
+ }
5028
+ }
5029
+ }
5030
+ });
5031
+ }
5032
+
5033
+ // src/commands/update.ts
5034
+ import { UpdateKit } from "update-kit";
5035
+ function registerUpdateCommand(cli2) {
5036
+ cli2.command("update", "Update pubm to the latest version").action(async () => {
5037
+ const kit = await UpdateKit.create({
5038
+ sources: [{ type: "npm", packageName: "pubm" }],
5039
+ delegateMode: "execute"
5040
+ });
5041
+ const result = await kit.autoUpdate({
5042
+ onProgress: (p) => {
5043
+ if (p.phase === "downloading" && p.totalBytes) {
5044
+ const pct = Math.round(p.bytesDownloaded / p.totalBytes * 100);
5045
+ process.stderr.write(`\rDownloading... ${pct}%`);
5046
+ } else {
5047
+ console.error(`${p.phase}...`);
5048
+ }
5049
+ }
5050
+ });
5051
+ switch (result.kind) {
5052
+ case "success":
5053
+ console.log(
5054
+ `Updated from ${result.fromVersion} to ${result.toVersion}`
5055
+ );
5056
+ break;
5057
+ case "needs-restart":
5058
+ console.log(result.message);
5059
+ break;
5060
+ case "failed":
5061
+ console.error(`Update failed: ${result.error.message}`);
5062
+ process.exitCode = 1;
5063
+ break;
5064
+ }
5065
+ });
5066
+ }
5067
+
5068
+ // src/commands/version-cmd.ts
5069
+ function registerVersionCommand(cli2) {
5070
+ cli2.command("version", "Consume changesets and bump versions").action(async () => {
5071
+ console.log("pubm version \u2014 coming in next phase");
5072
+ });
5073
+ }
5074
+
5075
+ // src/git.ts
5076
+ import semver from "semver";
5077
+ import { exec as exec3 } from "tinyexec";
5078
+ var GitError = class extends AbstractError {
5079
+ constructor() {
5080
+ super(...arguments);
5081
+ __publicField(this, "name", "Git Error");
5082
+ }
5083
+ };
5084
+ var Git = class {
5085
+ async git(args) {
5086
+ const { stdout } = await exec3("git", args, { throwOnError: true });
5087
+ return stdout;
5088
+ }
5089
+ async userName() {
5090
+ try {
5091
+ return (await this.git(["config", "--get", "user.name"])).trim();
5092
+ } catch (error) {
5093
+ throw new GitError("Failed to run `git config --get user.name`", {
5094
+ cause: error
5095
+ });
5096
+ }
5097
+ }
5098
+ async latestTag() {
5099
+ try {
5100
+ return (await this.git(["describe", "--tags", "--abbrev=0"])).trim();
5101
+ } catch {
5102
+ return null;
5103
+ }
5104
+ }
5105
+ async tags() {
5106
+ try {
5107
+ return (await this.git(["tag", "-l"])).trim().split("\n").sort(semver.compareIdentifiers);
5108
+ } catch (error) {
5109
+ throw new GitError("Failed to run `git tag -l`", {
5110
+ cause: error
5111
+ });
5112
+ }
5113
+ }
5114
+ async previousTag(tag) {
5115
+ try {
5116
+ const tags = await this.tags();
5117
+ const strip = (t) => t.replace(/^v/, "");
5118
+ return tags.at(tags.findIndex((t) => strip(t) === strip(tag)) - 1) ?? null;
5119
+ } catch {
5120
+ return null;
5121
+ }
5122
+ }
5123
+ async dryFetch() {
5124
+ try {
4918
5125
  return await this.git(["fetch", "--dry-run"]);
4919
5126
  } catch (error) {
4920
5127
  throw new GitError("Failed to run `git fetch --dry-run`", {
@@ -5130,7 +5337,7 @@ var Git = class {
5130
5337
  async push(options) {
5131
5338
  const args = ["push", options].filter((v) => v);
5132
5339
  try {
5133
- const { stderr } = await exec2("git", args, { throwOnError: true });
5340
+ const { stderr } = await exec3("git", args, { throwOnError: true });
5134
5341
  if (`${stderr}`.includes("GH006")) {
5135
5342
  return false;
5136
5343
  }
@@ -5190,7 +5397,7 @@ function resolveConfig(config) {
5190
5397
 
5191
5398
  // src/config/loader.ts
5192
5399
  import { stat } from "node:fs/promises";
5193
- import path6 from "node:path";
5400
+ import path7 from "node:path";
5194
5401
  var CONFIG_FILES = [
5195
5402
  "pubm.config.ts",
5196
5403
  "pubm.config.mts",
@@ -5201,7 +5408,7 @@ var CONFIG_FILES = [
5201
5408
  ];
5202
5409
  async function findConfigFile(cwd) {
5203
5410
  for (const file of CONFIG_FILES) {
5204
- const filePath = path6.join(cwd, file);
5411
+ const filePath = path7.join(cwd, file);
5205
5412
  try {
5206
5413
  if ((await stat(filePath)).isFile()) {
5207
5414
  return filePath;
@@ -5241,7 +5448,7 @@ import process14 from "node:process";
5241
5448
  import npmCli3 from "@npmcli/promise-spawn";
5242
5449
  import SemVer from "semver";
5243
5450
  import { isCI as isCI2 } from "std-env";
5244
- import { exec as exec7 } from "tinyexec";
5451
+ import { exec as exec8 } from "tinyexec";
5245
5452
 
5246
5453
  // src/utils/cli.ts
5247
5454
  var warningBadge = color.bgYellow(" Warning ");
@@ -5251,7 +5458,7 @@ function link2(text, url) {
5251
5458
 
5252
5459
  // src/ecosystem/rust.ts
5253
5460
  import { readFile, stat as stat2, writeFile } from "node:fs/promises";
5254
- import path7 from "node:path";
5461
+ import path8 from "node:path";
5255
5462
  import { parse, stringify } from "smol-toml";
5256
5463
 
5257
5464
  // src/ecosystem/ecosystem.ts
@@ -5265,14 +5472,14 @@ var Ecosystem = class {
5265
5472
  var RustEcosystem = class extends Ecosystem {
5266
5473
  static async detect(packagePath) {
5267
5474
  try {
5268
- return (await stat2(path7.join(packagePath, "Cargo.toml"))).isFile();
5475
+ return (await stat2(path8.join(packagePath, "Cargo.toml"))).isFile();
5269
5476
  } catch {
5270
5477
  return false;
5271
5478
  }
5272
5479
  }
5273
5480
  async readCargoToml() {
5274
5481
  const raw = await readFile(
5275
- path7.join(this.packagePath, "Cargo.toml"),
5482
+ path8.join(this.packagePath, "Cargo.toml"),
5276
5483
  "utf-8"
5277
5484
  );
5278
5485
  return parse(raw);
@@ -5288,7 +5495,7 @@ var RustEcosystem = class extends Ecosystem {
5288
5495
  return pkg.version;
5289
5496
  }
5290
5497
  async writeVersion(newVersion) {
5291
- const filePath = path7.join(this.packagePath, "Cargo.toml");
5498
+ const filePath = path8.join(this.packagePath, "Cargo.toml");
5292
5499
  const raw = await readFile(filePath, "utf-8");
5293
5500
  const cargo = parse(raw);
5294
5501
  const pkg = cargo.package;
@@ -5408,7 +5615,7 @@ function createListr(...args) {
5408
5615
 
5409
5616
  // src/utils/package.ts
5410
5617
  import { readFile as readFile2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
5411
- import path8 from "node:path";
5618
+ import path9 from "node:path";
5412
5619
  import process11 from "node:process";
5413
5620
  var cachedPackageJson = {};
5414
5621
  var cachedJsrJson = {};
@@ -5418,16 +5625,16 @@ function patchCachedJsrJson(contents, { cwd = process11.cwd() } = {}) {
5418
5625
  async function findOutFile(file, { cwd = process11.cwd() } = {}) {
5419
5626
  let directory = cwd;
5420
5627
  let filePath = "";
5421
- const { root } = path8.parse(cwd);
5628
+ const { root } = path9.parse(cwd);
5422
5629
  while (directory) {
5423
- filePath = path8.join(directory, file);
5630
+ filePath = path9.join(directory, file);
5424
5631
  try {
5425
5632
  if ((await stat3(filePath)).isFile()) {
5426
5633
  break;
5427
5634
  }
5428
5635
  } catch {
5429
5636
  }
5430
- directory = path8.dirname(directory);
5637
+ directory = path9.dirname(directory);
5431
5638
  if (directory === root) return null;
5432
5639
  }
5433
5640
  return filePath;
@@ -5593,7 +5800,7 @@ async function replaceVersion(version2, packages) {
5593
5800
  return "jsr.json";
5594
5801
  })(),
5595
5802
  ...(packages ?? []).filter((pkg) => pkg.registries.includes("crates")).map(async (pkg) => {
5596
- const eco = new RustEcosystem(path8.resolve(pkg.path));
5803
+ const eco = new RustEcosystem(path9.resolve(pkg.path));
5597
5804
  try {
5598
5805
  await eco.writeVersion(version2);
5599
5806
  } catch (error) {
@@ -5602,7 +5809,7 @@ async function replaceVersion(version2, packages) {
5602
5809
  { cause: error }
5603
5810
  );
5604
5811
  }
5605
- return path8.join(pkg.path, "Cargo.toml");
5812
+ return path9.join(pkg.path, "Cargo.toml");
5606
5813
  })
5607
5814
  ]);
5608
5815
  return results.filter((v) => v);
@@ -5643,8 +5850,8 @@ function collectRegistries(ctx) {
5643
5850
  }
5644
5851
 
5645
5852
  // src/registry/crates.ts
5646
- import path9 from "node:path";
5647
- import { exec as exec3, NonZeroExitError } from "tinyexec";
5853
+ import path10 from "node:path";
5854
+ import { exec as exec4, NonZeroExitError } from "tinyexec";
5648
5855
 
5649
5856
  // src/registry/registry.ts
5650
5857
  var Registry = class {
@@ -5684,7 +5891,7 @@ var CratesRegistry = class extends Registry {
5684
5891
  }
5685
5892
  async isInstalled() {
5686
5893
  try {
5687
- await exec3("cargo", ["--version"]);
5894
+ await exec4("cargo", ["--version"]);
5688
5895
  return true;
5689
5896
  } catch {
5690
5897
  return false;
@@ -5723,9 +5930,9 @@ var CratesRegistry = class extends Registry {
5723
5930
  try {
5724
5931
  const args = ["publish"];
5725
5932
  if (manifestDir) {
5726
- args.push("--manifest-path", path9.join(manifestDir, "Cargo.toml"));
5933
+ args.push("--manifest-path", path10.join(manifestDir, "Cargo.toml"));
5727
5934
  }
5728
- await exec3("cargo", args, { throwOnError: true });
5935
+ await exec4("cargo", args, { throwOnError: true });
5729
5936
  return true;
5730
5937
  } catch (error) {
5731
5938
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
@@ -5738,13 +5945,13 @@ ${stderr}` : "Failed to run `cargo publish`";
5738
5945
  try {
5739
5946
  const args = ["publish", "--dry-run"];
5740
5947
  if (manifestDir) {
5741
- args.push("--manifest-path", path9.join(manifestDir, "Cargo.toml"));
5948
+ args.push("--manifest-path", path10.join(manifestDir, "Cargo.toml"));
5742
5949
  }
5743
- await exec3("cargo", args, { throwOnError: true });
5950
+ await exec4("cargo", args, { throwOnError: true });
5744
5951
  } catch (error) {
5745
5952
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5746
- const message = stderr ? `Dry-run failed for \`cargo publish\`:
5747
- ${stderr}` : "Dry-run failed for `cargo publish`";
5953
+ const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
5954
+ ${stderr}` : "Failed to run `cargo publish --dry-run`";
5748
5955
  throw new CratesError(message, { cause: error });
5749
5956
  }
5750
5957
  }
@@ -5834,79 +6041,11 @@ function createCratesPublishTask(packagePath) {
5834
6041
  var cratesAvailableCheckTasks = createCratesAvailableCheckTask();
5835
6042
  var cratesPublishTasks = createCratesPublishTask();
5836
6043
 
5837
- // src/registry/jsr.ts
5838
- import { exec as exec4, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
6044
+ // src/tasks/dry-run-publish.ts
6045
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
5839
6046
 
5840
- // src/utils/db.ts
5841
- import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
5842
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync4 } from "node:fs";
5843
- import path10 from "node:path";
5844
- var a = "aes-256-cbc";
5845
- var n = statSync(import.meta.dirname);
5846
- var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
5847
- var l = createHash("md5").update(k).digest();
5848
- function e(e2, f) {
5849
- const c = createCipheriv(a, createHash("sha-256").update(f).digest(), l);
5850
- return c.update(e2, "utf8", "hex") + c.final("hex");
5851
- }
5852
- function d(g, h) {
5853
- const d2 = createDecipheriv(a, createHash("sha-256").update(h).digest(), l);
5854
- return d2.update(g, "hex", "utf8") + d2.final("utf8");
5855
- }
5856
- var Db = class {
5857
- constructor() {
5858
- __publicField(this, "path", path10.resolve(import.meta.dirname, ".pubm"));
5859
- try {
5860
- if (!statSync(this.path).isDirectory()) {
5861
- mkdirSync5(this.path);
5862
- }
5863
- } catch {
5864
- try {
5865
- mkdirSync5(this.path);
5866
- } catch (error) {
5867
- throw new Error(
5868
- `Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
5869
- );
5870
- }
5871
- }
5872
- }
5873
- set(field, value) {
5874
- try {
5875
- writeFileSync4(
5876
- path10.resolve(
5877
- this.path,
5878
- Buffer.from(e(field, field)).toString("base64")
5879
- ),
5880
- Buffer.from(e(`${value}`, field)),
5881
- { encoding: "binary" }
5882
- );
5883
- } catch (error) {
5884
- throw new Error(
5885
- `Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
5886
- );
5887
- }
5888
- }
5889
- get(field) {
5890
- const filePath = path10.resolve(
5891
- this.path,
5892
- Buffer.from(e(field, field)).toString("base64")
5893
- );
5894
- let raw;
5895
- try {
5896
- raw = readFileSync3(filePath);
5897
- } catch {
5898
- return null;
5899
- }
5900
- try {
5901
- return d(Buffer.from(raw).toString(), field);
5902
- } catch {
5903
- console.warn(
5904
- `Stored token for '${field}' appears corrupted. It will be re-requested.`
5905
- );
5906
- return null;
5907
- }
5908
- }
5909
- };
6047
+ // src/registry/jsr.ts
6048
+ import { exec as exec5, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5910
6049
 
5911
6050
  // src/utils/package-name.ts
5912
6051
  import { builtinModules } from "node:module";
@@ -5973,7 +6112,7 @@ var JsrRegisry = class extends Registry {
5973
6112
  this.client = new JsrClient(getApiEndpoint(this.registry));
5974
6113
  }
5975
6114
  async jsr(args) {
5976
- const { stdout } = await exec4("jsr", args, { throwOnError: true });
6115
+ const { stdout } = await exec5("jsr", args, { throwOnError: true });
5977
6116
  return stdout;
5978
6117
  }
5979
6118
  async isInstalled() {
@@ -5989,7 +6128,7 @@ var JsrRegisry = class extends Registry {
5989
6128
  }
5990
6129
  async ping() {
5991
6130
  try {
5992
- const { stdout } = await exec4(
6131
+ const { stdout } = await exec5(
5993
6132
  "ping",
5994
6133
  [new URL(this.registry).hostname, "-c", "1"],
5995
6134
  { throwOnError: true }
@@ -6004,7 +6143,7 @@ var JsrRegisry = class extends Registry {
6004
6143
  }
6005
6144
  async publish() {
6006
6145
  try {
6007
- await exec4(
6146
+ await exec5(
6008
6147
  "jsr",
6009
6148
  ["publish", "--allow-dirty", "--token", `${JsrClient.token}`],
6010
6149
  {
@@ -6035,7 +6174,7 @@ ${stderr}` : ""}`,
6035
6174
  }
6036
6175
  async dryRunPublish() {
6037
6176
  try {
6038
- await exec4(
6177
+ await exec5(
6039
6178
  "jsr",
6040
6179
  [
6041
6180
  "publish",
@@ -6047,12 +6186,9 @@ ${stderr}` : ""}`,
6047
6186
  { throwOnError: true }
6048
6187
  );
6049
6188
  } catch (error) {
6050
- const stderr = error instanceof NonZeroExitError2 ? error.output?.stderr : void 0;
6051
- throw new JsrError(
6052
- `Dry-run failed for \`jsr publish\`${stderr ? `
6053
- ${stderr}` : ""}`,
6054
- { cause: error }
6055
- );
6189
+ throw new JsrError("Failed to run `jsr publish --dry-run`", {
6190
+ cause: error
6191
+ });
6056
6192
  }
6057
6193
  }
6058
6194
  async version() {
@@ -6296,7 +6432,7 @@ async function jsrRegistry() {
6296
6432
  }
6297
6433
 
6298
6434
  // src/registry/npm.ts
6299
- import { exec as exec5, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
6435
+ import { exec as exec6, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
6300
6436
  var NpmError = class extends AbstractError {
6301
6437
  constructor() {
6302
6438
  super(...arguments);
@@ -6309,7 +6445,7 @@ var NpmRegistry = class extends Registry {
6309
6445
  __publicField(this, "registry", "https://registry.npmjs.org");
6310
6446
  }
6311
6447
  async npm(args) {
6312
- const { stdout } = await exec5("npm", args, { throwOnError: true });
6448
+ const { stdout } = await exec6("npm", args, { throwOnError: true });
6313
6449
  return stdout;
6314
6450
  }
6315
6451
  async isInstalled() {
@@ -6420,7 +6556,7 @@ var NpmRegistry = class extends Registry {
6420
6556
  }
6421
6557
  async ping() {
6422
6558
  try {
6423
- await exec5("npm", ["ping"], { throwOnError: true });
6559
+ await exec6("npm", ["ping"], { throwOnError: true });
6424
6560
  return true;
6425
6561
  } catch (error) {
6426
6562
  throw new NpmError("Failed to run `npm ping`", { cause: error });
@@ -6453,7 +6589,9 @@ var NpmRegistry = class extends Registry {
6453
6589
  try {
6454
6590
  await this.npm(["publish", "--dry-run"]);
6455
6591
  } catch (error) {
6456
- throw this.classifyPublishError(error);
6592
+ throw new NpmError("Failed to run `npm publish --dry-run`", {
6593
+ cause: error
6594
+ });
6457
6595
  }
6458
6596
  }
6459
6597
  async twoFactorAuthMode() {
@@ -6502,69 +6640,78 @@ async function npmRegistry() {
6502
6640
  }
6503
6641
 
6504
6642
  // src/tasks/dry-run-publish.ts
6643
+ var AUTH_ERROR_PATTERNS = [
6644
+ /401/i,
6645
+ /403/i,
6646
+ /unauthorized/i,
6647
+ /forbidden/i,
6648
+ /invalid.token/i,
6649
+ /eotp/i
6650
+ ];
6651
+ function isAuthError(error) {
6652
+ const message = error instanceof Error ? error.message : String(error);
6653
+ return AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(message));
6654
+ }
6655
+ async function withTokenRetry(registryKey, task, action) {
6656
+ try {
6657
+ await action();
6658
+ } catch (error) {
6659
+ if (!isAuthError(error)) throw error;
6660
+ const config = TOKEN_CONFIG[registryKey];
6661
+ if (!config) throw error;
6662
+ task.output = `Auth failed. Re-enter ${config.promptLabel}`;
6663
+ const newToken = await task.prompt(ListrEnquirerPromptAdapter2).run({
6664
+ type: "password",
6665
+ message: `Re-enter ${config.promptLabel}`
6666
+ });
6667
+ new Db().set(config.dbKey, newToken);
6668
+ process.env[config.envVar] = newToken;
6669
+ await action();
6670
+ }
6671
+ }
6505
6672
  var npmDryRunPublishTask = {
6506
6673
  title: "Dry-run npm publish",
6507
6674
  task: async (_, task) => {
6508
- const npm = await npmRegistry();
6509
6675
  task.output = "Running npm publish --dry-run...";
6510
- await npm.dryRunPublish();
6676
+ await withTokenRetry("npm", task, async () => {
6677
+ const npm = await npmRegistry();
6678
+ await npm.dryRunPublish();
6679
+ });
6511
6680
  }
6512
6681
  };
6513
6682
  var jsrDryRunPublishTask = {
6514
6683
  title: "Dry-run jsr publish",
6515
- skip: () => !JsrClient.token,
6516
6684
  task: async (_, task) => {
6517
- const jsr = await jsrRegistry();
6518
6685
  task.output = "Running jsr publish --dry-run...";
6519
- await jsr.dryRunPublish();
6686
+ await withTokenRetry("jsr", task, async () => {
6687
+ const jsr = await jsrRegistry();
6688
+ await jsr.dryRunPublish();
6689
+ });
6520
6690
  }
6521
6691
  };
6692
+ async function getCrateName2(packagePath) {
6693
+ const eco = new RustEcosystem(packagePath ?? process.cwd());
6694
+ return await eco.packageName();
6695
+ }
6522
6696
  function createCratesDryRunPublishTask(packagePath) {
6523
6697
  const label = packagePath ? ` (${packagePath})` : "";
6524
6698
  return {
6525
- title: `Dry-run cargo publish${label}`,
6699
+ title: `Dry-run crates.io publish${label}`,
6526
6700
  task: async (_, task) => {
6527
- const eco = new RustEcosystem(packagePath ?? process.cwd());
6528
- const packageName = await eco.packageName();
6529
- const registry = new CratesRegistry(packageName);
6530
6701
  task.output = "Running cargo publish --dry-run...";
6531
- await registry.dryRunPublish(packagePath);
6702
+ await withTokenRetry("crates", task, async () => {
6703
+ const packageName = await getCrateName2(packagePath);
6704
+ const registry = new CratesRegistry(packageName);
6705
+ await registry.dryRunPublish(packagePath);
6706
+ });
6532
6707
  }
6533
6708
  };
6534
6709
  }
6535
6710
  var cratesDryRunPublishTask = createCratesDryRunPublishTask();
6536
- function registryDryRunTask(registryKey) {
6537
- switch (registryKey) {
6538
- case "npm":
6539
- return npmDryRunPublishTask;
6540
- case "jsr":
6541
- return jsrDryRunPublishTask;
6542
- case "crates":
6543
- return cratesDryRunPublishTask;
6544
- default:
6545
- return npmDryRunPublishTask;
6546
- }
6547
- }
6548
- var dryRunPublishTask = {
6549
- title: "Validating publish (dry-run)",
6550
- task: (ctx, parentTask) => {
6551
- if (ctx.packages?.length) {
6552
- const tasks = ctx.packages.flatMap(
6553
- (pkg) => pkg.registries.map(
6554
- (registryKey) => registryKey === "crates" ? createCratesDryRunPublishTask(pkg.path) : registryDryRunTask(registryKey)
6555
- )
6556
- );
6557
- return parentTask.newListr(tasks, { concurrent: true });
6558
- }
6559
- return parentTask.newListr(collectRegistries(ctx).map(registryDryRunTask), {
6560
- concurrent: true
6561
- });
6562
- }
6563
- };
6564
6711
 
6565
6712
  // src/tasks/jsr.ts
6566
6713
  import process12 from "node:process";
6567
- import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
6714
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter3 } from "@listr2/prompt-adapter-enquirer";
6568
6715
  import npmCli from "@npmcli/promise-spawn";
6569
6716
  var { open } = npmCli;
6570
6717
  var JsrAvailableError = class extends AbstractError {
@@ -6593,7 +6740,7 @@ var jsrAvailableCheckTasks = {
6593
6740
  if (ctx.promptEnabled) {
6594
6741
  const maxAttempts = 3;
6595
6742
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6596
- JsrClient.token = await task.prompt(ListrEnquirerPromptAdapter).run({
6743
+ JsrClient.token = await task.prompt(ListrEnquirerPromptAdapter3).run({
6597
6744
  type: "password",
6598
6745
  message: `Please enter the jsr ${color.bold("API token")}${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`,
6599
6746
  footer: `
@@ -6643,7 +6790,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
6643
6790
  )
6644
6791
  )).filter((v) => v !== null);
6645
6792
  if (searchResults.length > 0) {
6646
- jsrName = await task.prompt(ListrEnquirerPromptAdapter).run({
6793
+ jsrName = await task.prompt(ListrEnquirerPromptAdapter3).run({
6647
6794
  type: "select",
6648
6795
  message: "Is there a scoped package you want to publish in the already published list?",
6649
6796
  choices: [
@@ -6661,7 +6808,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
6661
6808
  }
6662
6809
  const userName = await new Git().userName();
6663
6810
  task.output = "Select the scope of the package to publish";
6664
- jsrName = await task.prompt(ListrEnquirerPromptAdapter).run({
6811
+ jsrName = await task.prompt(ListrEnquirerPromptAdapter3).run({
6665
6812
  type: "select",
6666
6813
  message: "jsr.json does not exist, and the package name is not scoped. Please select a scope for the 'jsr' package",
6667
6814
  choices: [
@@ -6689,7 +6836,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
6689
6836
  });
6690
6837
  if (jsrName === "specify") {
6691
6838
  while (!isScopedPackage(jsrName)) {
6692
- jsrName = await task.prompt(ListrEnquirerPromptAdapter).run({
6839
+ jsrName = await task.prompt(ListrEnquirerPromptAdapter3).run({
6693
6840
  type: "input",
6694
6841
  message: "Package name"
6695
6842
  });
@@ -6757,7 +6904,7 @@ var jsrPublishTasks = {
6757
6904
  ${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
6758
6905
  open(urls[0]);
6759
6906
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6760
- await task.prompt(ListrEnquirerPromptAdapter).run({
6907
+ await task.prompt(ListrEnquirerPromptAdapter3).run({
6761
6908
  type: "input",
6762
6909
  message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6763
6910
  });
@@ -6786,7 +6933,7 @@ ${jsr.packageCreationUrls.join("\n")}`
6786
6933
  // src/tasks/npm.ts
6787
6934
  import { spawn } from "node:child_process";
6788
6935
  import process13 from "node:process";
6789
- import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
6936
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter4 } from "@listr2/prompt-adapter-enquirer";
6790
6937
  import npmCli2 from "@npmcli/promise-spawn";
6791
6938
  var { open: open2 } = npmCli2;
6792
6939
  var NpmAvailableError = class extends AbstractError {
@@ -6883,7 +7030,7 @@ var npmPublishTasks = {
6883
7030
  const maxAttempts = 3;
6884
7031
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6885
7032
  result = await npm.publish(
6886
- await task.prompt(ListrEnquirerPromptAdapter2).run({
7033
+ await task.prompt(ListrEnquirerPromptAdapter4).run({
6887
7034
  type: "password",
6888
7035
  message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6889
7036
  })
@@ -6918,7 +7065,7 @@ var npmPublishTasks = {
6918
7065
  };
6919
7066
 
6920
7067
  // src/tasks/prerequisites-check.ts
6921
- import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter3 } from "@listr2/prompt-adapter-enquirer";
7068
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter5 } from "@listr2/prompt-adapter-enquirer";
6922
7069
  var PrerequisitesCheckError = class extends AbstractError {
6923
7070
  constructor(message, { cause } = {}) {
6924
7071
  super(message, { cause });
@@ -6938,7 +7085,7 @@ var prerequisitesCheckTask = (options) => {
6938
7085
  title: "Verifying current branch is a release branch",
6939
7086
  task: async (ctx, task) => {
6940
7087
  if (await git.branch() !== ctx.branch) {
6941
- const swtichBranch = await task.prompt(ListrEnquirerPromptAdapter3).run({
7088
+ const swtichBranch = await task.prompt(ListrEnquirerPromptAdapter5).run({
6942
7089
  type: "toggle",
6943
7090
  message: `${warningBadge} The current HEAD branch is not the release target branch. Do you want to switch branch to ${ctx.branch}?`,
6944
7091
  enabled: "Yes",
@@ -6960,7 +7107,7 @@ var prerequisitesCheckTask = (options) => {
6960
7107
  task: async (_2, task) => {
6961
7108
  task.output = "Checking for updates with `git fetch`";
6962
7109
  if ((await git.dryFetch()).trim()) {
6963
- const fetch2 = await task.prompt(ListrEnquirerPromptAdapter3).run({
7110
+ const fetch2 = await task.prompt(ListrEnquirerPromptAdapter5).run({
6964
7111
  type: "toggle",
6965
7112
  message: `${warningBadge} Local history is outdated. Do you want to run \`git fetch\`?`,
6966
7113
  enabled: "Yes",
@@ -6977,7 +7124,7 @@ var prerequisitesCheckTask = (options) => {
6977
7124
  }
6978
7125
  task.output = "Checking for updates with `git pull`";
6979
7126
  if (await git.revisionDiffsCount()) {
6980
- const pull = await task.prompt(ListrEnquirerPromptAdapter3).run({
7127
+ const pull = await task.prompt(ListrEnquirerPromptAdapter5).run({
6981
7128
  type: "toggle",
6982
7129
  message: `${warningBadge} Local history is outdated. Do you want to run \`git pull\`?`,
6983
7130
  enabled: "Yes",
@@ -6999,7 +7146,7 @@ var prerequisitesCheckTask = (options) => {
6999
7146
  task: async (ctx, task) => {
7000
7147
  if (await git.status()) {
7001
7148
  task.output = "Local working tree is not clean.";
7002
- if (!await task.prompt(ListrEnquirerPromptAdapter3).run({
7149
+ if (!await task.prompt(ListrEnquirerPromptAdapter5).run({
7003
7150
  type: "toggle",
7004
7151
  message: `${warningBadge} Local working tree is not clean. Do you want to skip?`,
7005
7152
  enabled: "Yes",
@@ -7024,7 +7171,7 @@ var prerequisitesCheckTask = (options) => {
7024
7171
  return void 0;
7025
7172
  }
7026
7173
  if ((await git.commits(latestTag, "HEAD")).length <= 0) {
7027
- if (!await task.prompt(ListrEnquirerPromptAdapter3).run({
7174
+ if (!await task.prompt(ListrEnquirerPromptAdapter5).run({
7028
7175
  type: "toggle",
7029
7176
  message: `${warningBadge} No commits exist from the latest tag. Do you want to skip?`,
7030
7177
  enabled: "Yes",
@@ -7042,7 +7189,7 @@ var prerequisitesCheckTask = (options) => {
7042
7189
  task: async (ctx, task) => {
7043
7190
  const gitTag = `v${ctx.version}`;
7044
7191
  if (await git.checkTagExist(gitTag)) {
7045
- const deleteTag = await task.prompt(ListrEnquirerPromptAdapter3).run({
7192
+ const deleteTag = await task.prompt(ListrEnquirerPromptAdapter5).run({
7046
7193
  type: "toggle",
7047
7194
  message: `${warningBadge} The Git tag '${gitTag}' already exists. Do you want to delete tag?`,
7048
7195
  enabled: "Yes",
@@ -7064,13 +7211,13 @@ var prerequisitesCheckTask = (options) => {
7064
7211
  };
7065
7212
 
7066
7213
  // src/tasks/required-conditions-check.ts
7067
- import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter4 } from "@listr2/prompt-adapter-enquirer";
7214
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter6 } from "@listr2/prompt-adapter-enquirer";
7068
7215
 
7069
7216
  // src/registry/custom-registry.ts
7070
- import { exec as exec6 } from "tinyexec";
7217
+ import { exec as exec7 } from "tinyexec";
7071
7218
  var CustomRegistry = class extends NpmRegistry {
7072
7219
  async npm(args) {
7073
- const { stdout } = await exec6(
7220
+ const { stdout } = await exec7(
7074
7221
  "npm",
7075
7222
  args.concat("--registry", this.registry),
7076
7223
  { throwOnError: true }
@@ -7172,7 +7319,7 @@ var requiredConditionsCheckTask = (options) => createListr({
7172
7319
  task: async (_3, task) => {
7173
7320
  const jsr = await jsrRegistry();
7174
7321
  if (!await jsr.isInstalled()) {
7175
- const install = await task.prompt(ListrEnquirerPromptAdapter4).run({
7322
+ const install = await task.prompt(ListrEnquirerPromptAdapter6).run({
7176
7323
  type: "toggle",
7177
7324
  message: `${warningBadge} jsr is not installed. Do you want to install jsr?`,
7178
7325
  enabled: "Yes",
@@ -7306,14 +7453,66 @@ async function collectPublishTasks(ctx) {
7306
7453
  }
7307
7454
  return collectRegistries(ctx).map(registryTask);
7308
7455
  }
7456
+ function dryRunRegistryTask(registry) {
7457
+ switch (registry) {
7458
+ case "npm":
7459
+ return npmDryRunPublishTask;
7460
+ case "jsr":
7461
+ return jsrDryRunPublishTask;
7462
+ case "crates":
7463
+ return cratesDryRunPublishTask;
7464
+ default:
7465
+ return npmDryRunPublishTask;
7466
+ }
7467
+ }
7468
+ async function collectDryRunPublishTasks(ctx) {
7469
+ if (ctx.packages?.length) {
7470
+ const nonCratesTasks = ctx.packages.flatMap(
7471
+ (pkg) => pkg.registries.filter((reg) => reg !== "crates").map((reg) => dryRunRegistryTask(reg))
7472
+ );
7473
+ const cratesPaths = ctx.packages.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path);
7474
+ if (cratesPaths.length === 0) {
7475
+ return nonCratesTasks;
7476
+ }
7477
+ const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
7478
+ const sequentialCratesTask = {
7479
+ title: "Dry-run crates.io publish (sequential)",
7480
+ task: (_ctx, task) => task.newListr(
7481
+ sortedPaths.map((p) => createCratesDryRunPublishTask(p)),
7482
+ { concurrent: false }
7483
+ )
7484
+ };
7485
+ return [...nonCratesTasks, sequentialCratesTask];
7486
+ }
7487
+ return collectRegistries(ctx).map(dryRunRegistryTask);
7488
+ }
7309
7489
  async function run(options) {
7310
7490
  const ctx = {
7311
7491
  ...options,
7312
7492
  promptEnabled: !isCI2 && process14.stdin.isTTY
7313
7493
  };
7494
+ let cleanupEnv;
7314
7495
  try {
7315
7496
  if (options.contents) process14.chdir(options.contents);
7316
- if (!options.publishOnly) {
7497
+ if (options.preflight) {
7498
+ await createListr({
7499
+ title: "Collecting registry tokens",
7500
+ task: async (ctx2, task) => {
7501
+ const registries2 = collectRegistries(ctx2);
7502
+ const tokens = await collectTokens(registries2, task);
7503
+ await promptGhSecretsSync(tokens, task);
7504
+ cleanupEnv = injectTokensToEnv(tokens);
7505
+ ctx2.promptEnabled = false;
7506
+ }
7507
+ }).run(ctx);
7508
+ await prerequisitesCheckTask({
7509
+ skip: options.skipPrerequisitesCheck
7510
+ }).run(ctx);
7511
+ await requiredConditionsCheckTask({
7512
+ skip: options.skipConditionsCheck
7513
+ }).run(ctx);
7514
+ }
7515
+ if (!options.publishOnly && !options.preflight) {
7317
7516
  await prerequisitesCheckTask({
7318
7517
  skip: options.skipPrerequisitesCheck
7319
7518
  }).run(ctx);
@@ -7327,14 +7526,14 @@ async function run(options) {
7327
7526
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7328
7527
  concurrent: true
7329
7528
  })
7330
- } : [
7529
+ } : options.preflight ? [
7331
7530
  {
7332
7531
  skip: options.skipTests,
7333
7532
  title: "Running tests",
7334
7533
  task: async (ctx2) => {
7335
7534
  const packageManager = await getPackageManager();
7336
7535
  try {
7337
- await exec7(packageManager, ["run", ctx2.testScript], {
7536
+ await exec8(packageManager, ["run", ctx2.testScript], {
7338
7537
  throwOnError: true
7339
7538
  });
7340
7539
  } catch (error) {
@@ -7351,7 +7550,7 @@ async function run(options) {
7351
7550
  task: async (ctx2) => {
7352
7551
  const packageManager = await getPackageManager();
7353
7552
  try {
7354
- await exec7(packageManager, ["run", ctx2.buildScript], {
7553
+ await exec8(packageManager, ["run", ctx2.buildScript], {
7355
7554
  throwOnError: true
7356
7555
  });
7357
7556
  } catch (error) {
@@ -7363,8 +7562,45 @@ async function run(options) {
7363
7562
  }
7364
7563
  },
7365
7564
  {
7366
- ...dryRunPublishTask,
7367
- skip: (ctx2) => options.skipPublish || !!ctx2.preview
7565
+ title: "Validating publish (dry-run)",
7566
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
7567
+ concurrent: true
7568
+ })
7569
+ }
7570
+ ] : [
7571
+ {
7572
+ skip: options.skipTests,
7573
+ title: "Running tests",
7574
+ task: async (ctx2) => {
7575
+ const packageManager = await getPackageManager();
7576
+ try {
7577
+ await exec8(packageManager, ["run", ctx2.testScript], {
7578
+ throwOnError: true
7579
+ });
7580
+ } catch (error) {
7581
+ throw new AbstractError(
7582
+ `Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
7583
+ { cause: error }
7584
+ );
7585
+ }
7586
+ }
7587
+ },
7588
+ {
7589
+ skip: options.skipBuild,
7590
+ title: "Building the project",
7591
+ task: async (ctx2) => {
7592
+ const packageManager = await getPackageManager();
7593
+ try {
7594
+ await exec8(packageManager, ["run", ctx2.buildScript], {
7595
+ throwOnError: true
7596
+ });
7597
+ } catch (error) {
7598
+ throw new AbstractError(
7599
+ `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
7600
+ { cause: error }
7601
+ );
7602
+ }
7603
+ }
7368
7604
  },
7369
7605
  {
7370
7606
  title: "Bumping version",
@@ -7480,13 +7716,24 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
7480
7716
  parts.push(`${color.bold(name)} on ${color.red("crates.io")}`);
7481
7717
  }
7482
7718
  }
7483
- console.log(
7484
- `
7719
+ if (options.preflight) {
7720
+ cleanupEnv?.();
7721
+ console.log(
7722
+ `
7723
+
7724
+ \u2705 Preflight check passed. CI publish should succeed for ${parts.join(", ")}.
7725
+ `
7726
+ );
7727
+ } else {
7728
+ console.log(
7729
+ `
7485
7730
 
7486
7731
  \u{1F680} Successfully published ${parts.join(", ")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
7487
7732
  `
7488
- );
7733
+ );
7734
+ }
7489
7735
  } catch (e2) {
7736
+ cleanupEnv?.();
7490
7737
  consoleError(e2);
7491
7738
  await rollback();
7492
7739
  process14.exit(1);
@@ -7535,7 +7782,7 @@ async function pubm(options) {
7535
7782
  }
7536
7783
 
7537
7784
  // src/tasks/required-missing-information.ts
7538
- import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter5 } from "@listr2/prompt-adapter-enquirer";
7785
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter7 } from "@listr2/prompt-adapter-enquirer";
7539
7786
  import semver2 from "semver";
7540
7787
  var { RELEASE_TYPES, SemVer: SemVer2, prerelease: prerelease2 } = semver2;
7541
7788
  var requiredMissingInformationTasks = (options) => createListr({
@@ -7547,7 +7794,7 @@ var requiredMissingInformationTasks = (options) => createListr({
7547
7794
  skip: (ctx) => !!ctx.version,
7548
7795
  task: async (ctx, task) => {
7549
7796
  const currentVersion = await version();
7550
- let nextVersion = await task.prompt(ListrEnquirerPromptAdapter5).run({
7797
+ let nextVersion = await task.prompt(ListrEnquirerPromptAdapter7).run({
7551
7798
  type: "select",
7552
7799
  message: "Select SemVer increment or specify new version",
7553
7800
  choices: RELEASE_TYPES.map((releaseType) => {
@@ -7562,7 +7809,7 @@ var requiredMissingInformationTasks = (options) => createListr({
7562
7809
  name: "version"
7563
7810
  });
7564
7811
  if (nextVersion === "specify") {
7565
- nextVersion = await task.prompt(ListrEnquirerPromptAdapter5).run({
7812
+ nextVersion = await task.prompt(ListrEnquirerPromptAdapter7).run({
7566
7813
  type: "input",
7567
7814
  message: "Version",
7568
7815
  name: "version"
@@ -7584,7 +7831,7 @@ var requiredMissingInformationTasks = (options) => createListr({
7584
7831
  )
7585
7832
  ].filter((tag2) => tag2 !== defaultOptions.tag);
7586
7833
  if (distTags.length <= 0) distTags.push("next");
7587
- let tag = await task.prompt(ListrEnquirerPromptAdapter5).run({
7834
+ let tag = await task.prompt(ListrEnquirerPromptAdapter7).run({
7588
7835
  type: "select",
7589
7836
  message: "Select the tag for this pre-release version in npm",
7590
7837
  choices: distTags.map((distTag) => ({
@@ -7596,7 +7843,7 @@ var requiredMissingInformationTasks = (options) => createListr({
7596
7843
  name: "tag"
7597
7844
  });
7598
7845
  if (tag === "specify") {
7599
- tag = await task.prompt(ListrEnquirerPromptAdapter5).run({
7846
+ tag = await task.prompt(ListrEnquirerPromptAdapter7).run({
7600
7847
  type: "input",
7601
7848
  message: "Tag",
7602
7849
  name: "tag"
@@ -7682,6 +7929,11 @@ var publishOptions = [
7682
7929
  description: "Run only publish task for latest tag",
7683
7930
  options: { type: Boolean }
7684
7931
  },
7932
+ {
7933
+ rawName: "--preflight",
7934
+ description: "Simulate CI publish locally (dry-run with token-based auth)",
7935
+ options: { type: Boolean }
7936
+ },
7685
7937
  {
7686
7938
  rawName: "-t, --tag <name>",
7687
7939
  description: "Publish under a specific dist-tag",
@@ -7712,7 +7964,8 @@ function resolveCliOptions(options) {
7712
7964
  skipBuild: !options.build,
7713
7965
  registries: options.registry?.split(","),
7714
7966
  skipPrerequisitesCheck: !options.preCheck,
7715
- skipConditionsCheck: !options.conditionCheck
7967
+ skipConditionsCheck: !options.conditionCheck,
7968
+ preflight: options.preflight
7716
7969
  };
7717
7970
  }
7718
7971
  var cli = cac("pubm");
@@ -7724,6 +7977,7 @@ registerSnapshotCommand(cli);
7724
7977
  registerInitCommand(cli);
7725
7978
  registerMigrateCommand(cli);
7726
7979
  registerUpdateCommand(cli);
7980
+ registerSecretsCommand(cli);
7727
7981
  var defaultCmd = cli.command("[version]", "Publish packages to registries");
7728
7982
  for (const option of publishOptions) {
7729
7983
  defaultCmd.option(option.rawName, option.description, option.options);
@@ -7739,7 +7993,9 @@ defaultCmd.action(
7739
7993
  tag: options.tag
7740
7994
  };
7741
7995
  try {
7742
- if (isCI3) {
7996
+ if (options.preflight) {
7997
+ context.version = nextVersion || "0.0.0-preflight";
7998
+ } else if (isCI3) {
7743
7999
  if (options.publishOnly) {
7744
8000
  const git = new Git();
7745
8001
  const latestVersion = (await git.latestTag())?.slice(1);