@zeroheight/adoption-cli 2.2.1 → 2.2.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Release notes
2
2
 
3
+ ## [2.2.3](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.2.3) - 28th November 2024
4
+
5
+ - Update README to explain new CLI options
6
+
7
+ ## [2.2.2](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.2.2) - 26th November 2024
8
+
9
+ - Add global `--log-level` and `--log-file` options
10
+
3
11
  ## [2.2.1](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.2.1) - 13th November 2024
4
12
 
5
13
  - Handle API throttling
package/README.md CHANGED
@@ -35,6 +35,19 @@ export ZEROHEIGHT_CLIENT_ID="your-client-id"
35
35
  export ZEROHEIGHT_ACCESS_TOKEN="your-access-token"
36
36
  ```
37
37
 
38
+ #### Error logging
39
+
40
+ When running a command with the `--log-level <level> --log-file ./my-log-file.log` flags, diagnostic information will be written to a file. This can help diagnose issues and can be optionally shared with zeroheight directly to help resolve. If you don't specify `--log-file`, logs will be output to `stderr`.
41
+
42
+ There are 4 levels of increasing severity:
43
+
44
+ 1. `info`
45
+ 2. `warn`
46
+ 3. `error`
47
+ 4. `fatal`
48
+
49
+ **Note**: You will see logs for the specified level and those from the higher severity groups. For example, if you you choose `--log-level error` then the `error` and `fatal` level logs will be surfaced.
50
+
38
51
  ---
39
52
 
40
53
  ### Component usage analysis
@@ -81,6 +94,22 @@ Pass in `false` to disable the interactive mode e.g. when running in a CI enviro
81
94
  zh-adoption analyze --interactive false -r "My Repo"
82
95
  ```
83
96
 
97
+ `--log-level`
98
+
99
+ Provide a severity to output diagnostic messages.
100
+
101
+ ```bash
102
+ zh-adoption analyze --log-level info
103
+ ```
104
+
105
+ Write logs to a file so they can read and/or shared later.
106
+
107
+ `--log-file`
108
+
109
+ ```bash
110
+ zh-adoption analyze --log-file ./dev.log
111
+ ```
112
+
84
113
  ---
85
114
 
86
115
  ### Monitor repo
@@ -105,6 +134,22 @@ Provide a path to the specific folder to find package.json and lock file. This o
105
134
  zh-adoption monitor-repo -d ./webApp
106
135
  ```
107
136
 
137
+ `--log-level`
138
+
139
+ Provide a severity to output diagnostic messages.
140
+
141
+ ```bash
142
+ zh-adoption analyze --log-level info
143
+ ```
144
+
145
+ Write logs to a file so they can read and/or shared later.
146
+
147
+ `--log-file`
148
+
149
+ ```bash
150
+ zh-adoption analyze --log-file ./dev.log
151
+ ```
152
+
108
153
  ---
109
154
 
110
155
  ### Track package
@@ -129,6 +174,22 @@ Pass in `false` to disable the interactive mode e.g. when running in a CI enviro
129
174
  zh-adoption track-package --interactive false
130
175
  ```
131
176
 
177
+ `--log-level`
178
+
179
+ Provide a severity to output diagnostic messages.
180
+
181
+ ```bash
182
+ zh-adoption analyze --log-level info
183
+ ```
184
+
185
+ Write logs to a file so they can read and/or shared later.
186
+
187
+ `--log-file`
188
+
189
+ ```bash
190
+ zh-adoption analyze --log-file ./dev.log
191
+ ```
192
+
132
193
  ---
133
194
 
134
195
  More info on the commands can be seen by running
@@ -1,4 +1,5 @@
1
1
  import * as walk from "acorn-walk";
2
+ import logger from "../common/logging.js";
2
3
  const DYNAMIC_EXPRESSIONS = [
3
4
  "Identifier",
4
5
  "TemplateLiteral",
@@ -39,7 +40,10 @@ function transformVisitorStateToRawUsage(visitorState) {
39
40
  const componentProps = currentValue.props;
40
41
  props.forEach((prop) => {
41
42
  componentProps[prop.name] = {
42
- type: Array.from(new Set([...(componentProps[prop.name]?.type ?? []), ...(prop.type ?? [])])),
43
+ type: Array.from(new Set([
44
+ ...(componentProps[prop.name]?.type ?? []),
45
+ ...(prop.type ?? []),
46
+ ])),
43
47
  values: [...(componentProps[prop.name]?.values ?? []), ...prop.values],
44
48
  };
45
49
  });
@@ -133,21 +137,24 @@ export function analyze(ast) {
133
137
  values: [RUNTIME_VALUE],
134
138
  });
135
139
  }
136
- else if (expressionType && expressionType === 'ObjectExpression') {
140
+ else if (expressionType &&
141
+ expressionType === "ObjectExpression") {
137
142
  attrProps.push({
138
143
  name: attr.name.name,
139
144
  type: [expressionType],
140
145
  values: [OBJECT_VALUE],
141
146
  });
142
147
  }
143
- else if (expressionType && expressionType === 'ArrayExpression') {
148
+ else if (expressionType &&
149
+ expressionType === "ArrayExpression") {
144
150
  attrProps.push({
145
151
  name: attr.name.name,
146
152
  type: [expressionType],
147
153
  values: [ARRAY_VALUE],
148
154
  });
149
155
  }
150
- else if (expressionType && expressionType === 'BinaryExpression') {
156
+ else if (expressionType &&
157
+ expressionType === "BinaryExpression") {
151
158
  attrProps.push({
152
159
  name: attr.name.name,
153
160
  type: [expressionType],
@@ -158,7 +165,9 @@ export function analyze(ast) {
158
165
  attrProps.push({
159
166
  name: attr.name.name,
160
167
  type: [expressionType],
161
- values: [attr.value.expression.value || attr.value.expression.name],
168
+ values: [
169
+ attr.value.expression.value || attr.value.expression.name,
170
+ ],
162
171
  });
163
172
  }
164
173
  }
@@ -230,8 +239,7 @@ export function analyze(ast) {
230
239
  walk.recursive(ast, visitorState, visitorFunctions, walk.base);
231
240
  }
232
241
  catch (e) {
233
- // We don't want to log the error to the users - maybe could add some actually logging in the future
234
- // console.log("error", e);
242
+ logger.error({ error: e, ast }, "Error analyzing file");
235
243
  }
236
244
  finally {
237
245
  return transformVisitorStateToRawUsage(visitorState);
@@ -7,4 +7,7 @@
7
7
  * @param sourceFilename a filename to help with inference
8
8
  * @returns Parsed AST
9
9
  */
10
- export declare function parse(code: string, sourceFilename?: string): any;
10
+ export declare function parse(code: string, sourceFilename?: string): {
11
+ ast: any;
12
+ errors: string[];
13
+ };
@@ -10,7 +10,5 @@ import oxc from "oxc-parser";
10
10
  */
11
11
  export function parse(code, sourceFilename) {
12
12
  const result = oxc.parseSync(code, { sourceFilename, sourceType: "module" });
13
- // TODO: Surface these errors to help in debugging
14
- // console.error(result.errors)
15
- return JSON.parse(result.program);
13
+ return { ast: JSON.parse(result.program), errors: result.errors };
16
14
  }
package/dist/cli.js CHANGED
@@ -1,23 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  import * as React from "react";
3
- import { Command } from "commander";
3
+ import { Command, Option } from "commander";
4
4
  import { render } from "ink-render-string";
5
5
  import HelpInfo from "./components/help-info.js";
6
6
  import { analyzeCommand } from "./commands/analyze.js";
7
7
  import { authCommand } from "./commands/auth.js";
8
8
  import { monitorRepoCommand } from "./commands/monitor-repo.js";
9
9
  import { trackPackageCommand } from "./commands/track-package.js";
10
+ import logger, { setFileStream } from "./common/logging.js";
10
11
  const program = new Command();
11
12
  const { output, cleanup } = render(React.createElement(HelpInfo, null));
12
13
  program
13
14
  .name("zh-adoption")
14
15
  .description("CLI for measuring design system usage usage in your products")
15
- .version("2.2.1")
16
+ .version("2.2.3")
16
17
  .addHelpText("before", output)
18
+ .option("--log-file <path>", "Path to write logs to, if not provided logs only error logs will be written to stderr")
19
+ .addOption(new Option("--log-level <level>", "The lowest level of logs to display")
20
+ .default("error")
21
+ .choices(["fatal", "error", "warn", "info"]))
17
22
  .addCommand(analyzeCommand())
18
23
  .addCommand(authCommand())
19
24
  .addCommand(monitorRepoCommand())
20
- .addCommand(trackPackageCommand());
25
+ .addCommand(trackPackageCommand())
26
+ .hook("preAction", (thisCommand, actionCommand) => {
27
+ logger.level = thisCommand.opts()["logLevel"];
28
+ if (thisCommand.opts()["logFile"]) {
29
+ setFileStream(thisCommand.opts()["logFile"]);
30
+ }
31
+ logger.info({
32
+ command: {
33
+ name: actionCommand.name(),
34
+ opts: actionCommand.opts(),
35
+ globalOpts: thisCommand.opts(),
36
+ },
37
+ }, "Running command");
38
+ });
21
39
  cleanup();
22
40
  // Only start parsing if run as CLI, don't start parsing during testing
23
41
  if (process.env["NODE_ENV"] !== "test") {
@@ -5,6 +5,7 @@ import yn from "yn";
5
5
  import { analyzeFiles } from "./analyze.utils.js";
6
6
  import Analyze from "../components/analyze/analyze.js";
7
7
  import NonInteractiveAnalyze from "../components/analyze/non-interactive-analyze.js";
8
+ import logger, { setStdErrStream } from "../common/logging.js";
8
9
  export async function analyzeAction(options, renderOptions) {
9
10
  if (options.interactive) {
10
11
  render(React.createElement(Analyze, { dryRun: options.dryRun, onAnalyzeFiles: () => analyzeFiles(options.extensions, options.ignore), repoName: options.repoName }), renderOptions);
@@ -18,6 +19,9 @@ export function analyzeCommand() {
18
19
  return command
19
20
  .command("analyze")
20
21
  .description("Analyze your codebase to determine component usage metrics")
22
+ .configureHelp({
23
+ showGlobalOptions: true,
24
+ })
21
25
  .addOption(new Option("-e, --extensions [ext]", "file extensions to include when searching for components").default("**/*.{js,jsx,ts,tsx}", "glob pattern to determine file extensions"))
22
26
  .addOption(new Option("-i, --ignore [ext]", "files to ignore when searching for components").default("**/*.{test,spec}.*", "glob pattern to determine ignored files"))
23
27
  .addOption(new Option("-d, --dry-run", "don't push results to zeroheight").default(false))
@@ -26,11 +30,14 @@ export function analyzeCommand() {
26
30
  .default(true)
27
31
  .argParser((value) => yn(value)))
28
32
  .action(async (options) => {
33
+ if (!options.interactive) {
34
+ setStdErrStream();
35
+ }
29
36
  try {
30
37
  await analyzeAction(options);
31
38
  }
32
39
  catch (e) {
33
- console.error(e);
40
+ logger.error({ error: e }, "Unhandled exception running analyze command");
34
41
  process.exitCode = 1;
35
42
  }
36
43
  });
@@ -5,6 +5,7 @@ import { Glob } from "glob";
5
5
  import ignore from "ignore";
6
6
  import { parse } from "../ast/parser.js";
7
7
  import { analyze } from "../ast/analyze.js";
8
+ import logger from "../common/logging.js";
8
9
  /**
9
10
  * Get a list of files matching extensions, skips hidden files and node_modules
10
11
  * @param base starting directory
@@ -56,14 +57,20 @@ export async function analyzeFiles(extensions, ignorePattern) {
56
57
  for (const file of files) {
57
58
  try {
58
59
  const fileContents = await readFile(file, "utf-8");
59
- const ast = parse(fileContents, file);
60
+ const { ast, errors } = parse(fileContents, file);
61
+ if (errors.length > 0) {
62
+ logger.error({ file, errors }, "Error parsing file");
63
+ parseErrors.push(`Can't parse file ${file}`);
64
+ continue;
65
+ }
60
66
  const usage = analyze(ast);
61
67
  if (usage.length > 0) {
62
68
  const relativePath = file.slice(process.cwd().length);
63
69
  usageMap.set(relativePath, usage);
64
70
  }
65
71
  }
66
- catch {
72
+ catch (e) {
73
+ logger.error({ file, error: e }, "Error parsing file");
67
74
  parseErrors.push(`Can't parse file ${file}`);
68
75
  }
69
76
  }
@@ -10,6 +10,9 @@ export function authCommand() {
10
10
  return command
11
11
  .command("auth")
12
12
  .description("Authenticate with zeroheight")
13
+ .configureHelp({
14
+ showGlobalOptions: true,
15
+ })
13
16
  .addHelpText("before", "Set credentials for performing actions with zeroheight. Credentials will default to ZEROHEIGHT_CLIENT_ID and ZEROHEIGHT_ACCESS_TOKEN environment variables if not supplied")
14
17
  .option("-c, --client <client_id>", "zeroheight Client ID", process.env["ZEROHEIGHT_CLIENT_ID"])
15
18
  .option("-t, --token <access_token>", "zeroheight Access Token", process.env["ZEROHEIGHT_ACCESS_TOKEN"])
@@ -3,6 +3,7 @@ import { Command, Option } from "commander";
3
3
  import { render } from "ink";
4
4
  // import yn from "yn";
5
5
  import NonInteractiveMonitorRepo from "../components/monitor-repo/non-interactive-monitor-repo.js";
6
+ import { setStdErrStream } from "../common/logging.js";
6
7
  export async function monitorRepoAction(options, _renderOptions) {
7
8
  // if (options.interactive) {
8
9
  // render(<MonitorRepo />, renderOptions);
@@ -15,6 +16,9 @@ export function monitorRepoCommand() {
15
16
  return (command
16
17
  .command("monitor-repo")
17
18
  .description("Monitor package usage in a repository")
19
+ .configureHelp({
20
+ showGlobalOptions: true,
21
+ })
18
22
  // .addOption(
19
23
  // new Option(
20
24
  // "-in, --interactive [boolean]",
@@ -25,6 +29,7 @@ export function monitorRepoCommand() {
25
29
  // )
26
30
  .addOption(new Option("-d, --dir <path...>", "use package directory to find package.json and lockfile"))
27
31
  .action(async (options) => {
32
+ setStdErrStream();
28
33
  try {
29
34
  await monitorRepoAction(options);
30
35
  }
@@ -4,6 +4,7 @@ import { render } from "ink";
4
4
  import yn from "yn";
5
5
  import NonInteractiveTrackPackage from "../components/track-package/non-interactive-track-package.js";
6
6
  import TrackPackage from "../components/track-package/track-package.js";
7
+ import { setStdErrStream } from "../common/logging.js";
7
8
  export async function trackPackageAction(options, renderOptions) {
8
9
  if (options.interactive) {
9
10
  render(React.createElement(TrackPackage, null), renderOptions);
@@ -17,10 +18,16 @@ export function trackPackageCommand() {
17
18
  return command
18
19
  .command("track-package")
19
20
  .description("Track the latest version of your package in zeroheight")
21
+ .configureHelp({
22
+ showGlobalOptions: true,
23
+ })
20
24
  .addOption(new Option("-in, --interactive [boolean]", "disable to skip manual input (useful when running in CI)")
21
25
  .default(true)
22
26
  .argParser((value) => yn(value)))
23
27
  .action(async (options) => {
28
+ if (!options.interactive) {
29
+ setStdErrStream();
30
+ }
24
31
  try {
25
32
  await trackPackageAction(options);
26
33
  }
@@ -1,3 +1,5 @@
1
+ import { MaxRetriesError, UnauthorizedError, UnknownError } from "./errors.js";
2
+ import logger from "./logging.js";
1
3
  const API_PATH = "/open_api/v2";
2
4
  export var ResponseStatus;
3
5
  (function (ResponseStatus) {
@@ -64,6 +66,7 @@ async function request(path, credentials, init) {
64
66
  const maxRetries = 3;
65
67
  let retries = 0;
66
68
  while (retries < maxRetries) {
69
+ logger.info({ request: { url: url.toString(), init, retries } }, "Making API request");
67
70
  const response = await fetch(url, {
68
71
  ...init,
69
72
  headers: {
@@ -74,18 +77,26 @@ async function request(path, credentials, init) {
74
77
  },
75
78
  });
76
79
  if (response.status === 401) {
77
- throw new Error("Unauthorized");
80
+ logger.error({ response: { status: response.status, body: await response.text() } }, "Unauthorized response from API");
81
+ throw new UnauthorizedError();
78
82
  }
79
83
  else if (response.status === 429) {
80
84
  retries++;
81
85
  const responseData = await response.json();
82
86
  const waitTime = responseData.data.reset_time * 1000 - Date.now();
87
+ logger.info(`Rate limited, waiting for ${waitTime}ms`);
83
88
  await sleep(waitTime);
84
89
  continue;
85
90
  }
86
- return await response.json();
91
+ else if (response.status < 200 || response.status >= 300) {
92
+ logger.error({ response: { status: response.status, body: await response.text() } }, "API request failed");
93
+ throw new UnknownError();
94
+ }
95
+ const responseJson = await response.json();
96
+ logger.info({ response: { status: response.status, body: responseJson } }, "API response");
97
+ return responseJson;
87
98
  }
88
- throw new Error(`Request failed after ${maxRetries} retries`);
99
+ throw new MaxRetriesError();
89
100
  }
90
101
  export function mergeUsageProps(newProps, currentProps) {
91
102
  if (!currentProps)
@@ -0,0 +1,12 @@
1
+ export declare class ApiError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class UnauthorizedError extends ApiError {
5
+ constructor();
6
+ }
7
+ export declare class UnknownError extends ApiError {
8
+ constructor();
9
+ }
10
+ export declare class MaxRetriesError extends ApiError {
11
+ constructor();
12
+ }
@@ -0,0 +1,21 @@
1
+ export class ApiError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "ApiError";
5
+ }
6
+ }
7
+ export class UnauthorizedError extends ApiError {
8
+ constructor() {
9
+ super("Unauthorized. Please reset your authentication by running: npx @zeroheight/adoption-cli auth");
10
+ }
11
+ }
12
+ export class UnknownError extends ApiError {
13
+ constructor() {
14
+ super("An unknown error occurred while calling the zeroheight API");
15
+ }
16
+ }
17
+ export class MaxRetriesError extends ApiError {
18
+ constructor() {
19
+ super("Max retries exceeded while calling the zeroheight API");
20
+ }
21
+ }
@@ -0,0 +1,4 @@
1
+ declare const logger: import("pino").Logger<never, boolean>;
2
+ export declare function setFileStream(path: string): void;
3
+ export declare function setStdErrStream(): void;
4
+ export default logger;
@@ -0,0 +1,26 @@
1
+ import { pino } from "pino";
2
+ import { createWriteStream } from "fs";
3
+ import pretty from "pino-pretty";
4
+ const streams = [];
5
+ const loggerOptions = {
6
+ base: null,
7
+ level: "warn",
8
+ timestamp: pino.stdTimeFunctions.isoTime,
9
+ formatters: {
10
+ level: (label) => {
11
+ return { level: label.toUpperCase() };
12
+ },
13
+ },
14
+ };
15
+ const multistream = pino.multistream(streams);
16
+ const logger = pino(loggerOptions, multistream);
17
+ export function setFileStream(path) {
18
+ multistream.add({ stream: createWriteStream(path, { flags: "a" }) });
19
+ }
20
+ export function setStdErrStream() {
21
+ multistream.add({
22
+ stream: pretty({ destination: process.stderr, ignore: "pid,hostname" }),
23
+ level: "error",
24
+ });
25
+ }
26
+ export default logger;
@@ -10,6 +10,7 @@ import UsageTable from "../usage-table.js";
10
10
  import { configPath, writeConfig, readConfig, } from "../../common/config.js";
11
11
  import { ResponseStatus, getAuthDetails, getZeroheightURL, submitUsageData, } from "../../common/api.js";
12
12
  import { calculateNumberOfComponents } from "../../commands/analyze.utils.js";
13
+ import { ApiError } from "../../common/errors.js";
13
14
  export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
14
15
  const { exit } = useApp();
15
16
  const [usageResult, setUsageResult] = React.useState(null);
@@ -61,9 +62,10 @@ export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
61
62
  setResourceURL(resourceURL);
62
63
  }
63
64
  catch (e) {
64
- const errorMessage = e.message === "Unauthorized"
65
- ? "Unauthorized. Please reset your authentication by running: zh-adoption auth"
66
- : "Failed to send data to zeroheight";
65
+ let errorMessage = "Failed to send data to zeroheight";
66
+ if (e instanceof ApiError) {
67
+ errorMessage = e.message;
68
+ }
67
69
  setErrorList((s) => [...s, errorMessage]);
68
70
  }
69
71
  finally {
@@ -5,6 +5,7 @@ import Link from "ink-link";
5
5
  import { calculateNumberOfComponents } from "../../commands/analyze.utils.js";
6
6
  import { readConfig } from "../../common/config.js";
7
7
  import { submitUsageData } from "../../common/api.js";
8
+ import { ApiError } from "../../common/errors.js";
8
9
  export default function NonInteractiveAnalyze({ repoName, onAnalyzeFiles, }) {
9
10
  const [isSendingData, setIsSendingData] = React.useState(false);
10
11
  const [isAnalyzingFiles, setIsAnalyzingFiles] = React.useState(false);
@@ -36,9 +37,10 @@ export default function NonInteractiveAnalyze({ repoName, onAnalyzeFiles, }) {
36
37
  await submitUsageData(result, repoName, credentials);
37
38
  }
38
39
  catch (e) {
39
- const errorMessage = e.message === "Unauthorized"
40
- ? "Unauthorized. Please reset your authentication by running: zh-adoption auth"
41
- : "Failed to send data to zeroheight";
40
+ let errorMessage = "Failed to send data to zeroheight";
41
+ if (e instanceof ApiError) {
42
+ errorMessage = e.message;
43
+ }
42
44
  setErrorList((s) => [...s, errorMessage]);
43
45
  }
44
46
  finally {
@@ -7,6 +7,7 @@ import { findPackageFiles } from "../../commands/track-package.utils.js";
7
7
  import { ResponseStatus, submitMonitoredRepoDetails, } from "../../common/api.js";
8
8
  import { readConfig } from "../../common/config.js";
9
9
  import YarnLockParser from "../../lockfile-parsers/yarn-lock-parser.js";
10
+ import { ApiError } from "../../common/errors.js";
10
11
  var Step;
11
12
  (function (Step) {
12
13
  Step[Step["COMPLETE"] = 0] = "COMPLETE";
@@ -82,7 +83,11 @@ export default function NonInteractiveMonitorRepo({ packageDirs, }) {
82
83
  }
83
84
  }
84
85
  catch (e) {
85
- updateRepoStatus(packageMeta.name, Step.ERRORED, "Failed to send data to zeroheight");
86
+ let errorMessage = "Failed to send data to zeroheight";
87
+ if (e instanceof ApiError) {
88
+ errorMessage = e.message;
89
+ }
90
+ updateRepoStatus(packageMeta.name, Step.ERRORED, errorMessage);
86
91
  }
87
92
  }
88
93
  }
@@ -4,6 +4,7 @@ import Spinner from "ink-spinner";
4
4
  import { readConfig } from "../../common/config.js";
5
5
  import { getAliasesFromExports, getPackageInfo, } from "../../commands/track-package.utils.js";
6
6
  import { ResponseStatus, submitPackageDetails } from "../../common/api.js";
7
+ import { ApiError } from "../../common/errors.js";
7
8
  var Step;
8
9
  (function (Step) {
9
10
  Step[Step["COMPLETE"] = 0] = "COMPLETE";
@@ -63,9 +64,10 @@ export default function NonInteractiveTrackPackage() {
63
64
  exit();
64
65
  }
65
66
  catch (e) {
66
- const errorMessage = e.message === "Unauthorized"
67
- ? "Unauthorized. Please reset your authentication by running: zh-adoption auth"
68
- : "Failed to send data to zeroheight";
67
+ let errorMessage = "Failed to send data to zeroheight";
68
+ if (e instanceof ApiError) {
69
+ errorMessage = e.message;
70
+ }
69
71
  setErrorMessage(errorMessage);
70
72
  setCurrentStep(Step.ERRORED);
71
73
  exit();
@@ -7,6 +7,7 @@ import ConfirmInput from "../ui/confirm-input.js";
7
7
  import { readConfig } from "../../common/config.js";
8
8
  import { getAliasesFromExports, getPackageInfo, } from "../../commands/track-package.utils.js";
9
9
  import { submitPackageDetails } from "../../common/api.js";
10
+ import { ApiError } from "../../common/errors.js";
10
11
  var Step;
11
12
  (function (Step) {
12
13
  Step[Step["COMPLETE"] = 0] = "COMPLETE";
@@ -61,9 +62,10 @@ export default function TrackPackage() {
61
62
  }
62
63
  }
63
64
  catch (e) {
64
- const errorMessage = e.message === "Unauthorized"
65
- ? "Unauthorized. Please reset your authentication by running: zh-adoption auth"
66
- : "Failed to send data to zeroheight";
65
+ let errorMessage = "Failed to send data to zeroheight";
66
+ if (e instanceof ApiError) {
67
+ errorMessage = e.message;
68
+ }
67
69
  setErrorMessage(errorMessage);
68
70
  setCurrentStep(Step.ERRORED);
69
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroheight/adoption-cli",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "license": "ISC",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -39,6 +39,8 @@
39
39
  "ink-spinner": "^5.0.0",
40
40
  "ink-text-input": "^6.0.0",
41
41
  "oxc-parser": "^0.22.0",
42
+ "pino": "^9.5.0",
43
+ "pino-pretty": "^13.0.0",
42
44
  "react": "^18.2.0",
43
45
  "yaml": "^2.5.1",
44
46
  "yn": "^5.0.0"