openapi-typesafe-fetch 1.0.0-rc.2 → 1.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +763 -0
- package/README.md +2 -2
- package/dist/helpers.cjs +3 -0
- package/dist/helpers.cjs.map +1 -0
- package/dist/helpers.d.cts +299 -0
- package/dist/helpers.d.mts +299 -0
- package/dist/helpers.d.ts +299 -0
- package/dist/helpers.mjs +2 -0
- package/dist/helpers.mjs.map +1 -0
- package/dist/index.cjs +190 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -17
- package/dist/index.d.mts +64 -17
- package/dist/index.d.ts +64 -17
- package/dist/index.mjs +190 -131
- package/dist/index.mjs.map +1 -1
- package/package.json +18 -9
- package/src/helpers.ts +437 -0
- package/src/index.d.ts +60 -16
- package/src/index.js +305 -183
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
// HTTP types
|
|
2
|
+
|
|
3
|
+
export type HttpMethod =
|
|
4
|
+
| "get"
|
|
5
|
+
| "put"
|
|
6
|
+
| "post"
|
|
7
|
+
| "delete"
|
|
8
|
+
| "options"
|
|
9
|
+
| "head"
|
|
10
|
+
| "patch"
|
|
11
|
+
| "trace"
|
|
12
|
+
/** OpenAPI 3.2: a safe, idempotent request with a body. */
|
|
13
|
+
| "query";
|
|
14
|
+
/** 2XX statuses */
|
|
15
|
+
export type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
|
|
16
|
+
/** 4XX and 5XX statuses */
|
|
17
|
+
export type ErrorStatus =
|
|
18
|
+
| 500
|
|
19
|
+
| 501
|
|
20
|
+
| 502
|
|
21
|
+
| 503
|
|
22
|
+
| 504
|
|
23
|
+
| 505
|
|
24
|
+
| 506
|
|
25
|
+
| 507
|
|
26
|
+
| 508
|
|
27
|
+
| 510
|
|
28
|
+
| 511
|
|
29
|
+
| "5XX"
|
|
30
|
+
| 400
|
|
31
|
+
| 401
|
|
32
|
+
| 402
|
|
33
|
+
| 403
|
|
34
|
+
| 404
|
|
35
|
+
| 405
|
|
36
|
+
| 406
|
|
37
|
+
| 407
|
|
38
|
+
| 408
|
|
39
|
+
| 409
|
|
40
|
+
| 410
|
|
41
|
+
| 411
|
|
42
|
+
| 412
|
|
43
|
+
| 413
|
|
44
|
+
| 414
|
|
45
|
+
| 415
|
|
46
|
+
| 416
|
|
47
|
+
| 417
|
|
48
|
+
| 418
|
|
49
|
+
| 420
|
|
50
|
+
| 421
|
|
51
|
+
| 422
|
|
52
|
+
| 423
|
|
53
|
+
| 424
|
|
54
|
+
| 425
|
|
55
|
+
| 426
|
|
56
|
+
| 427
|
|
57
|
+
| 428
|
|
58
|
+
| 429
|
|
59
|
+
| 430
|
|
60
|
+
| 431
|
|
61
|
+
| 444
|
|
62
|
+
| 450
|
|
63
|
+
| 451
|
|
64
|
+
| 497
|
|
65
|
+
| 498
|
|
66
|
+
| 499
|
|
67
|
+
| "4XX"
|
|
68
|
+
| "default";
|
|
69
|
+
|
|
70
|
+
/** Get a union of OK Statuses */
|
|
71
|
+
export type OKStatusUnion<T> = FilterKeys<T, OkStatus>;
|
|
72
|
+
|
|
73
|
+
/** Get first error status, in order */
|
|
74
|
+
// biome-ignore format: this is dumb but reliable
|
|
75
|
+
export type FirstErrorStatus<T> =
|
|
76
|
+
T extends { 500: unknown } ? T[500] :
|
|
77
|
+
T extends { 501: unknown } ? T[501] :
|
|
78
|
+
T extends { 502: unknown } ? T[502] :
|
|
79
|
+
T extends { 503: unknown } ? T[503] :
|
|
80
|
+
T extends { 504: unknown } ? T[504] :
|
|
81
|
+
T extends { 505: unknown } ? T[505] :
|
|
82
|
+
T extends { 506: unknown } ? T[506] :
|
|
83
|
+
T extends { 507: unknown } ? T[507] :
|
|
84
|
+
T extends { 508: unknown } ? T[508] :
|
|
85
|
+
T extends { 510: unknown } ? T[510] :
|
|
86
|
+
T extends { 511: unknown } ? T[511] :
|
|
87
|
+
T extends { "5XX": unknown } ? T["5XX"] :
|
|
88
|
+
T extends { 400: unknown } ? T[400] :
|
|
89
|
+
T extends { 401: unknown } ? T[401] :
|
|
90
|
+
T extends { 402: unknown } ? T[402] :
|
|
91
|
+
T extends { 403: unknown } ? T[403] :
|
|
92
|
+
T extends { 404: unknown } ? T[404] :
|
|
93
|
+
T extends { 405: unknown } ? T[405] :
|
|
94
|
+
T extends { 406: unknown } ? T[406] :
|
|
95
|
+
T extends { 407: unknown } ? T[407] :
|
|
96
|
+
T extends { 408: unknown } ? T[408] :
|
|
97
|
+
T extends { 409: unknown } ? T[409] :
|
|
98
|
+
T extends { 410: unknown } ? T[410] :
|
|
99
|
+
T extends { 411: unknown } ? T[411] :
|
|
100
|
+
T extends { 412: unknown } ? T[412] :
|
|
101
|
+
T extends { 413: unknown } ? T[413] :
|
|
102
|
+
T extends { 414: unknown } ? T[414] :
|
|
103
|
+
T extends { 415: unknown } ? T[415] :
|
|
104
|
+
T extends { 416: unknown } ? T[416] :
|
|
105
|
+
T extends { 417: unknown } ? T[417] :
|
|
106
|
+
T extends { 418: unknown } ? T[418] :
|
|
107
|
+
T extends { 420: unknown } ? T[420] :
|
|
108
|
+
T extends { 421: unknown } ? T[421] :
|
|
109
|
+
T extends { 422: unknown } ? T[422] :
|
|
110
|
+
T extends { 423: unknown } ? T[423] :
|
|
111
|
+
T extends { 424: unknown } ? T[424] :
|
|
112
|
+
T extends { 425: unknown } ? T[425] :
|
|
113
|
+
T extends { 426: unknown } ? T[426] :
|
|
114
|
+
T extends { 427: unknown } ? T[427] :
|
|
115
|
+
T extends { 428: unknown } ? T[428] :
|
|
116
|
+
T extends { 429: unknown } ? T[429] :
|
|
117
|
+
T extends { 430: unknown } ? T[430] :
|
|
118
|
+
T extends { 431: unknown } ? T[431] :
|
|
119
|
+
T extends { 444: unknown } ? T[444] :
|
|
120
|
+
T extends { 450: unknown } ? T[450] :
|
|
121
|
+
T extends { 451: unknown } ? T[451] :
|
|
122
|
+
T extends { 497: unknown } ? T[497] :
|
|
123
|
+
T extends { 498: unknown } ? T[498] :
|
|
124
|
+
T extends { 499: unknown } ? T[499] :
|
|
125
|
+
T extends { "4XX": unknown } ? T["4XX"] :
|
|
126
|
+
T extends { default: unknown } ? T["default"] : never;
|
|
127
|
+
|
|
128
|
+
// OpenAPI type helpers
|
|
129
|
+
|
|
130
|
+
/** The paths that have each of the methods: upstream's `PathsWithMethod`. */
|
|
131
|
+
type PathsWithEveryMethod<Paths extends {}, PathnameMethod extends HttpMethod> = {
|
|
132
|
+
[Pathname in keyof Paths]: Paths[Pathname] extends {
|
|
133
|
+
[K in PathnameMethod]: unknown;
|
|
134
|
+
}
|
|
135
|
+
? Pathname
|
|
136
|
+
: never;
|
|
137
|
+
}[keyof Paths];
|
|
138
|
+
|
|
139
|
+
/** The paths that have at least one method. */
|
|
140
|
+
type PathsWithAnyMethod<Paths extends {}> = {
|
|
141
|
+
[Pathname in keyof Paths]: {
|
|
142
|
+
[Method in HttpMethod]: Paths[Pathname] extends { [K in Method]: unknown } ? Pathname : never;
|
|
143
|
+
}[HttpMethod];
|
|
144
|
+
}[keyof Paths];
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Given an OpenAPI **Paths Object**, find all paths that have the given method. For a union of
|
|
148
|
+
* methods, the paths that have each of them, so a call with either method finds its operation.
|
|
149
|
+
* For `HttpMethod` itself, a method not known yet, the paths that have any method too: TypeScript
|
|
150
|
+
* uses it where a hook's `Method` is not inferred, as in `Parameters<typeof $api.queryOptions>`,
|
|
151
|
+
* and the paths with every method were none (upstream #2178).
|
|
152
|
+
*
|
|
153
|
+
* A union with upstream's type, not a conditional type over the method: code that is generic in
|
|
154
|
+
* the method (`Path extends PathsWithMethod<Paths, Method>`, as swr-openapi has) must still
|
|
155
|
+
* relate one `PathsWithMethod` to another, and TypeScript cannot relate a type to a conditional
|
|
156
|
+
* type that waits for its method (TS2344). For a known method the second member is `never`.
|
|
157
|
+
*/
|
|
158
|
+
export type PathsWithMethod<Paths extends {}, PathnameMethod extends HttpMethod> =
|
|
159
|
+
| PathsWithEveryMethod<Paths, PathnameMethod>
|
|
160
|
+
| (HttpMethod extends PathnameMethod ? PathsWithAnyMethod<Paths> : never);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* DO NOT USE!
|
|
164
|
+
* Only used only for OperationObject type inference
|
|
165
|
+
*/
|
|
166
|
+
export interface OperationObject {
|
|
167
|
+
// biome-ignore lint/suspicious/noExplicitAny: upstream's type; operations are inferred against it
|
|
168
|
+
parameters: any;
|
|
169
|
+
// biome-ignore lint/suspicious/noExplicitAny: upstream's type; operations are inferred against it
|
|
170
|
+
requestBody: any; // note: "any" will get overridden in inference
|
|
171
|
+
// biome-ignore lint/suspicious/noExplicitAny: upstream's type; operations are inferred against it
|
|
172
|
+
responses: any;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Internal helper used in PathsWithMethod */
|
|
176
|
+
export type PathItemObject = {
|
|
177
|
+
[M in HttpMethod]: OperationObject;
|
|
178
|
+
} & {
|
|
179
|
+
// biome-ignore lint/suspicious/noExplicitAny: upstream's type; operations are inferred against it
|
|
180
|
+
parameters?: any;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/** Return `responses` for an Operation Object */
|
|
184
|
+
// biome-ignore lint/suspicious/noExplicitAny: the result must meet the `Record` constraints below
|
|
185
|
+
export type ResponseObjectMap<T> = T extends { responses: any } ? T["responses"] : unknown;
|
|
186
|
+
|
|
187
|
+
/** Return `content` for a Response Object */
|
|
188
|
+
// biome-ignore lint/suspicious/noExplicitAny: upstream's type; `unknown` changes generic results
|
|
189
|
+
export type ResponseContent<T> = T extends { content: any } ? T["content"] : unknown;
|
|
190
|
+
|
|
191
|
+
/** Return type of `requestBody` for an Operation Object */
|
|
192
|
+
export type OperationRequestBody<T> = "requestBody" extends keyof T ? T["requestBody"] : never;
|
|
193
|
+
|
|
194
|
+
/** Internal helper to get object type with only the `requestBody` property */
|
|
195
|
+
type PickRequestBody<T> = "requestBody" extends keyof T ? Pick<T, "requestBody"> : never;
|
|
196
|
+
|
|
197
|
+
/** Resolve to `true` if request body is optional, else `false` */
|
|
198
|
+
export type IsOperationRequestBodyOptional<T> =
|
|
199
|
+
RequiredKeysOf<PickRequestBody<T>> extends never ? true : false;
|
|
200
|
+
|
|
201
|
+
/** Internal helper used in OperationRequestBodyContent */
|
|
202
|
+
export type OperationRequestBodyMediaContent<T> =
|
|
203
|
+
IsOperationRequestBodyOptional<T> extends true
|
|
204
|
+
? ResponseContent<NonNullable<OperationRequestBody<T>>> | undefined
|
|
205
|
+
: ResponseContent<OperationRequestBody<T>>;
|
|
206
|
+
|
|
207
|
+
/** Return first `content` from a Request Object Mapping, allowing any media type */
|
|
208
|
+
export type OperationRequestBodyContent<T> =
|
|
209
|
+
FilterKeys<OperationRequestBodyMediaContent<T>, MediaType> extends never
|
|
210
|
+
? FilterKeys<NonNullable<OperationRequestBodyMediaContent<T>>, MediaType> | undefined
|
|
211
|
+
: FilterKeys<OperationRequestBodyMediaContent<T>, MediaType>;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The statuses of success responses in a Response Object Map: the 2XX statuses, or `default` when
|
|
215
|
+
* no 2XX status is declared. `default` covers every status the operation does not declare (OpenAPI
|
|
216
|
+
* Responses Object), so then it covers every success (upstream #1862). With a 2XX status declared,
|
|
217
|
+
* `default` is left to the errors, as before.
|
|
218
|
+
*/
|
|
219
|
+
type SuccessStatus<T> = [Extract<keyof T, OkStatus>] extends [never] ? "default" : OkStatus;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one. For
|
|
223
|
+
* a union of maps (a call with a union of paths), the union of each map's responses: `keyof` of a
|
|
224
|
+
* union is only the shared statuses, so one map of them lost the body of every path that did not
|
|
225
|
+
* share its statuses, and a `default`-only path typed the data `undefined` (upstream).
|
|
226
|
+
*/
|
|
227
|
+
export type SuccessResponse<
|
|
228
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
|
|
229
|
+
T extends Record<string | number, any>,
|
|
230
|
+
Media extends MediaType = MediaType,
|
|
231
|
+
> = T extends unknown ? GetResponseContent<T, Media, SuccessStatus<T>> : never;
|
|
232
|
+
|
|
233
|
+
export type GetResponseContent<
|
|
234
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
|
|
235
|
+
T extends Record<string | number, any>,
|
|
236
|
+
Media extends MediaType = MediaType,
|
|
237
|
+
ResponseCode extends keyof T = keyof T,
|
|
238
|
+
> = ResponseCode extends keyof T
|
|
239
|
+
? {
|
|
240
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects interface content types
|
|
241
|
+
[K in ResponseCode]: T[K]["content"] extends Record<string, any>
|
|
242
|
+
? FilterKeys<T[K]["content"], Media> extends never
|
|
243
|
+
? T[K]["content"]
|
|
244
|
+
: FilterKeys<T[K]["content"], Media>
|
|
245
|
+
: K extends keyof T
|
|
246
|
+
? T[K]["content"]
|
|
247
|
+
: never;
|
|
248
|
+
}[ResponseCode]
|
|
249
|
+
: never;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Return all 5XX and 4XX responses (in that order) from a Response Object Map; for a union of maps,
|
|
253
|
+
* the union of each map's responses, as `SuccessResponse`.
|
|
254
|
+
*/
|
|
255
|
+
export type ErrorResponse<
|
|
256
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
|
|
257
|
+
T extends Record<string | number, any>,
|
|
258
|
+
Media extends MediaType = MediaType,
|
|
259
|
+
> = T extends unknown ? GetResponseContent<T, Media, ErrorStatus> : never;
|
|
260
|
+
|
|
261
|
+
/** Return first JSON-like 2XX response from a path + HTTP method */
|
|
262
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
|
|
263
|
+
export type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<
|
|
264
|
+
ResponseObjectMap<PathMethod>,
|
|
265
|
+
`${string}/json`
|
|
266
|
+
>;
|
|
267
|
+
|
|
268
|
+
/** Return first JSON-like 5XX or 4XX response from a path + HTTP method */
|
|
269
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
|
|
270
|
+
export type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<
|
|
271
|
+
ResponseObjectMap<PathMethod>,
|
|
272
|
+
`${string}/json`
|
|
273
|
+
>;
|
|
274
|
+
|
|
275
|
+
/** Return JSON-like request body from a path + HTTP method */
|
|
276
|
+
export type RequestBodyJSON<PathMethod> = JSONLike<
|
|
277
|
+
FilterKeys<OperationRequestBody<PathMethod>, "content">
|
|
278
|
+
>;
|
|
279
|
+
|
|
280
|
+
// Generic TS utils
|
|
281
|
+
|
|
282
|
+
/** Find first match of multiple keys */
|
|
283
|
+
export type FilterKeys<Obj, Matchers> = Obj[keyof Obj & Matchers];
|
|
284
|
+
/**
|
|
285
|
+
* @deprecated Use `FilterKeys` instead
|
|
286
|
+
* Get the type of a value of an input object with a given key. If the key is
|
|
287
|
+
* not found, return a default type. Works with unions of objects too.
|
|
288
|
+
*/
|
|
289
|
+
export type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends unknown
|
|
290
|
+
? FilterKeys<Obj, KeyPattern> extends never
|
|
291
|
+
? Default
|
|
292
|
+
: FilterKeys<Obj, KeyPattern>
|
|
293
|
+
: never;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Return any `[string]/[string]` media type (important because openapi-typesafe-fetch allows any
|
|
297
|
+
* content response, not just JSON-like)
|
|
298
|
+
*/
|
|
299
|
+
export type MediaType = `${string}/${string}`;
|
|
300
|
+
/**
|
|
301
|
+
* Return any media type containing "json" (works for "application/json",
|
|
302
|
+
* "application/vnd.api+json", "application/vnd.oai.openapi+json")
|
|
303
|
+
*/
|
|
304
|
+
export type JSONLike<T> = FilterKeys<T, `${string}/json`>;
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Filter objects that have required keys
|
|
308
|
+
* @deprecated Use `RequiredKeysOf` instead
|
|
309
|
+
*/
|
|
310
|
+
export type FindRequiredKeys<T, K extends keyof T> = K extends unknown
|
|
311
|
+
? undefined extends T[K]
|
|
312
|
+
? never
|
|
313
|
+
: K
|
|
314
|
+
: K;
|
|
315
|
+
/**
|
|
316
|
+
* Does this object contain required keys?
|
|
317
|
+
* @deprecated Use `RequiredKeysOf` instead
|
|
318
|
+
*/
|
|
319
|
+
export type HasRequiredKeys<T> = FindRequiredKeys<T, keyof T>;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Helper to get the required keys of an object. If no keys are required, will be `undefined` with
|
|
323
|
+
* strictNullChecks enabled, else `never`
|
|
324
|
+
*/
|
|
325
|
+
type RequiredKeysOfHelper<T> = {
|
|
326
|
+
[K in keyof T]: {} extends Pick<T, K> ? never : K;
|
|
327
|
+
}[keyof T];
|
|
328
|
+
/** Get the required keys of an object, or `never` if no keys are required */
|
|
329
|
+
export type RequiredKeysOf<T> =
|
|
330
|
+
RequiredKeysOfHelper<T> extends undefined ? never : RequiredKeysOfHelper<T>;
|
|
331
|
+
|
|
332
|
+
// readOnly/writeOnly visibility markers and helpers
|
|
333
|
+
|
|
334
|
+
/** Marker type for readOnly properties (excluded from request bodies) */
|
|
335
|
+
export type $Read<T> = { readonly $read: T };
|
|
336
|
+
|
|
337
|
+
/** Marker type for writeOnly properties (excluded from response bodies) */
|
|
338
|
+
export type $Write<T> = { readonly $write: T };
|
|
339
|
+
|
|
340
|
+
/** Primitives, also branded ones (`string & { __brand: "Id" }`), which stay unmapped (#2765). */
|
|
341
|
+
type ReadWritePrimitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Types that stay unmapped. Mapping a function type drops its call signatures, so a `Blob` from
|
|
345
|
+
* `--binary-as-blob` lost its methods (`arrayBuffer()`, `text()`, ...) in response data.
|
|
346
|
+
*/
|
|
347
|
+
type ReadWriteLeaf = ReadWritePrimitive | ((...args: never) => unknown);
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Keys of `T` whose value is a `Marker`. A key whose value is only `null` or `undefined` is not
|
|
351
|
+
* marked: `NonNullable` of it is `never`, and `never` extends every marker (upstream #2717). An
|
|
352
|
+
* `any` key is not marked either (`0 extends 1 & T[K]` is true only for `any`): a check on `any`
|
|
353
|
+
* takes both branches, so `Readable` dropped the key and `Writable` typed it `undefined`. The
|
|
354
|
+
* checks are not distributive (their check types are not type parameters), so `never` is seen.
|
|
355
|
+
*/
|
|
356
|
+
type ReadWriteMarkedKeys<T, Marker> = {
|
|
357
|
+
[K in keyof T]-?: 0 extends 1 & T[K]
|
|
358
|
+
? never
|
|
359
|
+
: NonNullable<T[K]> extends never
|
|
360
|
+
? never
|
|
361
|
+
: NonNullable<T[K]> extends Marker
|
|
362
|
+
? K
|
|
363
|
+
: never;
|
|
364
|
+
}[keyof T];
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Resolve an array or a tuple element by element. An array uses `T[number]`, not `infer E`: for an
|
|
368
|
+
* intersection of arrays (`allOf`) inference keeps one member and narrowing breaks (upstream
|
|
369
|
+
* #2620). `T` is a plain array when an array of its elements is assignable to it; otherwise it is a
|
|
370
|
+
* tuple (also `[A, ...B[]]`), mapped homomorphically to stay a tuple (upstream #2632). Both keep
|
|
371
|
+
* `readonly` (upstream #2615).
|
|
372
|
+
*/
|
|
373
|
+
type ReadWriteResolveArray<
|
|
374
|
+
T extends readonly unknown[],
|
|
375
|
+
Resolved extends "read" | "write",
|
|
376
|
+
> = T[number][] extends T
|
|
377
|
+
? T extends unknown[]
|
|
378
|
+
? ReadWriteResolve<T[number], Resolved>[]
|
|
379
|
+
: readonly ReadWriteResolve<T[number], Resolved>[]
|
|
380
|
+
: { [K in keyof T]: ReadWriteResolve<T[K], Resolved> };
|
|
381
|
+
|
|
382
|
+
type ReadWriteResolve<T, Resolved extends "read" | "write"> = Resolved extends "read"
|
|
383
|
+
? Readable<T>
|
|
384
|
+
: Writable<T>;
|
|
385
|
+
|
|
386
|
+
// The markers are matched with `any`, as in the helper types that the generator writes.
|
|
387
|
+
// biome-ignore-start lint/suspicious/noExplicitAny: the same text as the generated helper types
|
|
388
|
+
/**
|
|
389
|
+
* Resolve type for reading (responses): strips $Write properties, unwraps $Read
|
|
390
|
+
* - $Read<T> → T (readable), continues recursion
|
|
391
|
+
* - $Write<T> → never (excluded from response)
|
|
392
|
+
* - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
|
|
393
|
+
* recursively
|
|
394
|
+
*/
|
|
395
|
+
export type Readable<T> =
|
|
396
|
+
T extends $Write<any>
|
|
397
|
+
? never
|
|
398
|
+
: T extends $Read<infer U>
|
|
399
|
+
? Readable<U>
|
|
400
|
+
: T extends ReadWriteLeaf
|
|
401
|
+
? T
|
|
402
|
+
: T extends readonly unknown[]
|
|
403
|
+
? ReadWriteResolveArray<T, "read">
|
|
404
|
+
: T extends object
|
|
405
|
+
? {
|
|
406
|
+
[K in keyof T as K extends ReadWriteMarkedKeys<T, $Write<any>>
|
|
407
|
+
? never
|
|
408
|
+
: K]: Readable<T[K]>;
|
|
409
|
+
}
|
|
410
|
+
: T;
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Resolve type for writing (requests): strips $Read properties, unwraps $Write
|
|
414
|
+
* - $Write<T> → T (writable), continues recursion
|
|
415
|
+
* - $Read<T> → never (excluded from request)
|
|
416
|
+
* - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
|
|
417
|
+
* recursively
|
|
418
|
+
*/
|
|
419
|
+
export type Writable<T> =
|
|
420
|
+
T extends $Read<any>
|
|
421
|
+
? never
|
|
422
|
+
: T extends $Write<infer U>
|
|
423
|
+
? Writable<U>
|
|
424
|
+
: T extends ReadWriteLeaf
|
|
425
|
+
? T
|
|
426
|
+
: T extends readonly unknown[]
|
|
427
|
+
? ReadWriteResolveArray<T, "write">
|
|
428
|
+
: T extends object
|
|
429
|
+
? {
|
|
430
|
+
[K in keyof T as K extends ReadWriteMarkedKeys<T, $Read<any>>
|
|
431
|
+
? never
|
|
432
|
+
: K]: Writable<T[K]>;
|
|
433
|
+
} & {
|
|
434
|
+
[K in ReadWriteMarkedKeys<T, $Read<any>>]?: never;
|
|
435
|
+
}
|
|
436
|
+
: T;
|
|
437
|
+
// biome-ignore-end lint/suspicious/noExplicitAny: the same text as the generated helper types
|
package/src/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// A stream body may be an async iterable (`RawRequestBody`). The reference brings that type into a
|
|
2
|
+
// project whose `lib` is older than ES2018, which fails with TS2583 otherwise. The declaration
|
|
3
|
+
// build copies it into the published declarations (build.config.ts).
|
|
4
|
+
/// <reference lib="es2018.asynciterable" />
|
|
1
5
|
import type {
|
|
2
6
|
ErrorResponse,
|
|
3
7
|
FilterKeys,
|
|
@@ -11,7 +15,16 @@ import type {
|
|
|
11
15
|
ResponseObjectMap,
|
|
12
16
|
SuccessResponse,
|
|
13
17
|
Writable,
|
|
14
|
-
} from "
|
|
18
|
+
} from "./helpers.js";
|
|
19
|
+
|
|
20
|
+
// The client declares the helper types in `./helpers.js`, published as
|
|
21
|
+
// `openapi-typesafe-fetch/helpers`, and `openapi-typesafe-helpers` re-exports them: one
|
|
22
|
+
// declaration, so generic code may mix the names of both packages. Types inferred from the
|
|
23
|
+
// client print them by that path, which a library that uses the client can import, also under
|
|
24
|
+
// pnpm and Yarn PnP (ADR-034). They are not exported here, as in openapi-fetch: a project that
|
|
25
|
+
// writes `export type * from` the client next to its own `ErrorResponse` fails with TS2308. This
|
|
26
|
+
// file has no `export { … }` either, so its other top-level types stay importable from
|
|
27
|
+
// `openapi-typesafe-fetch/src/index.js`, as openapi-fetch's are (`InitParam`).
|
|
15
28
|
|
|
16
29
|
/** Options for each client instance */
|
|
17
30
|
export interface ClientOptions extends Omit<RequestInit, "headers"> {
|
|
@@ -147,7 +160,17 @@ export type RequestBodyOption<T> =
|
|
|
147
160
|
? { body?: Writable<OperationRequestBodyContent<T>> }
|
|
148
161
|
: { body: Writable<OperationRequestBodyContent<T>> };
|
|
149
162
|
|
|
150
|
-
|
|
163
|
+
/**
|
|
164
|
+
* The init of a call to the operation `T`; for a union of operations, the union of their inits. It
|
|
165
|
+
* distributes so that the client's init type and a wrapper's `FetchOptions` of a generic path are
|
|
166
|
+
* the same type, which TypeScript can relate: an alias that distributed only inside the client
|
|
167
|
+
* gave a conditional type no `FetchOptions` argument was assignable to (TS2345). The operations
|
|
168
|
+
* of a union are not merged into one deep init type, which gave TS2589 on hundreds of paths
|
|
169
|
+
* (upstream #1882).
|
|
170
|
+
*/
|
|
171
|
+
export type FetchOptions<T> = T extends unknown
|
|
172
|
+
? RequestOptions<T> & Omit<RequestInit, "body" | "headers">
|
|
173
|
+
: never;
|
|
151
174
|
|
|
152
175
|
// Readable<T> strips $Write markers (writeOnly properties excluded from response)
|
|
153
176
|
export type FetchResponse<
|
|
@@ -254,19 +277,16 @@ export type MaybeOptionalInit<Params, Location extends PropertyKey> = OperationI
|
|
|
254
277
|
* One conditional type, as openapi-fetch's, not a union with a conditional `undefined`: while the
|
|
255
278
|
* operation is generic, a union made `FetchResponse` distribute over its members, so the `data` of
|
|
256
279
|
* a generic wrapper's call no longer related to `FetchResponse<Paths[Path][Method], Init, Media>`.
|
|
280
|
+
* Its check type is not a type parameter, so TypeScript takes a `FetchOptions` of a generic path
|
|
281
|
+
* for it when that type fits both branches. `FetchOptions` itself makes one init per operation:
|
|
282
|
+
* while the path is generic it stays an alias, not an `infer` type, which TypeScript reads as
|
|
283
|
+
* `unknown`, so the init stays the contextual type of an argument and a literal such as
|
|
284
|
+
* `"available"` keeps its type (swr-openapi's `Exact<Init>` checks).
|
|
257
285
|
*/
|
|
258
286
|
type OperationInits<Operation> =
|
|
259
|
-
RequiredKeysOf<
|
|
260
|
-
?
|
|
261
|
-
:
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Each operation's init. A distributive alias, not `extends infer Operation`: while the path is
|
|
265
|
-
* not known, TypeScript reads an `infer` type as `unknown`, so the init lost its type as the
|
|
266
|
-
* contextual type of an argument and a literal such as `"available"` widened to `string`
|
|
267
|
-
* (swr-openapi's `Exact<Init>` checks).
|
|
268
|
-
*/
|
|
269
|
-
type OperationFetchOptions<Operation> = Operation extends unknown ? FetchOptions<Operation> : never;
|
|
287
|
+
RequiredKeysOf<FetchOptions<Operation>> extends never
|
|
288
|
+
? FetchOptions<Operation> | undefined
|
|
289
|
+
: FetchOptions<Operation>;
|
|
270
290
|
|
|
271
291
|
// The final init param to accept.
|
|
272
292
|
// - Determines if the param is optional or not.
|
|
@@ -294,11 +314,19 @@ type WithAnyKeys<Init> = Init & { [key: string]: unknown };
|
|
|
294
314
|
* adds none.
|
|
295
315
|
*/
|
|
296
316
|
export type PathOperation<Paths, Path, Method> = Extract<
|
|
297
|
-
FilterKeys<Paths
|
|
317
|
+
FilterKeys<PathItem<Paths, Path>, Method>,
|
|
298
318
|
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
|
|
299
319
|
Record<string | number, any>
|
|
300
320
|
>;
|
|
301
321
|
|
|
322
|
+
/**
|
|
323
|
+
* The path item of each path of `Path`. Not `Paths[Path & keyof Paths]`: with template keys
|
|
324
|
+
* (`--path-params-as-types`), `/apps/${string}` intersected with the keys is every key it
|
|
325
|
+
* overlaps, such as `/apps/${string}/logs/${number}`, so a call to one path got the responses of
|
|
326
|
+
* the others too once the responses distribute over the operations.
|
|
327
|
+
*/
|
|
328
|
+
type PathItem<Paths, Path> = Path extends keyof Paths ? Paths[Path] : never;
|
|
329
|
+
|
|
302
330
|
export type ClientMethod<Paths extends {}, Method extends HttpMethod, Media extends MediaType> = <
|
|
303
331
|
Path extends PathsWithMethod<Paths, Method>,
|
|
304
332
|
Init extends MaybeOptionalInit<Paths[Path], Method>,
|
|
@@ -457,8 +485,18 @@ export declare function defaultPathSerializer(
|
|
|
457
485
|
pathParams: Record<string, unknown>,
|
|
458
486
|
): string;
|
|
459
487
|
|
|
460
|
-
/**
|
|
461
|
-
|
|
488
|
+
/**
|
|
489
|
+
* Bodies that `fetch` sends as they are, so `defaultBodySerializer` returns them unchanged. A
|
|
490
|
+
* stream is a `ReadableStream` or an async iterable, such as a Node.js `Readable`.
|
|
491
|
+
*/
|
|
492
|
+
type RawRequestBody =
|
|
493
|
+
| FormData
|
|
494
|
+
| Blob
|
|
495
|
+
| URLSearchParams
|
|
496
|
+
| ArrayBuffer
|
|
497
|
+
| ArrayBufferView
|
|
498
|
+
| ReadableStream
|
|
499
|
+
| AsyncIterable<unknown>;
|
|
462
500
|
|
|
463
501
|
/**
|
|
464
502
|
* The result of `defaultBodySerializer` for a body of type `Body`: the body itself when it is raw,
|
|
@@ -551,3 +589,9 @@ export declare function mergeHeaders(...allHeaders: (HeadersOptions | undefined)
|
|
|
551
589
|
|
|
552
590
|
/** Remove trailing slash from url */
|
|
553
591
|
export declare function removeTrailingSlash(url: string): string;
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Return a new ID, unique within the process: the request `id` that middleware callbacks get.
|
|
595
|
+
* It comes from a counter shared by all clients, not from `Math.random()`; the name is upstream's.
|
|
596
|
+
*/
|
|
597
|
+
export declare function randomID(): string;
|