@lokalise/api-contracts
Version:
139 lines • 5.34 kB
JavaScript
/**
* @deprecated Use `defineApiContract` with `sseResponse()` or `anyOfResponses()` instead. This function will be removed in a future version.
* @example
* ```typescript
* // SSE-only — Before (deprecated):
* const contract = buildSseContract({
* method: 'get',
* pathResolver: () => '/stream',
* serverSentEventSchemas: { event: eventSchema },
* })
*
* // SSE-only — After (recommended):
* const contract = defineApiContract({
* method: 'get',
* pathResolver: () => '/stream',
* responsesByStatusCode: { 200: sseResponse({ event: eventSchema }) },
* })
*
* // Dual-mode — Before (deprecated):
* const contract = buildSseContract({
* method: 'post',
* pathResolver: () => '/stream',
* requestBodySchema: bodySchema,
* successResponseBodySchema: jsonSchema,
* serverSentEventSchemas: { chunk: chunkSchema },
* })
*
* // Dual-mode — After (recommended):
* const contract = defineApiContract({
* method: 'post',
* pathResolver: () => '/stream',
* requestBodySchema: bodySchema,
* responsesByStatusCode: {
* 200: anyOfResponses([sseResponse({ chunk: chunkSchema }), jsonSchema]),
* },
* })
* ```
*
* Builds SSE (Server-Sent Events) and dual-mode contracts.
*
* This builder supports two contract types:
*
* **SSE-only contracts**: Pure streaming endpoints that only return SSE events.
* Use these for real-time notifications, live feeds, or any endpoint that only streams data.
*
* **Dual-mode contracts**: 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 (`application/json` for sync, `text/event-stream` for SSE).
* This is ideal for AI/LLM APIs (like OpenAI) where clients can choose between
* getting the full response at once or streaming it token-by-token.
*
* The contract type is automatically determined based on the presence of `successResponseBodySchema`:
*
* | `successResponseBodySchema` | `requestBodySchema` | Result |
* |----------------------------|---------------------|--------|
* | ❌ | ❌ | SSE-only GET |
* | ❌ | ✅ | SSE-only POST/PUT/PATCH |
* | ✅ | ❌ | Dual-mode GET |
* | ✅ | ✅ | Dual-mode POST/PUT/PATCH |
*
* @example
* ```typescript
* // SSE-only: Pure streaming endpoint (e.g., live notifications)
* const notificationsStream = buildSseContract({
* pathResolver: () => '/api/notifications/stream',
* requestPathParamsSchema: z.object({}),
* requestQuerySchema: z.object({ userId: z.string().optional() }),
* requestHeaderSchema: z.object({}),
* serverSentEventSchemas: {
* notification: z.object({ id: z.string(), message: z.string() }),
* },
* })
*
* // Dual-mode: Same endpoint supports both JSON and SSE (e.g., OpenAI-style API)
* // - Accept: application/json → returns { reply, usage } immediately
* // - Accept: text/event-stream → streams chunk events, then done event
* const chatCompletion = buildSseContract({
* method: 'POST',
* pathResolver: () => '/api/chat/completions',
* requestPathParamsSchema: z.object({}),
* requestQuerySchema: z.object({}),
* requestHeaderSchema: z.object({}),
* requestBodySchema: z.object({ message: z.string() }),
* successResponseBodySchema: z.object({ reply: z.string(), usage: z.object({ tokens: z.number() }) }),
* serverSentEventSchemas: {
* chunk: z.object({ delta: z.string() }),
* done: z.object({ usage: z.object({ total: z.number() }) }),
* },
* })
* ```
*/
// Helper to build base contract fields
// biome-ignore lint/suspicious/noExplicitAny: Config union type
function buildBaseFields(config, hasBody) {
return {
pathResolver: config.pathResolver,
requestPathParamsSchema: config.requestPathParamsSchema,
requestQuerySchema: config.requestQuerySchema,
requestHeaderSchema: config.requestHeaderSchema,
requestBodySchema: hasBody ? config.requestBodySchema : undefined,
serverSentEventSchemas: config.serverSentEventSchemas,
metadata: config.metadata,
description: config.description,
summary: config.summary,
tags: config.tags,
};
}
// Helper to determine method
function determineMethod(config) {
return config.method;
}
// Implementation
export function buildSseContract(config) {
const hasSyncResponseBody = 'successResponseBodySchema' in config && config.successResponseBodySchema !== undefined;
const hasBody = 'requestBodySchema' in config && config.requestBodySchema !== undefined;
const base = buildBaseFields(config, hasBody);
if (hasSyncResponseBody) {
// Dual-mode contract
return {
...base,
method: determineMethod(config),
successResponseBodySchema: config
.successResponseBodySchema,
responseHeaderSchema: config.responseHeaderSchema,
responseBodySchemasByStatusCode: config
.responseBodySchemasByStatusCode,
isDualMode: true,
};
}
// SSE-only contract
return {
...base,
method: determineMethod(config),
responseBodySchemasByStatusCode: config
.responseBodySchemasByStatusCode,
isSSE: true,
};
}
//# sourceMappingURL=sseContractBuilders.js.map