@kubb/plugin-axios 5.0.0-beta.75 → 5.0.0-beta.77
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.
- package/dist/index.cjs +178 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +178 -119
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/generators/clientGenerator.tsx +2 -1
- package/templates/axios.ts +50 -10
package/templates/axios.ts
CHANGED
|
@@ -86,7 +86,8 @@ export type BodySerializer = (body: unknown, contentType?: string) => unknown
|
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
88
|
* Parses a value before it is sent or after it is received, returning the parsed (and optionally
|
|
89
|
-
* transformed) value. Wires zod parsing through the per-call `parser.request` / `parser.response`
|
|
89
|
+
* transformed) value. Wires zod parsing through the per-call `parser.request` / `parser.response` /
|
|
90
|
+
* `parser.error` hooks (`error` runs on the error body when a non-2xx call does not throw).
|
|
90
91
|
*/
|
|
91
92
|
export type Parser<T = unknown> = (value: T) => T | Promise<T>
|
|
92
93
|
|
|
@@ -113,6 +114,17 @@ export type AuthToken = string | undefined
|
|
|
113
114
|
*/
|
|
114
115
|
export type AuthResolver = AuthToken | ((auth: Auth) => AuthToken | Promise<AuthToken>)
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Extra axios config the runtime spreads onto every request, an escape hatch for the per-call fields
|
|
119
|
+
* it does not set itself, such as `timeout`, `proxy`, `maxRedirects`, `decompress`, and the
|
|
120
|
+
* `onUploadProgress` / `onDownloadProgress` callbacks. The runtime-owned fields (`url`, `baseURL`,
|
|
121
|
+
* `method`, `headers`, `params`, `paramsSerializer`, `data`, `transformRequest`, `signal`,
|
|
122
|
+
* `responseType`, `validateStatus`) always win over anything set here. This is the per-request analog
|
|
123
|
+
* of the `transport` instance, which stays the place for cross-cutting concerns like retries and
|
|
124
|
+
* interceptors. It mirrors `options` in `@kubb/plugin-fetch`.
|
|
125
|
+
*/
|
|
126
|
+
export type AxiosOptions = AxiosRequestConfig
|
|
127
|
+
|
|
116
128
|
/**
|
|
117
129
|
* The request a generated function hands to the runtime. `body` / `headers` / `path` / `query` come
|
|
118
130
|
* from the grouped options; everything else is plain request configuration. `transport` carries an
|
|
@@ -128,6 +140,7 @@ export type RequestConfig<TBody = unknown, TRequest = AxiosRequestConfig, TRespo
|
|
|
128
140
|
body?: TBody
|
|
129
141
|
headers?: HeadersInit
|
|
130
142
|
signal?: AbortSignal
|
|
143
|
+
options?: AxiosOptions
|
|
131
144
|
contentType?: string
|
|
132
145
|
responseType?: ResponseType
|
|
133
146
|
throwOnError?: boolean
|
|
@@ -136,7 +149,7 @@ export type RequestConfig<TBody = unknown, TRequest = AxiosRequestConfig, TRespo
|
|
|
136
149
|
transport?: AxiosInstance
|
|
137
150
|
querySerializer?: QuerySerializer
|
|
138
151
|
bodySerializer?: BodySerializer
|
|
139
|
-
parser?: { request?: Parser; response?: Parser }
|
|
152
|
+
parser?: { request?: Parser; response?: Parser; error?: Parser }
|
|
140
153
|
security?: Array<Auth>
|
|
141
154
|
auth?: AuthResolver
|
|
142
155
|
}
|
|
@@ -161,6 +174,7 @@ export type Options<TData extends DataShape, ThrowOnError extends boolean = true
|
|
|
161
174
|
export type ClientConfig = {
|
|
162
175
|
baseURL?: string
|
|
163
176
|
headers?: HeadersInit
|
|
177
|
+
options?: AxiosOptions
|
|
164
178
|
throwOnError?: boolean
|
|
165
179
|
validateStatus?: (status: number) => boolean
|
|
166
180
|
transport?: AxiosInstance
|
|
@@ -254,13 +268,30 @@ function isFormBody(body: unknown): boolean {
|
|
|
254
268
|
)
|
|
255
269
|
}
|
|
256
270
|
|
|
271
|
+
function appendFormDataValue(formData: FormData, key: string, value: unknown): void {
|
|
272
|
+
if (value === undefined || value === null) return
|
|
273
|
+
if (value instanceof Blob) formData.append(key, value)
|
|
274
|
+
else if (value instanceof Date) formData.append(key, value.toISOString())
|
|
275
|
+
else if (typeof value === 'object') formData.append(key, JSON.stringify(value))
|
|
276
|
+
else formData.append(key, String(value))
|
|
277
|
+
}
|
|
278
|
+
|
|
257
279
|
/**
|
|
258
280
|
* Default body serializer: passes binary/form bodies through and JSON-serializes everything else.
|
|
259
|
-
* For `
|
|
281
|
+
* For `multipart/form-data` plain objects become `FormData` and for
|
|
282
|
+
* `application/x-www-form-urlencoded` they become `URLSearchParams`.
|
|
260
283
|
*/
|
|
261
284
|
export const defaultBodySerializer: BodySerializer = (body, contentType) => {
|
|
262
285
|
if (body === undefined || body === null) return undefined
|
|
263
286
|
if (isFormBody(body)) return body
|
|
287
|
+
if (contentType?.includes('multipart/form-data')) {
|
|
288
|
+
const formData = new FormData()
|
|
289
|
+
for (const [key, value] of Object.entries(body as Record<string, unknown>)) {
|
|
290
|
+
if (Array.isArray(value)) for (const item of value) appendFormDataValue(formData, key, item)
|
|
291
|
+
else appendFormDataValue(formData, key, value)
|
|
292
|
+
}
|
|
293
|
+
return formData
|
|
294
|
+
}
|
|
264
295
|
if (contentType?.includes('application/x-www-form-urlencoded')) {
|
|
265
296
|
return new URLSearchParams(body as Record<string, string>)
|
|
266
297
|
}
|
|
@@ -426,10 +457,7 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
426
457
|
const bodySerializer = requestConfig.bodySerializer ?? config.bodySerializer ?? defaultBodySerializer
|
|
427
458
|
|
|
428
459
|
const headers = mergeHeaders(config.headers, requestConfig.headers)
|
|
429
|
-
|
|
430
|
-
headers['Content-Type'] = requestConfig.contentType
|
|
431
|
-
}
|
|
432
|
-
const contentType = headers['Content-Type'] ?? headers['content-type']
|
|
460
|
+
const requestContentType = requestConfig.contentType ?? headers['Content-Type'] ?? headers['content-type']
|
|
433
461
|
|
|
434
462
|
const query: Record<string, unknown> = { ...((requestConfig.query ?? requestConfig.params) as Record<string, unknown> | undefined) }
|
|
435
463
|
|
|
@@ -441,6 +469,14 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
441
469
|
})
|
|
442
470
|
|
|
443
471
|
const validatedBody = await runParser(requestConfig.parser?.request, requestConfig.body)
|
|
472
|
+
const body = bodySerializer(validatedBody, requestContentType)
|
|
473
|
+
// A FormData body must keep its Content-Type unset so axios appends the multipart boundary.
|
|
474
|
+
if (body instanceof FormData) {
|
|
475
|
+
delete headers['Content-Type']
|
|
476
|
+
delete headers['content-type']
|
|
477
|
+
} else if (requestConfig.contentType) {
|
|
478
|
+
headers['Content-Type'] = requestConfig.contentType
|
|
479
|
+
}
|
|
444
480
|
const pathParams = requestConfig.path ?? {}
|
|
445
481
|
const url = (requestConfig.url ?? '').replace(/\{([^{}]+)\}/g, (_, key: string) => encodeURIComponent(String(pathParams[key] ?? '')))
|
|
446
482
|
const baseURL = [config.baseURL, requestConfig.baseURL].filter(Boolean).join('') || undefined
|
|
@@ -448,15 +484,18 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
448
484
|
const throwOnError = requestConfig.throwOnError ?? config.throwOnError ?? true
|
|
449
485
|
const validateStatus = requestConfig.validateStatus ?? config.validateStatus ?? (throwOnError ? undefined : () => true)
|
|
450
486
|
|
|
487
|
+
const options = config.options || requestConfig.options ? { ...config.options, ...requestConfig.options } : undefined
|
|
488
|
+
|
|
451
489
|
const axiosConfig: AxiosRequestConfig = {
|
|
490
|
+
...options, // timeout, proxy, maxRedirects, decompress, onUploadProgress, …
|
|
452
491
|
url,
|
|
453
492
|
baseURL,
|
|
454
493
|
method: requestConfig.method ?? 'GET',
|
|
455
494
|
headers,
|
|
456
495
|
params: query,
|
|
457
496
|
paramsSerializer: (params) => querySerializer(params as Record<string, unknown>),
|
|
458
|
-
data:
|
|
459
|
-
transformRequest: (data) =>
|
|
497
|
+
data: body,
|
|
498
|
+
transformRequest: (data) => data,
|
|
460
499
|
signal: requestConfig.signal,
|
|
461
500
|
responseType: requestConfig.responseType,
|
|
462
501
|
validateStatus,
|
|
@@ -466,10 +505,11 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
466
505
|
const response = await activeInstance.request<unknown, AxiosResponse>(axiosConfig)
|
|
467
506
|
const isSuccess = response.status >= 200 && response.status < 300
|
|
468
507
|
const data = isSuccess ? await runParser(requestConfig.parser?.response, response.data) : undefined
|
|
508
|
+
const error = isSuccess ? undefined : await runParser(requestConfig.parser?.error, response.data)
|
|
469
509
|
return {
|
|
470
510
|
status: response.status,
|
|
471
511
|
data,
|
|
472
|
-
error
|
|
512
|
+
error,
|
|
473
513
|
request: response.config as TRequest,
|
|
474
514
|
response: response as TResponse,
|
|
475
515
|
}
|