@lokalise/api-contracts 6.12.0 → 6.13.1
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 +71 -20
- package/dist/HttpStatusCodes.d.ts +38 -1
- package/dist/HttpStatusCodes.js +14 -0
- package/dist/HttpStatusCodes.js.map +1 -1
- package/dist/new/clientTypes.d.ts +55 -8
- package/dist/new/contractResponse.d.ts +4 -3
- package/dist/new/contractResponse.js +30 -5
- package/dist/new/contractResponse.js.map +1 -1
- package/dist/new/defineApiContract.d.ts +7 -7
- package/dist/new/defineApiContract.js +1 -1
- package/dist/new/defineApiContract.js.map +1 -1
- package/dist/new/inferTypes.d.ts +11 -6
- package/dist/typeUtils.d.ts +14 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -121,6 +121,52 @@ const chatCompletion = defineApiContract({
|
|
|
121
121
|
})
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
### Wildcard and default response keys
|
|
125
|
+
|
|
126
|
+
In addition to exact status codes, `responsesByStatusCode` accepts OpenAPI-style range keys (`'1xx'`–`'5xx'`) and `'default'` as fallbacks.
|
|
127
|
+
|
|
128
|
+
Lookup precedence at runtime: **exact code → range key → `'default'`**.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { defineApiContract } from '@lokalise/api-contracts'
|
|
132
|
+
import { z } from 'zod/v4'
|
|
133
|
+
|
|
134
|
+
// '2xx' covers all 200–299 responses
|
|
135
|
+
const listItems = defineApiContract({
|
|
136
|
+
method: 'get',
|
|
137
|
+
pathResolver: () => '/items',
|
|
138
|
+
responsesByStatusCode: {
|
|
139
|
+
'2xx': z.object({ items: z.array(z.string()) }),
|
|
140
|
+
'4xx': z.object({ message: z.string() }),
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// exact code takes precedence over the range key
|
|
145
|
+
const createItem = defineApiContract({
|
|
146
|
+
method: 'post',
|
|
147
|
+
pathResolver: () => '/items',
|
|
148
|
+
requestBodySchema: z.object({ name: z.string() }),
|
|
149
|
+
responsesByStatusCode: {
|
|
150
|
+
201: z.object({ id: z.string() }),
|
|
151
|
+
'4xx': z.object({ message: z.string() }),
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// 'default' matches any status code not covered by a more specific entry
|
|
156
|
+
const flexible = defineApiContract({
|
|
157
|
+
method: 'get',
|
|
158
|
+
pathResolver: () => '/data',
|
|
159
|
+
responsesByStatusCode: {
|
|
160
|
+
200: z.object({ data: z.unknown() }),
|
|
161
|
+
default: z.object({ error: z.string() }),
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The `'2xx'` range key participates in SSE detection and success/error type narrowing exactly like explicit 2xx codes: `InferNonSseClientResponse` maps it to `SuccessfulHttpStatusCode`, and `hasAnySuccessSseResponse` returns `true` when it holds an SSE schema.
|
|
167
|
+
|
|
168
|
+
`'default'` is split into a success half (`SuccessfulHttpStatusCode`) and a non-success half in `InferSseClientResponse` / `InferNonSseClientResponse` so that `captureAsError` type narrowing stays correct regardless of the actual status code.
|
|
169
|
+
|
|
124
170
|
### OpenAPI response descriptions
|
|
125
171
|
|
|
126
172
|
All response factories accept an optional `ResponseOptions` object as their last argument.
|
|
@@ -171,31 +217,36 @@ getSseSchemaByEventName(chatCompletion)
|
|
|
171
217
|
### All fields
|
|
172
218
|
|
|
173
219
|
```ts
|
|
174
|
-
|
|
220
|
+
type ApiContractOptions = {
|
|
175
221
|
// Required
|
|
176
|
-
method: 'get' | 'post' | 'put' | 'patch' | 'delete'
|
|
177
|
-
pathResolver: (pathParams) => string
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
222
|
+
method: 'get' | 'post' | 'put' | 'patch' | 'delete'
|
|
223
|
+
pathResolver: (pathParams: Record<string, string>) => string
|
|
224
|
+
// Accepts exact codes, OpenAPI-style range keys ('1xx'–'5xx'), and a catch-all 'default'.
|
|
225
|
+
// Lookup precedence at runtime: exact code → range key → 'default'.
|
|
226
|
+
responsesByStatusCode: Partial<
|
|
227
|
+
Record<
|
|
228
|
+
HttpStatusCode | '1xx' | '2xx' | '3xx' | '4xx' | '5xx' | 'default',
|
|
229
|
+
z.ZodType | NoBodyResponse | TypedTextResponse | TypedBlobResponse | TypedSseResponse | AnyOfResponses
|
|
230
|
+
>
|
|
231
|
+
>
|
|
181
232
|
|
|
182
233
|
// Path params — links pathResolver parameter type to the schema
|
|
183
|
-
requestPathParamsSchema
|
|
234
|
+
requestPathParamsSchema?: z.ZodObject<z.ZodRawShape>
|
|
184
235
|
|
|
185
236
|
// Request
|
|
186
|
-
requestBodySchema
|
|
187
|
-
requestQuerySchema
|
|
188
|
-
requestHeaderSchema
|
|
237
|
+
requestBodySchema?: z.ZodType | ContractNoBody // required for POST / PUT / PATCH, forbidden otherwise
|
|
238
|
+
requestQuerySchema?: z.ZodObject<z.ZodRawShape>
|
|
239
|
+
requestHeaderSchema?: z.ZodObject<z.ZodRawShape>
|
|
189
240
|
|
|
190
241
|
// Response
|
|
191
|
-
responseHeaderSchema
|
|
242
|
+
responseHeaderSchema?: z.ZodObject<z.ZodRawShape>
|
|
192
243
|
|
|
193
244
|
// Documentation
|
|
194
|
-
summary
|
|
195
|
-
description
|
|
196
|
-
tags
|
|
197
|
-
metadata
|
|
198
|
-
}
|
|
245
|
+
summary?: string
|
|
246
|
+
description?: string
|
|
247
|
+
tags?: readonly string[]
|
|
248
|
+
metadata?: Record<string, unknown>
|
|
249
|
+
}
|
|
199
250
|
```
|
|
200
251
|
|
|
201
252
|
### Header schemas
|
|
@@ -236,7 +287,7 @@ type CsvResponse = InferNonSseSuccessResponses<typeof exportCsv['responsesByStat
|
|
|
236
287
|
|
|
237
288
|
**`InferSseSuccessResponses<T>`** — extracts the SSE event schema map type from a `responsesByStatusCode` map. Returns `never` when no SSE schemas are present.
|
|
238
289
|
|
|
239
|
-
**`HasAnySseSuccessResponse<T>`** — `true` if any 2xx entry is a `TypedSseResponse` or an `AnyOfResponses` containing one.
|
|
290
|
+
**`HasAnySseSuccessResponse<T>`** — `true` if any 2xx entry (exact code or `'2xx'` range key) is a `TypedSseResponse` or an `AnyOfResponses` containing one.
|
|
240
291
|
|
|
241
292
|
**`HasAnyJsonSuccessResponse<T>`** — `true` if any 2xx entry is a JSON Zod schema or an `AnyOfResponses` containing one.
|
|
242
293
|
|
|
@@ -264,9 +315,9 @@ These types are primarily consumed by HTTP client implementations.
|
|
|
264
315
|
|
|
265
316
|
**`ClientRequestParams<TApiContract, TIsStreaming>`** — infers the request parameter object for a contract. Includes `pathParams`, `body`, `queryParams`, `headers` (required when the corresponding schema is defined), `pathPrefix` (always optional), and `streaming` (required for dual-mode contracts, forbidden otherwise).
|
|
266
317
|
|
|
267
|
-
**`InferSseClientResponse<TApiContract>`** — discriminated union of `{ statusCode, headers, body }` for SSE mode.
|
|
318
|
+
**`InferSseClientResponse<TApiContract>`** — discriminated union of `{ statusCode, headers, body }` for SSE mode. Exact 2xx codes and the `'2xx'` range key yield `AsyncIterable<SseEventOf<...>>`; error codes, other range keys, and `'default'` yield the declared body type. `'default'` is split into a `SuccessfulHttpStatusCode` half and a non-success half.
|
|
268
319
|
|
|
269
|
-
**`InferNonSseClientResponse<TApiContract>`** — same shape as above for non-SSE mode.
|
|
320
|
+
**`InferNonSseClientResponse<TApiContract>`** — same shape as above for non-SSE mode. Exact 2xx codes and the `'2xx'` range key yield JSON / `string` / `Blob` / `null` (SSE excluded); error codes, other range keys, and `'default'` yield the declared body type as-is. `'default'` is split the same way.
|
|
270
321
|
|
|
271
322
|
**`DefaultStreaming<T>`** — `true` for SSE-only contracts, `false` for everything else.
|
|
272
323
|
|
|
@@ -324,7 +375,7 @@ getIsEmptyResponseExpected(deleteUser) // true
|
|
|
324
375
|
getIsEmptyResponseExpected(getUser) // false
|
|
325
376
|
```
|
|
326
377
|
|
|
327
|
-
**`hasAnySuccessSseResponse`** — `true` when any 2xx entry is an SSE response (including inside `anyOfResponses`).
|
|
378
|
+
**`hasAnySuccessSseResponse`** — `true` when any 2xx entry (exact code or `'2xx'` range key) is an SSE response (including inside `anyOfResponses`).
|
|
328
379
|
|
|
329
380
|
```ts
|
|
330
381
|
import { hasAnySuccessSseResponse } from '@lokalise/api-contracts'
|
|
@@ -1,3 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
/** Tuple of all 1xx informational HTTP status codes. */
|
|
2
|
+
export declare const INFORMATIONAL_HTTP_STATUS_CODES: readonly [100, 101, 102, 103];
|
|
3
|
+
/** Union of all 1xx informational HTTP status codes. */
|
|
4
|
+
export type InformationalHttpStatusCode = (typeof INFORMATIONAL_HTTP_STATUS_CODES)[number];
|
|
5
|
+
/** Tuple of all 2xx successful HTTP status codes. */
|
|
2
6
|
export declare const SUCCESSFUL_HTTP_STATUS_CODES: readonly [200, 201, 202, 203, 204, 205, 206, 207, 208, 226];
|
|
7
|
+
/** Union of all 2xx successful HTTP status codes. */
|
|
3
8
|
export type SuccessfulHttpStatusCode = (typeof SUCCESSFUL_HTTP_STATUS_CODES)[number];
|
|
9
|
+
/** Tuple of all 3xx redirection HTTP status codes. */
|
|
10
|
+
export declare const REDIRECTION_HTTP_STATUS_CODES: readonly [300, 301, 302, 303, 304, 305, 306, 307, 308];
|
|
11
|
+
/** Union of all 3xx redirection HTTP status codes. */
|
|
12
|
+
export type RedirectionHttpStatusCode = (typeof REDIRECTION_HTTP_STATUS_CODES)[number];
|
|
13
|
+
/** Tuple of all 4xx client-error HTTP status codes. */
|
|
14
|
+
export declare const CLIENT_ERROR_HTTP_STATUS_CODES: readonly [400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 425, 426, 428, 429, 431, 451];
|
|
15
|
+
/** Union of all 4xx client-error HTTP status codes. */
|
|
16
|
+
export type ClientErrorHttpStatusCode = (typeof CLIENT_ERROR_HTTP_STATUS_CODES)[number];
|
|
17
|
+
/** Tuple of all 5xx server-error HTTP status codes. */
|
|
18
|
+
export declare const SERVER_ERROR_HTTP_STATUS_CODES: readonly [500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511];
|
|
19
|
+
/** Union of all 5xx server-error HTTP status codes. */
|
|
20
|
+
export type ServerErrorHttpStatusCode = (typeof SERVER_ERROR_HTTP_STATUS_CODES)[number];
|
|
21
|
+
/** Union of every standard HTTP status code across all classes (1xx–5xx). */
|
|
22
|
+
export type HttpStatusCode = InformationalHttpStatusCode | SuccessfulHttpStatusCode | RedirectionHttpStatusCode | ClientErrorHttpStatusCode | ServerErrorHttpStatusCode;
|
|
23
|
+
/** String representation of an HTTP status class. */
|
|
24
|
+
export type HttpStatusCodeRange = '1xx' | '2xx' | '3xx' | '4xx' | '5xx';
|
|
25
|
+
/** Range key or catch-all fallback. */
|
|
26
|
+
export type WildcardStatusCodeKey = HttpStatusCodeRange | 'default';
|
|
27
|
+
type RangeExpansion = {
|
|
28
|
+
'1xx': InformationalHttpStatusCode;
|
|
29
|
+
'2xx': SuccessfulHttpStatusCode;
|
|
30
|
+
'3xx': RedirectionHttpStatusCode;
|
|
31
|
+
'4xx': ClientErrorHttpStatusCode;
|
|
32
|
+
'5xx': ServerErrorHttpStatusCode;
|
|
33
|
+
default: HttpStatusCode;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Maps a `WildcardStatusCodeKey` to its concrete `HttpStatusCode` union.
|
|
37
|
+
* `'default'` expands to the full `HttpStatusCode` union.
|
|
38
|
+
*/
|
|
39
|
+
export type ExpandStatusRangeKey<K extends WildcardStatusCodeKey> = RangeExpansion[K];
|
|
40
|
+
export {};
|
package/dist/HttpStatusCodes.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
/** Tuple of all 1xx informational HTTP status codes. */
|
|
2
|
+
export const INFORMATIONAL_HTTP_STATUS_CODES = [100, 101, 102, 103];
|
|
3
|
+
/** Tuple of all 2xx successful HTTP status codes. */
|
|
1
4
|
export const SUCCESSFUL_HTTP_STATUS_CODES = [
|
|
2
5
|
200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
|
|
3
6
|
];
|
|
7
|
+
/** Tuple of all 3xx redirection HTTP status codes. */
|
|
8
|
+
export const REDIRECTION_HTTP_STATUS_CODES = [300, 301, 302, 303, 304, 305, 306, 307, 308];
|
|
9
|
+
/** Tuple of all 4xx client-error HTTP status codes. */
|
|
10
|
+
export const CLIENT_ERROR_HTTP_STATUS_CODES = [
|
|
11
|
+
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418,
|
|
12
|
+
421, 422, 423, 424, 425, 426, 428, 429, 431, 451,
|
|
13
|
+
];
|
|
14
|
+
/** Tuple of all 5xx server-error HTTP status codes. */
|
|
15
|
+
export const SERVER_ERROR_HTTP_STATUS_CODES = [
|
|
16
|
+
500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511,
|
|
17
|
+
];
|
|
4
18
|
//# sourceMappingURL=HttpStatusCodes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpStatusCodes.js","sourceRoot":"","sources":["../src/HttpStatusCodes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HttpStatusCodes.js","sourceRoot":"","sources":["../src/HttpStatusCodes.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAA;AAI5E,qDAAqD;AACrD,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;CACxC,CAAA;AAIV,sDAAsD;AACtD,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAA;AAInG,uDAAuD;AACvD,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;IAC7F,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;CACxC,CAAA;AAIV,uDAAuD;AACvD,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;CAC7C,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { z } from 'zod/v4';
|
|
2
2
|
import type { InferSchemaInput, InferSchemaOutput } from '../apiContracts.ts';
|
|
3
|
-
import type { HttpStatusCode, SuccessfulHttpStatusCode } from '../HttpStatusCodes.ts';
|
|
3
|
+
import type { ExpandStatusRangeKey, HttpStatusCode, HttpStatusCodeRange, SuccessfulHttpStatusCode, WildcardStatusCodeKey } from '../HttpStatusCodes.ts';
|
|
4
4
|
import type { Prettify } from '../typeUtils.ts';
|
|
5
5
|
import type { ContractNoBody } from './constants.ts';
|
|
6
6
|
import type { ResponsesByStatusCode, SseSchemaByEventName } from './contractResponse.ts';
|
|
@@ -27,8 +27,11 @@ export type ClientRequestParams<TApiContract extends ApiContract, TIsStreaming e
|
|
|
27
27
|
type InferClientResponseHeaders<TApiContract extends ApiContract> = TApiContract['responseHeaderSchema'] extends z.ZodType ? Omit<Record<string, string>, keyof InferSchemaOutput<TApiContract['responseHeaderSchema']>> & InferSchemaOutput<TApiContract['responseHeaderSchema']> : Record<string, string>;
|
|
28
28
|
/**
|
|
29
29
|
* Maps a single responsesByStatusCode entry value to its TypeScript body type.
|
|
30
|
+
* Both no-body forms (the ContractNoBody symbol and tagged noBodyResponse()) map to null.
|
|
30
31
|
*/
|
|
31
|
-
type InferClientResponseBody<T> = T extends typeof ContractNoBody ? null : T extends
|
|
32
|
+
type InferClientResponseBody<T> = T extends typeof ContractNoBody ? null : T extends {
|
|
33
|
+
_tag: 'NoBodyResponse';
|
|
34
|
+
} ? null : T extends z.ZodType ? InferSchemaOutput<T> : T extends {
|
|
32
35
|
_tag: 'TextResponse';
|
|
33
36
|
} ? string : T extends {
|
|
34
37
|
_tag: 'BlobResponse';
|
|
@@ -47,10 +50,47 @@ type SseInferClientResponseBody<T> = Extract<InferClientResponseBody<T>, AsyncIt
|
|
|
47
50
|
* Like InferClientResponseBody but returns only non-SSE bodies — SSE entries resolve to never.
|
|
48
51
|
*/
|
|
49
52
|
type NonSseInferClientResponseBody<T> = Exclude<InferClientResponseBody<T>, AsyncIterable<unknown>>;
|
|
53
|
+
type WildcardSseBody<V, K extends WildcardStatusCodeKey> = K extends '2xx' ? SseInferClientResponseBody<V> : InferClientResponseBody<V>;
|
|
54
|
+
type WildcardNonSseBody<V, K extends WildcardStatusCodeKey> = K extends '2xx' ? NonSseInferClientResponseBody<V> : InferClientResponseBody<V>;
|
|
55
|
+
type ExactStatusCodes<TApiContract extends ApiContract> = keyof TApiContract['responsesByStatusCode'] & HttpStatusCode;
|
|
56
|
+
type RangeStatusCodes<TApiContract extends ApiContract> = {
|
|
57
|
+
[K in keyof TApiContract['responsesByStatusCode'] & HttpStatusCodeRange]: ExpandStatusRangeKey<K>;
|
|
58
|
+
}[keyof TApiContract['responsesByStatusCode'] & HttpStatusCodeRange];
|
|
59
|
+
type DefaultSuccessStatusCodes<TApiContract extends ApiContract> = Exclude<SuccessfulHttpStatusCode, ExactStatusCodes<TApiContract> | RangeStatusCodes<TApiContract>>;
|
|
60
|
+
type DefaultNonSuccessStatusCodes<TApiContract extends ApiContract> = Exclude<Exclude<HttpStatusCode, SuccessfulHttpStatusCode>, ExactStatusCodes<TApiContract> | RangeStatusCodes<TApiContract>>;
|
|
61
|
+
type WildcardSseEntry<TApiContract extends ApiContract, K extends WildcardStatusCodeKey> = K extends 'default' ? {
|
|
62
|
+
statusCode: DefaultSuccessStatusCodes<TApiContract>;
|
|
63
|
+
headers: InferClientResponseHeaders<TApiContract>;
|
|
64
|
+
body: SseInferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>>;
|
|
65
|
+
} | {
|
|
66
|
+
statusCode: DefaultNonSuccessStatusCodes<TApiContract>;
|
|
67
|
+
headers: InferClientResponseHeaders<TApiContract>;
|
|
68
|
+
body: InferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>>;
|
|
69
|
+
} : {
|
|
70
|
+
statusCode: Exclude<ExpandStatusRangeKey<K>, ExactStatusCodes<TApiContract>>;
|
|
71
|
+
headers: InferClientResponseHeaders<TApiContract>;
|
|
72
|
+
body: WildcardSseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>, K>;
|
|
73
|
+
};
|
|
74
|
+
type WildcardNonSseEntry<TApiContract extends ApiContract, K extends WildcardStatusCodeKey> = K extends 'default' ? {
|
|
75
|
+
statusCode: DefaultSuccessStatusCodes<TApiContract>;
|
|
76
|
+
headers: InferClientResponseHeaders<TApiContract>;
|
|
77
|
+
body: NonSseInferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>>;
|
|
78
|
+
} | {
|
|
79
|
+
statusCode: DefaultNonSuccessStatusCodes<TApiContract>;
|
|
80
|
+
headers: InferClientResponseHeaders<TApiContract>;
|
|
81
|
+
body: InferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>>;
|
|
82
|
+
} : {
|
|
83
|
+
statusCode: Exclude<ExpandStatusRangeKey<K>, ExactStatusCodes<TApiContract>>;
|
|
84
|
+
headers: InferClientResponseHeaders<TApiContract>;
|
|
85
|
+
body: WildcardNonSseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>, K>;
|
|
86
|
+
};
|
|
50
87
|
/**
|
|
51
88
|
* Infers a discriminated union of `{ statusCode, headers, body }` for SSE mode:
|
|
52
|
-
* - success status codes → SSE body only (AsyncIterable)
|
|
53
|
-
* - error status codes
|
|
89
|
+
* - exact success status codes and `'2xx'` range → SSE body only (AsyncIterable)
|
|
90
|
+
* - error status codes, other ranges, and `'default'` → body as-is (all kinds)
|
|
91
|
+
*
|
|
92
|
+
* `'default'` is split into a success half (`SuccessfulHttpStatusCode`) and a non-success half
|
|
93
|
+
* so that `captureAsError` type narrowing stays correct regardless of the actual status code.
|
|
54
94
|
*
|
|
55
95
|
* Headers are typed via `InferClientResponseHeaders`: known headers from `responseHeaderSchema`
|
|
56
96
|
* are strongly typed; all other headers remain accessible as `string | undefined`.
|
|
@@ -61,11 +101,16 @@ export type InferSseClientResponse<TApiContract extends ApiContract> = {
|
|
|
61
101
|
headers: InferClientResponseHeaders<TApiContract>;
|
|
62
102
|
body: K extends SuccessfulHttpStatusCode ? SseInferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>> : InferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>>;
|
|
63
103
|
};
|
|
64
|
-
}[keyof TApiContract['responsesByStatusCode'] & HttpStatusCode]
|
|
104
|
+
}[keyof TApiContract['responsesByStatusCode'] & HttpStatusCode] | {
|
|
105
|
+
[K in keyof TApiContract['responsesByStatusCode'] & WildcardStatusCodeKey]: WildcardSseEntry<TApiContract, K>;
|
|
106
|
+
}[keyof TApiContract['responsesByStatusCode'] & WildcardStatusCodeKey];
|
|
65
107
|
/**
|
|
66
108
|
* Infers a discriminated union of `{ statusCode, headers, body }` for non-SSE mode:
|
|
67
|
-
* - success status codes → non-SSE body only (JSON / text / blob / null)
|
|
68
|
-
* - error status codes
|
|
109
|
+
* - exact success status codes and `'2xx'` range → non-SSE body only (JSON / text / blob / null)
|
|
110
|
+
* - error status codes, other ranges, and `'default'` → body as-is (all kinds)
|
|
111
|
+
*
|
|
112
|
+
* `'default'` is split into a success half (`SuccessfulHttpStatusCode`) and a non-success half
|
|
113
|
+
* so that `captureAsError` type narrowing stays correct regardless of the actual status code.
|
|
69
114
|
*
|
|
70
115
|
* Headers are typed via `InferClientResponseHeaders`: known headers from `responseHeaderSchema`
|
|
71
116
|
* are strongly typed; all other headers remain accessible as `string | undefined`.
|
|
@@ -76,5 +121,7 @@ export type InferNonSseClientResponse<TApiContract extends ApiContract> = {
|
|
|
76
121
|
headers: InferClientResponseHeaders<TApiContract>;
|
|
77
122
|
body: K extends SuccessfulHttpStatusCode ? NonSseInferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>> : InferClientResponseBody<NonNullable<TApiContract['responsesByStatusCode'][K]>>;
|
|
78
123
|
};
|
|
79
|
-
}[keyof TApiContract['responsesByStatusCode'] & HttpStatusCode]
|
|
124
|
+
}[keyof TApiContract['responsesByStatusCode'] & HttpStatusCode] | {
|
|
125
|
+
[K in keyof TApiContract['responsesByStatusCode'] & WildcardStatusCodeKey]: WildcardNonSseEntry<TApiContract, K>;
|
|
126
|
+
}[keyof TApiContract['responsesByStatusCode'] & WildcardStatusCodeKey];
|
|
80
127
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { z } from 'zod/v4';
|
|
2
|
-
import type { HttpStatusCode } from '../HttpStatusCodes.ts';
|
|
2
|
+
import type { HttpStatusCode, WildcardStatusCodeKey } from '../HttpStatusCodes.ts';
|
|
3
3
|
import { ContractNoBody } from './constants.ts';
|
|
4
4
|
export type ResponseOptions = {
|
|
5
5
|
readonly description?: string;
|
|
@@ -43,7 +43,7 @@ export type NoBodyResponse = {
|
|
|
43
43
|
export declare const noBodyResponse: (options?: ResponseOptions) => NoBodyResponse;
|
|
44
44
|
export declare const isNoBodyResponse: (value: ApiContractResponse) => value is NoBodyResponse;
|
|
45
45
|
export type ApiContractResponse = typeof ContractNoBody | NoBodyResponse | TypedApiContractResponse | AnyOfResponses;
|
|
46
|
-
export type ResponsesByStatusCode = Partial<Record<HttpStatusCode, ApiContractResponse>>;
|
|
46
|
+
export type ResponsesByStatusCode = Partial<Record<HttpStatusCode | WildcardStatusCodeKey, ApiContractResponse>>;
|
|
47
47
|
export type ResponseKind = {
|
|
48
48
|
kind: 'noContent';
|
|
49
49
|
} | {
|
|
@@ -76,6 +76,7 @@ export type ResponseKind = {
|
|
|
76
76
|
export declare const resolveContractResponse: (schemaEntry: ApiContractResponse, contentType: string | undefined, strict?: boolean) => ResponseKind | null;
|
|
77
77
|
/**
|
|
78
78
|
* Combines status-code lookup and content-type resolution into a single call.
|
|
79
|
-
*
|
|
79
|
+
* Lookup precedence: exact code → range key (e.g. `'4xx'`) → `'default'`.
|
|
80
|
+
* Returns `null` when no entry matches or the content-type cannot be matched.
|
|
80
81
|
*/
|
|
81
82
|
export declare function resolveResponseEntry(responsesByStatusCode: ResponsesByStatusCode, statusCode: number, contentType: string | undefined, strictContentType: boolean): ResponseKind | null;
|
|
@@ -97,15 +97,40 @@ export const resolveContractResponse = (schemaEntry, contentType, strict = true)
|
|
|
97
97
|
const matched = matchTypedResponse(schemaEntry, contentType);
|
|
98
98
|
return matched ?? (strict ? null : resolveByKind(schemaEntry));
|
|
99
99
|
};
|
|
100
|
+
function getRangeKey(statusCode) {
|
|
101
|
+
if (statusCode >= 100 && statusCode < 200)
|
|
102
|
+
return '1xx';
|
|
103
|
+
if (statusCode >= 200 && statusCode < 300)
|
|
104
|
+
return '2xx';
|
|
105
|
+
if (statusCode >= 300 && statusCode < 400)
|
|
106
|
+
return '3xx';
|
|
107
|
+
if (statusCode >= 400 && statusCode < 500)
|
|
108
|
+
return '4xx';
|
|
109
|
+
if (statusCode >= 500 && statusCode < 600)
|
|
110
|
+
return '5xx';
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
100
113
|
/**
|
|
101
114
|
* Combines status-code lookup and content-type resolution into a single call.
|
|
102
|
-
*
|
|
115
|
+
* Lookup precedence: exact code → range key (e.g. `'4xx'`) → `'default'`.
|
|
116
|
+
* Returns `null` when no entry matches or the content-type cannot be matched.
|
|
103
117
|
*/
|
|
104
118
|
export function resolveResponseEntry(responsesByStatusCode, statusCode, contentType, strictContentType) {
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
return
|
|
119
|
+
const exactEntry = responsesByStatusCode[statusCode];
|
|
120
|
+
if (exactEntry) {
|
|
121
|
+
return resolveContractResponse(exactEntry, contentType, strictContentType);
|
|
108
122
|
}
|
|
109
|
-
|
|
123
|
+
const rangeKey = getRangeKey(statusCode);
|
|
124
|
+
if (rangeKey) {
|
|
125
|
+
const rangeEntry = responsesByStatusCode[rangeKey];
|
|
126
|
+
if (rangeEntry) {
|
|
127
|
+
return resolveContractResponse(rangeEntry, contentType, strictContentType);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const defaultEntry = responsesByStatusCode.default;
|
|
131
|
+
if (defaultEntry) {
|
|
132
|
+
return resolveContractResponse(defaultEntry, contentType, strictContentType);
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
110
135
|
}
|
|
111
136
|
//# sourceMappingURL=contractResponse.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contractResponse.js","sourceRoot":"","sources":["../../src/new/contractResponse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contractResponse.js","sourceRoot":"","sources":["../../src/new/contractResponse.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
import type { CommonRouteDefinitionMetadata, InferSchemaOutput, RoutePathResolver } from '../apiContracts.ts';
|
|
3
|
-
import type { Exactly } from '../typeUtils.ts';
|
|
3
|
+
import type { DistributiveOmit, Exactly } from '../typeUtils.ts';
|
|
4
4
|
import { ContractNoBody } from './constants.ts';
|
|
5
5
|
import { type ResponsesByStatusCode, type SseSchemaByEventName } from './contractResponse.ts';
|
|
6
6
|
export type RequestPathParamsSchema = z.ZodObject;
|
|
@@ -32,13 +32,13 @@ export type PayloadApiContract = CommonApiContract & {
|
|
|
32
32
|
requestBodySchema: typeof ContractNoBody | z.ZodType;
|
|
33
33
|
};
|
|
34
34
|
export type ApiContract = GetApiContract | DeleteApiContract | PayloadApiContract;
|
|
35
|
-
type TypedPathApiContract<
|
|
36
|
-
pathResolver: RoutePathResolver<InferSchemaOutput<
|
|
37
|
-
requestPathParamsSchema?:
|
|
35
|
+
type TypedPathApiContract<TPathParamsSchema extends RequestPathParamsSchema | undefined> = DistributiveOmit<ApiContract, 'pathResolver' | 'requestPathParamsSchema'> & {
|
|
36
|
+
pathResolver: RoutePathResolver<InferSchemaOutput<TPathParamsSchema>>;
|
|
37
|
+
requestPathParamsSchema?: TPathParamsSchema;
|
|
38
38
|
};
|
|
39
|
-
export declare const defineApiContract: <
|
|
40
|
-
requestPathParamsSchema?:
|
|
41
|
-
}) =>
|
|
39
|
+
export declare const defineApiContract: <TPathParamsSchema extends RequestPathParamsSchema | undefined = undefined, const TContract extends TypedPathApiContract<TPathParamsSchema> = TypedPathApiContract<TPathParamsSchema>>(contract: Exactly<TContract, TypedPathApiContract<TPathParamsSchema>> & {
|
|
40
|
+
requestPathParamsSchema?: TPathParamsSchema;
|
|
41
|
+
}) => TContract;
|
|
42
42
|
export declare const mapApiContractToPath: (routeConfig: ApiContract) => string;
|
|
43
43
|
export declare const describeApiContract: (routeConfig: ApiContract) => string;
|
|
44
44
|
export declare const getSseSchemaByEventName: (routeConfig: ApiContract) => SseSchemaByEventName | null;
|
|
@@ -33,7 +33,7 @@ export const getSseSchemaByEventName = (routeConfig) => {
|
|
|
33
33
|
return Object.keys(result).length > 0 ? result : null;
|
|
34
34
|
};
|
|
35
35
|
export const hasAnySuccessSseResponse = (apiContract) => {
|
|
36
|
-
for (const code of SUCCESSFUL_HTTP_STATUS_CODES) {
|
|
36
|
+
for (const code of [...SUCCESSFUL_HTTP_STATUS_CODES, '2xx', 'default']) {
|
|
37
37
|
const value = apiContract.responsesByStatusCode[code];
|
|
38
38
|
if (!value) {
|
|
39
39
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineApiContract.js","sourceRoot":"","sources":["../../src/new/defineApiContract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAM1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAA;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,GAGf,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"defineApiContract.js","sourceRoot":"","sources":["../../src/new/defineApiContract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAM1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAA;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,GAGf,MAAM,uBAAuB,CAAA;AA6C9B,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAK/B,QAEC,EACU,EAAE,CAAC,QAAQ,CAAA;AAExB,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,WAAwB,EAAU,EAAE;IACvE,IAAI,CAAC,WAAW,CAAC,uBAAuB,EAAE,CAAC;QACzC,OAAO,WAAW,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,MAAM,CAElF,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACb,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,CAAA;QAEpB,OAAO,GAAG,CAAA;IACZ,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,WAAW,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;AACjD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,WAAwB,EAAU,EAAE;IACtE,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,oBAAoB,CAAC,WAAW,CAAC,EAAE,CAAA;AACnF,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,WAAwB,EAA+B,EAAE;IAC/F,MAAM,MAAM,GAAyB,EAAE,CAAA;IAEvC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,qBAAqB,CAAC,EAAE,CAAC;QACrE,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAA;QAChD,CAAC;aAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,IAAI,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC5B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAA;gBACnD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AACvD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,WAAwB,EAAW,EAAE;IAC5E,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,4BAA4B,EAAE,KAAc,EAAE,SAAkB,CAAC,EAAE,CAAC;QACzF,MAAM,KAAK,GAAG,WAAW,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;QAErD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,SAAQ;QACV,CAAC;QAED,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAA;QACb,CAAC;aAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,IAAI,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC5B,OAAO,IAAI,CAAA;gBACb,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,4EAA4E;AAC5E,gFAAgF;AAChF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,WAAwB,EAAoB,EAAE;IACrF,MAAM,OAAO,GAAgB,EAAE,CAAA;IAC/B,IAAI,qBAAqB,GAAG,KAAK,CAAA;IAEjC,KAAK,MAAM,IAAI,IAAI,4BAA4B,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,WAAW,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;QAErD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,SAAQ;QACV,CAAC;QAED,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACvF,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBACxB,CAAC;YACH,CAAC;QACH,CAAC;aAAM,IACL,KAAK,KAAK,cAAc;YACxB,gBAAgB,CAAC,KAAK,CAAC;YACvB,aAAa,CAAC,KAAK,CAAC;YACpB,cAAc,CAAC,KAAK,CAAC;YACrB,cAAc,CAAC,KAAK,CAAC,EACrB,CAAC;YACD,qBAAqB,GAAG,IAAI,CAAA;QAC9B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACjC,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,WAAW,CAAA;IACpB,CAAC;IAED,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACjD,CAAC,CAAA;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,WAAwB,EAAW,EAAE;IAC9E,IAAI,uBAAuB,GAAG,IAAI,CAAA;IAElC,KAAK,MAAM,IAAI,IAAI,4BAA4B,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,WAAW,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;QAErD,IAAI,KAAK,IAAI,KAAK,KAAK,cAAc,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YAClE,uBAAuB,GAAG,KAAK,CAAA;YAC/B,MAAK;QACP,CAAC;IACH,CAAC;IAED,OAAO,uBAAuB,CAAA;AAChC,CAAC,CAAA"}
|
package/dist/new/inferTypes.d.ts
CHANGED
|
@@ -2,14 +2,17 @@ import type { z } from 'zod/v4';
|
|
|
2
2
|
import type { SuccessfulHttpStatusCode } from '../HttpStatusCodes.ts';
|
|
3
3
|
import type { ValueOf } from '../typeUtils.ts';
|
|
4
4
|
import type { ContractNoBody } from './constants.ts';
|
|
5
|
-
import type { ResponsesByStatusCode } from './contractResponse.ts';
|
|
6
|
-
type ExtractSuccessResponses<T extends ResponsesByStatusCode> = ValueOf<T, Extract<keyof T, SuccessfulHttpStatusCode>>;
|
|
5
|
+
import type { NoBodyResponse, ResponsesByStatusCode } from './contractResponse.ts';
|
|
6
|
+
type ExtractSuccessResponses<T extends ResponsesByStatusCode> = ValueOf<T, Extract<keyof T, SuccessfulHttpStatusCode | '2xx' | 'default'>>;
|
|
7
7
|
/**
|
|
8
|
-
* Returns true if all success responses have no body
|
|
8
|
+
* Returns true if all success responses have no body
|
|
9
|
+
* (ContractNoBody, noBodyResponse(), or no success status codes defined).
|
|
10
|
+
*
|
|
11
|
+
* @deprecated No known consumers — will be removed in a future release.
|
|
9
12
|
*/
|
|
10
13
|
export type IsNoBodySuccessResponse<T extends ResponsesByStatusCode> = [
|
|
11
14
|
ExtractSuccessResponses<T>
|
|
12
|
-
] extends [typeof ContractNoBody | undefined] ? true : false;
|
|
15
|
+
] extends [typeof ContractNoBody | NoBodyResponse | undefined] ? true : false;
|
|
13
16
|
type UnpackAnyOf<T> = T extends {
|
|
14
17
|
_tag: 'AnyOfResponses';
|
|
15
18
|
responses: Array<infer Item>;
|
|
@@ -46,7 +49,7 @@ type NonSseBodyOf<T> = T extends {
|
|
|
46
49
|
/**
|
|
47
50
|
* Infers the TypeScript output type of all non-SSE success responses.
|
|
48
51
|
* JSON schemas → z.output<T>. TextResponse → string. BlobResponse → Blob.
|
|
49
|
-
* ContractNoBody → undefined. SseResponse → never (excluded).
|
|
52
|
+
* ContractNoBody and noBodyResponse() → undefined. SseResponse → never (excluded).
|
|
50
53
|
* AnyOfResponses are unpacked before mapping.
|
|
51
54
|
*/
|
|
52
55
|
export type InferNonSseSuccessResponses<T extends ResponsesByStatusCode> = NonSseBodyOf<FlatSuccessResponses<T>>;
|
|
@@ -90,5 +93,7 @@ export type AvailableResponseModes<T extends ResponsesByStatusCode> = (HasAnyJso
|
|
|
90
93
|
_tag: 'BlobResponse';
|
|
91
94
|
}> extends never ? never : 'blob') | (Extract<FlatSuccessResponses<T>, {
|
|
92
95
|
_tag: 'TextResponse';
|
|
93
|
-
}> extends never ? never : 'text') | (Extract<FlatSuccessResponses<T>, typeof ContractNoBody
|
|
96
|
+
}> extends never ? never : 'text') | (Extract<FlatSuccessResponses<T>, typeof ContractNoBody | {
|
|
97
|
+
_tag: 'NoBodyResponse';
|
|
98
|
+
}> extends never ? never : 'noContent');
|
|
94
99
|
export {};
|
package/dist/typeUtils.d.ts
CHANGED
|
@@ -15,6 +15,20 @@ export type IsUnion<T, U = T> = (T extends unknown ? ([U] extends [T] ? 0 : 1) :
|
|
|
15
15
|
export type Exactly<T, U> = T & {
|
|
16
16
|
[K in keyof T]: K extends keyof U ? T[K] : never;
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Distributed `keyof`: the union of keys across all members of a union type.
|
|
20
|
+
* (Plain `keyof` over a union yields only the keys common to every member.)
|
|
21
|
+
*/
|
|
22
|
+
export type KeysOfUnion<TUnion> = TUnion extends unknown ? keyof TUnion : never;
|
|
23
|
+
/**
|
|
24
|
+
* Like `Omit`, but distributes over unions: each union member is transformed
|
|
25
|
+
* separately, preserving discriminated-union relationships between keys.
|
|
26
|
+
* (Plain `Omit` collapses a union into a single object type of its common keys.)
|
|
27
|
+
*
|
|
28
|
+
* Keys are constrained to `KeysOfUnion` rather than `keyof TUnion`, so keys present
|
|
29
|
+
* on only some union members can be omitted too — members without the key pass through unchanged.
|
|
30
|
+
*/
|
|
31
|
+
export type DistributiveOmit<TUnion, TKeys extends KeysOfUnion<TUnion>> = TUnion extends unknown ? Omit<TUnion, TKeys> : never;
|
|
18
32
|
/**
|
|
19
33
|
* Extracts a union of value types from an object type.
|
|
20
34
|
* Optionally constrained to a subset of keys via ValueType.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lokalise/api-contracts",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.13.1",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist"
|
|
6
6
|
],
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"zod": ">=3.25.56"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@biomejs/biome": "^2.
|
|
39
|
+
"@biomejs/biome": "^2.5.0",
|
|
40
40
|
"@lokalise/biome-config": "^3.1.0",
|
|
41
41
|
"@lokalise/tsconfig": "^4.0.0",
|
|
42
|
-
"@types/node": "^25.9.
|
|
43
|
-
"@vitest/coverage-v8": "^4.
|
|
42
|
+
"@types/node": "^25.9.3",
|
|
43
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
44
44
|
"rimraf": "^6.1.2",
|
|
45
45
|
"typescript": "6.0.3",
|
|
46
|
-
"vitest": "^4.
|
|
46
|
+
"vitest": "^4.1.9",
|
|
47
47
|
"zod": "^4.3.6"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|