openapi-typesafe-helpers 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/README.md CHANGED
@@ -6,6 +6,15 @@ Type helpers shared by `openapi-typesafe-fetch` and `openapi-typesafe-react-quer
6
6
  A maintained successor to `openapi-typescript-helpers` (see the [repository README](../../README.md)).
7
7
  Most projects do not need to install it directly.
8
8
 
9
+ > **Release candidate.** The current version is a 1.0.0 release candidate (`1.0.0-rc.N`);
10
+ > names and output can still change before 1.0.0.
11
+
12
+ ## Versioning
13
+
14
+ Semantic versioning from 1.0.0, with one version for all openapi-typesafe packages. What the
15
+ version promises, and what may grow in a minor release, is listed under
16
+ [Versioning](https://github.com/openapi-typesafe/openapi-typesafe/blob/main/docs/compatibility.md#versioning).
17
+
9
18
  ## License
10
19
 
11
20
  [MIT](./LICENSE)
package/dist/index.d.cts CHANGED
@@ -1,172 +1,286 @@
1
- type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace" | "query";
1
+ type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"
2
+ /** OpenAPI 3.2: a safe, idempotent request with a body. */
3
+ | "query";
4
+ /** 2XX statuses */
2
5
  type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
6
+ /** 4XX and 5XX statuses */
3
7
  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";
8
+ /** Get a union of OK Statuses */
4
9
  type OKStatusUnion<T> = FilterKeys<T, OkStatus>;
10
+ /** Get first error status, in order */
5
11
  type FirstErrorStatus<T> = T extends {
6
- 500: any;
12
+ 500: unknown;
7
13
  } ? T[500] : T extends {
8
- 501: any;
14
+ 501: unknown;
9
15
  } ? T[501] : T extends {
10
- 502: any;
16
+ 502: unknown;
11
17
  } ? T[502] : T extends {
12
- 503: any;
18
+ 503: unknown;
13
19
  } ? T[503] : T extends {
14
- 504: any;
20
+ 504: unknown;
15
21
  } ? T[504] : T extends {
16
- 505: any;
22
+ 505: unknown;
17
23
  } ? T[505] : T extends {
18
- 506: any;
24
+ 506: unknown;
19
25
  } ? T[506] : T extends {
20
- 507: any;
26
+ 507: unknown;
21
27
  } ? T[507] : T extends {
22
- 508: any;
28
+ 508: unknown;
23
29
  } ? T[508] : T extends {
24
- 510: any;
30
+ 510: unknown;
25
31
  } ? T[510] : T extends {
26
- 511: any;
32
+ 511: unknown;
27
33
  } ? T[511] : T extends {
28
- "5XX": any;
34
+ "5XX": unknown;
29
35
  } ? T["5XX"] : T extends {
30
- 400: any;
36
+ 400: unknown;
31
37
  } ? T[400] : T extends {
32
- 401: any;
38
+ 401: unknown;
33
39
  } ? T[401] : T extends {
34
- 402: any;
40
+ 402: unknown;
35
41
  } ? T[402] : T extends {
36
- 403: any;
42
+ 403: unknown;
37
43
  } ? T[403] : T extends {
38
- 404: any;
44
+ 404: unknown;
39
45
  } ? T[404] : T extends {
40
- 405: any;
46
+ 405: unknown;
41
47
  } ? T[405] : T extends {
42
- 406: any;
48
+ 406: unknown;
43
49
  } ? T[406] : T extends {
44
- 407: any;
50
+ 407: unknown;
45
51
  } ? T[407] : T extends {
46
- 408: any;
52
+ 408: unknown;
47
53
  } ? T[408] : T extends {
48
- 409: any;
54
+ 409: unknown;
49
55
  } ? T[409] : T extends {
50
- 410: any;
56
+ 410: unknown;
51
57
  } ? T[410] : T extends {
52
- 411: any;
58
+ 411: unknown;
53
59
  } ? T[411] : T extends {
54
- 412: any;
60
+ 412: unknown;
55
61
  } ? T[412] : T extends {
56
- 413: any;
62
+ 413: unknown;
57
63
  } ? T[413] : T extends {
58
- 414: any;
64
+ 414: unknown;
59
65
  } ? T[414] : T extends {
60
- 415: any;
66
+ 415: unknown;
61
67
  } ? T[415] : T extends {
62
- 416: any;
68
+ 416: unknown;
63
69
  } ? T[416] : T extends {
64
- 417: any;
70
+ 417: unknown;
65
71
  } ? T[417] : T extends {
66
- 418: any;
72
+ 418: unknown;
67
73
  } ? T[418] : T extends {
68
- 420: any;
74
+ 420: unknown;
69
75
  } ? T[420] : T extends {
70
- 421: any;
76
+ 421: unknown;
71
77
  } ? T[421] : T extends {
72
- 422: any;
78
+ 422: unknown;
73
79
  } ? T[422] : T extends {
74
- 423: any;
80
+ 423: unknown;
75
81
  } ? T[423] : T extends {
76
- 424: any;
82
+ 424: unknown;
77
83
  } ? T[424] : T extends {
78
- 425: any;
84
+ 425: unknown;
79
85
  } ? T[425] : T extends {
80
- 426: any;
86
+ 426: unknown;
81
87
  } ? T[426] : T extends {
82
- 427: any;
88
+ 427: unknown;
83
89
  } ? T[427] : T extends {
84
- 428: any;
90
+ 428: unknown;
85
91
  } ? T[428] : T extends {
86
- 429: any;
92
+ 429: unknown;
87
93
  } ? T[429] : T extends {
88
- 430: any;
94
+ 430: unknown;
89
95
  } ? T[430] : T extends {
90
- 431: any;
96
+ 431: unknown;
91
97
  } ? T[431] : T extends {
92
- 444: any;
98
+ 444: unknown;
93
99
  } ? T[444] : T extends {
94
- 450: any;
100
+ 450: unknown;
95
101
  } ? T[450] : T extends {
96
- 451: any;
102
+ 451: unknown;
97
103
  } ? T[451] : T extends {
98
- 497: any;
104
+ 497: unknown;
99
105
  } ? T[497] : T extends {
100
- 498: any;
106
+ 498: unknown;
101
107
  } ? T[498] : T extends {
102
- 499: any;
108
+ 499: unknown;
103
109
  } ? T[499] : T extends {
104
- "4XX": any;
110
+ "4XX": unknown;
105
111
  } ? T["4XX"] : T extends {
106
- default: any;
112
+ default: unknown;
107
113
  } ? T["default"] : never;
108
- type PathsWithMethod<Paths extends {}, PathnameMethod extends HttpMethod> = {
114
+ /** The paths that have each of the methods: upstream's `PathsWithMethod`. */
115
+ type PathsWithEveryMethod<Paths extends {}, PathnameMethod extends HttpMethod> = {
109
116
  [Pathname in keyof Paths]: Paths[Pathname] extends {
110
- [K in PathnameMethod]: any;
117
+ [K in PathnameMethod]: unknown;
111
118
  } ? Pathname : never;
112
119
  }[keyof Paths];
120
+ /** The paths that have at least one method. */
121
+ type PathsWithAnyMethod<Paths extends {}> = {
122
+ [Pathname in keyof Paths]: {
123
+ [Method in HttpMethod]: Paths[Pathname] extends {
124
+ [K in Method]: unknown;
125
+ } ? Pathname : never;
126
+ }[HttpMethod];
127
+ }[keyof Paths];
128
+ /**
129
+ * Given an OpenAPI **Paths Object**, find all paths that have the given method. For a union of
130
+ * methods, the paths that have each of them, so a call with either method finds its operation.
131
+ * For `HttpMethod` itself, a method not known yet, the paths that have any method too: TypeScript
132
+ * uses it where a hook's `Method` is not inferred, as in `Parameters<typeof $api.queryOptions>`,
133
+ * and the paths with every method were none (upstream #2178).
134
+ *
135
+ * A union with upstream's type, not a conditional type over the method: code that is generic in
136
+ * the method (`Path extends PathsWithMethod<Paths, Method>`, as swr-openapi has) must still
137
+ * relate one `PathsWithMethod` to another, and TypeScript cannot relate a type to a conditional
138
+ * type that waits for its method (TS2344). For a known method the second member is `never`.
139
+ */
140
+ type PathsWithMethod<Paths extends {}, PathnameMethod extends HttpMethod> = PathsWithEveryMethod<Paths, PathnameMethod> | (HttpMethod extends PathnameMethod ? PathsWithAnyMethod<Paths> : never);
141
+ /**
142
+ * DO NOT USE!
143
+ * Only used only for OperationObject type inference
144
+ */
113
145
  interface OperationObject {
114
146
  parameters: any;
115
147
  requestBody: any;
116
148
  responses: any;
117
149
  }
150
+ /** Internal helper used in PathsWithMethod */
118
151
  type PathItemObject = {
119
152
  [M in HttpMethod]: OperationObject;
120
153
  } & {
121
154
  parameters?: any;
122
155
  };
156
+ /** Return `responses` for an Operation Object */
123
157
  type ResponseObjectMap<T> = T extends {
124
158
  responses: any;
125
159
  } ? T["responses"] : unknown;
160
+ /** Return `content` for a Response Object */
126
161
  type ResponseContent<T> = T extends {
127
162
  content: any;
128
163
  } ? T["content"] : unknown;
164
+ /** Return type of `requestBody` for an Operation Object */
129
165
  type OperationRequestBody<T> = "requestBody" extends keyof T ? T["requestBody"] : never;
166
+ /** Internal helper to get object type with only the `requestBody` property */
130
167
  type PickRequestBody<T> = "requestBody" extends keyof T ? Pick<T, "requestBody"> : never;
168
+ /** Resolve to `true` if request body is optional, else `false` */
131
169
  type IsOperationRequestBodyOptional<T> = RequiredKeysOf<PickRequestBody<T>> extends never ? true : false;
170
+ /** Internal helper used in OperationRequestBodyContent */
132
171
  type OperationRequestBodyMediaContent<T> = IsOperationRequestBodyOptional<T> extends true ? ResponseContent<NonNullable<OperationRequestBody<T>>> | undefined : ResponseContent<OperationRequestBody<T>>;
172
+ /** Return first `content` from a Request Object Mapping, allowing any media type */
133
173
  type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaContent<T>, MediaType> extends never ? FilterKeys<NonNullable<OperationRequestBodyMediaContent<T>>, MediaType> | undefined : FilterKeys<OperationRequestBodyMediaContent<T>, MediaType>;
134
- type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, OkStatus>;
174
+ /**
175
+ * The statuses of success responses in a Response Object Map: the 2XX statuses, or `default` when
176
+ * no 2XX status is declared. `default` covers every status the operation does not declare (OpenAPI
177
+ * Responses Object), so then it covers every success (upstream #1862). With a 2XX status declared,
178
+ * `default` is left to the errors, as before.
179
+ */
180
+ type SuccessStatus<T> = [Extract<keyof T, OkStatus>] extends [never] ? "default" : OkStatus;
181
+ /** Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one */
182
+ type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, SuccessStatus<T>>;
135
183
  type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends keyof T ? {
136
184
  [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;
137
185
  }[ResponseCode] : never;
186
+ /**
187
+ * Return all 5XX and 4XX responses (in that order) from a Response Object Map
188
+ */
138
189
  type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, ErrorStatus>;
190
+ /** Return first JSON-like 2XX response from a path + HTTP method */
139
191
  type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
192
+ /** Return first JSON-like 5XX or 4XX response from a path + HTTP method */
140
193
  type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
194
+ /** Return JSON-like request body from a path + HTTP method */
141
195
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
196
+ /** Find first match of multiple keys */
142
197
  type FilterKeys<Obj, Matchers> = Obj[keyof Obj & Matchers];
143
- type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends any ? FilterKeys<Obj, KeyPattern> extends never ? Default : FilterKeys<Obj, KeyPattern> : never;
198
+ /**
199
+ * @deprecated Use `FilterKeys` instead
200
+ * Get the type of a value of an input object with a given key. If the key is
201
+ * not found, return a default type. Works with unions of objects too.
202
+ */
203
+ type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends unknown ? FilterKeys<Obj, KeyPattern> extends never ? Default : FilterKeys<Obj, KeyPattern> : never;
204
+ /**
205
+ * Return any `[string]/[string]` media type (important because openapi-typesafe-fetch allows any
206
+ * content response, not just JSON-like)
207
+ */
144
208
  type MediaType = `${string}/${string}`;
209
+ /**
210
+ * Return any media type containing "json" (works for "application/json",
211
+ * "application/vnd.api+json", "application/vnd.oai.openapi+json")
212
+ */
145
213
  type JSONLike<T> = FilterKeys<T, `${string}/json`>;
214
+ /**
215
+ * Filter objects that have required keys
216
+ * @deprecated Use `RequiredKeysOf` instead
217
+ */
146
218
  type FindRequiredKeys<T, K extends keyof T> = K extends unknown ? undefined extends T[K] ? never : K : K;
219
+ /**
220
+ * Does this object contain required keys?
221
+ * @deprecated Use `RequiredKeysOf` instead
222
+ */
147
223
  type HasRequiredKeys<T> = FindRequiredKeys<T, keyof T>;
224
+ /**
225
+ * Helper to get the required keys of an object. If no keys are required, will be `undefined` with
226
+ * strictNullChecks enabled, else `never`
227
+ */
148
228
  type RequiredKeysOfHelper<T> = {
149
229
  [K in keyof T]: {} extends Pick<T, K> ? never : K;
150
230
  }[keyof T];
231
+ /** Get the required keys of an object, or `never` if no keys are required */
151
232
  type RequiredKeysOf<T> = RequiredKeysOfHelper<T> extends undefined ? never : RequiredKeysOfHelper<T>;
233
+ /** Marker type for readOnly properties (excluded from request bodies) */
152
234
  type $Read<T> = {
153
235
  readonly $read: T;
154
236
  };
237
+ /** Marker type for writeOnly properties (excluded from response bodies) */
155
238
  type $Write<T> = {
156
239
  readonly $write: T;
157
240
  };
241
+ /** Primitives, also branded ones (`string & { __brand: "Id" }`), which stay unmapped (#2765). */
158
242
  type ReadWritePrimitive = string | number | boolean | bigint | symbol | null | undefined;
243
+ /**
244
+ * Types that stay unmapped. Mapping a function type drops its call signatures, so a `Blob` from
245
+ * `--binary-as-blob` lost its methods (`arrayBuffer()`, `text()`, ...) in response data.
246
+ */
159
247
  type ReadWriteLeaf = ReadWritePrimitive | ((...args: never) => unknown);
248
+ /**
249
+ * Keys of `T` whose value is a `Marker`. A key whose value is only `null` or `undefined` is not
250
+ * marked: `NonNullable` of it is `never`, and `never` extends every marker (upstream #2717). The
251
+ * checks are not distributive (`NonNullable<T[K]>` is not a type parameter), so `never` is seen.
252
+ */
160
253
  type ReadWriteMarkedKeys<T, Marker> = {
161
254
  [K in keyof T]-?: NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never;
162
255
  }[keyof T];
256
+ /**
257
+ * Resolve an array or a tuple element by element. An array uses `T[number]`, not `infer E`: for an
258
+ * intersection of arrays (`allOf`) inference keeps one member and narrowing breaks (upstream
259
+ * #2620). `T` is a plain array when an array of its elements is assignable to it; otherwise it is a
260
+ * tuple (also `[A, ...B[]]`), mapped homomorphically to stay a tuple (upstream #2632). Both keep
261
+ * `readonly` (upstream #2615).
262
+ */
163
263
  type ReadWriteResolveArray<T extends readonly unknown[], Resolved extends "read" | "write"> = T[number][] extends T ? T extends unknown[] ? ReadWriteResolve<T[number], Resolved>[] : readonly ReadWriteResolve<T[number], Resolved>[] : {
164
264
  [K in keyof T]: ReadWriteResolve<T[K], Resolved>;
165
265
  };
166
266
  type ReadWriteResolve<T, Resolved extends "read" | "write"> = Resolved extends "read" ? Readable<T> : Writable<T>;
267
+ /**
268
+ * Resolve type for reading (responses): strips $Write properties, unwraps $Read
269
+ * - $Read<T> → T (readable), continues recursion
270
+ * - $Write<T> → never (excluded from response)
271
+ * - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
272
+ * recursively
273
+ */
167
274
  type Readable<T> = T extends $Write<any> ? never : T extends $Read<infer U> ? Readable<U> : T extends ReadWriteLeaf ? T : T extends readonly unknown[] ? ReadWriteResolveArray<T, "read"> : T extends object ? {
168
275
  [K in keyof T as K extends ReadWriteMarkedKeys<T, $Write<any>> ? never : K]: Readable<T[K]>;
169
276
  } : T;
277
+ /**
278
+ * Resolve type for writing (requests): strips $Read properties, unwraps $Write
279
+ * - $Write<T> → T (writable), continues recursion
280
+ * - $Read<T> → never (excluded from request)
281
+ * - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
282
+ * recursively
283
+ */
170
284
  type Writable<T> = T extends $Read<any> ? never : T extends $Write<infer U> ? Writable<U> : T extends ReadWriteLeaf ? T : T extends readonly unknown[] ? ReadWriteResolveArray<T, "write"> : T extends object ? {
171
285
  [K in keyof T as K extends ReadWriteMarkedKeys<T, $Read<any>> ? never : K]: Writable<T[K]>;
172
286
  } & {
package/dist/index.d.mts CHANGED
@@ -1,172 +1,286 @@
1
- type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace" | "query";
1
+ type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"
2
+ /** OpenAPI 3.2: a safe, idempotent request with a body. */
3
+ | "query";
4
+ /** 2XX statuses */
2
5
  type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
6
+ /** 4XX and 5XX statuses */
3
7
  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";
8
+ /** Get a union of OK Statuses */
4
9
  type OKStatusUnion<T> = FilterKeys<T, OkStatus>;
10
+ /** Get first error status, in order */
5
11
  type FirstErrorStatus<T> = T extends {
6
- 500: any;
12
+ 500: unknown;
7
13
  } ? T[500] : T extends {
8
- 501: any;
14
+ 501: unknown;
9
15
  } ? T[501] : T extends {
10
- 502: any;
16
+ 502: unknown;
11
17
  } ? T[502] : T extends {
12
- 503: any;
18
+ 503: unknown;
13
19
  } ? T[503] : T extends {
14
- 504: any;
20
+ 504: unknown;
15
21
  } ? T[504] : T extends {
16
- 505: any;
22
+ 505: unknown;
17
23
  } ? T[505] : T extends {
18
- 506: any;
24
+ 506: unknown;
19
25
  } ? T[506] : T extends {
20
- 507: any;
26
+ 507: unknown;
21
27
  } ? T[507] : T extends {
22
- 508: any;
28
+ 508: unknown;
23
29
  } ? T[508] : T extends {
24
- 510: any;
30
+ 510: unknown;
25
31
  } ? T[510] : T extends {
26
- 511: any;
32
+ 511: unknown;
27
33
  } ? T[511] : T extends {
28
- "5XX": any;
34
+ "5XX": unknown;
29
35
  } ? T["5XX"] : T extends {
30
- 400: any;
36
+ 400: unknown;
31
37
  } ? T[400] : T extends {
32
- 401: any;
38
+ 401: unknown;
33
39
  } ? T[401] : T extends {
34
- 402: any;
40
+ 402: unknown;
35
41
  } ? T[402] : T extends {
36
- 403: any;
42
+ 403: unknown;
37
43
  } ? T[403] : T extends {
38
- 404: any;
44
+ 404: unknown;
39
45
  } ? T[404] : T extends {
40
- 405: any;
46
+ 405: unknown;
41
47
  } ? T[405] : T extends {
42
- 406: any;
48
+ 406: unknown;
43
49
  } ? T[406] : T extends {
44
- 407: any;
50
+ 407: unknown;
45
51
  } ? T[407] : T extends {
46
- 408: any;
52
+ 408: unknown;
47
53
  } ? T[408] : T extends {
48
- 409: any;
54
+ 409: unknown;
49
55
  } ? T[409] : T extends {
50
- 410: any;
56
+ 410: unknown;
51
57
  } ? T[410] : T extends {
52
- 411: any;
58
+ 411: unknown;
53
59
  } ? T[411] : T extends {
54
- 412: any;
60
+ 412: unknown;
55
61
  } ? T[412] : T extends {
56
- 413: any;
62
+ 413: unknown;
57
63
  } ? T[413] : T extends {
58
- 414: any;
64
+ 414: unknown;
59
65
  } ? T[414] : T extends {
60
- 415: any;
66
+ 415: unknown;
61
67
  } ? T[415] : T extends {
62
- 416: any;
68
+ 416: unknown;
63
69
  } ? T[416] : T extends {
64
- 417: any;
70
+ 417: unknown;
65
71
  } ? T[417] : T extends {
66
- 418: any;
72
+ 418: unknown;
67
73
  } ? T[418] : T extends {
68
- 420: any;
74
+ 420: unknown;
69
75
  } ? T[420] : T extends {
70
- 421: any;
76
+ 421: unknown;
71
77
  } ? T[421] : T extends {
72
- 422: any;
78
+ 422: unknown;
73
79
  } ? T[422] : T extends {
74
- 423: any;
80
+ 423: unknown;
75
81
  } ? T[423] : T extends {
76
- 424: any;
82
+ 424: unknown;
77
83
  } ? T[424] : T extends {
78
- 425: any;
84
+ 425: unknown;
79
85
  } ? T[425] : T extends {
80
- 426: any;
86
+ 426: unknown;
81
87
  } ? T[426] : T extends {
82
- 427: any;
88
+ 427: unknown;
83
89
  } ? T[427] : T extends {
84
- 428: any;
90
+ 428: unknown;
85
91
  } ? T[428] : T extends {
86
- 429: any;
92
+ 429: unknown;
87
93
  } ? T[429] : T extends {
88
- 430: any;
94
+ 430: unknown;
89
95
  } ? T[430] : T extends {
90
- 431: any;
96
+ 431: unknown;
91
97
  } ? T[431] : T extends {
92
- 444: any;
98
+ 444: unknown;
93
99
  } ? T[444] : T extends {
94
- 450: any;
100
+ 450: unknown;
95
101
  } ? T[450] : T extends {
96
- 451: any;
102
+ 451: unknown;
97
103
  } ? T[451] : T extends {
98
- 497: any;
104
+ 497: unknown;
99
105
  } ? T[497] : T extends {
100
- 498: any;
106
+ 498: unknown;
101
107
  } ? T[498] : T extends {
102
- 499: any;
108
+ 499: unknown;
103
109
  } ? T[499] : T extends {
104
- "4XX": any;
110
+ "4XX": unknown;
105
111
  } ? T["4XX"] : T extends {
106
- default: any;
112
+ default: unknown;
107
113
  } ? T["default"] : never;
108
- type PathsWithMethod<Paths extends {}, PathnameMethod extends HttpMethod> = {
114
+ /** The paths that have each of the methods: upstream's `PathsWithMethod`. */
115
+ type PathsWithEveryMethod<Paths extends {}, PathnameMethod extends HttpMethod> = {
109
116
  [Pathname in keyof Paths]: Paths[Pathname] extends {
110
- [K in PathnameMethod]: any;
117
+ [K in PathnameMethod]: unknown;
111
118
  } ? Pathname : never;
112
119
  }[keyof Paths];
120
+ /** The paths that have at least one method. */
121
+ type PathsWithAnyMethod<Paths extends {}> = {
122
+ [Pathname in keyof Paths]: {
123
+ [Method in HttpMethod]: Paths[Pathname] extends {
124
+ [K in Method]: unknown;
125
+ } ? Pathname : never;
126
+ }[HttpMethod];
127
+ }[keyof Paths];
128
+ /**
129
+ * Given an OpenAPI **Paths Object**, find all paths that have the given method. For a union of
130
+ * methods, the paths that have each of them, so a call with either method finds its operation.
131
+ * For `HttpMethod` itself, a method not known yet, the paths that have any method too: TypeScript
132
+ * uses it where a hook's `Method` is not inferred, as in `Parameters<typeof $api.queryOptions>`,
133
+ * and the paths with every method were none (upstream #2178).
134
+ *
135
+ * A union with upstream's type, not a conditional type over the method: code that is generic in
136
+ * the method (`Path extends PathsWithMethod<Paths, Method>`, as swr-openapi has) must still
137
+ * relate one `PathsWithMethod` to another, and TypeScript cannot relate a type to a conditional
138
+ * type that waits for its method (TS2344). For a known method the second member is `never`.
139
+ */
140
+ type PathsWithMethod<Paths extends {}, PathnameMethod extends HttpMethod> = PathsWithEveryMethod<Paths, PathnameMethod> | (HttpMethod extends PathnameMethod ? PathsWithAnyMethod<Paths> : never);
141
+ /**
142
+ * DO NOT USE!
143
+ * Only used only for OperationObject type inference
144
+ */
113
145
  interface OperationObject {
114
146
  parameters: any;
115
147
  requestBody: any;
116
148
  responses: any;
117
149
  }
150
+ /** Internal helper used in PathsWithMethod */
118
151
  type PathItemObject = {
119
152
  [M in HttpMethod]: OperationObject;
120
153
  } & {
121
154
  parameters?: any;
122
155
  };
156
+ /** Return `responses` for an Operation Object */
123
157
  type ResponseObjectMap<T> = T extends {
124
158
  responses: any;
125
159
  } ? T["responses"] : unknown;
160
+ /** Return `content` for a Response Object */
126
161
  type ResponseContent<T> = T extends {
127
162
  content: any;
128
163
  } ? T["content"] : unknown;
164
+ /** Return type of `requestBody` for an Operation Object */
129
165
  type OperationRequestBody<T> = "requestBody" extends keyof T ? T["requestBody"] : never;
166
+ /** Internal helper to get object type with only the `requestBody` property */
130
167
  type PickRequestBody<T> = "requestBody" extends keyof T ? Pick<T, "requestBody"> : never;
168
+ /** Resolve to `true` if request body is optional, else `false` */
131
169
  type IsOperationRequestBodyOptional<T> = RequiredKeysOf<PickRequestBody<T>> extends never ? true : false;
170
+ /** Internal helper used in OperationRequestBodyContent */
132
171
  type OperationRequestBodyMediaContent<T> = IsOperationRequestBodyOptional<T> extends true ? ResponseContent<NonNullable<OperationRequestBody<T>>> | undefined : ResponseContent<OperationRequestBody<T>>;
172
+ /** Return first `content` from a Request Object Mapping, allowing any media type */
133
173
  type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaContent<T>, MediaType> extends never ? FilterKeys<NonNullable<OperationRequestBodyMediaContent<T>>, MediaType> | undefined : FilterKeys<OperationRequestBodyMediaContent<T>, MediaType>;
134
- type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, OkStatus>;
174
+ /**
175
+ * The statuses of success responses in a Response Object Map: the 2XX statuses, or `default` when
176
+ * no 2XX status is declared. `default` covers every status the operation does not declare (OpenAPI
177
+ * Responses Object), so then it covers every success (upstream #1862). With a 2XX status declared,
178
+ * `default` is left to the errors, as before.
179
+ */
180
+ type SuccessStatus<T> = [Extract<keyof T, OkStatus>] extends [never] ? "default" : OkStatus;
181
+ /** Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one */
182
+ type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, SuccessStatus<T>>;
135
183
  type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends keyof T ? {
136
184
  [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;
137
185
  }[ResponseCode] : never;
186
+ /**
187
+ * Return all 5XX and 4XX responses (in that order) from a Response Object Map
188
+ */
138
189
  type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, ErrorStatus>;
190
+ /** Return first JSON-like 2XX response from a path + HTTP method */
139
191
  type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
192
+ /** Return first JSON-like 5XX or 4XX response from a path + HTTP method */
140
193
  type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
194
+ /** Return JSON-like request body from a path + HTTP method */
141
195
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
196
+ /** Find first match of multiple keys */
142
197
  type FilterKeys<Obj, Matchers> = Obj[keyof Obj & Matchers];
143
- type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends any ? FilterKeys<Obj, KeyPattern> extends never ? Default : FilterKeys<Obj, KeyPattern> : never;
198
+ /**
199
+ * @deprecated Use `FilterKeys` instead
200
+ * Get the type of a value of an input object with a given key. If the key is
201
+ * not found, return a default type. Works with unions of objects too.
202
+ */
203
+ type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends unknown ? FilterKeys<Obj, KeyPattern> extends never ? Default : FilterKeys<Obj, KeyPattern> : never;
204
+ /**
205
+ * Return any `[string]/[string]` media type (important because openapi-typesafe-fetch allows any
206
+ * content response, not just JSON-like)
207
+ */
144
208
  type MediaType = `${string}/${string}`;
209
+ /**
210
+ * Return any media type containing "json" (works for "application/json",
211
+ * "application/vnd.api+json", "application/vnd.oai.openapi+json")
212
+ */
145
213
  type JSONLike<T> = FilterKeys<T, `${string}/json`>;
214
+ /**
215
+ * Filter objects that have required keys
216
+ * @deprecated Use `RequiredKeysOf` instead
217
+ */
146
218
  type FindRequiredKeys<T, K extends keyof T> = K extends unknown ? undefined extends T[K] ? never : K : K;
219
+ /**
220
+ * Does this object contain required keys?
221
+ * @deprecated Use `RequiredKeysOf` instead
222
+ */
147
223
  type HasRequiredKeys<T> = FindRequiredKeys<T, keyof T>;
224
+ /**
225
+ * Helper to get the required keys of an object. If no keys are required, will be `undefined` with
226
+ * strictNullChecks enabled, else `never`
227
+ */
148
228
  type RequiredKeysOfHelper<T> = {
149
229
  [K in keyof T]: {} extends Pick<T, K> ? never : K;
150
230
  }[keyof T];
231
+ /** Get the required keys of an object, or `never` if no keys are required */
151
232
  type RequiredKeysOf<T> = RequiredKeysOfHelper<T> extends undefined ? never : RequiredKeysOfHelper<T>;
233
+ /** Marker type for readOnly properties (excluded from request bodies) */
152
234
  type $Read<T> = {
153
235
  readonly $read: T;
154
236
  };
237
+ /** Marker type for writeOnly properties (excluded from response bodies) */
155
238
  type $Write<T> = {
156
239
  readonly $write: T;
157
240
  };
241
+ /** Primitives, also branded ones (`string & { __brand: "Id" }`), which stay unmapped (#2765). */
158
242
  type ReadWritePrimitive = string | number | boolean | bigint | symbol | null | undefined;
243
+ /**
244
+ * Types that stay unmapped. Mapping a function type drops its call signatures, so a `Blob` from
245
+ * `--binary-as-blob` lost its methods (`arrayBuffer()`, `text()`, ...) in response data.
246
+ */
159
247
  type ReadWriteLeaf = ReadWritePrimitive | ((...args: never) => unknown);
248
+ /**
249
+ * Keys of `T` whose value is a `Marker`. A key whose value is only `null` or `undefined` is not
250
+ * marked: `NonNullable` of it is `never`, and `never` extends every marker (upstream #2717). The
251
+ * checks are not distributive (`NonNullable<T[K]>` is not a type parameter), so `never` is seen.
252
+ */
160
253
  type ReadWriteMarkedKeys<T, Marker> = {
161
254
  [K in keyof T]-?: NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never;
162
255
  }[keyof T];
256
+ /**
257
+ * Resolve an array or a tuple element by element. An array uses `T[number]`, not `infer E`: for an
258
+ * intersection of arrays (`allOf`) inference keeps one member and narrowing breaks (upstream
259
+ * #2620). `T` is a plain array when an array of its elements is assignable to it; otherwise it is a
260
+ * tuple (also `[A, ...B[]]`), mapped homomorphically to stay a tuple (upstream #2632). Both keep
261
+ * `readonly` (upstream #2615).
262
+ */
163
263
  type ReadWriteResolveArray<T extends readonly unknown[], Resolved extends "read" | "write"> = T[number][] extends T ? T extends unknown[] ? ReadWriteResolve<T[number], Resolved>[] : readonly ReadWriteResolve<T[number], Resolved>[] : {
164
264
  [K in keyof T]: ReadWriteResolve<T[K], Resolved>;
165
265
  };
166
266
  type ReadWriteResolve<T, Resolved extends "read" | "write"> = Resolved extends "read" ? Readable<T> : Writable<T>;
267
+ /**
268
+ * Resolve type for reading (responses): strips $Write properties, unwraps $Read
269
+ * - $Read<T> → T (readable), continues recursion
270
+ * - $Write<T> → never (excluded from response)
271
+ * - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
272
+ * recursively
273
+ */
167
274
  type Readable<T> = T extends $Write<any> ? never : T extends $Read<infer U> ? Readable<U> : T extends ReadWriteLeaf ? T : T extends readonly unknown[] ? ReadWriteResolveArray<T, "read"> : T extends object ? {
168
275
  [K in keyof T as K extends ReadWriteMarkedKeys<T, $Write<any>> ? never : K]: Readable<T[K]>;
169
276
  } : T;
277
+ /**
278
+ * Resolve type for writing (requests): strips $Read properties, unwraps $Write
279
+ * - $Write<T> → T (writable), continues recursion
280
+ * - $Read<T> → never (excluded from request)
281
+ * - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
282
+ * recursively
283
+ */
170
284
  type Writable<T> = T extends $Read<any> ? never : T extends $Write<infer U> ? Writable<U> : T extends ReadWriteLeaf ? T : T extends readonly unknown[] ? ReadWriteResolveArray<T, "write"> : T extends object ? {
171
285
  [K in keyof T as K extends ReadWriteMarkedKeys<T, $Read<any>> ? never : K]: Writable<T[K]>;
172
286
  } & {
package/dist/index.d.ts CHANGED
@@ -1,172 +1,286 @@
1
- type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace" | "query";
1
+ type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"
2
+ /** OpenAPI 3.2: a safe, idempotent request with a body. */
3
+ | "query";
4
+ /** 2XX statuses */
2
5
  type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
6
+ /** 4XX and 5XX statuses */
3
7
  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";
8
+ /** Get a union of OK Statuses */
4
9
  type OKStatusUnion<T> = FilterKeys<T, OkStatus>;
10
+ /** Get first error status, in order */
5
11
  type FirstErrorStatus<T> = T extends {
6
- 500: any;
12
+ 500: unknown;
7
13
  } ? T[500] : T extends {
8
- 501: any;
14
+ 501: unknown;
9
15
  } ? T[501] : T extends {
10
- 502: any;
16
+ 502: unknown;
11
17
  } ? T[502] : T extends {
12
- 503: any;
18
+ 503: unknown;
13
19
  } ? T[503] : T extends {
14
- 504: any;
20
+ 504: unknown;
15
21
  } ? T[504] : T extends {
16
- 505: any;
22
+ 505: unknown;
17
23
  } ? T[505] : T extends {
18
- 506: any;
24
+ 506: unknown;
19
25
  } ? T[506] : T extends {
20
- 507: any;
26
+ 507: unknown;
21
27
  } ? T[507] : T extends {
22
- 508: any;
28
+ 508: unknown;
23
29
  } ? T[508] : T extends {
24
- 510: any;
30
+ 510: unknown;
25
31
  } ? T[510] : T extends {
26
- 511: any;
32
+ 511: unknown;
27
33
  } ? T[511] : T extends {
28
- "5XX": any;
34
+ "5XX": unknown;
29
35
  } ? T["5XX"] : T extends {
30
- 400: any;
36
+ 400: unknown;
31
37
  } ? T[400] : T extends {
32
- 401: any;
38
+ 401: unknown;
33
39
  } ? T[401] : T extends {
34
- 402: any;
40
+ 402: unknown;
35
41
  } ? T[402] : T extends {
36
- 403: any;
42
+ 403: unknown;
37
43
  } ? T[403] : T extends {
38
- 404: any;
44
+ 404: unknown;
39
45
  } ? T[404] : T extends {
40
- 405: any;
46
+ 405: unknown;
41
47
  } ? T[405] : T extends {
42
- 406: any;
48
+ 406: unknown;
43
49
  } ? T[406] : T extends {
44
- 407: any;
50
+ 407: unknown;
45
51
  } ? T[407] : T extends {
46
- 408: any;
52
+ 408: unknown;
47
53
  } ? T[408] : T extends {
48
- 409: any;
54
+ 409: unknown;
49
55
  } ? T[409] : T extends {
50
- 410: any;
56
+ 410: unknown;
51
57
  } ? T[410] : T extends {
52
- 411: any;
58
+ 411: unknown;
53
59
  } ? T[411] : T extends {
54
- 412: any;
60
+ 412: unknown;
55
61
  } ? T[412] : T extends {
56
- 413: any;
62
+ 413: unknown;
57
63
  } ? T[413] : T extends {
58
- 414: any;
64
+ 414: unknown;
59
65
  } ? T[414] : T extends {
60
- 415: any;
66
+ 415: unknown;
61
67
  } ? T[415] : T extends {
62
- 416: any;
68
+ 416: unknown;
63
69
  } ? T[416] : T extends {
64
- 417: any;
70
+ 417: unknown;
65
71
  } ? T[417] : T extends {
66
- 418: any;
72
+ 418: unknown;
67
73
  } ? T[418] : T extends {
68
- 420: any;
74
+ 420: unknown;
69
75
  } ? T[420] : T extends {
70
- 421: any;
76
+ 421: unknown;
71
77
  } ? T[421] : T extends {
72
- 422: any;
78
+ 422: unknown;
73
79
  } ? T[422] : T extends {
74
- 423: any;
80
+ 423: unknown;
75
81
  } ? T[423] : T extends {
76
- 424: any;
82
+ 424: unknown;
77
83
  } ? T[424] : T extends {
78
- 425: any;
84
+ 425: unknown;
79
85
  } ? T[425] : T extends {
80
- 426: any;
86
+ 426: unknown;
81
87
  } ? T[426] : T extends {
82
- 427: any;
88
+ 427: unknown;
83
89
  } ? T[427] : T extends {
84
- 428: any;
90
+ 428: unknown;
85
91
  } ? T[428] : T extends {
86
- 429: any;
92
+ 429: unknown;
87
93
  } ? T[429] : T extends {
88
- 430: any;
94
+ 430: unknown;
89
95
  } ? T[430] : T extends {
90
- 431: any;
96
+ 431: unknown;
91
97
  } ? T[431] : T extends {
92
- 444: any;
98
+ 444: unknown;
93
99
  } ? T[444] : T extends {
94
- 450: any;
100
+ 450: unknown;
95
101
  } ? T[450] : T extends {
96
- 451: any;
102
+ 451: unknown;
97
103
  } ? T[451] : T extends {
98
- 497: any;
104
+ 497: unknown;
99
105
  } ? T[497] : T extends {
100
- 498: any;
106
+ 498: unknown;
101
107
  } ? T[498] : T extends {
102
- 499: any;
108
+ 499: unknown;
103
109
  } ? T[499] : T extends {
104
- "4XX": any;
110
+ "4XX": unknown;
105
111
  } ? T["4XX"] : T extends {
106
- default: any;
112
+ default: unknown;
107
113
  } ? T["default"] : never;
108
- type PathsWithMethod<Paths extends {}, PathnameMethod extends HttpMethod> = {
114
+ /** The paths that have each of the methods: upstream's `PathsWithMethod`. */
115
+ type PathsWithEveryMethod<Paths extends {}, PathnameMethod extends HttpMethod> = {
109
116
  [Pathname in keyof Paths]: Paths[Pathname] extends {
110
- [K in PathnameMethod]: any;
117
+ [K in PathnameMethod]: unknown;
111
118
  } ? Pathname : never;
112
119
  }[keyof Paths];
120
+ /** The paths that have at least one method. */
121
+ type PathsWithAnyMethod<Paths extends {}> = {
122
+ [Pathname in keyof Paths]: {
123
+ [Method in HttpMethod]: Paths[Pathname] extends {
124
+ [K in Method]: unknown;
125
+ } ? Pathname : never;
126
+ }[HttpMethod];
127
+ }[keyof Paths];
128
+ /**
129
+ * Given an OpenAPI **Paths Object**, find all paths that have the given method. For a union of
130
+ * methods, the paths that have each of them, so a call with either method finds its operation.
131
+ * For `HttpMethod` itself, a method not known yet, the paths that have any method too: TypeScript
132
+ * uses it where a hook's `Method` is not inferred, as in `Parameters<typeof $api.queryOptions>`,
133
+ * and the paths with every method were none (upstream #2178).
134
+ *
135
+ * A union with upstream's type, not a conditional type over the method: code that is generic in
136
+ * the method (`Path extends PathsWithMethod<Paths, Method>`, as swr-openapi has) must still
137
+ * relate one `PathsWithMethod` to another, and TypeScript cannot relate a type to a conditional
138
+ * type that waits for its method (TS2344). For a known method the second member is `never`.
139
+ */
140
+ type PathsWithMethod<Paths extends {}, PathnameMethod extends HttpMethod> = PathsWithEveryMethod<Paths, PathnameMethod> | (HttpMethod extends PathnameMethod ? PathsWithAnyMethod<Paths> : never);
141
+ /**
142
+ * DO NOT USE!
143
+ * Only used only for OperationObject type inference
144
+ */
113
145
  interface OperationObject {
114
146
  parameters: any;
115
147
  requestBody: any;
116
148
  responses: any;
117
149
  }
150
+ /** Internal helper used in PathsWithMethod */
118
151
  type PathItemObject = {
119
152
  [M in HttpMethod]: OperationObject;
120
153
  } & {
121
154
  parameters?: any;
122
155
  };
156
+ /** Return `responses` for an Operation Object */
123
157
  type ResponseObjectMap<T> = T extends {
124
158
  responses: any;
125
159
  } ? T["responses"] : unknown;
160
+ /** Return `content` for a Response Object */
126
161
  type ResponseContent<T> = T extends {
127
162
  content: any;
128
163
  } ? T["content"] : unknown;
164
+ /** Return type of `requestBody` for an Operation Object */
129
165
  type OperationRequestBody<T> = "requestBody" extends keyof T ? T["requestBody"] : never;
166
+ /** Internal helper to get object type with only the `requestBody` property */
130
167
  type PickRequestBody<T> = "requestBody" extends keyof T ? Pick<T, "requestBody"> : never;
168
+ /** Resolve to `true` if request body is optional, else `false` */
131
169
  type IsOperationRequestBodyOptional<T> = RequiredKeysOf<PickRequestBody<T>> extends never ? true : false;
170
+ /** Internal helper used in OperationRequestBodyContent */
132
171
  type OperationRequestBodyMediaContent<T> = IsOperationRequestBodyOptional<T> extends true ? ResponseContent<NonNullable<OperationRequestBody<T>>> | undefined : ResponseContent<OperationRequestBody<T>>;
172
+ /** Return first `content` from a Request Object Mapping, allowing any media type */
133
173
  type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaContent<T>, MediaType> extends never ? FilterKeys<NonNullable<OperationRequestBodyMediaContent<T>>, MediaType> | undefined : FilterKeys<OperationRequestBodyMediaContent<T>, MediaType>;
134
- type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, OkStatus>;
174
+ /**
175
+ * The statuses of success responses in a Response Object Map: the 2XX statuses, or `default` when
176
+ * no 2XX status is declared. `default` covers every status the operation does not declare (OpenAPI
177
+ * Responses Object), so then it covers every success (upstream #1862). With a 2XX status declared,
178
+ * `default` is left to the errors, as before.
179
+ */
180
+ type SuccessStatus<T> = [Extract<keyof T, OkStatus>] extends [never] ? "default" : OkStatus;
181
+ /** Return all 2XX responses from a Response Object Map, or `default` if it declares no 2XX one */
182
+ type SuccessResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, SuccessStatus<T>>;
135
183
  type GetResponseContent<T extends Record<string | number, any>, Media extends MediaType = MediaType, ResponseCode extends keyof T = keyof T> = ResponseCode extends keyof T ? {
136
184
  [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;
137
185
  }[ResponseCode] : never;
186
+ /**
187
+ * Return all 5XX and 4XX responses (in that order) from a Response Object Map
188
+ */
138
189
  type ErrorResponse<T extends Record<string | number, any>, Media extends MediaType = MediaType> = GetResponseContent<T, Media, ErrorStatus>;
190
+ /** Return first JSON-like 2XX response from a path + HTTP method */
139
191
  type SuccessResponseJSON<PathMethod extends Record<string | number, any>> = SuccessResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
192
+ /** Return first JSON-like 5XX or 4XX response from a path + HTTP method */
140
193
  type ErrorResponseJSON<PathMethod extends Record<string | number, any>> = ErrorResponse<ResponseObjectMap<PathMethod>, `${string}/json`>;
194
+ /** Return JSON-like request body from a path + HTTP method */
141
195
  type RequestBodyJSON<PathMethod> = JSONLike<FilterKeys<OperationRequestBody<PathMethod>, "content">>;
196
+ /** Find first match of multiple keys */
142
197
  type FilterKeys<Obj, Matchers> = Obj[keyof Obj & Matchers];
143
- type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends any ? FilterKeys<Obj, KeyPattern> extends never ? Default : FilterKeys<Obj, KeyPattern> : never;
198
+ /**
199
+ * @deprecated Use `FilterKeys` instead
200
+ * Get the type of a value of an input object with a given key. If the key is
201
+ * not found, return a default type. Works with unions of objects too.
202
+ */
203
+ type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends unknown ? FilterKeys<Obj, KeyPattern> extends never ? Default : FilterKeys<Obj, KeyPattern> : never;
204
+ /**
205
+ * Return any `[string]/[string]` media type (important because openapi-typesafe-fetch allows any
206
+ * content response, not just JSON-like)
207
+ */
144
208
  type MediaType = `${string}/${string}`;
209
+ /**
210
+ * Return any media type containing "json" (works for "application/json",
211
+ * "application/vnd.api+json", "application/vnd.oai.openapi+json")
212
+ */
145
213
  type JSONLike<T> = FilterKeys<T, `${string}/json`>;
214
+ /**
215
+ * Filter objects that have required keys
216
+ * @deprecated Use `RequiredKeysOf` instead
217
+ */
146
218
  type FindRequiredKeys<T, K extends keyof T> = K extends unknown ? undefined extends T[K] ? never : K : K;
219
+ /**
220
+ * Does this object contain required keys?
221
+ * @deprecated Use `RequiredKeysOf` instead
222
+ */
147
223
  type HasRequiredKeys<T> = FindRequiredKeys<T, keyof T>;
224
+ /**
225
+ * Helper to get the required keys of an object. If no keys are required, will be `undefined` with
226
+ * strictNullChecks enabled, else `never`
227
+ */
148
228
  type RequiredKeysOfHelper<T> = {
149
229
  [K in keyof T]: {} extends Pick<T, K> ? never : K;
150
230
  }[keyof T];
231
+ /** Get the required keys of an object, or `never` if no keys are required */
151
232
  type RequiredKeysOf<T> = RequiredKeysOfHelper<T> extends undefined ? never : RequiredKeysOfHelper<T>;
233
+ /** Marker type for readOnly properties (excluded from request bodies) */
152
234
  type $Read<T> = {
153
235
  readonly $read: T;
154
236
  };
237
+ /** Marker type for writeOnly properties (excluded from response bodies) */
155
238
  type $Write<T> = {
156
239
  readonly $write: T;
157
240
  };
241
+ /** Primitives, also branded ones (`string & { __brand: "Id" }`), which stay unmapped (#2765). */
158
242
  type ReadWritePrimitive = string | number | boolean | bigint | symbol | null | undefined;
243
+ /**
244
+ * Types that stay unmapped. Mapping a function type drops its call signatures, so a `Blob` from
245
+ * `--binary-as-blob` lost its methods (`arrayBuffer()`, `text()`, ...) in response data.
246
+ */
159
247
  type ReadWriteLeaf = ReadWritePrimitive | ((...args: never) => unknown);
248
+ /**
249
+ * Keys of `T` whose value is a `Marker`. A key whose value is only `null` or `undefined` is not
250
+ * marked: `NonNullable` of it is `never`, and `never` extends every marker (upstream #2717). The
251
+ * checks are not distributive (`NonNullable<T[K]>` is not a type parameter), so `never` is seen.
252
+ */
160
253
  type ReadWriteMarkedKeys<T, Marker> = {
161
254
  [K in keyof T]-?: NonNullable<T[K]> extends never ? never : NonNullable<T[K]> extends Marker ? K : never;
162
255
  }[keyof T];
256
+ /**
257
+ * Resolve an array or a tuple element by element. An array uses `T[number]`, not `infer E`: for an
258
+ * intersection of arrays (`allOf`) inference keeps one member and narrowing breaks (upstream
259
+ * #2620). `T` is a plain array when an array of its elements is assignable to it; otherwise it is a
260
+ * tuple (also `[A, ...B[]]`), mapped homomorphically to stay a tuple (upstream #2632). Both keep
261
+ * `readonly` (upstream #2615).
262
+ */
163
263
  type ReadWriteResolveArray<T extends readonly unknown[], Resolved extends "read" | "write"> = T[number][] extends T ? T extends unknown[] ? ReadWriteResolve<T[number], Resolved>[] : readonly ReadWriteResolve<T[number], Resolved>[] : {
164
264
  [K in keyof T]: ReadWriteResolve<T[K], Resolved>;
165
265
  };
166
266
  type ReadWriteResolve<T, Resolved extends "read" | "write"> = Resolved extends "read" ? Readable<T> : Writable<T>;
267
+ /**
268
+ * Resolve type for reading (responses): strips $Write properties, unwraps $Read
269
+ * - $Read<T> → T (readable), continues recursion
270
+ * - $Write<T> → never (excluded from response)
271
+ * - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
272
+ * recursively
273
+ */
167
274
  type Readable<T> = T extends $Write<any> ? never : T extends $Read<infer U> ? Readable<U> : T extends ReadWriteLeaf ? T : T extends readonly unknown[] ? ReadWriteResolveArray<T, "read"> : T extends object ? {
168
275
  [K in keyof T as K extends ReadWriteMarkedKeys<T, $Write<any>> ? never : K]: Readable<T[K]>;
169
276
  } : T;
277
+ /**
278
+ * Resolve type for writing (requests): strips $Read properties, unwraps $Write
279
+ * - $Write<T> → T (writable), continues recursion
280
+ * - $Read<T> → never (excluded from request)
281
+ * - primitives (also branded), functions, arrays and tuples keep their shape; objects resolve
282
+ * recursively
283
+ */
170
284
  type Writable<T> = T extends $Read<any> ? never : T extends $Write<infer U> ? Writable<U> : T extends ReadWriteLeaf ? T : T extends readonly unknown[] ? ReadWriteResolveArray<T, "write"> : T extends object ? {
171
285
  [K in keyof T as K extends ReadWriteMarkedKeys<T, $Read<any>> ? never : K]: Writable<T[K]>;
172
286
  } & {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "openapi-typesafe-helpers",
3
3
  "description": "TypeScript helpers for consuming openapi-typesafe types",
4
- "version": "1.0.0-rc.0",
4
+ "version": "1.0.0-rc.2",
5
5
  "author": {
6
6
  "name": "Fedor Chishchin",
7
7
  "url": "https://github.com/fedorello"
@@ -36,7 +36,8 @@
36
36
  "url": "https://github.com/openapi-typesafe/openapi-typesafe/issues"
37
37
  },
38
38
  "devDependencies": {
39
- "typescript": "^6.0.3"
39
+ "typescript": "^6.0.3",
40
+ "vite-node": "6.0.0"
40
41
  },
41
42
  "engines": {
42
43
  "node": ">=22"
@@ -44,9 +45,10 @@
44
45
  "scripts": {
45
46
  "build": "unbuild",
46
47
  "format": "biome format src --write",
47
- "lint": "pnpm run lint:js && pnpm run lint:ts",
48
+ "lint": "pnpm run lint:js && pnpm run lint:ts && pnpm run lint:sizes",
48
49
  "lint:js": "biome check src",
49
50
  "lint:ts": "tsc --noEmit",
51
+ "lint:sizes": "vite-node ../openapi-typesafe/scripts/size-rules/check-sizes.ts src",
50
52
  "test": "pnpm run test:exports",
51
53
  "test:exports": "pnpm run build && ../../scripts/check-exports.sh"
52
54
  }