@kubb/plugin-client 5.0.0-beta.3 → 5.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +24 -4
  2. package/dist/clients/axios.cjs +25 -3
  3. package/dist/clients/axios.cjs.map +1 -1
  4. package/dist/clients/axios.d.ts +9 -2
  5. package/dist/clients/axios.js +25 -3
  6. package/dist/clients/axios.js.map +1 -1
  7. package/dist/clients/fetch.cjs +20 -2
  8. package/dist/clients/fetch.cjs.map +1 -1
  9. package/dist/clients/fetch.d.ts +9 -2
  10. package/dist/clients/fetch.js +20 -2
  11. package/dist/clients/fetch.js.map +1 -1
  12. package/dist/index.cjs +524 -301
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +150 -84
  15. package/dist/index.js +525 -302
  16. package/dist/index.js.map +1 -1
  17. package/dist/templates/clients/axios.source.cjs +1 -1
  18. package/dist/templates/clients/axios.source.js +1 -1
  19. package/dist/templates/clients/fetch.source.cjs +1 -1
  20. package/dist/templates/clients/fetch.source.js +1 -1
  21. package/extension.yaml +1293 -0
  22. package/package.json +11 -17
  23. package/src/clients/axios.ts +41 -7
  24. package/src/clients/fetch.ts +30 -3
  25. package/src/components/ClassClient.tsx +17 -19
  26. package/src/components/Client.tsx +68 -51
  27. package/src/components/StaticClassClient.tsx +17 -19
  28. package/src/components/Url.tsx +7 -9
  29. package/src/components/WrapperClient.tsx +9 -5
  30. package/src/functionParams.ts +8 -8
  31. package/src/generators/classClientGenerator.tsx +40 -38
  32. package/src/generators/clientGenerator.tsx +32 -35
  33. package/src/generators/groupedClientGenerator.tsx +14 -8
  34. package/src/generators/operationsGenerator.tsx +12 -6
  35. package/src/generators/staticClassClientGenerator.tsx +34 -32
  36. package/src/plugin.ts +24 -11
  37. package/src/resolvers/resolverClient.ts +31 -8
  38. package/src/types.ts +90 -53
  39. package/src/utils.ts +30 -53
  40. package/templates/clients/axios.ts +0 -73
  41. package/templates/clients/fetch.ts +0 -96
  42. package/templates/config.ts +0 -43
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <h1>Plugin Client</h1>
2
+ <h1>@kubb/plugin-client</h1>
3
3
  <a href="https://kubb.dev" target="_blank" rel="noopener noreferrer">
4
4
  <img width="180" src="https://raw.githubusercontent.com/kubb-labs/kubb/main/assets/logo.png" alt="Kubb logo">
5
5
  </a>
@@ -15,7 +15,7 @@
15
15
  <span> · </span>
16
16
  <a href="https://codesandbox.io/s/github/kubb-labs/plugins/tree/main/examples/client" target="_blank">View Client Demo</a>
17
17
  <span> · </span>
18
- <a href="https://kubb.dev/" target="_blank">Documentation</a>
18
+ <a href="https://kubb.dev/plugins/client" target="_blank">Documentation</a>
19
19
  <span> · </span>
20
20
  <a href="https://github.com/kubb-labs/kubb/issues/" target="_blank">Report Bug</a>
21
21
  <span> · </span>
@@ -23,11 +23,31 @@
23
23
  </h4>
24
24
  </div>
25
25
 
26
- Swagger integration for Axios to generate all the different clients based on an OpenAPI specification.
26
+ `@kubb/plugin-client` generates HTTP clients from your OpenAPI specification. It supports Axios, Fetch, and custom adapters, with request and response types inferred directly from the spec.
27
+
28
+ ## Features
29
+
30
+ - Supports Axios, Fetch, and custom HTTP adapters
31
+ - Infers request params, request body, and response types from the spec
32
+ - Works with `@kubb/plugin-ts` and `@kubb/plugin-zod`
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ bun add @kubb/plugin-client
38
+ # or
39
+ pnpm add @kubb/plugin-client
40
+ # or
41
+ npm install @kubb/plugin-client
42
+ ```
43
+
44
+ ## Documentation
45
+
46
+ See the [full documentation](https://kubb.dev/plugins/client) for configuration options and examples.
27
47
 
28
48
  ## Supporting Kubb
29
49
 
30
- Kubb uses an MIT-licensed open source project with its ongoing development made possible entirely by the support of Sponsors. If you would like to become a sponsor, please consider:
50
+ Kubb is an MIT-licensed open source project with its ongoing development made possible entirely by the support of Sponsors. If you would like to become a sponsor, please consider:
31
51
 
32
52
  - [Become a Sponsor on GitHub](https://github.com/sponsors/stijnvanhulle)
33
53
 
@@ -21,15 +21,37 @@ const mergeConfig = (...configs) => {
21
21
  ...merged,
22
22
  ...config,
23
23
  headers: {
24
- ...merged.headers,
25
- ...config.headers
24
+ ...Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers,
25
+ ...Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers
26
26
  }
27
27
  };
28
28
  }, {});
29
29
  };
30
+ /**
31
+ * Serializes header values into the string form axios ultimately puts on the wire.
32
+ * Objects (including arrays) are JSON-stringified so spec-defined object headers like `X-Filter`
33
+ * are sent in their canonical JSON-string form rather than `[object Object]`.
34
+ */
35
+ function serializeHeaders(headers) {
36
+ if (!headers) return {};
37
+ const entries = Array.isArray(headers) ? headers : Object.entries(headers);
38
+ const result = {};
39
+ for (const [key, value] of entries) {
40
+ if (value === void 0 || value === null) continue;
41
+ result[key] = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
42
+ }
43
+ return result;
44
+ }
30
45
  const axiosInstance = axios.default.create(getConfig());
31
46
  const client = async (config, _request) => {
32
- return axiosInstance.request(mergeConfig(getConfig(), config)).catch((e) => {
47
+ const { contentType, headers, ...axiosConfig } = mergeConfig(getConfig(), config);
48
+ return axiosInstance.request({
49
+ ...axiosConfig,
50
+ headers: {
51
+ ...contentType && contentType !== "multipart/form-data" ? { "Content-Type": contentType } : {},
52
+ ...serializeHeaders(headers)
53
+ }
54
+ }).catch((e) => {
33
55
  throw e;
34
56
  });
35
57
  };
@@ -1 +1 @@
1
- {"version":3,"file":"axios.cjs","names":[],"sources":["../../src/clients/axios.ts"],"sourcesContent":["import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'\nimport axios from 'axios'\n\ndeclare const AXIOS_BASE: string\ndeclare const AXIOS_HEADERS: string\n\n/**\n * Subset of AxiosRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n validateStatus?: (status: number) => boolean\n headers?: AxiosRequestConfig['headers']\n paramsSerializer?: AxiosRequestConfig['paramsSerializer']\n}\n\n/**\n * Subset of AxiosResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: AxiosResponse['headers']\n}\n\nexport type ResponseErrorConfig<TError = unknown> = AxiosError<TError>\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nlet _config: Partial<RequestConfig> = {\n baseURL: typeof AXIOS_BASE !== 'undefined' ? AXIOS_BASE : undefined,\n headers: typeof AXIOS_HEADERS !== 'undefined' ? JSON.parse(AXIOS_HEADERS) : undefined,\n}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: RequestConfig) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...merged.headers,\n ...config.headers,\n },\n }\n }, {})\n}\n\nexport const axiosInstance = axios.create(getConfig())\n\nexport const client = async <TResponseData, TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n return axiosInstance.request<TResponseData, ResponseConfig<TResponseData>>(mergeConfig(getConfig(), config)).catch((e: AxiosError<TError>) => {\n throw e\n })\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;;;;;;;AAuCA,IAAI,UAAkC;CACpC,SAAS,OAAO,eAAe,cAAc,aAAa,KAAA;CAC1D,SAAS,OAAO,kBAAkB,cAAc,KAAK,MAAM,cAAc,GAAG,KAAA;CAC7E;AAED,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAA0B;AAClD,WAAU;AACV,QAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;AACjG,QAAO,QAAQ,QAAoB,QAAQ,WAAW;AACpD,SAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAG,OAAO;IACV,GAAG,OAAO;IACX;GACF;IACA,EAAE,CAAC;;AAGR,MAAa,gBAAgB,MAAA,QAAM,OAAO,WAAW,CAAC;AAEtD,MAAa,SAAS,OACpB,QACA,aAC2C;AAC3C,QAAO,cAAc,QAAsD,YAAY,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO,MAA0B;AAC5I,QAAM;GACN;;AAGJ,OAAO,YAAY;AACnB,OAAO,YAAY"}
1
+ {"version":3,"file":"axios.cjs","names":[],"sources":["../../src/clients/axios.ts"],"sourcesContent":["import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'\nimport axios from 'axios'\n\ndeclare const AXIOS_BASE: string\ndeclare const AXIOS_HEADERS: string\n\n/**\n * Header values may be objects (e.g. JSON-encoded headers like `X-Filter` in the Linode API).\n * Non-string values are JSON-serialized before the request is sent.\n */\nexport type HeaderValue = string | number | boolean | null | undefined | object\nexport type HeadersInit = Array<[string, HeaderValue]> | Record<string, HeaderValue>\n\n/**\n * Subset of AxiosRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n validateStatus?: (status: number) => boolean\n headers?: HeadersInit\n paramsSerializer?: AxiosRequestConfig['paramsSerializer']\n contentType?: string\n}\n\n/**\n * Subset of AxiosResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: AxiosResponse['headers']\n}\n\nexport type ResponseErrorConfig<TError = unknown> = AxiosError<TError>\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nlet _config: Partial<RequestConfig> = {\n baseURL: typeof AXIOS_BASE !== 'undefined' ? AXIOS_BASE : undefined,\n headers: typeof AXIOS_HEADERS !== 'undefined' ? JSON.parse(AXIOS_HEADERS) : undefined,\n}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: RequestConfig) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...(Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers),\n ...(Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers),\n },\n }\n }, {})\n}\n\n/**\n * Serializes header values into the string form axios ultimately puts on the wire.\n * Objects (including arrays) are JSON-stringified so spec-defined object headers like `X-Filter`\n * are sent in their canonical JSON-string form rather than `[object Object]`.\n */\nfunction serializeHeaders(headers: HeadersInit | undefined): Record<string, string> {\n if (!headers) return {}\n const entries = Array.isArray(headers) ? headers : Object.entries(headers)\n const result: Record<string, string> = {}\n for (const [key, value] of entries) {\n if (value === undefined || value === null) continue\n result[key] = typeof value === 'string' ? value : typeof value === 'object' ? JSON.stringify(value) : String(value)\n }\n return result\n}\n\nexport const axiosInstance = axios.create(getConfig() as AxiosRequestConfig)\n\nexport const client = async <TResponseData, TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n const requestConfig = mergeConfig(getConfig(), config)\n const { contentType, headers, ...axiosConfig } = requestConfig\n return axiosInstance\n .request<TResponseData, ResponseConfig<TResponseData>>({\n ...axiosConfig,\n headers: {\n ...(contentType && contentType !== 'multipart/form-data' ? { 'Content-Type': contentType } : {}),\n ...serializeHeaders(headers),\n },\n })\n .catch((e: AxiosError<TError>) => {\n throw e\n })\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;;;;;;;AA+CA,IAAI,UAAkC;CACpC,SAAS,OAAO,eAAe,cAAc,aAAa,KAAA;CAC1D,SAAS,OAAO,kBAAkB,cAAc,KAAK,MAAM,cAAc,GAAG,KAAA;CAC7E;AAED,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAA0B;CAClD,UAAU;CACV,OAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;CACjG,OAAO,QAAQ,QAAoB,QAAQ,WAAW;EACpD,OAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IAChF,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IACjF;GACF;IACA,EAAE,CAAC;;;;;;;AAQR,SAAS,iBAAiB,SAA0D;CAClF,IAAI,CAAC,SAAS,OAAO,EAAE;CACvB,MAAM,UAAU,MAAM,QAAQ,QAAQ,GAAG,UAAU,OAAO,QAAQ,QAAQ;CAC1E,MAAM,SAAiC,EAAE;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,SAAS;EAClC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAC3C,OAAO,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,UAAU,WAAW,KAAK,UAAU,MAAM,GAAG,OAAO,MAAM;;CAErH,OAAO;;AAGT,MAAa,gBAAgB,MAAA,QAAM,OAAO,WAAW,CAAuB;AAE5E,MAAa,SAAS,OACpB,QACA,aAC2C;CAE3C,MAAM,EAAE,aAAa,SAAS,GAAG,gBADX,YAAY,WAAW,EAAE,OACe;CAC9D,OAAO,cACJ,QAAsD;EACrD,GAAG;EACH,SAAS;GACP,GAAI,eAAe,gBAAgB,wBAAwB,EAAE,gBAAgB,aAAa,GAAG,EAAE;GAC/F,GAAG,iBAAiB,QAAQ;GAC7B;EACF,CAAC,CACD,OAAO,MAA0B;EAChC,MAAM;GACN;;AAGN,OAAO,YAAY;AACnB,OAAO,YAAY"}
@@ -3,6 +3,12 @@ import * as _$axios from "axios";
3
3
  import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
4
4
 
5
5
  //#region src/clients/axios.d.ts
6
+ /**
7
+ * Header values may be objects (e.g. JSON-encoded headers like `X-Filter` in the Linode API).
8
+ * Non-string values are JSON-serialized before the request is sent.
9
+ */
10
+ type HeaderValue = string | number | boolean | null | undefined | object;
11
+ type HeadersInit = Array<[string, HeaderValue]> | Record<string, HeaderValue>;
6
12
  /**
7
13
  * Subset of AxiosRequestConfig
8
14
  */
@@ -15,8 +21,9 @@ type RequestConfig<TData = unknown> = {
15
21
  responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';
16
22
  signal?: AbortSignal;
17
23
  validateStatus?: (status: number) => boolean;
18
- headers?: AxiosRequestConfig['headers'];
24
+ headers?: HeadersInit;
19
25
  paramsSerializer?: AxiosRequestConfig['paramsSerializer'];
26
+ contentType?: string;
20
27
  };
21
28
  /**
22
29
  * Subset of AxiosResponse
@@ -39,5 +46,5 @@ declare const client: {
39
46
  setConfig: (config: RequestConfig) => Partial<RequestConfig<unknown>>;
40
47
  };
41
48
  //#endregion
42
- export { Client, RequestConfig, ResponseConfig, ResponseErrorConfig, axiosInstance, client, client as default, getConfig, mergeConfig, setConfig };
49
+ export { Client, HeaderValue, HeadersInit, RequestConfig, ResponseConfig, ResponseErrorConfig, axiosInstance, client, client as default, getConfig, mergeConfig, setConfig };
43
50
  //# sourceMappingURL=axios.d.ts.map
@@ -16,15 +16,37 @@ const mergeConfig = (...configs) => {
16
16
  ...merged,
17
17
  ...config,
18
18
  headers: {
19
- ...merged.headers,
20
- ...config.headers
19
+ ...Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers,
20
+ ...Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers
21
21
  }
22
22
  };
23
23
  }, {});
24
24
  };
25
+ /**
26
+ * Serializes header values into the string form axios ultimately puts on the wire.
27
+ * Objects (including arrays) are JSON-stringified so spec-defined object headers like `X-Filter`
28
+ * are sent in their canonical JSON-string form rather than `[object Object]`.
29
+ */
30
+ function serializeHeaders(headers) {
31
+ if (!headers) return {};
32
+ const entries = Array.isArray(headers) ? headers : Object.entries(headers);
33
+ const result = {};
34
+ for (const [key, value] of entries) {
35
+ if (value === void 0 || value === null) continue;
36
+ result[key] = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
37
+ }
38
+ return result;
39
+ }
25
40
  const axiosInstance = axios.create(getConfig());
26
41
  const client = async (config, _request) => {
27
- return axiosInstance.request(mergeConfig(getConfig(), config)).catch((e) => {
42
+ const { contentType, headers, ...axiosConfig } = mergeConfig(getConfig(), config);
43
+ return axiosInstance.request({
44
+ ...axiosConfig,
45
+ headers: {
46
+ ...contentType && contentType !== "multipart/form-data" ? { "Content-Type": contentType } : {},
47
+ ...serializeHeaders(headers)
48
+ }
49
+ }).catch((e) => {
28
50
  throw e;
29
51
  });
30
52
  };
@@ -1 +1 @@
1
- {"version":3,"file":"axios.js","names":[],"sources":["../../src/clients/axios.ts"],"sourcesContent":["import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'\nimport axios from 'axios'\n\ndeclare const AXIOS_BASE: string\ndeclare const AXIOS_HEADERS: string\n\n/**\n * Subset of AxiosRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n validateStatus?: (status: number) => boolean\n headers?: AxiosRequestConfig['headers']\n paramsSerializer?: AxiosRequestConfig['paramsSerializer']\n}\n\n/**\n * Subset of AxiosResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: AxiosResponse['headers']\n}\n\nexport type ResponseErrorConfig<TError = unknown> = AxiosError<TError>\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nlet _config: Partial<RequestConfig> = {\n baseURL: typeof AXIOS_BASE !== 'undefined' ? AXIOS_BASE : undefined,\n headers: typeof AXIOS_HEADERS !== 'undefined' ? JSON.parse(AXIOS_HEADERS) : undefined,\n}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: RequestConfig) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...merged.headers,\n ...config.headers,\n },\n }\n }, {})\n}\n\nexport const axiosInstance = axios.create(getConfig())\n\nexport const client = async <TResponseData, TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n return axiosInstance.request<TResponseData, ResponseConfig<TResponseData>>(mergeConfig(getConfig(), config)).catch((e: AxiosError<TError>) => {\n throw e\n })\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;;AAuCA,IAAI,UAAkC;CACpC,SAAS,OAAO,eAAe,cAAc,aAAa,KAAA;CAC1D,SAAS,OAAO,kBAAkB,cAAc,KAAK,MAAM,cAAc,GAAG,KAAA;CAC7E;AAED,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAA0B;AAClD,WAAU;AACV,QAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;AACjG,QAAO,QAAQ,QAAoB,QAAQ,WAAW;AACpD,SAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAG,OAAO;IACV,GAAG,OAAO;IACX;GACF;IACA,EAAE,CAAC;;AAGR,MAAa,gBAAgB,MAAM,OAAO,WAAW,CAAC;AAEtD,MAAa,SAAS,OACpB,QACA,aAC2C;AAC3C,QAAO,cAAc,QAAsD,YAAY,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO,MAA0B;AAC5I,QAAM;GACN;;AAGJ,OAAO,YAAY;AACnB,OAAO,YAAY"}
1
+ {"version":3,"file":"axios.js","names":[],"sources":["../../src/clients/axios.ts"],"sourcesContent":["import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'\nimport axios from 'axios'\n\ndeclare const AXIOS_BASE: string\ndeclare const AXIOS_HEADERS: string\n\n/**\n * Header values may be objects (e.g. JSON-encoded headers like `X-Filter` in the Linode API).\n * Non-string values are JSON-serialized before the request is sent.\n */\nexport type HeaderValue = string | number | boolean | null | undefined | object\nexport type HeadersInit = Array<[string, HeaderValue]> | Record<string, HeaderValue>\n\n/**\n * Subset of AxiosRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n validateStatus?: (status: number) => boolean\n headers?: HeadersInit\n paramsSerializer?: AxiosRequestConfig['paramsSerializer']\n contentType?: string\n}\n\n/**\n * Subset of AxiosResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: AxiosResponse['headers']\n}\n\nexport type ResponseErrorConfig<TError = unknown> = AxiosError<TError>\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nlet _config: Partial<RequestConfig> = {\n baseURL: typeof AXIOS_BASE !== 'undefined' ? AXIOS_BASE : undefined,\n headers: typeof AXIOS_HEADERS !== 'undefined' ? JSON.parse(AXIOS_HEADERS) : undefined,\n}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: RequestConfig) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...(Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers),\n ...(Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers),\n },\n }\n }, {})\n}\n\n/**\n * Serializes header values into the string form axios ultimately puts on the wire.\n * Objects (including arrays) are JSON-stringified so spec-defined object headers like `X-Filter`\n * are sent in their canonical JSON-string form rather than `[object Object]`.\n */\nfunction serializeHeaders(headers: HeadersInit | undefined): Record<string, string> {\n if (!headers) return {}\n const entries = Array.isArray(headers) ? headers : Object.entries(headers)\n const result: Record<string, string> = {}\n for (const [key, value] of entries) {\n if (value === undefined || value === null) continue\n result[key] = typeof value === 'string' ? value : typeof value === 'object' ? JSON.stringify(value) : String(value)\n }\n return result\n}\n\nexport const axiosInstance = axios.create(getConfig() as AxiosRequestConfig)\n\nexport const client = async <TResponseData, TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n const requestConfig = mergeConfig(getConfig(), config)\n const { contentType, headers, ...axiosConfig } = requestConfig\n return axiosInstance\n .request<TResponseData, ResponseConfig<TResponseData>>({\n ...axiosConfig,\n headers: {\n ...(contentType && contentType !== 'multipart/form-data' ? { 'Content-Type': contentType } : {}),\n ...serializeHeaders(headers),\n },\n })\n .catch((e: AxiosError<TError>) => {\n throw e\n })\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;;AA+CA,IAAI,UAAkC;CACpC,SAAS,OAAO,eAAe,cAAc,aAAa,KAAA;CAC1D,SAAS,OAAO,kBAAkB,cAAc,KAAK,MAAM,cAAc,GAAG,KAAA;CAC7E;AAED,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAA0B;CAClD,UAAU;CACV,OAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;CACjG,OAAO,QAAQ,QAAoB,QAAQ,WAAW;EACpD,OAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IAChF,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IACjF;GACF;IACA,EAAE,CAAC;;;;;;;AAQR,SAAS,iBAAiB,SAA0D;CAClF,IAAI,CAAC,SAAS,OAAO,EAAE;CACvB,MAAM,UAAU,MAAM,QAAQ,QAAQ,GAAG,UAAU,OAAO,QAAQ,QAAQ;CAC1E,MAAM,SAAiC,EAAE;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,SAAS;EAClC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAC3C,OAAO,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,UAAU,WAAW,KAAK,UAAU,MAAM,GAAG,OAAO,MAAM;;CAErH,OAAO;;AAGT,MAAa,gBAAgB,MAAM,OAAO,WAAW,CAAuB;AAE5E,MAAa,SAAS,OACpB,QACA,aAC2C;CAE3C,MAAM,EAAE,aAAa,SAAS,GAAG,gBADX,YAAY,WAAW,EAAE,OACe;CAC9D,OAAO,cACJ,QAAsD;EACrD,GAAG;EACH,SAAS;GACP,GAAI,eAAe,gBAAgB,wBAAwB,EAAE,gBAAgB,aAAa,GAAG,EAAE;GAC/F,GAAG,iBAAiB,QAAQ;GAC7B;EACF,CAAC,CACD,OAAO,MAA0B;EAChC,MAAM;GACN;;AAGN,OAAO,YAAY;AACnB,OAAO,YAAY"}
@@ -22,6 +22,21 @@ const mergeConfig = (...configs) => {
22
22
  };
23
23
  }, {});
24
24
  };
25
+ /**
26
+ * Serializes header values into the string form `fetch` expects.
27
+ * Objects (including arrays) are JSON-stringified so spec-defined object
28
+ * headers like `X-Filter` are sent in their canonical JSON-string form.
29
+ */
30
+ function serializeHeaders(headers) {
31
+ if (!headers) return {};
32
+ const entries = Array.isArray(headers) ? headers : Object.entries(headers);
33
+ const result = {};
34
+ for (const [key, value] of entries) {
35
+ if (value === void 0 || value === null) continue;
36
+ result[key] = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
37
+ }
38
+ return result;
39
+ }
25
40
  const client = async (paramsConfig, _request) => {
26
41
  const normalizedParams = new URLSearchParams();
27
42
  const config = mergeConfig(getConfig(), paramsConfig);
@@ -30,12 +45,15 @@ const client = async (paramsConfig, _request) => {
30
45
  });
31
46
  let targetUrl = [config.baseURL, config.url].filter(Boolean).join("");
32
47
  if (config.params) targetUrl += `?${normalizedParams}`;
33
- const response = await fetch(targetUrl, {
48
+ const response = await globalThis.fetch(targetUrl, {
34
49
  credentials: config.credentials || "same-origin",
35
50
  method: config.method?.toUpperCase(),
36
51
  body: config.data instanceof FormData ? config.data : JSON.stringify(config.data),
37
52
  signal: config.signal,
38
- headers: config.headers
53
+ headers: {
54
+ ...config.contentType && config.contentType !== "multipart/form-data" ? { "Content-Type": config.contentType } : {},
55
+ ...serializeHeaders(config.headers)
56
+ }
39
57
  });
40
58
  return {
41
59
  data: [
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.cjs","names":[],"sources":["../../src/clients/fetch.ts"],"sourcesContent":["/**\n * RequestCredentials\n */\nexport type RequestCredentials = 'omit' | 'same-origin' | 'include'\n\n/**\n * Subset of FetchRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n headers?: [string, string][] | Record<string, string>\n credentials?: RequestCredentials\n}\n\n/**\n * Subset of FetchResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: Headers\n}\n\nlet _config: Partial<RequestConfig> = {}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: Partial<RequestConfig>) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...(Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers),\n ...(Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers),\n },\n }\n }, {})\n}\n\nexport type ResponseErrorConfig<TError = unknown> = TError\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nexport const client = async <TResponseData, _TError = unknown, RequestData = unknown>(\n paramsConfig: RequestConfig<RequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n const normalizedParams = new URLSearchParams()\n\n const config = mergeConfig(getConfig(), paramsConfig)\n\n Object.entries(config.params || {}).forEach(([key, value]) => {\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : value.toString())\n }\n })\n\n let targetUrl = [config.baseURL, config.url].filter(Boolean).join('')\n\n if (config.params) {\n targetUrl += `?${normalizedParams}`\n }\n\n const response = await fetch(targetUrl, {\n credentials: config.credentials || 'same-origin',\n method: config.method?.toUpperCase(),\n body: config.data instanceof FormData ? config.data : JSON.stringify(config.data),\n signal: config.signal,\n headers: config.headers,\n })\n\n const data = [204, 205, 304].includes(response.status) || !response.body ? {} : await response.json()\n\n return {\n data: data as TResponseData,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers as Headers,\n }\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;;;;;AA8BA,IAAI,UAAkC,EAAE;AAExC,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAAmC;AAC3D,WAAU;AACV,QAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;AACjG,QAAO,QAAQ,QAAoB,QAAQ,WAAW;AACpD,SAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IAChF,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IACjF;GACF;IACA,EAAE,CAAC;;AAUR,MAAa,SAAS,OACpB,cACA,aAC2C;CAC3C,MAAM,mBAAmB,IAAI,iBAAiB;CAE9C,MAAM,SAAS,YAAY,WAAW,EAAE,aAAa;AAErD,QAAO,QAAQ,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;AAC5D,MAAI,UAAU,KAAA,EACZ,kBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,MAAM,UAAU,CAAC;GAE1E;CAEF,IAAI,YAAY,CAAC,OAAO,SAAS,OAAO,IAAI,CAAC,OAAO,QAAQ,CAAC,KAAK,GAAG;AAErE,KAAI,OAAO,OACT,cAAa,IAAI;CAGnB,MAAM,WAAW,MAAM,MAAM,WAAW;EACtC,aAAa,OAAO,eAAe;EACnC,QAAQ,OAAO,QAAQ,aAAa;EACpC,MAAM,OAAO,gBAAgB,WAAW,OAAO,OAAO,KAAK,UAAU,OAAO,KAAK;EACjF,QAAQ,OAAO;EACf,SAAS,OAAO;EACjB,CAAC;AAIF,QAAO;EACL,MAHW;GAAC;GAAK;GAAK;GAAI,CAAC,SAAS,SAAS,OAAO,IAAI,CAAC,SAAS,OAAO,EAAE,GAAG,MAAM,SAAS,MAAM;EAInG,QAAQ,SAAS;EACjB,YAAY,SAAS;EACrB,SAAS,SAAS;EACnB;;AAGH,OAAO,YAAY;AACnB,OAAO,YAAY"}
1
+ {"version":3,"file":"fetch.cjs","names":[],"sources":["../../src/clients/fetch.ts"],"sourcesContent":["/**\n * RequestCredentials\n */\nexport type RequestCredentials = 'omit' | 'same-origin' | 'include'\n\n/**\n * Header values may be objects (e.g. JSON-encoded filter headers like `X-Filter`).\n * Non-string values are JSON-serialized before the request is sent.\n */\nexport type HeaderValue = string | number | boolean | null | undefined | object\nexport type HeadersInit = Array<[string, HeaderValue]> | Record<string, HeaderValue>\n\n/**\n * Subset of FetchRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n headers?: HeadersInit\n credentials?: RequestCredentials\n contentType?: string\n}\n\n/**\n * Subset of FetchResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: Headers\n}\n\nlet _config: Partial<RequestConfig> = {}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: Partial<RequestConfig>) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...(Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers),\n ...(Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers),\n },\n }\n }, {})\n}\n\n/**\n * Serializes header values into the string form `fetch` expects.\n * Objects (including arrays) are JSON-stringified so spec-defined object\n * headers like `X-Filter` are sent in their canonical JSON-string form.\n */\nfunction serializeHeaders(headers: HeadersInit | undefined): Record<string, string> {\n if (!headers) return {}\n const entries = Array.isArray(headers) ? headers : Object.entries(headers)\n const result: Record<string, string> = {}\n for (const [key, value] of entries) {\n if (value === undefined || value === null) continue\n result[key] = typeof value === 'string' ? value : typeof value === 'object' ? JSON.stringify(value) : String(value)\n }\n return result\n}\n\nexport type ResponseErrorConfig<TError = unknown> = TError\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nexport const client = async <TResponseData, _TError = unknown, RequestData = unknown>(\n paramsConfig: RequestConfig<RequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n const normalizedParams = new URLSearchParams()\n\n const config = mergeConfig(getConfig(), paramsConfig)\n\n Object.entries(config.params || {}).forEach(([key, value]) => {\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : value.toString())\n }\n })\n\n let targetUrl = [config.baseURL, config.url].filter(Boolean).join('')\n\n if (config.params) {\n targetUrl += `?${normalizedParams}`\n }\n\n const response = await globalThis.fetch(targetUrl, {\n credentials: config.credentials || 'same-origin',\n method: config.method?.toUpperCase(),\n body: config.data instanceof FormData ? config.data : JSON.stringify(config.data),\n signal: config.signal,\n headers: {\n ...(config.contentType && config.contentType !== 'multipart/form-data' ? { 'Content-Type': config.contentType } : {}),\n ...serializeHeaders(config.headers),\n },\n })\n\n const data = [204, 205, 304].includes(response.status) || !response.body ? {} : await response.json()\n\n return {\n data: data as TResponseData,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers as Headers,\n }\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;;;;;AAsCA,IAAI,UAAkC,EAAE;AAExC,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAAmC;CAC3D,UAAU;CACV,OAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;CACjG,OAAO,QAAQ,QAAoB,QAAQ,WAAW;EACpD,OAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IAChF,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IACjF;GACF;IACA,EAAE,CAAC;;;;;;;AAQR,SAAS,iBAAiB,SAA0D;CAClF,IAAI,CAAC,SAAS,OAAO,EAAE;CACvB,MAAM,UAAU,MAAM,QAAQ,QAAQ,GAAG,UAAU,OAAO,QAAQ,QAAQ;CAC1E,MAAM,SAAiC,EAAE;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,SAAS;EAClC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAC3C,OAAO,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,UAAU,WAAW,KAAK,UAAU,MAAM,GAAG,OAAO,MAAM;;CAErH,OAAO;;AAUT,MAAa,SAAS,OACpB,cACA,aAC2C;CAC3C,MAAM,mBAAmB,IAAI,iBAAiB;CAE9C,MAAM,SAAS,YAAY,WAAW,EAAE,aAAa;CAErD,OAAO,QAAQ,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAC5D,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,MAAM,UAAU,CAAC;GAE1E;CAEF,IAAI,YAAY,CAAC,OAAO,SAAS,OAAO,IAAI,CAAC,OAAO,QAAQ,CAAC,KAAK,GAAG;CAErE,IAAI,OAAO,QACT,aAAa,IAAI;CAGnB,MAAM,WAAW,MAAM,WAAW,MAAM,WAAW;EACjD,aAAa,OAAO,eAAe;EACnC,QAAQ,OAAO,QAAQ,aAAa;EACpC,MAAM,OAAO,gBAAgB,WAAW,OAAO,OAAO,KAAK,UAAU,OAAO,KAAK;EACjF,QAAQ,OAAO;EACf,SAAS;GACP,GAAI,OAAO,eAAe,OAAO,gBAAgB,wBAAwB,EAAE,gBAAgB,OAAO,aAAa,GAAG,EAAE;GACpH,GAAG,iBAAiB,OAAO,QAAQ;GACpC;EACF,CAAC;CAIF,OAAO;EACL,MAHW;GAAC;GAAK;GAAK;GAAI,CAAC,SAAS,SAAS,OAAO,IAAI,CAAC,SAAS,OAAO,EAAE,GAAG,MAAM,SAAS,MAAM;EAInG,QAAQ,SAAS;EACjB,YAAY,SAAS;EACrB,SAAS,SAAS;EACnB;;AAGH,OAAO,YAAY;AACnB,OAAO,YAAY"}
@@ -5,6 +5,12 @@ import { t as __name } from "../chunk--u3MIqq1.js";
5
5
  * RequestCredentials
6
6
  */
7
7
  type RequestCredentials = 'omit' | 'same-origin' | 'include';
8
+ /**
9
+ * Header values may be objects (e.g. JSON-encoded filter headers like `X-Filter`).
10
+ * Non-string values are JSON-serialized before the request is sent.
11
+ */
12
+ type HeaderValue = string | number | boolean | null | undefined | object;
13
+ type HeadersInit = Array<[string, HeaderValue]> | Record<string, HeaderValue>;
8
14
  /**
9
15
  * Subset of FetchRequestConfig
10
16
  */
@@ -16,8 +22,9 @@ type RequestConfig<TData = unknown> = {
16
22
  data?: TData | FormData;
17
23
  responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';
18
24
  signal?: AbortSignal;
19
- headers?: [string, string][] | Record<string, string>;
25
+ headers?: HeadersInit;
20
26
  credentials?: RequestCredentials;
27
+ contentType?: string;
21
28
  };
22
29
  /**
23
30
  * Subset of FetchResponse
@@ -39,5 +46,5 @@ declare const client: {
39
46
  setConfig: (config: Partial<RequestConfig>) => Partial<RequestConfig<unknown>>;
40
47
  };
41
48
  //#endregion
42
- export { Client, RequestConfig, RequestCredentials, ResponseConfig, ResponseErrorConfig, client, client as default, getConfig, mergeConfig, setConfig };
49
+ export { Client, HeaderValue, HeadersInit, RequestConfig, RequestCredentials, ResponseConfig, ResponseErrorConfig, client, client as default, getConfig, mergeConfig, setConfig };
43
50
  //# sourceMappingURL=fetch.d.ts.map
@@ -18,6 +18,21 @@ const mergeConfig = (...configs) => {
18
18
  };
19
19
  }, {});
20
20
  };
21
+ /**
22
+ * Serializes header values into the string form `fetch` expects.
23
+ * Objects (including arrays) are JSON-stringified so spec-defined object
24
+ * headers like `X-Filter` are sent in their canonical JSON-string form.
25
+ */
26
+ function serializeHeaders(headers) {
27
+ if (!headers) return {};
28
+ const entries = Array.isArray(headers) ? headers : Object.entries(headers);
29
+ const result = {};
30
+ for (const [key, value] of entries) {
31
+ if (value === void 0 || value === null) continue;
32
+ result[key] = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
33
+ }
34
+ return result;
35
+ }
21
36
  const client = async (paramsConfig, _request) => {
22
37
  const normalizedParams = new URLSearchParams();
23
38
  const config = mergeConfig(getConfig(), paramsConfig);
@@ -26,12 +41,15 @@ const client = async (paramsConfig, _request) => {
26
41
  });
27
42
  let targetUrl = [config.baseURL, config.url].filter(Boolean).join("");
28
43
  if (config.params) targetUrl += `?${normalizedParams}`;
29
- const response = await fetch(targetUrl, {
44
+ const response = await globalThis.fetch(targetUrl, {
30
45
  credentials: config.credentials || "same-origin",
31
46
  method: config.method?.toUpperCase(),
32
47
  body: config.data instanceof FormData ? config.data : JSON.stringify(config.data),
33
48
  signal: config.signal,
34
- headers: config.headers
49
+ headers: {
50
+ ...config.contentType && config.contentType !== "multipart/form-data" ? { "Content-Type": config.contentType } : {},
51
+ ...serializeHeaders(config.headers)
52
+ }
35
53
  });
36
54
  return {
37
55
  data: [
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.js","names":[],"sources":["../../src/clients/fetch.ts"],"sourcesContent":["/**\n * RequestCredentials\n */\nexport type RequestCredentials = 'omit' | 'same-origin' | 'include'\n\n/**\n * Subset of FetchRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n headers?: [string, string][] | Record<string, string>\n credentials?: RequestCredentials\n}\n\n/**\n * Subset of FetchResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: Headers\n}\n\nlet _config: Partial<RequestConfig> = {}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: Partial<RequestConfig>) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...(Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers),\n ...(Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers),\n },\n }\n }, {})\n}\n\nexport type ResponseErrorConfig<TError = unknown> = TError\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nexport const client = async <TResponseData, _TError = unknown, RequestData = unknown>(\n paramsConfig: RequestConfig<RequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n const normalizedParams = new URLSearchParams()\n\n const config = mergeConfig(getConfig(), paramsConfig)\n\n Object.entries(config.params || {}).forEach(([key, value]) => {\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : value.toString())\n }\n })\n\n let targetUrl = [config.baseURL, config.url].filter(Boolean).join('')\n\n if (config.params) {\n targetUrl += `?${normalizedParams}`\n }\n\n const response = await fetch(targetUrl, {\n credentials: config.credentials || 'same-origin',\n method: config.method?.toUpperCase(),\n body: config.data instanceof FormData ? config.data : JSON.stringify(config.data),\n signal: config.signal,\n headers: config.headers,\n })\n\n const data = [204, 205, 304].includes(response.status) || !response.body ? {} : await response.json()\n\n return {\n data: data as TResponseData,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers as Headers,\n }\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;AA8BA,IAAI,UAAkC,EAAE;AAExC,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAAmC;AAC3D,WAAU;AACV,QAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;AACjG,QAAO,QAAQ,QAAoB,QAAQ,WAAW;AACpD,SAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IAChF,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IACjF;GACF;IACA,EAAE,CAAC;;AAUR,MAAa,SAAS,OACpB,cACA,aAC2C;CAC3C,MAAM,mBAAmB,IAAI,iBAAiB;CAE9C,MAAM,SAAS,YAAY,WAAW,EAAE,aAAa;AAErD,QAAO,QAAQ,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;AAC5D,MAAI,UAAU,KAAA,EACZ,kBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,MAAM,UAAU,CAAC;GAE1E;CAEF,IAAI,YAAY,CAAC,OAAO,SAAS,OAAO,IAAI,CAAC,OAAO,QAAQ,CAAC,KAAK,GAAG;AAErE,KAAI,OAAO,OACT,cAAa,IAAI;CAGnB,MAAM,WAAW,MAAM,MAAM,WAAW;EACtC,aAAa,OAAO,eAAe;EACnC,QAAQ,OAAO,QAAQ,aAAa;EACpC,MAAM,OAAO,gBAAgB,WAAW,OAAO,OAAO,KAAK,UAAU,OAAO,KAAK;EACjF,QAAQ,OAAO;EACf,SAAS,OAAO;EACjB,CAAC;AAIF,QAAO;EACL,MAHW;GAAC;GAAK;GAAK;GAAI,CAAC,SAAS,SAAS,OAAO,IAAI,CAAC,SAAS,OAAO,EAAE,GAAG,MAAM,SAAS,MAAM;EAInG,QAAQ,SAAS;EACjB,YAAY,SAAS;EACrB,SAAS,SAAS;EACnB;;AAGH,OAAO,YAAY;AACnB,OAAO,YAAY"}
1
+ {"version":3,"file":"fetch.js","names":[],"sources":["../../src/clients/fetch.ts"],"sourcesContent":["/**\n * RequestCredentials\n */\nexport type RequestCredentials = 'omit' | 'same-origin' | 'include'\n\n/**\n * Header values may be objects (e.g. JSON-encoded filter headers like `X-Filter`).\n * Non-string values are JSON-serialized before the request is sent.\n */\nexport type HeaderValue = string | number | boolean | null | undefined | object\nexport type HeadersInit = Array<[string, HeaderValue]> | Record<string, HeaderValue>\n\n/**\n * Subset of FetchRequestConfig\n */\nexport type RequestConfig<TData = unknown> = {\n baseURL?: string\n url?: string\n method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'\n params?: unknown\n data?: TData | FormData\n responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n signal?: AbortSignal\n headers?: HeadersInit\n credentials?: RequestCredentials\n contentType?: string\n}\n\n/**\n * Subset of FetchResponse\n */\nexport type ResponseConfig<TData = unknown> = {\n data: TData\n status: number\n statusText: string\n headers: Headers\n}\n\nlet _config: Partial<RequestConfig> = {}\n\nexport const getConfig = () => _config\n\nexport const setConfig = (config: Partial<RequestConfig>) => {\n _config = config\n return getConfig()\n}\n\nexport const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {\n return configs.reduce<Partial<T>>((merged, config) => {\n return {\n ...merged,\n ...config,\n headers: {\n ...(Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers),\n ...(Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers),\n },\n }\n }, {})\n}\n\n/**\n * Serializes header values into the string form `fetch` expects.\n * Objects (including arrays) are JSON-stringified so spec-defined object\n * headers like `X-Filter` are sent in their canonical JSON-string form.\n */\nfunction serializeHeaders(headers: HeadersInit | undefined): Record<string, string> {\n if (!headers) return {}\n const entries = Array.isArray(headers) ? headers : Object.entries(headers)\n const result: Record<string, string> = {}\n for (const [key, value] of entries) {\n if (value === undefined || value === null) continue\n result[key] = typeof value === 'string' ? value : typeof value === 'object' ? JSON.stringify(value) : String(value)\n }\n return result\n}\n\nexport type ResponseErrorConfig<TError = unknown> = TError\n\nexport type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(\n config: RequestConfig<TRequestData>,\n request?: unknown,\n) => Promise<ResponseConfig<TResponseData>>\n\nexport const client = async <TResponseData, _TError = unknown, RequestData = unknown>(\n paramsConfig: RequestConfig<RequestData>,\n _request?: unknown,\n): Promise<ResponseConfig<TResponseData>> => {\n const normalizedParams = new URLSearchParams()\n\n const config = mergeConfig(getConfig(), paramsConfig)\n\n Object.entries(config.params || {}).forEach(([key, value]) => {\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : value.toString())\n }\n })\n\n let targetUrl = [config.baseURL, config.url].filter(Boolean).join('')\n\n if (config.params) {\n targetUrl += `?${normalizedParams}`\n }\n\n const response = await globalThis.fetch(targetUrl, {\n credentials: config.credentials || 'same-origin',\n method: config.method?.toUpperCase(),\n body: config.data instanceof FormData ? config.data : JSON.stringify(config.data),\n signal: config.signal,\n headers: {\n ...(config.contentType && config.contentType !== 'multipart/form-data' ? { 'Content-Type': config.contentType } : {}),\n ...serializeHeaders(config.headers),\n },\n })\n\n const data = [204, 205, 304].includes(response.status) || !response.body ? {} : await response.json()\n\n return {\n data: data as TResponseData,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers as Headers,\n }\n}\n\nclient.getConfig = getConfig\nclient.setConfig = setConfig\n\nexport default client\n"],"mappings":";;AAsCA,IAAI,UAAkC,EAAE;AAExC,MAAa,kBAAkB;AAE/B,MAAa,aAAa,WAAmC;CAC3D,UAAU;CACV,OAAO,WAAW;;AAGpB,MAAa,eAAwC,GAAG,YAA2C;CACjG,OAAO,QAAQ,QAAoB,QAAQ,WAAW;EACpD,OAAO;GACL,GAAG;GACH,GAAG;GACH,SAAS;IACP,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IAChF,GAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,YAAY,OAAO,QAAQ,GAAG,OAAO;IACjF;GACF;IACA,EAAE,CAAC;;;;;;;AAQR,SAAS,iBAAiB,SAA0D;CAClF,IAAI,CAAC,SAAS,OAAO,EAAE;CACvB,MAAM,UAAU,MAAM,QAAQ,QAAQ,GAAG,UAAU,OAAO,QAAQ,QAAQ;CAC1E,MAAM,SAAiC,EAAE;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,SAAS;EAClC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAC3C,OAAO,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,UAAU,WAAW,KAAK,UAAU,MAAM,GAAG,OAAO,MAAM;;CAErH,OAAO;;AAUT,MAAa,SAAS,OACpB,cACA,aAC2C;CAC3C,MAAM,mBAAmB,IAAI,iBAAiB;CAE9C,MAAM,SAAS,YAAY,WAAW,EAAE,aAAa;CAErD,OAAO,QAAQ,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAC5D,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,MAAM,UAAU,CAAC;GAE1E;CAEF,IAAI,YAAY,CAAC,OAAO,SAAS,OAAO,IAAI,CAAC,OAAO,QAAQ,CAAC,KAAK,GAAG;CAErE,IAAI,OAAO,QACT,aAAa,IAAI;CAGnB,MAAM,WAAW,MAAM,WAAW,MAAM,WAAW;EACjD,aAAa,OAAO,eAAe;EACnC,QAAQ,OAAO,QAAQ,aAAa;EACpC,MAAM,OAAO,gBAAgB,WAAW,OAAO,OAAO,KAAK,UAAU,OAAO,KAAK;EACjF,QAAQ,OAAO;EACf,SAAS;GACP,GAAI,OAAO,eAAe,OAAO,gBAAgB,wBAAwB,EAAE,gBAAgB,OAAO,aAAa,GAAG,EAAE;GACpH,GAAG,iBAAiB,OAAO,QAAQ;GACpC;EACF,CAAC;CAIF,OAAO;EACL,MAHW;GAAC;GAAK;GAAK;GAAI,CAAC,SAAS,SAAS,OAAO,IAAI,CAAC,SAAS,OAAO,EAAE,GAAG,MAAM,SAAS,MAAM;EAInG,QAAQ,SAAS;EACjB,YAAY,SAAS;EACrB,SAAS,SAAS;EACnB;;AAGH,OAAO,YAAY;AACnB,OAAO,YAAY"}