UNPKG

soon-fetch

Version:

a request lib alternative to axios with timeout, request reusing, race, response cache ...

2 lines (1 loc) 5.61 kB
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[p,l]=u.split("?"),y=new URLSearchParams([...o(l),...o(i)]);let h=((t,o)=>{if(r(t))return t;const s=r(o)?o:n(o);return e(s)+e(n(t))})(p,f);return y.size&&(h=h+"?"+y),h},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=new FormData;return t&&"object"==typeof t&&!Array.isArray(t)&&Object.entries(t).forEach((([t,n])=>{if(null==n||"function"==typeof n)return;let r;r=n instanceof Blob?n:"object"==typeof n?JSON.stringify(n):String(n),e.append(t,r)})),e}function u(...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 p(t){const{url:e,options:n,baseURL:r,baseOptions:o}=t,a=u(o,n),f=s(e,{...a,baseURL:r}),p=a?.body,l=c(p);a.body=l?JSON.stringify(p):p,l&&a.headers.append("Content-Type","application/json");const y=new AbortController;return a.signal=i([a.signal,y.signal]),{url:f,options:a,is_body_json:l,abortController:y}}function l(t){const{url:e,options:n}=p(t);return[e,n]}const y=["get","post","put","delete","patch"];function h(t,e){const n={};return t.forEach((t=>{n[t]=e(t)})),n}function d(e){const n=(n,r,o)=>{const s=!!t(n).length;return(...t)=>{const a=[...t],{hasBody:i,hasQuery:c,isFormData:u}=o||{},p=s?a.shift():void 0,l=c?a.shift():void 0;let y=i?a.shift():void 0;u&&(y=f(y));const h=a.shift();return e(n,r,p,l,y,h,o?.options)}},r={};return y.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})}),FormData:()=>({Send:r=>n(e,t,{hasBody:!0,isFormData:!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})}),FormData:()=>({Send:r=>n(e,t,{hasBody:!0,hasQuery:!0,isFormData:!0,options:r})})})})})),r}function m(t,e){e&&(e.pop()?.abort("race abort"),e.push(t))}function g(t){if(Array.isArray(t))return t.map(g);if("object"==typeof t&&null!==t){const e={};return Object.keys(t).sort().forEach((n=>{e[n]=g(t[n])})),e}return t}function b(t){const{url:e,options:n}=t,{headers:r,method:o,body:s,query:a,params:i}=n??{},c=g(Object.fromEntries(new Headers(r).entries()??[]));return(o??"get").toLowerCase()+e+JSON.stringify(g(a)??"")+JSON.stringify(g(i)??"")+JSON.stringify(c)+("object"==typeof s&&null!=s?JSON.stringify(g(s)):s??"")}function A(){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=>{if(t instanceof Response)return t.clone();if("function"==typeof t||t instanceof Promise)return t;try{return structuredClone(t)}catch(e){return JSON.parse(JSON.stringify(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 S(){const t={},e=e=>t[e]=void 0;return{get:e=>t[e],set:(n,r)=>{t[n]=r,r.finally((()=>e(n)))}}}function w(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 E(t,e){const n=A(),r=S(),o=(o,s)=>new Promise(((a,i)=>{const c=t(o,s),f=p({url:o,options:s,...c}),u=b(f),{abortController:l}=f,y=new AbortController;y.signal.addEventListener("abort",(()=>{i(y.signal.reason)}));const h=e({parsed:{...f,requestKey:u}});if(f.options?.share){const t=r.get(u);if(t)return a(t)}if(m(f.options.share?y:l,f.options?.aborts),f.options?.staleTime){const t=n.get(u);if(void 0!==t)return a(t)}const d=h(o,s);f.options?.share&&r.set(u,d),d.then((t=>{a(t),f.options?.staleTime&&n.set(u,t,(new Date).getTime()+f.options.staleTime)})).catch((t=>i(t)))})),s=d(((t,e,n,r,s,a,i)=>o(t,{...i,...a,method:e,params:n,query:r,body:s}))),a=h([...y,"head","options"],(t=>(e,n)=>o(e,{...n,method:t})));return{request:o,...s,...a}}export{A as createCache,S as createShare,d as createShortApi,h as createShortMethods,w as createSilentRefresh,E as createSoon,g as deepSort,b as genRequestKey,c as isBodyJson,a as mergeHeaders,u as mergeOptions,i as mergeSignals,s as mergeUrl,l as parseUrlOptions,p as parseWithBase,m as raceAbort,f as toFormData};