openapi-typesafe-fetch 1.0.0-rc.4 → 1.0.0-rc.6

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,65 @@
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.6
6
+
7
+ ### Patch Changes
8
+
9
+ - [#87](https://github.com/openapi-typesafe/openapi-typesafe/pull/87) [`dfb0fe3`](https://github.com/openapi-typesafe/openapi-typesafe/commit/dfb0fe3c61a75d2b8ca512577f4b699049171a64) Thanks [@fedorello](https://github.com/fedorello)! - Leave out `null` and `undefined` items and members of arrays and objects in every parameter style, also in the path and headers (`ids=1,null`, `filter=owner,undefined` and `x-p: a,,b,1` were sent before), and default `explode` to `true` for the `form` query style only, as OpenAPI does, so `{ array: { style: "pipeDelimited" } }` sends `id=3|4|5`. A nested value in a header parameter now fails with an error that names header parameters, not `querySerializer()`.
10
+
11
+ ## 1.0.0-rc.5
12
+
13
+ ### Minor Changes
14
+
15
+ - [#68](https://github.com/openapi-typesafe/openapi-typesafe/pull/68) [`0f0792b`](https://github.com/openapi-typesafe/openapi-typesafe/commit/0f0792b8efa4f5139653cd285455f7d6165ddb95) Thanks [@fedorello](https://github.com/fedorello)! - A `FormData`, `Blob`, `File` or `ReadableStream` body from `undici` or another realm (an iframe) is
16
+ sent as it is: the client told a body by `instanceof`, so it JSON-encoded one of another class as
17
+ `{}`. `createClient` takes a `FormData` class next to `Request`: a multipart body is built with it,
18
+ and a FormData body of another class is copied into one of it, since undici's `Request` read the
19
+ platform's FormData as the text `[object FormData]`. With undici, pass
20
+ `createClient({ fetch, Request, FormData })` from the package. An instance of the platform's own
21
+ global class is still sent as it is, so the FormData, Blob and URLSearchParams of React Native and
22
+ the Blob and File of jsdom 20 (Jest 29), which have no type tag or no `arrayBuffer`, keep working.
23
+
24
+ - [#67](https://github.com/openapi-typesafe/openapi-typesafe/pull/67) [`fbaded2`](https://github.com/openapi-typesafe/openapi-typesafe/commit/fbaded241385d032d5b28d2c93d5e0a7d6ae292b) Thanks [@fedorello](https://github.com/fedorello)! - The client has a read-only `baseUrl`: the `baseUrl` option without its trailing slash, or `""`.
25
+
26
+ ### Patch Changes
27
+
28
+ - [#68](https://github.com/openapi-typesafe/openapi-typesafe/pull/68) [`0f0792b`](https://github.com/openapi-typesafe/openapi-typesafe/commit/0f0792b8efa4f5139653cd285455f7d6165ddb95) Thanks [@fedorello](https://github.com/fedorello)! - An empty success body read with `parseAs: "text"`, `"blob"`, `"arrayBuffer"` or `"stream"` gives
29
+ `""`, an empty `Blob` or `ArrayBuffer`, or `response.body`, as its type says. A 204, an answer to
30
+ HEAD or a `Content-Length: 0` gave `undefined`, while the same empty body without a length gave
31
+ `""`. With the default `parseAs: "json"` an empty body is still `undefined`.
32
+
33
+ - [#68](https://github.com/openapi-typesafe/openapi-typesafe/pull/68) [`0f0792b`](https://github.com/openapi-typesafe/openapi-typesafe/commit/0f0792b8efa4f5139653cd285455f7d6165ddb95) Thanks [@fedorello](https://github.com/fedorello)! - `params.header` values are written in the `simple` style, the only style OpenAPI has for headers:
34
+ a `Date` as ISO 8601 (it was local time), an array as `a,b` (it was `a, b`) and an object as
35
+ `role,admin` (it was `[object Object]`), not percent-encoded, also in the names. A nested object or
36
+ array inside one throws, as in the query. An empty array or object sends no header, and `null`
37
+ still removes one.
38
+
39
+ - [#68](https://github.com/openapi-typesafe/openapi-typesafe/pull/68) [`0f0792b`](https://github.com/openapi-typesafe/openapi-typesafe/commit/0f0792b8efa4f5139653cd285455f7d6165ddb95) Thanks [@fedorello](https://github.com/fedorello)! - A form or multipart `Content-Type` with spaces or tabs before its parameters
40
+ (`application/x-www-form-urlencoded ; charset=utf-8`, RFC 9110 OWS) gives form data or `FormData`
41
+ again, in the runtime and in the result type of `defaultBodySerializer`; the object was sent as
42
+ JSON. The JSON check already took them.
43
+
44
+ - [#68](https://github.com/openapi-typesafe/openapi-typesafe/pull/68) [`0f0792b`](https://github.com/openapi-typesafe/openapi-typesafe/commit/0f0792b8efa4f5139653cd285455f7d6165ddb95) Thanks [@fedorello](https://github.com/fedorello)! - `style` and `explode` of the `array` and `object` options of `QuerySerializerOptions` are optional,
45
+ and one left out takes the client's value, else its default (`form` or `deepObject`, exploded): a
46
+ request's `{ array: { explode: false } }` keeps the client's `style`. `onError` may return an
47
+ `Error` of another realm (an iframe, `node:vm`); it was rejected as not an error.
48
+
49
+ - [#68](https://github.com/openapi-typesafe/openapi-typesafe/pull/68) [`0f0792b`](https://github.com/openapi-typesafe/openapi-typesafe/commit/0f0792b8efa4f5139653cd285455f7d6165ddb95) Thanks [@fedorello](https://github.com/fedorello)! - The query of a request whose path template has a fragment (`/docs#intro`) goes before the
50
+ fragment, joined with a query the template already has. It was added after the `#`, where it is
51
+ part of the fragment, so `fetch` never sent it.
52
+
53
+ - [#75](https://github.com/openapi-typesafe/openapi-typesafe/pull/75) [`a74608e`](https://github.com/openapi-typesafe/openapi-typesafe/commit/a74608ec36b0a938594e0d0fac10c9fb222c1742) Thanks [@fedorello](https://github.com/fedorello)! - `Readable` and `Writable` find the `$Read` and `$Write` properties of an object with an index
54
+ signature (`additionalProperties`). The keys were read through the index signature, so no
55
+ property was marked: a request body required the `readOnly` `id`, and a response kept the
56
+ `writeOnly` password. The helper types that `--read-write-markers` writes are fixed the same way.
57
+
58
+ - [#68](https://github.com/openapi-typesafe/openapi-typesafe/pull/68) [`0f0792b`](https://github.com/openapi-typesafe/openapi-typesafe/commit/0f0792b8efa4f5139653cd285455f7d6165ddb95) Thanks [@fedorello](https://github.com/fedorello)! - A response is typed as data or an error by the first digit of its status, as `response.ok` reads
59
+ it: a declared 205, 208 or 226 types `data` with its body, and a 419, a 3XX (read with
60
+ `redirect: "manual"`) or a 1XX types `error` with its body.
61
+ The types classified by two closed lists of statuses, `OkStatus` and `ErrorStatus`, which stay
62
+ exported. New helper types `SuccessStatusKey` and `ErrorStatusKey` name the statuses of each kind.
63
+
5
64
  ## 1.0.0-rc.4
6
65
 
7
66
  ### Minor Changes
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # openapi-typesafe-fetch
2
2
 
3
- A type-safe `fetch` client for types generated by [`openapi-typesafe`](../openapi-typesafe). About 9 kB minified
4
- (3.7 kB gzipped), no runtime dependencies, works in browsers, Node.js, Bun and Deno, and with React, Vue, Svelte or plain JavaScript.
3
+ A type-safe `fetch` client for types generated by [`openapi-typesafe`](../openapi-typesafe). About 11 kB minified
4
+ (4 kB gzipped), no runtime dependencies, works in browsers, Node.js, Bun and Deno, and with React, Vue, Svelte or plain JavaScript.
5
5
 
6
6
  A maintained, drop-in successor to `openapi-fetch` (see the [repository README](../../README.md)).
7
7
 
@@ -26,7 +26,7 @@ const client = createClient<paths>({ baseUrl: "https://api.example.com/v1/" });
26
26
 
27
27
  const {
28
28
  data, // present on a 2xx response
29
- error, // present on a 4xx or 5xx response
29
+ error, // present on a response that is not 2xx
30
30
  } = await client.GET("/posts/{post_id}", {
31
31
  params: { path: { post_id: "123" } },
32
32
  });
@@ -2,12 +2,32 @@
2
2
  type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"
3
3
  /** OpenAPI 3.2: a safe, idempotent request with a body. */
4
4
  | "query";
5
- /** 2XX statuses */
5
+ /**
6
+ * Some 2XX statuses, kept for code that names them. The client does not classify by this list: a
7
+ * status is a success or an error by its first digit (`SuccessStatusKey`, `ErrorStatusKey`).
8
+ */
6
9
  type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
7
- /** 4XX and 5XX statuses */
10
+ /** Some 4XX and 5XX statuses and `default`, kept like `OkStatus`. */
8
11
  type ErrorStatus = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | "5XX" | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | "4XX" | "default";
12
+ type StatusDigit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
13
+ /**
14
+ * The keys of a Response Object Map whose text matches a pattern. A generated map has numeric keys
15
+ * (`200`) and string ones (`"2XX"`, `"default"`), and a numeric literal matches no template
16
+ * literal type, so each key is matched by its text.
17
+ */
18
+ type StatusKeysMatching<Keys, Pattern extends string> = Keys extends string | number ? `${Keys}` extends Pattern ? Keys : never : never;
19
+ /**
20
+ * The success statuses of a Response Object Map: every 2XX one. A closed list of statuses left out
21
+ * the ones it did not name (205, 208, 226), and their bodies were typed as no data.
22
+ */
23
+ type SuccessStatusKey<T> = StatusKeysMatching<keyof T, `2${StatusDigit}${StatusDigit}` | "2XX">;
24
+ /**
25
+ * The error statuses of a Response Object Map: every 1XX, 3XX, 4XX and 5XX one, and `default`. The
26
+ * client returns each response that is not `ok` as `error`, also a 3XX with `redirect: "manual"`.
27
+ */
28
+ type ErrorStatusKey<T> = StatusKeysMatching<keyof T, `${1 | 3 | 4 | 5}${StatusDigit}${StatusDigit}` | `${1 | 3 | 4 | 5}XX` | "default">;
9
29
  /** Get a union of OK Statuses */
10
- type OKStatusUnion<T> = FilterKeys<T, OkStatus>;
30
+ type OKStatusUnion<T> = FilterKeys<T, SuccessStatusKey<T>>;
11
31
  /** Get first error status, in order */
12
32
  type FirstErrorStatus<T> = T extends {
13
33
  500: unknown;
@@ -178,7 +198,7 @@ type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaConten
178
198
  * Responses Object), so then it covers every success (upstream #1862). With a 2XX status declared,
179
199
  * `default` is left to the errors, as before.
180
200
  */
181
- type SuccessStatus<T> = [Extract<keyof T, OkStatus>] extends [never] ? "default" : OkStatus;
201
+ type SuccessStatus<T> = [SuccessStatusKey<T>] extends [never] ? Extract<keyof T, "default"> : SuccessStatusKey<T>;
182
202
  /**
183
203
  * Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one. For
184
204
  * a union of maps (a call with a union of paths), the union of each map's responses: `keyof` of a
@@ -190,13 +210,13 @@ type GetResponseContent<T extends Record<string | number, any>, Media extends Me
190
210
  [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? FilterKeys<T[K]["content"], Media> extends never ? T[K]["content"] : FilterKeys<T[K]["content"], Media> : K extends keyof T ? T[K]["content"] : never;
191
211
  }[ResponseCode] : never;
192
212
  /**
193
- * Return all 5XX and 4XX responses (in that order) from a Response Object Map; for a union of maps,
194
- * the union of each map's responses, as `SuccessResponse`.
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`.
195
215
  */
196
- type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatus> : never;
216
+ type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatusKey<T>> : never;
197
217
  /** Return first JSON-like 2XX response from a path + HTTP method */
198
218
  type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
199
- /** Return first JSON-like 5XX or 4XX response from a path + HTTP method */
219
+ /** Return JSON-like 1XX, 3XX, 4XX, 5XX and `default` responses from a path + HTTP method */
200
220
  type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
201
221
  /** Return JSON-like request body from a path + HTTP method */
202
222
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
@@ -258,10 +278,13 @@ type ReadWriteLeaf = ReadWritePrimitive | ((...args: never) => unknown);
258
278
  * `any` key is not marked either (`0 extends 1 & T[K]` is true only for `any`): a check on `any`
259
279
  * takes both branches, so `Readable` dropped the key and `Writable` typed it `undefined`. The
260
280
  * checks are not distributive (their check types are not type parameters), so `never` is seen.
281
+ * The keys are read by remapping, not as `{ ... }[keyof T]`: with an index signature (from
282
+ * `additionalProperties`) `keyof T` is `string`, which indexes the signature only, so no property
283
+ * was marked and `Writable` made a readOnly property a required `never`.
261
284
  */
262
- type ReadWriteMarkedKeys<T, Marker> = {
263
- [K in keyof T]-?: 0 extends 1 & T[K] ? never : NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never;
264
- }[keyof T];
285
+ type ReadWriteMarkedKeys<T, Marker> = keyof {
286
+ [K in keyof T as 0 extends 1 & T[K] ? never : NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never]: never;
287
+ };
265
288
  /**
266
289
  * Resolve an array or a tuple element by element. An array uses `T[number]`, not `infer E`: for an
267
290
  * intersection of arrays (`allOf`) inference keeps one member and narrowing breaks (upstream
@@ -296,4 +319,4 @@ type Writable<T> = T extends $Read<any> ? never : T extends $Write<infer U> ? Wr
296
319
  [K in ReadWriteMarkedKeys<T, $Read<any>>]?: never;
297
320
  } : T;
298
321
 
299
- export type { $Read, $Write, ErrorResponse, ErrorResponseJSON, ErrorStatus, FilterKeys, FindRequiredKeys, FirstErrorStatus, GetResponseContent, GetValueWithDefault, HasRequiredKeys, HttpMethod, IsOperationRequestBodyOptional, JSONLike, MediaType, OKStatusUnion, OkStatus, OperationObject, OperationRequestBody, OperationRequestBodyContent, OperationRequestBodyMediaContent, PathItemObject, PathsWithMethod, Readable, RequestBodyJSON, RequiredKeysOf, ResponseContent, ResponseObjectMap, SuccessResponse, SuccessResponseJSON, Writable };
322
+ export type { $Read, $Write, ErrorResponse, ErrorResponseJSON, ErrorStatus, ErrorStatusKey, FilterKeys, FindRequiredKeys, FirstErrorStatus, GetResponseContent, GetValueWithDefault, HasRequiredKeys, HttpMethod, IsOperationRequestBodyOptional, JSONLike, MediaType, OKStatusUnion, OkStatus, OperationObject, OperationRequestBody, OperationRequestBodyContent, OperationRequestBodyMediaContent, PathItemObject, PathsWithMethod, Readable, RequestBodyJSON, RequiredKeysOf, ResponseContent, ResponseObjectMap, SuccessResponse, SuccessResponseJSON, SuccessStatusKey, Writable };
@@ -2,12 +2,32 @@
2
2
  type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"
3
3
  /** OpenAPI 3.2: a safe, idempotent request with a body. */
4
4
  | "query";
5
- /** 2XX statuses */
5
+ /**
6
+ * Some 2XX statuses, kept for code that names them. The client does not classify by this list: a
7
+ * status is a success or an error by its first digit (`SuccessStatusKey`, `ErrorStatusKey`).
8
+ */
6
9
  type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
7
- /** 4XX and 5XX statuses */
10
+ /** Some 4XX and 5XX statuses and `default`, kept like `OkStatus`. */
8
11
  type ErrorStatus = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | "5XX" | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | "4XX" | "default";
12
+ type StatusDigit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
13
+ /**
14
+ * The keys of a Response Object Map whose text matches a pattern. A generated map has numeric keys
15
+ * (`200`) and string ones (`"2XX"`, `"default"`), and a numeric literal matches no template
16
+ * literal type, so each key is matched by its text.
17
+ */
18
+ type StatusKeysMatching<Keys, Pattern extends string> = Keys extends string | number ? `${Keys}` extends Pattern ? Keys : never : never;
19
+ /**
20
+ * The success statuses of a Response Object Map: every 2XX one. A closed list of statuses left out
21
+ * the ones it did not name (205, 208, 226), and their bodies were typed as no data.
22
+ */
23
+ type SuccessStatusKey<T> = StatusKeysMatching<keyof T, `2${StatusDigit}${StatusDigit}` | "2XX">;
24
+ /**
25
+ * The error statuses of a Response Object Map: every 1XX, 3XX, 4XX and 5XX one, and `default`. The
26
+ * client returns each response that is not `ok` as `error`, also a 3XX with `redirect: "manual"`.
27
+ */
28
+ type ErrorStatusKey<T> = StatusKeysMatching<keyof T, `${1 | 3 | 4 | 5}${StatusDigit}${StatusDigit}` | `${1 | 3 | 4 | 5}XX` | "default">;
9
29
  /** Get a union of OK Statuses */
10
- type OKStatusUnion<T> = FilterKeys<T, OkStatus>;
30
+ type OKStatusUnion<T> = FilterKeys<T, SuccessStatusKey<T>>;
11
31
  /** Get first error status, in order */
12
32
  type FirstErrorStatus<T> = T extends {
13
33
  500: unknown;
@@ -178,7 +198,7 @@ type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaConten
178
198
  * Responses Object), so then it covers every success (upstream #1862). With a 2XX status declared,
179
199
  * `default` is left to the errors, as before.
180
200
  */
181
- type SuccessStatus<T> = [Extract<keyof T, OkStatus>] extends [never] ? "default" : OkStatus;
201
+ type SuccessStatus<T> = [SuccessStatusKey<T>] extends [never] ? Extract<keyof T, "default"> : SuccessStatusKey<T>;
182
202
  /**
183
203
  * Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one. For
184
204
  * a union of maps (a call with a union of paths), the union of each map's responses: `keyof` of a
@@ -190,13 +210,13 @@ type GetResponseContent<T extends Record<string | number, any>, Media extends Me
190
210
  [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? FilterKeys<T[K]["content"], Media> extends never ? T[K]["content"] : FilterKeys<T[K]["content"], Media> : K extends keyof T ? T[K]["content"] : never;
191
211
  }[ResponseCode] : never;
192
212
  /**
193
- * Return all 5XX and 4XX responses (in that order) from a Response Object Map; for a union of maps,
194
- * the union of each map's responses, as `SuccessResponse`.
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`.
195
215
  */
196
- type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatus> : never;
216
+ type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatusKey<T>> : never;
197
217
  /** Return first JSON-like 2XX response from a path + HTTP method */
198
218
  type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
199
- /** Return first JSON-like 5XX or 4XX response from a path + HTTP method */
219
+ /** Return JSON-like 1XX, 3XX, 4XX, 5XX and `default` responses from a path + HTTP method */
200
220
  type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
201
221
  /** Return JSON-like request body from a path + HTTP method */
202
222
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
@@ -258,10 +278,13 @@ type ReadWriteLeaf = ReadWritePrimitive | ((...args: never) => unknown);
258
278
  * `any` key is not marked either (`0 extends 1 & T[K]` is true only for `any`): a check on `any`
259
279
  * takes both branches, so `Readable` dropped the key and `Writable` typed it `undefined`. The
260
280
  * checks are not distributive (their check types are not type parameters), so `never` is seen.
281
+ * The keys are read by remapping, not as `{ ... }[keyof T]`: with an index signature (from
282
+ * `additionalProperties`) `keyof T` is `string`, which indexes the signature only, so no property
283
+ * was marked and `Writable` made a readOnly property a required `never`.
261
284
  */
262
- type ReadWriteMarkedKeys<T, Marker> = {
263
- [K in keyof T]-?: 0 extends 1 & T[K] ? never : NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never;
264
- }[keyof T];
285
+ type ReadWriteMarkedKeys<T, Marker> = keyof {
286
+ [K in keyof T as 0 extends 1 & T[K] ? never : NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never]: never;
287
+ };
265
288
  /**
266
289
  * Resolve an array or a tuple element by element. An array uses `T[number]`, not `infer E`: for an
267
290
  * intersection of arrays (`allOf`) inference keeps one member and narrowing breaks (upstream
@@ -296,4 +319,4 @@ type Writable<T> = T extends $Read<any> ? never : T extends $Write<infer U> ? Wr
296
319
  [K in ReadWriteMarkedKeys<T, $Read<any>>]?: never;
297
320
  } : T;
298
321
 
299
- export type { $Read, $Write, ErrorResponse, ErrorResponseJSON, ErrorStatus, FilterKeys, FindRequiredKeys, FirstErrorStatus, GetResponseContent, GetValueWithDefault, HasRequiredKeys, HttpMethod, IsOperationRequestBodyOptional, JSONLike, MediaType, OKStatusUnion, OkStatus, OperationObject, OperationRequestBody, OperationRequestBodyContent, OperationRequestBodyMediaContent, PathItemObject, PathsWithMethod, Readable, RequestBodyJSON, RequiredKeysOf, ResponseContent, ResponseObjectMap, SuccessResponse, SuccessResponseJSON, Writable };
322
+ export type { $Read, $Write, ErrorResponse, ErrorResponseJSON, ErrorStatus, ErrorStatusKey, FilterKeys, FindRequiredKeys, FirstErrorStatus, GetResponseContent, GetValueWithDefault, HasRequiredKeys, HttpMethod, IsOperationRequestBodyOptional, JSONLike, MediaType, OKStatusUnion, OkStatus, OperationObject, OperationRequestBody, OperationRequestBodyContent, OperationRequestBodyMediaContent, PathItemObject, PathsWithMethod, Readable, RequestBodyJSON, RequiredKeysOf, ResponseContent, ResponseObjectMap, SuccessResponse, SuccessResponseJSON, SuccessStatusKey, Writable };
package/dist/helpers.d.ts CHANGED
@@ -2,12 +2,32 @@
2
2
  type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"
3
3
  /** OpenAPI 3.2: a safe, idempotent request with a body. */
4
4
  | "query";
5
- /** 2XX statuses */
5
+ /**
6
+ * Some 2XX statuses, kept for code that names them. The client does not classify by this list: a
7
+ * status is a success or an error by its first digit (`SuccessStatusKey`, `ErrorStatusKey`).
8
+ */
6
9
  type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
7
- /** 4XX and 5XX statuses */
10
+ /** Some 4XX and 5XX statuses and `default`, kept like `OkStatus`. */
8
11
  type ErrorStatus = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | "5XX" | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | "4XX" | "default";
12
+ type StatusDigit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
13
+ /**
14
+ * The keys of a Response Object Map whose text matches a pattern. A generated map has numeric keys
15
+ * (`200`) and string ones (`"2XX"`, `"default"`), and a numeric literal matches no template
16
+ * literal type, so each key is matched by its text.
17
+ */
18
+ type StatusKeysMatching<Keys, Pattern extends string> = Keys extends string | number ? `${Keys}` extends Pattern ? Keys : never : never;
19
+ /**
20
+ * The success statuses of a Response Object Map: every 2XX one. A closed list of statuses left out
21
+ * the ones it did not name (205, 208, 226), and their bodies were typed as no data.
22
+ */
23
+ type SuccessStatusKey<T> = StatusKeysMatching<keyof T, `2${StatusDigit}${StatusDigit}` | "2XX">;
24
+ /**
25
+ * The error statuses of a Response Object Map: every 1XX, 3XX, 4XX and 5XX one, and `default`. The
26
+ * client returns each response that is not `ok` as `error`, also a 3XX with `redirect: "manual"`.
27
+ */
28
+ type ErrorStatusKey<T> = StatusKeysMatching<keyof T, `${1 | 3 | 4 | 5}${StatusDigit}${StatusDigit}` | `${1 | 3 | 4 | 5}XX` | "default">;
9
29
  /** Get a union of OK Statuses */
10
- type OKStatusUnion<T> = FilterKeys<T, OkStatus>;
30
+ type OKStatusUnion<T> = FilterKeys<T, SuccessStatusKey<T>>;
11
31
  /** Get first error status, in order */
12
32
  type FirstErrorStatus<T> = T extends {
13
33
  500: unknown;
@@ -178,7 +198,7 @@ type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaConten
178
198
  * Responses Object), so then it covers every success (upstream #1862). With a 2XX status declared,
179
199
  * `default` is left to the errors, as before.
180
200
  */
181
- type SuccessStatus<T> = [Extract<keyof T, OkStatus>] extends [never] ? "default" : OkStatus;
201
+ type SuccessStatus<T> = [SuccessStatusKey<T>] extends [never] ? Extract<keyof T, "default"> : SuccessStatusKey<T>;
182
202
  /**
183
203
  * Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one. For
184
204
  * a union of maps (a call with a union of paths), the union of each map's responses: `keyof` of a
@@ -190,13 +210,13 @@ type GetResponseContent<T extends Record<string | number, any>, Media extends Me
190
210
  [K in ResponseCode]: T[K]["content"] extends Record<string, any> ? FilterKeys<T[K]["content"], Media> extends never ? T[K]["content"] : FilterKeys<T[K]["content"], Media> : K extends keyof T ? T[K]["content"] : never;
191
211
  }[ResponseCode] : never;
192
212
  /**
193
- * Return all 5XX and 4XX responses (in that order) from a Response Object Map; for a union of maps,
194
- * the union of each map's responses, as `SuccessResponse`.
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`.
195
215
  */
196
- type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatus> : never;
216
+ type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = T extends unknown ? GetResponseContent<T, Media, ErrorStatusKey<T>> : never;
197
217
  /** Return first JSON-like 2XX response from a path + HTTP method */
198
218
  type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
199
- /** Return first JSON-like 5XX or 4XX response from a path + HTTP method */
219
+ /** Return JSON-like 1XX, 3XX, 4XX, 5XX and `default` responses from a path + HTTP method */
200
220
  type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
201
221
  /** Return JSON-like request body from a path + HTTP method */
202
222
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
@@ -258,10 +278,13 @@ type ReadWriteLeaf = ReadWritePrimitive | ((...args: never) => unknown);
258
278
  * `any` key is not marked either (`0 extends 1 & T[K]` is true only for `any`): a check on `any`
259
279
  * takes both branches, so `Readable` dropped the key and `Writable` typed it `undefined`. The
260
280
  * checks are not distributive (their check types are not type parameters), so `never` is seen.
281
+ * The keys are read by remapping, not as `{ ... }[keyof T]`: with an index signature (from
282
+ * `additionalProperties`) `keyof T` is `string`, which indexes the signature only, so no property
283
+ * was marked and `Writable` made a readOnly property a required `never`.
261
284
  */
262
- type ReadWriteMarkedKeys<T, Marker> = {
263
- [K in keyof T]-?: 0 extends 1 & T[K] ? never : NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never;
264
- }[keyof T];
285
+ type ReadWriteMarkedKeys<T, Marker> = keyof {
286
+ [K in keyof T as 0 extends 1 & T[K] ? never : NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never]: never;
287
+ };
265
288
  /**
266
289
  * Resolve an array or a tuple element by element. An array uses `T[number]`, not `infer E`: for an
267
290
  * intersection of arrays (`allOf`) inference keeps one member and narrowing breaks (upstream
@@ -296,4 +319,4 @@ type Writable<T> = T extends $Read<any> ? never : T extends $Write<infer U> ? Wr
296
319
  [K in ReadWriteMarkedKeys<T, $Read<any>>]?: never;
297
320
  } : T;
298
321
 
299
- export type { $Read, $Write, ErrorResponse, ErrorResponseJSON, ErrorStatus, FilterKeys, FindRequiredKeys, FirstErrorStatus, GetResponseContent, GetValueWithDefault, HasRequiredKeys, HttpMethod, IsOperationRequestBodyOptional, JSONLike, MediaType, OKStatusUnion, OkStatus, OperationObject, OperationRequestBody, OperationRequestBodyContent, OperationRequestBodyMediaContent, PathItemObject, PathsWithMethod, Readable, RequestBodyJSON, RequiredKeysOf, ResponseContent, ResponseObjectMap, SuccessResponse, SuccessResponseJSON, Writable };
322
+ export type { $Read, $Write, ErrorResponse, ErrorResponseJSON, ErrorStatus, ErrorStatusKey, FilterKeys, FindRequiredKeys, FirstErrorStatus, GetResponseContent, GetValueWithDefault, HasRequiredKeys, HttpMethod, IsOperationRequestBodyOptional, JSONLike, MediaType, OKStatusUnion, OkStatus, OperationObject, OperationRequestBody, OperationRequestBodyContent, OperationRequestBodyMediaContent, PathItemObject, PathsWithMethod, Readable, RequestBodyJSON, RequiredKeysOf, ResponseContent, ResponseObjectMap, SuccessResponse, SuccessResponseJSON, SuccessStatusKey, Writable };