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

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
@@ -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");
@@ -211,7 +216,9 @@ type SoonOptions = Omit<RequestInit, "body"> & {
211
216
 
212
217
  - [快捷方法](#快捷方法)
213
218
  - [Restful Url 参数自动处理](#restful-url-参数自动处理)
219
+ - [共享未完成的请求](#共享未完成的请求)
214
220
  - [超时](#超时)
221
+ - [缓存](#缓存) -[请求竞态](#请求竞态)
215
222
  - [快速定义 API](#快速定义-api)
216
223
 
217
224
  - [API](#api-1)
@@ -222,21 +229,26 @@ type SoonOptions = Omit<RequestInit, "body"> & {
222
229
  > [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
223
230
 
224
231
  ```typescript
225
- const request = <T>(url: string, options?: SoonOptions): Promise<T> => {
226
- const [_url, _options] = parseUrlOptions({
232
+ const soon = createSoon(
233
+ (url, options) => ({
227
234
  url,
228
235
  options,
229
236
  baseURL: "/api",
230
237
  baseOptions: {
231
- timeout: 20 * 1000,
232
- headers: { Authorization: localStorage.getItem("token") ?? "" },
238
+ headers: new Headers({
239
+ Authorization: "Bearer " + localStorage.getItem("token"),
240
+ }),
241
+ timeout: 5000,
233
242
  },
234
- });
235
-
236
- return fetch(_url, _options).then((res) => res.json());
237
- };
238
-
239
- const soon = createSoon(request);
243
+ }),
244
+ ({ parsed }) => {
245
+ return <T>() => {
246
+ return fetch(parsed.url, parsed.options).then((res) =>
247
+ res.json()
248
+ ) as Promise<T>;
249
+ };
250
+ }
251
+ );
240
252
 
241
253
  /** GET */
242
254
  soon.get("/user?id=123");
@@ -296,7 +308,7 @@ soon.get(url, { timeout: 1000 * 20 });
296
308
  soon.get(url, { share: true });
297
309
  ```
298
310
 
299
- ##### 缓存响应
311
+ ##### 缓存
300
312
 
301
313
  如果在指定时间内再次发起相同的请求,则会返回缓存的响应。
302
314
 
@@ -304,7 +316,7 @@ soon.get(url, { share: true });
304
316
  soon.get(url, { staleTime: 1000 * 60 * 5 });
305
317
  ```
306
318
 
307
- ##### 请求竞争
319
+ ##### 请求竞态
308
320
 
309
321
  如果在第一个请求完成之前发起第二个请求,则会中止第一个请求,以避免因响应顺序错乱导致的问题。
310
322
 
@@ -376,6 +388,9 @@ type SoonOptions = Omit<RequestInit, "body"> & {
376
388
  params?: Record<string, string | number>;
377
389
  timeout?: number;
378
390
  body?: RequestInit["body"] | object;
391
+ aborts?: AbortController[];
392
+ share?: boolean;
393
+ staleTime?: number;
379
394
  };
380
395
  ```
381
396
 
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=[],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 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(r(e))return e;const s=r(o)?o:n(o);return t(s)+t(n(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,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 t=Object.assign({},...e),n=a(...e.map((e=>e?.headers)));return t.headers=n,t.signal=i(e.map((e=>e?.signal)),t.timeout),t}function p(e){const{url:t,options:n,baseURL:r,baseOptions:o}=e,a=f(o,n),p=s(t,{...a,baseURL:r}),u=a?.body,l=c(u);return a.body=l?JSON.stringify(u):u,l&&a.headers.append("Content-Type","application/json"),a.signal=i([a.signal]),[p,a]}const u=["get","post","put","delete","patch"];function l(e,t){const n={};return e.forEach((e=>{n[e]=t(e)})),n}function h(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,p=c?a.shift():void 0,u=i?a.shift():void 0,l=a.shift();return t(n,r,f,p,u,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("soon-fetch 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((n=>{t[n]=d(e[n])})),t}return e}function g(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 b(){const e={},t=t=>e[t]=void 0;return{get:t=>e[t],set:(n,r)=>{e[n]=r,r.finally((()=>t(n)))}}}exports.createCache=m,exports.createShare=b,exports.createShortApi=h,exports.createShortMethods=l,exports.createSilentRefresh=function(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=[]})))}},exports.createSoon=function(e,t){const n=m(),r=b(),o=(o,s)=>new Promise(((a,c)=>{const f=new AbortController,u=e(o,s),[l,h]=p(u);console.log("[init]",l,h,u),h.signal=i([f.signal,h.signal]);const d={url:l,options:h},m=t({parsed:d}),b=g({url:d.url,...d.options,headers:new Headers(d.options?.headers)});if(d.options?.share){const e=r.get(b);if(e)return a(e)}if(d.options?.staleTime){const e=n.get(b);if(void 0!==e)return a(e)}console.log("abort",b,d.options),y(f,d.options?.aborts);const A=m(o,s);d.options?.share&&r.set(b,A),A.then((e=>{a(e),d.options?.staleTime&&n.set(b,e,(new Date).getTime()+d.options.staleTime)})).catch((e=>c(e)))})),s=h(((e,t,n,r,s,a,i)=>o(e,{...i,...a,method:t,params:n,query:r,body:s}))),a=l([...u,"head","options"],(e=>(t,n)=>o(t,{...n,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=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,20 @@ 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
+ };
24
27
  declare function parseUrlOptions<Options extends SoonOptions>(urlOptions: {
25
28
  url: string;
26
29
  options?: Options;
27
30
  baseURL?: string;
28
31
  baseOptions?: Options;
29
- }): readonly [string, Options & {
32
+ }): [url: string, options: Options & {
30
33
  headers: Headers;
31
34
  body?: BodyInit | null;
32
- }, boolean];
35
+ }];
33
36
  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): {
37
+ 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
38
  GET: <Url extends string>(url: Url) => {
36
39
  Send: <Res>(options?: Parameters<Wrapper>[5]) => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, options?: Parameters<Wrapper>[5] | undefined]) => Promise<Res>;
37
40
  Query: <Query>() => unknown extends Query ? never : {
@@ -71,8 +74,19 @@ declare function createShare(): {
71
74
  set: (key: string, value: Promise<any>) => void;
72
75
  };
73
76
  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;
77
+ declare function createSoon<Options extends SoonOptions>(getConfig: (url: string, options?: Options) => {
78
+ url: string;
79
+ options?: Options;
80
+ baseURL: string;
81
+ baseOptions?: Options;
82
+ }, wrapper: (instance: {
83
+ parsed: {
84
+ url: string;
85
+ options: Options & {
86
+ headers: Headers;
87
+ body?: BodyInit | null;
88
+ };
89
+ };
76
90
  }) => <T>(url: string, options?: Options) => Promise<T>): {
77
91
  options: <T>(url: string, options?: Options) => Promise<T>;
78
92
  get: <T>(url: string, options?: Options) => Promise<T>;
@@ -135,7 +149,7 @@ declare function createSoon<Options extends SoonOptions>(wrapper: (instance: {
135
149
  };
136
150
  };
137
151
  };
138
- request: <T>(url: string, options: Options) => Promise<T>;
152
+ request: <T>(url: string, options?: Options) => Promise<T>;
139
153
  };
140
154
 
141
- export { type SoonOptions, createCache, createShare, createShortApi, createShortMethods, createSilentRefresh, createSoon, deepSort, genRequestKey, isBodyJson, mergeHeaders, mergeSignals, mergeUrl, parseUrlOptions, raceAbort };
155
+ export { type SoonOptions, createCache, createShare, createShortApi, createShortMethods, createSilentRefresh, createSoon, deepSort, genRequestKey, isBodyJson, mergeHeaders, mergeOptions, mergeSignals, mergeUrl, parseUrlOptions, 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);return a.body=p?JSON.stringify(l):l,p&&a.headers.append("Content-Type","application/json"),a.signal=i([a.signal]),[u,a]}const l=["get","post","put","delete","patch"];function p(t,e){const n={};return t.forEach((t=>{n[t]=e(t)})),n}function h(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 l.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 y(t,e){e&&(e.pop()?.abort("soon-fetch race abort"),e.push(t))}function d(t){if(Array.isArray(t))return t.map(d);if("object"==typeof t&&null!==t){const e={};return Object.keys(t).sort().forEach((n=>{e[n]=d(t[n])})),e}return t}function g(t){const{url:e,headers:n,method:r,body:o,query:s,params:a}=t,i=d(Object.fromEntries(n?.entries()??[]));return(r??"get").toLowerCase()+e+JSON.stringify(d(s)??"")+JSON.stringify(d(a)??"")+JSON.stringify(i)+("object"==typeof o&&null!=o?JSON.stringify(d(o)):o)}function m(){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 b(){const t={},e=e=>t[e]=void 0;return{get:e=>t[e],set:(n,r)=>{t[n]=r,r.finally((()=>e(n)))}}}function A(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 S(t,e){const n=m(),r=b(),o=(o,s)=>new Promise(((a,c)=>{const f=new AbortController,l=t(o,s),[p,h]=u(l);console.log("[init]",p,h,l),h.signal=i([f.signal,h.signal]);const d={url:p,options:h},m=e({parsed:d}),b=g({url:d.url,...d.options,headers:new Headers(d.options?.headers)});if(d.options?.share){const t=r.get(b);if(t)return a(t)}if(d.options?.staleTime){const t=n.get(b);if(void 0!==t)return a(t)}console.log("abort",b,d.options),y(f,d.options?.aborts);const A=m(o,s);d.options?.share&&r.set(b,A),A.then((t=>{a(t),d.options?.staleTime&&n.set(b,t,(new Date).getTime()+d.options.staleTime)})).catch((t=>c(t)))})),s=h(((t,e,n,r,s,a,i)=>o(t,{...i,...a,method:e,params:n,query:r,body:s}))),a=p([...l,"head","options"],(t=>(e,n)=>o(e,{...n,method:t})));return{request:o,...s,...a}}export{m as createCache,b as createShare,h as createShortApi,p as createShortMethods,A as createSilentRefresh,S as createSoon,d as deepSort,g as genRequestKey,c as isBodyJson,a as mergeHeaders,f as mergeOptions,i as mergeSignals,s as mergeUrl,u as parseUrlOptions,y 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.3",
4
+ "description": "a 5Kb request lib alternative to axios",
5
5
  "homepage": "https://github.com/leafio/soon-fetch",
6
6
  "main": "./dist/index.cjs.js",
7
7
  "module": "/dist/index.js",