@kubb/plugin-axios 5.4.1 → 5.4.2
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 +59 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +59 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/plugin.ts +39 -3
- package/templates/axios.ts +95 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-axios",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.2",
|
|
4
4
|
"description": "Generate a slim, type-safe HTTP client with Kubb based on axios.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api-client",
|
|
@@ -66,18 +66,18 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@kubb/plugin-ts": "^5.0.2",
|
|
69
|
-
"@kubb/plugin-zod": "^5.1
|
|
69
|
+
"@kubb/plugin-zod": "^5.2.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@internals/client": "0.0.0",
|
|
73
73
|
"@internals/shared": "0.0.0",
|
|
74
74
|
"axios": "^1.20.0",
|
|
75
|
-
"kubb": "^5.
|
|
75
|
+
"kubb": "^5.3.13",
|
|
76
76
|
"typescript": "^6.0.3"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"axios": "^1.20.0",
|
|
80
|
-
"kubb": "^5.
|
|
80
|
+
"kubb": "^5.3.13"
|
|
81
81
|
},
|
|
82
82
|
"peerDependenciesMeta": {
|
|
83
83
|
"axios": {
|
package/src/plugin.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
1
2
|
import path from 'node:path'
|
|
2
3
|
import { createSdkGenerator, defaultMacros, resolverClient } from '@internals/client'
|
|
3
4
|
import { createGroupConfig } from '@internals/shared'
|
|
4
|
-
import { definePlugin, Resolver } from 'kubb/kit'
|
|
5
|
+
import { ast, definePlugin, Resolver } from 'kubb/kit'
|
|
5
6
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
6
7
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
7
8
|
import { clientGenerator } from './generators/clientGenerator.tsx'
|
|
@@ -90,10 +91,45 @@ export const pluginAxios = definePlugin<PluginAxios>((options) => {
|
|
|
90
91
|
copy: axiosSerializersTemplatePath,
|
|
91
92
|
})
|
|
92
93
|
|
|
94
|
+
const clientPath = path.resolve(root, '.kubb/client.ts')
|
|
95
|
+
const clientSource = readFileSync(axiosClientTemplatePath, 'utf8')
|
|
96
|
+
const clientBody = clientSource.slice(clientSource.indexOf('\n\n') + 2)
|
|
97
|
+
const runtimeRoot = path.dirname(clientPath)
|
|
98
|
+
|
|
93
99
|
ctx.injectFile({
|
|
94
100
|
baseName: 'client.ts',
|
|
95
|
-
path:
|
|
96
|
-
|
|
101
|
+
path: clientPath,
|
|
102
|
+
imports: [
|
|
103
|
+
ast.factory.createImport({ name: 'axios', path: 'axios' }),
|
|
104
|
+
ast.factory.createImport({
|
|
105
|
+
name: ['AxiosError', 'AxiosInstance', 'AxiosRequestConfig', 'AxiosResponse', 'InternalAxiosRequestConfig'],
|
|
106
|
+
path: 'axios',
|
|
107
|
+
isTypeOnly: true,
|
|
108
|
+
}),
|
|
109
|
+
ast.factory.createImport({
|
|
110
|
+
name: ['applyHeaderStyles', 'defaultBodySerializer', 'defaultPathSerializer', 'defaultQuerySerializer', 'isDefaultJsonBody', 'serializeCookies'],
|
|
111
|
+
path: path.resolve(root, '.kubb/serializers.ts'),
|
|
112
|
+
root: runtimeRoot,
|
|
113
|
+
}),
|
|
114
|
+
ast.factory.createImport({
|
|
115
|
+
name: ['HeadersInit', 'PathParamStyle', 'PathSerializer', 'Serializers', 'Styles'],
|
|
116
|
+
path: path.resolve(root, '.kubb/serializers.ts'),
|
|
117
|
+
root: runtimeRoot,
|
|
118
|
+
isTypeOnly: true,
|
|
119
|
+
}),
|
|
120
|
+
ast.factory.createImport({
|
|
121
|
+
name: ['ParseError', 'validateStandardSchema'],
|
|
122
|
+
path: path.resolve(root, '.kubb/standardSchema.ts'),
|
|
123
|
+
root: runtimeRoot,
|
|
124
|
+
}),
|
|
125
|
+
ast.factory.createImport({
|
|
126
|
+
name: ['StandardSchemaValidator'],
|
|
127
|
+
path: path.resolve(root, '.kubb/standardSchema.ts'),
|
|
128
|
+
root: runtimeRoot,
|
|
129
|
+
isTypeOnly: true,
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
132
|
+
sources: [ast.factory.createSource({ nodes: [ast.factory.createText(clientBody)] })],
|
|
97
133
|
footer: baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : undefined,
|
|
98
134
|
})
|
|
99
135
|
|
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.
|
|
@@ -289,24 +289,27 @@ export type CallResult<TRequest = AxiosRequestConfig, TResponse = AxiosResponse>
|
|
|
289
289
|
response: TResponse
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
export type InterceptorFn<T
|
|
292
|
+
export type InterceptorFn<T, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = (
|
|
293
|
+
value: T,
|
|
294
|
+
requestConfig?: RequestConfig<unknown, TRequest, TResponse>,
|
|
295
|
+
) => T | Promise<T>
|
|
293
296
|
|
|
294
297
|
/**
|
|
295
298
|
* A single interceptor channel with a transport-agnostic `use` / `eject` / `update` API, backed by axios's native interceptor managers.
|
|
296
299
|
*/
|
|
297
|
-
export type InterceptorChannel<T> = {
|
|
298
|
-
use: (fn: InterceptorFn<T>) => number
|
|
300
|
+
export type InterceptorChannel<T, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
|
|
301
|
+
use: (fn: InterceptorFn<T, TRequest, TResponse>) => number
|
|
299
302
|
eject: (id: number) => void
|
|
300
|
-
update: (id: number, fn: InterceptorFn<T>) => void
|
|
303
|
+
update: (id: number, fn: InterceptorFn<T, TRequest, TResponse>) => void
|
|
301
304
|
}
|
|
302
305
|
|
|
303
306
|
/**
|
|
304
307
|
* The three interceptor channels every client instance exposes, wrapping axios's native managers with `error` mapped onto the response rejection handler.
|
|
305
308
|
*/
|
|
306
|
-
export type Interceptors = {
|
|
307
|
-
request: InterceptorChannel<InternalAxiosRequestConfig>
|
|
308
|
-
response: InterceptorChannel<AxiosResponse>
|
|
309
|
-
error: InterceptorChannel<AxiosError>
|
|
309
|
+
export type Interceptors<TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
|
|
310
|
+
request: InterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>
|
|
311
|
+
response: InterceptorChannel<AxiosResponse, TRequest, TResponse>
|
|
312
|
+
error: InterceptorChannel<AxiosError, TRequest, TResponse>
|
|
310
313
|
}
|
|
311
314
|
|
|
312
315
|
/**
|
|
@@ -318,7 +321,7 @@ export type ClientInstance<TRequest = AxiosRequestConfig, TResponse = AxiosRespo
|
|
|
318
321
|
getConfig: () => ClientConfig
|
|
319
322
|
setConfig: (config: ClientConfig) => ClientConfig
|
|
320
323
|
getUrl: <TBody = unknown>(config: RequestConfig<TBody, TRequest, TResponse>) => string
|
|
321
|
-
interceptors: Interceptors
|
|
324
|
+
interceptors: Interceptors<TRequest, TResponse>
|
|
322
325
|
createClient: (config?: ClientConfig) => ClientInstance<TRequest, TResponse>
|
|
323
326
|
}
|
|
324
327
|
|
|
@@ -397,13 +400,27 @@ function serializeUrl({
|
|
|
397
400
|
/**
|
|
398
401
|
* 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
402
|
*/
|
|
400
|
-
function createInterceptorChannel<T
|
|
403
|
+
function createInterceptorChannel<T, TRequest, TResponse>(
|
|
404
|
+
register: (fn: InterceptorFn<T, TRequest, TResponse>) => number,
|
|
405
|
+
ejectNative: (id: number) => void,
|
|
406
|
+
getRequestConfig: (value: T) => RequestConfig<unknown, TRequest, TResponse> | undefined,
|
|
407
|
+
getContextId: (value: T) => number | undefined,
|
|
408
|
+
setContextId: (value: T, id: number | undefined) => void,
|
|
409
|
+
): InterceptorChannel<T, TRequest, TResponse> {
|
|
401
410
|
const ids = new Map<number, number>()
|
|
402
411
|
let counter = 0
|
|
412
|
+
const registerWithContext = (fn: InterceptorFn<T, TRequest, TResponse>) =>
|
|
413
|
+
register(async (value) => {
|
|
414
|
+
const contextId = getContextId(value)
|
|
415
|
+
const result = await fn(value, getRequestConfig(value))
|
|
416
|
+
if (contextId !== undefined) setContextId(result, contextId)
|
|
417
|
+
return result
|
|
418
|
+
})
|
|
419
|
+
|
|
403
420
|
return {
|
|
404
421
|
use(fn) {
|
|
405
422
|
const id = ++counter
|
|
406
|
-
ids.set(id,
|
|
423
|
+
ids.set(id, registerWithContext(fn))
|
|
407
424
|
return id
|
|
408
425
|
},
|
|
409
426
|
eject(id) {
|
|
@@ -415,7 +432,7 @@ function createInterceptorChannel<T>(register: (fn: InterceptorFn<T>) => number,
|
|
|
415
432
|
update(id, fn) {
|
|
416
433
|
const nativeId = ids.get(id)
|
|
417
434
|
if (nativeId !== undefined) ejectNative(nativeId)
|
|
418
|
-
ids.set(id,
|
|
435
|
+
ids.set(id, registerWithContext(fn))
|
|
419
436
|
},
|
|
420
437
|
}
|
|
421
438
|
}
|
|
@@ -665,53 +682,89 @@ async function settleResponse<TRequest, TResponse>({
|
|
|
665
682
|
export function createClientCore<TRequest = AxiosRequestConfig, TResponse = AxiosResponse>(options: ClientConfig = {}): ClientInstance<TRequest, TResponse> {
|
|
666
683
|
let config: ClientConfig = { ...options }
|
|
667
684
|
const instance = config.transport ?? axios.create()
|
|
685
|
+
const requestContexts = new Map<number, RequestConfig<unknown, TRequest, TResponse>>()
|
|
686
|
+
let requestContextId = 0
|
|
687
|
+
const getContextId = (value: AxiosRequestConfig | AxiosResponse | AxiosError) => {
|
|
688
|
+
if (!value || typeof value !== 'object') return undefined
|
|
689
|
+
const axiosConfig = 'config' in value ? value.config : value
|
|
690
|
+
return (axiosConfig as (AxiosRequestConfig & { __kubbRequestContext?: number }) | undefined)?.__kubbRequestContext
|
|
691
|
+
}
|
|
692
|
+
const getRequestConfig = (value: AxiosRequestConfig | AxiosResponse | AxiosError) => {
|
|
693
|
+
const id = getContextId(value)
|
|
694
|
+
return id === undefined ? undefined : requestContexts.get(id)
|
|
695
|
+
}
|
|
696
|
+
const setContextId = (value: AxiosRequestConfig | AxiosResponse | AxiosError, id: number | undefined) => {
|
|
697
|
+
if (!value || typeof value !== 'object') return
|
|
698
|
+
const axiosConfig = 'config' in value ? value.config : value
|
|
699
|
+
if (!axiosConfig) return
|
|
700
|
+
const contextualConfig = axiosConfig as AxiosRequestConfig & { __kubbRequestContext?: number }
|
|
701
|
+
if (id === undefined) delete contextualConfig.__kubbRequestContext
|
|
702
|
+
else contextualConfig.__kubbRequestContext = id
|
|
703
|
+
}
|
|
668
704
|
|
|
669
705
|
const requestManager = instance.interceptors.request
|
|
670
706
|
const responseManager = instance.interceptors.response
|
|
671
|
-
const interceptors: Interceptors = {
|
|
672
|
-
request: createInterceptorChannel<InternalAxiosRequestConfig>(
|
|
707
|
+
const interceptors: Interceptors<TRequest, TResponse> = {
|
|
708
|
+
request: createInterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>(
|
|
673
709
|
(fn) => requestManager.use(fn),
|
|
674
710
|
(id) => requestManager.eject(id),
|
|
711
|
+
getRequestConfig,
|
|
712
|
+
getContextId,
|
|
713
|
+
setContextId,
|
|
675
714
|
),
|
|
676
|
-
response: createInterceptorChannel<AxiosResponse>(
|
|
715
|
+
response: createInterceptorChannel<AxiosResponse, TRequest, TResponse>(
|
|
677
716
|
(fn) => responseManager.use(fn),
|
|
678
717
|
(id) => responseManager.eject(id),
|
|
718
|
+
getRequestConfig,
|
|
719
|
+
getContextId,
|
|
720
|
+
setContextId,
|
|
679
721
|
),
|
|
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
|
-
}),
|
|
722
|
+
error: createInterceptorChannel<AxiosError, TRequest, TResponse>(
|
|
723
|
+
(fn) => responseManager.use(undefined, (error: unknown) => Promise.resolve(fn(error as AxiosError)).then(() => Promise.reject(error))),
|
|
686
724
|
(id) => responseManager.eject(id),
|
|
725
|
+
getRequestConfig,
|
|
726
|
+
getContextId,
|
|
727
|
+
setContextId,
|
|
687
728
|
),
|
|
688
729
|
}
|
|
689
730
|
|
|
690
731
|
const client = (async <TBody = unknown>(requestConfig: RequestConfig<TBody, TRequest, TResponse>): Promise<CallResult<TRequest, TResponse>> => {
|
|
691
732
|
const activeInstance = requestConfig.transport ?? config.transport ?? instance
|
|
692
733
|
const { axiosConfig, codecs, throwOnError } = await resolveRequest({ config, requestConfig })
|
|
734
|
+
const contextId = ++requestContextId
|
|
735
|
+
requestContexts.set(contextId, requestConfig)
|
|
736
|
+
;(axiosConfig as AxiosRequestConfig & { __kubbRequestContext: number }).__kubbRequestContext = contextId
|
|
693
737
|
|
|
694
738
|
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,
|
|
739
|
+
try {
|
|
740
|
+
const response = await activeInstance.request<unknown, AxiosResponse>(axiosConfig)
|
|
741
|
+
const result = await settleResponse<TRequest, TResponse>({
|
|
742
|
+
response,
|
|
743
|
+
codecs,
|
|
744
|
+
validator: requestConfig.validator,
|
|
745
|
+
onValidationError: requestConfig.onValidationError ?? config.onValidationError,
|
|
712
746
|
})
|
|
747
|
+
setContextId(response, undefined)
|
|
748
|
+
return result
|
|
749
|
+
} catch (error) {
|
|
750
|
+
const axiosError = error as AxiosError
|
|
751
|
+
setContextId(axiosError, undefined)
|
|
752
|
+
if (axiosError.response) setContextId(axiosError.response, undefined)
|
|
753
|
+
if (throwOnError && axiosError.response) {
|
|
754
|
+
throw new ResponseError({
|
|
755
|
+
data: axiosError.response.data,
|
|
756
|
+
status: axiosError.response.status,
|
|
757
|
+
statusText: axiosError.response.statusText,
|
|
758
|
+
contentType: getResponseContentType(axiosError.response.headers as Record<string, unknown>),
|
|
759
|
+
request: axiosError.config as TRequest,
|
|
760
|
+
response: axiosError.response as TResponse,
|
|
761
|
+
})
|
|
762
|
+
}
|
|
763
|
+
throw error
|
|
713
764
|
}
|
|
714
|
-
|
|
765
|
+
} finally {
|
|
766
|
+
setContextId(axiosConfig, undefined)
|
|
767
|
+
requestContexts.delete(contextId)
|
|
715
768
|
}
|
|
716
769
|
}) as ClientInstance<TRequest, TResponse>
|
|
717
770
|
|