@kubb/plugin-axios 5.5.0-canary.20260924T110844 → 5.5.0
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 +58 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +58 -24
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/plugin.ts +5 -2
- package/templates/axios.ts +99 -51
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-axios",
|
|
3
|
-
"version": "5.5.0
|
|
3
|
+
"version": "5.5.0",
|
|
4
4
|
"description": "Generate a slim, type-safe HTTP client with Kubb based on axios.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api-client",
|
|
@@ -65,19 +65,19 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@kubb/plugin-ts": "^5.
|
|
69
|
-
"@kubb/plugin-zod": "^5.
|
|
68
|
+
"@kubb/plugin-ts": "^5.0.2",
|
|
69
|
+
"@kubb/plugin-zod": "^5.2.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@internals/client": "0.
|
|
73
|
-
"@internals/shared": "0.
|
|
72
|
+
"@internals/client": "0.0.0",
|
|
73
|
+
"@internals/shared": "0.0.0",
|
|
74
74
|
"axios": "^1.20.0",
|
|
75
|
-
"kubb": "^5.3.
|
|
75
|
+
"kubb": "^5.3.14",
|
|
76
76
|
"typescript": "^6.0.3"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"axios": "^1.20.0",
|
|
80
|
-
"kubb": "^5.3.
|
|
80
|
+
"kubb": "^5.3.14"
|
|
81
81
|
},
|
|
82
82
|
"peerDependenciesMeta": {
|
|
83
83
|
"axios": {
|
package/src/plugin.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import { createSdkGenerator, defaultMacros, resolverClient } from '@internals/client'
|
|
2
|
+
import { createSdkGenerator, defaultMacros, resolverClient, runtimeTemplate } from '@internals/client'
|
|
3
3
|
import { createGroupConfig } from '@internals/shared'
|
|
4
4
|
import { definePlugin, Resolver } from 'kubb/kit'
|
|
5
5
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
@@ -43,6 +43,7 @@ export const pluginAxios = definePlugin<PluginAxios>((options) => {
|
|
|
43
43
|
include,
|
|
44
44
|
override = [],
|
|
45
45
|
baseURL,
|
|
46
|
+
throwOnErrorDefault = true,
|
|
46
47
|
validator = false,
|
|
47
48
|
returnType = 'full',
|
|
48
49
|
group,
|
|
@@ -57,6 +58,7 @@ export const pluginAxios = definePlugin<PluginAxios>((options) => {
|
|
|
57
58
|
override,
|
|
58
59
|
group: createGroupConfig(group),
|
|
59
60
|
baseURL,
|
|
61
|
+
throwOnErrorDefault,
|
|
60
62
|
validator,
|
|
61
63
|
returnType,
|
|
62
64
|
sdk: sdk ? { mode: sdk.mode ?? 'tag', name: sdk.name } : undefined,
|
|
@@ -93,7 +95,8 @@ export const pluginAxios = definePlugin<PluginAxios>((options) => {
|
|
|
93
95
|
ctx.injectFile({
|
|
94
96
|
baseName: 'client.ts',
|
|
95
97
|
path: path.resolve(root, '.kubb/client.ts'),
|
|
96
|
-
|
|
98
|
+
// Keep sources empty so the barrel exports the whole runtime module.
|
|
99
|
+
banner: runtimeTemplate(axiosClientTemplatePath, ctx.config),
|
|
97
100
|
footer: baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : undefined,
|
|
98
101
|
})
|
|
99
102
|
|
package/templates/axios.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
|
|
3
|
-
import { applyHeaderStyles, defaultBodySerializer, defaultPathSerializer, defaultQuerySerializer, isDefaultJsonBody, serializeCookies } from './serializers'
|
|
4
|
-
import type { HeadersInit, PathParamStyle, PathSerializer, Serializers, Styles } from './serializers'
|
|
5
|
-
import { ParseError, type StandardSchemaValidator, validateStandardSchema } from './standardSchema'
|
|
3
|
+
import { applyHeaderStyles, defaultBodySerializer, defaultPathSerializer, defaultQuerySerializer, isDefaultJsonBody, serializeCookies } from './serializers.ts'
|
|
4
|
+
import type { HeadersInit, PathParamStyle, PathSerializer, Serializers, Styles } from './serializers.ts'
|
|
5
|
+
import { ParseError, type StandardSchemaValidator, validateStandardSchema } from './standardSchema.ts'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* HTTP status codes treated as a success, everything else is an error.
|
|
@@ -111,10 +111,8 @@ export function withUnwrap<T extends { data: unknown; error: unknown }>(promise:
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
|
-
* The shape a generated operation returns
|
|
115
|
-
*
|
|
116
|
-
* `RequestResult` when a call sets `throwOnError: false` and still needs `error` to discriminate a
|
|
117
|
-
* failed response.
|
|
114
|
+
* The shape a generated operation returns with `returnType: 'data'`: the success body when
|
|
115
|
+
* `throwOnError` is true, or the full `RequestResult` when it is false.
|
|
118
116
|
*/
|
|
119
117
|
export type UnwrappedResult<
|
|
120
118
|
TResponses,
|
|
@@ -124,11 +122,8 @@ export type UnwrappedResult<
|
|
|
124
122
|
> = ThrowOnError extends true ? RequestResult<TResponses, true, TRequest, TResponse>['data'] : RequestResult<TResponses, ThrowOnError, TRequest, TResponse>
|
|
125
123
|
|
|
126
124
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* a call that sets `throwOnError: false`, since that path still needs `error` to discriminate a
|
|
130
|
-
* failed response. Backs `returnType: 'data'`, mirroring how `toEventStream` centralizes the
|
|
131
|
-
* post-processing for `text/event-stream` operations.
|
|
125
|
+
* Returns the success body when `throwOnError` is true, or the full result when it is false.
|
|
126
|
+
* Backs generated operations with `returnType: 'data'`.
|
|
132
127
|
*/
|
|
133
128
|
export function unwrapResult<T extends { data: unknown; error: unknown }>(promise: Promise<T>, throwOnError: boolean | undefined): Promise<T | T['data']> {
|
|
134
129
|
return promise.then((result) => ((throwOnError ?? true) ? result.data : result))
|
|
@@ -289,24 +284,27 @@ export type CallResult<TRequest = AxiosRequestConfig, TResponse = AxiosResponse>
|
|
|
289
284
|
response: TResponse
|
|
290
285
|
}
|
|
291
286
|
|
|
292
|
-
export type InterceptorFn<T
|
|
287
|
+
export type InterceptorFn<T, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = (
|
|
288
|
+
value: T,
|
|
289
|
+
requestConfig?: RequestConfig<unknown, TRequest, TResponse>,
|
|
290
|
+
) => T | Promise<T>
|
|
293
291
|
|
|
294
292
|
/**
|
|
295
293
|
* A single interceptor channel with a transport-agnostic `use` / `eject` / `update` API, backed by axios's native interceptor managers.
|
|
296
294
|
*/
|
|
297
|
-
export type InterceptorChannel<T> = {
|
|
298
|
-
use: (fn: InterceptorFn<T>) => number
|
|
295
|
+
export type InterceptorChannel<T, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
|
|
296
|
+
use: (fn: InterceptorFn<T, TRequest, TResponse>) => number
|
|
299
297
|
eject: (id: number) => void
|
|
300
|
-
update: (id: number, fn: InterceptorFn<T>) => void
|
|
298
|
+
update: (id: number, fn: InterceptorFn<T, TRequest, TResponse>) => void
|
|
301
299
|
}
|
|
302
300
|
|
|
303
301
|
/**
|
|
304
302
|
* The three interceptor channels every client instance exposes, wrapping axios's native managers with `error` mapped onto the response rejection handler.
|
|
305
303
|
*/
|
|
306
|
-
export type Interceptors = {
|
|
307
|
-
request: InterceptorChannel<InternalAxiosRequestConfig>
|
|
308
|
-
response: InterceptorChannel<AxiosResponse>
|
|
309
|
-
error: InterceptorChannel<AxiosError>
|
|
304
|
+
export type Interceptors<TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
|
|
305
|
+
request: InterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>
|
|
306
|
+
response: InterceptorChannel<AxiosResponse, TRequest, TResponse>
|
|
307
|
+
error: InterceptorChannel<AxiosError, TRequest, TResponse>
|
|
310
308
|
}
|
|
311
309
|
|
|
312
310
|
/**
|
|
@@ -318,7 +316,7 @@ export type ClientInstance<TRequest = AxiosRequestConfig, TResponse = AxiosRespo
|
|
|
318
316
|
getConfig: () => ClientConfig
|
|
319
317
|
setConfig: (config: ClientConfig) => ClientConfig
|
|
320
318
|
getUrl: <TBody = unknown>(config: RequestConfig<TBody, TRequest, TResponse>) => string
|
|
321
|
-
interceptors: Interceptors
|
|
319
|
+
interceptors: Interceptors<TRequest, TResponse>
|
|
322
320
|
createClient: (config?: ClientConfig) => ClientInstance<TRequest, TResponse>
|
|
323
321
|
}
|
|
324
322
|
|
|
@@ -397,13 +395,27 @@ function serializeUrl({
|
|
|
397
395
|
/**
|
|
398
396
|
* Wraps an axios interceptor registration behind the shared `use` / `eject` / `update` API, mapping a stable external id onto axios's own so `update` can swap a handler in place.
|
|
399
397
|
*/
|
|
400
|
-
function createInterceptorChannel<T
|
|
398
|
+
function createInterceptorChannel<T, TRequest, TResponse>(
|
|
399
|
+
register: (fn: InterceptorFn<T, TRequest, TResponse>) => number,
|
|
400
|
+
ejectNative: (id: number) => void,
|
|
401
|
+
getRequestConfig: (value: T) => RequestConfig<unknown, TRequest, TResponse> | undefined,
|
|
402
|
+
getContextId: (value: T) => number | undefined,
|
|
403
|
+
setContextId: (value: T, id: number | undefined) => void,
|
|
404
|
+
): InterceptorChannel<T, TRequest, TResponse> {
|
|
401
405
|
const ids = new Map<number, number>()
|
|
402
406
|
let counter = 0
|
|
407
|
+
const registerWithContext = (fn: InterceptorFn<T, TRequest, TResponse>) =>
|
|
408
|
+
register(async (value) => {
|
|
409
|
+
const contextId = getContextId(value)
|
|
410
|
+
const result = await fn(value, getRequestConfig(value))
|
|
411
|
+
if (contextId !== undefined) setContextId(result, contextId)
|
|
412
|
+
return result
|
|
413
|
+
})
|
|
414
|
+
|
|
403
415
|
return {
|
|
404
416
|
use(fn) {
|
|
405
417
|
const id = ++counter
|
|
406
|
-
ids.set(id,
|
|
418
|
+
ids.set(id, registerWithContext(fn))
|
|
407
419
|
return id
|
|
408
420
|
},
|
|
409
421
|
eject(id) {
|
|
@@ -415,7 +427,7 @@ function createInterceptorChannel<T>(register: (fn: InterceptorFn<T>) => number,
|
|
|
415
427
|
update(id, fn) {
|
|
416
428
|
const nativeId = ids.get(id)
|
|
417
429
|
if (nativeId !== undefined) ejectNative(nativeId)
|
|
418
|
-
ids.set(id,
|
|
430
|
+
ids.set(id, registerWithContext(fn))
|
|
419
431
|
},
|
|
420
432
|
}
|
|
421
433
|
}
|
|
@@ -665,53 +677,89 @@ async function settleResponse<TRequest, TResponse>({
|
|
|
665
677
|
export function createClientCore<TRequest = AxiosRequestConfig, TResponse = AxiosResponse>(options: ClientConfig = {}): ClientInstance<TRequest, TResponse> {
|
|
666
678
|
let config: ClientConfig = { ...options }
|
|
667
679
|
const instance = config.transport ?? axios.create()
|
|
680
|
+
const requestContexts = new Map<number, RequestConfig<unknown, TRequest, TResponse>>()
|
|
681
|
+
let requestContextId = 0
|
|
682
|
+
const getContextId = (value: AxiosRequestConfig | AxiosResponse | AxiosError) => {
|
|
683
|
+
if (!value || typeof value !== 'object') return undefined
|
|
684
|
+
const axiosConfig = 'config' in value ? value.config : value
|
|
685
|
+
return (axiosConfig as (AxiosRequestConfig & { __kubbRequestContext?: number }) | undefined)?.__kubbRequestContext
|
|
686
|
+
}
|
|
687
|
+
const getRequestConfig = (value: AxiosRequestConfig | AxiosResponse | AxiosError) => {
|
|
688
|
+
const id = getContextId(value)
|
|
689
|
+
return id === undefined ? undefined : requestContexts.get(id)
|
|
690
|
+
}
|
|
691
|
+
const setContextId = (value: AxiosRequestConfig | AxiosResponse | AxiosError, id: number | undefined) => {
|
|
692
|
+
if (!value || typeof value !== 'object') return
|
|
693
|
+
const axiosConfig = 'config' in value ? value.config : value
|
|
694
|
+
if (!axiosConfig) return
|
|
695
|
+
const contextualConfig = axiosConfig as AxiosRequestConfig & { __kubbRequestContext?: number }
|
|
696
|
+
if (id === undefined) delete contextualConfig.__kubbRequestContext
|
|
697
|
+
else contextualConfig.__kubbRequestContext = id
|
|
698
|
+
}
|
|
668
699
|
|
|
669
700
|
const requestManager = instance.interceptors.request
|
|
670
701
|
const responseManager = instance.interceptors.response
|
|
671
|
-
const interceptors: Interceptors = {
|
|
672
|
-
request: createInterceptorChannel<InternalAxiosRequestConfig>(
|
|
702
|
+
const interceptors: Interceptors<TRequest, TResponse> = {
|
|
703
|
+
request: createInterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>(
|
|
673
704
|
(fn) => requestManager.use(fn),
|
|
674
705
|
(id) => requestManager.eject(id),
|
|
706
|
+
getRequestConfig,
|
|
707
|
+
getContextId,
|
|
708
|
+
setContextId,
|
|
675
709
|
),
|
|
676
|
-
response: createInterceptorChannel<AxiosResponse>(
|
|
710
|
+
response: createInterceptorChannel<AxiosResponse, TRequest, TResponse>(
|
|
677
711
|
(fn) => responseManager.use(fn),
|
|
678
712
|
(id) => responseManager.eject(id),
|
|
713
|
+
getRequestConfig,
|
|
714
|
+
getContextId,
|
|
715
|
+
setContextId,
|
|
679
716
|
),
|
|
680
|
-
error: createInterceptorChannel<AxiosError>(
|
|
681
|
-
(fn) =>
|
|
682
|
-
responseManager.use(undefined, async (error: unknown) => {
|
|
683
|
-
await fn(error as AxiosError)
|
|
684
|
-
return Promise.reject(error)
|
|
685
|
-
}),
|
|
717
|
+
error: createInterceptorChannel<AxiosError, TRequest, TResponse>(
|
|
718
|
+
(fn) => responseManager.use(undefined, (error: unknown) => Promise.resolve(fn(error as AxiosError)).then(() => Promise.reject(error))),
|
|
686
719
|
(id) => responseManager.eject(id),
|
|
720
|
+
getRequestConfig,
|
|
721
|
+
getContextId,
|
|
722
|
+
setContextId,
|
|
687
723
|
),
|
|
688
724
|
}
|
|
689
725
|
|
|
690
726
|
const client = (async <TBody = unknown>(requestConfig: RequestConfig<TBody, TRequest, TResponse>): Promise<CallResult<TRequest, TResponse>> => {
|
|
691
727
|
const activeInstance = requestConfig.transport ?? config.transport ?? instance
|
|
692
728
|
const { axiosConfig, codecs, throwOnError } = await resolveRequest({ config, requestConfig })
|
|
729
|
+
const contextId = ++requestContextId
|
|
730
|
+
requestContexts.set(contextId, requestConfig)
|
|
731
|
+
;(axiosConfig as AxiosRequestConfig & { __kubbRequestContext: number }).__kubbRequestContext = contextId
|
|
693
732
|
|
|
694
733
|
try {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
} catch (error) {
|
|
703
|
-
const axiosError = error as AxiosError
|
|
704
|
-
if (throwOnError && axiosError.response) {
|
|
705
|
-
throw new ResponseError({
|
|
706
|
-
data: axiosError.response.data,
|
|
707
|
-
status: axiosError.response.status,
|
|
708
|
-
statusText: axiosError.response.statusText,
|
|
709
|
-
contentType: getResponseContentType(axiosError.response.headers as Record<string, unknown>),
|
|
710
|
-
request: axiosError.config as TRequest,
|
|
711
|
-
response: axiosError.response as TResponse,
|
|
734
|
+
try {
|
|
735
|
+
const response = await activeInstance.request<unknown, AxiosResponse>(axiosConfig)
|
|
736
|
+
const result = await settleResponse<TRequest, TResponse>({
|
|
737
|
+
response,
|
|
738
|
+
codecs,
|
|
739
|
+
validator: requestConfig.validator,
|
|
740
|
+
onValidationError: requestConfig.onValidationError ?? config.onValidationError,
|
|
712
741
|
})
|
|
742
|
+
setContextId(response, undefined)
|
|
743
|
+
return result
|
|
744
|
+
} catch (error) {
|
|
745
|
+
const axiosError = error as AxiosError
|
|
746
|
+
setContextId(axiosError, undefined)
|
|
747
|
+
if (axiosError.response) setContextId(axiosError.response, undefined)
|
|
748
|
+
if (throwOnError && axiosError.response) {
|
|
749
|
+
throw new ResponseError({
|
|
750
|
+
data: axiosError.response.data,
|
|
751
|
+
status: axiosError.response.status,
|
|
752
|
+
statusText: axiosError.response.statusText,
|
|
753
|
+
contentType: getResponseContentType(axiosError.response.headers as Record<string, unknown>),
|
|
754
|
+
request: axiosError.config as TRequest,
|
|
755
|
+
response: axiosError.response as TResponse,
|
|
756
|
+
})
|
|
757
|
+
}
|
|
758
|
+
throw error
|
|
713
759
|
}
|
|
714
|
-
|
|
760
|
+
} finally {
|
|
761
|
+
setContextId(axiosConfig, undefined)
|
|
762
|
+
requestContexts.delete(contextId)
|
|
715
763
|
}
|
|
716
764
|
}) as ClientInstance<TRequest, TResponse>
|
|
717
765
|
|