openapi-typesafe-fetch 1.0.0-rc.7 → 1.0.0-rc.8

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 CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  This package is a fork of `openapi-fetch`. The entries above `0.17.0` are releases of openapi-typesafe-fetch; `0.17.0` and the entries below it are the upstream history up to the fork point (upstream commit `d46a319a`), with `openapi-fetch` versions.
4
4
 
5
+ ## 1.0.0-rc.8
6
+
7
+ ### Patch Changes
8
+
9
+ - [#103](https://github.com/openapi-typesafe/openapi-typesafe/pull/103) [`091b864`](https://github.com/openapi-typesafe/openapi-typesafe/commit/091b864cdb6dd9e1ff5810d4d44cebee8a01a721) Thanks [@fedorello](https://github.com/fedorello)! - `createClient<any>()` types `data` and `error` as `any` again; they were `never`, so
10
+ `if (error)` made the whole result `never`. `SuccessResponse<any>` and `ErrorResponse<any>` are
11
+ `any` too.
12
+
13
+ - [#103](https://github.com/openapi-typesafe/openapi-typesafe/pull/103) [`091b864`](https://github.com/openapi-typesafe/openapi-typesafe/commit/091b864cdb6dd9e1ff5810d4d44cebee8a01a721) Thanks [@fedorello](https://github.com/fedorello)! - Response types follow what the client returns. `ErrorResponseJSON`, `SuccessResponseJSON` and the
14
+ `data` and `error` of a client made with ``createClient<paths, `${string}/json`>()`` keep a
15
+ JSON body of any JSON media type: an `application/problem+json` error, an
16
+ `application/vnd.api+json` success and `application/json; charset=utf-8` bodies were left out,
17
+ so the error could be `never` or miss a status. An error that is not JSON, such as the HTML page
18
+ of a proxy, is typed `string`, the text the client returns, instead of being left out.
19
+
20
+ - [#103](https://github.com/openapi-typesafe/openapi-typesafe/pull/103) [`091b864`](https://github.com/openapi-typesafe/openapi-typesafe/commit/091b864cdb6dd9e1ff5810d4d44cebee8a01a721) Thanks [@fedorello](https://github.com/fedorello)! - Every `querySerializer` option accepts `undefined` in its type, so a wrapper that passes its own
21
+ optional option on (`{ array: { explode: options.explode } }`) compiles with
22
+ `exactOptionalPropertyTypes`.
23
+
24
+ - [#103](https://github.com/openapi-typesafe/openapi-typesafe/pull/103) [`091b864`](https://github.com/openapi-typesafe/openapi-typesafe/commit/091b864cdb6dd9e1ff5810d4d44cebee8a01a721) Thanks [@fedorello](https://github.com/fedorello)! - `serializeArrayParam` and `serializeObjectParam` with a nested value in a path style (`simple`,
25
+ `label`, `matrix`) name `pathSerializer()` in their error, also inside your own `pathSerializer`,
26
+ instead of `querySerializer()`.
27
+
5
28
  ## 1.0.0-rc.7
6
29
 
7
30
  ### Patch Changes
@@ -199,31 +199,61 @@ type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaConten
199
199
  * `default` is left to the errors, as before.
200
200
  */
201
201
  type SuccessStatus<T> = [SuccessStatusKey<T>] extends [never] ? Extract<keyof T, "default"> : SuccessStatusKey<T>;
202
+ /** `true` for `any`: a conditional type on `any` takes both branches, so `any` is checked first. */
203
+ type IsAny<T> = 0 extends 1 & T ? true : false;
202
204
  /**
203
205
  * Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one. For
204
206
  * a union of maps (a call with a union of paths), the union of each map's responses: `keyof` of a
205
207
  * union is only the shared statuses, so one map of them lost the body of every path that did not
206
- * share its statuses, and a `default`-only path typed the data `undefined` (upstream).
208
+ * share its statuses, and a `default`-only path typed the data `undefined` (upstream). An `any`
209
+ * map (`createClient<any>()`) gives `any`: no key of `keyof any` is a status, which gave `never`.
207
210
  */
208
- type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, SuccessStatus<T>> : never;
211
+ type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = IsAny<T> extends true ? T : T extends unknown ? ResponseBodies<T, Media, SuccessStatus<T>, never> : never;
209
212
  /**
210
- * The bodies of the responses `ResponseCode` in the media types `Media`. A response with a
211
- * `content` map but no media type in `Media` adds nothing: it gave its whole map, so the error of
212
- * `ErrorResponseJSON` for a `text/html` 500 was `{ "text/html": string }`, a body no response has.
213
- * A response without `content` keeps its `content` type (`undefined` for `content?: never`).
213
+ * Return all 1XX, 3XX, 4XX, 5XX and `default` responses from a Response Object Map; for a union
214
+ * of maps, the union of each map's responses, as `SuccessResponse`. An `any` map gives `any`.
215
+ */
216
+ type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = IsAny<T> extends true ? T : T extends unknown ? ResponseBodies<T, Media, ErrorStatusKey<T>, string> : never;
217
+ /**
218
+ * The bodies of the responses `ResponseCode` as the client returns them (`ResponseBody`): each
219
+ * success one as `SuccessResponse` gives it, each other one as `ErrorResponse` does.
220
+ */
221
+ type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends SuccessStatus<T> ? ResponseBodies<T, Media, ResponseCode, never> : ResponseBodies<T, Media, ResponseCode, string>;
222
+ /**
223
+ * The bodies of the responses `ResponseCode`. A response without `content` keeps its `content`
224
+ * type (`undefined` for `content?: never`). `OtherBody` is the class's body that is not JSON: it
225
+ * is passed in, not read from the status, so the result for a generic map stays a type that
226
+ * TypeScript relates to the one of another client (ADR-030).
214
227
  */
215
- type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends keyof T ? {
216
- [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? FilterKeys<T[K]["content"], Media> : K extends keyof T ? T[K]["content"] : never;
228
+ type ResponseBodies<T extends Record<string | number, any>, Media, ResponseCode extends keyof T, OtherBody> = ResponseCode extends keyof T ? {
229
+ [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? ResponseBody<T[K]["content"], Media, OtherBody> : K extends keyof T ? T[K]["content"] : never;
217
230
  }[ResponseCode] : never;
218
231
  /**
219
- * Return all 1XX, 3XX, 4XX, 5XX and `default` responses from a Response Object Map; for a union
220
- * of maps, the union of each map's responses, as `SuccessResponse`.
232
+ * The body of one response with the content map `Content`, as the client returns it. `Media`
233
+ * picks among its media types, as an `Accept` header does, and each one picked gives its schema.
234
+ * A response with none of them can still arrive (a proxy's HTML 500), so it gives what the client
235
+ * makes of its bodies: a JSON body gives its schema, any other body `OtherBody`, a `string` for an
236
+ * error (the client returns the text) and nothing for a success (the default `parseAs: "json"`
237
+ * rejects it; another `parseAs` types the data by itself). `Media` is read only in
238
+ * `keyof Content & Media`, as in `FilterKeys`, so TypeScript still relates the results of two
239
+ * clients with different `Media` in generic code (ADR-030).
240
+ */
241
+ type ResponseBody<Content, Media, OtherBody> = ([keyof Content & Media] extends [never] ? JSONLike<Content> : Content[keyof Content & Media]) | ([keyof Content & Media] extends [never] ? false extends {
242
+ [Key in keyof Content]: IsJSONMediaType<Key>;
243
+ }[keyof Content] ? OtherBody : never : never);
244
+ /**
245
+ * The media types that the client parses as JSON, as a content map writes them: `json` or a
246
+ * `+json` suffix (RFC 6838), with or without parameters, as the client's `JSON_MEDIA_TYPE_RE`, and
247
+ * the wildcards for any type and any `application` type, which may hold JSON. Not `x-ndjson` or
248
+ * `json-seq`, whose bodies are lines of JSON.
221
249
  */
222
- type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatusKey<T>> : never;
250
+ type JSONMediaType = `${string}/${"" | `${string}+`}json` | `${string}/${"" | `${string}+`}json${";" | " " | "\t"}${string}` | "*/*" | "application/*";
251
+ /** Whether the client parses a body of the media type `Key` as JSON, in any letter case. */
252
+ type IsJSONMediaType<Key> = Key extends string ? Lowercase<Key> extends JSONMediaType ? true : false : false;
223
253
  /** Return first JSON-like 2XX response from a path + HTTP method */
224
- type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
254
+ type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, JSONMediaType>;
225
255
  /** Return JSON-like 1XX, 3XX, 4XX, 5XX and `default` responses from a path + HTTP method */
226
- type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
256
+ type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, JSONMediaType>;
227
257
  /** Return JSON-like request body from a path + HTTP method */
228
258
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
229
259
  /** Find first match of multiple keys */
@@ -240,10 +270,13 @@ type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends unknown ? Filte
240
270
  */
241
271
  type MediaType = `${string}/${string}`;
242
272
  /**
243
- * Return any media type containing "json" (works for "application/json",
244
- * "application/vnd.api+json", "application/vnd.oai.openapi+json")
273
+ * The values of the JSON media types of a content map, as the client parses them: `json` or a
274
+ * `+json` suffix, with or without parameters ("application/json", "application/vnd.api+json",
275
+ * "application/json; charset=utf-8").
245
276
  */
246
- type JSONLike<T> = FilterKeys<T, `${string}/json`>;
277
+ type JSONLike<T> = {
278
+ [Key in keyof T]: IsJSONMediaType<Key> extends true ? T[Key] : never;
279
+ }[keyof T];
247
280
  /**
248
281
  * Filter objects that have required keys
249
282
  * @deprecated Use `RequiredKeysOf` instead
@@ -199,31 +199,61 @@ type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaConten
199
199
  * `default` is left to the errors, as before.
200
200
  */
201
201
  type SuccessStatus<T> = [SuccessStatusKey<T>] extends [never] ? Extract<keyof T, "default"> : SuccessStatusKey<T>;
202
+ /** `true` for `any`: a conditional type on `any` takes both branches, so `any` is checked first. */
203
+ type IsAny<T> = 0 extends 1 & T ? true : false;
202
204
  /**
203
205
  * Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one. For
204
206
  * a union of maps (a call with a union of paths), the union of each map's responses: `keyof` of a
205
207
  * union is only the shared statuses, so one map of them lost the body of every path that did not
206
- * share its statuses, and a `default`-only path typed the data `undefined` (upstream).
208
+ * share its statuses, and a `default`-only path typed the data `undefined` (upstream). An `any`
209
+ * map (`createClient<any>()`) gives `any`: no key of `keyof any` is a status, which gave `never`.
207
210
  */
208
- type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, SuccessStatus<T>> : never;
211
+ type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = IsAny<T> extends true ? T : T extends unknown ? ResponseBodies<T, Media, SuccessStatus<T>, never> : never;
209
212
  /**
210
- * The bodies of the responses `ResponseCode` in the media types `Media`. A response with a
211
- * `content` map but no media type in `Media` adds nothing: it gave its whole map, so the error of
212
- * `ErrorResponseJSON` for a `text/html` 500 was `{ "text/html": string }`, a body no response has.
213
- * A response without `content` keeps its `content` type (`undefined` for `content?: never`).
213
+ * Return all 1XX, 3XX, 4XX, 5XX and `default` responses from a Response Object Map; for a union
214
+ * of maps, the union of each map's responses, as `SuccessResponse`. An `any` map gives `any`.
215
+ */
216
+ type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = IsAny<T> extends true ? T : T extends unknown ? ResponseBodies<T, Media, ErrorStatusKey<T>, string> : never;
217
+ /**
218
+ * The bodies of the responses `ResponseCode` as the client returns them (`ResponseBody`): each
219
+ * success one as `SuccessResponse` gives it, each other one as `ErrorResponse` does.
220
+ */
221
+ type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends SuccessStatus<T> ? ResponseBodies<T, Media, ResponseCode, never> : ResponseBodies<T, Media, ResponseCode, string>;
222
+ /**
223
+ * The bodies of the responses `ResponseCode`. A response without `content` keeps its `content`
224
+ * type (`undefined` for `content?: never`). `OtherBody` is the class's body that is not JSON: it
225
+ * is passed in, not read from the status, so the result for a generic map stays a type that
226
+ * TypeScript relates to the one of another client (ADR-030).
214
227
  */
215
- type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends keyof T ? {
216
- [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? FilterKeys<T[K]["content"], Media> : K extends keyof T ? T[K]["content"] : never;
228
+ type ResponseBodies<T extends Record<string | number, any>, Media, ResponseCode extends keyof T, OtherBody> = ResponseCode extends keyof T ? {
229
+ [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? ResponseBody<T[K]["content"], Media, OtherBody> : K extends keyof T ? T[K]["content"] : never;
217
230
  }[ResponseCode] : never;
218
231
  /**
219
- * Return all 1XX, 3XX, 4XX, 5XX and `default` responses from a Response Object Map; for a union
220
- * of maps, the union of each map's responses, as `SuccessResponse`.
232
+ * The body of one response with the content map `Content`, as the client returns it. `Media`
233
+ * picks among its media types, as an `Accept` header does, and each one picked gives its schema.
234
+ * A response with none of them can still arrive (a proxy's HTML 500), so it gives what the client
235
+ * makes of its bodies: a JSON body gives its schema, any other body `OtherBody`, a `string` for an
236
+ * error (the client returns the text) and nothing for a success (the default `parseAs: "json"`
237
+ * rejects it; another `parseAs` types the data by itself). `Media` is read only in
238
+ * `keyof Content & Media`, as in `FilterKeys`, so TypeScript still relates the results of two
239
+ * clients with different `Media` in generic code (ADR-030).
240
+ */
241
+ type ResponseBody<Content, Media, OtherBody> = ([keyof Content & Media] extends [never] ? JSONLike<Content> : Content[keyof Content & Media]) | ([keyof Content & Media] extends [never] ? false extends {
242
+ [Key in keyof Content]: IsJSONMediaType<Key>;
243
+ }[keyof Content] ? OtherBody : never : never);
244
+ /**
245
+ * The media types that the client parses as JSON, as a content map writes them: `json` or a
246
+ * `+json` suffix (RFC 6838), with or without parameters, as the client's `JSON_MEDIA_TYPE_RE`, and
247
+ * the wildcards for any type and any `application` type, which may hold JSON. Not `x-ndjson` or
248
+ * `json-seq`, whose bodies are lines of JSON.
221
249
  */
222
- type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatusKey<T>> : never;
250
+ type JSONMediaType = `${string}/${"" | `${string}+`}json` | `${string}/${"" | `${string}+`}json${";" | " " | "\t"}${string}` | "*/*" | "application/*";
251
+ /** Whether the client parses a body of the media type `Key` as JSON, in any letter case. */
252
+ type IsJSONMediaType<Key> = Key extends string ? Lowercase<Key> extends JSONMediaType ? true : false : false;
223
253
  /** Return first JSON-like 2XX response from a path + HTTP method */
224
- type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
254
+ type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, JSONMediaType>;
225
255
  /** Return JSON-like 1XX, 3XX, 4XX, 5XX and `default` responses from a path + HTTP method */
226
- type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
256
+ type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, JSONMediaType>;
227
257
  /** Return JSON-like request body from a path + HTTP method */
228
258
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
229
259
  /** Find first match of multiple keys */
@@ -240,10 +270,13 @@ type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends unknown ? Filte
240
270
  */
241
271
  type MediaType = `${string}/${string}`;
242
272
  /**
243
- * Return any media type containing "json" (works for "application/json",
244
- * "application/vnd.api+json", "application/vnd.oai.openapi+json")
273
+ * The values of the JSON media types of a content map, as the client parses them: `json` or a
274
+ * `+json` suffix, with or without parameters ("application/json", "application/vnd.api+json",
275
+ * "application/json; charset=utf-8").
245
276
  */
246
- type JSONLike<T> = FilterKeys<T, `${string}/json`>;
277
+ type JSONLike<T> = {
278
+ [Key in keyof T]: IsJSONMediaType<Key> extends true ? T[Key] : never;
279
+ }[keyof T];
247
280
  /**
248
281
  * Filter objects that have required keys
249
282
  * @deprecated Use `RequiredKeysOf` instead
package/dist/helpers.d.ts CHANGED
@@ -199,31 +199,61 @@ type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaConten
199
199
  * `default` is left to the errors, as before.
200
200
  */
201
201
  type SuccessStatus<T> = [SuccessStatusKey<T>] extends [never] ? Extract<keyof T, "default"> : SuccessStatusKey<T>;
202
+ /** `true` for `any`: a conditional type on `any` takes both branches, so `any` is checked first. */
203
+ type IsAny<T> = 0 extends 1 & T ? true : false;
202
204
  /**
203
205
  * Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one. For
204
206
  * a union of maps (a call with a union of paths), the union of each map's responses: `keyof` of a
205
207
  * union is only the shared statuses, so one map of them lost the body of every path that did not
206
- * share its statuses, and a `default`-only path typed the data `undefined` (upstream).
208
+ * share its statuses, and a `default`-only path typed the data `undefined` (upstream). An `any`
209
+ * map (`createClient<any>()`) gives `any`: no key of `keyof any` is a status, which gave `never`.
207
210
  */
208
- type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, SuccessStatus<T>> : never;
211
+ type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = IsAny<T> extends true ? T : T extends unknown ? ResponseBodies<T, Media, SuccessStatus<T>, never> : never;
209
212
  /**
210
- * The bodies of the responses `ResponseCode` in the media types `Media`. A response with a
211
- * `content` map but no media type in `Media` adds nothing: it gave its whole map, so the error of
212
- * `ErrorResponseJSON` for a `text/html` 500 was `{ "text/html": string }`, a body no response has.
213
- * A response without `content` keeps its `content` type (`undefined` for `content?: never`).
213
+ * Return all 1XX, 3XX, 4XX, 5XX and `default` responses from a Response Object Map; for a union
214
+ * of maps, the union of each map's responses, as `SuccessResponse`. An `any` map gives `any`.
215
+ */
216
+ type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = IsAny<T> extends true ? T : T extends unknown ? ResponseBodies<T, Media, ErrorStatusKey<T>, string> : never;
217
+ /**
218
+ * The bodies of the responses `ResponseCode` as the client returns them (`ResponseBody`): each
219
+ * success one as `SuccessResponse` gives it, each other one as `ErrorResponse` does.
220
+ */
221
+ type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends SuccessStatus<T> ? ResponseBodies<T, Media, ResponseCode, never> : ResponseBodies<T, Media, ResponseCode, string>;
222
+ /**
223
+ * The bodies of the responses `ResponseCode`. A response without `content` keeps its `content`
224
+ * type (`undefined` for `content?: never`). `OtherBody` is the class's body that is not JSON: it
225
+ * is passed in, not read from the status, so the result for a generic map stays a type that
226
+ * TypeScript relates to the one of another client (ADR-030).
214
227
  */
215
- type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends keyof T ? {
216
- [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? FilterKeys<T[K]["content"], Media> : K extends keyof T ? T[K]["content"] : never;
228
+ type ResponseBodies<T extends Record<string | number, any>, Media, ResponseCode extends keyof T, OtherBody> = ResponseCode extends keyof T ? {
229
+ [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? ResponseBody<T[K]["content"], Media, OtherBody> : K extends keyof T ? T[K]["content"] : never;
217
230
  }[ResponseCode] : never;
218
231
  /**
219
- * Return all 1XX, 3XX, 4XX, 5XX and `default` responses from a Response Object Map; for a union
220
- * of maps, the union of each map's responses, as `SuccessResponse`.
232
+ * The body of one response with the content map `Content`, as the client returns it. `Media`
233
+ * picks among its media types, as an `Accept` header does, and each one picked gives its schema.
234
+ * A response with none of them can still arrive (a proxy's HTML 500), so it gives what the client
235
+ * makes of its bodies: a JSON body gives its schema, any other body `OtherBody`, a `string` for an
236
+ * error (the client returns the text) and nothing for a success (the default `parseAs: "json"`
237
+ * rejects it; another `parseAs` types the data by itself). `Media` is read only in
238
+ * `keyof Content & Media`, as in `FilterKeys`, so TypeScript still relates the results of two
239
+ * clients with different `Media` in generic code (ADR-030).
240
+ */
241
+ type ResponseBody<Content, Media, OtherBody> = ([keyof Content & Media] extends [never] ? JSONLike<Content> : Content[keyof Content & Media]) | ([keyof Content & Media] extends [never] ? false extends {
242
+ [Key in keyof Content]: IsJSONMediaType<Key>;
243
+ }[keyof Content] ? OtherBody : never : never);
244
+ /**
245
+ * The media types that the client parses as JSON, as a content map writes them: `json` or a
246
+ * `+json` suffix (RFC 6838), with or without parameters, as the client's `JSON_MEDIA_TYPE_RE`, and
247
+ * the wildcards for any type and any `application` type, which may hold JSON. Not `x-ndjson` or
248
+ * `json-seq`, whose bodies are lines of JSON.
221
249
  */
222
- type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatusKey<T>> : never;
250
+ type JSONMediaType = `${string}/${"" | `${string}+`}json` | `${string}/${"" | `${string}+`}json${";" | " " | "\t"}${string}` | "*/*" | "application/*";
251
+ /** Whether the client parses a body of the media type `Key` as JSON, in any letter case. */
252
+ type IsJSONMediaType<Key> = Key extends string ? Lowercase<Key> extends JSONMediaType ? true : false : false;
223
253
  /** Return first JSON-like 2XX response from a path + HTTP method */
224
- type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
254
+ type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, JSONMediaType>;
225
255
  /** Return JSON-like 1XX, 3XX, 4XX, 5XX and `default` responses from a path + HTTP method */
226
- type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
256
+ type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, JSONMediaType>;
227
257
  /** Return JSON-like request body from a path + HTTP method */
228
258
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
229
259
  /** Find first match of multiple keys */
@@ -240,10 +270,13 @@ type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends unknown ? Filte
240
270
  */
241
271
  type MediaType = `${string}/${string}`;
242
272
  /**
243
- * Return any media type containing "json" (works for "application/json",
244
- * "application/vnd.api+json", "application/vnd.oai.openapi+json")
273
+ * The values of the JSON media types of a content map, as the client parses them: `json` or a
274
+ * `+json` suffix, with or without parameters ("application/json", "application/vnd.api+json",
275
+ * "application/json; charset=utf-8").
245
276
  */
246
- type JSONLike<T> = FilterKeys<T, `${string}/json`>;
277
+ type JSONLike<T> = {
278
+ [Key in keyof T]: IsJSONMediaType<Key> extends true ? T[Key] : never;
279
+ }[keyof T];
247
280
  /**
248
281
  * Filter objects that have required keys
249
282
  * @deprecated Use `RequiredKeysOf` instead
package/dist/index.cjs CHANGED
@@ -440,6 +440,13 @@ function checkFlatMembers(value, message) {
440
440
  throw new Error(message);
441
441
  }
442
442
  }
443
+ const PATH_STYLE_JOINERS = { simple: ",", label: ".", matrix: ";" };
444
+ function checkFlatParam(value, style) {
445
+ checkFlatMembers(
446
+ value,
447
+ PATH_STYLE_JOINERS[style] ? PATH_NESTED_VALUE_ERROR : QUERY_NESTED_VALUE_ERROR
448
+ );
449
+ }
443
450
  function encodeValue(value, allowReserved) {
444
451
  if (isNestedValue(value)) {
445
452
  throw new Error(QUERY_NESTED_VALUE_ERROR);
@@ -454,7 +461,7 @@ function serializePrimitiveParam(name, value, options) {
454
461
  return `${name}=${encodeValue(value, options?.allowReserved)}`;
455
462
  }
456
463
  function joinExploded(values, style) {
457
- const joiner = { simple: ",", label: ".", matrix: ";" }[style] || "&";
464
+ const joiner = PATH_STYLE_JOINERS[style] || "&";
458
465
  const final = values.join(joiner);
459
466
  return style === "label" || style === "matrix" ? `${joiner}${final}` : final;
460
467
  }
@@ -491,6 +498,7 @@ function serializeObjectParam(name, value, options) {
491
498
  if (!value || typeof value !== "object" || !Object.keys(value).length) {
492
499
  return "";
493
500
  }
501
+ checkFlatParam(value, options.style);
494
502
  if (options.style !== "deepObject" && options.explode === false) {
495
503
  return serializeUnexplodedObject(name, value, options);
496
504
  }
@@ -530,6 +538,7 @@ function serializeArrayParam(name, value, options) {
530
538
  if (!Array.isArray(value) || !value.length) {
531
539
  return "";
532
540
  }
541
+ checkFlatParam(value, options.style);
533
542
  if (options.explode === false) {
534
543
  return serializeUnexplodedArray(name, value, options);
535
544
  }
@@ -620,11 +629,9 @@ function defaultPathSerializer(pathname, pathParams) {
620
629
  }
621
630
  function serializePathParam(name, value, options) {
622
631
  if (Array.isArray(value)) {
623
- checkFlatMembers(value, PATH_NESTED_VALUE_ERROR);
624
632
  return serializeArrayParam(name, value, options);
625
633
  }
626
634
  if (typeof value === "object" && !isDate(value)) {
627
- checkFlatMembers(value, PATH_NESTED_VALUE_ERROR);
628
635
  return serializeObjectParam(name, value, options);
629
636
  }
630
637
  if (options.style === "matrix") {