@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 CHANGED
@@ -1,420 +1,345 @@
1
1
  # api-contracts
2
2
 
3
- Key idea behind API contracts: backend owns entire definition for the route, including its path, HTTP method used and
4
- response structure expectations, and exposes it as a part of its API schemas. Then frontend consumes that definition
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
- This reduces amount of assumptions FE needs to make about the behaviour of BE, reduces amount of code that needs to be
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
- type-safe way).
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
- ## Universal Contract Builder
10
+ ## Defining contracts
12
11
 
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:
12
+ ### REST routes
14
13
 
15
- | `serverSentEventSchemas` | Contract Type |
16
- |--------------------------|---------------|
17
- | | REST contract (GET, POST, PUT, PATCH, DELETE) |
18
- | ✅ | SSE or Dual-mode contract |
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 { buildContract } from '@lokalise/api-contracts'
22
- import { z } from 'zod'
54
+ import { defineApiContract, textResponse } from '@lokalise/api-contracts'
23
55
 
24
- // REST GET route
25
- const getUsers = buildContract({
26
- successResponseBodySchema: z.array(userSchema),
27
- pathResolver: () => '/api/users',
56
+ const exportCsv = defineApiContract({
57
+ method: 'get',
58
+ pathResolver: () => '/export.csv',
59
+ responsesByStatusCode: { 200: textResponse('text/csv') },
28
60
  })
29
61
 
30
- // REST POST route
31
- const createUser = buildContract({
32
- method: 'post',
33
- requestBodySchema: createUserSchema,
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
- // REST DELETE route
39
- const deleteUser = buildContract({
40
- method: 'delete',
41
- requestPathParamsSchema: z.object({ userId: z.string() }),
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
- // SSE-only streaming endpoint
46
- const notifications = buildContract({
47
- method: 'get',
48
- pathResolver: () => '/api/notifications/stream',
49
- serverSentEventSchemas: {
50
- notification: z.object({ id: z.string(), message: z.string() }),
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 endpoint (supports both JSON and SSE)
55
- const chatCompletion = buildContract({
56
- method: 'post',
57
- pathResolver: () => '/api/chat/completions',
58
- requestBodySchema: z.object({ message: z.string() }),
59
- successResponseBodySchema: z.object({ reply: z.string() }),
60
- serverSentEventSchemas: {
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({ usage: z.object({ tokens: z.number() }) }),
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
- You can also use the specialized builders directly (`buildRestContract`, `buildSseContract`) if you prefer explicit control over contract types.
124
+ `getSseSchemaByEventName(contract)` extracts SSE event schemas from a contract:
68
125
 
69
- ## REST Contracts
126
+ ```ts
127
+ import { getSseSchemaByEventName } from '@lokalise/api-contracts'
70
128
 
71
- Use `buildRestContract` to create REST API contracts. The contract type is automatically determined based on the configuration:
129
+ getSseSchemaByEventName(notifications)
130
+ // { notification: ZodObject<...> }
72
131
 
73
- | `method` | `requestBodySchema` | Result |
74
- |----------|---------------------|--------|
75
- | omitted/undefined | ❌ | GET route |
76
- | `'delete'` | ❌ | DELETE route |
77
- | `'post'`/`'put'`/`'patch'` | ✅ | Payload route |
132
+ getSseSchemaByEventName(chatCompletion)
133
+ // { chunk: ZodObject<...>, done: ZodObject<...> }
134
+ ```
78
135
 
79
- Usage examples:
136
+ ### All fields
80
137
 
81
138
  ```ts
82
- import { buildRestContract } from '@lokalise/api-contracts'
83
-
84
- // GET route - method is inferred automatically
85
- const getContract = buildRestContract({
86
- successResponseBodySchema: RESPONSE_BODY_SCHEMA,
87
- requestPathParamsSchema: REQUEST_PATH_PARAMS_SCHEMA,
88
- requestQuerySchema: REQUEST_QUERY_SCHEMA,
89
- requestHeaderSchema: REQUEST_HEADER_SCHEMA,
90
- responseHeaderSchema: RESPONSE_HEADER_SCHEMA,
91
- pathResolver: (pathParams) => `/users/${pathParams.userId}`,
92
- summary: 'Route summary',
93
- metadata: { allowedRoles: ['admin'] },
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
- // POST route - requires method and requestBodySchema
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
- // DELETE route - method is 'delete', no body, defaults isEmptyResponseExpected to true
107
- const deleteContract = buildRestContract({
108
- method: 'delete',
109
- successResponseBodySchema: RESPONSE_BODY_SCHEMA,
110
- requestPathParamsSchema: REQUEST_PATH_PARAMS_SCHEMA,
111
- pathResolver: (pathParams) => `/users/${pathParams.userId}`,
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
- ### Deprecated Builders
186
+ ### Type utilities
116
187
 
117
- The individual builders `buildGetRoute`, `buildPayloadRoute`, and `buildDeleteRoute` are deprecated. Use `buildRestContract` instead:
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
- // Before (deprecated):
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
- // After (recommended):
128
- import { buildRestContract } from '@lokalise/api-contracts'
193
+ type UserResponse = InferNonSseSuccessResponses<typeof getUser['responsesByStatusCode']>
194
+ // { id: string; name: string }
129
195
 
130
- const getContract = buildRestContract({ ... }) // method inferred as 'get'
131
- const postContract = buildRestContract({ method: 'post', ... })
132
- const deleteContract = buildRestContract({ method: 'delete', ... })
196
+ type CsvResponse = InferNonSseSuccessResponses<typeof exportCsv['responsesByStatusCode']>
197
+ // string
133
198
  ```
134
199
 
135
- In the previous example, the `metadata` property is an optional, free-form field that allows you to store any additional
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
- Here is how you can apply strict typing to the `metadata` property using TypeScript module augmentation:
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
- // Augment the module to extend the interface with specific properties
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
- Note that in order to make contract-based requests, you need to use a compatible HTTP client
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
- In case you are using fastify on the backend, you can also use `@lokalise/fastify-api-contracts` in order to simplify definition of your fastify routes, utilizing contracts as the single source of truth.
208
+ **`HasAnyNonSseSuccessResponse<T>`** `true` if any 2xx entry is a non-SSE response (JSON, text, blob, or no-body).
159
209
 
160
- ## Header Schemas
210
+ **`IsNoBodySuccessResponse<T>`** `true` when all 2xx entries are `ContractNoBody` or no 2xx status codes are defined.
161
211
 
162
- ### Request Headers (`requestHeaderSchema`)
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
- 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.
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 { buildRestContract } from '@lokalise/api-contracts'
168
- import { z } from 'zod'
169
-
170
- const contract = buildRestContract({
171
- successResponseBodySchema: DATA_SCHEMA,
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
- ### Response Headers (`responseHeaderSchema`)
226
+ ### Client types
182
227
 
183
- Use `responseHeaderSchema` to define and validate headers that the server will send in the response. This is particularly useful for documenting:
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
- ```ts
190
- import { buildRestContract } from '@lokalise/api-contracts'
191
- import { z } from 'zod'
192
-
193
- const contract = buildRestContract({
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
- Both header schemas can be used together in a single contract:
236
+ **`DefaultStreaming<T>`** `true` for SSE-only contracts, `false` for everything else.
206
237
 
207
238
  ```ts
208
- const contract = buildRestContract({
209
- successResponseBodySchema: DATA_SCHEMA,
210
- requestHeaderSchema: z.object({
211
- 'authorization': z.string(),
212
- }),
213
- responseHeaderSchema: z.object({
214
- 'x-ratelimit-limit': z.string(),
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
- These header schemas are primarily used for:
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
- ## Utility Functions
250
+ **`ApiContract`** union of all contract variants (`GetApiContract | DeleteApiContract | PayloadApiContract`). Use this to type function parameters that accept any contract.
228
251
 
229
- ### `mapRouteToPath`
252
+ **`GetApiContract`**, **`DeleteApiContract`**, **`PayloadApiContract`** — individual contract variants if you need to narrow the type.
230
253
 
231
- Converts a route definition to its corresponding path pattern with parameter placeholders.
254
+ **`RequestPathParamsSchema`**, **`RequestQuerySchema`**, **`RequestHeaderSchema`**, **`ResponseHeaderSchema`** — type aliases for `z.ZodObject`. Use these to constrain schema arguments in generic helpers.
232
255
 
233
- ```ts
234
- import { mapRouteToPath, buildRestContract } from '@lokalise/api-contracts'
256
+ ### Utility functions
235
257
 
236
- const userContract = buildRestContract({
237
- requestPathParamsSchema: z.object({ userId: z.string() }),
238
- successResponseBodySchema: USER_SCHEMA,
239
- pathResolver: (pathParams) => `/users/${pathParams.userId}`,
240
- })
258
+ **`mapApiContractToPath`** Express/Fastify-style path pattern.
259
+
260
+ ```ts
261
+ import { mapApiContractToPath } from '@lokalise/api-contracts'
241
262
 
242
- const pathPattern = mapRouteToPath(userContract)
243
- // Returns: "/users/:userId"
263
+ mapApiContractToPath(getUser) // "/users/:userId"
244
264
  ```
245
265
 
246
- This function is useful when you need to:
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
- The function replaces actual path parameters with placeholder syntax (`:paramName`), making it compatible with Express-style route patterns.
268
+ ```ts
269
+ import { describeApiContract } from '@lokalise/api-contracts'
252
270
 
253
- ### `describeContract`
271
+ describeApiContract(getUser) // "GET /users/:userId"
272
+ ```
254
273
 
255
- Generates a human-readable description of a route contract, combining the HTTP method with the route path.
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 { describeContract, buildRestContract } from '@lokalise/api-contracts'
277
+ import { getSuccessResponseSchema } from '@lokalise/api-contracts'
259
278
 
260
- const getContract = buildRestContract({
261
- requestPathParamsSchema: z.object({ userId: z.string() }),
262
- successResponseBodySchema: USER_SCHEMA,
263
- pathResolver: (pathParams) => `/users/${pathParams.userId}`,
264
- })
279
+ getSuccessResponseSchema(getUser) // ZodObject
280
+ getSuccessResponseSchema(deleteUser) // null
281
+ ```
265
282
 
266
- const postContract = buildRestContract({
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
- console.log(describeContract(getContract)) // "GET /users/:userId"
278
- console.log(describeContract(postContract)) // "POST /orgs/:orgId/users/:userId"
279
- ```
285
+ ```ts
286
+ import { getIsEmptyResponseExpected } from '@lokalise/api-contracts'
280
287
 
281
- This function is particularly useful for:
282
- - Logging and debugging API calls
283
- - Generating documentation or route summaries
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
- ## SSE and Dual-Mode Contracts
292
+ **`hasAnySuccessSseResponse`** — `true` when any 2xx entry is an SSE response (including inside `anyOfResponses`).
288
293
 
289
- Use `buildSseContract` for endpoints that support Server-Sent Events (SSE) streaming. This builder supports two contract types:
294
+ ```ts
295
+ import { hasAnySuccessSseResponse } from '@lokalise/api-contracts'
290
296
 
291
- ### SSE-Only Contracts
297
+ hasAnySuccessSseResponse(notifications) // true
298
+ hasAnySuccessSseResponse(getUser) // false
299
+ hasAnySuccessSseResponse(chatCompletion) // true (dual-mode)
300
+ ```
292
301
 
293
- Pure streaming endpoints that only return SSE events. Use these for real-time notifications, live feeds, or any endpoint that only streams data.
302
+ **`getSseSchemaByEventName`** extracts SSE event schemas from a contract. Returns `null` when no SSE schemas are present.
294
303
 
295
304
  ```ts
296
- import { buildSseContract } from '@lokalise/api-contracts'
297
- import { z } from 'zod'
298
-
299
- // GET SSE endpoint for live notifications
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
- ### Dual-Mode (Hybrid) Contracts
311
+ ## Module augmentation
341
312
 
342
- Hybrid endpoints that support **both** synchronous JSON responses **and** SSE streaming from the same URL. The response mode is determined by the client's `Accept` header:
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
- This pattern is ideal for AI/LLM APIs (like OpenAI's Chat Completions API) where clients can choose between getting the full response at once or streaming it token-by-token.
315
+ ```typescript
316
+ // file -> apiContracts.d.ts
317
+ import '@lokalise/api-contracts';
347
318
 
348
- ```ts
349
- import { buildSseContract } from '@lokalise/api-contracts'
350
- import { z } from 'zod'
351
-
352
- // OpenAI-style chat completion endpoint
353
- // - Accept: application/json → returns { reply, usage } immediately
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
- ### Response Schemas by Status Code
327
+ ## HTTP clients
390
328
 
391
- Both SSE-only and dual-mode contracts support `responseBodySchemasByStatusCode` for defining different response shapes for errors that occur **before streaming starts** (e.g., authentication failures, validation errors, resource not found):
329
+ To make contract-based requests, use a compatible HTTP client (`@lokalise/frontend-http-client` or `@lokalise/backend-http-client`).
392
330
 
393
- ```ts
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
- ### Contract Type Detection
333
+ ## Future: request body content type
412
334
 
413
- `buildSseContract` automatically determines the contract type based on the presence of `successResponseBodySchema`:
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
- | `successResponseBodySchema` | `requestBodySchema` | Result |
416
- |----------------------------|---------------------|--------|
417
- | ❌ | ❌ | SSE-only GET |
418
- | | ✅ | SSE-only POST/PUT/PATCH |
419
- | | | Dual-mode GET |
420
- | ✅ | ✅ | Dual-mode POST/PUT/PATCH |
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
+ ```
@@ -59,7 +59,7 @@ export type DeleteRouteDefinition<SuccessResponseBodySchema extends z.Schema | u
59
59
  method: 'delete';
60
60
  };
61
61
  /**
62
- * @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
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 = buildRestContract({
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 `buildRestContract` instead. This function will be removed in a future version.
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 = buildRestContract({
95
- * successResponseBodySchema: responseSchema,
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 `buildRestContract` instead. This function will be removed in a future version.
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
- * successResponseBodySchema: responseSchema,
108
- * pathResolver: () => '/api/users/123',
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 = buildRestContract({
113
+ * const route = defineApiContract({
113
114
  * method: 'delete',
114
- * successResponseBodySchema: responseSchema,
115
- * pathResolver: () => '/api/users/123',
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
- * This method maps given route definition to a string of the format '/static-path-part/:path-param-value'
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;
@@ -1,6 +1,6 @@
1
1
  const EMPTY_PARAMS = {};
2
2
  /**
3
- * @deprecated Use `buildRestContract` instead. This function will be removed in a future version.
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 = buildRestContract({
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 `buildRestContract` instead. This function will be removed in a future version.
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 = buildRestContract({
54
- * successResponseBodySchema: responseSchema,
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 `buildRestContract` instead. This function will be removed in a future version.
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
- * successResponseBodySchema: responseSchema,
84
- * pathResolver: () => '/api/users/123',
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 = buildRestContract({
89
+ * const route = defineApiContract({
89
90
  * method: 'delete',
90
- * successResponseBodySchema: responseSchema,
91
- * pathResolver: () => '/api/users/123',
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
- * This method maps given route definition to a string of the format '/static-path-part/:path-param-value'
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
  }
@@ -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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"}
@@ -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;AAuTrC,+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"}
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":"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"}
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":"AAiSA,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
+ {"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;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"}
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
- "name": "@lokalise/api-contracts",
3
- "version": "6.9.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
- "scripts": {
35
- "build": "rimraf dist && tsc --project tsconfig.build.json",
36
- "lint": "biome check . && tsc",
37
- "lint:fix": "biome check --write",
38
- "test": "vitest run",
39
- "test:ci": "vitest run --coverage",
40
- "prepublishOnly": "npm run build",
41
- "package-version": "echo $npm_package_version",
42
- "postversion": "biome check --write package.json"
43
- },
44
- "peerDependencies": {
45
- "zod": ">=3.25.56"
46
- },
47
- "devDependencies": {
48
- "@biomejs/biome": "^2.4.7",
49
- "@lokalise/biome-config": "^3.1.0",
50
- "@lokalise/tsconfig": "^3.1.0",
51
- "@vitest/coverage-v8": "^4.0.18",
52
- "rimraf": "^6.1.2",
53
- "typescript": "5.9.3",
54
- "vitest": "^4.0.18",
55
- "zod": "^4.3.6"
56
- },
57
- "dependencies": {}
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
+ }