ofexios
Version:
unjs/ofetch based HTTP client with similar API to axios for browser and Node.js
3 lines (2 loc) • 10.6 kB
JavaScript
;Object.defineProperty(exports,"__esModule",{value:!0});const g=require("callable-instance"),cosmokit=require("cosmokit"),destr=require("destr"),ofetch=require("ofetch");function _interopDefaultCompat(r){return r&&typeof r=="object"&&"default"in r?r.default:r}const g__default=_interopDefaultCompat(g),polys=(r=>{var t=typeof t>"u"?void 0:t;let s=r?.ReadableStream,e=r?.TransformStream;if(typeof t<"u")try{const o=t("stream/web");s||(s=o.ReadableStream),e||(e=o.TransformStream)}catch{try{const o=t("web-streams-polyfill");s||(s=o.ReadableStream),e||(e=o.TransformStream)}catch{throw new TypeError("stream polyfill is not available")}}return{ReadableStream:s,TransformStream:e}})(this),TransformStream=polys.TransformStream;var FexiosErrorCodes=(r=>(r.BODY_USED="BODY_USED",r.NO_BODY_READER="NO_BODY_READER",r.TIMEOUT="TIMEOUT",r.NETWORK_ERROR="NETWORK_ERROR",r.BODY_NOT_ALLOWED="BODY_NOT_ALLOWED",r.HOOK_CONTEXT_CHANGED="HOOK_CONTEXT_CHANGED",r.ABORTED_BY_HOOK="ABORTED_BY_HOOK",r.INVALID_HOOK_CALLBACK="INVALID_HOOK_CALLBACK",r.UNEXPECTED_HOOK_RETURN="UNEXPECTED_HOOK_RETURN",r))(FexiosErrorCodes||{});class FexiosError extends Error{constructor(t,s,e,o){super(s,o),this.name="FexiosError",this.code=t,this.context=e}}class FexiosResponseError extends FexiosError{constructor(t,s,e){super(s.statusText,t,void 0,e),this.name="FexiosResponseError",this.response=s}}const isFexiosError=r=>!(r instanceof FexiosResponseError)&&r instanceof FexiosError;class FexiosResponse{constructor(t,s,e){this.rawResponse=t,this.data=s,this.ok=t.ok,this.status=t.status,this.statusText=t.statusText,this.headers=t.headers;for(const[o,i]of Object.entries(e||{}))this[o]=i}}const u=typeof Promise.withResolvers>"u"?function(){let r,t;return{promise:new Promise((s,e)=>{r=s,t=e}),resolve:r,reject:t}}:()=>Promise.withResolvers();function checkThrow(r){if(!r.ok)throw new FexiosResponseError(`Request failed with status code ${r.status}`,r);return r}function concat(r){let t=0;for(const o of r)t+=o.length;const s=new Uint8Array(t);let e=0;for(const o of r)s.set(o,e),e+=o.length;return s}async function readToUint8ArrayUnsized(r,t){let s=0;const e=[];for await(const o of r)e.push(o),s+=o.length,t?.(o.length,o);return concat(e)}async function readToUint8Array(r,t,s){if(!t)return readToUint8ArrayUnsized(r,s);const e=new Uint8Array(t||0);let o=0,i;for await(const n of r){if(i){const[a,c]=await i;c.enqueue(n),o+=n.length,s?.(o,n);continue}if(o+n.length>e.length){console.warn(`readToUint8Array overflowed (${e.length}++${o+n.length-e.length}), fallback to concat`);const{promise:a,resolve:c}=u();i=a;const{promise:h,resolve:l,reject:d}=u();readToUint8ArrayUnsized(new ReadableStream({start(f){f.enqueue(e.subarray(0,o)),f.enqueue(n),o+=n.length,c([h,f])}})).then(l).catch(d);continue}e.set(n,o),s?.(o,n,e),o+=n.length}if(i){const[n,a]=await i;a.close();const c=await n;return s?.(o,void 0,c),c}return e}const y=new Set(["image/svg","application/xml","application/xhtml","application/html"]),b=/^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;function detectResponseType(r=""){if(!r)return"json";const t=r.split(";").shift()||"";return b.test(t)?"json":t==="application/octet-stream"?"stream":y.has(t)||t.startsWith("text/")?"text":"blob"}class Fexios extends g__default{async request(t,s){let e=s=s||{};typeof t=="string"||t instanceof URL?e.url=t.toString():typeof t=="object"&&(e={...t,...e}),e=await this.emit("beforeInit",e);const o=s.baseURL||this.baseConfigs.baseURL||globalThis.location?.href,i=o?new URL(o,globalThis.location?.href):void 0,n=new URL(e.url.toString(),i);if(e.url=n.href,e.baseURL=i?i.href:n.origin,e.headers=this.mergeHeaders(this.baseConfigs.headers,s.headers),e.query=this.mergeQuery(this.baseConfigs.query,n.searchParams,s.query),n.search=new URLSearchParams(e.query).toString(),e.url=n.toString(),this.METHODS_WITHOUT_BODY.includes(e.method?.toLocaleLowerCase())&&e.body)throw new FexiosError(FexiosErrorCodes.BODY_NOT_ALLOWED,`Request method "${e.method}" does not allow body`);e=await this.emit("beforeRequest",e);let a;typeof e.body<"u"&&e.body!==null&&(e.body instanceof Blob||e.body instanceof FormData||e.body instanceof URLSearchParams?a=e.body:typeof e.body=="object"?(a=JSON.stringify(e.body),e.headers["content-type"]="application/json; charset=UTF-8"):a=e.body),!s.headers?.["content-type"]&&a&&(a instanceof FormData||a instanceof URLSearchParams?typeof a=="string"&&typeof e.body=="object"?e.headers["content-type"]="application/json; charset=UTF-8":a instanceof Blob&&(e.headers["content-type"]=a.type):delete e.headers["content-type"]),e.body=a,e=await this.emit("afterBodyTransformed",e);const c=e.abortController||globalThis.AbortController?new AbortController:void 0,h=new Request(e.url,{method:e.method||"GET",credentials:e.credentials,cache:e.cache,mode:e.mode,headers:e.headers,body:e.body,signal:c?.signal});if(e.rawRequest=h,e=await this.emit("beforeActualFetch",e),e.url.startsWith("ws")){console.info("WebSocket:",e.url);const p=new WebSocket(e.url);return e.rawResponse=new Response,e.response=new FexiosResponse(e.rawResponse,p,{ok:!0,status:101,statusText:"Switching Protocols"}),e.data=p,e.headers=new Headers,this.emit("afterResponse",e)}const l=e.timeout||this.baseConfigs.timeout||cosmokit.Time.minute,d=setTimeout(()=>{if(c?.abort(),!c)throw new FexiosError(FexiosErrorCodes.TIMEOUT,`Request timed out after ${l}ms`,e)},l),f=await fetch(e.rawRequest).catch(p=>Promise.reject(new FexiosError(FexiosErrorCodes.NETWORK_ERROR,p.error.message,e)));return e.rawResponse=f,e.response=await Fexios.resolveResponseBody(f,e.responseType,(p,m)=>{console.info("Download progress:",p),s?.onProgress?.(p,m)}).finally(()=>{clearTimeout(d)}),e.data=e.response.data,e.headers=e.response.headers,this.emit("afterResponse",e)}mergeQuery(t,...s){const e=new URLSearchParams(t);for(const o of s)new URLSearchParams(o).forEach((i,n)=>{e.set(n,i)});return Object.fromEntries(e.entries())}mergeHeaders(t,...s){const e={},o=new Headers(t);for(const i of s)new Headers(i).forEach((n,a)=>{o.set(a,n)});return o.forEach((i,n)=>{e[n]=i}),e}async emit(t,s){const e=this.hooks.filter(o=>o.event===t);try{let o=0;for(const i of e){const n=`${t}#${i.action.name||`anonymous#${o}`}`,a=Symbol("FexiosHookContext");s[a]=a;const c=await i.action.call(this,s);if(c===!1)throw new FexiosError(FexiosErrorCodes.ABORTED_BY_HOOK,`Request aborted by hook "${n}"`,s);if(typeof c=="object"&&c[a]===a)s=c;else{const h=globalThis["".concat("console")];try{throw new FexiosError(FexiosErrorCodes.HOOK_CONTEXT_CHANGED,`Hook "${n}" should return the original FexiosContext or return false to abort the request, but got "${c}".`)}catch(l){h.warn(l.stack||l)}}delete s[a],o++}}catch(o){return Promise.reject(o)}return s}on(t,s,e=!1){if(typeof s!="function")throw new FexiosError(FexiosErrorCodes.INVALID_HOOK_CALLBACK,`Hook should be a function, but got "${typeof s}"`);return this.hooks[e?"unshift":"push"]({event:t,action:s}),this}off(t,s){return this.hooks=this.hooks.filter(e=>e.event!==t||e.action!==s),this}createInterceptor(t){return{handlers:()=>this.hooks.filter(s=>s.event===t).map(s=>s.action),use:(s,e=!1)=>this.on(t,s,e),clear:()=>{this.hooks=this.hooks.filter(s=>s.event!==t)}}}createMethodShortcut(t){return Object.defineProperty(this,t,{value:(s,e,o)=>(this.METHODS_WITHOUT_BODY.includes(t.toLocaleLowerCase())?o=e:(o=o||{},o.body=e),this.request(s,{...o,method:t}))}),this}static async resolveResponseBody(t,s,e){if(t.bodyUsed)throw new FexiosError("BODY_USED","Response body has already been used or locked");const o=t.headers.get("content-type")||"";if(Number(t.headers.get("content-length")),(t.status===101||t.status===426||t.headers.get("upgrade"))&&typeof globalThis.WebSocket<"u"){const h=new WebSocket(t.url);return await new Promise((l,d)=>{h.onopen=l,h.onerror=d}),new FexiosResponse(t,h,{ok:!0,status:101,statusText:"Switching Protocols"})}if(o.startsWith("text/event-stream")&&!["text","json"].includes(s||"")&&typeof globalThis.EventSource<"u"){const h=new EventSource(t.url);return await new Promise((l,d)=>{h.onopen=l,h.onerror=d}),new FexiosResponse(t,h)}if(s==="stream")return new FexiosResponse(t,destr.safeDestr(await t.text()));const i=s||detectResponseType(t.headers.get("content-type")||"");if(!s&&i==="stream"){let h=0;const l=new TransformStream({transform(d,f){f.enqueue(d),h+=d.length,e?.(h,d)},flush(d){d.terminate()}});return new FexiosResponse(t,t.body?.pipeThrough(l))}if(s==="blob"||o.startsWith("image/")||o.startsWith("video/")||o.startsWith("audio/"))return new FexiosResponse(t,await t.blob());const n=t.body;if(!n)throw new FexiosError(FexiosErrorCodes.NO_BODY_READER,"Failed to get ReadableStream from response body");const a=await readToUint8Array(n,+(t.headers.get("content-length")||0),e),c=new FexiosResponse(t,void 0);if(this.isText(a)?c.data=new TextDecoder().decode(a):c.data=new Blob([a],{type:t.headers.get("content-type")||void 0}),typeof c.data=="string"&&s!=="text"&&(s==="json"||o.startsWith("application/json")))try{c.data=destr.safeDestr(c.data)}catch(h){console.warn("Failed to parse response data as JSON:",h)}return typeof c.data>"u"&&(c.data=a.length>0?a:void 0),checkThrow(c)}static isText(t,s=1024){if(!(t instanceof Uint8Array))throw new TypeError("Input must be a Uint8Array");const e=t.slice(0,s),o=new TextDecoder("utf-8",{fatal:!0});try{const i=o.decode(e),n=/[\x00-\x08\x0E-\x1F\x7F]/g,a=i.match(n);return!(a&&a.length/i.length>.1)}catch{return!1}}extends(t){const s=new Fexios({...this.baseConfigs,...t});return s.hooks=[...this.hooks],s}static create(t){return new Fexios(t)}constructor(t={}){super("request"),this.hooks=[],this.DEFAULT_CONFIGS={baseURL:"",timeout:cosmokit.Time.minute,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=Fexios.create,this.baseConfigs=t,this.ALL_METHODS.forEach(this.createMethodShortcut.bind(this))}}Fexios.BLOB_MIME_TYPE=["image/","video/","audio/"];const createFexios=Fexios.create,fexios=createFexios();exports.fetch=ofetch.fetch,exports.Fexios=Fexios,exports.FexiosError=FexiosError,exports.FexiosErrorCodes=FexiosErrorCodes,exports.FexiosResponse=FexiosResponse,exports.FexiosResponseError=FexiosResponseError,exports.createFexios=createFexios,exports.default=fexios,exports.fexios=fexios,exports.isFexiosError=isFexiosError;
//# sourceMappingURL=index.cjs.map