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.js CHANGED
@@ -1927,6 +1927,80 @@ var init_cli_truncate = __esm({
1927
1927
  }
1928
1928
  });
1929
1929
 
1930
+ // src/config/defaults.ts
1931
+ var defaultValidate = {
1932
+ cleanInstall: true,
1933
+ entryPoints: true,
1934
+ extraneousFiles: true
1935
+ };
1936
+ var defaultSnapshot = {
1937
+ useCalculatedVersion: false,
1938
+ prereleaseTemplate: "{tag}-{timestamp}"
1939
+ };
1940
+ var defaultConfig = {
1941
+ versioning: "independent",
1942
+ branch: "main",
1943
+ changelog: true,
1944
+ changelogFormat: "default",
1945
+ commit: false,
1946
+ access: "public",
1947
+ fixed: [],
1948
+ linked: [],
1949
+ updateInternalDependencies: "patch",
1950
+ ignore: [],
1951
+ tag: "latest",
1952
+ contents: ".",
1953
+ saveToken: true,
1954
+ releaseDraft: true,
1955
+ releaseNotes: true,
1956
+ registries: ["npm", "jsr"],
1957
+ rollbackStrategy: "individual"
1958
+ };
1959
+ function resolveConfig(config) {
1960
+ const packages = config.packages ?? [
1961
+ { path: ".", registries: ["npm", "jsr"] }
1962
+ ];
1963
+ return {
1964
+ ...defaultConfig,
1965
+ ...config,
1966
+ packages,
1967
+ validate: { ...defaultValidate, ...config.validate },
1968
+ snapshot: { ...defaultSnapshot, ...config.snapshot }
1969
+ };
1970
+ }
1971
+
1972
+ // src/config/loader.ts
1973
+ import { stat } from "node:fs/promises";
1974
+ import path from "node:path";
1975
+ var CONFIG_FILES = [
1976
+ "pubm.config.ts",
1977
+ "pubm.config.mts",
1978
+ "pubm.config.cts",
1979
+ "pubm.config.js",
1980
+ "pubm.config.mjs",
1981
+ "pubm.config.cjs"
1982
+ ];
1983
+ async function findConfigFile(cwd) {
1984
+ for (const file of CONFIG_FILES) {
1985
+ const filePath = path.join(cwd, file);
1986
+ try {
1987
+ if ((await stat(filePath)).isFile()) {
1988
+ return filePath;
1989
+ }
1990
+ } catch {
1991
+ }
1992
+ }
1993
+ return null;
1994
+ }
1995
+ async function loadConfig(cwd = process.cwd()) {
1996
+ const configPath = await findConfigFile(cwd);
1997
+ if (!configPath) return null;
1998
+ const { createJiti } = await import("jiti");
1999
+ const jiti = createJiti(cwd, { interopDefault: true });
2000
+ const mod = await jiti.import(configPath);
2001
+ return mod.default ?? mod;
2002
+ }
2003
+
1930
2004
  // src/options.ts
1931
2005
  var defaultOptions = {
1932
2006
  testScript: "test",
@@ -1936,13 +2010,16 @@ var defaultOptions = {
1936
2010
  registries: ["npm", "jsr"]
1937
2011
  };
1938
2012
  function resolveOptions(options) {
1939
- const nextOptions = { ...options, ...defaultOptions };
2013
+ const defined = Object.fromEntries(
2014
+ Object.entries(options).filter(([, v]) => v !== void 0)
2015
+ );
2016
+ const nextOptions = { ...defaultOptions, ...defined };
1940
2017
  return nextOptions;
1941
2018
  }
1942
2019
 
1943
2020
  // src/tasks/runner.ts
1944
2021
  import process10 from "node:process";
1945
- import npmCli from "@npmcli/promise-spawn";
2022
+ import npmCli3 from "@npmcli/promise-spawn";
1946
2023
 
1947
2024
  // node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.mjs
1948
2025
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -1963,15 +2040,15 @@ var isCompatibleTerminal = tty && tty.isatty && tty.isatty(1) && env.TERM && !is
1963
2040
  var isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
1964
2041
  var isColorSupported = !isDisabled && (isForced || isWindows && !isDumbTerminal || isCompatibleTerminal || isCI);
1965
2042
  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));
1966
- var clearBleed = (index, string, open2, close, replace) => index < 0 ? open2 + string + close : open2 + replaceClose(index, string, close, replace) + close;
1967
- var filterEmpty = (open2, close, replace = open2, at = open2.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(
2043
+ var clearBleed = (index, string, open4, close, replace) => index < 0 ? open4 + string + close : open4 + replaceClose(index, string, close, replace) + close;
2044
+ var filterEmpty = (open4, close, replace = open4, at = open4.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(
1968
2045
  ("" + string).indexOf(close, at),
1969
2046
  string,
1970
- open2,
2047
+ open4,
1971
2048
  close,
1972
2049
  replace
1973
2050
  ) : "";
1974
- var init = (open2, close, replace) => filterEmpty(`\x1B[${open2}m`, `\x1B[${close}m`, replace);
2051
+ var init = (open4, close, replace) => filterEmpty(`\x1B[${open4}m`, `\x1B[${close}m`, replace);
1975
2052
  var colors = {
1976
2053
  reset: init(0, 0),
1977
2054
  bold: init(1, 22, "\x1B[22m\x1B[1m"),
@@ -4451,7 +4528,7 @@ var Git = class {
4451
4528
  try {
4452
4529
  return (await this.git(["tag", "-l"])).trim().split("\n").sort(semver.compareIdentifiers);
4453
4530
  } catch (error) {
4454
- throw new GitError("Failed to run `git config --get user.name`", {
4531
+ throw new GitError("Failed to run `git tag -l`", {
4455
4532
  cause: error
4456
4533
  });
4457
4534
  }
@@ -4497,7 +4574,8 @@ var Git = class {
4497
4574
  async revisionDiffsCount() {
4498
4575
  try {
4499
4576
  return Number.parseInt(
4500
- await this.git(["rev-list", "@{u}...HEAD", "--count", "--left-only"])
4577
+ await this.git(["rev-list", "@{u}...HEAD", "--count", "--left-only"]),
4578
+ 10
4501
4579
  );
4502
4580
  } catch (error) {
4503
4581
  throw new GitError(
@@ -4714,8 +4792,24 @@ async function rollback() {
4714
4792
  called = true;
4715
4793
  if (rollbacks.length <= 0) return void 0;
4716
4794
  console.log("Rollback...");
4717
- await Promise.all(rollbacks.map(({ fn, ctx }) => fn(ctx)));
4718
- console.log("Rollback completed");
4795
+ const results = await Promise.allSettled(
4796
+ rollbacks.map(({ fn, ctx }) => fn(ctx))
4797
+ );
4798
+ const failures = results.filter(
4799
+ (r) => r.status === "rejected"
4800
+ );
4801
+ if (failures.length > 0) {
4802
+ for (const failure of failures) {
4803
+ console.error(
4804
+ `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
4805
+ );
4806
+ }
4807
+ console.log(
4808
+ "Rollback completed with errors. Some operations may require manual recovery."
4809
+ );
4810
+ } else {
4811
+ console.log("Rollback completed");
4812
+ }
4719
4813
  }
4720
4814
 
4721
4815
  // src/utils/listr.ts
@@ -4727,8 +4821,8 @@ function createListr(...args) {
4727
4821
  }
4728
4822
 
4729
4823
  // src/utils/package.ts
4730
- import { readFile, stat, writeFile } from "node:fs/promises";
4731
- import path from "node:path";
4824
+ import { readFile, stat as stat2, writeFile } from "node:fs/promises";
4825
+ import path2 from "node:path";
4732
4826
  import process7 from "node:process";
4733
4827
  var cachedPackageJson = {};
4734
4828
  var cachedJsrJson = {};
@@ -4738,16 +4832,16 @@ function patchCachedJsrJson(contents, { cwd = process7.cwd() } = {}) {
4738
4832
  async function findOutFile(file, { cwd = process7.cwd() } = {}) {
4739
4833
  let directory = cwd;
4740
4834
  let filePath = "";
4741
- const { root } = path.parse(cwd);
4835
+ const { root } = path2.parse(cwd);
4742
4836
  while (directory) {
4743
- filePath = path.join(directory, file);
4837
+ filePath = path2.join(directory, file);
4744
4838
  try {
4745
- if ((await stat(filePath)).isFile()) {
4839
+ if ((await stat2(filePath)).isFile()) {
4746
4840
  break;
4747
4841
  }
4748
4842
  } catch {
4749
4843
  }
4750
- directory = path.dirname(directory);
4844
+ directory = path2.dirname(directory);
4751
4845
  if (directory === root) return null;
4752
4846
  }
4753
4847
  return filePath;
@@ -4882,20 +4976,34 @@ async function replaceVersion(version2) {
4882
4976
  const packageJsonPath = await findOutFile("package.json");
4883
4977
  if (!packageJsonPath) return void 0;
4884
4978
  const packageJson = (await readFile(packageJsonPath)).toString();
4885
- await writeFile(
4886
- packageJsonPath,
4887
- packageJson.replace(versionRegex, `$1${version2}$2`)
4888
- );
4979
+ try {
4980
+ await writeFile(
4981
+ packageJsonPath,
4982
+ packageJson.replace(versionRegex, `$1${version2}$2`)
4983
+ );
4984
+ } catch (error) {
4985
+ throw new AbstractError(
4986
+ `Failed to write version to package.json: ${error instanceof Error ? error.message : error}`,
4987
+ { cause: error }
4988
+ );
4989
+ }
4889
4990
  return "package.json";
4890
4991
  })(),
4891
4992
  (async () => {
4892
4993
  const jsrJsonPath = await findOutFile("jsr.json");
4893
4994
  if (!jsrJsonPath) return void 0;
4894
4995
  const jsrJson = (await readFile(jsrJsonPath)).toString();
4895
- await writeFile(
4896
- jsrJsonPath,
4897
- jsrJson.replace(versionRegex, `$1${version2}$2`)
4898
- );
4996
+ try {
4997
+ await writeFile(
4998
+ jsrJsonPath,
4999
+ jsrJson.replace(versionRegex, `$1${version2}$2`)
5000
+ );
5001
+ } catch (error) {
5002
+ throw new AbstractError(
5003
+ `Failed to write version to jsr.json: ${error instanceof Error ? error.message : error}`,
5004
+ { cause: error }
5005
+ );
5006
+ }
4899
5007
  return "jsr.json";
4900
5008
  })()
4901
5009
  ]);
@@ -4914,12 +5022,13 @@ async function getPackageManager() {
4914
5022
  if (await findOutFile(lockFile2)) return packageManager;
4915
5023
  }
4916
5024
  }
5025
+ console.warn("No lock file found, defaulting to npm.");
4917
5026
  return "npm";
4918
5027
  }
4919
5028
 
4920
5029
  // src/ecosystem/rust.ts
4921
- import { readFile as readFile2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
4922
- import path2 from "node:path";
5030
+ import { readFile as readFile2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
5031
+ import path3 from "node:path";
4923
5032
  import { parse, stringify } from "smol-toml";
4924
5033
 
4925
5034
  // src/ecosystem/ecosystem.ts
@@ -4933,14 +5042,14 @@ var Ecosystem = class {
4933
5042
  var RustEcosystem = class extends Ecosystem {
4934
5043
  static async detect(packagePath) {
4935
5044
  try {
4936
- return (await stat2(path2.join(packagePath, "Cargo.toml"))).isFile();
5045
+ return (await stat3(path3.join(packagePath, "Cargo.toml"))).isFile();
4937
5046
  } catch {
4938
5047
  return false;
4939
5048
  }
4940
5049
  }
4941
5050
  async readCargoToml() {
4942
5051
  const raw = await readFile2(
4943
- path2.join(this.packagePath, "Cargo.toml"),
5052
+ path3.join(this.packagePath, "Cargo.toml"),
4944
5053
  "utf-8"
4945
5054
  );
4946
5055
  return parse(raw);
@@ -4956,7 +5065,7 @@ var RustEcosystem = class extends Ecosystem {
4956
5065
  return pkg.version;
4957
5066
  }
4958
5067
  async writeVersion(newVersion) {
4959
- const filePath = path2.join(this.packagePath, "Cargo.toml");
5068
+ const filePath = path3.join(this.packagePath, "Cargo.toml");
4960
5069
  const raw = await readFile2(filePath, "utf-8");
4961
5070
  const cargo = parse(raw);
4962
5071
  const pkg = cargo.package;
@@ -5032,13 +5141,21 @@ var CratesRegistry = class extends Registry {
5032
5141
  { headers: this.headers }
5033
5142
  );
5034
5143
  if (!response.ok) {
5035
- throw new Error(`Crate '${this.packageName}' not found`);
5144
+ if (response.status === 404) {
5145
+ throw new CratesError(
5146
+ `Crate '${this.packageName}' not found on crates.io`
5147
+ );
5148
+ }
5149
+ throw new CratesError(
5150
+ `crates.io API error (HTTP ${response.status}) for crate '${this.packageName}'`
5151
+ );
5036
5152
  }
5037
5153
  const data = await response.json();
5038
5154
  return data.crate.max_version;
5039
5155
  } catch (error) {
5156
+ if (error instanceof CratesError) throw error;
5040
5157
  throw new CratesError(
5041
- `Failed to fetch version for crate '${this.packageName}'`,
5158
+ `Cannot reach crates.io to fetch version for '${this.packageName}'`,
5042
5159
  { cause: error }
5043
5160
  );
5044
5161
  }
@@ -5068,6 +5185,12 @@ var CratesRegistry = class extends Registry {
5068
5185
  if (process.env.CARGO_REGISTRY_TOKEN) return true;
5069
5186
  return this.isInstalled();
5070
5187
  }
5188
+ getRequirements() {
5189
+ return {
5190
+ needsPackageScripts: false,
5191
+ requiredManifest: "Cargo.toml"
5192
+ };
5193
+ }
5071
5194
  async isPackageNameAvaliable() {
5072
5195
  try {
5073
5196
  const response = await fetch(
@@ -5075,8 +5198,11 @@ var CratesRegistry = class extends Registry {
5075
5198
  { headers: this.headers }
5076
5199
  );
5077
5200
  return !response.ok;
5078
- } catch {
5079
- return true;
5201
+ } catch (error) {
5202
+ throw new CratesError(
5203
+ `Failed to check package name availability on crates.io`,
5204
+ { cause: error }
5205
+ );
5080
5206
  }
5081
5207
  }
5082
5208
  };
@@ -5125,6 +5251,7 @@ var cratesPublishTasks = {
5125
5251
  // src/tasks/jsr.ts
5126
5252
  import process8 from "node:process";
5127
5253
  import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
5254
+ import npmCli from "@npmcli/promise-spawn";
5128
5255
 
5129
5256
  // src/registry/jsr.ts
5130
5257
  import { exec as exec4, NonZeroExitError } from "tinyexec";
@@ -5132,7 +5259,7 @@ import { exec as exec4, NonZeroExitError } from "tinyexec";
5132
5259
  // src/utils/db.ts
5133
5260
  import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
5134
5261
  import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
5135
- import path3 from "node:path";
5262
+ import path4 from "node:path";
5136
5263
  var a = "aes-256-cbc";
5137
5264
  var n = statSync(import.meta.dirname);
5138
5265
  var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
@@ -5147,38 +5274,56 @@ function d(g, h) {
5147
5274
  }
5148
5275
  var Db = class {
5149
5276
  constructor() {
5150
- __publicField(this, "path", path3.resolve(import.meta.dirname, ".pubm"));
5277
+ __publicField(this, "path", path4.resolve(import.meta.dirname, ".pubm"));
5151
5278
  try {
5152
5279
  if (!statSync(this.path).isDirectory()) {
5153
5280
  mkdirSync(this.path);
5154
5281
  }
5155
5282
  } catch {
5156
- mkdirSync(this.path);
5283
+ try {
5284
+ mkdirSync(this.path);
5285
+ } catch (error) {
5286
+ throw new Error(
5287
+ `Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
5288
+ );
5289
+ }
5157
5290
  }
5158
5291
  }
5159
5292
  set(field, value) {
5160
- writeFileSync(
5161
- path3.resolve(this.path, Buffer.from(e(field, field)).toString("base64")),
5162
- Buffer.from(e(`${value}`, field)),
5163
- { encoding: "binary" }
5164
- );
5293
+ try {
5294
+ writeFileSync(
5295
+ path4.resolve(
5296
+ this.path,
5297
+ Buffer.from(e(field, field)).toString("base64")
5298
+ ),
5299
+ Buffer.from(e(`${value}`, field)),
5300
+ { encoding: "binary" }
5301
+ );
5302
+ } catch (error) {
5303
+ throw new Error(
5304
+ `Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
5305
+ );
5306
+ }
5165
5307
  }
5166
5308
  get(field) {
5309
+ const filePath = path4.resolve(
5310
+ this.path,
5311
+ Buffer.from(e(field, field)).toString("base64")
5312
+ );
5313
+ let raw;
5167
5314
  try {
5168
- return d(
5169
- Buffer.from(
5170
- readFileSync(
5171
- path3.resolve(
5172
- this.path,
5173
- Buffer.from(e(field, field)).toString("base64")
5174
- )
5175
- )
5176
- ).toString(),
5177
- field
5178
- );
5315
+ raw = readFileSync(filePath);
5179
5316
  } catch {
5180
5317
  return null;
5181
5318
  }
5319
+ try {
5320
+ return d(Buffer.from(raw).toString(), field);
5321
+ } catch {
5322
+ console.warn(
5323
+ `Stored token for '${field}' appears corrupted. It will be re-requested.`
5324
+ );
5325
+ return null;
5326
+ }
5182
5327
  }
5183
5328
  };
5184
5329
 
@@ -5192,9 +5337,12 @@ function getScope(packageName) {
5192
5337
  }
5193
5338
  function getScopeAndName(packageName) {
5194
5339
  const matches = packageName.match(/^@([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)$/);
5195
- const scope = matches?.[1];
5196
- const name = matches?.[2];
5197
- return [`${scope}`, `${name}`];
5340
+ if (!matches) {
5341
+ throw new Error(
5342
+ `Invalid scoped package name: '${packageName}'. Expected format: @scope/name`
5343
+ );
5344
+ }
5345
+ return [matches[1], matches[2]];
5198
5346
  }
5199
5347
  var scopedPackagePattern = /^(?:@([^/]+?)[/])?([^/]+?)$/;
5200
5348
  var blacklist = ["node_modules", "favicon.ico"];
@@ -5240,6 +5388,7 @@ var JsrRegisry = class extends Registry {
5240
5388
  super(packageName, registry);
5241
5389
  __publicField(this, "registry", "https://jsr.io");
5242
5390
  __publicField(this, "client");
5391
+ __publicField(this, "packageCreationUrls");
5243
5392
  this.client = new JsrClient(getApiEndpoint(this.registry));
5244
5393
  }
5245
5394
  async jsr(args) {
@@ -5281,9 +5430,19 @@ var JsrRegisry = class extends Registry {
5281
5430
  throwOnError: true
5282
5431
  }
5283
5432
  );
5433
+ this.packageCreationUrls = void 0;
5284
5434
  return true;
5285
5435
  } catch (error) {
5286
5436
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5437
+ if (stderr?.includes("don't exist")) {
5438
+ const urls = [...stderr.matchAll(/https:\/\/jsr\.io\/new\S+/g)].map(
5439
+ (m) => m[0]
5440
+ );
5441
+ if (urls.length > 0) {
5442
+ this.packageCreationUrls = urls;
5443
+ return false;
5444
+ }
5445
+ }
5287
5446
  throw new JsrError(
5288
5447
  `Failed to run \`jsr publish --allow-dirty --token ***\`${stderr ? `
5289
5448
  ${stderr}` : ""}`,
@@ -5313,6 +5472,12 @@ ${stderr}` : ""}`,
5313
5472
  async isPackageNameAvaliable() {
5314
5473
  return isValidPackageName(this.packageName);
5315
5474
  }
5475
+ getRequirements() {
5476
+ return {
5477
+ needsPackageScripts: false,
5478
+ requiredManifest: "jsr.json"
5479
+ };
5480
+ }
5316
5481
  };
5317
5482
  var _JsrClient = class _JsrClient {
5318
5483
  constructor(apiEndpoint) {
@@ -5334,9 +5499,7 @@ var _JsrClient = class _JsrClient {
5334
5499
  const response = await this.fetch("/user");
5335
5500
  if (response.status === 401) return null;
5336
5501
  if (!response.ok) {
5337
- throw new Error(
5338
- `HTTP ${response.status}: ${response.statusText}`
5339
- );
5502
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5340
5503
  }
5341
5504
  return await response.json();
5342
5505
  } catch (error) {
@@ -5350,9 +5513,7 @@ var _JsrClient = class _JsrClient {
5350
5513
  const response = await this.fetch(`/user/member/${scope}`);
5351
5514
  if (response.status === 401) return null;
5352
5515
  if (!response.ok) {
5353
- throw new Error(
5354
- `HTTP ${response.status}: ${response.statusText}`
5355
- );
5516
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5356
5517
  }
5357
5518
  return await response.json();
5358
5519
  } catch (error) {
@@ -5369,15 +5530,11 @@ var _JsrClient = class _JsrClient {
5369
5530
  const response = await this.fetch("/user/scopes");
5370
5531
  if (response.status === 401) return [];
5371
5532
  if (!response.ok) {
5372
- throw new Error(
5373
- `HTTP ${response.status}: ${response.statusText}`
5374
- );
5533
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5375
5534
  }
5376
5535
  const body = await response.json();
5377
5536
  if (!Array.isArray(body)) {
5378
- throw new Error(
5379
- `Expected array response but got ${typeof body}`
5380
- );
5537
+ throw new Error(`Expected array response but got ${typeof body}`);
5381
5538
  }
5382
5539
  return body.map(({ scope }) => scope);
5383
5540
  } catch (error) {
@@ -5393,9 +5550,15 @@ var _JsrClient = class _JsrClient {
5393
5550
  const [scope, name] = getScopeAndName(packageName);
5394
5551
  try {
5395
5552
  const response = await this.fetch(`/scopes/${scope}/packages/${name}`);
5396
- if (!response.ok) return null;
5553
+ if (response.status === 404) return null;
5554
+ if (!response.ok) {
5555
+ throw new JsrError(
5556
+ `JSR API error (HTTP ${response.status}) for package '${packageName}'`
5557
+ );
5558
+ }
5397
5559
  return await response.json();
5398
5560
  } catch (error) {
5561
+ if (error instanceof JsrError) throw error;
5399
5562
  throw new JsrError(
5400
5563
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages/${name}\``,
5401
5564
  {
@@ -5410,8 +5573,18 @@ var _JsrClient = class _JsrClient {
5410
5573
  method: "POST",
5411
5574
  body: JSON.stringify({ scope })
5412
5575
  });
5413
- return response.status === 200 || response.status === 201;
5576
+ if (response.status === 200 || response.status === 201) return true;
5577
+ let detail = "";
5578
+ try {
5579
+ const body = await response.json();
5580
+ detail = body.message || body.error || JSON.stringify(body);
5581
+ } catch {
5582
+ }
5583
+ throw new JsrError(
5584
+ `Failed to create scope '${scope}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5585
+ );
5414
5586
  } catch (error) {
5587
+ if (error instanceof JsrError) throw error;
5415
5588
  throw new JsrError(`Failed to fetch \`${this.apiEndpoint}/scopes\``, {
5416
5589
  cause: error
5417
5590
  });
@@ -5422,8 +5595,18 @@ var _JsrClient = class _JsrClient {
5422
5595
  const response = await this.fetch(`/scopes/${scope}`, {
5423
5596
  method: "DELETE"
5424
5597
  });
5425
- return response.status === 200 || response.status === 204;
5598
+ if (response.status === 200 || response.status === 204) return true;
5599
+ let detail = "";
5600
+ try {
5601
+ const body = await response.json();
5602
+ detail = body.message || body.error || JSON.stringify(body);
5603
+ } catch {
5604
+ }
5605
+ throw new JsrError(
5606
+ `Failed to delete scope '${scope}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5607
+ );
5426
5608
  } catch (error) {
5609
+ if (error instanceof JsrError) throw error;
5427
5610
  throw new JsrError(
5428
5611
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}\``,
5429
5612
  {
@@ -5439,8 +5622,18 @@ var _JsrClient = class _JsrClient {
5439
5622
  method: "POST",
5440
5623
  body: JSON.stringify({ package: name })
5441
5624
  });
5442
- return response.status === 200 || response.status === 201;
5625
+ if (response.status === 200 || response.status === 201) return true;
5626
+ let detail = "";
5627
+ try {
5628
+ const body = await response.json();
5629
+ detail = body.message || body.error || JSON.stringify(body);
5630
+ } catch {
5631
+ }
5632
+ throw new JsrError(
5633
+ `Failed to create package '${packageName}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5634
+ );
5443
5635
  } catch (error) {
5636
+ if (error instanceof JsrError) throw error;
5444
5637
  throw new JsrError(
5445
5638
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages\``,
5446
5639
  {
@@ -5455,8 +5648,18 @@ var _JsrClient = class _JsrClient {
5455
5648
  const response = await this.fetch(`/scopes/${scope}/packages/${name}`, {
5456
5649
  method: "DELETE"
5457
5650
  });
5458
- return response.status === 200 || response.status === 204;
5651
+ if (response.status === 200 || response.status === 204) return true;
5652
+ let detail = "";
5653
+ try {
5654
+ const body = await response.json();
5655
+ detail = body.message || body.error || JSON.stringify(body);
5656
+ } catch {
5657
+ }
5658
+ throw new JsrError(
5659
+ `Failed to delete package '${packageName}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5660
+ );
5459
5661
  } catch (error) {
5662
+ if (error instanceof JsrError) throw error;
5460
5663
  throw new JsrError(
5461
5664
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages/${name}\``,
5462
5665
  {
@@ -5469,9 +5672,7 @@ var _JsrClient = class _JsrClient {
5469
5672
  try {
5470
5673
  const response = await this.fetch(`/packages?query=${query}`);
5471
5674
  if (!response.ok) {
5472
- throw new Error(
5473
- `HTTP ${response.status}: ${response.statusText}`
5474
- );
5675
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5475
5676
  }
5476
5677
  return await response.json();
5477
5678
  } catch (error) {
@@ -5549,7 +5750,7 @@ var NpmRegistry = class extends Registry {
5549
5750
  await this.npm(["whoami"]);
5550
5751
  return true;
5551
5752
  } catch (error) {
5552
- if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("ENEEDAUTH")) {
5753
+ if (error instanceof NonZeroExitError2) {
5553
5754
  return false;
5554
5755
  }
5555
5756
  throw new NpmError("Failed to run `npm whoami`", { cause: error });
@@ -5557,16 +5758,22 @@ var NpmRegistry = class extends Registry {
5557
5758
  }
5558
5759
  async collaborators() {
5559
5760
  try {
5560
- return JSON.parse(
5561
- await this.npm([
5562
- "access",
5563
- "list",
5564
- "collaborators",
5565
- this.packageName,
5566
- "--json"
5567
- ])
5568
- );
5761
+ const output = await this.npm([
5762
+ "access",
5763
+ "list",
5764
+ "collaborators",
5765
+ this.packageName,
5766
+ "--json"
5767
+ ]);
5768
+ try {
5769
+ return JSON.parse(output);
5770
+ } catch {
5771
+ throw new NpmError(
5772
+ `Unexpected response from npm registry for collaborators of '${this.packageName}'`
5773
+ );
5774
+ }
5569
5775
  } catch (error) {
5776
+ if (error instanceof NpmError) throw error;
5570
5777
  throw new NpmError(
5571
5778
  `Failed to run \`npm access list collaborators ${this.packageName} --json\``,
5572
5779
  { cause: error }
@@ -5580,12 +5787,21 @@ var NpmRegistry = class extends Registry {
5580
5787
  }
5581
5788
  async distTags() {
5582
5789
  try {
5583
- return Object.keys(
5584
- JSON.parse(
5585
- await this.npm(["view", this.packageName, "dist-tags", "--json"])
5586
- )
5587
- );
5790
+ const output = await this.npm([
5791
+ "view",
5792
+ this.packageName,
5793
+ "dist-tags",
5794
+ "--json"
5795
+ ]);
5796
+ try {
5797
+ return Object.keys(JSON.parse(output));
5798
+ } catch {
5799
+ throw new NpmError(
5800
+ `Unexpected response from npm registry for dist-tags of '${this.packageName}'`
5801
+ );
5802
+ }
5588
5803
  } catch (error) {
5804
+ if (error instanceof NpmError) throw error;
5589
5805
  throw new NpmError(
5590
5806
  `Failed to run \`npm view ${this.packageName} dist-tags --json\``,
5591
5807
  { cause: error }
@@ -5615,12 +5831,7 @@ var NpmRegistry = class extends Registry {
5615
5831
  if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
5616
5832
  return false;
5617
5833
  }
5618
- throw new NpmError(
5619
- "Failed to run `npm publish --provenance --access public`",
5620
- {
5621
- cause: error
5622
- }
5623
- );
5834
+ throw this.classifyPublishError(error);
5624
5835
  }
5625
5836
  }
5626
5837
  async publish(otp) {
@@ -5632,14 +5843,39 @@ var NpmRegistry = class extends Registry {
5632
5843
  if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
5633
5844
  return false;
5634
5845
  }
5635
- throw new NpmError(`Failed to run \`npm ${args.join(" ")}\``, {
5636
- cause: error
5637
- });
5846
+ throw this.classifyPublishError(error);
5638
5847
  }
5639
5848
  }
5640
5849
  async isPackageNameAvaliable() {
5641
5850
  return isValidPackageName(this.packageName);
5642
5851
  }
5852
+ getRequirements() {
5853
+ return {
5854
+ needsPackageScripts: true,
5855
+ requiredManifest: "package.json"
5856
+ };
5857
+ }
5858
+ classifyPublishError(error) {
5859
+ if (error instanceof NonZeroExitError2) {
5860
+ const stderr = error.output?.stderr ?? "";
5861
+ if (stderr.includes("EOTP")) {
5862
+ return new NpmError("OTP required for publishing", { cause: error });
5863
+ }
5864
+ if (stderr.includes("403") || stderr.includes("Forbidden")) {
5865
+ return new NpmError(
5866
+ "Permission denied (403 Forbidden). Check your npm access token permissions.",
5867
+ { cause: error }
5868
+ );
5869
+ }
5870
+ if (stderr.includes("429") || stderr.includes("Too Many Requests")) {
5871
+ return new NpmError(
5872
+ "Rate limited by npm registry. Please wait and try again.",
5873
+ { cause: error }
5874
+ );
5875
+ }
5876
+ }
5877
+ return new NpmError("Failed to publish to npm", { cause: error });
5878
+ }
5643
5879
  };
5644
5880
  async function npmRegistry() {
5645
5881
  const packageJson = await getPackageJson();
@@ -5647,6 +5883,7 @@ async function npmRegistry() {
5647
5883
  }
5648
5884
 
5649
5885
  // src/tasks/jsr.ts
5886
+ var { open } = npmCli;
5650
5887
  var JsrAvailableError = class extends AbstractError {
5651
5888
  constructor(message, { cause } = {}) {
5652
5889
  super(message, { cause });
@@ -5671,17 +5908,34 @@ var jsrAvailableCheckTasks = {
5671
5908
  if (!JsrClient.token) {
5672
5909
  task.output = "Retrieving jsr API token";
5673
5910
  if (ctx.promptEnabled) {
5674
- while (true) {
5911
+ const maxAttempts = 3;
5912
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
5675
5913
  JsrClient.token = await task.prompt(ListrEnquirerPromptAdapter).run({
5676
5914
  type: "password",
5677
- message: `Please enter the jsr ${color.bold("API token")}`,
5915
+ message: `Please enter the jsr ${color.bold("API token")}${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`,
5678
5916
  footer: `
5679
5917
  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'")}.`
5680
5918
  });
5681
5919
  try {
5682
5920
  if (await jsr.client.user()) break;
5683
- task.output = "The jsr API token is invalid. Please re-enter a valid token.";
5684
- } catch {
5921
+ if (attempt < maxAttempts) {
5922
+ task.output = "The jsr API token is invalid. Please re-enter a valid token.";
5923
+ }
5924
+ } catch (error) {
5925
+ if (error instanceof Error && (error.message.includes("fetch") || error.message.includes("network") || error.message.includes("ENOTFOUND"))) {
5926
+ throw new JsrAvailableError(
5927
+ "JSR API is unreachable. Check your network connection.",
5928
+ { cause: error }
5929
+ );
5930
+ }
5931
+ if (attempt < maxAttempts) {
5932
+ task.output = "The jsr API token is invalid. Please re-enter a valid token.";
5933
+ }
5934
+ }
5935
+ if (attempt === maxAttempts) {
5936
+ throw new JsrAvailableError(
5937
+ "JSR token verification failed after 3 attempts."
5938
+ );
5685
5939
  }
5686
5940
  }
5687
5941
  } else {
@@ -5810,13 +6064,48 @@ var jsrPublishTasks = {
5810
6064
  }
5811
6065
  JsrClient.token = jsrTokenEnv;
5812
6066
  }
5813
- await jsr.publish();
6067
+ let result = await jsr.publish();
6068
+ if (!result && jsr.packageCreationUrls) {
6069
+ if (ctx.promptEnabled) {
6070
+ task.title = "Running jsr publish (package creation needed)";
6071
+ const urls = jsr.packageCreationUrls;
6072
+ const maxAttempts = 3;
6073
+ task.output = `Package doesn't exist on jsr. Create it at:
6074
+ ${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
6075
+ open(urls[0]);
6076
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6077
+ await task.prompt(ListrEnquirerPromptAdapter).run({
6078
+ type: "input",
6079
+ message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6080
+ });
6081
+ result = await jsr.publish();
6082
+ if (result) break;
6083
+ if (attempt < maxAttempts) {
6084
+ task.output = "Package still doesn't exist. Please create it and try again.";
6085
+ }
6086
+ }
6087
+ if (!result) {
6088
+ throw new JsrAvailableError(
6089
+ "Package creation not completed after 3 attempts."
6090
+ );
6091
+ }
6092
+ task.title = "Running jsr publish (package created)";
6093
+ } else {
6094
+ throw new JsrAvailableError(
6095
+ `Package doesn't exist on jsr. Create it at:
6096
+ ${jsr.packageCreationUrls.join("\n")}`
6097
+ );
6098
+ }
6099
+ }
5814
6100
  }
5815
6101
  };
5816
6102
 
5817
6103
  // src/tasks/npm.ts
6104
+ import { spawn } from "node:child_process";
5818
6105
  import process9 from "node:process";
5819
6106
  import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
6107
+ import npmCli2 from "@npmcli/promise-spawn";
6108
+ var { open: open2 } = npmCli2;
5820
6109
  var NpmAvailableError = class extends AbstractError {
5821
6110
  constructor(message, { cause } = {}) {
5822
6111
  super(message, { cause });
@@ -5827,12 +6116,55 @@ var NpmAvailableError = class extends AbstractError {
5827
6116
  var npmAvailableCheckTasks = {
5828
6117
  title: "Checking npm avaliable for publising",
5829
6118
  skip: (ctx) => !!ctx.preview,
5830
- task: async () => {
6119
+ task: async (ctx, task) => {
5831
6120
  const npm = await npmRegistry();
5832
6121
  if (!await npm.isLoggedIn()) {
5833
- throw new NpmAvailableError(
5834
- "You are not logged in. Please log in first using `npm login`."
5835
- );
6122
+ if (ctx.promptEnabled) {
6123
+ try {
6124
+ task.output = "Launching npm login...";
6125
+ await new Promise((resolve, reject) => {
6126
+ const child = spawn("npm", ["login"], {
6127
+ stdio: ["pipe", "pipe", "pipe"]
6128
+ });
6129
+ let opened = false;
6130
+ const onData = (data) => {
6131
+ const text = data.toString();
6132
+ const urlMatch = text.match(
6133
+ /https:\/\/www\.npmjs\.com\/login[^\s]*/
6134
+ );
6135
+ if (urlMatch && !opened) {
6136
+ opened = true;
6137
+ task.output = `Login at: ${color.cyan(urlMatch[0])}`;
6138
+ open2(urlMatch[0]);
6139
+ child.stdin?.write("\n");
6140
+ }
6141
+ };
6142
+ child.stdout?.on("data", onData);
6143
+ child.stderr?.on("data", onData);
6144
+ child.on(
6145
+ "close",
6146
+ (code) => code === 0 ? resolve() : reject(
6147
+ new Error(`npm login exited with code ${code}`)
6148
+ )
6149
+ );
6150
+ child.on("error", reject);
6151
+ });
6152
+ } catch (error) {
6153
+ throw new NpmAvailableError(
6154
+ "npm login failed. Please run `npm login` manually and try again.",
6155
+ { cause: error }
6156
+ );
6157
+ }
6158
+ if (!await npm.isLoggedIn()) {
6159
+ throw new NpmAvailableError(
6160
+ "Still not logged in after npm login. Please verify your credentials."
6161
+ );
6162
+ }
6163
+ } else {
6164
+ throw new NpmAvailableError(
6165
+ "Not logged in to npm. Set NODE_AUTH_TOKEN in your CI environment. For GitHub Actions, add it as a repository secret."
6166
+ );
6167
+ }
5836
6168
  }
5837
6169
  if (await npm.isPublished()) {
5838
6170
  if (!await npm.hasPermission()) {
@@ -5860,24 +6192,31 @@ var npmPublishTasks = {
5860
6192
  let result = await npm.publish();
5861
6193
  if (!result) {
5862
6194
  task.title = "Running npm publish (OTP code needed)";
5863
- while (!result) {
6195
+ const maxAttempts = 3;
6196
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
5864
6197
  result = await npm.publish(
5865
6198
  await task.prompt(ListrEnquirerPromptAdapter2).run({
5866
6199
  type: "password",
5867
- message: "npm OTP code"
6200
+ message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
5868
6201
  })
5869
6202
  );
5870
- if (!result) {
5871
- task.output = "2FA failed";
6203
+ if (result) break;
6204
+ if (attempt < maxAttempts) {
6205
+ task.output = "2FA failed. Please try again.";
5872
6206
  }
5873
6207
  }
6208
+ if (!result) {
6209
+ throw new NpmAvailableError(
6210
+ "OTP verification failed after 3 attempts."
6211
+ );
6212
+ }
5874
6213
  task.title = "Running npm publish (2FA passed)";
5875
6214
  }
5876
6215
  } else {
5877
6216
  const npmTokenEnv = process9.env.NODE_AUTH_TOKEN;
5878
6217
  if (!npmTokenEnv) {
5879
6218
  throw new NpmAvailableError(
5880
- "NODE_AUTH_TOKEN not found in the environment variables. Please set the token and try again."
6219
+ "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"
5881
6220
  );
5882
6221
  }
5883
6222
  const result = await npm.publishProvenance();
@@ -5983,6 +6322,7 @@ var prerequisitesCheckTask = (options) => {
5983
6322
  );
5984
6323
  }
5985
6324
  ctx.cleanWorkingTree = false;
6325
+ return;
5986
6326
  }
5987
6327
  ctx.cleanWorkingTree = true;
5988
6328
  }
@@ -6083,6 +6423,16 @@ async function validateEngineVersion(engine, version2) {
6083
6423
  }
6084
6424
 
6085
6425
  // src/tasks/required-conditions-check.ts
6426
+ var registryRequirementsMap = {
6427
+ npm: { needsPackageScripts: true },
6428
+ jsr: { needsPackageScripts: false },
6429
+ crates: { needsPackageScripts: false }
6430
+ };
6431
+ function needsPackageScripts(registries) {
6432
+ return registries.some(
6433
+ (r) => registryRequirementsMap[r]?.needsPackageScripts ?? true
6434
+ );
6435
+ }
6086
6436
  var RequiredConditionCheckError = class extends AbstractError {
6087
6437
  constructor(message, { cause } = {}) {
6088
6438
  super(message, { cause });
@@ -6158,7 +6508,7 @@ var requiredConditionsCheckTask = (options) => createListr({
6158
6508
  },
6159
6509
  {
6160
6510
  title: "Checking if test and build scripts exist",
6161
- skip: (ctx) => ctx.jsrOnly,
6511
+ skip: (ctx) => !needsPackageScripts(ctx.registries),
6162
6512
  task: async (ctx) => {
6163
6513
  const { scripts } = await getPackageJson();
6164
6514
  const errors = [];
@@ -6212,14 +6562,28 @@ var requiredConditionsCheckTask = (options) => createListr({
6212
6562
  });
6213
6563
 
6214
6564
  // src/tasks/runner.ts
6215
- var { open } = npmCli;
6565
+ var { open: open3 } = npmCli3;
6216
6566
  var { prerelease } = SemVer;
6567
+ function collectRegistries(ctx) {
6568
+ if (ctx.packages?.length) {
6569
+ const seen = /* @__PURE__ */ new Set();
6570
+ const result = [];
6571
+ for (const pkg of ctx.packages) {
6572
+ for (const reg of pkg.registries) {
6573
+ if (!seen.has(reg)) {
6574
+ seen.add(reg);
6575
+ result.push(reg);
6576
+ }
6577
+ }
6578
+ }
6579
+ return result;
6580
+ }
6581
+ return ctx.registries;
6582
+ }
6217
6583
  async function run(options) {
6218
6584
  const ctx = {
6219
6585
  ...options,
6220
- promptEnabled: !isCI2 && process10.stdin.isTTY,
6221
- npmOnly: options.registries.every((registry) => registry !== "jsr"),
6222
- jsrOnly: options.registries.every((registry) => registry === "jsr")
6586
+ promptEnabled: !isCI2 && process10.stdin.isTTY
6223
6587
  };
6224
6588
  try {
6225
6589
  if (options.contents) process10.chdir(options.contents);
@@ -6235,7 +6599,7 @@ async function run(options) {
6235
6599
  options.publishOnly ? {
6236
6600
  title: "Publishing",
6237
6601
  task: (ctx2, parentTask) => parentTask.newListr(
6238
- ctx2.registries.map((registry) => {
6602
+ collectRegistries(ctx2).map((registry) => {
6239
6603
  switch (registry) {
6240
6604
  case "npm":
6241
6605
  return npmPublishTasks;
@@ -6255,9 +6619,16 @@ async function run(options) {
6255
6619
  title: "Running tests",
6256
6620
  task: async (ctx2) => {
6257
6621
  const packageManager = await getPackageManager();
6258
- await exec7(packageManager, ["run", ctx2.testScript], {
6259
- throwOnError: true
6260
- });
6622
+ try {
6623
+ await exec7(packageManager, ["run", ctx2.testScript], {
6624
+ throwOnError: true
6625
+ });
6626
+ } catch (error) {
6627
+ throw new AbstractError(
6628
+ `Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
6629
+ { cause: error }
6630
+ );
6631
+ }
6261
6632
  }
6262
6633
  },
6263
6634
  {
@@ -6271,7 +6642,7 @@ async function run(options) {
6271
6642
  });
6272
6643
  } catch (error) {
6273
6644
  throw new AbstractError(
6274
- `Failed to run \`${packageManager} run ${ctx2.buildScript}\``,
6645
+ `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
6275
6646
  { cause: error }
6276
6647
  );
6277
6648
  }
@@ -6286,15 +6657,27 @@ async function run(options) {
6286
6657
  let commited = false;
6287
6658
  addRollback(async () => {
6288
6659
  if (tagCreated) {
6289
- console.log("Deleting tag...");
6290
- await git.deleteTag(`${await git.latestTag()}`);
6660
+ try {
6661
+ console.log("Deleting tag...");
6662
+ await git.deleteTag(`${await git.latestTag()}`);
6663
+ } catch (error) {
6664
+ console.error(
6665
+ `Failed to delete tag: ${error instanceof Error ? error.message : error}`
6666
+ );
6667
+ }
6291
6668
  }
6292
6669
  if (commited) {
6293
- console.log("Reset commits...");
6294
- await git.reset();
6295
- await git.stash();
6296
- await git.reset("HEAD^", "--hard");
6297
- await git.popStash();
6670
+ try {
6671
+ console.log("Reset commits...");
6672
+ await git.reset();
6673
+ await git.stash();
6674
+ await git.reset("HEAD^", "--hard");
6675
+ await git.popStash();
6676
+ } catch (error) {
6677
+ console.error(
6678
+ `Failed to reset commits: ${error instanceof Error ? error.message : error}`
6679
+ );
6680
+ }
6298
6681
  }
6299
6682
  }, ctx2);
6300
6683
  await git.reset();
@@ -6314,7 +6697,7 @@ async function run(options) {
6314
6697
  skip: (ctx2) => options.skipPublish || !!ctx2.preview,
6315
6698
  title: "Publishing",
6316
6699
  task: (ctx2, parentTask) => parentTask.newListr(
6317
- ctx2.registries.map((registry) => {
6700
+ collectRegistries(ctx2).map((registry) => {
6318
6701
  switch (registry) {
6319
6702
  case "npm":
6320
6703
  return npmPublishTasks;
@@ -6367,7 +6750,7 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
6367
6750
  );
6368
6751
  const linkUrl = link2("Link", releaseDraftUrl.toString());
6369
6752
  task.title += ` ${linkUrl}`;
6370
- await open(releaseDraftUrl.toString());
6753
+ await open3(releaseDraftUrl.toString());
6371
6754
  }
6372
6755
  }
6373
6756
  ]
@@ -6429,11 +6812,11 @@ function generateChangelog(version2, entries, depUpdates) {
6429
6812
 
6430
6813
  // src/changeset/migrate.ts
6431
6814
  import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync } from "node:fs";
6432
- import path4 from "node:path";
6815
+ import path5 from "node:path";
6433
6816
  import process11 from "node:process";
6434
6817
  var SKIPPED_FILES = /* @__PURE__ */ new Set(["config.json", "README.md"]);
6435
6818
  function migrateFromChangesets(cwd = process11.cwd()) {
6436
- const changesetDir = path4.join(cwd, ".changeset");
6819
+ const changesetDir = path5.join(cwd, ".changeset");
6437
6820
  if (!existsSync(changesetDir)) {
6438
6821
  return {
6439
6822
  success: false,
@@ -6442,7 +6825,7 @@ function migrateFromChangesets(cwd = process11.cwd()) {
6442
6825
  configMigrated: false
6443
6826
  };
6444
6827
  }
6445
- const pubmDir = path4.join(cwd, ".pubm", "changesets");
6828
+ const pubmDir = path5.join(cwd, ".pubm", "changesets");
6446
6829
  mkdirSync2(pubmDir, { recursive: true });
6447
6830
  const files = readdirSync(changesetDir);
6448
6831
  const migratedFiles = [];
@@ -6457,15 +6840,15 @@ function migrateFromChangesets(cwd = process11.cwd()) {
6457
6840
  }
6458
6841
  if (file === "pre.json") {
6459
6842
  copyFileSync(
6460
- path4.join(changesetDir, file),
6461
- path4.resolve(cwd, ".pubm", "pre.json")
6843
+ path5.join(changesetDir, file),
6844
+ path5.resolve(cwd, ".pubm", "pre.json")
6462
6845
  );
6463
6846
  migratedFiles.push(file);
6464
6847
  continue;
6465
6848
  }
6466
6849
  if (file.endsWith(".md")) {
6467
- const src = path4.join(changesetDir, file);
6468
- const dest = path4.join(pubmDir, file);
6850
+ const src = path5.join(changesetDir, file);
6851
+ const dest = path5.join(pubmDir, file);
6469
6852
  copyFileSync(src, dest);
6470
6853
  migratedFiles.push(file);
6471
6854
  }
@@ -6512,10 +6895,10 @@ function parseChangeset(content, fileName) {
6512
6895
 
6513
6896
  // src/changeset/reader.ts
6514
6897
  import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
6515
- import path5 from "node:path";
6898
+ import path6 from "node:path";
6516
6899
  import process12 from "node:process";
6517
6900
  function readChangesets(cwd = process12.cwd()) {
6518
- const changesetsDir = path5.join(cwd, ".pubm", "changesets");
6901
+ const changesetsDir = path6.join(cwd, ".pubm", "changesets");
6519
6902
  if (!existsSync2(changesetsDir)) {
6520
6903
  return [];
6521
6904
  }
@@ -6525,7 +6908,7 @@ function readChangesets(cwd = process12.cwd()) {
6525
6908
  if (!file.endsWith(".md") || file === "README.md") {
6526
6909
  continue;
6527
6910
  }
6528
- const filePath = path5.join(changesetsDir, file);
6911
+ const filePath = path6.join(changesetsDir, file);
6529
6912
  const content = readFileSync2(filePath, "utf-8");
6530
6913
  changesets.push(parseChangeset(content, file));
6531
6914
  }
@@ -6595,7 +6978,7 @@ function calculateVersionBumps(currentVersions, cwd = process14.cwd()) {
6595
6978
 
6596
6979
  // src/changeset/writer.ts
6597
6980
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "node:fs";
6598
- import path6 from "node:path";
6981
+ import path7 from "node:path";
6599
6982
  import process15 from "node:process";
6600
6983
  import { stringify as stringifyYaml } from "yaml";
6601
6984
  var adjectives = [
@@ -6690,90 +7073,16 @@ ${summary}
6690
7073
  return content;
6691
7074
  }
6692
7075
  function writeChangeset(releases, summary, cwd = process15.cwd()) {
6693
- const changesetsDir = path6.join(cwd, ".pubm", "changesets");
7076
+ const changesetsDir = path7.join(cwd, ".pubm", "changesets");
6694
7077
  mkdirSync3(changesetsDir, { recursive: true });
6695
7078
  const id = generateChangesetId();
6696
7079
  const fileName = `${id}.md`;
6697
- const filePath = path6.join(changesetsDir, fileName);
7080
+ const filePath = path7.join(changesetsDir, fileName);
6698
7081
  const content = generateChangesetContent(releases, summary);
6699
7082
  writeFileSync2(filePath, content, "utf-8");
6700
7083
  return filePath;
6701
7084
  }
6702
7085
 
6703
- // src/config/defaults.ts
6704
- var defaultValidate = {
6705
- cleanInstall: true,
6706
- entryPoints: true,
6707
- extraneousFiles: true
6708
- };
6709
- var defaultSnapshot = {
6710
- useCalculatedVersion: false,
6711
- prereleaseTemplate: "{tag}-{timestamp}"
6712
- };
6713
- var defaultConfig = {
6714
- versioning: "independent",
6715
- branch: "main",
6716
- changelog: true,
6717
- changelogFormat: "default",
6718
- commit: false,
6719
- access: "public",
6720
- fixed: [],
6721
- linked: [],
6722
- updateInternalDependencies: "patch",
6723
- ignore: [],
6724
- tag: "latest",
6725
- contents: ".",
6726
- saveToken: true,
6727
- releaseDraft: true,
6728
- releaseNotes: true,
6729
- registries: ["npm", "jsr"],
6730
- rollbackStrategy: "individual"
6731
- };
6732
- function resolveConfig(config) {
6733
- const packages = config.packages ?? [
6734
- { path: ".", registries: ["npm", "jsr"] }
6735
- ];
6736
- return {
6737
- ...defaultConfig,
6738
- ...config,
6739
- packages,
6740
- validate: { ...defaultValidate, ...config.validate },
6741
- snapshot: { ...defaultSnapshot, ...config.snapshot }
6742
- };
6743
- }
6744
-
6745
- // src/config/loader.ts
6746
- import { stat as stat3 } from "node:fs/promises";
6747
- import path7 from "node:path";
6748
- var CONFIG_FILES = [
6749
- "pubm.config.ts",
6750
- "pubm.config.mts",
6751
- "pubm.config.cts",
6752
- "pubm.config.js",
6753
- "pubm.config.mjs",
6754
- "pubm.config.cjs"
6755
- ];
6756
- async function findConfigFile(cwd) {
6757
- for (const file of CONFIG_FILES) {
6758
- const filePath = path7.join(cwd, file);
6759
- try {
6760
- if ((await stat3(filePath)).isFile()) {
6761
- return filePath;
6762
- }
6763
- } catch {
6764
- }
6765
- }
6766
- return null;
6767
- }
6768
- async function loadConfig(cwd = process.cwd()) {
6769
- const configPath = await findConfigFile(cwd);
6770
- if (!configPath) return null;
6771
- const { createJiti } = await import("jiti");
6772
- const jiti = createJiti(cwd, { interopDefault: true });
6773
- const mod = await jiti.import(configPath);
6774
- return mod.default ?? mod;
6775
- }
6776
-
6777
7086
  // src/config/types.ts
6778
7087
  function defineConfig(config) {
6779
7088
  return config;
@@ -6825,13 +7134,47 @@ function topologicalSort(graph) {
6825
7134
  return sorted.reverse();
6826
7135
  }
6827
7136
 
6828
- // src/monorepo/groups.ts
7137
+ // src/monorepo/discover.ts
7138
+ import { existsSync as existsSync4, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
7139
+ import path8 from "node:path";
6829
7140
  import micromatch from "micromatch";
7141
+
7142
+ // src/monorepo/workspace.ts
7143
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
7144
+ import { join } from "node:path";
7145
+ import { parse as parse2 } from "yaml";
7146
+ function detectWorkspace(cwd) {
7147
+ const root = cwd ?? process.cwd();
7148
+ const pnpmWorkspacePath = join(root, "pnpm-workspace.yaml");
7149
+ if (existsSync3(pnpmWorkspacePath)) {
7150
+ const content = readFileSync3(pnpmWorkspacePath, "utf-8");
7151
+ const parsed = parse2(content);
7152
+ const packages = parsed?.packages ?? [];
7153
+ return { type: "pnpm", patterns: packages };
7154
+ }
7155
+ const packageJsonPath = join(root, "package.json");
7156
+ if (existsSync3(packageJsonPath)) {
7157
+ const content = readFileSync3(packageJsonPath, "utf-8");
7158
+ const pkg = JSON.parse(content);
7159
+ if (pkg.workspaces) {
7160
+ if (Array.isArray(pkg.workspaces)) {
7161
+ return { type: "npm", patterns: pkg.workspaces };
7162
+ }
7163
+ if (typeof pkg.workspaces === "object" && Array.isArray(pkg.workspaces.packages)) {
7164
+ return { type: "yarn", patterns: pkg.workspaces.packages };
7165
+ }
7166
+ }
7167
+ }
7168
+ return null;
7169
+ }
7170
+
7171
+ // src/monorepo/groups.ts
7172
+ import micromatch2 from "micromatch";
6830
7173
  function resolveGroups(groups, allPackages) {
6831
7174
  return groups.map((group) => {
6832
7175
  const resolved = /* @__PURE__ */ new Set();
6833
7176
  for (const pattern of group) {
6834
- const matches = micromatch(allPackages, pattern);
7177
+ const matches = micromatch2(allPackages, pattern);
6835
7178
  for (const match of matches) {
6836
7179
  resolved.add(match);
6837
7180
  }
@@ -6868,50 +7211,21 @@ function applyLinkedGroup(bumps, group) {
6868
7211
  }
6869
7212
  }
6870
7213
 
6871
- // src/monorepo/workspace.ts
6872
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
6873
- import { join } from "node:path";
6874
- import { parse as parse2 } from "yaml";
6875
- function detectWorkspace(cwd) {
6876
- const root = cwd ?? process.cwd();
6877
- const pnpmWorkspacePath = join(root, "pnpm-workspace.yaml");
6878
- if (existsSync3(pnpmWorkspacePath)) {
6879
- const content = readFileSync3(pnpmWorkspacePath, "utf-8");
6880
- const parsed = parse2(content);
6881
- const packages = parsed?.packages ?? [];
6882
- return { type: "pnpm", patterns: packages };
6883
- }
6884
- const packageJsonPath = join(root, "package.json");
6885
- if (existsSync3(packageJsonPath)) {
6886
- const content = readFileSync3(packageJsonPath, "utf-8");
6887
- const pkg = JSON.parse(content);
6888
- if (pkg.workspaces) {
6889
- if (Array.isArray(pkg.workspaces)) {
6890
- return { type: "npm", patterns: pkg.workspaces };
6891
- }
6892
- if (typeof pkg.workspaces === "object" && Array.isArray(pkg.workspaces.packages)) {
6893
- return { type: "yarn", patterns: pkg.workspaces.packages };
6894
- }
6895
- }
6896
- }
6897
- return null;
6898
- }
6899
-
6900
7214
  // src/prerelease/pre.ts
6901
7215
  import {
6902
- existsSync as existsSync4,
7216
+ existsSync as existsSync5,
6903
7217
  mkdirSync as mkdirSync4,
6904
7218
  readFileSync as readFileSync4,
6905
7219
  rmSync,
6906
7220
  writeFileSync as writeFileSync3
6907
7221
  } from "node:fs";
6908
- import path8 from "node:path";
7222
+ import path9 from "node:path";
6909
7223
  function getPreStatePath(cwd) {
6910
- return path8.resolve(cwd ?? process.cwd(), ".pubm", "pre.json");
7224
+ return path9.resolve(cwd ?? process.cwd(), ".pubm", "pre.json");
6911
7225
  }
6912
7226
  function readPreState(cwd) {
6913
7227
  const filePath = getPreStatePath(cwd);
6914
- if (!existsSync4(filePath)) {
7228
+ if (!existsSync5(filePath)) {
6915
7229
  return null;
6916
7230
  }
6917
7231
  const content = readFileSync4(filePath, "utf-8");
@@ -6919,13 +7233,13 @@ function readPreState(cwd) {
6919
7233
  }
6920
7234
  function enterPreMode(tag, cwd) {
6921
7235
  const filePath = getPreStatePath(cwd);
6922
- if (existsSync4(filePath)) {
7236
+ if (existsSync5(filePath)) {
6923
7237
  throw new Error(
6924
7238
  "Already in pre mode. Exit pre mode first before entering a new one."
6925
7239
  );
6926
7240
  }
6927
- const dir = path8.dirname(filePath);
6928
- if (!existsSync4(dir)) {
7241
+ const dir = path9.dirname(filePath);
7242
+ if (!existsSync5(dir)) {
6929
7243
  mkdirSync4(dir, { recursive: true });
6930
7244
  }
6931
7245
  const state = {
@@ -6937,7 +7251,7 @@ function enterPreMode(tag, cwd) {
6937
7251
  }
6938
7252
  function exitPreMode(cwd) {
6939
7253
  const filePath = getPreStatePath(cwd);
6940
- if (!existsSync4(filePath)) {
7254
+ if (!existsSync5(filePath)) {
6941
7255
  throw new Error("Not in pre mode. Enter pre mode first before exiting.");
6942
7256
  }
6943
7257
  rmSync(filePath);
@@ -6965,11 +7279,11 @@ function generateSnapshotVersion(options) {
6965
7279
  }
6966
7280
 
6967
7281
  // src/validate/entry-points.ts
6968
- import { existsSync as existsSync5 } from "node:fs";
6969
- import path9 from "node:path";
7282
+ import { existsSync as existsSync6 } from "node:fs";
7283
+ import path10 from "node:path";
6970
7284
  var SIMPLE_FIELDS = ["main", "module", "types", "typings"];
6971
7285
  function checkPath(filePath, cwd) {
6972
- return existsSync5(path9.resolve(cwd, filePath));
7286
+ return existsSync6(path10.resolve(cwd, filePath));
6973
7287
  }
6974
7288
  function validateExports(exports, cwd, prefix = "exports") {
6975
7289
  const errors = [];
@@ -7022,7 +7336,7 @@ function validateEntryPoints(pkg, cwd) {
7022
7336
  }
7023
7337
 
7024
7338
  // src/validate/extraneous-files.ts
7025
- import micromatch2 from "micromatch";
7339
+ import micromatch3 from "micromatch";
7026
7340
  var PATTERNS = [
7027
7341
  {
7028
7342
  pattern: [".env", ".env.*"],
@@ -7060,7 +7374,7 @@ function detectExtraneousFiles(files) {
7060
7374
  const seen = /* @__PURE__ */ new Set();
7061
7375
  for (const { pattern, reason, basename } of PATTERNS) {
7062
7376
  const options = basename ? { basename: true } : {};
7063
- const matched = micromatch2(files, pattern, options);
7377
+ const matched = micromatch3(files, pattern, options);
7064
7378
  for (const file of matched) {
7065
7379
  if (!seen.has(file)) {
7066
7380
  seen.add(file);
@@ -7073,7 +7387,18 @@ function detectExtraneousFiles(files) {
7073
7387
 
7074
7388
  // src/index.ts
7075
7389
  async function pubm(options) {
7076
- const resolvedOptions = resolveOptions({ ...options });
7390
+ const config = await loadConfig();
7391
+ const configOptions = {};
7392
+ if (config) {
7393
+ const resolved = resolveConfig(config);
7394
+ if (resolved.packages) {
7395
+ configOptions.packages = resolved.packages;
7396
+ }
7397
+ if (!options.registries && resolved.registries) {
7398
+ configOptions.registries = resolved.registries;
7399
+ }
7400
+ }
7401
+ const resolvedOptions = resolveOptions({ ...configOptions, ...options });
7077
7402
  await run(resolvedOptions);
7078
7403
  }
7079
7404
  export {