axios
Version:
Promise based HTTP client for the browser and node.js
238 lines (208 loc) • 7.87 kB
JavaScript
;
import utils from '../utils.js';
import AxiosHeaders from './AxiosHeaders.js';
import assertParamsAcyclic from './assertParamsAcyclic.js';
const headersToObject = (thing) => (thing instanceof AxiosHeaders ? { ...thing } : thing);
const ownEnumerableKeys = (thing) => {
if (Object.getOwnPropertySymbols && Object.getOwnPropertyDescriptor) {
return Object.keys(thing).concat(
Object.getOwnPropertySymbols(thing).filter((symbol) => {
const descriptor = Object.getOwnPropertyDescriptor(thing, symbol);
return descriptor && descriptor.enumerable;
})
);
}
return Object.keys(thing);
};
// Copy params without recursive merges so the serializer remains responsible
// for its depth policy. Only visit defaults that contribute to the result.
function mergeParams(a, b) {
const pending = [];
const copies = new WeakMap();
const empty = {};
function copy(target, source) {
if (!utils.isPlainObject(source)) {
return utils.isArray(source) ? source.slice() : source;
}
const base = utils.isPlainObject(target) ? target : empty;
let bases = copies.get(source);
if (!bases) {
bases = new WeakMap();
copies.set(source, bases);
}
if (bases.has(base)) return bases.get(base);
const result = {};
bases.set(base, result);
pending.push({ base, source, result });
return result;
}
const result = utils.isUndefined(b) ? copy(undefined, a) : copy(a, b);
while (pending.length) {
const { base, source, result: target } = pending.pop();
const baseKeys = new Set(ownEnumerableKeys(base));
const sourceKeys = new Set(ownEnumerableKeys(source));
const keys = new Set([...baseKeys, ...sourceKeys]);
keys.forEach((key) => {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') return;
const hasSource = sourceKeys.has(key);
const value = hasSource ? source[key] : undefined;
const previous =
(!hasSource || utils.isPlainObject(value)) && baseKeys.has(key) ? base[key] : undefined;
target[key] = hasSource ? copy(previous, value) : copy(undefined, previous);
});
}
assertParamsAcyclic(result);
return result;
}
function getMergedValue(target, source, prop, caseless) {
if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
return utils.merge.call({ caseless }, target, source);
} else if (utils.isPlainObject(source)) {
return utils.merge({}, source);
} else if (utils.isArray(source)) {
return source.slice();
}
return source;
}
function mergeDeepProperties(a, b, prop, caseless) {
if (!utils.isUndefined(b)) {
return getMergedValue(a, b, prop, caseless);
} else if (!utils.isUndefined(a)) {
return getMergedValue(undefined, a, prop, caseless);
}
}
// eslint-disable-next-line consistent-return
function valueFromConfig2(a, b) {
if (!utils.isUndefined(b)) {
return getMergedValue(undefined, b);
}
}
// eslint-disable-next-line consistent-return
function defaultToConfig2(a, b) {
if (!utils.isUndefined(b)) {
return getMergedValue(undefined, b);
} else if (!utils.isUndefined(a)) {
return getMergedValue(undefined, a);
}
}
function getMergedTransitionalOption(config1, config2, prop) {
const transitional2 = utils.hasOwnProp(config2, 'transitional')
? config2.transitional
: undefined;
if (!utils.isUndefined(transitional2)) {
if (utils.isPlainObject(transitional2)) {
if (utils.hasOwnProp(transitional2, prop)) {
return transitional2[prop];
}
} else {
return undefined;
}
}
const transitional1 = utils.hasOwnProp(config1, 'transitional')
? config1.transitional
: undefined;
if (utils.isPlainObject(transitional1) && utils.hasOwnProp(transitional1, prop)) {
return transitional1[prop];
}
return undefined;
}
// eslint-disable-next-line consistent-return
function mergeDirectKeys(a, b, prop, config1, config2) {
if (utils.hasOwnProp(config2, prop)) {
return getMergedValue(a, b);
} else if (utils.hasOwnProp(config1, prop)) {
return getMergedValue(undefined, a);
}
}
function mergeHeaders(a, b, prop) {
return mergeDeepProperties(headersToObject(a), headersToObject(b), prop, true);
}
// Null-proto, frozen descriptor so a polluted Object.prototype.get cannot turn
// this data descriptor into an accessor descriptor on the way in.
// defineProperty only reads it, so one shared instance serves every call.
const hasOwnPropertyDescriptor = Object.freeze({
__proto__: null,
value: Object.prototype.hasOwnProperty,
enumerable: false,
writable: true,
configurable: true,
});
// The strategy table and the strategies above are stateless, so they are
// built once at module load instead of on every call: mergeConfig runs at
// most twice per request (once from a method alias, once from `_request`).
const mergeMap = {
url: valueFromConfig2,
method: valueFromConfig2,
data: valueFromConfig2,
baseURL: defaultToConfig2,
transformRequest: defaultToConfig2,
transformResponse: defaultToConfig2,
params: mergeParams,
paramsSerializer: defaultToConfig2,
timeout: defaultToConfig2,
timeoutErrorMessage: defaultToConfig2,
withCredentials: defaultToConfig2,
withXSRFToken: defaultToConfig2,
adapter: defaultToConfig2,
responseType: defaultToConfig2,
xsrfCookieName: defaultToConfig2,
xsrfHeaderName: defaultToConfig2,
onUploadProgress: defaultToConfig2,
onDownloadProgress: defaultToConfig2,
decompress: defaultToConfig2,
maxContentLength: defaultToConfig2,
maxBodyLength: defaultToConfig2,
beforeRedirect: defaultToConfig2,
transport: defaultToConfig2,
httpAgent: defaultToConfig2,
httpsAgent: defaultToConfig2,
cancelToken: defaultToConfig2,
socketPath: defaultToConfig2,
allowedSocketPaths: defaultToConfig2,
responseEncoding: defaultToConfig2,
validateStatus: mergeDirectKeys,
headers: mergeHeaders,
};
/**
* Config-specific merge-function which creates a new config-object
* by merging two configuration objects together.
*
* @param {Object} config1
* @param {Object} config2
*
* @returns {Object} New object resulting from merging config2 to config1
*/
export default function mergeConfig(config1, config2) {
// eslint-disable-next-line no-param-reassign
config1 = config1 || {};
config2 = config2 || {};
// Use a null-prototype object so that downstream reads such as `config.auth`
// or `config.baseURL` cannot inherit polluted values from Object.prototype.
// `hasOwnProperty` is restored as a non-enumerable own slot to preserve
// ergonomics for user code that relies on it.
const config = Object.create(null);
Object.defineProperty(config, 'hasOwnProperty', hasOwnPropertyDescriptor);
const keys = ownEnumerableKeys({ ...config1, ...config2 });
for (let i = 0, len = keys.length; i < len; i++) {
const prop = keys[i];
if (prop === '__proto__' || prop === 'constructor' || prop === 'prototype') continue;
const merge = utils.hasOwnProp(mergeMap, prop) ? mergeMap[prop] : mergeDeepProperties;
const a = utils.hasOwnProp(config1, prop) ? config1[prop] : undefined;
const b = utils.hasOwnProp(config2, prop) ? config2[prop] : undefined;
const configValue =
merge === mergeDirectKeys ? merge(a, b, prop, config1, config2) : merge(a, b, prop);
(utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue);
}
if (
utils.hasOwnProp(config2, 'validateStatus') &&
utils.isUndefined(config2.validateStatus) &&
getMergedTransitionalOption(config1, config2, 'validateStatusUndefinedResolves') === false
) {
if (utils.hasOwnProp(config1, 'validateStatus')) {
config.validateStatus = getMergedValue(undefined, config1.validateStatus);
} else {
delete config.validateStatus;
}
}
return config;
}