UNPKG

fexios

Version:

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

3 lines (2 loc) 18.6 kB
(function(u,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(u=typeof globalThis<"u"?globalThis:u||self,p(u.Fexios={}))})(this,(function(u){"use strict";var v=Object.defineProperty;var $=(u,p,w)=>p in u?v(u,p,{enumerable:!0,configurable:!0,writable:!0,value:w}):u[p]=w;var g=(u,p,w)=>$(u,typeof p!="symbol"?p+"":p,w);var p=(o=>(o.BODY_USED="BODY_USED",o.NO_BODY_READER="NO_BODY_READER",o.TIMEOUT="TIMEOUT",o.NETWORK_ERROR="NETWORK_ERROR",o.BODY_NOT_ALLOWED="BODY_NOT_ALLOWED",o.HOOK_CONTEXT_CHANGED="HOOK_CONTEXT_CHANGED",o.ABORTED_BY_HOOK="ABORTED_BY_HOOK",o.INVALID_HOOK_CALLBACK="INVALID_HOOK_CALLBACK",o.UNEXPECTED_HOOK_RETURN="UNEXPECTED_HOOK_RETURN",o.UNSUPPORTED_RESPONSE_TYPE="UNSUPPORTED_RESPONSE_TYPE",o.BODY_TRANSFORM_ERROR="BODY_TRANSFORM_ERROR",o))(p||{});class w extends Error{constructor(n,t,e,r){super(t,r);g(this,"name","FexiosError");this.code=n,this.context=e}static is(n,t){return!(n instanceof w)||n instanceof A?!1:t?n.code===t:!0}}class A extends w{constructor(n,t,e){super(t.statusText,n,void 0,e);g(this,"name","FexiosResponseError");this.response=t}static is(n){return n instanceof A}}const K=o=>w.is(o),H=function(o){const f=this.constructor.prototype,n=Reflect.get(f,o,f);function t(...e){return Reflect.apply(n,t,e)}Reflect.setPrototypeOf(t,f);for(const e of Reflect.ownKeys(n)){const r=Reflect.getOwnPropertyDescriptor(n,e);r&&Reflect.defineProperty(t,e,r)}return t};H.prototype=Object.create(Function.prototype);const C=H;function D(o){if(typeof globalThis.structuredClone=="function")try{return globalThis.structuredClone(o)}catch{}return _(o,new WeakMap)}function _(o,f){if(typeof o!="object"||o===null)return o;const n=f.get(o);if(n)return n;if(o instanceof Date)return new Date(o.getTime());if(o instanceof RegExp)return new RegExp(o.source,o.flags);if(o instanceof URL)return new URL(o.toString());if(o instanceof URLSearchParams)return new URLSearchParams(o.toString());if(o instanceof ArrayBuffer)return o.slice(0);if(ArrayBuffer.isView(o)){if(o instanceof DataView)return new DataView(o.buffer.slice(0),o.byteOffset,o.byteLength);const i=o,a=i.constructor;return new a(i.buffer.slice(0),i.byteOffset,i.length)}if(o instanceof Map){const i=new Map;return f.set(o,i),o.forEach((a,l)=>{i.set(_(l,f),_(a,f))}),i}if(o instanceof Set){const i=new Set;return f.set(o,i),o.forEach(a=>i.add(_(a,f))),i}if(Array.isArray(o)){const i=new Array(o.length);f.set(o,i);for(let a=0;a<i.length;a++)i[a]=_(o[a],f);return i}const t=Object.getPrototypeOf(o),e=Object.create(t);f.set(o,e);const r=Object.getOwnPropertyDescriptors(o);for(const i of Reflect.ownKeys(r)){const a=r[i];"value"in a&&(a.value=_(a.value,f)),Object.defineProperty(e,i,a)}return e}function T(o){if(typeof o!="object"||o===null)return!1;const f=Object.getPrototypeOf(o);return f===Object.prototype||f===null}function k(o,...f){const n=D(o||{});for(const t of f)if(t!=null)for(const e of Reflect.ownKeys(t)){const r=t[e];if(typeof r>"u")continue;if(r===null){delete n[e];continue}const i=n[e];T(i)&&T(r)?n[e]=k(i,r):n[e]=D(r)}return n}u.FexiosHeaderBuilder=void 0,(o=>{o.makeHeaders=f=>{if(!f)return new Headers;if(f instanceof Headers)return new Headers(f);const n=new Headers;if(f instanceof Map){for(const[t,e]of f.entries())if(e!=null)if(Array.isArray(e))for(const r of e)r!=null&&n.append(t,String(r));else n.append(t,String(e));return n}if(T(f)){for(const[t,e]of Object.entries(f))if(e!=null)if(Array.isArray(e))for(const r of e)r!=null&&n.append(t,String(r));else n.append(t,String(e));return n}throw new TypeError("only plain object, Map/ReadonlyMap, or Headers is supported")},o.toHeaderRecord=f=>{if(f instanceof Headers){const n={};return f.forEach((t,e)=>{n[e]=n[e]?[...n[e],t]:[t]}),n}if(f instanceof Map){const n={};for(const[t,e]of f.entries())if(e!=null)if(Array.isArray(e)){const r=e.filter(i=>i!=null).map(i=>String(i));r.length&&(n[t]=(n[t]??[]).concat(r))}else{const r=String(e);n[t]=n[t]?[...n[t],r]:[r]}return n}throw new TypeError(`unsupported type transformation, got: ${Object.prototype.toString.call(f)}`)},o.mergeHeaders=(...f)=>{const n=new Headers,t=e=>{for(const[r,i]of Object.entries(e))if(i!==void 0){if(i===null){n.delete(r);continue}if(Array.isArray(i)){n.delete(r);for(const a of i)a!=null&&n.append(r,String(a))}else n.set(r,String(i))}};for(const e of f){if(e==null)continue;if(e instanceof Headers){e.forEach((i,a)=>{n.set(a,i)});continue}if(T(e)){t(e);continue}const r=(0,o.toHeaderRecord)(e);for(const[i,a]of Object.entries(r)){n.delete(i);for(const l of a)n.append(i,l)}}return n}})(u.FexiosHeaderBuilder||(u.FexiosHeaderBuilder={})),u.FexiosQueryBuilder=void 0,(o=>{o.makeSearchParams=t=>{if(!t)return new URLSearchParams;if(t instanceof URLSearchParams)return t;if(typeof t!="object"||t?.constructor!==Object)throw new TypeError("only plain object is supported");const e=new URLSearchParams,r=(l,c)=>{c!=null&&e.append(l,c)},i=(l,c)=>{c!=null&&e.set(l,c)},a=(l,c)=>{if(c!=null){if(Array.isArray(c)){for(const d of c)r(l,d?.toString());return}if(typeof c=="object"&&c.constructor===Object){for(const[d,s]of Object.entries(c)){if(s==null)continue;const b=d.endsWith("[]"),h=b?d.slice(0,-2):d,m=`${l}[${h}]`;if(b){const E=`${m}[]`;if(Array.isArray(s))for(const L of s)r(E,L?.toString());else typeof s=="object"&&s!==null&&s.constructor===Object?a(`${m}[]`,s):r(E,s?.toString())}else if(Array.isArray(s))for(const E of s)r(m,E?.toString());else typeof s=="object"&&s!==null&&s.constructor===Object?a(m,s):i(m,s?.toString())}return}i(l,c?.toString())}};for(const[l,c]of Object.entries(t))a(l,c);return e},o.makeQueryString=t=>(0,o.makeSearchParams)(t).toString(),o.makeURL=(t,e,r,i)=>{const a=typeof window<"u"&&window.location?.origin||"http://localhost",l=typeof t=="string"?new URL(t,i??a):new URL(t),c=(0,o.toQueryRecord)(l.searchParams),d=(0,o.mergeQueries)(c,e||{}),s=(0,o.makeSearchParams)(d);return l.search=s.toString(),typeof r<"u"&&(l.hash=r),l},o.toQueryRecord=t=>{typeof t=="string"&&(t=(0,o.fromString)(t));const e={},r=a=>{if(!a.includes("["))return{path:[a],forceArray:!1};const c=[a.slice(0,a.indexOf("["))],d=/\[([^\]]*)\]/g;let s,b=!1,h=!1;for(;s=d.exec(a);)s[1]===""?(b=!0,h=!0):(c.push(s[1]),h=!1);return b&&h&&(c[c.length-1]=c[c.length-1]+"[]"),{path:c,forceArray:b}},i=(a,l,c,d)=>{let s=a;for(let b=0;b<l.length;b++){const h=l[b];b===l.length-1?d?s[h]===void 0?s[h]=[c]:Array.isArray(s[h])?s[h].push(c):s[h]=[s[h],c]:s[h]===void 0?s[h]=c:Array.isArray(s[h])?s[h].push(c):s[h]=[s[h],c]:((s[h]===void 0||typeof s[h]!="object"||Array.isArray(s[h]))&&(s[h]={}),s=s[h])}};for(const[a,l]of t.entries()){const{path:c,forceArray:d}=r(String(a));i(e,c,l?.toString(),d)}return e},o.fromString=t=>{const e=t.trim();if(!e)return new URLSearchParams;if(e.startsWith("?"))return new URLSearchParams(e.slice(1));const r=e.indexOf("?");if(r>=0){const i=e.indexOf("#",r+1),a=e.slice(r+1,i>=0?i:void 0);return new URLSearchParams(a)}return new URLSearchParams(e)},o.mergeQueries=(...t)=>{const e={};for(const r of t)r!=null&&n(e,f(r));return e};function f(t){if(!t)return{};if(t instanceof URLSearchParams||t instanceof FormData||t instanceof Map)return(0,o.toQueryRecord)(t);if(typeof t=="string")return(0,o.toQueryRecord)((0,o.fromString)(t));if(T(t))return t;throw new TypeError(`unsupported type transformation, got: ${Object.prototype.toString.call(t)}`)}function n(t,e){for(const[r,i]of Object.entries(e)){if(i===void 0)continue;if(i===null){delete t[r];continue}const a=t[r];T(a)&&T(i)?n(a,i):t[r]=D(i)}}})(u.FexiosQueryBuilder||(u.FexiosQueryBuilder={}));class P{constructor(f,n,t){g(this,"ok");g(this,"status");g(this,"statusText");g(this,"headers");g(this,"url");g(this,"redirected");this.rawResponse=f,this.data=n,this.responseType=t,["ok","status","statusText","headers","url","redirected"].forEach(e=>{Reflect.defineProperty(this,e,{get:()=>f[e]})})}}const B=o=>{const f=o.reduce((e,r)=>e+r.length,0),n=new Uint8Array(f);let t=0;for(const e of o)n.set(e,t),t+=e.length;return n};async function W(o,f,n){const t=o.getReader();if(!t)throw new w(p.NO_BODY_READER,"Failed to get ReadableStream from response body");const e=[];let r=0;try{for(;;){const{done:a,value:l}=await t.read();if(a)break;e.push(l),r+=l.length,n&&f>0&&n(r/f,B(e))}}finally{t.releaseLock?.()}const i=B(e);return n?.(1,i),i}const q=o=>{if(o){if(o.includes("application/json")||o.endsWith("+json"))return"json";if(o.startsWith("text/"))return"text";if(o.includes("multipart/form-data")||o.includes("application/x-www-form-urlencoded"))return"form";if(/^image\//.test(o)||/^video\//.test(o)||/^audio\//.test(o)||o.includes("application/pdf"))return"blob";if(o.includes("application/octet-stream")||o.includes("application/zip")||o.includes("application/x-tar")||o.includes("application/x-7z-compressed")||o.includes("application/x-gzip"))return"arrayBuffer"}};async function N(o,f,n,t,e){const r=o.clone(),i=o.headers.get("content-type")?.toLowerCase()??"",a=o.headers.get("content-length"),l=a?Number(a):0,c=o.headers.get("upgrade")?.toLowerCase(),d=o.headers.get("connection")?.toLowerCase();let s=f??q(i)??"text";if(f||(c==="websocket"&&d==="upgrade"?s="ws":i.includes("text/event-stream")&&(s="stream")),s==="stream"){const R=o.url||o.url||"",y=await I(R,o,e),S=t?.(y);if(typeof S=="boolean"?S:!y.ok)throw new A(y.statusText,y);return y}if(s==="ws"){const R=o.url||o.url||"",y=await j(R,o,e),S=t?.(y);if(typeof S=="boolean"?S:!y.ok)throw new A(y.statusText,y);return y}const b=/\bcharset=([^;]+)/i.exec(i)?.[1]?.trim()||"utf-8",h=new TextDecoder(b);let m;try{if(s==="form")m=await r.formData();else{const R=await W(r.body,l,n);if(s==="arrayBuffer")m=R.buffer.slice(R.byteOffset,R.byteOffset+R.byteLength);else if(s==="blob")m=new Blob([R],{type:i||"application/octet-stream"});else if(s==="text"){const y=h.decode(R);if(f)m=y;else{const S=y.trim();if(S.startsWith("{")&&S.endsWith("}")||S.startsWith("[")&&S.endsWith("]"))try{m=JSON.parse(S),s="json"}catch{m=y}else m=y}}else if(s==="json"){const y=h.decode(R);m=y.length?JSON.parse(y):null}else m=R}}catch(R){if(!(R instanceof Error))throw R;try{m=await r.text(),s="text"}catch{throw new w(p.BODY_TRANSFORM_ERROR,`Failed to transform response body to ${s}`,void 0,{cause:R})}}const E=new P(o,m,s),L=t?.(E);if(typeof L=="boolean"?L:!E.ok)throw new A(E.statusText,E);return E}async function j(o,f,n){const t=new WebSocket(o.toString()),e=n??6e4;return await new Promise((r,i)=>{const a=e>0?setTimeout(()=>{t.close(),i(new w(p.TIMEOUT,`WebSocket connection timed out after ${e}ms`))},e):void 0;let l=!1;const c=()=>{clearTimeout(a),t.removeEventListener("open",d),t.removeEventListener("error",s),t.removeEventListener("close",b)},d=()=>{l||(l=!0,c(),r())},s=h=>{l||(l=!0,c(),i(new w(p.NETWORK_ERROR,"WebSocket connection failed",void 0,{cause:h})))},b=h=>{l||(l=!0,c(),i(new w(p.NETWORK_ERROR,`WebSocket connection closed unexpectedly (code: ${h.code}, reason: ${h.reason})`,void 0,{cause:h})))};t.addEventListener("open",d),t.addEventListener("error",s),t.addEventListener("close",b)}),new P(f||new Response(null),t,"ws")}async function I(o,f,n){const t=new EventSource(o.toString()),e=n??6e4;return await new Promise((r,i)=>{const a=e>0?setTimeout(()=>{t.close(),i(new w(p.TIMEOUT,`EventSource connection timed out after ${e}ms`))},e):void 0;let l=!1;const c=()=>{clearTimeout(a),t.removeEventListener("open",d),t.removeEventListener("error",s)},d=()=>{l||(l=!0,c(),r())},s=b=>{l||(l=!0,c(),i(new w(p.NETWORK_ERROR,"EventSource connection failed",void 0,{cause:b})))};t.addEventListener("open",d),t.addEventListener("error",s)}),new P(f||new Response(null),t,"stream")}const O=class O extends C{constructor(n={}){super("request");g(this,"baseConfigs");g(this,"hooks",[]);g(this,"mergeQueries",u.FexiosQueryBuilder.mergeQueries);g(this,"mergeHeaders",u.FexiosHeaderBuilder.mergeHeaders);g(this,"interceptors",{request:this.createInterceptor("beforeRequest"),response:this.createInterceptor("afterResponse")});g(this,"create",O.create);g(this,"_plugins",new Map);g(this,"checkIsPlainObject",T);g(this,"mergeQuery",this.mergeQueries);this.baseConfigs=k(O.DEFAULT_CONFIGS,n),O.ALL_METHODS.forEach(t=>this.createMethodShortcut(t.toLowerCase()))}get defaults(){return this.baseConfigs}set defaults(n){this.baseConfigs=n}async request(n,t){let e=t||{};if(typeof n=="string"||n instanceof URL?e.url=n.toString():typeof n=="object"&&(e=n),e=await this.emit("beforeInit",e),e[O.FINAL_SYMBOL])return e;if(e=this.applyDefaults(e),O.METHODS_WITHOUT_BODY.includes(e.method?.toLocaleLowerCase())&&e.body)throw new w(p.BODY_NOT_ALLOWED,`Request method "${e.method}" does not allow body`);if(e=await this.emit("beforeRequest",e),e[O.FINAL_SYMBOL])return e;let r;const i={};if(typeof e.body<"u"&&e.body!==null&&(e.body instanceof Blob||e.body instanceof FormData||e.body instanceof URLSearchParams?r=e.body:typeof e.body=="object"&&e.body!==null?(r=JSON.stringify(e.body),e.headers=this.mergeHeaders(e.headers,{"Content-Type":"application/json"})):r=e.body),!u.FexiosHeaderBuilder.makeHeaders(e.headers||{}).get("content-type")&&r&&(r instanceof FormData||r instanceof URLSearchParams?i["content-type"]=null:typeof r=="string"&&typeof e.body=="object"?i["content-type"]="application/json":r instanceof Blob&&(i["content-type"]=r.type||"application/octet-stream")),e.body=r,e=await this.emit("afterBodyTransformed",e),e[O.FINAL_SYMBOL])return e;const l=e.abortController??(globalThis.AbortController?new AbortController:void 0),c=globalThis.location?.href||"http://localhost",d=new URL(e.baseURL||this.baseConfigs.baseURL||c,c),s=new URL(e.url,d),b=u.FexiosQueryBuilder.makeURL(s,e.query,s.hash).toString(),h=new Request(b,{method:e.method||"GET",credentials:e.credentials,cache:e.cache,mode:e.mode,headers:u.FexiosHeaderBuilder.mergeHeaders(this.baseConfigs.headers,e.headers||{},i),body:e.body,signal:l?.signal});if(e.rawRequest=h,e=await this.emit("beforeActualFetch",e),e[O.FINAL_SYMBOL])return e;const m=e.timeout??this.baseConfigs.timeout??60*1e3,E=e.shouldThrow??this.baseConfigs.shouldThrow;if(e.url.startsWith("ws")||e.responseType==="ws"){const R=await j(e.url,void 0,m),y={...e,response:R,rawResponse:void 0,data:R.data,headers:R.headers};return this.emit("afterResponse",y)}let L;try{l&&(L=m>0?setTimeout(()=>{l.abort()},m):void 0);const y=await(e.fetch||this.baseConfigs.fetch||globalThis.fetch)(e.rawRequest).catch(S=>{throw L&&clearTimeout(L),l?.signal.aborted?new w(p.TIMEOUT,`Request timed out after ${m}ms`,e):new w(p.NETWORK_ERROR,S.message,e)});return L&&clearTimeout(L),e.rawResponse=y,e.response=await N(y,e.responseType,(S,Y)=>{t?.onProgress?.(S,Y)},E,m),e.rawResponse=e.response.rawResponse,Object.defineProperties(e,{url:{get:()=>e.rawResponse?.url||b},data:{get:()=>e.response.data},headers:{get:()=>e.rawResponse.headers},responseType:{get:()=>e.response.responseType}}),this.emit("afterResponse",e)}catch(R){throw L&&clearTimeout(L),R}}applyDefaults(n){const t=n;"customEnv"in this.baseConfigs&&(t.customEnv=k({},this.baseConfigs.customEnv,t.customEnv));const e=globalThis.location?.href||"http://localhost",r=t.baseURL||this.baseConfigs.baseURL||e,i=new URL(r,e),a=new URL(t.url.toString(),i),l=u.FexiosQueryBuilder.toQueryRecord(i.searchParams),c=u.FexiosQueryBuilder.toQueryRecord(a.searchParams),d=u.FexiosQueryBuilder.mergeQueries(l,c);a.search=u.FexiosQueryBuilder.makeSearchParams(d).toString(),t.url=a.toString();const s=u.FexiosQueryBuilder.mergeQueries(this.baseConfigs.query,t.query);return t.query&&this.restoreNulls(s,t.query),t.query=s,t}restoreNulls(n,t){if(!(!t||typeof t!="object"))for(const[e,r]of Object.entries(t))r===null?n[e]=null:T(r)&&((!n[e]||typeof n[e]!="object")&&(n[e]={}),this.restoreNulls(n[e],r))}async emit(n,t,e={shouldHandleShortCircuitResponse:!0}){const r=this.hooks.filter(a=>a.event===n);if(r.length===0)return t;const i=async(a,l)=>{const c={...a,rawResponse:l},d=await N(l,a.responseType,(s,b)=>a.onProgress?.(s,b),a.shouldThrow??this.baseConfigs.shouldThrow,a.timeout??this.baseConfigs.timeout??60*1e3);if(c.response=d,c.rawResponse=d.rawResponse,c.data=d.data,c.headers=d.headers,n!=="afterResponse"){const s=await this.emit("afterResponse",c);return s[O.FINAL_SYMBOL]=!0,s}else return c[O.FINAL_SYMBOL]=!0,c};for(let a=0;a<r.length;a++){const l=r[a],c=`${String(n)}#${l.action.name||`anonymous#${a}`}`,d=Symbol("FEXIOS_HOOK_CTX_MARK");try{t[d]=d}catch{}const s=await l.action.call(this,t);try{delete t[d]}catch{}if(s===!1)throw new w(p.ABORTED_BY_HOOK,`Request aborted by hook "${c}"`,t);if(s instanceof Response){if(e.shouldHandleShortCircuitResponse!==!1)return i(t,s);t.rawResponse=s}else s&&typeof s=="object"&&s[d]===d&&(t=s)}return t}on(n,t,e=!1){if(typeof t!="function")throw new w(p.INVALID_HOOK_CALLBACK,`Hook should be a function, but got "${typeof t}"`);return this.hooks[e?"unshift":"push"]({event:n,action:t}),this}off(n,t){return n==="*"||!n?this.hooks=this.hooks.filter(e=>e.action!==t):this.hooks=this.hooks.filter(e=>e.event!==n||e.action!==t),this}createInterceptor(n){return{handlers:()=>this.hooks.filter(t=>t.event===n).map(t=>t.action),use:(t,e=!1)=>this.on(n,t,e),clear:()=>{this.hooks=this.hooks.filter(t=>t.event!==n)}}}createMethodShortcut(n){return Reflect.defineProperty(this,n,{get:()=>(t,e,r)=>(O.METHODS_WITHOUT_BODY.includes(n.toLocaleLowerCase())?r=e:(r=r||{},r.body=e),this.request(t,{...r,method:n}))}),this}extends(n){const t=new O(k(this.baseConfigs,n));return t.hooks=[...this.hooks],t._plugins=new Map(this._plugins),t._plugins.forEach(async e=>{await t.plugin(e)}),t}static create(n){return new O(n)}async plugin(n){if(typeof n?.name=="string"&&typeof n?.install=="function"){if(this._plugins.has(n.name))return this;const t=await n.install(this);if(this._plugins.set(n.name,n),t instanceof O)return t}return this}};g(O,"version","5.3.1"),g(O,"FINAL_SYMBOL",Symbol("FEXIOS_FINAL_CONTEXT")),g(O,"DEFAULT_CONFIGS",{baseURL:"",timeout:0,credentials:void 0,headers:{},query:{},responseType:void 0,shouldThrow(n){return!n.ok},fetch:globalThis.fetch}),g(O,"ALL_METHODS",["get","post","put","patch","delete","head","options","trace"]),g(O,"METHODS_WITHOUT_BODY",["get","head","options","trace"]);let U=O;const M=U.create,F=M();u.CallableInstance=C,u.Fexios=U,u.FexiosError=w,u.FexiosErrorCodes=p,u.FexiosResponse=P,u.FexiosResponseError=A,u.checkIsPlainObject=T,u.clone=D,u.createFexios=M,u.createFexiosEventSourceResponse=I,u.createFexiosResponse=N,u.createFexiosWebSocketResponse=j,u.deepMerge=k,u.default=F,u.fexios=F,u.isFexiosError=K,u.isPlainObject=T,Object.defineProperties(u,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})})); //# sourceMappingURL=index.js.map