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 +23 -0
- package/dist/helpers.d.cts +49 -16
- package/dist/helpers.d.mts +49 -16
- package/dist/helpers.d.ts +49 -16
- package/dist/index.cjs +10 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -14
- package/dist/index.d.mts +22 -14
- package/dist/index.d.ts +22 -14
- package/dist/index.mjs +10 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/helpers.ts +88 -19
- package/src/index.d.ts +22 -14
- package/src/index.js +23 -7
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
|
package/dist/helpers.d.cts
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 ?
|
|
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
|
-
*
|
|
211
|
-
*
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
216
|
-
[K in ResponseCode]: T[K]["content"] extends Record<string, any> ?
|
|
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
|
-
*
|
|
220
|
-
*
|
|
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
|
|
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>,
|
|
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>,
|
|
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
|
-
*
|
|
244
|
-
* "application/
|
|
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> =
|
|
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.mts
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 ?
|
|
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
|
-
*
|
|
211
|
-
*
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
216
|
-
[K in ResponseCode]: T[K]["content"] extends Record<string, any> ?
|
|
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
|
-
*
|
|
220
|
-
*
|
|
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
|
|
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>,
|
|
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>,
|
|
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
|
-
*
|
|
244
|
-
* "application/
|
|
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> =
|
|
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 ?
|
|
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
|
-
*
|
|
211
|
-
*
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
216
|
-
[K in ResponseCode]: T[K]["content"] extends Record<string, any> ?
|
|
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
|
-
*
|
|
220
|
-
*
|
|
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
|
|
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>,
|
|
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>,
|
|
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
|
-
*
|
|
244
|
-
* "application/
|
|
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> =
|
|
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 =
|
|
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") {
|