aziosxjs 0.4.1 → 1.0.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/LICENSE +21 -0
- package/README.md +92 -267
- package/dist/cache/memoryCache.d.ts +1 -0
- package/dist/cache/memoryCache.js +7 -1
- package/dist/cache/memoryCache.js.map +1 -1
- package/dist/core/Azios.d.ts +33 -8
- package/dist/core/Azios.js +68 -2
- package/dist/core/Azios.js.map +1 -1
- package/dist/core/dispatchRequest.js +10 -75
- package/dist/core/dispatchRequest.js.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/MiddlewarePipeline.d.ts +54 -0
- package/dist/middleware/MiddlewarePipeline.js +122 -0
- package/dist/middleware/MiddlewarePipeline.js.map +1 -0
- package/dist/middleware/compose.d.ts +1 -0
- package/dist/middleware/compose.js +25 -0
- package/dist/middleware/compose.js.map +1 -0
- package/dist/middleware/middlewareManager.d.ts +4 -0
- package/dist/middleware/middlewareManager.js +12 -0
- package/dist/middleware/middlewareManager.js.map +1 -0
- package/dist/plugins/pluginManager.d.ts +44 -0
- package/dist/plugins/pluginManager.js +120 -0
- package/dist/plugins/pluginManager.js.map +1 -0
- package/dist/runtimes/AdapterFactory.d.ts +22 -0
- package/dist/runtimes/AdapterFactory.js +43 -0
- package/dist/runtimes/AdapterFactory.js.map +1 -0
- package/dist/runtimes/HttpAdapter.d.ts +23 -0
- package/dist/runtimes/HttpAdapter.js +10 -0
- package/dist/runtimes/HttpAdapter.js.map +1 -0
- package/dist/runtimes/UniversalHttpAdapter.d.ts +19 -0
- package/dist/runtimes/UniversalHttpAdapter.js +114 -0
- package/dist/runtimes/UniversalHttpAdapter.js.map +1 -0
- package/dist/runtimes/detectRuntime.d.ts +34 -0
- package/dist/runtimes/detectRuntime.js +77 -0
- package/dist/runtimes/detectRuntime.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/middleware.d.ts +16 -0
- package/dist/types/middleware.js +3 -0
- package/dist/types/middleware.js.map +1 -0
- package/dist/types/plugin.d.ts +43 -0
- package/dist/types/plugin.js +3 -0
- package/dist/types/plugin.js.map +1 -0
- package/dist/types/request.d.ts +5 -0
- package/dist/types/response.d.ts +23 -1
- package/package.json +24 -5
- package/src/adapters/httpAdapter.ts +0 -0
- package/src/adapters/index.ts +0 -0
- package/src/adapters/xhrAdapter.ts +0 -0
- package/src/cache/memoryCache.ts +45 -0
- package/src/config/mergeConfig.ts +0 -0
- package/src/core/Azios.ts +184 -0
- package/src/core/createInstance.ts +23 -0
- package/src/core/dispatchRequest.ts +111 -0
- package/src/core/requestMethods.ts +0 -0
- package/src/core/requestStore.ts +13 -0
- package/src/errors/AziosError.ts +26 -0
- package/src/helpers/buildURL.ts +44 -0
- package/src/helpers/normalizeHeaders.ts +0 -0
- package/src/helpers/parseHeaders.ts +0 -0
- package/src/index.ts +64 -0
- package/src/interceptors/InterceptorManager.ts +30 -0
- package/src/middleware/MiddlewarePipeline.ts +155 -0
- package/src/middleware/compose.ts +33 -0
- package/src/middleware/middlewareManager.ts +9 -0
- package/src/plugins/pluginManager.ts +143 -0
- package/src/rateLimiter/rateLimiter.ts +36 -0
- package/src/runtimes/AdapterFactory.ts +46 -0
- package/src/runtimes/HttpAdapter.ts +26 -0
- package/src/runtimes/UniversalHttpAdapter.ts +136 -0
- package/src/runtimes/detectRuntime.ts +85 -0
- package/src/types/config.ts +30 -0
- package/src/types/index.ts +5 -0
- package/src/types/middleware.ts +21 -0
- package/src/types/plugin.ts +50 -0
- package/src/types/request.ts +39 -0
- package/src/types/response.ts +37 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import dispatchRequest from "./dispatchRequest"
|
|
2
|
+
import { AziosRequestConfig } from "../types/config"
|
|
3
|
+
import type { AziosInstance } from "../types/request"
|
|
4
|
+
import type { AziosPlugin } from "../types/plugin"
|
|
5
|
+
import InterceptorManager from "../interceptors/InterceptorManager"
|
|
6
|
+
import PluginManager from "../plugins/pluginManager"
|
|
7
|
+
import MiddlewareManager from "../middleware/middlewareManager"
|
|
8
|
+
import { compose } from "../middleware/compose"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Core Azios HTTP client class
|
|
12
|
+
* Production-grade implementation with plugin system, middleware, and interceptors
|
|
13
|
+
*/
|
|
14
|
+
export default class Azios {
|
|
15
|
+
defaults: AziosRequestConfig
|
|
16
|
+
|
|
17
|
+
interceptors: {
|
|
18
|
+
request: InterceptorManager<AziosRequestConfig>
|
|
19
|
+
response: InterceptorManager<any>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
middlewares: MiddlewareManager
|
|
23
|
+
plugins: PluginManager
|
|
24
|
+
|
|
25
|
+
constructor(config: AziosRequestConfig) {
|
|
26
|
+
this.defaults = config
|
|
27
|
+
|
|
28
|
+
this.interceptors = {
|
|
29
|
+
request: new InterceptorManager(),
|
|
30
|
+
response: new InterceptorManager()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.middlewares = new MiddlewareManager()
|
|
34
|
+
this.plugins = new PluginManager()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Register middleware using Koa-style middleware pattern
|
|
39
|
+
*/
|
|
40
|
+
use(fn: any) {
|
|
41
|
+
this.middlewares.use(fn)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Install a plugin into this instance
|
|
46
|
+
* @throws Error if plugin is already installed or installation fails
|
|
47
|
+
*/
|
|
48
|
+
async installPlugin(plugin: AziosPlugin): Promise<void> {
|
|
49
|
+
await this.plugins.install(plugin, this as any as AziosInstance)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Uninstall a plugin by name
|
|
54
|
+
*/
|
|
55
|
+
async uninstallPlugin(pluginName: string): Promise<void> {
|
|
56
|
+
await this.plugins.uninstall(pluginName, this as any as AziosInstance)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Core request method - central request pipeline
|
|
61
|
+
*/
|
|
62
|
+
async request(config: AziosRequestConfig): Promise<any> {
|
|
63
|
+
config = { ...this.defaults, ...config }
|
|
64
|
+
|
|
65
|
+
// -------------------------
|
|
66
|
+
// PLUGIN: PRE-REQUEST HOOKS
|
|
67
|
+
// -------------------------
|
|
68
|
+
config = await this.plugins.executeBeforeRequestHooks(config)
|
|
69
|
+
|
|
70
|
+
// -------------------------
|
|
71
|
+
// MIDDLEWARE PIPELINE
|
|
72
|
+
// -------------------------
|
|
73
|
+
const context = { config }
|
|
74
|
+
const fn = compose(this.middlewares.middlewares)
|
|
75
|
+
await fn(context)
|
|
76
|
+
config = context.config
|
|
77
|
+
|
|
78
|
+
// -------------------------
|
|
79
|
+
// INTERCEPTOR PIPELINE
|
|
80
|
+
// -------------------------
|
|
81
|
+
const chain: any[] = []
|
|
82
|
+
|
|
83
|
+
// Request interceptors (reverse order)
|
|
84
|
+
this.interceptors.request.forEach(interceptor => {
|
|
85
|
+
chain.unshift(interceptor.fulfilled, interceptor.rejected)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Main dispatch
|
|
89
|
+
chain.push(dispatchRequest, undefined)
|
|
90
|
+
|
|
91
|
+
// Response interceptors (normal order)
|
|
92
|
+
this.interceptors.response.forEach(interceptor => {
|
|
93
|
+
chain.push(interceptor.fulfilled, interceptor.rejected)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
let promise = Promise.resolve(config)
|
|
97
|
+
|
|
98
|
+
while (chain.length) {
|
|
99
|
+
const fulfilled = chain.shift()
|
|
100
|
+
const rejected = chain.shift()
|
|
101
|
+
promise = promise.then(fulfilled, rejected)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const response = await promise as any
|
|
106
|
+
|
|
107
|
+
// -------------------------
|
|
108
|
+
// PLUGIN: POST-RESPONSE HOOKS
|
|
109
|
+
// -------------------------
|
|
110
|
+
return await this.plugins.executeAfterResponseHooks(response)
|
|
111
|
+
} catch (error) {
|
|
112
|
+
// -------------------------
|
|
113
|
+
// PLUGIN: ERROR HOOKS
|
|
114
|
+
// -------------------------
|
|
115
|
+
if (error instanceof Error) {
|
|
116
|
+
throw await this.plugins.executeOnErrorHooks(error)
|
|
117
|
+
}
|
|
118
|
+
throw error
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// =========================================
|
|
123
|
+
// HTTP METHODS
|
|
124
|
+
// =========================================
|
|
125
|
+
|
|
126
|
+
get(url: string, config?: AziosRequestConfig) {
|
|
127
|
+
return this.request({
|
|
128
|
+
...config,
|
|
129
|
+
method: "GET",
|
|
130
|
+
url
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
post(url: string, data?: any, config?: AziosRequestConfig) {
|
|
135
|
+
return this.request({
|
|
136
|
+
...config,
|
|
137
|
+
method: "POST",
|
|
138
|
+
url,
|
|
139
|
+
data
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
put(url: string, data?: any, config?: AziosRequestConfig) {
|
|
144
|
+
return this.request({
|
|
145
|
+
...config,
|
|
146
|
+
method: "PUT",
|
|
147
|
+
url,
|
|
148
|
+
data
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
patch(url: string, data?: any, config?: AziosRequestConfig) {
|
|
153
|
+
return this.request({
|
|
154
|
+
...config,
|
|
155
|
+
method: "PATCH",
|
|
156
|
+
url,
|
|
157
|
+
data
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
delete(url: string, config?: AziosRequestConfig) {
|
|
162
|
+
return this.request({
|
|
163
|
+
...config,
|
|
164
|
+
method: "DELETE",
|
|
165
|
+
url
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
head(url: string, config?: AziosRequestConfig) {
|
|
170
|
+
return this.request({
|
|
171
|
+
...config,
|
|
172
|
+
method: "HEAD",
|
|
173
|
+
url
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
options(url: string, config?: AziosRequestConfig) {
|
|
178
|
+
return this.request({
|
|
179
|
+
...config,
|
|
180
|
+
method: "OPTIONS",
|
|
181
|
+
url
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Azios from "./Azios"
|
|
2
|
+
import { AziosRequestConfig } from "../types/config"
|
|
3
|
+
import { AziosInstance } from "../types/request"
|
|
4
|
+
|
|
5
|
+
export function createInstance(config: AziosRequestConfig): AziosInstance {
|
|
6
|
+
|
|
7
|
+
const context = new Azios(config)
|
|
8
|
+
|
|
9
|
+
const instance = Azios.prototype.request.bind(context) as AziosInstance
|
|
10
|
+
|
|
11
|
+
Object.getOwnPropertyNames(Azios.prototype).forEach(method => {
|
|
12
|
+
|
|
13
|
+
if (method !== "constructor") {
|
|
14
|
+
;(instance as any)[method] = (Azios.prototype as any)[method].bind(context)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
// attach interceptors
|
|
20
|
+
instance.interceptors = context.interceptors
|
|
21
|
+
|
|
22
|
+
return instance
|
|
23
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { AziosRequestConfig } from "../types/config"
|
|
2
|
+
import { AziosResponse } from "../types/response"
|
|
3
|
+
import buildURL from "../helpers/buildURL"
|
|
4
|
+
import AziosError from "../errors/AziosError"
|
|
5
|
+
import AdapterFactory from "../runtimes/AdapterFactory"
|
|
6
|
+
|
|
7
|
+
import { getPending, setPending, removePending } from "./requestStore"
|
|
8
|
+
import { getCache, setCache } from "../cache/memoryCache"
|
|
9
|
+
import { schedule } from "../rateLimiter/rateLimiter"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sleep utility for retry delays
|
|
13
|
+
*/
|
|
14
|
+
function delay(ms: number): Promise<void> {
|
|
15
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Make a single HTTP request using the universal adapter
|
|
20
|
+
* This ensures cross-runtime compatibility
|
|
21
|
+
*/
|
|
22
|
+
async function makeRequest(config: AziosRequestConfig): Promise<AziosResponse> {
|
|
23
|
+
const adapter = AdapterFactory.getAdapter()
|
|
24
|
+
return adapter.request(config)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default async function dispatchRequest(config: AziosRequestConfig) {
|
|
28
|
+
|
|
29
|
+
const requestKey =
|
|
30
|
+
`${config.method}-${config.url}-${JSON.stringify(config.params)}`
|
|
31
|
+
|
|
32
|
+
// -------------------------
|
|
33
|
+
// CACHE CHECK (Sprint 4)
|
|
34
|
+
// -------------------------
|
|
35
|
+
|
|
36
|
+
if (config.cache) {
|
|
37
|
+
|
|
38
|
+
const cached = getCache(requestKey)
|
|
39
|
+
|
|
40
|
+
if (cached) {
|
|
41
|
+
return cached
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// -------------------------
|
|
47
|
+
// DEDUPLICATION (Sprint 3)
|
|
48
|
+
// -------------------------
|
|
49
|
+
|
|
50
|
+
const existing = getPending(requestKey)
|
|
51
|
+
|
|
52
|
+
if (existing) {
|
|
53
|
+
return existing
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const retries = config.retry || 0
|
|
57
|
+
const retryDelay = config.retryDelay || 300
|
|
58
|
+
|
|
59
|
+
let attempt = 0
|
|
60
|
+
|
|
61
|
+
const promise = (async () => {
|
|
62
|
+
|
|
63
|
+
while (true) {
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
|
|
67
|
+
const task = () => makeRequest(config)
|
|
68
|
+
|
|
69
|
+
const response = config.rateLimit
|
|
70
|
+
? await schedule(task, config.rateLimit)
|
|
71
|
+
: await task()
|
|
72
|
+
|
|
73
|
+
// -------------------------
|
|
74
|
+
// CACHE STORE
|
|
75
|
+
// -------------------------
|
|
76
|
+
|
|
77
|
+
if (config.cache) {
|
|
78
|
+
setCache(requestKey, response, config.cacheTTL ?? 5000)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
removePending(requestKey)
|
|
82
|
+
|
|
83
|
+
return response
|
|
84
|
+
|
|
85
|
+
} catch (err) {
|
|
86
|
+
|
|
87
|
+
if (attempt >= retries) {
|
|
88
|
+
|
|
89
|
+
removePending(requestKey)
|
|
90
|
+
|
|
91
|
+
throw err
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
attempt++
|
|
96
|
+
|
|
97
|
+
const backoff = retryDelay * Math.pow(2, attempt)
|
|
98
|
+
|
|
99
|
+
await delay(backoff)
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
})()
|
|
106
|
+
|
|
107
|
+
setPending(requestKey, promise)
|
|
108
|
+
|
|
109
|
+
return promise
|
|
110
|
+
|
|
111
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const pendingRequests = new Map()
|
|
2
|
+
|
|
3
|
+
export function getPending(key: string) {
|
|
4
|
+
return pendingRequests.get(key)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function setPending(key: string, promise: Promise<any>) {
|
|
8
|
+
pendingRequests.set(key, promise)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function removePending(key: string) {
|
|
12
|
+
pendingRequests.delete(key)
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default class AziosError extends Error {
|
|
2
|
+
|
|
3
|
+
config: any
|
|
4
|
+
code?: string
|
|
5
|
+
request?: any
|
|
6
|
+
response?: any
|
|
7
|
+
|
|
8
|
+
constructor(
|
|
9
|
+
message: string,
|
|
10
|
+
code?: string,
|
|
11
|
+
config?: any,
|
|
12
|
+
request?: any,
|
|
13
|
+
response?: any
|
|
14
|
+
) {
|
|
15
|
+
|
|
16
|
+
super(message)
|
|
17
|
+
|
|
18
|
+
this.name = "AziosError"
|
|
19
|
+
this.code = code
|
|
20
|
+
this.config = config
|
|
21
|
+
this.request = request
|
|
22
|
+
this.response = response
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export default function buildURL(
|
|
2
|
+
url: string,
|
|
3
|
+
params?: Record<string, any>
|
|
4
|
+
) {
|
|
5
|
+
|
|
6
|
+
if (!params) return url
|
|
7
|
+
|
|
8
|
+
const parts: string[] = []
|
|
9
|
+
|
|
10
|
+
Object.keys(params).forEach(key => {
|
|
11
|
+
|
|
12
|
+
const val = params[key]
|
|
13
|
+
|
|
14
|
+
if (val === null || typeof val === "undefined") {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (Array.isArray(val)) {
|
|
19
|
+
|
|
20
|
+
val.forEach(v => {
|
|
21
|
+
parts.push(
|
|
22
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(v)}`
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
} else {
|
|
27
|
+
|
|
28
|
+
parts.push(
|
|
29
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(val)}`
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if (parts.length === 0) return url
|
|
37
|
+
|
|
38
|
+
const serialized = parts.join("&")
|
|
39
|
+
|
|
40
|
+
return url.includes("?")
|
|
41
|
+
? `${url}&${serialized}`
|
|
42
|
+
: `${url}?${serialized}`
|
|
43
|
+
|
|
44
|
+
}
|
|
File without changes
|
|
File without changes
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createInstance } from "./core/createInstance"
|
|
2
|
+
|
|
3
|
+
// Export main instance
|
|
4
|
+
const azios = createInstance({
|
|
5
|
+
url: ""
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
// ============================================
|
|
9
|
+
// Type Exports
|
|
10
|
+
// ============================================
|
|
11
|
+
|
|
12
|
+
export type { AziosRequestConfig } from "./types/config"
|
|
13
|
+
export type { AziosResponse } from "./types/response"
|
|
14
|
+
export type { AziosInstance } from "./types/request"
|
|
15
|
+
export type { AziosPlugin, PluginHooks } from "./types/plugin"
|
|
16
|
+
|
|
17
|
+
// ============================================
|
|
18
|
+
// Class Exports
|
|
19
|
+
// ============================================
|
|
20
|
+
|
|
21
|
+
export { default as AziosError } from "./errors/AziosError"
|
|
22
|
+
export { default as InterceptorManager } from "./interceptors/InterceptorManager"
|
|
23
|
+
export { default as Azios } from "./core/Azios"
|
|
24
|
+
export { createInstance } from "./core/createInstance"
|
|
25
|
+
|
|
26
|
+
// ============================================
|
|
27
|
+
// Plugin System Exports
|
|
28
|
+
// ============================================
|
|
29
|
+
|
|
30
|
+
export { default as PluginManager } from "./plugins/pluginManager"
|
|
31
|
+
|
|
32
|
+
// ============================================
|
|
33
|
+
// Middleware System Exports
|
|
34
|
+
// ============================================
|
|
35
|
+
|
|
36
|
+
export { default as MiddlewarePipeline } from "./middleware/MiddlewarePipeline"
|
|
37
|
+
export type { RequestMiddleware, ResponseMiddleware } from "./middleware/MiddlewarePipeline"
|
|
38
|
+
|
|
39
|
+
// ============================================
|
|
40
|
+
// Runtime Support Exports
|
|
41
|
+
// ============================================
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
detectRuntime,
|
|
45
|
+
currentRuntime,
|
|
46
|
+
isRuntime,
|
|
47
|
+
isServerRuntime,
|
|
48
|
+
isBrowserRuntime,
|
|
49
|
+
RuntimeType
|
|
50
|
+
} from "./runtimes/detectRuntime"
|
|
51
|
+
export { default as AdapterFactory } from "./runtimes/AdapterFactory"
|
|
52
|
+
|
|
53
|
+
// ============================================
|
|
54
|
+
// Cache & Rate Limit Exports
|
|
55
|
+
// ============================================
|
|
56
|
+
|
|
57
|
+
export { getCache, setCache, clearCache } from "./cache/memoryCache"
|
|
58
|
+
export { schedule } from "./rateLimiter/rateLimiter"
|
|
59
|
+
|
|
60
|
+
// ============================================
|
|
61
|
+
// Default Export
|
|
62
|
+
// ============================================
|
|
63
|
+
|
|
64
|
+
export default azios
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type FulfilledFn<T> = (val: T) => T | Promise<T>
|
|
2
|
+
type RejectedFn = (error: any) => any
|
|
3
|
+
|
|
4
|
+
interface Interceptor<T> {
|
|
5
|
+
fulfilled: FulfilledFn<T>
|
|
6
|
+
rejected?: RejectedFn
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default class InterceptorManager<T> {
|
|
10
|
+
|
|
11
|
+
private handlers: Array<Interceptor<T> | null> = []
|
|
12
|
+
|
|
13
|
+
use(fulfilled: FulfilledFn<T>, rejected?: RejectedFn) {
|
|
14
|
+
this.handlers.push({
|
|
15
|
+
fulfilled,
|
|
16
|
+
rejected
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return this.handlers.length - 1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
forEach(fn: (interceptor: Interceptor<T>) => void) {
|
|
23
|
+
this.handlers.forEach(h => {
|
|
24
|
+
if (h !== null) {
|
|
25
|
+
fn(h)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { AziosRequestConfig } from '../types/config'
|
|
2
|
+
import type { AziosResponse } from '../types/response'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Middleware handler types
|
|
6
|
+
* Supports async middleware in the style of Koa
|
|
7
|
+
*/
|
|
8
|
+
export type RequestMiddleware = (
|
|
9
|
+
config: AziosRequestConfig,
|
|
10
|
+
next: () => Promise<AziosResponse>
|
|
11
|
+
) => Promise<AziosResponse>
|
|
12
|
+
|
|
13
|
+
export type ResponseMiddleware = (
|
|
14
|
+
response: AziosResponse,
|
|
15
|
+
next: () => Promise<AziosResponse>
|
|
16
|
+
) => Promise<AziosResponse>
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Composition utility for middleware chaining
|
|
20
|
+
* Implements Koa-style middleware flow control
|
|
21
|
+
*/
|
|
22
|
+
export class MiddlewarePipeline {
|
|
23
|
+
private requestMiddleware: RequestMiddleware[] = []
|
|
24
|
+
private responseMiddleware: ResponseMiddleware[] = []
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Add request middleware
|
|
28
|
+
* Middleware will execute in registration order
|
|
29
|
+
*/
|
|
30
|
+
useRequest(middleware: RequestMiddleware): this {
|
|
31
|
+
this.requestMiddleware.push(middleware)
|
|
32
|
+
return this
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Add response middleware
|
|
37
|
+
* Middleware will execute in registration order
|
|
38
|
+
*/
|
|
39
|
+
useResponse(middleware: ResponseMiddleware): this {
|
|
40
|
+
this.responseMiddleware.push(middleware)
|
|
41
|
+
return this
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Compose request middleware with proper async error handling
|
|
46
|
+
*/
|
|
47
|
+
private composeRequest(): RequestMiddleware {
|
|
48
|
+
const middleware = this.requestMiddleware
|
|
49
|
+
|
|
50
|
+
return async (config: AziosRequestConfig, next: () => Promise<AziosResponse>) => {
|
|
51
|
+
let index = -1
|
|
52
|
+
|
|
53
|
+
const dispatch = async (i: number): Promise<AziosResponse> => {
|
|
54
|
+
if (i <= index) {
|
|
55
|
+
throw new Error('next() called multiple times in middleware')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
index = i
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
if (i < middleware.length) {
|
|
62
|
+
return await middleware[i](config, () => dispatch(i + 1))
|
|
63
|
+
} else {
|
|
64
|
+
return await next()
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
throw err
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return dispatch(0)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Compose response middleware with proper async error handling
|
|
77
|
+
*/
|
|
78
|
+
private composeResponse(): ResponseMiddleware {
|
|
79
|
+
const middleware = this.responseMiddleware
|
|
80
|
+
|
|
81
|
+
return async (response: AziosResponse, next: () => Promise<AziosResponse>) => {
|
|
82
|
+
let index = -1
|
|
83
|
+
|
|
84
|
+
const dispatch = async (i: number): Promise<AziosResponse> => {
|
|
85
|
+
if (i <= index) {
|
|
86
|
+
throw new Error('next() called multiple times in middleware')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
index = i
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
if (i < middleware.length) {
|
|
93
|
+
return await middleware[i](response, () => dispatch(i + 1))
|
|
94
|
+
} else {
|
|
95
|
+
return await next()
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
throw err
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return dispatch(0)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Execute request middleware pipeline
|
|
108
|
+
*/
|
|
109
|
+
async executeRequestMiddleware(
|
|
110
|
+
config: AziosRequestConfig,
|
|
111
|
+
next: () => Promise<AziosResponse>
|
|
112
|
+
): Promise<AziosResponse> {
|
|
113
|
+
if (this.requestMiddleware.length === 0) {
|
|
114
|
+
return next()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const composed = this.composeRequest()
|
|
118
|
+
return composed(config, next)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Execute response middleware pipeline
|
|
123
|
+
*/
|
|
124
|
+
async executeResponseMiddleware(
|
|
125
|
+
response: AziosResponse,
|
|
126
|
+
next: () => Promise<AziosResponse>
|
|
127
|
+
): Promise<AziosResponse> {
|
|
128
|
+
if (this.responseMiddleware.length === 0) {
|
|
129
|
+
return next()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const composed = this.composeResponse()
|
|
133
|
+
return composed(response, next)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Clear all middleware
|
|
138
|
+
*/
|
|
139
|
+
clear(): void {
|
|
140
|
+
this.requestMiddleware = []
|
|
141
|
+
this.responseMiddleware = []
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get registered middleware count
|
|
146
|
+
*/
|
|
147
|
+
getMiddlewareCount(): { request: number; response: number } {
|
|
148
|
+
return {
|
|
149
|
+
request: this.requestMiddleware.length,
|
|
150
|
+
response: this.responseMiddleware.length
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export default MiddlewarePipeline
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function compose(middleware: any[]) {
|
|
2
|
+
|
|
3
|
+
return function (context: any) {
|
|
4
|
+
|
|
5
|
+
let index = -1
|
|
6
|
+
|
|
7
|
+
function dispatch(i: number): Promise<any> {
|
|
8
|
+
|
|
9
|
+
if (i <= index) {
|
|
10
|
+
return Promise.reject(new Error("next() called multiple times"))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
index = i
|
|
14
|
+
|
|
15
|
+
const fn = middleware[i]
|
|
16
|
+
|
|
17
|
+
if (!fn) return Promise.resolve()
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
return Promise.resolve(
|
|
21
|
+
fn(context, () => dispatch(i + 1))
|
|
22
|
+
)
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return Promise.reject(err)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return dispatch(0)
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|