@ucdjs/release-scripts 0.1.0-beta.40 → 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 +128 -53
  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
  }
@@ -562,7 +632,10 @@ async function commitChanges(message, workspaceRoot) {
562
632
  } });
563
633
  return ok(true);
564
634
  } catch (error) {
565
- return err(toGitError("commitChanges", error));
635
+ const gitError = toGitError("commitChanges", error);
636
+ logger.error(`Git commit failed: ${gitError.message}`);
637
+ if (gitError.stderr) logger.error(`Git stderr: ${gitError.stderr}`);
638
+ return err(gitError);
566
639
  }
567
640
  }
568
641
  async function pushBranch(branch, workspaceRoot, options) {
@@ -602,7 +675,8 @@ async function readFileFromGit(workspaceRoot, ref, filePath) {
602
675
  cwd: workspaceRoot,
603
676
  stdio: "pipe"
604
677
  } })).stdout);
605
- } catch {
678
+ } catch (error) {
679
+ logger.verbose(`Failed to read ${filePath} from ${ref}: ${formatUnknownError(error).message}`);
606
680
  return ok(null);
607
681
  }
608
682
  }
@@ -1660,11 +1734,7 @@ async function syncPullRequest(options) {
1660
1734
  } catch (error) {
1661
1735
  return {
1662
1736
  ok: false,
1663
- error: {
1664
- type: "github",
1665
- operation: "getExistingPullRequest",
1666
- message: error instanceof Error ? error.message : String(error)
1667
- }
1737
+ error: toGitHubError("getExistingPullRequest", error)
1668
1738
  };
1669
1739
  }
1670
1740
  const doesExist = !!existing;
@@ -1682,11 +1752,7 @@ async function syncPullRequest(options) {
1682
1752
  } catch (error) {
1683
1753
  return {
1684
1754
  ok: false,
1685
- error: {
1686
- type: "github",
1687
- operation: "upsertPullRequest",
1688
- message: error instanceof Error ? error.message : String(error)
1689
- }
1755
+ error: toGitHubError("upsertPullRequest", error)
1690
1756
  };
1691
1757
  }
1692
1758
  return ok({
@@ -1700,10 +1766,11 @@ async function syncPullRequest(options) {
1700
1766
  async function prepareWorkflow(options) {
1701
1767
  if (options.safeguards) {
1702
1768
  const clean = await isWorkingDirectoryClean(options.workspaceRoot);
1703
- 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.");
1704
1771
  }
1705
1772
  const discovered = await discoverWorkspacePackages(options.workspaceRoot, options);
1706
- if (!discovered.ok) exitWithError(`Failed to discover packages: ${discovered.error.message}`);
1773
+ if (!discovered.ok) exitWithError("Failed to discover packages.", void 0, discovered.error);
1707
1774
  const ensured = ensureHasPackages(discovered.value);
1708
1775
  if (!ensured.ok) {
1709
1776
  logger.warn(ensured.error.message);
@@ -1722,15 +1789,16 @@ async function prepareWorkflow(options) {
1722
1789
  releaseBranch: options.branch.release,
1723
1790
  defaultBranch: options.branch.default
1724
1791
  });
1725
- if (!prepareBranchResult.ok) exitWithError(prepareBranchResult.error.message);
1792
+ if (!prepareBranchResult.ok) exitWithError("Failed to prepare release branch.", void 0, prepareBranchResult.error);
1726
1793
  const overridesPath = join(options.workspaceRoot, ucdjsReleaseOverridesPath);
1727
1794
  let existingOverrides = {};
1728
1795
  try {
1729
1796
  const overridesContent = await readFile(overridesPath, "utf-8");
1730
1797
  existingOverrides = JSON.parse(overridesContent);
1731
1798
  logger.info("Found existing version overrides file.");
1732
- } catch {
1799
+ } catch (error) {
1733
1800
  logger.info("No existing version overrides file found. Continuing...");
1801
+ logger.verbose(`Reading overrides file failed: ${formatUnknownError(error).message}`);
1734
1802
  }
1735
1803
  const updatesResult = await calculateUpdates({
1736
1804
  workspacePackages,
@@ -1739,7 +1807,7 @@ async function prepareWorkflow(options) {
1739
1807
  globalCommitMode: options.globalCommitMode === "none" ? false : options.globalCommitMode,
1740
1808
  overrides: existingOverrides
1741
1809
  });
1742
- if (!updatesResult.ok) exitWithError(updatesResult.error.message);
1810
+ if (!updatesResult.ok) exitWithError("Failed to calculate package updates.", void 0, updatesResult.error);
1743
1811
  const { allUpdates, applyUpdates, overrides: newOverrides } = updatesResult.value;
1744
1812
  if (Object.keys(newOverrides).length > 0) {
1745
1813
  logger.info("Writing version overrides file...");
@@ -1812,7 +1880,7 @@ async function prepareWorkflow(options) {
1812
1880
  commitMessage: "chore: update release versions",
1813
1881
  hasChanges: true
1814
1882
  });
1815
- if (!hasChangesToPush.ok) exitWithError(hasChangesToPush.error.message);
1883
+ if (!hasChangesToPush.ok) exitWithError("Failed to sync release changes.", void 0, hasChangesToPush.error);
1816
1884
  if (!hasChangesToPush.value) {
1817
1885
  const prResult = await syncPullRequest({
1818
1886
  github: options.githubClient,
@@ -1822,7 +1890,7 @@ async function prepareWorkflow(options) {
1822
1890
  pullRequestBody: options.pullRequest?.body,
1823
1891
  updates: allUpdates
1824
1892
  });
1825
- if (!prResult.ok) exitWithError(prResult.error.message);
1893
+ if (!prResult.ok) exitWithError("Failed to sync release pull request.", void 0, prResult.error);
1826
1894
  if (prResult.value.pullRequest) {
1827
1895
  logger.item("No updates needed, PR is already up to date");
1828
1896
  return {
@@ -1842,13 +1910,14 @@ async function prepareWorkflow(options) {
1842
1910
  pullRequestBody: options.pullRequest?.body,
1843
1911
  updates: allUpdates
1844
1912
  });
1845
- if (!prResult.ok) exitWithError(prResult.error.message);
1913
+ if (!prResult.ok) exitWithError("Failed to sync release pull request.", void 0, prResult.error);
1846
1914
  if (prResult.value.pullRequest?.html_url) {
1847
1915
  logger.section("🚀 Pull Request");
1848
1916
  logger.success(`Pull request ${prResult.value.created ? "created" : "updated"}: ${prResult.value.pullRequest.html_url}`);
1849
1917
  }
1850
1918
  const returnToDefault = await checkoutBranch(options.branch.default, options.workspaceRoot);
1851
- 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}`);
1852
1921
  return {
1853
1922
  updates: allUpdates,
1854
1923
  prUrl: prResult.value.pullRequest?.html_url,
@@ -1859,11 +1928,14 @@ async function prepareWorkflow(options) {
1859
1928
  //#endregion
1860
1929
  //#region src/core/npm.ts
1861
1930
  function toNPMError(operation, error, code) {
1931
+ const formatted = formatUnknownError(error);
1862
1932
  return {
1863
1933
  type: "npm",
1864
1934
  operation,
1865
- message: error instanceof Error ? error.message : String(error),
1866
- code
1935
+ message: formatted.message,
1936
+ code: code || formatted.code,
1937
+ stderr: formatted.stderr,
1938
+ status: formatted.status
1867
1939
  };
1868
1940
  }
1869
1941
  /**
@@ -1958,7 +2030,7 @@ async function publishPackage(packageName, workspaceRoot, options) {
1958
2030
  } });
1959
2031
  return ok(void 0);
1960
2032
  } catch (error) {
1961
- const errorMessage = error instanceof Error ? error.message : String(error);
2033
+ const errorMessage = formatUnknownError(error).message;
1962
2034
  return err(toNPMError("publishPackage", error, errorMessage.includes("E403") ? "E403" : errorMessage.includes("EPUBLISHCONFLICT") ? "EPUBLISHCONFLICT" : errorMessage.includes("EOTP") ? "EOTP" : void 0));
1963
2035
  }
1964
2036
  }
@@ -1968,7 +2040,7 @@ async function publishPackage(packageName, workspaceRoot, options) {
1968
2040
  async function publishWorkflow(options) {
1969
2041
  logger.section("📦 Publishing Packages");
1970
2042
  const discovered = await discoverWorkspacePackages(options.workspaceRoot, options);
1971
- if (!discovered.ok) exitWithError(`Failed to discover packages: ${discovered.error.message}`);
2043
+ if (!discovered.ok) exitWithError("Failed to discover packages.", void 0, discovered.error);
1972
2044
  const workspacePackages = discovered.value;
1973
2045
  logger.item(`Found ${workspacePackages.length} packages in workspace`);
1974
2046
  const graph = buildPackageDependencyGraph(workspacePackages);
@@ -1994,7 +2066,7 @@ async function publishWorkflow(options) {
1994
2066
  if (!existsResult.ok) {
1995
2067
  logger.error(`Failed to check version: ${existsResult.error.message}`);
1996
2068
  status.failed.push(packageName);
1997
- 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);
1998
2070
  }
1999
2071
  if (existsResult.value) {
2000
2072
  logger.info(`Version ${farver.cyan(version)} already exists on NPM, skipping`);
@@ -2007,7 +2079,7 @@ async function publishWorkflow(options) {
2007
2079
  if (!buildResult.ok) {
2008
2080
  logger.error(`Failed to build package: ${buildResult.error.message}`);
2009
2081
  status.failed.push(packageName);
2010
- 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);
2011
2083
  }
2012
2084
  }
2013
2085
  logger.step(`Publishing ${farver.cyan(`${packageName}@${version}`)} to NPM...`);
@@ -2019,7 +2091,7 @@ async function publishWorkflow(options) {
2019
2091
  if (publishResult.error.code === "E403") hint = "Authentication failed. Ensure your NPM token or OIDC configuration is correct";
2020
2092
  else if (publishResult.error.code === "EPUBLISHCONFLICT") hint = "Version conflict. The version may have been published recently";
2021
2093
  else if (publishResult.error.code === "EOTP") hint = "2FA/OTP required. Provide the otp option or use OIDC authentication";
2022
- exitWithError(`Publishing failed for ${packageName}`, hint);
2094
+ exitWithError(`Publishing failed for ${packageName}`, hint, publishResult.error);
2023
2095
  }
2024
2096
  logger.success(`Published ${farver.cyan(`${packageName}@${version}`)}`);
2025
2097
  status.published.push(packageName);
@@ -2050,7 +2122,8 @@ async function publishWorkflow(options) {
2050
2122
  async function verifyWorkflow(options) {
2051
2123
  if (options.safeguards) {
2052
2124
  const clean = await isWorkingDirectoryClean(options.workspaceRoot);
2053
- 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.");
2054
2127
  }
2055
2128
  const releaseBranch = options.branch.release;
2056
2129
  const defaultBranch = options.branch.default;
@@ -2061,10 +2134,11 @@ async function verifyWorkflow(options) {
2061
2134
  }
2062
2135
  logger.info(`Found release PR #${releasePr.number}. Verifying against default branch "${defaultBranch}"...`);
2063
2136
  const originalBranch = await getCurrentBranch(options.workspaceRoot);
2064
- if (!originalBranch.ok) exitWithError(originalBranch.error.message);
2137
+ if (!originalBranch.ok) exitWithError("Failed to detect current branch.", void 0, originalBranch.error);
2065
2138
  if (originalBranch.value !== defaultBranch) {
2066
2139
  const checkout = await checkoutBranch(defaultBranch, options.workspaceRoot);
2067
- 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}`);
2068
2142
  }
2069
2143
  let existingOverrides = {};
2070
2144
  try {
@@ -2073,11 +2147,12 @@ async function verifyWorkflow(options) {
2073
2147
  existingOverrides = JSON.parse(overridesContent.value);
2074
2148
  logger.info("Found existing version overrides file on release branch.");
2075
2149
  }
2076
- } catch {
2150
+ } catch (error) {
2077
2151
  logger.info("No version overrides file found on release branch. Continuing...");
2152
+ logger.verbose(`Reading release overrides failed: ${formatUnknownError(error).message}`);
2078
2153
  }
2079
2154
  const discovered = await discoverWorkspacePackages(options.workspaceRoot, options);
2080
- if (!discovered.ok) exitWithError(`Failed to discover packages: ${discovered.error.message}`);
2155
+ if (!discovered.ok) exitWithError("Failed to discover packages.", void 0, discovered.error);
2081
2156
  const ensured = ensureHasPackages(discovered.value);
2082
2157
  if (!ensured.ok) {
2083
2158
  logger.warn(ensured.error.message);
@@ -2091,7 +2166,7 @@ async function verifyWorkflow(options) {
2091
2166
  globalCommitMode: options.globalCommitMode === "none" ? false : options.globalCommitMode,
2092
2167
  overrides: existingOverrides
2093
2168
  });
2094
- if (!updatesResult.ok) exitWithError(updatesResult.error.message);
2169
+ if (!updatesResult.ok) exitWithError("Failed to calculate expected package updates.", void 0, updatesResult.error);
2095
2170
  const expectedUpdates = updatesResult.value.allUpdates;
2096
2171
  const expectedVersionMap = new Map(expectedUpdates.map((u) => [u.package.name, u.newVersion]));
2097
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.40",
3
+ "version": "0.1.0-beta.42",
4
4
  "description": "@ucdjs release scripts",
5
5
  "type": "module",
6
6
  "license": "MIT",