1+ import type { ErrorResponse , HttpMethod , SuccessResponse , FilterKeys , MediaType , PathsWithMethod , ResponseObjectMap , OperationRequestBodyContent } from "openapi-typescript-helpers" ;
2+
13// settings & const
24const DEFAULT_HEADERS = {
35 "Content-Type" : "application/json" ,
@@ -19,55 +21,24 @@ interface ClientOptions extends RequestInit {
1921 /** global bodySerializer */
2022 bodySerializer ?: BodySerializer < unknown > ;
2123}
22- export interface BaseParams {
23- params ?: { query ?: Record < string , unknown > } ;
24- }
25-
26- // const
27-
28- export type PathItemObject = { [ M in HttpMethod ] : OperationObject } & { parameters ?: any } ;
24+ export type QuerySerializer < T > = ( query : T extends { parameters : any } ? NonNullable < T [ "parameters" ] [ "query" ] > : Record < string , unknown > ) => string ;
25+ export type BodySerializer < T > = ( body : OperationRequestBodyContent < T > ) => any ;
2926export type ParseAs = "json" | "text" | "blob" | "arrayBuffer" | "stream" ;
30- export interface OperationObject {
31- parameters : any ;
32- requestBody : any ; // note: "any" will get overridden in inference
33- responses : any ;
27+ export interface DefaultParamsOption {
28+ params ?: { query ?: Record < string , unknown > } ;
3429}
35- export type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace" ;
36- export type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX" ;
37- // prettier-ignore
38- export type ErrorStatus = 500 | '5XX' | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 429 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | '4XX' | "default" ;
39-
40- // util
41- /** Get a union of paths which have method */
42- export type PathsWith < Paths extends Record < string , PathItemObject > , PathnameMethod extends HttpMethod > = {
43- [ Pathname in keyof Paths ] : Paths [ Pathname ] extends { [ K in PathnameMethod ] : any } ? Pathname : never ;
44- } [ keyof Paths ] ;
45- /** Find first match of multiple keys */
46- export type FilterKeys < Obj , Matchers > = { [ K in keyof Obj ] : K extends Matchers ? Obj [ K ] : never } [ keyof Obj ] ;
47- export type MediaType = `${string } /${string } `;
48-
49- // general purpose types
50- export type Params < T > = T extends { parameters : any } ? { params : NonNullable < T [ "parameters" ] > } : BaseParams ;
51- export type RequestBodyObj < T > = T extends { requestBody ?: any } ? T [ "requestBody" ] : never ;
52- export type RequestBodyContent < T > = undefined extends RequestBodyObj < T > ? FilterKeys < NonNullable < RequestBodyObj < T > > , "content" > | undefined : FilterKeys < RequestBodyObj < T > , "content" > ;
53- export type RequestBodyMedia < T > = FilterKeys < RequestBodyContent < T > , MediaType > extends never ? FilterKeys < NonNullable < RequestBodyContent < T > > , MediaType > | undefined : FilterKeys < RequestBodyContent < T > , MediaType > ;
54- export type RequestBody < T > = RequestBodyMedia < T > extends never ? { body ?: never } : undefined extends RequestBodyMedia < T > ? { body ?: RequestBodyMedia < T > } : { body : RequestBodyMedia < T > } ;
55- export type QuerySerializer < T > = ( query : T extends { parameters : any } ? NonNullable < T [ "parameters" ] [ "query" ] > : Record < string , unknown > ) => string ;
56- export type BodySerializer < T > = ( body : RequestBodyMedia < T > ) => any ;
57- export type RequestOptions < T > = Params < T > &
58- RequestBody < T > & {
30+ export type ParamsOption < T > = T extends { parameters : any } ? { params : NonNullable < T [ "parameters" ] > } : DefaultParamsOption ;
31+ export type RequestBodyOption < T > = OperationRequestBodyContent < T > extends never ? { body ?: never } : undefined extends OperationRequestBodyContent < T > ? { body ?: OperationRequestBodyContent < T > } : { body : OperationRequestBodyContent < T > } ;
32+ export type FetchOptions < T > = RequestOptions < T > & Omit < RequestInit , "body" > ;
33+ export type FetchResponse < T > =
34+ | { data : FilterKeys < SuccessResponse < ResponseObjectMap < T > > , MediaType > ; error ?: never ; response : Response }
35+ | { data ?: never ; error : FilterKeys < ErrorResponse < ResponseObjectMap < T > > , MediaType > ; response : Response } ;
36+ export type RequestOptions < T > = ParamsOption < T > &
37+ RequestBodyOption < T > & {
5938 querySerializer ?: QuerySerializer < T > ;
6039 bodySerializer ?: BodySerializer < T > ;
6140 parseAs ?: ParseAs ;
6241 } ;
63- export type Success < T > = FilterKeys < FilterKeys < T , OkStatus > , "content" > ;
64- export type Error < T > = FilterKeys < FilterKeys < T , ErrorStatus > , "content" > ;
65-
66- // fetch types
67- export type FetchOptions < T > = RequestOptions < T > & Omit < RequestInit , "body" > ;
68- export type FetchResponse < T > =
69- | { data : T extends { responses : any } ? NonNullable < FilterKeys < Success < T [ "responses" ] > , MediaType > > : unknown ; error ?: never ; response : Response }
70- | { data ?: never ; error : T extends { responses : any } ? NonNullable < FilterKeys < Error < T [ "responses" ] > , MediaType > > : unknown ; response : Response } ;
7142
7243export default function createClient < Paths extends { } > ( clientOptions : ClientOptions = { } ) {
7344 const { fetch = globalThis . fetch , querySerializer : globalQuerySerializer , bodySerializer : globalBodySerializer , ...options } = clientOptions ;
@@ -94,7 +65,7 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
9465 // handle empty content
9566 // note: we return `{}` because we want user truthy checks for `.data` or `.error` to succeed
9667 if ( response . status === 204 || response . headers . get ( "Content-Length" ) === "0" ) {
97- return response . ok ? { data : { } as any , response } : { error : { } as any , response } ;
68+ return response . ok ? { data : { } as any , response : response as any } : { error : { } as any , response : response as any } ;
9869 }
9970
10071 // parse response (falling back to .text() when necessary)
@@ -104,7 +75,7 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
10475 const cloned = response . clone ( ) ;
10576 data = typeof cloned [ parseAs ] === "function" ? await cloned [ parseAs ] ( ) : await cloned . text ( ) ;
10677 }
107- return { data, response } ;
78+ return { data, response : response as any } ;
10879 }
10980
11081 // handle errors (always parse as .json() or .text())
@@ -114,40 +85,40 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
11485 } catch {
11586 error = await response . clone ( ) . text ( ) ;
11687 }
117- return { error, response } ;
88+ return { error, response : response as any } ;
11889 }
11990
12091 return {
12192 /** Call a GET endpoint */
122- async GET < P extends PathsWith < Paths , "get" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "get" > > ) {
93+ async GET < P extends PathsWithMethod < Paths , "get" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "get" > > ) {
12394 return coreFetch < P , "get" > ( url , { ...init , method : "GET" } as any ) ;
12495 } ,
12596 /** Call a PUT endpoint */
126- async PUT < P extends PathsWith < Paths , "put" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "put" > > ) {
97+ async PUT < P extends PathsWithMethod < Paths , "put" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "put" > > ) {
12798 return coreFetch < P , "put" > ( url , { ...init , method : "PUT" } as any ) ;
12899 } ,
129100 /** Call a POST endpoint */
130- async POST < P extends PathsWith < Paths , "post" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "post" > > ) {
101+ async POST < P extends PathsWithMethod < Paths , "post" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "post" > > ) {
131102 return coreFetch < P , "post" > ( url , { ...init , method : "POST" } as any ) ;
132103 } ,
133104 /** Call a DELETE endpoint */
134- async DELETE < P extends PathsWith < Paths , "delete" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "delete" > > ) {
105+ async DELETE < P extends PathsWithMethod < Paths , "delete" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "delete" > > ) {
135106 return coreFetch < P , "delete" > ( url , { ...init , method : "DELETE" } as any ) ;
136107 } ,
137108 /** Call a OPTIONS endpoint */
138- async OPTIONS < P extends PathsWith < Paths , "options" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "options" > > ) {
109+ async OPTIONS < P extends PathsWithMethod < Paths , "options" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "options" > > ) {
139110 return coreFetch < P , "options" > ( url , { ...init , method : "OPTIONS" } as any ) ;
140111 } ,
141112 /** Call a HEAD endpoint */
142- async HEAD < P extends PathsWith < Paths , "head" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "head" > > ) {
113+ async HEAD < P extends PathsWithMethod < Paths , "head" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "head" > > ) {
143114 return coreFetch < P , "head" > ( url , { ...init , method : "HEAD" } as any ) ;
144115 } ,
145116 /** Call a PATCH endpoint */
146- async PATCH < P extends PathsWith < Paths , "patch" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "patch" > > ) {
117+ async PATCH < P extends PathsWithMethod < Paths , "patch" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "patch" > > ) {
147118 return coreFetch < P , "patch" > ( url , { ...init , method : "PATCH" } as any ) ;
148119 } ,
149120 /** Call a TRACE endpoint */
150- async TRACE < P extends PathsWith < Paths , "trace" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "trace" > > ) {
121+ async TRACE < P extends PathsWithMethod < Paths , "trace" > > ( url : P , init : FetchOptions < FilterKeys < Paths [ P ] , "trace" > > ) {
151122 return coreFetch < P , "trace" > ( url , { ...init , method : "TRACE" } as any ) ;
152123 } ,
153124 } ;
0 commit comments