@ahoo-wang/fetcher 0.6.0 → 0.6.5
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/README.md +257 -7
- package/README.zh-CN.md +89 -86
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +75 -47
- package/dist/index.umd.js +1 -1
- package/dist/mergeRequest.d.ts +41 -0
- package/dist/mergeRequest.d.ts.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
[](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
|
|
7
7
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher)
|
|
8
8
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher)
|
|
9
|
+
[](https://deepwiki.com/Ahoo-Wang/fetcher)
|
|
9
10
|
|
|
10
11
|
A modern, ultra-lightweight (1.9kB) HTTP client with built-in path parameters, query parameters, and Axios-like API. 86%
|
|
11
12
|
smaller than Axios while providing the same powerful features.
|
|
@@ -53,7 +54,7 @@ const response = await fetcher.get('/users/{id}', {
|
|
|
53
54
|
path: { id: 123 },
|
|
54
55
|
query: { include: 'profile' },
|
|
55
56
|
});
|
|
56
|
-
const userData = await response.json();
|
|
57
|
+
const userData = await response.json<User>();
|
|
57
58
|
|
|
58
59
|
// POST request with automatic JSON conversion
|
|
59
60
|
const createUserResponse = await fetcher.post('/users', {
|
|
@@ -100,12 +101,46 @@ import { fetcher } from '@ahoo-wang/fetcher';
|
|
|
100
101
|
|
|
101
102
|
// Use the default fetcher directly
|
|
102
103
|
const response = await fetcher.get('/users');
|
|
103
|
-
const data = await response.json();
|
|
104
|
+
const data = await response.json<User>();
|
|
104
105
|
```
|
|
105
106
|
|
|
106
107
|
## 🔗 Interceptor System
|
|
107
108
|
|
|
108
|
-
###
|
|
109
|
+
### Interceptor
|
|
110
|
+
|
|
111
|
+
Interceptor interface that defines the basic structure of interceptors.
|
|
112
|
+
|
|
113
|
+
**Properties:**
|
|
114
|
+
|
|
115
|
+
- `name: string` - The name of the interceptor, used to identify it, must be unique
|
|
116
|
+
- `order: number` - The execution order of the interceptor, smaller values have higher priority
|
|
117
|
+
|
|
118
|
+
**Methods:**
|
|
119
|
+
|
|
120
|
+
- `intercept(exchange: FetchExchange): FetchExchange | Promise<FetchExchange>` - Intercept and process data
|
|
121
|
+
|
|
122
|
+
### InterceptorManager
|
|
123
|
+
|
|
124
|
+
Interceptor manager for managing multiple interceptors of the same type.
|
|
125
|
+
|
|
126
|
+
**Methods:**
|
|
127
|
+
|
|
128
|
+
- `use(interceptor: Interceptor): boolean` - Add interceptor, returns whether the addition was successful
|
|
129
|
+
- `eject(name: string): boolean` - Remove interceptor by name, returns whether the removal was successful
|
|
130
|
+
- `clear(): void` - Clear all interceptors
|
|
131
|
+
- `intercept(exchange: FetchExchange): Promise<FetchExchange>` - Execute all interceptors in sequence
|
|
132
|
+
|
|
133
|
+
### FetcherInterceptors
|
|
134
|
+
|
|
135
|
+
Fetcher interceptor collection, including request, response, and error interceptor managers.
|
|
136
|
+
|
|
137
|
+
**Properties:**
|
|
138
|
+
|
|
139
|
+
- `request: InterceptorManager` - Request interceptor manager
|
|
140
|
+
- `response: InterceptorManager` - Response interceptor manager
|
|
141
|
+
- `error: InterceptorManager` - Error interceptor manager
|
|
142
|
+
|
|
143
|
+
### Using Interceptors
|
|
109
144
|
|
|
110
145
|
```typescript
|
|
111
146
|
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
@@ -113,7 +148,9 @@ import { Fetcher } from '@ahoo-wang/fetcher';
|
|
|
113
148
|
const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
114
149
|
|
|
115
150
|
// Add request interceptor (e.g., for authentication)
|
|
116
|
-
const
|
|
151
|
+
const success = fetcher.interceptors.request.use({
|
|
152
|
+
name: 'auth-interceptor',
|
|
153
|
+
order: 100,
|
|
117
154
|
intercept(exchange) {
|
|
118
155
|
return {
|
|
119
156
|
...exchange,
|
|
@@ -128,8 +165,84 @@ const interceptorId = fetcher.interceptors.request.use({
|
|
|
128
165
|
},
|
|
129
166
|
});
|
|
130
167
|
|
|
131
|
-
//
|
|
132
|
-
fetcher.interceptors.
|
|
168
|
+
// Add response interceptor (e.g., for logging)
|
|
169
|
+
fetcher.interceptors.response.use({
|
|
170
|
+
name: 'logging-interceptor',
|
|
171
|
+
order: 10,
|
|
172
|
+
intercept(exchange) {
|
|
173
|
+
console.log('Response received:', exchange.response?.status);
|
|
174
|
+
return exchange;
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Add error interceptor (e.g., for unified error handling)
|
|
179
|
+
fetcher.interceptors.error.use({
|
|
180
|
+
name: 'error-interceptor',
|
|
181
|
+
order: 50,
|
|
182
|
+
intercept(exchange) {
|
|
183
|
+
if (exchange.error?.name === 'FetchTimeoutError') {
|
|
184
|
+
console.error('Request timeout:', exchange.error.message);
|
|
185
|
+
} else {
|
|
186
|
+
console.error('Network error:', exchange.error?.message);
|
|
187
|
+
}
|
|
188
|
+
return exchange;
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Remove interceptor by name
|
|
193
|
+
fetcher.interceptors.request.eject('auth-interceptor');
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Ordered Execution
|
|
197
|
+
|
|
198
|
+
The `OrderedCapable` system allows you to control the execution order of interceptors and other components.
|
|
199
|
+
|
|
200
|
+
#### Ordering Concept
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { OrderedCapable } from '@ahoo-wang/fetcher';
|
|
204
|
+
|
|
205
|
+
// Lower order values have higher priority
|
|
206
|
+
const highPriority: OrderedCapable = { order: 1 }; // Executes first
|
|
207
|
+
const mediumPriority: OrderedCapable = { order: 10 }; // Executes second
|
|
208
|
+
const lowPriority: OrderedCapable = { order: 100 }; // Executes last
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Interceptor Ordering
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Add interceptors with different orders
|
|
215
|
+
fetcher.interceptors.request.use({
|
|
216
|
+
name: 'timing-interceptor',
|
|
217
|
+
order: 5, // Executes very early
|
|
218
|
+
intercept(exchange) {
|
|
219
|
+
console.log('Very early timing');
|
|
220
|
+
return exchange;
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
fetcher.interceptors.request.use({
|
|
225
|
+
name: 'logging-interceptor',
|
|
226
|
+
order: 10, // Executes early
|
|
227
|
+
intercept(exchange) {
|
|
228
|
+
console.log('Early logging');
|
|
229
|
+
return exchange;
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
fetcher.interceptors.request.use({
|
|
234
|
+
name: 'auth-interceptor',
|
|
235
|
+
order: 50, // Executes later
|
|
236
|
+
intercept(exchange) {
|
|
237
|
+
// Add auth headers
|
|
238
|
+
return exchange;
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Execution order will be:
|
|
243
|
+
// 1. timing-interceptor (order: 5)
|
|
244
|
+
// 2. logging-interceptor (order: 10)
|
|
245
|
+
// 3. auth-interceptor (order: 50)
|
|
133
246
|
```
|
|
134
247
|
|
|
135
248
|
### Response Interceptors
|
|
@@ -189,6 +302,109 @@ new Fetcher(options ? : FetcherOptions);
|
|
|
189
302
|
- `head(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - HEAD request
|
|
190
303
|
- `options(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - OPTIONS request
|
|
191
304
|
|
|
305
|
+
### Response Extension
|
|
306
|
+
|
|
307
|
+
To provide better TypeScript support, we extend the native Response interface with a type-safe json() method:
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// Now you can use it with type safety
|
|
311
|
+
const response = await fetcher.get('/users/123');
|
|
312
|
+
const userData = await response.json<User>(); // Type is Promise<User>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### NamedFetcher Class
|
|
316
|
+
|
|
317
|
+
An extension of the Fetcher class that automatically registers itself with the global fetcherRegistrar.
|
|
318
|
+
|
|
319
|
+
#### Constructor
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
new NamedFetcher(name
|
|
323
|
+
:
|
|
324
|
+
string, options ? : FetcherOptions
|
|
325
|
+
)
|
|
326
|
+
;
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### FetcherRegistrar
|
|
330
|
+
|
|
331
|
+
Global instance for managing multiple Fetcher instances by name.
|
|
332
|
+
|
|
333
|
+
#### Properties
|
|
334
|
+
|
|
335
|
+
- `default`: Get or set the default fetcher instance
|
|
336
|
+
|
|
337
|
+
#### Methods
|
|
338
|
+
|
|
339
|
+
- `register(name: string, fetcher: Fetcher): void` - Register a fetcher with a name
|
|
340
|
+
- `unregister(name: string): boolean` - Unregister a fetcher by name
|
|
341
|
+
- `get(name: string): Fetcher | undefined` - Get a fetcher by name
|
|
342
|
+
- `requiredGet(name: string): Fetcher` - Get a fetcher by name, throws if not found
|
|
343
|
+
- `fetchers: Map<string, Fetcher>` - Get all registered fetchers
|
|
344
|
+
|
|
345
|
+
### Interceptor System
|
|
346
|
+
|
|
347
|
+
#### Interceptor
|
|
348
|
+
|
|
349
|
+
Interceptor interface that defines the basic structure of interceptors.
|
|
350
|
+
|
|
351
|
+
**Properties:**
|
|
352
|
+
|
|
353
|
+
- `name: string` - The name of the interceptor, used to identify it, must be unique
|
|
354
|
+
- `order: number` - The execution order of the interceptor, smaller values have higher priority
|
|
355
|
+
|
|
356
|
+
**Methods:**
|
|
357
|
+
|
|
358
|
+
- `intercept(exchange: FetchExchange): FetchExchange | Promise<FetchExchange>` - Intercept and process data
|
|
359
|
+
|
|
360
|
+
#### InterceptorManager
|
|
361
|
+
|
|
362
|
+
Interceptor manager for managing multiple interceptors of the same type.
|
|
363
|
+
|
|
364
|
+
**Methods:**
|
|
365
|
+
|
|
366
|
+
- `use(interceptor: Interceptor): boolean` - Add interceptor, returns whether the addition was successful
|
|
367
|
+
- `eject(name: string): boolean` - Remove interceptor by name, returns whether the removal was successful
|
|
368
|
+
- `clear(): void` - Clear all interceptors
|
|
369
|
+
- `intercept(exchange: FetchExchange): Promise<FetchExchange>` - Execute all interceptors in sequence
|
|
370
|
+
|
|
371
|
+
#### FetcherInterceptors
|
|
372
|
+
|
|
373
|
+
Fetcher interceptor collection, including request, response, and error interceptor managers.
|
|
374
|
+
|
|
375
|
+
**Properties:**
|
|
376
|
+
|
|
377
|
+
- `request: InterceptorManager` - Request interceptor manager
|
|
378
|
+
- `response: InterceptorManager` - Response interceptor manager
|
|
379
|
+
- `error: InterceptorManager` - Error interceptor manager
|
|
380
|
+
|
|
381
|
+
**Options:**
|
|
382
|
+
|
|
383
|
+
- `baseURL`: Base URL for all requests
|
|
384
|
+
- `timeout`: Request timeout in milliseconds
|
|
385
|
+
- `headers`: Default request headers
|
|
386
|
+
|
|
387
|
+
#### Methods
|
|
388
|
+
|
|
389
|
+
- `fetch(url: string, request?: FetcherRequest): Promise<Response>` - Generic HTTP request method
|
|
390
|
+
- `get(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - GET request
|
|
391
|
+
- `post(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - POST request
|
|
392
|
+
- `put(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - PUT request
|
|
393
|
+
- `delete(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - DELETE request
|
|
394
|
+
- `patch(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - PATCH request
|
|
395
|
+
- `head(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - HEAD request
|
|
396
|
+
- `options(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - OPTIONS request
|
|
397
|
+
|
|
398
|
+
### Response Extension
|
|
399
|
+
|
|
400
|
+
To provide better TypeScript support, we extend the native Response interface with a type-safe json() method:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// Now you can use it with type safety
|
|
404
|
+
const response = await fetcher.get('/users/123');
|
|
405
|
+
const userData = await response.json<User>(); // Type is Promise<User>
|
|
406
|
+
```
|
|
407
|
+
|
|
192
408
|
### NamedFetcher Class
|
|
193
409
|
|
|
194
410
|
An extension of the Fetcher class that automatically registers itself with the global fetcherRegistrar.
|
|
@@ -221,6 +437,40 @@ Global instance for managing multiple Fetcher instances by name.
|
|
|
221
437
|
|
|
222
438
|
### Interceptor System
|
|
223
439
|
|
|
440
|
+
#### Interceptor
|
|
441
|
+
|
|
442
|
+
Interceptor interface that defines the basic structure of interceptors.
|
|
443
|
+
|
|
444
|
+
**Properties:**
|
|
445
|
+
|
|
446
|
+
- `name: string` - The name of the interceptor, used to identify it, must be unique
|
|
447
|
+
- `order: number` - The execution order of the interceptor, smaller values have higher priority
|
|
448
|
+
|
|
449
|
+
**Methods:**
|
|
450
|
+
|
|
451
|
+
- `intercept(exchange: FetchExchange): FetchExchange | Promise<FetchExchange>` - Intercept and process data
|
|
452
|
+
|
|
453
|
+
#### InterceptorManager
|
|
454
|
+
|
|
455
|
+
Interceptor manager for managing multiple interceptors of the same type.
|
|
456
|
+
|
|
457
|
+
**Methods:**
|
|
458
|
+
|
|
459
|
+
- `use(interceptor: Interceptor): boolean` - Add interceptor, returns whether the addition was successful
|
|
460
|
+
- `eject(name: string): boolean` - Remove interceptor by name, returns whether the removal was successful
|
|
461
|
+
- `clear(): void` - Clear all interceptors
|
|
462
|
+
- `intercept(exchange: FetchExchange): Promise<FetchExchange>` - Execute all interceptors in sequence
|
|
463
|
+
|
|
464
|
+
#### FetcherInterceptors
|
|
465
|
+
|
|
466
|
+
Fetcher interceptor collection, including request, response, and error interceptor managers.
|
|
467
|
+
|
|
468
|
+
**Properties:**
|
|
469
|
+
|
|
470
|
+
- `request: InterceptorManager` - Request interceptor manager
|
|
471
|
+
- `response: InterceptorManager` - Response interceptor manager
|
|
472
|
+
- `error: InterceptorManager` - Error interceptor manager
|
|
473
|
+
|
|
224
474
|
#### Request Interceptors
|
|
225
475
|
|
|
226
476
|
```typescript
|
|
@@ -229,7 +479,7 @@ import { Fetcher } from '@ahoo-wang/fetcher';
|
|
|
229
479
|
const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
230
480
|
|
|
231
481
|
// Add request interceptor (e.g., for authentication)
|
|
232
|
-
const
|
|
482
|
+
const success = fetcher.interceptors.request.use({
|
|
233
483
|
name: 'auth-interceptor',
|
|
234
484
|
order: 100,
|
|
235
485
|
intercept(exchange) {
|
package/README.zh-CN.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
[](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
|
|
7
7
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher)
|
|
8
8
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher)
|
|
9
|
+
[](https://deepwiki.com/Ahoo-Wang/fetcher)
|
|
9
10
|
|
|
10
11
|
一个现代、超轻量级(1.9kB)的 HTTP 客户端,内置路径参数、查询参数和类似 Axios 的 API。比 Axios 小 86%,同时提供相同的强大功能。
|
|
11
12
|
|
|
@@ -52,7 +53,7 @@ const response = await fetcher.get('/users/{id}', {
|
|
|
52
53
|
path: { id: 123 },
|
|
53
54
|
query: { include: 'profile' },
|
|
54
55
|
});
|
|
55
|
-
const userData = await response.json();
|
|
56
|
+
const userData = await response.json<User>();
|
|
56
57
|
|
|
57
58
|
// 带自动 JSON 转换的 POST 请求
|
|
58
59
|
const createUserResponse = await fetcher.post('/users', {
|
|
@@ -99,12 +100,46 @@ import { fetcher } from '@ahoo-wang/fetcher';
|
|
|
99
100
|
|
|
100
101
|
// 直接使用默认 fetcher
|
|
101
102
|
const response = await fetcher.get('/users');
|
|
102
|
-
const data = await response.json();
|
|
103
|
+
const data = await response.json<User>();
|
|
103
104
|
```
|
|
104
105
|
|
|
105
106
|
## 🔗 拦截器系统
|
|
106
107
|
|
|
107
|
-
###
|
|
108
|
+
### Interceptor
|
|
109
|
+
|
|
110
|
+
拦截器接口,定义了拦截器的基本结构。
|
|
111
|
+
|
|
112
|
+
**属性:**
|
|
113
|
+
|
|
114
|
+
- `name: string` - 拦截器的名称,用于标识拦截器,不可重复
|
|
115
|
+
- `order: number` - 拦截器的执行顺序,数值越小优先级越高
|
|
116
|
+
|
|
117
|
+
**方法:**
|
|
118
|
+
|
|
119
|
+
- `intercept(exchange: FetchExchange): FetchExchange | Promise<FetchExchange>` - 拦截并处理数据
|
|
120
|
+
|
|
121
|
+
### InterceptorManager
|
|
122
|
+
|
|
123
|
+
用于管理同一类型多个拦截器的拦截器管理器。
|
|
124
|
+
|
|
125
|
+
**方法:**
|
|
126
|
+
|
|
127
|
+
- `use(interceptor: Interceptor): boolean` - 添加拦截器,返回是否添加成功
|
|
128
|
+
- `eject(name: string): boolean` - 按名称移除拦截器,返回是否移除成功
|
|
129
|
+
- `clear(): void` - 清除所有拦截器
|
|
130
|
+
- `intercept(exchange: FetchExchange): Promise<FetchExchange>` - 顺序执行所有拦截器
|
|
131
|
+
|
|
132
|
+
### FetcherInterceptors
|
|
133
|
+
|
|
134
|
+
Fetcher 拦截器集合,包括请求、响应和错误拦截器管理器。
|
|
135
|
+
|
|
136
|
+
**属性:**
|
|
137
|
+
|
|
138
|
+
- `request: InterceptorManager` - 请求拦截器管理器
|
|
139
|
+
- `response: InterceptorManager` - 响应拦截器管理器
|
|
140
|
+
- `error: InterceptorManager` - 错误拦截器管理器
|
|
141
|
+
|
|
142
|
+
### 使用拦截器
|
|
108
143
|
|
|
109
144
|
```typescript
|
|
110
145
|
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
@@ -112,7 +147,9 @@ import { Fetcher } from '@ahoo-wang/fetcher';
|
|
|
112
147
|
const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
113
148
|
|
|
114
149
|
// 添加请求拦截器(例如用于认证)
|
|
115
|
-
const
|
|
150
|
+
const success = fetcher.interceptors.request.use({
|
|
151
|
+
name: 'auth-interceptor',
|
|
152
|
+
order: 100,
|
|
116
153
|
intercept(exchange) {
|
|
117
154
|
return {
|
|
118
155
|
...exchange,
|
|
@@ -127,27 +164,20 @@ const interceptorId = fetcher.interceptors.request.use({
|
|
|
127
164
|
},
|
|
128
165
|
});
|
|
129
166
|
|
|
130
|
-
// 移除拦截器
|
|
131
|
-
fetcher.interceptors.request.eject(interceptorId);
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### 响应拦截器
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
167
|
// 添加响应拦截器(例如用于日志记录)
|
|
138
168
|
fetcher.interceptors.response.use({
|
|
169
|
+
name: 'logging-interceptor',
|
|
170
|
+
order: 10,
|
|
139
171
|
intercept(exchange) {
|
|
140
|
-
console.log('收到响应:', exchange.response
|
|
172
|
+
console.log('收到响应:', exchange.response?.status);
|
|
141
173
|
return exchange;
|
|
142
174
|
},
|
|
143
175
|
});
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### 错误拦截器
|
|
147
176
|
|
|
148
|
-
```typescript
|
|
149
177
|
// 添加错误拦截器(例如用于统一错误处理)
|
|
150
178
|
fetcher.interceptors.error.use({
|
|
179
|
+
name: 'error-interceptor',
|
|
180
|
+
order: 50,
|
|
151
181
|
intercept(exchange) {
|
|
152
182
|
if (exchange.error?.name === 'FetchTimeoutError') {
|
|
153
183
|
console.error('请求超时:', exchange.error.message);
|
|
@@ -157,89 +187,62 @@ fetcher.interceptors.error.use({
|
|
|
157
187
|
return exchange;
|
|
158
188
|
},
|
|
159
189
|
});
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## 📚 API 参考
|
|
163
190
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
提供各种 HTTP 方法的核心 HTTP 客户端类。
|
|
167
|
-
|
|
168
|
-
#### 构造函数
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
new Fetcher(options ? : FetcherOptions);
|
|
191
|
+
// 按名称移除拦截器
|
|
192
|
+
fetcher.interceptors.request.eject('auth-interceptor');
|
|
172
193
|
```
|
|
173
194
|
|
|
174
|
-
|
|
195
|
+
### 有序执行
|
|
175
196
|
|
|
176
|
-
|
|
177
|
-
- `timeout`:以毫秒为单位的请求超时
|
|
178
|
-
- `headers`:默认请求头部
|
|
197
|
+
`OrderedCapable` 系统允许您控制拦截器和其他组件的执行顺序。
|
|
179
198
|
|
|
180
|
-
####
|
|
181
|
-
|
|
182
|
-
- `fetch(url: string, request?: FetcherRequest): Promise<Response>` - 通用 HTTP 请求方法
|
|
183
|
-
- `get(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - GET 请求
|
|
184
|
-
- `post(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - POST 请求
|
|
185
|
-
- `put(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - PUT 请求
|
|
186
|
-
- `delete(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - DELETE 请求
|
|
187
|
-
- `patch(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - PATCH 请求
|
|
188
|
-
- `head(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - HEAD 请求
|
|
189
|
-
- `options(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - OPTIONS 请求
|
|
190
|
-
|
|
191
|
-
### NamedFetcher 类
|
|
192
|
-
|
|
193
|
-
Fetcher 类的扩展,它会自动使用提供的名称在全局 fetcherRegistrar 中注册自己。
|
|
194
|
-
|
|
195
|
-
#### 构造函数
|
|
199
|
+
#### 排序概念
|
|
196
200
|
|
|
197
201
|
```typescript
|
|
198
|
-
|
|
199
|
-
:
|
|
200
|
-
string, options ? : FetcherOptions
|
|
201
|
-
)
|
|
202
|
-
;
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### FetcherRegistrar
|
|
206
|
-
|
|
207
|
-
用于按名称管理多个 Fetcher 实例的全局实例。
|
|
208
|
-
|
|
209
|
-
#### 属性
|
|
210
|
-
|
|
211
|
-
- `default`:获取或设置默认 fetcher 实例
|
|
212
|
-
|
|
213
|
-
#### 方法
|
|
202
|
+
import { OrderedCapable } from '@ahoo-wang/fetcher';
|
|
214
203
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
### 拦截器系统
|
|
222
|
-
|
|
223
|
-
#### InterceptorManager
|
|
224
|
-
|
|
225
|
-
用于管理同一类型多个拦截器的拦截器管理器。
|
|
226
|
-
|
|
227
|
-
**方法:**
|
|
204
|
+
// 数值越小优先级越高
|
|
205
|
+
const highPriority: OrderedCapable = { order: 1 }; // 首先执行
|
|
206
|
+
const mediumPriority: OrderedCapable = { order: 10 }; // 其次执行
|
|
207
|
+
const lowPriority: OrderedCapable = { order: 100 }; // 最后执行
|
|
208
|
+
```
|
|
228
209
|
|
|
229
|
-
|
|
230
|
-
- `eject(index: number): void` - 按 ID 移除拦截器
|
|
231
|
-
- `clear(): void` - 清除所有拦截器
|
|
232
|
-
- `intercept(exchange: FetchExchange): Promise<FetchExchange>` - 顺序执行所有拦截器
|
|
210
|
+
#### 拦截器排序
|
|
233
211
|
|
|
234
|
-
|
|
212
|
+
```typescript
|
|
213
|
+
// 添加具有不同顺序的拦截器
|
|
214
|
+
fetcher.interceptors.request.use({
|
|
215
|
+
name: 'timing-interceptor',
|
|
216
|
+
order: 5, // 很早执行
|
|
217
|
+
intercept(exchange) {
|
|
218
|
+
console.log('很早的计时');
|
|
219
|
+
return exchange;
|
|
220
|
+
},
|
|
221
|
+
});
|
|
235
222
|
|
|
236
|
-
|
|
223
|
+
fetcher.interceptors.request.use({
|
|
224
|
+
name: 'logging-interceptor',
|
|
225
|
+
order: 10, // 较早执行
|
|
226
|
+
intercept(exchange) {
|
|
227
|
+
console.log('较早的日志');
|
|
228
|
+
return exchange;
|
|
229
|
+
},
|
|
230
|
+
});
|
|
237
231
|
|
|
238
|
-
|
|
232
|
+
fetcher.interceptors.request.use({
|
|
233
|
+
name: 'auth-interceptor',
|
|
234
|
+
order: 50, // 较晚执行
|
|
235
|
+
intercept(exchange) {
|
|
236
|
+
// 添加认证头部
|
|
237
|
+
return exchange;
|
|
238
|
+
},
|
|
239
|
+
});
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
241
|
+
// 执行顺序将是:
|
|
242
|
+
// 1. timing-interceptor (order: 5)
|
|
243
|
+
// 2. logging-interceptor (order: 10)
|
|
244
|
+
// 3. auth-interceptor (order: 50)
|
|
245
|
+
```
|
|
243
246
|
|
|
244
247
|
## 🛠️ 开发
|
|
245
248
|
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC"}
|
package/dist/index.es.js
CHANGED
|
@@ -4,7 +4,7 @@ function w(r) {
|
|
|
4
4
|
function I(r, e) {
|
|
5
5
|
return w(e) ? e : e ? r.replace(/\/?\/$/, "") + "/" + e.replace(/^\/+/, "") : r;
|
|
6
6
|
}
|
|
7
|
-
class
|
|
7
|
+
class O {
|
|
8
8
|
/**
|
|
9
9
|
* 创建UrlBuilder实例
|
|
10
10
|
*
|
|
@@ -48,7 +48,7 @@ class P {
|
|
|
48
48
|
}) : e;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
function
|
|
51
|
+
function P(r, e) {
|
|
52
52
|
return typeof r < "u" ? r : e;
|
|
53
53
|
}
|
|
54
54
|
class m extends Error {
|
|
@@ -57,8 +57,8 @@ class m extends Error {
|
|
|
57
57
|
super(o), this.name = "FetchTimeoutError", this.exchange = e, Object.setPrototypeOf(this, m.prototype);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
var
|
|
61
|
-
const
|
|
60
|
+
var u = /* @__PURE__ */ ((r) => (r.GET = "GET", r.POST = "POST", r.PUT = "PUT", r.DELETE = "DELETE", r.PATCH = "PATCH", r.HEAD = "HEAD", r.OPTIONS = "OPTIONS", r))(u || {}), q = /* @__PURE__ */ ((r) => (r.METHOD = "method", r.BODY = "body", r))(q || {});
|
|
61
|
+
const l = "Content-Type";
|
|
62
62
|
var p = /* @__PURE__ */ ((r) => (r.APPLICATION_JSON = "application/json", r.TEXT_EVENT_STREAM = "text/event-stream", r))(p || {});
|
|
63
63
|
function h(r, e) {
|
|
64
64
|
return e ? r.filter(e).sort((t, s) => t.order - s.order) : [...r].sort((t, s) => t.order - s.order);
|
|
@@ -107,12 +107,12 @@ class d {
|
|
|
107
107
|
return t;
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
class
|
|
110
|
+
class A {
|
|
111
111
|
constructor() {
|
|
112
112
|
this.request = new d(), this.response = new d(), this.error = new d();
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
class
|
|
115
|
+
class F {
|
|
116
116
|
constructor() {
|
|
117
117
|
this.name = "RequestBodyInterceptor", this.order = Number.MIN_SAFE_INTEGER;
|
|
118
118
|
}
|
|
@@ -146,23 +146,23 @@ class S {
|
|
|
146
146
|
const s = { ...t };
|
|
147
147
|
s.body = JSON.stringify(t.body), s.headers || (s.headers = {});
|
|
148
148
|
const o = s.headers;
|
|
149
|
-
return o[
|
|
149
|
+
return o[l] || (o[l] = p.APPLICATION_JSON), { ...e, request: s };
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
const
|
|
153
|
-
[
|
|
154
|
-
},
|
|
152
|
+
const E = {
|
|
153
|
+
[l]: p.APPLICATION_JSON
|
|
154
|
+
}, T = {
|
|
155
155
|
baseURL: "",
|
|
156
|
-
headers:
|
|
156
|
+
headers: E
|
|
157
157
|
};
|
|
158
|
-
class
|
|
158
|
+
class S {
|
|
159
159
|
/**
|
|
160
160
|
* Create a Fetcher instance
|
|
161
161
|
*
|
|
162
162
|
* @param options - Fetcher configuration options
|
|
163
163
|
*/
|
|
164
|
-
constructor(e =
|
|
165
|
-
this.headers =
|
|
164
|
+
constructor(e = T) {
|
|
165
|
+
this.headers = E, this.interceptors = new A(), this.urlBuilder = new O(e.baseURL), e.headers !== void 0 && (this.headers = e.headers), this.timeout = e.timeout, this.interceptors.request.use(new F());
|
|
166
166
|
}
|
|
167
167
|
/**
|
|
168
168
|
* Make an HTTP request
|
|
@@ -206,10 +206,10 @@ class q {
|
|
|
206
206
|
...n
|
|
207
207
|
};
|
|
208
208
|
n = await this.interceptors.request.intercept(a), n.response = await this.timeoutFetch(n);
|
|
209
|
-
const
|
|
209
|
+
const c = {
|
|
210
210
|
...n
|
|
211
211
|
};
|
|
212
|
-
return n = await this.interceptors.response.intercept(
|
|
212
|
+
return n = await this.interceptors.response.intercept(c), n;
|
|
213
213
|
} catch (a) {
|
|
214
214
|
if (n.error = a, n = await this.interceptors.error.intercept(n), n.response)
|
|
215
215
|
return n;
|
|
@@ -228,19 +228,19 @@ class q {
|
|
|
228
228
|
* @throws FetchTimeoutError Thrown when the request times out
|
|
229
229
|
*/
|
|
230
230
|
async timeoutFetch(e) {
|
|
231
|
-
const t = e.url, s = e.request, o = s.timeout, i =
|
|
231
|
+
const t = e.url, s = e.request, o = s.timeout, i = P(o, this.timeout);
|
|
232
232
|
if (!i)
|
|
233
233
|
return fetch(t, s);
|
|
234
234
|
const n = new AbortController(), a = {
|
|
235
235
|
...s,
|
|
236
236
|
signal: n.signal
|
|
237
237
|
};
|
|
238
|
-
let
|
|
238
|
+
let c = null;
|
|
239
239
|
const g = new Promise((L, b) => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const
|
|
243
|
-
n.abort(
|
|
240
|
+
c = setTimeout(() => {
|
|
241
|
+
c && clearTimeout(c);
|
|
242
|
+
const y = new m(e, i);
|
|
243
|
+
n.abort(y), b(y);
|
|
244
244
|
}, i);
|
|
245
245
|
});
|
|
246
246
|
try {
|
|
@@ -249,7 +249,7 @@ class q {
|
|
|
249
249
|
g
|
|
250
250
|
]);
|
|
251
251
|
} finally {
|
|
252
|
-
|
|
252
|
+
c && clearTimeout(c);
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
/**
|
|
@@ -274,7 +274,7 @@ class q {
|
|
|
274
274
|
* @returns Promise<Response> HTTP response
|
|
275
275
|
*/
|
|
276
276
|
async get(e, t = {}) {
|
|
277
|
-
return this.methodFetch(
|
|
277
|
+
return this.methodFetch(u.GET, e, t);
|
|
278
278
|
}
|
|
279
279
|
/**
|
|
280
280
|
* Make a POST request
|
|
@@ -284,7 +284,7 @@ class q {
|
|
|
284
284
|
* @returns Promise<Response> HTTP response
|
|
285
285
|
*/
|
|
286
286
|
async post(e, t = {}) {
|
|
287
|
-
return this.methodFetch(
|
|
287
|
+
return this.methodFetch(u.POST, e, t);
|
|
288
288
|
}
|
|
289
289
|
/**
|
|
290
290
|
* Make a PUT request
|
|
@@ -294,7 +294,7 @@ class q {
|
|
|
294
294
|
* @returns Promise<Response> HTTP response
|
|
295
295
|
*/
|
|
296
296
|
async put(e, t = {}) {
|
|
297
|
-
return this.methodFetch(
|
|
297
|
+
return this.methodFetch(u.PUT, e, t);
|
|
298
298
|
}
|
|
299
299
|
/**
|
|
300
300
|
* Make a DELETE request
|
|
@@ -304,7 +304,7 @@ class q {
|
|
|
304
304
|
* @returns Promise<Response> HTTP response
|
|
305
305
|
*/
|
|
306
306
|
async delete(e, t = {}) {
|
|
307
|
-
return this.methodFetch(
|
|
307
|
+
return this.methodFetch(u.DELETE, e, t);
|
|
308
308
|
}
|
|
309
309
|
/**
|
|
310
310
|
* Make a PATCH request
|
|
@@ -314,7 +314,7 @@ class q {
|
|
|
314
314
|
* @returns Promise<Response> HTTP response
|
|
315
315
|
*/
|
|
316
316
|
async patch(e, t = {}) {
|
|
317
|
-
return this.methodFetch(
|
|
317
|
+
return this.methodFetch(u.PATCH, e, t);
|
|
318
318
|
}
|
|
319
319
|
/**
|
|
320
320
|
* Make a HEAD request
|
|
@@ -324,7 +324,7 @@ class q {
|
|
|
324
324
|
* @returns Promise<Response> HTTP response
|
|
325
325
|
*/
|
|
326
326
|
async head(e, t = {}) {
|
|
327
|
-
return this.methodFetch(
|
|
327
|
+
return this.methodFetch(u.HEAD, e, t);
|
|
328
328
|
}
|
|
329
329
|
/**
|
|
330
330
|
* Make an OPTIONS request
|
|
@@ -334,10 +334,10 @@ class q {
|
|
|
334
334
|
* @returns Promise<Response> HTTP response
|
|
335
335
|
*/
|
|
336
336
|
async options(e, t = {}) {
|
|
337
|
-
return this.methodFetch(
|
|
337
|
+
return this.methodFetch(u.OPTIONS, e, t);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
|
-
const
|
|
340
|
+
const f = "default";
|
|
341
341
|
class N {
|
|
342
342
|
constructor() {
|
|
343
343
|
this.registrar = /* @__PURE__ */ new Map();
|
|
@@ -411,7 +411,7 @@ class N {
|
|
|
411
411
|
* const defaultFetcher = fetcherRegistrar.default;
|
|
412
412
|
*/
|
|
413
413
|
get default() {
|
|
414
|
-
return this.requiredGet(
|
|
414
|
+
return this.requiredGet(f);
|
|
415
415
|
}
|
|
416
416
|
/**
|
|
417
417
|
* Set the default Fetcher instance
|
|
@@ -422,7 +422,7 @@ class N {
|
|
|
422
422
|
* fetcherRegistrar.default = fetcher;
|
|
423
423
|
*/
|
|
424
424
|
set default(e) {
|
|
425
|
-
this.register(
|
|
425
|
+
this.register(f, e);
|
|
426
426
|
}
|
|
427
427
|
/**
|
|
428
428
|
* Get a copy of all registered fetchers
|
|
@@ -439,7 +439,34 @@ class N {
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
const R = new N();
|
|
442
|
-
|
|
442
|
+
function _(r, e) {
|
|
443
|
+
if (Object.keys(r).length === 0)
|
|
444
|
+
return e;
|
|
445
|
+
if (Object.keys(e).length === 0)
|
|
446
|
+
return r;
|
|
447
|
+
const t = {
|
|
448
|
+
...r.path,
|
|
449
|
+
...e.path
|
|
450
|
+
}, s = {
|
|
451
|
+
...r.query,
|
|
452
|
+
...e.query
|
|
453
|
+
}, o = {
|
|
454
|
+
...r.headers,
|
|
455
|
+
...e.headers
|
|
456
|
+
}, i = e.method ?? r.method, n = e.body ?? r.body, a = e.timeout ?? r.timeout, c = e.signal ?? r.signal;
|
|
457
|
+
return {
|
|
458
|
+
...r,
|
|
459
|
+
...e,
|
|
460
|
+
method: i,
|
|
461
|
+
path: t,
|
|
462
|
+
query: s,
|
|
463
|
+
headers: o,
|
|
464
|
+
body: n,
|
|
465
|
+
timeout: a,
|
|
466
|
+
signal: c
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
class U extends S {
|
|
443
470
|
/**
|
|
444
471
|
* Create a NamedFetcher instance and automatically register it with the global fetcherRegistrar
|
|
445
472
|
*
|
|
@@ -457,30 +484,31 @@ class U extends q {
|
|
|
457
484
|
* headers: { 'Authorization': 'Bearer token' }
|
|
458
485
|
* });
|
|
459
486
|
*/
|
|
460
|
-
constructor(e, t =
|
|
487
|
+
constructor(e, t = T) {
|
|
461
488
|
super(t), this.name = e, R.register(e, this);
|
|
462
489
|
}
|
|
463
490
|
}
|
|
464
|
-
const
|
|
491
|
+
const D = new U(f);
|
|
465
492
|
export {
|
|
466
|
-
|
|
493
|
+
l as ContentTypeHeader,
|
|
467
494
|
p as ContentTypeValues,
|
|
468
|
-
|
|
469
|
-
|
|
495
|
+
f as DEFAULT_FETCHER_NAME,
|
|
496
|
+
T as DEFAULT_OPTIONS,
|
|
470
497
|
m as FetchTimeoutError,
|
|
471
|
-
|
|
472
|
-
|
|
498
|
+
S as Fetcher,
|
|
499
|
+
A as FetcherInterceptors,
|
|
473
500
|
N as FetcherRegistrar,
|
|
474
|
-
|
|
501
|
+
u as HttpMethod,
|
|
475
502
|
d as InterceptorManager,
|
|
476
503
|
U as NamedFetcher,
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
504
|
+
F as RequestBodyInterceptor,
|
|
505
|
+
q as RequestField,
|
|
506
|
+
O as UrlBuilder,
|
|
480
507
|
I as combineURLs,
|
|
481
|
-
|
|
508
|
+
D as fetcher,
|
|
482
509
|
R as fetcherRegistrar,
|
|
483
510
|
w as isAbsoluteURL,
|
|
484
|
-
|
|
511
|
+
_ as mergeRequest,
|
|
512
|
+
P as resolveTimeout,
|
|
485
513
|
h as toSorted
|
|
486
514
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(n,
|
|
1
|
+
(function(n,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(n=typeof globalThis<"u"?globalThis:n||self,d(n.Fetcher={}))})(this,(function(n){"use strict";function d(r){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(r)}function g(r,e){return d(e)?e:e?r.replace(/\/?\/$/,"")+"/"+e.replace(/^\/+/,""):r}class b{constructor(e){this.baseURL=e}build(e,t,s){const i=g(this.baseURL,e);let c=this.interpolateUrl(i,t);if(s){const o=new URLSearchParams(s).toString();o&&(c+="?"+o)}return c}interpolateUrl(e,t){return t?e.replace(/{([^}]+)}/g,(s,i)=>{const c=t[i];if(c===void 0)throw new Error(`Missing required path parameter: ${i}`);return String(c)}):e}}function F(r,e){return typeof r<"u"?r:e}class l extends Error{constructor(e,t){const s=e.request?.method||"GET",i=`Request timeout of ${t}ms exceeded for ${s} ${e.url}`;super(i),this.name="FetchTimeoutError",this.exchange=e,Object.setPrototypeOf(this,l.prototype)}}var u=(r=>(r.GET="GET",r.POST="POST",r.PUT="PUT",r.DELETE="DELETE",r.PATCH="PATCH",r.HEAD="HEAD",r.OPTIONS="OPTIONS",r))(u||{}),I=(r=>(r.METHOD="method",r.BODY="body",r))(I||{});const f="Content-Type";var m=(r=>(r.APPLICATION_JSON="application/json",r.TEXT_EVENT_STREAM="text/event-stream",r))(m||{});function y(r,e){return e?r.filter(e).sort((t,s)=>t.order-s.order):[...r].sort((t,s)=>t.order-s.order)}class T{constructor(e=[]){this.sortedInterceptors=[],this.sortedInterceptors=y(e)}get name(){return this.constructor.name}get order(){return Number.MIN_SAFE_INTEGER}use(e){return this.sortedInterceptors.some(t=>t.name===e.name)?!1:(this.sortedInterceptors=y([...this.sortedInterceptors,e]),!0)}eject(e){const t=this.sortedInterceptors;return this.sortedInterceptors=y(t,s=>s.name!==e),t.length!==this.sortedInterceptors.length}clear(){this.sortedInterceptors=[]}async intercept(e){let t=e;for(const s of this.sortedInterceptors)t=await s.intercept(t);return t}}class w{constructor(){this.request=new T,this.response=new T,this.error=new T}}class A{constructor(){this.name="RequestBodyInterceptor",this.order=Number.MIN_SAFE_INTEGER}intercept(e){const t=e.request;if(t.body===void 0||t.body===null||typeof t.body!="object"||t.body instanceof ArrayBuffer||ArrayBuffer.isView(t.body)||t.body instanceof Blob||t.body instanceof File||t.body instanceof URLSearchParams||t.body instanceof FormData||t.body instanceof ReadableStream)return e;const s={...t};s.body=JSON.stringify(t.body),s.headers||(s.headers={});const i=s.headers;return i[f]||(i[f]=m.APPLICATION_JSON),{...e,request:s}}}const O={[f]:m.APPLICATION_JSON},p={baseURL:"",headers:O};class R{constructor(e=p){this.headers=O,this.interceptors=new w,this.urlBuilder=new b(e.baseURL),e.headers!==void 0&&(this.headers=e.headers),this.timeout=e.timeout,this.interceptors.request.use(new A)}async fetch(e,t={}){const s=await this.request(e,t);if(!s.response)throw new Error(`Request to ${s.url} failed with no response`);return s.response}async request(e,t={}){const s={...this.headers||{},...t.headers||{}},i={...t,headers:Object.keys(s).length>0?s:void 0},c=this.urlBuilder.build(e,t.path,t.query);let o={fetcher:this,url:c,request:i,response:void 0,error:void 0};try{const h={...o};o=await this.interceptors.request.intercept(h),o.response=await this.timeoutFetch(o);const a={...o};return o=await this.interceptors.response.intercept(a),o}catch(h){if(o.error=h,o=await this.interceptors.error.intercept(o),o.response)return o;throw o.error}}async timeoutFetch(e){const t=e.url,s=e.request,i=s.timeout,c=F(i,this.timeout);if(!c)return fetch(t,s);const o=new AbortController,h={...s,signal:o.signal};let a=null;const _=new Promise((C,D)=>{a=setTimeout(()=>{a&&clearTimeout(a);const N=new l(e,c);o.abort(N),D(N)},c)});try{return await Promise.race([fetch(t,h),_])}finally{a&&clearTimeout(a)}}async methodFetch(e,t,s={}){return this.fetch(t,{...s,method:e})}async get(e,t={}){return this.methodFetch(u.GET,e,t)}async post(e,t={}){return this.methodFetch(u.POST,e,t)}async put(e,t={}){return this.methodFetch(u.PUT,e,t)}async delete(e,t={}){return this.methodFetch(u.DELETE,e,t)}async patch(e,t={}){return this.methodFetch(u.PATCH,e,t)}async head(e,t={}){return this.methodFetch(u.HEAD,e,t)}async options(e,t={}){return this.methodFetch(u.OPTIONS,e,t)}}const E="default";class q{constructor(){this.registrar=new Map}register(e,t){this.registrar.set(e,t)}unregister(e){return this.registrar.delete(e)}get(e){return this.registrar.get(e)}requiredGet(e){const t=this.get(e);if(!t)throw new Error(`Fetcher ${e} not found`);return t}get default(){return this.requiredGet(E)}set default(e){this.register(E,e)}get fetchers(){return new Map(this.registrar)}}const P=new q;function U(r,e){if(Object.keys(r).length===0)return e;if(Object.keys(e).length===0)return r;const t={...r.path,...e.path},s={...r.query,...e.query},i={...r.headers,...e.headers},c=e.method??r.method,o=e.body??r.body,h=e.timeout??r.timeout,a=e.signal??r.signal;return{...r,...e,method:c,path:t,query:s,headers:i,body:o,timeout:h,signal:a}}class S extends R{constructor(e,t=p){super(t),this.name=e,P.register(e,this)}}const L=new S(E);n.ContentTypeHeader=f,n.ContentTypeValues=m,n.DEFAULT_FETCHER_NAME=E,n.DEFAULT_OPTIONS=p,n.FetchTimeoutError=l,n.Fetcher=R,n.FetcherInterceptors=w,n.FetcherRegistrar=q,n.HttpMethod=u,n.InterceptorManager=T,n.NamedFetcher=S,n.RequestBodyInterceptor=A,n.RequestField=I,n.UrlBuilder=b,n.combineURLs=g,n.fetcher=L,n.fetcherRegistrar=P,n.isAbsoluteURL=d,n.mergeRequest=U,n.resolveTimeout=F,n.toSorted=y,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FetcherRequest } from './fetcher';
|
|
2
|
+
/**
|
|
3
|
+
* Merges two FetcherRequest objects into one.
|
|
4
|
+
*
|
|
5
|
+
* This function combines two FetcherRequest objects, with the second object's properties
|
|
6
|
+
* taking precedence over the first object's properties. Special handling is applied
|
|
7
|
+
* to nested objects like path, query, and headers which are merged recursively.
|
|
8
|
+
* For primitive values, the second object's values override the first's.
|
|
9
|
+
*
|
|
10
|
+
* @param first - The first request object (lower priority)
|
|
11
|
+
* @param second - The second request object (higher priority)
|
|
12
|
+
* @returns A new FetcherRequest object with merged properties
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const request1 = {
|
|
17
|
+
* method: 'GET',
|
|
18
|
+
* path: { id: 1 },
|
|
19
|
+
* headers: { 'Content-Type': 'application/json' }
|
|
20
|
+
* };
|
|
21
|
+
*
|
|
22
|
+
* const request2 = {
|
|
23
|
+
* method: 'POST',
|
|
24
|
+
* query: { filter: 'active' },
|
|
25
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* const merged = mergeRequest(request1, request2);
|
|
29
|
+
* // Result: {
|
|
30
|
+
* // method: 'POST',
|
|
31
|
+
* // path: { id: 1 },
|
|
32
|
+
* // query: { filter: 'active' },
|
|
33
|
+
* // headers: {
|
|
34
|
+
* // 'Content-Type': 'application/json',
|
|
35
|
+
* // 'Authorization': 'Bearer token'
|
|
36
|
+
* // }
|
|
37
|
+
* // }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function mergeRequest(first: FetcherRequest, second: FetcherRequest): FetcherRequest;
|
|
41
|
+
//# sourceMappingURL=mergeRequest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeRequest.d.ts","sourceRoot":"","sources":["../src/mergeRequest.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,cAAc,GACrB,cAAc,CA6ChB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -42,4 +42,18 @@ export interface NamedCapable {
|
|
|
42
42
|
*/
|
|
43
43
|
name: string;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Global extension of Response interface
|
|
47
|
+
* Adds type-safe json() method support to Response objects
|
|
48
|
+
*/
|
|
49
|
+
declare global {
|
|
50
|
+
interface Response {
|
|
51
|
+
/**
|
|
52
|
+
* Parse response body as JSON in a type-safe manner
|
|
53
|
+
* @template T The type of returned data, defaults to any
|
|
54
|
+
* @returns Promise<T> The parsed JSON data
|
|
55
|
+
*/
|
|
56
|
+
json<T = any>(): Promise<T>;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
45
59
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,oBAAY,UAAU;IACpB,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,GAAG,QAAQ;IACX,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,OAAO,YAAY;CACpB;AAED,oBAAY,YAAY;IACtB,MAAM,WAAW;IACjB,IAAI,SAAS;CACd;AAED,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,oBAAY,iBAAiB;IAC3B,gBAAgB,qBAAqB;IACrC,iBAAiB,sBAAsB;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,oBAAY,UAAU;IACpB,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,GAAG,QAAQ;IACX,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,OAAO,YAAY;CACpB;AAED,oBAAY,YAAY;IACtB,MAAM,WAAW;IACjB,IAAI,SAAS;CACd;AAED,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,oBAAY,iBAAiB;IAC3B,gBAAgB,qBAAqB;IACrC,iBAAiB,sBAAsB;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,QAAQ;QAChB;;;;WAIG;QACH,IAAI,CAAC,CAAC,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;KAC7B;CACF"}
|