@gentrace/core
Version:
Core Gentrace Node.JS library
250 lines (223 loc) • 6.57 kB
text/typescript
// @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;