fexios
Version:
Fetch based HTTP client with similar API to axios for browser and Node.js
9 lines (8 loc) • 11.6 kB
JavaScript
(function(u,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(u=typeof globalThis<"u"?globalThis:u||self,d(u.Fexios={}))})(this,function(u){"use strict";var d=(t=>(t.BODY_USED="BODY_USED",t.NO_BODY_READER="NO_BODY_READER",t.TIMEOUT="TIMEOUT",t.NETWORK_ERROR="NETWORK_ERROR",t.BODY_NOT_ALLOWED="BODY_NOT_ALLOWED",t.HOOK_CONTEXT_CHANGED="HOOK_CONTEXT_CHANGED",t.ABORTED_BY_HOOK="ABORTED_BY_HOOK",t.INVALID_HOOK_CALLBACK="INVALID_HOOK_CALLBACK",t.UNEXPECTED_HOOK_RETURN="UNEXPECTED_HOOK_RETURN",t))(d||{});class b extends Error{constructor(s,r,e,o){super(r,o),this.code=s,this.context=e,this.name="FexiosError"}}class D extends b{constructor(s,r,e){super(r.statusText,s,void 0,e),this.response=r,this.name="FexiosResponseError"}}const W=t=>!(t instanceof D)&&t instanceof b;function L(t,s=2048){if(!(t instanceof Uint8Array))throw new TypeError("Input must be a Uint8Array");if(t.length===0)return!0;const r=Math.min(Math.max(t.length,256),s),e=t.slice(0,r);if(q(e))return!1;const o=K(e);if(o.nullByteRatio>.05||o.highByteRatio>.95)return!1;const a=["utf-8","utf-16le","utf-16be","iso-8859-1"];let n=-1,f=!1;for(const c of a)try{const i=new TextDecoder(c,{fatal:!0}).decode(e),l=M(i);l>n&&(n=l,f=l>.7)}catch{continue}return f}function q(t){if(t.length<4)return!1;const s=[[137,80,78,71],[255,216,255],[71,73,70],[37,80,68,70],[80,75,3,4],[80,75,5,6],[80,75,7,8],[127,69,76,70],[77,90],[202,254,186,190],[0,0,1,0],[82,73,70,70]];for(const r of s)if(t.length>=r.length){let e=!0;for(let o=0;o<r.length;o++)if(t[o]!==r[o]){e=!1;break}if(e)return!0}return!1}function K(t){let s=0,r=0,e=0;for(const o of t)o===0&&s++,o>127&&r++,(o<32&&o!==9&&o!==10&&o!==13||o===127)&&e++;return{nullByteRatio:s/t.length,highByteRatio:r/t.length,controlCharRatio:e/t.length}}function M(t){if(t.length===0)return 1;let s=1,r=0;for(let o=0;o<t.length;o++){const n=t[o].charCodeAt(0);n>=32&&n<=126||n===9||n===10||n===13||n===32?r++:n>127&&n<65534?!v(n)&&!x(n)&&r++:s-=.1}const e=r/t.length;return s*=e,F(t)&&(s*=1.1),Math.max(0,Math.min(1,s))}function v(t){return t>=0&&t<=31||t>=127&&t<=159}function x(t){return t>=57344&&t<=63743||t>=983040&&t<=1048573||t>=1048576&&t<=1114109}function F(t){return[/\b\w+\b/,/[.!?]+\s/,/\s+/,/[a-zA-Z]{3,}/,/[\u4e00-\u9fa5]+/,/\d+/].some(r=>r.test(t))}function S(t){if(typeof t!="object"||t===null||Object.prototype.toString.call(t)!=="[object Object]")return!1;const s=Object.getPrototypeOf(t);return s===Object.prototype||s===null}function k(t,s={}){const r={};return Object.entries(t).forEach(([e,o])=>{o!=null&&(s.dropEmptyString&&o===""||(r[e]=o))}),r}class w{constructor(s,r,e){this.rawResponse=s,this.data=r,this.ok=s.ok,this.status=s.status,this.statusText=s.statusText,this.headers=s.headers,Object.entries(e||{}).forEach(([o,a])=>{this[o]=a})}}async function A(t,s,r){var f;if(t.bodyUsed)throw new b(d.BODY_USED,"Response body has already been used or locked");const e=t.headers.get("content-type")||"",o=Number(t.headers.get("content-length"))||0,a=(c,h)=>h==="json"||c.startsWith("application/json"),n=(c,h,i)=>i==="blob"||c.startsWith("image/")&&!c.startsWith("image/svg")||c.startsWith("video/")||c.startsWith("audio/")||!L(h);if((t.status===101||t.status===426||t.headers.get("upgrade"))&&typeof globalThis.WebSocket<"u"){const c=new WebSocket(t.url);return await new Promise((h,i)=>{c.onopen=h,c.onerror=i}),new w(t,c,{ok:!0,status:101,statusText:"Switching Protocols"})}else if(e.startsWith("text/event-stream")&&!["text","json"].includes(s||"")&&typeof globalThis.EventSource<"u"){const c=new EventSource(t.url);return await new Promise((h,i)=>{c.onopen=h,c.onerror=i}),new w(t,c)}else{if(s==="stream")return new w(t,t.body);{const h=(f=t.clone().body)==null?void 0:f.getReader();if(!h)throw new b(d.NO_BODY_READER,"Failed to get ReadableStream from response body");let i=new Uint8Array;for(;;){const{done:m,value:O}=await h.read();if(m)break;if(O&&(i=new Uint8Array([...i,...O]),r&&o>0)){const g=Math.min(i.length/o,1);r(g,i)}}const l=new w(t,void 0);if(s==="arrayBuffer")return l.data=i.buffer,l;if(a(e,s))try{const m=new TextDecoder().decode(i);l.data=JSON.parse(m)}catch{}if(typeof l.data!="string"&&n(e,i,s)?l.data=new Blob([i],{type:t.headers.get("content-type")||void 0}):l.data=new TextDecoder().decode(i),typeof l.data=="string"&&s!=="text"){const m=l.data.trim(),O=m[0],g=m[m.length-1];if(O==="{"&&g==="}"||O==="["&&g==="]")try{l.data=JSON.parse(l.data)}catch{}}if(typeof l.data>"u"&&(l.data=i.length>0?i:void 0),l.ok)return l;throw new D(`Request failed with status code ${t.status}`,l)}}}class j{static makeSearchParams(s){const r=new URLSearchParams;return Object.entries(s).forEach(([e,o])=>{Array.isArray(o)?o.forEach(a=>r.append(e,String(a))):r.set(e,String(o))}),r}static makeQueryString(s){return this.makeSearchParams(s).toString()}}function Y(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var B,N;function $(){if(N)return B;N=1;function t(s){var r=this.constructor.prototype[s],e=function(){return r.apply(e,arguments)};return Object.setPrototypeOf(e,this.constructor.prototype),Object.getOwnPropertyNames(r).forEach(function(o){Object.defineProperty(e,o,Object.getOwnPropertyDescriptor(r,o))}),e}return t.prototype=Object.create(Function.prototype),B=t,B}var Q=$();const G=Y(Q);class T extends G{constructor(s={}){super("request"),this.baseConfigs=s,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=T.create,this.dropUndefinedAndNull=k,this.checkIsPlainObject=S,this.ALL_METHODS.forEach(this.createMethodShortcut.bind(this))}async request(s,r){var g,U,I,P;let e=r=r||{};typeof s=="string"||s instanceof URL?e.url=s.toString():typeof s=="object"&&(e={...s,...e}),e=await this.emit("beforeInit",e);const o=r.baseURL||this.baseConfigs.baseURL||((g=globalThis.location)==null?void 0:g.href),a=o?new URL(o,(U=globalThis.location)==null?void 0:U.href):void 0,n=new URL(e.url.toString(),a);e.url=n.href,e.baseURL=a?a.href:n.origin,e.headers=this.mergeHeaders(this.baseConfigs.headers,r.headers);const f=a==null?void 0:a.searchParams,c=new URLSearchParams(n.searchParams);if(n.search="",e.url=n.href,e.query=this.mergeQuery(f,this.baseConfigs.query,c,r.query),n.search=j.makeQueryString(e.query),e.url=n.toString(),this.METHODS_WITHOUT_BODY.includes((I=e.method)==null?void 0:I.toLocaleLowerCase())&&e.body)throw new b(d.BODY_NOT_ALLOWED,`Request method "${e.method}" does not allow body`);e=await this.emit("beforeRequest",e);let h;typeof e.body<"u"&&e.body!==null&&(e.body instanceof Blob||e.body instanceof FormData||e.body instanceof URLSearchParams?h=e.body:typeof e.body=="object"&&e.body!==null?(h=JSON.stringify(e.body),e.headers["content-type"]="application/json"):h=e.body),!((P=r.headers)!=null&&P["content-type"])&&h&&(h instanceof FormData||h instanceof URLSearchParams?delete e.headers["content-type"]:typeof h=="string"&&typeof e.body=="object"?e.headers["content-type"]="application/json":h instanceof Blob&&(e.headers["content-type"]=h.type)),e.body=h,e=await this.emit("afterBodyTransformed",e);const i=e.abortController||globalThis.AbortController?new AbortController:void 0,l=new Request(e.url,{method:e.method||"GET",credentials:e.credentials,cache:e.cache,mode:e.mode,headers:e.headers,body:e.body,signal:i==null?void 0:i.signal});e.rawRequest=l,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 E=setTimeout(()=>{R(new b(d.TIMEOUT,`WebSocket connection timed out after ${m}ms`,e))},m);y.onopen=()=>{clearTimeout(E),p()},y.onerror=C=>{clearTimeout(E),R(new b(d.NETWORK_ERROR,"WebSocket connection failed",e))},y.onclose=C=>{C.code!==1e3&&(clearTimeout(E),R(new b(d.NETWORK_ERROR,`WebSocket closed with code ${C.code}`,e)))}}),e.rawResponse=new Response,e.response=new w(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 b?y:new b(d.NETWORK_ERROR,`WebSocket creation failed: ${y}`,e)}let O;try{i&&(O=setTimeout(()=>{i.abort()},m));const y=await fetch(e.rawRequest).catch(p=>{throw O&&clearTimeout(O),i!=null&&i.signal.aborted?new b(d.TIMEOUT,`Request timed out after ${m}ms`,e):new b(d.NETWORK_ERROR,p.message,e)});return O&&clearTimeout(O),e.rawResponse=y,e.response=await A(y,e.responseType,(p,R)=>{var E;(E=r==null?void 0:r.onProgress)==null||E.call(r,p,R)}),e.data=e.response.data,e.headers=e.response.headers,this.emit("afterResponse",e)}catch(y){throw O&&clearTimeout(O),y}}mergeQuery(s,...r){const e={},o=a=>{a&&(S(a)?Object.entries(a).forEach(([n,f])=>{f==null?delete e[n]:Array.isArray(f)?(n.endsWith("[]"),e[n]=f.map(String)):e[n]=String(f)}):new URLSearchParams(a).forEach((f,c)=>{e[c]=f}))};return o(s),r.forEach(o),e}mergeHeaders(s,...r){const e={},o=new Headers(s);for(const a of r){if(a==null)continue;if(S(a)){const f=k(a);if(Object.keys(f).length===0)continue;new Headers(f).forEach((h,i)=>{o.set(i,h)})}else new Headers(a).forEach((c,h)=>{o.set(h,c)})}return o.forEach((a,n)=>{e[n]=a}),e}async emit(s,r){const e=this.hooks.filter(o=>o.event===s);try{let o=0;for(const a of e){const n=`${s}#${a.action.name||`anonymous#${o}`}`,f=Symbol("FexiosHookContext");r[f]=f;const c=await a.action.call(this,r);if(c===!1)throw new b(d.ABORTED_BY_HOOK,`Request aborted by hook "${n}"`,r);if(typeof c=="object"&&c[f]===f)r=c;else{const h=globalThis["".concat("console")];try{throw new b(d.HOOK_CONTEXT_CHANGED,`Hook "${n}" should return the original FexiosContext or return false to abort the request, but got "${c}".`)}catch(i){h.warn(i.stack||i)}}delete r[f],o++}}catch(o){return Promise.reject(o)}return r}on(s,r,e=!1){if(typeof r!="function")throw new b(d.INVALID_HOOK_CALLBACK,`Hook should be a function, but got "${typeof r}"`);return this.hooks[e?"unshift":"push"]({event:s,action:r}),this}off(s,r){return s==="*"||!s?this.hooks=this.hooks.filter(e=>e.action!==r):this.hooks=this.hooks.filter(e=>e.event!==s||e.action!==r),this}createInterceptor(s){return{handlers:()=>this.hooks.filter(r=>r.event===s).map(r=>r.action),use:(r,e=!1)=>this.on(s,r,e),clear:()=>{this.hooks=this.hooks.filter(r=>r.event!==s)}}}createMethodShortcut(s){return Object.defineProperty(this,s,{value:(r,e,o)=>(this.METHODS_WITHOUT_BODY.includes(s.toLocaleLowerCase())?o=e:(o=o||{},o.body=e),this.request(r,{...o,method:s}))}),this}extends(s){const r=new T({...this.baseConfigs,...s});return r.hooks=[...this.hooks],r}static create(s){return new T(s)}}/**
* 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=T.create,_=H();typeof globalThis<"u"?globalThis.fexios=_:typeof window<"u"&&(window.fexios=_),u.Fexios=T,u.FexiosError=b,u.FexiosErrorCodes=d,u.FexiosQueryBuilder=j,u.FexiosResponse=w,u.FexiosResponseError=D,u.checkIfTextData=L,u.checkIsPlainObject=S,u.createFexios=H,u.default=_,u.dropUndefinedAndNull=k,u.fexios=_,u.isFexiosError=W,u.resolveResponseBody=A,Object.defineProperties(u,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
//# sourceMappingURL=index.umd.cjs.map