Skip to content

Commit 6d84398

Browse files
committed
chore(cli): wrap third-party errors (get-port, latest-version, inquirer, stream-json) in CliError
- getLatestVersionOfCli: catch PackageNotFoundError/SyntaxError from latest-version, rethrow as CliError(NETWORK_ERROR) - CliContext.getInput: catch ExitPromptError from @inquirer/prompts, convert to TaskAbortSignal (matches confirmPrompt behavior) - cli.ts docs dev: catch SystemError from get-port, rethrow as CliError(ENVIRONMENT_ERROR) - diff.ts readIr: catch stream-json parser errors from streamObjectFromFile, report as CliError(PARSE_ERROR) Made-with: Cursor
1 parent 2de3fdd commit 6d84398

51 files changed

Lines changed: 291 additions & 235 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/cli/cli-v2/src/context/adapter/TaskContextAdapter.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createLogger, LOG_LEVELS, Logger, LogLevel } from "@fern-api/logger";
22
import {
3-
type CliErrorCode,
3+
type CliError.Code,
44
type CreateInteractiveTaskParams,
55
type Finishable,
66
type InteractiveTaskContext,
@@ -55,12 +55,12 @@ export class TaskContextAdapter implements TaskContext {
5555
await run();
5656
}
5757

58-
public failAndThrow(message?: string, error?: unknown, options?: { code?: CliErrorCode }): never {
58+
public failAndThrow(message?: string, error?: unknown, options?: { code?: CliError.Code }): never {
5959
this.failWithoutThrowing(message, error, options);
6060
throw new TaskAbortSignal();
6161
}
6262

63-
public failWithoutThrowing(message?: string, error?: unknown, options?: { code?: CliErrorCode }): void {
63+
public failWithoutThrowing(message?: string, error?: unknown, options?: { code?: CliError.Code }): void {
6464
this.result = TaskResult.Failure;
6565
if (error instanceof TaskAbortSignal) {
6666
return;
@@ -73,7 +73,7 @@ export class TaskContextAdapter implements TaskContext {
7373
reportError(this.context, error, { ...options, message });
7474
}
7575

76-
public captureException(error: unknown, code?: CliErrorCode): void {
76+
public captureException(error: unknown, code?: CliError.Code): void {
7777
const errorCode = resolveErrorCode(error, code) ?? "INTERNAL_ERROR";
7878
this.context.telemetry.captureException(error, { errorCode });
7979
}

packages/cli/cli-v2/src/context/withContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { LogLevel } from "@fern-api/logger";
22
import {
33
CliError,
4-
type CliErrorCode,
4+
type CliError.Code,
55
resolveErrorCode,
66
shouldReportToSentry,
77
TaskAbortSignal
@@ -117,7 +117,7 @@ function handleError(context: Context, error: unknown): void {
117117
export function reportError(
118118
context: Context,
119119
error: unknown,
120-
options?: { message?: string; code?: CliErrorCode }
120+
options?: { message?: string; code?: CliError.Code }
121121
): void {
122122
if (error instanceof TaskAbortSignal) {
123123
return;

packages/cli/cli/src/cli-context/CliContext.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Project } from "@fern-api/project-loader";
55
import { isVersionAhead } from "@fern-api/semver-utils";
66
import {
77
CliError,
8-
type CliErrorCode,
8+
type CliError.Code,
99
Finishable,
1010
PosthogEvent,
1111
resolveErrorCode,
@@ -120,12 +120,12 @@ export class CliContext {
120120
);
121121
}
122122

123-
public failAndThrow(message?: string, error?: unknown, options?: { code?: CliErrorCode }): never {
123+
public failAndThrow(message?: string, error?: unknown, options?: { code?: CliError.Code }): never {
124124
this.failWithoutThrowing(message, error, options);
125125
throw new TaskAbortSignal();
126126
}
127127

128-
public failWithoutThrowing(message?: string, error?: unknown, options?: { code?: CliErrorCode }): void {
128+
public failWithoutThrowing(message?: string, error?: unknown, options?: { code?: CliError.Code }): void {
129129
this.didSucceed = false;
130130
if (error instanceof TaskAbortSignal) {
131131
// We already tracked the true error, so we can just return.
@@ -286,7 +286,7 @@ export class CliContext {
286286
}
287287
}
288288

289-
public async captureException(error: unknown, code?: CliErrorCode): Promise<void> {
289+
public async captureException(error: unknown, code?: CliError.Code): Promise<void> {
290290
await this.sentryClient.captureException(error, code);
291291
}
292292

@@ -458,7 +458,15 @@ export class CliContext {
458458
* @returns Promise<string> representing the user's input
459459
*/
460460
public async getInput(config: { message: string; default?: string }): Promise<string> {
461-
return await input({ message: config.message, default: config.default });
461+
try {
462+
return await input({ message: config.message, default: config.default });
463+
} catch (error) {
464+
if ((error as Error)?.name === "ExitPromptError") {
465+
this.logger.info("\nCancelled by user.");
466+
throw new TaskAbortSignal();
467+
}
468+
throw error;
469+
}
462470
}
463471
}
464472

packages/cli/cli/src/cli-context/StdoutRedirector.ts

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

33
/**
44
* Redirects process.stdout to process.stderr.
@@ -24,7 +24,7 @@ export class StdoutRedirector {
2424
if (this.redirected) {
2525
throw new CliError({
2626
message: "StdoutRedirector: already redirected — did you forget to restore()?",
27-
code: "INTERNAL_ERROR"
27+
code: CliError.Code.InternalError
2828
});
2929
}
3030
this.originalWrite = process.stdout.write;

packages/cli/cli/src/cli-context/TaskContextImpl.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { addPrefixToString } from "@fern-api/core-utils";
33
import { createLogger, LogLevel } from "@fern-api/logger";
44
import {
55
CliError,
6-
type CliErrorCode,
6+
type CliError.Code,
77
CreateInteractiveTaskParams,
88
Finishable,
99
InteractiveTaskContext,
@@ -29,7 +29,7 @@ export declare namespace TaskContextImpl {
2929
onResult?: (result: TaskResult) => void;
3030
shouldBufferLogs: boolean;
3131
instrumentPostHogEvent: (event: PosthogEvent) => void;
32-
captureException?: (error: unknown, code?: CliErrorCode) => void;
32+
captureException?: (error: unknown, code?: CliError.Code) => void;
3333
}
3434
}
3535

@@ -43,7 +43,7 @@ export class TaskContextImpl implements Startable<TaskContext>, Finishable, Task
4343
protected status: "notStarted" | "running" | "finished" = "notStarted";
4444
private onResult: ((result: TaskResult) => void) | undefined;
4545
private instrumentPostHogEventImpl: (event: PosthogEvent) => void;
46-
private captureExceptionImpl?: (error: unknown, code?: CliErrorCode) => void;
46+
private captureExceptionImpl?: (error: unknown, code?: CliError.Code) => void;
4747
public constructor({
4848
logImmediately,
4949
logPrefix,
@@ -83,13 +83,13 @@ export class TaskContextImpl implements Startable<TaskContext>, Finishable, Task
8383

8484
public takeOverTerminal: (run: () => void | Promise<void>) => Promise<void>;
8585

86-
public failAndThrow(message?: string, error?: unknown, options?: { code?: CliErrorCode }): never {
86+
public failAndThrow(message?: string, error?: unknown, options?: { code?: CliError.Code }): never {
8787
this.failWithoutThrowing(message, error, options);
8888
this.finish();
8989
throw new TaskAbortSignal();
9090
}
9191

92-
public failWithoutThrowing(message?: string, error?: unknown, options?: { code?: CliErrorCode }): void {
92+
public failWithoutThrowing(message?: string, error?: unknown, options?: { code?: CliError.Code }): void {
9393
this.result = TaskResult.Failure;
9494

9595
if (error instanceof TaskAbortSignal) {
@@ -116,7 +116,7 @@ export class TaskContextImpl implements Startable<TaskContext>, Finishable, Task
116116
}
117117
}
118118

119-
public captureException(error: unknown, code?: CliErrorCode): void {
119+
public captureException(error: unknown, code?: CliError.Code): void {
120120
this.captureExceptionImpl?.(error, code);
121121
}
122122

packages/cli/cli/src/cli-context/upgrade-utils/getFernUpgradeMessage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CliError } from "@fern-api/task-context";
1+
import { CliError} from "@fern-api/task-context";
22
import { FernRegistryClient } from "@fern-fern/generators-sdk";
33
import boxen from "boxen";
44
import chalk from "chalk";
@@ -127,7 +127,7 @@ async function normalizeGeneratorName(generatorImage: string): Promise<string> {
127127
});
128128
const generatorResponse = await client.generators.getGeneratorByImage({ dockerImage: generatorImage });
129129
if (!generatorResponse.ok || generatorResponse.body == null) {
130-
throw new CliError({ message: `Generator ${generatorImage} not found`, code: "INTERNAL_ERROR" });
130+
throw new CliError({ message: `Generator ${generatorImage} not found`, code: CliError.Code.InternalError });
131131
}
132132
return generatorResponse.body.displayName;
133133
}

packages/cli/cli/src/cli-context/upgrade-utils/getLatestVersionOfCli.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CliError} from "@fern-api/task-context";
12
import latestVersion from "latest-version";
23

34
import { CliEnvironment } from "../CliEnvironment.js";
@@ -14,7 +15,14 @@ export async function getLatestVersionOfCli({
1415
if (cliEnvironment.packageName !== "fern-api" || cliEnvironment.packageVersion === "0.0.0") {
1516
return cliEnvironment.packageVersion;
1617
}
17-
return latestVersion(cliEnvironment.packageName, {
18-
version: includePreReleases ? "prerelease" : "latest"
19-
});
18+
try {
19+
return await latestVersion(cliEnvironment.packageName, {
20+
version: includePreReleases ? "prerelease" : "latest"
21+
});
22+
} catch (error) {
23+
throw new CliError({
24+
message: `Failed to resolve latest CLI version: ${error instanceof Error ? error.message : String(error)}`,
25+
code: CliError.Code.NetworkError
26+
});
27+
}
2028
}

0 commit comments

Comments
 (0)