soon-fetch 3.0.0-beta.2 → 3.0.0-beta.4

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
@@ -10,7 +10,7 @@
10
10
  > - ⭐ rapid define a request api
11
11
  > - ⌛ timeout disconnect
12
12
  > - 🔤 automatic parse or serialization of JSON
13
- > - 📏 .min size less than **3K**, smaller after zip
13
+ > - 📏 .min size less than **5K**, smaller after zip
14
14
  > - 💡 smart type tips with Typescript
15
15
 
16
16
  - [Example](#example)
@@ -18,35 +18,40 @@
18
18
  - [Shortcut](#shortcut)
19
19
  - [Restful Url Params](#restful-url-params)
20
20
  - [Timeout](#timeout)
21
+ - [Share pending request](#share-pending-request)
22
+ - [Cache response](#cache-response)
23
+ - [Request race](#request-race)
21
24
  - [Rapid Define APIs](#rapid-define-apis)
22
25
  - [API](#api)
23
26
 
24
-
25
27
  ### Example
26
28
 
27
29
  > [github: soon-admin-vue3 ](https://github.com/leafio/soon-admin-vue3)
28
30
  > [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
29
31
 
30
32
  ```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
- });
33
+ import { createSoon } from "soon-fetch";
34
+
35
+ const soon = createSoon(
36
+ (url, options) => ({
37
+ url,
38
+ options,
39
+ baseURL: "/api",
40
+ baseOptions: {
41
+ headers: new Headers({
42
+ Authorization: "Bearer " + localStorage.getItem("token"),
43
+ }),
44
+ timeout: 5000,
45
+ },
46
+ }),
47
+ ({ parsed }) => {
48
+ return <T>() => {
49
+ return fetch(parsed.url, parsed.options).then((res) =>
50
+ res.json()
51
+ ) as Promise<T>;
52
+ };
53
+ }
54
+ );
50
55
 
51
56
  /** GET */
52
57
  soon.get("/user?id=123");
@@ -145,7 +150,8 @@ export default function App() {
145
150
 
146
151
  ```typescript
147
152
  //can be GET POST PATCH PUT DELETE
148
-
153
+ soon.GET(url:string).Query<Query>().Send<Response>()
154
+ soon.POST(url:string).Body<Body>().Send<Response>()
149
155
  //define an api
150
156
  export const getUserInfo = soon.GET("/user/:id").Send();
151
157
  //then use in any where
@@ -211,7 +217,9 @@ type SoonOptions = Omit<RequestInit, "body"> & {
211
217
 
212
218
  - [快捷方法](#快捷方法)
213
219
  - [Restful Url 参数自动处理](#restful-url-参数自动处理)
220
+ - [共享未完成的请求](#共享未完成的请求)
214
221
  - [超时](#超时)
222
+ - [缓存](#缓存) -[请求竞态](#请求竞态)
215
223
  - [快速定义 API](#快速定义-api)
216
224
 
217
225
  - [API](#api-1)
@@ -222,21 +230,26 @@ type SoonOptions = Omit<RequestInit, "body"> & {
222
230
  > [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
223
231
 
224
232
  ```typescript
225
- const request = <T>(url: string, options?: SoonOptions): Promise<T> => {
226
- const [_url, _options] = parseUrlOptions({
233
+ const soon = createSoon(
234
+ (url, options) => ({
227
235
  url,
228
236
  options,
229
237
  baseURL: "/api",
230
238
  baseOptions: {
231
- timeout: 20 * 1000,
232
- headers: { Authorization: localStorage.getItem("token") ?? "" },
239
+ headers: new Headers({
240
+ Authorization: "Bearer " + localStorage.getItem("token"),
241
+ }),
242
+ timeout: 5000,
233
243
  },
234
- });
235
-
236
- return fetch(_url, _options).then((res) => res.json());
237
- };
238
-
239
- const soon = createSoon(request);
244
+ }),
245
+ ({ parsed }) => {
246
+ return <T>() => {
247
+ return fetch(parsed.url, parsed.options).then((res) =>
248
+ res.json()
249
+ ) as Promise<T>;
250
+ };
251
+ }
252
+ );
240
253
 
241
254
  /** GET */
242
255
  soon.get("/user?id=123");
@@ -296,7 +309,7 @@ soon.get(url, { timeout: 1000 * 20 });
296
309
  soon.get(url, { share: true });
297
310
  ```
298
311
 
299
- ##### 缓存响应
312
+ ##### 缓存
300
313
 
301
314
  如果在指定时间内再次发起相同的请求,则会返回缓存的响应。
302
315
 
@@ -304,7 +317,7 @@ soon.get(url, { share: true });
304
317
  soon.get(url, { staleTime: 1000 * 60 * 5 });
305
318
  ```
306
319
 
307
- ##### 请求竞争
320
+ ##### 请求竞态
308
321
 
309
322
  如果在第一个请求完成之前发起第二个请求,则会中止第一个请求,以避免因响应顺序错乱导致的问题。
310
323
 
@@ -335,19 +348,22 @@ export default function App() {
335
348
 
336
349
  ##### 快速定义 API
337
350
 
338
- ```typescript
351
+ ```ts
339
352
  //可以是 GET POST PATCH PUT DELETE
340
353
  //GET 请求数据传递至query,其他方法请求数据传递至body
341
- soon.API(url:string).POST<RequestType,ResponseType>()
354
+ soon.GET(url:string).Query<Query>().Send<Response>()
355
+ soon.POST(url:string).Body<Body>().Send<Response>()
342
356
 
343
357
  //定义一个api
344
- export const getUserInfo=soon.API('/user/:id').GET()
358
+ export const getUserInfo=soon.GET('/user/:id').Send()
345
359
  //使用
346
360
  getUserInfo({id:2}).then(res=>console.log(res))
347
361
 
348
362
  //用typescript,
349
- export const login=soon.API('/user/login')
350
- .POST<{username:string,password:string},{token:string}>()
363
+ export const login=soon
364
+ .POST('/user/login')
365
+ .Body<{username:string,password:string}>()
366
+ .Send<{token:string}>()
351
367
  //开发工具会有请求和响应的智能提醒
352
368
  login({username:'admin',password:'123'}).then(res=>{
353
369
  localStorage.setItem('token', res.token);
@@ -376,6 +392,9 @@ type SoonOptions = Omit<RequestInit, "body"> & {
376
392
  params?: Record<string, string | number>;
377
393
  timeout?: number;
378
394
  body?: RequestInit["body"] | object;
395
+ aborts?: AbortController[];
396
+ share?: boolean;
397
+ staleTime?: number;
379
398
  };
380
399
  ```
381
400
 
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("?"),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;
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
@@ -1,8 +1,8 @@
1
1
  type SoonOptions = Omit<RequestInit, "body"> & {
2
+ body?: RequestInit["body"] | object;
2
3
  query?: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams;
3
4
  params?: Record<string, string | number>;
4
5
  timeout?: number;
5
- body?: RequestInit["body"] | object;
6
6
  aborts?: AbortController[];
7
7
  share?: boolean;
8
8
  staleTime?: number;
@@ -21,17 +21,34 @@ declare const mergeUrl: (url: string, config: {
21
21
  declare const mergeHeaders: (...headersList: (HeadersInit | undefined)[]) => Headers;
22
22
  declare function mergeSignals(signals?: (AbortSignal | null | undefined)[], timeout?: number): AbortSignal | undefined;
23
23
  declare function isBodyJson(body: any): boolean;
24
+ declare function mergeOptions<Options extends SoonOptions>(...optionsList: (Options | undefined)[]): Options & {
25
+ headers: Headers;
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
+ };
24
41
  declare function parseUrlOptions<Options extends SoonOptions>(urlOptions: {
25
42
  url: string;
26
43
  options?: Options;
27
44
  baseURL?: string;
28
45
  baseOptions?: Options;
29
- }): readonly [string, Options & {
46
+ }): [url: string, options: Options & {
30
47
  headers: Headers;
31
48
  body?: BodyInit | null;
32
- }, boolean];
49
+ }];
33
50
  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): {
51
+ 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: RequestInit["body"] | object, options?: any, defineOptions?: any) => Promise<T>>(wrapper: Wrapper): {
35
52
  GET: <Url extends string>(url: Url) => {
36
53
  Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
37
54
  Query: <Query>() => unknown extends Query ? never : {
@@ -55,11 +72,13 @@ declare function raceAbort(abortController: AbortController, controllers?: Abort
55
72
  declare function deepSort(obj: unknown): unknown;
56
73
  declare function genRequestKey(req: {
57
74
  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>;
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
+ };
63
82
  }): string;
64
83
  declare function createCache(): {
65
84
  get: (key: string) => unknown;
@@ -71,8 +90,22 @@ declare function createShare(): {
71
90
  set: (key: string, value: Promise<any>) => void;
72
91
  };
73
92
  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;
93
+ declare function createSoon<Options extends SoonOptions>(getConfig: (url: string, options?: Options) => {
94
+ url: string;
95
+ options?: Options;
96
+ baseURL?: string;
97
+ baseOptions?: Options;
98
+ }, wrapper: (instance: {
99
+ parsed: {
100
+ url: string;
101
+ options: Options & {
102
+ headers: Headers;
103
+ body?: RequestInit["body"];
104
+ };
105
+ is_body_json: boolean;
106
+ abortController: AbortController;
107
+ requestKey: string;
108
+ };
76
109
  }) => <T>(url: string, options?: Options) => Promise<T>): {
77
110
  options: <T>(url: string, options?: Options) => Promise<T>;
78
111
  get: <T>(url: string, options?: Options) => Promise<T>;
@@ -135,7 +168,7 @@ declare function createSoon<Options extends SoonOptions>(wrapper: (instance: {
135
168
  };
136
169
  };
137
170
  };
138
- request: <T>(url: string, options: Options) => Promise<T>;
171
+ request: <T>(url: string, options?: Options) => Promise<T>;
139
172
  };
140
173
 
141
- export { type SoonOptions, createCache, createShare, createShortApi, createShortMethods, createSilentRefresh, createSoon, deepSort, genRequestKey, isBodyJson, mergeHeaders, 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 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};
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-beta.2",
4
- "description": "a 3Kb request lib alternative to axios",
3
+ "version": "3.0.0-beta.4",
4
+ "description": "a 5Kb request lib alternative to axios with timeout, request reusing ,cache ,race ...",
5
5
  "homepage": "https://github.com/leafio/soon-fetch",
6
6
  "main": "./dist/index.cjs.js",
7
7
  "module": "/dist/index.js",