@@ -70,29 +70,29 @@ export const generateFile = (options: GeneratorOptions) => {
7070 ctx . runtime === "none"
7171 ? ( file : string ) => file
7272 : ( file : string ) => {
73- const model = Codegen . TypeScriptToModel . Generate ( file ) ;
74- const transformer = runtimeValidationGenerator [ ctx . runtime as Exclude < typeof ctx . runtime , "none" > ] ;
75- // tmp fix for typebox, there's currently a "// todo" only with Codegen.ModelToTypeBox.Generate
76- // https://github.com/sinclairzx81/typebox-codegen/blob/44d44d55932371b69f349331b1c8a60f5d760d9e/src/model/model-to-typebox.ts#L31
77- const generated = ctx . runtime === "typebox" ? Codegen . TypeScriptToTypeBox . Generate ( file ) : transformer ( model ) ;
78-
79- let converted = "" ;
80- const match = generated . match ( / ( c o n s t _ _ E N D P O I N T S _ S T A R T _ _ = ) ( [ \s \S ] * ?) ( e x p o r t t y p e _ _ E N D P O I N T S _ E N D _ _ ) / ) ;
81- const content = match ?. [ 2 ] ;
82-
83- if ( content && ctx . runtime in replacerByRuntime ) {
84- const before = generated . slice ( 0 , generated . indexOf ( "export type __ENDPOINTS_START" ) ) ;
85- converted =
86- before +
87- replacerByRuntime [ ctx . runtime as keyof typeof replacerByRuntime ] (
88- content . slice ( content . indexOf ( "export" ) ) ,
89- ) ;
90- } else {
91- converted = generated ;
92- }
73+ const model = Codegen . TypeScriptToModel . Generate ( file ) ;
74+ const transformer = runtimeValidationGenerator [ ctx . runtime as Exclude < typeof ctx . runtime , "none" > ] ;
75+ // tmp fix for typebox, there's currently a "// todo" only with Codegen.ModelToTypeBox.Generate
76+ // https://github.com/sinclairzx81/typebox-codegen/blob/44d44d55932371b69f349331b1c8a60f5d760d9e/src/model/model-to-typebox.ts#L31
77+ const generated = ctx . runtime === "typebox" ? Codegen . TypeScriptToTypeBox . Generate ( file ) : transformer ( model ) ;
78+
79+ let converted = "" ;
80+ const match = generated . match ( / ( c o n s t _ _ E N D P O I N T S _ S T A R T _ _ = ) ( [ \s \S ] * ?) ( e x p o r t t y p e _ _ E N D P O I N T S _ E N D _ _ ) / ) ;
81+ const content = match ?. [ 2 ] ;
82+
83+ if ( content && ctx . runtime in replacerByRuntime ) {
84+ const before = generated . slice ( 0 , generated . indexOf ( "export type __ENDPOINTS_START" ) ) ;
85+ converted =
86+ before +
87+ replacerByRuntime [ ctx . runtime as keyof typeof replacerByRuntime ] (
88+ content . slice ( content . indexOf ( "export" ) ) ,
89+ ) ;
90+ } else {
91+ converted = generated ;
92+ }
9393
94- return converted ;
95- } ;
94+ return converted ;
95+ } ;
9696
9797 const file = `
9898 ${ transform ( schemaList + endpointSchemaList ) }
@@ -132,6 +132,24 @@ const parameterObjectToString = (parameters: Box<AnyBoxDef> | Record<string, Any
132132 }
133133 return str + "}" ;
134134} ;
135+
136+ const responseHeadersObjectToString = ( responseHeaders : Record < string , AnyBox > , ctx : GeneratorContext ) => {
137+ let str = "{" ;
138+ for ( const [ key , responseHeader ] of Object . entries ( responseHeaders ) ) {
139+ const value = ctx . runtime === "none"
140+ ? responseHeader . recompute ( ( box ) => {
141+ if ( Box . isReference ( box ) && ! box . params . generics && box . value !== "null" ) {
142+ box . value = `Schemas.${ box . value } ` ;
143+ }
144+
145+ return box ;
146+ } ) . value
147+ : responseHeader . value
148+ str += `${ wrapWithQuotesIfNeeded ( key . toLowerCase ( ) ) } : ${ value } ,\n` ;
149+ }
150+ return str + "}" ;
151+ }
152+
135153const generateEndpointSchemaList = ( ctx : GeneratorContext ) => {
136154 let file = `
137155 ${ ctx . runtime === "none" ? "export namespace Endpoints {" : "" }
@@ -145,39 +163,42 @@ const generateEndpointSchemaList = (ctx: GeneratorContext) => {
145163 path: "${ endpoint . path } ",
146164 requestFormat: "${ endpoint . requestFormat } ",
147165 ${
148- endpoint . meta . hasParameters
149- ? `parameters: {
166+ endpoint . meta . hasParameters
167+ ? `parameters: {
150168 ${ parameters . query ? `query: ${ parameterObjectToString ( parameters . query ) } ,` : "" }
151169 ${ parameters . path ? `path: ${ parameterObjectToString ( parameters . path ) } ,` : "" }
152170 ${ parameters . header ? `header: ${ parameterObjectToString ( parameters . header ) } ,` : "" }
153171 ${
154172 parameters . body
155173 ? `body: ${ parameterObjectToString (
156- ctx . runtime === "none"
157- ? parameters . body . recompute ( ( box ) => {
158- if ( Box . isReference ( box ) && ! box . params . generics ) {
159- box . value = `Schemas.${ box . value } ` ;
160- }
161- return box ;
162- } )
163- : parameters . body ,
164- ) } ,`
174+ ctx . runtime === "none"
175+ ? parameters . body . recompute ( ( box ) => {
176+ if ( Box . isReference ( box ) && ! box . params . generics ) {
177+ box . value = `Schemas.${ box . value } ` ;
178+ }
179+ return box ;
180+ } )
181+ : parameters . body ,
182+ ) } ,`
165183 : ""
166184 }
167185 }`
168- : "parameters: never,"
169- }
186+ : "parameters: never,"
187+ }
170188 response: ${
171- ctx . runtime === "none"
172- ? endpoint . response . recompute ( ( box ) => {
173- if ( Box . isReference ( box ) && ! box . params . generics && box . value !== "null" ) {
174- box . value = `Schemas.${ box . value } ` ;
175- }
176-
177- return box ;
178- } ) . value
179- : endpoint . response . value
180- } ,
189+ ctx . runtime === "none"
190+ ? endpoint . response . recompute ( ( box ) => {
191+ if ( Box . isReference ( box ) && ! box . params . generics && box . value !== "null" ) {
192+ box . value = `Schemas.${ box . value } ` ;
193+ }
194+
195+ return box ;
196+ } ) . value
197+ : endpoint . response . value
198+ } ,
199+ ${
200+ endpoint . responseHeaders ? `responseHeaders: ${ responseHeadersObjectToString ( endpoint . responseHeaders , ctx ) } ,` : ""
201+ }
181202 }\n` ;
182203 } ) ;
183204
@@ -199,14 +220,14 @@ const generateEndpointByMethod = (ctx: GeneratorContext) => {
199220 // <EndpointByMethod>
200221 export ${ ctx . runtime === "none" ? "type" : "const" } EndpointByMethod = {
201222 ${ Object . entries ( byMethods )
202- . map ( ( [ method , list ] ) => {
203- return `${ method } : {
223+ . map ( ( [ method , list ] ) => {
224+ return `${ method } : {
204225 ${ list . map (
205- ( endpoint ) => `"${ endpoint . path } ": ${ ctx . runtime === "none" ? "Endpoints." : "" } ${ endpoint . meta . alias } ` ,
206- ) }
226+ ( endpoint ) => `"${ endpoint . path } ": ${ ctx . runtime === "none" ? "Endpoints." : "" } ${ endpoint . meta . alias } ` ,
227+ ) }
207228 }` ;
208- } )
209- . join ( ",\n" ) }
229+ } )
230+ . join ( ",\n" ) }
210231 }
211232 ${ ctx . runtime === "none" ? "" : "export type EndpointByMethod = typeof EndpointByMethod;" }
212233 // </EndpointByMethod>
@@ -216,8 +237,8 @@ const generateEndpointByMethod = (ctx: GeneratorContext) => {
216237
217238 // <EndpointByMethod.Shorthands>
218239 ${ Object . keys ( byMethods )
219- . map ( ( method ) => `export type ${ capitalize ( method ) } Endpoints = EndpointByMethod["${ method } "]` )
220- . join ( "\n" ) }
240+ . map ( ( method ) => `export type ${ capitalize ( method ) } Endpoints = EndpointByMethod["${ method } "]` )
241+ . join ( "\n" ) }
221242 // </EndpointByMethod.Shorthands>
222243 ` ;
223244
@@ -246,6 +267,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text";
246267export type DefaultEndpoint = {
247268 parameters?: EndpointParameters | undefined;
248269 response: unknown;
270+ responseHeaders?: Record<string, unknown>;
249271};
250272
251273export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
@@ -260,6 +282,7 @@ export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
260282 areParametersRequired: boolean;
261283 };
262284 response: TConfig["response"];
285+ responseHeaders?: TConfig["responseHeaders"]
263286};
264287
265288export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
@@ -303,18 +326,18 @@ export class ApiClient {
303326 ${ method } <Path extends keyof ${ capitalizedMethod } Endpoints, TEndpoint extends ${ capitalizedMethod } Endpoints[Path]>(
304327 path: Path,
305328 ...params: MaybeOptionalArg<${ match ( ctx . runtime )
306- . with ( "zod" , "yup" , ( ) => infer ( `TEndpoint["parameters"]` ) )
307- . with ( "arktype" , "io-ts" , "typebox" , "valibot" , ( ) => infer ( `TEndpoint` ) + `["parameters"]` )
308- . otherwise ( ( ) => `TEndpoint["parameters"]` ) } >
329+ . with ( "zod" , "yup" , ( ) => infer ( `TEndpoint["parameters"]` ) )
330+ . with ( "arktype" , "io-ts" , "typebox" , "valibot" , ( ) => infer ( `TEndpoint` ) + `["parameters"]` )
331+ . otherwise ( ( ) => `TEndpoint["parameters"]` ) } >
309332 ): Promise<${ match ( ctx . runtime )
310- . with ( "zod" , "yup" , ( ) => infer ( `TEndpoint["response"]` ) )
311- . with ( "arktype" , "io-ts" , "typebox" , "valibot" , ( ) => infer ( `TEndpoint` ) + `["response"]` )
312- . otherwise ( ( ) => `TEndpoint["response"]` ) } > {
333+ . with ( "zod" , "yup" , ( ) => infer ( `TEndpoint["response"]` ) )
334+ . with ( "arktype" , "io-ts" , "typebox" , "valibot" , ( ) => infer ( `TEndpoint` ) + `["response"]` )
335+ . otherwise ( ( ) => `TEndpoint["response"]` ) } > {
313336 return this.fetcher("${ method } ", this.baseUrl + path, params[0])
314337 .then(response => this.parseResponse(response))${ match ( ctx . runtime )
315- . with ( "zod" , "yup" , ( ) => `as Promise<${ infer ( `TEndpoint["response"]` ) } >` )
316- . with ( "arktype" , "io-ts" , "typebox" , "valibot" , ( ) => `as Promise<${ infer ( `TEndpoint` ) + `["response"]` } >` )
317- . otherwise ( ( ) => `as Promise<TEndpoint["response"]>` ) } ;
338+ . with ( "zod" , "yup" , ( ) => `as Promise<${ infer ( `TEndpoint["response"]` ) } >` )
339+ . with ( "arktype" , "io-ts" , "typebox" , "valibot" , ( ) => `as Promise<${ infer ( `TEndpoint` ) + `["response"]` } >` )
340+ . otherwise ( ( ) => `as Promise<TEndpoint["response"]>` ) } ;
318341 }
319342 // </ApiClient.${ method } >
320343 `
@@ -334,17 +357,17 @@ export class ApiClient {
334357 method: TMethod,
335358 path: TPath,
336359 ...params: MaybeOptionalArg<${ match ( ctx . runtime )
337- . with ( "zod" , "yup" , ( ) =>
338- inferByRuntime [ ctx . runtime ] ( `TEndpoint extends { parameters: infer Params } ? Params : never` ) ,
339- )
340- . with (
341- "arktype" ,
342- "io-ts" ,
343- "typebox" ,
344- "valibot" ,
345- ( ) => inferByRuntime [ ctx . runtime ] ( `TEndpoint` ) + `["parameters"]` ,
346- )
347- . otherwise ( ( ) => `TEndpoint extends { parameters: infer Params } ? Params : never` ) } >)
360+ . with ( "zod" , "yup" , ( ) =>
361+ inferByRuntime [ ctx . runtime ] ( `TEndpoint extends { parameters: infer Params } ? Params : never` ) ,
362+ )
363+ . with (
364+ "arktype" ,
365+ "io-ts" ,
366+ "typebox" ,
367+ "valibot" ,
368+ ( ) => inferByRuntime [ ctx . runtime ] ( `TEndpoint` ) + `["parameters"]` ,
369+ )
370+ . otherwise ( ( ) => `TEndpoint extends { parameters: infer Params } ? Params : never` ) } >)
348371 : Promise<Omit<Response, "json"> & {
349372 /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */
350373 json: () => Promise<TEndpoint extends { response: infer Res } ? Res : never>;
0 commit comments