Skip to content

Commit 32b33b9

Browse files
committed
refactor(cli): rename FernCliError to TaskAbortSignal
Made-with: Cursor
1 parent d39fd2d commit 32b33b9

17 files changed

Lines changed: 62 additions & 60 deletions

File tree

packages/cli/cli-logger/src/logErrorMessage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Logger, LogLevel } from "@fern-api/logger";
2-
import { FernCliError } from "@fern-api/task-context";
2+
import { TaskAbortSignal } from "@fern-api/task-context";
33
import chalk from "chalk";
44

55
export function logErrorMessage({
@@ -20,7 +20,7 @@ export function logErrorMessage({
2020
}
2121

2222
// thrower is responsible for logging, so we don't need to log the error's message too
23-
if (error instanceof FernCliError) {
23+
if (error instanceof TaskAbortSignal) {
2424
return;
2525
}
2626

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
Startable,
88
TaskContext
99
} from "@fern-api/task-context";
10-
import { FernCliError, TaskResult } from "@fern-api/task-context";
10+
import { TaskAbortSignal, TaskResult } from "@fern-api/task-context";
1111
import type { Task } from "../../ui/Task.js";
1212
import type { Context } from "../Context.js";
1313
import { TaskContextLogger } from "./TaskContextLogger.js";
@@ -50,7 +50,7 @@ export class TaskContextAdapter implements TaskContext {
5050

5151
public failAndThrow(message?: string, error?: unknown): never {
5252
this.failWithoutThrowing(message, error);
53-
throw new FernCliError();
53+
throw new TaskAbortSignal();
5454
}
5555

5656
public failWithoutThrowing(message?: string, error?: unknown): void {

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

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { LogLevel } from "@fern-api/logger";
2-
import { FernCliError } from "@fern-api/task-context";
2+
import { TaskAbortSignal } from "@fern-api/task-context";
33
import chalk from "chalk";
44
import { KeyringUnavailableError } from "../auth/errors/KeyringUnavailableError.js";
55
import { CliError } from "../errors/CliError.js";
@@ -90,9 +90,7 @@ function handleError(context: Context, error: unknown): void {
9090
return;
9191
}
9292

93-
if (error instanceof FernCliError) {
94-
// FernCliError is thrown by failAndThrow() after logging the error
95-
// message via the TaskContext logger. No additional output needed.
93+
if (error instanceof TaskAbortSignal) {
9694
return;
9795
}
9896

@@ -119,24 +117,18 @@ function handleError(context: Context, error: unknown): void {
119117
*
120118
* Only unexpected/internal errors are reported. User-facing errors
121119
* (validation, auth, CLI usage) are not bugs and should not be tracked.
122-
*
123-
* TODO: FernCliError is currently excluded because it loses context --
124-
* it's a blank marker error thrown by failAndThrow() after logging.
125-
* Many FernCliError instances originate from shared packages and represent
126-
* server-side failures (e.g. API registration, protobuf upload) that
127-
* *should* be reported. A refactoring is needed to make FernCliError
128-
* carry its original cause/code so we can distinguish reportable
129-
* server failures from user config errors.
130120
*/
131121
function shouldReportToSentry(error: unknown): boolean {
122+
if (error instanceof TaskAbortSignal) {
123+
return false;
124+
}
132125
if (error instanceof CliError) {
133126
return error.code === "INTERNAL_ERROR";
134127
}
135128
if (
136129
error instanceof ValidationError ||
137130
error instanceof SourcedValidationError ||
138-
error instanceof KeyringUnavailableError ||
139-
error instanceof FernCliError
131+
error instanceof KeyringUnavailableError
140132
) {
141133
return false;
142134
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createLogger, LOG_LEVELS, LogLevel } from "@fern-api/logger";
33
import { getPosthogManager } from "@fern-api/posthog-manager";
44
import { Project } from "@fern-api/project-loader";
55
import { isVersionAhead } from "@fern-api/semver-utils";
6-
import { FernCliError, Finishable, PosthogEvent, Startable, TaskContext, TaskResult } from "@fern-api/task-context";
6+
import { Finishable, PosthogEvent, Startable, TaskAbortSignal, TaskContext, TaskResult } from "@fern-api/task-context";
77
import { Workspace } from "@fern-api/workspace-loader";
88
import { input, select } from "@inquirer/prompts";
99
import chalk from "chalk";
@@ -95,7 +95,7 @@ export class CliContext {
9595

9696
public failAndThrow(message?: string, error?: unknown): never {
9797
this.failWithoutThrowing(message, error);
98-
throw new FernCliError();
98+
throw new TaskAbortSignal();
9999
}
100100

101101
public failWithoutThrowing(message?: string, error?: unknown): void {
@@ -223,13 +223,17 @@ export class CliContext {
223223
try {
224224
result = await run(context);
225225
} catch (error) {
226+
if (error instanceof TaskAbortSignal) {
227+
// thrower is responsible for logging, so we generally don't need to log here.
228+
throw error;
229+
}
226230
if ((error as Error).message.includes("globalThis")) {
227231
context.logger.error(this.USE_NODE_18_OR_ABOVE_MESSAGE);
228232
context.failWithoutThrowing();
229233
} else {
230234
context.failWithoutThrowing(undefined, error);
231235
}
232-
throw new FernCliError();
236+
throw new TaskAbortSignal();
233237
} finally {
234238
context.finish();
235239
}
@@ -398,7 +402,7 @@ export class CliContext {
398402
// User pressed Ctrl+C
399403
if ((error as Error)?.name === "ExitPromptError") {
400404
this.logger.info("\nCancelled by user.");
401-
throw new FernCliError();
405+
throw new TaskAbortSignal();
402406
}
403407
throw error;
404408
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { addPrefixToString } from "@fern-api/core-utils";
33
import { createLogger, LogLevel } from "@fern-api/logger";
44
import {
55
CreateInteractiveTaskParams,
6-
FernCliError,
76
Finishable,
87
InteractiveTaskContext,
98
PosthogEvent,
109
Startable,
10+
TaskAbortSignal,
1111
TaskContext,
1212
TaskResult
1313
} from "@fern-api/task-context";
@@ -77,7 +77,7 @@ export class TaskContextImpl implements Startable<TaskContext>, Finishable, Task
7777
public failAndThrow(message?: string, error?: unknown): never {
7878
this.failWithoutThrowing(message, error);
7979
this.finish();
80-
throw new FernCliError();
80+
throw new TaskAbortSignal();
8181
}
8282

8383
public failWithoutThrowing(message?: string, error?: unknown): void {

packages/cli/cli/src/cli.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {
3434
import { LOG_LEVELS, LogLevel } from "@fern-api/logger";
3535
import { askToLogin, login, logout } from "@fern-api/login";
3636
import { protocGenFern } from "@fern-api/protoc-gen-fern";
37-
import { FernCliError, LoggableFernCliError } from "@fern-api/task-context";
37+
import { LoggableFernCliError, TaskAbortSignal } from "@fern-api/task-context";
3838
import getPort from "get-port";
3939
import { Argv } from "yargs";
4040
import { hideBin } from "yargs/helpers";
@@ -142,12 +142,12 @@ async function runCli() {
142142
error
143143
}
144144
});
145-
if ((error as Error)?.message.includes("globalThis")) {
146-
cliContext.logger.error(USE_NODE_18_OR_ABOVE_MESSAGE);
147-
cliContext.failWithoutThrowing();
148-
} else if (error instanceof FernCliError) {
145+
if (error instanceof TaskAbortSignal) {
149146
// thrower is responsible for logging, so we generally don't need to log here.
150147
cliContext.failWithoutThrowing();
148+
} else if ((error as Error)?.message.includes("globalThis")) {
149+
cliContext.logger.error(USE_NODE_18_OR_ABOVE_MESSAGE);
150+
cliContext.failWithoutThrowing();
151151
} else if (error instanceof LoggableFernCliError) {
152152
cliContext.logger.error(`Failed. ${error.log}`);
153153
} else {

packages/cli/cli/src/commands/diff/diff.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { diffSemverOrThrow } from "@fern-api/core-utils";
22
import { AbsoluteFilePath, cwd, doesPathExist, resolve, streamObjectFromFile } from "@fern-api/fs-utils";
33
import { IntermediateRepresentation, serialization } from "@fern-api/ir-sdk";
44
import { IntermediateRepresentationChangeDetector } from "@fern-api/ir-utils";
5-
import { FernCliError } from "@fern-api/task-context";
5+
import { TaskAbortSignal } from "@fern-api/task-context";
66
import semver from "semver";
77

88
import { CliContext } from "../../cli-context/CliContext.js";
@@ -57,7 +57,7 @@ export async function diff({
5757
const nextVersion = semver.inc(fromVersion, bump);
5858
if (!nextVersion) {
5959
context.failWithoutThrowing(`Invalid current version: ${fromVersion}`);
60-
throw new FernCliError();
60+
throw new TaskAbortSignal();
6161
}
6262
return { bump, nextVersion, errors };
6363
}
@@ -74,13 +74,13 @@ async function readIr({
7474
const absoluteFilepath = AbsoluteFilePath.of(resolve(cwd(), filepath));
7575
if (!(await doesPathExist(absoluteFilepath, "file"))) {
7676
context.failWithoutThrowing(`File not found: ${absoluteFilepath}`);
77-
throw new FernCliError();
77+
throw new TaskAbortSignal();
7878
}
7979
const ir = await streamObjectFromFile(absoluteFilepath);
8080
const parsed = serialization.IntermediateRepresentation.parse(ir);
8181
if (!parsed.ok) {
8282
context.failWithoutThrowing(`Invalid --${flagName}; expected a filepath containing a valid IR`);
83-
throw new FernCliError();
83+
throw new TaskAbortSignal();
8484
}
8585
return parsed.value;
8686
}
@@ -162,7 +162,7 @@ export function diffGeneratorVersions(
162162
};
163163
} catch (error) {
164164
context.failWithoutThrowing(`Error diffing generator versions ${from} and ${to}: ${error}`);
165-
throw new FernCliError();
165+
throw new TaskAbortSignal();
166166
}
167167
}
168168

packages/cli/cli/src/commands/sdk-diff/sdkDiffCommand.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
maxVersionBump
2222
} from "@fern-api/local-workspace-runner";
2323
import { Project } from "@fern-api/project-loader";
24-
import { FernCliError, TaskContext } from "@fern-api/task-context";
24+
import { TaskAbortSignal, TaskContext } from "@fern-api/task-context";
2525
import { exec } from "child_process";
2626
import { promisify } from "util";
2727
import { CliContext } from "../../cli-context/CliContext.js";
@@ -82,12 +82,12 @@ export async function sdkDiffCommand({
8282
// Validate that both directories exist
8383
if (!(await doesPathExist(fromPath, "directory"))) {
8484
context.failWithoutThrowing(`Directory not found: ${fromPath}`);
85-
throw new FernCliError();
85+
throw new TaskAbortSignal();
8686
}
8787

8888
if (!(await doesPathExist(toPath, "directory"))) {
8989
context.failWithoutThrowing(`Directory not found: ${toPath}`);
90-
throw new FernCliError();
90+
throw new TaskAbortSignal();
9191
}
9292

9393
const clientRegistry = await getClientRegistry(context, project);
@@ -234,7 +234,7 @@ export async function sdkDiffCommand({
234234
: "") +
235235
`Error: ${errorMessage}`
236236
);
237-
throw new FernCliError();
237+
throw new TaskAbortSignal();
238238
}
239239
}
240240

packages/cli/configuration-loader/src/docs-yml/__test__/collapsibleNavigationConfig.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { docsYml } from "@fern-api/configuration";
22
import { validateAgainstJsonSchema } from "@fern-api/core-utils";
33
import { AbsoluteFilePath } from "@fern-api/fs-utils";
4-
import { createMockTaskContext, FernCliError } from "@fern-api/task-context";
4+
import { createMockTaskContext, TaskAbortSignal } from "@fern-api/task-context";
55
import fs from "fs";
66
import os from "os";
77
import path from "path";
@@ -38,7 +38,7 @@ describe("docs.yml navigation collapsible config", () => {
3838
absoluteFilepathToDocsConfig: AbsoluteFilePath.of("/tmp/docs.yml"),
3939
context
4040
})
41-
).rejects.toBeInstanceOf(FernCliError);
41+
).rejects.toBeInstanceOf(TaskAbortSignal);
4242
});
4343

4444
it("should throw if collapsible is used alongside deprecated collapsed", async () => {
@@ -64,7 +64,7 @@ describe("docs.yml navigation collapsible config", () => {
6464
absoluteFilepathToDocsConfig: AbsoluteFilePath.of("/tmp/docs.yml"),
6565
context
6666
})
67-
).rejects.toBeInstanceOf(FernCliError);
67+
).rejects.toBeInstanceOf(TaskAbortSignal);
6868
});
6969

7070
it("should throw if collapsible is used alongside deprecated collapsed: open-by-default", async () => {
@@ -90,7 +90,7 @@ describe("docs.yml navigation collapsible config", () => {
9090
absoluteFilepathToDocsConfig: AbsoluteFilePath.of("/tmp/docs.yml"),
9191
context
9292
})
93-
).rejects.toBeInstanceOf(FernCliError);
93+
).rejects.toBeInstanceOf(TaskAbortSignal);
9494
});
9595

9696
it("should accept open-by-default as a collapsed value on sections", async () => {
@@ -246,7 +246,7 @@ describe("docs.yml navigation collapsible config", () => {
246246
absoluteFilepathToDocsConfig: AbsoluteFilePath.of(docsConfigPath),
247247
context
248248
})
249-
).rejects.toBeInstanceOf(FernCliError);
249+
).rejects.toBeInstanceOf(TaskAbortSignal);
250250
});
251251
});
252252

packages/cli/generation/local-generation/local-workspace-runner/src/GenerationRunner.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { generatorsYml, SNIPPET_JSON_FILENAME } from "@fern-api/configuration";
44
import { AbsoluteFilePath, join, RelativeFilePath } from "@fern-api/fs-utils";
55
import { generateIntermediateRepresentation } from "@fern-api/ir-generator";
66
import { IntermediateRepresentation } from "@fern-api/ir-sdk";
7-
import { FernCliError, LoggableFernCliError, TaskContext } from "@fern-api/task-context";
7+
import { LoggableFernCliError, TaskAbortSignal, TaskContext } from "@fern-api/task-context";
88
import { FernGeneratorExec } from "@fern-fern/generator-exec-sdk";
99
import chalk from "chalk";
1010
import { generateDynamicSnippetTests } from "./dynamic-snippets/generateDynamicSnippetTests.js";
@@ -99,7 +99,7 @@ export class GenerationRunner {
9999
);
100100
}
101101
} catch (error) {
102-
if (error instanceof FernCliError) {
102+
if (error instanceof TaskAbortSignal) {
103103
// already logged by failAndThrow, nothing to do
104104
} else if (error instanceof LoggableFernCliError) {
105105
interactiveTaskContext.failWithoutThrowing(`Generation failed: ${error.log}`, error);

0 commit comments

Comments
 (0)