openapi-typesafe-fetch 1.0.0-rc.0 → 1.0.0-rc.2

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/dist/index.d.cts CHANGED
@@ -15,28 +15,59 @@ interface ClientOptions extends Omit<RequestInit, "headers"> {
15
15
  /** global pathSerializer */
16
16
  pathSerializer?: PathSerializer;
17
17
  headers?: HeadersOptions;
18
- /** RequestInit extension object to pass as 2nd argument to fetch when supported (defaults to undefined) */
18
+ /**
19
+ * RequestInit extension object to pass as 2nd argument to fetch when supported (defaults to
20
+ * undefined)
21
+ */
19
22
  requestInitExt?: Record<string, unknown>;
20
23
  }
21
24
 
25
+ /**
26
+ * `ClientOptions` for a Fetch API implementation with its own types, such as the `undici` package
27
+ * (upstream #2562). TypeScript cannot relate its `Request` and `Response` to the DOM lib's
28
+ * (`duplex`, `RequestDestination`, `BodyInit` differ), so only what the client needs is checked: a
29
+ * `fetch` that returns a promise and a `Request` class that takes a URL. Pass the `fetch` and the
30
+ * `Request` of the same implementation; middleware and results still use the DOM types. The
31
+ * `createClient` overload with `ClientOptions` comes first, so an inline `fetch` gets a typed
32
+ * `Request` input.
33
+ */
34
+ interface CustomFetchClientOptions extends Omit<ClientOptions, "fetch" | "Request"> {
35
+ /** `fetch` of another implementation (for example `fetch` from `undici`) */
36
+ fetch?: (input: never, init?: never) => PromiseLike<object>;
37
+ /** `Request` of the same implementation (for example `Request` from `undici`) */
38
+ Request?: new (
39
+ input: string,
40
+ init: never,
41
+ ) => object;
42
+ }
43
+
22
44
  type HeadersOptions =
23
45
  | Required<RequestInit>["headers"]
24
46
  | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined>;
25
47
 
26
48
  type QuerySerializer<T> = (
27
- query: T extends { parameters: any } ? NonNullable<T["parameters"]["query"]> : Record<string, unknown>,
49
+ // biome-ignore lint/suspicious/noExplicitAny: T["parameters"]["query"] needs an any constraint
50
+ query: T extends { parameters: any }
51
+ ? NonNullable<T["parameters"]["query"]>
52
+ : Record<string, unknown>,
28
53
  ) => string;
29
54
 
30
55
  /** @see https://swagger.io/docs/specification/serialization/#query */
31
56
  type QuerySerializerOptions = {
32
- /** Set serialization for arrays. @see https://swagger.io/docs/specification/serialization/#query */
57
+ /**
58
+ * Set serialization for arrays.
59
+ * @see https://swagger.io/docs/specification/serialization/#query
60
+ */
33
61
  array?: {
34
62
  /** default: "form" */
35
63
  style: "form" | "spaceDelimited" | "pipeDelimited";
36
64
  /** default: true */
37
65
  explode: boolean;
38
66
  };
39
- /** Set serialization for objects. @see https://swagger.io/docs/specification/serialization/#query */
67
+ /**
68
+ * Set serialization for objects.
69
+ * @see https://swagger.io/docs/specification/serialization/#query
70
+ */
40
71
  object?: {
41
72
  /** default: "deepObject" */
42
73
  style: "form" | "deepObject";
@@ -53,6 +84,8 @@ type QuerySerializerOptions = {
53
84
  allowReserved?: boolean;
54
85
  };
55
86
 
87
+ // The result is the fetch body; openapi-fetch types it as `any` (ADR-030).
88
+ // biome-ignore lint/suspicious/noExplicitAny: openapi-fetch's type, kept for parity
56
89
  type BodySerializer<T> = (body: OperationRequestBodyContent<T>) => any;
57
90
 
58
91
  type PathSerializer = (pathname: string, pathParams: Record<string, unknown>) => string;
@@ -78,25 +111,38 @@ interface DefaultParamsOption {
78
111
  }
79
112
 
80
113
  type ParamsOption<T> = T extends {
81
- parameters: any;
114
+ parameters: unknown;
82
115
  }
83
116
  ? RequiredKeysOf<T["parameters"]> extends never
84
117
  ? { params?: T["parameters"] }
85
118
  : { params: T["parameters"] }
86
119
  : DefaultParamsOption;
87
120
 
88
- // Writable<T> strips $Read markers (readOnly properties excluded from request body)
121
+ /** `true` for `any`: the operation of `createClient<any>()`, which says nothing about its body. */
122
+ type IsAny<T> = 0 extends 1 & T ? true : false;
123
+
124
+ // Writable<T> strips $Read markers (readOnly properties excluded from request body).
125
+ // An `any` operation is checked first: a conditional type on `any` takes both branches, which
126
+ // gave `{ body?: never } | { body: any }` and so a required init (upstream #2526).
89
127
  type RequestBodyOption<T> =
90
- Writable<OperationRequestBodyContent<T>> extends never
91
- ? { body?: never }
92
- : IsOperationRequestBodyOptional<T> extends true
93
- ? { body?: Writable<OperationRequestBodyContent<T>> }
94
- : { body: Writable<OperationRequestBodyContent<T>> };
128
+ IsAny<T> extends true
129
+ ? // biome-ignore lint/suspicious/noExplicitAny: an any schema takes any body (upstream #2526)
130
+ { body?: any }
131
+ : Writable<OperationRequestBodyContent<T>> extends never
132
+ ? { body?: never }
133
+ : IsOperationRequestBodyOptional<T> extends true
134
+ ? { body?: Writable<OperationRequestBodyContent<T>> }
135
+ : { body: Writable<OperationRequestBodyContent<T>> };
95
136
 
96
137
  type FetchOptions<T> = RequestOptions<T> & Omit<RequestInit, "body" | "headers">;
97
138
 
98
139
  // Readable<T> strips $Write markers (writeOnly properties excluded from response)
99
- type FetchResponse<T extends Record<string | number, any>, Options, Media extends MediaType> =
140
+ type FetchResponse<
141
+ // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
142
+ T extends Record<string | number, any>,
143
+ Options,
144
+ Media extends MediaType,
145
+ > =
100
146
  | {
101
147
  data: ParseAsResponse<Readable<SuccessResponse<ResponseObjectMap<T>, Media>>, Options>;
102
148
  error?: never;
@@ -145,7 +191,7 @@ interface MiddlewareCallbackParams {
145
191
  readonly params: MiddlewareRequestParams;
146
192
  /** Unique ID for this request */
147
193
  readonly id: string;
148
- /** createClient options (read-only) */
194
+ /** The options of this request after merging the client and request options (read-only) */
149
195
  readonly options: MergedOptions;
150
196
  }
151
197
 
@@ -176,29 +222,79 @@ type Middleware =
176
222
  onError: MiddlewareOnError;
177
223
  };
178
224
 
179
- /** This type helper makes the 2nd function param required if params/requestBody are required; otherwise, optional */
180
- type MaybeOptionalInit<Params, Location extends keyof Params> =
181
- RequiredKeysOf<FetchOptions<FilterKeys<Params, Location>>> extends never
182
- ? FetchOptions<FilterKeys<Params, Location>> | undefined
183
- : FetchOptions<FilterKeys<Params, Location>>;
225
+ /**
226
+ * This type helper makes the 2nd function param required if params/requestBody are required;
227
+ * otherwise, optional. `Location` is any key: with `Paths extends {}` a method is not known to be a
228
+ * key of `Paths[Path]` (upstream #2267). For a union of operations, the union of their inits: one
229
+ * deep type of every init gave TS2589 on hundreds of paths (upstream #1882).
230
+ */
231
+ type MaybeOptionalInit<Params, Location extends PropertyKey> = OperationInits<
232
+ FilterKeys<Params, Location>
233
+ >;
234
+
235
+ /**
236
+ * The inits of the operations, and `undefined` only when none of them requires anything. One
237
+ * operation's init is `undefined` too when nothing in it is required, as in openapi-fetch. For a
238
+ * union of operations (a union of paths, or `Parameters<typeof client.request>`), `undefined`
239
+ * would make the whole init optional, although a call to one of the paths sends the init it gets,
240
+ * and a path parameter left out stays `{id}` in the URL.
241
+ * One conditional type, as openapi-fetch's, not a union with a conditional `undefined`: while the
242
+ * operation is generic, a union made `FetchResponse` distribute over its members, so the `data` of
243
+ * a generic wrapper's call no longer related to `FetchResponse<Paths[Path][Method], Init, Media>`.
244
+ */
245
+ type OperationInits<Operation> =
246
+ RequiredKeysOf<OperationFetchOptions<Operation>> extends never
247
+ ? OperationFetchOptions<Operation> | undefined
248
+ : OperationFetchOptions<Operation>;
249
+
250
+ /**
251
+ * Each operation's init. A distributive alias, not `extends infer Operation`: while the path is
252
+ * not known, TypeScript reads an `infer` type as `unknown`, so the init lost its type as the
253
+ * contextual type of an argument and a literal such as `"available"` widened to `string`
254
+ * (swr-openapi's `Exact<Init>` checks).
255
+ */
256
+ type OperationFetchOptions<Operation> = Operation extends unknown ? FetchOptions<Operation> : never;
184
257
 
185
258
  // The final init param to accept.
186
259
  // - Determines if the param is optional or not.
187
260
  // - Performs arbitrary [key: string] addition.
188
- // Note: the addition MUST happen after all the inference happens (otherwise TS can’t infer if init is required or not).
261
+ // Note: the addition MUST happen after all the inference happens (otherwise TS can’t infer if
262
+ // init is required or not).
263
+ // - The condition is openapi-fetch's. TypeScript relates two conditional types only when their
264
+ // `extends` types are the same and their check types relate, so a client type written against
265
+ // openapi-fetch's `InitParam` (a wrapper, a cast) accepted this client only with this condition.
266
+ // A union of inits is required through `MaybeOptionalInit`, which then leaves out `undefined`.
267
+ // - The addition is a named alias: written out in both branches, TypeScript gave `never` for
268
+ // `Parameters` of the methods.
189
269
  type InitParam<Init> =
190
- RequiredKeysOf<Init> extends never ? [(Init & { [key: string]: unknown })?] : [Init & { [key: string]: unknown }];
270
+ RequiredKeysOf<Init> extends never ? [WithAnyKeys<Init>?] : [WithAnyKeys<Init>];
191
271
 
192
- type ClientMethod<
193
- Paths extends Record<string, Record<HttpMethod, {}>>,
194
- Method extends HttpMethod,
195
- Media extends MediaType,
196
- > = <Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>>(
272
+ /** The init with any other key (`[key: string]: unknown`), added after inference. */
273
+ type WithAnyKeys<Init> = Init & { [key: string]: unknown };
274
+
275
+ /**
276
+ * @internal Exported for openapi-typesafe-react-query only; not part of the API (ADR-034).
277
+ * The operation of `Method` on `Path`. Not `Paths[Path][Method]`: `Paths` is only `{}`, because
278
+ * the generated `interface paths` has no index signature and failed a `Record<string, …>`
279
+ * constraint with TS2344 (upstream #2267), so TypeScript does not know `Method` is a key. For a
280
+ * union of paths or methods, the union of their operations; a method a path lacks (`put?: never`)
281
+ * adds none.
282
+ */
283
+ type PathOperation<Paths, Path, Method> = Extract<
284
+ FilterKeys<Paths[Path & keyof Paths], Method>,
285
+ // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
286
+ Record<string | number, any>
287
+ >;
288
+
289
+ type ClientMethod<Paths extends {}, Method extends HttpMethod, Media extends MediaType> = <
290
+ Path extends PathsWithMethod<Paths, Method>,
291
+ Init extends MaybeOptionalInit<Paths[Path], Method>,
292
+ >(
197
293
  url: Path,
198
294
  ...init: InitParam<Init>
199
- ) => Promise<FetchResponse<Paths[Path][Method], Init, Media>>;
295
+ ) => Promise<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>;
200
296
 
201
- type ClientRequestMethod<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
297
+ type ClientRequestMethod<Paths extends {}, Media extends MediaType> = <
202
298
  Method extends HttpMethod,
203
299
  Path extends PathsWithMethod<Paths, Method>,
204
300
  Init extends MaybeOptionalInit<Paths[Path], Method>,
@@ -206,11 +302,21 @@ type ClientRequestMethod<Paths extends Record<string, Record<HttpMethod, {}>>, M
206
302
  method: Method,
207
303
  url: Path,
208
304
  ...init: InitParam<Init>
209
- ) => Promise<FetchResponse<Paths[Path][Method], Init, Media>>;
305
+ ) => Promise<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>;
210
306
 
211
307
  // Only HTTP method keys become calls: `parameters` and `additionalOperations` are not methods.
212
- type ClientForPath<PathInfo extends Record<string | number, any>, Media extends MediaType> = {
213
- [Method in keyof PathInfo as Method extends HttpMethod ? Uppercase<Method> : never]: <
308
+ // They are left out by `Pick`, which keeps the `?` of a method, not by the key remapping: that is
309
+ // openapi-fetch's, and TypeScript relates two generic mapped types only when their key remappings
310
+ // are the same, so a path-based client type written like openapi-fetch's failed a cast (TS2352).
311
+ type ClientForPath<
312
+ // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
313
+ PathInfo extends Record<string | number, any>,
314
+ Media extends MediaType,
315
+ > = PathMethodCalls<Pick<PathInfo, HttpMethod & keyof PathInfo>, Media>;
316
+
317
+ // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
318
+ type PathMethodCalls<PathInfo extends Record<string | number, any>, Media extends MediaType> = {
319
+ [Method in keyof PathInfo as Uppercase<string & Method>]: <
214
320
  Init extends MaybeOptionalInit<PathInfo, Method>,
215
321
  >(
216
322
  ...init: InitParam<Init>
@@ -243,34 +349,56 @@ interface Client<Paths extends {}, Media extends MediaType = MediaType> {
243
349
  eject(...middleware: Middleware[]): void;
244
350
  }
245
351
 
246
- type ClientPathsWithMethod<CreatedClient extends Client<any, any>, Method extends HttpMethod> =
352
+ type ClientPathsWithMethod<
353
+ // biome-ignore lint/suspicious/noExplicitAny: matches a client of any paths, as in openapi-fetch
354
+ CreatedClient extends Client<any, any>,
355
+ Method extends HttpMethod,
356
+ > =
247
357
  CreatedClient extends Client<infer Paths, infer _Media> ? PathsWithMethod<Paths, Method> : never;
248
358
 
249
359
  type MethodResponse<
360
+ // biome-ignore lint/suspicious/noExplicitAny: matches a client of any paths, as in openapi-fetch
250
361
  CreatedClient extends Client<any, any>,
251
362
  Method extends HttpMethod,
252
363
  Path extends ClientPathsWithMethod<CreatedClient, Method>,
253
364
  Options = {},
254
365
  > =
255
- CreatedClient extends Client<infer Paths extends { [key: string]: any }, infer Media extends MediaType>
366
+ CreatedClient extends Client<
367
+ // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
368
+ infer Paths extends { [key: string]: any },
369
+ infer Media extends MediaType
370
+ >
256
371
  ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>["data"]>
257
372
  : never;
258
373
 
259
374
  declare function createClient<Paths extends {}, Media extends MediaType = MediaType>(
260
375
  clientOptions?: ClientOptions,
261
376
  ): Client<Paths, Media>;
377
+ declare function createClient<Paths extends {}, Media extends MediaType = MediaType>(
378
+ clientOptions?: CustomFetchClientOptions,
379
+ ): Client<Paths, Media>;
262
380
 
263
- type PathBasedClient<Paths extends Record<string | number, any>, Media extends MediaType = MediaType> = {
381
+ type PathBasedClient<
382
+ // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
383
+ Paths extends Record<string | number, any>,
384
+ Media extends MediaType = MediaType,
385
+ > = {
264
386
  [Path in keyof Paths]: ClientForPath<Paths[Path], Media>;
265
387
  };
266
388
 
267
- declare function wrapAsPathBasedClient<Paths extends {}, Media extends MediaType = MediaType>(
268
- client: Client<Paths, Media>,
269
- ): PathBasedClient<Paths, Media>;
389
+ declare function wrapAsPathBasedClient<
390
+ Paths extends {},
391
+ Media extends MediaType = MediaType,
392
+ >(client: Client<Paths, Media>): PathBasedClient<Paths, Media>;
270
393
 
271
- declare function createPathBasedClient<Paths extends {}, Media extends MediaType = MediaType>(
272
- clientOptions?: ClientOptions,
273
- ): PathBasedClient<Paths, Media>;
394
+ declare function createPathBasedClient<
395
+ Paths extends {},
396
+ Media extends MediaType = MediaType,
397
+ >(clientOptions?: ClientOptions): PathBasedClient<Paths, Media>;
398
+ declare function createPathBasedClient<
399
+ Paths extends {},
400
+ Media extends MediaType = MediaType,
401
+ >(clientOptions?: CustomFetchClientOptions): PathBasedClient<Paths, Media>;
274
402
 
275
403
  /** Serialize primitive params to string */
276
404
  declare function serializePrimitiveParam(
@@ -311,12 +439,85 @@ declare function createQuerySerializer<T = unknown>(
311
439
  * @type {import("./index.js").defaultPathSerializer}
312
440
  * @see https://swagger.io/docs/specification/serialization/#path
313
441
  */
314
- declare function defaultPathSerializer(pathname: string, pathParams: Record<string, unknown>): string;
442
+ declare function defaultPathSerializer(
443
+ pathname: string,
444
+ pathParams: Record<string, unknown>,
445
+ ): string;
446
+
447
+ /** Bodies that `fetch` sends as they are, so `defaultBodySerializer` returns them unchanged. */
448
+ type RawRequestBody = FormData | Blob | URLSearchParams | ArrayBuffer | ArrayBufferView;
449
+
450
+ /**
451
+ * The result of `defaultBodySerializer` for a body of type `Body`: the body itself when it is raw,
452
+ * `undefined` for `undefined` (`JSON.stringify` gives it back), `ObjectResult` for an object, and a
453
+ * string otherwise. `unknown` (and `any`) may be any of these.
454
+ */
455
+ type SerializedBody<Body, ObjectResult> = unknown extends Body
456
+ ? RawRequestBody | string | FormData | undefined
457
+ : Body extends RawRequestBody
458
+ ? Body
459
+ : Body extends undefined
460
+ ? undefined
461
+ : Body extends object
462
+ ? ObjectResult
463
+ : string;
464
+
465
+ /** The `Content-Type` values that make `defaultBodySerializer` turn an object into `FormData`. */
466
+ type MultipartMediaType = "multipart/form-data" | `multipart/form-data;${string}`;
467
+
468
+ /** `Headers` drops the spaces and tabs in front of a value before the media type is read. */
469
+ type TrimmedStart<Text extends string> = Text extends ` ${infer Rest}` | `\t${infer Rest}`
470
+ ? TrimmedStart<Rest>
471
+ : Text;
472
+
473
+ /**
474
+ * The `Content-Type` of headers written as a record, in any letter case of the name: `never` when
475
+ * the record has none, `unknown` when the type does not tell (`Headers`, pairs, a string record).
476
+ */
477
+ type ContentTypeOf<GivenHeaders> = GivenHeaders extends undefined
478
+ ? never
479
+ : GivenHeaders extends readonly unknown[] | { get(name: string): unknown }
480
+ ? unknown
481
+ : string extends keyof GivenHeaders
482
+ ? unknown
483
+ : ContentTypeEntries<GivenHeaders>[keyof GivenHeaders];
484
+
485
+ /** For each entry of a header record: its value when it is the `Content-Type`, else `never`. */
486
+ type ContentTypeEntries<Record> = { [Name in keyof Record]: ContentTypeEntry<Name, Record[Name]> };
487
+
488
+ /** The value of a header record entry when its name is `Content-Type` in any letter case. */
489
+ type ContentTypeEntry<Name, Value> =
490
+ Lowercase<Name & string> extends "content-type" ? Value : never;
491
+
492
+ /**
493
+ * What an object becomes with a `Content-Type` of type `ContentType`: `FormData` for a multipart
494
+ * one, a string for any other one or for none, and either when the type is not a literal.
495
+ */
496
+ type ObjectBodyResult<ContentType> = [ContentType] extends [never]
497
+ ? string
498
+ : ContentType extends null | undefined
499
+ ? string
500
+ : ContentType extends string
501
+ ? string extends ContentType
502
+ ? string | FormData
503
+ : Lowercase<TrimmedStart<ContentType>> extends MultipartMediaType
504
+ ? FormData
505
+ : string
506
+ : string | FormData;
315
507
 
316
508
  /**
317
- * Serialize body object to string; with a form content type in `headers`, as URL-encoded form data.
509
+ * Serialize a request body. A raw body (`FormData`, `Blob`, `URLSearchParams`, `ArrayBuffer`, a
510
+ * typed array) is returned as it is. With `headers`: a form `Content-Type` gives URL-encoded form
511
+ * data, a `multipart/form-data` one turns an object into `FormData`, and another non-JSON one keeps
512
+ * a string as it is. Anything else is JSON. Without `headers` an object can only become JSON.
513
+ * Headers written as a record literal tell the result type of an object body: `FormData` for a
514
+ * multipart `Content-Type`, `string` for another one or none. Other headers: `string | FormData`.
318
515
  */
319
- declare function defaultBodySerializer<T>(body: T, headers?: HeadersOptions): string;
516
+ declare function defaultBodySerializer<T>(body: T): SerializedBody<T, string>;
517
+ declare function defaultBodySerializer<
518
+ T,
519
+ const GivenHeaders extends HeadersOptions | undefined,
520
+ >(body: T, headers: GivenHeaders): SerializedBody<T, ObjectBodyResult<ContentTypeOf<GivenHeaders>>>;
320
521
 
321
522
  /** Construct URL string from baseUrl and handle path and query params */
322
523
  declare function createFinalURL<O>(
@@ -338,7 +539,5 @@ declare function mergeHeaders(...allHeaders: (HeadersOptions | undefined)[]): He
338
539
  /** Remove trailing slash from url */
339
540
  declare function removeTrailingSlash(url: string): string;
340
541
 
341
- // @ts-ignore
342
- export = createClient;
343
- export { createFinalURL, createPathBasedClient, createQuerySerializer, defaultBodySerializer, defaultPathSerializer, mergeHeaders, removeTrailingSlash, serializeArrayParam, serializeObjectParam, serializePrimitiveParam, wrapAsPathBasedClient };
344
- export type { BodySerializer, Client, ClientForPath, ClientMethod, ClientOptions, ClientPathsWithMethod, ClientRequestMethod, DefaultParamsOption, FetchOptions, FetchResponse, HeadersOptions, MaybeOptionalInit, MergedOptions, MethodResponse, Middleware, MiddlewareCallbackParams, MiddlewareRequestParams, ParamsOption, ParseAs, ParseAsResponse, PathBasedClient, PathSerializer, QuerySerializer, QuerySerializerOptions, RequestBodyOption, RequestOptions };
542
+ export { createFinalURL, createPathBasedClient, createQuerySerializer, createClient as default, defaultBodySerializer, defaultPathSerializer, mergeHeaders, removeTrailingSlash, serializeArrayParam, serializeObjectParam, serializePrimitiveParam, wrapAsPathBasedClient };
543
+ export type { BodySerializer, Client, ClientForPath, ClientMethod, ClientOptions, ClientPathsWithMethod, ClientRequestMethod, CustomFetchClientOptions, DefaultParamsOption, FetchOptions, FetchResponse, HeadersOptions, MaybeOptionalInit, MergedOptions, MethodResponse, Middleware, MiddlewareCallbackParams, MiddlewareRequestParams, ParamsOption, ParseAs, ParseAsResponse, PathBasedClient, PathOperation, PathSerializer, QuerySerializer, QuerySerializerOptions, RequestBodyOption, RequestOptions };