{"version":3,"file":"index.cjs","sources":["../src/runtime.ts","../src/index.ts"],"sourcesContent":["import * as qs from \"./query\";\nimport { joinUrl } from \"./util\";\nimport { ok } from \"./index\";\nimport { CustomHeaders, mergeHeaders, normalizeHeaders } from \"./headers\";\n\nexport { type CustomHeaders };\n\nexport type RequestOpts = {\n  baseUrl?: string;\n  fetch?: typeof fetch;\n  formDataConstructor?: new () => FormData;\n  headers?: HeadersInit | CustomHeaders;\n} & Omit<RequestInit, \"body\" | \"headers\">;\n\nexport type Defaults<Headers extends RequestOpts[\"headers\"] = CustomHeaders> =\n  Omit<RequestOpts, \"headers\" | \"baseUrl\"> & {\n    baseUrl: string;\n    headers: Headers;\n  };\n\ntype FetchRequestOpts = RequestOpts & {\n  body?: string | FormData | Blob;\n};\n\ntype JsonRequestOpts = RequestOpts & {\n  body?: any;\n};\n\ntype FormRequestOpts = RequestOpts & {\n  body?: Record<string, any>;\n};\n\nexport type ApiResponse = { status: number; data?: any };\n\nexport type WithHeaders<T extends ApiResponse> = T & { headers: Headers };\n\ntype MultipartRequestOpts = RequestOpts & {\n  body?: Record<string, unknown>;\n};\n\nexport function runtime(defaults: RequestOpts = {}) {\n  async function fetchText(url: string, req?: FetchRequestOpts) {\n    const res = await doFetch(url, req);\n    let data;\n    try {\n      data = await res.text();\n    } catch (err) {}\n\n    return {\n      status: res.status,\n      headers: res.headers,\n      contentType: res.headers.get(\"content-type\"),\n      data,\n    };\n  }\n\n  async function fetchJson<T extends ApiResponse>(\n    url: string,\n    req: FetchRequestOpts = {},\n  ) {\n    const { status, headers, contentType, data } = await fetchText(url, {\n      ...req,\n      headers: mergeHeaders(\n        {\n          Accept: \"application/json\",\n        },\n        req.headers,\n      ),\n    });\n\n    const isJson = contentType ? contentType.includes(\"json\") : false;\n\n    if (isJson) {\n      return {\n        status,\n        headers,\n        data: data ? JSON.parse(data) : null,\n      } as WithHeaders<T>;\n    }\n\n    return { status, headers, data } as WithHeaders<T>;\n  }\n\n  async function fetchBlob<T extends ApiResponse>(\n    url: string,\n    req: FetchRequestOpts = {},\n  ) {\n    const res = await doFetch(url, req);\n    let data;\n    try {\n      data = await res.blob();\n    } catch (err) {}\n    return { status: res.status, headers: res.headers, data } as WithHeaders<T>;\n  }\n\n  async function doFetch(url: string, req: FetchRequestOpts = {}) {\n    const {\n      baseUrl,\n      fetch: customFetch,\n      ...init\n    } = {\n      ...defaults,\n      ...req,\n      headers: mergeHeaders(defaults.headers, req.headers),\n    };\n    const href = joinUrl(baseUrl, url);\n    const res = await (customFetch || fetch)(href, init);\n    return res;\n  }\n\n  return {\n    ok,\n    fetchText,\n    fetchJson,\n    fetchBlob,\n    mergeHeaders,\n\n    json({ body, headers, ...req }: JsonRequestOpts) {\n      return {\n        ...req,\n        ...(body != null && { body: JSON.stringify(body) }),\n        headers: mergeHeaders(\n          {\n            \"Content-Type\": \"application/json\",\n          },\n          headers,\n        ),\n      };\n    },\n\n    form({ body, headers, ...req }: FormRequestOpts) {\n      return {\n        ...req,\n        ...(body != null && { body: qs.form(body) }),\n        headers: mergeHeaders(\n          {\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n          },\n          headers,\n        ),\n      };\n    },\n\n    multipart({ body, headers, ...req }: MultipartRequestOpts) {\n      if (body == null)\n        return { ...req, body, headers: normalizeHeaders(headers) };\n\n      const data = new (defaults.formDataConstructor ||\n        req.formDataConstructor ||\n        FormData)();\n\n      const append = (name: string, value: unknown) => {\n        if (typeof value === \"string\" || value instanceof Blob) {\n          data.append(name, value);\n        } else if (typeof value === \"number\" || typeof value === \"boolean\") {\n          data.append(name, String(value));\n        } else {\n          data.append(\n            name,\n            new Blob([JSON.stringify(value)], { type: \"application/json\" }),\n          );\n        }\n      };\n\n      Object.entries(body).forEach(([name, value]) => {\n        if (Array.isArray(value)) {\n          value.forEach((v) => append(name, v));\n        } else {\n          append(name, value);\n        }\n      });\n\n      return {\n        ...req,\n        body: data,\n        headers: normalizeHeaders(headers),\n      };\n    },\n  };\n}\n","import type { ApiResponse, WithHeaders } from \"./runtime\";\n\nexport * from \"./runtime\";\n\n/**\n * Type to access a response's data property for a given status.\n */\nexport type DataType<T extends ApiResponse, S extends number> = T extends {\n  status: S;\n}\n  ? T[\"data\"]\n  : never;\n\n/**\n * Object with methods to handle possible status codes of an ApiResponse.\n */\nexport type ResponseHandler<T extends ApiResponse> = {\n  [P in T[\"status\"]]?: (res: DataType<T, P>) => any;\n} & {\n  default?: (status: number, data: any) => any;\n};\n\nexport type FunctionReturnType<T> = T extends (...args: any[]) => any\n  ? ReturnType<T>\n  : never;\n\n/**\n * Utility function to handle different status codes.\n *\n * Example:\n *\n * const userId = await handle(api.register({ email, password }), {\n *   200: (user: User) => user.id,\n *   400: (err: string) => console.log(err),\n * })\n **/\nexport async function handle<\n  T extends WithHeaders<ApiResponse>,\n  H extends ResponseHandler<T>,\n>(promise: Promise<T>, handler: H): Promise<FunctionReturnType<H[keyof H]>> {\n  const { status, data, headers } = await promise;\n  const statusHandler = (handler as any)[status];\n  if (statusHandler) return statusHandler(data);\n  if (handler.default) return handler.default(status, data);\n  throw new HttpError(status, data, headers);\n}\n\nexport const SUCCESS_CODES = [200, 201, 202, 204] as const;\nexport type SuccessCodes = (typeof SUCCESS_CODES)[number];\n\nexport type SuccessResponse<T extends ApiResponse> = DataType<T, SuccessCodes>;\n\n/**\n * Utility function to directly return any successful response\n * and throw a HttpError otherwise.\n *\n * Example:\n *\n * try {\n *   const userId = await ok(api.register({ email, password }));\n * }\n * catch (err) {\n *   console.log(err.status)\n * }\n */\nexport async function ok<T extends WithHeaders<ApiResponse>>(\n  promise: Promise<T>,\n): Promise<SuccessResponse<T>> {\n  const res = await promise;\n  if (SUCCESS_CODES.some((s) => s == res.status)) return res.data;\n  throw new HttpError(res.status, res.data, res.headers);\n}\n\nexport type Args<T> = T extends (...args: infer U) => any ? U : any;\nexport type ApiFunction = (...args: any[]) => Promise<WithHeaders<ApiResponse>>;\nexport type AsyncReturnType<T> = T extends (...args: any[]) => Promise<infer V>\n  ? V\n  : never;\n\nexport type OkResponse<T extends ApiFunction> = SuccessResponse<\n  AsyncReturnType<T>\n>;\n\nexport type Okify<T extends ApiFunction> = (\n  ...args: Args<T>\n) => Promise<OkResponse<T>>;\n\n/**\n * Utility function to wrap an API function with `ok(...)`.\n */\nexport function okify<T extends ApiFunction>(fn: T): Okify<T> {\n  return (...args: Args<T>) => ok(fn(...args));\n}\n\ntype OptimisticApi<T> = {\n  [K in keyof T]: T[K] extends ApiFunction ? Okify<T[K]> : T[K];\n};\n\n/**\n * Utility to `okify` each function of an API.\n */\nexport function optimistic<T extends Record<string, ApiFunction | unknown>>(\n  api: T,\n): OptimisticApi<T> {\n  const okApi: any = {};\n  Object.entries(api).forEach(([key, value]) => {\n    okApi[key] = typeof value === \"function\" ? okify(value as any) : value;\n  });\n  return okApi;\n}\n\nexport class HttpError extends Error {\n  status: number;\n  data?: any;\n  headers: Headers;\n\n  constructor(status: number, data: any, headers: Headers) {\n    super(`Error: ${status}`);\n    this.status = status;\n    this.data = data;\n    this.headers = headers;\n  }\n}\n"],"names":["runtime","defaults","fetchText","url","req","__async","res","doFetch","data","err","fetchJson","_0","status","headers","contentType","__spreadProps","__spreadValues","mergeHeaders","fetchBlob","_a","baseUrl","customFetch","init","__objRest","href","joinUrl","ok","_b","body","_c","_d","qs.form","_e","_f","normalizeHeaders","append","name","value","v","handle","promise","handler","statusHandler","HttpError","SUCCESS_CODES","s","okify","fn","args","optimistic","api","okApi","key","__publicField"],"mappings":"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"}