import type { RequestMethod } from '../operation.js'; import type { Flatten, IsOptional } from '../type-utils.js'; /** * @description Converts an OpenAPI paths specification into our internal operation index format * - REST operations are combined with their parent path e.g. 'GET /path/a' * - Parameters are kept, or defaulted to an empty object if not present * - Response and Request objects are reformatted with generics * * Only application/json request and response bodies are included * * @example * ```ts * type A = InferOperationIndex<{ * '/path/a': { * put: { * parameters: { a: string }; * requestBody: { content: { 'application/json': { b: string } } }; * }; * }; * }>; * // A is { * // 'PUT /path/a': RequiredRequestBody<{ * // a: string; * // }, * // unknown, * // { * // b: string; * // }> * // } */ export type InferOperationIndex = Flatten<{ [PathStr in keyof PathsSpec & string]: PathOperationIndex; }>; /** * @description Formats the OpenAPI parameters, responses, and responseBody specs at a given path * in preparation for flattening the paths in to a single index type * * @see InferOperationIndex for more information */ type PathOperationIndex = { [K in keyof PathSpec as PathKey]: PathSpec[K] extends { parameters?: infer Params; responses?: infer Responses; } ? { parameters: Params & ParamsRequestBody; response: ResponseUnion; } : never; }; /** * @description Combines the REST operation method with the endpoint path to form a unique key * @example * ```ts * type A = PathKey<'get', '/path/a'> * // A is 'GET /path/a' */ type PathKey = K extends Lowercase ? `${Uppercase} ${Path}` : never; /** * @description Converts OpenAPI response specifications into a union of possible responses * Only responses with a 'application/json' content type are included * * @example * ```ts * type A = Response<{ * 200: { content: { 'application/json': { a: string } } }; * 204: { content: { 'application/json': {} } }; * }>; * // A is { status: 200, body: { a: string } } | { status: 204, body: {} } * ``` */ type ResponseUnion = { [Status in keyof ResponsesSpec]: { status: Status; body: ResponsesSpec[Status] extends { content: { 'application/json': infer ResponseBody; }; } ? ResponseBody : never; }; }[keyof ResponsesSpec]; /** * @description Extracts the request body from an OpenAPI specification * - Keeps the optional or required nature of the request body from the original spec * - Only includes 'application/json' content type request bodies * - Returns a type with no body property if the original spec has no request body property (e.g. GET requests) * * @example * ```ts * type A = ParamsRequestBody<{ * requestBody: { content: { 'application/json': { a: string } } }; * }>; * // A is { body: { a: string } } * ``` */ type ParamsRequestBody = PathSpec extends { requestBody?: { content: { 'application/json': infer RequestBody; }; }; } ? unknown extends RequestBody ? unknown : IsOptional extends true ? Partial<{ body: RequestBody; }> : { body: RequestBody; } : never; export {};