UNPKG

fexios

Version:

Fetch based HTTP client with similar API to axios for browser and Node.js

9 lines (8 loc) 10.2 kB
(function(l,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(l=typeof globalThis<"u"?globalThis:l||self,d(l.Fexios={}))})(this,function(l){"use strict";var d=(s=>(s.BODY_USED="BODY_USED",s.NO_BODY_READER="NO_BODY_READER",s.TIMEOUT="TIMEOUT",s.NETWORK_ERROR="NETWORK_ERROR",s.BODY_NOT_ALLOWED="BODY_NOT_ALLOWED",s.HOOK_CONTEXT_CHANGED="HOOK_CONTEXT_CHANGED",s.ABORTED_BY_HOOK="ABORTED_BY_HOOK",s.INVALID_HOOK_CALLBACK="INVALID_HOOK_CALLBACK",s.UNEXPECTED_HOOK_RETURN="UNEXPECTED_HOOK_RETURN",s))(d||{});class u extends Error{constructor(r,t,e,o){super(t,o),this.code=r,this.context=e,this.name="FexiosError"}}class D extends u{constructor(r,t,e){super(t.statusText,r,void 0,e),this.response=t,this.name="FexiosResponseError"}}const q=s=>!(s instanceof D)&&s instanceof u;function N(s,r=1024){if(!(s instanceof Uint8Array))throw new TypeError("Input must be a Uint8Array");const t=s.slice(0,r),e=new TextDecoder("utf-8",{fatal:!0});try{const o=e.decode(t),n=/[\x00-\x08\x0E-\x1F\x7F]/g,h=o.match(n);return!(h&&h.length/o.length>.1)}catch{return!1}}function _(s){if(typeof s!="object"||s===null||Object.prototype.toString.call(s)!=="[object Object]")return!1;const r=Object.getPrototypeOf(s);return r===Object.prototype||r===null}function k(s,r={}){const t={};return Object.entries(s).forEach(([e,o])=>{o!=null&&(r.dropEmptyString&&o===""||(t[e]=o))}),t}class g{constructor(r,t,e){this.rawResponse=r,this.data=t,this.ok=r.ok,this.status=r.status,this.statusText=r.statusText,this.headers=r.headers,Object.entries(e||{}).forEach(([o,n])=>{this[o]=n})}}async function j(s,r,t){var f;if(s.bodyUsed)throw new u(d.BODY_USED,"Response body has already been used or locked");const e=s.headers.get("content-type")||"",o=Number(s.headers.get("content-length"))||0,n=(i,c)=>c==="json"||i.startsWith("application/json"),h=(i,c,a)=>a==="blob"||i.startsWith("image/")||i.startsWith("video/")||i.startsWith("audio/")||!N(c);if((s.status===101||s.status===426||s.headers.get("upgrade"))&&typeof globalThis.WebSocket<"u"){const i=new WebSocket(s.url);return await new Promise((c,a)=>{i.onopen=c,i.onerror=a}),new g(s,i,{ok:!0,status:101,statusText:"Switching Protocols"})}else if(e.startsWith("text/event-stream")&&!["text","json"].includes(r||"")&&typeof globalThis.EventSource<"u"){const i=new EventSource(s.url);return await new Promise((c,a)=>{i.onopen=c,i.onerror=a}),new g(s,i)}else{if(r==="stream")return new g(s,s.body);{const c=(f=s.clone().body)==null?void 0:f.getReader();if(!c)throw new u(d.NO_BODY_READER,"Failed to get ReadableStream from response body");let a=new Uint8Array;for(;;){const{done:m,value:O}=await c.read();if(m)break;if(O&&(a=new Uint8Array([...a,...O]),t&&o>0)){const w=Math.min(a.length/o,1);t(w,a)}}const b=new g(s,void 0);if(h(e,a,r)?b.data=new Blob([a],{type:s.headers.get("content-type")||void 0}):b.data=new TextDecoder().decode(a),n(e,r))try{b.data=JSON.parse(b.data)}catch{}if(typeof b.data=="string"&&r!=="text"){const m=b.data.trim(),O=m[0],w=m[m.length-1];if(O==="{"&&w==="}"||O==="["&&w==="]")try{b.data=JSON.parse(b.data)}catch{}}if(typeof b.data>"u"&&(b.data=a.length>0?a:void 0),b.ok)return b;throw new D(`Request failed with status code ${s.status}`,b)}}}class I{static makeSearchParams(r){const t=new URLSearchParams;return Object.entries(r).forEach(([e,o])=>{Array.isArray(o)?o.forEach(n=>t.append(e,String(n))):t.set(e,String(o))}),t}static makeQueryString(r){return this.makeSearchParams(r).toString()}}function W(s){return s&&s.__esModule&&Object.prototype.hasOwnProperty.call(s,"default")?s.default:s}var U,P;function x(){if(P)return U;P=1;function s(r){var t=this.constructor.prototype[r],e=function(){return t.apply(e,arguments)};return Object.setPrototypeOf(e,this.constructor.prototype),Object.getOwnPropertyNames(t).forEach(function(o){Object.defineProperty(e,o,Object.getOwnPropertyDescriptor(t,o))}),e}return s.prototype=Object.create(Function.prototype),U=s,U}var K=x();const F=W(K);class E extends F{constructor(r={}){super("request"),this.baseConfigs=r,this.hooks=[],this.DEFAULT_CONFIGS={baseURL:"",timeout:60*1e3,credentials:"same-origin",headers:{},query:{},responseType:void 0},this.ALL_METHODS=["get","post","put","patch","delete","head","options","trace"],this.METHODS_WITHOUT_BODY=["get","head","options","trace"],this.interceptors={request:this.createInterceptor("beforeRequest"),response:this.createInterceptor("afterResponse")},this.create=E.create,this.dropUndefinedAndNull=k,this.checkIsPlainObject=_,this.ALL_METHODS.forEach(this.createMethodShortcut.bind(this))}async request(r,t){var w,A,C,B;let e=t=t||{};typeof r=="string"||r instanceof URL?e.url=r.toString():typeof r=="object"&&(e={...r,...e}),e=await this.emit("beforeInit",e);const o=t.baseURL||this.baseConfigs.baseURL||((w=globalThis.location)==null?void 0:w.href),n=o?new URL(o,(A=globalThis.location)==null?void 0:A.href):void 0,h=new URL(e.url.toString(),n);e.url=h.href,e.baseURL=n?n.href:h.origin,e.headers=this.mergeHeaders(this.baseConfigs.headers,t.headers);const f=n==null?void 0:n.searchParams,i=new URLSearchParams(h.searchParams);if(h.search="",e.url=h.href,e.query=this.mergeQuery(f,this.baseConfigs.query,i,t.query),h.search=I.makeQueryString(e.query),e.url=h.toString(),this.METHODS_WITHOUT_BODY.includes((C=e.method)==null?void 0:C.toLocaleLowerCase())&&e.body)throw new u(d.BODY_NOT_ALLOWED,`Request method "${e.method}" does not allow body`);e=await this.emit("beforeRequest",e);let c;typeof e.body<"u"&&e.body!==null&&(e.body instanceof Blob||e.body instanceof FormData||e.body instanceof URLSearchParams?c=e.body:typeof e.body=="object"&&e.body!==null?(c=JSON.stringify(e.body),e.headers["content-type"]="application/json"):c=e.body),!((B=t.headers)!=null&&B["content-type"])&&c&&(c instanceof FormData||c instanceof URLSearchParams?delete e.headers["content-type"]:typeof c=="string"&&typeof e.body=="object"?e.headers["content-type"]="application/json":c instanceof Blob&&(e.headers["content-type"]=c.type)),e.body=c,e=await this.emit("afterBodyTransformed",e);const a=e.abortController||globalThis.AbortController?new AbortController:void 0,b=new Request(e.url,{method:e.method||"GET",credentials:e.credentials,cache:e.cache,mode:e.mode,headers:e.headers,body:e.body,signal:a==null?void 0:a.signal});e.rawRequest=b,e=await this.emit("beforeActualFetch",e);const m=e.timeout||this.baseConfigs.timeout||60*1e3;if(e.url.startsWith("ws"))try{const y=new WebSocket(e.url);return await new Promise((p,R)=>{const T=setTimeout(()=>{R(new u(d.TIMEOUT,`WebSocket connection timed out after ${m}ms`,e))},m);y.onopen=()=>{clearTimeout(T),p()},y.onerror=L=>{clearTimeout(T),R(new u(d.NETWORK_ERROR,"WebSocket connection failed",e))},y.onclose=L=>{L.code!==1e3&&(clearTimeout(T),R(new u(d.NETWORK_ERROR,`WebSocket closed with code ${L.code}`,e)))}}),e.rawResponse=new Response,e.response=new g(e.rawResponse,y,{ok:!0,status:101,statusText:"Switching Protocols"}),e.data=y,e.headers=new Headers,this.emit("afterResponse",e)}catch(y){throw y instanceof u?y:new u(d.NETWORK_ERROR,`WebSocket creation failed: ${y}`,e)}let O;try{a&&(O=setTimeout(()=>{a.abort()},m));const y=await fetch(e.rawRequest).catch(p=>{throw O&&clearTimeout(O),a!=null&&a.signal.aborted?new u(d.TIMEOUT,`Request timed out after ${m}ms`,e):new u(d.NETWORK_ERROR,p.message,e)});return O&&clearTimeout(O),e.rawResponse=y,e.response=await j(y,e.responseType,(p,R)=>{var T;(T=t==null?void 0:t.onProgress)==null||T.call(t,p,R)}),e.data=e.response.data,e.headers=e.response.headers,this.emit("afterResponse",e)}catch(y){throw O&&clearTimeout(O),y}}mergeQuery(r,...t){const e={},o=n=>{n&&(_(n)?Object.entries(n).forEach(([h,f])=>{f==null?delete e[h]:Array.isArray(f)?(h.endsWith("[]"),e[h]=f.map(String)):e[h]=String(f)}):new URLSearchParams(n).forEach((f,i)=>{e[i]=f}))};return o(r),t.forEach(o),e}mergeHeaders(r,...t){const e={},o=new Headers(r);for(const n of t){if(n==null)continue;if(_(n)){const f=k(n);if(Object.keys(f).length===0)continue;new Headers(f).forEach((c,a)=>{o.set(a,c)})}else new Headers(n).forEach((i,c)=>{o.set(c,i)})}return o.forEach((n,h)=>{e[h]=n}),e}async emit(r,t){const e=this.hooks.filter(o=>o.event===r);try{let o=0;for(const n of e){const h=`${r}#${n.action.name||`anonymous#${o}`}`,f=Symbol("FexiosHookContext");t[f]=f;const i=await n.action.call(this,t);if(i===!1)throw new u(d.ABORTED_BY_HOOK,`Request aborted by hook "${h}"`,t);if(typeof i=="object"&&i[f]===f)t=i;else{const c=globalThis["".concat("console")];try{throw new u(d.HOOK_CONTEXT_CHANGED,`Hook "${h}" should return the original FexiosContext or return false to abort the request, but got "${i}".`)}catch(a){c.warn(a.stack||a)}}delete t[f],o++}}catch(o){return Promise.reject(o)}return t}on(r,t,e=!1){if(typeof t!="function")throw new u(d.INVALID_HOOK_CALLBACK,`Hook should be a function, but got "${typeof t}"`);return this.hooks[e?"unshift":"push"]({event:r,action:t}),this}off(r,t){return r==="*"||!r?this.hooks=this.hooks.filter(e=>e.action!==t):this.hooks=this.hooks.filter(e=>e.event!==r||e.action!==t),this}createInterceptor(r){return{handlers:()=>this.hooks.filter(t=>t.event===r).map(t=>t.action),use:(t,e=!1)=>this.on(r,t,e),clear:()=>{this.hooks=this.hooks.filter(t=>t.event!==r)}}}createMethodShortcut(r){return Object.defineProperty(this,r,{value:(t,e,o)=>(this.METHODS_WITHOUT_BODY.includes(r.toLocaleLowerCase())?o=e:(o=o||{},o.body=e),this.request(t,{...o,method:r}))}),this}extends(r){const t=new E({...this.baseConfigs,...r});return t.hooks=[...this.hooks],t}static create(r){return new E(r)}}/** * Fexios * @desc Fetch based HTTP client with similar API to axios for browser and Node.js * * @license MIT * @author dragon-fish <dragon-fish@qq.com> */const H=E.create,S=H();typeof globalThis<"u"?globalThis.fexios=S:typeof window<"u"&&(window.fexios=S),l.Fexios=E,l.FexiosError=u,l.FexiosErrorCodes=d,l.FexiosQueryBuilder=I,l.FexiosResponse=g,l.FexiosResponseError=D,l.checkIfTextData=N,l.checkIsPlainObject=_,l.createFexios=H,l.default=S,l.dropUndefinedAndNull=k,l.fexios=S,l.isFexiosError=q,l.resolveResponseBody=j,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); //# sourceMappingURL=index.umd.cjs.map