soon-fetch 3.1.1 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  > - 🚀 request race
14
14
  > - 📝 response cache
15
15
  > - 🔤 automatic serialization of JSON
16
- > - 📏 .min size less than **5K**, smaller after zip
16
+ > - 📏 .min size less than **6K**, smaller after zip
17
17
  > - 💡 smart type tips with Typescript
18
18
 
19
19
  - [Example](#example)
@@ -33,31 +33,28 @@
33
33
  > [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
34
34
 
35
35
  ```typescript
36
- import { createSoon } from "soon-fetch";
36
+ import { createSoon, soonFetch } from "soon-fetch";
37
+
38
+ // 使用 soonFetch 作为基础请求函数
39
+ const request = async <T>(url: string, options?: SoonOptions) => {
40
+ const isGet = !options?.method || options?.method.toLocaleLowerCase() === "get";
41
+ const response = await soonFetch<T>({
42
+ url,
43
+ options,
44
+ baseURL: '/api',
45
+ baseOptions: {
46
+ timeout: 20 * 1000,
47
+ headers: new Headers({
48
+ Authorization: "Bearer " + localStorage.getItem("token"),
49
+ }),
50
+ share: isGet ? true : false,
51
+ staleTime: isGet ? 2 * 1000 : 0,
52
+ },
53
+ });
54
+ return response;
55
+ };
37
56
 
38
- const soon = createSoon(
39
- (url, options) => {
40
- const isGet = !options?.method || options?.method.toLocaleLowerCase() === "get"
41
- return {
42
- baseURL: '/api',
43
- baseOptions: {
44
- timeout: 20 * 1000,
45
- headers: new Headers({
46
- Authorization: "Bearer " + localStorage.getItem("token"),
47
- }),
48
- share: isGet ? true : false,
49
- staleTime: isGet ? 2 * 1000 : 0,
50
- },
51
- }
52
- },
53
- ({ parsed }) => {
54
- return <T>() => {
55
- return fetch(parsed.url, parsed.options).then((res) =>
56
- res.json()
57
- ) as Promise<T>;
58
- };
59
- }
60
- );
57
+ const soon = createSoon(request);
61
58
 
62
59
  /** GET */
63
60
  soon.get("/user?id=123");
@@ -71,7 +68,7 @@ soon.post("/login", { body: { username: "admin", password: "123456" } });
71
68
  export const login = soon
72
69
  .POST("/user/login")
73
70
  .Body<{ username: string; password: string }>()
74
- .Send<{ token: string }>();
71
+ .Ok<{ token: string }>();
75
72
  //the develop tools will have type tips for request and response
76
73
  login({ username: "admin", password: "123" }).then((res) => {
77
74
  localStorage.setItem("token", res.token);
@@ -134,7 +131,7 @@ soon.get(url, { staleTime: 1000 * 60 * 5 });
134
131
  import { useEffect, useRef, useState } from "react";
135
132
 
136
133
  type User = { name: string; job: string };
137
- const api = soon.GET("/api/users").Query<{ page: number }>().Send<User[]>();
134
+ const api = soon.GET("/api/users").Query<{ page: number }>().Ok<User[]>();
138
135
  export default function App() {
139
136
  const refAbort = useRef([]);
140
137
  const [list, setList] = useState<User[]>([]);
@@ -161,18 +158,24 @@ export default function App() {
161
158
 
162
159
  ```typescript
163
160
  //can be GET POST PATCH PUT DELETE
164
- soon.GET(url:string).Query<Query>().Send<Response>()
165
- soon.POST(url:string).Body<Body>().Send<Response>()
161
+ soon.GET(url:string).Query<Query>().Ok<Response>()
162
+ soon.POST(url:string).Body<Body>().Ok<Response>()
163
+ soon.GET(url:string).Options({ timeout: 5000 }).Ok<Response>()
164
+ soon.POST(url:string).Body<Body>().Options({ timeout: 5000 }).Ok<Response>()
166
165
  //define an api
167
- export const getUserInfo = soon.GET("/user/:id").Send();
166
+ export const getUserInfo = soon.GET("/user/:id").Ok();
168
167
  //then use in any where
169
168
  getUserInfo({ id: 2 }).then((res) => console.log(res));
169
+ //define an api with options
170
+ export const getUserInfoWithOptions = soon.GET("/user/:id").Options({ timeout: 5000 }).Ok();
171
+ //then use in any where
172
+ getUserInfoWithOptions({ id: 2 }).then((res) => console.log(res));
170
173
 
171
174
  //with typescript,
172
175
  export const login = soon
173
176
  .POST("/user/login")
174
177
  .Body<{ username: string; password: string }>()
175
- .Send<{ token: string }>();
178
+ .Ok<{ token: string }>();
176
179
  //the develop tools will have type tips for request and response
177
180
  login({ username: "admin", password: "123" }).then((res) => {
178
181
  localStorage.setItem("token", res.token);
@@ -213,25 +216,32 @@ Create a soon request instance.
213
216
 
214
217
  **Parameters:**
215
218
 
216
- - `getConfig`: A function to get request configuration, receives url and options, returns an object containing url, options, baseURL, and baseOptions
217
- - `wrapper`: A wrapper function to handle request logic
219
+ - `request`: A function to handle the actual request, receives url and options, returns a Promise
218
220
 
219
- **Returns:** An object containing request method and various shortcut methods
221
+ **Returns:** An object containing request method, API methods (GET, POST, PUT, DELETE, PATCH), and shortcut methods (get, post, put, delete, patch, head, options)
220
222
 
221
223
  **Example:**
222
224
 
223
- ```javascript
225
+ ```typescript
224
226
  const soon = createSoon(
225
- (url, options) => ({ url, options,baseURL:'/api', baseOptions:{timeout: 5000} }),
226
- ({ parsed }) =>
227
- async (url, options) => {
228
- const response = await fetch(parsed.url, parsed.options);
229
- return response.json();
230
- }
227
+ async <T>(url: string, options?: SoonOptions): Promise<T> => {
228
+ const response = await fetch(url, options);
229
+ return response.json();
230
+ }
231
231
  );
232
232
 
233
233
  // Usage example
234
- const data = await soon.get("/api/users");
234
+ const data = await soon.get<{ id: number; name: string }[]> ("/api/users");
235
+
236
+ // Define API with options
237
+ export const login = soon
238
+ .POST("/user/login")
239
+ .Body<{ username: string; password: string }>()
240
+ .Ok<{ token: string }>();
241
+
242
+ login({ username: "admin", password: "123" }).then((res) => {
243
+ localStorage.setItem("token", res.token);
244
+ });
235
245
  ```
236
246
 
237
247
  #### createShortApi
@@ -247,18 +257,18 @@ Used to generate type-safe API calling methods, supporting path parameters, quer
247
257
 
248
258
  **Example:**
249
259
 
250
- ```javascript
260
+ ```typescript
251
261
  const API = createShortApi(
252
- async (url, method, params, query, body, options) => {
262
+ async <T>(url: string, method: string, params: Record<string, string | number> | undefined, query: any, body: any, options: any): Promise<T> => {
253
263
  // Handle request logic
254
- const _url = mergeUrl(url, { params, query });
264
+ const { url: _url } = parseUrl(url, { params, query });
255
265
  const response = await fetch(_url, { ...options, method, body });
256
266
  return response.json();
257
267
  }
258
268
  );
259
269
 
260
270
  // Usage example
261
- const getUser = API.GET("/api/users/:id").Send();
271
+ const getUser = API.GET("/api/users/:id").Ok<{ id: number; name: string }>();
262
272
  const userData = await getUser({ id: 1 });
263
273
  ```
264
274
 
@@ -275,11 +285,14 @@ Factory function to create shortcut methods.
275
285
 
276
286
  **Example:**
277
287
 
278
- ```javascript
279
- const methods = createShortMethods(["get", "post"], (method) => {
280
- return (url, options) => fetch(url, { ...options, method });
288
+ ```typescript
289
+ const methods = createShortMethods(["get", "post"] as const, (method) => {
290
+ return async <T>(url: string, options?: SoonOptions): Promise<T> => {
291
+ const response = await fetch(url, { ...options, method });
292
+ return response.json();
293
+ };
281
294
  });
282
- // Usage: methods.get('/api/users')
295
+ // Usage: methods.get<{ id: number; name: string }[]>('/api/users')
283
296
  ```
284
297
 
285
298
  #### parseUrlOptions
@@ -294,7 +307,7 @@ Parse URL options.
294
307
 
295
308
  **Example:**
296
309
 
297
- ```javascript
310
+ ```typescript
298
311
  const [url, options] = parseUrlOptions({
299
312
  url: "/api/users/:id",
300
313
  options: { params: { id: "123" } },
@@ -315,9 +328,9 @@ Merge multiple Headers objects.
315
328
 
316
329
  **Example:**
317
330
 
318
- ```javascript
319
- const headers1 = { "Content-Type": "application/json" };
320
- const headers2 = { Authorization: "Bearer token" };
331
+ ```typescript
332
+ const headers1: HeadersInit = { "Content-Type": "application/json" };
333
+ const headers2: HeadersInit = { Authorization: "Bearer token" };
321
334
  const mergedHeaders = mergeHeaders(headers1, headers2);
322
335
  ```
323
336
 
@@ -334,7 +347,7 @@ Merge multiple AbortSignal signals.
334
347
 
335
348
  **Example:**
336
349
 
337
- ```javascript
350
+ ```typescript
338
351
  const controller1 = new AbortController();
339
352
  const controller2 = new AbortController();
340
353
  const mergedSignal = mergeSignals(
@@ -357,8 +370,8 @@ Handle baseURL, path parameters and query parameters to generate complete URL.
357
370
 
358
371
  **Example:**
359
372
 
360
- ```javascript
361
- const url = mergeUrl("/api/users/:id", {
373
+ ```typescript
374
+ const { url } = parseUrl("/api/users/:id", {
362
375
  params: { id: "123" },
363
376
  query: { filter: "active" },
364
377
  baseURL: "https://api.example.com",
@@ -379,7 +392,7 @@ Merge request options, including special handling of headers and signals.
379
392
 
380
393
  **Example:**
381
394
 
382
- ```javascript
395
+ ```typescript
383
396
  const defaultOptions = {
384
397
  timeout: 5000,
385
398
  headers: { "Content-Type": "application/json" },
@@ -404,7 +417,7 @@ Check if body is a plain object, not special types like FormData or Blob.
404
417
 
405
418
  **Example:**
406
419
 
407
- ```javascript
420
+ ```typescript
408
421
  isBodyJson({ name: "John" }); // true
409
422
  isBodyJson(new FormData()); // false
410
423
  isBodyJson("string"); // false
@@ -423,7 +436,7 @@ Generate a unique key value based on the request's URL, method, headers, body, q
423
436
 
424
437
  **Example:**
425
438
 
426
- ```javascript
439
+ ```typescript
427
440
  const key = genRequestKey({
428
441
  url: "/api/users",
429
442
  options: { method: "GET", params: { id: 1 } },
@@ -442,26 +455,40 @@ Used to handle request race conditions, terminating previous requests.
442
455
 
443
456
  **Example:**
444
457
 
445
- ```javascript
458
+ ```typescript
446
459
  const controller = new AbortController();
447
- const controllers = [];
448
- raceAbort(controller, controllers); // Terminate previous requests and add current controller
460
+ const controllers: AbortController[] = [];
461
+ // 注意:raceAbort 函数已不再直接导出,而是通过 createRequestStore 使用
449
462
  ```
450
463
 
451
- #### createCache
464
+ #### createRequestStore
465
+
466
+ Create request store instance.
467
+ Provides request caching, sharing, and race condition handling.
452
468
 
453
- Create cache instance.
454
- Provides request result caching function, supports expiration time.
469
+ **Parameters:**
455
470
 
456
- **Returns:** Cache object containing get, set, remove methods
471
+ - `options`: Configuration options
472
+ - `maxCacheSize`: Maximum cache size (default: 100000)
473
+ - `checkInterval`: Cache check interval in milliseconds (default: 60000)
474
+
475
+ **Returns:** Request store object with the following methods:
476
+ - `entry(key)`: Get entry for specific request key
477
+ - `dispose()`: Dispose the store and clear intervals
478
+ - `get(key)`: Get request by key
479
+ - `set(key, value)`: Set request by key
480
+ - `remove(key)`: Remove request by key
481
+ - `getAll()`: Get all requests
482
+ - `removeAll()`: Remove all requests
483
+ - `clearCache()`: Clear all cache
484
+ - `clearExpiredCache()`: Clear expired cache
485
+ - `abortAll()`: Abort all requests
457
486
 
458
487
  **Example:**
459
488
 
460
- ```javascript
461
- const cache = createCache();
462
- cache.set("key", { data: "value" }, Date.now() + 60000); // Cache for 1 minute
463
- const data = cache.get("key");
464
- cache.remove("key");
489
+ ```typescript
490
+ const store = createRequestStore({ maxCacheSize: 1000, checkInterval: 30000 });
491
+ // Use store in requests
465
492
  ```
466
493
 
467
494
  #### deepSort
@@ -472,33 +499,18 @@ Recursively sort the keys of an object to generate a stable object serialization
472
499
  **Parameters:**
473
500
 
474
501
  - `obj`: Object to sort
502
+ - `sortArr`: Whether to sort arrays (default: false)
475
503
 
476
504
  **Returns:** Object with sorted keys
477
505
 
478
506
  **Example:**
479
507
 
480
- ```javascript
508
+ ```typescript
481
509
  const obj = { b: 2, a: 1, c: { z: 3, y: 2 } };
482
510
  const sorted = deepSort(obj);
483
511
  // Returns: { a: 1, b: 2, c: { y: 2, z: 3 } }
484
512
  ```
485
513
 
486
- #### createShare
487
-
488
- Create request sharing instance.
489
- Used to share the same request to avoid duplicate requests.
490
-
491
- **Returns:** Sharing object containing get and set methods
492
-
493
- **Example:**
494
-
495
- ```javascript
496
- const share = createShare();
497
- const promise = fetch("/api/data");
498
- share.set("key", promise);
499
- const sharedPromise = share.get("key"); // Get the same promise
500
- ```
501
-
502
514
  #### createSilentRefresh
503
515
 
504
516
  Create silent refresh instance.
@@ -512,7 +524,7 @@ Used to handle silent refresh functionality when token expires.
512
524
 
513
525
  **Example:**
514
526
 
515
- ```javascript
527
+ ```typescript
516
528
  const silentRefresh = createSilentRefresh(async () => {
517
529
  // Refresh token logic
518
530
  await refreshToken();
@@ -525,6 +537,37 @@ silentRefresh(
525
537
  );
526
538
  ```
527
539
 
540
+ #### soonFetch
541
+
542
+ A lightweight fetch wrapper with caching, sharing, and race condition handling.
543
+
544
+ **Parameters:**
545
+
546
+ - `config`: Configuration object
547
+ - `url`: Request URL
548
+ - `options`: Request options (SoonOptions)
549
+ - `baseURL`: Base URL
550
+ - `baseOptions`: Base request options
551
+ - `store`: Custom request store
552
+ - `sortRequestKey`: Whether to sort request key
553
+
554
+ **Returns:** Promise that resolves to the response
555
+
556
+ **Example:**
557
+
558
+ ```typescript
559
+ const data = await soonFetch<User[]>({
560
+ url: "/api/users",
561
+ options: {
562
+ method: "GET",
563
+ query: { page: 1 },
564
+ share: true,
565
+ staleTime: 5000,
566
+ },
567
+ baseURL: "https://api.example.com",
568
+ });
569
+ ```
570
+
528
571
  #### parseWithBase
529
572
 
530
573
  Parse base URL configuration.
@@ -538,7 +581,7 @@ Process baseURL, headers, body and other configuration items to generate final r
538
581
 
539
582
  **Example:**
540
583
 
541
- ```javascript
584
+ ```typescript
542
585
  const result = parseWithBase({
543
586
  url: "/api/users",
544
587
  options: { method: "GET" },
@@ -559,7 +602,7 @@ Used to convert plain objects to FormData format, supports files and regular val
559
602
 
560
603
  **Example:**
561
604
 
562
- ```javascript
605
+ ```typescript
563
606
  const formData = toFormData({
564
607
  name: "John",
565
608
  avatar: fileBlob,
@@ -567,13 +610,103 @@ const formData = toFormData({
567
610
  });
568
611
  ```
569
612
 
613
+ #### progressDownload
614
+
615
+ Download with progress tracking.
616
+ Used to download files with progress updates.
617
+
618
+ **Parameters:**
619
+
620
+ - `response`: Response object
621
+ - `onProgress`: Progress callback function
622
+
623
+ **Returns:** Promise that resolves to ArrayBuffer
624
+
625
+ **Example:**
626
+
627
+ ```typescript
628
+ const response = await fetch("/api/download");
629
+ const buffer = await progressDownload(response, (progress, downloaded, total) => {
630
+ console.log(`Progress: ${progress}%, Downloaded: ${downloaded}/${total}`);
631
+ });
632
+ ```
633
+
634
+ #### progressReadBody
635
+
636
+ Read response body with progress tracking.
637
+ Used to read response bodies with progress updates.
638
+
639
+ **Parameters:**
640
+
641
+ - `body`: ReadableStream<Uint8Array>
642
+ - `onProgress`: Progress callback function
643
+ - `total`: Total size in bytes (default: 0)
644
+
645
+ **Returns:** Promise that resolves to ArrayBuffer
646
+
647
+ **Example:**
648
+
649
+ ```typescript
650
+ const response = await fetch("/api/download");
651
+ const buffer = await progressReadBody(response.body!, (progress, downloaded, total) => {
652
+ console.log(`Progress: ${progress}%, Downloaded: ${downloaded}/${total}`);
653
+ });
654
+ ```
655
+
656
+ #### parseOptions
657
+
658
+ Parse and merge request options.
659
+ Used to process request options, including baseURL, headers, and body.
660
+
661
+ **Parameters:**
662
+
663
+ - `urlOptions`: Object containing url, options, baseURL, and baseOptions
664
+
665
+ **Returns:** Object containing parsed url, options, is_body_json, and abortController
666
+
667
+ **Example:**
668
+
669
+ ```typescript
670
+ const parsed = parseOptions({
671
+ url: "/api/users",
672
+ options: { method: "GET", query: { page: 1 } },
673
+ baseURL: "https://api.example.com",
674
+ baseOptions: { timeout: 5000 },
675
+ });
676
+ ```
677
+
678
+ #### requestWithStore
679
+
680
+ Request wrapper with store support.
681
+ Used to handle requests with caching, sharing, and race condition handling.
682
+
683
+ **Parameters:**
684
+
685
+ - `store`: Request store instance
686
+ - `requestFn`: Request function
687
+ - `requestKey`: Request key
688
+ - `fetchAbort`: AbortController
689
+ - `options`: Options object
690
+
691
+ **Returns:** Promise that resolves to the response
692
+
693
+ **Example:**
694
+
695
+ ```typescript
696
+ const store = createRequestStore();
697
+ const data = await requestWithStore(store, () => fetch(url, options), requestKey, abortController, {
698
+ share: true,
699
+ staleTime: 5000,
700
+ });
701
+ ```
702
+
570
703
  [English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
571
704
 
572
705
  <!-- omit in toc -->
573
706
 
574
707
  #### soon-fetch
575
708
 
576
- **极轻量的请求库,不到 5K**
709
+ **极轻量的请求库,不到 6K**
577
710
 
578
711
  > - 🌐 自动解析 rest Url 的参数
579
712
  > - ⭐ 快捷定义请求 api
@@ -582,7 +715,7 @@ const formData = toFormData({
582
715
  > - 🚀 请求竞态
583
716
  > - 📝 响应缓存
584
717
  > - 🔤 自动处理 JSON
585
- > - 📏 不到 **5K** , zip 后会更小
718
+ > - 📏 不到 **6K** , zip 后会更小
586
719
  > - 💡 用 typescript 有智能类型提醒
587
720
 
588
721
  - [示例](#示例)
@@ -605,29 +738,28 @@ const formData = toFormData({
605
738
  > [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
606
739
 
607
740
  ```typescript
608
- const soon = createSoon(
609
- (url, options) => {
610
- const isGet = !options?.method || options?.method.toLocaleLowerCase() === "get"
611
- return {
612
- baseURL: '/api',
613
- baseOptions: {
614
- timeout: 20 * 1000,
615
- headers: new Headers({
616
- Authorization: "Bearer " + localStorage.getItem("token"),
617
- }),
618
- share: isGet ? true : false,
619
- staleTime: isGet ? 2 * 1000 : 0,
620
- },
621
- }
622
- },
623
- ({ parsed }) => {
624
- return <T>() => {
625
- return fetch(parsed.url, parsed.options).then((res) =>
626
- res.json()
627
- ) as Promise<T>;
628
- };
629
- }
630
- );
741
+ import { createSoon, soonFetch } from "soon-fetch";
742
+
743
+ // 使用 soonFetch 作为基础请求函数
744
+ const request = async <T>(url: string, options?: SoonOptions) => {
745
+ const isGet = !options?.method || options?.method.toLocaleLowerCase() === "get";
746
+ const response = await soonFetch<T>({
747
+ url,
748
+ options,
749
+ baseURL: '/api',
750
+ baseOptions: {
751
+ timeout: 20 * 1000,
752
+ headers: new Headers({
753
+ Authorization: "Bearer " + localStorage.getItem("token"),
754
+ }),
755
+ share: isGet ? true : false,
756
+ staleTime: isGet ? 2 * 1000 : 0,
757
+ },
758
+ });
759
+ return response;
760
+ };
761
+
762
+ const soon = createSoon(request);
631
763
 
632
764
  /** GET */
633
765
  soon.get("/user?id=123");
@@ -641,7 +773,7 @@ soon.post("/login", { body: { username: "admin", password: "123456" } });
641
773
  export const login = soon
642
774
  .POST("/user/login")
643
775
  .Body<{ username: string; password: string }>()
644
- .Send<{ token: string }>();
776
+ .Ok<{ token: string }>();
645
777
 
646
778
  //开发工具会有请求和响应的智能提醒
647
779
  login({ username: "admin", password: "123" }).then((res) => {
@@ -705,7 +837,7 @@ soon.get(url, { staleTime: 1000 * 60 * 5 });
705
837
  import { useEffect, useRef, useState } from "react";
706
838
 
707
839
  type User = { name: string; job: string };
708
- const api = soon.GET("/api/users").Query<{ page: number }>().Send<User[]>();
840
+ const api = soon.GET("/api/users").Query<{ page: number }>().Ok<User[]>();
709
841
  export default function App() {
710
842
  const refAbort = useRef([]);
711
843
  const [list, setList] = useState<User[]>([]);
@@ -733,19 +865,25 @@ export default function App() {
733
865
  ```ts
734
866
  //可以是 GET POST PATCH PUT DELETE
735
867
  //GET 请求数据传递至query,其他方法请求数据传递至body
736
- soon.GET(url:string).Query<Query>().Send<Response>()
737
- soon.POST(url:string).Body<Body>().Send<Response>()
868
+ soon.GET(url:string).Query<Query>().Ok<Response>()
869
+ soon.POST(url:string).Body<Body>().Ok<Response>()
870
+ soon.GET(url:string).Options({ timeout: 5000 }).Ok<Response>()
871
+ soon.POST(url:string).Body<Body>().Options({ timeout: 5000 }).Ok<Response>()
738
872
 
739
873
  //定义一个api
740
- export const getUserInfo=soon.GET('/user/:id').Send()
874
+ export const getUserInfo=soon.GET('/user/:id').Ok()
741
875
  //使用
742
876
  getUserInfo({id:2}).then(res=>console.log(res))
877
+ //定义一个带选项的api
878
+ export const getUserInfoWithOptions=soon.GET('/user/:id').Options({ timeout: 5000 }).Ok()
879
+ //使用
880
+ getUserInfoWithOptions({id:2}).then(res=>console.log(res))
743
881
 
744
882
  //用typescript,
745
883
  export const login=soon
746
884
  .POST('/user/login')
747
885
  .Body<{username:string,password:string}>()
748
- .Send<{token:string}>()
886
+ .Ok<{token:string}>()
749
887
  //开发工具会有请求和响应的智能提醒
750
888
  login({username:'admin',password:'123'}).then(res=>{
751
889
  localStorage.setItem('token', res.token);
@@ -786,24 +924,32 @@ type SoonOptions = Omit<RequestInit, "body"> & {
786
924
 
787
925
  **参数:**
788
926
 
789
- - `getConfig`: 用于获取请求配置的函数,接收 url 和 options,返回包含 url、options、baseURL 和 baseOptions 的对象
790
- - `wrapper`: 包装器函数,用于处理请求逻辑
927
+ - `request`: 用于处理实际请求的函数,接收 url 和 options,返回一个 Promise
791
928
 
792
- **返回:** 包含 request 方法和各种快捷方法的对象
929
+ **返回:** 包含 request 方法、API 方法(GET、POST、PUT、DELETE、PATCH)和快捷方法(get、post、put、delete、patch、head、options)的对象
793
930
 
794
931
  **示例:**
795
932
 
796
- ```javascript
933
+ ```typescript
797
934
  const soon = createSoon(
798
- (url, options) => ({ url, options,baseURL:'/api', baseOptions:{timeout: 5000} }),
799
- ({ parsed }) =>
800
- async (url, options) => {
801
- const response = await fetch(parsed.url, parsed.options);
802
- return response.json();
803
- }
935
+ async (url, options) => {
936
+ const response = await fetch(url, options);
937
+ return response.json();
938
+ }
804
939
  );
940
+
805
941
  // 使用示例
806
942
  const data = await soon.get("/api/users");
943
+
944
+ // 定义带选项的 API
945
+ export const login = soon
946
+ .POST("/user/login")
947
+ .Body<{ username: string; password: string }>()
948
+ .Ok<{ token: string }>();
949
+
950
+ login({ username: "admin", password: "123" }).then((res) => {
951
+ localStorage.setItem("token", res.token);
952
+ });
807
953
  ```
808
954
 
809
955
  #### createShortApi
@@ -819,7 +965,7 @@ const data = await soon.get("/api/users");
819
965
 
820
966
  **示例:**
821
967
 
822
- ```javascript
968
+ ```typescript
823
969
  const API = createShortApi(
824
970
  async (url, method, params, query, body, options) => {
825
971
  // 处理请求逻辑
@@ -830,7 +976,7 @@ const API = createShortApi(
830
976
  );
831
977
 
832
978
  // 使用示例
833
- const getUser = API.GET("/api/users/:id").Send();
979
+ const getUser = API.GET("/api/users/:id").Ok();
834
980
  const userData = await getUser({ id: 1 });
835
981
  ```
836
982
 
@@ -847,8 +993,8 @@ const userData = await getUser({ id: 1 });
847
993
 
848
994
  **示例:**
849
995
 
850
- ```javascript
851
- const methods = createShortMethods(["get", "post"], (method) => {
996
+ ```typescript
997
+ const methods = createShortMethods(["get", "post"] as const, (method) => {
852
998
  return (url, options) => fetch(url, { ...options, method });
853
999
  });
854
1000
  // 使用: methods.get('/api/users')
@@ -866,7 +1012,7 @@ const methods = createShortMethods(["get", "post"], (method) => {
866
1012
 
867
1013
  **示例:**
868
1014
 
869
- ```javascript
1015
+ ```typescript
870
1016
  const [url, options] = parseUrlOptions({
871
1017
  url: "/api/users/:id",
872
1018
  options: { params: { id: "123" } },
@@ -887,9 +1033,9 @@ const [url, options] = parseUrlOptions({
887
1033
 
888
1034
  **示例:**
889
1035
 
890
- ```javascript
891
- const headers1 = { "Content-Type": "application/json" };
892
- const headers2 = { Authorization: "Bearer token" };
1036
+ ```typescript
1037
+ const headers1: HeadersInit = { "Content-Type": "application/json" };
1038
+ const headers2: HeadersInit = { Authorization: "Bearer token" };
893
1039
  const mergedHeaders = mergeHeaders(headers1, headers2);
894
1040
  ```
895
1041
 
@@ -906,7 +1052,7 @@ const mergedHeaders = mergeHeaders(headers1, headers2);
906
1052
 
907
1053
  **示例:**
908
1054
 
909
- ```javascript
1055
+ ```typescript
910
1056
  const controller1 = new AbortController();
911
1057
  const controller2 = new AbortController();
912
1058
  const mergedSignal = mergeSignals(
@@ -929,7 +1075,7 @@ const mergedSignal = mergeSignals(
929
1075
 
930
1076
  **示例:**
931
1077
 
932
- ```javascript
1078
+ ```typescript
933
1079
  const url = mergeUrl("/api/users/:id", {
934
1080
  params: { id: "123" },
935
1081
  query: { filter: "active" },
@@ -951,7 +1097,7 @@ const url = mergeUrl("/api/users/:id", {
951
1097
 
952
1098
  **示例:**
953
1099
 
954
- ```javascript
1100
+ ```typescript
955
1101
  const defaultOptions = {
956
1102
  timeout: 5000,
957
1103
  headers: { "Content-Type": "application/json" },
@@ -976,7 +1122,7 @@ const mergedOptions = mergeOptions(defaultOptions, requestOptions);
976
1122
 
977
1123
  **示例:**
978
1124
 
979
- ```javascript
1125
+ ```typescript
980
1126
  isBodyJson({ name: "John" }); // true
981
1127
  isBodyJson(new FormData()); // false
982
1128
  isBodyJson("string"); // false
@@ -995,7 +1141,7 @@ isBodyJson("string"); // false
995
1141
 
996
1142
  **示例:**
997
1143
 
998
- ```javascript
1144
+ ```typescript
999
1145
  const key = genRequestKey({
1000
1146
  url: "/api/users",
1001
1147
  options: { method: "GET", params: { id: 1 } },
@@ -1014,26 +1160,40 @@ const key = genRequestKey({
1014
1160
 
1015
1161
  **示例:**
1016
1162
 
1017
- ```javascript
1163
+ ```typescript
1018
1164
  const controller = new AbortController();
1019
1165
  const controllers = [];
1020
1166
  raceAbort(controller, controllers); // 终止之前的请求并添加当前控制器
1021
1167
  ```
1022
1168
 
1023
- #### createCache
1169
+ #### createRequestStore
1024
1170
 
1025
- 创建缓存实例。
1026
- 提供请求结果缓存功能,支持过期时间。
1171
+ 创建请求存储实例。
1172
+ 提供请求缓存、共享和竞态条件处理。
1027
1173
 
1028
- **返回:** 包含 get、set、remove 方法的缓存对象
1174
+ **参数:**
1175
+
1176
+ - `options`: 配置选项
1177
+ - `maxCacheSize`: 最大缓存大小(默认: 100000)
1178
+ - `checkInterval`: 缓存检查间隔(毫秒,默认: 60000)
1179
+
1180
+ **返回:** 请求存储对象,包含以下方法:
1181
+ - `entry(key)`: 获取特定请求键的条目
1182
+ - `dispose()`: 销毁存储并清除定时器
1183
+ - `get(key)`: 根据键获取请求
1184
+ - `set(key, value)`: 根据键设置请求
1185
+ - `remove(key)`: 根据键移除请求
1186
+ - `getAll()`: 获取所有请求
1187
+ - `removeAll()`: 移除所有请求
1188
+ - `clearCache()`: 清除所有缓存
1189
+ - `clearExpiredCache()`: 清除过期缓存
1190
+ - `abortAll()`: 中止所有请求
1029
1191
 
1030
1192
  **示例:**
1031
1193
 
1032
- ```javascript
1033
- const cache = createCache();
1034
- cache.set("key", { data: "value" }, Date.now() + 60000); // 缓存1分钟
1035
- const data = cache.get("key");
1036
- cache.remove("key");
1194
+ ```typescript
1195
+ const store = createRequestStore({ maxCacheSize: 1000, checkInterval: 30000 });
1196
+ // 在请求中使用 store
1037
1197
  ```
1038
1198
 
1039
1199
  #### deepSort
@@ -1044,33 +1204,18 @@ cache.remove("key");
1044
1204
  **参数:**
1045
1205
 
1046
1206
  - `obj`: 要排序的对象
1207
+ - `sortArr`: 是否对数组进行排序(默认: false)
1047
1208
 
1048
1209
  **返回:** 键排序后的对象
1049
1210
 
1050
1211
  **示例:**
1051
1212
 
1052
- ```javascript
1213
+ ```typescript
1053
1214
  const obj = { b: 2, a: 1, c: { z: 3, y: 2 } };
1054
1215
  const sorted = deepSort(obj);
1055
1216
  // 返回: { a: 1, b: 2, c: { y: 2, z: 3 } }
1056
1217
  ```
1057
1218
 
1058
- #### createShare
1059
-
1060
- 创建请求共享实例。
1061
- 用于共享相同请求,避免重复请求。
1062
-
1063
- **返回:** 包含 get 和 set 方法的共享对象
1064
-
1065
- **示例:**
1066
-
1067
- ```javascript
1068
- const share = createShare();
1069
- const promise = fetch("/api/data");
1070
- share.set("key", promise);
1071
- const sharedPromise = share.get("key"); // 获取相同 promise
1072
- ```
1073
-
1074
1219
  #### createSilentRefresh
1075
1220
 
1076
1221
  创建静默刷新实例。
@@ -1084,7 +1229,7 @@ const sharedPromise = share.get("key"); // 获取相同 promise
1084
1229
 
1085
1230
  **示例:**
1086
1231
 
1087
- ```javascript
1232
+ ```typescript
1088
1233
  const silentRefresh = createSilentRefresh(async () => {
1089
1234
  // 刷新token逻辑
1090
1235
  await refreshToken();
@@ -1097,6 +1242,37 @@ silentRefresh(
1097
1242
  );
1098
1243
  ```
1099
1244
 
1245
+ #### soonFetch
1246
+
1247
+ 一个轻量级的 fetch 包装器,支持缓存、共享和竞态条件处理。
1248
+
1249
+ **参数:**
1250
+
1251
+ - `config`: 配置对象
1252
+ - `url`: 请求 URL
1253
+ - `options`: 请求选项 (SoonOptions)
1254
+ - `baseURL`: 基础 URL
1255
+ - `baseOptions`: 基础请求选项
1256
+ - `store`: 自定义请求存储
1257
+ - `sortRequestKey`: 是否对请求键进行排序
1258
+
1259
+ **返回:** 解析为响应的 Promise
1260
+
1261
+ **示例:**
1262
+
1263
+ ```typescript
1264
+ const data = await soonFetch<User[]>({
1265
+ url: "/api/users",
1266
+ options: {
1267
+ method: "GET",
1268
+ query: { page: 1 },
1269
+ share: true,
1270
+ staleTime: 5000,
1271
+ },
1272
+ baseURL: "https://api.example.com",
1273
+ });
1274
+ ```
1275
+
1100
1276
  #### parseWithBase
1101
1277
 
1102
1278
  解析基础 URL 配置。
@@ -1110,7 +1286,7 @@ silentRefresh(
1110
1286
 
1111
1287
  **示例:**
1112
1288
 
1113
- ```javascript
1289
+ ```typescript
1114
1290
  const result = parseWithBase({
1115
1291
  url: "/api/users",
1116
1292
  options: { method: "GET" },
@@ -1131,7 +1307,7 @@ const result = parseWithBase({
1131
1307
 
1132
1308
  **示例:**
1133
1309
 
1134
- ```javascript
1310
+ ```typescript
1135
1311
  const formData = toFormData({
1136
1312
  name: "John",
1137
1313
  avatar: fileBlob,
@@ -1139,6 +1315,96 @@ const formData = toFormData({
1139
1315
  });
1140
1316
  ```
1141
1317
 
1318
+ #### progressDownload
1319
+
1320
+ 带进度的下载。
1321
+ 用于下载文件并跟踪进度。
1322
+
1323
+ **参数:**
1324
+
1325
+ - `response`: 响应对象
1326
+ - `onProgress`: 进度回调函数
1327
+
1328
+ **返回:** 解析为 ArrayBuffer 的 Promise
1329
+
1330
+ **示例:**
1331
+
1332
+ ```typescript
1333
+ const response = await fetch("/api/download");
1334
+ const buffer = await progressDownload(response, (progress, downloaded, total) => {
1335
+ console.log(`进度: ${progress}%, 已下载: ${downloaded}/${total}`);
1336
+ });
1337
+ ```
1338
+
1339
+ #### progressReadBody
1340
+
1341
+ 带进度的读取响应体。
1342
+ 用于读取响应体并跟踪进度。
1343
+
1344
+ **参数:**
1345
+
1346
+ - `body`: ReadableStream<Uint8Array>
1347
+ - `onProgress`: 进度回调函数
1348
+ - `total`: 总大小(字节,默认: 0)
1349
+
1350
+ **返回:** 解析为 ArrayBuffer 的 Promise
1351
+
1352
+ **示例:**
1353
+
1354
+ ```typescript
1355
+ const response = await fetch("/api/download");
1356
+ const buffer = await progressReadBody(response.body, (progress, downloaded, total) => {
1357
+ console.log(`进度: ${progress}%, 已下载: ${downloaded}/${total}`);
1358
+ });
1359
+ ```
1360
+
1361
+ #### parseOptions
1362
+
1363
+ 解析和合并请求选项。
1364
+ 用于处理请求选项,包括 baseURL、headers 和 body。
1365
+
1366
+ **参数:**
1367
+
1368
+ - `urlOptions`: 包含 url、options、baseURL 和 baseOptions 的对象
1369
+
1370
+ **返回:** 包含解析后的 url、options、is_body_json 和 abortController 的对象
1371
+
1372
+ **示例:**
1373
+
1374
+ ```typescript
1375
+ const parsed = parseOptions({
1376
+ url: "/api/users",
1377
+ options: { method: "GET", query: { page: 1 } },
1378
+ baseURL: "https://api.example.com",
1379
+ baseOptions: { timeout: 5000 },
1380
+ });
1381
+ ```
1382
+
1383
+ #### requestWithStore
1384
+
1385
+ 带存储支持的请求包装器。
1386
+ 用于处理带有缓存、共享和竞态条件处理的请求。
1387
+
1388
+ **参数:**
1389
+
1390
+ - `store`: 请求存储实例
1391
+ - `requestFn`: 请求函数
1392
+ - `requestKey`: 请求键
1393
+ - `fetchAbort`: AbortController
1394
+ - `options`: 选项对象
1395
+
1396
+ **返回:** 解析为响应的 Promise
1397
+
1398
+ **示例:**
1399
+
1400
+ ```typescript
1401
+ const store = createRequestStore();
1402
+ const data = await requestWithStore(store, () => fetch(url, options), requestKey, abortController, {
1403
+ share: true,
1404
+ staleTime: 5000,
1405
+ });
1406
+ ```
1407
+
1142
1408
 
1143
1409
  [English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
1144
1410