UNPKG

@gentrace/core

Version:
250 lines (223 loc) 6.57 kB
// @ts-ignore import settle from "../axios-unsafe-lib/core/settle"; // @ts-ignore import buildURL from "../axios-unsafe-lib/helpers/buildURL"; // @ts-ignore import buildFullPath from "../axios-unsafe-lib/core/buildFullPath"; import globalAxios from "axios"; const typeOfTest = (type: any) => (thing: any) => typeof thing === type; /** * Determine if a value is undefined * * @param {*} val The value to test * * @returns {boolean} True if the value is undefined, otherwise false */ const isUndefined = typeOfTest("undefined"); const isStandardBrowserEnv = (() => { let product; if ( typeof navigator !== "undefined" && ((product = navigator.product) === "ReactNative" || product === "NativeScript" || product === "NS") ) { return false; } return typeof window !== "undefined" && typeof document !== "undefined"; })(); function isFunction(val: any) { return toString.call(val) === "[object Function]"; } var kindOf = (function (cache) { // eslint-disable-next-line func-names return function (thing: any) { var str = toString.call(thing); return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); }; })(Object.create(null)); const isFormData = (thing: any) => { let kind; return ( thing && ((typeof FormData === "function" && thing instanceof FormData) || (isFunction(thing.append) && ((kind = kindOf(thing)) === "formdata" || // detect form-data instance (kind === "object" && isFunction(thing.toString) && thing.toString() === "[object FormData]")))) ); }; function createRequest(config: any) { const headers = new Headers(config.headers); // HTTP basic authentication if (config.auth) { const username = config.auth.username || ""; const password = config.auth.password ? decodeURI(encodeURIComponent(config.auth.password)) : ""; headers.set("Authorization", `Basic ${btoa(username + ":" + password)}`); } const method = config.method.toUpperCase(); const options: any = { headers: headers, method, }; if (method !== "GET" && method !== "HEAD") { options.body = config.data; // In these cases the browser will automatically set the correct Content-Type, // but only if that header hasn't been set yet. So that's why we're deleting it. if (isFormData(options.body) && isStandardBrowserEnv) { headers.delete("Content-Type"); } } if (config.mode) { options.mode = config.mode; } if (config.cache) { options.cache = config.cache; } if (config.integrity) { options.integrity = config.integrity; } if (config.redirect) { options.redirect = config.redirect; } if (config.referrer) { options.referrer = config.referrer; } // This config is similar to XHR’s withCredentials flag, but with three available values instead of two. // So if withCredentials is not set, default value 'same-origin' will be used if (!isUndefined(config.withCredentials)) { options.credentials = config.withCredentials ? "include" : "omit"; } const fullPath = buildFullPath(config.baseURL, config.url); const url = buildURL(fullPath, config.params, config.paramsSerializer); // Expected browser to throw error if there is any wrong configuration value return new Request(url, options); } function enhanceError( error: any, config: any, code: any, request: any, response: any, ) { error.config = config; if (code) { error.code = code; } error.request = request; error.response = response; error.isAxiosError = true; error.toJSON = function toJSON() { return { // Standard message: this.message, name: this.name, // Microsoft description: this.description, number: this.number, // Mozilla fileName: this.fileName, lineNumber: this.lineNumber, columnNumber: this.columnNumber, stack: this.stack, // Axios config: this.config, code: this.code, status: this.response && this.response.status ? this.response.status : null, }; }; return error; } function createError( message: any, config: any, code: any, request: any, response: any, ) { // @ts-ignore if (globalAxios.AxiosError && typeof globalAxios.AxiosError === "function") { // @ts-ignore return new globalAxios.AxiosError( message, // @ts-ignore globalAxios.AxiosError[code], config, request, response, ); } var error = new Error(message); return enhanceError(error, config, code, request, response); } async function getResponse(request: any, config: any) { let stageOne; try { stageOne = await fetch(request); } catch (e) { // @ts-ignore return createError("Network Error", config, "ERR_NETWORK", request); } const response: any = { ok: stageOne.ok, status: stageOne.status, statusText: stageOne.statusText, headers: new Headers(stageOne.headers), // Make a copy of headers config: config, request, }; if (stageOne.status >= 200 && stageOne.status !== 204) { switch (config.responseType) { case "arraybuffer": response.data = await stageOne.arrayBuffer(); break; case "blob": response.data = await stageOne.blob(); break; case "json": response.data = await stageOne.json(); break; case "formData": response.data = await stageOne.formData(); break; default: response.data = await stageOne.text(); break; } } return response; } // @ts-ignore async function fetchAdapter(config) { const request = createRequest(config); const promiseChain = [getResponse(request, config)]; if (config.timeout && config.timeout > 0) { promiseChain.push( new Promise((res) => { setTimeout(() => { const message = config.timeoutErrorMessage ? config.timeoutErrorMessage : "timeout of " + config.timeout + "ms exceeded"; // @ts-ignore res(createError(message, config, "ECONNABORTED", request)); }, config.timeout); }), ); } const data = await Promise.race(promiseChain); return new Promise((resolve, reject) => { if (data instanceof Error) { reject(data); } else { Object.prototype.toString.call(config.settle) === "[object Function]" ? config.settle(resolve, reject, data) : settle(resolve, reject, data); } }); } export default fetchAdapter;