Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli/api-importers/asyncapi-to-ir/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"dependencies": {
"@fern-api/ir-sdk": "workspace:*",
"@fern-api/ir-utils": "workspace:*",
"@fern-api/task-context": "workspace:*",
"@fern-api/v3-importer-commons": "workspace:*",
"lodash-es": "catalog:",
"openapi-types": "^12.1.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HttpHeader, PathParameter, QueryParameter, WebSocketMessage, WebSocketMessageBody } from "@fern-api/ir-sdk";
import { constructHttpPath } from "@fern-api/ir-utils";
import { CliError } from "@fern-api/task-context";
import { Converters } from "@fern-api/v3-importer-commons";
import { OpenAPIV3 } from "openapi-types";
import { AbstractChannelConverter } from "../../converters/AbstractChannelConverter.js";
Expand Down Expand Up @@ -310,7 +311,10 @@ export class ChannelConverter3_0 extends AbstractChannelConverter<AsyncAPIV3.Cha

private getChannelPathFromOperation(operation: AsyncAPIV3.Operation): string {
if (!operation.channel.$ref.startsWith(CHANNEL_REFERENCE_PREFIX)) {
throw new Error(`Failed to resolve channel path from operation ${operation.channel.$ref}`);
throw new CliError({
message: `Failed to resolve channel path from operation ${operation.channel.$ref}`,
code: CliError.Code.ReferenceError
});
}
return operation.channel.$ref.substring(CHANNEL_REFERENCE_PREFIX.length);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RawSchemas } from "@fern-api/fern-definition-schema";
import { RelativeFilePath, sep } from "@fern-api/path-utils";
import { CliError } from "@fern-api/task-context";

export class FernDefinitionDirectory {
private files: Record<RelativeFilePath, RawSchemas.DefinitionFileSchema> = {};
Expand Down Expand Up @@ -41,7 +42,10 @@ export class FernDefinitionDirectory {
}
const [directory, ...remainingPath] = pathParts;
if (directory == null) {
throw new Error(`Internal error; cannot add file with path: ${pathParts}`);
throw new CliError({
message: `Internal error; cannot add file with path: ${pathParts}`,
code: CliError.Code.InternalError
});
}
if (!this.directories[directory]) {
this.directories[directory] = new FernDefinitionDirectory();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@fern-api/fern-definition-schema": "workspace:*",
"@fern-api/fs-utils": "workspace:*",
"@fern-api/importer-commons": "workspace:*",
"@fern-api/task-context": "workspace:*",
"js-yaml": "catalog:"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { parseEndpointLocator, removeSuffix } from "@fern-api/core-utils";
import { RawSchemas } from "@fern-api/fern-definition-schema";
import { AbsoluteFilePath, dirname, getFilename, join, RelativeFilePath, relativize } from "@fern-api/fs-utils";
import { APIDefinitionImporter, FernDefinitionBuilderImpl, HttpServiceInfo } from "@fern-api/importer-commons";
import { CliError } from "@fern-api/task-context";

import { listConjureFiles } from "./utils/listConjureFiles.js";
import { visitConjureTypeDeclaration } from "./utils/visitConjureTypeDeclaration.js";
Expand Down Expand Up @@ -77,7 +78,10 @@ export class ConjureImporter extends APIDefinitionImporter<ConjureImporter.Args>
if (definition.services == null || Object.keys(definition.services ?? {}).length === 0) {
const fernFilePath = this.conjureFilepathToFernFilepath[filepath];
if (fernFilePath == null) {
throw new Error(`Failed to find corresponding fern filepath for conjure file ${filepath}`);
throw new CliError({
message: `Failed to find corresponding fern filepath for conjure file ${filepath}`,
code: CliError.Code.InternalError
});
}

for (const [import_, importedFilepath] of Object.entries(definition.types?.conjureImports ?? {})) {
Expand Down Expand Up @@ -151,9 +155,10 @@ export class ConjureImporter extends APIDefinitionImporter<ConjureImporter.Args>
for (const pathParameter of endpointLocator.pathParameters) {
const pathParameterType = endpointDeclaration.args[pathParameter];
if (pathParameterType == null) {
throw new Error(
`Failed to find path parameter ${pathParameter} in ${endpointDeclaration.http}`
);
throw new CliError({
message: `Failed to find path parameter ${pathParameter} in ${endpointDeclaration.http}`,
code: CliError.Code.InternalError
});
}
pathParameters[pathParameter] =
typeof pathParameterType === "string"
Expand Down Expand Up @@ -338,9 +343,10 @@ export class ConjureImporter extends APIDefinitionImporter<ConjureImporter.Args>
);
const correspondingFernFilePath = this.conjureFilepathToFernFilepath[relativeFilePathToImportedFile];
if (correspondingFernFilePath == null) {
throw new Error(
`Failed to find corresponding fern filepath for conjure file ${relativeFilePathToImportedFile}`
);
throw new CliError({
message: `Failed to find corresponding fern filepath for conjure file ${relativeFilePathToImportedFile}`,
code: CliError.Code.InternalError
});
}
return correspondingFernFilePath;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { assertNever, MediaType } from "@fern-api/core-utils";
import { RawSchemas } from "@fern-api/fern-definition-schema";
import { Endpoint, EndpointExample, Request, RetriesConfiguration, Schema, SchemaId } from "@fern-api/openapi-ir";
import { RelativeFilePath } from "@fern-api/path-utils";
import { CliError } from "@fern-api/task-context";
import { buildEndpointExample } from "./buildEndpointExample.js";
import { ERROR_DECLARATIONS_FILENAME, EXTERNAL_AUDIENCE } from "./buildFernDefinition.js";
import { buildHeader } from "./buildHeader.js";
Expand Down Expand Up @@ -283,7 +284,10 @@ export function buildEndpoint({
};
},
_other: () => {
throw new Error("Unrecognized Response type: " + endpoint.response?.type);
throw new CliError({
message: "Unrecognized Response type: " + endpoint.response?.type,
code: CliError.Code.InternalError
});
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
WebhookTimestampFormat
} from "@fern-api/openapi-ir";
import { join, RelativeFilePath } from "@fern-api/path-utils";
import { CliError } from "@fern-api/task-context";
import { camelCase, isEqual } from "lodash-es";
import { buildHeader } from "./buildHeader.js";
import { buildTypeReference } from "./buildTypeReference.js";
Expand Down Expand Up @@ -142,7 +143,10 @@ export function buildWebhooks(context: OpenApiIrConverterContext): void {
};
},
_other: () => {
throw new Error("Unrecognized Response type: " + webhook.response?.type);
throw new CliError({
message: "Unrecognized Response type: " + webhook.response?.type,
code: CliError.Code.InternalError
});
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RawSchemas } from "@fern-api/fern-definition-schema";
import { HttpMethod } from "@fern-api/openapi-ir";
import { CliError } from "@fern-api/task-context";

export function convertToHttpMethod(httpMethod: HttpMethod): RawSchemas.HttpMethodSchema {
return HttpMethod._visit<RawSchemas.HttpMethodSchema>(httpMethod, {
Expand All @@ -10,13 +11,13 @@ export function convertToHttpMethod(httpMethod: HttpMethod): RawSchemas.HttpMeth
delete: () => RawSchemas.HttpMethodSchema.Delete,
head: () => RawSchemas.HttpMethodSchema.Head,
options: () => {
throw new Error("OPTIONS is unsupported");
throw new CliError({ message: "OPTIONS is unsupported", code: CliError.Code.ConfigError });
},
trace: () => {
throw new Error("TRACE is unsupported");
throw new CliError({ message: "TRACE is unsupported", code: CliError.Code.ConfigError });
},
_other: () => {
throw new Error("Unknown http method is unsupported");
throw new CliError({ message: "Unknown http method is unsupported", code: CliError.Code.ConfigError });
}
});
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { FERN_PACKAGE_MARKER_FILENAME } from "@fern-api/configuration";
import { Endpoint, HttpMethod } from "@fern-api/openapi-ir";
import { join, RelativeFilePath } from "@fern-api/path-utils";
import { CliError } from "@fern-api/task-context";
import { camelCase, compact, isEqual } from "lodash-es";

import { convertEndpointSdkNameToFileWithoutExtension } from "./convertSdkGroupName.js";

export interface EndpointLocation {
Expand Down Expand Up @@ -108,7 +108,10 @@ function getUnresolvedEndpointLocation(endpoint: Endpoint): EndpointLocation {
}

if (fileParts.length >= operationIdTokens.length) {
throw new Error(`Cannot get file for endpoint ${JSON.stringify(endpoint)}`);
throw new CliError({
message: `Cannot get file for endpoint ${JSON.stringify(endpoint)}`,
code: CliError.Code.InternalError
});
}

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@fern-api/ir-sdk": "workspace:*",
"@fern-api/ir-utils": "workspace:*",
"@fern-api/logger": "workspace:*",
"@fern-api/task-context": "workspace:*",
"js-yaml": "catalog:",
"js-yaml-source-map": "catalog:",
"lodash-es": "catalog:",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { recursivelyVisitRawTypeReference } from "@fern-api/fern-definition-schema";
import * as FernIr from "@fern-api/ir-sdk";
import { CliError } from "@fern-api/task-context";

export function createTypeReferenceFromFernType(fernType: string): FernIr.TypeReference | undefined {
return recursivelyVisitRawTypeReference<FernIr.TypeReference | undefined>({
Expand Down Expand Up @@ -153,7 +154,10 @@ export function createTypeReferenceFromFernType(fernType: string): FernIr.TypeRe
string: (value) => FernIr.Literal.string(value),
boolean: (value) => FernIr.Literal.boolean(value),
_other: () => {
throw new Error("Unexpected literal type");
throw new CliError({
message: "Unexpected literal type",
code: CliError.Code.InternalError
});
}
})
)
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1869,10 +1869,16 @@ function addDocsPreviewDeleteCommand(cli: Argv<GlobalCliOptions>, cliContext: Cl
.check((argv) => {
const sources = [argv.target, argv.url, argv.id].filter(Boolean);
if (sources.length === 0) {
throw new Error("Must provide a preview URL or --id.");
throw new CliError({
message: "Must provide a preview URL or --id.",
code: CliError.Code.ConfigError
});
}
if (sources.length > 1) {
throw new Error("Provide only one of: [target], --url, or --id.");
throw new CliError({
message: "Provide only one of: [target], --url, or --id.",
code: CliError.Code.ConfigError
});
}
return true;
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ function resolveTarget({
return { type: "id", value: id };
}
if (target == null) {
throw new Error("Must provide a preview URL or --id.");
throw new CliError({
message: "Must provide a preview URL or --id.",
code: CliError.Code.ConfigError
});
}
if (isPreviewUrl(target)) {
return { type: "url", value: target };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@ describe("loadAndRunMigrations", () => {
).rejects.toThrow("Invalid generator configuration");
});

// Timeout bumped to 60s because these two happy-path tests fall through
// to loadMigrationModule, which performs a real `npm install
// @fern-api/generator-migrations@latest`. That call is network-bound and
// regularly exceeds the 5s vitest default on CI (observed 2s–5s+).
it("should accept valid config with name property", async () => {
const mockLogger = {
debug: vi.fn(),
Expand All @@ -456,7 +460,7 @@ describe("loadAndRunMigrations", () => {
expect(mockLogger.warn).not.toHaveBeenCalledWith(
expect.stringContaining("Invalid generator configuration structure")
);
});
}, 60000);

it("should accept config with additional properties", async () => {
const mockLogger = {
Expand All @@ -483,6 +487,6 @@ describe("loadAndRunMigrations", () => {
expect(mockLogger.warn).not.toHaveBeenCalledWith(
expect.stringContaining("Invalid generator configuration structure")
);
});
}, 60000);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {
} from "@fern-api/configuration";
import { FdrAPI as CjsFdrSdk } from "@fern-api/fdr-sdk";
import { AbsoluteFilePath, dirname, join, RelativeFilePath } from "@fern-api/fs-utils";
import { CliError } from "@fern-api/task-context";
import { cp, mkdir, writeFile } from "fs/promises";
import yaml from "js-yaml";

import { FernDocsBuilder, FernDocsNavigationBuilder } from "./FernDocsBuilder.js";

interface MarkdownPage {
Expand Down Expand Up @@ -88,7 +88,7 @@ export class FernDocsBuilderImpl extends FernDocsBuilder {
versionConfig: docsYml.RawSchemas.VersionConfig;
navigation: docsYml.RawSchemas.VersionFileConfig;
}): void {
throw new Error("Method not implemented.");
throw new CliError({ message: "Method not implemented.", code: CliError.Code.InternalError });
}

public addNavbarLink({ link }: { link: docsYml.RawSchemas.NavbarLink }): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TabInfo
} from "@fern-api/docs-importer-commons";
import { AbsoluteFilePath, dirname, join, RelativeFilePath } from "@fern-api/fs-utils";
import { CliError } from "@fern-api/task-context";
import { readFile } from "fs/promises";

import { convertColors } from "./convertColors.js";
Expand Down Expand Up @@ -154,7 +155,11 @@ export class MintlifyImporter extends DocsImporter<MintlifyImporter.Args> {
if (Object.keys(this.tabUrlToInfo).length > 0) {
const tabUrl = getTabForMintItem({ mintItem });
if (tabUrl == null) {
return this.context.failAndThrow(`Failed to assign navigation item to a tab group: ${mintItem.group}`);
return this.context.failAndThrow(
`Failed to assign navigation item to a tab group: ${mintItem.group}`,
undefined,
{ code: CliError.Code.ConfigError }
);
}
const tab = this.tabUrlToInfo[tabUrl] ?? this.getDefaultDocumentationTab(builder);
return tab.navigationBuilder;
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/docs-importers/readme/src/assert.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CliError } from "@fern-api/task-context";
import type { Element, Root } from "hast";
import { CONTINUE, EXIT, visit } from "unist-util-visit";
import { z } from "zod";
Expand All @@ -10,7 +11,7 @@ export function assertIsNumber(val: unknown): asserts val is number {

export function assertIsDefined<T>(val: T): asserts val is NonNullable<T> {
if (val === undefined || val == null) {
throw new Error("Value is nullable.");
throw new CliError({ message: "Value is nullable.", code: CliError.Code.InternalError });
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { mkdirSync, writeFileSync } from "node:fs";
import { dirname } from "node:path";
import { CliError } from "@fern-api/task-context";
import { join } from "path";

export function createFilename(
Expand Down Expand Up @@ -73,7 +74,10 @@ export function writePage({
})
);
} catch (error) {
throw new Error(`${cleanedWritePath}: failed to download to disk`);
throw new CliError({
message: `${cleanedWritePath}: failed to download to disk`,
code: CliError.Code.NetworkError
});
}
}

Expand Down
14 changes: 10 additions & 4 deletions packages/cli/docs-importers/readme/src/utils/files/images.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync, mkdirSync } from "node:fs";
import { dirname, join } from "node:path";

import { CliError } from "@fern-api/task-context";
import type { Result } from "../../types/result.js";
import { fetchImage } from "../network.js";
import { write } from "./file.js";
Expand Down Expand Up @@ -42,7 +42,10 @@ async function writeImageToFile(src: string, rootPath: string): Promise<string>
const imagePath = join(rootPath, filename);

if (!isValidImageSrc(filename)) {
throw new Error(`${filename} - file extension not supported`);
throw new CliError({
message: `${filename} - file extension not supported`,
code: CliError.Code.InternalError
});
}
if (existsSync(imagePath)) {
return imagePath;
Expand All @@ -51,15 +54,18 @@ async function writeImageToFile(src: string, rootPath: string): Promise<string>
try {
mkdirSync(dirname(imagePath), { recursive: true });
} catch (error) {
throw new Error(`${imagePath} - failed to create directory`);
throw new CliError({ message: `${imagePath} - failed to create directory`, code: CliError.Code.InternalError });
}

try {
const imageData = await fetchImage(src);
write(imagePath, imageData);
return imagePath;
} catch (error) {
throw new Error(`${imagePath}: failed to download file from source`);
throw new CliError({
message: `${imagePath}: failed to download file from source`,
code: CliError.Code.NetworkError
});
}
}

Expand Down
Loading
Loading