From 9e13b999c64537857fddc067753d43cec65436ab Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Thu, 19 Dec 2024 22:18:02 +0000 Subject: [PATCH 01/11] docs dev --- packages/cli/cli/package.json | 2 +- packages/cli/cli/src/cli.ts | 8 +- .../src/commands/docs-dev/devDocsWorkspace.ts | 7 +- ...eOpenApiToFdrApiDefinitionForWorkspaces.ts | 54 +------- .../cli/configuration-loader/package.json | 2 +- packages/cli/configuration/package.json | 2 +- .../cli/docs-importers/commons/package.json | 2 +- .../cli/docs-importers/mintlify/package.json | 2 +- packages/cli/docs-markdown-utils/package.json | 2 +- packages/cli/docs-preview/package.json | 2 +- packages/cli/docs-preview/src/previewDocs.ts | 20 ++- .../cli/docs-preview/src/runPreviewServer.ts | 8 +- packages/cli/docs-resolver/package.json | 8 +- packages/cli/docs-resolver/src/index.ts | 1 + .../utils/generateFdrFromOpenApiWorkspace.ts | 52 ++++++++ packages/cli/ete-tests/package.json | 2 +- .../remote-workspace-runner/package.json | 2 +- packages/cli/register/package.json | 2 +- packages/core/package.json | 2 +- pnpm-lock.yaml | 121 ++++++++++++------ 20 files changed, 191 insertions(+), 110 deletions(-) create mode 100644 packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts diff --git a/packages/cli/cli/package.json b/packages/cli/cli/package.json index 5e55db7a1cc..b06f1763b27 100644 --- a/packages/cli/cli/package.json +++ b/packages/cli/cli/package.json @@ -46,7 +46,7 @@ "@fern-api/configuration-loader": "workspace:*", "@fern-api/core": "workspace:*", "@fern-api/core-utils": "workspace:*", - "@fern-api/docs-parsers": "^0.0.12", + "@fern-api/docs-parsers": "^0.0.13", "@fern-api/docs-preview": "workspace:*", "@fern-api/docs-resolver": "workspace:*", "@fern-api/docs-validator": "workspace:*", diff --git a/packages/cli/cli/src/cli.ts b/packages/cli/cli/src/cli.ts index a015859b267..94d9c60d9b3 100644 --- a/packages/cli/cli/src/cli.ts +++ b/packages/cli/cli/src/cli.ts @@ -992,6 +992,10 @@ function addDocsPreviewCommand(cli: Argv<GlobalCliOptions>, cliContext: CliConte string: true, hidden: true, description: "Path of the local docs bundle to use" + }) + .option("v2", { + boolean: true, + description: "Use openapi parser v2" }), async (argv) => { let port: number; @@ -1001,6 +1005,7 @@ function addDocsPreviewCommand(cli: Argv<GlobalCliOptions>, cliContext: CliConte port = await getPort({ port: [3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010] }); } const bundlePath: string | undefined = argv.bundlePath; + const v2 = argv.v2; await previewDocsWorkspace({ loadProject: () => loadProjectAndRegisterWorkspacesWithContext(cliContext, { @@ -1009,7 +1014,8 @@ function addDocsPreviewCommand(cli: Argv<GlobalCliOptions>, cliContext: CliConte }), cliContext, port, - bundlePath + bundlePath, + v2 }); } ); diff --git a/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts b/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts index 244231a0ac1..18660ebc2f8 100644 --- a/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts +++ b/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts @@ -8,12 +8,14 @@ export async function previewDocsWorkspace({ loadProject, cliContext, port, - bundlePath + bundlePath, + v2 }: { loadProject: () => Promise<Project>; cliContext: CliContext; port: number; bundlePath?: string; + v2?: boolean; }): Promise<void> { const project = await loadProject(); const docsWorkspace = project.docsWorkspaces; @@ -58,7 +60,8 @@ export async function previewDocsWorkspace({ }, context, port, - bundlePath + bundlePath, + v2 }); }); } diff --git a/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts b/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts index d7df7f8c4f8..c1296f93ede 100644 --- a/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts +++ b/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts @@ -3,13 +3,7 @@ import { Project } from "@fern-api/project-loader"; import { writeFile } from "fs/promises"; import path from "path"; import { CliContext } from "../../cli-context/CliContext"; -import { getAllOpenAPISpecs, LazyFernWorkspace, OSSWorkspace, OpenAPILoader } from "@fern-api/lazy-fern-workspace"; -// TODO: clean up imports -import { OpenApiDocumentConverterNode } from "@fern-api/docs-parsers"; -import { ErrorCollector } from "@fern-api/docs-parsers"; -import { BaseOpenApiV3_1ConverterNodeContext } from "@fern-api/docs-parsers"; -import { OpenAPIV3_1 } from "openapi-types"; -import { merge } from "lodash-es"; +import { generateFdrFromOpenApiWorkspace } from "@fern-api/docs-resolver"; export async function generateOpenApiToFdrApiDefinitionForWorkspaces({ project, @@ -23,49 +17,11 @@ export async function generateOpenApiToFdrApiDefinitionForWorkspaces({ await Promise.all( project.apiWorkspaces.map(async (workspace) => { await cliContext.runTaskForWorkspace(workspace, async (context) => { - await cliContext.runTaskForWorkspace(workspace, async (context) => { - if (workspace instanceof LazyFernWorkspace) { - context.logger.info("Skipping, API is specified as a Fern Definition."); - return; - } else if (!(workspace instanceof OSSWorkspace)) { - return; - } + const fdrApiDefinition = await generateFdrFromOpenApiWorkspace(workspace, context); - const openApiLoader = new OpenAPILoader(workspace.absoluteFilePath); - const openApiSpecs = await getAllOpenAPISpecs({ context, specs: workspace.specs }); - - const openApiDocuments = await openApiLoader.loadDocuments({ context, specs: openApiSpecs }); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let fdrApiDefinition: any; - for (const openApi of openApiDocuments) { - if (openApi.type !== "openapi") { - continue; - } - - const oasContext: BaseOpenApiV3_1ConverterNodeContext = { - document: openApi.value as OpenAPIV3_1.Document, - logger: context.logger, - errors: new ErrorCollector() - }; - - const openApiFdrJson = new OpenApiDocumentConverterNode({ - input: openApi.value as OpenAPIV3_1.Document, - context: oasContext, - accessPath: [], - pathId: workspace.workspaceName ?? "openapi parser" - }); - - fdrApiDefinition = merge(fdrApiDefinition, openApiFdrJson.convert()); - } - - const resolvedOutputFilePath = path.resolve(outputFilepath); - await writeFile( - resolvedOutputFilePath, - await stringifyLargeObject(fdrApiDefinition, { pretty: true }) - ); - context.logger.info(`Wrote FDR API definition to ${resolvedOutputFilePath}`); - }); + const resolvedOutputFilePath = path.resolve(outputFilepath); + await writeFile(resolvedOutputFilePath, await stringifyLargeObject(fdrApiDefinition, { pretty: true })); + context.logger.info(`Wrote FDR API definition to ${resolvedOutputFilePath}`); }); }) ); diff --git a/packages/cli/configuration-loader/package.json b/packages/cli/configuration-loader/package.json index cd34c514fd6..f17c15b5027 100644 --- a/packages/cli/configuration-loader/package.json +++ b/packages/cli/configuration-loader/package.json @@ -33,7 +33,7 @@ "@fern-api/fs-utils": "workspace:*", "@fern-api/task-context": "workspace:*", "@fern-api/fern-definition-schema": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "@fern-fern/fiddle-sdk": "0.0.584", "@fern-fern/generators-sdk": "0.114.0-5745f9e74", "find-up": "^6.3.0", diff --git a/packages/cli/configuration/package.json b/packages/cli/configuration/package.json index 470e518a3b4..a974fa02743 100644 --- a/packages/cli/configuration/package.json +++ b/packages/cli/configuration/package.json @@ -31,7 +31,7 @@ "@fern-api/core-utils": "workspace:*", "@fern-api/path-utils": "workspace:*", "@fern-api/fern-definition-schema": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "@fern-fern/fiddle-sdk": "0.0.584", "zod": "^3.22.3" }, diff --git a/packages/cli/docs-importers/commons/package.json b/packages/cli/docs-importers/commons/package.json index c35763532bb..5586b6810d0 100644 --- a/packages/cli/docs-importers/commons/package.json +++ b/packages/cli/docs-importers/commons/package.json @@ -31,7 +31,7 @@ "@fern-api/configuration": "workspace:*", "@fern-api/fs-utils": "workspace:*", "@fern-api/task-context": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "js-yaml": "^4.1.0" }, "devDependencies": { diff --git a/packages/cli/docs-importers/mintlify/package.json b/packages/cli/docs-importers/mintlify/package.json index a77bd52b3c4..1a6663ad16c 100644 --- a/packages/cli/docs-importers/mintlify/package.json +++ b/packages/cli/docs-importers/mintlify/package.json @@ -34,7 +34,7 @@ "@fern-api/task-context": "workspace:*", "@fern-api/fs-utils": "workspace:*", "@fern-api/logger": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "gray-matter": "^4.0.3" }, "devDependencies": { diff --git a/packages/cli/docs-markdown-utils/package.json b/packages/cli/docs-markdown-utils/package.json index 3e157089867..b59d51c478f 100644 --- a/packages/cli/docs-markdown-utils/package.json +++ b/packages/cli/docs-markdown-utils/package.json @@ -30,7 +30,7 @@ "dependencies": { "@fern-api/fs-utils": "workspace:*", "@fern-api/task-context": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "gray-matter": "^4.0.3", "mdast-util-from-markdown": "^2.0.1", "mdast-util-mdx": "^3.0.0", diff --git a/packages/cli/docs-preview/package.json b/packages/cli/docs-preview/package.json index 9342d7e2369..f24e91e0df7 100644 --- a/packages/cli/docs-preview/package.json +++ b/packages/cli/docs-preview/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@fern-api/docs-resolver": "workspace:*", - "@fern-api/fdr-sdk": "0.126.1-444264056", + "@fern-api/fdr-sdk": "0.127.3-6479103bd", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-sdk": "workspace:*", "@fern-api/logger": "workspace:*", diff --git a/packages/cli/docs-preview/src/previewDocs.ts b/packages/cli/docs-preview/src/previewDocs.ts index 26fb2132da9..f738fc46b35 100644 --- a/packages/cli/docs-preview/src/previewDocs.ts +++ b/packages/cli/docs-preview/src/previewDocs.ts @@ -18,15 +18,19 @@ import { Project } from "@fern-api/project-loader"; import { convertIrToFdrApi } from "@fern-api/register"; import { TaskContext } from "@fern-api/task-context"; import { v4 as uuidv4 } from "uuid"; +import { generateFdrFromOpenApiWorkspace } from "@fern-api/docs-resolver"; +import { isNonNullish } from "../../../commons/core-utils/src"; export async function getPreviewDocsDefinition({ domain, project, - context + context, + v2 }: { domain: string; project: Project; context: TaskContext; + v2?: boolean; }): Promise<DocsV1Read.DocsDefinition> { const docsWorkspace = project.docsWorkspaces; const apiWorkspaces = project.apiWorkspaces; @@ -80,7 +84,19 @@ export async function getPreviewDocsDefinition({ }); return { - apis: apiCollector.getAPIsForDefinition(), + apis: v2 ? {} : apiCollector.getAPIsForDefinition(), + apisV2: v2 + ? Object.fromEntries( + ( + await Promise.all( + fernWorkspaces.map((workspace) => [ + uuidv4(), + generateFdrFromOpenApiWorkspace(workspace, context) + ]) + ) + ).filter(isNonNullish) + ) + : {}, config: readDocsConfig, files: {}, filesV2, diff --git a/packages/cli/docs-preview/src/runPreviewServer.ts b/packages/cli/docs-preview/src/runPreviewServer.ts index 3c9433fcafb..7ec931e916c 100644 --- a/packages/cli/docs-preview/src/runPreviewServer.ts +++ b/packages/cli/docs-preview/src/runPreviewServer.ts @@ -16,6 +16,7 @@ import { getPreviewDocsDefinition } from "./previewDocs"; const EMPTY_DOCS_DEFINITION: DocsV1Read.DocsDefinition = { pages: {}, apis: {}, + apisV2: {}, files: {}, filesV2: {}, config: { @@ -54,7 +55,8 @@ export async function runPreviewServer({ validateProject, context, port, - bundlePath + bundlePath, + v2 }: { initialProject: Project; reloadProject: () => Promise<Project>; @@ -62,6 +64,7 @@ export async function runPreviewServer({ context: TaskContext; port: number; bundlePath?: string; + v2?: boolean; }): Promise<void> { if (bundlePath != null) { context.logger.info(`Using bundle from path: ${bundlePath}`); @@ -128,7 +131,8 @@ export async function runPreviewServer({ const newDocsDefinition = await getPreviewDocsDefinition({ domain: `${instance.host}${instance.pathname}`, project, - context + context, + v2 }); context.logger.info(`Reload completed in ${Date.now() - startTime}ms`); return newDocsDefinition; diff --git a/packages/cli/docs-resolver/package.json b/packages/cli/docs-resolver/package.json index 0e874efb46a..d879ff37de4 100644 --- a/packages/cli/docs-resolver/package.json +++ b/packages/cli/docs-resolver/package.json @@ -27,12 +27,15 @@ "depcheck": "depcheck" }, "dependencies": { + "@fern-api/api-workspace-commons": "workspace:*", "@fern-api/cli-source-resolver": "workspace:*", "@fern-api/configuration-loader": "workspace:*", "@fern-api/core-utils": "workspace:*", "@fern-api/docs-markdown-utils": "workspace:*", - "@fern-api/fdr-sdk": "0.126.1-444264056", - "@fern-api/ui-core-utils": "0.126.1-444264056", + "@fern-api/docs-parsers": "^0.0.13", + "@fern-api/fdr-sdk": "0.127.3-6479103bd", + "@fern-api/lazy-fern-workspace": "workspace:*", + "@fern-api/ui-core-utils": "0.127.3-6479103bd", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-generator": "workspace:*", "@fern-api/ir-sdk": "workspace:*", @@ -49,6 +52,7 @@ "@types/node": "18.7.18", "depcheck": "^1.4.6", "eslint": "^8.56.0", + "openapi-types": "^10.0.0", "organize-imports-cli": "^0.10.0", "prettier": "^2.7.1", "typescript": "4.6.4", diff --git a/packages/cli/docs-resolver/src/index.ts b/packages/cli/docs-resolver/src/index.ts index a6692d7a13e..cc615757fb1 100644 --- a/packages/cli/docs-resolver/src/index.ts +++ b/packages/cli/docs-resolver/src/index.ts @@ -1,2 +1,3 @@ export { DocsDefinitionResolver, type UploadedFile } from "./DocsDefinitionResolver"; export { wrapWithHttps } from "./wrapWithHttps"; +export { generateFdrFromOpenApiWorkspace } from "./utils/generateFdrFromOpenApiWorkspace"; diff --git a/packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts b/packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts new file mode 100644 index 00000000000..d67cb531b35 --- /dev/null +++ b/packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts @@ -0,0 +1,52 @@ +import { getAllOpenAPISpecs, LazyFernWorkspace, OSSWorkspace, OpenAPILoader } from "@fern-api/lazy-fern-workspace"; +import { + ErrorCollector, + OpenApiDocumentConverterNode, + BaseOpenApiV3_1ConverterNodeContext +} from "@fern-api/docs-parsers"; +import { OpenAPIV3_1 } from "openapi-types"; +import { merge } from "lodash-es"; +import { AbstractAPIWorkspace } from "@fern-api/api-workspace-commons"; +import { TaskContext } from "@fern-api/task-context"; + +export async function generateFdrFromOpenApiWorkspace( + workspace: AbstractAPIWorkspace<unknown>, + context: TaskContext +): Promise<ReturnType<OpenApiDocumentConverterNode["convert"]>> { + if (workspace instanceof LazyFernWorkspace) { + context.logger.info("Skipping, API is specified as a Fern Definition."); + return; + } else if (!(workspace instanceof OSSWorkspace)) { + return; + } + + const openApiLoader = new OpenAPILoader(workspace.absoluteFilePath); + const openApiSpecs = await getAllOpenAPISpecs({ context, specs: workspace.specs }); + + const openApiDocuments = await openApiLoader.loadDocuments({ context, specs: openApiSpecs }); + + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + let fdrApiDefinition: ReturnType<OpenApiDocumentConverterNode["convert"]>; + for (const openApi of openApiDocuments) { + if (openApi.type !== "openapi") { + continue; + } + + const oasContext: BaseOpenApiV3_1ConverterNodeContext = { + document: openApi.value as OpenAPIV3_1.Document, + logger: context.logger, + errors: new ErrorCollector() + }; + + const openApiFdrJson = new OpenApiDocumentConverterNode({ + input: openApi.value as OpenAPIV3_1.Document, + context: oasContext, + accessPath: [], + pathId: workspace.workspaceName ?? "openapi parser" + }); + + fdrApiDefinition = merge(fdrApiDefinition, openApiFdrJson.convert()); + } + + return fdrApiDefinition; +} diff --git a/packages/cli/ete-tests/package.json b/packages/cli/ete-tests/package.json index a67311c1f82..59a36fa6329 100644 --- a/packages/cli/ete-tests/package.json +++ b/packages/cli/ete-tests/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@fern-api/configuration": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "@fern-api/fs-utils": "workspace:*", "@fern-api/logging-execa": "workspace:*", "@fern-typescript/fetcher": "workspace:*", diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/package.json b/packages/cli/generation/remote-generation/remote-workspace-runner/package.json index 042dbc724a6..5d3c27eb9ae 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/package.json +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/package.json @@ -35,7 +35,7 @@ "@fern-api/core-utils": "workspace:*", "@fern-api/docs-resolver": "workspace:*", "@fern-api/logging-execa": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-generator": "workspace:*", "@fern-api/ir-migrations": "workspace:*", diff --git a/packages/cli/register/package.json b/packages/cli/register/package.json index 8daa14d587d..ec70d43f350 100644 --- a/packages/cli/register/package.json +++ b/packages/cli/register/package.json @@ -33,7 +33,7 @@ "@fern-api/configuration": "workspace:*", "@fern-api/core": "workspace:*", "@fern-api/core-utils": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-generator": "workspace:*", "@fern-api/ir-sdk": "workspace:*", diff --git a/packages/core/package.json b/packages/core/package.json index 6a52b4380fa..cc5200e19ee 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,7 +28,7 @@ "depcheck": "depcheck" }, "dependencies": { - "@fern-fern/fdr-cjs-sdk": "0.126.1-444264056", + "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", "@fern-fern/generators-sdk": "0.114.0-5745f9e74", "@fern-api/venus-api-sdk": "0.10.2", "@fern-fern/fdr-test-sdk": "^0.0.5297", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1cb3f71d27e..e42e9112998 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3856,8 +3856,8 @@ importers: specifier: workspace:* version: link:../../commons/core-utils '@fern-api/docs-parsers': - specifier: ^0.0.12 - version: 0.0.12(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) + specifier: ^0.0.13 + version: 0.0.13(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/docs-preview': specifier: workspace:* version: link:../docs-preview @@ -4225,6 +4225,8 @@ importers: specifier: ^2.1.4 version: 2.1.4(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) + packages/cli/cli/dist/local: {} + packages/cli/configuration: dependencies: '@fern-api/core-utils': @@ -4237,8 +4239,8 @@ importers: specifier: workspace:* version: link:../../commons/path-utils '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd '@fern-fern/fiddle-sdk': specifier: 0.0.584 version: 0.0.584 @@ -4286,8 +4288,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd '@fern-fern/fiddle-sdk': specifier: 0.0.584 version: 0.0.584 @@ -4362,8 +4364,8 @@ importers: specifier: workspace:* version: link:../../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -4414,8 +4416,8 @@ importers: specifier: workspace:* version: link:../../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -4451,8 +4453,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -4506,8 +4508,8 @@ importers: specifier: workspace:* version: link:../docs-resolver '@fern-api/fdr-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056(typescript@4.6.4) + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd(typescript@4.6.4) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -4605,6 +4607,9 @@ importers: packages/cli/docs-resolver: dependencies: + '@fern-api/api-workspace-commons': + specifier: workspace:* + version: link:../workspace/commons '@fern-api/cli-source-resolver': specifier: workspace:* version: link:../cli-source-resolver @@ -4617,9 +4622,12 @@ importers: '@fern-api/docs-markdown-utils': specifier: workspace:* version: link:../docs-markdown-utils + '@fern-api/docs-parsers': + specifier: ^0.0.13 + version: 0.0.13(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/fdr-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056(typescript@4.6.4) + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd(typescript@4.6.4) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -4629,6 +4637,9 @@ importers: '@fern-api/ir-sdk': specifier: workspace:* version: link:../../ir-sdk + '@fern-api/lazy-fern-workspace': + specifier: workspace:* + version: link:../workspace/lazy-fern-workspace '@fern-api/register': specifier: workspace:* version: link:../register @@ -4636,8 +4647,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-api/ui-core-utils': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd dayjs: specifier: ^1.11.11 version: 1.11.11 @@ -4666,6 +4677,9 @@ importers: eslint: specifier: ^8.56.0 version: 8.56.0 + openapi-types: + specifier: ^10.0.0 + version: 10.0.0 organize-imports-cli: specifier: ^0.10.0 version: 0.10.0 @@ -4691,8 +4705,8 @@ importers: specifier: workspace:* version: link:../../commons/logging-execa '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd '@fern-typescript/fetcher': specifier: workspace:* version: link:../../../generators/typescript/utils/core-utilities/fetcher @@ -5495,8 +5509,8 @@ importers: specifier: workspace:* version: link:../../../workspace/loader '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd '@fern-fern/fiddle-sdk': specifier: 0.0.584 version: 0.0.584 @@ -5965,8 +5979,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -6743,8 +6757,8 @@ importers: specifier: 0.10.2 version: 0.10.2 '@fern-fern/fdr-cjs-sdk': - specifier: 0.126.1-444264056 - version: 0.126.1-444264056 + specifier: 0.127.3-6479103bd + version: 0.127.3-6479103bd '@fern-fern/fdr-test-sdk': specifier: ^0.0.5297 version: 0.0.5297 @@ -8080,8 +8094,8 @@ packages: '@fern-api/core-utils@0.4.24-rc1': resolution: {integrity: sha512-aYu4lQK2qZIKzTF9TeFrICTPJ/zGEZUEWQmZt6pJeHu+R6afrcCBNkUleWU1OpHlDbe+xXUUBOktRg0PM9Hywg==} - '@fern-api/docs-parsers@0.0.12': - resolution: {integrity: sha512-bTnhztE8xZlnyzm3lvnvv+DGGup81qD/A8k2f9OP4I9kxzGsR7p/U/OnEDD+ztXWvlemdH7QxKLjzVmZBLLeJg==} + '@fern-api/docs-parsers@0.0.13': + resolution: {integrity: sha512-pmMUD28tWPGutTGDR2Q8vx/3v9YHlYMCUP2uVsI94Qam51Pm/uPVFikYoPrSxDYfG1NNcxGVddC7FKW18S99Xw==} '@fern-api/dynamic-ir-sdk@53.24.0': resolution: {integrity: sha512-4XvzvSsh7lNZz5oYN0LH+FRpFGxg9TjfdipSJMpgHvPShe85m/tcfbOzbS0DHBpQGlKb/gHQtQRPAoBR1wdDAw==} @@ -8089,8 +8103,8 @@ packages: '@fern-api/dynamic-ir-sdk@54.0.0': resolution: {integrity: sha512-5ewX7042AGuw697jA6ubulCIQbfTM2HC8UbHxTakE0px871HJTRwHNT2Y9o2kjMbsRXTzyYdb5lOVr+85sc/KQ==} - '@fern-api/fdr-sdk@0.126.1-444264056': - resolution: {integrity: sha512-Xl1Ctteav1GOulX40756FLEoJAI7VCn6zgkdDb6RRSZhm9Z8fjaBRv/cdMo1saupqHNd62Hm5b8FdmGjwWFAPw==} + '@fern-api/fdr-sdk@0.127.3-6479103bd': + resolution: {integrity: sha512-9WPubeQHSQJ2ece9lG2TY4Rc879e7AhvDYOamlYqsD5gy16eATRUkOvj7d6pWch5JwYJaMM8jcdZrQ9dltf/qQ==} '@fern-api/logger@0.4.24-rc1': resolution: {integrity: sha512-yh0E2F3K3IPnJZcE4dv+u8I51iKgTgv/reinKo4K5YmYEG1iLtw5vBEYMOPkQmsYFPAKIh++OMB/6TrsahMWew==} @@ -8104,8 +8118,8 @@ packages: '@fern-api/ui-core-utils@0.0.0': resolution: {integrity: sha512-8T3YLd+n8z5Vs+WNRIwH6PUW31ZC4/lkRD5G2+qyBcdePfOVYV3CHp3eiUrSSArOr0SJmzN/mQwPm3iAaey7nw==} - '@fern-api/ui-core-utils@0.126.1-444264056': - resolution: {integrity: sha512-9L3Tgl83nGaw9Jug17ZXSkPL7ZTeOP3SukG/Fz2Y2Aa/GqEToCrexuGkTO090nwuv2zO9gi7CYDHvQOEl5IIMQ==} + '@fern-api/ui-core-utils@0.127.3-6479103bd': + resolution: {integrity: sha512-WLrD3Ad2zzPGfVickTiJ1JoKN97Px9Dzu+Zl8oHcreIdf3Ngmf5LH/jCcwRx7LenRKdRj6+UMFBxA1ZdVDg3ng==} '@fern-api/venus-api-sdk@0.10.2': resolution: {integrity: sha512-BD18ZNJeWYvztRXSUWY2lfZ8jFUbNAfHWctvIsWyXgI7C8EL3N0+CWUGD5cZ1QqRnq42nm6XJdWRKhny3yrz4g==} @@ -8113,8 +8127,8 @@ packages: '@fern-fern/docs-config@0.0.80': resolution: {integrity: sha512-wAZCNxwM4qIPn3idoIihPP65KWPjNuoTSfdP4+5f8K2jJLgs5cdkY0pm4cQlGWvi5K6b+lfbUWDaslnXscJ2gQ==} - '@fern-fern/fdr-cjs-sdk@0.126.1-444264056': - resolution: {integrity: sha512-APStUB4pA/H620WMm/K6uyCtk7hxXB4ZuroY+VIfLexpieC39ZSHeN6DlpbuG/vPkF6LUtnpYlfWxGpEVCmhrw==} + '@fern-fern/fdr-cjs-sdk@0.127.3-6479103bd': + resolution: {integrity: sha512-nYpgLaorgK0s+6P6iKC8TYa4QF4RjZxr4y5/jogpkQc74sm2fmZq7vpRiCTmr9wt+Ei6EZqbemqvULh6G8y25g==} '@fern-fern/fdr-test-sdk@0.0.5297': resolution: {integrity: sha512-jrZUZ6oIA64LHtrv77xEq0X7qJhT9xRMGWhKcLjIUArMsD7h6KMWUHVdoB/1MP0Mz/uL/E2xmM31SOgJicpIcA==} @@ -9921,6 +9935,14 @@ packages: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + date-fns-tz@3.2.0: + resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==} + peerDependencies: + date-fns: ^3.0.0 || ^4.0.0 + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.11: resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} @@ -10230,6 +10252,9 @@ packages: es-toolkit@1.26.0: resolution: {integrity: sha512-gg6ZaI4eTwzkQGkdEG+t4b4VH2mTxodBQ2KvyTKAQFIag3Vvxoq3XZ1ie4ijIQmVxiipL2+dZLDMkh1VIvAFyg==} + es-toolkit@1.30.1: + resolution: {integrity: sha512-ZXflqanzH8BpHkDhFa10bBf6ONDCe84EPUm7SSICGzuuROSluT2ynTPtwn9PcRelMtorCRozSknI/U0MNYp0Uw==} + es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} @@ -12159,6 +12184,9 @@ packages: resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} engines: {node: '>=12'} + openapi-types@10.0.0: + resolution: {integrity: sha512-Y8xOCT2eiKGYDzMW9R4x5cmfc3vGaaI4EL2pwhDmodWw1HlK18YcZ4uJxc7Rdp7/gGzAygzH9SXr6GKYIXbRcQ==} + openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} @@ -15208,11 +15236,10 @@ snapshots: lodash-es: 4.17.21 strip-ansi: 7.1.0 - '@fern-api/docs-parsers@0.0.12(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': + '@fern-api/docs-parsers@0.0.13(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': dependencies: '@fern-api/logger': 0.4.24-rc1 '@fern-api/ui-core-utils': 0.0.0 - ajv: 8.17.1 es-toolkit: 1.26.0 openapi-types: 12.1.3 ts-essentials: 10.0.1(typescript@4.6.4) @@ -15241,12 +15268,12 @@ snapshots: '@fern-api/dynamic-ir-sdk@54.0.0': {} - '@fern-api/fdr-sdk@0.126.1-444264056(typescript@4.6.4)': + '@fern-api/fdr-sdk@0.127.3-6479103bd(typescript@4.6.4)': dependencies: - '@fern-api/ui-core-utils': 0.126.1-444264056 + '@fern-api/ui-core-utils': 0.127.3-6479103bd '@ungap/structured-clone': 1.2.0 dayjs: 1.11.11 - es-toolkit: 1.26.0 + es-toolkit: 1.30.1 fast-deep-equal: 3.1.3 form-data: 4.0.0 formdata-node: 6.0.3 @@ -15288,8 +15315,10 @@ snapshots: title: 3.5.3 ua-parser-js: 1.0.37 - '@fern-api/ui-core-utils@0.126.1-444264056': + '@fern-api/ui-core-utils@0.127.3-6479103bd': dependencies: + date-fns: 4.1.0 + date-fns-tz: 3.2.0(date-fns@4.1.0) strip-ansi: 7.1.0 title: 3.5.3 ua-parser-js: 1.0.37 @@ -15308,7 +15337,7 @@ snapshots: '@fern-fern/docs-config@0.0.80': {} - '@fern-fern/fdr-cjs-sdk@0.126.1-444264056': + '@fern-fern/fdr-cjs-sdk@0.127.3-6479103bd': dependencies: form-data: 4.0.0 formdata-node: 6.0.3 @@ -17470,6 +17499,12 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + date-fns-tz@3.2.0(date-fns@4.1.0): + dependencies: + date-fns: 4.1.0 + + date-fns@4.1.0: {} + dayjs@1.11.11: {} debug@2.6.9: @@ -17812,6 +17847,8 @@ snapshots: es-toolkit@1.26.0: {} + es-toolkit@1.30.1: {} + es6-promise@3.3.1: {} esbuild@0.21.5: @@ -20433,6 +20470,8 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + openapi-types@10.0.0: {} + openapi-types@12.1.3: {} optionator@0.9.3: From 1607ad2835ac0c8b32c215af294711e7e391bab4 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Fri, 20 Dec 2024 02:33:35 +0000 Subject: [PATCH 02/11] correlate IDs properly, docs dev working --- packages/cli/cli/package.json | 2 +- packages/cli/docs-preview/src/previewDocs.ts | 15 ++++++++++---- packages/cli/docs-resolver/package.json | 2 +- pnpm-lock.yaml | 21 ++++++++------------ 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/cli/cli/package.json b/packages/cli/cli/package.json index b06f1763b27..bf5b094e129 100644 --- a/packages/cli/cli/package.json +++ b/packages/cli/cli/package.json @@ -46,7 +46,7 @@ "@fern-api/configuration-loader": "workspace:*", "@fern-api/core": "workspace:*", "@fern-api/core-utils": "workspace:*", - "@fern-api/docs-parsers": "^0.0.13", + "@fern-api/docs-parsers": "^0.0.14", "@fern-api/docs-preview": "workspace:*", "@fern-api/docs-resolver": "workspace:*", "@fern-api/docs-validator": "workspace:*", diff --git a/packages/cli/docs-preview/src/previewDocs.ts b/packages/cli/docs-preview/src/previewDocs.ts index f738fc46b35..8a14568abba 100644 --- a/packages/cli/docs-preview/src/previewDocs.ts +++ b/packages/cli/docs-preview/src/previewDocs.ts @@ -48,6 +48,9 @@ export async function getPreviewDocsDefinition({ ) ); + // TODO: remove this once we remove V1 API generation + const apiDefinitionIds: FdrAPI.ApiDefinitionId[] = []; + const apiCollector = new ReferencedAPICollector(context); const filesV2: Record<string, DocsV1Read.File_> = {}; @@ -71,7 +74,11 @@ export async function getPreviewDocsDefinition({ fileId }; }), - async (opts) => apiCollector.addReferencedAPI(opts) + async (opts) => { + const id = apiCollector.addReferencedAPI(opts); + apiDefinitionIds.push(FdrAPI.ApiDefinitionId(id)); + return id; + } ); const writeDocsDefinition = await resolver.resolve(); @@ -89,9 +96,9 @@ export async function getPreviewDocsDefinition({ ? Object.fromEntries( ( await Promise.all( - fernWorkspaces.map((workspace) => [ - uuidv4(), - generateFdrFromOpenApiWorkspace(workspace, context) + project.apiWorkspaces.map(async (workspace, i) => [ + apiDefinitionIds[i], + await generateFdrFromOpenApiWorkspace(workspace, context) ]) ) ).filter(isNonNullish) diff --git a/packages/cli/docs-resolver/package.json b/packages/cli/docs-resolver/package.json index d879ff37de4..0c1544ed3e0 100644 --- a/packages/cli/docs-resolver/package.json +++ b/packages/cli/docs-resolver/package.json @@ -32,7 +32,7 @@ "@fern-api/configuration-loader": "workspace:*", "@fern-api/core-utils": "workspace:*", "@fern-api/docs-markdown-utils": "workspace:*", - "@fern-api/docs-parsers": "^0.0.13", + "@fern-api/docs-parsers": "^0.0.14", "@fern-api/fdr-sdk": "0.127.3-6479103bd", "@fern-api/lazy-fern-workspace": "workspace:*", "@fern-api/ui-core-utils": "0.127.3-6479103bd", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e42e9112998..bf8dd41766a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3856,8 +3856,8 @@ importers: specifier: workspace:* version: link:../../commons/core-utils '@fern-api/docs-parsers': - specifier: ^0.0.13 - version: 0.0.13(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) + specifier: ^0.0.14 + version: 0.0.14(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/docs-preview': specifier: workspace:* version: link:../docs-preview @@ -4623,8 +4623,8 @@ importers: specifier: workspace:* version: link:../docs-markdown-utils '@fern-api/docs-parsers': - specifier: ^0.0.13 - version: 0.0.13(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) + specifier: ^0.0.14 + version: 0.0.14(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/fdr-sdk': specifier: 0.127.3-6479103bd version: 0.127.3-6479103bd(typescript@4.6.4) @@ -8094,8 +8094,8 @@ packages: '@fern-api/core-utils@0.4.24-rc1': resolution: {integrity: sha512-aYu4lQK2qZIKzTF9TeFrICTPJ/zGEZUEWQmZt6pJeHu+R6afrcCBNkUleWU1OpHlDbe+xXUUBOktRg0PM9Hywg==} - '@fern-api/docs-parsers@0.0.13': - resolution: {integrity: sha512-pmMUD28tWPGutTGDR2Q8vx/3v9YHlYMCUP2uVsI94Qam51Pm/uPVFikYoPrSxDYfG1NNcxGVddC7FKW18S99Xw==} + '@fern-api/docs-parsers@0.0.14': + resolution: {integrity: sha512-82MLnKAATRiCjD3Xcf60Mw9vGSZ2yj7iy8xaRGWBGudDjZBb2p4kcj95musonaF2f/IFNkq4iX3AONgB7FNhYA==} '@fern-api/dynamic-ir-sdk@53.24.0': resolution: {integrity: sha512-4XvzvSsh7lNZz5oYN0LH+FRpFGxg9TjfdipSJMpgHvPShe85m/tcfbOzbS0DHBpQGlKb/gHQtQRPAoBR1wdDAw==} @@ -10249,9 +10249,6 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - es-toolkit@1.26.0: - resolution: {integrity: sha512-gg6ZaI4eTwzkQGkdEG+t4b4VH2mTxodBQ2KvyTKAQFIag3Vvxoq3XZ1ie4ijIQmVxiipL2+dZLDMkh1VIvAFyg==} - es-toolkit@1.30.1: resolution: {integrity: sha512-ZXflqanzH8BpHkDhFa10bBf6ONDCe84EPUm7SSICGzuuROSluT2ynTPtwn9PcRelMtorCRozSknI/U0MNYp0Uw==} @@ -15236,11 +15233,11 @@ snapshots: lodash-es: 4.17.21 strip-ansi: 7.1.0 - '@fern-api/docs-parsers@0.0.13(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': + '@fern-api/docs-parsers@0.0.14(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': dependencies: '@fern-api/logger': 0.4.24-rc1 '@fern-api/ui-core-utils': 0.0.0 - es-toolkit: 1.26.0 + es-toolkit: 1.30.1 openapi-types: 12.1.3 ts-essentials: 10.0.1(typescript@4.6.4) uuid: 9.0.1 @@ -17845,8 +17842,6 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - es-toolkit@1.26.0: {} - es-toolkit@1.30.1: {} es6-promise@3.3.1: {} From 73a5ea24a2a8cc13cde4b65f404a9b3713ad0b13 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Tue, 7 Jan 2025 17:56:21 -0500 Subject: [PATCH 03/11] docs dev working for design customer --- fern/apis/docs-yml/definition/docs.yml | 4 + packages/cli/cli/package.json | 2 +- packages/cli/cli/src/cli.ts | 8 +- .../src/commands/docs-dev/devDocsWorkspace.ts | 7 +- .../generate/generateDocsWorkspace.ts | 14 + .../writeDocsDefinitionForProject.ts | 14 + .../cli/configuration-loader/package.json | 2 +- packages/cli/configuration/package.json | 2 +- .../docs/types/ExperimentalConfig.ts | 2 + .../docs/types/ExperimentalConfig.ts | 2 + .../cli/docs-importers/commons/package.json | 2 +- .../cli/docs-importers/mintlify/package.json | 2 +- packages/cli/docs-markdown-utils/package.json | 2 +- packages/cli/docs-preview/package.json | 4 +- packages/cli/docs-preview/src/previewDocs.ts | 77 +- .../cli/docs-preview/src/runPreviewServer.ts | 7 +- packages/cli/docs-resolver/package.json | 6 +- .../src/ApiReferenceNodeConverter.ts | 2 +- .../src/ApiReferenceNodeConverterLatest.ts | 1000 +++++++++++++++++ .../src/DocsDefinitionResolver.ts | 50 +- .../docs-resolver/src/__test__/dry.test.ts | 2 + packages/cli/ete-tests/package.json | 2 +- .../remote-workspace-runner/package.json | 2 +- .../src/publishDocs.ts | 7 +- .../runRemoteGenerationForDocsWorkspace.ts | 4 + packages/cli/register/package.json | 2 +- packages/core/package.json | 2 +- pnpm-lock.yaml | 88 +- 28 files changed, 1217 insertions(+), 101 deletions(-) create mode 100644 packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts diff --git a/fern/apis/docs-yml/definition/docs.yml b/fern/apis/docs-yml/definition/docs.yml index 86e48294f70..e0556d34b18 100644 --- a/fern/apis/docs-yml/definition/docs.yml +++ b/fern/apis/docs-yml/definition/docs.yml @@ -974,6 +974,10 @@ types: If `disable-stream-toggle` is set to true, the stream toggle will be disabled. This behavior is unstable and may change in the future. + direct-openapi-parser: + type: optional<boolean> + docs: | + If `direct-openapi-parser` is set to true, the OpenAPI parser will be used directly, without Fern. PlaygroundSettings: properties: diff --git a/packages/cli/cli/package.json b/packages/cli/cli/package.json index bf5b094e129..12893a93d9b 100644 --- a/packages/cli/cli/package.json +++ b/packages/cli/cli/package.json @@ -46,7 +46,7 @@ "@fern-api/configuration-loader": "workspace:*", "@fern-api/core": "workspace:*", "@fern-api/core-utils": "workspace:*", - "@fern-api/docs-parsers": "^0.0.14", + "@fern-api/docs-parsers": "^0.0.19", "@fern-api/docs-preview": "workspace:*", "@fern-api/docs-resolver": "workspace:*", "@fern-api/docs-validator": "workspace:*", diff --git a/packages/cli/cli/src/cli.ts b/packages/cli/cli/src/cli.ts index 94d9c60d9b3..a015859b267 100644 --- a/packages/cli/cli/src/cli.ts +++ b/packages/cli/cli/src/cli.ts @@ -992,10 +992,6 @@ function addDocsPreviewCommand(cli: Argv<GlobalCliOptions>, cliContext: CliConte string: true, hidden: true, description: "Path of the local docs bundle to use" - }) - .option("v2", { - boolean: true, - description: "Use openapi parser v2" }), async (argv) => { let port: number; @@ -1005,7 +1001,6 @@ function addDocsPreviewCommand(cli: Argv<GlobalCliOptions>, cliContext: CliConte port = await getPort({ port: [3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010] }); } const bundlePath: string | undefined = argv.bundlePath; - const v2 = argv.v2; await previewDocsWorkspace({ loadProject: () => loadProjectAndRegisterWorkspacesWithContext(cliContext, { @@ -1014,8 +1009,7 @@ function addDocsPreviewCommand(cli: Argv<GlobalCliOptions>, cliContext: CliConte }), cliContext, port, - bundlePath, - v2 + bundlePath }); } ); diff --git a/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts b/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts index 18660ebc2f8..244231a0ac1 100644 --- a/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts +++ b/packages/cli/cli/src/commands/docs-dev/devDocsWorkspace.ts @@ -8,14 +8,12 @@ export async function previewDocsWorkspace({ loadProject, cliContext, port, - bundlePath, - v2 + bundlePath }: { loadProject: () => Promise<Project>; cliContext: CliContext; port: number; bundlePath?: string; - v2?: boolean; }): Promise<void> { const project = await loadProject(); const docsWorkspace = project.docsWorkspaces; @@ -60,8 +58,7 @@ export async function previewDocsWorkspace({ }, context, port, - bundlePath, - v2 + bundlePath }); }); } diff --git a/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts b/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts index 1bc187b20e8..558bd9e2097 100644 --- a/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts +++ b/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts @@ -4,6 +4,8 @@ import { Project } from "@fern-api/project-loader"; import { runRemoteGenerationForDocsWorkspace } from "@fern-api/remote-workspace-runner"; import { CliContext } from "../../cli-context/CliContext"; import { validateDocsWorkspaceAndLogIssues } from "../validate/validateDocsWorkspaceAndLogIssues"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { isNonNullish } from "@fern-api/core-utils"; export async function generateDocsWorkspace({ project, @@ -57,9 +59,21 @@ export async function generateDocsWorkspace({ }) ); + const ossWorkspaces = ( + await Promise.all( + project.apiWorkspaces.map(async (workspace) => { + if (workspace instanceof OSSWorkspace) { + return workspace as OSSWorkspace; + } + return null; + }) + ) + ).filter(isNonNullish); + await runRemoteGenerationForDocsWorkspace({ organization: project.config.organization, fernWorkspaces, + ossWorkspaces, docsWorkspace, context, token, diff --git a/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts b/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts index 3381c3d9722..fe7b737612b 100644 --- a/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts +++ b/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts @@ -4,6 +4,8 @@ import { AbsoluteFilePath } from "@fern-api/fs-utils"; import { writeFile } from "fs/promises"; import chalk from "chalk"; import { DocsDefinitionResolver } from "@fern-api/docs-resolver"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { isNonNullish } from "@fern-api/core-utils"; export async function writeDocsDefinitionForProject({ project, @@ -20,6 +22,17 @@ export async function writeDocsDefinitionForProject({ } await cliContext.runTaskForWorkspace(docsWorkspace, async (context) => { + const ossWorkspaces = ( + await Promise.all( + project.apiWorkspaces.map(async (workspace) => { + if (workspace instanceof OSSWorkspace) { + return workspace as OSSWorkspace; + } + return null; + }) + ) + ).filter(isNonNullish); + const fernWorkspaces = await Promise.all( project.apiWorkspaces.map(async (workspace) => { return workspace.toFernWorkspace( @@ -32,6 +45,7 @@ export async function writeDocsDefinitionForProject({ const docsResolver = new DocsDefinitionResolver( docsWorkspace.config.instances[0]?.url ?? "http://localhost:8080", docsWorkspace, + ossWorkspaces, fernWorkspaces, context ); diff --git a/packages/cli/configuration-loader/package.json b/packages/cli/configuration-loader/package.json index f17c15b5027..13583d0379d 100644 --- a/packages/cli/configuration-loader/package.json +++ b/packages/cli/configuration-loader/package.json @@ -33,7 +33,7 @@ "@fern-api/fs-utils": "workspace:*", "@fern-api/task-context": "workspace:*", "@fern-api/fern-definition-schema": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "@fern-fern/fiddle-sdk": "0.0.584", "@fern-fern/generators-sdk": "0.114.0-5745f9e74", "find-up": "^6.3.0", diff --git a/packages/cli/configuration/package.json b/packages/cli/configuration/package.json index a974fa02743..1bd393dd2b2 100644 --- a/packages/cli/configuration/package.json +++ b/packages/cli/configuration/package.json @@ -31,7 +31,7 @@ "@fern-api/core-utils": "workspace:*", "@fern-api/path-utils": "workspace:*", "@fern-api/fern-definition-schema": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "@fern-fern/fiddle-sdk": "0.0.584", "zod": "^3.22.3" }, diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts index 414ee0c3393..2be87a68ee0 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts @@ -14,4 +14,6 @@ export interface ExperimentalConfig { * This behavior is unstable and may change in the future. */ disableStreamToggle?: boolean; + /** If `direct-openapi-parser` is set to true, the OpenAPI parser will be used directly, without Fern. */ + directOpenapiParser?: boolean; } diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts index 95cf8362dee..3cef35969be 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts @@ -15,11 +15,13 @@ export const ExperimentalConfig: core.serialization.ObjectSchema< core.serialization.list(core.serialization.string()).optional() ), disableStreamToggle: core.serialization.property("disable-stream-toggle", core.serialization.boolean().optional()), + directOpenapiParser: core.serialization.property("direct-openapi-parser", core.serialization.boolean().optional()), }); export declare namespace ExperimentalConfig { interface Raw { "mdx-components"?: string[] | null; "disable-stream-toggle"?: boolean | null; + "direct-openapi-parser"?: boolean | null; } } diff --git a/packages/cli/docs-importers/commons/package.json b/packages/cli/docs-importers/commons/package.json index 5586b6810d0..aa6ebe3f18e 100644 --- a/packages/cli/docs-importers/commons/package.json +++ b/packages/cli/docs-importers/commons/package.json @@ -31,7 +31,7 @@ "@fern-api/configuration": "workspace:*", "@fern-api/fs-utils": "workspace:*", "@fern-api/task-context": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "js-yaml": "^4.1.0" }, "devDependencies": { diff --git a/packages/cli/docs-importers/mintlify/package.json b/packages/cli/docs-importers/mintlify/package.json index 1a6663ad16c..d2b91b955cc 100644 --- a/packages/cli/docs-importers/mintlify/package.json +++ b/packages/cli/docs-importers/mintlify/package.json @@ -34,7 +34,7 @@ "@fern-api/task-context": "workspace:*", "@fern-api/fs-utils": "workspace:*", "@fern-api/logger": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "gray-matter": "^4.0.3" }, "devDependencies": { diff --git a/packages/cli/docs-markdown-utils/package.json b/packages/cli/docs-markdown-utils/package.json index b59d51c478f..c6f903135f6 100644 --- a/packages/cli/docs-markdown-utils/package.json +++ b/packages/cli/docs-markdown-utils/package.json @@ -30,7 +30,7 @@ "dependencies": { "@fern-api/fs-utils": "workspace:*", "@fern-api/task-context": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "gray-matter": "^4.0.3", "mdast-util-from-markdown": "^2.0.1", "mdast-util-mdx": "^3.0.0", diff --git a/packages/cli/docs-preview/package.json b/packages/cli/docs-preview/package.json index f24e91e0df7..d1780560886 100644 --- a/packages/cli/docs-preview/package.json +++ b/packages/cli/docs-preview/package.json @@ -28,10 +28,12 @@ "depcheck": "depcheck" }, "dependencies": { + "@fern-api/core-utils": "workspace:*", "@fern-api/docs-resolver": "workspace:*", - "@fern-api/fdr-sdk": "0.127.3-6479103bd", + "@fern-api/fdr-sdk": "0.127.4-331678a74", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-sdk": "workspace:*", + "@fern-api/lazy-fern-workspace": "workspace:*", "@fern-api/logger": "workspace:*", "@fern-api/project-loader": "workspace:*", "@fern-api/register": "workspace:*", diff --git a/packages/cli/docs-preview/src/previewDocs.ts b/packages/cli/docs-preview/src/previewDocs.ts index 8a14568abba..38fce8e199b 100644 --- a/packages/cli/docs-preview/src/previewDocs.ts +++ b/packages/cli/docs-preview/src/previewDocs.ts @@ -18,19 +18,18 @@ import { Project } from "@fern-api/project-loader"; import { convertIrToFdrApi } from "@fern-api/register"; import { TaskContext } from "@fern-api/task-context"; import { v4 as uuidv4 } from "uuid"; -import { generateFdrFromOpenApiWorkspace } from "@fern-api/docs-resolver"; -import { isNonNullish } from "../../../commons/core-utils/src"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { isNonNullish } from "@fern-api/core-utils"; +import { parseDocsConfiguration } from "../../configuration-loader/src/docs-yml/parseDocsConfiguration"; export async function getPreviewDocsDefinition({ domain, project, - context, - v2 + context }: { domain: string; project: Project; context: TaskContext; - v2?: boolean; }): Promise<DocsV1Read.DocsDefinition> { const docsWorkspace = project.docsWorkspaces; const apiWorkspaces = project.apiWorkspaces; @@ -48,16 +47,33 @@ export async function getPreviewDocsDefinition({ ) ); - // TODO: remove this once we remove V1 API generation - const apiDefinitionIds: FdrAPI.ApiDefinitionId[] = []; + const ossWorkspaces = ( + await Promise.all( + apiWorkspaces.map(async (workspace) => { + if (workspace instanceof OSSWorkspace) { + return workspace as OSSWorkspace; + } + return null; + }) + ) + ).filter(isNonNullish); const apiCollector = new ReferencedAPICollector(context); + const apiCollectorV2 = new ReferencedAPICollectorV2(context); const filesV2: Record<string, DocsV1Read.File_> = {}; + const parsedDocsConfig = await parseDocsConfiguration({ + rawDocsConfiguration: docsWorkspace.config, + context, + absolutePathToFernFolder: docsWorkspace.absoluteFilePath, + absoluteFilepathToDocsConfig: docsWorkspace.absoluteFilepathToDocsConfig + }); + const resolver = new DocsDefinitionResolver( domain, docsWorkspace, + ossWorkspaces, fernWorkspaces, context, undefined, @@ -74,11 +90,8 @@ export async function getPreviewDocsDefinition({ fileId }; }), - async (opts) => { - const id = apiCollector.addReferencedAPI(opts); - apiDefinitionIds.push(FdrAPI.ApiDefinitionId(id)); - return id; - } + async (opts) => apiCollector.addReferencedAPI(opts), + async (opts) => apiCollectorV2.addReferencedAPI(opts) ); const writeDocsDefinition = await resolver.resolve(); @@ -91,19 +104,8 @@ export async function getPreviewDocsDefinition({ }); return { - apis: v2 ? {} : apiCollector.getAPIsForDefinition(), - apisV2: v2 - ? Object.fromEntries( - ( - await Promise.all( - project.apiWorkspaces.map(async (workspace, i) => [ - apiDefinitionIds[i], - await generateFdrFromOpenApiWorkspace(workspace, context) - ]) - ) - ).filter(isNonNullish) - ) - : {}, + apis: parsedDocsConfig.experimental?.directOpenapiParser ? {} : apiCollector.getAPIsForDefinition(), + apisV2: parsedDocsConfig.experimental?.directOpenapiParser ? apiCollectorV2.getAPIsForDefinition() : {}, config: readDocsConfig, files: {}, filesV2, @@ -165,3 +167,28 @@ class ReferencedAPICollector { return this.apis; } } + +class ReferencedAPICollectorV2 { + private readonly apis: Record<APIDefinitionID, FdrAPI.api.latest.ApiDefinition> = {}; + + constructor(private readonly context: TaskContext) {} + + public addReferencedAPI({ api }: { api: FdrAPI.api.latest.ApiDefinition }): APIDefinitionID { + try { + this.apis[api.id] = api; + return api.id; + } catch (e) { + // Print Error + const err = e as Error; + this.context.logger.error(`Failed to read referenced API: ${err?.message} ${err?.stack}`); + if (err.stack != null) { + this.context.logger.error(err?.stack); + } + throw e; + } + } + + public getAPIsForDefinition(): Record<FdrAPI.ApiDefinitionId, FdrAPI.api.latest.ApiDefinition> { + return this.apis; + } +} diff --git a/packages/cli/docs-preview/src/runPreviewServer.ts b/packages/cli/docs-preview/src/runPreviewServer.ts index 7ec931e916c..d78753c868b 100644 --- a/packages/cli/docs-preview/src/runPreviewServer.ts +++ b/packages/cli/docs-preview/src/runPreviewServer.ts @@ -55,8 +55,7 @@ export async function runPreviewServer({ validateProject, context, port, - bundlePath, - v2 + bundlePath }: { initialProject: Project; reloadProject: () => Promise<Project>; @@ -64,7 +63,6 @@ export async function runPreviewServer({ context: TaskContext; port: number; bundlePath?: string; - v2?: boolean; }): Promise<void> { if (bundlePath != null) { context.logger.info(`Using bundle from path: ${bundlePath}`); @@ -131,8 +129,7 @@ export async function runPreviewServer({ const newDocsDefinition = await getPreviewDocsDefinition({ domain: `${instance.host}${instance.pathname}`, project, - context, - v2 + context }); context.logger.info(`Reload completed in ${Date.now() - startTime}ms`); return newDocsDefinition; diff --git a/packages/cli/docs-resolver/package.json b/packages/cli/docs-resolver/package.json index 0c1544ed3e0..5c77f9a5a59 100644 --- a/packages/cli/docs-resolver/package.json +++ b/packages/cli/docs-resolver/package.json @@ -32,10 +32,10 @@ "@fern-api/configuration-loader": "workspace:*", "@fern-api/core-utils": "workspace:*", "@fern-api/docs-markdown-utils": "workspace:*", - "@fern-api/docs-parsers": "^0.0.14", - "@fern-api/fdr-sdk": "0.127.3-6479103bd", + "@fern-api/docs-parsers": "^0.0.19", + "@fern-api/fdr-sdk": "0.127.4-331678a74", "@fern-api/lazy-fern-workspace": "workspace:*", - "@fern-api/ui-core-utils": "0.127.3-6479103bd", + "@fern-api/ui-core-utils": "0.127.4-331678a74", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-generator": "workspace:*", "@fern-api/ir-sdk": "workspace:*", diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts index aa2d13f6aaa..77ae404959b 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts @@ -1,6 +1,6 @@ import { docsYml } from "@fern-api/configuration-loader"; import { isNonNullish } from "@fern-api/core-utils"; -import { APIV1Read, FernNavigation } from "@fern-api/fdr-sdk"; +import { APIV1Read, FdrAPI, FernNavigation } from "@fern-api/fdr-sdk"; import { AbsoluteFilePath, relative, RelativeFilePath } from "@fern-api/fs-utils"; import { TaskContext } from "@fern-api/task-context"; import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts new file mode 100644 index 00000000000..ede0ae31e0c --- /dev/null +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts @@ -0,0 +1,1000 @@ +import { docsYml } from "@fern-api/configuration-loader"; +import { isNonNullish } from "@fern-api/core-utils"; +import { FdrAPI, FernNavigation } from "@fern-api/fdr-sdk"; +import { AbsoluteFilePath, relative, RelativeFilePath } from "@fern-api/fs-utils"; +import { TaskContext } from "@fern-api/task-context"; +import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; +import { camelCase, kebabCase } from "lodash-es"; +import urlJoin from "url-join"; +import { ChangelogNodeConverter } from "./ChangelogNodeConverter"; +import { NodeIdGenerator } from "./NodeIdGenerator"; +import { stringifyEndpointPathParts } from "./utils/stringifyEndpointPathParts"; +import { titleCase, visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { threadId } from "worker_threads"; + +// TODO: these functions need an extra piece of information from the fdr latest shape, to see if the operation id takes precedence over slug generation +function getLatestEndpointUrlSlug(endpoint: FdrAPI.api.latest.endpoint.EndpointDefinition) { + const slugParts = endpoint.namespace?.map((subpackageId) => subpackageId.toString()) ?? []; + slugParts.push(kebabCase(endpoint.id.split(".").pop() ?? "")); + return endpoint.operationId != null ? kebabCase(endpoint.operationId) : urlJoin(slugParts); +} + +function getLatestWebSocketUrlSlug(webSocket: FdrAPI.api.latest.websocket.WebSocketChannel) { + const slugParts = webSocket.namespace?.map((subpackageId) => subpackageId.toString()) ?? []; + slugParts.push(kebabCase(webSocket.id.split(".").pop() ?? "")); + return webSocket.operationId != null ? kebabCase(webSocket.operationId) : urlJoin(slugParts); +} + +function getLatestWebhookUrlSlug(webhook: FdrAPI.api.latest.webhook.WebhookDefinition) { + const slugParts = webhook.namespace?.map((subpackageId) => subpackageId.toString()) ?? []; + slugParts.push(kebabCase(webhook.id.split(".").pop() ?? "")); + return webhook.operationId != null ? kebabCase(webhook.operationId) : urlJoin(slugParts); +} +// END TODO + +export class ApiReferenceNodeConverterLatest { + apiDefinitionId: FernNavigation.V1.ApiDefinitionId; + #api: FdrAPI.api.latest.ApiDefinition; + #visitedEndpoints = new Set<FernNavigation.V1.EndpointId>(); + #visitedWebSockets = new Set<FernNavigation.V1.WebSocketId>(); + #visitedWebhooks = new Set<FernNavigation.V1.WebhookId>(); + #visitedSubpackages = new Set<string>(); + #nodeIdToSubpackageId = new Map<string, string[]>(); + #children: FernNavigation.V1.ApiPackageChild[] = []; + #overviewPageId: FernNavigation.V1.PageId | undefined; + #slug: FernNavigation.V1.SlugGenerator; + #idgen: NodeIdGenerator; + #topLevelSubpackages: Map<string, FdrAPI.navigation.v1.ApiPackageNode> = new Map(); + private disableEndpointPairs; + constructor( + private apiSection: docsYml.DocsNavigationItem.ApiSection, + api: FdrAPI.api.latest.ApiDefinition, + parentSlug: FernNavigation.V1.SlugGenerator, + private workspace: OSSWorkspace, + private docsWorkspace: DocsWorkspace, + private taskContext: TaskContext, + private markdownFilesToFullSlugs: Map<AbsoluteFilePath, string>, + idgen: NodeIdGenerator + ) { + this.#api = api; + this.disableEndpointPairs = docsWorkspace.config.experimental?.disableStreamToggle ?? false; + this.apiDefinitionId = FernNavigation.V1.ApiDefinitionId(api.id); + + // we are assuming that the apiDefinitionId is unique. + this.#idgen = idgen; + + this.#overviewPageId = + this.apiSection.overviewAbsolutePath != null + ? FernNavigation.V1.PageId(this.toRelativeFilepath(this.apiSection.overviewAbsolutePath)) + : undefined; + + // the overview page markdown could contain a full slug, which would be used as the base slug for the API section. + const maybeFullSlug = + this.apiSection.overviewAbsolutePath != null + ? this.markdownFilesToFullSlugs.get(this.apiSection.overviewAbsolutePath) + : undefined; + + this.#slug = parentSlug.apply({ + fullSlug: maybeFullSlug?.split("/"), + skipUrlSlug: this.apiSection.skipUrlSlug, + urlSlug: this.apiSection.slug ?? kebabCase(this.apiSection.title) + }); + + // Step 1. Convert the navigation items that are manually defined in the API section. + if (this.apiSection.navigation != null) { + this.#children = this.#convertApiReferenceLayoutItems(this.apiSection.navigation, this.#slug); + } + + // Step 2. Fill in the any missing navigation items from the API definition + this.#children = this.#mergeAndFilterChildren( + this.#children.map((child) => this.#enrichApiPackageChild(child)), + this.#convertApiDefinitionPackage(this.#api, this.#slug) + ); + } + + public get(): FernNavigation.V1.ApiReferenceNode { + const pointsTo = FernNavigation.V1.followRedirects(this.#children); + const changelogNodeConverter = new ChangelogNodeConverter( + this.markdownFilesToFullSlugs, + this.workspace.changelog?.files.map((file) => file.absoluteFilepath), + this.docsWorkspace, + this.#idgen + ).orUndefined(); + return { + id: this.#idgen.get(this.apiDefinitionId), + type: "apiReference", + title: this.apiSection.title, + apiDefinitionId: this.apiDefinitionId, + overviewPageId: this.#overviewPageId, + paginated: this.apiSection.paginated, + slug: this.#slug.get(), + icon: this.apiSection.icon, + hidden: this.apiSection.hidden, + hideTitle: this.apiSection.flattened, + showErrors: this.apiSection.showErrors, + changelog: changelogNodeConverter?.toChangelogNode({ + parentSlug: this.#slug, + viewers: undefined + }), + children: this.#children, + availability: undefined, + pointsTo, + noindex: undefined, + playground: this.#convertPlaygroundSettings(this.apiSection.playground), + authed: undefined, + viewers: this.apiSection.viewers, + orphaned: this.apiSection.orphaned + }; + } + + #findEndpointByLocator(locator: string): FdrAPI.api.latest.endpoint.EndpointDefinition | undefined { + return ( + this.#api?.endpoints[FdrAPI.EndpointId(locator)] ?? + this.#api?.endpoints[FdrAPI.EndpointId(locator.split(".").pop() ?? "")] ?? + this.#api?.endpoints[FdrAPI.EndpointId(locator.split("/").pop() ?? "")] ?? + Object.values(this.#api?.endpoints ?? {}).find((endpoint) => endpoint.id.includes(locator)) ?? + Object.values(this.#api?.endpoints ?? {}).find( + (endpoint) => `${endpoint.method} ${stringifyEndpointPathParts(endpoint.path)}` === locator + ) ?? + Object.values(this.#api?.endpoints ?? {}).find( + (endpoint) => `STREAM ${stringifyEndpointPathParts(endpoint.path)}` === locator + ) + ); + } + + #findWebSocketByLocator(locator: string): FdrAPI.api.latest.websocket.WebSocketChannel | undefined { + return ( + this.#api?.websockets[FdrAPI.WebSocketId(locator)] ?? + this.#api?.websockets[FdrAPI.WebSocketId(locator.split(".").pop() ?? "")] ?? + this.#api?.websockets[FdrAPI.WebSocketId(locator.split("/").pop() ?? "")] ?? + Object.values(this.#api?.websockets ?? {}).find((endpoint) => endpoint.id.includes(locator)) + ); + } + + #findWebhookByLocator(locator: string): FdrAPI.api.latest.webhook.WebhookDefinition | undefined { + return ( + this.#api?.webhooks[FdrAPI.WebhookId(locator)] ?? + this.#api?.webhooks[FdrAPI.WebhookId(locator.split(".").pop() ?? "")] ?? + this.#api?.webhooks[FdrAPI.WebhookId(locator.split("/").pop() ?? "")] ?? + Object.values(this.#api?.webhooks ?? {}).find((endpoint) => endpoint.id.includes(locator)) + ); + } + + // Step 1 + + #convertApiReferenceLayoutItems( + navigation: docsYml.ParsedApiReferenceLayoutItem[], + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.ApiPackageChild[] { + return navigation + .map((item) => + visitDiscriminatedUnion(item)._visit<FernNavigation.V1.ApiPackageChild | undefined>({ + link: (link) => ({ + id: this.#idgen.get(link.url), + type: "link", + title: link.text, + icon: link.icon, + url: FernNavigation.Url(link.url) + }), + page: (page) => this.#toPageNode(page, parentSlug), + package: (pkg) => this.#convertPackage(pkg, parentSlug), + section: (section) => this.#convertSection(section, parentSlug), + item: ({ value: unknownIdentifier }): FernNavigation.V1.ApiPackageChild | undefined => + this.#convertUnknownIdentifier(unknownIdentifier, parentSlug), + endpoint: (endpoint) => this.#convertEndpoint(endpoint, parentSlug) + }) + ) + .filter(isNonNullish); + } + + #toPageNode( + page: docsYml.DocsNavigationItem.Page, + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.PageNode { + const pageId = FernNavigation.V1.PageId(this.toRelativeFilepath(page.absolutePath)); + const pageSlug = parentSlug.apply({ + fullSlug: this.markdownFilesToFullSlugs.get(page.absolutePath)?.split("/"), + urlSlug: page.slug ?? kebabCase(page.title) + }); + return { + id: this.#idgen.get(pageId), + type: "page", + pageId, + title: page.title, + slug: pageSlug.get(), + icon: page.icon, + hidden: page.hidden, + noindex: page.noindex, + authed: undefined, + viewers: page.viewers, + orphaned: page.orphaned + }; + } + + #convertPackage( + pkg: docsYml.ParsedApiReferenceLayoutItem.Package, + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.ApiPackageNode { + const overviewPageId = + pkg.overviewAbsolutePath != null + ? FernNavigation.V1.PageId(this.toRelativeFilepath(pkg.overviewAbsolutePath)) + : undefined; + + const maybeFullSlug = + pkg.overviewAbsolutePath != null ? this.markdownFilesToFullSlugs.get(pkg.overviewAbsolutePath) : undefined; + + const subpackage = + this.#api.subpackages[FdrAPI.api.v1.SubpackageId(pkg.package)] ?? + this.#api.subpackages[ + FdrAPI.api.v1.SubpackageId(pkg.package.replace(".yml", "").replace(".yaml", "").split(".").pop() ?? "") + ] ?? + this.#api.subpackages[FdrAPI.api.v1.SubpackageId(pkg.package.split("/").pop() ?? "")]; + + if (subpackage != null) { + const subpackageNodeId = this.#idgen.get(overviewPageId ?? `${this.apiDefinitionId}:${subpackage.id}`); + + if (this.#visitedSubpackages.has(subpackage.id)) { + this.taskContext.logger.error( + `Duplicate subpackage found in the API Reference layout: ${subpackage.id}` + ); + } + + this.#visitedSubpackages.add(subpackage.id); + this.#nodeIdToSubpackageId.set(subpackageNodeId, [subpackage.id]); + const urlSlug = pkg.slug ?? subpackage.name; + const slug = parentSlug.apply({ + fullSlug: maybeFullSlug?.split("/"), + skipUrlSlug: pkg.skipUrlSlug, + urlSlug + }); + const convertedItems = this.#convertApiReferenceLayoutItems(pkg.contents, slug); + const subpackageNode: FernNavigation.V1.ApiPackageNode = { + id: subpackageNodeId, + type: "apiPackage", + children: convertedItems, + title: pkg.title ?? subpackage.displayName ?? titleCase(subpackage.name), + slug: slug.get(), + icon: pkg.icon, + hidden: pkg.hidden, + overviewPageId, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: this.#convertPlaygroundSettings(pkg.playground), + authed: undefined, + viewers: pkg.viewers, + orphaned: pkg.orphaned + }; + + this.#topLevelSubpackages.set(subpackage.id, subpackageNode); + return subpackageNode; + } else { + this.taskContext.logger.warn( + `Subpackage ${pkg.package} not found in ${this.apiDefinitionId}, treating it as a section` + ); + const urlSlug = pkg.slug ?? kebabCase(pkg.package); + const slug = parentSlug.apply({ + fullSlug: maybeFullSlug?.split("/"), + skipUrlSlug: pkg.skipUrlSlug, + urlSlug + }); + const convertedItems = this.#convertApiReferenceLayoutItems(pkg.contents, slug); + const sectionNode: FernNavigation.V1.ApiPackageNode = { + id: this.#idgen.get(overviewPageId ?? `${this.apiDefinitionId}:${kebabCase(pkg.package)}`), + type: "apiPackage", + children: convertedItems, + title: pkg.title ?? pkg.package, + slug: slug.get(), + icon: pkg.icon, + hidden: pkg.hidden, + overviewPageId, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: this.#convertPlaygroundSettings(pkg.playground), + authed: undefined, + viewers: pkg.viewers, + orphaned: pkg.orphaned + }; + + this.#topLevelSubpackages.set(pkg.package, sectionNode); + return sectionNode; + } + } + + #convertSection( + section: docsYml.ParsedApiReferenceLayoutItem.Section, + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.ApiPackageNode { + const overviewPageId = + section.overviewAbsolutePath != null + ? FernNavigation.V1.PageId(this.toRelativeFilepath(section.overviewAbsolutePath)) + : undefined; + + const maybeFullSlug = + section.overviewAbsolutePath != null + ? this.markdownFilesToFullSlugs.get(section.overviewAbsolutePath) + : undefined; + + const nodeId = this.#idgen.get(overviewPageId ?? maybeFullSlug ?? parentSlug.get()); + + const subpackageIds = section.referencedSubpackages + .map((locator) => { + const subpackage = + this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(locator)] ?? + this.#api?.subpackages[ + FdrAPI.api.v1.SubpackageId( + locator.replace(".yml", "").replace(".yaml", "").split(".").pop() ?? "" + ) + ] ?? + this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(locator.split("/").pop() ?? "")]; + + return subpackage != null ? subpackage.id : undefined; + }) + .filter((subpackageId) => { + if (subpackageId == null) { + this.taskContext.logger.error(`Subpackage ${subpackageId} not found in ${this.apiDefinitionId}`); + } + return subpackageId != null; + }) + .filter(isNonNullish); + + this.#nodeIdToSubpackageId.set(nodeId, subpackageIds); + subpackageIds.forEach((subpackageId) => { + if (this.#visitedSubpackages.has(subpackageId)) { + this.taskContext.logger.error( + `Duplicate subpackage found in the API Reference layout: ${subpackageId}` + ); + } + this.#visitedSubpackages.add(subpackageId); + }); + + const urlSlug = section.slug ?? kebabCase(section.title); + const slug = parentSlug.apply({ + fullSlug: maybeFullSlug?.split("/"), + skipUrlSlug: section.skipUrlSlug, + urlSlug + }); + const convertedItems = this.#convertApiReferenceLayoutItems(section.contents, slug); + return { + id: nodeId, + type: "apiPackage", + children: convertedItems, + title: section.title, + slug: slug.get(), + icon: section.icon, + hidden: section.hidden, + overviewPageId, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: this.#convertPlaygroundSettings(section.playground), + authed: undefined, + viewers: section.viewers, + orphaned: section.orphaned + }; + } + + #convertUnknownIdentifier( + unknownIdentifier: string, + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.ApiPackageChild | undefined { + unknownIdentifier = unknownIdentifier.trim(); + // unknownIdentifier could either be a package, endpoint, websocket, or webhook. + // We need to determine which one it is. + + // if the unknownIdentifier is a subpackage, we need to check subpackage metadata, and any locators (strip .yml) + + const subpackage = + this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(unknownIdentifier)] ?? + this.#api?.subpackages[ + FdrAPI.api.v1.SubpackageId( + unknownIdentifier.replace(".yml", "").replace(".yaml", "").split(".").pop() ?? "" + ) + ] ?? + this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(unknownIdentifier.split("/").pop() ?? "")]; + + if (subpackage != null) { + const subpackageNodeId = this.#idgen.get(`${this.apiDefinitionId}:${subpackage.id}`); + + if (this.#visitedSubpackages.has(subpackage.id)) { + this.taskContext.logger.error( + `Duplicate subpackage found in the API Reference layout: ${subpackage.id}` + ); + } + + this.#visitedSubpackages.add(subpackage.id); + this.#nodeIdToSubpackageId.set(subpackageNodeId, [subpackage.id]); + const urlSlug = subpackage.name; + const slug = parentSlug.apply({ urlSlug }); + const subpackageNode: FernNavigation.V1.ApiPackageNode = { + id: subpackageNodeId, + type: "apiPackage", + children: [], + title: subpackage.displayName ?? titleCase(subpackage.name), + slug: slug.get(), + icon: undefined, + hidden: undefined, + overviewPageId: undefined, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + + this.#topLevelSubpackages.set(subpackage.id, subpackageNode); + return subpackageNode; + } + + // if the unknownIdentifier is not a subpackage, it could be an http endpoint, websocket, or webhook. + return this.#convertEndpoint( + { + type: "endpoint", + endpoint: unknownIdentifier, + title: undefined, + icon: undefined, + slug: undefined, + hidden: undefined, + playground: undefined, + viewers: undefined, + orphaned: undefined + }, + parentSlug + ); + } + + #convertEndpoint( + endpointItem: docsYml.ParsedApiReferenceLayoutItem.Endpoint, + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.ApiPackageChild | undefined { + const endpoint = this.#findEndpointByLocator(endpointItem.endpoint); + + if (endpoint != null) { + if (endpoint.id == null) { + throw new Error(`Expected Endpoint ID for ${endpoint.id}. Got undefined.`); + } + if (this.#visitedEndpoints.has(endpoint.id)) { + this.taskContext.logger.error(`Duplicate endpoint found in the API Reference layout: ${endpoint.id}`); + } + this.#visitedEndpoints.add(endpoint.id); + const endpointSlug = + endpointItem.slug != null + ? parentSlug.append(endpointItem.slug) + : parentSlug.apply({ urlSlug: getLatestEndpointUrlSlug(endpoint) }); + return { + id: this.#idgen.get(`${this.apiDefinitionId}:${endpoint.id}`), + type: "endpoint", + method: endpoint.method, + endpointId: endpoint.id, + apiDefinitionId: this.apiDefinitionId, + availability: FernNavigation.V1.convertAvailability(endpoint.availability), + isResponseStream: endpoint.responses?.[0]?.body.type === "stream", + title: endpointItem.title ?? endpoint.displayName ?? stringifyEndpointPathParts(endpoint.path), + slug: endpointSlug.get(), + icon: endpointItem.icon, + hidden: endpointItem.hidden, + playground: this.#convertPlaygroundSettings(endpointItem.playground), + authed: undefined, + viewers: endpointItem.viewers, + orphaned: endpointItem.orphaned + }; + } + + const webSocket = this.#findWebSocketByLocator(endpointItem.endpoint); + + if (webSocket != null) { + if (webSocket.id == null) { + throw new Error(`Expected WebSocket ID for ${webSocket.id}. Got undefined.`); + } + if (this.#visitedWebSockets.has(webSocket.id)) { + this.taskContext.logger.error( + `Duplicate web socket found in the API Reference layout: ${webSocket.id}` + ); + } + this.#visitedWebSockets.add(webSocket.id); + return { + id: this.#idgen.get(`${this.apiDefinitionId}:${webSocket.id}`), + type: "webSocket", + webSocketId: webSocket.id, + title: endpointItem.title ?? webSocket.displayName ?? stringifyEndpointPathParts(webSocket.path), + slug: (endpointItem.slug != null + ? parentSlug.append(endpointItem.slug) + : parentSlug.apply({ urlSlug: getLatestWebSocketUrlSlug(webSocket) }) + ).get(), + icon: endpointItem.icon, + hidden: endpointItem.hidden, + apiDefinitionId: this.apiDefinitionId, + availability: FernNavigation.V1.convertAvailability(webSocket.availability), + playground: this.#convertPlaygroundSettings(endpointItem.playground), + authed: undefined, + viewers: endpointItem.viewers, + orphaned: endpointItem.orphaned + }; + } + + const webhook = this.#findWebhookByLocator(endpointItem.endpoint); + + if (webhook != null) { + if (webhook.id == null) { + throw new Error(`Expected Webhook ID for ${webhook.id}. Got undefined.`); + } + if (this.#visitedWebhooks.has(webhook.id)) { + this.taskContext.logger.error(`Duplicate webhook found in the API Reference layout: ${webhook.id}`); + } + this.#visitedWebhooks.add(webhook.id); + return { + id: this.#idgen.get(`${this.apiDefinitionId}:${webhook.id}`), + type: "webhook", + webhookId: webhook.id, + method: webhook.method, + title: endpointItem.title ?? webhook.displayName ?? urlJoin("/", ...webhook.path), + slug: (endpointItem.slug != null + ? parentSlug.append(endpointItem.slug) + : parentSlug.apply({ urlSlug: getLatestWebhookUrlSlug(webhook) }) + ).get(), + icon: endpointItem.icon, + hidden: endpointItem.hidden, + apiDefinitionId: this.apiDefinitionId, + availability: undefined, + authed: undefined, + viewers: endpointItem.viewers, + orphaned: endpointItem.orphaned + }; + } + + this.taskContext.logger.error("Unknown identifier in the API Reference layout: ", endpointItem.endpoint); + + return; + } + + // Step 2 + + #mergeAndFilterChildren( + left: FernNavigation.V1.ApiPackageChild[], + right: FernNavigation.V1.ApiPackageChild[] + ): FernNavigation.V1.ApiPackageChild[] { + return this.mergeEndpointPairs([...left, ...right]).filter((child) => + child.type === "apiPackage" ? child.children.length > 0 : true + ); + } + + #enrichApiPackageChild(child: FernNavigation.V1.ApiPackageChild): FernNavigation.V1.ApiPackageChild { + if (child.type === "apiPackage") { + // expand the subpackage to include children that haven't been visited yet + const slug = FernNavigation.V1.SlugGenerator.init(child.slug); + const subpackageIds = this.#nodeIdToSubpackageId.get(child.id) ?? []; + const subpackageChildren = subpackageIds.flatMap((subpackageId) => + this.#convertApiDefinitionPackageId(subpackageId, slug) + ); + + // recursively apply enrichment to children + const enrichedChildren = child.children.map((innerChild) => this.#enrichApiPackageChild(innerChild)); + + // combine children with subpackage (tacked on at the end to preserve order) + const children = this.#mergeAndFilterChildren(enrichedChildren, subpackageChildren); + + return { + ...child, + children, + pointsTo: undefined + }; + } + return child; + } + + #convertApiDefinitionPackage( + pkg: FdrAPI.api.latest.ApiDefinition, + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.ApiPackageChild[] { + // if an endpoint, websocket, webhook, or subpackage is not visited, add it to the additional children list + let additionalChildren: FernNavigation.V1.ApiPackageChild[] = []; + + Object.entries(pkg.subpackages).forEach(([subpackageId, subpackageMetadata]) => { + if (this.#visitedSubpackages.has(subpackageId)) { + return; + } + + const slug = parentSlug.apply({ + urlSlug: subpackageMetadata.name + }); + const subpackageNode: FernNavigation.V1.ApiPackageNode = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${subpackageId}`), + type: "apiPackage", + children: [], + title: subpackageMetadata.displayName ?? titleCase(subpackageMetadata.name), + slug: slug.get(), + icon: undefined, + hidden: undefined, + overviewPageId: undefined, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + this.#topLevelSubpackages.set(subpackageId, subpackageNode); + additionalChildren.push(subpackageNode); + }); + + Object.entries(pkg.endpoints).forEach(([endpointId, endpoint]) => { + if (endpointId == null) { + throw new Error(`Expected Endpoint ID for ${endpoint.id}. Got undefined.`); + } + if (this.#visitedEndpoints.has(FdrAPI.EndpointId(endpointId))) { + return; + } + + const endpointSlug = parentSlug.apply({ + urlSlug: getLatestEndpointUrlSlug(endpoint) + }); + + const endpointNode: FernNavigation.V1.EndpointNode = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${endpointId}`), + type: "endpoint", + method: endpoint.method, + endpointId: FdrAPI.EndpointId(endpointId), + apiDefinitionId: this.apiDefinitionId, + availability: FernNavigation.V1.convertAvailability(endpoint.availability), + isResponseStream: endpoint.responses?.[0]?.body.type === "stream", + title: endpoint.displayName ?? stringifyEndpointPathParts(endpoint.path), + slug: endpointSlug.get(), + icon: undefined, + hidden: undefined, + playground: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + + if (endpoint.namespace != null && endpoint.namespace.length > 0) { + const firstNamespacePart = camelCase(endpoint.namespace[0]); + if (firstNamespacePart != null) { + let subpackageCursor = this.#topLevelSubpackages.get(firstNamespacePart); + if (subpackageCursor == null) { + this.taskContext.logger.error( + `Subpackage ${firstNamespacePart} not found in ${this.apiDefinitionId}` + ); + return; + } + let slugGenerator = parentSlug.apply({ urlSlug: subpackageCursor.slug }); + + for (const namespacePart of endpoint.namespace.slice(1)) { + let newSubpackageCursor: FdrAPI.navigation.v1.ApiPackageChild | undefined = + subpackageCursor.children.find( + (child) => + child.type === "apiPackage" && child.id === camelCase(namespacePart.toString()) + ); + slugGenerator = slugGenerator.append(kebabCase(namespacePart)); + if (newSubpackageCursor == null) { + newSubpackageCursor = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${namespacePart}`), + type: "apiPackage", + children: [], + title: titleCase(namespacePart), + slug: slugGenerator.get(), + icon: undefined, + hidden: undefined, + overviewPageId: undefined, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + } + if (newSubpackageCursor != null && newSubpackageCursor.type === "apiPackage") { + subpackageCursor = newSubpackageCursor; + } + } + + subpackageCursor.children.push(endpointNode); + } + } else { + additionalChildren.push(endpointNode); + } + }); + + Object.entries(pkg.websockets).forEach(([webSocketId, webSocket]) => { + if (webSocketId == null) { + throw new Error(`Expected WebSocket ID for ${webSocket.id}. Got undefined.`); + } + if (this.#visitedWebSockets.has(FdrAPI.WebSocketId(webSocketId))) { + return; + } + const webSocketNode: FernNavigation.V1.WebSocketNode = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${webSocketId}`), + type: "webSocket", + webSocketId: FdrAPI.WebSocketId(webSocketId), + title: webSocket.displayName ?? stringifyEndpointPathParts(webSocket.path), + slug: parentSlug + .apply({ + urlSlug: getLatestWebSocketUrlSlug(webSocket) + }) + .get(), + icon: undefined, + hidden: undefined, + apiDefinitionId: this.apiDefinitionId, + availability: FernNavigation.V1.convertAvailability(webSocket.availability), + playground: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + if (webSocket.namespace != null && webSocket.namespace.length > 0) { + const firstNamespacePart = webSocket.namespace[0]; + if (firstNamespacePart != null) { + let subpackageCursor = this.#topLevelSubpackages.get(firstNamespacePart); + if (subpackageCursor == null) { + throw new Error(`Subpackage ${firstNamespacePart} not found in ${this.apiDefinitionId}`); + } + let slugGenerator = parentSlug.apply({ urlSlug: subpackageCursor.slug }); + + for (const namespacePart of webSocket.namespace.slice(1)) { + let newSubpackageCursor: FdrAPI.navigation.v1.ApiPackageChild | undefined = + subpackageCursor.children.find( + (child) => child.type === "apiPackage" && child.id === namespacePart.toString() + ); + slugGenerator = slugGenerator.append(namespacePart); + if (newSubpackageCursor == null) { + newSubpackageCursor = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${namespacePart}`), + type: "apiPackage", + children: [], + title: titleCase(namespacePart), + slug: slugGenerator.get(), + icon: undefined, + hidden: undefined, + overviewPageId: undefined, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + } + if (newSubpackageCursor != null && newSubpackageCursor.type === "apiPackage") { + subpackageCursor = newSubpackageCursor; + } + } + subpackageCursor.children.push(webSocketNode); + } + } else { + additionalChildren.push(webSocketNode); + } + }); + + Object.entries(pkg.webhooks).forEach(([webhookId, webhook]) => { + if (webhookId == null) { + throw new Error(`Expected Webhook ID for ${webhook.id}. Got undefined.`); + } + if (this.#visitedWebhooks.has(FdrAPI.WebhookId(webhookId))) { + return; + } + const webhookNode: FernNavigation.V1.WebhookNode = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${webhookId}`), + type: "webhook", + webhookId: FdrAPI.WebhookId(webhookId), + method: webhook.method, + title: webhook.displayName ?? titleCase(webhook.id), + slug: parentSlug + .apply({ + urlSlug: getLatestWebhookUrlSlug(webhook) + }) + .get(), + icon: undefined, + hidden: undefined, + apiDefinitionId: this.apiDefinitionId, + availability: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + + if (webhook.namespace != null && webhook.namespace.length > 0) { + const firstNamespacePart = webhook.namespace[0]; + if (firstNamespacePart != null) { + let subpackageCursor = this.#topLevelSubpackages.get(firstNamespacePart); + if (subpackageCursor == null) { + throw new Error(`Subpackage ${firstNamespacePart} not found in ${this.apiDefinitionId}`); + } + let slugGenerator = parentSlug.apply({ urlSlug: subpackageCursor.slug }); + + for (const namespacePart of webhook.namespace.slice(1)) { + let newSubpackageCursor: FdrAPI.navigation.v1.ApiPackageChild | undefined = + subpackageCursor.children.find( + (child) => child.type === "apiPackage" && child.id === namespacePart.toString() + ); + slugGenerator = slugGenerator.append(namespacePart); + if (newSubpackageCursor == null) { + newSubpackageCursor = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${namespacePart}`), + type: "apiPackage", + children: [], + title: namespacePart, + slug: slugGenerator.get(), + icon: undefined, + hidden: undefined, + overviewPageId: undefined, + availability: undefined, + apiDefinitionId: this.apiDefinitionId, + pointsTo: undefined, + noindex: undefined, + playground: undefined, + authed: undefined, + viewers: undefined, + orphaned: undefined + }; + } + if (newSubpackageCursor != null && newSubpackageCursor.type === "apiPackage") { + subpackageCursor = newSubpackageCursor; + } + } + subpackageCursor.children.push(webhookNode); + } + } else { + additionalChildren.push(webhookNode); + } + }); + + additionalChildren = this.mergeEndpointPairs(additionalChildren); + + if (this.apiSection.alphabetized) { + additionalChildren = additionalChildren.sort((a, b) => { + const aTitle = a.type === "endpointPair" ? a.nonStream.title : a.title; + const bTitle = b.type === "endpointPair" ? b.nonStream.title : b.title; + return aTitle.localeCompare(bTitle); + }); + } + + return additionalChildren; + } + + // TODO: optimize this with some DP, where we store incrementally found endpoints (constructing an indexed tree of subpackages) + #resolveSubpackage(subpackageId: string): FdrAPI.api.latest.ApiDefinition { + const endpoints = Object.fromEntries( + Object.entries(this.#api?.endpoints ?? {}).filter( + ([_, endpoint]) => + endpoint.namespace != null && + camelCase(endpoint.namespace[endpoint.namespace.length - 1]) === + FdrAPI.api.v1.SubpackageId(subpackageId) + ) + ); + const websockets = Object.fromEntries( + Object.entries(this.#api?.websockets ?? {}).filter( + ([_, webSocket]) => + webSocket.namespace != null && + camelCase(webSocket.namespace[webSocket.namespace.length - 1]) === + FdrAPI.api.v1.SubpackageId(subpackageId) + ) + ); + const webhooks = Object.fromEntries( + Object.entries(this.#api?.webhooks ?? {}).filter( + ([_, webhook]) => + webhook.namespace != null && + camelCase(webhook.namespace[webhook.namespace.length - 1]) === + FdrAPI.api.v1.SubpackageId(subpackageId) + ) + ); + return { + id: FdrAPI.ApiDefinitionId(subpackageId), + endpoints, + websockets, + webhooks, + types: {}, + subpackages: {}, + auths: {}, + globalHeaders: undefined + }; + } + + #convertApiDefinitionPackageId( + packageId: string | undefined, + parentSlug: FernNavigation.V1.SlugGenerator + ): FernNavigation.V1.ApiPackageChild[] { + const pkg = packageId != null ? this.#resolveSubpackage(packageId) : undefined; + + if (pkg == null) { + this.taskContext.logger.error(`Subpackage ${packageId} not found in ${this.apiDefinitionId}`); + return []; + } + + // if an endpoint, websocket, webhook, or subpackage is not visited, add it to the additional children list + return this.#convertApiDefinitionPackage(pkg, parentSlug); + } + + #convertPlaygroundSettings( + playgroundSettings?: docsYml.RawSchemas.PlaygroundSettings + ): FernNavigation.V1.PlaygroundSettings | undefined { + if (playgroundSettings) { + return { + environments: + playgroundSettings.environments != null && playgroundSettings.environments.length > 0 + ? playgroundSettings.environments.map((environmentId) => + FernNavigation.V1.EnvironmentId(environmentId) + ) + : undefined, + button: + playgroundSettings.button != null && playgroundSettings.button.href + ? { href: FernNavigation.V1.Url(playgroundSettings.button.href) } + : undefined, + "limit-websocket-messages-per-connection": + playgroundSettings.limitWebsocketMessagesPerConnection != null + ? playgroundSettings.limitWebsocketMessagesPerConnection + : undefined + }; + } + + return; + } + + private mergeEndpointPairs(children: FernNavigation.V1.ApiPackageChild[]): FernNavigation.V1.ApiPackageChild[] { + if (this.disableEndpointPairs) { + return children; + } + + const toRet: FernNavigation.V1.ApiPackageChild[] = []; + + const methodAndPathToEndpointNode = new Map<string, FernNavigation.V1.EndpointNode>(); + children.forEach((child) => { + if (child.type !== "endpoint") { + toRet.push(child); + return; + } + + const endpoint = this.#api?.endpoints[child.endpointId]; + if (endpoint == null) { + throw new Error(`Endpoint ${child.endpointId} not found`); + } + + const methodAndPath = `${endpoint.method} ${stringifyEndpointPathParts(endpoint.path)}`; + + const existing = methodAndPathToEndpointNode.get(methodAndPath); + methodAndPathToEndpointNode.set(methodAndPath, child); + + if (existing == null || existing.isResponseStream === child.isResponseStream) { + toRet.push(child); + return; + } + + const idx = toRet.indexOf(existing); + const stream = child.isResponseStream ? child : existing; + const nonStream = child.isResponseStream ? existing : child; + const pairNode: FernNavigation.V1.EndpointPairNode = { + id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${nonStream.endpointId}+${stream.endpointId}`), + type: "endpointPair", + stream, + nonStream + }; + + toRet[idx] = pairNode; + }); + + return toRet; + } + + private toRelativeFilepath(filepath: AbsoluteFilePath): RelativeFilePath; + private toRelativeFilepath(filepath: AbsoluteFilePath | undefined): RelativeFilePath | undefined; + private toRelativeFilepath(filepath: AbsoluteFilePath | undefined): RelativeFilePath | undefined { + if (filepath == null) { + return undefined; + } + return relative(this.docsWorkspace.absoluteFilePath, filepath); + } +} diff --git a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts index 011c62af493..9fdac46d3f4 100644 --- a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts +++ b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts @@ -6,7 +6,7 @@ import { replaceReferencedCode, replaceReferencedMarkdown } from "@fern-api/docs-markdown-utils"; -import { APIV1Write, DocsV1Write, FernNavigation } from "@fern-api/fdr-sdk"; +import { APIV1Write, DocsV1Write, FdrAPI, FernNavigation } from "@fern-api/fdr-sdk"; import { AbsoluteFilePath, listFiles, relative, RelativeFilePath, relativize, resolve } from "@fern-api/fs-utils"; import { generateIntermediateRepresentation } from "@fern-api/ir-generator"; import { IntermediateRepresentation } from "@fern-api/ir-sdk"; @@ -26,6 +26,9 @@ import { convertIrToApiDefinition } from "./utils/convertIrToApiDefinition"; import { collectFilesFromDocsConfig } from "./utils/getImageFilepathsToUpload"; import { wrapWithHttps } from "./wrapWithHttps"; import { SourceResolverImpl } from "@fern-api/cli-source-resolver"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { generateFdrFromOpenApiWorkspace } from "./utils/generateFdrFromOpenApiWorkspace"; +import { ApiReferenceNodeConverterLatest } from "./ApiReferenceNodeConverterLatest"; dayjs.extend(utc); @@ -51,6 +54,8 @@ type RegisterApiFn = (opts: { apiName?: string; }) => AsyncOrSync<string>; +type RegisterApiV2Fn = (opts: { api: FdrAPI.api.latest.ApiDefinition; apiName?: string }) => AsyncOrSync<string>; + const defaultUploadFiles: UploadFilesFn = (files) => { return files.map((file) => ({ ...file, fileId: String(file.relativeFilePath) })); }; @@ -61,16 +66,23 @@ const defaultRegisterApi: RegisterApiFn = async ({ ir }) => { return `${ir.apiName.snakeCase.unsafeName}-${apiCounter}`; }; +const defaultRegisterApiV2: RegisterApiV2Fn = async ({ api }) => { + apiCounter++; + return `${api.id}-${apiCounter}`; +}; + export class DocsDefinitionResolver { constructor( private domain: string, private docsWorkspace: DocsWorkspace, + private ossWorkspaces: OSSWorkspace[], private fernWorkspaces: FernWorkspace[], private taskContext: TaskContext, // Optional private editThisPage?: docsYml.RawSchemas.EditThisPageConfig, private uploadFiles: UploadFilesFn = defaultUploadFiles, - private registerApi: RegisterApiFn = defaultRegisterApi + private registerApi: RegisterApiFn = defaultRegisterApi, + private registerApiV2: RegisterApiV2Fn = defaultRegisterApiV2 ) {} #idgen = NodeIdGenerator.init(); @@ -346,6 +358,18 @@ export class DocsDefinitionResolver { throw new Error("Failed to load API Definition referenced in docs"); } + private getOpenApiWorkspaceForApiSection(apiSection: docsYml.DocsNavigationItem.ApiSection): OSSWorkspace { + if (this.ossWorkspaces.length === 1 && this.ossWorkspaces[0] != null) { + return this.ossWorkspaces[0]; + } else if (apiSection.apiName != null) { + const ossWorkspace = this.ossWorkspaces.find((workspace) => workspace.workspaceName === apiSection.apiName); + if (ossWorkspace != null) { + return ossWorkspace; + } + } + throw new Error("Failed to load API Definition referenced in docs"); + } + private async toRootNode(): Promise<FernNavigation.V1.RootNode> { const slug = FernNavigation.V1.SlugGenerator.init(FernNavigation.slugjoin(this.getDocsBasePath())); const id = this.#idgen.get("root"); @@ -542,6 +566,28 @@ export class DocsDefinitionResolver { item: docsYml.DocsNavigationItem.ApiSection, parentSlug: FernNavigation.V1.SlugGenerator ): Promise<FernNavigation.V1.ApiReferenceNode> { + if (this.parsedDocsConfig.experimental?.directOpenapiParser) { + const workspace = this.getOpenApiWorkspaceForApiSection(item); + const api = await generateFdrFromOpenApiWorkspace(workspace, this.taskContext); + if (api == null) { + throw new Error("Failed to generate API Definition from OpenAPI workspace"); + } + await this.registerApiV2({ + api, + apiName: item.apiName + }); + const node = new ApiReferenceNodeConverterLatest( + item, + api, + parentSlug, + workspace, + this.docsWorkspace, + this.taskContext, + this.markdownFilesToFullSlugs, + this.#idgen + ); + return node.get(); + } const workspace = this.getFernWorkspaceForApiSection(item); const snippetsConfig = convertDocsSnippetsConfigToFdr(item.snippetsConfiguration); const ir = generateIntermediateRepresentation({ diff --git a/packages/cli/docs-resolver/src/__test__/dry.test.ts b/packages/cli/docs-resolver/src/__test__/dry.test.ts index afa718448b1..a88497675dc 100644 --- a/packages/cli/docs-resolver/src/__test__/dry.test.ts +++ b/packages/cli/docs-resolver/src/__test__/dry.test.ts @@ -22,9 +22,11 @@ it.skip("converts to api reference node", async () => { "domain", docsWorkspace, [], + [], context, undefined, async (_files) => [], + async (_opts) => "", async (_opts) => "" ); diff --git a/packages/cli/ete-tests/package.json b/packages/cli/ete-tests/package.json index 59a36fa6329..541d2a5e7e1 100644 --- a/packages/cli/ete-tests/package.json +++ b/packages/cli/ete-tests/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@fern-api/configuration": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "@fern-api/fs-utils": "workspace:*", "@fern-api/logging-execa": "workspace:*", "@fern-typescript/fetcher": "workspace:*", diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/package.json b/packages/cli/generation/remote-generation/remote-workspace-runner/package.json index 5d3c27eb9ae..6b771eeb3dd 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/package.json +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/package.json @@ -35,7 +35,7 @@ "@fern-api/core-utils": "workspace:*", "@fern-api/docs-resolver": "workspace:*", "@fern-api/logging-execa": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-generator": "workspace:*", "@fern-api/ir-migrations": "workspace:*", diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts index 5f1684edaa3..5fff325b6bb 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts @@ -16,6 +16,7 @@ import { chunk } from "lodash-es"; import * as mime from "mime-types"; import terminalLink from "terminal-link"; import { measureImageSizes } from "./measureImageSizes"; +import { OSSWorkspace } from "../../../../workspace/lazy-fern-workspace/src"; const MEASURE_IMAGE_BATCH_SIZE = 10; const UPLOAD_FILE_BATCH_SIZE = 10; @@ -33,6 +34,7 @@ export async function publishDocs({ domain, customDomains, fernWorkspaces, + ossWorkspaces, context, preview, editThisPage, @@ -44,6 +46,7 @@ export async function publishDocs({ domain: string; customDomains: string[]; fernWorkspaces: FernWorkspace[]; + ossWorkspaces: OSSWorkspace[]; context: TaskContext; preview: boolean; editThisPage: docsYml.RawSchemas.FernDocsConfig.EditThisPageConfig | undefined; @@ -60,6 +63,7 @@ export async function publishDocs({ const resolver = new DocsDefinitionResolver( domain, docsWorkspace, + ossWorkspaces, fernWorkspaces, context, editThisPage, @@ -158,7 +162,8 @@ export async function publishDocs({ const response = await fdr.api.v1.register.registerApiDefinition({ orgId: CjsFdrSdk.OrgId(organization), apiId: CjsFdrSdk.ApiId(ir.apiName.originalName), - definition: apiDefinition + definition: apiDefinition, + definitionV2: undefined }); if (response.ok) { diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts b/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts index d21227958df..32faf84c7d8 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts @@ -3,10 +3,12 @@ import { replaceEnvVariables } from "@fern-api/core-utils"; import { TaskContext } from "@fern-api/task-context"; import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; import { publishDocs } from "./publishDocs"; +import { OSSWorkspace } from "../../../../workspace/lazy-fern-workspace/src"; export async function runRemoteGenerationForDocsWorkspace({ organization, fernWorkspaces, + ossWorkspaces, docsWorkspace, context, token, @@ -15,6 +17,7 @@ export async function runRemoteGenerationForDocsWorkspace({ }: { organization: string; fernWorkspaces: FernWorkspace[]; + ossWorkspaces: OSSWorkspace[]; docsWorkspace: DocsWorkspace; context: TaskContext; token: FernToken; @@ -76,6 +79,7 @@ export async function runRemoteGenerationForDocsWorkspace({ organization, context, fernWorkspaces, + ossWorkspaces, preview, editThisPage: maybeInstance.editThisPage, isPrivate: maybeInstance.private diff --git a/packages/cli/register/package.json b/packages/cli/register/package.json index ec70d43f350..31904aad626 100644 --- a/packages/cli/register/package.json +++ b/packages/cli/register/package.json @@ -33,7 +33,7 @@ "@fern-api/configuration": "workspace:*", "@fern-api/core": "workspace:*", "@fern-api/core-utils": "workspace:*", - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-generator": "workspace:*", "@fern-api/ir-sdk": "workspace:*", diff --git a/packages/core/package.json b/packages/core/package.json index cc5200e19ee..b8c710ddcf5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,7 +28,7 @@ "depcheck": "depcheck" }, "dependencies": { - "@fern-fern/fdr-cjs-sdk": "0.127.3-6479103bd", + "@fern-fern/fdr-cjs-sdk": "0.127.4-331678a74", "@fern-fern/generators-sdk": "0.114.0-5745f9e74", "@fern-api/venus-api-sdk": "0.10.2", "@fern-fern/fdr-test-sdk": "^0.0.5297", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf8dd41766a..6ef20670a0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3856,8 +3856,8 @@ importers: specifier: workspace:* version: link:../../commons/core-utils '@fern-api/docs-parsers': - specifier: ^0.0.14 - version: 0.0.14(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) + specifier: ^0.0.19 + version: 0.0.19(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/docs-preview': specifier: workspace:* version: link:../docs-preview @@ -4239,8 +4239,8 @@ importers: specifier: workspace:* version: link:../../commons/path-utils '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 '@fern-fern/fiddle-sdk': specifier: 0.0.584 version: 0.0.584 @@ -4288,8 +4288,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 '@fern-fern/fiddle-sdk': specifier: 0.0.584 version: 0.0.584 @@ -4364,8 +4364,8 @@ importers: specifier: workspace:* version: link:../../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -4416,8 +4416,8 @@ importers: specifier: workspace:* version: link:../../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -4453,8 +4453,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -4504,18 +4504,24 @@ importers: packages/cli/docs-preview: dependencies: + '@fern-api/core-utils': + specifier: workspace:* + version: link:../../commons/core-utils '@fern-api/docs-resolver': specifier: workspace:* version: link:../docs-resolver '@fern-api/fdr-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd(typescript@4.6.4) + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74(typescript@4.6.4) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils '@fern-api/ir-sdk': specifier: workspace:* version: link:../../ir-sdk + '@fern-api/lazy-fern-workspace': + specifier: workspace:* + version: link:../workspace/lazy-fern-workspace '@fern-api/logger': specifier: workspace:* version: link:../logger @@ -4623,11 +4629,11 @@ importers: specifier: workspace:* version: link:../docs-markdown-utils '@fern-api/docs-parsers': - specifier: ^0.0.14 - version: 0.0.14(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) + specifier: ^0.0.19 + version: 0.0.19(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/fdr-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd(typescript@4.6.4) + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74(typescript@4.6.4) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -4647,8 +4653,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-api/ui-core-utils': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 dayjs: specifier: ^1.11.11 version: 1.11.11 @@ -4705,8 +4711,8 @@ importers: specifier: workspace:* version: link:../../commons/logging-execa '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 '@fern-typescript/fetcher': specifier: workspace:* version: link:../../../generators/typescript/utils/core-utilities/fetcher @@ -5509,8 +5515,8 @@ importers: specifier: workspace:* version: link:../../../workspace/loader '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 '@fern-fern/fiddle-sdk': specifier: 0.0.584 version: 0.0.584 @@ -5979,8 +5985,8 @@ importers: specifier: workspace:* version: link:../task-context '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -6757,8 +6763,8 @@ importers: specifier: 0.10.2 version: 0.10.2 '@fern-fern/fdr-cjs-sdk': - specifier: 0.127.3-6479103bd - version: 0.127.3-6479103bd + specifier: 0.127.4-331678a74 + version: 0.127.4-331678a74 '@fern-fern/fdr-test-sdk': specifier: ^0.0.5297 version: 0.0.5297 @@ -8094,8 +8100,8 @@ packages: '@fern-api/core-utils@0.4.24-rc1': resolution: {integrity: sha512-aYu4lQK2qZIKzTF9TeFrICTPJ/zGEZUEWQmZt6pJeHu+R6afrcCBNkUleWU1OpHlDbe+xXUUBOktRg0PM9Hywg==} - '@fern-api/docs-parsers@0.0.14': - resolution: {integrity: sha512-82MLnKAATRiCjD3Xcf60Mw9vGSZ2yj7iy8xaRGWBGudDjZBb2p4kcj95musonaF2f/IFNkq4iX3AONgB7FNhYA==} + '@fern-api/docs-parsers@0.0.19': + resolution: {integrity: sha512-DWXyxW25dG/lCwAilvHcd394vHpSAF8wF1Q8P/PFxKBATHkVZxXHfmNeRC+soS4rn4E0vPOZJO3QR8d+ULZZkg==} '@fern-api/dynamic-ir-sdk@53.24.0': resolution: {integrity: sha512-4XvzvSsh7lNZz5oYN0LH+FRpFGxg9TjfdipSJMpgHvPShe85m/tcfbOzbS0DHBpQGlKb/gHQtQRPAoBR1wdDAw==} @@ -8103,8 +8109,8 @@ packages: '@fern-api/dynamic-ir-sdk@54.0.0': resolution: {integrity: sha512-5ewX7042AGuw697jA6ubulCIQbfTM2HC8UbHxTakE0px871HJTRwHNT2Y9o2kjMbsRXTzyYdb5lOVr+85sc/KQ==} - '@fern-api/fdr-sdk@0.127.3-6479103bd': - resolution: {integrity: sha512-9WPubeQHSQJ2ece9lG2TY4Rc879e7AhvDYOamlYqsD5gy16eATRUkOvj7d6pWch5JwYJaMM8jcdZrQ9dltf/qQ==} + '@fern-api/fdr-sdk@0.127.4-331678a74': + resolution: {integrity: sha512-uat4R+gWFoe8UT67q0Yhc6dDwHUn84Jb6do7r5tDk4fUWcy4oPaCMQxutvyclNj6M3AUtFY8uEy/8dCNgk3ADA==} '@fern-api/logger@0.4.24-rc1': resolution: {integrity: sha512-yh0E2F3K3IPnJZcE4dv+u8I51iKgTgv/reinKo4K5YmYEG1iLtw5vBEYMOPkQmsYFPAKIh++OMB/6TrsahMWew==} @@ -8118,8 +8124,8 @@ packages: '@fern-api/ui-core-utils@0.0.0': resolution: {integrity: sha512-8T3YLd+n8z5Vs+WNRIwH6PUW31ZC4/lkRD5G2+qyBcdePfOVYV3CHp3eiUrSSArOr0SJmzN/mQwPm3iAaey7nw==} - '@fern-api/ui-core-utils@0.127.3-6479103bd': - resolution: {integrity: sha512-WLrD3Ad2zzPGfVickTiJ1JoKN97Px9Dzu+Zl8oHcreIdf3Ngmf5LH/jCcwRx7LenRKdRj6+UMFBxA1ZdVDg3ng==} + '@fern-api/ui-core-utils@0.127.4-331678a74': + resolution: {integrity: sha512-hBUEOTKVbGqfKQ+cA8MR1jvn4XYVqTBkW3I0G1LF0NSgzRr8dvpL+BYGmhLQkXweOjTfYsVNIvqzX80oUU2Urw==} '@fern-api/venus-api-sdk@0.10.2': resolution: {integrity: sha512-BD18ZNJeWYvztRXSUWY2lfZ8jFUbNAfHWctvIsWyXgI7C8EL3N0+CWUGD5cZ1QqRnq42nm6XJdWRKhny3yrz4g==} @@ -8127,8 +8133,8 @@ packages: '@fern-fern/docs-config@0.0.80': resolution: {integrity: sha512-wAZCNxwM4qIPn3idoIihPP65KWPjNuoTSfdP4+5f8K2jJLgs5cdkY0pm4cQlGWvi5K6b+lfbUWDaslnXscJ2gQ==} - '@fern-fern/fdr-cjs-sdk@0.127.3-6479103bd': - resolution: {integrity: sha512-nYpgLaorgK0s+6P6iKC8TYa4QF4RjZxr4y5/jogpkQc74sm2fmZq7vpRiCTmr9wt+Ei6EZqbemqvULh6G8y25g==} + '@fern-fern/fdr-cjs-sdk@0.127.4-331678a74': + resolution: {integrity: sha512-ZWWiN71iD/xK9zZPBac5dL2UU3iJPOmK9SazVwPkTxcAMRgVX+Pu/hNKz07NEvQD79l4otCmK5PB8IYMyqlncw==} '@fern-fern/fdr-test-sdk@0.0.5297': resolution: {integrity: sha512-jrZUZ6oIA64LHtrv77xEq0X7qJhT9xRMGWhKcLjIUArMsD7h6KMWUHVdoB/1MP0Mz/uL/E2xmM31SOgJicpIcA==} @@ -15233,7 +15239,7 @@ snapshots: lodash-es: 4.17.21 strip-ansi: 7.1.0 - '@fern-api/docs-parsers@0.0.14(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': + '@fern-api/docs-parsers@0.0.19(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': dependencies: '@fern-api/logger': 0.4.24-rc1 '@fern-api/ui-core-utils': 0.0.0 @@ -15265,9 +15271,9 @@ snapshots: '@fern-api/dynamic-ir-sdk@54.0.0': {} - '@fern-api/fdr-sdk@0.127.3-6479103bd(typescript@4.6.4)': + '@fern-api/fdr-sdk@0.127.4-331678a74(typescript@4.6.4)': dependencies: - '@fern-api/ui-core-utils': 0.127.3-6479103bd + '@fern-api/ui-core-utils': 0.127.4-331678a74 '@ungap/structured-clone': 1.2.0 dayjs: 1.11.11 es-toolkit: 1.30.1 @@ -15312,7 +15318,7 @@ snapshots: title: 3.5.3 ua-parser-js: 1.0.37 - '@fern-api/ui-core-utils@0.127.3-6479103bd': + '@fern-api/ui-core-utils@0.127.4-331678a74': dependencies: date-fns: 4.1.0 date-fns-tz: 3.2.0(date-fns@4.1.0) @@ -15334,7 +15340,7 @@ snapshots: '@fern-fern/docs-config@0.0.80': {} - '@fern-fern/fdr-cjs-sdk@0.127.3-6479103bd': + '@fern-fern/fdr-cjs-sdk@0.127.4-331678a74': dependencies: form-data: 4.0.0 formdata-node: 6.0.3 From cdc8d6831c73d2b73415f60674ad8b17453c26cf Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Tue, 7 Jan 2025 18:00:10 -0500 Subject: [PATCH 04/11] json schema --- docs-yml.schema.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs-yml.schema.json b/docs-yml.schema.json index e0df9708bf3..86d1d728042 100644 --- a/docs-yml.schema.json +++ b/docs-yml.schema.json @@ -2214,6 +2214,16 @@ "type": "null" } ] + }, + "direct-openapi-parser": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false From 0c89750ef95e6eb17f8b43d4f628096fb588ad7f Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Tue, 7 Jan 2025 19:16:36 -0500 Subject: [PATCH 05/11] remove unused deps --- packages/cli/cli/package.json | 2 -- pnpm-lock.yaml | 34 ---------------------------------- 2 files changed, 36 deletions(-) diff --git a/packages/cli/cli/package.json b/packages/cli/cli/package.json index 9942ff1caad..c04bc4833de 100644 --- a/packages/cli/cli/package.json +++ b/packages/cli/cli/package.json @@ -45,7 +45,6 @@ "@fern-api/configuration-loader": "workspace:*", "@fern-api/core": "workspace:*", "@fern-api/core-utils": "workspace:*", - "@fern-api/docs-parsers": "^0.0.19", "@fern-api/docs-preview": "workspace:*", "@fern-api/docs-resolver": "workspace:*", "@fern-api/docs-validator": "workspace:*", @@ -105,7 +104,6 @@ "js-yaml": "^4.1.0", "latest-version": "^9.0.0", "lodash-es": "^4.17.21", - "openapi-types": "^12.1.3", "ora": "^7.0.1", "@trivago/prettier-plugin-sort-imports": "^5.2.1", "prettier": "^3.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77af3d10e13..fd6232aaa36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3941,9 +3941,6 @@ importers: '@fern-api/core-utils': specifier: workspace:* version: link:../../commons/core-utils - '@fern-api/docs-parsers': - specifier: ^0.0.19 - version: 0.0.19(@types/node@18.15.3)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@5.7.2) '@fern-api/docs-preview': specifier: workspace:* version: link:../docs-preview @@ -4124,9 +4121,6 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.17.21 - openapi-types: - specifier: ^12.1.3 - version: 12.1.3 ora: specifier: ^7.0.1 version: 7.0.1 @@ -15676,34 +15670,6 @@ snapshots: lodash-es: 4.17.21 strip-ansi: 7.1.0 - '@fern-api/docs-parsers@0.0.19(@types/node@18.15.3)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@5.7.2)': - dependencies: - '@fern-api/logger': 0.4.24-rc1 - '@fern-api/ui-core-utils': 0.0.0 - es-toolkit: 1.30.1 - openapi-types: 12.1.3 - ts-essentials: 10.0.1(typescript@5.7.2) - uuid: 9.0.1 - vitest: 2.1.8(@types/node@18.15.3)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) - whatwg-mimetype: 4.0.0 - transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/node' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - typescript - '@fern-api/docs-parsers@0.0.19(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': dependencies: '@fern-api/logger': 0.4.24-rc1 From 530977e7bc1d809f6c5eff1c4c0e9da4147c9985 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Tue, 7 Jan 2025 19:18:31 -0500 Subject: [PATCH 06/11] fix issues with newer fdr version --- packages/cli/docs-importers/mintlify/src/convertMarkdown.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli/docs-importers/mintlify/src/convertMarkdown.ts b/packages/cli/docs-importers/mintlify/src/convertMarkdown.ts index b6c5ec18610..8e0d39d5989 100644 --- a/packages/cli/docs-importers/mintlify/src/convertMarkdown.ts +++ b/packages/cli/docs-importers/mintlify/src/convertMarkdown.ts @@ -2,7 +2,7 @@ import { readFile } from "fs/promises"; import grayMatter from "gray-matter"; import { FernDocsBuilder } from "@fern-api/docs-importer-commons"; -import { AbsoluteFilePath, RelativeFilePath, dirname, join, relativize } from "@fern-api/fs-utils"; +import { AbsoluteFilePath, RelativeFilePath, dirname, join } from "@fern-api/fs-utils"; import { FernRegistry as CjsFdrSdk, FernRegistry } from "@fern-fern/fdr-cjs-sdk"; @@ -84,7 +84,9 @@ export async function convertMarkdown({ "twitter:card": undefined, noindex: undefined, nofollow: undefined, - "jsonld:breadcrumb": undefined + "jsonld:breadcrumb": undefined, + logo: undefined, + keywords: undefined }, content: transformedContent }; From d02c88a4822136f94b211202af5d94bd6a1e3e00 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Tue, 7 Jan 2025 19:25:53 -0500 Subject: [PATCH 07/11] linter --- packages/cli/docs-resolver/package.json | 1 - pnpm-lock.yaml | 68 ------------------------- 2 files changed, 69 deletions(-) diff --git a/packages/cli/docs-resolver/package.json b/packages/cli/docs-resolver/package.json index 342a46e2a60..fcb25ce6e9c 100644 --- a/packages/cli/docs-resolver/package.json +++ b/packages/cli/docs-resolver/package.json @@ -52,7 +52,6 @@ "depcheck": "^1.4.6", "eslint": "^8.56.0", "openapi-types": "^10.0.0", - "organize-imports-cli": "^0.10.0", "prettier": "^2.7.1", "typescript": "4.6.4", "vitest": "^2.0.5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd6232aaa36..d31e87d907e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4767,9 +4767,6 @@ importers: openapi-types: specifier: ^10.0.0 version: 10.0.0 - organize-imports-cli: - specifier: ^0.10.0 - version: 0.10.0 prettier: specifier: ^2.7.1 version: 2.8.8 @@ -9145,12 +9142,6 @@ packages: '@types/stream-json@1.7.7': resolution: {integrity: sha512-hHG7cLQ09H/m9i0jzL6UJAeLLxIWej90ECn0svO4T8J0nGcl89xZDQ2ujT4WKlvg0GWkcxJbjIDzW/v7BYUM6Q==} - '@types/strip-bom@3.0.0': - resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - - '@types/strip-json-comments@0.0.30': - resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - '@types/swagger2openapi@7.0.4': resolution: {integrity: sha512-ffMqzciTDihOKH4Q//9Ond1yb5JP1P5FC/aFPsLK4blea1Fwk2aYctiNCkAh5etDYFswFXS+5LV/vuGkf+PU6A==} @@ -10394,10 +10385,6 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - editorconfig@0.15.3: - resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} - hasBin: true - ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -11947,9 +11934,6 @@ packages: resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} engines: {node: 20 || >=22} - lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} - lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -12479,10 +12463,6 @@ packages: resolution: {integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==} engines: {node: '>=16'} - organize-imports-cli@0.10.0: - resolution: {integrity: sha512-cVyNEeiDxX/zA6gdK1QS2rr3TK1VymIkT0LagnAk4f6eE0IC0bo3BeUkMzm3q3GnCJzYC+6lfuMpBE0Cequ7Vg==} - hasBin: true - os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -12822,9 +12802,6 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -13231,9 +13208,6 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - sigmund@1.0.1: - resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -13789,9 +13763,6 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tsconfig@7.0.0: - resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} - tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -14399,9 +14370,6 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -16779,10 +16747,6 @@ snapshots: '@types/node': 18.15.3 '@types/stream-chain': 2.1.0 - '@types/strip-bom@3.0.0': {} - - '@types/strip-json-comments@0.0.30': {} - '@types/swagger2openapi@7.0.4': dependencies: '@types/node': 18.15.3 @@ -18201,13 +18165,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - editorconfig@0.15.3: - dependencies: - commander: 2.20.3 - lru-cache: 4.1.5 - semver: 5.7.2 - sigmund: 1.0.1 - ee-first@1.1.1: {} ejs@3.1.10: @@ -20306,11 +20263,6 @@ snapshots: lru-cache@11.0.1: {} - lru-cache@4.1.5: - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 - lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -21161,13 +21113,6 @@ snapshots: string-width: 6.1.0 strip-ansi: 7.1.0 - organize-imports-cli@0.10.0: - dependencies: - chalk: 4.1.2 - editorconfig: 0.15.3 - ts-morph: 15.1.0 - tsconfig: 7.0.0 - os-tmpdir@1.0.2: {} p-finally@1.0.0: {} @@ -21448,8 +21393,6 @@ snapshots: proxy-from-env@1.1.0: {} - pseudomap@1.0.2: {} - psl@1.9.0: {} punycode@2.3.1: {} @@ -21967,8 +21910,6 @@ snapshots: siginfo@2.0.0: {} - sigmund@1.0.1: {} - signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -22672,13 +22613,6 @@ snapshots: minimist: 1.2.6 strip-bom: 3.0.0 - tsconfig@7.0.0: - dependencies: - '@types/strip-bom': 3.0.0 - '@types/strip-json-comments': 0.0.30 - strip-bom: 3.0.0 - strip-json-comments: 2.0.1 - tslib@1.14.1: {} tslib@2.6.2: {} @@ -23552,8 +23486,6 @@ snapshots: y18n@5.0.8: {} - yallist@2.1.2: {} - yallist@3.1.1: {} yallist@4.0.0: {} From bb2acdb070ed702163c61871bc4877e047885b0e Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Tue, 7 Jan 2025 19:38:12 -0500 Subject: [PATCH 08/11] format --- ...nerateOpenApiToFdrApiDefinitionForWorkspaces.ts | 6 ++++-- .../src/commands/generate/generateDocsWorkspace.ts | 4 ++-- .../writeDocsDefinitionForProject.ts | 6 +++--- packages/cli/docs-preview/src/previewDocs.ts | 5 +++-- .../docs-resolver/src/ApiReferenceNodeConverter.ts | 2 +- .../src/ApiReferenceNodeConverterLatest.ts | 14 ++++++++------ .../docs-resolver/src/DocsDefinitionResolver.ts | 8 ++++---- .../src/utils/generateFdrFromOpenApiWorkspace.ts | 13 +++++++------ .../remote-workspace-runner/src/publishDocs.ts | 2 +- .../src/runRemoteGenerationForDocsWorkspace.ts | 2 +- 10 files changed, 34 insertions(+), 28 deletions(-) diff --git a/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts b/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts index 01111d8101f..720a6f3f7a1 100644 --- a/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts +++ b/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts @@ -1,9 +1,11 @@ import { writeFile } from "fs/promises"; import path from "path"; -import { CliContext } from "../../cli-context/CliContext"; + import { generateFdrFromOpenApiWorkspace } from "@fern-api/docs-resolver"; -import { Project } from "@fern-api/project-loader"; import { AbsoluteFilePath, stringifyLargeObject } from "@fern-api/fs-utils"; +import { Project } from "@fern-api/project-loader"; + +import { CliContext } from "../../cli-context/CliContext"; export async function generateOpenApiToFdrApiDefinitionForWorkspaces({ project, diff --git a/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts b/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts index 13017c14214..389500144c3 100644 --- a/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts +++ b/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts @@ -1,12 +1,12 @@ import { createOrganizationIfDoesNotExist } from "@fern-api/auth"; +import { isNonNullish } from "@fern-api/core-utils"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { askToLogin } from "@fern-api/login"; import { Project } from "@fern-api/project-loader"; import { runRemoteGenerationForDocsWorkspace } from "@fern-api/remote-workspace-runner"; import { CliContext } from "../../cli-context/CliContext"; import { validateDocsWorkspaceAndLogIssues } from "../validate/validateDocsWorkspaceAndLogIssues"; -import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; -import { isNonNullish } from "@fern-api/core-utils"; export async function generateDocsWorkspace({ project, diff --git a/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts b/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts index 19e1fbbe053..92b9aa4ce02 100644 --- a/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts +++ b/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts @@ -1,13 +1,13 @@ import chalk from "chalk"; import { writeFile } from "fs/promises"; +import { isNonNullish } from "@fern-api/core-utils"; import { DocsDefinitionResolver } from "@fern-api/docs-resolver"; +import { AbsoluteFilePath } from "@fern-api/fs-utils"; import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; -import { isNonNullish } from "@fern-api/core-utils"; - import { Project } from "@fern-api/project-loader"; + import { CliContext } from "../../cli-context/CliContext"; -import { AbsoluteFilePath } from "@fern-api/fs-utils"; export async function writeDocsDefinitionForProject({ project, diff --git a/packages/cli/docs-preview/src/previewDocs.ts b/packages/cli/docs-preview/src/previewDocs.ts index f860dfab4b2..1ec1f6abb51 100644 --- a/packages/cli/docs-preview/src/previewDocs.ts +++ b/packages/cli/docs-preview/src/previewDocs.ts @@ -1,5 +1,6 @@ import { v4 as uuidv4 } from "uuid"; +import { isNonNullish } from "@fern-api/core-utils"; import { DocsDefinitionResolver } from "@fern-api/docs-resolver"; import { APIV1Read, @@ -15,11 +16,11 @@ import { } from "@fern-api/fdr-sdk"; import { convertToFernHostAbsoluteFilePath } from "@fern-api/fs-utils"; import { IntermediateRepresentation } from "@fern-api/ir-sdk"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { Project } from "@fern-api/project-loader"; import { convertIrToFdrApi } from "@fern-api/register"; import { TaskContext } from "@fern-api/task-context"; -import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; -import { isNonNullish } from "@fern-api/core-utils"; + import { parseDocsConfiguration } from "../../configuration-loader/src/docs-yml/parseDocsConfiguration"; export async function getPreviewDocsDefinition({ diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts index 437f385feb2..c1d2092462b 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts @@ -4,7 +4,7 @@ import urlJoin from "url-join"; import { docsYml } from "@fern-api/configuration-loader"; import { isNonNullish } from "@fern-api/core-utils"; import { APIV1Read, FernNavigation } from "@fern-api/fdr-sdk"; -import { AbsoluteFilePath, relative, RelativeFilePath } from "@fern-api/fs-utils"; +import { AbsoluteFilePath, RelativeFilePath, relative } from "@fern-api/fs-utils"; import { TaskContext } from "@fern-api/task-context"; import { titleCase, visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts index ede0ae31e0c..0bb81339273 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts @@ -1,17 +1,19 @@ +import { camelCase, kebabCase } from "lodash-es"; +import urlJoin from "url-join"; +import { threadId } from "worker_threads"; + import { docsYml } from "@fern-api/configuration-loader"; import { isNonNullish } from "@fern-api/core-utils"; import { FdrAPI, FernNavigation } from "@fern-api/fdr-sdk"; -import { AbsoluteFilePath, relative, RelativeFilePath } from "@fern-api/fs-utils"; +import { AbsoluteFilePath, RelativeFilePath, relative } from "@fern-api/fs-utils"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { TaskContext } from "@fern-api/task-context"; +import { titleCase, visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; -import { camelCase, kebabCase } from "lodash-es"; -import urlJoin from "url-join"; + import { ChangelogNodeConverter } from "./ChangelogNodeConverter"; import { NodeIdGenerator } from "./NodeIdGenerator"; import { stringifyEndpointPathParts } from "./utils/stringifyEndpointPathParts"; -import { titleCase, visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; -import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; -import { threadId } from "worker_threads"; // TODO: these functions need an extra piece of information from the fdr latest shape, to see if the operation id takes precedence over slug generation function getLatestEndpointUrlSlug(endpoint: FdrAPI.api.latest.endpoint.EndpointDefinition) { diff --git a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts index 6ae8feeb41f..15db2285e27 100644 --- a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts +++ b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts @@ -15,22 +15,22 @@ import { replaceReferencedMarkdown } from "@fern-api/docs-markdown-utils"; import { APIV1Write, DocsV1Write, FdrAPI, FernNavigation } from "@fern-api/fdr-sdk"; -import { AbsoluteFilePath, listFiles, relative, RelativeFilePath, resolve } from "@fern-api/fs-utils"; +import { AbsoluteFilePath, RelativeFilePath, listFiles, relative, resolve } from "@fern-api/fs-utils"; import { generateIntermediateRepresentation } from "@fern-api/ir-generator"; import { IntermediateRepresentation } from "@fern-api/ir-sdk"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { TaskContext } from "@fern-api/task-context"; import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; import { ApiReferenceNodeConverter } from "./ApiReferenceNodeConverter"; +import { ApiReferenceNodeConverterLatest } from "./ApiReferenceNodeConverterLatest"; import { ChangelogNodeConverter } from "./ChangelogNodeConverter"; import { NodeIdGenerator } from "./NodeIdGenerator"; import { convertDocsSnippetsConfigToFdr } from "./utils/convertDocsSnippetsConfigToFdr"; import { convertIrToApiDefinition } from "./utils/convertIrToApiDefinition"; +import { generateFdrFromOpenApiWorkspace } from "./utils/generateFdrFromOpenApiWorkspace"; import { collectFilesFromDocsConfig } from "./utils/getImageFilepathsToUpload"; import { wrapWithHttps } from "./wrapWithHttps"; -import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; -import { generateFdrFromOpenApiWorkspace } from "./utils/generateFdrFromOpenApiWorkspace"; -import { ApiReferenceNodeConverterLatest } from "./ApiReferenceNodeConverterLatest"; dayjs.extend(utc); diff --git a/packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts b/packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts index d67cb531b35..ba186b30b4a 100644 --- a/packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts +++ b/packages/cli/docs-resolver/src/utils/generateFdrFromOpenApiWorkspace.ts @@ -1,12 +1,13 @@ -import { getAllOpenAPISpecs, LazyFernWorkspace, OSSWorkspace, OpenAPILoader } from "@fern-api/lazy-fern-workspace"; +import { merge } from "lodash-es"; +import { OpenAPIV3_1 } from "openapi-types"; + +import { AbstractAPIWorkspace } from "@fern-api/api-workspace-commons"; import { + BaseOpenApiV3_1ConverterNodeContext, ErrorCollector, - OpenApiDocumentConverterNode, - BaseOpenApiV3_1ConverterNodeContext + OpenApiDocumentConverterNode } from "@fern-api/docs-parsers"; -import { OpenAPIV3_1 } from "openapi-types"; -import { merge } from "lodash-es"; -import { AbstractAPIWorkspace } from "@fern-api/api-workspace-commons"; +import { LazyFernWorkspace, OSSWorkspace, OpenAPILoader, getAllOpenAPISpecs } from "@fern-api/lazy-fern-workspace"; import { TaskContext } from "@fern-api/task-context"; export async function generateFdrFromOpenApiWorkspace( diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts index d5be4e30270..d0941a96b7b 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts @@ -18,8 +18,8 @@ import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; import { FernRegistry as CjsFdrSdk } from "@fern-fern/fdr-cjs-sdk"; -import { measureImageSizes } from "./measureImageSizes"; import { OSSWorkspace } from "../../../../workspace/lazy-fern-workspace/src"; +import { measureImageSizes } from "./measureImageSizes"; const MEASURE_IMAGE_BATCH_SIZE = 10; const UPLOAD_FILE_BATCH_SIZE = 10; diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts b/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts index 67959fed421..1e409d22867 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/src/runRemoteGenerationForDocsWorkspace.ts @@ -3,8 +3,8 @@ import { replaceEnvVariables } from "@fern-api/core-utils"; import { TaskContext } from "@fern-api/task-context"; import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; -import { publishDocs } from "./publishDocs"; import { OSSWorkspace } from "../../../../workspace/lazy-fern-workspace/src"; +import { publishDocs } from "./publishDocs"; export async function runRemoteGenerationForDocsWorkspace({ organization, From f598160259412d55b33af0bcb350a7d7a8f3675b Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Wed, 8 Jan 2025 16:39:04 -0500 Subject: [PATCH 09/11] added fixture test for when fdr-sdk supports directory imports --- packages/cli/docs-resolver/package.json | 2 +- .../src/ApiReferenceNodeConverter.ts | 154 ++------ .../src/ApiReferenceNodeConverterLatest.ts | 205 ++++------- .../fixtures/openapi-latest/fern/docs.yml | 9 + .../openapi-latest/fern/fern.config.json | 4 + .../openapi-latest/fern/generators.yml | 1 + .../fixtures/openapi-latest/fern/openapi.yml | 332 ++++++++++++++++++ .../src/__test__/openapi-latest.test.ts | 79 +++++ .../src/utils/convertIrToApiDefinition.ts | 8 +- .../src/utils/convertPlaygroundSettings.ts | 27 ++ .../src/utils/enrichApiPackageChild.ts | 48 +++ .../src/utils/mergeAndFilterChildren.ts | 27 ++ .../src/utils/mergeEndpointPairs.ts | 58 +++ .../cli/docs-resolver/src/utils/toPageNode.ts | 42 +++ .../src/utils/toRelativeFilepath.ts | 17 + pnpm-lock.yaml | 10 +- 16 files changed, 750 insertions(+), 273 deletions(-) create mode 100644 packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/docs.yml create mode 100644 packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/fern.config.json create mode 100644 packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/generators.yml create mode 100644 packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/openapi.yml create mode 100644 packages/cli/docs-resolver/src/__test__/openapi-latest.test.ts create mode 100644 packages/cli/docs-resolver/src/utils/convertPlaygroundSettings.ts create mode 100644 packages/cli/docs-resolver/src/utils/enrichApiPackageChild.ts create mode 100644 packages/cli/docs-resolver/src/utils/mergeAndFilterChildren.ts create mode 100644 packages/cli/docs-resolver/src/utils/mergeEndpointPairs.ts create mode 100644 packages/cli/docs-resolver/src/utils/toPageNode.ts create mode 100644 packages/cli/docs-resolver/src/utils/toRelativeFilepath.ts diff --git a/packages/cli/docs-resolver/package.json b/packages/cli/docs-resolver/package.json index fcb25ce6e9c..d5669729c38 100644 --- a/packages/cli/docs-resolver/package.json +++ b/packages/cli/docs-resolver/package.json @@ -31,7 +31,7 @@ "@fern-api/configuration-loader": "workspace:*", "@fern-api/core-utils": "workspace:*", "@fern-api/docs-markdown-utils": "workspace:*", - "@fern-api/docs-parsers": "^0.0.19", + "@fern-api/docs-parsers": "^0.0.20", "@fern-api/fdr-sdk": "0.127.4-331678a74", "@fern-api/lazy-fern-workspace": "workspace:*", "@fern-api/ui-core-utils": "0.127.4-331678a74", diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts index c1d2092462b..5c222480b4a 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts @@ -4,7 +4,7 @@ import urlJoin from "url-join"; import { docsYml } from "@fern-api/configuration-loader"; import { isNonNullish } from "@fern-api/core-utils"; import { APIV1Read, FernNavigation } from "@fern-api/fdr-sdk"; -import { AbsoluteFilePath, RelativeFilePath, relative } from "@fern-api/fs-utils"; +import { AbsoluteFilePath } from "@fern-api/fs-utils"; import { TaskContext } from "@fern-api/task-context"; import { titleCase, visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; @@ -12,8 +12,14 @@ import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; import { ApiDefinitionHolder } from "./ApiDefinitionHolder"; import { ChangelogNodeConverter } from "./ChangelogNodeConverter"; import { NodeIdGenerator } from "./NodeIdGenerator"; +import { convertPlaygroundSettings } from "./utils/convertPlaygroundSettings"; +import { enrichApiPackageChild } from "./utils/enrichApiPackageChild"; import { isSubpackage } from "./utils/isSubpackage"; +import { mergeAndFilterChildren } from "./utils/mergeAndFilterChildren"; +import { mergeEndpointPairs } from "./utils/mergeEndpointPairs"; import { stringifyEndpointPathParts } from "./utils/stringifyEndpointPathParts"; +import { toPageNode } from "./utils/toPageNode"; +import { toRelativeFilepath } from "./utils/toRelativeFilepath"; export class ApiReferenceNodeConverter { apiDefinitionId: FernNavigation.V1.ApiDefinitionId; @@ -47,7 +53,7 @@ export class ApiReferenceNodeConverter { this.#overviewPageId = this.apiSection.overviewAbsolutePath != null - ? FernNavigation.V1.PageId(this.toRelativeFilepath(this.apiSection.overviewAbsolutePath)) + ? FernNavigation.V1.PageId(toRelativeFilepath(this.docsWorkspace, this.apiSection.overviewAbsolutePath)) : undefined; // the overview page markdown could contain a full slug, which would be used as the base slug for the API section. @@ -148,24 +154,13 @@ export class ApiReferenceNodeConverter { page: docsYml.DocsNavigationItem.Page, parentSlug: FernNavigation.V1.SlugGenerator ): FernNavigation.V1.PageNode { - const pageId = FernNavigation.V1.PageId(this.toRelativeFilepath(page.absolutePath)); - const pageSlug = parentSlug.apply({ - fullSlug: this.markdownFilesToFullSlugs.get(page.absolutePath)?.split("/"), - urlSlug: page.slug ?? kebabCase(page.title) + return toPageNode({ + page, + parentSlug, + docsWorkspace: this.docsWorkspace, + markdownFilesToFullSlugs: this.markdownFilesToFullSlugs, + idgen: this.#idgen }); - return { - id: this.#idgen.get(pageId), - type: "page", - pageId, - title: page.title, - slug: pageSlug.get(), - icon: page.icon, - hidden: page.hidden, - noindex: page.noindex, - authed: undefined, - viewers: page.viewers, - orphaned: page.orphaned - }; } #convertPackage( @@ -174,7 +169,7 @@ export class ApiReferenceNodeConverter { ): FernNavigation.V1.ApiPackageNode { const overviewPageId = pkg.overviewAbsolutePath != null - ? FernNavigation.V1.PageId(this.toRelativeFilepath(pkg.overviewAbsolutePath)) + ? FernNavigation.V1.PageId(toRelativeFilepath(this.docsWorkspace, pkg.overviewAbsolutePath)) : undefined; const maybeFullSlug = @@ -265,7 +260,7 @@ export class ApiReferenceNodeConverter { ): FernNavigation.V1.ApiPackageNode { const overviewPageId = section.overviewAbsolutePath != null - ? FernNavigation.V1.PageId(this.toRelativeFilepath(section.overviewAbsolutePath)) + ? FernNavigation.V1.PageId(toRelativeFilepath(this.docsWorkspace, section.overviewAbsolutePath)) : undefined; const maybeFullSlug = @@ -512,33 +507,24 @@ export class ApiReferenceNodeConverter { left: FernNavigation.V1.ApiPackageChild[], right: FernNavigation.V1.ApiPackageChild[] ): FernNavigation.V1.ApiPackageChild[] { - return this.mergeEndpointPairs([...left, ...right]).filter((child) => - child.type === "apiPackage" ? child.children.length > 0 : true - ); + return mergeAndFilterChildren({ + left, + right, + findEndpointById: (endpointId) => this.#holder.endpoints.get(endpointId), + stringifyEndpointPathParts: (endpoint) => stringifyEndpointPathParts(endpoint.path.parts), + disableEndpointPairs: this.disableEndpointPairs, + apiDefinitionId: this.apiDefinitionId + }); } #enrichApiPackageChild(child: FernNavigation.V1.ApiPackageChild): FernNavigation.V1.ApiPackageChild { - if (child.type === "apiPackage") { - // expand the subpackage to include children that haven't been visited yet - const slug = FernNavigation.V1.SlugGenerator.init(child.slug); - const subpackageIds = this.#nodeIdToSubpackageId.get(child.id) ?? []; - const subpackageChildren = subpackageIds.flatMap((subpackageId) => - this.#convertApiDefinitionPackageId(subpackageId, slug) - ); - - // recursively apply enrichment to children - const enrichedChildren = child.children.map((innerChild) => this.#enrichApiPackageChild(innerChild)); - - // combine children with subpackage (tacked on at the end to preserve order) - const children = this.#mergeAndFilterChildren(enrichedChildren, subpackageChildren); - - return { - ...child, - children, - pointsTo: undefined - }; - } - return child; + return enrichApiPackageChild({ + child, + nodeIdToSubpackageId: this.#nodeIdToSubpackageId, + convertApiDefinitionPackageId: (subpackageId, slug) => + this.#convertApiDefinitionPackageId(subpackageId, slug), + mergeAndFilterChildren: this.#mergeAndFilterChildren.bind(this) + }); } #convertApiDefinitionPackage( @@ -698,79 +684,17 @@ export class ApiReferenceNodeConverter { #convertPlaygroundSettings( playgroundSettings?: docsYml.RawSchemas.PlaygroundSettings ): FernNavigation.V1.PlaygroundSettings | undefined { - if (playgroundSettings) { - return { - environments: - playgroundSettings.environments != null && playgroundSettings.environments.length > 0 - ? playgroundSettings.environments.map((environmentId) => - FernNavigation.V1.EnvironmentId(environmentId) - ) - : undefined, - button: - playgroundSettings.button != null && playgroundSettings.button.href - ? { href: FernNavigation.V1.Url(playgroundSettings.button.href) } - : undefined, - "limit-websocket-messages-per-connection": - playgroundSettings.limitWebsocketMessagesPerConnection != null - ? playgroundSettings.limitWebsocketMessagesPerConnection - : undefined - }; - } - - return; + return convertPlaygroundSettings(playgroundSettings); } private mergeEndpointPairs(children: FernNavigation.V1.ApiPackageChild[]): FernNavigation.V1.ApiPackageChild[] { - if (this.disableEndpointPairs) { - return children; - } - - const toRet: FernNavigation.V1.ApiPackageChild[] = []; - - const methodAndPathToEndpointNode = new Map<string, FernNavigation.V1.EndpointNode>(); - children.forEach((child) => { - if (child.type !== "endpoint") { - toRet.push(child); - return; - } - - const endpoint = this.#holder.endpoints.get(child.endpointId); - if (endpoint == null) { - throw new Error(`Endpoint ${child.endpointId} not found`); - } - - const methodAndPath = `${endpoint.method} ${stringifyEndpointPathParts(endpoint.path.parts)}`; - - const existing = methodAndPathToEndpointNode.get(methodAndPath); - methodAndPathToEndpointNode.set(methodAndPath, child); - - if (existing == null || existing.isResponseStream === child.isResponseStream) { - toRet.push(child); - return; - } - - const idx = toRet.indexOf(existing); - const stream = child.isResponseStream ? child : existing; - const nonStream = child.isResponseStream ? existing : child; - const pairNode: FernNavigation.V1.EndpointPairNode = { - id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${nonStream.endpointId}+${stream.endpointId}`), - type: "endpointPair", - stream, - nonStream - }; - - toRet[idx] = pairNode; + return mergeEndpointPairs({ + children, + findEndpointById: (endpointId: APIV1Read.EndpointId) => this.#holder.endpoints.get(endpointId), + stringifyEndpointPathParts: (endpoint: APIV1Read.EndpointDefinition) => + stringifyEndpointPathParts(endpoint.path.parts), + disableEndpointPairs: this.disableEndpointPairs, + apiDefinitionId: this.apiDefinitionId }); - - return toRet; - } - - private toRelativeFilepath(filepath: AbsoluteFilePath): RelativeFilePath; - private toRelativeFilepath(filepath: AbsoluteFilePath | undefined): RelativeFilePath | undefined; - private toRelativeFilepath(filepath: AbsoluteFilePath | undefined): RelativeFilePath | undefined { - if (filepath == null) { - return undefined; - } - return relative(this.docsWorkspace.absoluteFilePath, filepath); } } diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts index 0bb81339273..dca531db38d 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts @@ -1,19 +1,24 @@ import { camelCase, kebabCase } from "lodash-es"; import urlJoin from "url-join"; -import { threadId } from "worker_threads"; import { docsYml } from "@fern-api/configuration-loader"; import { isNonNullish } from "@fern-api/core-utils"; import { FdrAPI, FernNavigation } from "@fern-api/fdr-sdk"; -import { AbsoluteFilePath, RelativeFilePath, relative } from "@fern-api/fs-utils"; +import { AbsoluteFilePath } from "@fern-api/fs-utils"; import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { TaskContext } from "@fern-api/task-context"; import { titleCase, visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; -import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader"; +import { DocsWorkspace } from "@fern-api/workspace-loader"; import { ChangelogNodeConverter } from "./ChangelogNodeConverter"; import { NodeIdGenerator } from "./NodeIdGenerator"; +import { convertPlaygroundSettings } from "./utils/convertPlaygroundSettings"; +import { enrichApiPackageChild } from "./utils/enrichApiPackageChild"; +import { mergeAndFilterChildren } from "./utils/mergeAndFilterChildren"; +import { mergeEndpointPairs } from "./utils/mergeEndpointPairs"; import { stringifyEndpointPathParts } from "./utils/stringifyEndpointPathParts"; +import { toPageNode } from "./utils/toPageNode"; +import { toRelativeFilepath } from "./utils/toRelativeFilepath"; // TODO: these functions need an extra piece of information from the fdr latest shape, to see if the operation id takes precedence over slug generation function getLatestEndpointUrlSlug(endpoint: FdrAPI.api.latest.endpoint.EndpointDefinition) { @@ -68,7 +73,7 @@ export class ApiReferenceNodeConverterLatest { this.#overviewPageId = this.apiSection.overviewAbsolutePath != null - ? FernNavigation.V1.PageId(this.toRelativeFilepath(this.apiSection.overviewAbsolutePath)) + ? FernNavigation.V1.PageId(toRelativeFilepath(this.docsWorkspace, this.apiSection.overviewAbsolutePath)) : undefined; // the overview page markdown could contain a full slug, which would be used as the base slug for the API section. @@ -130,6 +135,16 @@ export class ApiReferenceNodeConverterLatest { }; } + #findSubpackageByLocator(locator: string): FdrAPI.api.latest.SubpackageMetadata | undefined { + return ( + this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(locator)] ?? + this.#api?.subpackages[ + FdrAPI.api.v1.SubpackageId(locator.replace(".yml", "").replace(".yaml", "").split(".").pop() ?? "") + ] ?? + this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(locator.split("/").pop() ?? "")] + ); + } + #findEndpointByLocator(locator: string): FdrAPI.api.latest.endpoint.EndpointDefinition | undefined { return ( this.#api?.endpoints[FdrAPI.EndpointId(locator)] ?? @@ -194,24 +209,13 @@ export class ApiReferenceNodeConverterLatest { page: docsYml.DocsNavigationItem.Page, parentSlug: FernNavigation.V1.SlugGenerator ): FernNavigation.V1.PageNode { - const pageId = FernNavigation.V1.PageId(this.toRelativeFilepath(page.absolutePath)); - const pageSlug = parentSlug.apply({ - fullSlug: this.markdownFilesToFullSlugs.get(page.absolutePath)?.split("/"), - urlSlug: page.slug ?? kebabCase(page.title) + return toPageNode({ + docsWorkspace: this.docsWorkspace, + page, + parentSlug, + idgen: this.#idgen, + markdownFilesToFullSlugs: this.markdownFilesToFullSlugs }); - return { - id: this.#idgen.get(pageId), - type: "page", - pageId, - title: page.title, - slug: pageSlug.get(), - icon: page.icon, - hidden: page.hidden, - noindex: page.noindex, - authed: undefined, - viewers: page.viewers, - orphaned: page.orphaned - }; } #convertPackage( @@ -220,18 +224,13 @@ export class ApiReferenceNodeConverterLatest { ): FernNavigation.V1.ApiPackageNode { const overviewPageId = pkg.overviewAbsolutePath != null - ? FernNavigation.V1.PageId(this.toRelativeFilepath(pkg.overviewAbsolutePath)) + ? FernNavigation.V1.PageId(toRelativeFilepath(this.docsWorkspace, pkg.overviewAbsolutePath)) : undefined; const maybeFullSlug = pkg.overviewAbsolutePath != null ? this.markdownFilesToFullSlugs.get(pkg.overviewAbsolutePath) : undefined; - const subpackage = - this.#api.subpackages[FdrAPI.api.v1.SubpackageId(pkg.package)] ?? - this.#api.subpackages[ - FdrAPI.api.v1.SubpackageId(pkg.package.replace(".yml", "").replace(".yaml", "").split(".").pop() ?? "") - ] ?? - this.#api.subpackages[FdrAPI.api.v1.SubpackageId(pkg.package.split("/").pop() ?? "")]; + const subpackage = this.#findSubpackageByLocator(pkg.package); if (subpackage != null) { const subpackageNodeId = this.#idgen.get(overviewPageId ?? `${this.apiDefinitionId}:${subpackage.id}`); @@ -313,7 +312,7 @@ export class ApiReferenceNodeConverterLatest { ): FernNavigation.V1.ApiPackageNode { const overviewPageId = section.overviewAbsolutePath != null - ? FernNavigation.V1.PageId(this.toRelativeFilepath(section.overviewAbsolutePath)) + ? FernNavigation.V1.PageId(toRelativeFilepath(this.docsWorkspace, section.overviewAbsolutePath)) : undefined; const maybeFullSlug = @@ -325,14 +324,7 @@ export class ApiReferenceNodeConverterLatest { const subpackageIds = section.referencedSubpackages .map((locator) => { - const subpackage = - this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(locator)] ?? - this.#api?.subpackages[ - FdrAPI.api.v1.SubpackageId( - locator.replace(".yml", "").replace(".yaml", "").split(".").pop() ?? "" - ) - ] ?? - this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(locator.split("/").pop() ?? "")]; + const subpackage = this.#findSubpackageByLocator(locator); return subpackage != null ? subpackage.id : undefined; }) @@ -391,26 +383,20 @@ export class ApiReferenceNodeConverterLatest { // if the unknownIdentifier is a subpackage, we need to check subpackage metadata, and any locators (strip .yml) - const subpackage = - this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(unknownIdentifier)] ?? - this.#api?.subpackages[ - FdrAPI.api.v1.SubpackageId( - unknownIdentifier.replace(".yml", "").replace(".yaml", "").split(".").pop() ?? "" - ) - ] ?? - this.#api?.subpackages[FdrAPI.api.v1.SubpackageId(unknownIdentifier.split("/").pop() ?? "")]; + const subpackage = this.#findSubpackageByLocator(unknownIdentifier); if (subpackage != null) { - const subpackageNodeId = this.#idgen.get(`${this.apiDefinitionId}:${subpackage.id}`); + const subpackageId = subpackage.id; + const subpackageNodeId = this.#idgen.get(`${this.apiDefinitionId}:${subpackageId}`); - if (this.#visitedSubpackages.has(subpackage.id)) { + if (this.#visitedSubpackages.has(subpackageId)) { this.taskContext.logger.error( - `Duplicate subpackage found in the API Reference layout: ${subpackage.id}` + `Duplicate subpackage found in the API Reference layout: ${subpackageId}` ); } - this.#visitedSubpackages.add(subpackage.id); - this.#nodeIdToSubpackageId.set(subpackageNodeId, [subpackage.id]); + this.#visitedSubpackages.add(subpackageId); + this.#nodeIdToSubpackageId.set(subpackageNodeId, [subpackageId]); const urlSlug = subpackage.name; const slug = parentSlug.apply({ urlSlug }); const subpackageNode: FernNavigation.V1.ApiPackageNode = { @@ -432,7 +418,7 @@ export class ApiReferenceNodeConverterLatest { orphaned: undefined }; - this.#topLevelSubpackages.set(subpackage.id, subpackageNode); + this.#topLevelSubpackages.set(subpackageId, subpackageNode); return subpackageNode; } @@ -563,33 +549,24 @@ export class ApiReferenceNodeConverterLatest { left: FernNavigation.V1.ApiPackageChild[], right: FernNavigation.V1.ApiPackageChild[] ): FernNavigation.V1.ApiPackageChild[] { - return this.mergeEndpointPairs([...left, ...right]).filter((child) => - child.type === "apiPackage" ? child.children.length > 0 : true - ); + return mergeAndFilterChildren({ + left, + right, + findEndpointById: (endpointId) => this.#findEndpointByLocator(endpointId), + stringifyEndpointPathParts: (endpoint) => stringifyEndpointPathParts(endpoint.path), + disableEndpointPairs: this.disableEndpointPairs, + apiDefinitionId: this.apiDefinitionId + }); } #enrichApiPackageChild(child: FernNavigation.V1.ApiPackageChild): FernNavigation.V1.ApiPackageChild { - if (child.type === "apiPackage") { - // expand the subpackage to include children that haven't been visited yet - const slug = FernNavigation.V1.SlugGenerator.init(child.slug); - const subpackageIds = this.#nodeIdToSubpackageId.get(child.id) ?? []; - const subpackageChildren = subpackageIds.flatMap((subpackageId) => - this.#convertApiDefinitionPackageId(subpackageId, slug) - ); - - // recursively apply enrichment to children - const enrichedChildren = child.children.map((innerChild) => this.#enrichApiPackageChild(innerChild)); - - // combine children with subpackage (tacked on at the end to preserve order) - const children = this.#mergeAndFilterChildren(enrichedChildren, subpackageChildren); - - return { - ...child, - children, - pointsTo: undefined - }; - } - return child; + return enrichApiPackageChild({ + child, + nodeIdToSubpackageId: this.#nodeIdToSubpackageId, + convertApiDefinitionPackageId: (subpackageId, slug) => + this.#convertApiDefinitionPackageId(subpackageId, slug), + mergeAndFilterChildren: this.#mergeAndFilterChildren.bind(this) + }); } #convertApiDefinitionPackage( @@ -924,79 +901,17 @@ export class ApiReferenceNodeConverterLatest { #convertPlaygroundSettings( playgroundSettings?: docsYml.RawSchemas.PlaygroundSettings ): FernNavigation.V1.PlaygroundSettings | undefined { - if (playgroundSettings) { - return { - environments: - playgroundSettings.environments != null && playgroundSettings.environments.length > 0 - ? playgroundSettings.environments.map((environmentId) => - FernNavigation.V1.EnvironmentId(environmentId) - ) - : undefined, - button: - playgroundSettings.button != null && playgroundSettings.button.href - ? { href: FernNavigation.V1.Url(playgroundSettings.button.href) } - : undefined, - "limit-websocket-messages-per-connection": - playgroundSettings.limitWebsocketMessagesPerConnection != null - ? playgroundSettings.limitWebsocketMessagesPerConnection - : undefined - }; - } - - return; + return convertPlaygroundSettings(playgroundSettings); } private mergeEndpointPairs(children: FernNavigation.V1.ApiPackageChild[]): FernNavigation.V1.ApiPackageChild[] { - if (this.disableEndpointPairs) { - return children; - } - - const toRet: FernNavigation.V1.ApiPackageChild[] = []; - - const methodAndPathToEndpointNode = new Map<string, FernNavigation.V1.EndpointNode>(); - children.forEach((child) => { - if (child.type !== "endpoint") { - toRet.push(child); - return; - } - - const endpoint = this.#api?.endpoints[child.endpointId]; - if (endpoint == null) { - throw new Error(`Endpoint ${child.endpointId} not found`); - } - - const methodAndPath = `${endpoint.method} ${stringifyEndpointPathParts(endpoint.path)}`; - - const existing = methodAndPathToEndpointNode.get(methodAndPath); - methodAndPathToEndpointNode.set(methodAndPath, child); - - if (existing == null || existing.isResponseStream === child.isResponseStream) { - toRet.push(child); - return; - } - - const idx = toRet.indexOf(existing); - const stream = child.isResponseStream ? child : existing; - const nonStream = child.isResponseStream ? existing : child; - const pairNode: FernNavigation.V1.EndpointPairNode = { - id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${nonStream.endpointId}+${stream.endpointId}`), - type: "endpointPair", - stream, - nonStream - }; - - toRet[idx] = pairNode; + return mergeEndpointPairs({ + children, + findEndpointById: (endpointId: FdrAPI.EndpointId) => this.#api?.endpoints[endpointId], + stringifyEndpointPathParts: (endpoint: FdrAPI.api.latest.EndpointDefinition) => + stringifyEndpointPathParts(endpoint.path), + disableEndpointPairs: this.disableEndpointPairs, + apiDefinitionId: this.apiDefinitionId }); - - return toRet; - } - - private toRelativeFilepath(filepath: AbsoluteFilePath): RelativeFilePath; - private toRelativeFilepath(filepath: AbsoluteFilePath | undefined): RelativeFilePath | undefined; - private toRelativeFilepath(filepath: AbsoluteFilePath | undefined): RelativeFilePath | undefined { - if (filepath == null) { - return undefined; - } - return relative(this.docsWorkspace.absoluteFilePath, filepath); } } diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/docs.yml b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/docs.yml new file mode 100644 index 00000000000..2d51970a5ac --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/docs.yml @@ -0,0 +1,9 @@ +instances: + - url: https://fern-platform-test.docs.dev.buildwithfern.com + - url: https://fern-platform-test.docs.buildwithfern.com +title: SmokeTest | Documentation +navigation: + - api: API Reference +colors: + accentPrimary: '#ffffff' + background: '#000000' diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/fern.config.json b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/fern.config.json new file mode 100644 index 00000000000..7980537f564 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/fern.config.json @@ -0,0 +1,4 @@ +{ + "organization": "fern", + "version": "*" +} \ No newline at end of file diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/generators.yml b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/generators.yml @@ -0,0 +1 @@ +{} diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/openapi.yml b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/openapi.yml new file mode 100644 index 00000000000..79d4a073543 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/openapi-latest/fern/openapi.yml @@ -0,0 +1,332 @@ +openapi: 3.1.0 +info: + title: Swagger Petstore - OpenAPI 3.1 + description: |- + This is a sample Pet Store Server based on the OpenAPI 3.1 specification. + You can find out more about + Swagger at [http://swagger.io](http://swagger.io). + summary: Pet Store 3.1 + version: 1.0.0 +servers: + - url: /api/v31 +tags: + - name: pet + description: Everything about your Pets + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user +paths: + /pet: + put: + tags: + - pet + summary: Update an existing pet + description: Update an existing pet by Id + operationId: updatePet + requestBody: + description: Pet object that needs to be updated in the store + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in JSON Format + required: + - id + writeOnly: true + application/xml: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in XML Format + required: + - id + writeOnly: true + required: true + responses: + "200": + description: Successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in XML Format + readOnly: true + application/json: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in JSON Format + readOnly: true + "400": + description: Invalid ID supplied + "404": + description: Pet not found + "405": + description: Validation exception + security: + - petstore_auth: + - "write:pets" + - "read:pets" + post: + tags: + - pet + summary: Add a new pet to the store + description: Add a new pet to the store + operationId: addPet + requestBody: + description: Create a new pet in the store + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in JSON Format + required: + - id + writeOnly: true + application/xml: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in XML Format + required: + - id + writeOnly: true + required: true + responses: + "200": + description: Successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in XML Format + readOnly: true + application/json: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in JSON Format + readOnly: true + "405": + description: Invalid input + security: + - petstore_auth: + - "write:pets" + - "read:pets" + "/pet/{petId}": + get: + tags: + - pets + summary: Find pet by ID + description: >- + Returns a pet when 0 < ID <= 10. ID > 10 or nonintegers will simulate + API error conditions + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + type: integer + format: int64 + description: param ID of pet that needs to be fetched + exclusiveMaximum: 10 + exclusiveMinimum: 1 + responses: + "400": + description: Invalid ID supplied + "404": + description: Pet not found + default: + description: The pet + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in JSON format + application/xml: + schema: + $ref: "#/components/schemas/Pet" + description: A Pet in XML format + security: + - petstore_auth: + - "write:pets" + - "read:pets" + - api_key: [] +components: + schemas: + Order: + x-swagger-router-model: io.swagger.petstore.model.Order + properties: + id: + type: integer + format: int64 + example: 10 + petId: + type: integer + format: int64 + example: 198772 + quantity: + type: integer + format: int32 + example: 7 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + example: approved + complete: + type: boolean + xml: + name: order + type: object + Customer: + properties: + id: + type: integer + format: int64 + example: 100000 + username: + type: string + example: fehguy + address: + type: array + items: + $ref: "#/components/schemas/Address" + xml: + wrapped: true + name: addresses + xml: + name: customer + type: object + Address: + properties: + street: + type: string + example: 437 Lytton + city: + type: string + example: Palo Alto + state: + type: string + example: CA + zip: + type: string + example: 94301 + xml: + name: address + type: object + Category: + x-swagger-router-model: io.swagger.petstore.model.Category + properties: + id: + type: integer + format: int64 + example: 1 + name: + type: string + example: Dogs + xml: + name: category + type: object + User: + x-swagger-router-model: io.swagger.petstore.model.User + properties: + id: + type: integer + format: int64 + example: 10 + username: + type: string + example: theUser + firstName: + type: string + example: John + lastName: + type: string + example: James + email: + type: string + example: john@email.com + password: + type: string + example: 12345 + phone: + type: string + example: 12345 + userStatus: + type: integer + format: int32 + example: 1 + description: User Status + xml: + name: user + type: object + Tag: + x-swagger-router-model: io.swagger.petstore.model.Tag + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: tag + type: object + Pet: + x-swagger-router-model: io.swagger.petstore.model.Pet + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + example: 10 + name: + type: string + example: doggie + category: + $ref: "#/components/schemas/Category" + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + $ref: "#/components/schemas/Tag" + xml: + name: tag + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: pet + type: object + ApiResponse: + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + xml: + name: "##default" + type: object diff --git a/packages/cli/docs-resolver/src/__test__/openapi-latest.test.ts b/packages/cli/docs-resolver/src/__test__/openapi-latest.test.ts new file mode 100644 index 00000000000..746910f1638 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/openapi-latest.test.ts @@ -0,0 +1,79 @@ +import { parseDocsConfiguration } from "@fern-api/configuration-loader"; +import { FernNavigation } from "@fern-api/fdr-sdk"; +import { AbsoluteFilePath, resolve } from "@fern-api/fs-utils"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { createMockTaskContext } from "@fern-api/task-context"; +import { loadAPIWorkspace, loadDocsWorkspace } from "@fern-api/workspace-loader"; + +import { ApiReferenceNodeConverterLatest } from "../ApiReferenceNodeConverterLatest"; +import { NodeIdGenerator } from "../NodeIdGenerator"; +import { generateFdrFromOpenApiWorkspace } from "../utils/generateFdrFromOpenApiWorkspace"; + +const context = createMockTaskContext(); + +// eslint-disable-next-line jest/no-disabled-tests +it.skip("converts to api reference latest node", async () => { + const docsWorkspace = await loadDocsWorkspace({ + fernDirectory: resolve(AbsoluteFilePath.of(__dirname), "fixtures/openapi-latest/fern"), + context + }); + + if (docsWorkspace == null) { + throw new Error("Workspace is null"); + } + + const parsedDocsConfig = await parseDocsConfiguration({ + rawDocsConfiguration: docsWorkspace.config, + context, + absolutePathToFernFolder: docsWorkspace.absoluteFilePath, + absoluteFilepathToDocsConfig: docsWorkspace.absoluteFilepathToDocsConfig + }); + + if (parsedDocsConfig.navigation.type !== "untabbed") { + throw new Error("Expected untabbed navigation"); + } + + if (parsedDocsConfig.navigation.items[0]?.type !== "apiSection") { + throw new Error("Expected apiSection"); + } + + const apiSection = parsedDocsConfig.navigation.items[0]; + + const result = await loadAPIWorkspace({ + absolutePathToWorkspace: resolve(AbsoluteFilePath.of(__dirname), "fixtures/openapi-latest/fern"), + context, + cliVersion: "0.0.0", + workspaceName: undefined + }); + + if (!result.didSucceed) { + throw new Error("API workspace failed to load"); + } + + const apiWorkspace = result.workspace; + + if (!(apiWorkspace instanceof OSSWorkspace)) { + throw new Error("Expected oss workspace"); + } + + const slug = FernNavigation.V1.SlugGenerator.init("/base/path"); + + const api = await generateFdrFromOpenApiWorkspace(apiWorkspace, context); + + if (api == null) { + throw new Error("API is null"); + } + + const node = new ApiReferenceNodeConverterLatest( + apiSection, + api, + slug, + apiWorkspace, + docsWorkspace, + context, + new Map(), + NodeIdGenerator.init() + ).get(); + + expect(node).toMatchSnapshot(); +}); diff --git a/packages/cli/docs-resolver/src/utils/convertIrToApiDefinition.ts b/packages/cli/docs-resolver/src/utils/convertIrToApiDefinition.ts index a0e02a633eb..0ca7cec8471 100644 --- a/packages/cli/docs-resolver/src/utils/convertIrToApiDefinition.ts +++ b/packages/cli/docs-resolver/src/utils/convertIrToApiDefinition.ts @@ -1,10 +1,4 @@ -import { - APIV1Read, - DocsV1Read, - SDKSnippetHolder, - convertAPIDefinitionToDb, - convertDbAPIDefinitionToRead -} from "@fern-api/fdr-sdk"; +import { APIV1Read, SDKSnippetHolder, convertAPIDefinitionToDb, convertDbAPIDefinitionToRead } from "@fern-api/fdr-sdk"; import { IntermediateRepresentation } from "@fern-api/ir-sdk"; import { convertIrToFdrApi } from "@fern-api/register"; diff --git a/packages/cli/docs-resolver/src/utils/convertPlaygroundSettings.ts b/packages/cli/docs-resolver/src/utils/convertPlaygroundSettings.ts new file mode 100644 index 00000000000..c5a5acf56e1 --- /dev/null +++ b/packages/cli/docs-resolver/src/utils/convertPlaygroundSettings.ts @@ -0,0 +1,27 @@ +import { docsYml } from "@fern-api/configuration-loader"; +import { FernNavigation } from "@fern-api/fdr-sdk"; + +export function convertPlaygroundSettings( + playgroundSettings?: docsYml.RawSchemas.PlaygroundSettings +): FernNavigation.V1.PlaygroundSettings | undefined { + if (playgroundSettings) { + return { + environments: + playgroundSettings.environments != null && playgroundSettings.environments.length > 0 + ? playgroundSettings.environments.map((environmentId) => + FernNavigation.V1.EnvironmentId(environmentId) + ) + : undefined, + button: + playgroundSettings.button != null && playgroundSettings.button.href + ? { href: FernNavigation.V1.Url(playgroundSettings.button.href) } + : undefined, + "limit-websocket-messages-per-connection": + playgroundSettings.limitWebsocketMessagesPerConnection != null + ? playgroundSettings.limitWebsocketMessagesPerConnection + : undefined + }; + } + + return; +} diff --git a/packages/cli/docs-resolver/src/utils/enrichApiPackageChild.ts b/packages/cli/docs-resolver/src/utils/enrichApiPackageChild.ts new file mode 100644 index 00000000000..9f2ad6cd772 --- /dev/null +++ b/packages/cli/docs-resolver/src/utils/enrichApiPackageChild.ts @@ -0,0 +1,48 @@ +import { FernNavigation } from "@fern-api/fdr-sdk"; + +export function enrichApiPackageChild({ + child, + nodeIdToSubpackageId, + convertApiDefinitionPackageId, + mergeAndFilterChildren +}: { + child: FernNavigation.V1.ApiPackageChild; + nodeIdToSubpackageId: Map<string, string[]>; + convertApiDefinitionPackageId: ( + subpackageId: string, + slug: FernNavigation.V1.SlugGenerator + ) => FernNavigation.V1.ApiPackageChild[]; + mergeAndFilterChildren: ( + children: FernNavigation.V1.ApiPackageChild[], + subpackageChildren: FernNavigation.V1.ApiPackageChild[] + ) => FernNavigation.V1.ApiPackageChild[]; +}): FernNavigation.V1.ApiPackageChild { + if (child.type === "apiPackage") { + // expand the subpackage to include children that haven't been visited yet + const slug = FernNavigation.V1.SlugGenerator.init(child.slug); + const subpackageIds = nodeIdToSubpackageId.get(child.id) ?? []; + const subpackageChildren = subpackageIds.flatMap((subpackageId) => + convertApiDefinitionPackageId(subpackageId, slug) + ); + + // recursively apply enrichment to children + const enrichedChildren = child.children.map((innerChild) => + enrichApiPackageChild({ + child: innerChild, + nodeIdToSubpackageId, + convertApiDefinitionPackageId, + mergeAndFilterChildren + }) + ); + + // combine children with subpackage (tacked on at the end to preserve order) + const children = mergeAndFilterChildren(enrichedChildren, subpackageChildren); + + return { + ...child, + children, + pointsTo: undefined + }; + } + return child; +} diff --git a/packages/cli/docs-resolver/src/utils/mergeAndFilterChildren.ts b/packages/cli/docs-resolver/src/utils/mergeAndFilterChildren.ts new file mode 100644 index 00000000000..0b018753808 --- /dev/null +++ b/packages/cli/docs-resolver/src/utils/mergeAndFilterChildren.ts @@ -0,0 +1,27 @@ +import { FernNavigation } from "@fern-api/fdr-sdk"; + +import { mergeEndpointPairs } from "./mergeEndpointPairs"; + +export function mergeAndFilterChildren<EndpointType extends { method: string }>({ + left, + right, + findEndpointById, + stringifyEndpointPathParts, + disableEndpointPairs, + apiDefinitionId +}: { + left: FernNavigation.V1.ApiPackageChild[]; + right: FernNavigation.V1.ApiPackageChild[]; + findEndpointById: (endpointId: FernNavigation.EndpointId) => EndpointType | undefined; + stringifyEndpointPathParts: (endpoint: EndpointType) => string; + disableEndpointPairs: boolean; + apiDefinitionId: FernNavigation.V1.ApiDefinitionId; +}): FernNavigation.V1.ApiPackageChild[] { + return mergeEndpointPairs({ + children: [...left, ...right], + findEndpointById, + stringifyEndpointPathParts, + disableEndpointPairs, + apiDefinitionId + }).filter((child) => (child.type === "apiPackage" ? child.children.length > 0 : true)); +} diff --git a/packages/cli/docs-resolver/src/utils/mergeEndpointPairs.ts b/packages/cli/docs-resolver/src/utils/mergeEndpointPairs.ts new file mode 100644 index 00000000000..f0ba4617a3c --- /dev/null +++ b/packages/cli/docs-resolver/src/utils/mergeEndpointPairs.ts @@ -0,0 +1,58 @@ +import { FernNavigation } from "@fern-api/fdr-sdk"; + +export function mergeEndpointPairs<EndpointType extends { method: string }>({ + children, + findEndpointById, + stringifyEndpointPathParts, + disableEndpointPairs, + apiDefinitionId +}: { + children: FernNavigation.V1.ApiPackageChild[]; + findEndpointById: (endpointId: FernNavigation.EndpointId) => EndpointType | undefined; + stringifyEndpointPathParts: (endpoint: EndpointType) => string; + disableEndpointPairs: boolean; + apiDefinitionId: FernNavigation.V1.ApiDefinitionId; +}): FernNavigation.V1.ApiPackageChild[] { + if (disableEndpointPairs) { + return children; + } + + const toRet: FernNavigation.V1.ApiPackageChild[] = []; + + const methodAndPathToEndpointNode = new Map<string, FernNavigation.V1.EndpointNode>(); + children.forEach((child) => { + if (child.type !== "endpoint") { + toRet.push(child); + return; + } + + const endpoint = findEndpointById(child.endpointId); + if (endpoint == null) { + throw new Error(`Endpoint ${child.endpointId} not found`); + } + + const methodAndPath = `${endpoint.method} ${stringifyEndpointPathParts(endpoint)}`; + + const existing = methodAndPathToEndpointNode.get(methodAndPath); + methodAndPathToEndpointNode.set(methodAndPath, child); + + if (existing == null || existing.isResponseStream === child.isResponseStream) { + toRet.push(child); + return; + } + + const idx = toRet.indexOf(existing); + const stream = child.isResponseStream ? child : existing; + const nonStream = child.isResponseStream ? existing : child; + const pairNode: FernNavigation.V1.EndpointPairNode = { + id: FernNavigation.V1.NodeId(`${apiDefinitionId}:${nonStream.endpointId}+${stream.endpointId}`), + type: "endpointPair", + stream, + nonStream + }; + + toRet[idx] = pairNode; + }); + + return toRet; +} diff --git a/packages/cli/docs-resolver/src/utils/toPageNode.ts b/packages/cli/docs-resolver/src/utils/toPageNode.ts new file mode 100644 index 00000000000..612aeced7a7 --- /dev/null +++ b/packages/cli/docs-resolver/src/utils/toPageNode.ts @@ -0,0 +1,42 @@ +import { kebabCase } from "lodash-es"; + +import { docsYml } from "@fern-api/configuration-loader"; +import { FernNavigation } from "@fern-api/fdr-sdk"; +import { AbsoluteFilePath } from "@fern-api/fs-utils"; +import { DocsWorkspace } from "@fern-api/workspace-loader"; + +import { NodeIdGenerator } from "../NodeIdGenerator"; +import { toRelativeFilepath } from "./toRelativeFilepath"; + +export function toPageNode({ + docsWorkspace, + page, + parentSlug, + idgen, + markdownFilesToFullSlugs +}: { + docsWorkspace: DocsWorkspace; + page: docsYml.DocsNavigationItem.Page; + parentSlug: FernNavigation.V1.SlugGenerator; + idgen: NodeIdGenerator; + markdownFilesToFullSlugs: Map<AbsoluteFilePath, string>; +}): FernNavigation.V1.PageNode { + const pageId = FernNavigation.V1.PageId(toRelativeFilepath(docsWorkspace, page.absolutePath)); + const pageSlug = parentSlug.apply({ + fullSlug: markdownFilesToFullSlugs.get(page.absolutePath)?.split("/"), + urlSlug: page.slug ?? kebabCase(page.title) + }); + return { + id: idgen.get(pageId), + type: "page", + pageId, + title: page.title, + slug: pageSlug.get(), + icon: page.icon, + hidden: page.hidden, + noindex: page.noindex, + authed: undefined, + viewers: page.viewers, + orphaned: page.orphaned + }; +} diff --git a/packages/cli/docs-resolver/src/utils/toRelativeFilepath.ts b/packages/cli/docs-resolver/src/utils/toRelativeFilepath.ts new file mode 100644 index 00000000000..136d21006f1 --- /dev/null +++ b/packages/cli/docs-resolver/src/utils/toRelativeFilepath.ts @@ -0,0 +1,17 @@ +import { AbsoluteFilePath, RelativeFilePath, relative } from "@fern-api/fs-utils"; +import { DocsWorkspace } from "@fern-api/workspace-loader"; + +export function toRelativeFilepath(docsWorkspace: DocsWorkspace, filepath: AbsoluteFilePath): RelativeFilePath; +export function toRelativeFilepath( + docsWorkspace: DocsWorkspace, + filepath: AbsoluteFilePath | undefined +): RelativeFilePath | undefined; +export function toRelativeFilepath( + docsWorkspace: DocsWorkspace, + filepath: AbsoluteFilePath | undefined +): RelativeFilePath | undefined { + if (filepath == null) { + return undefined; + } + return relative(docsWorkspace.absoluteFilePath, filepath); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d31e87d907e..a219a116672 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4710,8 +4710,8 @@ importers: specifier: workspace:* version: link:../docs-markdown-utils '@fern-api/docs-parsers': - specifier: ^0.0.19 - version: 0.0.19(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) + specifier: ^0.0.20 + version: 0.0.20(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/fdr-sdk': specifier: 0.127.4-331678a74 version: 0.127.4-331678a74(typescript@4.6.4) @@ -8276,8 +8276,8 @@ packages: '@fern-api/core-utils@0.4.24-rc1': resolution: {integrity: sha512-aYu4lQK2qZIKzTF9TeFrICTPJ/zGEZUEWQmZt6pJeHu+R6afrcCBNkUleWU1OpHlDbe+xXUUBOktRg0PM9Hywg==} - '@fern-api/docs-parsers@0.0.19': - resolution: {integrity: sha512-DWXyxW25dG/lCwAilvHcd394vHpSAF8wF1Q8P/PFxKBATHkVZxXHfmNeRC+soS4rn4E0vPOZJO3QR8d+ULZZkg==} + '@fern-api/docs-parsers@0.0.20': + resolution: {integrity: sha512-N4Xvc1GA5EkZiYkFLnbeOH2fwe4omYC3L/2fArYDvP0KZgpMigSGQOjwbH8MDboZ45BcXzbEs5NsOeCeNMHkgA==} '@fern-api/dynamic-ir-sdk@53.24.0': resolution: {integrity: sha512-4XvzvSsh7lNZz5oYN0LH+FRpFGxg9TjfdipSJMpgHvPShe85m/tcfbOzbS0DHBpQGlKb/gHQtQRPAoBR1wdDAw==} @@ -15638,7 +15638,7 @@ snapshots: lodash-es: 4.17.21 strip-ansi: 7.1.0 - '@fern-api/docs-parsers@0.0.19(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': + '@fern-api/docs-parsers@0.0.20(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': dependencies: '@fern-api/logger': 0.4.24-rc1 '@fern-api/ui-core-utils': 0.0.0 From d948e83ce17c8d9e17d8ce8ae41a08dcd526fb63 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Wed, 8 Jan 2025 17:28:05 -0500 Subject: [PATCH 10/11] address comments --- fern/apis/docs-yml/definition/docs.yml | 4 ++-- .../commands/generate/generateDocsWorkspace.ts | 12 ++---------- .../writeDocsDefinitionForProject.ts | 12 ++---------- .../cli/cli/src/utils/filterOssWorkspaces.ts | 16 ++++++++++++++++ .../resources/docs/types/ExperimentalConfig.ts | 4 ++-- .../resources/docs/types/ExperimentalConfig.ts | 4 ++-- packages/cli/docs-preview/src/previewDocs.ts | 14 ++++++++++---- .../docs-resolver/src/DocsDefinitionResolver.ts | 2 +- 8 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 packages/cli/cli/src/utils/filterOssWorkspaces.ts diff --git a/fern/apis/docs-yml/definition/docs.yml b/fern/apis/docs-yml/definition/docs.yml index f7fdaea99f9..ad03f1b9102 100644 --- a/fern/apis/docs-yml/definition/docs.yml +++ b/fern/apis/docs-yml/definition/docs.yml @@ -974,10 +974,10 @@ types: If `disable-stream-toggle` is set to true, the stream toggle will be disabled. This behavior is unstable and may change in the future. - direct-openapi-parser: + openapi-parser-v2: type: optional<boolean> docs: | - If `direct-openapi-parser` is set to true, the OpenAPI parser will be used directly, without Fern. + If `openapi-parser-v2` is set to true, the OpenAPI parser will be used directly, without Fern. PlaygroundSettings: properties: diff --git a/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts b/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts index 389500144c3..53d6499b36b 100644 --- a/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts +++ b/packages/cli/cli/src/commands/generate/generateDocsWorkspace.ts @@ -6,6 +6,7 @@ import { Project } from "@fern-api/project-loader"; import { runRemoteGenerationForDocsWorkspace } from "@fern-api/remote-workspace-runner"; import { CliContext } from "../../cli-context/CliContext"; +import { filterOssWorkspaces } from "../../utils/filterOssWorkspaces"; import { validateDocsWorkspaceAndLogIssues } from "../validate/validateDocsWorkspaceAndLogIssues"; export async function generateDocsWorkspace({ @@ -60,16 +61,7 @@ export async function generateDocsWorkspace({ }) ); - const ossWorkspaces = ( - await Promise.all( - project.apiWorkspaces.map(async (workspace) => { - if (workspace instanceof OSSWorkspace) { - return workspace as OSSWorkspace; - } - return null; - }) - ) - ).filter(isNonNullish); + const ossWorkspaces = await filterOssWorkspaces(project); await runRemoteGenerationForDocsWorkspace({ organization: project.config.organization, diff --git a/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts b/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts index 92b9aa4ce02..dbf7dde153c 100644 --- a/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts +++ b/packages/cli/cli/src/commands/write-docs-definition/writeDocsDefinitionForProject.ts @@ -8,6 +8,7 @@ import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { Project } from "@fern-api/project-loader"; import { CliContext } from "../../cli-context/CliContext"; +import { filterOssWorkspaces } from "../../utils/filterOssWorkspaces"; export async function writeDocsDefinitionForProject({ project, @@ -24,16 +25,7 @@ export async function writeDocsDefinitionForProject({ } await cliContext.runTaskForWorkspace(docsWorkspace, async (context) => { - const ossWorkspaces = ( - await Promise.all( - project.apiWorkspaces.map(async (workspace) => { - if (workspace instanceof OSSWorkspace) { - return workspace as OSSWorkspace; - } - return null; - }) - ) - ).filter(isNonNullish); + const ossWorkspaces = await filterOssWorkspaces(project); const fernWorkspaces = await Promise.all( project.apiWorkspaces.map(async (workspace) => { diff --git a/packages/cli/cli/src/utils/filterOssWorkspaces.ts b/packages/cli/cli/src/utils/filterOssWorkspaces.ts new file mode 100644 index 00000000000..48b0080ff97 --- /dev/null +++ b/packages/cli/cli/src/utils/filterOssWorkspaces.ts @@ -0,0 +1,16 @@ +import { isNonNullish } from "@fern-api/core-utils"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { Project } from "@fern-api/project-loader"; + +export async function filterOssWorkspaces(project: Project) { + return ( + await Promise.all( + project.apiWorkspaces.map(async (workspace) => { + if (workspace instanceof OSSWorkspace) { + return workspace as OSSWorkspace; + } + return null; + }) + ) + ).filter(isNonNullish); +} diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts index 2be87a68ee0..bf1dea97ac2 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/ExperimentalConfig.ts @@ -14,6 +14,6 @@ export interface ExperimentalConfig { * This behavior is unstable and may change in the future. */ disableStreamToggle?: boolean; - /** If `direct-openapi-parser` is set to true, the OpenAPI parser will be used directly, without Fern. */ - directOpenapiParser?: boolean; + /** If `openapi-parser-v2` is set to true, the OpenAPI parser will be used directly, without Fern. */ + openapiParserV2?: boolean; } diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts index 15e56d8453f..67ed0562277 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/ExperimentalConfig.ts @@ -15,13 +15,13 @@ export const ExperimentalConfig: core.serialization.ObjectSchema< core.serialization.list(core.serialization.string()).optional(), ), disableStreamToggle: core.serialization.property("disable-stream-toggle", core.serialization.boolean().optional()), - directOpenapiParser: core.serialization.property("direct-openapi-parser", core.serialization.boolean().optional()), + openapiParserV2: core.serialization.property("openapi-parser-v2", core.serialization.boolean().optional()), }); export declare namespace ExperimentalConfig { export interface Raw { "mdx-components"?: string[] | null; "disable-stream-toggle"?: boolean | null; - "direct-openapi-parser"?: boolean | null; + "openapi-parser-v2"?: boolean | null; } } diff --git a/packages/cli/docs-preview/src/previewDocs.ts b/packages/cli/docs-preview/src/previewDocs.ts index 1ec1f6abb51..5ddf4d498e0 100644 --- a/packages/cli/docs-preview/src/previewDocs.ts +++ b/packages/cli/docs-preview/src/previewDocs.ts @@ -105,8 +105,8 @@ export async function getPreviewDocsDefinition({ }); return { - apis: parsedDocsConfig.experimental?.directOpenapiParser ? {} : apiCollector.getAPIsForDefinition(), - apisV2: parsedDocsConfig.experimental?.directOpenapiParser ? apiCollectorV2.getAPIsForDefinition() : {}, + apis: parsedDocsConfig.experimental?.openapiParserV2 ? {} : apiCollector.getAPIsForDefinition(), + apisV2: parsedDocsConfig.experimental?.openapiParserV2 ? apiCollectorV2.getAPIsForDefinition() : {}, config: readDocsConfig, files: {}, filesV2, @@ -156,7 +156,10 @@ class ReferencedAPICollector { } catch (e) { // Print Error const err = e as Error; - this.context.logger.error(`Failed to read referenced API: ${err?.message} ${err?.stack}`); + this.context.logger.debug(`Failed to read referenced API: ${err?.message} ${err?.stack}`); + this.context.logger.error( + "An error occured while trying to read an API definition. Please reach out to support." + ); if (err.stack != null) { this.context.logger.error(err?.stack); } @@ -181,7 +184,10 @@ class ReferencedAPICollectorV2 { } catch (e) { // Print Error const err = e as Error; - this.context.logger.error(`Failed to read referenced API: ${err?.message} ${err?.stack}`); + this.context.logger.debug(`Failed to read referenced API: ${err?.message} ${err?.stack}`); + this.context.logger.error( + "An error occured while trying to read an API definition. Please reach out to support." + ); if (err.stack != null) { this.context.logger.error(err?.stack); } diff --git a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts index 15db2285e27..fb8c5c560f1 100644 --- a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts +++ b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts @@ -576,7 +576,7 @@ export class DocsDefinitionResolver { item: docsYml.DocsNavigationItem.ApiSection, parentSlug: FernNavigation.V1.SlugGenerator ): Promise<FernNavigation.V1.ApiReferenceNode> { - if (this.parsedDocsConfig.experimental?.directOpenapiParser) { + if (this.parsedDocsConfig.experimental?.openapiParserV2) { const workspace = this.getOpenApiWorkspaceForApiSection(item); const api = await generateFdrFromOpenApiWorkspace(workspace, this.taskContext); if (api == null) { From 917cd17bd27ffe64e308895aa8ec1a9d26832449 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava <rohin@buildwithfern.com> Date: Wed, 8 Jan 2025 17:38:27 -0500 Subject: [PATCH 11/11] ci/cd --- docs-yml.schema.json | 2 +- packages/cli/cli/src/utils/filterOssWorkspaces.ts | 2 +- packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts | 3 ++- .../cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs-yml.schema.json b/docs-yml.schema.json index bf779c195b1..7bb37f2dc7b 100644 --- a/docs-yml.schema.json +++ b/docs-yml.schema.json @@ -2259,7 +2259,7 @@ } ] }, - "direct-openapi-parser": { + "openapi-parser-v2": { "oneOf": [ { "type": "boolean" diff --git a/packages/cli/cli/src/utils/filterOssWorkspaces.ts b/packages/cli/cli/src/utils/filterOssWorkspaces.ts index 48b0080ff97..259bd895217 100644 --- a/packages/cli/cli/src/utils/filterOssWorkspaces.ts +++ b/packages/cli/cli/src/utils/filterOssWorkspaces.ts @@ -2,7 +2,7 @@ import { isNonNullish } from "@fern-api/core-utils"; import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { Project } from "@fern-api/project-loader"; -export async function filterOssWorkspaces(project: Project) { +export async function filterOssWorkspaces(project: Project): Promise<OSSWorkspace[]> { return ( await Promise.all( project.apiWorkspaces.map(async (workspace) => { diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts index 5c222480b4a..1702ff82daf 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverter.ts @@ -511,7 +511,8 @@ export class ApiReferenceNodeConverter { left, right, findEndpointById: (endpointId) => this.#holder.endpoints.get(endpointId), - stringifyEndpointPathParts: (endpoint) => stringifyEndpointPathParts(endpoint.path.parts), + stringifyEndpointPathParts: (endpoint: APIV1Read.EndpointDefinition) => + stringifyEndpointPathParts(endpoint.path.parts), disableEndpointPairs: this.disableEndpointPairs, apiDefinitionId: this.apiDefinitionId }); diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts index dca531db38d..5a913ddf9d4 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts @@ -553,7 +553,8 @@ export class ApiReferenceNodeConverterLatest { left, right, findEndpointById: (endpointId) => this.#findEndpointByLocator(endpointId), - stringifyEndpointPathParts: (endpoint) => stringifyEndpointPathParts(endpoint.path), + stringifyEndpointPathParts: (endpoint: FdrAPI.api.latest.EndpointDefinition) => + stringifyEndpointPathParts(endpoint.path), disableEndpointPairs: this.disableEndpointPairs, apiDefinitionId: this.apiDefinitionId });