import ky from 'ky'; import type { APIExecuter, APINamespace, HttpMethod, InvalidResponse, RawArgType, ValidResponse } from '../api/base.js'; import isArray from 'lodash-es/isArray.js'; import isEmpty from 'lodash-es/isEmpty.js'; const apiPath = process.env.API_ROOT_PATH ?? process.env.VITE_API_ROOT_PATH ?? '/api'; interface BasicAPICallT< ArgType extends RawArgType, ResultType extends ValidResponse, ErrorType extends InvalidResponse > { (args: ArgType): Promise; (args: ArgType, returnError: false): Promise; (args: ArgType, returnError: true): Promise; } interface EmptyAPICallT { (): Promise; (args: undefined): Promise; (args: undefined, returnError: false): Promise; (args: undefined, returnError: true): Promise; } // eslint-disable-next-line @typescript-eslint/no-explicit-any export type ArgTypeOf = T extends APICallT ? A : never; export type APICallT< ArgType extends RawArgType, ResultType extends ValidResponse = ValidResponse, ErrorType extends InvalidResponse = InvalidResponse > = ArgType extends undefined ? EmptyAPICallT : BasicAPICallT; export type APITail = typeof GET | typeof POST | typeof PUT | typeof PATCH | typeof HEAD | typeof DELETE; const httpMethodMap = new Map([ [GET, "get"], [POST, "post"], [PUT, "put"], [PATCH, "patch"], [HEAD, "head"], [DELETE, "delete"], ]); export function extractHttpMethod(tail: APITail): HttpMethod { return httpMethodMap.get(tail) ?? "post"; } export async function GET( args?: ArgType ): Promise; export async function GET( args: ArgType | undefined, returnError: false ): Promise; export async function GET< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined >(args: ArgType | undefined, returnError: true): Promise; export async function GET< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined >(args?: ArgType, returnError = false): Promise { console.error(`Can't directly call GET. ${args}, ${returnError}. Use auto-generated path API.`); return callClientAPI("get", [], args, undefined, true); } export async function POST( args?: ArgType ): Promise; export async function POST( args: ArgType | undefined, returnError: false ): Promise; export async function POST< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args: ArgType | undefined, returnError: true): Promise; export async function POST< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args?: ArgType, returnError = false): Promise { console.error(`Can't directly call POST. ${args}, ${returnError}. Use auto-generated path API.`); return callClientAPI("post", [], args, undefined, true); } export async function PUT( args?: ArgType ): Promise; export async function PUT( args: ArgType | undefined, returnError: false ): Promise; export async function PUT< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args: ArgType | undefined, returnError: true): Promise; export async function PUT< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args?: ArgType, returnError = false): Promise { console.error(`Can't directly call PUT. ${args}, ${returnError}. Use auto-generated path API.`); return callClientAPI("put", [], args, undefined, true); } export async function PATCH( args?: ArgType ): Promise; export async function PATCH( args: ArgType | undefined, returnError: false ): Promise; export async function PATCH< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args: ArgType | undefined, returnError: true): Promise; export async function PATCH< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args?: ArgType, returnError = false): Promise { console.error(`Can't directly call PATCH. ${args}, ${returnError}. Use auto-generated path API.`); return callClientAPI("patch", [], args, undefined, true); } export async function HEAD( args?: ArgType ): Promise; export async function HEAD( args: ArgType | undefined, returnError: false ): Promise; export async function HEAD< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined >(args: ArgType | undefined, returnError: true): Promise; export async function HEAD< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined >(args?: ArgType, returnError = false): Promise { console.error(`Can't directly call HEAD. ${args}, ${returnError}. Use auto-generated path API.`); return callClientAPI("head", [], args, undefined, true); } export async function DELETE( args?: ArgType ): Promise; export async function DELETE( args: ArgType | undefined, returnError: false ): Promise; export async function DELETE< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args: ArgType | undefined, returnError: true): Promise; export async function DELETE< ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType >(args?: ArgType, returnError = false): Promise { console.error(`Can't directly call DELETE. ${args}, ${returnError}. Use auto-generated path API.`); return callClientAPI("patch", [], args, undefined, true); } export async function callClientAPI( method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record | undefined ): Promise; export async function callClientAPI( method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record | undefined, returnError: false ): Promise; export async function callClientAPI( method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record | undefined, returnError: true ): Promise; export async function callClientAPI( method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record | undefined, returnError = false ): Promise { if (isArray(path)) { path = path.join("/"); } if (args && isEmpty(args)) { args = undefined; } const result = (await (() => { if (method == "get") { return ky(apiPath, { searchParams: { ...paramArgs, ...(args as typeof paramArgs), path, }, method, headers: { "content-type": "application/json", }, timeout: 30000, retry: 0, }); } return ky(apiPath, { searchParams: { ...paramArgs, path, }, method, json: args, headers: { "content-type": "application/json", }, timeout: 30000, retry: 0, }); })().json()) as ErrorType | ResultType; if (!result.result) { if (returnError) { return result; } throw result.reason; } return result; } //TODO: type이 하나 만들어져야 함. 이것은 typescript의 type system으로. //TODO: const로 실제 구조가 하나 더 만들어 져야 함. 이것은 위의 type을 satisfy하여야 함.