soon-fetch 3.0.0-beta.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -26
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +28 -9
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
### soon-fetch
|
|
6
6
|
|
|
7
|
-
**A lightweight http request lib , alternative to axios
|
|
7
|
+
**A lightweight http request lib , alternative to axios with timeout, request reusing, race, response cache ...**
|
|
8
8
|
|
|
9
9
|
> - 🌐 automatic parse restful api url parameters
|
|
10
10
|
> - ⭐ rapid define a request api
|
|
11
11
|
> - ⌛ timeout disconnect
|
|
12
|
-
> -
|
|
13
|
-
> -
|
|
12
|
+
> - 📦 request reusing
|
|
13
|
+
> - 🚀 request race
|
|
14
|
+
> - 📝 response cache
|
|
15
|
+
> - 🔤 automatic serialization of JSON
|
|
16
|
+
> - 📏 .min size less than **5K**, smaller after zip
|
|
14
17
|
> - 💡 smart type tips with Typescript
|
|
15
18
|
|
|
16
19
|
- [Example](#example)
|
|
@@ -19,7 +22,7 @@
|
|
|
19
22
|
- [Restful Url Params](#restful-url-params)
|
|
20
23
|
- [Timeout](#timeout)
|
|
21
24
|
- [Share pending request](#share-pending-request)
|
|
22
|
-
- [
|
|
25
|
+
- [Response cache](#response-cache)
|
|
23
26
|
- [Request race](#request-race)
|
|
24
27
|
- [Rapid Define APIs](#rapid-define-apis)
|
|
25
28
|
- [API](#api)
|
|
@@ -63,9 +66,10 @@ soon.post("/login", { body: { username: "admin", password: "123456" } });
|
|
|
63
66
|
|
|
64
67
|
/**Define API */
|
|
65
68
|
export const login = soon
|
|
66
|
-
.
|
|
67
|
-
.
|
|
68
|
-
|
|
69
|
+
.POST("/user/login")
|
|
70
|
+
.Body<{ username: string; password: string }>()
|
|
71
|
+
.Send<{ token: string }>();
|
|
72
|
+
//the develop tools will have type tips for request and response
|
|
69
73
|
login({ username: "admin", password: "123" }).then((res) => {
|
|
70
74
|
localStorage.setItem("token", res.token);
|
|
71
75
|
});
|
|
@@ -111,7 +115,7 @@ If a request is made again before the first completes, will reuse the first requ
|
|
|
111
115
|
soon.get(url, { share: true });
|
|
112
116
|
```
|
|
113
117
|
|
|
114
|
-
#####
|
|
118
|
+
##### Response cache
|
|
115
119
|
|
|
116
120
|
A cached response will be returned if the request is made again within the specified time.
|
|
117
121
|
|
|
@@ -124,14 +128,18 @@ soon.get(url, { staleTime: 1000 * 60 * 5 });
|
|
|
124
128
|
If a second request is made before the first completes, abort the first to avoid race conditions from out-of-order responses.
|
|
125
129
|
|
|
126
130
|
```tsx
|
|
131
|
+
import { useEffect, useRef, useState } from "react";
|
|
132
|
+
|
|
127
133
|
type User = { name: string; job: string };
|
|
128
134
|
const api = soon.GET("/api/users").Query<{ page: number }>().Send<User[]>();
|
|
129
135
|
export default function App() {
|
|
130
|
-
const refAbort = useRef
|
|
136
|
+
const refAbort = useRef([]);
|
|
131
137
|
const [list, setList] = useState<User[]>([]);
|
|
132
138
|
const [page, setPage] = useState(1);
|
|
133
139
|
useEffect(() => {
|
|
134
|
-
api({ page }
|
|
140
|
+
api({ page }, { aborts: refAbort.current })
|
|
141
|
+
.then(setList)
|
|
142
|
+
.catch(console.log);
|
|
135
143
|
}, [page]);
|
|
136
144
|
return (
|
|
137
145
|
<div>
|
|
@@ -150,7 +158,8 @@ export default function App() {
|
|
|
150
158
|
|
|
151
159
|
```typescript
|
|
152
160
|
//can be GET POST PATCH PUT DELETE
|
|
153
|
-
|
|
161
|
+
soon.GET(url:string).Query<Query>().Send<Response>()
|
|
162
|
+
soon.POST(url:string).Body<Body>().Send<Response>()
|
|
154
163
|
//define an api
|
|
155
164
|
export const getUserInfo = soon.GET("/user/:id").Send();
|
|
156
165
|
//then use in any where
|
|
@@ -175,6 +184,7 @@ login({ username: "admin", password: "123" }).then((res) => {
|
|
|
175
184
|
// function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
|
|
176
185
|
// RequestInit is fetch's init options
|
|
177
186
|
type SoonOptions = Omit<RequestInit, "body"> & {
|
|
187
|
+
body?: RequestInit["body"] | object;
|
|
178
188
|
query?:
|
|
179
189
|
| Record<
|
|
180
190
|
string,
|
|
@@ -188,8 +198,7 @@ type SoonOptions = Omit<RequestInit, "body"> & {
|
|
|
188
198
|
| URLSearchParams;
|
|
189
199
|
params?: Record<string, string | number>;
|
|
190
200
|
timeout?: number;
|
|
191
|
-
|
|
192
|
-
aborts?: AbortController[];
|
|
201
|
+
aborts?: AbortController[] | never[];
|
|
193
202
|
share?: boolean;
|
|
194
203
|
staleTime?: number;
|
|
195
204
|
};
|
|
@@ -206,8 +215,11 @@ type SoonOptions = Omit<RequestInit, "body"> & {
|
|
|
206
215
|
> - 🌐 自动解析 rest Url 的参数
|
|
207
216
|
> - ⭐ 快捷定义请求 api
|
|
208
217
|
> - ⌛ 超时断开
|
|
218
|
+
> - 📦 请求复用
|
|
219
|
+
> - 🚀 请求竞态
|
|
220
|
+
> - 📝 响应缓存
|
|
209
221
|
> - 🔤 自动处理 JSON
|
|
210
|
-
> - 📏 不到 **
|
|
222
|
+
> - 📏 不到 **5K** , zip 后会更小
|
|
211
223
|
> - 💡 用 typescript 有智能类型提醒
|
|
212
224
|
|
|
213
225
|
- [示例](#示例)
|
|
@@ -218,7 +230,8 @@ type SoonOptions = Omit<RequestInit, "body"> & {
|
|
|
218
230
|
- [Restful Url 参数自动处理](#restful-url-参数自动处理)
|
|
219
231
|
- [共享未完成的请求](#共享未完成的请求)
|
|
220
232
|
- [超时](#超时)
|
|
221
|
-
- [
|
|
233
|
+
- [响应缓存](#响应缓存)
|
|
234
|
+
- [请求竞态](#请求竞态)
|
|
222
235
|
- [快速定义 API](#快速定义-api)
|
|
223
236
|
|
|
224
237
|
- [API](#api-1)
|
|
@@ -260,9 +273,11 @@ soon.post("/login", { body: { username: "admin", password: "123456" } });
|
|
|
260
273
|
|
|
261
274
|
/**定义 API */
|
|
262
275
|
export const login = soon
|
|
263
|
-
.
|
|
264
|
-
.
|
|
276
|
+
.POST("/user/login")
|
|
277
|
+
.Body<{ username: string; password: string }>()
|
|
278
|
+
.Send<{ token: string }>();
|
|
265
279
|
|
|
280
|
+
//开发工具会有请求和响应的智能提醒
|
|
266
281
|
login({ username: "admin", password: "123" }).then((res) => {
|
|
267
282
|
localStorage.setItem("token", res.token);
|
|
268
283
|
});
|
|
@@ -308,7 +323,7 @@ soon.get(url, { timeout: 1000 * 20 });
|
|
|
308
323
|
soon.get(url, { share: true });
|
|
309
324
|
```
|
|
310
325
|
|
|
311
|
-
#####
|
|
326
|
+
##### 响应缓存
|
|
312
327
|
|
|
313
328
|
如果在指定时间内再次发起相同的请求,则会返回缓存的响应。
|
|
314
329
|
|
|
@@ -321,10 +336,12 @@ soon.get(url, { staleTime: 1000 * 60 * 5 });
|
|
|
321
336
|
如果在第一个请求完成之前发起第二个请求,则会中止第一个请求,以避免因响应顺序错乱导致的问题。
|
|
322
337
|
|
|
323
338
|
```tsx
|
|
339
|
+
import { useEffect, useRef, useState } from "react";
|
|
340
|
+
|
|
324
341
|
type User = { name: string; job: string };
|
|
325
342
|
const api = soon.GET("/api/users").Query<{ page: number }>().Send<User[]>();
|
|
326
343
|
export default function App() {
|
|
327
|
-
const refAbort = useRef
|
|
344
|
+
const refAbort = useRef([]);
|
|
328
345
|
const [list, setList] = useState<User[]>([]);
|
|
329
346
|
const [page, setPage] = useState(1);
|
|
330
347
|
useEffect(() => {
|
|
@@ -347,19 +364,22 @@ export default function App() {
|
|
|
347
364
|
|
|
348
365
|
##### 快速定义 API
|
|
349
366
|
|
|
350
|
-
```
|
|
367
|
+
```ts
|
|
351
368
|
//可以是 GET POST PATCH PUT DELETE
|
|
352
369
|
//GET 请求数据传递至query,其他方法请求数据传递至body
|
|
353
|
-
soon.
|
|
370
|
+
soon.GET(url:string).Query<Query>().Send<Response>()
|
|
371
|
+
soon.POST(url:string).Body<Body>().Send<Response>()
|
|
354
372
|
|
|
355
373
|
//定义一个api
|
|
356
|
-
export const getUserInfo=soon.
|
|
374
|
+
export const getUserInfo=soon.GET('/user/:id').Send()
|
|
357
375
|
//使用
|
|
358
376
|
getUserInfo({id:2}).then(res=>console.log(res))
|
|
359
377
|
|
|
360
378
|
//用typescript,
|
|
361
|
-
export const login=soon
|
|
362
|
-
|
|
379
|
+
export const login=soon
|
|
380
|
+
.POST('/user/login')
|
|
381
|
+
.Body<{username:string,password:string}>()
|
|
382
|
+
.Send<{token:string}>()
|
|
363
383
|
//开发工具会有请求和响应的智能提醒
|
|
364
384
|
login({username:'admin',password:'123'}).then(res=>{
|
|
365
385
|
localStorage.setItem('token', res.token);
|
|
@@ -374,6 +394,7 @@ export default function App() {
|
|
|
374
394
|
// function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
|
|
375
395
|
// RequestInit 为原生 fetch 的 init 选项
|
|
376
396
|
type SoonOptions = Omit<RequestInit, "body"> & {
|
|
397
|
+
body?: RequestInit["body"] | object;
|
|
377
398
|
query?:
|
|
378
399
|
| Record<
|
|
379
400
|
string,
|
|
@@ -387,8 +408,7 @@ type SoonOptions = Omit<RequestInit, "body"> & {
|
|
|
387
408
|
| URLSearchParams;
|
|
388
409
|
params?: Record<string, string | number>;
|
|
389
410
|
timeout?: number;
|
|
390
|
-
|
|
391
|
-
aborts?: AbortController[];
|
|
411
|
+
aborts?: AbortController[] | never[];
|
|
392
412
|
share?: boolean;
|
|
393
413
|
staleTime?: number;
|
|
394
414
|
};
|
package/dist/index.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e=e=>{const t=[],
|
|
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 p=s.trim();e(s).forEach((e=>{c&&(p=p.replace(":"+e,""+c[e]))}));const[u,l]=p.split("?"),h=new URLSearchParams([...o(l),...o(i)]);let y=((e,o)=>{if(n(e))return e;const s=n(o)?o:r(o);return t(s)+t(r(e))})(u,f);return h.size&&(y=y+"?"+h),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}function f(...e){const t=Object.assign({},...e),r=a(...e.map((e=>e?.headers)));return t.headers=r,t.signal=i(e.map((e=>e?.signal)),t.timeout),t}function p(e){const{url:t,options:r,baseURL:n,baseOptions:o}=e,a=f(o,r),p=s(t,{...a,baseURL:n}),u=a?.body,l=c(u);a.body=l?JSON.stringify(u):u,l&&a.headers.append("Content-Type","application/json");const h=new AbortController;return a.signal=i([a.signal,h.signal]),{url:p,options:a,is_body_json:l,abortController:h}}const u=["get","post","put","delete","patch"];function l(e,t){const r={};return e.forEach((e=>{r[e]=t(e)})),r}function h(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,p=c?a.shift():void 0,u=i?a.shift():void 0,l=a.shift();return t(r,n,f,p,u,l,o?.options)}},n={};return u.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 y(e,t){t&&(t.pop()?.abort("race 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((r=>{t[r]=d(e[r])})),t}return e}function g(e){const{url:t,options:r}=e,{headers:n,method:o,body:s,query:a,params:i}=r??{},c=d(Object.fromEntries(new Headers(n).entries()??[]));return(o??"get").toLowerCase()+t+JSON.stringify(d(a)??"")+JSON.stringify(d(i)??"")+JSON.stringify(c)+("object"==typeof s&&null!=s?JSON.stringify(d(s)):s??"")}function m(){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 b(){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=m,exports.createShare=b,exports.createShortApi=h,exports.createShortMethods=l,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,t){const r=m(),n=b(),o=(o,s)=>new Promise(((a,i)=>{const c=p(e(o,s)),f=g(c),{abortController:u}=c,l=new AbortController;l.signal.addEventListener("abort",(()=>{i(l.signal.reason)}));const h=t({parsed:{...c,requestKey:f}});if(c.options?.share){const e=n.get(f);if(e)return a(e)}if(y(c.options.share?l:u,c.options?.aborts),c.options?.staleTime){const e=r.get(f);if(void 0!==e)return a(e)}const d=h(o,s);c.options?.share&&n.set(f,d),d.then((e=>{a(e),c.options?.staleTime&&r.set(f,e,(new Date).getTime()+c.options.staleTime)})).catch((e=>i(e)))})),s=h(((e,t,r,n,s,a,i)=>o(e,{...i,...a,method:t,params:r,query:n,body:s}))),a=l([...u,"head","options"],(e=>(t,r)=>o(t,{...r,method:e})));return{request:o,...s,...a}},exports.deepSort=d,exports.genRequestKey=g,exports.isBodyJson=c,exports.mergeHeaders=a,exports.mergeOptions=f,exports.mergeSignals=i,exports.mergeUrl=s,exports.parseUrlOptions=function(e){const{url:t,options:r}=p(e);return[t,r]},exports.parseWithBase=p,exports.raceAbort=y;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ type SoonOptions = Omit<RequestInit, "body"> & {
|
|
|
3
3
|
query?: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams;
|
|
4
4
|
params?: Record<string, string | number>;
|
|
5
5
|
timeout?: number;
|
|
6
|
-
aborts?: AbortController[];
|
|
6
|
+
aborts?: AbortController[] | never[];
|
|
7
7
|
share?: boolean;
|
|
8
8
|
staleTime?: number;
|
|
9
9
|
};
|
|
@@ -24,6 +24,20 @@ declare function isBodyJson(body: any): boolean;
|
|
|
24
24
|
declare function mergeOptions<Options extends SoonOptions>(...optionsList: (Options | undefined)[]): Options & {
|
|
25
25
|
headers: Headers;
|
|
26
26
|
};
|
|
27
|
+
declare function parseWithBase<Options extends SoonOptions>(urlOptions: {
|
|
28
|
+
url: string;
|
|
29
|
+
options?: Options;
|
|
30
|
+
baseURL?: string;
|
|
31
|
+
baseOptions?: Options;
|
|
32
|
+
}): {
|
|
33
|
+
url: string;
|
|
34
|
+
options: Options & {
|
|
35
|
+
headers: Headers;
|
|
36
|
+
body?: RequestInit["body"];
|
|
37
|
+
};
|
|
38
|
+
is_body_json: boolean;
|
|
39
|
+
abortController: AbortController;
|
|
40
|
+
};
|
|
27
41
|
declare function parseUrlOptions<Options extends SoonOptions>(urlOptions: {
|
|
28
42
|
url: string;
|
|
29
43
|
options?: Options;
|
|
@@ -58,11 +72,13 @@ declare function raceAbort(abortController: AbortController, controllers?: Abort
|
|
|
58
72
|
declare function deepSort(obj: unknown): unknown;
|
|
59
73
|
declare function genRequestKey(req: {
|
|
60
74
|
url: string;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
options?: {
|
|
76
|
+
method?: string;
|
|
77
|
+
headers?: RequestInit["headers"];
|
|
78
|
+
body?: RequestInit["body"] | object;
|
|
79
|
+
query?: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams;
|
|
80
|
+
params?: Record<string, string | number>;
|
|
81
|
+
};
|
|
66
82
|
}): string;
|
|
67
83
|
declare function createCache(): {
|
|
68
84
|
get: (key: string) => unknown;
|
|
@@ -77,15 +93,18 @@ declare function createSilentRefresh(refresh_token_fn: () => Promise<void>): (su
|
|
|
77
93
|
declare function createSoon<Options extends SoonOptions>(getConfig: (url: string, options?: Options) => {
|
|
78
94
|
url: string;
|
|
79
95
|
options?: Options;
|
|
80
|
-
baseURL
|
|
96
|
+
baseURL?: string;
|
|
81
97
|
baseOptions?: Options;
|
|
82
98
|
}, wrapper: (instance: {
|
|
83
99
|
parsed: {
|
|
84
100
|
url: string;
|
|
85
101
|
options: Options & {
|
|
86
102
|
headers: Headers;
|
|
87
|
-
body?:
|
|
103
|
+
body?: RequestInit["body"];
|
|
88
104
|
};
|
|
105
|
+
is_body_json: boolean;
|
|
106
|
+
abortController: AbortController;
|
|
107
|
+
requestKey: string;
|
|
89
108
|
};
|
|
90
109
|
}) => <T>(url: string, options?: Options) => Promise<T>): {
|
|
91
110
|
options: <T>(url: string, options?: Options) => Promise<T>;
|
|
@@ -152,4 +171,4 @@ declare function createSoon<Options extends SoonOptions>(getConfig: (url: string
|
|
|
152
171
|
request: <T>(url: string, options?: Options) => Promise<T>;
|
|
153
172
|
};
|
|
154
173
|
|
|
155
|
-
export { type SoonOptions, createCache, createShare, createShortApi, createShortMethods, createSilentRefresh, createSoon, deepSort, genRequestKey, isBodyJson, mergeHeaders, mergeOptions, mergeSignals, mergeUrl, parseUrlOptions, raceAbort };
|
|
174
|
+
export { type SoonOptions, createCache, createShare, createShortApi, createShortMethods, createSilentRefresh, createSoon, deepSort, genRequestKey, isBodyJson, mergeHeaders, mergeOptions, mergeSignals, mergeUrl, parseUrlOptions, parseWithBase, raceAbort };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const t=t=>{const e=[],n=t.match(/:([^:/\d]+)\/?/g);return n&&n.forEach((t=>{e.push(t.replace(/\//g,"").replace(/:/g,""))})),e},e=(t="")=>t.endsWith("/")?t.slice(0,-1):t,n=(t="")=>t.startsWith("/")?t:"/"+t,r=(t="")=>t.startsWith("http"),o=t=>{if(!t)return[];if(t instanceof URLSearchParams||"string"==typeof t||Array.isArray(t))return Array.from(new URLSearchParams(t).entries());const e=[];return Object.keys(t).forEach((n=>{const r=t[n];(Array.isArray(r)?r:[r]).forEach((t=>{e.push([n,t??""])}))})),e},s=(s,a)=>{const{query:i,params:c,baseURL:f}=a;let u=s.trim();t(s).forEach((t=>{c&&(u=u.replace(":"+t,""+c[t]))}));const[l,p]=u.split("?"),h=new URLSearchParams([...o(p),...o(i)]);let y=((t,o)=>{if(r(t))return t;const s=r(o)?o:n(o);return e(s)+e(n(t))})(l,f);return h.size&&(y=y+"?"+h),y},a=(...t)=>{const e=new Headers;return t.forEach((t=>{t&&new Headers(t).forEach(((t,n)=>{e.set(n,t)}))})),e};function i(t,e){const n=(t??[]).filter((t=>!!t));return e&&n.push(AbortSignal.timeout(e)),n.length?AbortSignal.any(n):void 0}function c(t){return!(!t||"object"!=typeof t||(t instanceof Blob||t instanceof ArrayBuffer||t instanceof FormData||t instanceof File||t instanceof DataView||t instanceof URLSearchParams||t instanceof ReadableStream||(e=t,e instanceof Int8Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int16Array||e instanceof Uint16Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Float32Array||e instanceof Float64Array||e instanceof BigInt64Array||e instanceof BigUint64Array)));var e}function f(...t){const e=Object.assign({},...t),n=a(...t.map((t=>t?.headers)));return e.headers=n,e.signal=i(t.map((t=>t?.signal)),e.timeout),e}function u(t){const{url:e,options:n,baseURL:r,baseOptions:o}=t,a=f(o,n),u=s(e,{...a,baseURL:r}),l=a?.body,p=c(l);
|
|
1
|
+
const t=t=>{const e=[],n=t.match(/:([^:/\d]+)\/?/g);return n&&n.forEach((t=>{e.push(t.replace(/\//g,"").replace(/:/g,""))})),e},e=(t="")=>t.endsWith("/")?t.slice(0,-1):t,n=(t="")=>t.startsWith("/")?t:"/"+t,r=(t="")=>t.startsWith("http"),o=t=>{if(!t)return[];if(t instanceof URLSearchParams||"string"==typeof t||Array.isArray(t))return Array.from(new URLSearchParams(t).entries());const e=[];return Object.keys(t).forEach((n=>{const r=t[n];(Array.isArray(r)?r:[r]).forEach((t=>{e.push([n,t??""])}))})),e},s=(s,a)=>{const{query:i,params:c,baseURL:f}=a;let u=s.trim();t(s).forEach((t=>{c&&(u=u.replace(":"+t,""+c[t]))}));const[l,p]=u.split("?"),h=new URLSearchParams([...o(p),...o(i)]);let y=((t,o)=>{if(r(t))return t;const s=r(o)?o:n(o);return e(s)+e(n(t))})(l,f);return h.size&&(y=y+"?"+h),y},a=(...t)=>{const e=new Headers;return t.forEach((t=>{t&&new Headers(t).forEach(((t,n)=>{e.set(n,t)}))})),e};function i(t,e){const n=(t??[]).filter((t=>!!t));return e&&n.push(AbortSignal.timeout(e)),n.length?AbortSignal.any(n):void 0}function c(t){return!(!t||"object"!=typeof t||(t instanceof Blob||t instanceof ArrayBuffer||t instanceof FormData||t instanceof File||t instanceof DataView||t instanceof URLSearchParams||t instanceof ReadableStream||(e=t,e instanceof Int8Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int16Array||e instanceof Uint16Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Float32Array||e instanceof Float64Array||e instanceof BigInt64Array||e instanceof BigUint64Array)));var e}function f(...t){const e=Object.assign({},...t),n=a(...t.map((t=>t?.headers)));return e.headers=n,e.signal=i(t.map((t=>t?.signal)),e.timeout),e}function u(t){const{url:e,options:n,baseURL:r,baseOptions:o}=t,a=f(o,n),u=s(e,{...a,baseURL:r}),l=a?.body,p=c(l);a.body=p?JSON.stringify(l):l,p&&a.headers.append("Content-Type","application/json");const h=new AbortController;return a.signal=i([a.signal,h.signal]),{url:u,options:a,is_body_json:p,abortController:h}}function l(t){const{url:e,options:n}=u(t);return[e,n]}const p=["get","post","put","delete","patch"];function h(t,e){const n={};return t.forEach((t=>{n[t]=e(t)})),n}function y(e){const n=(n,r,o)=>{const s=!!t(n).length;return(...t)=>{const a=[...t],{hasBody:i,hasQuery:c}=o||{},f=s?a.shift():void 0,u=c?a.shift():void 0,l=i?a.shift():void 0,p=a.shift();return e(n,r,f,u,l,p,o?.options)}},r={};return p.forEach((t=>{const e=t.toUpperCase();r[e]=e=>({Send:r=>n(e,t,{options:r}),Body:()=>({Send:r=>n(e,t,{hasBody:!0,options:r})}),Query:()=>({Send:r=>n(e,t,{hasQuery:!0,options:r}),Body:()=>({Send:r=>n(e,t,{hasBody:!0,hasQuery:!0,options:r})})})})})),r}function d(t,e){e&&(e.pop()?.abort("race abort"),e.push(t))}function m(t){if(Array.isArray(t))return t.map(m);if("object"==typeof t&&null!==t){const e={};return Object.keys(t).sort().forEach((n=>{e[n]=m(t[n])})),e}return t}function g(t){const{url:e,options:n}=t,{headers:r,method:o,body:s,query:a,params:i}=n??{},c=m(Object.fromEntries(new Headers(r).entries()??[]));return(o??"get").toLowerCase()+e+JSON.stringify(m(a)??"")+JSON.stringify(m(i)??"")+JSON.stringify(c)+("object"==typeof s&&null!=s?JSON.stringify(m(s)):s??"")}function b(){const t={},e=[];setInterval((()=>{const n=Date.now();for(let r=e.length-1;r>=0;r--)e[r].expiredTime<n&&(delete t[e[r].key],e.splice(r,1))}),6e4);const n=t=>t instanceof Response?t.clone():"function"==typeof t||t instanceof Promise?t:structuredClone(t);function r(n){delete t[n];for(let t=e.length-1;t>=0;t--)if(e[t].key===n){e.splice(t,1);break}}return{get:function(e){const o=t[e];if(void 0!==o)return o.expiredTime>Date.now()?n(o.data):void r(e)},set:function(r,o,s){t[r]={data:n(o),expiredTime:s},e.push({key:r,expiredTime:s})},remove:r}}function A(){const t={},e=e=>t[e]=void 0;return{get:e=>t[e],set:(n,r)=>{t[n]=r,r.finally((()=>e(n)))}}}function S(t){let e=[],n=!1;return(r,o)=>{e.push({success:r,fail:o}),n||(n=!0,t().then((()=>{e.forEach((t=>t.success()))})).catch((t=>{e.forEach((t=>t.fail()))})).finally((()=>{n=!1,e=[]})))}}function w(t,e){const n=b(),r=A(),o=(o,s)=>new Promise(((a,i)=>{const c=u(t(o,s)),f=g(c),{abortController:l}=c,p=new AbortController;p.signal.addEventListener("abort",(()=>{i(p.signal.reason)}));const h=e({parsed:{...c,requestKey:f}});if(c.options?.share){const t=r.get(f);if(t)return a(t)}if(d(c.options.share?p:l,c.options?.aborts),c.options?.staleTime){const t=n.get(f);if(void 0!==t)return a(t)}const y=h(o,s);c.options?.share&&r.set(f,y),y.then((t=>{a(t),c.options?.staleTime&&n.set(f,t,(new Date).getTime()+c.options.staleTime)})).catch((t=>i(t)))})),s=y(((t,e,n,r,s,a,i)=>o(t,{...i,...a,method:e,params:n,query:r,body:s}))),a=h([...p,"head","options"],(t=>(e,n)=>o(e,{...n,method:t})));return{request:o,...s,...a}}export{b as createCache,A as createShare,y as createShortApi,h as createShortMethods,S as createSilentRefresh,w as createSoon,m as deepSort,g as genRequestKey,c as isBodyJson,a as mergeHeaders,f as mergeOptions,i as mergeSignals,s as mergeUrl,l as parseUrlOptions,u as parseWithBase,d as raceAbort};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "soon-fetch",
|
|
3
|
-
"version": "3.0.0
|
|
4
|
-
"description": "a 5Kb request lib alternative to axios",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "a 5Kb request lib alternative to axios with timeout, request reusing, race, response cache ...",
|
|
5
5
|
"homepage": "https://github.com/leafio/soon-fetch",
|
|
6
6
|
"main": "./dist/index.cjs.js",
|
|
7
7
|
"module": "/dist/index.js",
|