From 62515c9e099043ef32f089f1e88e303964ce325c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Burkard?= <22095555+JeromeBu@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:16:45 +0100 Subject: [PATCH] remove need for ResponseType, and use response content-type instead --- src/axios/createAxiosSharedClient.ts | 11 +----- src/defineRoutes.ts | 4 -- src/fetch/createFetchSharedClient.ts | 55 +++++++++++++++++++--------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/axios/createAxiosSharedClient.ts b/src/axios/createAxiosSharedClient.ts index a31ff63..b51586e 100644 --- a/src/axios/createAxiosSharedClient.ts +++ b/src/axios/createAxiosSharedClient.ts @@ -1,20 +1,12 @@ -import type { AxiosInstance, ResponseType as AxiosResponseType } from "axios"; +import type { AxiosInstance } from "axios"; import type { UnknownSharedRoute, Url } from ".."; import { configureCreateHttpClient, HandlerCreator } from ".."; -import { ResponseType } from "../defineRoutes"; import { HttpClientOptions, validateInputParams, validateSchemaWithExplicitError, } from "../validations"; -const toAxiosResponseType: Record = { - arrayBuffer: "arraybuffer", - blob: "blob", - json: "json", - text: "text", -}; - export const createAxiosHandlerCreator = >( axios: AxiosInstance, @@ -34,7 +26,6 @@ export const createAxiosHandlerCreator = url: replaceParamsInUrl(route.url, urlParams as Url), data: body, params: queryParams, - responseType: toAxiosResponseType[route.responseType], headers: { ...axios.defaults.headers, ...(headers ?? ({} as any)), diff --git a/src/defineRoutes.ts b/src/defineRoutes.ts index b32bde1..ac04468 100644 --- a/src/defineRoutes.ts +++ b/src/defineRoutes.ts @@ -11,14 +11,11 @@ export type ResponsesToHttpResponse = ValueO { [K in keyof Responses & number]: HttpResponse> } >; -export type ResponseType = "json" | "arrayBuffer" | "blob" | "text"; - type OptionalFields = { requestBodySchema?: z.Schema; queryParamsSchema?: z.Schema; responses?: Responses; headersSchema?: z.Schema; - responseType?: ResponseType; }; export type HttpMethod = "get" | "post" | "put" | "patch" | "delete"; @@ -73,7 +70,6 @@ export const defineRoute = < responses: { 201: z.void().or(z.string().max(0)) } as any, // as some framework return "" instead of void (like express) headersSchema: z.object({}) as any, ...route, - responseType: route.responseType ?? "json", }); const verifyRoutesUniqAndListRoutes = >( diff --git a/src/fetch/createFetchSharedClient.ts b/src/fetch/createFetchSharedClient.ts index eb7bf32..c3544e8 100644 --- a/src/fetch/createFetchSharedClient.ts +++ b/src/fetch/createFetchSharedClient.ts @@ -1,6 +1,5 @@ import type { HttpResponse, UnknownSharedRoute, Url } from ".."; import { configureCreateHttpClient, HandlerCreator } from ".."; -import { ResponseType } from "../defineRoutes"; import { convertToFormData } from "./convertToFormData"; import { HttpClientOptions, @@ -83,9 +82,8 @@ export const createFetchHandlerCreator = }, ); - const processedBody = await responseTypeToResponseBody(res, route.responseType); - const headersAsObject = objectFromEntries((res.headers as any).entries()); + const processedBody = await responseTypeToResponseBody(res); if (options?.onResponseSideEffect) { options.onResponseSideEffect({ @@ -124,21 +122,44 @@ export const createFetchHandlerCreator = }; }; -const responseTypeToResponseBody = (res: Response, responseType: ResponseType) => { - switch (responseType) { - case "json": - return res.json(); - case "text": - return res.text(); - case "blob": - return res.blob(); - case "arrayBuffer": - return res.arrayBuffer(); - default: { - const exhaustiveCheck: never = responseType; - return exhaustiveCheck; - } +const responseTypeToResponseBody = (res: Response) => { + const contentType = res.headers.get("content-type")?.toLowerCase() || ""; + + if ( + contentType.includes("application/json") || + contentType.includes("application/ld+json") + ) { + return res.json(); + } + + // Binary/blob types + if ( + contentType.includes("image/") || + contentType.includes("audio/") || + contentType.includes("video/") || + contentType.includes("application/pdf") || + contentType.includes("application/zip") || + contentType.includes("application/octet-stream") || + contentType.includes("application/vnd.") + ) { + return res.blob(); } + + // Form data + if (contentType.includes("multipart/form-data")) { + return res.formData(); + } + + // ArrayBuffer for binary data that needs processing + if ( + contentType.includes("application/java-archive") || + contentType.includes("application/x-shockwave-flash") + ) { + return res.arrayBuffer(); + } + + // Default to text for everything else (html, plain text, xml, css, etc) + return res.text(); }; export const createFetchSharedClient = <