@@ -7,7 +7,8 @@ import type { CppLibraryDocsIr } from "@fern-api/library-docs-generator";
77import { generate , generateCpp } from "@fern-api/library-docs-generator" ;
88import { askToLogin } from "@fern-api/login" ;
99import { Project } from "@fern-api/project-loader" ;
10- import { InteractiveTaskContext , TaskContext } from "@fern-api/task-context" ;
10+ import { CliError , InteractiveTaskContext , TaskContext } from "@fern-api/task-context" ;
11+
1112import chalk from "chalk" ;
1213import { readFile } from "fs/promises" ;
1314import { CliContext } from "../../cli-context/CliContext.js" ;
@@ -77,7 +78,7 @@ function createLibraryDocsClient({ token }: { token: string }): LibraryDocsClien
7778
7879 if ( ! response . ok ) {
7980 const text = await response . text ( ) . catch ( ( ) => "" ) ;
80- throw new Error ( `HTTP ${ response . status } : ${ text } ` ) ;
81+ throw new CliError ( { message : `HTTP ${ response . status } : ${ text } ` , code : CliError . Code . NetworkError } ) ;
8182 }
8283
8384 return ( await response . json ( ) ) as T ;
@@ -116,7 +117,9 @@ export async function generateLibraryDocs({ project, cliContext, library }: Gene
116117 const docsWorkspace = project . docsWorkspaces ;
117118
118119 if ( docsWorkspace == null ) {
119- cliContext . failAndThrow ( "No docs workspace found. Make sure you have a docs.yml file." ) ;
120+ cliContext . failAndThrow ( "No docs workspace found. Make sure you have a docs.yml file." , undefined , {
121+ code : CliError . Code . ConfigError
122+ } ) ;
120123 return ;
121124 }
122125
@@ -125,7 +128,9 @@ export async function generateLibraryDocs({ project, cliContext, library }: Gene
125128
126129 if ( libraries == null || Object . keys ( libraries ) . length === 0 ) {
127130 cliContext . failAndThrow (
128- "No libraries configured in docs.yml. Add a `libraries` section to configure library documentation."
131+ "No libraries configured in docs.yml. Add a `libraries` section to configure library documentation." ,
132+ undefined ,
133+ { code : CliError . Code . ConfigError }
129134 ) ;
130135 return ;
131136 }
@@ -134,7 +139,9 @@ export async function generateLibraryDocs({ project, cliContext, library }: Gene
134139
135140 if ( library != null && libraries [ library ] == null ) {
136141 cliContext . failAndThrow (
137- `Library '${ library } ' not found in docs.yml. Available libraries: ${ Object . keys ( libraries ) . join ( ", " ) } `
142+ `Library '${ library } ' not found in docs.yml. Available libraries: ${ Object . keys ( libraries ) . join ( ", " ) } ` ,
143+ undefined ,
144+ { code : CliError . Code . ConfigError }
138145 ) ;
139146 return ;
140147 }
@@ -144,7 +151,9 @@ export async function generateLibraryDocs({ project, cliContext, library }: Gene
144151 } ) ;
145152
146153 if ( token == null ) {
147- cliContext . failAndThrow ( "Failed to authenticate. Please run 'fern login' first." ) ;
154+ cliContext . failAndThrow ( "Failed to authenticate. Please run 'fern login' first." , undefined , {
155+ code : CliError . Code . AuthError
156+ } ) ;
148157 return ;
149158 }
150159
@@ -158,7 +167,9 @@ export async function generateLibraryDocs({ project, cliContext, library }: Gene
158167 }
159168 if ( ! isGitLibraryInput ( config . input ) ) {
160169 context . failAndThrow (
161- `Library '${ name } ' uses 'path' input which is not yet supported. Please use 'git' input.`
170+ `Library '${ name } ' uses 'path' input which is not yet supported. Please use 'git' input.` ,
171+ undefined ,
172+ { code : CliError . Code . ConfigError }
162173 ) ;
163174 return false ;
164175 }
@@ -204,7 +215,9 @@ async function generateSingleLibrary({
204215 // Validate language-specific config
205216 if ( config . config ?. doxyfile != null && config . lang !== "cpp" ) {
206217 return interactiveTaskContext . failAndThrow (
207- `Library '${ name } ': 'doxyfile' config is only valid for lang: cpp`
218+ `Library '${ name } ': 'doxyfile' config is only valid for lang: cpp` ,
219+ undefined ,
220+ { code : CliError . Code . ConfigError }
208221 ) ;
209222 }
210223
@@ -216,14 +229,20 @@ async function generateSingleLibrary({
216229 doxyfileContent = await readFile ( doxyfilePath , "utf-8" ) ;
217230 } catch {
218231 return interactiveTaskContext . failAndThrow (
219- `Library '${ name } ': Could not read Doxyfile at '${ config . config . doxyfile } ' (resolved to ${ doxyfilePath } )`
232+ `Library '${ name } ': Could not read Doxyfile at '${ config . config . doxyfile } ' (resolved to ${ doxyfilePath } )` ,
233+ undefined ,
234+ { code : CliError . Code . ConfigError }
220235 ) ;
221236 }
222237 }
223238
224239 const language = config . lang === "python" ? "PYTHON" : config . lang === "cpp" ? "CPP" : undefined ;
225240 if ( language == null ) {
226- return interactiveTaskContext . failAndThrow ( `Library '${ name } ': unsupported language '${ config . lang } '` ) ;
241+ return interactiveTaskContext . failAndThrow (
242+ `Library '${ name } ': unsupported language '${ config . lang } '` ,
243+ undefined ,
244+ { code : CliError . Code . ConfigError }
245+ ) ;
227246 }
228247
229248 const client = createLibraryDocsClient ( { token : token . value } ) ;
@@ -268,7 +287,11 @@ async function generateSingleLibrary({
268287 } ) ;
269288 interactiveTaskContext . logger . debug ( `Generated ${ generateResult . pageCount } pages at ${ resolvedOutputPath } ` ) ;
270289 } else {
271- return interactiveTaskContext . failAndThrow ( `Library '${ name } ': unsupported language '${ config . lang } '` ) ;
290+ return interactiveTaskContext . failAndThrow (
291+ `Library '${ name } ': unsupported language '${ config . lang } '` ,
292+ undefined ,
293+ { code : CliError . Code . ConfigError }
294+ ) ;
272295 }
273296 } ) ;
274297}
@@ -302,7 +325,9 @@ async function startGeneration(
302325 return result . jobId ;
303326 } catch ( error ) {
304327 return context . failAndThrow (
305- `Failed to start generation for library '${ opts . name } ': ${ extractErrorMessage ( error ) } `
328+ `Failed to start generation for library '${ opts . name } ': ${ extractErrorMessage ( error ) } ` ,
329+ error ,
330+ { code : CliError . Code . NetworkError }
306331 ) ;
307332 }
308333}
@@ -323,7 +348,9 @@ async function pollForCompletion(
323348 status = await client . getLibraryDocsGenerationStatus ( { jobId } ) ;
324349 } catch ( error ) {
325350 return context . failAndThrow (
326- `Failed to check generation status for library '${ libraryName } ': ${ extractErrorMessage ( error ) } `
351+ `Failed to check generation status for library '${ libraryName } ': ${ extractErrorMessage ( error ) } ` ,
352+ error ,
353+ { code : CliError . Code . NetworkError }
327354 ) ;
328355 }
329356
@@ -339,16 +366,24 @@ async function pollForCompletion(
339366 return ;
340367 case "FAILED" :
341368 return context . failAndThrow (
342- `Generation failed for library '${ libraryName } ': ${ status . error ?. message ?? "Unknown error" } (${ status . error ?. code ?? "UNKNOWN" } )`
369+ `Generation failed for library '${ libraryName } ': ${ status . error ?. message ?? "Unknown error" } (${ status . error ?. code ?? "UNKNOWN" } )` ,
370+ undefined ,
371+ { code : CliError . Code . InternalError }
343372 ) ;
344373 default :
345374 return context . failAndThrow (
346- `Unexpected generation status for library '${ libraryName } ': ${ status . status } `
375+ `Unexpected generation status for library '${ libraryName } ': ${ status . status } ` ,
376+ undefined ,
377+ { code : CliError . Code . InternalError }
347378 ) ;
348379 }
349380 }
350381
351- return context . failAndThrow ( `Generation timed out for library '${ libraryName } ' after ${ POLL_TIMEOUT_MS / 1000 } s` ) ;
382+ return context . failAndThrow (
383+ `Generation timed out for library '${ libraryName } ' after ${ POLL_TIMEOUT_MS / 1000 } s` ,
384+ undefined ,
385+ { code : CliError . Code . NetworkError }
386+ ) ;
352387}
353388
354389async function downloadIr (
@@ -364,33 +399,41 @@ async function downloadIr(
364399 resultUrl = result . resultUrl ;
365400 } catch ( error ) {
366401 return context . failAndThrow (
367- `Failed to fetch generation result for library '${ libraryName } ': ${ extractErrorMessage ( error ) } `
402+ `Failed to fetch generation result for library '${ libraryName } ': ${ extractErrorMessage ( error ) } ` ,
403+ error ,
404+ { code : CliError . Code . NetworkError }
368405 ) ;
369406 }
370407
371408 context . logger . debug ( `Fetching IR from ${ resultUrl } ` ) ;
372409 const irFetchResponse = await fetch ( resultUrl ) ;
373410 if ( ! irFetchResponse . ok ) {
374411 return context . failAndThrow (
375- `Failed to download IR for library '${ libraryName } ': HTTP ${ irFetchResponse . status } `
412+ `Failed to download IR for library '${ libraryName } ': HTTP ${ irFetchResponse . status } ` ,
413+ undefined ,
414+ { code : CliError . Code . NetworkError }
376415 ) ;
377416 }
378417
379418 const irWrapper = await irFetchResponse . json ( ) ;
380419 const ir = irWrapper . ir ;
381420
382421 if ( ir == null ) {
383- return context . failAndThrow ( `IR is empty for library '${ libraryName } '` ) ;
422+ return context . failAndThrow ( `IR is empty for library '${ libraryName } '` , undefined , { code : CliError . Code . InternalError } ) ;
384423 }
385424
386425 if ( language === "CPP" ) {
387426 if ( ir . rootNamespace == null ) {
388- return context . failAndThrow ( `IR has no rootNamespace for C++ library '${ libraryName } '` ) ;
427+ return context . failAndThrow ( `IR has no rootNamespace for C++ library '${ libraryName } '` , undefined , {
428+ code : CliError . Code . InternalError
429+ } ) ;
389430 }
390431 context . logger . debug ( `Downloaded C++ IR for '${ libraryName } '` ) ;
391432 } else {
392433 if ( ir . rootModule == null ) {
393- return context . failAndThrow ( `IR has no rootModule for library '${ libraryName } '` ) ;
434+ return context . failAndThrow ( `IR has no rootModule for library '${ libraryName } '` , undefined , {
435+ code : CliError . Code . InternalError
436+ } ) ;
394437 }
395438 context . logger . debug ( `Downloaded IR with ${ Object . keys ( ir . rootModule ) . length } top-level keys` ) ;
396439 }
0 commit comments