@lokalise/api-contracts 6.7.0 → 6.8.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.
@@ -1 +1,3 @@
1
1
  export type HttpStatusCode = 100 | 101 | 102 | 103 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
2
+ export declare const SUCCESSFUL_HTTP_STATUS_CODES: readonly [200, 201, 202, 203, 204, 205, 206, 207, 208, 226];
3
+ export type SuccessfulHttpStatusCode = (typeof SUCCESSFUL_HTTP_STATUS_CODES)[number];
@@ -1,2 +1,4 @@
1
- export {};
1
+ export const SUCCESSFUL_HTTP_STATUS_CODES = [
2
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
3
+ ];
2
4
  //# sourceMappingURL=HttpStatusCodes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HttpStatusCodes.js","sourceRoot":"","sources":["../src/HttpStatusCodes.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"HttpStatusCodes.js","sourceRoot":"","sources":["../src/HttpStatusCodes.ts"],"names":[],"mappings":"AAiEA,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;CACxC,CAAA"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export * from './apiContracts.ts';
2
2
  export * from './contractBuilder.ts';
3
3
  export * from './HttpStatusCodes.ts';
4
+ export * from './new/constants.ts';
5
+ export * from './new/contractResponse.ts';
6
+ export * from './new/defineApiContract.ts';
7
+ export * from './new/inferTypes.ts';
4
8
  export * from './pathUtils.ts';
5
9
  export * from './rest/restContractBuilder.ts';
6
10
  export * from './sse/dualModeContracts.ts';
package/dist/index.js CHANGED
@@ -2,6 +2,10 @@ export * from "./apiContracts.js";
2
2
  // Universal contract builder
3
3
  export * from "./contractBuilder.js";
4
4
  export * from "./HttpStatusCodes.js";
5
+ export * from "./new/constants.js";
6
+ export * from "./new/contractResponse.js";
7
+ export * from "./new/defineApiContract.js";
8
+ export * from "./new/inferTypes.js";
5
9
  export * from "./pathUtils.js";
6
10
  export * from "./rest/restContractBuilder.js";
7
11
  // Dual-mode (hybrid) contracts
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,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"}
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,oBAAoB,CAAA;AAClC,cAAc,2BAA2B,CAAA;AACzC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,qBAAqB,CAAA;AACnC,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 @@
1
+ export declare const ContractNoBody: unique symbol;
@@ -0,0 +1,2 @@
1
+ export const ContractNoBody = Symbol.for('ContractNoBody');
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/new/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA"}
@@ -0,0 +1,46 @@
1
+ import type { z } from 'zod/v4';
2
+ import type { HttpStatusCode } from '../HttpStatusCodes.ts';
3
+ import { ContractNoBody } from './constants.ts';
4
+ export type TypedTextResponse = {
5
+ readonly _tag: 'TextResponse';
6
+ readonly contentType: string;
7
+ };
8
+ export declare const textResponse: (contentType: string) => TypedTextResponse;
9
+ export declare const isTextResponse: (value: ApiContractResponse) => value is TypedTextResponse;
10
+ export type TypedBlobResponse = {
11
+ readonly _tag: 'BlobResponse';
12
+ readonly contentType: string;
13
+ };
14
+ export declare const blobResponse: (contentType: string) => TypedBlobResponse;
15
+ export declare const isBlobResponse: (value: ApiContractResponse) => value is TypedBlobResponse;
16
+ export type SseSchemaByEventName = Record<string, z.ZodType>;
17
+ export type TypedSseResponse<T extends SseSchemaByEventName = SseSchemaByEventName> = {
18
+ readonly _tag: 'SseResponse';
19
+ readonly schemaByEventName: T;
20
+ };
21
+ export declare const sseResponse: <T extends SseSchemaByEventName>(schemaByEventName: T) => TypedSseResponse<T>;
22
+ export declare const isSseResponse: (value: ApiContractResponse) => value is TypedSseResponse;
23
+ export type TypedJsonResponse = z.ZodType;
24
+ export type TypedApiContractResponse = TypedJsonResponse | TypedTextResponse | TypedBlobResponse | TypedSseResponse;
25
+ export type AnyOfResponses<T extends TypedApiContractResponse = TypedApiContractResponse> = {
26
+ readonly _tag: 'AnyOfResponses';
27
+ readonly responses: T[];
28
+ };
29
+ export declare const anyOfResponses: <T extends TypedApiContractResponse>(responses: T[]) => AnyOfResponses<T>;
30
+ export declare const isAnyOfResponses: (value: ApiContractResponse) => value is AnyOfResponses;
31
+ export type ApiContractResponse = typeof ContractNoBody | TypedApiContractResponse | AnyOfResponses;
32
+ export type ResponsesByStatusCode = Partial<Record<HttpStatusCode, ApiContractResponse>>;
33
+ export type ResponseKind = {
34
+ kind: 'noContent';
35
+ } | {
36
+ kind: 'text';
37
+ } | {
38
+ kind: 'blob';
39
+ } | {
40
+ kind: 'json';
41
+ schema: z.ZodType;
42
+ } | {
43
+ kind: 'sse';
44
+ schemaByEventName: SseSchemaByEventName;
45
+ };
46
+ export declare const resolveContractResponse: (schemaEntry: ApiContractResponse, contentType: string | undefined) => ResponseKind | null;
@@ -0,0 +1,57 @@
1
+ import { ContractNoBody } from "./constants.js";
2
+ export const textResponse = (contentType) => ({
3
+ _tag: 'TextResponse',
4
+ contentType,
5
+ });
6
+ export const isTextResponse = (value) => typeof value === 'object' && value !== null && '_tag' in value && value._tag === 'TextResponse';
7
+ export const blobResponse = (contentType) => ({
8
+ _tag: 'BlobResponse',
9
+ contentType,
10
+ });
11
+ export const isBlobResponse = (value) => typeof value === 'object' && value !== null && '_tag' in value && value._tag === 'BlobResponse';
12
+ export const sseResponse = (schemaByEventName) => ({
13
+ _tag: 'SseResponse',
14
+ schemaByEventName,
15
+ });
16
+ export const isSseResponse = (value) => typeof value === 'object' && value !== null && '_tag' in value && value._tag === 'SseResponse';
17
+ export const anyOfResponses = (responses) => ({
18
+ _tag: 'AnyOfResponses',
19
+ responses,
20
+ });
21
+ export const isAnyOfResponses = (value) => typeof value === 'object' && value !== null && '_tag' in value && value._tag === 'AnyOfResponses';
22
+ const matchTypedResponse = (entry, contentType) => {
23
+ if (isTextResponse(entry)) {
24
+ return contentType.includes(entry.contentType) ? { kind: 'text' } : null;
25
+ }
26
+ if (isBlobResponse(entry)) {
27
+ return contentType.includes(entry.contentType) ? { kind: 'blob' } : null;
28
+ }
29
+ if (isSseResponse(entry)) {
30
+ return contentType.includes('text/event-stream')
31
+ ? { kind: 'sse', schemaByEventName: entry.schemaByEventName }
32
+ : null;
33
+ }
34
+ if (contentType.includes('application/json')) {
35
+ return { kind: 'json', schema: entry };
36
+ }
37
+ return null;
38
+ };
39
+ export const resolveContractResponse = (schemaEntry, contentType) => {
40
+ if (schemaEntry === ContractNoBody) {
41
+ return { kind: 'noContent' };
42
+ }
43
+ if (!contentType) {
44
+ return null;
45
+ }
46
+ if (isAnyOfResponses(schemaEntry)) {
47
+ for (const item of schemaEntry.responses) {
48
+ const resolved = matchTypedResponse(item, contentType);
49
+ if (resolved) {
50
+ return resolved;
51
+ }
52
+ }
53
+ return null;
54
+ }
55
+ return matchTypedResponse(schemaEntry, contentType);
56
+ };
57
+ //# sourceMappingURL=contractResponse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contractResponse.js","sourceRoot":"","sources":["../../src/new/contractResponse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod/v4';
2
+ import type { CommonRouteDefinitionMetadata, InferSchemaOutput, RoutePathResolver } from '../apiContracts.ts';
3
+ import type { Exactly } from '../typeUtils.ts';
4
+ import { ContractNoBody } from './constants.ts';
5
+ import { type ResponsesByStatusCode, type SseSchemaByEventName } from './contractResponse.ts';
6
+ export type RequestPathParamsSchema = z.ZodObject;
7
+ export type CommonApiContract = {
8
+ pathResolver: RoutePathResolver<any>;
9
+ requestPathParamsSchema?: RequestPathParamsSchema;
10
+ requestQuerySchema?: z.ZodType;
11
+ requestHeaderSchema?: z.ZodType;
12
+ responseHeaderSchema?: z.ZodType;
13
+ responsesByStatusCode: ResponsesByStatusCode;
14
+ metadata?: CommonRouteDefinitionMetadata;
15
+ summary?: string;
16
+ description?: string;
17
+ tags?: readonly string[];
18
+ };
19
+ export type GetApiContract = CommonApiContract & {
20
+ method: 'get';
21
+ requestBodySchema?: never;
22
+ };
23
+ export type DeleteApiContract = CommonApiContract & {
24
+ method: 'delete';
25
+ requestBodySchema?: never;
26
+ };
27
+ export type PayloadApiContract = CommonApiContract & {
28
+ method: 'post' | 'put' | 'patch';
29
+ requestBodySchema: typeof ContractNoBody | z.ZodType;
30
+ };
31
+ export type ApiContract = GetApiContract | DeleteApiContract | PayloadApiContract;
32
+ type TypedPathApiContract<T extends RequestPathParamsSchema> = Omit<ApiContract, 'pathResolver' | 'requestPathParamsSchema'> & {
33
+ pathResolver: RoutePathResolver<InferSchemaOutput<T>>;
34
+ requestPathParamsSchema?: T;
35
+ };
36
+ export declare const defineApiContract: <PathParamsSchema extends RequestPathParamsSchema, const Contract extends TypedPathApiContract<PathParamsSchema>>(contract: Exactly<Contract, TypedPathApiContract<PathParamsSchema>> & {
37
+ requestPathParamsSchema?: PathParamsSchema;
38
+ }) => Contract;
39
+ export declare const mapApiContractToPath: (routeConfig: ApiContract) => string;
40
+ export declare const describeApiContract: (routeConfig: ApiContract) => string;
41
+ export declare const getSseSchemaByEventName: (routeConfig: ApiContract) => SseSchemaByEventName | null;
42
+ export declare const getSuccessResponseSchema: (routeConfig: ApiContract) => z.ZodType | null;
43
+ export declare const getIsEmptyResponseExpected: (routeConfig: ApiContract) => boolean;
44
+ export {};
@@ -0,0 +1,80 @@
1
+ import { z } from 'zod/v4';
2
+ import { SUCCESSFUL_HTTP_STATUS_CODES } from "../HttpStatusCodes.js";
3
+ import { ContractNoBody } from "./constants.js";
4
+ import { isAnyOfResponses, isBlobResponse, isSseResponse, isTextResponse, } from "./contractResponse.js";
5
+ export const defineApiContract = (contract) => contract;
6
+ export const mapApiContractToPath = (routeConfig) => {
7
+ if (!routeConfig.requestPathParamsSchema) {
8
+ return routeConfig.pathResolver(undefined);
9
+ }
10
+ const resolverParams = Object.keys(routeConfig.requestPathParamsSchema.shape).reduce((acc, key) => {
11
+ acc[key] = `:${key}`;
12
+ return acc;
13
+ }, {});
14
+ return routeConfig.pathResolver(resolverParams);
15
+ };
16
+ export const describeApiContract = (routeConfig) => {
17
+ return `${routeConfig.method.toUpperCase()} ${mapApiContractToPath(routeConfig)}`;
18
+ };
19
+ export const getSseSchemaByEventName = (routeConfig) => {
20
+ const result = {};
21
+ for (const value of Object.values(routeConfig.responsesByStatusCode)) {
22
+ if (isSseResponse(value)) {
23
+ Object.assign(result, value.schemaByEventName);
24
+ }
25
+ else if (isAnyOfResponses(value)) {
26
+ for (const response of value.responses) {
27
+ if (isSseResponse(response)) {
28
+ Object.assign(result, response.schemaByEventName);
29
+ }
30
+ }
31
+ }
32
+ }
33
+ return Object.keys(result).length > 0 ? result : null;
34
+ };
35
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: it is acceptable
36
+ export const getSuccessResponseSchema = (routeConfig) => {
37
+ const schemas = [];
38
+ let hasDirectNonJsonEntry = false;
39
+ for (const code of SUCCESSFUL_HTTP_STATUS_CODES) {
40
+ const value = routeConfig.responsesByStatusCode[code];
41
+ if (!value) {
42
+ continue;
43
+ }
44
+ if (isAnyOfResponses(value)) {
45
+ for (const response of value.responses) {
46
+ if (!isSseResponse(response) && !isTextResponse(response) && !isBlobResponse(response)) {
47
+ schemas.push(response);
48
+ }
49
+ }
50
+ }
51
+ else if (value === ContractNoBody ||
52
+ isSseResponse(value) ||
53
+ isTextResponse(value) ||
54
+ isBlobResponse(value)) {
55
+ hasDirectNonJsonEntry = true;
56
+ }
57
+ else {
58
+ schemas.push(value);
59
+ }
60
+ }
61
+ if (schemas.length > 1) {
62
+ return z.union(schemas);
63
+ }
64
+ if (schemas.length === 1) {
65
+ return schemas[0];
66
+ }
67
+ return hasDirectNonJsonEntry ? z.never() : null;
68
+ };
69
+ export const getIsEmptyResponseExpected = (routeConfig) => {
70
+ let isEmptyResponseExpected = true;
71
+ for (const code of SUCCESSFUL_HTTP_STATUS_CODES) {
72
+ const value = routeConfig.responsesByStatusCode[code];
73
+ if (value && value !== ContractNoBody) {
74
+ isEmptyResponseExpected = false;
75
+ break;
76
+ }
77
+ }
78
+ return isEmptyResponseExpected;
79
+ };
80
+ //# sourceMappingURL=defineApiContract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineApiContract.js","sourceRoot":"","sources":["../../src/new/defineApiContract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import type { z } from 'zod/v4';
2
+ import type { SuccessfulHttpStatusCode } from '../HttpStatusCodes.ts';
3
+ import type { IsUnion, ValueOf } from '../typeUtils.ts';
4
+ import type { ContractNoBody } from './constants.ts';
5
+ import type { ResponsesByStatusCode } from './contractResponse.ts';
6
+ type ExtractSuccessResponses<T extends ResponsesByStatusCode> = ValueOf<T, Extract<keyof T, SuccessfulHttpStatusCode>>;
7
+ /**
8
+ * Returns true if all success responses have no body (ContractNoBody or no success status codes defined).
9
+ */
10
+ export type IsNoBodySuccessResponse<T extends ResponsesByStatusCode> = [
11
+ ExtractSuccessResponses<T>
12
+ ] extends [typeof ContractNoBody | undefined] ? true : false;
13
+ type UnpackAnyOf<T> = T extends {
14
+ _tag: 'AnyOfResponses';
15
+ responses: Array<infer Item>;
16
+ } ? Item : T;
17
+ type FlatSuccessResponses<T extends ResponsesByStatusCode> = UnpackAnyOf<ExtractSuccessResponses<T>>;
18
+ /**
19
+ * Returns true if any success status code entry is TypedSseResponse,
20
+ * or an AnyOfResponses containing a TypedSseResponse.
21
+ */
22
+ export type HasAnySseSuccessResponse<T extends ResponsesByStatusCode> = Extract<FlatSuccessResponses<T>, {
23
+ _tag: 'SseResponse';
24
+ }> extends never ? false : true;
25
+ type SseSchemaOf<T> = T extends {
26
+ _tag: 'SseResponse';
27
+ schemaByEventName: infer S;
28
+ } ? S : never;
29
+ /**
30
+ * Extracts the merged SSE event schema map from a responsesByStatusCode map.
31
+ * Returns the union of all `schemaByEventName` objects from TypedSseResponse entries,
32
+ * including those nested inside AnyOfResponses.
33
+ */
34
+ export type InferSseSuccessResponses<T extends ResponsesByStatusCode> = SseSchemaOf<FlatSuccessResponses<T>>;
35
+ /**
36
+ * Returns true if any success status code entry is a JSON Zod schema,
37
+ * or an AnyOfResponses containing one.
38
+ */
39
+ export type HasAnyJsonSuccessResponse<T extends ResponsesByStatusCode> = Extract<FlatSuccessResponses<T>, z.ZodType> extends never ? false : true;
40
+ type JsonSchemaOf<T> = T extends z.ZodType ? T : never;
41
+ /**
42
+ * Extracts the union of JSON Zod schemas from all success responses,
43
+ * including those nested inside AnyOfResponses. Text, Blob, and SSE responses are excluded.
44
+ */
45
+ export type InferJsonSuccessResponses<T extends ResponsesByStatusCode> = JsonSchemaOf<FlatSuccessResponses<T>>;
46
+ type NonSseBodyOf<T> = T extends {
47
+ _tag: 'SseResponse';
48
+ } ? never : T extends {
49
+ _tag: 'BlobResponse';
50
+ } ? Blob : T extends {
51
+ _tag: 'TextResponse';
52
+ } ? string : T extends z.ZodType ? z.output<T> : undefined;
53
+ /**
54
+ * Infers the TypeScript output type of all non-SSE success responses.
55
+ * JSON schemas → z.output<T>. TextResponse → string. BlobResponse → Blob.
56
+ * ContractNoBody → undefined. SseResponse → never (excluded).
57
+ * AnyOfResponses are unpacked before mapping.
58
+ */
59
+ export type InferNonSseSuccessResponses<T extends ResponsesByStatusCode> = NonSseBodyOf<FlatSuccessResponses<T>>;
60
+ /**
61
+ * Discriminated union of SSE events inferred from a schemaByEventName map.
62
+ * Each event is `{ event: EventName, data: z.output<Schema> }`.
63
+ */
64
+ export type SseEventOf<S> = {
65
+ [K in keyof S]: K extends string ? {
66
+ event: K;
67
+ data: S[K] extends z.ZodType ? z.output<S[K]> : never;
68
+ } : never;
69
+ }[keyof S];
70
+ /**
71
+ * True when the contract has both SSE and non-SSE success responses (dual-mode).
72
+ */
73
+ export type IsDualModeSse<T extends ResponsesByStatusCode> = HasAnySseSuccessResponse<T> extends true ? IsUnion<AvailableResponseModes<T>> extends true ? true : false : false;
74
+ /**
75
+ * Union of response mode literals available for a given responsesByStatusCode map.
76
+ */
77
+ export type AvailableResponseModes<T extends ResponsesByStatusCode> = (HasAnyJsonSuccessResponse<T> extends true ? 'json' : never) | (HasAnySseSuccessResponse<T> extends true ? 'sse' : never) | (Extract<FlatSuccessResponses<T>, {
78
+ _tag: 'BlobResponse';
79
+ }> extends never ? never : 'blob') | (Extract<FlatSuccessResponses<T>, {
80
+ _tag: 'TextResponse';
81
+ }> extends never ? never : 'text') | (Extract<FlatSuccessResponses<T>, typeof ContractNoBody> extends never ? never : 'noContent');
82
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inferTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inferTypes.js","sourceRoot":"","sources":["../../src/new/inferTypes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Returns true when T is a union with more than one member.
3
+ */
4
+ export type IsUnion<T, U = T> = (T extends unknown ? ([U] extends [T] ? 0 : 1) : never) extends 0 ? false : true;
5
+ /**
6
+ * Helper to prevent extra keys. If T has keys not in U, it forces an error.
7
+ */
8
+ export type Exactly<T, U> = T & {
9
+ [K in keyof T]: K extends keyof U ? T[K] : never;
10
+ };
11
+ /**
12
+ * Extracts a union of value types from an object type.
13
+ * Optionally constrained to a subset of keys via ValueType.
14
+ */
15
+ export type ValueOf<ObjectType, ValueType extends keyof ObjectType = keyof ObjectType> = ObjectType[ValueType];
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=typeUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeUtils.js","sourceRoot":"","sources":["../src/typeUtils.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/api-contracts",
3
- "version": "6.7.0",
3
+ "version": "6.8.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],