fexios
Version:
Fetch based HTTP client with similar API to axios for browser and Node.js
9 lines (8 loc) • 11.4 kB
JavaScript
var Fexios=function(u){"use strict";var b=(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))(b||{});class d extends Error{constructor(s,r,e,o){super(r,o),this.code=s,this.context=e,this.name="FexiosError"}}class D extends d{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 d;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 i=["utf-8","utf-16le","utf-16be","iso-8859-1"];let n=-1,l=!1;for(const c of i)try{const a=new TextDecoder(c,{fatal:!0}).decode(e),f=M(a);f>n&&(n=f,l=f>.7)}catch{continue}return l}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)&&!F(n)&&r++:s-=.1}const e=r/t.length;return s*=e,Y(t)&&(s*=1.1),Math.max(0,Math.min(1,s))}function v(t){return t>=0&&t<=31||t>=127&&t<=159}function F(t){return t>=57344&&t<=63743||t>=983040&&t<=1048573||t>=1048576&&t<=1114109}function Y(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,i])=>{this[o]=i})}}async function A(t,s,r){var l;if(t.bodyUsed)throw new d(b.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,i=(c,h)=>h==="json"||c.startsWith("application/json"),n=(c,h,a)=>a==="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,a)=>{c.onopen=h,c.onerror=a}),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,a)=>{c.onopen=h,c.onerror=a}),new w(t,c)}else{if(s==="stream")return new w(t,t.body);{const h=(l=t.clone().body)==null?void 0:l.getReader();if(!h)throw new d(b.NO_BODY_READER,"Failed to get ReadableStream from response body");let a=new Uint8Array;for(;;){const{done:O,value:g}=await h.read();if(O)break;if(g&&(a=new Uint8Array([...a,...g]),r&&o>0)){const m=Math.min(a.length/o,1);r(m,a)}}const f=new w(t,void 0);if(s==="arrayBuffer")return f.data=a.buffer,f;if(i(e,s))try{const O=new TextDecoder().decode(a);f.data=JSON.parse(O)}catch{}if(typeof f.data!="string"&&n(e,a,s)?f.data=new Blob([a],{type:t.headers.get("content-type")||void 0}):f.data=new TextDecoder().decode(a),typeof f.data=="string"&&s!=="text"){const O=f.data.trim(),g=O[0],m=O[O.length-1];if(g==="{"&&m==="}"||g==="["&&m==="]")try{f.data=JSON.parse(f.data)}catch{}}if(typeof f.data>"u"&&(f.data=a.length>0?a:void 0),f.ok)return f;throw new D(`Request failed with status code ${t.status}`,f)}}}class N{static makeSearchParams(s){const r=new URLSearchParams;return Object.entries(s).forEach(([e,o])=>{Array.isArray(o)?o.forEach(i=>r.append(e,String(i))):r.set(e,String(o))}),r}static makeQueryString(s){return this.makeSearchParams(s).toString()}}function x(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var B,j;function $(){if(j)return B;j=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=x(Q);class T extends G{constructor(s={}){super("request"),this.baseConfigs=s,this.hooks=[],this.DEFAULT_CONFIGS={baseURL:"",timeout:6e4,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 m,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||((m=globalThis.location)==null?void 0:m.href),i=o?new URL(o,(U=globalThis.location)==null?void 0:U.href):void 0,n=new URL(e.url.toString(),i);e.url=n.href,e.baseURL=i?i.href:n.origin,e.headers=this.mergeHeaders(this.baseConfigs.headers,r.headers);const l=i==null?void 0:i.searchParams,c=new URLSearchParams(n.searchParams);if(n.search="",e.url=n.href,e.query=this.mergeQuery(l,this.baseConfigs.query,c,r.query),n.search=N.makeQueryString(e.query),e.url=n.toString(),this.METHODS_WITHOUT_BODY.includes((I=e.method)==null?void 0:I.toLocaleLowerCase())&&e.body)throw new d(b.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 a=e.abortController||globalThis.AbortController?new AbortController:void 0,f=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=f,e=await this.emit("beforeActualFetch",e);const O=e.timeout||this.baseConfigs.timeout||60*1e3;if(e.url.startsWith("ws"))try{const y=new WebSocket(e.url);return await new Promise((R,p)=>{const E=setTimeout(()=>{p(new d(b.TIMEOUT,`WebSocket connection timed out after ${O}ms`,e))},O);y.onopen=()=>{clearTimeout(E),R()},y.onerror=C=>{clearTimeout(E),p(new d(b.NETWORK_ERROR,"WebSocket connection failed",e))},y.onclose=C=>{C.code!==1e3&&(clearTimeout(E),p(new d(b.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 d?y:new d(b.NETWORK_ERROR,`WebSocket creation failed: ${y}`,e)}let g;try{a&&(g=setTimeout(()=>{a.abort()},O));const y=await fetch(e.rawRequest).catch(R=>{throw g&&clearTimeout(g),a!=null&&a.signal.aborted?new d(b.TIMEOUT,`Request timed out after ${O}ms`,e):new d(b.NETWORK_ERROR,R.message,e)});return g&&clearTimeout(g),e.rawResponse=y,e.response=await A(y,e.responseType,(R,p)=>{var E;(E=r==null?void 0:r.onProgress)==null||E.call(r,R,p)}),e.data=e.response.data,e.headers=e.response.headers,this.emit("afterResponse",e)}catch(y){throw g&&clearTimeout(g),y}}mergeQuery(s,...r){const e={},o=i=>{i&&(S(i)?Object.entries(i).forEach(([n,l])=>{l==null?delete e[n]:Array.isArray(l)?(n.endsWith("[]"),e[n]=l.map(String)):e[n]=String(l)}):new URLSearchParams(i).forEach((l,c)=>{e[c]=l}))};return o(s),r.forEach(o),e}mergeHeaders(s,...r){const e={},o=new Headers(s);for(const i of r){if(i==null)continue;if(S(i)){const l=k(i);if(Object.keys(l).length===0)continue;new Headers(l).forEach((h,a)=>{o.set(a,h)})}else new Headers(i).forEach((c,h)=>{o.set(h,c)})}return o.forEach((i,n)=>{e[n]=i}),e}async emit(s,r){const e=this.hooks.filter(o=>o.event===s);try{let o=0;for(const i of e){const n=`${s}#${i.action.name||`anonymous#${o}`}`,l=Symbol("FexiosHookContext");r[l]=l;const c=await i.action.call(this,r);if(c===!1)throw new d(b.ABORTED_BY_HOOK,`Request aborted by hook "${n}"`,r);if(typeof c=="object"&&c[l]===l)r=c;else{const h=globalThis["".concat("console")];try{throw new d(b.HOOK_CONTEXT_CHANGED,`Hook "${n}" should return the original FexiosContext or return false to abort the request, but got "${c}".`)}catch(a){h.warn(a.stack||a)}}delete r[l],o++}}catch(o){return Promise.reject(o)}return r}on(s,r,e=!1){if(typeof r!="function")throw new d(b.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();return typeof globalThis<"u"?globalThis.fexios=_:typeof window<"u"&&(window.fexios=_),u.Fexios=T,u.FexiosError=d,u.FexiosErrorCodes=b,u.FexiosQueryBuilder=N,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"}}),u}({});
//# sourceMappingURL=index.iife.js.map