11import path from 'node:path' ;
22
33import { type Logger , Project } from '@hey-api/codegen-core' ;
4- import { $RefParser } from '@hey-api/json-schema-ref-parser' ;
4+ import { $RefParser , ResolverError } from '@hey-api/json-schema-ref-parser' ;
5+ import type { Input , OpenApi , WatchValues } from '@hey-api/shared' ;
56import {
67 applyNaming ,
78 buildGraph ,
89 compileInputPath ,
910 Context ,
1011 getSpec ,
11- type Input ,
12+ InputError ,
1213 logInputPaths ,
13- type OpenApi ,
1414 parseOpenApiSpec ,
1515 patchOpenApiSpec ,
1616 postprocessOutput ,
17- type WatchValues ,
1817} from '@hey-api/shared' ;
1918import colors from 'ansi-colors' ;
2019
@@ -66,10 +65,20 @@ export async function createClient({
6665 // if in watch mode, subsequent errors won't throw to gracefully handle
6766 // cases where server might be reloading
6867 if ( error && ! _watches ) {
69- const text = await response . text ( ) . catch ( ( ) => '' ) ;
70- throw new Error (
71- `Request failed with status ${ response . status } : ${ text || response . statusText } ` ,
72- ) ;
68+ const text = await response . text ( ) . catch ( ( ) : string => '' ) ;
69+ const message = `Request failed with status ${ response . status } : ${ text || response . statusText } ` ;
70+ // Handle 4xx client errors as input errors (bad URL, bad API key, etc.)
71+ if ( response . status >= 400 && response . status < 500 ) {
72+ const statusText = response . statusText || 'Unknown' ;
73+ const originalError = new Error ( message ) as Error & { source ?: string } ;
74+ originalError . source = String ( inputPaths [ index ] ! . path ) ;
75+ throw new InputError (
76+ `Input request failed: ${ response . status } ${ statusText } ` ,
77+ originalError ,
78+ ) ;
79+ }
80+ // For 5xx server errors, keep the generic error (could be a bug)
81+ throw new Error ( message ) ;
7382 }
7483
7584 return { arrayBuffer, resolvedInput } ;
@@ -82,18 +91,26 @@ export async function createClient({
8291
8392 if ( specData . length ) {
8493 const refParser = new $RefParser ( ) ;
85- const data =
86- specData . length > 1
87- ? await refParser . bundleMany ( {
88- arrayBuffer : specData . map ( ( data ) => data . arrayBuffer ! ) ,
89- pathOrUrlOrSchemas : [ ] ,
90- resolvedInputs : specData . map ( ( data ) => data . resolvedInput ! ) ,
91- } )
92- : await refParser . bundle ( {
93- arrayBuffer : specData [ 0 ] ! . arrayBuffer ,
94- pathOrUrlOrSchema : undefined ,
95- resolvedInput : specData [ 0 ] ! . resolvedInput ! ,
96- } ) ;
94+ let data : unknown ;
95+ try {
96+ data =
97+ specData . length > 1
98+ ? await refParser . bundleMany ( {
99+ arrayBuffer : specData . map ( ( data ) => data . arrayBuffer ! ) ,
100+ pathOrUrlOrSchemas : [ ] ,
101+ resolvedInputs : specData . map ( ( data ) => data . resolvedInput ! ) ,
102+ } )
103+ : await refParser . bundle ( {
104+ arrayBuffer : specData [ 0 ] ! . arrayBuffer ,
105+ pathOrUrlOrSchema : undefined ,
106+ resolvedInput : specData [ 0 ] ! . resolvedInput ! ,
107+ } ) ;
108+ } catch ( err ) {
109+ if ( err instanceof ResolverError && err . ioErrorCode === 'ENOENT' ) {
110+ throw new InputError ( 'Input file not found' , err ) ;
111+ }
112+ throw err ;
113+ }
97114
98115 // on subsequent runs in watch mode, print the message only if we know we're
99116 // generating the output
0 commit comments