@lokalise/api-contracts 6.2.1 → 6.4.0
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 +114 -17
- package/dist/apiContracts.d.ts +56 -0
- package/dist/apiContracts.js +56 -0
- package/dist/apiContracts.js.map +1 -1
- package/dist/contractBuilder.d.ts +84 -0
- package/dist/contractBuilder.js +17 -0
- package/dist/contractBuilder.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/rest/restContractBuilder.d.ts +100 -0
- package/dist/rest/restContractBuilder.js +45 -0
- package/dist/rest/restContractBuilder.js.map +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -8,12 +8,86 @@ This reduces amount of assumptions FE needs to make about the behaviour of BE, r
|
|
|
8
8
|
written on FE, and makes the code more type-safe (as path parameter setting is handled by logic exposed by BE, in a
|
|
9
9
|
type-safe way).
|
|
10
10
|
|
|
11
|
+
## Universal Contract Builder
|
|
12
|
+
|
|
13
|
+
Use `buildContract` as a single entry point for creating any type of API contract. It automatically delegates to the appropriate specialized builder based on the configuration:
|
|
14
|
+
|
|
15
|
+
| `sseEvents` | Contract Type |
|
|
16
|
+
|-------------|---------------|
|
|
17
|
+
| ❌ | REST contract (GET, POST, PUT, PATCH, DELETE) |
|
|
18
|
+
| ✅ | SSE or Dual-mode contract |
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { buildContract } from '@lokalise/api-contracts'
|
|
22
|
+
import { z } from 'zod'
|
|
23
|
+
|
|
24
|
+
// REST GET route
|
|
25
|
+
const getUsers = buildContract({
|
|
26
|
+
successResponseBodySchema: z.array(userSchema),
|
|
27
|
+
pathResolver: () => '/api/users',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// REST POST route
|
|
31
|
+
const createUser = buildContract({
|
|
32
|
+
method: 'post',
|
|
33
|
+
requestBodySchema: createUserSchema,
|
|
34
|
+
successResponseBodySchema: userSchema,
|
|
35
|
+
pathResolver: () => '/api/users',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// REST DELETE route
|
|
39
|
+
const deleteUser = buildContract({
|
|
40
|
+
method: 'delete',
|
|
41
|
+
requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
42
|
+
pathResolver: (params) => `/api/users/${params.userId}`,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// SSE-only streaming endpoint
|
|
46
|
+
const notifications = buildContract({
|
|
47
|
+
pathResolver: () => '/api/notifications/stream',
|
|
48
|
+
params: z.object({}),
|
|
49
|
+
query: z.object({}),
|
|
50
|
+
requestHeaders: z.object({}),
|
|
51
|
+
sseEvents: {
|
|
52
|
+
notification: z.object({ id: z.string(), message: z.string() }),
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Dual-mode endpoint (supports both JSON and SSE)
|
|
57
|
+
const chatCompletion = buildContract({
|
|
58
|
+
method: 'post',
|
|
59
|
+
pathResolver: () => '/api/chat/completions',
|
|
60
|
+
params: z.object({}),
|
|
61
|
+
query: z.object({}),
|
|
62
|
+
requestHeaders: z.object({}),
|
|
63
|
+
requestBody: z.object({ message: z.string() }),
|
|
64
|
+
syncResponseBody: z.object({ reply: z.string() }),
|
|
65
|
+
sseEvents: {
|
|
66
|
+
chunk: z.object({ delta: z.string() }),
|
|
67
|
+
done: z.object({ usage: z.object({ tokens: z.number() }) }),
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You can also use the specialized builders directly (`buildRestContract`, `buildSseContract`) if you prefer explicit control over contract types.
|
|
73
|
+
|
|
74
|
+
## REST Contracts
|
|
75
|
+
|
|
76
|
+
Use `buildRestContract` to create REST API contracts. The contract type is automatically determined based on the configuration:
|
|
77
|
+
|
|
78
|
+
| `method` | `requestBodySchema` | Result |
|
|
79
|
+
|----------|---------------------|--------|
|
|
80
|
+
| omitted/undefined | ❌ | GET route |
|
|
81
|
+
| `'delete'` | ❌ | DELETE route |
|
|
82
|
+
| `'post'`/`'put'`/`'patch'` | ✅ | Payload route |
|
|
83
|
+
|
|
11
84
|
Usage examples:
|
|
12
85
|
|
|
13
86
|
```ts
|
|
14
|
-
import {
|
|
87
|
+
import { buildRestContract } from '@lokalise/api-contracts'
|
|
15
88
|
|
|
16
|
-
|
|
89
|
+
// GET route - method is inferred automatically
|
|
90
|
+
const getContract = buildRestContract({
|
|
17
91
|
successResponseBodySchema: RESPONSE_BODY_SCHEMA,
|
|
18
92
|
requestPathParamsSchema: REQUEST_PATH_PARAMS_SCHEMA,
|
|
19
93
|
requestQuerySchema: REQUEST_QUERY_SCHEMA,
|
|
@@ -24,8 +98,9 @@ const getContract = buildGetRoute({
|
|
|
24
98
|
metadata: { allowedRoles: ['admin'] },
|
|
25
99
|
})
|
|
26
100
|
|
|
27
|
-
|
|
28
|
-
|
|
101
|
+
// POST route - requires method and requestBodySchema
|
|
102
|
+
const postContract = buildRestContract({
|
|
103
|
+
method: 'post', // can also be 'put' or 'patch'
|
|
29
104
|
successResponseBodySchema: RESPONSE_BODY_SCHEMA,
|
|
30
105
|
requestBodySchema: REQUEST_BODY_SCHEMA,
|
|
31
106
|
pathResolver: () => '/',
|
|
@@ -33,13 +108,35 @@ const postContract = buildPayloadRoute({
|
|
|
33
108
|
metadata: { allowedPermission: ['edit'] },
|
|
34
109
|
})
|
|
35
110
|
|
|
36
|
-
|
|
111
|
+
// DELETE route - method is 'delete', no body, defaults isEmptyResponseExpected to true
|
|
112
|
+
const deleteContract = buildRestContract({
|
|
113
|
+
method: 'delete',
|
|
37
114
|
successResponseBodySchema: RESPONSE_BODY_SCHEMA,
|
|
38
115
|
requestPathParamsSchema: REQUEST_PATH_PARAMS_SCHEMA,
|
|
39
116
|
pathResolver: (pathParams) => `/users/${pathParams.userId}`,
|
|
40
117
|
})
|
|
41
118
|
```
|
|
42
119
|
|
|
120
|
+
### Deprecated Builders
|
|
121
|
+
|
|
122
|
+
The individual builders `buildGetRoute`, `buildPayloadRoute`, and `buildDeleteRoute` are deprecated. Use `buildRestContract` instead:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// Before (deprecated):
|
|
126
|
+
import { buildGetRoute, buildPayloadRoute, buildDeleteRoute } from '@lokalise/api-contracts'
|
|
127
|
+
|
|
128
|
+
const getContract = buildGetRoute({ ... })
|
|
129
|
+
const postContract = buildPayloadRoute({ method: 'post', ... })
|
|
130
|
+
const deleteContract = buildDeleteRoute({ ... })
|
|
131
|
+
|
|
132
|
+
// After (recommended):
|
|
133
|
+
import { buildRestContract } from '@lokalise/api-contracts'
|
|
134
|
+
|
|
135
|
+
const getContract = buildRestContract({ ... }) // method inferred as 'get'
|
|
136
|
+
const postContract = buildRestContract({ method: 'post', ... })
|
|
137
|
+
const deleteContract = buildRestContract({ method: 'delete', ... })
|
|
138
|
+
```
|
|
139
|
+
|
|
43
140
|
In the previous example, the `metadata` property is an optional, free-form field that allows you to store any additional
|
|
44
141
|
information related to the route. If you require more precise type definitions for the `metadata` field, you can utilize
|
|
45
142
|
TypeScript's module augmentation mechanism to enforce stricter typing. This allows for more controlled and type-safe
|
|
@@ -72,10 +169,10 @@ In case you are using fastify on the backend, you can also use `@lokalise/fastif
|
|
|
72
169
|
Use `requestHeaderSchema` to define and validate headers that the client must send with the request. This is useful for authentication headers, API keys, content negotiation, and other request-specific headers.
|
|
73
170
|
|
|
74
171
|
```ts
|
|
75
|
-
import {
|
|
172
|
+
import { buildRestContract } from '@lokalise/api-contracts'
|
|
76
173
|
import { z } from 'zod'
|
|
77
174
|
|
|
78
|
-
const contract =
|
|
175
|
+
const contract = buildRestContract({
|
|
79
176
|
successResponseBodySchema: DATA_SCHEMA,
|
|
80
177
|
requestHeaderSchema: z.object({
|
|
81
178
|
'authorization': z.string(),
|
|
@@ -95,10 +192,10 @@ Use `responseHeaderSchema` to define and validate headers that the server will s
|
|
|
95
192
|
- Custom API metadata headers
|
|
96
193
|
|
|
97
194
|
```ts
|
|
98
|
-
import {
|
|
195
|
+
import { buildRestContract } from '@lokalise/api-contracts'
|
|
99
196
|
import { z } from 'zod'
|
|
100
197
|
|
|
101
|
-
const contract =
|
|
198
|
+
const contract = buildRestContract({
|
|
102
199
|
successResponseBodySchema: DATA_SCHEMA,
|
|
103
200
|
responseHeaderSchema: z.object({
|
|
104
201
|
'x-ratelimit-limit': z.string(),
|
|
@@ -113,7 +210,7 @@ const contract = buildGetRoute({
|
|
|
113
210
|
Both header schemas can be used together in a single contract:
|
|
114
211
|
|
|
115
212
|
```ts
|
|
116
|
-
const contract =
|
|
213
|
+
const contract = buildRestContract({
|
|
117
214
|
successResponseBodySchema: DATA_SCHEMA,
|
|
118
215
|
requestHeaderSchema: z.object({
|
|
119
216
|
'authorization': z.string(),
|
|
@@ -139,9 +236,9 @@ These header schemas are primarily used for:
|
|
|
139
236
|
Converts a route definition to its corresponding path pattern with parameter placeholders.
|
|
140
237
|
|
|
141
238
|
```ts
|
|
142
|
-
import { mapRouteToPath,
|
|
239
|
+
import { mapRouteToPath, buildRestContract } from '@lokalise/api-contracts'
|
|
143
240
|
|
|
144
|
-
const userContract =
|
|
241
|
+
const userContract = buildRestContract({
|
|
145
242
|
requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
146
243
|
successResponseBodySchema: USER_SCHEMA,
|
|
147
244
|
pathResolver: (pathParams) => `/users/${pathParams.userId}`,
|
|
@@ -163,19 +260,19 @@ The function replaces actual path parameters with placeholder syntax (`:paramNam
|
|
|
163
260
|
Generates a human-readable description of a route contract, combining the HTTP method with the route path.
|
|
164
261
|
|
|
165
262
|
```ts
|
|
166
|
-
import { describeContract,
|
|
263
|
+
import { describeContract, buildRestContract } from '@lokalise/api-contracts'
|
|
167
264
|
|
|
168
|
-
const getContract =
|
|
265
|
+
const getContract = buildRestContract({
|
|
169
266
|
requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
170
267
|
successResponseBodySchema: USER_SCHEMA,
|
|
171
268
|
pathResolver: (pathParams) => `/users/${pathParams.userId}`,
|
|
172
269
|
})
|
|
173
270
|
|
|
174
|
-
const postContract =
|
|
271
|
+
const postContract = buildRestContract({
|
|
175
272
|
method: 'post',
|
|
176
|
-
requestPathParamsSchema: z.object({
|
|
273
|
+
requestPathParamsSchema: z.object({
|
|
177
274
|
orgId: z.string(),
|
|
178
|
-
userId: z.string()
|
|
275
|
+
userId: z.string()
|
|
179
276
|
}),
|
|
180
277
|
requestBodySchema: CREATE_USER_SCHEMA,
|
|
181
278
|
successResponseBodySchema: USER_SCHEMA,
|
package/dist/apiContracts.d.ts
CHANGED
|
@@ -58,8 +58,64 @@ export type GetRouteDefinition<SuccessResponseBodySchema extends z.Schema | unde
|
|
|
58
58
|
export type DeleteRouteDefinition<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = true, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined> = CommonRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode> & {
|
|
59
59
|
method: 'delete';
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Before (deprecated):
|
|
66
|
+
* const route = buildPayloadRoute({
|
|
67
|
+
* method: 'post',
|
|
68
|
+
* requestBodySchema: bodySchema,
|
|
69
|
+
* successResponseBodySchema: responseSchema,
|
|
70
|
+
* pathResolver: () => '/api/users',
|
|
71
|
+
* })
|
|
72
|
+
*
|
|
73
|
+
* // After (recommended):
|
|
74
|
+
* const route = buildRestContract({
|
|
75
|
+
* method: 'post',
|
|
76
|
+
* requestBodySchema: bodySchema,
|
|
77
|
+
* successResponseBodySchema: responseSchema,
|
|
78
|
+
* pathResolver: () => '/api/users',
|
|
79
|
+
* })
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
61
82
|
export declare function buildPayloadRoute<RequestBodySchema extends z.Schema | undefined = undefined, SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(params: PayloadRouteDefinition<RequestBodySchema, SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>): PayloadRouteDefinition<RequestBodySchema, SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
83
|
+
/**
|
|
84
|
+
* @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* // Before (deprecated):
|
|
88
|
+
* const route = buildGetRoute({
|
|
89
|
+
* successResponseBodySchema: responseSchema,
|
|
90
|
+
* pathResolver: () => '/api/users',
|
|
91
|
+
* })
|
|
92
|
+
*
|
|
93
|
+
* // After (recommended):
|
|
94
|
+
* const route = buildRestContract({
|
|
95
|
+
* successResponseBodySchema: responseSchema,
|
|
96
|
+
* pathResolver: () => '/api/users',
|
|
97
|
+
* })
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
62
100
|
export declare function buildGetRoute<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(params: Omit<GetRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>, 'method'>): GetRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
101
|
+
/**
|
|
102
|
+
* @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // Before (deprecated):
|
|
106
|
+
* const route = buildDeleteRoute({
|
|
107
|
+
* successResponseBodySchema: responseSchema,
|
|
108
|
+
* pathResolver: () => '/api/users/123',
|
|
109
|
+
* })
|
|
110
|
+
*
|
|
111
|
+
* // After (recommended):
|
|
112
|
+
* const route = buildRestContract({
|
|
113
|
+
* method: 'delete',
|
|
114
|
+
* successResponseBodySchema: responseSchema,
|
|
115
|
+
* pathResolver: () => '/api/users/123',
|
|
116
|
+
* })
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
63
119
|
export declare function buildDeleteRoute<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = true, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(params: Omit<DeleteRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>, 'method'>): DeleteRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
64
120
|
/**
|
|
65
121
|
* This method maps given route definition to a string of the format '/static-path-part/:path-param-value'
|
package/dist/apiContracts.js
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
const EMPTY_PARAMS = {};
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* // Before (deprecated):
|
|
7
|
+
* const route = buildPayloadRoute({
|
|
8
|
+
* method: 'post',
|
|
9
|
+
* requestBodySchema: bodySchema,
|
|
10
|
+
* successResponseBodySchema: responseSchema,
|
|
11
|
+
* pathResolver: () => '/api/users',
|
|
12
|
+
* })
|
|
13
|
+
*
|
|
14
|
+
* // After (recommended):
|
|
15
|
+
* const route = buildRestContract({
|
|
16
|
+
* method: 'post',
|
|
17
|
+
* requestBodySchema: bodySchema,
|
|
18
|
+
* successResponseBodySchema: responseSchema,
|
|
19
|
+
* pathResolver: () => '/api/users',
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
2
23
|
export function buildPayloadRoute(params) {
|
|
3
24
|
return {
|
|
4
25
|
isEmptyResponseExpected: params.isEmptyResponseExpected ?? false,
|
|
@@ -18,6 +39,23 @@ export function buildPayloadRoute(params) {
|
|
|
18
39
|
tags: params.tags,
|
|
19
40
|
};
|
|
20
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Before (deprecated):
|
|
47
|
+
* const route = buildGetRoute({
|
|
48
|
+
* successResponseBodySchema: responseSchema,
|
|
49
|
+
* pathResolver: () => '/api/users',
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* // After (recommended):
|
|
53
|
+
* const route = buildRestContract({
|
|
54
|
+
* successResponseBodySchema: responseSchema,
|
|
55
|
+
* pathResolver: () => '/api/users',
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
21
59
|
export function buildGetRoute(params) {
|
|
22
60
|
return {
|
|
23
61
|
isEmptyResponseExpected: params.isEmptyResponseExpected ?? false,
|
|
@@ -36,6 +74,24 @@ export function buildGetRoute(params) {
|
|
|
36
74
|
tags: params.tags,
|
|
37
75
|
};
|
|
38
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // Before (deprecated):
|
|
82
|
+
* const route = buildDeleteRoute({
|
|
83
|
+
* successResponseBodySchema: responseSchema,
|
|
84
|
+
* pathResolver: () => '/api/users/123',
|
|
85
|
+
* })
|
|
86
|
+
*
|
|
87
|
+
* // After (recommended):
|
|
88
|
+
* const route = buildRestContract({
|
|
89
|
+
* method: 'delete',
|
|
90
|
+
* successResponseBodySchema: responseSchema,
|
|
91
|
+
* pathResolver: () => '/api/users/123',
|
|
92
|
+
* })
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
39
95
|
export function buildDeleteRoute(params) {
|
|
40
96
|
return {
|
|
41
97
|
isEmptyResponseExpected: params.isEmptyResponseExpected ?? true,
|
package/dist/apiContracts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.js","sourceRoot":"","sources":["../src/apiContracts.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"apiContracts.js","sourceRoot":"","sources":["../src/apiContracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { z } from 'zod/v4';
|
|
2
|
+
import type { DeleteRouteDefinition, GetRouteDefinition, PayloadRouteDefinition } from './apiContracts.ts';
|
|
3
|
+
import type { HttpStatusCode } from './HttpStatusCodes.ts';
|
|
4
|
+
import { type DeleteContractConfig, type GetContractConfig, type PayloadContractConfig } from './rest/restContractBuilder.ts';
|
|
5
|
+
import type { DualModeContractDefinition } from './sse/dualModeContracts.ts';
|
|
6
|
+
import { type DualModeGetContractConfig, type DualModePayloadContractConfig, type SSEGetContractConfig, type SSEPayloadContractConfig } from './sse/sseContractBuilders.ts';
|
|
7
|
+
import type { SSEContractDefinition } from './sse/sseContracts.ts';
|
|
8
|
+
import type { SSEEventSchemas } from './sse/sseTypes.ts';
|
|
9
|
+
/**
|
|
10
|
+
* Universal contract builder that creates either REST or SSE contracts based on configuration.
|
|
11
|
+
*
|
|
12
|
+
* This is a unified entry point that delegates to:
|
|
13
|
+
* - `buildRestContract` when no `sseEvents` is provided
|
|
14
|
+
* - `buildSseContract` when `sseEvents` is provided
|
|
15
|
+
*
|
|
16
|
+
* ## Contract Type Detection
|
|
17
|
+
*
|
|
18
|
+
* | `sseEvents` | `syncResponseBody` | `requestBody`/`requestBodySchema` | Result |
|
|
19
|
+
* |-------------|-------------------|-----------------------------------|--------|
|
|
20
|
+
* | ❌ | - | ❌ | REST GET |
|
|
21
|
+
* | ❌ | - | ✅ (method: post/put/patch) | REST Payload |
|
|
22
|
+
* | ❌ | - | ❌ (method: delete) | REST DELETE |
|
|
23
|
+
* | ✅ | ❌ | ❌ | SSE-only GET |
|
|
24
|
+
* | ✅ | ❌ | ✅ | SSE-only POST/PUT/PATCH |
|
|
25
|
+
* | ✅ | ✅ | ❌ | Dual-mode GET |
|
|
26
|
+
* | ✅ | ✅ | ✅ | Dual-mode POST/PUT/PATCH |
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* // REST GET route
|
|
31
|
+
* const getUsers = buildContract({
|
|
32
|
+
* successResponseBodySchema: z.array(userSchema),
|
|
33
|
+
* pathResolver: () => '/api/users',
|
|
34
|
+
* })
|
|
35
|
+
*
|
|
36
|
+
* // REST POST route
|
|
37
|
+
* const createUser = buildContract({
|
|
38
|
+
* method: 'post',
|
|
39
|
+
* requestBodySchema: createUserSchema,
|
|
40
|
+
* successResponseBodySchema: userSchema,
|
|
41
|
+
* pathResolver: () => '/api/users',
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* // REST DELETE route
|
|
45
|
+
* const deleteUser = buildContract({
|
|
46
|
+
* method: 'delete',
|
|
47
|
+
* pathResolver: (params) => `/api/users/${params.userId}`,
|
|
48
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* // SSE-only streaming endpoint
|
|
52
|
+
* const notifications = buildContract({
|
|
53
|
+
* pathResolver: () => '/api/notifications/stream',
|
|
54
|
+
* params: z.object({}),
|
|
55
|
+
* query: z.object({}),
|
|
56
|
+
* requestHeaders: z.object({}),
|
|
57
|
+
* sseEvents: {
|
|
58
|
+
* notification: z.object({ id: z.string(), message: z.string() }),
|
|
59
|
+
* },
|
|
60
|
+
* })
|
|
61
|
+
*
|
|
62
|
+
* // Dual-mode endpoint (supports both JSON and SSE)
|
|
63
|
+
* const chatCompletion = buildContract({
|
|
64
|
+
* method: 'post',
|
|
65
|
+
* pathResolver: () => '/api/chat/completions',
|
|
66
|
+
* params: z.object({}),
|
|
67
|
+
* query: z.object({}),
|
|
68
|
+
* requestHeaders: z.object({}),
|
|
69
|
+
* requestBody: z.object({ message: z.string() }),
|
|
70
|
+
* syncResponseBody: z.object({ reply: z.string() }),
|
|
71
|
+
* sseEvents: {
|
|
72
|
+
* chunk: z.object({ delta: z.string() }),
|
|
73
|
+
* done: z.object({ usage: z.object({ tokens: z.number() }) }),
|
|
74
|
+
* },
|
|
75
|
+
* })
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function buildContract<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(config: GetContractConfig<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>): GetRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
79
|
+
export declare function buildContract<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = true, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(config: DeleteContractConfig<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>): DeleteRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
80
|
+
export declare function buildContract<RequestBodySchema extends z.Schema | undefined = undefined, SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(config: PayloadContractConfig<RequestBodySchema, SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>): PayloadRouteDefinition<RequestBodySchema, SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
81
|
+
export declare function buildContract<Params extends z.ZodTypeAny, Query extends z.ZodTypeAny, RequestHeaders extends z.ZodTypeAny, JsonResponse extends z.ZodTypeAny, Events extends SSEEventSchemas, ResponseHeaders extends z.ZodTypeAny | undefined = undefined, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined = undefined>(config: DualModeGetContractConfig<Params, Query, RequestHeaders, JsonResponse, Events, ResponseHeaders, ResponseSchemasByStatusCode>): DualModeContractDefinition<'get', Params, Query, RequestHeaders, undefined, JsonResponse, Events, ResponseHeaders, ResponseSchemasByStatusCode>;
|
|
82
|
+
export declare function buildContract<Params extends z.ZodTypeAny, Query extends z.ZodTypeAny, RequestHeaders extends z.ZodTypeAny, Events extends SSEEventSchemas, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined = undefined>(config: SSEGetContractConfig<Params, Query, RequestHeaders, Events, ResponseSchemasByStatusCode>): SSEContractDefinition<'get', Params, Query, RequestHeaders, undefined, Events, ResponseSchemasByStatusCode>;
|
|
83
|
+
export declare function buildContract<Params extends z.ZodTypeAny, Query extends z.ZodTypeAny, RequestHeaders extends z.ZodTypeAny, Body extends z.ZodTypeAny, JsonResponse extends z.ZodTypeAny, Events extends SSEEventSchemas, ResponseHeaders extends z.ZodTypeAny | undefined = undefined, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined = undefined>(config: DualModePayloadContractConfig<Params, Query, RequestHeaders, Body, JsonResponse, Events, ResponseHeaders, ResponseSchemasByStatusCode>): DualModeContractDefinition<'post' | 'put' | 'patch', Params, Query, RequestHeaders, Body, JsonResponse, Events, ResponseHeaders, ResponseSchemasByStatusCode>;
|
|
84
|
+
export declare function buildContract<Params extends z.ZodTypeAny, Query extends z.ZodTypeAny, RequestHeaders extends z.ZodTypeAny, Body extends z.ZodTypeAny, Events extends SSEEventSchemas, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined = undefined>(config: SSEPayloadContractConfig<Params, Query, RequestHeaders, Body, Events, ResponseSchemasByStatusCode>): SSEContractDefinition<'post' | 'put' | 'patch', Params, Query, RequestHeaders, Body, Events, ResponseSchemasByStatusCode>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { buildRestContract, } from "./rest/restContractBuilder.js";
|
|
2
|
+
import { buildSseContract, } from "./sse/sseContractBuilders.js";
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Implementation
|
|
5
|
+
// ============================================================================
|
|
6
|
+
export function buildContract(
|
|
7
|
+
// biome-ignore lint/suspicious/noExplicitAny: Union of all config types
|
|
8
|
+
config) {
|
|
9
|
+
const hasSseEvents = 'sseEvents' in config && config.sseEvents !== undefined;
|
|
10
|
+
if (hasSseEvents) {
|
|
11
|
+
// Delegate to SSE contract builder
|
|
12
|
+
return buildSseContract(config);
|
|
13
|
+
}
|
|
14
|
+
// Delegate to REST contract builder
|
|
15
|
+
return buildRestContract(config);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=contractBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contractBuilder.js","sourceRoot":"","sources":["../src/contractBuilder.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,iBAAiB,GAIlB,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACL,gBAAgB,GAKjB,MAAM,8BAA8B,CAAA;AAqTrC,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,MAAM,UAAU,aAAa;AAC3B,wEAAwE;AACxE,MAAW;IAGX,MAAM,YAAY,GAAG,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,CAAA;IAE5E,IAAI,YAAY,EAAE,CAAC;QACjB,mCAAmC;QACnC,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACjC,CAAC;IAED,oCAAoC;IACpC,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;AAClC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './apiContracts.ts';
|
|
2
|
+
export * from './contractBuilder.ts';
|
|
2
3
|
export * from './HttpStatusCodes.ts';
|
|
3
4
|
export * from './pathUtils.ts';
|
|
5
|
+
export * from './rest/restContractBuilder.ts';
|
|
4
6
|
export * from './sse/dualModeContracts.ts';
|
|
5
7
|
export * from './sse/sseContractBuilders.ts';
|
|
6
8
|
export * from './sse/sseContracts.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from "./apiContracts.js";
|
|
2
|
+
// Universal contract builder
|
|
3
|
+
export * from "./contractBuilder.js";
|
|
2
4
|
export * from "./HttpStatusCodes.js";
|
|
3
5
|
export * from "./pathUtils.js";
|
|
6
|
+
export * from "./rest/restContractBuilder.js";
|
|
4
7
|
// Dual-mode (hybrid) contracts
|
|
5
8
|
export * from "./sse/dualModeContracts.js";
|
|
6
9
|
// Contract builders
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,gBAAgB,CAAA;AAC9B,+BAA+B;AAC/B,cAAc,4BAA4B,CAAA;AAC1C,oBAAoB;AACpB,cAAc,8BAA8B,CAAA;AAC5C,gBAAgB;AAChB,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,6BAA6B;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,+BAA+B,CAAA;AAC7C,+BAA+B;AAC/B,cAAc,4BAA4B,CAAA;AAC1C,oBAAoB;AACpB,cAAc,8BAA8B,CAAA;AAC5C,gBAAgB;AAChB,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { z } from 'zod/v4';
|
|
2
|
+
import type { CommonRouteDefinition, DeleteRouteDefinition, GetRouteDefinition, PayloadRouteDefinition } from '../apiContracts.ts';
|
|
3
|
+
import type { HttpStatusCode } from '../HttpStatusCodes.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for building a GET route.
|
|
6
|
+
* GET routes have no request body and the method is inferred automatically.
|
|
7
|
+
*/
|
|
8
|
+
export type GetContractConfig<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined> = Omit<CommonRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>, 'method'> & {
|
|
9
|
+
method?: never;
|
|
10
|
+
requestBodySchema?: never;
|
|
11
|
+
/** Discriminator to distinguish from SSE contracts in buildContract */
|
|
12
|
+
sseEvents?: never;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for building a DELETE route.
|
|
16
|
+
* DELETE routes have no request body and default to empty response expected.
|
|
17
|
+
*/
|
|
18
|
+
export type DeleteContractConfig<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = true, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined> = Omit<CommonRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>, 'method'> & {
|
|
19
|
+
method: 'delete';
|
|
20
|
+
requestBodySchema?: never;
|
|
21
|
+
/** Discriminator to distinguish from SSE contracts in buildContract */
|
|
22
|
+
sseEvents?: never;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for building a payload route (POST, PUT, PATCH).
|
|
26
|
+
* Payload routes require a request body and an explicit method.
|
|
27
|
+
*/
|
|
28
|
+
export type PayloadContractConfig<RequestBodySchema extends z.Schema | undefined = undefined, SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined> = CommonRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode> & {
|
|
29
|
+
method: 'post' | 'put' | 'patch';
|
|
30
|
+
requestBodySchema: RequestBodySchema;
|
|
31
|
+
/** Discriminator to distinguish from SSE contracts in buildContract */
|
|
32
|
+
sseEvents?: never;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Builds REST API contracts with automatic type inference.
|
|
36
|
+
*
|
|
37
|
+
* This unified builder replaces the individual `buildGetRoute`, `buildPayloadRoute`,
|
|
38
|
+
* and `buildDeleteRoute` functions, providing a single entry point for all REST contracts.
|
|
39
|
+
*
|
|
40
|
+
* The contract type is automatically determined based on the configuration:
|
|
41
|
+
*
|
|
42
|
+
* | `method` | `requestBodySchema` | Result |
|
|
43
|
+
* |----------|---------------------|--------|
|
|
44
|
+
* | omitted/undefined | ❌ | GET route |
|
|
45
|
+
* | `'delete'` | ❌ | DELETE route |
|
|
46
|
+
* | `'post'`/`'put'`/`'patch'` | ✅ | Payload route |
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* // GET route - method is inferred automatically
|
|
51
|
+
* const getUsers = buildRestContract({
|
|
52
|
+
* pathResolver: () => '/api/users',
|
|
53
|
+
* successResponseBodySchema: z.array(userSchema),
|
|
54
|
+
* })
|
|
55
|
+
*
|
|
56
|
+
* // GET route with path params
|
|
57
|
+
* const getUser = buildRestContract({
|
|
58
|
+
* pathResolver: (params) => `/api/users/${params.userId}`,
|
|
59
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
60
|
+
* successResponseBodySchema: userSchema,
|
|
61
|
+
* })
|
|
62
|
+
*
|
|
63
|
+
* // POST route - requires method and requestBodySchema
|
|
64
|
+
* const createUser = buildRestContract({
|
|
65
|
+
* method: 'post',
|
|
66
|
+
* pathResolver: () => '/api/users',
|
|
67
|
+
* requestBodySchema: createUserSchema,
|
|
68
|
+
* successResponseBodySchema: userSchema,
|
|
69
|
+
* })
|
|
70
|
+
*
|
|
71
|
+
* // PUT route
|
|
72
|
+
* const updateUser = buildRestContract({
|
|
73
|
+
* method: 'put',
|
|
74
|
+
* pathResolver: (params) => `/api/users/${params.userId}`,
|
|
75
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
76
|
+
* requestBodySchema: updateUserSchema,
|
|
77
|
+
* successResponseBodySchema: userSchema,
|
|
78
|
+
* })
|
|
79
|
+
*
|
|
80
|
+
* // PATCH route
|
|
81
|
+
* const patchUser = buildRestContract({
|
|
82
|
+
* method: 'patch',
|
|
83
|
+
* pathResolver: (params) => `/api/users/${params.userId}`,
|
|
84
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
85
|
+
* requestBodySchema: patchUserSchema,
|
|
86
|
+
* successResponseBodySchema: userSchema,
|
|
87
|
+
* })
|
|
88
|
+
*
|
|
89
|
+
* // DELETE route - method is 'delete', no body
|
|
90
|
+
* const deleteUser = buildRestContract({
|
|
91
|
+
* method: 'delete',
|
|
92
|
+
* pathResolver: (params) => `/api/users/${params.userId}`,
|
|
93
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
94
|
+
* successResponseBodySchema: z.undefined(),
|
|
95
|
+
* })
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export declare function buildRestContract<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(config: GetContractConfig<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>): GetRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
99
|
+
export declare function buildRestContract<SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = true, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(config: DeleteContractConfig<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>): DeleteRouteDefinition<SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
100
|
+
export declare function buildRestContract<RequestBodySchema extends z.Schema | undefined = undefined, SuccessResponseBodySchema extends z.Schema | undefined = undefined, PathParamsSchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeaderSchema extends z.Schema | undefined = undefined, ResponseHeaderSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.Schema>> | undefined = undefined>(config: PayloadContractConfig<RequestBodySchema, SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>): PayloadRouteDefinition<RequestBodySchema, SuccessResponseBodySchema, PathParamsSchema, RequestQuerySchema, RequestHeaderSchema, ResponseHeaderSchema, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Implementation
|
|
2
|
+
export function buildRestContract(config) {
|
|
3
|
+
const method = config.method;
|
|
4
|
+
const hasBody = 'requestBodySchema' in config && config.requestBodySchema !== undefined;
|
|
5
|
+
// Determine default for isEmptyResponseExpected based on route type
|
|
6
|
+
const isDeleteRoute = method === 'delete';
|
|
7
|
+
const defaultIsEmptyResponseExpected = isDeleteRoute;
|
|
8
|
+
const baseFields = {
|
|
9
|
+
isEmptyResponseExpected: config.isEmptyResponseExpected ?? defaultIsEmptyResponseExpected,
|
|
10
|
+
isNonJSONResponseExpected: config.isNonJSONResponseExpected ?? false,
|
|
11
|
+
pathResolver: config.pathResolver,
|
|
12
|
+
requestHeaderSchema: config.requestHeaderSchema,
|
|
13
|
+
responseHeaderSchema: config.responseHeaderSchema,
|
|
14
|
+
requestPathParamsSchema: config.requestPathParamsSchema,
|
|
15
|
+
requestQuerySchema: config.requestQuerySchema,
|
|
16
|
+
successResponseBodySchema: config.successResponseBodySchema,
|
|
17
|
+
description: config.description,
|
|
18
|
+
summary: config.summary,
|
|
19
|
+
responseSchemasByStatusCode: config.responseSchemasByStatusCode,
|
|
20
|
+
metadata: config.metadata,
|
|
21
|
+
tags: config.tags,
|
|
22
|
+
};
|
|
23
|
+
if (hasBody) {
|
|
24
|
+
// Payload route (POST/PUT/PATCH)
|
|
25
|
+
return {
|
|
26
|
+
...baseFields,
|
|
27
|
+
method: method,
|
|
28
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type assertion needed for config union
|
|
29
|
+
requestBodySchema: config.requestBodySchema,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (isDeleteRoute) {
|
|
33
|
+
// DELETE route
|
|
34
|
+
return {
|
|
35
|
+
...baseFields,
|
|
36
|
+
method: 'delete',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// GET route (default)
|
|
40
|
+
return {
|
|
41
|
+
...baseFields,
|
|
42
|
+
method: 'get',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=restContractBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restContractBuilder.js","sourceRoot":"","sources":["../../src/rest/restContractBuilder.ts"],"names":[],"mappings":"AA+RA,iBAAiB;AACjB,MAAM,UAAU,iBAAiB,CAC/B,MAKsE;IAGtE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAC5B,MAAM,OAAO,GAAG,mBAAmB,IAAI,MAAM,IAAI,MAAM,CAAC,iBAAiB,KAAK,SAAS,CAAA;IAEvF,oEAAoE;IACpE,MAAM,aAAa,GAAG,MAAM,KAAK,QAAQ,CAAA;IACzC,MAAM,8BAA8B,GAAG,aAAa,CAAA;IAEpD,MAAM,UAAU,GAAG;QACjB,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,IAAI,8BAA8B;QACzF,yBAAyB,EAAE,MAAM,CAAC,yBAAyB,IAAI,KAAK;QACpE,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;QACjD,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;QACvD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,yBAAyB,EAAE,MAAM,CAAC,yBAAyB;QAC3D,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,2BAA2B,EAAE,MAAM,CAAC,2BAA2B;QAC/D,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,IAAI,EAAE,MAAM,CAAC,IAAI;KAClB,CAAA;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,iCAAiC;QACjC,OAAO;YACL,GAAG,UAAU;YACb,MAAM,EAAE,MAAkC;YAC1C,qFAAqF;YACrF,iBAAiB,EAAG,MAAqC,CAAC,iBAAiB;SAC5E,CAAA;IACH,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,eAAe;QACf,OAAO;YACL,GAAG,UAAU;YACb,MAAM,EAAE,QAAiB;SAC1B,CAAA;IACH,CAAC;IAED,sBAAsB;IACtB,OAAO;QACL,GAAG,UAAU;QACb,MAAM,EAAE,KAAc;KACvB,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lokalise/api-contracts",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.4.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist"
|
|
6
6
|
],
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"@biomejs/biome": "^2.3.7",
|
|
49
49
|
"@lokalise/biome-config": "^3.1.0",
|
|
50
50
|
"@lokalise/tsconfig": "^3.1.0",
|
|
51
|
-
"@vitest/coverage-v8": "^4.0.
|
|
52
|
-
"rimraf": "^6.
|
|
51
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
52
|
+
"rimraf": "^6.1.2",
|
|
53
53
|
"typescript": "5.9.3",
|
|
54
|
-
"vitest": "^4.0.
|
|
54
|
+
"vitest": "^4.0.18",
|
|
55
55
|
"zod": "^4.3.6"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {}
|