import { HttpResponse, type DefaultBodyType, type HttpResponseInit } from "msw"; import type { Wildcard } from "./http-status-wildcard.ts"; import type { JSONLike, NoContent, TextLike } from "./type-utils.ts"; /** * Requires or removes the status code from {@linkcode HttpResponseInit} depending * on the chosen OpenAPI status code. When the status is a wildcard, a specific * status code must be provided. */ type DynamicResponseInit = Status extends keyof Wildcard ? ResponseInitForWildcard : ResponseInitNoStatus | void; interface ResponseInitNoStatus extends Omit {} interface ResponseInitForWildcard extends ResponseInitNoStatus { status: Wildcard[Key]; } /** Creates a type-safe text response, which may require an additional status code. */ type TextResponse = ( body: ResponseBody extends string ? ResponseBody : never, init: DynamicResponseInit, ) => HttpResponse; /** Creates a type-safe json response, which may require an additional status code. */ type JsonResponse = ( body: ResponseBody extends DefaultBodyType ? ResponseBody : never, init: DynamicResponseInit, ) => HttpResponse; /** Creates a type-safe empty response, which may require an additional status code. */ type EmptyResponse = ( init: DynamicResponseInit, ) => HttpResponse; /** * A type-safe response helper that narrows available status codes and content types, * based on the given OpenAPI spec. The response body is specifically narrowed to * the specified status code and content type. */ export interface OpenApiResponse< ResponseMap, ExpectedResponseBody extends DefaultBodyType, > { ( status: Status, ): { text: TextLike extends never ? unknown : TextResponse, Status>; json: JSONLike extends never ? unknown : JsonResponse, Status>; empty: NoContent extends never ? unknown : EmptyResponse; }; untyped(response: Response): HttpResponse; } export function createResponseHelper< ResponseMap, ExpectedResponseBody extends DefaultBodyType, >(): OpenApiResponse { const response: OpenApiResponse = ( status, ) => { const text: TextResponse< TextLike, typeof status > = (body, init) => { return HttpResponse.text(body, { status: status as number, ...init, }); }; const json: JsonResponse< JSONLike, typeof status > = (body, init) => { return HttpResponse.json(body, { status: status as number, ...init }); }; const empty: EmptyResponse = (init) => { const headers = new Headers(init?.headers); if (!headers.has("content-length")) headers.set("content-length", "0"); return new HttpResponse(null, { status: status as number, ...init, headers, }); }; return { text, json, empty }; }; response.untyped = (response) => { return response as HttpResponse; }; return response; }