@ucdjs/release-scripts 0.1.0-beta.41 → 0.1.0-beta.42

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.
Files changed (2) hide show
  1. package/dist/index.mjs +124 -52
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -123,8 +123,48 @@ async function dryRun(bin, args, opts) {
123
123
  return logger.verbose(farver.blue(`[dryrun] ${bin} ${args.join(" ")}`), opts || "");
124
124
  }
125
125
  const runIfNotDry = isDryRun ? dryRun : run;
126
- function exitWithError(message, hint) {
126
+ function isRecord(value) {
127
+ return typeof value === "object" && value !== null;
128
+ }
129
+ function formatUnknownError(error) {
130
+ if (error instanceof Error) {
131
+ const base = {
132
+ message: error.message || error.name,
133
+ stack: error.stack
134
+ };
135
+ const maybeError = error;
136
+ if (typeof maybeError.code === "string") base.code = maybeError.code;
137
+ if (typeof maybeError.status === "number") base.status = maybeError.status;
138
+ if (typeof maybeError.stderr === "string" && maybeError.stderr.trim()) base.stderr = maybeError.stderr.trim();
139
+ if (!base.stderr && typeof maybeError.cause === "string" && maybeError.cause.trim()) base.stderr = maybeError.cause.trim();
140
+ return base;
141
+ }
142
+ if (typeof error === "string") return { message: error };
143
+ if (isRecord(error)) {
144
+ const formatted = { message: typeof error.message === "string" ? error.message : typeof error.error === "string" ? error.error : JSON.stringify(error) };
145
+ if (typeof error.code === "string") formatted.code = error.code;
146
+ if (typeof error.status === "number") formatted.status = error.status;
147
+ if (typeof error.stderr === "string" && error.stderr.trim()) formatted.stderr = error.stderr.trim();
148
+ return formatted;
149
+ }
150
+ return { message: String(error) };
151
+ }
152
+ function exitWithError(message, hint, cause) {
127
153
  logger.error(farver.bold(message));
154
+ if (cause !== void 0) {
155
+ const formatted = formatUnknownError(cause);
156
+ if (formatted.message && formatted.message !== message) console.error(farver.gray(` Cause: ${formatted.message}`));
157
+ if (formatted.code) console.error(farver.gray(` Code: ${formatted.code}`));
158
+ if (typeof formatted.status === "number") console.error(farver.gray(` Status: ${formatted.status}`));
159
+ if (formatted.stderr) {
160
+ console.error(farver.gray(" Stderr:"));
161
+ console.error(farver.gray(` ${formatted.stderr}`));
162
+ }
163
+ if (isVerbose && formatted.stack) {
164
+ console.error(farver.gray(" Stack:"));
165
+ console.error(farver.gray(` ${formatted.stack}`));
166
+ }
167
+ }
128
168
  if (hint) console.error(farver.gray(` ${hint}`));
129
169
  process.exit(1);
130
170
  }
@@ -140,6 +180,15 @@ if (isDryRun || isVerbose || isForce) {
140
180
 
141
181
  //#endregion
142
182
  //#region src/core/github.ts
183
+ function toGitHubError(operation, error) {
184
+ const formatted = formatUnknownError(error);
185
+ return {
186
+ type: "github",
187
+ operation,
188
+ message: formatted.message,
189
+ status: formatted.status
190
+ };
191
+ }
143
192
  var GitHubClient = class {
144
193
  owner;
145
194
  repo;
@@ -152,18 +201,36 @@ var GitHubClient = class {
152
201
  }
153
202
  async request(path, init = {}) {
154
203
  const url = path.startsWith("http") ? path : `${this.apiBase}${path}`;
155
- const res = await fetch(url, {
156
- ...init,
157
- headers: {
158
- ...init.headers,
159
- "Accept": "application/vnd.github.v3+json",
160
- "Authorization": `token ${this.githubToken}`,
161
- "User-Agent": "ucdjs-release-scripts (+https://github.com/ucdjs/ucdjs-release-scripts)"
162
- }
163
- });
204
+ const method = init.method ?? "GET";
205
+ let res;
206
+ try {
207
+ res = await fetch(url, {
208
+ ...init,
209
+ headers: {
210
+ ...init.headers,
211
+ "Accept": "application/vnd.github.v3+json",
212
+ "Authorization": `token ${this.githubToken}`,
213
+ "User-Agent": "ucdjs-release-scripts (+https://github.com/ucdjs/ucdjs-release-scripts)"
214
+ }
215
+ });
216
+ } catch (error) {
217
+ throw Object.assign(/* @__PURE__ */ new Error(`[${method} ${path}] GitHub request failed: ${formatUnknownError(error).message}`), { status: void 0 });
218
+ }
164
219
  if (!res.ok) {
165
220
  const errorText = await res.text();
166
- throw new Error(`GitHub API request failed with status ${res.status}: ${errorText || "No response body"}`);
221
+ const parsedMessage = (() => {
222
+ try {
223
+ const parsed = JSON.parse(errorText);
224
+ if (typeof parsed.message === "string" && parsed.message.trim()) {
225
+ if (Array.isArray(parsed.errors) && parsed.errors.length > 0) return `${parsed.message} (${JSON.stringify(parsed.errors)})`;
226
+ return parsed.message;
227
+ }
228
+ return errorText;
229
+ } catch {
230
+ return errorText;
231
+ }
232
+ })();
233
+ throw Object.assign(/* @__PURE__ */ new Error(`[${method} ${path}] GitHub API request failed (${res.status} ${res.statusText}): ${parsedMessage || "No response body"}`), { status: res.status });
167
234
  }
168
235
  if (res.status === 204) return;
169
236
  return res.json();
@@ -238,14 +305,14 @@ var GitHubClient = class {
238
305
  if (!data.items || data.items.length === 0) return info;
239
306
  info.login = data.items[0].login;
240
307
  } catch (err) {
241
- logger.warn(`Failed to resolve author info for email ${info.email}: ${err.message}`);
308
+ logger.warn(`Failed to resolve author info for email ${info.email}: ${formatUnknownError(err).message}`);
242
309
  }
243
310
  if (info.login) return info;
244
311
  if (info.commits.length > 0) try {
245
312
  const data = await this.request(`/repos/${this.owner}/${this.repo}/commits/${info.commits[0]}`);
246
313
  if (data.author && data.author.login) info.login = data.author.login;
247
314
  } catch (err) {
248
- logger.warn(`Failed to resolve author info from commits for email ${info.email}: ${err.message}`);
315
+ logger.warn(`Failed to resolve author info from commits for email ${info.email}: ${formatUnknownError(err).message}`);
249
316
  }
250
317
  return info;
251
318
  }
@@ -391,11 +458,12 @@ function err(error) {
391
458
  //#endregion
392
459
  //#region src/core/git.ts
393
460
  function toGitError(operation, error) {
461
+ const formatted = formatUnknownError(error);
394
462
  return {
395
463
  type: "git",
396
464
  operation,
397
- message: error instanceof Error ? error.message : String(error),
398
- stderr: (typeof error === "object" && error && "stderr" in error ? String(error.stderr ?? "") : void 0)?.trim() || void 0
465
+ message: formatted.message,
466
+ stderr: formatted.stderr
399
467
  };
400
468
  }
401
469
  async function isWorkingDirectoryClean(workspaceRoot) {
@@ -425,7 +493,8 @@ async function doesBranchExist(branch, workspaceRoot) {
425
493
  stdio: "pipe"
426
494
  } });
427
495
  return ok(true);
428
- } catch {
496
+ } catch (error) {
497
+ logger.verbose(`Failed to verify branch "${branch}": ${formatUnknownError(error).message}`);
429
498
  return ok(false);
430
499
  }
431
500
  }
@@ -495,8 +564,8 @@ async function checkoutBranch(branch, workspaceRoot) {
495
564
  stdio: "pipe"
496
565
  } });
497
566
  logger.verbose(`Available branches:\n${branchResult.stdout}`);
498
- } catch {
499
- logger.verbose("Could not list available branches");
567
+ } catch (error) {
568
+ logger.verbose(`Could not list available branches: ${formatUnknownError(error).message}`);
500
569
  }
501
570
  return err(gitError);
502
571
  }
@@ -539,7 +608,8 @@ async function isBranchAheadOfRemote(branch, workspaceRoot) {
539
608
  stdio: "pipe"
540
609
  } });
541
610
  return ok(Number.parseInt(result.stdout.trim(), 10) > 0);
542
- } catch {
611
+ } catch (error) {
612
+ logger.verbose(`Failed to compare branch "${branch}" with remote: ${formatUnknownError(error).message}`);
543
613
  return ok(true);
544
614
  }
545
615
  }
@@ -605,7 +675,8 @@ async function readFileFromGit(workspaceRoot, ref, filePath) {
605
675
  cwd: workspaceRoot,
606
676
  stdio: "pipe"
607
677
  } })).stdout);
608
- } catch {
678
+ } catch (error) {
679
+ logger.verbose(`Failed to read ${filePath} from ${ref}: ${formatUnknownError(error).message}`);
609
680
  return ok(null);
610
681
  }
611
682
  }
@@ -1663,11 +1734,7 @@ async function syncPullRequest(options) {
1663
1734
  } catch (error) {
1664
1735
  return {
1665
1736
  ok: false,
1666
- error: {
1667
- type: "github",
1668
- operation: "getExistingPullRequest",
1669
- message: error instanceof Error ? error.message : String(error)
1670
- }
1737
+ error: toGitHubError("getExistingPullRequest", error)
1671
1738
  };
1672
1739
  }
1673
1740
  const doesExist = !!existing;
@@ -1685,11 +1752,7 @@ async function syncPullRequest(options) {
1685
1752
  } catch (error) {
1686
1753
  return {
1687
1754
  ok: false,
1688
- error: {
1689
- type: "github",
1690
- operation: "upsertPullRequest",
1691
- message: error instanceof Error ? error.message : String(error)
1692
- }
1755
+ error: toGitHubError("upsertPullRequest", error)
1693
1756
  };
1694
1757
  }
1695
1758
  return ok({
@@ -1703,10 +1766,11 @@ async function syncPullRequest(options) {
1703
1766
  async function prepareWorkflow(options) {
1704
1767
  if (options.safeguards) {
1705
1768
  const clean = await isWorkingDirectoryClean(options.workspaceRoot);
1706
- if (!clean.ok || !clean.value) exitWithError("Working directory is not clean. Please commit or stash your changes before proceeding.");
1769
+ if (!clean.ok) exitWithError("Failed to verify working directory state.", "Ensure this is a valid git repository and try again.", clean.error);
1770
+ if (!clean.value) exitWithError("Working directory is not clean. Please commit or stash your changes before proceeding.");
1707
1771
  }
1708
1772
  const discovered = await discoverWorkspacePackages(options.workspaceRoot, options);
1709
- if (!discovered.ok) exitWithError(`Failed to discover packages: ${discovered.error.message}`);
1773
+ if (!discovered.ok) exitWithError("Failed to discover packages.", void 0, discovered.error);
1710
1774
  const ensured = ensureHasPackages(discovered.value);
1711
1775
  if (!ensured.ok) {
1712
1776
  logger.warn(ensured.error.message);
@@ -1725,15 +1789,16 @@ async function prepareWorkflow(options) {
1725
1789
  releaseBranch: options.branch.release,
1726
1790
  defaultBranch: options.branch.default
1727
1791
  });
1728
- if (!prepareBranchResult.ok) exitWithError(prepareBranchResult.error.message);
1792
+ if (!prepareBranchResult.ok) exitWithError("Failed to prepare release branch.", void 0, prepareBranchResult.error);
1729
1793
  const overridesPath = join(options.workspaceRoot, ucdjsReleaseOverridesPath);
1730
1794
  let existingOverrides = {};
1731
1795
  try {
1732
1796
  const overridesContent = await readFile(overridesPath, "utf-8");
1733
1797
  existingOverrides = JSON.parse(overridesContent);
1734
1798
  logger.info("Found existing version overrides file.");
1735
- } catch {
1799
+ } catch (error) {
1736
1800
  logger.info("No existing version overrides file found. Continuing...");
1801
+ logger.verbose(`Reading overrides file failed: ${formatUnknownError(error).message}`);
1737
1802
  }
1738
1803
  const updatesResult = await calculateUpdates({
1739
1804
  workspacePackages,
@@ -1742,7 +1807,7 @@ async function prepareWorkflow(options) {
1742
1807
  globalCommitMode: options.globalCommitMode === "none" ? false : options.globalCommitMode,
1743
1808
  overrides: existingOverrides
1744
1809
  });
1745
- if (!updatesResult.ok) exitWithError(updatesResult.error.message);
1810
+ if (!updatesResult.ok) exitWithError("Failed to calculate package updates.", void 0, updatesResult.error);
1746
1811
  const { allUpdates, applyUpdates, overrides: newOverrides } = updatesResult.value;
1747
1812
  if (Object.keys(newOverrides).length > 0) {
1748
1813
  logger.info("Writing version overrides file...");
@@ -1815,7 +1880,7 @@ async function prepareWorkflow(options) {
1815
1880
  commitMessage: "chore: update release versions",
1816
1881
  hasChanges: true
1817
1882
  });
1818
- if (!hasChangesToPush.ok) exitWithError(hasChangesToPush.error.message);
1883
+ if (!hasChangesToPush.ok) exitWithError("Failed to sync release changes.", void 0, hasChangesToPush.error);
1819
1884
  if (!hasChangesToPush.value) {
1820
1885
  const prResult = await syncPullRequest({
1821
1886
  github: options.githubClient,
@@ -1825,7 +1890,7 @@ async function prepareWorkflow(options) {
1825
1890
  pullRequestBody: options.pullRequest?.body,
1826
1891
  updates: allUpdates
1827
1892
  });
1828
- if (!prResult.ok) exitWithError(prResult.error.message);
1893
+ if (!prResult.ok) exitWithError("Failed to sync release pull request.", void 0, prResult.error);
1829
1894
  if (prResult.value.pullRequest) {
1830
1895
  logger.item("No updates needed, PR is already up to date");
1831
1896
  return {
@@ -1845,13 +1910,14 @@ async function prepareWorkflow(options) {
1845
1910
  pullRequestBody: options.pullRequest?.body,
1846
1911
  updates: allUpdates
1847
1912
  });
1848
- if (!prResult.ok) exitWithError(prResult.error.message);
1913
+ if (!prResult.ok) exitWithError("Failed to sync release pull request.", void 0, prResult.error);
1849
1914
  if (prResult.value.pullRequest?.html_url) {
1850
1915
  logger.section("🚀 Pull Request");
1851
1916
  logger.success(`Pull request ${prResult.value.created ? "created" : "updated"}: ${prResult.value.pullRequest.html_url}`);
1852
1917
  }
1853
1918
  const returnToDefault = await checkoutBranch(options.branch.default, options.workspaceRoot);
1854
- if (!returnToDefault.ok || !returnToDefault.value) exitWithError(`Failed to checkout branch: ${options.branch.default}`);
1919
+ if (!returnToDefault.ok) exitWithError(`Failed to checkout branch: ${options.branch.default}`, void 0, returnToDefault.error);
1920
+ if (!returnToDefault.value) exitWithError(`Failed to checkout branch: ${options.branch.default}`);
1855
1921
  return {
1856
1922
  updates: allUpdates,
1857
1923
  prUrl: prResult.value.pullRequest?.html_url,
@@ -1862,11 +1928,14 @@ async function prepareWorkflow(options) {
1862
1928
  //#endregion
1863
1929
  //#region src/core/npm.ts
1864
1930
  function toNPMError(operation, error, code) {
1931
+ const formatted = formatUnknownError(error);
1865
1932
  return {
1866
1933
  type: "npm",
1867
1934
  operation,
1868
- message: error instanceof Error ? error.message : String(error),
1869
- code
1935
+ message: formatted.message,
1936
+ code: code || formatted.code,
1937
+ stderr: formatted.stderr,
1938
+ status: formatted.status
1870
1939
  };
1871
1940
  }
1872
1941
  /**
@@ -1961,7 +2030,7 @@ async function publishPackage(packageName, workspaceRoot, options) {
1961
2030
  } });
1962
2031
  return ok(void 0);
1963
2032
  } catch (error) {
1964
- const errorMessage = error instanceof Error ? error.message : String(error);
2033
+ const errorMessage = formatUnknownError(error).message;
1965
2034
  return err(toNPMError("publishPackage", error, errorMessage.includes("E403") ? "E403" : errorMessage.includes("EPUBLISHCONFLICT") ? "EPUBLISHCONFLICT" : errorMessage.includes("EOTP") ? "EOTP" : void 0));
1966
2035
  }
1967
2036
  }
@@ -1971,7 +2040,7 @@ async function publishPackage(packageName, workspaceRoot, options) {
1971
2040
  async function publishWorkflow(options) {
1972
2041
  logger.section("📦 Publishing Packages");
1973
2042
  const discovered = await discoverWorkspacePackages(options.workspaceRoot, options);
1974
- if (!discovered.ok) exitWithError(`Failed to discover packages: ${discovered.error.message}`);
2043
+ if (!discovered.ok) exitWithError("Failed to discover packages.", void 0, discovered.error);
1975
2044
  const workspacePackages = discovered.value;
1976
2045
  logger.item(`Found ${workspacePackages.length} packages in workspace`);
1977
2046
  const graph = buildPackageDependencyGraph(workspacePackages);
@@ -1997,7 +2066,7 @@ async function publishWorkflow(options) {
1997
2066
  if (!existsResult.ok) {
1998
2067
  logger.error(`Failed to check version: ${existsResult.error.message}`);
1999
2068
  status.failed.push(packageName);
2000
- exitWithError(`Publishing failed for ${packageName}: ${existsResult.error.message}`, "Check your network connection and NPM registry access");
2069
+ exitWithError(`Publishing failed for ${packageName}.`, "Check your network connection and NPM registry access", existsResult.error);
2001
2070
  }
2002
2071
  if (existsResult.value) {
2003
2072
  logger.info(`Version ${farver.cyan(version)} already exists on NPM, skipping`);
@@ -2010,7 +2079,7 @@ async function publishWorkflow(options) {
2010
2079
  if (!buildResult.ok) {
2011
2080
  logger.error(`Failed to build package: ${buildResult.error.message}`);
2012
2081
  status.failed.push(packageName);
2013
- exitWithError(`Publishing failed for ${packageName}: build failed`, "Check your build scripts and dependencies");
2082
+ exitWithError(`Publishing failed for ${packageName}: build failed`, "Check your build scripts and dependencies", buildResult.error);
2014
2083
  }
2015
2084
  }
2016
2085
  logger.step(`Publishing ${farver.cyan(`${packageName}@${version}`)} to NPM...`);
@@ -2022,7 +2091,7 @@ async function publishWorkflow(options) {
2022
2091
  if (publishResult.error.code === "E403") hint = "Authentication failed. Ensure your NPM token or OIDC configuration is correct";
2023
2092
  else if (publishResult.error.code === "EPUBLISHCONFLICT") hint = "Version conflict. The version may have been published recently";
2024
2093
  else if (publishResult.error.code === "EOTP") hint = "2FA/OTP required. Provide the otp option or use OIDC authentication";
2025
- exitWithError(`Publishing failed for ${packageName}`, hint);
2094
+ exitWithError(`Publishing failed for ${packageName}`, hint, publishResult.error);
2026
2095
  }
2027
2096
  logger.success(`Published ${farver.cyan(`${packageName}@${version}`)}`);
2028
2097
  status.published.push(packageName);
@@ -2053,7 +2122,8 @@ async function publishWorkflow(options) {
2053
2122
  async function verifyWorkflow(options) {
2054
2123
  if (options.safeguards) {
2055
2124
  const clean = await isWorkingDirectoryClean(options.workspaceRoot);
2056
- if (!clean.ok || !clean.value) exitWithError("Working directory is not clean. Please commit or stash your changes before proceeding.");
2125
+ if (!clean.ok) exitWithError("Failed to verify working directory state.", "Ensure this is a valid git repository and try again.", clean.error);
2126
+ if (!clean.value) exitWithError("Working directory is not clean. Please commit or stash your changes before proceeding.");
2057
2127
  }
2058
2128
  const releaseBranch = options.branch.release;
2059
2129
  const defaultBranch = options.branch.default;
@@ -2064,10 +2134,11 @@ async function verifyWorkflow(options) {
2064
2134
  }
2065
2135
  logger.info(`Found release PR #${releasePr.number}. Verifying against default branch "${defaultBranch}"...`);
2066
2136
  const originalBranch = await getCurrentBranch(options.workspaceRoot);
2067
- if (!originalBranch.ok) exitWithError(originalBranch.error.message);
2137
+ if (!originalBranch.ok) exitWithError("Failed to detect current branch.", void 0, originalBranch.error);
2068
2138
  if (originalBranch.value !== defaultBranch) {
2069
2139
  const checkout = await checkoutBranch(defaultBranch, options.workspaceRoot);
2070
- if (!checkout.ok || !checkout.value) exitWithError(`Failed to checkout branch: ${defaultBranch}`);
2140
+ if (!checkout.ok) exitWithError(`Failed to checkout branch: ${defaultBranch}`, void 0, checkout.error);
2141
+ if (!checkout.value) exitWithError(`Failed to checkout branch: ${defaultBranch}`);
2071
2142
  }
2072
2143
  let existingOverrides = {};
2073
2144
  try {
@@ -2076,11 +2147,12 @@ async function verifyWorkflow(options) {
2076
2147
  existingOverrides = JSON.parse(overridesContent.value);
2077
2148
  logger.info("Found existing version overrides file on release branch.");
2078
2149
  }
2079
- } catch {
2150
+ } catch (error) {
2080
2151
  logger.info("No version overrides file found on release branch. Continuing...");
2152
+ logger.verbose(`Reading release overrides failed: ${formatUnknownError(error).message}`);
2081
2153
  }
2082
2154
  const discovered = await discoverWorkspacePackages(options.workspaceRoot, options);
2083
- if (!discovered.ok) exitWithError(`Failed to discover packages: ${discovered.error.message}`);
2155
+ if (!discovered.ok) exitWithError("Failed to discover packages.", void 0, discovered.error);
2084
2156
  const ensured = ensureHasPackages(discovered.value);
2085
2157
  if (!ensured.ok) {
2086
2158
  logger.warn(ensured.error.message);
@@ -2094,7 +2166,7 @@ async function verifyWorkflow(options) {
2094
2166
  globalCommitMode: options.globalCommitMode === "none" ? false : options.globalCommitMode,
2095
2167
  overrides: existingOverrides
2096
2168
  });
2097
- if (!updatesResult.ok) exitWithError(updatesResult.error.message);
2169
+ if (!updatesResult.ok) exitWithError("Failed to calculate expected package updates.", void 0, updatesResult.error);
2098
2170
  const expectedUpdates = updatesResult.value.allUpdates;
2099
2171
  const expectedVersionMap = new Map(expectedUpdates.map((u) => [u.package.name, u.newVersion]));
2100
2172
  const prVersionMap = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ucdjs/release-scripts",
3
- "version": "0.1.0-beta.41",
3
+ "version": "0.1.0-beta.42",
4
4
  "description": "@ucdjs release scripts",
5
5
  "type": "module",
6
6
  "license": "MIT",