@kubb/plugin-axios 5.0.0-beta.73 → 5.0.0-beta.76
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/LICENSE +21 -0
- package/dist/index.cjs +179 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +179 -120
- package/dist/index.js.map +1 -1
- package/package.json +24 -24
- package/src/generators/clientGenerator.tsx +2 -1
- package/src/plugin.ts +1 -3
- package/templates/axios.ts +53 -10
package/src/plugin.ts
CHANGED
|
@@ -75,9 +75,7 @@ export const pluginAxios = definePlugin<PluginAxios>((options) => {
|
|
|
75
75
|
ctx.setResolver(resolved.resolver)
|
|
76
76
|
ctx.setMacros([...defaultMacros, ...(options.macros ?? [])])
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
ctx.addGenerator(gen)
|
|
80
|
-
}
|
|
78
|
+
ctx.addGenerator(...selectedGenerators)
|
|
81
79
|
|
|
82
80
|
const root = path.resolve(ctx.config.root, ctx.config.output.path)
|
|
83
81
|
|
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
|
|
|
@@ -136,7 +137,7 @@ export type RequestConfig<TBody = unknown, TRequest = AxiosRequestConfig, TRespo
|
|
|
136
137
|
transport?: AxiosInstance
|
|
137
138
|
querySerializer?: QuerySerializer
|
|
138
139
|
bodySerializer?: BodySerializer
|
|
139
|
-
parser?: { request?: Parser; response?: Parser }
|
|
140
|
+
parser?: { request?: Parser; response?: Parser; error?: Parser }
|
|
140
141
|
security?: Array<Auth>
|
|
141
142
|
auth?: AuthResolver
|
|
142
143
|
}
|
|
@@ -214,6 +215,7 @@ export type ClientInstance<TRequest = AxiosRequestConfig, TResponse = AxiosRespo
|
|
|
214
215
|
<TBody = unknown>(config: RequestConfig<TBody, TRequest, TResponse>): Promise<CallResult<TRequest, TResponse>>
|
|
215
216
|
getConfig: () => ClientConfig
|
|
216
217
|
setConfig: (config: ClientConfig) => ClientConfig
|
|
218
|
+
getUrl: <TBody = unknown>(config: RequestConfig<TBody, TRequest, TResponse>) => string
|
|
217
219
|
interceptors: Interceptors
|
|
218
220
|
createClient: (config?: ClientConfig) => ClientInstance<TRequest, TResponse>
|
|
219
221
|
}
|
|
@@ -253,13 +255,30 @@ function isFormBody(body: unknown): boolean {
|
|
|
253
255
|
)
|
|
254
256
|
}
|
|
255
257
|
|
|
258
|
+
function appendFormDataValue(formData: FormData, key: string, value: unknown): void {
|
|
259
|
+
if (value === undefined || value === null) return
|
|
260
|
+
if (value instanceof Blob) formData.append(key, value)
|
|
261
|
+
else if (value instanceof Date) formData.append(key, value.toISOString())
|
|
262
|
+
else if (typeof value === 'object') formData.append(key, JSON.stringify(value))
|
|
263
|
+
else formData.append(key, String(value))
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
/**
|
|
257
267
|
* Default body serializer: passes binary/form bodies through and JSON-serializes everything else.
|
|
258
|
-
* For `
|
|
268
|
+
* For `multipart/form-data` plain objects become `FormData` and for
|
|
269
|
+
* `application/x-www-form-urlencoded` they become `URLSearchParams`.
|
|
259
270
|
*/
|
|
260
271
|
export const defaultBodySerializer: BodySerializer = (body, contentType) => {
|
|
261
272
|
if (body === undefined || body === null) return undefined
|
|
262
273
|
if (isFormBody(body)) return body
|
|
274
|
+
if (contentType?.includes('multipart/form-data')) {
|
|
275
|
+
const formData = new FormData()
|
|
276
|
+
for (const [key, value] of Object.entries(body as Record<string, unknown>)) {
|
|
277
|
+
if (Array.isArray(value)) for (const item of value) appendFormDataValue(formData, key, item)
|
|
278
|
+
else appendFormDataValue(formData, key, value)
|
|
279
|
+
}
|
|
280
|
+
return formData
|
|
281
|
+
}
|
|
263
282
|
if (contentType?.includes('application/x-www-form-urlencoded')) {
|
|
264
283
|
return new URLSearchParams(body as Record<string, string>)
|
|
265
284
|
}
|
|
@@ -308,6 +327,19 @@ function mergeHeaders(...sources: Array<HeadersInit | undefined>): Record<string
|
|
|
308
327
|
return Object.assign({}, ...sources.map(serializeHeaders))
|
|
309
328
|
}
|
|
310
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Joins the base and request URL parts, interpolates `{param}` segments from the path params
|
|
332
|
+
* (URL-encoded), and appends the serialized query. Backs `getUrl` so a URL can be constructed
|
|
333
|
+
* without sending the request.
|
|
334
|
+
*/
|
|
335
|
+
function serializeUrl(parts: Array<string | undefined>, pathParams: Record<string, unknown>, search: string): string {
|
|
336
|
+
const path = parts
|
|
337
|
+
.filter(Boolean)
|
|
338
|
+
.join('')
|
|
339
|
+
.replace(/\{([^{}]+)\}/g, (_, key: string) => encodeURIComponent(String(pathParams[key] ?? '')))
|
|
340
|
+
return path + (search ? `?${search}` : '')
|
|
341
|
+
}
|
|
342
|
+
|
|
311
343
|
/**
|
|
312
344
|
* Walks the per-operation security in order and places the first resolved token on the request,
|
|
313
345
|
* mutating `headers` / `query` in place. Bearer (and oauth2 / openIdConnect) tokens become a `Bearer`
|
|
@@ -412,10 +444,7 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
412
444
|
const bodySerializer = requestConfig.bodySerializer ?? config.bodySerializer ?? defaultBodySerializer
|
|
413
445
|
|
|
414
446
|
const headers = mergeHeaders(config.headers, requestConfig.headers)
|
|
415
|
-
|
|
416
|
-
headers['Content-Type'] = requestConfig.contentType
|
|
417
|
-
}
|
|
418
|
-
const contentType = headers['Content-Type'] ?? headers['content-type']
|
|
447
|
+
const requestContentType = requestConfig.contentType ?? headers['Content-Type'] ?? headers['content-type']
|
|
419
448
|
|
|
420
449
|
const query: Record<string, unknown> = { ...((requestConfig.query ?? requestConfig.params) as Record<string, unknown> | undefined) }
|
|
421
450
|
|
|
@@ -427,6 +456,14 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
427
456
|
})
|
|
428
457
|
|
|
429
458
|
const validatedBody = await runParser(requestConfig.parser?.request, requestConfig.body)
|
|
459
|
+
const body = bodySerializer(validatedBody, requestContentType)
|
|
460
|
+
// A FormData body must keep its Content-Type unset so axios appends the multipart boundary.
|
|
461
|
+
if (body instanceof FormData) {
|
|
462
|
+
delete headers['Content-Type']
|
|
463
|
+
delete headers['content-type']
|
|
464
|
+
} else if (requestConfig.contentType) {
|
|
465
|
+
headers['Content-Type'] = requestConfig.contentType
|
|
466
|
+
}
|
|
430
467
|
const pathParams = requestConfig.path ?? {}
|
|
431
468
|
const url = (requestConfig.url ?? '').replace(/\{([^{}]+)\}/g, (_, key: string) => encodeURIComponent(String(pathParams[key] ?? '')))
|
|
432
469
|
const baseURL = [config.baseURL, requestConfig.baseURL].filter(Boolean).join('') || undefined
|
|
@@ -441,8 +478,8 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
441
478
|
headers,
|
|
442
479
|
params: query,
|
|
443
480
|
paramsSerializer: (params) => querySerializer(params as Record<string, unknown>),
|
|
444
|
-
data:
|
|
445
|
-
transformRequest: (data) =>
|
|
481
|
+
data: body,
|
|
482
|
+
transformRequest: (data) => data,
|
|
446
483
|
signal: requestConfig.signal,
|
|
447
484
|
responseType: requestConfig.responseType,
|
|
448
485
|
validateStatus,
|
|
@@ -452,10 +489,11 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
452
489
|
const response = await activeInstance.request<unknown, AxiosResponse>(axiosConfig)
|
|
453
490
|
const isSuccess = response.status >= 200 && response.status < 300
|
|
454
491
|
const data = isSuccess ? await runParser(requestConfig.parser?.response, response.data) : undefined
|
|
492
|
+
const error = isSuccess ? undefined : await runParser(requestConfig.parser?.error, response.data)
|
|
455
493
|
return {
|
|
456
494
|
status: response.status,
|
|
457
495
|
data,
|
|
458
|
-
error
|
|
496
|
+
error,
|
|
459
497
|
request: response.config as TRequest,
|
|
460
498
|
response: response as TResponse,
|
|
461
499
|
}
|
|
@@ -479,6 +517,11 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
|
|
|
479
517
|
config = { ...config, ...next, headers: { ...serializeHeaders(config.headers), ...serializeHeaders(next.headers) } }
|
|
480
518
|
return config
|
|
481
519
|
}
|
|
520
|
+
client.getUrl = (requestConfig) => {
|
|
521
|
+
const querySerializer = requestConfig.querySerializer ?? config.querySerializer ?? defaultQuerySerializer
|
|
522
|
+
const query: Record<string, unknown> = { ...((requestConfig.query ?? requestConfig.params) as Record<string, unknown> | undefined) }
|
|
523
|
+
return serializeUrl([config.baseURL, requestConfig.baseURL, requestConfig.url], requestConfig.path ?? {}, querySerializer(query))
|
|
524
|
+
}
|
|
482
525
|
client.interceptors = interceptors
|
|
483
526
|
client.createClient = (next) => createClientCore<TRequest, TResponse>({ ...config, ...next })
|
|
484
527
|
|