soon-fetch 3.0.0-beta.1 → 3.0.0-beta.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 +390 -404
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +76 -90
- package/dist/index.js +1 -1
- package/package.json +41 -41
package/README.md
CHANGED
|
@@ -1,404 +1,390 @@
|
|
|
1
|
-
[English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
|
|
2
|
-
|
|
3
|
-
<!-- omit in toc -->
|
|
4
|
-
|
|
5
|
-
###
|
|
6
|
-
|
|
7
|
-
**A lightweight http request lib , alternative to axios**
|
|
8
|
-
|
|
9
|
-
> - 🌐 automatic parse restful api url parameters
|
|
10
|
-
> - ⭐ rapid define a request api
|
|
11
|
-
> - ⌛ timeout disconnect
|
|
12
|
-
> - 🔤 automatic parse or serialization of JSON
|
|
13
|
-
> - 📏 .min size less than **3K**, smaller after zip
|
|
14
|
-
> - 💡 smart type tips with Typescript
|
|
15
|
-
|
|
16
|
-
- [Example](#example)
|
|
17
|
-
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
> [github: soon-admin-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
soon.get("/user
|
|
53
|
-
soon.get("/user
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
soon.
|
|
75
|
-
soon.
|
|
76
|
-
soon.
|
|
77
|
-
soon.
|
|
78
|
-
soon.
|
|
79
|
-
soon.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
- [
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
#####
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
喜欢 soon-fetch 的话 , 在 github 上给个 **star** 吧.
|
|
392
|
-
GitHub: https://github.com/leafio/soon-fetch
|
|
393
|
-
|
|
394
|
-
> Email: leafnote@outlook.com
|
|
395
|
-
|
|
396
|
-
[English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
|
|
397
|
-
|
|
398
|
-
<!-- omit in toc -->
|
|
399
|
-
|
|
400
|
-
##### 安装 Installation
|
|
401
|
-
|
|
402
|
-
```bash
|
|
403
|
-
npm install soon-fetch
|
|
404
|
-
```
|
|
1
|
+
[English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
|
|
2
|
+
|
|
3
|
+
<!-- omit in toc -->
|
|
4
|
+
|
|
5
|
+
### soon-fetch
|
|
6
|
+
|
|
7
|
+
**A lightweight http request lib , alternative to axios**
|
|
8
|
+
|
|
9
|
+
> - 🌐 automatic parse restful api url parameters
|
|
10
|
+
> - ⭐ rapid define a request api
|
|
11
|
+
> - ⌛ timeout disconnect
|
|
12
|
+
> - 🔤 automatic parse or serialization of JSON
|
|
13
|
+
> - 📏 .min size less than **3K**, smaller after zip
|
|
14
|
+
> - 💡 smart type tips with Typescript
|
|
15
|
+
|
|
16
|
+
- [Example](#example)
|
|
17
|
+
- [Features](#features)
|
|
18
|
+
- [Shortcut](#shortcut)
|
|
19
|
+
- [Restful Url Params](#restful-url-params)
|
|
20
|
+
- [Timeout](#timeout)
|
|
21
|
+
- [Rapid Define APIs](#rapid-define-apis)
|
|
22
|
+
- [API](#api)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Example
|
|
26
|
+
|
|
27
|
+
> [github: soon-admin-vue3 ](https://github.com/leafio/soon-admin-vue3)
|
|
28
|
+
> [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { createSoon, parseUrlOptions, type SoonOptions } from "soon-fetch";
|
|
32
|
+
|
|
33
|
+
const soon = createSoon(({ abortController }) => {
|
|
34
|
+
return <T>(url: string, options?: SoonOptions) => {
|
|
35
|
+
const [_url, _options] = parseUrlOptions({
|
|
36
|
+
url,
|
|
37
|
+
options,
|
|
38
|
+
baseURL: "/api",
|
|
39
|
+
baseOptions: {
|
|
40
|
+
headers: new Headers({
|
|
41
|
+
Authorization: "Bearer " + localStorage.getItem("token"),
|
|
42
|
+
}),
|
|
43
|
+
timeout: 5000,
|
|
44
|
+
signal: abortController.signal,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
return fetch(_url, _options).then((res) => res.json()) as Promise<T>;
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** GET */
|
|
52
|
+
soon.get("/user?id=123");
|
|
53
|
+
soon.get("/user", { query: { id: 123 } });
|
|
54
|
+
soon.get("/user/:id", { params: { id: 123 } });
|
|
55
|
+
|
|
56
|
+
/** POST */
|
|
57
|
+
soon.post("/login", { body: { username: "admin", password: "123456" } });
|
|
58
|
+
|
|
59
|
+
/**Define API */
|
|
60
|
+
export const login = soon
|
|
61
|
+
.API("/user/login")
|
|
62
|
+
.POST<{ username: string; password: string }, { token: string }>();
|
|
63
|
+
|
|
64
|
+
login({ username: "admin", password: "123" }).then((res) => {
|
|
65
|
+
localStorage.setItem("token", res.token);
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Features
|
|
70
|
+
|
|
71
|
+
##### Shortcut
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
soon.get(url, options);
|
|
75
|
+
soon.post(url, options);
|
|
76
|
+
soon.put(url, options);
|
|
77
|
+
soon.patch(url, options);
|
|
78
|
+
soon.delete(url, options);
|
|
79
|
+
soon.head(url, options);
|
|
80
|
+
soon.options(url, options);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
##### Restful Url Params
|
|
84
|
+
|
|
85
|
+
url like /:key , will handle the key
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
soon.get("/api/user/:id", { params: { id: 1 } });
|
|
89
|
+
// api/user/1
|
|
90
|
+
soon.get("/api/:job/:year", { params: { job: "engineer", year: 5 } });
|
|
91
|
+
//api/engineer/5
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
##### Timeout
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
//** the request level timeout, will override the instance level timeout */
|
|
98
|
+
soon.get(url, { timeout: 1000 * 20 });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
##### Share pending request
|
|
102
|
+
|
|
103
|
+
If a request is made again before the first completes, will reuse the first request instead of making a new request.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
soon.get(url, { share: true });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
##### Cache response
|
|
110
|
+
|
|
111
|
+
A cached response will be returned if the request is made again within the specified time.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
soon.get(url, { staleTime: 1000 * 60 * 5 });
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
##### Request race
|
|
118
|
+
|
|
119
|
+
If a second request is made before the first completes, abort the first to avoid race conditions from out-of-order responses.
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
type User = { name: string; job: string };
|
|
123
|
+
const api = soon.GET("/api/users").Query<{ page: number }>().Send<User[]>();
|
|
124
|
+
export default function App() {
|
|
125
|
+
const refAbort = useRef<AbortController[]>([]);
|
|
126
|
+
const [list, setList] = useState<User[]>([]);
|
|
127
|
+
const [page, setPage] = useState(1);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
api({ page }).then(setList).catch(console.log);
|
|
130
|
+
}, [page]);
|
|
131
|
+
return (
|
|
132
|
+
<div>
|
|
133
|
+
<button onClick={() => setPage((pre) => pre + 1)}>next</button>
|
|
134
|
+
<div>
|
|
135
|
+
{list.map((item) => (
|
|
136
|
+
<div key={item.name}>{item.name}</div>
|
|
137
|
+
))}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
##### Rapid Define APIs
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
//can be GET POST PATCH PUT DELETE
|
|
148
|
+
|
|
149
|
+
//define an api
|
|
150
|
+
export const getUserInfo = soon.GET("/user/:id").Send();
|
|
151
|
+
//then use in any where
|
|
152
|
+
getUserInfo({ id: 2 }).then((res) => console.log(res));
|
|
153
|
+
|
|
154
|
+
//with typescript,
|
|
155
|
+
export const login = soon
|
|
156
|
+
.POST("/user/login")
|
|
157
|
+
.Body<{ username: string; password: string }>()
|
|
158
|
+
.Send<{ token: string }>();
|
|
159
|
+
//the develop tools will have type tips for request and response
|
|
160
|
+
login({ username: "admin", password: "123" }).then((res) => {
|
|
161
|
+
localStorage.setItem("token", res.token);
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### API
|
|
166
|
+
|
|
167
|
+
#### SoonOptions
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
// function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
|
|
171
|
+
// RequestInit is fetch's init options
|
|
172
|
+
type SoonOptions = Omit<RequestInit, "body"> & {
|
|
173
|
+
query?:
|
|
174
|
+
| Record<
|
|
175
|
+
string,
|
|
176
|
+
| string
|
|
177
|
+
| number
|
|
178
|
+
| boolean
|
|
179
|
+
| null
|
|
180
|
+
| undefined
|
|
181
|
+
| (string | number | boolean | null | undefined)[]
|
|
182
|
+
>
|
|
183
|
+
| URLSearchParams;
|
|
184
|
+
params?: Record<string, string | number>;
|
|
185
|
+
timeout?: number;
|
|
186
|
+
body?: RequestInit["body"] | object;
|
|
187
|
+
aborts?: AbortController[];
|
|
188
|
+
share?: boolean;
|
|
189
|
+
staleTime?: number;
|
|
190
|
+
};
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
[English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
|
|
194
|
+
|
|
195
|
+
<!-- omit in toc -->
|
|
196
|
+
|
|
197
|
+
#### soon-fetch
|
|
198
|
+
|
|
199
|
+
**极轻量的请求库,不到 3K**
|
|
200
|
+
|
|
201
|
+
> - 🌐 自动解析 rest Url 的参数
|
|
202
|
+
> - ⭐ 快捷定义请求 api
|
|
203
|
+
> - ⌛ 超时断开
|
|
204
|
+
> - 🔤 自动处理 JSON
|
|
205
|
+
> - 📏 不到 **3K** , zip 后会更小
|
|
206
|
+
> - 💡 用 typescript 有智能类型提醒
|
|
207
|
+
|
|
208
|
+
- [示例](#示例)
|
|
209
|
+
|
|
210
|
+
- [特别功能](#特别功能)
|
|
211
|
+
|
|
212
|
+
- [快捷方法](#快捷方法)
|
|
213
|
+
- [Restful Url 参数自动处理](#restful-url-参数自动处理)
|
|
214
|
+
- [超时](#超时)
|
|
215
|
+
- [快速定义 API](#快速定义-api)
|
|
216
|
+
|
|
217
|
+
- [API](#api-1)
|
|
218
|
+
|
|
219
|
+
### 示例
|
|
220
|
+
|
|
221
|
+
> [github: soon-admin-vue3 ](https://github.com/leafio/soon-admin-vue3)
|
|
222
|
+
> [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const request = <T>(url: string, options?: SoonOptions): Promise<T> => {
|
|
226
|
+
const [_url, _options] = parseUrlOptions({
|
|
227
|
+
url,
|
|
228
|
+
options,
|
|
229
|
+
baseURL: "/api",
|
|
230
|
+
baseOptions: {
|
|
231
|
+
timeout: 20 * 1000,
|
|
232
|
+
headers: { Authorization: localStorage.getItem("token") ?? "" },
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return fetch(_url, _options).then((res) => res.json());
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const soon = createSoon(request);
|
|
240
|
+
|
|
241
|
+
/** GET */
|
|
242
|
+
soon.get("/user?id=123");
|
|
243
|
+
soon.get("/user", { query: { id: 123 } });
|
|
244
|
+
soon.get("/user/:id", { params: { id: 123 } });
|
|
245
|
+
|
|
246
|
+
/** POST */
|
|
247
|
+
soon.post("/login", { body: { username: "admin", password: "123456" } });
|
|
248
|
+
|
|
249
|
+
/**定义 API */
|
|
250
|
+
export const login = soon
|
|
251
|
+
.API("/user/login")
|
|
252
|
+
.POST<{ username: string; password: string }, { token: string }>();
|
|
253
|
+
|
|
254
|
+
login({ username: "admin", password: "123" }).then((res) => {
|
|
255
|
+
localStorage.setItem("token", res.token);
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 特别功能
|
|
260
|
+
|
|
261
|
+
##### 快捷方法
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
soon.get(url, options);
|
|
265
|
+
soon.post(url, options);
|
|
266
|
+
soon.put(url, options);
|
|
267
|
+
soon.patch(url, options);
|
|
268
|
+
soon.delete(url, options);
|
|
269
|
+
soon.head(url, options);
|
|
270
|
+
soon.options(url, options);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
###### Restful Url 参数自动处理
|
|
274
|
+
|
|
275
|
+
url 包含 /:key 会解析匹配 key
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
soon.get("/api/user/:id", { params: { id: 1 } });
|
|
279
|
+
// api/user/1
|
|
280
|
+
soon.get("/api/:job/:year", { params: { job: "engineer", year: 5 } });
|
|
281
|
+
//api/engineer/5
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
##### 超时
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
//** 请求级超时, 会覆盖实例级超时 */
|
|
288
|
+
soon.get(url, { timeout: 1000 * 20 });
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
##### 共享未完成的请求
|
|
292
|
+
|
|
293
|
+
如果在第一个请求完成之前再次发起相同的请求,则会复用第一个请求,而不是发起新的请求。
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
soon.get(url, { share: true });
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
##### 缓存响应
|
|
300
|
+
|
|
301
|
+
如果在指定时间内再次发起相同的请求,则会返回缓存的响应。
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
soon.get(url, { staleTime: 1000 * 60 * 5 });
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
##### 请求竞争
|
|
308
|
+
|
|
309
|
+
如果在第一个请求完成之前发起第二个请求,则会中止第一个请求,以避免因响应顺序错乱导致的问题。
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
type User = { name: string; job: string };
|
|
313
|
+
const api = soon.GET("/api/users").Query<{ page: number }>().Send<User[]>();
|
|
314
|
+
export default function App() {
|
|
315
|
+
const refAbort = useRef<AbortController[]>([]);
|
|
316
|
+
const [list, setList] = useState<User[]>([]);
|
|
317
|
+
const [page, setPage] = useState(1);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
api({ page }, { aborts: refAbort.current })
|
|
320
|
+
.then(setList)
|
|
321
|
+
.catch(console.log);
|
|
322
|
+
}, [page]);
|
|
323
|
+
return (
|
|
324
|
+
<div>
|
|
325
|
+
<button onClick={() => setPage((pre) => pre + 1)}>next</button>
|
|
326
|
+
<div>
|
|
327
|
+
{list.map((item) => (
|
|
328
|
+
<div key={item.name}>{item.name}</div>
|
|
329
|
+
))}
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
##### 快速定义 API
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
//可以是 GET POST PATCH PUT DELETE
|
|
340
|
+
//GET 请求数据传递至query,其他方法请求数据传递至body
|
|
341
|
+
soon.API(url:string).POST<RequestType,ResponseType>()
|
|
342
|
+
|
|
343
|
+
//定义一个api
|
|
344
|
+
export const getUserInfo=soon.API('/user/:id').GET()
|
|
345
|
+
//使用
|
|
346
|
+
getUserInfo({id:2}).then(res=>console.log(res))
|
|
347
|
+
|
|
348
|
+
//用typescript,
|
|
349
|
+
export const login=soon.API('/user/login')
|
|
350
|
+
.POST<{username:string,password:string},{token:string}>()
|
|
351
|
+
//开发工具会有请求和响应的智能提醒
|
|
352
|
+
login({username:'admin',password:'123'}).then(res=>{
|
|
353
|
+
localStorage.setItem('token', res.token);
|
|
354
|
+
})
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### API
|
|
358
|
+
|
|
359
|
+
#### SoonOptions
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
// function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
|
|
363
|
+
// RequestInit 为原生 fetch 的 init 选项
|
|
364
|
+
type SoonOptions = Omit<RequestInit, "body"> & {
|
|
365
|
+
query?:
|
|
366
|
+
| Record<
|
|
367
|
+
string,
|
|
368
|
+
| string
|
|
369
|
+
| number
|
|
370
|
+
| boolean
|
|
371
|
+
| null
|
|
372
|
+
| undefined
|
|
373
|
+
| (string | number | boolean | null | undefined)[]
|
|
374
|
+
>
|
|
375
|
+
| URLSearchParams;
|
|
376
|
+
params?: Record<string, string | number>;
|
|
377
|
+
timeout?: number;
|
|
378
|
+
body?: RequestInit["body"] | object;
|
|
379
|
+
};
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
[English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
|
|
383
|
+
|
|
384
|
+
<!-- omit in toc -->
|
|
385
|
+
|
|
386
|
+
##### 安装 Installation
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
npm install soon-fetch
|
|
390
|
+
```
|
package/dist/index.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e=e=>{const t=[],r=e.match(/:([^:/\d]+)\/?/g);return r&&r.forEach(e=>{t.push(e.replace(/\//g,"").replace(/:/g,""))}),t},t=(e="")=>e.endsWith("/")?e.slice(0,-1):e,r=(e="")=>e.startsWith("/")?e:"/"+e,n=(e="")=>e.startsWith("http"),o=e=>{if(!e)return[];if(e instanceof URLSearchParams||"string"==typeof e||Array.isArray(e))return Array.from(new URLSearchParams(e).entries());const t=[];return Object.keys(e).forEach(r=>{const n=e[r];(Array.isArray(n)?n:[n]).forEach(e=>{t.push([r,e??""])})}),t},s=(s,a)=>{const{query:i,params:c,baseURL:f}=a;let u=s.trim();e(s).forEach(e=>{c&&(u=u.replace(":"+e,""+c[e]))});const[p,h]=u.split("?"),
|
|
1
|
+
"use strict";const e=e=>{const t=[],r=e.match(/:([^:/\d]+)\/?/g);return r&&r.forEach((e=>{t.push(e.replace(/\//g,"").replace(/:/g,""))})),t},t=(e="")=>e.endsWith("/")?e.slice(0,-1):e,r=(e="")=>e.startsWith("/")?e:"/"+e,n=(e="")=>e.startsWith("http"),o=e=>{if(!e)return[];if(e instanceof URLSearchParams||"string"==typeof e||Array.isArray(e))return Array.from(new URLSearchParams(e).entries());const t=[];return Object.keys(e).forEach((r=>{const n=e[r];(Array.isArray(n)?n:[n]).forEach((e=>{t.push([r,e??""])}))})),t},s=(s,a)=>{const{query:i,params:c,baseURL:f}=a;let u=s.trim();e(s).forEach((e=>{c&&(u=u.replace(":"+e,""+c[e]))}));const[p,h]=u.split("?"),l=new URLSearchParams([...o(h),...o(i)]);let y=((e,o)=>{if(n(e))return e;const s=n(o)?o:r(o);return t(s)+t(r(e))})(p,f);return l.size&&(y=y+"?"+l),y},a=(...e)=>{const t=new Headers;return e.forEach((e=>{e&&new Headers(e).forEach(((e,r)=>{t.set(r,e)}))})),t};function i(e,t){const r=(e??[]).filter((e=>!!e));return t&&r.push(AbortSignal.timeout(t)),r.length?AbortSignal.any(r):void 0}function c(e){return!(!e||"object"!=typeof e||(e instanceof Blob||e instanceof ArrayBuffer||e instanceof FormData||e instanceof File||e instanceof DataView||e instanceof URLSearchParams||e instanceof ReadableStream||(t=e,t instanceof Int8Array||t instanceof Uint8Array||t instanceof Uint8ClampedArray||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array||t instanceof Float64Array||t instanceof BigInt64Array||t instanceof BigUint64Array)));var t}const f=["get","post","put","delete","patch"];function u(e,t){const r={};return e.forEach((e=>{r[e]=t(e)})),r}function p(t){const r=(r,n,o)=>{const s=!!e(r).length;return(...e)=>{const a=[...e],{hasBody:i,hasQuery:c}=o||{},f=s?a.shift():void 0,u=c?a.shift():void 0,p=i?a.shift():void 0,h=a.shift();return t(r,n,f,u,p,h,o?.options)}},n={};return f.forEach((e=>{const t=e.toUpperCase();n[t]=t=>({Send:n=>r(t,e,{options:n}),Body:()=>({Send:n=>r(t,e,{hasBody:!0,options:n})}),Query:()=>({Send:n=>r(t,e,{hasQuery:!0,options:n}),Body:()=>({Send:n=>r(t,e,{hasBody:!0,hasQuery:!0,options:n})})})})})),n}function h(e,t){t&&(t.pop()?.abort(),t.push(e))}function l(e){if(Array.isArray(e))return e.map(l);if("object"==typeof e&&null!==e){const t={};return Object.keys(e).sort().forEach((r=>{t[r]=l(e[r])})),t}return e}function y(e){const{url:t,headers:r,method:n,body:o,query:s,params:a}=e,i=l(Object.fromEntries(r?.entries()??[]));return(n??"get").toLowerCase()+t+JSON.stringify(l(s)??"")+JSON.stringify(l(a)??"")+JSON.stringify(i)+("object"==typeof o&&null!=o?JSON.stringify(l(o)):o)}function d(){const e={},t=[];setInterval((()=>{const r=Date.now();for(let n=t.length-1;n>=0;n--)t[n].expiredTime<r&&(delete e[t[n].key],t.splice(n,1))}),6e4);const r=e=>e instanceof Response?e.clone():"function"==typeof e||e instanceof Promise?e:structuredClone(e);function n(r){delete e[r];for(let e=t.length-1;e>=0;e--)if(t[e].key===r){t.splice(e,1);break}}return{get:function(t){const o=e[t];if(void 0!==o)return o.expiredTime>Date.now()?r(o.data):void n(t)},set:function(n,o,s){e[n]={data:r(o),expiredTime:s},t.push({key:n,expiredTime:s})},remove:n}}function g(){const e={},t=t=>e[t]=void 0;return{get:t=>e[t],set:(r,n)=>{e[r]=n,n.finally((()=>t(r)))}}}exports.createCache=d,exports.createShare=g,exports.createShortApi=p,exports.createShortMethods=u,exports.createSilentRefresh=function(e){let t=[],r=!1;return(n,o)=>{t.push({success:n,fail:o}),r||(r=!0,e().then((()=>{t.forEach((e=>e.success()))})).catch((e=>{t.forEach((e=>e.fail()))})).finally((()=>{r=!1,t=[]})))}},exports.createSoon=function(e){const t=d(),r=g(),n=(n,o)=>new Promise(((s,a)=>{const i=new AbortController,c=e({abortController:i});h(i,o?.aborts);const f=y({url:n,...o,headers:new Headers(o?.headers)});if(o?.share){const e=r.get(f);if(e)return s(e)}if(o?.staleTime){const e=t.get(f);if(void 0!==e)return s(e)}const u=c(n,o);o?.share&&r.set(f,u),u.then((e=>{s(e),o?.staleTime&&t.set(f,e,(new Date).getTime()+o.staleTime)})).catch((e=>a(e)))})),o=p(((e,t,r,o,s,a,i)=>n(e,{...i,...a,method:t,params:r,query:o,body:s}))),s=u([...f,"head","options"],(e=>(t,r)=>n(t,{...r,method:e})));return{request:n,...o,...s}},exports.deepSort=l,exports.genRequestKey=y,exports.isBodyJson=c,exports.mergeHeaders=a,exports.mergeSignals=i,exports.mergeUrl=s,exports.parseUrlOptions=function(e){const{url:t,options:r,baseURL:n,baseOptions:o}=e,f=function(e,t){const r={...e,...t},n=a(e?.headers,t?.headers);return r.headers=n,r.signal=i([e?.signal,t?.signal],r.timeout),r}(o,r),u=s(t,{...f,baseURL:n}),p=f?.body,h=c(p);return f.body=h?JSON.stringify(p):p,h&&f.headers.append("Content-Type","application/json"),f.signal=i([f.signal]),[u,f,h]},exports.raceAbort=h;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,12 +3,15 @@ type SoonOptions = Omit<RequestInit, "body"> & {
|
|
|
3
3
|
params?: Record<string, string | number>;
|
|
4
4
|
timeout?: number;
|
|
5
5
|
body?: RequestInit["body"] | object;
|
|
6
|
+
aborts?: AbortController[];
|
|
7
|
+
share?: boolean;
|
|
8
|
+
staleTime?: number;
|
|
6
9
|
};
|
|
7
10
|
type GetUrlKey<Url> = Url extends `${string}/:${infer Key}/${infer Right}` ? `${Key}` | GetUrlKey<`/${Right}`> : Url extends `${string}/:${infer Key}` ? `${Key}` : never;
|
|
8
11
|
type OptionParams<Args> = NonNullable<Args> extends never ? [] : keyof NonNullable<Args> extends never ? [] : Exclude<Args, NonNullable<Args>> extends never ? [params: Args] : [params?: Args];
|
|
9
12
|
type OptionQuery<Args> = NonNullable<Args> extends never ? [] : keyof NonNullable<Args> extends never ? [] : Exclude<Args, NonNullable<Args>> extends never ? Partial<Args> extends Args ? [query?: Args] : [query: Args] : [query?: Args];
|
|
10
13
|
type OptionBody<Args> = NonNullable<Args> extends never ? [] : Exclude<Args, NonNullable<Args>> extends never ? [body: Args] : [body?: Args];
|
|
11
|
-
type Tuple2Union<T> = T extends [infer T1, infer T2, ...infer R] ? T1 | T2 | Tuple2Union<R> : T extends [infer T_Only] ? T_Only : never;
|
|
14
|
+
type Tuple2Union<T> = T extends readonly [infer T1, infer T2, ...infer R] ? T1 | T2 | Tuple2Union<R> : T extends [infer T_Only] ? T_Only : never;
|
|
12
15
|
|
|
13
16
|
declare const mergeUrl: (url: string, config: {
|
|
14
17
|
query?: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams;
|
|
@@ -26,130 +29,113 @@ declare function parseUrlOptions<Options extends SoonOptions>(urlOptions: {
|
|
|
26
29
|
}): readonly [string, Options & {
|
|
27
30
|
headers: Headers;
|
|
28
31
|
body?: BodyInit | null;
|
|
29
|
-
}];
|
|
30
|
-
declare function
|
|
31
|
-
|
|
32
|
-
get: RequestFn;
|
|
33
|
-
post: RequestFn;
|
|
34
|
-
put: RequestFn;
|
|
35
|
-
delete: RequestFn;
|
|
36
|
-
patch: RequestFn;
|
|
37
|
-
head: RequestFn;
|
|
32
|
+
}, boolean];
|
|
33
|
+
declare function createShortMethods<Methods extends readonly string[], Wrapper extends (method: string) => <T>(...args: any) => Promise<T>>(methods: Methods, wrapper: Wrapper): Record<Tuple2Union<Methods>, ReturnType<typeof wrapper>>;
|
|
34
|
+
declare function createShortApi<Wrapper extends <T>(url: string, method: string, params: Record<string, string | number> | undefined, query: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams | undefined, body: object | undefined, options?: any, defineOptions?: any) => Promise<T>>(wrapper: Wrapper): {
|
|
38
35
|
GET: <Url extends string>(url: Url) => {
|
|
39
|
-
Send: <Res>(options?:
|
|
36
|
+
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
37
|
+
Query: <Query>() => unknown extends Query ? never : {
|
|
38
|
+
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
} & Record<"POST" | "PATCH" | "DELETE" | "PUT", <Url extends string>(url: Url) => {
|
|
42
|
+
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
43
|
+
Body: <Body>() => unknown extends Body ? never : {
|
|
44
|
+
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Body>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
45
|
+
};
|
|
46
|
+
Query: <Query>() => unknown extends Query ? never : {
|
|
47
|
+
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
48
|
+
Body: <Body>() => unknown extends Body ? never : {
|
|
49
|
+
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, ...OptionBody<Body>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
type NoData<T> = Omit<T, "method" | "body" | "params" | "query">;
|
|
54
|
+
declare function raceAbort(abortController: AbortController, controllers?: AbortController[]): void;
|
|
55
|
+
declare function deepSort(obj: unknown): unknown;
|
|
56
|
+
declare function genRequestKey(req: {
|
|
57
|
+
url: string;
|
|
58
|
+
method?: string;
|
|
59
|
+
headers?: Headers;
|
|
60
|
+
body?: RequestInit["body"] | object;
|
|
61
|
+
query?: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams;
|
|
62
|
+
params?: Record<string, string | number>;
|
|
63
|
+
}): string;
|
|
64
|
+
declare function createCache(): {
|
|
65
|
+
get: (key: string) => unknown;
|
|
66
|
+
set: (key: string, res: Response | unknown, expiredTime: number) => void;
|
|
67
|
+
remove: (key: string) => void;
|
|
68
|
+
};
|
|
69
|
+
declare function createShare(): {
|
|
70
|
+
get: (key: string) => Promise<any> | undefined;
|
|
71
|
+
set: (key: string, value: Promise<any>) => void;
|
|
72
|
+
};
|
|
73
|
+
declare function createSilentRefresh(refresh_token_fn: () => Promise<void>): (success: () => void, fail: () => void) => void;
|
|
74
|
+
declare function createSoon<Options extends SoonOptions>(wrapper: (instance: {
|
|
75
|
+
abortController: AbortController;
|
|
76
|
+
}) => <T>(url: string, options?: Options) => Promise<T>): {
|
|
77
|
+
options: <T>(url: string, options?: Options) => Promise<T>;
|
|
78
|
+
get: <T>(url: string, options?: Options) => Promise<T>;
|
|
79
|
+
post: <T>(url: string, options?: Options) => Promise<T>;
|
|
80
|
+
put: <T>(url: string, options?: Options) => Promise<T>;
|
|
81
|
+
delete: <T>(url: string, options?: Options) => Promise<T>;
|
|
82
|
+
patch: <T>(url: string, options?: Options) => Promise<T>;
|
|
83
|
+
head: <T>(url: string, options?: Options) => Promise<T>;
|
|
84
|
+
GET: <Url extends string>(url: Url) => {
|
|
85
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
40
86
|
Query: <Query>() => unknown extends Query ? never : {
|
|
41
|
-
Send: <Res>(options?:
|
|
87
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
42
88
|
};
|
|
43
89
|
};
|
|
44
90
|
POST: <Url extends string>(url: Url) => {
|
|
45
|
-
Send: <Res>(options?:
|
|
91
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
46
92
|
Body: <Body>() => unknown extends Body ? never : {
|
|
47
|
-
Send: <Res>(options?:
|
|
93
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
48
94
|
};
|
|
49
95
|
Query: <Query>() => unknown extends Query ? never : {
|
|
50
|
-
Send: <Res>(options?:
|
|
96
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
51
97
|
Body: <Body>() => unknown extends Body ? never : {
|
|
52
|
-
Send: <Res>(options?:
|
|
98
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
53
99
|
};
|
|
54
100
|
};
|
|
55
101
|
};
|
|
56
102
|
PATCH: <Url extends string>(url: Url) => {
|
|
57
|
-
Send: <Res>(options?:
|
|
103
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
58
104
|
Body: <Body>() => unknown extends Body ? never : {
|
|
59
|
-
Send: <Res>(options?:
|
|
105
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
60
106
|
};
|
|
61
107
|
Query: <Query>() => unknown extends Query ? never : {
|
|
62
|
-
Send: <Res>(options?:
|
|
108
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
63
109
|
Body: <Body>() => unknown extends Body ? never : {
|
|
64
|
-
Send: <Res>(options?:
|
|
110
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
65
111
|
};
|
|
66
112
|
};
|
|
67
113
|
};
|
|
68
114
|
DELETE: <Url extends string>(url: Url) => {
|
|
69
|
-
Send: <Res>(options?:
|
|
115
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
70
116
|
Body: <Body>() => unknown extends Body ? never : {
|
|
71
|
-
Send: <Res>(options?:
|
|
117
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
72
118
|
};
|
|
73
119
|
Query: <Query>() => unknown extends Query ? never : {
|
|
74
|
-
Send: <Res>(options?:
|
|
120
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
75
121
|
Body: <Body>() => unknown extends Body ? never : {
|
|
76
|
-
Send: <Res>(options?:
|
|
122
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
77
123
|
};
|
|
78
124
|
};
|
|
79
125
|
};
|
|
80
126
|
PUT: <Url extends string>(url: Url) => {
|
|
81
|
-
Send: <Res>(options?:
|
|
127
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
82
128
|
Body: <Body>() => unknown extends Body ? never : {
|
|
83
|
-
Send: <Res>(options?:
|
|
129
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
84
130
|
};
|
|
85
131
|
Query: <Query>() => unknown extends Query ? never : {
|
|
86
|
-
Send: <Res>(options?:
|
|
132
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
87
133
|
Body: <Body>() => unknown extends Body ? never : {
|
|
88
|
-
Send: <Res>(options?:
|
|
134
|
+
Send: <Res>(options?: NoData<Options> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, ...OptionBody<Body>, options?: NoData<Options> | undefined]) => Promise<Res>;
|
|
89
135
|
};
|
|
90
136
|
};
|
|
91
137
|
};
|
|
92
|
-
request:
|
|
93
|
-
};
|
|
94
|
-
declare function createShortMethods<Methods extends string[], RequestFun extends <T>(url: string, options?: {
|
|
95
|
-
method?: string;
|
|
96
|
-
}) => Promise<any>>(methods: Methods, requestFun: RequestFun): Record<Tuple2Union<Methods>, typeof requestFun>;
|
|
97
|
-
declare function createShortApiGeneral<Wrapper extends <T>(url: string, method: string, params: Record<string, string | number> | undefined, query: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams | undefined, body: object | undefined, options?: any, defineOptions?: any) => Promise<T>>(wrapper: Wrapper): {
|
|
98
|
-
GET: <Url extends string>(url: Url) => {
|
|
99
|
-
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
100
|
-
Query: <Query>() => unknown extends Query ? never : {
|
|
101
|
-
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
} & Record<"POST" | "PATCH" | "DELETE" | "PUT", <Url extends string>(url: Url) => {
|
|
105
|
-
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
106
|
-
Body: <Body>() => unknown extends Body ? never : {
|
|
107
|
-
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Body>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
108
|
-
};
|
|
109
|
-
Query: <Query>() => unknown extends Query ? never : {
|
|
110
|
-
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
111
|
-
Body: <Body>() => unknown extends Body ? never : {
|
|
112
|
-
Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, ...OptionBody<Body>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
|
|
113
|
-
};
|
|
114
|
-
};
|
|
115
|
-
}>;
|
|
116
|
-
declare function createShortAPI<requestFn extends <T>(url: string, options?: SoonOptions) => Promise<T>>(requestFn: requestFn): {
|
|
117
|
-
GET: <Url extends string>(url: Url) => {
|
|
118
|
-
Send: <Res>(options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined]) => Promise<Res>;
|
|
119
|
-
Query: <Query>() => unknown extends Query ? never : {
|
|
120
|
-
Send: <Res>(options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined]) => Promise<Res>;
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
} & Record<"POST" | "PATCH" | "DELETE" | "PUT", <Url extends string>(url: Url) => {
|
|
124
|
-
Send: <Res>(options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined]) => Promise<Res>;
|
|
125
|
-
Body: <Body>() => unknown extends Body ? never : {
|
|
126
|
-
Send: <Res>(options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Body>, options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined]) => Promise<Res>;
|
|
127
|
-
};
|
|
128
|
-
Query: <Query>() => unknown extends Query ? never : {
|
|
129
|
-
Send: <Res>(options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined]) => Promise<Res>;
|
|
130
|
-
Body: <Body>() => unknown extends Body ? never : {
|
|
131
|
-
Send: <Res>(options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Query>, ...OptionBody<Body>, options?: Omit<Exclude<Parameters<NoInfer<requestFn>>[1], undefined>, "body" | "method" | "params" | "query"> | undefined]) => Promise<Res>;
|
|
132
|
-
};
|
|
133
|
-
};
|
|
134
|
-
}>;
|
|
135
|
-
declare function raceAbort(abortController: AbortController, controllers?: AbortController[]): void;
|
|
136
|
-
declare function deepSort(obj: unknown): unknown;
|
|
137
|
-
declare function genRequestKey(req: {
|
|
138
|
-
url: string;
|
|
139
|
-
headers?: Headers;
|
|
140
|
-
method?: string;
|
|
141
|
-
body?: any;
|
|
142
|
-
}): string;
|
|
143
|
-
declare function createCache(): {
|
|
144
|
-
get: (key: string) => unknown;
|
|
145
|
-
set: (key: string, res: unknown, expiredTime: number) => void;
|
|
146
|
-
remove: (key: string) => void;
|
|
138
|
+
request: <T>(url: string, options: Options) => Promise<T>;
|
|
147
139
|
};
|
|
148
|
-
declare function createShare(): {
|
|
149
|
-
get: (key: string) => Promise<any> | undefined;
|
|
150
|
-
set: (key: string, value: Promise<any>) => void;
|
|
151
|
-
};
|
|
152
|
-
declare function createSilentRefresh(refresh_token_fn: () => Promise<void>): (success: () => void, fail: () => void) => void;
|
|
153
140
|
|
|
154
|
-
export { createCache, createShare,
|
|
155
|
-
export type { SoonOptions };
|
|
141
|
+
export { type SoonOptions, createCache, createShare, createShortApi, createShortMethods, createSilentRefresh, createSoon, deepSort, genRequestKey, isBodyJson, mergeHeaders, mergeSignals, mergeUrl, parseUrlOptions, raceAbort };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const e=e=>{const t=[],n=e.match(/:([^:/\d]+)\/?/g);return n&&n.forEach((e=>{t.push(e.replace(/\//g,"").replace(/:/g,""))})),t},t=(e="")=>e.endsWith("/")?e.slice(0,-1):e,n=(e="")=>e.startsWith("/")?e:"/"+e,r=(e="")=>e.startsWith("http"),o=e=>{if(!e)return[];if(e instanceof URLSearchParams||"string"==typeof e||Array.isArray(e))return Array.from(new URLSearchParams(e).entries());const t=[];return Object.keys(e).forEach((n=>{const r=e[n];(Array.isArray(r)?r:[r]).forEach((e=>{t.push([n,e??""])}))})),t},s=(s,a)=>{const{query:i,params:c,baseURL:f}=a;let u=s.trim();e(s).forEach((e=>{c&&(u=u.replace(":"+e,""+c[e]))}));const[h,l]=u.split("?"),y=new URLSearchParams([...o(l),...o(i)]);let d=((e,o)=>{if(r(e))return e;const s=r(o)?o:n(o);return t(s)+t(n(e))})(h,f);return y.size&&(d=d+"?"+y),d},a=(...e)=>{const t=new Headers;return e.forEach((e=>{e&&new Headers(e).forEach(((e,n)=>{t.set(n,e)}))})),t};function i(e,t){const n=(e??[]).filter((e=>!!e));return t&&n.push(AbortSignal.timeout(t)),n.length?AbortSignal.any(n):void 0}function c(e){return!(!e||"object"!=typeof e||(e instanceof Blob||e instanceof ArrayBuffer||e instanceof FormData||e instanceof File||e instanceof DataView||e instanceof URLSearchParams||e instanceof ReadableStream||(t=e,t instanceof Int8Array||t instanceof Uint8Array||t instanceof Uint8ClampedArray||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array||t instanceof Float64Array||t instanceof BigInt64Array||t instanceof BigUint64Array)));var t}function f(e){const{url:t,options:n,baseURL:r,baseOptions:o}=e,f=function(e,t){const n={...e,...t},r=a(e?.headers,t?.headers);return n.headers=r,n.signal=i([e?.signal,t?.signal],n.timeout),n}(o,n),u=s(t,{...f,baseURL:r}),h=f?.body,l=c(h);return f.body=l?JSON.stringify(h):h,l&&f.headers.append("Content-Type","application/json"),f.signal=i([f.signal]),[u,f,l]}const u=["get","post","put","delete","patch"];function h(e,t){const n={};return e.forEach((e=>{n[e]=t(e)})),n}function l(t){const n=(n,r,o)=>{const s=!!e(n).length;return(...e)=>{const a=[...e],{hasBody:i,hasQuery:c}=o||{},f=s?a.shift():void 0,u=c?a.shift():void 0,h=i?a.shift():void 0,l=a.shift();return t(n,r,f,u,h,l,o?.options)}},r={};return u.forEach((e=>{const t=e.toUpperCase();r[t]=t=>({Send:r=>n(t,e,{options:r}),Body:()=>({Send:r=>n(t,e,{hasBody:!0,options:r})}),Query:()=>({Send:r=>n(t,e,{hasQuery:!0,options:r}),Body:()=>({Send:r=>n(t,e,{hasBody:!0,hasQuery:!0,options:r})})})})})),r}function y(e,t){t&&(t.pop()?.abort(),t.push(e))}function d(e){if(Array.isArray(e))return e.map(d);if("object"==typeof e&&null!==e){const t={};return Object.keys(e).sort().forEach((n=>{t[n]=d(e[n])})),t}return e}function p(e){const{url:t,headers:n,method:r,body:o,query:s,params:a}=e,i=d(Object.fromEntries(n?.entries()??[]));return(r??"get").toLowerCase()+t+JSON.stringify(d(s)??"")+JSON.stringify(d(a)??"")+JSON.stringify(i)+("object"==typeof o&&null!=o?JSON.stringify(d(o)):o)}function m(){const e={},t=[];setInterval((()=>{const n=Date.now();for(let r=t.length-1;r>=0;r--)t[r].expiredTime<n&&(delete e[t[r].key],t.splice(r,1))}),6e4);const n=e=>e instanceof Response?e.clone():"function"==typeof e||e instanceof Promise?e:structuredClone(e);function r(n){delete e[n];for(let e=t.length-1;e>=0;e--)if(t[e].key===n){t.splice(e,1);break}}return{get:function(t){const o=e[t];if(void 0!==o)return o.expiredTime>Date.now()?n(o.data):void r(t)},set:function(r,o,s){e[r]={data:n(o),expiredTime:s},t.push({key:r,expiredTime:s})},remove:r}}function g(){const e={},t=t=>e[t]=void 0;return{get:t=>e[t],set:(n,r)=>{e[n]=r,r.finally((()=>t(n)))}}}function b(e){let t=[],n=!1;return(r,o)=>{t.push({success:r,fail:o}),n||(n=!0,e().then((()=>{t.forEach((e=>e.success()))})).catch((e=>{t.forEach((e=>e.fail()))})).finally((()=>{n=!1,t=[]})))}}function A(e){const t=m(),n=g(),r=(r,o)=>new Promise(((s,a)=>{const i=new AbortController,c=e({abortController:i});y(i,o?.aborts);const f=p({url:r,...o,headers:new Headers(o?.headers)});if(o?.share){const e=n.get(f);if(e)return s(e)}if(o?.staleTime){const e=t.get(f);if(void 0!==e)return s(e)}const u=c(r,o);o?.share&&n.set(f,u),u.then((e=>{s(e),o?.staleTime&&t.set(f,e,(new Date).getTime()+o.staleTime)})).catch((e=>a(e)))})),o=l(((e,t,n,o,s,a,i)=>r(e,{...i,...a,method:t,params:n,query:o,body:s}))),s=h([...u,"head","options"],(e=>(t,n)=>r(t,{...n,method:e})));return{request:r,...o,...s}}export{m as createCache,g as createShare,l as createShortApi,h as createShortMethods,b as createSilentRefresh,A as createSoon,d as deepSort,p as genRequestKey,c as isBodyJson,a as mergeHeaders,i as mergeSignals,s as mergeUrl,f as parseUrlOptions,y as raceAbort};
|
package/package.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "soon-fetch",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
4
|
-
"description": "a 3Kb request lib alternative to axios",
|
|
5
|
-
"homepage": "https://github.com/leafio/soon-fetch",
|
|
6
|
-
"main": "./dist/index.cjs.js",
|
|
7
|
-
"module": "/dist/index.js",
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"files": [
|
|
10
|
-
"./dist/*"
|
|
11
|
-
],
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build:types": "tsc",
|
|
14
|
-
"build": "rollup --config",
|
|
15
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
"request",
|
|
19
|
-
"fetch",
|
|
20
|
-
"lightweight",
|
|
21
|
-
"timeout",
|
|
22
|
-
"http",
|
|
23
|
-
"axios"
|
|
24
|
-
],
|
|
25
|
-
"author": "",
|
|
26
|
-
"license": "MIT",
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@babel/core": "^7.25.2",
|
|
29
|
-
"@babel/preset-env": "^7.25.4",
|
|
30
|
-
"@rollup/plugin-babel": "^6.0.3",
|
|
31
|
-
"@rollup/plugin-node-resolve": "^15.1.0",
|
|
32
|
-
"@rollup/plugin-terser": "^0.4.3",
|
|
33
|
-
"rollup": "^4.22.0",
|
|
34
|
-
"rollup-plugin-commonjs": "^10.1.0",
|
|
35
|
-
"rollup-plugin-dts": "^6.1.1",
|
|
36
|
-
"rollup-plugin-typescript": "^1.0.1",
|
|
37
|
-
"tslib": "^2.7.0",
|
|
38
|
-
"typescript": "^5.6.2"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {}
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "soon-fetch",
|
|
3
|
+
"version": "3.0.0-beta.2",
|
|
4
|
+
"description": "a 3Kb request lib alternative to axios",
|
|
5
|
+
"homepage": "https://github.com/leafio/soon-fetch",
|
|
6
|
+
"main": "./dist/index.cjs.js",
|
|
7
|
+
"module": "/dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"./dist/*"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build:types": "tsc",
|
|
14
|
+
"build": "rollup --config",
|
|
15
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"request",
|
|
19
|
+
"fetch",
|
|
20
|
+
"lightweight",
|
|
21
|
+
"timeout",
|
|
22
|
+
"http",
|
|
23
|
+
"axios"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@babel/core": "^7.25.2",
|
|
29
|
+
"@babel/preset-env": "^7.25.4",
|
|
30
|
+
"@rollup/plugin-babel": "^6.0.3",
|
|
31
|
+
"@rollup/plugin-node-resolve": "^15.1.0",
|
|
32
|
+
"@rollup/plugin-terser": "^0.4.3",
|
|
33
|
+
"rollup": "^4.22.0",
|
|
34
|
+
"rollup-plugin-commonjs": "^10.1.0",
|
|
35
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
36
|
+
"rollup-plugin-typescript": "^1.0.1",
|
|
37
|
+
"tslib": "^2.7.0",
|
|
38
|
+
"typescript": "^5.6.2"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {}
|
|
41
|
+
}
|