1
+ import type { ErrorResponse , HttpMethod , SuccessResponse , FilterKeys , MediaType , PathsWithMethod , ResponseObjectMap , OperationRequestBodyContent } from "openapi-typescript-helpers" ;
2
+
1
3
// settings & const
2
4
const DEFAULT_HEADERS = {
3
5
"Content-Type" : "application/json" ,
@@ -19,55 +21,24 @@ interface ClientOptions extends RequestInit {
19
21
/** global bodySerializer */
20
22
bodySerializer ?: BodySerializer < unknown > ;
21
23
}
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 ;
29
26
export 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 > } ;
34
29
}
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 > & {
59
38
querySerializer ?: QuerySerializer < T > ;
60
39
bodySerializer ?: BodySerializer < T > ;
61
40
parseAs ?: ParseAs ;
62
41
} ;
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 } ;
71
42
72
43
export default function createClient < Paths extends { } > ( clientOptions : ClientOptions = { } ) {
73
44
const { fetch = globalThis . fetch , querySerializer : globalQuerySerializer , bodySerializer : globalBodySerializer , ...options } = clientOptions ;
@@ -94,7 +65,7 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
94
65
// handle empty content
95
66
// note: we return `{}` because we want user truthy checks for `.data` or `.error` to succeed
96
67
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 } ;
98
69
}
99
70
100
71
// parse response (falling back to .text() when necessary)
@@ -104,7 +75,7 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
104
75
const cloned = response . clone ( ) ;
105
76
data = typeof cloned [ parseAs ] === "function" ? await cloned [ parseAs ] ( ) : await cloned . text ( ) ;
106
77
}
107
- return { data, response } ;
78
+ return { data, response : response as any } ;
108
79
}
109
80
110
81
// handle errors (always parse as .json() or .text())
@@ -114,40 +85,40 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
114
85
} catch {
115
86
error = await response . clone ( ) . text ( ) ;
116
87
}
117
- return { error, response } ;
88
+ return { error, response : response as any } ;
118
89
}
119
90
120
91
return {
121
92
/** 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" > > ) {
123
94
return coreFetch < P , "get" > ( url , { ...init , method : "GET" } as any ) ;
124
95
} ,
125
96
/** 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" > > ) {
127
98
return coreFetch < P , "put" > ( url , { ...init , method : "PUT" } as any ) ;
128
99
} ,
129
100
/** 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" > > ) {
131
102
return coreFetch < P , "post" > ( url , { ...init , method : "POST" } as any ) ;
132
103
} ,
133
104
/** 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" > > ) {
135
106
return coreFetch < P , "delete" > ( url , { ...init , method : "DELETE" } as any ) ;
136
107
} ,
137
108
/** 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" > > ) {
139
110
return coreFetch < P , "options" > ( url , { ...init , method : "OPTIONS" } as any ) ;
140
111
} ,
141
112
/** 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" > > ) {
143
114
return coreFetch < P , "head" > ( url , { ...init , method : "HEAD" } as any ) ;
144
115
} ,
145
116
/** 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" > > ) {
147
118
return coreFetch < P , "patch" > ( url , { ...init , method : "PATCH" } as any ) ;
148
119
} ,
149
120
/** 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" > > ) {
151
122
return coreFetch < P , "trace" > ( url , { ...init , method : "TRACE" } as any ) ;
152
123
} ,
153
124
} ;
0 commit comments