@lokalise/api-contracts 6.9.0 → 6.11.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 +252 -327
- package/dist/apiContracts.d.ts +19 -13
- package/dist/apiContracts.js +19 -13
- package/dist/apiContracts.js.map +1 -1
- package/dist/contractBuilder.d.ts +20 -0
- package/dist/contractBuilder.js.map +1 -1
- package/dist/new/contractResponse.d.ts +1 -0
- package/dist/new/contractResponse.js +1 -0
- package/dist/new/contractResponse.js.map +1 -1
- package/dist/rest/restContractBuilder.d.ts +20 -0
- package/dist/rest/restContractBuilder.js.map +1 -1
- package/dist/sse/sseContractBuilders.js +37 -0
- package/dist/sse/sseContractBuilders.js.map +1 -1
- package/package.json +56 -57
package/README.md
CHANGED
|
@@ -1,420 +1,345 @@
|
|
|
1
1
|
# api-contracts
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
instead of forming full request configuration manually on the client side.
|
|
3
|
+
API contracts are shared definitions that live in a shared package and are consumed by both the client and the backend.
|
|
4
|
+
The contract describes a route — its path, HTTP method, and request/response schemas — and serves as the single source of truth for both sides.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
The backend implements the route against the contract.
|
|
7
|
+
The client uses the same contract to make type-safe requests without duplicating configuration.
|
|
8
|
+
This eliminates assumptions across the boundary and keeps documentation, validation, and types in sync.
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Defining contracts
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
### REST routes
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
```ts
|
|
15
|
+
import { defineApiContract, ContractNoBody } from '@lokalise/api-contracts'
|
|
16
|
+
import { z } from 'zod/v4'
|
|
17
|
+
|
|
18
|
+
// GET with path params
|
|
19
|
+
const getUser = defineApiContract({
|
|
20
|
+
method: 'get',
|
|
21
|
+
requestPathParamsSchema: z.object({ userId: z.uuid() }),
|
|
22
|
+
pathResolver: ({ userId }) => `/users/${userId}`,
|
|
23
|
+
responsesByStatusCode: {
|
|
24
|
+
200: z.object({ id: z.string(), name: z.string() }),
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// POST
|
|
29
|
+
const createUser = defineApiContract({
|
|
30
|
+
method: 'post',
|
|
31
|
+
pathResolver: () => '/users',
|
|
32
|
+
requestBodySchema: z.object({ name: z.string() }),
|
|
33
|
+
responsesByStatusCode: {
|
|
34
|
+
201: z.object({ id: z.string(), name: z.string() }),
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// DELETE with no response body
|
|
39
|
+
const deleteUser = defineApiContract({
|
|
40
|
+
method: 'delete',
|
|
41
|
+
requestPathParamsSchema: z.object({ userId: z.uuid() }),
|
|
42
|
+
pathResolver: ({ userId }) => `/users/${userId}`,
|
|
43
|
+
responsesByStatusCode: {
|
|
44
|
+
204: ContractNoBody,
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Non-JSON responses
|
|
50
|
+
|
|
51
|
+
Use `textResponse` for text-based responses (plain text, CSV, HTML, XML, YAML, etc.):
|
|
19
52
|
|
|
20
53
|
```ts
|
|
21
|
-
import {
|
|
22
|
-
import { z } from 'zod'
|
|
54
|
+
import { defineApiContract, textResponse } from '@lokalise/api-contracts'
|
|
23
55
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
56
|
+
const exportCsv = defineApiContract({
|
|
57
|
+
method: 'get',
|
|
58
|
+
pathResolver: () => '/export.csv',
|
|
59
|
+
responsesByStatusCode: { 200: textResponse('text/csv') },
|
|
28
60
|
})
|
|
29
61
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
successResponseBodySchema: userSchema,
|
|
35
|
-
pathResolver: () => '/api/users',
|
|
62
|
+
const getPage = defineApiContract({
|
|
63
|
+
method: 'get',
|
|
64
|
+
pathResolver: () => '/page',
|
|
65
|
+
responsesByStatusCode: { 200: textResponse('text/html') },
|
|
36
66
|
})
|
|
37
67
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
pathResolver: (params) => `/api/users/${params.userId}`,
|
|
68
|
+
const getDocument = defineApiContract({
|
|
69
|
+
method: 'get',
|
|
70
|
+
pathResolver: () => '/document',
|
|
71
|
+
responsesByStatusCode: { 200: textResponse('application/xml') },
|
|
43
72
|
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use `blobResponse` for binary responses (images, PDFs, etc.):
|
|
44
76
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
```ts
|
|
78
|
+
import { defineApiContract, blobResponse } from '@lokalise/api-contracts'
|
|
79
|
+
|
|
80
|
+
const downloadPhoto = defineApiContract({
|
|
81
|
+
method: 'get',
|
|
82
|
+
pathResolver: () => '/photo.png',
|
|
83
|
+
responsesByStatusCode: { 200: blobResponse('image/png') },
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### SSE and dual-mode routes
|
|
88
|
+
|
|
89
|
+
Use `sseResponse()` inside `responsesByStatusCode` to define SSE event schemas.
|
|
90
|
+
For endpoints that can respond with either JSON or an SSE stream depending on the `Accept` header, use `anyOfResponses()` to declare both options on the same status code.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { defineApiContract, sseResponse, anyOfResponses } from '@lokalise/api-contracts'
|
|
94
|
+
import { z } from 'zod/v4'
|
|
95
|
+
|
|
96
|
+
// SSE-only
|
|
97
|
+
const notifications = defineApiContract({
|
|
98
|
+
method: 'get',
|
|
99
|
+
pathResolver: () => '/notifications/stream',
|
|
100
|
+
responsesByStatusCode: {
|
|
101
|
+
200: sseResponse({
|
|
102
|
+
notification: z.object({ id: z.string(), message: z.string() }),
|
|
103
|
+
}),
|
|
104
|
+
},
|
|
52
105
|
})
|
|
53
106
|
|
|
54
|
-
// Dual-mode
|
|
55
|
-
const chatCompletion =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
107
|
+
// Dual-mode: JSON response or SSE stream depending on Accept header
|
|
108
|
+
const chatCompletion = defineApiContract({
|
|
109
|
+
method: 'post',
|
|
110
|
+
pathResolver: () => '/chat/completions',
|
|
111
|
+
requestBodySchema: z.object({ message: z.string() }),
|
|
112
|
+
responsesByStatusCode: {
|
|
113
|
+
200: anyOfResponses([
|
|
114
|
+
sseResponse({
|
|
61
115
|
chunk: z.object({ delta: z.string() }),
|
|
62
|
-
done: z.object({
|
|
63
|
-
|
|
116
|
+
done: z.object({ finish_reason: z.string() }),
|
|
117
|
+
}),
|
|
118
|
+
z.object({ text: z.string() }),
|
|
119
|
+
]),
|
|
120
|
+
},
|
|
64
121
|
})
|
|
65
122
|
```
|
|
66
123
|
|
|
67
|
-
|
|
124
|
+
`getSseSchemaByEventName(contract)` extracts SSE event schemas from a contract:
|
|
68
125
|
|
|
69
|
-
|
|
126
|
+
```ts
|
|
127
|
+
import { getSseSchemaByEventName } from '@lokalise/api-contracts'
|
|
70
128
|
|
|
71
|
-
|
|
129
|
+
getSseSchemaByEventName(notifications)
|
|
130
|
+
// { notification: ZodObject<...> }
|
|
72
131
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
| `'delete'` | ❌ | DELETE route |
|
|
77
|
-
| `'post'`/`'put'`/`'patch'` | ✅ | Payload route |
|
|
132
|
+
getSseSchemaByEventName(chatCompletion)
|
|
133
|
+
// { chunk: ZodObject<...>, done: ZodObject<...> }
|
|
134
|
+
```
|
|
78
135
|
|
|
79
|
-
|
|
136
|
+
### All fields
|
|
80
137
|
|
|
81
138
|
```ts
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
139
|
+
defineApiContract({
|
|
140
|
+
// Required
|
|
141
|
+
method: 'get' | 'post' | 'put' | 'patch' | 'delete',
|
|
142
|
+
pathResolver: (pathParams) => string,
|
|
143
|
+
responsesByStatusCode: {
|
|
144
|
+
[statusCode]: z.ZodType | ContractNoBody | TypedTextResponse | TypedBlobResponse | TypedSseResponse | AnyOfResponses
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// Path params — links pathResolver parameter type to the schema
|
|
148
|
+
requestPathParamsSchema: z.ZodObject,
|
|
149
|
+
|
|
150
|
+
// Request
|
|
151
|
+
requestBodySchema: z.ZodType | ContractNoBody, // required for POST / PUT / PATCH, forbidden otherwise
|
|
152
|
+
requestQuerySchema: z.ZodObject,
|
|
153
|
+
requestHeaderSchema: z.ZodObject,
|
|
154
|
+
|
|
155
|
+
// Response
|
|
156
|
+
responseHeaderSchema: z.ZodObject,
|
|
157
|
+
|
|
158
|
+
// Documentation
|
|
159
|
+
summary: string,
|
|
160
|
+
description: string,
|
|
161
|
+
tags: readonly string[],
|
|
162
|
+
metadata: Record<string, unknown>,
|
|
94
163
|
})
|
|
164
|
+
```
|
|
95
165
|
|
|
96
|
-
|
|
97
|
-
const postContract = buildRestContract({
|
|
98
|
-
method: 'post', // can also be 'put' or 'patch'
|
|
99
|
-
successResponseBodySchema: RESPONSE_BODY_SCHEMA,
|
|
100
|
-
requestBodySchema: REQUEST_BODY_SCHEMA,
|
|
101
|
-
pathResolver: () => '/',
|
|
102
|
-
summary: 'Route summary',
|
|
103
|
-
metadata: { allowedPermission: ['edit'] },
|
|
104
|
-
})
|
|
166
|
+
### Header schemas
|
|
105
167
|
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
168
|
+
```ts
|
|
169
|
+
const contract = defineApiContract({
|
|
170
|
+
method: 'get',
|
|
171
|
+
pathResolver: () => '/api/data',
|
|
172
|
+
requestHeaderSchema: z.object({
|
|
173
|
+
authorization: z.string(),
|
|
174
|
+
'x-api-key': z.string(),
|
|
175
|
+
}),
|
|
176
|
+
responseHeaderSchema: z.object({
|
|
177
|
+
'x-ratelimit-remaining': z.string(),
|
|
178
|
+
'cache-control': z.string(),
|
|
179
|
+
}),
|
|
180
|
+
responsesByStatusCode: {
|
|
181
|
+
200: dataSchema,
|
|
182
|
+
},
|
|
112
183
|
})
|
|
113
184
|
```
|
|
114
185
|
|
|
115
|
-
###
|
|
186
|
+
### Type utilities
|
|
116
187
|
|
|
117
|
-
|
|
188
|
+
**`InferNonSseSuccessResponses<T>`** — TypeScript output type of all non-SSE 2xx responses. JSON schemas → `z.output<T>`, `textResponse` → `string`, `blobResponse` → `Blob`, `ContractNoBody` → `undefined`, `sseResponse` → `never` (excluded). `anyOfResponses` entries are unpacked before mapping.
|
|
118
189
|
|
|
119
190
|
```ts
|
|
120
|
-
|
|
121
|
-
import { buildGetRoute, buildPayloadRoute, buildDeleteRoute } from '@lokalise/api-contracts'
|
|
122
|
-
|
|
123
|
-
const getContract = buildGetRoute({ ... })
|
|
124
|
-
const postContract = buildPayloadRoute({ method: 'post', ... })
|
|
125
|
-
const deleteContract = buildDeleteRoute({ ... })
|
|
191
|
+
import type { InferNonSseSuccessResponses } from '@lokalise/api-contracts'
|
|
126
192
|
|
|
127
|
-
|
|
128
|
-
|
|
193
|
+
type UserResponse = InferNonSseSuccessResponses<typeof getUser['responsesByStatusCode']>
|
|
194
|
+
// { id: string; name: string }
|
|
129
195
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const deleteContract = buildRestContract({ method: 'delete', ... })
|
|
196
|
+
type CsvResponse = InferNonSseSuccessResponses<typeof exportCsv['responsesByStatusCode']>
|
|
197
|
+
// string
|
|
133
198
|
```
|
|
134
199
|
|
|
135
|
-
|
|
136
|
-
information related to the route. If you require more precise type definitions for the `metadata` field, you can utilize
|
|
137
|
-
TypeScript's module augmentation mechanism to enforce stricter typing. This allows for more controlled and type-safe
|
|
138
|
-
usage in your route definitions.
|
|
200
|
+
**`InferJsonSuccessResponses<T>`** — union of Zod schema types for all JSON 2xx entries. Text, Blob, SSE, and `ContractNoBody` entries are excluded.
|
|
139
201
|
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
// file -> apiContracts.d.ts
|
|
143
|
-
// Import the existing module to ensure TypeScript recognizes the original definitions
|
|
144
|
-
import '@lokalise/api-contracts';
|
|
202
|
+
**`InferSseSuccessResponses<T>`** — extracts the SSE event schema map type from a `responsesByStatusCode` map. Returns `never` when no SSE schemas are present.
|
|
145
203
|
|
|
146
|
-
|
|
147
|
-
declare module '@lokalise/api-contracts' {
|
|
148
|
-
interface CommonRouteDefinitionMetadata {
|
|
149
|
-
myTestProp?: string[];
|
|
150
|
-
mySecondTestProp?: number;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
```
|
|
204
|
+
**`HasAnySseSuccessResponse<T>`** — `true` if any 2xx entry is a `TypedSseResponse` or an `AnyOfResponses` containing one.
|
|
154
205
|
|
|
155
|
-
|
|
156
|
-
(`@lokalise/frontend-http-client` or `@lokalise/backend-http-client`)
|
|
206
|
+
**`HasAnyJsonSuccessResponse<T>`** — `true` if any 2xx entry is a JSON Zod schema or an `AnyOfResponses` containing one.
|
|
157
207
|
|
|
158
|
-
|
|
208
|
+
**`HasAnyNonSseSuccessResponse<T>`** — `true` if any 2xx entry is a non-SSE response (JSON, text, blob, or no-body).
|
|
159
209
|
|
|
160
|
-
|
|
210
|
+
**`IsNoBodySuccessResponse<T>`** — `true` when all 2xx entries are `ContractNoBody` or no 2xx status codes are defined.
|
|
161
211
|
|
|
162
|
-
|
|
212
|
+
**`ContractResponseMode<T>`** — classifies a contract into `'dual'` (SSE + non-SSE), `'sse'` (SSE-only), or `'non-sse'` (JSON/text/blob/no-body).
|
|
163
213
|
|
|
164
|
-
|
|
214
|
+
**`AvailableResponseModes<T>`** — union of mode literals available for a contract: `'json' | 'sse' | 'blob' | 'text' | 'noContent'`.
|
|
215
|
+
|
|
216
|
+
**`SseEventOf<S>`** — discriminated union of SSE events inferred from a `schemaByEventName` map. Aligns with the browser `MessageEvent` shape: `{ type, data, lastEventId, retry }`.
|
|
165
217
|
|
|
166
218
|
```ts
|
|
167
|
-
import {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
requestHeaderSchema: z.object({
|
|
173
|
-
'authorization': z.string(),
|
|
174
|
-
'x-api-key': z.string(),
|
|
175
|
-
'accept-language': z.string().optional(),
|
|
176
|
-
}),
|
|
177
|
-
pathResolver: () => '/api/data',
|
|
178
|
-
})
|
|
219
|
+
import type { SseEventOf, InferSseSuccessResponses } from '@lokalise/api-contracts'
|
|
220
|
+
|
|
221
|
+
type NotificationEvents = InferSseSuccessResponses<typeof notifications['responsesByStatusCode']>
|
|
222
|
+
type NotificationEvent = SseEventOf<NotificationEvents>
|
|
223
|
+
// { type: 'notification'; data: { id: string; message: string }; lastEventId: string; retry: number | undefined }
|
|
179
224
|
```
|
|
180
225
|
|
|
181
|
-
###
|
|
226
|
+
### Client types
|
|
182
227
|
|
|
183
|
-
|
|
184
|
-
- Rate limiting headers
|
|
185
|
-
- Pagination headers
|
|
186
|
-
- Cache control headers
|
|
187
|
-
- Custom API metadata headers
|
|
228
|
+
These types are primarily consumed by HTTP client implementations.
|
|
188
229
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
successResponseBodySchema: DATA_SCHEMA,
|
|
195
|
-
responseHeaderSchema: z.object({
|
|
196
|
-
'x-ratelimit-limit': z.string(),
|
|
197
|
-
'x-ratelimit-remaining': z.string(),
|
|
198
|
-
'x-ratelimit-reset': z.string(),
|
|
199
|
-
'cache-control': z.string(),
|
|
200
|
-
}),
|
|
201
|
-
pathResolver: () => '/api/data',
|
|
202
|
-
})
|
|
203
|
-
```
|
|
230
|
+
**`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).
|
|
231
|
+
|
|
232
|
+
**`InferSseClientResponse<TApiContract>`** — discriminated union of `{ statusCode, headers, body }` for SSE mode. Success status codes yield `AsyncIterable<SseEventOf<...>>`; error codes yield the declared body type.
|
|
233
|
+
|
|
234
|
+
**`InferNonSseClientResponse<TApiContract>`** — same shape as above for non-SSE mode. Success status codes yield JSON / `string` / `Blob` / `null`; SSE entries are excluded (`never`).
|
|
204
235
|
|
|
205
|
-
|
|
236
|
+
**`DefaultStreaming<T>`** — `true` for SSE-only contracts, `false` for everything else.
|
|
206
237
|
|
|
207
238
|
```ts
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
'x-ratelimit-remaining': z.string(),
|
|
216
|
-
}),
|
|
217
|
-
pathResolver: () => '/api/data',
|
|
218
|
-
})
|
|
239
|
+
import type { ClientRequestParams, InferNonSseClientResponse } from '@lokalise/api-contracts'
|
|
240
|
+
|
|
241
|
+
type GetUserParams = ClientRequestParams<typeof getUser, false>
|
|
242
|
+
// { pathParams: { userId: string }; pathPrefix?: string }
|
|
243
|
+
|
|
244
|
+
type GetUserResponse = InferNonSseClientResponse<typeof getUser>
|
|
245
|
+
// { statusCode: 200; headers: Record<string, string>; body: { id: string; name: string } }
|
|
219
246
|
```
|
|
220
247
|
|
|
221
|
-
|
|
222
|
-
- OpenAPI/Swagger documentation generation
|
|
223
|
-
- Client-side validation of response headers
|
|
224
|
-
- Type-safe header access in TypeScript
|
|
225
|
-
- Contract testing between frontend and backend
|
|
248
|
+
### Contract type aliases
|
|
226
249
|
|
|
227
|
-
|
|
250
|
+
**`ApiContract`** — union of all contract variants (`GetApiContract | DeleteApiContract | PayloadApiContract`). Use this to type function parameters that accept any contract.
|
|
228
251
|
|
|
229
|
-
|
|
252
|
+
**`GetApiContract`**, **`DeleteApiContract`**, **`PayloadApiContract`** — individual contract variants if you need to narrow the type.
|
|
230
253
|
|
|
231
|
-
|
|
254
|
+
**`RequestPathParamsSchema`**, **`RequestQuerySchema`**, **`RequestHeaderSchema`**, **`ResponseHeaderSchema`** — type aliases for `z.ZodObject`. Use these to constrain schema arguments in generic helpers.
|
|
232
255
|
|
|
233
|
-
|
|
234
|
-
import { mapRouteToPath, buildRestContract } from '@lokalise/api-contracts'
|
|
256
|
+
### Utility functions
|
|
235
257
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
})
|
|
258
|
+
**`mapApiContractToPath`** — Express/Fastify-style path pattern.
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import { mapApiContractToPath } from '@lokalise/api-contracts'
|
|
241
262
|
|
|
242
|
-
|
|
243
|
-
// Returns: "/users/:userId"
|
|
263
|
+
mapApiContractToPath(getUser) // "/users/:userId"
|
|
244
264
|
```
|
|
245
265
|
|
|
246
|
-
|
|
247
|
-
- Generate OpenAPI/Swagger documentation
|
|
248
|
-
- Create route patterns for server-side routing frameworks
|
|
249
|
-
- Display route information in debugging or logging
|
|
266
|
+
**`describeApiContract`** — human-readable `"METHOD /path"` string.
|
|
250
267
|
|
|
251
|
-
|
|
268
|
+
```ts
|
|
269
|
+
import { describeApiContract } from '@lokalise/api-contracts'
|
|
252
270
|
|
|
253
|
-
|
|
271
|
+
describeApiContract(getUser) // "GET /users/:userId"
|
|
272
|
+
```
|
|
254
273
|
|
|
255
|
-
|
|
274
|
+
**`getSuccessResponseSchema`** — merged Zod schema from all 2xx JSON entries. `ContractNoBody` and non-JSON entries are excluded. Returns `null` when no schema is present.
|
|
256
275
|
|
|
257
276
|
```ts
|
|
258
|
-
import {
|
|
277
|
+
import { getSuccessResponseSchema } from '@lokalise/api-contracts'
|
|
259
278
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
pathResolver: (pathParams) => `/users/${pathParams.userId}`,
|
|
264
|
-
})
|
|
279
|
+
getSuccessResponseSchema(getUser) // ZodObject
|
|
280
|
+
getSuccessResponseSchema(deleteUser) // null
|
|
281
|
+
```
|
|
265
282
|
|
|
266
|
-
|
|
267
|
-
method: 'post',
|
|
268
|
-
requestPathParamsSchema: z.object({
|
|
269
|
-
orgId: z.string(),
|
|
270
|
-
userId: z.string()
|
|
271
|
-
}),
|
|
272
|
-
requestBodySchema: CREATE_USER_SCHEMA,
|
|
273
|
-
successResponseBodySchema: USER_SCHEMA,
|
|
274
|
-
pathResolver: (pathParams) => `/orgs/${pathParams.orgId}/users/${pathParams.userId}`,
|
|
275
|
-
})
|
|
283
|
+
**`getIsEmptyResponseExpected`** — `true` when no Zod schema exists among 2xx entries.
|
|
276
284
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
```
|
|
285
|
+
```ts
|
|
286
|
+
import { getIsEmptyResponseExpected } from '@lokalise/api-contracts'
|
|
280
287
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
- Error messages that need to reference specific endpoints
|
|
285
|
-
- Test descriptions and assertions
|
|
288
|
+
getIsEmptyResponseExpected(deleteUser) // true
|
|
289
|
+
getIsEmptyResponseExpected(getUser) // false
|
|
290
|
+
```
|
|
286
291
|
|
|
287
|
-
|
|
292
|
+
**`hasAnySuccessSseResponse`** — `true` when any 2xx entry is an SSE response (including inside `anyOfResponses`).
|
|
288
293
|
|
|
289
|
-
|
|
294
|
+
```ts
|
|
295
|
+
import { hasAnySuccessSseResponse } from '@lokalise/api-contracts'
|
|
290
296
|
|
|
291
|
-
|
|
297
|
+
hasAnySuccessSseResponse(notifications) // true
|
|
298
|
+
hasAnySuccessSseResponse(getUser) // false
|
|
299
|
+
hasAnySuccessSseResponse(chatCompletion) // true (dual-mode)
|
|
300
|
+
```
|
|
292
301
|
|
|
293
|
-
|
|
302
|
+
**`getSseSchemaByEventName`** — extracts SSE event schemas from a contract. Returns `null` when no SSE schemas are present.
|
|
294
303
|
|
|
295
304
|
```ts
|
|
296
|
-
import {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
//
|
|
300
|
-
// requestPathParamsSchema, requestQuerySchema, requestHeaderSchema are optional
|
|
301
|
-
const notificationsStream = buildSseContract({
|
|
302
|
-
method: 'get',
|
|
303
|
-
pathResolver: () => '/api/notifications/stream',
|
|
304
|
-
requestQuerySchema: z.object({ userId: z.string().optional() }),
|
|
305
|
-
serverSentEventSchemas: {
|
|
306
|
-
notification: z.object({ id: z.string(), message: z.string() }),
|
|
307
|
-
},
|
|
308
|
-
})
|
|
309
|
-
// Result: isSSE: true
|
|
310
|
-
|
|
311
|
-
// POST SSE endpoint (e.g., streaming file processing)
|
|
312
|
-
const processStream = buildSseContract({
|
|
313
|
-
method: 'post',
|
|
314
|
-
pathResolver: () => '/api/process/stream',
|
|
315
|
-
requestBodySchema: z.object({ fileId: z.string() }),
|
|
316
|
-
serverSentEventSchemas: {
|
|
317
|
-
progress: z.object({ percent: z.number() }),
|
|
318
|
-
done: z.object({ result: z.string() }),
|
|
319
|
-
},
|
|
320
|
-
})
|
|
321
|
-
// Result: isSSE: true
|
|
322
|
-
|
|
323
|
-
// SSE endpoint with error schemas (for errors before streaming starts)
|
|
324
|
-
const channelStream = buildSseContract({
|
|
325
|
-
method: 'get',
|
|
326
|
-
pathResolver: (params) => `/api/channels/${params.channelId}/stream`,
|
|
327
|
-
requestPathParamsSchema: z.object({ channelId: z.string() }),
|
|
328
|
-
requestHeaderSchema: z.object({ authorization: z.string() }),
|
|
329
|
-
serverSentEventSchemas: {
|
|
330
|
-
message: z.object({ text: z.string() }),
|
|
331
|
-
},
|
|
332
|
-
// Errors returned before streaming begins
|
|
333
|
-
responseBodySchemasByStatusCode: {
|
|
334
|
-
401: z.object({ error: z.literal('Unauthorized') }),
|
|
335
|
-
404: z.object({ error: z.literal('Channel not found') }),
|
|
336
|
-
},
|
|
337
|
-
})
|
|
305
|
+
import { getSseSchemaByEventName } from '@lokalise/api-contracts'
|
|
306
|
+
|
|
307
|
+
getSseSchemaByEventName(notifications) // { notification: ZodObject<...> }
|
|
308
|
+
getSseSchemaByEventName(getUser) // null
|
|
338
309
|
```
|
|
339
310
|
|
|
340
|
-
|
|
311
|
+
## Module augmentation
|
|
341
312
|
|
|
342
|
-
|
|
343
|
-
- `Accept: application/json` → Synchronous JSON response
|
|
344
|
-
- `Accept: text/event-stream` → SSE streaming
|
|
313
|
+
If you require more precise type definitions for the `metadata` field, you can utilize TypeScript's module augmentation mechanism to enforce stricter typing:
|
|
345
314
|
|
|
346
|
-
|
|
315
|
+
```typescript
|
|
316
|
+
// file -> apiContracts.d.ts
|
|
317
|
+
import '@lokalise/api-contracts';
|
|
347
318
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
// - Accept: text/event-stream → streams chunk events, then done event
|
|
355
|
-
const chatCompletion = buildSseContract({
|
|
356
|
-
method: 'post',
|
|
357
|
-
pathResolver: () => '/api/chat/completions',
|
|
358
|
-
requestBodySchema: z.object({ message: z.string() }),
|
|
359
|
-
// Adding successResponseBodySchema makes it dual-mode
|
|
360
|
-
successResponseBodySchema: z.object({
|
|
361
|
-
reply: z.string(),
|
|
362
|
-
usage: z.object({ tokens: z.number() }),
|
|
363
|
-
}),
|
|
364
|
-
serverSentEventSchemas: {
|
|
365
|
-
chunk: z.object({ delta: z.string() }),
|
|
366
|
-
done: z.object({ usage: z.object({ totalTokens: z.number() }) }),
|
|
367
|
-
},
|
|
368
|
-
})
|
|
369
|
-
// Result: isDualMode: true
|
|
370
|
-
|
|
371
|
-
// GET dual-mode endpoint for job status (poll or stream)
|
|
372
|
-
const jobStatus = buildSseContract({
|
|
373
|
-
method: 'get',
|
|
374
|
-
pathResolver: (params) => `/api/jobs/${params.jobId}/status`,
|
|
375
|
-
requestPathParamsSchema: z.object({ jobId: z.string().uuid() }),
|
|
376
|
-
requestQuerySchema: z.object({ verbose: z.string().optional() }),
|
|
377
|
-
successResponseBodySchema: z.object({
|
|
378
|
-
status: z.enum(['pending', 'running', 'completed', 'failed']),
|
|
379
|
-
progress: z.number(),
|
|
380
|
-
}),
|
|
381
|
-
serverSentEventSchemas: {
|
|
382
|
-
progress: z.object({ percent: z.number() }),
|
|
383
|
-
done: z.object({ result: z.string() }),
|
|
384
|
-
},
|
|
385
|
-
})
|
|
386
|
-
// Result: isDualMode: true
|
|
319
|
+
declare module '@lokalise/api-contracts' {
|
|
320
|
+
interface CommonRouteDefinitionMetadata {
|
|
321
|
+
myTestProp?: string[];
|
|
322
|
+
mySecondTestProp?: number;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
387
325
|
```
|
|
388
326
|
|
|
389
|
-
|
|
327
|
+
## HTTP clients
|
|
390
328
|
|
|
391
|
-
|
|
329
|
+
To make contract-based requests, use a compatible HTTP client (`@lokalise/frontend-http-client` or `@lokalise/backend-http-client`).
|
|
392
330
|
|
|
393
|
-
|
|
394
|
-
const chatCompletion = buildSseContract({
|
|
395
|
-
method: 'post',
|
|
396
|
-
pathResolver: () => '/api/chat/completions',
|
|
397
|
-
requestHeaderSchema: z.object({ authorization: z.string() }),
|
|
398
|
-
requestBodySchema: z.object({ message: z.string() }),
|
|
399
|
-
successResponseBodySchema: z.object({ reply: z.string() }),
|
|
400
|
-
serverSentEventSchemas: {
|
|
401
|
-
chunk: z.object({ delta: z.string() }),
|
|
402
|
-
},
|
|
403
|
-
responseBodySchemasByStatusCode: {
|
|
404
|
-
400: z.object({ error: z.string(), details: z.array(z.string()) }),
|
|
405
|
-
401: z.object({ error: z.literal('Unauthorized') }),
|
|
406
|
-
429: z.object({ error: z.string(), retryAfter: z.number() }),
|
|
407
|
-
},
|
|
408
|
-
})
|
|
409
|
-
```
|
|
331
|
+
For Fastify backends, use `@lokalise/fastify-api-contracts` to simplify route definition using contracts as the single source of truth.
|
|
410
332
|
|
|
411
|
-
|
|
333
|
+
## Future: request body content type
|
|
412
334
|
|
|
413
|
-
`
|
|
335
|
+
Currently, HTTP clients default to `application/json` when a request body is present. The planned improvement is a `requestBodyContentType` field on `defineApiContract`:
|
|
414
336
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
337
|
+
```ts
|
|
338
|
+
defineApiContract({
|
|
339
|
+
method: 'post',
|
|
340
|
+
pathResolver: () => '/upload',
|
|
341
|
+
requestBodySchema: z.object({ file: z.unknown() }),
|
|
342
|
+
requestBodyContentType: 'multipart/form-data',
|
|
343
|
+
responsesByStatusCode: { 200: z.object({ url: z.string() }) },
|
|
344
|
+
})
|
|
345
|
+
```
|
package/dist/apiContracts.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export type DeleteRouteDefinition<SuccessResponseBodySchema extends z.Schema | u
|
|
|
59
59
|
method: 'delete';
|
|
60
60
|
};
|
|
61
61
|
/**
|
|
62
|
-
* @deprecated Use `
|
|
62
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
63
63
|
* @example
|
|
64
64
|
* ```typescript
|
|
65
65
|
* // Before (deprecated):
|
|
@@ -71,17 +71,17 @@ export type DeleteRouteDefinition<SuccessResponseBodySchema extends z.Schema | u
|
|
|
71
71
|
* })
|
|
72
72
|
*
|
|
73
73
|
* // After (recommended):
|
|
74
|
-
* const route =
|
|
74
|
+
* const route = defineApiContract({
|
|
75
75
|
* method: 'post',
|
|
76
76
|
* requestBodySchema: bodySchema,
|
|
77
|
-
* successResponseBodySchema: responseSchema,
|
|
78
77
|
* pathResolver: () => '/api/users',
|
|
78
|
+
* responsesByStatusCode: { 201: responseSchema },
|
|
79
79
|
* })
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
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
83
|
/**
|
|
84
|
-
* @deprecated Use `
|
|
84
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
85
85
|
* @example
|
|
86
86
|
* ```typescript
|
|
87
87
|
* // Before (deprecated):
|
|
@@ -91,34 +91,40 @@ export declare function buildPayloadRoute<RequestBodySchema extends z.Schema | u
|
|
|
91
91
|
* })
|
|
92
92
|
*
|
|
93
93
|
* // After (recommended):
|
|
94
|
-
* const route =
|
|
95
|
-
*
|
|
94
|
+
* const route = defineApiContract({
|
|
95
|
+
* method: 'get',
|
|
96
96
|
* pathResolver: () => '/api/users',
|
|
97
|
+
* responsesByStatusCode: { 200: responseSchema },
|
|
97
98
|
* })
|
|
98
99
|
* ```
|
|
99
100
|
*/
|
|
100
101
|
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
|
/**
|
|
102
|
-
* @deprecated Use `
|
|
103
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
103
104
|
* @example
|
|
104
105
|
* ```typescript
|
|
105
106
|
* // Before (deprecated):
|
|
106
107
|
* const route = buildDeleteRoute({
|
|
107
|
-
*
|
|
108
|
-
* pathResolver: () =>
|
|
108
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
109
|
+
* pathResolver: (params) => `/api/users/${params.userId}`,
|
|
109
110
|
* })
|
|
110
111
|
*
|
|
111
112
|
* // After (recommended):
|
|
112
|
-
* const route =
|
|
113
|
+
* const route = defineApiContract({
|
|
113
114
|
* method: 'delete',
|
|
114
|
-
*
|
|
115
|
-
* pathResolver: () =>
|
|
115
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
116
|
+
* pathResolver: ({ userId }) => `/api/users/${userId}`,
|
|
117
|
+
* responsesByStatusCode: { 204: ContractNoBody },
|
|
116
118
|
* })
|
|
117
119
|
* ```
|
|
118
120
|
*/
|
|
119
121
|
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>;
|
|
120
122
|
/**
|
|
121
|
-
*
|
|
123
|
+
* @deprecated Use `mapApiContractToPath` instead.
|
|
124
|
+
* Maps a route definition to a path string of the format '/static-path-part/:path-param-value'.
|
|
122
125
|
*/
|
|
123
126
|
export declare function mapRouteToPath(routeDefinition: CommonRouteDefinition<any, any, any, any, any, any, any, any>): string;
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated Use `describeApiContract` instead.
|
|
129
|
+
*/
|
|
124
130
|
export declare function describeContract(contract: PayloadRouteDefinition<any, any, any, any, any, any, any, any, any> | GetRouteDefinition<any, any, any, any, any, any, any, any> | DeleteRouteDefinition<any, any, any, any, any, any, any, any>): string;
|
package/dist/apiContracts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const EMPTY_PARAMS = {};
|
|
2
2
|
/**
|
|
3
|
-
* @deprecated Use `
|
|
3
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
4
4
|
* @example
|
|
5
5
|
* ```typescript
|
|
6
6
|
* // Before (deprecated):
|
|
@@ -12,11 +12,11 @@ const EMPTY_PARAMS = {};
|
|
|
12
12
|
* })
|
|
13
13
|
*
|
|
14
14
|
* // After (recommended):
|
|
15
|
-
* const route =
|
|
15
|
+
* const route = defineApiContract({
|
|
16
16
|
* method: 'post',
|
|
17
17
|
* requestBodySchema: bodySchema,
|
|
18
|
-
* successResponseBodySchema: responseSchema,
|
|
19
18
|
* pathResolver: () => '/api/users',
|
|
19
|
+
* responsesByStatusCode: { 201: responseSchema },
|
|
20
20
|
* })
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
@@ -40,7 +40,7 @@ export function buildPayloadRoute(params) {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
* @deprecated Use `
|
|
43
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
44
44
|
* @example
|
|
45
45
|
* ```typescript
|
|
46
46
|
* // Before (deprecated):
|
|
@@ -50,9 +50,10 @@ export function buildPayloadRoute(params) {
|
|
|
50
50
|
* })
|
|
51
51
|
*
|
|
52
52
|
* // After (recommended):
|
|
53
|
-
* const route =
|
|
54
|
-
*
|
|
53
|
+
* const route = defineApiContract({
|
|
54
|
+
* method: 'get',
|
|
55
55
|
* pathResolver: () => '/api/users',
|
|
56
|
+
* responsesByStatusCode: { 200: responseSchema },
|
|
56
57
|
* })
|
|
57
58
|
* ```
|
|
58
59
|
*/
|
|
@@ -75,20 +76,21 @@ export function buildGetRoute(params) {
|
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
/**
|
|
78
|
-
* @deprecated Use `
|
|
79
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
79
80
|
* @example
|
|
80
81
|
* ```typescript
|
|
81
82
|
* // Before (deprecated):
|
|
82
83
|
* const route = buildDeleteRoute({
|
|
83
|
-
*
|
|
84
|
-
* pathResolver: () =>
|
|
84
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
85
|
+
* pathResolver: (params) => `/api/users/${params.userId}`,
|
|
85
86
|
* })
|
|
86
87
|
*
|
|
87
88
|
* // After (recommended):
|
|
88
|
-
* const route =
|
|
89
|
+
* const route = defineApiContract({
|
|
89
90
|
* method: 'delete',
|
|
90
|
-
*
|
|
91
|
-
* pathResolver: () =>
|
|
91
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
92
|
+
* pathResolver: ({ userId }) => `/api/users/${userId}`,
|
|
93
|
+
* responsesByStatusCode: { 204: ContractNoBody },
|
|
92
94
|
* })
|
|
93
95
|
* ```
|
|
94
96
|
*/
|
|
@@ -111,7 +113,8 @@ export function buildDeleteRoute(params) {
|
|
|
111
113
|
};
|
|
112
114
|
}
|
|
113
115
|
/**
|
|
114
|
-
*
|
|
116
|
+
* @deprecated Use `mapApiContractToPath` instead.
|
|
117
|
+
* Maps a route definition to a path string of the format '/static-path-part/:path-param-value'.
|
|
115
118
|
*/
|
|
116
119
|
export function mapRouteToPath(
|
|
117
120
|
// biome-ignore lint/suspicious/noExplicitAny: We don't care about types here, we just need Zod schema
|
|
@@ -126,6 +129,9 @@ routeDefinition) {
|
|
|
126
129
|
}
|
|
127
130
|
return routeDefinition.pathResolver(resolverParams);
|
|
128
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* @deprecated Use `describeApiContract` instead.
|
|
134
|
+
*/
|
|
129
135
|
export function describeContract(contract) {
|
|
130
136
|
return `${contract.method.toUpperCase()} ${mapRouteToPath(contract)}`;
|
|
131
137
|
}
|
package/dist/apiContracts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.js","sourceRoot":"","sources":["../src/apiContracts.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,EAAE,CAAA;AA8JvB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,iBAAiB,CAa/B,MAUC;IAYD,OAAO;QACL,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,IAAK,KAAiC;QAC7F,yBAAyB,EACvB,MAAM,CAAC,yBAAyB,IAAK,KAAmC;QAC1E,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,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;AACH,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"apiContracts.js","sourceRoot":"","sources":["../src/apiContracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,26 @@ import { type DualModeGetContractConfig, type DualModePayloadContractConfig, typ
|
|
|
7
7
|
import type { SSEContractDefinition } from './sse/sseContracts.ts';
|
|
8
8
|
import type { SSEEventSchemas } from './sse/sseTypes.ts';
|
|
9
9
|
/**
|
|
10
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // Before (deprecated):
|
|
14
|
+
* const contract = buildContract({
|
|
15
|
+
* method: 'post',
|
|
16
|
+
* requestBodySchema: bodySchema,
|
|
17
|
+
* successResponseBodySchema: responseSchema,
|
|
18
|
+
* pathResolver: () => '/api/resource',
|
|
19
|
+
* })
|
|
20
|
+
*
|
|
21
|
+
* // After (recommended):
|
|
22
|
+
* const contract = defineApiContract({
|
|
23
|
+
* method: 'post',
|
|
24
|
+
* requestBodySchema: bodySchema,
|
|
25
|
+
* pathResolver: () => '/api/resource',
|
|
26
|
+
* responsesByStatusCode: { 201: responseSchema },
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
10
30
|
* Universal contract builder that creates either REST or SSE contracts based on configuration.
|
|
11
31
|
*
|
|
12
32
|
* This is a unified entry point that delegates to:
|
|
@@ -1 +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;
|
|
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;AA2UrC,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,MAAM,UAAU,aAAa;AAC3B,wEAAwE;AACxE,MAAW;IAGX,MAAM,YAAY,GAChB,wBAAwB,IAAI,MAAM,IAAI,MAAM,CAAC,sBAAsB,KAAK,SAAS,CAAA;IAEnF,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"}
|
|
@@ -21,6 +21,7 @@ export type TypedSseResponse<T extends SseSchemaByEventName = SseSchemaByEventNa
|
|
|
21
21
|
export declare const sseResponse: <T extends SseSchemaByEventName>(schemaByEventName: T) => TypedSseResponse<T>;
|
|
22
22
|
export declare const isSseResponse: (value: ApiContractResponse) => value is TypedSseResponse;
|
|
23
23
|
export type TypedJsonResponse = z.ZodType;
|
|
24
|
+
export declare const isJsonResponse: (value: ApiContractResponse) => value is TypedJsonResponse;
|
|
24
25
|
export type TypedApiContractResponse = TypedJsonResponse | TypedTextResponse | TypedBlobResponse | TypedSseResponse;
|
|
25
26
|
export type AnyOfResponses<T extends TypedApiContractResponse = TypedApiContractResponse> = {
|
|
26
27
|
readonly _tag: 'AnyOfResponses';
|
|
@@ -14,6 +14,7 @@ export const sseResponse = (schemaByEventName) => ({
|
|
|
14
14
|
schemaByEventName,
|
|
15
15
|
});
|
|
16
16
|
export const isSseResponse = (value) => typeof value === 'object' && value !== null && '_tag' in value && value._tag === 'SseResponse';
|
|
17
|
+
export const isJsonResponse = (value) => typeof value === 'object' && value !== null && !('_tag' in value);
|
|
17
18
|
export const anyOfResponses = (responses) => ({
|
|
18
19
|
_tag: 'AnyOfResponses',
|
|
19
20
|
responses,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contractResponse.js","sourceRoot":"","sources":["../../src/new/contractResponse.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAO/C,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAqB,EAAE,CAAC,CAAC;IACvE,IAAI,EAAE,cAAc;IACpB,WAAW;CACZ,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAA0B,EAA8B,EAAE,CACvF,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,CAAA;AAOjG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAqB,EAAE,CAAC,CAAC;IACvE,IAAI,EAAE,cAAc;IACpB,WAAW;CACZ,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAA0B,EAA8B,EAAE,CACvF,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,CAAA;AASjG,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,iBAAoB,EACC,EAAE,CAAC,CAAC;IACzB,IAAI,EAAE,aAAa;IACnB,iBAAiB;CAClB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAA0B,EAA6B,EAAE,CACrF,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"contractResponse.js","sourceRoot":"","sources":["../../src/new/contractResponse.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,26 @@ export type PayloadContractConfig<RequestBodySchema extends z.Schema | undefined
|
|
|
32
32
|
serverSentEventSchemas?: never;
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
|
+
* @deprecated Use `defineApiContract` instead. This function will be removed in a future version.
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Before (deprecated):
|
|
39
|
+
* const contract = buildRestContract({
|
|
40
|
+
* method: 'get',
|
|
41
|
+
* pathResolver: (params) => `/users/${params.userId}`,
|
|
42
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
43
|
+
* successResponseBodySchema: userSchema,
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* // After (recommended):
|
|
47
|
+
* const contract = defineApiContract({
|
|
48
|
+
* method: 'get',
|
|
49
|
+
* pathResolver: ({ userId }) => `/users/${userId}`,
|
|
50
|
+
* requestPathParamsSchema: z.object({ userId: z.string() }),
|
|
51
|
+
* responsesByStatusCode: { 200: userSchema },
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
35
55
|
* Builds REST API contracts with automatic type inference.
|
|
36
56
|
*
|
|
37
57
|
* This unified builder replaces the individual `buildGetRoute`, `buildPayloadRoute`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restContractBuilder.js","sourceRoot":"","sources":["../../src/rest/restContractBuilder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"restContractBuilder.js","sourceRoot":"","sources":["../../src/rest/restContractBuilder.ts"],"names":[],"mappings":"AAqTA,iBAAiB;AACjB,MAAM,UAAU,iBAAiB,CAC/B,MAKsE;IAGtE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;IAEzB,MAAM,UAAU,GAAG;QACjB,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,IAAI,MAAM,KAAK,QAAQ;QAC9E,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,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QAChE,OAAO;YACL,GAAG,UAAU;YACb,MAAM;YACN,qFAAqF;YACrF,iBAAiB,EAAG,MAAqC,CAAC,iBAAiB;SAC5E,CAAA;IACH,CAAC;IAED,OAAO;QACL,GAAG,UAAU;QACb,MAAM;KACP,CAAA;AACH,CAAC"}
|
|
@@ -1,4 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @deprecated Use `defineApiContract` with `sseResponse()` or `anyOfResponses()` instead. This function will be removed in a future version.
|
|
3
|
+
* @example
|
|
4
|
+
* ```typescript
|
|
5
|
+
* // SSE-only — Before (deprecated):
|
|
6
|
+
* const contract = buildSseContract({
|
|
7
|
+
* method: 'get',
|
|
8
|
+
* pathResolver: () => '/stream',
|
|
9
|
+
* serverSentEventSchemas: { event: eventSchema },
|
|
10
|
+
* })
|
|
11
|
+
*
|
|
12
|
+
* // SSE-only — After (recommended):
|
|
13
|
+
* const contract = defineApiContract({
|
|
14
|
+
* method: 'get',
|
|
15
|
+
* pathResolver: () => '/stream',
|
|
16
|
+
* responsesByStatusCode: { 200: sseResponse({ event: eventSchema }) },
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* // Dual-mode — Before (deprecated):
|
|
20
|
+
* const contract = buildSseContract({
|
|
21
|
+
* method: 'post',
|
|
22
|
+
* pathResolver: () => '/stream',
|
|
23
|
+
* requestBodySchema: bodySchema,
|
|
24
|
+
* successResponseBodySchema: jsonSchema,
|
|
25
|
+
* serverSentEventSchemas: { chunk: chunkSchema },
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* // Dual-mode — After (recommended):
|
|
29
|
+
* const contract = defineApiContract({
|
|
30
|
+
* method: 'post',
|
|
31
|
+
* pathResolver: () => '/stream',
|
|
32
|
+
* requestBodySchema: bodySchema,
|
|
33
|
+
* responsesByStatusCode: {
|
|
34
|
+
* 200: anyOfResponses([sseResponse({ chunk: chunkSchema }), jsonSchema]),
|
|
35
|
+
* },
|
|
36
|
+
* })
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
2
39
|
* Builds SSE (Server-Sent Events) and dual-mode contracts.
|
|
3
40
|
*
|
|
4
41
|
* This builder supports two contract types:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sseContractBuilders.js","sourceRoot":"","sources":["../../src/sse/sseContractBuilders.ts"],"names":[],"mappings":"AA2MA
|
|
1
|
+
{"version":3,"file":"sseContractBuilders.js","sourceRoot":"","sources":["../../src/sse/sseContractBuilders.ts"],"names":[],"mappings":"AA2MA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FG;AAEH,uCAAuC;AACvC,gEAAgE;AAChE,SAAS,eAAe,CAAC,MAAW,EAAE,OAAgB;IACpD,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;QACvD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;QACjE,sBAAsB,EAAE,MAAM,CAAC,sBAAsB;QACrD,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,MAAM,CAAC,IAAI;KAClB,CAAA;AACH,CAAC;AAED,6BAA6B;AAC7B,SAAS,eAAe,CAAC,MAA0B;IACjD,OAAO,MAAM,CAAC,MAAM,CAAA;AACtB,CAAC;AAwHD,iBAAiB;AACjB,MAAM,UAAU,gBAAgB,CAC9B,MAOiD;IAGjD,MAAM,mBAAmB,GACvB,2BAA2B,IAAI,MAAM,IAAI,MAAM,CAAC,yBAAyB,KAAK,SAAS,CAAA;IACzF,MAAM,OAAO,GAAG,mBAAmB,IAAI,MAAM,IAAI,MAAM,CAAC,iBAAiB,KAAK,SAAS,CAAA;IACvF,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAE7C,IAAI,mBAAmB,EAAE,CAAC;QACxB,qBAAqB;QACrB,OAAO;YACL,GAAG,IAAI;YACP,MAAM,EAAE,eAAe,CAAC,MAA4B,CAAC;YACrD,yBAAyB,EAAG,MAAiD;iBAC1E,yBAAyB;YAC5B,oBAAoB,EAAG,MAA6C,CAAC,oBAAoB;YACzF,+BAA+B,EAAG,MAAwD;iBACvF,+BAA+B;YAClC,UAAU,EAAE,IAAI;SACjB,CAAA;IACH,CAAC;IAED,oBAAoB;IACpB,OAAO;QACL,GAAG,IAAI;QACP,MAAM,EAAE,eAAe,CAAC,MAA4B,CAAC;QACrD,+BAA+B,EAAG,MAAwD;aACvF,+BAA+B;QAClC,KAAK,EAAE,IAAI;KACZ,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,58 +1,57 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
2
|
+
"name": "@lokalise/api-contracts",
|
|
3
|
+
"version": "6.11.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"enableTransparentWorkspaces": "false",
|
|
11
|
+
"homepage": "https://github.com/lokalise/shared-ts-libs",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git://github.com/lokalise/shared-ts-libs.git"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.js",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"private": false,
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"api",
|
|
26
|
+
"contracts",
|
|
27
|
+
"contract",
|
|
28
|
+
"frontend",
|
|
29
|
+
"backend",
|
|
30
|
+
"single",
|
|
31
|
+
"source",
|
|
32
|
+
"truth"
|
|
33
|
+
],
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"zod": ">=3.25.56"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@biomejs/biome": "^2.4.7",
|
|
39
|
+
"@lokalise/biome-config": "^3.1.0",
|
|
40
|
+
"@lokalise/tsconfig": "^4.0.0",
|
|
41
|
+
"@types/node": "^25.0.3",
|
|
42
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
43
|
+
"rimraf": "^6.1.2",
|
|
44
|
+
"typescript": "6.0.3",
|
|
45
|
+
"vitest": "^4.0.18",
|
|
46
|
+
"zod": "^4.3.6"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "rimraf dist && tsc --project tsconfig.build.json",
|
|
50
|
+
"lint": "biome check . && tsc",
|
|
51
|
+
"lint:fix": "biome check --write",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:ci": "vitest run --coverage",
|
|
54
|
+
"package-version": "echo $npm_package_version",
|
|
55
|
+
"postversion": "biome check --write package.json"
|
|
56
|
+
}
|
|
57
|
+
}
|