@ahoo-wang/fetcher 0.0.1 → 0.0.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/README.md +172 -38
- package/dist/index.umd.js +1 -1
- package/dist/urls.d.ts +13 -0
- package/dist/urls.d.ts.map +1 -1
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# fetcher
|
|
1
|
+
# @fetcher/core
|
|
2
2
|
|
|
3
3
|
Fetcher-core 是一个基于现代 Fetch API 的 HTTP 客户端库,旨在简化和优化与后端 RESTful API 的交互。它提供了类似于 Axios 的 API,支持路径参数、查询参数、超时设置以及请求和响应拦截器。
|
|
4
4
|
|
|
@@ -10,6 +10,7 @@ Fetcher-core 是一个基于现代 Fetch API 的 HTTP 客户端库,旨在简
|
|
|
10
10
|
- **请求拦截器**: 支持在请求发送前对请求进行修改。
|
|
11
11
|
- **响应拦截器**: 支持在响应返回后对响应进行处理。
|
|
12
12
|
- **模块化设计**: 代码结构清晰,易于维护和扩展。
|
|
13
|
+
- **TypeScript 支持**: 完整的 TypeScript 类型定义。
|
|
13
14
|
|
|
14
15
|
## 安装
|
|
15
16
|
|
|
@@ -19,8 +20,22 @@ Fetcher-core 是一个基于现代 Fetch API 的 HTTP 客户端库,旨在简
|
|
|
19
20
|
pnpm add @fetcher/core
|
|
20
21
|
```
|
|
21
22
|
|
|
23
|
+
使用 npm 安装:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @fetcher/core
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
使用 yarn 安装:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
yarn add @fetcher/core
|
|
33
|
+
```
|
|
34
|
+
|
|
22
35
|
## 使用示例
|
|
23
36
|
|
|
37
|
+
### 基本用法
|
|
38
|
+
|
|
24
39
|
```typescript
|
|
25
40
|
import { Fetcher } from '@fetcher/core';
|
|
26
41
|
|
|
@@ -29,8 +44,25 @@ const fetcher = new Fetcher({
|
|
|
29
44
|
timeout: 5000,
|
|
30
45
|
});
|
|
31
46
|
|
|
47
|
+
// GET 请求带路径参数和查询参数
|
|
48
|
+
fetcher
|
|
49
|
+
.get('/users/{id}', {
|
|
50
|
+
pathParams: { id: 123 },
|
|
51
|
+
queryParams: { include: 'profile' },
|
|
52
|
+
})
|
|
53
|
+
.then(response => {
|
|
54
|
+
console.log(response.data);
|
|
55
|
+
})
|
|
56
|
+
.catch(error => {
|
|
57
|
+
console.error(error);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// POST 请求带 JSON 数据
|
|
32
61
|
fetcher
|
|
33
|
-
.
|
|
62
|
+
.post('/users', {
|
|
63
|
+
body: JSON.stringify({ name: 'John Doe', email: 'john@example.com' }),
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
})
|
|
34
66
|
.then(response => {
|
|
35
67
|
console.log(response.data);
|
|
36
68
|
})
|
|
@@ -39,73 +71,175 @@ fetcher
|
|
|
39
71
|
});
|
|
40
72
|
```
|
|
41
73
|
|
|
74
|
+
### 拦截器用法
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Fetcher } from '@fetcher/core';
|
|
78
|
+
|
|
79
|
+
const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
80
|
+
|
|
81
|
+
// 添加请求拦截器
|
|
82
|
+
const requestInterceptorId = fetcher.interceptors.request.use({
|
|
83
|
+
intercept(request) {
|
|
84
|
+
// 修改请求配置,例如添加认证头
|
|
85
|
+
return {
|
|
86
|
+
...request,
|
|
87
|
+
headers: {
|
|
88
|
+
...request.headers,
|
|
89
|
+
Authorization: 'Bearer token',
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// 添加响应拦截器
|
|
96
|
+
const responseInterceptorId = fetcher.interceptors.response.use({
|
|
97
|
+
intercept(response) {
|
|
98
|
+
// 处理响应数据,例如解析 JSON
|
|
99
|
+
return response;
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// 添加错误拦截器
|
|
104
|
+
const errorInterceptorId = fetcher.interceptors.error.use({
|
|
105
|
+
intercept(error) {
|
|
106
|
+
// 处理错误,例如记录日志
|
|
107
|
+
console.error('Request failed:', error);
|
|
108
|
+
return error;
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
42
113
|
## API 参考
|
|
43
114
|
|
|
44
|
-
### Fetcher
|
|
115
|
+
### Fetcher 类
|
|
116
|
+
|
|
117
|
+
核心 HTTP 客户端类,提供各种 HTTP 方法。
|
|
118
|
+
|
|
119
|
+
#### 构造函数
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
new Fetcher(options?: FetcherOptions)
|
|
123
|
+
```
|
|
45
124
|
|
|
46
|
-
|
|
47
|
-
- `get(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>`
|
|
48
|
-
- `post(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>`
|
|
49
|
-
- `put(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>`
|
|
50
|
-
- `delete(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>`
|
|
51
|
-
- `patch(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>`
|
|
52
|
-
- `head(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>`
|
|
53
|
-
- `options(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>`
|
|
125
|
+
**参数:**
|
|
54
126
|
|
|
55
|
-
|
|
127
|
+
- `options.baseURL`: 基础 URL
|
|
128
|
+
- `options.timeout`: 请求超时时间(毫秒)
|
|
129
|
+
- `options.headers`: 默认请求头
|
|
56
130
|
|
|
57
|
-
|
|
131
|
+
#### 方法
|
|
58
132
|
|
|
59
|
-
|
|
133
|
+
- `fetch(url: string, request?: FetcherRequest): Promise<Response>` - 通用 HTTP 请求方法
|
|
134
|
+
- `get(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - GET 请求
|
|
135
|
+
- `post(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - POST 请求
|
|
136
|
+
- `put(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - PUT 请求
|
|
137
|
+
- `delete(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - DELETE 请求
|
|
138
|
+
- `patch(url: string, request?: Omit<FetcherRequest, 'method'>): Promise<Response>` - PATCH 请求
|
|
139
|
+
- `head(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - HEAD 请求
|
|
140
|
+
- `options(url: string, request?: Omit<FetcherRequest, 'method' | 'body'>): Promise<Response>` - OPTIONS 请求
|
|
60
141
|
|
|
61
|
-
|
|
62
|
-
- `eject(index: number): void`
|
|
63
|
-
- `clear(): void`
|
|
64
|
-
- `intercept(data: R): Promise<R>`
|
|
142
|
+
### UrlBuilder 类
|
|
65
143
|
|
|
66
|
-
|
|
144
|
+
URL 构建器,用于构建带参数的完整 URL。
|
|
67
145
|
|
|
68
|
-
|
|
69
|
-
- `response: ResponseInterceptorManager`
|
|
70
|
-
- `error: ErrorInterceptorManager`
|
|
146
|
+
#### 方法
|
|
71
147
|
|
|
72
|
-
|
|
148
|
+
- `build(path: string, pathParams?: Record<string, any>, queryParams?: Record<string, any>): string` - 构建完整 URL
|
|
149
|
+
|
|
150
|
+
### InterceptorManager 类
|
|
151
|
+
|
|
152
|
+
拦截器管理器,用于管理同一类型的多个拦截器。
|
|
153
|
+
|
|
154
|
+
#### 方法
|
|
155
|
+
|
|
156
|
+
- `use(interceptor: T): number` - 添加拦截器,返回拦截器 ID
|
|
157
|
+
- `eject(index: number): void` - 根据 ID 移除拦截器
|
|
158
|
+
- `clear(): void` - 清空所有拦截器
|
|
159
|
+
- `intercept(data: R): Promise<R>` - 依次执行所有拦截器
|
|
160
|
+
|
|
161
|
+
### FetcherInterceptors 类
|
|
162
|
+
|
|
163
|
+
Fetcher 拦截器集合,包含请求、响应和错误拦截器管理器。
|
|
164
|
+
|
|
165
|
+
#### 属性
|
|
166
|
+
|
|
167
|
+
- `request: RequestInterceptorManager` - 请求拦截器管理器
|
|
168
|
+
- `response: ResponseInterceptorManager` - 响应拦截器管理器
|
|
169
|
+
- `error: ErrorInterceptorManager` - 错误拦截器管理器
|
|
170
|
+
|
|
171
|
+
## 完整示例
|
|
73
172
|
|
|
74
173
|
```typescript
|
|
75
174
|
import { Fetcher } from '@fetcher/core';
|
|
76
175
|
|
|
77
|
-
|
|
176
|
+
// 创建 fetcher 实例
|
|
177
|
+
const fetcher = new Fetcher({
|
|
178
|
+
baseURL: 'https://api.example.com',
|
|
179
|
+
timeout: 10000,
|
|
180
|
+
headers: {
|
|
181
|
+
'Content-Type': 'application/json',
|
|
182
|
+
},
|
|
183
|
+
});
|
|
78
184
|
|
|
79
|
-
// 添加请求拦截器
|
|
80
|
-
|
|
185
|
+
// 添加请求拦截器 - 添加认证头
|
|
186
|
+
fetcher.interceptors.request.use({
|
|
81
187
|
intercept(request) {
|
|
82
|
-
// 修改请求配置
|
|
83
188
|
return {
|
|
84
189
|
...request,
|
|
85
190
|
headers: {
|
|
86
191
|
...request.headers,
|
|
87
|
-
Authorization: 'Bearer
|
|
192
|
+
Authorization: 'Bearer ' + getAuthToken(),
|
|
88
193
|
},
|
|
89
194
|
};
|
|
90
195
|
},
|
|
91
196
|
});
|
|
92
197
|
|
|
93
|
-
// 添加响应拦截器
|
|
94
|
-
|
|
198
|
+
// 添加响应拦截器 - 自动解析 JSON
|
|
199
|
+
fetcher.interceptors.response.use({
|
|
95
200
|
intercept(response) {
|
|
96
|
-
|
|
201
|
+
if (response.headers.get('content-type')?.includes('application/json')) {
|
|
202
|
+
return response.json().then(data => {
|
|
203
|
+
return new Response(JSON.stringify(data), response);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
97
206
|
return response;
|
|
98
207
|
},
|
|
99
208
|
});
|
|
100
209
|
|
|
101
|
-
//
|
|
102
|
-
fetcher.interceptors.
|
|
103
|
-
|
|
210
|
+
// 添加错误拦截器 - 统一错误处理
|
|
211
|
+
fetcher.interceptors.error.use({
|
|
212
|
+
intercept(error) {
|
|
213
|
+
if (error.name === 'FetchTimeoutError') {
|
|
214
|
+
console.error('Request timeout:', error.message);
|
|
215
|
+
} else {
|
|
216
|
+
console.error('Network error:', error.message);
|
|
217
|
+
}
|
|
218
|
+
return error;
|
|
219
|
+
},
|
|
220
|
+
});
|
|
104
221
|
|
|
105
|
-
//
|
|
106
|
-
fetcher
|
|
107
|
-
|
|
108
|
-
|
|
222
|
+
// 使用 fetcher 发起请求
|
|
223
|
+
fetcher
|
|
224
|
+
.get('/users/{id}', {
|
|
225
|
+
pathParams: { id: 123 },
|
|
226
|
+
queryParams: { include: 'profile,posts' },
|
|
227
|
+
})
|
|
228
|
+
.then(response => response.json())
|
|
229
|
+
.then(data => {
|
|
230
|
+
console.log('User data:', data);
|
|
231
|
+
})
|
|
232
|
+
.catch(error => {
|
|
233
|
+
console.error('Failed to fetch user:', error);
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## 测试
|
|
238
|
+
|
|
239
|
+
运行测试:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
pnpm test
|
|
109
243
|
```
|
|
110
244
|
|
|
111
245
|
## 贡献
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(i,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(i=typeof globalThis<"u"?globalThis:i||self,u(i.
|
|
1
|
+
(function(i,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(i=typeof globalThis<"u"?globalThis:i||self,u(i.Fetcher={}))})(this,(function(i){"use strict";function u(r){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(r)}function f(r,e){return u(e)?e:e?r.replace(/\/?\/$/,"")+"/"+e.replace(/^\/+/,""):r}class d{constructor(e){this.baseURL=e}build(e,t,s){let n=f(this.baseURL,e),h=this.interpolateUrl(n,t);if(s){const o=new URLSearchParams(s).toString();o&&(h+="?"+o)}return h}interpolateUrl(e,t){return t?e.replace(/{([^}]+)}/g,(s,n)=>{const h=t[n];if(h===void 0)throw new Error(`Missing required path parameter: ${n}`);return String(h)}):e}}function m(r,e){return typeof r<"u"?r:e}class a extends Error{constructor(e,t,s){const n=`Request timeout of ${s}ms exceeded for ${t?.method||"GET"} ${e}`;super(n),this.name="FetchTimeoutError",this.url=e,this.request=t,Object.setPrototypeOf(this,a.prototype)}}var c=(r=>(r.GET="GET",r.POST="POST",r.PUT="PUT",r.DELETE="DELETE",r.PATCH="PATCH",r.HEAD="HEAD",r.OPTIONS="OPTIONS",r))(c||{}),p=(r=>(r.METHOD="method",r.BODY="body",r))(p||{});class l{constructor(){this.interceptors=[]}use(e){const t=this.interceptors.length;return this.interceptors.push(e),t}eject(e){this.interceptors[e]&&(this.interceptors[e]=null)}clear(){this.interceptors=[]}async intercept(e){let t=e;for(let s of this.interceptors)s&&(t=await s.intercept(t));return t}}class E{constructor(){this.request=new l,this.response=new l,this.error=new l}}const y={baseURL:""};class b{constructor(e=y){this.interceptors=new E,this.urlBuilder=new d(e.baseURL),this.headers=e.headers,this.timeout=e.timeout}async fetch(e,t={}){const s={...this.headers||{},...t.headers||{}};let n={...t,headers:Object.keys(s).length>0?s:void 0};const h=this.urlBuilder.build(e,t.pathParams,t.queryParams);n=await this.interceptors.request.intercept(n);try{let o=await this.timeoutFetch(h,n);return o=await this.interceptors.response.intercept(o),o}catch(o){throw await this.interceptors.error.intercept(o)}}async timeoutFetch(e,t){const s=m(t.timeout,this.timeout);if(!s)return fetch(e,t);const n=new AbortController,h={...t,signal:n.signal};let o=null;const T=new Promise((P,w)=>{o=setTimeout(()=>{n.abort(),w(new a(e,t,s))},s)});try{return await Promise.race([fetch(e,h),T])}finally{o&&clearTimeout(o)}}async get(e,t={}){return this.fetch(e,{...t,method:c.GET})}async post(e,t={}){return this.fetch(e,{...t,method:c.POST})}async put(e,t={}){return this.fetch(e,{...t,method:c.PUT})}async delete(e,t={}){return this.fetch(e,{...t,method:c.DELETE})}async patch(e,t={}){return this.fetch(e,{...t,method:c.PATCH})}async head(e,t={}){return this.fetch(e,{...t,method:c.HEAD})}async options(e,t={}){return this.fetch(e,{...t,method:c.OPTIONS})}}i.FetchTimeoutError=a,i.Fetcher=b,i.HttpMethod=c,i.RequestField=p,i.UrlBuilder=d,i.combineURLs=f,i.isAbsoluteURL=u,i.resolveTimeout=m,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})}));
|
package/dist/urls.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检查给定的 URL 是否为绝对 URL
|
|
3
|
+
*
|
|
4
|
+
* @param url - 需要检查的 URL 字符串
|
|
5
|
+
* @returns boolean - 如果是绝对 URL 返回 true,否则返回 false
|
|
6
|
+
*/
|
|
1
7
|
export declare function isAbsoluteURL(url: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* 将基础 URL 和相对 URL 组合成完整的 URL
|
|
10
|
+
*
|
|
11
|
+
* @param baseURL - 基础 URL
|
|
12
|
+
* @param relativeURL - 相对 URL
|
|
13
|
+
* @returns string - 组合后的完整 URL
|
|
14
|
+
*/
|
|
2
15
|
export declare function combineURLs(baseURL: string, relativeURL: string): string;
|
|
3
16
|
//# sourceMappingURL=urls.d.ts.map
|
package/dist/urls.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../src/urls.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,WAExC;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAQ/D"}
|
|
1
|
+
{"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../src/urls.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,WAExC;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAQ/D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahoo-wang/fetcher",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Core library providing basic HTTP client functionality for Fetcher",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fetch",
|
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
"main": "./dist/index.umd.js",
|
|
23
23
|
"module": "./dist/index.es.js",
|
|
24
24
|
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": "./dist/index.es.js",
|
|
28
|
+
"require": "./dist/index.umd.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
25
31
|
"files": [
|
|
26
32
|
"dist"
|
|
27
33
|
],
|