@lokalise/api-contracts 6.2.1 → 6.4.0

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