pubm 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -2416,15 +2416,15 @@ var isCompatibleTerminal = tty && tty.isatty && tty.isatty(1) && env.TERM && !is
2416
2416
  var isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
2417
2417
  var isColorSupported = !isDisabled && (isForced || isWindows && !isDumbTerminal || isCompatibleTerminal || isCI);
2418
2418
  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));
2419
- var clearBleed = (index, string, open2, close, replace) => index < 0 ? open2 + string + close : open2 + replaceClose(index, string, close, replace) + close;
2420
- var filterEmpty = (open2, close, replace = open2, at = open2.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(
2419
+ var clearBleed = (index, string, open3, close, replace) => index < 0 ? open3 + string + close : open3 + replaceClose(index, string, close, replace) + close;
2420
+ var filterEmpty = (open3, close, replace = open3, at = open3.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(
2421
2421
  ("" + string).indexOf(close, at),
2422
2422
  string,
2423
- open2,
2423
+ open3,
2424
2424
  close,
2425
2425
  replace
2426
2426
  ) : "";
2427
- var init = (open2, close, replace) => filterEmpty(`\x1B[${open2}m`, `\x1B[${close}m`, replace);
2427
+ var init = (open3, close, replace) => filterEmpty(`\x1B[${open3}m`, `\x1B[${close}m`, replace);
2428
2428
  var colors = {
2429
2429
  reset: init(0, 0),
2430
2430
  bold: init(1, 22, "\x1B[22m\x1B[1m"),
@@ -4899,7 +4899,7 @@ var Git = class {
4899
4899
  try {
4900
4900
  return (await this.git(["tag", "-l"])).trim().split("\n").sort(semver.compareIdentifiers);
4901
4901
  } catch (error) {
4902
- throw new GitError("Failed to run `git config --get user.name`", {
4902
+ throw new GitError("Failed to run `git tag -l`", {
4903
4903
  cause: error
4904
4904
  });
4905
4905
  }
@@ -5160,7 +5160,7 @@ function resolveOptions(options) {
5160
5160
 
5161
5161
  // src/tasks/runner.ts
5162
5162
  import process14 from "node:process";
5163
- import npmCli from "@npmcli/promise-spawn";
5163
+ import npmCli2 from "@npmcli/promise-spawn";
5164
5164
  import SemVer from "semver";
5165
5165
  import { isCI as isCI2 } from "std-env";
5166
5166
  import { exec as exec7 } from "tinyexec";
@@ -5182,8 +5182,24 @@ async function rollback() {
5182
5182
  called = true;
5183
5183
  if (rollbacks.length <= 0) return void 0;
5184
5184
  console.log("Rollback...");
5185
- await Promise.all(rollbacks.map(({ fn, ctx }) => fn(ctx)));
5186
- console.log("Rollback completed");
5185
+ const results = await Promise.allSettled(
5186
+ rollbacks.map(({ fn, ctx }) => fn(ctx))
5187
+ );
5188
+ const failures = results.filter(
5189
+ (r) => r.status === "rejected"
5190
+ );
5191
+ if (failures.length > 0) {
5192
+ for (const failure of failures) {
5193
+ console.error(
5194
+ `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
5195
+ );
5196
+ }
5197
+ console.log(
5198
+ "Rollback completed with errors. Some operations may require manual recovery."
5199
+ );
5200
+ } else {
5201
+ console.log("Rollback completed");
5202
+ }
5187
5203
  }
5188
5204
 
5189
5205
  // src/utils/listr.ts
@@ -5350,20 +5366,34 @@ async function replaceVersion(version2) {
5350
5366
  const packageJsonPath = await findOutFile("package.json");
5351
5367
  if (!packageJsonPath) return void 0;
5352
5368
  const packageJson = (await readFile(packageJsonPath)).toString();
5353
- await writeFile(
5354
- packageJsonPath,
5355
- packageJson.replace(versionRegex, `$1${version2}$2`)
5356
- );
5369
+ try {
5370
+ await writeFile(
5371
+ packageJsonPath,
5372
+ packageJson.replace(versionRegex, `$1${version2}$2`)
5373
+ );
5374
+ } catch (error) {
5375
+ throw new AbstractError(
5376
+ `Failed to write version to package.json: ${error instanceof Error ? error.message : error}`,
5377
+ { cause: error }
5378
+ );
5379
+ }
5357
5380
  return "package.json";
5358
5381
  })(),
5359
5382
  (async () => {
5360
5383
  const jsrJsonPath = await findOutFile("jsr.json");
5361
5384
  if (!jsrJsonPath) return void 0;
5362
5385
  const jsrJson = (await readFile(jsrJsonPath)).toString();
5363
- await writeFile(
5364
- jsrJsonPath,
5365
- jsrJson.replace(versionRegex, `$1${version2}$2`)
5366
- );
5386
+ try {
5387
+ await writeFile(
5388
+ jsrJsonPath,
5389
+ jsrJson.replace(versionRegex, `$1${version2}$2`)
5390
+ );
5391
+ } catch (error) {
5392
+ throw new AbstractError(
5393
+ `Failed to write version to jsr.json: ${error instanceof Error ? error.message : error}`,
5394
+ { cause: error }
5395
+ );
5396
+ }
5367
5397
  return "jsr.json";
5368
5398
  })()
5369
5399
  ]);
@@ -5382,6 +5412,7 @@ async function getPackageManager() {
5382
5412
  if (await findOutFile(lockFile2)) return packageManager;
5383
5413
  }
5384
5414
  }
5415
+ console.warn("No lock file found, defaulting to npm.");
5385
5416
  return "npm";
5386
5417
  }
5387
5418
 
@@ -5500,13 +5531,21 @@ var CratesRegistry = class extends Registry {
5500
5531
  { headers: this.headers }
5501
5532
  );
5502
5533
  if (!response.ok) {
5503
- throw new Error(`Crate '${this.packageName}' not found`);
5534
+ if (response.status === 404) {
5535
+ throw new CratesError(
5536
+ `Crate '${this.packageName}' not found on crates.io`
5537
+ );
5538
+ }
5539
+ throw new CratesError(
5540
+ `crates.io API error (HTTP ${response.status}) for crate '${this.packageName}'`
5541
+ );
5504
5542
  }
5505
5543
  const data = await response.json();
5506
5544
  return data.crate.max_version;
5507
5545
  } catch (error) {
5546
+ if (error instanceof CratesError) throw error;
5508
5547
  throw new CratesError(
5509
- `Failed to fetch version for crate '${this.packageName}'`,
5548
+ `Cannot reach crates.io to fetch version for '${this.packageName}'`,
5510
5549
  { cause: error }
5511
5550
  );
5512
5551
  }
@@ -5536,6 +5575,12 @@ var CratesRegistry = class extends Registry {
5536
5575
  if (process.env.CARGO_REGISTRY_TOKEN) return true;
5537
5576
  return this.isInstalled();
5538
5577
  }
5578
+ getRequirements() {
5579
+ return {
5580
+ needsPackageScripts: false,
5581
+ requiredManifest: "Cargo.toml"
5582
+ };
5583
+ }
5539
5584
  async isPackageNameAvaliable() {
5540
5585
  try {
5541
5586
  const response = await fetch(
@@ -5543,8 +5588,11 @@ var CratesRegistry = class extends Registry {
5543
5588
  { headers: this.headers }
5544
5589
  );
5545
5590
  return !response.ok;
5546
- } catch {
5547
- return true;
5591
+ } catch (error) {
5592
+ throw new CratesError(
5593
+ `Failed to check package name availability on crates.io`,
5594
+ { cause: error }
5595
+ );
5548
5596
  }
5549
5597
  }
5550
5598
  };
@@ -5595,7 +5643,7 @@ import process12 from "node:process";
5595
5643
  import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
5596
5644
 
5597
5645
  // src/registry/jsr.ts
5598
- import { NonZeroExitError, exec as exec4 } from "tinyexec";
5646
+ import { exec as exec4, NonZeroExitError } from "tinyexec";
5599
5647
 
5600
5648
  // src/utils/db.ts
5601
5649
  import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
@@ -5621,32 +5669,50 @@ var Db = class {
5621
5669
  mkdirSync5(this.path);
5622
5670
  }
5623
5671
  } catch {
5624
- mkdirSync5(this.path);
5672
+ try {
5673
+ mkdirSync5(this.path);
5674
+ } catch (error) {
5675
+ throw new Error(
5676
+ `Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
5677
+ );
5678
+ }
5625
5679
  }
5626
5680
  }
5627
5681
  set(field, value) {
5628
- writeFileSync4(
5629
- path8.resolve(this.path, Buffer.from(e(field, field)).toString("base64")),
5630
- Buffer.from(e(`${value}`, field)),
5631
- { encoding: "binary" }
5632
- );
5682
+ try {
5683
+ writeFileSync4(
5684
+ path8.resolve(
5685
+ this.path,
5686
+ Buffer.from(e(field, field)).toString("base64")
5687
+ ),
5688
+ Buffer.from(e(`${value}`, field)),
5689
+ { encoding: "binary" }
5690
+ );
5691
+ } catch (error) {
5692
+ throw new Error(
5693
+ `Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
5694
+ );
5695
+ }
5633
5696
  }
5634
5697
  get(field) {
5698
+ const filePath = path8.resolve(
5699
+ this.path,
5700
+ Buffer.from(e(field, field)).toString("base64")
5701
+ );
5702
+ let raw;
5635
5703
  try {
5636
- return d(
5637
- Buffer.from(
5638
- readFileSync3(
5639
- path8.resolve(
5640
- this.path,
5641
- Buffer.from(e(field, field)).toString("base64")
5642
- )
5643
- )
5644
- ).toString(),
5645
- field
5646
- );
5704
+ raw = readFileSync3(filePath);
5647
5705
  } catch {
5648
5706
  return null;
5649
5707
  }
5708
+ try {
5709
+ return d(Buffer.from(raw).toString(), field);
5710
+ } catch {
5711
+ console.warn(
5712
+ `Stored token for '${field}' appears corrupted. It will be re-requested.`
5713
+ );
5714
+ return null;
5715
+ }
5650
5716
  }
5651
5717
  };
5652
5718
 
@@ -5660,9 +5726,12 @@ function getScope(packageName) {
5660
5726
  }
5661
5727
  function getScopeAndName(packageName) {
5662
5728
  const matches = packageName.match(/^@([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)$/);
5663
- const scope = matches?.[1];
5664
- const name = matches?.[2];
5665
- return [`${scope}`, `${name}`];
5729
+ if (!matches) {
5730
+ throw new Error(
5731
+ `Invalid scoped package name: '${packageName}'. Expected format: @scope/name`
5732
+ );
5733
+ }
5734
+ return [matches[1], matches[2]];
5666
5735
  }
5667
5736
  var scopedPackagePattern = /^(?:@([^/]+?)[/])?([^/]+?)$/;
5668
5737
  var blacklist = ["node_modules", "favicon.ico"];
@@ -5781,6 +5850,12 @@ ${stderr}` : ""}`,
5781
5850
  async isPackageNameAvaliable() {
5782
5851
  return isValidPackageName(this.packageName);
5783
5852
  }
5853
+ getRequirements() {
5854
+ return {
5855
+ needsPackageScripts: false,
5856
+ requiredManifest: "jsr.json"
5857
+ };
5858
+ }
5784
5859
  };
5785
5860
  var _JsrClient = class _JsrClient {
5786
5861
  constructor(apiEndpoint) {
@@ -5801,6 +5876,9 @@ var _JsrClient = class _JsrClient {
5801
5876
  try {
5802
5877
  const response = await this.fetch("/user");
5803
5878
  if (response.status === 401) return null;
5879
+ if (!response.ok) {
5880
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5881
+ }
5804
5882
  return await response.json();
5805
5883
  } catch (error) {
5806
5884
  throw new JsrError(`Failed to fetch \`${this.apiEndpoint}/user\``, {
@@ -5812,6 +5890,9 @@ var _JsrClient = class _JsrClient {
5812
5890
  try {
5813
5891
  const response = await this.fetch(`/user/member/${scope}`);
5814
5892
  if (response.status === 401) return null;
5893
+ if (!response.ok) {
5894
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5895
+ }
5815
5896
  return await response.json();
5816
5897
  } catch (error) {
5817
5898
  throw new JsrError(
@@ -5826,9 +5907,14 @@ var _JsrClient = class _JsrClient {
5826
5907
  try {
5827
5908
  const response = await this.fetch("/user/scopes");
5828
5909
  if (response.status === 401) return [];
5829
- return (await response.json()).map(
5830
- ({ scope }) => scope
5831
- );
5910
+ if (!response.ok) {
5911
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5912
+ }
5913
+ const body = await response.json();
5914
+ if (!Array.isArray(body)) {
5915
+ throw new Error(`Expected array response but got ${typeof body}`);
5916
+ }
5917
+ return body.map(({ scope }) => scope);
5832
5918
  } catch (error) {
5833
5919
  throw new JsrError(
5834
5920
  `Failed to fetch \`${this.apiEndpoint}/user/scopes\``,
@@ -5842,10 +5928,17 @@ var _JsrClient = class _JsrClient {
5842
5928
  const [scope, name] = getScopeAndName(packageName);
5843
5929
  try {
5844
5930
  const response = await this.fetch(`/scopes/${scope}/packages/${name}`);
5931
+ if (response.status === 404) return null;
5932
+ if (!response.ok) {
5933
+ throw new JsrError(
5934
+ `JSR API error (HTTP ${response.status}) for package '${packageName}'`
5935
+ );
5936
+ }
5845
5937
  return await response.json();
5846
5938
  } catch (error) {
5939
+ if (error instanceof JsrError) throw error;
5847
5940
  throw new JsrError(
5848
- `Failed to fetch \`${this.apiEndpoint}/user/scopes\``,
5941
+ `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages/${name}\``,
5849
5942
  {
5850
5943
  cause: error
5851
5944
  }
@@ -5858,8 +5951,18 @@ var _JsrClient = class _JsrClient {
5858
5951
  method: "POST",
5859
5952
  body: JSON.stringify({ scope })
5860
5953
  });
5861
- return response.status === 200 || response.status === 201;
5954
+ if (response.status === 200 || response.status === 201) return true;
5955
+ let detail = "";
5956
+ try {
5957
+ const body = await response.json();
5958
+ detail = body.message || body.error || JSON.stringify(body);
5959
+ } catch {
5960
+ }
5961
+ throw new JsrError(
5962
+ `Failed to create scope '${scope}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5963
+ );
5862
5964
  } catch (error) {
5965
+ if (error instanceof JsrError) throw error;
5863
5966
  throw new JsrError(`Failed to fetch \`${this.apiEndpoint}/scopes\``, {
5864
5967
  cause: error
5865
5968
  });
@@ -5870,8 +5973,18 @@ var _JsrClient = class _JsrClient {
5870
5973
  const response = await this.fetch(`/scopes/${scope}`, {
5871
5974
  method: "DELETE"
5872
5975
  });
5873
- return response.status === 200 || response.status === 204;
5976
+ if (response.status === 200 || response.status === 204) return true;
5977
+ let detail = "";
5978
+ try {
5979
+ const body = await response.json();
5980
+ detail = body.message || body.error || JSON.stringify(body);
5981
+ } catch {
5982
+ }
5983
+ throw new JsrError(
5984
+ `Failed to delete scope '${scope}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
5985
+ );
5874
5986
  } catch (error) {
5987
+ if (error instanceof JsrError) throw error;
5875
5988
  throw new JsrError(
5876
5989
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}\``,
5877
5990
  {
@@ -5887,8 +6000,18 @@ var _JsrClient = class _JsrClient {
5887
6000
  method: "POST",
5888
6001
  body: JSON.stringify({ package: name })
5889
6002
  });
5890
- return response.status === 200 || response.status === 201;
6003
+ if (response.status === 200 || response.status === 201) return true;
6004
+ let detail = "";
6005
+ try {
6006
+ const body = await response.json();
6007
+ detail = body.message || body.error || JSON.stringify(body);
6008
+ } catch {
6009
+ }
6010
+ throw new JsrError(
6011
+ `Failed to create package '${packageName}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
6012
+ );
5891
6013
  } catch (error) {
6014
+ if (error instanceof JsrError) throw error;
5892
6015
  throw new JsrError(
5893
6016
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages\``,
5894
6017
  {
@@ -5903,8 +6026,18 @@ var _JsrClient = class _JsrClient {
5903
6026
  const response = await this.fetch(`/scopes/${scope}/packages/${name}`, {
5904
6027
  method: "DELETE"
5905
6028
  });
5906
- return response.status === 200 || response.status === 204;
6029
+ if (response.status === 200 || response.status === 204) return true;
6030
+ let detail = "";
6031
+ try {
6032
+ const body = await response.json();
6033
+ detail = body.message || body.error || JSON.stringify(body);
6034
+ } catch {
6035
+ }
6036
+ throw new JsrError(
6037
+ `Failed to delete package '${packageName}': HTTP ${response.status}${detail ? ` \u2014 ${detail}` : ""}`
6038
+ );
5907
6039
  } catch (error) {
6040
+ if (error instanceof JsrError) throw error;
5908
6041
  throw new JsrError(
5909
6042
  `Failed to fetch \`${this.apiEndpoint}/scopes/${scope}/packages/${name}\``,
5910
6043
  {
@@ -5916,6 +6049,9 @@ var _JsrClient = class _JsrClient {
5916
6049
  async searchPackage(query) {
5917
6050
  try {
5918
6051
  const response = await this.fetch(`/packages?query=${query}`);
6052
+ if (!response.ok) {
6053
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
6054
+ }
5919
6055
  return await response.json();
5920
6056
  } catch (error) {
5921
6057
  throw new JsrError(
@@ -5935,7 +6071,7 @@ async function jsrRegistry() {
5935
6071
  }
5936
6072
 
5937
6073
  // src/registry/npm.ts
5938
- import { NonZeroExitError as NonZeroExitError2, exec as exec5 } from "tinyexec";
6074
+ import { exec as exec5, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5939
6075
  var NpmError = class extends AbstractError {
5940
6076
  constructor() {
5941
6077
  super(...arguments);
@@ -5992,7 +6128,7 @@ var NpmRegistry = class extends Registry {
5992
6128
  await this.npm(["whoami"]);
5993
6129
  return true;
5994
6130
  } catch (error) {
5995
- if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("ENEEDAUTH")) {
6131
+ if (error instanceof NonZeroExitError2) {
5996
6132
  return false;
5997
6133
  }
5998
6134
  throw new NpmError("Failed to run `npm whoami`", { cause: error });
@@ -6000,16 +6136,22 @@ var NpmRegistry = class extends Registry {
6000
6136
  }
6001
6137
  async collaborators() {
6002
6138
  try {
6003
- return JSON.parse(
6004
- await this.npm([
6005
- "access",
6006
- "list",
6007
- "collaborators",
6008
- this.packageName,
6009
- "--json"
6010
- ])
6011
- );
6139
+ const output = await this.npm([
6140
+ "access",
6141
+ "list",
6142
+ "collaborators",
6143
+ this.packageName,
6144
+ "--json"
6145
+ ]);
6146
+ try {
6147
+ return JSON.parse(output);
6148
+ } catch {
6149
+ throw new NpmError(
6150
+ `Unexpected response from npm registry for collaborators of '${this.packageName}'`
6151
+ );
6152
+ }
6012
6153
  } catch (error) {
6154
+ if (error instanceof NpmError) throw error;
6013
6155
  throw new NpmError(
6014
6156
  `Failed to run \`npm access list collaborators ${this.packageName} --json\``,
6015
6157
  { cause: error }
@@ -6023,12 +6165,21 @@ var NpmRegistry = class extends Registry {
6023
6165
  }
6024
6166
  async distTags() {
6025
6167
  try {
6026
- return Object.keys(
6027
- JSON.parse(
6028
- await this.npm(["view", this.packageName, "dist-tags", "--json"])
6029
- )
6030
- );
6168
+ const output = await this.npm([
6169
+ "view",
6170
+ this.packageName,
6171
+ "dist-tags",
6172
+ "--json"
6173
+ ]);
6174
+ try {
6175
+ return Object.keys(JSON.parse(output));
6176
+ } catch {
6177
+ throw new NpmError(
6178
+ `Unexpected response from npm registry for dist-tags of '${this.packageName}'`
6179
+ );
6180
+ }
6031
6181
  } catch (error) {
6182
+ if (error instanceof NpmError) throw error;
6032
6183
  throw new NpmError(
6033
6184
  `Failed to run \`npm view ${this.packageName} dist-tags --json\``,
6034
6185
  { cause: error }
@@ -6058,12 +6209,7 @@ var NpmRegistry = class extends Registry {
6058
6209
  if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
6059
6210
  return false;
6060
6211
  }
6061
- throw new NpmError(
6062
- "Failed to run `npm publish --provenance --access public`",
6063
- {
6064
- cause: error
6065
- }
6066
- );
6212
+ throw this.classifyPublishError(error);
6067
6213
  }
6068
6214
  }
6069
6215
  async publish(otp) {
@@ -6075,14 +6221,39 @@ var NpmRegistry = class extends Registry {
6075
6221
  if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
6076
6222
  return false;
6077
6223
  }
6078
- throw new NpmError(`Failed to run \`npm ${args.join(" ")}\``, {
6079
- cause: error
6080
- });
6224
+ throw this.classifyPublishError(error);
6081
6225
  }
6082
6226
  }
6083
6227
  async isPackageNameAvaliable() {
6084
6228
  return isValidPackageName(this.packageName);
6085
6229
  }
6230
+ getRequirements() {
6231
+ return {
6232
+ needsPackageScripts: true,
6233
+ requiredManifest: "package.json"
6234
+ };
6235
+ }
6236
+ classifyPublishError(error) {
6237
+ if (error instanceof NonZeroExitError2) {
6238
+ const stderr = error.output?.stderr ?? "";
6239
+ if (stderr.includes("EOTP")) {
6240
+ return new NpmError("OTP required for publishing", { cause: error });
6241
+ }
6242
+ if (stderr.includes("403") || stderr.includes("Forbidden")) {
6243
+ return new NpmError(
6244
+ "Permission denied (403 Forbidden). Check your npm access token permissions.",
6245
+ { cause: error }
6246
+ );
6247
+ }
6248
+ if (stderr.includes("429") || stderr.includes("Too Many Requests")) {
6249
+ return new NpmError(
6250
+ "Rate limited by npm registry. Please wait and try again.",
6251
+ { cause: error }
6252
+ );
6253
+ }
6254
+ }
6255
+ return new NpmError("Failed to publish to npm", { cause: error });
6256
+ }
6086
6257
  };
6087
6258
  async function npmRegistry() {
6088
6259
  const packageJson = await getPackageJson();
@@ -6114,17 +6285,34 @@ var jsrAvailableCheckTasks = {
6114
6285
  if (!JsrClient.token) {
6115
6286
  task.output = "Retrieving jsr API token";
6116
6287
  if (ctx.promptEnabled) {
6117
- while (true) {
6288
+ const maxAttempts = 3;
6289
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6118
6290
  JsrClient.token = await task.prompt(ListrEnquirerPromptAdapter).run({
6119
6291
  type: "password",
6120
- message: `Please enter the jsr ${color.bold("API token")}`,
6292
+ message: `Please enter the jsr ${color.bold("API token")}${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`,
6121
6293
  footer: `
6122
6294
  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'")}.`
6123
6295
  });
6124
6296
  try {
6125
6297
  if (await jsr.client.user()) break;
6126
- task.output = "The jsr API token is invalid. Please re-enter a valid token.";
6127
- } catch {
6298
+ if (attempt < maxAttempts) {
6299
+ task.output = "The jsr API token is invalid. Please re-enter a valid token.";
6300
+ }
6301
+ } catch (error) {
6302
+ if (error instanceof Error && (error.message.includes("fetch") || error.message.includes("network") || error.message.includes("ENOTFOUND"))) {
6303
+ throw new JsrAvailableError(
6304
+ "JSR API is unreachable. Check your network connection.",
6305
+ { cause: error }
6306
+ );
6307
+ }
6308
+ if (attempt < maxAttempts) {
6309
+ task.output = "The jsr API token is invalid. Please re-enter a valid token.";
6310
+ }
6311
+ }
6312
+ if (attempt === maxAttempts) {
6313
+ throw new JsrAvailableError(
6314
+ "JSR token verification failed after 3 attempts."
6315
+ );
6128
6316
  }
6129
6317
  }
6130
6318
  } else {
@@ -6147,7 +6335,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
6147
6335
  scopes.map(
6148
6336
  (scope2) => jsr.client.package(`@${scope2}/${jsr.packageName}`)
6149
6337
  )
6150
- )).filter((v) => v);
6338
+ )).filter((v) => v !== null);
6151
6339
  if (searchResults.length > 0) {
6152
6340
  jsrName = await task.prompt(ListrEnquirerPromptAdapter).run({
6153
6341
  type: "select",
@@ -6258,8 +6446,11 @@ var jsrPublishTasks = {
6258
6446
  };
6259
6447
 
6260
6448
  // src/tasks/npm.ts
6449
+ import { spawn } from "node:child_process";
6261
6450
  import process13 from "node:process";
6262
6451
  import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
6452
+ import npmCli from "@npmcli/promise-spawn";
6453
+ var { open } = npmCli;
6263
6454
  var NpmAvailableError = class extends AbstractError {
6264
6455
  constructor(message, { cause } = {}) {
6265
6456
  super(message, { cause });
@@ -6270,12 +6461,55 @@ var NpmAvailableError = class extends AbstractError {
6270
6461
  var npmAvailableCheckTasks = {
6271
6462
  title: "Checking npm avaliable for publising",
6272
6463
  skip: (ctx) => !!ctx.preview,
6273
- task: async () => {
6464
+ task: async (ctx, task) => {
6274
6465
  const npm = await npmRegistry();
6275
6466
  if (!await npm.isLoggedIn()) {
6276
- throw new NpmAvailableError(
6277
- "You are not logged in. Please log in first using `npm login`."
6278
- );
6467
+ if (ctx.promptEnabled) {
6468
+ try {
6469
+ task.output = "Launching npm login...";
6470
+ await new Promise((resolve, reject) => {
6471
+ const child = spawn("npm", ["login"], {
6472
+ stdio: ["pipe", "pipe", "pipe"]
6473
+ });
6474
+ let opened = false;
6475
+ const onData = (data) => {
6476
+ const text = data.toString();
6477
+ const urlMatch = text.match(
6478
+ /https:\/\/www\.npmjs\.com\/login[^\s]*/
6479
+ );
6480
+ if (urlMatch && !opened) {
6481
+ opened = true;
6482
+ task.output = `Login at: ${color.cyan(urlMatch[0])}`;
6483
+ open(urlMatch[0]);
6484
+ child.stdin?.write("\n");
6485
+ }
6486
+ };
6487
+ child.stdout?.on("data", onData);
6488
+ child.stderr?.on("data", onData);
6489
+ child.on(
6490
+ "close",
6491
+ (code) => code === 0 ? resolve() : reject(
6492
+ new Error(`npm login exited with code ${code}`)
6493
+ )
6494
+ );
6495
+ child.on("error", reject);
6496
+ });
6497
+ } catch (error) {
6498
+ throw new NpmAvailableError(
6499
+ "npm login failed. Please run `npm login` manually and try again.",
6500
+ { cause: error }
6501
+ );
6502
+ }
6503
+ if (!await npm.isLoggedIn()) {
6504
+ throw new NpmAvailableError(
6505
+ "Still not logged in after npm login. Please verify your credentials."
6506
+ );
6507
+ }
6508
+ } else {
6509
+ throw new NpmAvailableError(
6510
+ "Not logged in to npm. Set NODE_AUTH_TOKEN in your CI environment. For GitHub Actions, add it as a repository secret."
6511
+ );
6512
+ }
6279
6513
  }
6280
6514
  if (await npm.isPublished()) {
6281
6515
  if (!await npm.hasPermission()) {
@@ -6303,24 +6537,31 @@ var npmPublishTasks = {
6303
6537
  let result = await npm.publish();
6304
6538
  if (!result) {
6305
6539
  task.title = "Running npm publish (OTP code needed)";
6306
- while (!result) {
6540
+ const maxAttempts = 3;
6541
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6307
6542
  result = await npm.publish(
6308
6543
  await task.prompt(ListrEnquirerPromptAdapter2).run({
6309
6544
  type: "password",
6310
- message: "npm OTP code"
6545
+ message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6311
6546
  })
6312
6547
  );
6313
- if (!result) {
6314
- task.output = "2FA failed";
6548
+ if (result) break;
6549
+ if (attempt < maxAttempts) {
6550
+ task.output = "2FA failed. Please try again.";
6315
6551
  }
6316
6552
  }
6553
+ if (!result) {
6554
+ throw new NpmAvailableError(
6555
+ "OTP verification failed after 3 attempts."
6556
+ );
6557
+ }
6317
6558
  task.title = "Running npm publish (2FA passed)";
6318
6559
  }
6319
6560
  } else {
6320
6561
  const npmTokenEnv = process13.env.NODE_AUTH_TOKEN;
6321
6562
  if (!npmTokenEnv) {
6322
6563
  throw new NpmAvailableError(
6323
- "NODE_AUTH_TOKEN not found in the environment variables. Please set the token and try again."
6564
+ "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"
6324
6565
  );
6325
6566
  }
6326
6567
  const result = await npm.publishProvenance();
@@ -6426,6 +6667,7 @@ var prerequisitesCheckTask = (options) => {
6426
6667
  );
6427
6668
  }
6428
6669
  ctx.cleanWorkingTree = false;
6670
+ return;
6429
6671
  }
6430
6672
  ctx.cleanWorkingTree = true;
6431
6673
  }
@@ -6526,6 +6768,16 @@ async function validateEngineVersion(engine, version2) {
6526
6768
  }
6527
6769
 
6528
6770
  // src/tasks/required-conditions-check.ts
6771
+ var registryRequirementsMap = {
6772
+ npm: { needsPackageScripts: true },
6773
+ jsr: { needsPackageScripts: false },
6774
+ crates: { needsPackageScripts: false }
6775
+ };
6776
+ function needsPackageScripts(registries) {
6777
+ return registries.some(
6778
+ (r) => registryRequirementsMap[r]?.needsPackageScripts ?? true
6779
+ );
6780
+ }
6529
6781
  var RequiredConditionCheckError = class extends AbstractError {
6530
6782
  constructor(message, { cause } = {}) {
6531
6783
  super(message, { cause });
@@ -6601,7 +6853,7 @@ var requiredConditionsCheckTask = (options) => createListr({
6601
6853
  },
6602
6854
  {
6603
6855
  title: "Checking if test and build scripts exist",
6604
- skip: (ctx) => ctx.jsrOnly,
6856
+ skip: (ctx) => !needsPackageScripts(ctx.registries),
6605
6857
  task: async (ctx) => {
6606
6858
  const { scripts } = await getPackageJson();
6607
6859
  const errors = [];
@@ -6655,14 +6907,12 @@ var requiredConditionsCheckTask = (options) => createListr({
6655
6907
  });
6656
6908
 
6657
6909
  // src/tasks/runner.ts
6658
- var { open } = npmCli;
6910
+ var { open: open2 } = npmCli2;
6659
6911
  var { prerelease } = SemVer;
6660
6912
  async function run(options) {
6661
6913
  const ctx = {
6662
6914
  ...options,
6663
- promptEnabled: !isCI2 && process14.stdin.isTTY,
6664
- npmOnly: options.registries.every((registry) => registry !== "jsr"),
6665
- jsrOnly: options.registries.every((registry) => registry === "jsr")
6915
+ promptEnabled: !isCI2 && process14.stdin.isTTY
6666
6916
  };
6667
6917
  try {
6668
6918
  if (options.contents) process14.chdir(options.contents);
@@ -6698,9 +6948,16 @@ async function run(options) {
6698
6948
  title: "Running tests",
6699
6949
  task: async (ctx2) => {
6700
6950
  const packageManager = await getPackageManager();
6701
- await exec7(packageManager, ["run", ctx2.testScript], {
6702
- throwOnError: true
6703
- });
6951
+ try {
6952
+ await exec7(packageManager, ["run", ctx2.testScript], {
6953
+ throwOnError: true
6954
+ });
6955
+ } catch (error) {
6956
+ throw new AbstractError(
6957
+ `Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
6958
+ { cause: error }
6959
+ );
6960
+ }
6704
6961
  }
6705
6962
  },
6706
6963
  {
@@ -6714,7 +6971,7 @@ async function run(options) {
6714
6971
  });
6715
6972
  } catch (error) {
6716
6973
  throw new AbstractError(
6717
- `Failed to run \`${packageManager} run ${ctx2.buildScript}\``,
6974
+ `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
6718
6975
  { cause: error }
6719
6976
  );
6720
6977
  }
@@ -6729,15 +6986,27 @@ async function run(options) {
6729
6986
  let commited = false;
6730
6987
  addRollback(async () => {
6731
6988
  if (tagCreated) {
6732
- console.log("Deleting tag...");
6733
- await git.deleteTag(`${await git.latestTag()}`);
6989
+ try {
6990
+ console.log("Deleting tag...");
6991
+ await git.deleteTag(`${await git.latestTag()}`);
6992
+ } catch (error) {
6993
+ console.error(
6994
+ `Failed to delete tag: ${error instanceof Error ? error.message : error}`
6995
+ );
6996
+ }
6734
6997
  }
6735
6998
  if (commited) {
6736
- console.log("Reset commits...");
6737
- await git.reset();
6738
- await git.stash();
6739
- await git.reset("HEAD^", "--hard");
6740
- await git.popStash();
6999
+ try {
7000
+ console.log("Reset commits...");
7001
+ await git.reset();
7002
+ await git.stash();
7003
+ await git.reset("HEAD^", "--hard");
7004
+ await git.popStash();
7005
+ } catch (error) {
7006
+ console.error(
7007
+ `Failed to reset commits: ${error instanceof Error ? error.message : error}`
7008
+ );
7009
+ }
6741
7010
  }
6742
7011
  }, ctx2);
6743
7012
  await git.reset();
@@ -6810,7 +7079,7 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
6810
7079
  );
6811
7080
  const linkUrl = link2("Link", releaseDraftUrl.toString());
6812
7081
  task.title += ` ${linkUrl}`;
6813
- await open(releaseDraftUrl.toString());
7082
+ await open2(releaseDraftUrl.toString());
6814
7083
  }
6815
7084
  }
6816
7085
  ]
@@ -6838,7 +7107,9 @@ import { inc } from "semver";
6838
7107
  import { stat as stat3 } from "node:fs/promises";
6839
7108
  import path9 from "node:path";
6840
7109
 
6841
- // src/monorepo/groups.ts
7110
+ // src/monorepo/discover.ts
7111
+ import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
7112
+ import path10 from "node:path";
6842
7113
  import micromatch from "micromatch";
6843
7114
 
6844
7115
  // src/monorepo/workspace.ts
@@ -6846,12 +7117,15 @@ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:f
6846
7117
  import { join } from "node:path";
6847
7118
  import { parse as parse2 } from "yaml";
6848
7119
 
7120
+ // src/monorepo/groups.ts
7121
+ import micromatch2 from "micromatch";
7122
+
6849
7123
  // src/validate/entry-points.ts
6850
- import { existsSync as existsSync6 } from "node:fs";
6851
- import path10 from "node:path";
7124
+ import { existsSync as existsSync7 } from "node:fs";
7125
+ import path11 from "node:path";
6852
7126
 
6853
7127
  // src/validate/extraneous-files.ts
6854
- import micromatch2 from "micromatch";
7128
+ import micromatch3 from "micromatch";
6855
7129
 
6856
7130
  // src/index.ts
6857
7131
  async function pubm(options) {
@@ -7096,6 +7370,7 @@ defaultCmd.action(
7096
7370
  );
7097
7371
  } catch (e2) {
7098
7372
  consoleError(e2);
7373
+ process.exitCode = 1;
7099
7374
  }
7100
7375
  }
7101
7376
  );