Skip to content

Commit e3ded50

Browse files
committed
feat(cli): correct error messages in cli v2
1 parent ac0f4c3 commit e3ded50

34 files changed

Lines changed: 149 additions & 130 deletions

File tree

packages/cli/cli-v2/src/auth/errors/KeyringUnavailableError.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class KeyringUnavailableError extends CliError {
1010
constructor(platform: NodeJS.Platform, cause?: Error) {
1111
super({
1212
message: getKeyringErrorMessage(platform),
13-
code: "AUTH_ERROR"
13+
code: CliError.Code.AuthError
1414
});
1515
Object.setPrototypeOf(this, KeyringUnavailableError.prototype);
1616
this.platform = platform;

packages/cli/cli-v2/src/commands/api/check/command.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CliError, TaskAbortSignal } from "@fern-api/task-context";
1+
import { CliError } from "@fern-api/task-context";
22

33
import chalk from "chalk";
44
import type { Argv } from "yargs";
@@ -28,7 +28,7 @@ export class CheckCommand {
2828
const availableApis = Object.keys(workspace.apis).join(", ");
2929
throw new CliError({
3030
message: `API '${args.api}' not found. Available APIs: ${availableApis}`,
31-
code: "CONFIG_ERROR"
31+
code: CliError.Code.ConfigError
3232
});
3333
}
3434

@@ -45,7 +45,7 @@ export class CheckCommand {
4545
const response = this.buildJsonResponse({ apiCheckResult: result, hasErrors });
4646
context.stdout.info(JSON.stringify(response, null, 2));
4747
if (hasErrors) {
48-
throw new TaskAbortSignal();
48+
throw CliError.validationError();
4949
}
5050
return;
5151
}
@@ -58,7 +58,7 @@ export class CheckCommand {
5858
}
5959

6060
if (hasErrors) {
61-
throw new TaskAbortSignal();
61+
throw CliError.validationError();
6262
}
6363

6464
if (result.warningCount > 0) {

packages/cli/cli-v2/src/commands/api/compile/command.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class CompileCommand {
6969
const available = apiNames.join(", ");
7070
throw new CliError({
7171
message: `API '${args.api}' not found. Available APIs: ${available}`,
72-
code: "CONFIG_ERROR"
72+
code: CliError.Code.ConfigError
7373
});
7474
}
7575
return { apiName: args.api, definition };
@@ -80,14 +80,14 @@ export class CompileCommand {
8080
if (apiName == null) {
8181
throw new CliError({
8282
message: "Internal error; no APIs found in workspace",
83-
code: "INTERNAL_ERROR"
83+
code: CliError.Code.InternalError
8484
});
8585
}
8686
const definition = workspace.apis[apiName];
8787
if (definition == null) {
8888
throw new CliError({
8989
message: `Internal error; API '${apiName}' not found in workspace`,
90-
code: "INTERNAL_ERROR"
90+
code: CliError.Code.InternalError
9191
});
9292
}
9393
return { apiName, definition };
@@ -96,7 +96,7 @@ export class CompileCommand {
9696
const available = apiNames.join(", ");
9797
throw new CliError({
9898
message: `Multiple APIs found: ${available}. Use --api to select one.`,
99-
code: "CONFIG_ERROR"
99+
code: CliError.Code.ConfigError
100100
});
101101
}
102102

packages/cli/cli-v2/src/commands/api/merge/command.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class MergeCommand {
2828
const workspace = await context.loadWorkspaceOrThrow();
2929

3030
if (Object.keys(workspace.apis).length === 0) {
31-
throw new CliError({ message: "No APIs found in workspace.", code: "CONFIG_ERROR" });
31+
throw new CliError({ message: "No APIs found in workspace.", code: CliError.Code.ConfigError });
3232
}
3333

3434
const entries = filterSpecs(workspace, { api: args.api });
@@ -44,7 +44,7 @@ export class MergeCommand {
4444
if (fernYmlPath == null) {
4545
throw new CliError({
4646
message: `No ${FERN_YML_FILENAME} found. Run 'fern init' to initialize a project.`,
47-
code: "CONFIG_ERROR"
47+
code: CliError.Code.ConfigError
4848
});
4949
}
5050
editor = await FernYmlEditor.load({ fernYmlPath });

packages/cli/cli-v2/src/commands/api/split/command.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class SplitCommand {
4646
const workspace = await context.loadWorkspaceOrThrow();
4747

4848
if (Object.keys(workspace.apis).length === 0) {
49-
throw new CliError({ message: "No APIs found in workspace.", code: "CONFIG_ERROR" });
49+
throw new CliError({ message: "No APIs found in workspace.", code: CliError.Code.ConfigError });
5050
}
5151

5252
const entries = filterSpecs(workspace, { api: args.api });
@@ -61,7 +61,7 @@ export class SplitCommand {
6161
if (fernYmlPath == null) {
6262
throw new CliError({
6363
message: `No ${FERN_YML_FILENAME} found. Run 'fern init' to initialize a project.`,
64-
code: "CONFIG_ERROR"
64+
code: CliError.Code.ConfigError
6565
});
6666
}
6767
const editor = await FernYmlEditor.load({ fernYmlPath });
@@ -242,7 +242,7 @@ export class SplitCommand {
242242
const detail = extractErrorMessage(error);
243243
throw new CliError({
244244
message: `Failed to get file from git HEAD: ${absolutePath}. Is the file tracked by git and has at least one commit?\n Cause: ${detail}`,
245-
code: "PARSE_ERROR"
245+
code: CliError.Code.ParseError
246246
});
247247
}
248248
}
@@ -261,7 +261,7 @@ function resolvePathOrThrow(context: Context, outputPath: string): AbsoluteFileP
261261
if (resolved == null) {
262262
throw new CliError({
263263
message: `Could not resolve output path: ${outputPath}`,
264-
code: "CONFIG_ERROR"
264+
code: CliError.Code.ConfigError
265265
});
266266
}
267267
return resolved;

packages/cli/cli-v2/src/commands/api/utils/loadSpec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ export async function loadSpec(filepath: AbsoluteFilePath): Promise<Spec> {
1818
contents = await readFile(filepath, "utf8");
1919
} catch (error) {
2020
if (isEnoentError(error)) {
21-
throw new CliError({ message: `File does not exist: ${filepath}`, code: "CONFIG_ERROR" });
21+
throw new CliError({ message: `File does not exist: ${filepath}`, code: CliError.Code.ConfigError });
2222
}
23-
throw new CliError({ message: `Failed to read file: ${filepath}`, code: "PARSE_ERROR" });
23+
throw new CliError({ message: `Failed to read file: ${filepath}`, code: CliError.Code.ParseError });
2424
}
2525
return parseSpec(contents, filepath);
2626
}
@@ -38,7 +38,7 @@ export function parseSpec(contents: string, filepath: string): Spec {
3838
} catch {
3939
throw new CliError({
4040
message: `Failed to parse file as JSON or YAML: ${filepath}`,
41-
code: "PARSE_ERROR"
41+
code: CliError.Code.ParseError
4242
});
4343
}
4444
}

packages/cli/cli-v2/src/commands/auth/login/command.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { verifyAndDecodeJwt } from "@fern-api/auth";
22
import { LogLevel } from "@fern-api/logger";
33
import { type Auth0TokenResponse, getTokenFromAuth0 } from "@fern-api/login";
4-
import { TaskAbortSignal } from "@fern-api/task-context";
4+
import { CliError } from "@fern-api/task-context";
55
import chalk from "chalk";
66
import type { Argv } from "yargs";
77
import { TaskContextAdapter } from "../../../context/adapter/TaskContextAdapter.js";
@@ -41,13 +41,13 @@ export class LoginCommand {
4141
const payload = await verifyAndDecodeJwt(idToken);
4242
if (payload == null) {
4343
context.stdout.error(`${Icons.error} Internal error; could not verify ID token`);
44-
throw new TaskAbortSignal();
44+
throw CliError.internalError();
4545
}
4646

4747
const email = payload.email;
4848
if (email == null) {
4949
context.stdout.error(`${Icons.error} Internal error; ID token does not contain email claim`);
50-
throw new TaskAbortSignal();
50+
throw CliError.internalError();
5151
}
5252

5353
const { isNew, totalAccounts } = await context.tokenService.login(email, accessToken);

packages/cli/cli-v2/src/commands/auth/logout/command.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TaskAbortSignal } from "@fern-api/task-context";
1+
import { CliError } from "@fern-api/task-context";
22
import chalk from "chalk";
33
import inquirer from "inquirer";
44
import type { Argv } from "yargs";
@@ -64,7 +64,7 @@ export class LogoutCommand {
6464

6565
if (!context.isTTY) {
6666
context.stdout.error(`${Icons.error} Use --force to skip confirmation in non-interactive mode`);
67-
throw new TaskAbortSignal();
67+
throw new CliError({ code: CliError.Code.ValidationError });
6868
}
6969

7070
const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([
@@ -90,7 +90,9 @@ export class LogoutCommand {
9090

9191
if (!removed) {
9292
context.stdout.error(`${Icons.error} Account not found: ${user}`);
93-
throw new TaskAbortSignal();
93+
throw new CliError({
94+
code: CliError.Code.EnvironmentError
95+
});
9496
}
9597

9698
context.stdout.info(`${Icons.success} Logged out of ${chalk.bold(user)}`);
@@ -105,7 +107,9 @@ export class LogoutCommand {
105107
context.stdout.error(
106108
`${Icons.error} Multiple accounts found. Use --user or --all in non-interactive mode.`
107109
);
108-
throw new TaskAbortSignal();
110+
throw new CliError({
111+
code: CliError.Code.EnvironmentError
112+
});
109113
}
110114

111115
const choices = accounts.map((account) => ({

packages/cli/cli-v2/src/commands/auth/switch/command.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TaskAbortSignal } from "@fern-api/task-context";
1+
import { CliError } from "@fern-api/task-context";
22
import chalk from "chalk";
33
import inquirer from "inquirer";
44
import type { Argv } from "yargs";
@@ -20,14 +20,16 @@ export class SwitchCommand {
2020
context.stdout.warn(`${chalk.yellow("⚠")} You are not logged in to Fern.`);
2121
context.stdout.info("");
2222
context.stdout.info(chalk.dim(" To log in, run: fern auth login"));
23-
throw new TaskAbortSignal();
23+
throw new CliError({
24+
code: CliError.Code.ConfigError
25+
});
2426
}
2527

2628
if (accounts.length === 1) {
2729
const account = accounts[0];
2830
if (account == null) {
2931
context.stdout.error(`${Icons.error} Internal error; no accounts found`);
30-
throw new TaskAbortSignal();
32+
throw CliError.internalError();
3133
}
3234

3335
context.stdout.warn(
@@ -59,7 +61,9 @@ export class SwitchCommand {
5961
): Promise<void> {
6062
if (!context.isTTY) {
6163
context.stdout.error(`${Icons.error} Use --user to specify account in non-interactive mode`);
62-
throw new TaskAbortSignal();
64+
throw new CliError({
65+
code: CliError.Code.EnvironmentError
66+
});
6367
}
6468

6569
const choices = accounts.map((account) => ({
@@ -84,7 +88,9 @@ export class SwitchCommand {
8488
const success = await context.tokenService.switchAccount(user);
8589
if (!success) {
8690
context.stdout.error(`${Icons.error} Account not found: ${user}`);
87-
throw new TaskAbortSignal();
91+
throw new CliError({
92+
code: CliError.Code.EnvironmentError
93+
});
8894
}
8995
context.stdout.info(`${Icons.success} Switched to ${chalk.bold(user)}`);
9096
}

packages/cli/cli-v2/src/commands/auth/token/command.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createOrganizationIfDoesNotExist } from "@fern-api/auth";
22
import { createVenusService } from "@fern-api/core";
3-
import { TaskAbortSignal } from "@fern-api/task-context";
3+
import { CliError } from "@fern-api/task-context";
44
import type { Argv } from "yargs";
55
import { TaskContextAdapter } from "../../../context/adapter/TaskContextAdapter.js";
66
import type { Context } from "../../../context/Context.js";
@@ -41,24 +41,32 @@ export class TokenCommand {
4141
response.error._visit({
4242
organizationNotFoundError: () => {
4343
process.stderr.write(`${Icons.error} Organization "${orgId}" was not found.\n`);
44-
throw new TaskAbortSignal();
44+
throw new CliError({
45+
code: CliError.Code.ConfigError
46+
});
4547
},
4648
unauthorizedError: () => {
4749
process.stderr.write(`${Icons.error} You do not have access to organization "${orgId}".\n`);
48-
throw new TaskAbortSignal();
50+
throw new CliError({
51+
code: CliError.Code.AuthError
52+
});
4953
},
5054
missingOrgPermissionsError: () => {
5155
process.stderr.write(
5256
`${Icons.error} You do not have the required permissions in organization "${orgId}".\n`
5357
);
54-
throw new TaskAbortSignal();
58+
throw new CliError({
59+
code: CliError.Code.AuthError
60+
});
5561
},
5662
_other: () => {
5763
process.stderr.write(
5864
`${Icons.error} Failed to generate token.\n` +
5965
`\n Please contact support@buildwithfern.com for assistance.\n`
6066
);
61-
throw new TaskAbortSignal();
67+
throw new CliError({
68+
code: CliError.Code.InternalError
69+
});
6270
}
6371
});
6472
}
@@ -75,7 +83,7 @@ export class TokenCommand {
7583
`${Icons.error} No organization specified.\n` +
7684
`\n Run fern init or specify an organization with --org, then run this command again.\n`
7785
);
78-
throw new TaskAbortSignal();
86+
throw new CliError({ code: CliError.Code.ConfigError });
7987
}
8088
}
8189
}

0 commit comments

Comments
 (0)