pubm 0.1.3 → 0.1.5

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/dist/index.cjs CHANGED
@@ -1960,6 +1960,80 @@ __export(src_exports, {
1960
1960
  });
1961
1961
  module.exports = __toCommonJS(src_exports);
1962
1962
 
1963
+ // src/config/defaults.ts
1964
+ var defaultValidate = {
1965
+ cleanInstall: true,
1966
+ entryPoints: true,
1967
+ extraneousFiles: true
1968
+ };
1969
+ var defaultSnapshot = {
1970
+ useCalculatedVersion: false,
1971
+ prereleaseTemplate: "{tag}-{timestamp}"
1972
+ };
1973
+ var defaultConfig = {
1974
+ versioning: "independent",
1975
+ branch: "main",
1976
+ changelog: true,
1977
+ changelogFormat: "default",
1978
+ commit: false,
1979
+ access: "public",
1980
+ fixed: [],
1981
+ linked: [],
1982
+ updateInternalDependencies: "patch",
1983
+ ignore: [],
1984
+ tag: "latest",
1985
+ contents: ".",
1986
+ saveToken: true,
1987
+ releaseDraft: true,
1988
+ releaseNotes: true,
1989
+ registries: ["npm", "jsr"],
1990
+ rollbackStrategy: "individual"
1991
+ };
1992
+ function resolveConfig(config) {
1993
+ const packages = config.packages ?? [
1994
+ { path: ".", registries: ["npm", "jsr"] }
1995
+ ];
1996
+ return {
1997
+ ...defaultConfig,
1998
+ ...config,
1999
+ packages,
2000
+ validate: { ...defaultValidate, ...config.validate },
2001
+ snapshot: { ...defaultSnapshot, ...config.snapshot }
2002
+ };
2003
+ }
2004
+
2005
+ // src/config/loader.ts
2006
+ var import_promises = require("fs/promises");
2007
+ var import_node_path = __toESM(require("path"), 1);
2008
+ var CONFIG_FILES = [
2009
+ "pubm.config.ts",
2010
+ "pubm.config.mts",
2011
+ "pubm.config.cts",
2012
+ "pubm.config.js",
2013
+ "pubm.config.mjs",
2014
+ "pubm.config.cjs"
2015
+ ];
2016
+ async function findConfigFile(cwd) {
2017
+ for (const file of CONFIG_FILES) {
2018
+ const filePath = import_node_path.default.join(cwd, file);
2019
+ try {
2020
+ if ((await (0, import_promises.stat)(filePath)).isFile()) {
2021
+ return filePath;
2022
+ }
2023
+ } catch {
2024
+ }
2025
+ }
2026
+ return null;
2027
+ }
2028
+ async function loadConfig(cwd = process.cwd()) {
2029
+ const configPath = await findConfigFile(cwd);
2030
+ if (!configPath) return null;
2031
+ const { createJiti } = await import("jiti");
2032
+ const jiti = createJiti(cwd, { interopDefault: true });
2033
+ const mod = await jiti.import(configPath);
2034
+ return mod.default ?? mod;
2035
+ }
2036
+
1963
2037
  // src/options.ts
1964
2038
  var defaultOptions = {
1965
2039
  testScript: "test",
@@ -1969,13 +2043,16 @@ var defaultOptions = {
1969
2043
  registries: ["npm", "jsr"]
1970
2044
  };
1971
2045
  function resolveOptions(options) {
1972
- const nextOptions = { ...options, ...defaultOptions };
2046
+ const defined = Object.fromEntries(
2047
+ Object.entries(options).filter(([, v]) => v !== void 0)
2048
+ );
2049
+ const nextOptions = { ...defaultOptions, ...defined };
1973
2050
  return nextOptions;
1974
2051
  }
1975
2052
 
1976
2053
  // src/tasks/runner.ts
1977
2054
  var import_node_process8 = __toESM(require("process"), 1);
1978
- var import_promise_spawn = __toESM(require("@npmcli/promise-spawn"), 1);
2055
+ var import_promise_spawn3 = __toESM(require("@npmcli/promise-spawn"), 1);
1979
2056
 
1980
2057
  // node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.mjs
1981
2058
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -1996,15 +2073,15 @@ var isCompatibleTerminal = tty && tty.isatty && tty.isatty(1) && env.TERM && !is
1996
2073
  var isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
1997
2074
  var isColorSupported = !isDisabled && (isForced || isWindows && !isDumbTerminal || isCompatibleTerminal || isCI);
1998
2075
  var replaceClose = (index, string, close, replace, head = string.substring(0, index) + replace, tail = string.substring(index + close.length), next = tail.indexOf(close)) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
1999
- var clearBleed = (index, string, open2, close, replace) => index < 0 ? open2 + string + close : open2 + replaceClose(index, string, close, replace) + close;
2000
- var filterEmpty = (open2, close, replace = open2, at = open2.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(
2076
+ var clearBleed = (index, string, open4, close, replace) => index < 0 ? open4 + string + close : open4 + replaceClose(index, string, close, replace) + close;
2077
+ var filterEmpty = (open4, close, replace = open4, at = open4.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(
2001
2078
  ("" + string).indexOf(close, at),
2002
2079
  string,
2003
- open2,
2080
+ open4,
2004
2081
  close,
2005
2082
  replace
2006
2083
  ) : "";
2007
- var init = (open2, close, replace) => filterEmpty(`\x1B[${open2}m`, `\x1B[${close}m`, replace);
2084
+ var init = (open4, close, replace) => filterEmpty(`\x1B[${open4}m`, `\x1B[${close}m`, replace);
2008
2085
  var colors = {
2009
2086
  reset: init(0, 0),
2010
2087
  bold: init(1, 22, "\x1B[22m\x1B[1m"),
@@ -4484,7 +4561,7 @@ var Git = class {
4484
4561
  try {
4485
4562
  return (await this.git(["tag", "-l"])).trim().split("\n").sort(import_semver.default.compareIdentifiers);
4486
4563
  } catch (error) {
4487
- throw new GitError("Failed to run `git config --get user.name`", {
4564
+ throw new GitError("Failed to run `git tag -l`", {
4488
4565
  cause: error
4489
4566
  });
4490
4567
  }
@@ -4530,7 +4607,8 @@ var Git = class {
4530
4607
  async revisionDiffsCount() {
4531
4608
  try {
4532
4609
  return Number.parseInt(
4533
- await this.git(["rev-list", "@{u}...HEAD", "--count", "--left-only"])
4610
+ await this.git(["rev-list", "@{u}...HEAD", "--count", "--left-only"]),
4611
+ 10
4534
4612
  );
4535
4613
  } catch (error) {
4536
4614
  throw new GitError(
@@ -4747,8 +4825,24 @@ async function rollback() {
4747
4825
  called = true;
4748
4826
  if (rollbacks.length <= 0) return void 0;
4749
4827
  console.log("Rollback...");
4750
- await Promise.all(rollbacks.map(({ fn, ctx }) => fn(ctx)));
4751
- console.log("Rollback completed");
4828
+ const results = await Promise.allSettled(
4829
+ rollbacks.map(({ fn, ctx }) => fn(ctx))
4830
+ );
4831
+ const failures = results.filter(
4832
+ (r) => r.status === "rejected"
4833
+ );
4834
+ if (failures.length > 0) {
4835
+ for (const failure of failures) {
4836
+ console.error(
4837
+ `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
4838
+ );
4839
+ }
4840
+ console.log(
4841
+ "Rollback completed with errors. Some operations may require manual recovery."
4842
+ );
4843
+ } else {
4844
+ console.log("Rollback completed");
4845
+ }
4752
4846
  }
4753
4847
 
4754
4848
  // src/utils/listr.ts
@@ -4760,8 +4854,8 @@ function createListr(...args) {
4760
4854
  }
4761
4855
 
4762
4856
  // src/utils/package.ts
4763
- var import_promises = require("fs/promises");
4764
- var import_node_path = __toESM(require("path"), 1);
4857
+ var import_promises2 = require("fs/promises");
4858
+ var import_node_path2 = __toESM(require("path"), 1);
4765
4859
  var import_node_process5 = __toESM(require("process"), 1);
4766
4860
  var cachedPackageJson = {};
4767
4861
  var cachedJsrJson = {};
@@ -4771,16 +4865,16 @@ function patchCachedJsrJson(contents, { cwd = import_node_process5.default.cwd()
4771
4865
  async function findOutFile(file, { cwd = import_node_process5.default.cwd() } = {}) {
4772
4866
  let directory = cwd;
4773
4867
  let filePath = "";
4774
- const { root } = import_node_path.default.parse(cwd);
4868
+ const { root } = import_node_path2.default.parse(cwd);
4775
4869
  while (directory) {
4776
- filePath = import_node_path.default.join(directory, file);
4870
+ filePath = import_node_path2.default.join(directory, file);
4777
4871
  try {
4778
- if ((await (0, import_promises.stat)(filePath)).isFile()) {
4872
+ if ((await (0, import_promises2.stat)(filePath)).isFile()) {
4779
4873
  break;
4780
4874
  }
4781
4875
  } catch {
4782
4876
  }
4783
- directory = import_node_path.default.dirname(directory);
4877
+ directory = import_node_path2.default.dirname(directory);
4784
4878
  if (directory === root) return null;
4785
4879
  }
4786
4880
  return filePath;
@@ -4792,7 +4886,7 @@ async function getPackageJson({
4792
4886
  if (cachedPackageJson[cwd]) return cachedPackageJson[cwd];
4793
4887
  try {
4794
4888
  const packageJsonPath = await findOutFile("package.json");
4795
- const raw = packageJsonPath && (await (0, import_promises.readFile)(packageJsonPath)).toString();
4889
+ const raw = packageJsonPath && (await (0, import_promises2.readFile)(packageJsonPath)).toString();
4796
4890
  if (!raw) {
4797
4891
  if (!fallbackJsr) {
4798
4892
  throw new Error(
@@ -4825,7 +4919,7 @@ async function getJsrJson({
4825
4919
  if (cachedJsrJson[cwd]) return cachedJsrJson[cwd];
4826
4920
  try {
4827
4921
  const jsrJsonPath = await findOutFile("jsr.json");
4828
- const raw = jsrJsonPath && (await (0, import_promises.readFile)(jsrJsonPath)).toString();
4922
+ const raw = jsrJsonPath && (await (0, import_promises2.readFile)(jsrJsonPath)).toString();
4829
4923
  if (!raw) {
4830
4924
  if (!fallbackPackage) {
4831
4925
  throw new Error(
@@ -4914,21 +5008,35 @@ async function replaceVersion(version2) {
4914
5008
  (async () => {
4915
5009
  const packageJsonPath = await findOutFile("package.json");
4916
5010
  if (!packageJsonPath) return void 0;
4917
- const packageJson = (await (0, import_promises.readFile)(packageJsonPath)).toString();
4918
- await (0, import_promises.writeFile)(
4919
- packageJsonPath,
4920
- packageJson.replace(versionRegex, `$1${version2}$2`)
4921
- );
5011
+ const packageJson = (await (0, import_promises2.readFile)(packageJsonPath)).toString();
5012
+ try {
5013
+ await (0, import_promises2.writeFile)(
5014
+ packageJsonPath,
5015
+ packageJson.replace(versionRegex, `$1${version2}$2`)
5016
+ );
5017
+ } catch (error) {
5018
+ throw new AbstractError(
5019
+ `Failed to write version to package.json: ${error instanceof Error ? error.message : error}`,
5020
+ { cause: error }
5021
+ );
5022
+ }
4922
5023
  return "package.json";
4923
5024
  })(),
4924
5025
  (async () => {
4925
5026
  const jsrJsonPath = await findOutFile("jsr.json");
4926
5027
  if (!jsrJsonPath) return void 0;
4927
- const jsrJson = (await (0, import_promises.readFile)(jsrJsonPath)).toString();
4928
- await (0, import_promises.writeFile)(
4929
- jsrJsonPath,
4930
- jsrJson.replace(versionRegex, `$1${version2}$2`)
4931
- );
5028
+ const jsrJson = (await (0, import_promises2.readFile)(jsrJsonPath)).toString();
5029
+ try {
5030
+ await (0, import_promises2.writeFile)(
5031
+ jsrJsonPath,
5032
+ jsrJson.replace(versionRegex, `$1${version2}$2`)
5033
+ );
5034
+ } catch (error) {
5035
+ throw new AbstractError(
5036
+ `Failed to write version to jsr.json: ${error instanceof Error ? error.message : error}`,
5037
+ { cause: error }
5038
+ );
5039
+ }
4932
5040
  return "jsr.json";
4933
5041
  })()
4934
5042
  ]);
@@ -4947,12 +5055,13 @@ async function getPackageManager() {
4947
5055
  if (await findOutFile(lockFile2)) return packageManager;
4948
5056
  }
4949
5057
  }
5058
+ console.warn("No lock file found, defaulting to npm.");
4950
5059
  return "npm";
4951
5060
  }
4952
5061
 
4953
5062
  // src/ecosystem/rust.ts
4954
- var import_promises2 = require("fs/promises");
4955
- var import_node_path2 = __toESM(require("path"), 1);
5063
+ var import_promises3 = require("fs/promises");
5064
+ var import_node_path3 = __toESM(require("path"), 1);
4956
5065
  var import_smol_toml = require("smol-toml");
4957
5066
 
4958
5067
  // src/ecosystem/ecosystem.ts
@@ -4966,14 +5075,14 @@ var Ecosystem = class {
4966
5075
  var RustEcosystem = class extends Ecosystem {
4967
5076
  static async detect(packagePath) {
4968
5077
  try {
4969
- return (await (0, import_promises2.stat)(import_node_path2.default.join(packagePath, "Cargo.toml"))).isFile();
5078
+ return (await (0, import_promises3.stat)(import_node_path3.default.join(packagePath, "Cargo.toml"))).isFile();
4970
5079
  } catch {
4971
5080
  return false;
4972
5081
  }
4973
5082
  }
4974
5083
  async readCargoToml() {
4975
- const raw = await (0, import_promises2.readFile)(
4976
- import_node_path2.default.join(this.packagePath, "Cargo.toml"),
5084
+ const raw = await (0, import_promises3.readFile)(
5085
+ import_node_path3.default.join(this.packagePath, "Cargo.toml"),
4977
5086
  "utf-8"
4978
5087
  );
4979
5088
  return (0, import_smol_toml.parse)(raw);
@@ -4989,12 +5098,12 @@ var RustEcosystem = class extends Ecosystem {
4989
5098
  return pkg.version;
4990
5099
  }
4991
5100
  async writeVersion(newVersion) {
4992
- const filePath = import_node_path2.default.join(this.packagePath, "Cargo.toml");
4993
- const raw = await (0, import_promises2.readFile)(filePath, "utf-8");
5101
+ const filePath = import_node_path3.default.join(this.packagePath, "Cargo.toml");
5102
+ const raw = await (0, import_promises3.readFile)(filePath, "utf-8");
4994
5103
  const cargo = (0, import_smol_toml.parse)(raw);
4995
5104
  const pkg = cargo.package;
4996
5105
  pkg.version = newVersion;
4997
- await (0, import_promises2.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
5106
+ await (0, import_promises3.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
4998
5107
  }
4999
5108
  manifestFiles() {
5000
5109
  return ["Cargo.toml"];
@@ -5065,13 +5174,21 @@ var CratesRegistry = class extends Registry {
5065
5174
  { headers: this.headers }
5066
5175
  );
5067
5176
  if (!response.ok) {
5068
- throw new Error(`Crate '${this.packageName}' not found`);
5177
+ if (response.status === 404) {
5178
+ throw new CratesError(
5179
+ `Crate '${this.packageName}' not found on crates.io`
5180
+ );
5181
+ }
5182
+ throw new CratesError(
5183
+ `crates.io API error (HTTP ${response.status}) for crate '${this.packageName}'`
5184
+ );
5069
5185
  }
5070
5186
  const data = await response.json();
5071
5187
  return data.crate.max_version;
5072
5188
  } catch (error) {
5189
+ if (error instanceof CratesError) throw error;
5073
5190
  throw new CratesError(
5074
- `Failed to fetch version for crate '${this.packageName}'`,
5191
+ `Cannot reach crates.io to fetch version for '${this.packageName}'`,
5075
5192
  { cause: error }
5076
5193
  );
5077
5194
  }
@@ -5101,6 +5218,12 @@ var CratesRegistry = class extends Registry {
5101
5218
  if (process.env.CARGO_REGISTRY_TOKEN) return true;
5102
5219
  return this.isInstalled();
5103
5220
  }
5221
+ getRequirements() {
5222
+ return {
5223
+ needsPackageScripts: false,
5224
+ requiredManifest: "Cargo.toml"
5225
+ };
5226
+ }
5104
5227
  async isPackageNameAvaliable() {
5105
5228
  try {
5106
5229
  const response = await fetch(
@@ -5108,8 +5231,11 @@ var CratesRegistry = class extends Registry {
5108
5231
  { headers: this.headers }
5109
5232
  );
5110
5233
  return !response.ok;
5111
- } catch {
5112
- return true;
5234
+ } catch (error) {
5235
+ throw new CratesError(
5236
+ `Failed to check package name availability on crates.io`,
5237
+ { cause: error }
5238
+ );
5113
5239
  }
5114
5240
  }
5115
5241
  };
@@ -5158,6 +5284,7 @@ var cratesPublishTasks = {
5158
5284
  // src/tasks/jsr.ts
5159
5285
  var import_node_process6 = __toESM(require("process"), 1);
5160
5286
  var import_prompt_adapter_enquirer = require("@listr2/prompt-adapter-enquirer");
5287
+ var import_promise_spawn = __toESM(require("@npmcli/promise-spawn"), 1);
5161
5288
 
5162
5289
  // src/registry/jsr.ts
5163
5290
  var import_tinyexec3 = require("tinyexec");
@@ -5165,7 +5292,7 @@ var import_tinyexec3 = require("tinyexec");
5165
5292
  // src/utils/db.ts
5166
5293
  var import_node_crypto = require("crypto");
5167
5294
  var import_node_fs = require("fs");
5168
- var import_node_path3 = __toESM(require("path"), 1);
5295
+ var import_node_path4 = __toESM(require("path"), 1);
5169
5296
  var import_meta = {};
5170
5297
  var a = "aes-256-cbc";
5171
5298
  var n = (0, import_node_fs.statSync)(import_meta.dirname);
@@ -5181,38 +5308,56 @@ function d(g, h) {
5181
5308
  }
5182
5309
  var Db = class {
5183
5310
  constructor() {
5184
- __publicField(this, "path", import_node_path3.default.resolve(import_meta.dirname, ".pubm"));
5311
+ __publicField(this, "path", import_node_path4.default.resolve(import_meta.dirname, ".pubm"));
5185
5312
  try {
5186
5313
  if (!(0, import_node_fs.statSync)(this.path).isDirectory()) {
5187
5314
  (0, import_node_fs.mkdirSync)(this.path);
5188
5315
  }
5189
5316
  } catch {
5190
- (0, import_node_fs.mkdirSync)(this.path);
5317
+ try {
5318
+ (0, import_node_fs.mkdirSync)(this.path);
5319
+ } catch (error) {
5320
+ throw new Error(
5321
+ `Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
5322
+ );
5323
+ }
5191
5324
  }
5192
5325
  }
5193
5326
  set(field, value) {
5194
- (0, import_node_fs.writeFileSync)(
5195
- import_node_path3.default.resolve(this.path, Buffer.from(e(field, field)).toString("base64")),
5196
- Buffer.from(e(`${value}`, field)),
5197
- { encoding: "binary" }
5198
- );
5327
+ try {
5328
+ (0, import_node_fs.writeFileSync)(
5329
+ import_node_path4.default.resolve(
5330
+ this.path,
5331
+ Buffer.from(e(field, field)).toString("base64")
5332
+ ),
5333
+ Buffer.from(e(`${value}`, field)),
5334
+ { encoding: "binary" }
5335
+ );
5336
+ } catch (error) {
5337
+ throw new Error(
5338
+ `Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
5339
+ );
5340
+ }
5199
5341
  }
5200
5342
  get(field) {
5343
+ const filePath = import_node_path4.default.resolve(
5344
+ this.path,
5345
+ Buffer.from(e(field, field)).toString("base64")
5346
+ );
5347
+ let raw;
5201
5348
  try {
5202
- return d(
5203
- Buffer.from(
5204
- (0, import_node_fs.readFileSync)(
5205
- import_node_path3.default.resolve(
5206
- this.path,
5207
- Buffer.from(e(field, field)).toString("base64")
5208
- )
5209
- )
5210
- ).toString(),
5211
- field
5212
- );
5349
+ raw = (0, import_node_fs.readFileSync)(filePath);
5213
5350
  } catch {
5214
5351
  return null;
5215
5352
  }
5353
+ try {
5354
+ return d(Buffer.from(raw).toString(), field);
5355
+ } catch {
5356
+ console.warn(
5357
+ `Stored token for '${field}' appears corrupted. It will be re-requested.`
5358
+ );
5359
+ return null;
5360
+ }
5216
5361
  }
5217
5362
  };
5218
5363
 
@@ -5226,9 +5371,12 @@ function getScope(packageName) {
5226
5371
  }
5227
5372
  function getScopeAndName(packageName) {
5228
5373
  const matches = packageName.match(/^@([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)$/);
5229
- const scope = matches?.[1];
5230
- const name = matches?.[2];
5231
- return [`${scope}`, `${name}`];
5374
+ if (!matches) {
5375
+ throw new Error(
5376
+ `Invalid scoped package name: '${packageName}'. Expected format: @scope/name`
5377
+ );
5378
+ }
5379
+ return [matches[1], matches[2]];
5232
5380
  }
5233
5381
  var scopedPackagePattern = /^(?:@([^/]+?)[/])?([^/]+?)$/;
5234
5382
  var blacklist = ["node_modules", "favicon.ico"];
@@ -5275,6 +5423,7 @@ var JsrRegisry = class extends Registry {
5275
5423
  super(packageName, registry);
5276
5424
  __publicField(this, "registry", "https://jsr.io");
5277
5425
  __publicField(this, "client");
5426
+ __publicField(this, "packageCreationUrls");
5278
5427
  this.client = new JsrClient(getApiEndpoint(this.registry));
5279
5428
  }
5280
5429
  async jsr(args) {
@@ -5316,9 +5465,19 @@ var JsrRegisry = class extends Registry {
5316
5465
  throwOnError: true
5317
5466
  }
5318
5467
  );
5468
+ this.packageCreationUrls = void 0;
5319
5469
  return true;
5320
5470
  } catch (error) {
5321
5471
  const stderr = error instanceof import_tinyexec3.NonZeroExitError ? error.output?.stderr : void 0;
5472
+ if (stderr?.includes("don't exist")) {
5473
+ const urls = [...stderr.matchAll(/https:\/\/jsr\.io\/new\S+/g)].map(
5474
+ (m) => m[0]
5475
+ );
5476
+ if (urls.length > 0) {
5477
+ this.packageCreationUrls = urls;
5478
+ return false;
5479
+ }
5480
+ }
5322
5481
  throw new JsrError(
5323
5482
  `Failed to run \`jsr publish --allow-dirty --token ***\`${stderr ? `
5324
5483
  ${stderr}` : ""}`,
@@ -5348,6 +5507,12 @@ ${stderr}` : ""}`,
5348
5507
  async isPackageNameAvaliable() {
5349
5508
  return isValidPackageName(this.packageName);
5350
5509
  }
5510
+ getRequirements() {
5511
+ return {
5512
+ needsPackageScripts: false,
5513
+ requiredManifest: "jsr.json"
5514
+ };
5515
+ }
5351
5516
  };
5352
5517
  var _JsrClient = class _JsrClient {
5353
5518
  constructor(apiEndpoint) {
@@ -5369,9 +5534,7 @@ var _JsrClient = class _JsrClient {
5369
5534
  const response = await this.fetch("/user");
5370
5535
  if (response.status === 401) return null;
5371
5536
  if (!response.ok) {
5372
- throw new Error(
5373
- `HTTP ${response.status}: ${response.statusText}`
5374
- );
5537
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5375
5538
  }
5376
5539
  return await response.json();
5377
5540
  } catch (error) {
@@ -5385,9 +5548,7 @@ var _JsrClient = class _JsrClient {
5385
5548
  const response = await this.fetch(`/user/member/${scope}`);
5386
5549
  if (response.status === 401) return null;
5387
5550
  if (!response.ok) {
5388
- throw new Error(
5389
- `HTTP ${response.status}: ${response.statusText}`
5390
- );
5551
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5391
5552
  }
5392
5553
  return await response.json();
5393
5554
  } catch (error) {
@@ -5404,15 +5565,11 @@ var _JsrClient = class _JsrClient {
5404
5565
  const response = await this.fetch("/user/scopes");
5405
5566
  if (response.status === 401) return [];
5406
5567
  if (!response.ok) {
5407
- throw new Error(
5408
- `HTTP ${response.status}: ${response.statusText}`
5409
- );
5568
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5410
5569
  }
5411
5570
  const body = await response.json();
5412
5571
  if (!Array.isArray(body)) {
5413
- throw new Error(
5414
- `Expected array response but got ${typeof body}`
5415
- );
5572
+ throw new Error(`Expected array response but got ${typeof body}`);
5416
5573
  }
5417
5574
  return body.map(({ scope }) => scope);
5418
5575
  } catch (error) {
@@ -5428,9 +5585,15 @@ var _JsrClient = class _JsrClient {
5428
5585
  const [scope, name] = getScopeAndName(packageName);
5429
5586
  try {
5430
5587
  const response = await this.fetch(`/scopes/${scope}/packages/${name}`);
5431
- if (!response.ok) return null;
5588
+ if (response.status === 404) return null;
5589
+ if (!response.ok) {
5590
+ throw new JsrError(
5591
+ `JSR API error (HTTP ${response.status}) for package '${packageName}'`
5592
+ );
5593
+ }
5432
5594
  return await response.json();
5433
5595
  } catch (error) {
5596
+ if (error instanceof JsrError) throw error;
5434
5597
  throw new JsrError(
5435
5598
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages/${name}\``,
5436
5599
  {
@@ -5445,8 +5608,18 @@ var _JsrClient = class _JsrClient {
5445
5608
  method: "POST",
5446
5609
  body: JSON.stringify({ scope })
5447
5610
  });
5448
- return response.status === 200 || response.status === 201;
5611
+ if (response.status === 200 || response.status === 201) return true;
5612
+ let detail = "";
5613
+ try {
5614
+ const body = await response.json();
5615
+ detail = body.message || body.error || JSON.stringify(body);
5616
+ } catch {
5617
+ }
5618
+ throw new JsrError(
5619
+ `Failed to create scope '${scope}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5620
+ );
5449
5621
  } catch (error) {
5622
+ if (error instanceof JsrError) throw error;
5450
5623
  throw new JsrError(`Failed to fetch \`${this.apiEndpoint}/scopes\``, {
5451
5624
  cause: error
5452
5625
  });
@@ -5457,8 +5630,18 @@ var _JsrClient = class _JsrClient {
5457
5630
  const response = await this.fetch(`/scopes/${scope}`, {
5458
5631
  method: "DELETE"
5459
5632
  });
5460
- return response.status === 200 || response.status === 204;
5633
+ if (response.status === 200 || response.status === 204) return true;
5634
+ let detail = "";
5635
+ try {
5636
+ const body = await response.json();
5637
+ detail = body.message || body.error || JSON.stringify(body);
5638
+ } catch {
5639
+ }
5640
+ throw new JsrError(
5641
+ `Failed to delete scope '${scope}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5642
+ );
5461
5643
  } catch (error) {
5644
+ if (error instanceof JsrError) throw error;
5462
5645
  throw new JsrError(
5463
5646
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}\``,
5464
5647
  {
@@ -5474,8 +5657,18 @@ var _JsrClient = class _JsrClient {
5474
5657
  method: "POST",
5475
5658
  body: JSON.stringify({ package: name })
5476
5659
  });
5477
- return response.status === 200 || response.status === 201;
5660
+ if (response.status === 200 || response.status === 201) return true;
5661
+ let detail = "";
5662
+ try {
5663
+ const body = await response.json();
5664
+ detail = body.message || body.error || JSON.stringify(body);
5665
+ } catch {
5666
+ }
5667
+ throw new JsrError(
5668
+ `Failed to create package '${packageName}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5669
+ );
5478
5670
  } catch (error) {
5671
+ if (error instanceof JsrError) throw error;
5479
5672
  throw new JsrError(
5480
5673
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages\``,
5481
5674
  {
@@ -5490,8 +5683,18 @@ var _JsrClient = class _JsrClient {
5490
5683
  const response = await this.fetch(`/scopes/${scope}/packages/${name}`, {
5491
5684
  method: "DELETE"
5492
5685
  });
5493
- return response.status === 200 || response.status === 204;
5686
+ if (response.status === 200 || response.status === 204) return true;
5687
+ let detail = "";
5688
+ try {
5689
+ const body = await response.json();
5690
+ detail = body.message || body.error || JSON.stringify(body);
5691
+ } catch {
5692
+ }
5693
+ throw new JsrError(
5694
+ `Failed to delete package '${packageName}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5695
+ );
5494
5696
  } catch (error) {
5697
+ if (error instanceof JsrError) throw error;
5495
5698
  throw new JsrError(
5496
5699
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages/${name}\``,
5497
5700
  {
@@ -5504,9 +5707,7 @@ var _JsrClient = class _JsrClient {
5504
5707
  try {
5505
5708
  const response = await this.fetch(`/packages?query=${query}`);
5506
5709
  if (!response.ok) {
5507
- throw new Error(
5508
- `HTTP ${response.status}: ${response.statusText}`
5509
- );
5710
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5510
5711
  }
5511
5712
  return await response.json();
5512
5713
  } catch (error) {
@@ -5584,7 +5785,7 @@ var NpmRegistry = class extends Registry {
5584
5785
  await this.npm(["whoami"]);
5585
5786
  return true;
5586
5787
  } catch (error) {
5587
- if (error instanceof import_tinyexec4.NonZeroExitError && error.output?.stderr.includes("ENEEDAUTH")) {
5788
+ if (error instanceof import_tinyexec4.NonZeroExitError) {
5588
5789
  return false;
5589
5790
  }
5590
5791
  throw new NpmError("Failed to run `npm whoami`", { cause: error });
@@ -5592,16 +5793,22 @@ var NpmRegistry = class extends Registry {
5592
5793
  }
5593
5794
  async collaborators() {
5594
5795
  try {
5595
- return JSON.parse(
5596
- await this.npm([
5597
- "access",
5598
- "list",
5599
- "collaborators",
5600
- this.packageName,
5601
- "--json"
5602
- ])
5603
- );
5796
+ const output = await this.npm([
5797
+ "access",
5798
+ "list",
5799
+ "collaborators",
5800
+ this.packageName,
5801
+ "--json"
5802
+ ]);
5803
+ try {
5804
+ return JSON.parse(output);
5805
+ } catch {
5806
+ throw new NpmError(
5807
+ `Unexpected response from npm registry for collaborators of '${this.packageName}'`
5808
+ );
5809
+ }
5604
5810
  } catch (error) {
5811
+ if (error instanceof NpmError) throw error;
5605
5812
  throw new NpmError(
5606
5813
  `Failed to run \`npm access list collaborators ${this.packageName} --json\``,
5607
5814
  { cause: error }
@@ -5615,12 +5822,21 @@ var NpmRegistry = class extends Registry {
5615
5822
  }
5616
5823
  async distTags() {
5617
5824
  try {
5618
- return Object.keys(
5619
- JSON.parse(
5620
- await this.npm(["view", this.packageName, "dist-tags", "--json"])
5621
- )
5622
- );
5825
+ const output = await this.npm([
5826
+ "view",
5827
+ this.packageName,
5828
+ "dist-tags",
5829
+ "--json"
5830
+ ]);
5831
+ try {
5832
+ return Object.keys(JSON.parse(output));
5833
+ } catch {
5834
+ throw new NpmError(
5835
+ `Unexpected response from npm registry for dist-tags of '${this.packageName}'`
5836
+ );
5837
+ }
5623
5838
  } catch (error) {
5839
+ if (error instanceof NpmError) throw error;
5624
5840
  throw new NpmError(
5625
5841
  `Failed to run \`npm view ${this.packageName} dist-tags --json\``,
5626
5842
  { cause: error }
@@ -5650,12 +5866,7 @@ var NpmRegistry = class extends Registry {
5650
5866
  if (error instanceof import_tinyexec4.NonZeroExitError && error.output?.stderr.includes("EOTP")) {
5651
5867
  return false;
5652
5868
  }
5653
- throw new NpmError(
5654
- "Failed to run `npm publish --provenance --access public`",
5655
- {
5656
- cause: error
5657
- }
5658
- );
5869
+ throw this.classifyPublishError(error);
5659
5870
  }
5660
5871
  }
5661
5872
  async publish(otp) {
@@ -5667,14 +5878,39 @@ var NpmRegistry = class extends Registry {
5667
5878
  if (error instanceof import_tinyexec4.NonZeroExitError && error.output?.stderr.includes("EOTP")) {
5668
5879
  return false;
5669
5880
  }
5670
- throw new NpmError(`Failed to run \`npm ${args.join(" ")}\``, {
5671
- cause: error
5672
- });
5881
+ throw this.classifyPublishError(error);
5673
5882
  }
5674
5883
  }
5675
5884
  async isPackageNameAvaliable() {
5676
5885
  return isValidPackageName(this.packageName);
5677
5886
  }
5887
+ getRequirements() {
5888
+ return {
5889
+ needsPackageScripts: true,
5890
+ requiredManifest: "package.json"
5891
+ };
5892
+ }
5893
+ classifyPublishError(error) {
5894
+ if (error instanceof import_tinyexec4.NonZeroExitError) {
5895
+ const stderr = error.output?.stderr ?? "";
5896
+ if (stderr.includes("EOTP")) {
5897
+ return new NpmError("OTP required for publishing", { cause: error });
5898
+ }
5899
+ if (stderr.includes("403") || stderr.includes("Forbidden")) {
5900
+ return new NpmError(
5901
+ "Permission denied (403 Forbidden). Check your npm access token permissions.",
5902
+ { cause: error }
5903
+ );
5904
+ }
5905
+ if (stderr.includes("429") || stderr.includes("Too Many Requests")) {
5906
+ return new NpmError(
5907
+ "Rate limited by npm registry. Please wait and try again.",
5908
+ { cause: error }
5909
+ );
5910
+ }
5911
+ }
5912
+ return new NpmError("Failed to publish to npm", { cause: error });
5913
+ }
5678
5914
  };
5679
5915
  async function npmRegistry() {
5680
5916
  const packageJson = await getPackageJson();
@@ -5682,6 +5918,7 @@ async function npmRegistry() {
5682
5918
  }
5683
5919
 
5684
5920
  // src/tasks/jsr.ts
5921
+ var { open } = import_promise_spawn.default;
5685
5922
  var JsrAvailableError = class extends AbstractError {
5686
5923
  constructor(message, { cause } = {}) {
5687
5924
  super(message, { cause });
@@ -5706,17 +5943,34 @@ var jsrAvailableCheckTasks = {
5706
5943
  if (!JsrClient.token) {
5707
5944
  task.output = "Retrieving jsr API token";
5708
5945
  if (ctx.promptEnabled) {
5709
- while (true) {
5946
+ const maxAttempts = 3;
5947
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
5710
5948
  JsrClient.token = await task.prompt(import_prompt_adapter_enquirer.ListrEnquirerPromptAdapter).run({
5711
5949
  type: "password",
5712
- message: `Please enter the jsr ${color.bold("API token")}`,
5950
+ message: `Please enter the jsr ${color.bold("API token")}${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`,
5713
5951
  footer: `
5714
5952
  Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/tokens/create/"))}. ${color.red("You should select")} ${color.bold("'Interact with the JSR API'")}.`
5715
5953
  });
5716
5954
  try {
5717
5955
  if (await jsr.client.user()) break;
5718
- task.output = "The jsr API token is invalid. Please re-enter a valid token.";
5719
- } catch {
5956
+ if (attempt < maxAttempts) {
5957
+ task.output = "The jsr API token is invalid. Please re-enter a valid token.";
5958
+ }
5959
+ } catch (error) {
5960
+ if (error instanceof Error && (error.message.includes("fetch") || error.message.includes("network") || error.message.includes("ENOTFOUND"))) {
5961
+ throw new JsrAvailableError(
5962
+ "JSR API is unreachable. Check your network connection.",
5963
+ { cause: error }
5964
+ );
5965
+ }
5966
+ if (attempt < maxAttempts) {
5967
+ task.output = "The jsr API token is invalid. Please re-enter a valid token.";
5968
+ }
5969
+ }
5970
+ if (attempt === maxAttempts) {
5971
+ throw new JsrAvailableError(
5972
+ "JSR token verification failed after 3 attempts."
5973
+ );
5720
5974
  }
5721
5975
  }
5722
5976
  } else {
@@ -5845,13 +6099,48 @@ var jsrPublishTasks = {
5845
6099
  }
5846
6100
  JsrClient.token = jsrTokenEnv;
5847
6101
  }
5848
- await jsr.publish();
6102
+ let result = await jsr.publish();
6103
+ if (!result && jsr.packageCreationUrls) {
6104
+ if (ctx.promptEnabled) {
6105
+ task.title = "Running jsr publish (package creation needed)";
6106
+ const urls = jsr.packageCreationUrls;
6107
+ const maxAttempts = 3;
6108
+ task.output = `Package doesn't exist on jsr. Create it at:
6109
+ ${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
6110
+ open(urls[0]);
6111
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6112
+ await task.prompt(import_prompt_adapter_enquirer.ListrEnquirerPromptAdapter).run({
6113
+ type: "input",
6114
+ message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6115
+ });
6116
+ result = await jsr.publish();
6117
+ if (result) break;
6118
+ if (attempt < maxAttempts) {
6119
+ task.output = "Package still doesn't exist. Please create it and try again.";
6120
+ }
6121
+ }
6122
+ if (!result) {
6123
+ throw new JsrAvailableError(
6124
+ "Package creation not completed after 3 attempts."
6125
+ );
6126
+ }
6127
+ task.title = "Running jsr publish (package created)";
6128
+ } else {
6129
+ throw new JsrAvailableError(
6130
+ `Package doesn't exist on jsr. Create it at:
6131
+ ${jsr.packageCreationUrls.join("\n")}`
6132
+ );
6133
+ }
6134
+ }
5849
6135
  }
5850
6136
  };
5851
6137
 
5852
6138
  // src/tasks/npm.ts
6139
+ var import_node_child_process = require("child_process");
5853
6140
  var import_node_process7 = __toESM(require("process"), 1);
5854
6141
  var import_prompt_adapter_enquirer2 = require("@listr2/prompt-adapter-enquirer");
6142
+ var import_promise_spawn2 = __toESM(require("@npmcli/promise-spawn"), 1);
6143
+ var { open: open2 } = import_promise_spawn2.default;
5855
6144
  var NpmAvailableError = class extends AbstractError {
5856
6145
  constructor(message, { cause } = {}) {
5857
6146
  super(message, { cause });
@@ -5862,12 +6151,55 @@ var NpmAvailableError = class extends AbstractError {
5862
6151
  var npmAvailableCheckTasks = {
5863
6152
  title: "Checking npm avaliable for publising",
5864
6153
  skip: (ctx) => !!ctx.preview,
5865
- task: async () => {
6154
+ task: async (ctx, task) => {
5866
6155
  const npm = await npmRegistry();
5867
6156
  if (!await npm.isLoggedIn()) {
5868
- throw new NpmAvailableError(
5869
- "You are not logged in. Please log in first using `npm login`."
5870
- );
6157
+ if (ctx.promptEnabled) {
6158
+ try {
6159
+ task.output = "Launching npm login...";
6160
+ await new Promise((resolve, reject) => {
6161
+ const child = (0, import_node_child_process.spawn)("npm", ["login"], {
6162
+ stdio: ["pipe", "pipe", "pipe"]
6163
+ });
6164
+ let opened = false;
6165
+ const onData = (data) => {
6166
+ const text = data.toString();
6167
+ const urlMatch = text.match(
6168
+ /https:\/\/www\.npmjs\.com\/login[^\s]*/
6169
+ );
6170
+ if (urlMatch && !opened) {
6171
+ opened = true;
6172
+ task.output = `Login at: ${color.cyan(urlMatch[0])}`;
6173
+ open2(urlMatch[0]);
6174
+ child.stdin?.write("\n");
6175
+ }
6176
+ };
6177
+ child.stdout?.on("data", onData);
6178
+ child.stderr?.on("data", onData);
6179
+ child.on(
6180
+ "close",
6181
+ (code) => code === 0 ? resolve() : reject(
6182
+ new Error(`npm login exited with code ${code}`)
6183
+ )
6184
+ );
6185
+ child.on("error", reject);
6186
+ });
6187
+ } catch (error) {
6188
+ throw new NpmAvailableError(
6189
+ "npm login failed. Please run `npm login` manually and try again.",
6190
+ { cause: error }
6191
+ );
6192
+ }
6193
+ if (!await npm.isLoggedIn()) {
6194
+ throw new NpmAvailableError(
6195
+ "Still not logged in after npm login. Please verify your credentials."
6196
+ );
6197
+ }
6198
+ } else {
6199
+ throw new NpmAvailableError(
6200
+ "Not logged in to npm. Set NODE_AUTH_TOKEN in your CI environment. For GitHub Actions, add it as a repository secret."
6201
+ );
6202
+ }
5871
6203
  }
5872
6204
  if (await npm.isPublished()) {
5873
6205
  if (!await npm.hasPermission()) {
@@ -5895,24 +6227,31 @@ var npmPublishTasks = {
5895
6227
  let result = await npm.publish();
5896
6228
  if (!result) {
5897
6229
  task.title = "Running npm publish (OTP code needed)";
5898
- while (!result) {
6230
+ const maxAttempts = 3;
6231
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
5899
6232
  result = await npm.publish(
5900
6233
  await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
5901
6234
  type: "password",
5902
- message: "npm OTP code"
6235
+ message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
5903
6236
  })
5904
6237
  );
5905
- if (!result) {
5906
- task.output = "2FA failed";
6238
+ if (result) break;
6239
+ if (attempt < maxAttempts) {
6240
+ task.output = "2FA failed. Please try again.";
5907
6241
  }
5908
6242
  }
6243
+ if (!result) {
6244
+ throw new NpmAvailableError(
6245
+ "OTP verification failed after 3 attempts."
6246
+ );
6247
+ }
5909
6248
  task.title = "Running npm publish (2FA passed)";
5910
6249
  }
5911
6250
  } else {
5912
6251
  const npmTokenEnv = import_node_process7.default.env.NODE_AUTH_TOKEN;
5913
6252
  if (!npmTokenEnv) {
5914
6253
  throw new NpmAvailableError(
5915
- "NODE_AUTH_TOKEN not found in the environment variables. Please set the token and try again."
6254
+ "NODE_AUTH_TOKEN not found in environment variables. Set it in your CI configuration:\n GitHub Actions: Add NODE_AUTH_TOKEN as a repository secret\n Other CI: Export NODE_AUTH_TOKEN with your npm access token"
5916
6255
  );
5917
6256
  }
5918
6257
  const result = await npm.publishProvenance();
@@ -6018,6 +6357,7 @@ var prerequisitesCheckTask = (options) => {
6018
6357
  );
6019
6358
  }
6020
6359
  ctx.cleanWorkingTree = false;
6360
+ return;
6021
6361
  }
6022
6362
  ctx.cleanWorkingTree = true;
6023
6363
  }
@@ -6119,6 +6459,16 @@ async function validateEngineVersion(engine, version2) {
6119
6459
  }
6120
6460
 
6121
6461
  // src/tasks/required-conditions-check.ts
6462
+ var registryRequirementsMap = {
6463
+ npm: { needsPackageScripts: true },
6464
+ jsr: { needsPackageScripts: false },
6465
+ crates: { needsPackageScripts: false }
6466
+ };
6467
+ function needsPackageScripts(registries) {
6468
+ return registries.some(
6469
+ (r) => registryRequirementsMap[r]?.needsPackageScripts ?? true
6470
+ );
6471
+ }
6122
6472
  var RequiredConditionCheckError = class extends AbstractError {
6123
6473
  constructor(message, { cause } = {}) {
6124
6474
  super(message, { cause });
@@ -6194,7 +6544,7 @@ var requiredConditionsCheckTask = (options) => createListr({
6194
6544
  },
6195
6545
  {
6196
6546
  title: "Checking if test and build scripts exist",
6197
- skip: (ctx) => ctx.jsrOnly,
6547
+ skip: (ctx) => !needsPackageScripts(ctx.registries),
6198
6548
  task: async (ctx) => {
6199
6549
  const { scripts } = await getPackageJson();
6200
6550
  const errors = [];
@@ -6248,14 +6598,28 @@ var requiredConditionsCheckTask = (options) => createListr({
6248
6598
  });
6249
6599
 
6250
6600
  // src/tasks/runner.ts
6251
- var { open } = import_promise_spawn.default;
6601
+ var { open: open3 } = import_promise_spawn3.default;
6252
6602
  var { prerelease } = import_semver3.default;
6603
+ function collectRegistries(ctx) {
6604
+ if (ctx.packages?.length) {
6605
+ const seen = /* @__PURE__ */ new Set();
6606
+ const result = [];
6607
+ for (const pkg of ctx.packages) {
6608
+ for (const reg of pkg.registries) {
6609
+ if (!seen.has(reg)) {
6610
+ seen.add(reg);
6611
+ result.push(reg);
6612
+ }
6613
+ }
6614
+ }
6615
+ return result;
6616
+ }
6617
+ return ctx.registries;
6618
+ }
6253
6619
  async function run(options) {
6254
6620
  const ctx = {
6255
6621
  ...options,
6256
- promptEnabled: !import_std_env.isCI && import_node_process8.default.stdin.isTTY,
6257
- npmOnly: options.registries.every((registry) => registry !== "jsr"),
6258
- jsrOnly: options.registries.every((registry) => registry === "jsr")
6622
+ promptEnabled: !import_std_env.isCI && import_node_process8.default.stdin.isTTY
6259
6623
  };
6260
6624
  try {
6261
6625
  if (options.contents) import_node_process8.default.chdir(options.contents);
@@ -6271,7 +6635,7 @@ async function run(options) {
6271
6635
  options.publishOnly ? {
6272
6636
  title: "Publishing",
6273
6637
  task: (ctx2, parentTask) => parentTask.newListr(
6274
- ctx2.registries.map((registry) => {
6638
+ collectRegistries(ctx2).map((registry) => {
6275
6639
  switch (registry) {
6276
6640
  case "npm":
6277
6641
  return npmPublishTasks;
@@ -6291,9 +6655,16 @@ async function run(options) {
6291
6655
  title: "Running tests",
6292
6656
  task: async (ctx2) => {
6293
6657
  const packageManager = await getPackageManager();
6294
- await (0, import_tinyexec6.exec)(packageManager, ["run", ctx2.testScript], {
6295
- throwOnError: true
6296
- });
6658
+ try {
6659
+ await (0, import_tinyexec6.exec)(packageManager, ["run", ctx2.testScript], {
6660
+ throwOnError: true
6661
+ });
6662
+ } catch (error) {
6663
+ throw new AbstractError(
6664
+ `Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
6665
+ { cause: error }
6666
+ );
6667
+ }
6297
6668
  }
6298
6669
  },
6299
6670
  {
@@ -6307,7 +6678,7 @@ async function run(options) {
6307
6678
  });
6308
6679
  } catch (error) {
6309
6680
  throw new AbstractError(
6310
- `Failed to run \`${packageManager} run ${ctx2.buildScript}\``,
6681
+ `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
6311
6682
  { cause: error }
6312
6683
  );
6313
6684
  }
@@ -6322,15 +6693,27 @@ async function run(options) {
6322
6693
  let commited = false;
6323
6694
  addRollback(async () => {
6324
6695
  if (tagCreated) {
6325
- console.log("Deleting tag...");
6326
- await git.deleteTag(`${await git.latestTag()}`);
6696
+ try {
6697
+ console.log("Deleting tag...");
6698
+ await git.deleteTag(`${await git.latestTag()}`);
6699
+ } catch (error) {
6700
+ console.error(
6701
+ `Failed to delete tag: ${error instanceof Error ? error.message : error}`
6702
+ );
6703
+ }
6327
6704
  }
6328
6705
  if (commited) {
6329
- console.log("Reset commits...");
6330
- await git.reset();
6331
- await git.stash();
6332
- await git.reset("HEAD^", "--hard");
6333
- await git.popStash();
6706
+ try {
6707
+ console.log("Reset commits...");
6708
+ await git.reset();
6709
+ await git.stash();
6710
+ await git.reset("HEAD^", "--hard");
6711
+ await git.popStash();
6712
+ } catch (error) {
6713
+ console.error(
6714
+ `Failed to reset commits: ${error instanceof Error ? error.message : error}`
6715
+ );
6716
+ }
6334
6717
  }
6335
6718
  }, ctx2);
6336
6719
  await git.reset();
@@ -6350,7 +6733,7 @@ async function run(options) {
6350
6733
  skip: (ctx2) => options.skipPublish || !!ctx2.preview,
6351
6734
  title: "Publishing",
6352
6735
  task: (ctx2, parentTask) => parentTask.newListr(
6353
- ctx2.registries.map((registry) => {
6736
+ collectRegistries(ctx2).map((registry) => {
6354
6737
  switch (registry) {
6355
6738
  case "npm":
6356
6739
  return npmPublishTasks;
@@ -6403,7 +6786,7 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
6403
6786
  );
6404
6787
  const linkUrl = link2("Link", releaseDraftUrl.toString());
6405
6788
  task.title += ` ${linkUrl}`;
6406
- await open(releaseDraftUrl.toString());
6789
+ await open3(releaseDraftUrl.toString());
6407
6790
  }
6408
6791
  }
6409
6792
  ]
@@ -6465,11 +6848,11 @@ function generateChangelog(version2, entries, depUpdates) {
6465
6848
 
6466
6849
  // src/changeset/migrate.ts
6467
6850
  var import_node_fs2 = require("fs");
6468
- var import_node_path4 = __toESM(require("path"), 1);
6851
+ var import_node_path5 = __toESM(require("path"), 1);
6469
6852
  var import_node_process9 = __toESM(require("process"), 1);
6470
6853
  var SKIPPED_FILES = /* @__PURE__ */ new Set(["config.json", "README.md"]);
6471
6854
  function migrateFromChangesets(cwd = import_node_process9.default.cwd()) {
6472
- const changesetDir = import_node_path4.default.join(cwd, ".changeset");
6855
+ const changesetDir = import_node_path5.default.join(cwd, ".changeset");
6473
6856
  if (!(0, import_node_fs2.existsSync)(changesetDir)) {
6474
6857
  return {
6475
6858
  success: false,
@@ -6478,7 +6861,7 @@ function migrateFromChangesets(cwd = import_node_process9.default.cwd()) {
6478
6861
  configMigrated: false
6479
6862
  };
6480
6863
  }
6481
- const pubmDir = import_node_path4.default.join(cwd, ".pubm", "changesets");
6864
+ const pubmDir = import_node_path5.default.join(cwd, ".pubm", "changesets");
6482
6865
  (0, import_node_fs2.mkdirSync)(pubmDir, { recursive: true });
6483
6866
  const files = (0, import_node_fs2.readdirSync)(changesetDir);
6484
6867
  const migratedFiles = [];
@@ -6493,15 +6876,15 @@ function migrateFromChangesets(cwd = import_node_process9.default.cwd()) {
6493
6876
  }
6494
6877
  if (file === "pre.json") {
6495
6878
  (0, import_node_fs2.copyFileSync)(
6496
- import_node_path4.default.join(changesetDir, file),
6497
- import_node_path4.default.resolve(cwd, ".pubm", "pre.json")
6879
+ import_node_path5.default.join(changesetDir, file),
6880
+ import_node_path5.default.resolve(cwd, ".pubm", "pre.json")
6498
6881
  );
6499
6882
  migratedFiles.push(file);
6500
6883
  continue;
6501
6884
  }
6502
6885
  if (file.endsWith(".md")) {
6503
- const src = import_node_path4.default.join(changesetDir, file);
6504
- const dest = import_node_path4.default.join(pubmDir, file);
6886
+ const src = import_node_path5.default.join(changesetDir, file);
6887
+ const dest = import_node_path5.default.join(pubmDir, file);
6505
6888
  (0, import_node_fs2.copyFileSync)(src, dest);
6506
6889
  migratedFiles.push(file);
6507
6890
  }
@@ -6548,10 +6931,10 @@ function parseChangeset(content, fileName) {
6548
6931
 
6549
6932
  // src/changeset/reader.ts
6550
6933
  var import_node_fs3 = require("fs");
6551
- var import_node_path5 = __toESM(require("path"), 1);
6934
+ var import_node_path6 = __toESM(require("path"), 1);
6552
6935
  var import_node_process10 = __toESM(require("process"), 1);
6553
6936
  function readChangesets(cwd = import_node_process10.default.cwd()) {
6554
- const changesetsDir = import_node_path5.default.join(cwd, ".pubm", "changesets");
6937
+ const changesetsDir = import_node_path6.default.join(cwd, ".pubm", "changesets");
6555
6938
  if (!(0, import_node_fs3.existsSync)(changesetsDir)) {
6556
6939
  return [];
6557
6940
  }
@@ -6561,7 +6944,7 @@ function readChangesets(cwd = import_node_process10.default.cwd()) {
6561
6944
  if (!file.endsWith(".md") || file === "README.md") {
6562
6945
  continue;
6563
6946
  }
6564
- const filePath = import_node_path5.default.join(changesetsDir, file);
6947
+ const filePath = import_node_path6.default.join(changesetsDir, file);
6565
6948
  const content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
6566
6949
  changesets.push(parseChangeset(content, file));
6567
6950
  }
@@ -6631,7 +7014,7 @@ function calculateVersionBumps(currentVersions, cwd = import_node_process12.defa
6631
7014
 
6632
7015
  // src/changeset/writer.ts
6633
7016
  var import_node_fs4 = require("fs");
6634
- var import_node_path6 = __toESM(require("path"), 1);
7017
+ var import_node_path7 = __toESM(require("path"), 1);
6635
7018
  var import_node_process13 = __toESM(require("process"), 1);
6636
7019
  var import_yaml2 = require("yaml");
6637
7020
  var adjectives = [
@@ -6726,90 +7109,16 @@ ${summary}
6726
7109
  return content;
6727
7110
  }
6728
7111
  function writeChangeset(releases, summary, cwd = import_node_process13.default.cwd()) {
6729
- const changesetsDir = import_node_path6.default.join(cwd, ".pubm", "changesets");
7112
+ const changesetsDir = import_node_path7.default.join(cwd, ".pubm", "changesets");
6730
7113
  (0, import_node_fs4.mkdirSync)(changesetsDir, { recursive: true });
6731
7114
  const id = generateChangesetId();
6732
7115
  const fileName = `${id}.md`;
6733
- const filePath = import_node_path6.default.join(changesetsDir, fileName);
7116
+ const filePath = import_node_path7.default.join(changesetsDir, fileName);
6734
7117
  const content = generateChangesetContent(releases, summary);
6735
7118
  (0, import_node_fs4.writeFileSync)(filePath, content, "utf-8");
6736
7119
  return filePath;
6737
7120
  }
6738
7121
 
6739
- // src/config/defaults.ts
6740
- var defaultValidate = {
6741
- cleanInstall: true,
6742
- entryPoints: true,
6743
- extraneousFiles: true
6744
- };
6745
- var defaultSnapshot = {
6746
- useCalculatedVersion: false,
6747
- prereleaseTemplate: "{tag}-{timestamp}"
6748
- };
6749
- var defaultConfig = {
6750
- versioning: "independent",
6751
- branch: "main",
6752
- changelog: true,
6753
- changelogFormat: "default",
6754
- commit: false,
6755
- access: "public",
6756
- fixed: [],
6757
- linked: [],
6758
- updateInternalDependencies: "patch",
6759
- ignore: [],
6760
- tag: "latest",
6761
- contents: ".",
6762
- saveToken: true,
6763
- releaseDraft: true,
6764
- releaseNotes: true,
6765
- registries: ["npm", "jsr"],
6766
- rollbackStrategy: "individual"
6767
- };
6768
- function resolveConfig(config) {
6769
- const packages = config.packages ?? [
6770
- { path: ".", registries: ["npm", "jsr"] }
6771
- ];
6772
- return {
6773
- ...defaultConfig,
6774
- ...config,
6775
- packages,
6776
- validate: { ...defaultValidate, ...config.validate },
6777
- snapshot: { ...defaultSnapshot, ...config.snapshot }
6778
- };
6779
- }
6780
-
6781
- // src/config/loader.ts
6782
- var import_promises3 = require("fs/promises");
6783
- var import_node_path7 = __toESM(require("path"), 1);
6784
- var CONFIG_FILES = [
6785
- "pubm.config.ts",
6786
- "pubm.config.mts",
6787
- "pubm.config.cts",
6788
- "pubm.config.js",
6789
- "pubm.config.mjs",
6790
- "pubm.config.cjs"
6791
- ];
6792
- async function findConfigFile(cwd) {
6793
- for (const file of CONFIG_FILES) {
6794
- const filePath = import_node_path7.default.join(cwd, file);
6795
- try {
6796
- if ((await (0, import_promises3.stat)(filePath)).isFile()) {
6797
- return filePath;
6798
- }
6799
- } catch {
6800
- }
6801
- }
6802
- return null;
6803
- }
6804
- async function loadConfig(cwd = process.cwd()) {
6805
- const configPath = await findConfigFile(cwd);
6806
- if (!configPath) return null;
6807
- const { createJiti } = await import("jiti");
6808
- const jiti = createJiti(cwd, { interopDefault: true });
6809
- const mod = await jiti.import(configPath);
6810
- return mod.default ?? mod;
6811
- }
6812
-
6813
7122
  // src/config/types.ts
6814
7123
  function defineConfig(config) {
6815
7124
  return config;
@@ -6861,13 +7170,47 @@ function topologicalSort(graph) {
6861
7170
  return sorted.reverse();
6862
7171
  }
6863
7172
 
6864
- // src/monorepo/groups.ts
7173
+ // src/monorepo/discover.ts
7174
+ var import_node_fs6 = require("fs");
7175
+ var import_node_path9 = __toESM(require("path"), 1);
6865
7176
  var import_micromatch = __toESM(require("micromatch"), 1);
7177
+
7178
+ // src/monorepo/workspace.ts
7179
+ var import_node_fs5 = require("fs");
7180
+ var import_node_path8 = require("path");
7181
+ var import_yaml3 = require("yaml");
7182
+ function detectWorkspace(cwd) {
7183
+ const root = cwd ?? process.cwd();
7184
+ const pnpmWorkspacePath = (0, import_node_path8.join)(root, "pnpm-workspace.yaml");
7185
+ if ((0, import_node_fs5.existsSync)(pnpmWorkspacePath)) {
7186
+ const content = (0, import_node_fs5.readFileSync)(pnpmWorkspacePath, "utf-8");
7187
+ const parsed = (0, import_yaml3.parse)(content);
7188
+ const packages = parsed?.packages ?? [];
7189
+ return { type: "pnpm", patterns: packages };
7190
+ }
7191
+ const packageJsonPath = (0, import_node_path8.join)(root, "package.json");
7192
+ if ((0, import_node_fs5.existsSync)(packageJsonPath)) {
7193
+ const content = (0, import_node_fs5.readFileSync)(packageJsonPath, "utf-8");
7194
+ const pkg = JSON.parse(content);
7195
+ if (pkg.workspaces) {
7196
+ if (Array.isArray(pkg.workspaces)) {
7197
+ return { type: "npm", patterns: pkg.workspaces };
7198
+ }
7199
+ if (typeof pkg.workspaces === "object" && Array.isArray(pkg.workspaces.packages)) {
7200
+ return { type: "yarn", patterns: pkg.workspaces.packages };
7201
+ }
7202
+ }
7203
+ }
7204
+ return null;
7205
+ }
7206
+
7207
+ // src/monorepo/groups.ts
7208
+ var import_micromatch2 = __toESM(require("micromatch"), 1);
6866
7209
  function resolveGroups(groups, allPackages) {
6867
7210
  return groups.map((group) => {
6868
7211
  const resolved = /* @__PURE__ */ new Set();
6869
7212
  for (const pattern of group) {
6870
- const matches = (0, import_micromatch.default)(allPackages, pattern);
7213
+ const matches = (0, import_micromatch2.default)(allPackages, pattern);
6871
7214
  for (const match of matches) {
6872
7215
  resolved.add(match);
6873
7216
  }
@@ -6904,73 +7247,44 @@ function applyLinkedGroup(bumps, group) {
6904
7247
  }
6905
7248
  }
6906
7249
 
6907
- // src/monorepo/workspace.ts
6908
- var import_node_fs5 = require("fs");
6909
- var import_node_path8 = require("path");
6910
- var import_yaml3 = require("yaml");
6911
- function detectWorkspace(cwd) {
6912
- const root = cwd ?? process.cwd();
6913
- const pnpmWorkspacePath = (0, import_node_path8.join)(root, "pnpm-workspace.yaml");
6914
- if ((0, import_node_fs5.existsSync)(pnpmWorkspacePath)) {
6915
- const content = (0, import_node_fs5.readFileSync)(pnpmWorkspacePath, "utf-8");
6916
- const parsed = (0, import_yaml3.parse)(content);
6917
- const packages = parsed?.packages ?? [];
6918
- return { type: "pnpm", patterns: packages };
6919
- }
6920
- const packageJsonPath = (0, import_node_path8.join)(root, "package.json");
6921
- if ((0, import_node_fs5.existsSync)(packageJsonPath)) {
6922
- const content = (0, import_node_fs5.readFileSync)(packageJsonPath, "utf-8");
6923
- const pkg = JSON.parse(content);
6924
- if (pkg.workspaces) {
6925
- if (Array.isArray(pkg.workspaces)) {
6926
- return { type: "npm", patterns: pkg.workspaces };
6927
- }
6928
- if (typeof pkg.workspaces === "object" && Array.isArray(pkg.workspaces.packages)) {
6929
- return { type: "yarn", patterns: pkg.workspaces.packages };
6930
- }
6931
- }
6932
- }
6933
- return null;
6934
- }
6935
-
6936
7250
  // src/prerelease/pre.ts
6937
- var import_node_fs6 = require("fs");
6938
- var import_node_path9 = __toESM(require("path"), 1);
7251
+ var import_node_fs7 = require("fs");
7252
+ var import_node_path10 = __toESM(require("path"), 1);
6939
7253
  function getPreStatePath(cwd) {
6940
- return import_node_path9.default.resolve(cwd ?? process.cwd(), ".pubm", "pre.json");
7254
+ return import_node_path10.default.resolve(cwd ?? process.cwd(), ".pubm", "pre.json");
6941
7255
  }
6942
7256
  function readPreState(cwd) {
6943
7257
  const filePath = getPreStatePath(cwd);
6944
- if (!(0, import_node_fs6.existsSync)(filePath)) {
7258
+ if (!(0, import_node_fs7.existsSync)(filePath)) {
6945
7259
  return null;
6946
7260
  }
6947
- const content = (0, import_node_fs6.readFileSync)(filePath, "utf-8");
7261
+ const content = (0, import_node_fs7.readFileSync)(filePath, "utf-8");
6948
7262
  return JSON.parse(content);
6949
7263
  }
6950
7264
  function enterPreMode(tag, cwd) {
6951
7265
  const filePath = getPreStatePath(cwd);
6952
- if ((0, import_node_fs6.existsSync)(filePath)) {
7266
+ if ((0, import_node_fs7.existsSync)(filePath)) {
6953
7267
  throw new Error(
6954
7268
  "Already in pre mode. Exit pre mode first before entering a new one."
6955
7269
  );
6956
7270
  }
6957
- const dir = import_node_path9.default.dirname(filePath);
6958
- if (!(0, import_node_fs6.existsSync)(dir)) {
6959
- (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
7271
+ const dir = import_node_path10.default.dirname(filePath);
7272
+ if (!(0, import_node_fs7.existsSync)(dir)) {
7273
+ (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
6960
7274
  }
6961
7275
  const state = {
6962
7276
  mode: "pre",
6963
7277
  tag,
6964
7278
  packages: {}
6965
7279
  };
6966
- (0, import_node_fs6.writeFileSync)(filePath, JSON.stringify(state, null, 2), "utf-8");
7280
+ (0, import_node_fs7.writeFileSync)(filePath, JSON.stringify(state, null, 2), "utf-8");
6967
7281
  }
6968
7282
  function exitPreMode(cwd) {
6969
7283
  const filePath = getPreStatePath(cwd);
6970
- if (!(0, import_node_fs6.existsSync)(filePath)) {
7284
+ if (!(0, import_node_fs7.existsSync)(filePath)) {
6971
7285
  throw new Error("Not in pre mode. Enter pre mode first before exiting.");
6972
7286
  }
6973
- (0, import_node_fs6.rmSync)(filePath);
7287
+ (0, import_node_fs7.rmSync)(filePath);
6974
7288
  }
6975
7289
 
6976
7290
  // src/prerelease/snapshot.ts
@@ -6995,11 +7309,11 @@ function generateSnapshotVersion(options) {
6995
7309
  }
6996
7310
 
6997
7311
  // src/validate/entry-points.ts
6998
- var import_node_fs7 = require("fs");
6999
- var import_node_path10 = __toESM(require("path"), 1);
7312
+ var import_node_fs8 = require("fs");
7313
+ var import_node_path11 = __toESM(require("path"), 1);
7000
7314
  var SIMPLE_FIELDS = ["main", "module", "types", "typings"];
7001
7315
  function checkPath(filePath, cwd) {
7002
- return (0, import_node_fs7.existsSync)(import_node_path10.default.resolve(cwd, filePath));
7316
+ return (0, import_node_fs8.existsSync)(import_node_path11.default.resolve(cwd, filePath));
7003
7317
  }
7004
7318
  function validateExports(exports2, cwd, prefix = "exports") {
7005
7319
  const errors = [];
@@ -7052,7 +7366,7 @@ function validateEntryPoints(pkg, cwd) {
7052
7366
  }
7053
7367
 
7054
7368
  // src/validate/extraneous-files.ts
7055
- var import_micromatch2 = __toESM(require("micromatch"), 1);
7369
+ var import_micromatch3 = __toESM(require("micromatch"), 1);
7056
7370
  var PATTERNS = [
7057
7371
  {
7058
7372
  pattern: [".env", ".env.*"],
@@ -7090,7 +7404,7 @@ function detectExtraneousFiles(files) {
7090
7404
  const seen = /* @__PURE__ */ new Set();
7091
7405
  for (const { pattern, reason, basename } of PATTERNS) {
7092
7406
  const options = basename ? { basename: true } : {};
7093
- const matched = (0, import_micromatch2.default)(files, pattern, options);
7407
+ const matched = (0, import_micromatch3.default)(files, pattern, options);
7094
7408
  for (const file of matched) {
7095
7409
  if (!seen.has(file)) {
7096
7410
  seen.add(file);
@@ -7103,7 +7417,18 @@ function detectExtraneousFiles(files) {
7103
7417
 
7104
7418
  // src/index.ts
7105
7419
  async function pubm(options) {
7106
- const resolvedOptions = resolveOptions({ ...options });
7420
+ const config = await loadConfig();
7421
+ const configOptions = {};
7422
+ if (config) {
7423
+ const resolved = resolveConfig(config);
7424
+ if (resolved.packages) {
7425
+ configOptions.packages = resolved.packages;
7426
+ }
7427
+ if (!options.registries && resolved.registries) {
7428
+ configOptions.registries = resolved.registries;
7429
+ }
7430
+ }
7431
+ const resolvedOptions = resolveOptions({ ...configOptions, ...options });
7107
7432
  await run(resolvedOptions);
7108
7433
  }
7109
7434
  // Annotate the CommonJS export names for ESM import in node: