@lokalise/api-contracts 6.5.1 → 6.5.3

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
@@ -12,8 +12,8 @@ type-safe way).
12
12
 
13
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
14
 
15
- | `sseEvents` | Contract Type |
16
- |-------------|---------------|
15
+ | `serverSentEventSchemas` | Contract Type |
16
+ |--------------------------|---------------|
17
17
  | ❌ | REST contract (GET, POST, PUT, PATCH, DELETE) |
18
18
  | ✅ | SSE or Dual-mode contract |
19
19
 
@@ -44,11 +44,9 @@ const deleteUser = buildContract({
44
44
 
45
45
  // SSE-only streaming endpoint
46
46
  const notifications = buildContract({
47
+ method: 'get',
47
48
  pathResolver: () => '/api/notifications/stream',
48
- params: z.object({}),
49
- query: z.object({}),
50
- requestHeaders: z.object({}),
51
- sseEvents: {
49
+ serverSentEventSchemas: {
52
50
  notification: z.object({ id: z.string(), message: z.string() }),
53
51
  },
54
52
  })
@@ -57,12 +55,9 @@ const notifications = buildContract({
57
55
  const chatCompletion = buildContract({
58
56
  method: 'post',
59
57
  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: {
58
+ requestBodySchema: z.object({ message: z.string() }),
59
+ successResponseBodySchema: z.object({ reply: z.string() }),
60
+ serverSentEventSchemas: {
66
61
  chunk: z.object({ delta: z.string() }),
67
62
  done: z.object({ usage: z.object({ tokens: z.number() }) }),
68
63
  },
@@ -302,12 +297,12 @@ import { buildSseContract } from '@lokalise/api-contracts'
302
297
  import { z } from 'zod'
303
298
 
304
299
  // GET SSE endpoint for live notifications
300
+ // requestPathParamsSchema, requestQuerySchema, requestHeaderSchema are optional
305
301
  const notificationsStream = buildSseContract({
302
+ method: 'get',
306
303
  pathResolver: () => '/api/notifications/stream',
307
- params: z.object({}),
308
- query: z.object({ userId: z.string().optional() }),
309
- requestHeaders: z.object({}),
310
- sseEvents: {
304
+ requestQuerySchema: z.object({ userId: z.string().optional() }),
305
+ serverSentEventSchemas: {
311
306
  notification: z.object({ id: z.string(), message: z.string() }),
312
307
  },
313
308
  })
@@ -317,11 +312,8 @@ const notificationsStream = buildSseContract({
317
312
  const processStream = buildSseContract({
318
313
  method: 'post',
319
314
  pathResolver: () => '/api/process/stream',
320
- params: z.object({}),
321
- query: z.object({}),
322
- requestHeaders: z.object({}),
323
- requestBody: z.object({ fileId: z.string() }),
324
- sseEvents: {
315
+ requestBodySchema: z.object({ fileId: z.string() }),
316
+ serverSentEventSchemas: {
325
317
  progress: z.object({ percent: z.number() }),
326
318
  done: z.object({ result: z.string() }),
327
319
  },
@@ -330,15 +322,15 @@ const processStream = buildSseContract({
330
322
 
331
323
  // SSE endpoint with error schemas (for errors before streaming starts)
332
324
  const channelStream = buildSseContract({
325
+ method: 'get',
333
326
  pathResolver: (params) => `/api/channels/${params.channelId}/stream`,
334
- params: z.object({ channelId: z.string() }),
335
- query: z.object({}),
336
- requestHeaders: z.object({ authorization: z.string() }),
337
- sseEvents: {
327
+ requestPathParamsSchema: z.object({ channelId: z.string() }),
328
+ requestHeaderSchema: z.object({ authorization: z.string() }),
329
+ serverSentEventSchemas: {
338
330
  message: z.object({ text: z.string() }),
339
331
  },
340
332
  // Errors returned before streaming begins
341
- responseSchemasByStatusCode: {
333
+ responseBodySchemasByStatusCode: {
342
334
  401: z.object({ error: z.literal('Unauthorized') }),
343
335
  404: z.object({ error: z.literal('Channel not found') }),
344
336
  },
@@ -363,16 +355,13 @@ import { z } from 'zod'
363
355
  const chatCompletion = buildSseContract({
364
356
  method: 'post',
365
357
  pathResolver: () => '/api/chat/completions',
366
- params: z.object({}),
367
- query: z.object({}),
368
- requestHeaders: z.object({}),
369
- requestBody: z.object({ message: z.string() }),
370
- // Adding syncResponseBody makes it dual-mode
371
- syncResponseBody: z.object({
358
+ requestBodySchema: z.object({ message: z.string() }),
359
+ // Adding successResponseBodySchema makes it dual-mode
360
+ successResponseBodySchema: z.object({
372
361
  reply: z.string(),
373
362
  usage: z.object({ tokens: z.number() }),
374
363
  }),
375
- sseEvents: {
364
+ serverSentEventSchemas: {
376
365
  chunk: z.object({ delta: z.string() }),
377
366
  done: z.object({ usage: z.object({ totalTokens: z.number() }) }),
378
367
  },
@@ -381,15 +370,15 @@ const chatCompletion = buildSseContract({
381
370
 
382
371
  // GET dual-mode endpoint for job status (poll or stream)
383
372
  const jobStatus = buildSseContract({
373
+ method: 'get',
384
374
  pathResolver: (params) => `/api/jobs/${params.jobId}/status`,
385
- params: z.object({ jobId: z.string().uuid() }),
386
- query: z.object({ verbose: z.string().optional() }),
387
- requestHeaders: z.object({}),
388
- syncResponseBody: z.object({
375
+ requestPathParamsSchema: z.object({ jobId: z.string().uuid() }),
376
+ requestQuerySchema: z.object({ verbose: z.string().optional() }),
377
+ successResponseBodySchema: z.object({
389
378
  status: z.enum(['pending', 'running', 'completed', 'failed']),
390
379
  progress: z.number(),
391
380
  }),
392
- sseEvents: {
381
+ serverSentEventSchemas: {
393
382
  progress: z.object({ percent: z.number() }),
394
383
  done: z.object({ result: z.string() }),
395
384
  },
@@ -399,21 +388,19 @@ const jobStatus = buildSseContract({
399
388
 
400
389
  ### Response Schemas by Status Code
401
390
 
402
- Both SSE-only and dual-mode contracts support `responseSchemasByStatusCode` for defining different response shapes for errors that occur **before streaming starts** (e.g., authentication failures, validation errors, resource not found):
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):
403
392
 
404
393
  ```ts
405
394
  const chatCompletion = buildSseContract({
406
395
  method: 'post',
407
396
  pathResolver: () => '/api/chat/completions',
408
- params: z.object({}),
409
- query: z.object({}),
410
- requestHeaders: z.object({ authorization: z.string() }),
411
- requestBody: z.object({ message: z.string() }),
412
- syncResponseBody: z.object({ reply: z.string() }),
413
- sseEvents: {
397
+ requestHeaderSchema: z.object({ authorization: z.string() }),
398
+ requestBodySchema: z.object({ message: z.string() }),
399
+ successResponseBodySchema: z.object({ reply: z.string() }),
400
+ serverSentEventSchemas: {
414
401
  chunk: z.object({ delta: z.string() }),
415
402
  },
416
- responseSchemasByStatusCode: {
403
+ responseBodySchemasByStatusCode: {
417
404
  400: z.object({ error: z.string(), details: z.array(z.string()) }),
418
405
  401: z.object({ error: z.literal('Unauthorized') }),
419
406
  429: z.object({ error: z.string(), retryAfter: z.number() }),
@@ -423,10 +410,10 @@ const chatCompletion = buildSseContract({
423
410
 
424
411
  ### Contract Type Detection
425
412
 
426
- `buildSseContract` automatically determines the contract type based on the presence of `syncResponseBody`:
413
+ `buildSseContract` automatically determines the contract type based on the presence of `successResponseBodySchema`:
427
414
 
428
- | `syncResponseBody` | `requestBody` | Result |
429
- |-------------------|---------------|--------|
415
+ | `successResponseBodySchema` | `requestBodySchema` | Result |
416
+ |----------------------------|---------------------|--------|
430
417
  | ❌ | ❌ | SSE-only GET |
431
418
  | ❌ | ✅ | SSE-only POST/PUT/PATCH |
432
419
  | ✅ | ❌ | Dual-mode GET |
@@ -1,12 +1,8 @@
1
1
  // Implementation
2
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;
3
+ const { method } = config;
8
4
  const baseFields = {
9
- isEmptyResponseExpected: config.isEmptyResponseExpected ?? defaultIsEmptyResponseExpected,
5
+ isEmptyResponseExpected: config.isEmptyResponseExpected ?? method === 'delete',
10
6
  isNonJSONResponseExpected: config.isNonJSONResponseExpected ?? false,
11
7
  pathResolver: config.pathResolver,
12
8
  requestHeaderSchema: config.requestHeaderSchema,
@@ -20,26 +16,17 @@ export function buildRestContract(config) {
20
16
  metadata: config.metadata,
21
17
  tags: config.tags,
22
18
  };
23
- if (hasBody) {
24
- // Payload route (POST/PUT/PATCH)
19
+ if (method === 'post' || method === 'put' || method === 'patch') {
25
20
  return {
26
21
  ...baseFields,
27
- method: method,
22
+ method,
28
23
  // biome-ignore lint/suspicious/noExplicitAny: Type assertion needed for config union
29
24
  requestBodySchema: config.requestBodySchema,
30
25
  };
31
26
  }
32
- if (isDeleteRoute) {
33
- // DELETE route
34
- return {
35
- ...baseFields,
36
- method: 'delete',
37
- };
38
- }
39
- // GET route
40
27
  return {
41
28
  ...baseFields,
42
- method: 'get',
29
+ method,
43
30
  };
44
31
  }
45
32
  //# sourceMappingURL=restContractBuilder.js.map
@@ -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,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,YAAY;IACZ,OAAO;QACL,GAAG,UAAU;QACb,MAAM,EAAE,KAAc;KACvB,CAAA;AACH,CAAC"}
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"}
@@ -20,9 +20,9 @@ import type { SSEEventSchemas } from './sseTypes.ts';
20
20
  export type DualModeContractDefinition<Method extends SSEMethod = SSEMethod, Params extends z.ZodTypeAny = z.ZodTypeAny, Query extends z.ZodTypeAny = z.ZodTypeAny, RequestHeaders extends z.ZodTypeAny = z.ZodTypeAny, Body extends z.ZodTypeAny | undefined = undefined, SyncResponse extends z.ZodTypeAny = z.ZodTypeAny, Events extends SSEEventSchemas = SSEEventSchemas, ResponseHeaders extends z.ZodTypeAny | undefined = undefined, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined = undefined> = {
21
21
  method: Method;
22
22
  pathResolver: RoutePathResolver<z.infer<Params>>;
23
- requestPathParamsSchema: Params;
24
- requestQuerySchema: Query;
25
- requestHeaderSchema: RequestHeaders;
23
+ requestPathParamsSchema?: Params;
24
+ requestQuerySchema?: Query;
25
+ requestHeaderSchema?: RequestHeaders;
26
26
  requestBodySchema: Body;
27
27
  /** Sync response schema - use with `sync` handler */
28
28
  successResponseBodySchema: SyncResponse;
@@ -50,9 +50,9 @@ export type DualModeContractDefinition<Method extends SSEMethod = SSEMethod, Par
50
50
  export type AnyDualModeContractDefinition = {
51
51
  method: SSEMethod;
52
52
  pathResolver: RoutePathResolver<any>;
53
- requestPathParamsSchema: z.ZodTypeAny;
54
- requestQuerySchema: z.ZodTypeAny;
55
- requestHeaderSchema: z.ZodTypeAny;
53
+ requestPathParamsSchema?: z.ZodTypeAny;
54
+ requestQuerySchema?: z.ZodTypeAny;
55
+ requestHeaderSchema?: z.ZodTypeAny;
56
56
  requestBodySchema: z.ZodTypeAny | undefined;
57
57
  /** Sync response schema - use with `sync` handler */
58
58
  successResponseBodySchema: z.ZodTypeAny;
@@ -11,9 +11,9 @@ import type { SSEEventSchemas } from './sseTypes.ts';
11
11
  export type SSEGetContractConfig<Params extends z.ZodTypeAny, Query extends z.ZodTypeAny, RequestHeaders extends z.ZodTypeAny, Events extends SSEEventSchemas, ResponseSchemasByStatusCode extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined = undefined> = {
12
12
  method: 'get';
13
13
  pathResolver: RoutePathResolver<z.infer<Params>>;
14
- requestPathParamsSchema: Params;
15
- requestQuerySchema: Query;
16
- requestHeaderSchema: RequestHeaders;
14
+ requestPathParamsSchema?: Params;
15
+ requestQuerySchema?: Query;
16
+ requestHeaderSchema?: RequestHeaders;
17
17
  serverSentEventSchemas: Events;
18
18
  /**
19
19
  * Error response schemas by HTTP status code.
@@ -39,9 +39,9 @@ export type SSEGetContractConfig<Params extends z.ZodTypeAny, Query extends z.Zo
39
39
  export type SSEPayloadContractConfig<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> = {
40
40
  method: 'post' | 'put' | 'patch';
41
41
  pathResolver: RoutePathResolver<z.infer<Params>>;
42
- requestPathParamsSchema: Params;
43
- requestQuerySchema: Query;
44
- requestHeaderSchema: RequestHeaders;
42
+ requestPathParamsSchema?: Params;
43
+ requestQuerySchema?: Query;
44
+ requestHeaderSchema?: RequestHeaders;
45
45
  requestBodySchema: Body;
46
46
  serverSentEventSchemas: Events;
47
47
  /**
@@ -67,9 +67,9 @@ export type SSEPayloadContractConfig<Params extends z.ZodTypeAny, Query extends
67
67
  export type DualModeGetContractConfig<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> = {
68
68
  method: 'get';
69
69
  pathResolver: RoutePathResolver<z.infer<Params>>;
70
- requestPathParamsSchema: Params;
71
- requestQuerySchema: Query;
72
- requestHeaderSchema: RequestHeaders;
70
+ requestPathParamsSchema?: Params;
71
+ requestQuerySchema?: Query;
72
+ requestHeaderSchema?: RequestHeaders;
73
73
  /** Single sync response schema */
74
74
  successResponseBodySchema: JsonResponse;
75
75
  /**
@@ -108,9 +108,9 @@ export type DualModeGetContractConfig<Params extends z.ZodTypeAny, Query extends
108
108
  export type DualModePayloadContractConfig<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> = {
109
109
  method: 'post' | 'put' | 'patch';
110
110
  pathResolver: RoutePathResolver<z.infer<Params>>;
111
- requestPathParamsSchema: Params;
112
- requestQuerySchema: Query;
113
- requestHeaderSchema: RequestHeaders;
111
+ requestPathParamsSchema?: Params;
112
+ requestQuerySchema?: Query;
113
+ requestHeaderSchema?: RequestHeaders;
114
114
  requestBodySchema: Body;
115
115
  /** Single sync response schema */
116
116
  successResponseBodySchema: JsonResponse;
@@ -26,9 +26,9 @@ export type SSEContractDefinition<Method extends SSEMethod = SSEMethod, Params e
26
26
  * Receives typed params and returns the URL path string.
27
27
  */
28
28
  pathResolver: RoutePathResolver<z.infer<Params>>;
29
- requestPathParamsSchema: Params;
30
- requestQuerySchema: Query;
31
- requestHeaderSchema: RequestHeaders;
29
+ requestPathParamsSchema?: Params;
30
+ requestQuerySchema?: Query;
31
+ requestHeaderSchema?: RequestHeaders;
32
32
  requestBodySchema: Body;
33
33
  serverSentEventSchemas: Events;
34
34
  /**
@@ -54,9 +54,9 @@ export type SSEContractDefinition<Method extends SSEMethod = SSEMethod, Params e
54
54
  export type AnySSEContractDefinition = {
55
55
  method: SSEMethod;
56
56
  pathResolver: RoutePathResolver<any>;
57
- requestPathParamsSchema: z.ZodTypeAny;
58
- requestQuerySchema: z.ZodTypeAny;
59
- requestHeaderSchema: z.ZodTypeAny;
57
+ requestPathParamsSchema?: z.ZodTypeAny;
58
+ requestQuerySchema?: z.ZodTypeAny;
59
+ requestHeaderSchema?: z.ZodTypeAny;
60
60
  requestBodySchema: z.ZodTypeAny | undefined;
61
61
  serverSentEventSchemas: SSEEventSchemas;
62
62
  responseBodySchemasByStatusCode?: Partial<Record<HttpStatusCode, z.ZodTypeAny>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/api-contracts",
3
- "version": "6.5.1",
3
+ "version": "6.5.3",
4
4
  "files": [
5
5
  "dist"
6
6
  ],