Base UI
NPM Installation / build (16.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (16.x, windows-latest) (push) Has been cancelled
NPM Installation / build (17.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (17.x, windows-latest) (push) Has been cancelled
NPM Installation / build (18.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (18.x, windows-latest) (push) Has been cancelled
NPM Installation / build (16.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (16.x, windows-latest) (push) Has been cancelled
NPM Installation / build (17.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (17.x, windows-latest) (push) Has been cancelled
NPM Installation / build (18.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (18.x, windows-latest) (push) Has been cancelled
This commit is contained in:
+36
@@ -0,0 +1,36 @@
|
||||
# axios // adapters
|
||||
|
||||
The modules under `adapters/` are modules that handle dispatching a request and settling a returned `Promise` once a response is received.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
var settle = require('../core/settle');
|
||||
|
||||
module.exports = function myAdapter(config) {
|
||||
// At this point:
|
||||
// - config has been merged with defaults
|
||||
// - request transformers have already run
|
||||
// - request interceptors have already run
|
||||
|
||||
// Make the request using config provided
|
||||
// Upon response settle the Promise
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var response = {
|
||||
data: responseData,
|
||||
status: request.status,
|
||||
statusText: request.statusText,
|
||||
headers: responseHeaders,
|
||||
config: config,
|
||||
request: request,
|
||||
};
|
||||
|
||||
settle(resolve, reject, response);
|
||||
|
||||
// From here:
|
||||
// - response transformers will run
|
||||
// - response interceptors will run
|
||||
});
|
||||
};
|
||||
```
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
import utils from '../utils.js';
|
||||
import httpAdapter from './http.js';
|
||||
import xhrAdapter from './xhr.js';
|
||||
import * as fetchAdapter from './fetch.js';
|
||||
import AxiosError from '../core/AxiosError.js';
|
||||
|
||||
/**
|
||||
* Known adapters mapping.
|
||||
* Provides environment-specific adapters for Axios:
|
||||
* - `http` for Node.js
|
||||
* - `xhr` for browsers
|
||||
* - `fetch` for fetch API-based requests
|
||||
*
|
||||
* @type {Object<string, Function|Object>}
|
||||
*/
|
||||
const knownAdapters = {
|
||||
http: httpAdapter,
|
||||
xhr: xhrAdapter,
|
||||
fetch: {
|
||||
get: fetchAdapter.getFetch,
|
||||
},
|
||||
};
|
||||
|
||||
// Assign adapter names for easier debugging and identification
|
||||
utils.forEach(knownAdapters, (fn, value) => {
|
||||
if (fn) {
|
||||
try {
|
||||
// Null-proto descriptors so a polluted Object.prototype.get cannot turn
|
||||
// these data descriptors into accessor descriptors on the way in.
|
||||
Object.defineProperty(fn, 'name', { __proto__: null, value });
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-empty
|
||||
}
|
||||
Object.defineProperty(fn, 'adapterName', { __proto__: null, value });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Render a rejection reason string for unknown or unsupported adapters
|
||||
*
|
||||
* @param {string} reason
|
||||
* @returns {string}
|
||||
*/
|
||||
const renderReason = (reason) => `- ${reason}`;
|
||||
|
||||
/**
|
||||
* Check if the adapter is resolved (function, null, or false)
|
||||
*
|
||||
* @param {Function|null|false} adapter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isResolvedHandle = (adapter) =>
|
||||
utils.isFunction(adapter) || adapter === null || adapter === false;
|
||||
|
||||
/**
|
||||
* Get the first suitable adapter from the provided list.
|
||||
* Tries each adapter in order until a supported one is found.
|
||||
* Throws an AxiosError if no adapter is suitable.
|
||||
*
|
||||
* @param {Array<string|Function>|string|Function} adapters - Adapter(s) by name or function.
|
||||
* @param {Object} config - Axios request configuration
|
||||
* @throws {AxiosError} If no suitable adapter is available
|
||||
* @returns {Function} The resolved adapter function
|
||||
*/
|
||||
function getAdapter(adapters, config) {
|
||||
adapters = utils.isArray(adapters) ? adapters : [adapters];
|
||||
|
||||
const { length } = adapters;
|
||||
let nameOrAdapter;
|
||||
let adapter;
|
||||
|
||||
const rejectedReasons = {};
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
nameOrAdapter = adapters[i];
|
||||
let id;
|
||||
|
||||
adapter = nameOrAdapter;
|
||||
|
||||
if (!isResolvedHandle(nameOrAdapter)) {
|
||||
adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
|
||||
|
||||
if (adapter === undefined) {
|
||||
throw new AxiosError(`Unknown adapter '${id}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter && (utils.isFunction(adapter) || (adapter = adapter.get(config)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
rejectedReasons[id || '#' + i] = adapter;
|
||||
}
|
||||
|
||||
if (!adapter) {
|
||||
const reasons = Object.entries(rejectedReasons).map(
|
||||
([id, state]) =>
|
||||
`adapter ${id} ` +
|
||||
(state === false ? 'is not supported by the environment' : 'is not available in the build')
|
||||
);
|
||||
|
||||
let s = length
|
||||
? reasons.length > 1
|
||||
? 'since :\n' + reasons.map(renderReason).join('\n')
|
||||
: ' ' + renderReason(reasons[0])
|
||||
: 'as no adapter specified';
|
||||
|
||||
throw new AxiosError(
|
||||
`There is no suitable adapter to dispatch the request ` + s,
|
||||
AxiosError.ERR_NOT_SUPPORT
|
||||
);
|
||||
}
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports Axios adapters and utility to resolve an adapter
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* Resolve an adapter from a list of adapter names or functions.
|
||||
* @type {Function}
|
||||
*/
|
||||
getAdapter,
|
||||
|
||||
/**
|
||||
* Exposes all known adapters
|
||||
* @type {Object<string, Function|Object>}
|
||||
*/
|
||||
adapters: knownAdapters,
|
||||
};
|
||||
+643
@@ -0,0 +1,643 @@
|
||||
import platform from '../platform/index.js';
|
||||
import utils from '../utils.js';
|
||||
import AxiosError from '../core/AxiosError.js';
|
||||
import composeSignals from '../helpers/composeSignals.js';
|
||||
import { trackStream } from '../helpers/trackStream.js';
|
||||
import AxiosHeaders from '../core/AxiosHeaders.js';
|
||||
import {
|
||||
progressEventReducer,
|
||||
progressEventDecorator,
|
||||
asyncDecorator,
|
||||
} from '../helpers/progressEventReducer.js';
|
||||
import resolveConfig from '../helpers/resolveConfig.js';
|
||||
import settle from '../core/settle.js';
|
||||
import estimateDataURLDecodedBytes from '../helpers/estimateDataURLDecodedBytes.js';
|
||||
import { VERSION } from '../env/data.js';
|
||||
import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js';
|
||||
|
||||
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
|
||||
const { isFunction } = utils;
|
||||
|
||||
/**
|
||||
* Encode a UTF-8 string to a Latin-1 byte string for use with btoa().
|
||||
* This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern.
|
||||
*
|
||||
* @param {string} str The string to encode
|
||||
*
|
||||
* @returns {string} UTF-8 bytes as a Latin-1 string
|
||||
*/
|
||||
const encodeUTF8 = (str) =>
|
||||
encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) =>
|
||||
String.fromCharCode(parseInt(hex, 16))
|
||||
);
|
||||
|
||||
// Node's WHATWG URL parser returns `username` and `password` percent-encoded.
|
||||
// Decode before composing the `auth` option so credentials such as
|
||||
// `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the
|
||||
// original value for malformed input so a bad encoding never throws.
|
||||
const decodeURIComponentSafe = (value) => {
|
||||
if (!utils.isString(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch (error) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
const test = (fn, ...args) => {
|
||||
try {
|
||||
return !!fn(...args);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const maybeWithAuthCredentials = (url) => {
|
||||
const protocolIndex = url.indexOf('://');
|
||||
let urlToCheck = url;
|
||||
if (protocolIndex !== -1) {
|
||||
urlToCheck = urlToCheck.slice(protocolIndex + 3);
|
||||
}
|
||||
return urlToCheck.includes('@') || urlToCheck.includes(':');
|
||||
};
|
||||
|
||||
const factory = (env) => {
|
||||
const globalObject =
|
||||
utils.global !== undefined && utils.global !== null
|
||||
? utils.global
|
||||
: globalThis;
|
||||
const { ReadableStream, TextEncoder } = globalObject;
|
||||
|
||||
env = utils.merge.call(
|
||||
{
|
||||
skipUndefined: true,
|
||||
},
|
||||
{
|
||||
Request: globalObject.Request,
|
||||
Response: globalObject.Response,
|
||||
},
|
||||
env
|
||||
);
|
||||
|
||||
const { fetch: envFetch, Request, Response } = env;
|
||||
const isFetchSupported = envFetch ? isFunction(envFetch) : typeof fetch === 'function';
|
||||
const isRequestSupported = isFunction(Request);
|
||||
const isResponseSupported = isFunction(Response);
|
||||
|
||||
if (!isFetchSupported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isReadableStreamSupported = isFetchSupported && isFunction(ReadableStream);
|
||||
|
||||
const encodeText =
|
||||
isFetchSupported &&
|
||||
(typeof TextEncoder === 'function'
|
||||
? (
|
||||
(encoder) => (str) =>
|
||||
encoder.encode(str)
|
||||
)(new TextEncoder())
|
||||
: async (str) => new Uint8Array(await new Request(str).arrayBuffer()));
|
||||
|
||||
const supportsRequestStream =
|
||||
isRequestSupported &&
|
||||
isReadableStreamSupported &&
|
||||
test(() => {
|
||||
let duplexAccessed = false;
|
||||
|
||||
const request = new Request(platform.origin, {
|
||||
body: new ReadableStream(),
|
||||
method: 'POST',
|
||||
get duplex() {
|
||||
duplexAccessed = true;
|
||||
return 'half';
|
||||
},
|
||||
});
|
||||
|
||||
const hasContentType = request.headers.has('Content-Type');
|
||||
|
||||
if (request.body != null) {
|
||||
request.body.cancel();
|
||||
}
|
||||
|
||||
return duplexAccessed && !hasContentType;
|
||||
});
|
||||
|
||||
const supportsResponseStream =
|
||||
isResponseSupported &&
|
||||
isReadableStreamSupported &&
|
||||
test(() => utils.isReadableStream(new Response('').body));
|
||||
|
||||
const resolvers = {
|
||||
stream: supportsResponseStream && ((res) => res.body),
|
||||
};
|
||||
|
||||
isFetchSupported &&
|
||||
(() => {
|
||||
['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach((type) => {
|
||||
!resolvers[type] &&
|
||||
(resolvers[type] = (res, config) => {
|
||||
let method = res && res[type];
|
||||
|
||||
if (method) {
|
||||
return method.call(res);
|
||||
}
|
||||
|
||||
throw new AxiosError(
|
||||
`Response type '${type}' is not supported`,
|
||||
AxiosError.ERR_NOT_SUPPORT,
|
||||
config
|
||||
);
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
const getBodyLength = async (body) => {
|
||||
if (body == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (utils.isBlob(body)) {
|
||||
return body.size;
|
||||
}
|
||||
|
||||
if (utils.isSpecCompliantForm(body)) {
|
||||
const _request = new Request(platform.origin, {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
return (await _request.arrayBuffer()).byteLength;
|
||||
}
|
||||
|
||||
if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
|
||||
return body.byteLength;
|
||||
}
|
||||
|
||||
if (utils.isURLSearchParams(body)) {
|
||||
body = body + '';
|
||||
}
|
||||
|
||||
if (utils.isString(body)) {
|
||||
return (await encodeText(body)).byteLength;
|
||||
}
|
||||
};
|
||||
|
||||
const resolveBodyLength = async (headers, body) => {
|
||||
const length = utils.toFiniteNumber(headers.getContentLength());
|
||||
|
||||
return length == null ? getBodyLength(body) : length;
|
||||
};
|
||||
|
||||
return async (config) => {
|
||||
let {
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
signal,
|
||||
cancelToken,
|
||||
timeout,
|
||||
onDownloadProgress,
|
||||
onUploadProgress,
|
||||
responseType,
|
||||
headers,
|
||||
withCredentials = 'same-origin',
|
||||
fetchOptions,
|
||||
maxContentLength,
|
||||
maxBodyLength,
|
||||
} = resolveConfig(config);
|
||||
|
||||
const hasMaxContentLength = utils.isNumber(maxContentLength) && maxContentLength > -1;
|
||||
const hasMaxBodyLength = utils.isNumber(maxBodyLength) && maxBodyLength > -1;
|
||||
const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined);
|
||||
|
||||
let _fetch = envFetch || fetch;
|
||||
|
||||
responseType = responseType ? (responseType + '').toLowerCase() : 'text';
|
||||
|
||||
let composedSignal = composeSignals(
|
||||
[signal, cancelToken && cancelToken.toAbortSignal()],
|
||||
timeout
|
||||
);
|
||||
|
||||
let request = null;
|
||||
|
||||
const unsubscribe =
|
||||
composedSignal &&
|
||||
composedSignal.unsubscribe &&
|
||||
(() => {
|
||||
composedSignal.unsubscribe();
|
||||
});
|
||||
|
||||
let requestContentLength;
|
||||
|
||||
// AxiosError we raise while the request body is being streamed. Captured
|
||||
// by identity so the catch block can surface it directly, regardless of
|
||||
// how the runtime wraps the resulting fetch rejection (undici exposes it
|
||||
// as `err.cause`; some browsers drop the original error entirely).
|
||||
let pendingBodyError = null;
|
||||
|
||||
const maxBodyLengthError = () =>
|
||||
new AxiosError(
|
||||
'Request body larger than maxBodyLength limit',
|
||||
AxiosError.ERR_BAD_REQUEST,
|
||||
config,
|
||||
request
|
||||
);
|
||||
|
||||
try {
|
||||
// HTTP basic authentication
|
||||
let auth = undefined;
|
||||
const configAuth = own('auth');
|
||||
|
||||
if (configAuth) {
|
||||
const username = utils.getSafeProp(configAuth, 'username') || '';
|
||||
const password = utils.getSafeProp(configAuth, 'password') || '';
|
||||
auth = {
|
||||
username,
|
||||
password
|
||||
};
|
||||
}
|
||||
|
||||
if (maybeWithAuthCredentials(url)) {
|
||||
const parsedURL = new URL(url, platform.origin);
|
||||
|
||||
if (!auth && (parsedURL.username || parsedURL.password)) {
|
||||
const urlUsername = decodeURIComponentSafe(parsedURL.username);
|
||||
const urlPassword = decodeURIComponentSafe(parsedURL.password);
|
||||
auth = {
|
||||
username: urlUsername,
|
||||
password: urlPassword
|
||||
};
|
||||
}
|
||||
|
||||
if (parsedURL.username || parsedURL.password) {
|
||||
parsedURL.username = '';
|
||||
parsedURL.password = '';
|
||||
url = parsedURL.href;
|
||||
}
|
||||
}
|
||||
|
||||
if (auth) {
|
||||
headers.delete('authorization');
|
||||
headers.set(
|
||||
'Authorization',
|
||||
'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || '')))
|
||||
);
|
||||
}
|
||||
|
||||
// Enforce maxContentLength for data: URLs up-front so we never materialize
|
||||
// an oversized payload. The HTTP adapter applies the same check (see http.js
|
||||
// "if (protocol === 'data:')" branch).
|
||||
if (hasMaxContentLength && typeof url === 'string' && url.startsWith('data:')) {
|
||||
const estimated = estimateDataURLDecodedBytes(url);
|
||||
if (estimated > maxContentLength) {
|
||||
throw new AxiosError(
|
||||
'maxContentLength size of ' + maxContentLength + ' exceeded',
|
||||
AxiosError.ERR_BAD_RESPONSE,
|
||||
config,
|
||||
request
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce maxBodyLength against known-size bodies before dispatch using
|
||||
// the body's *actual* size — never a caller-declared Content-Length,
|
||||
// which could under-report to slip an oversized body past the check.
|
||||
// Unknown-size streams return undefined here and are counted per-chunk
|
||||
// below as fetch consumes them.
|
||||
if (hasMaxBodyLength && method !== 'get' && method !== 'head') {
|
||||
const outboundLength = await getBodyLength(data);
|
||||
if (typeof outboundLength === 'number' && isFinite(outboundLength)) {
|
||||
requestContentLength = outboundLength;
|
||||
if (outboundLength > maxBodyLength) {
|
||||
throw maxBodyLengthError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A streamed body under maxBodyLength must be counted as fetch consumes
|
||||
// it; its size is never trusted from a caller-declared Content-Length.
|
||||
const mustEnforceStreamBody =
|
||||
hasMaxBodyLength && (utils.isReadableStream(data) || utils.isStream(data));
|
||||
|
||||
const trackRequestStream = (stream, onProgress, flush) =>
|
||||
trackStream(
|
||||
stream,
|
||||
DEFAULT_CHUNK_SIZE,
|
||||
(loadedBytes) => {
|
||||
if (hasMaxBodyLength && loadedBytes > maxBodyLength) {
|
||||
throw (pendingBodyError = maxBodyLengthError());
|
||||
}
|
||||
onProgress && onProgress(loadedBytes);
|
||||
},
|
||||
flush
|
||||
);
|
||||
|
||||
if (
|
||||
supportsRequestStream &&
|
||||
method !== 'get' &&
|
||||
method !== 'head' &&
|
||||
(onUploadProgress || mustEnforceStreamBody)
|
||||
) {
|
||||
requestContentLength =
|
||||
requestContentLength == null ? await resolveBodyLength(headers, data) : requestContentLength;
|
||||
|
||||
// A declared length of 0 is only trusted to skip the wrap when we are
|
||||
// not enforcing a stream limit (which must not rely on that header).
|
||||
if (requestContentLength !== 0 || mustEnforceStreamBody) {
|
||||
let _request = new Request(url, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
duplex: 'half',
|
||||
});
|
||||
|
||||
let contentTypeHeader;
|
||||
|
||||
if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
|
||||
headers.setContentType(contentTypeHeader);
|
||||
}
|
||||
|
||||
if (_request.body) {
|
||||
const [onProgress, flush] =
|
||||
(onUploadProgress &&
|
||||
progressEventDecorator(
|
||||
requestContentLength,
|
||||
progressEventReducer(asyncDecorator(onUploadProgress))
|
||||
)) ||
|
||||
[];
|
||||
|
||||
data = trackRequestStream(_request.body, onProgress, flush);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
mustEnforceStreamBody &&
|
||||
!isRequestSupported &&
|
||||
isReadableStreamSupported &&
|
||||
method !== 'get' &&
|
||||
method !== 'head'
|
||||
) {
|
||||
data = trackRequestStream(data);
|
||||
} else if (
|
||||
mustEnforceStreamBody &&
|
||||
isRequestSupported &&
|
||||
!supportsRequestStream &&
|
||||
method !== 'get' &&
|
||||
method !== 'head'
|
||||
) {
|
||||
throw new AxiosError(
|
||||
'Stream request bodies are not supported by the current fetch implementation',
|
||||
AxiosError.ERR_NOT_SUPPORT,
|
||||
config,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
if (!utils.isString(withCredentials)) {
|
||||
withCredentials = withCredentials ? 'include' : 'omit';
|
||||
}
|
||||
|
||||
// Cloudflare Workers throws when credentials are defined
|
||||
// see https://github.com/cloudflare/workerd/issues/902
|
||||
const isCredentialsSupported = isRequestSupported && 'credentials' in Request.prototype;
|
||||
|
||||
// If data is FormData and Content-Type is multipart/form-data without boundary,
|
||||
// delete it so fetch can set it correctly with the boundary
|
||||
if (utils.isFormData(data)) {
|
||||
const contentType = headers.getContentType();
|
||||
if (
|
||||
contentType &&
|
||||
/^multipart\/form-data/i.test(contentType) &&
|
||||
!/boundary=/i.test(contentType)
|
||||
) {
|
||||
headers.delete('content-type');
|
||||
}
|
||||
}
|
||||
|
||||
// Set User-Agent header if not already set (fetch defaults to 'node' in Node.js)
|
||||
headers.set('User-Agent', 'axios/' + VERSION, false);
|
||||
|
||||
const resolvedOptions = {
|
||||
...fetchOptions,
|
||||
signal: composedSignal,
|
||||
method: method.toUpperCase(),
|
||||
headers: toByteStringHeaderObject(headers.normalize()),
|
||||
body: data,
|
||||
duplex: 'half',
|
||||
credentials: isCredentialsSupported ? withCredentials : undefined,
|
||||
};
|
||||
|
||||
request = isRequestSupported && new Request(url, resolvedOptions);
|
||||
|
||||
let response = await (isRequestSupported
|
||||
? _fetch(request, fetchOptions)
|
||||
: _fetch(url, resolvedOptions));
|
||||
|
||||
const responseHeaders = AxiosHeaders.from(response.headers);
|
||||
|
||||
// Cheap pre-check: if the server honestly declares a content-length that
|
||||
// already exceeds the cap, reject before we start streaming.
|
||||
if (hasMaxContentLength) {
|
||||
const declaredLength = utils.toFiniteNumber(responseHeaders.getContentLength());
|
||||
if (declaredLength != null && declaredLength > maxContentLength) {
|
||||
throw new AxiosError(
|
||||
'maxContentLength size of ' + maxContentLength + ' exceeded',
|
||||
AxiosError.ERR_BAD_RESPONSE,
|
||||
config,
|
||||
request
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const isStreamResponse =
|
||||
supportsResponseStream && (responseType === 'stream' || responseType === 'response');
|
||||
|
||||
if (
|
||||
supportsResponseStream &&
|
||||
response.body &&
|
||||
(onDownloadProgress || hasMaxContentLength || (isStreamResponse && unsubscribe))
|
||||
) {
|
||||
const options = {};
|
||||
|
||||
['status', 'statusText', 'headers'].forEach((prop) => {
|
||||
options[prop] = response[prop];
|
||||
});
|
||||
|
||||
const responseContentLength = utils.toFiniteNumber(responseHeaders.getContentLength());
|
||||
|
||||
const [onProgress, flush] =
|
||||
(onDownloadProgress &&
|
||||
progressEventDecorator(
|
||||
responseContentLength,
|
||||
progressEventReducer(asyncDecorator(onDownloadProgress), true)
|
||||
)) ||
|
||||
[];
|
||||
|
||||
let bytesRead = 0;
|
||||
const onChunkProgress = (loadedBytes) => {
|
||||
if (hasMaxContentLength) {
|
||||
bytesRead = loadedBytes;
|
||||
if (bytesRead > maxContentLength) {
|
||||
throw new AxiosError(
|
||||
'maxContentLength size of ' + maxContentLength + ' exceeded',
|
||||
AxiosError.ERR_BAD_RESPONSE,
|
||||
config,
|
||||
request
|
||||
);
|
||||
}
|
||||
}
|
||||
onProgress && onProgress(loadedBytes);
|
||||
};
|
||||
|
||||
response = new Response(
|
||||
trackStream(response.body, DEFAULT_CHUNK_SIZE, onChunkProgress, () => {
|
||||
flush && flush();
|
||||
unsubscribe && unsubscribe();
|
||||
}),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
responseType = responseType || 'text';
|
||||
|
||||
let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](
|
||||
response,
|
||||
config
|
||||
);
|
||||
|
||||
// Fallback enforcement for environments without ReadableStream support
|
||||
// (legacy runtimes). Detect materialized size from typed output; skip
|
||||
// streams/Response passthrough since the user will read those themselves.
|
||||
if (hasMaxContentLength && !supportsResponseStream && !isStreamResponse) {
|
||||
let materializedSize;
|
||||
if (responseData != null) {
|
||||
if (typeof responseData.byteLength === 'number') {
|
||||
materializedSize = responseData.byteLength;
|
||||
} else if (typeof responseData.size === 'number') {
|
||||
materializedSize = responseData.size;
|
||||
} else if (typeof responseData === 'string') {
|
||||
materializedSize =
|
||||
typeof TextEncoder === 'function'
|
||||
? new TextEncoder().encode(responseData).byteLength
|
||||
: responseData.length;
|
||||
}
|
||||
}
|
||||
if (typeof materializedSize === 'number' && materializedSize > maxContentLength) {
|
||||
throw new AxiosError(
|
||||
'maxContentLength size of ' + maxContentLength + ' exceeded',
|
||||
AxiosError.ERR_BAD_RESPONSE,
|
||||
config,
|
||||
request
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
!isStreamResponse && unsubscribe && unsubscribe();
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
settle(resolve, reject, {
|
||||
data: responseData,
|
||||
headers: AxiosHeaders.from(response.headers),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
config,
|
||||
request,
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
unsubscribe && unsubscribe();
|
||||
|
||||
// Safari can surface fetch aborts as a DOMException-like object whose
|
||||
// branded getters throw. Prefer our composed signal reason before reading
|
||||
// the caught error, preserving timeout vs cancellation semantics.
|
||||
if (composedSignal && composedSignal.aborted && composedSignal.reason instanceof AxiosError) {
|
||||
const canceledError = composedSignal.reason;
|
||||
canceledError.config = config;
|
||||
request && (canceledError.request = request);
|
||||
if (err !== canceledError) {
|
||||
// Non-enumerable to match native Error `cause` semantics so loggers
|
||||
// don't recurse into circular fetch internals (see #7205).
|
||||
Object.defineProperty(canceledError, 'cause', {
|
||||
__proto__: null,
|
||||
value: err,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
throw canceledError;
|
||||
}
|
||||
|
||||
// Surface a maxBodyLength violation we raised while the request body was
|
||||
// being streamed. Matching by identity (rather than reading
|
||||
// `err.cause.isAxiosError`) keeps the error deterministic across runtimes
|
||||
// and avoids both prototype-pollution reads and mis-attributing a foreign
|
||||
// AxiosError that merely happened to land in `err.cause`.
|
||||
if (pendingBodyError) {
|
||||
request && !pendingBodyError.request && (pendingBodyError.request = request);
|
||||
throw pendingBodyError;
|
||||
}
|
||||
|
||||
// Re-throw AxiosErrors we raised synchronously (data: URL / content-length
|
||||
// pre-checks, response size enforcement) without re-wrapping them.
|
||||
if (err instanceof AxiosError) {
|
||||
request && !err.request && (err.request = request);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
|
||||
const networkError = new AxiosError(
|
||||
'Network Error',
|
||||
AxiosError.ERR_NETWORK,
|
||||
config,
|
||||
request,
|
||||
err && err.response
|
||||
);
|
||||
// Non-enumerable to match native Error `cause` semantics so loggers
|
||||
// don't recurse into circular fetch internals (see #7205).
|
||||
Object.defineProperty(networkError, 'cause', {
|
||||
__proto__: null,
|
||||
value: err.cause || err,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
});
|
||||
throw networkError;
|
||||
}
|
||||
|
||||
throw AxiosError.from(err, err && err.code, config, request, err && err.response);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const seedCache = new Map();
|
||||
|
||||
export const getFetch = (config) => {
|
||||
let env = (config && config.env) || {};
|
||||
const { fetch, Request, Response } = env;
|
||||
const seeds = [Request, Response, fetch];
|
||||
|
||||
let len = seeds.length,
|
||||
i = len,
|
||||
seed,
|
||||
target,
|
||||
map = seedCache;
|
||||
|
||||
while (i--) {
|
||||
seed = seeds[i];
|
||||
target = map.get(seed);
|
||||
|
||||
target === undefined && map.set(seed, (target = i ? new Map() : factory(env)));
|
||||
|
||||
map = target;
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
const adapter = getFetch();
|
||||
|
||||
export default adapter;
|
||||
+1417
File diff suppressed because it is too large
Load Diff
+228
@@ -0,0 +1,228 @@
|
||||
import utils from '../utils.js';
|
||||
import settle from '../core/settle.js';
|
||||
import transitionalDefaults from '../defaults/transitional.js';
|
||||
import AxiosError from '../core/AxiosError.js';
|
||||
import CanceledError from '../cancel/CanceledError.js';
|
||||
import parseProtocol from '../helpers/parseProtocol.js';
|
||||
import platform from '../platform/index.js';
|
||||
import AxiosHeaders from '../core/AxiosHeaders.js';
|
||||
import { progressEventReducer } from '../helpers/progressEventReducer.js';
|
||||
import resolveConfig from '../helpers/resolveConfig.js';
|
||||
import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js';
|
||||
|
||||
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
|
||||
|
||||
export default isXHRAdapterSupported &&
|
||||
function (config) {
|
||||
return new Promise(function dispatchXhrRequest(resolve, reject) {
|
||||
const _config = resolveConfig(config);
|
||||
let requestData = _config.data;
|
||||
const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
|
||||
let { responseType, onUploadProgress, onDownloadProgress } = _config;
|
||||
let onCanceled;
|
||||
let uploadThrottled, downloadThrottled;
|
||||
let flushUpload, flushDownload;
|
||||
|
||||
function done() {
|
||||
flushUpload && flushUpload(); // flush events
|
||||
flushDownload && flushDownload(); // flush events
|
||||
|
||||
_config.cancelToken && _config.cancelToken.unsubscribe(onCanceled);
|
||||
|
||||
_config.signal && _config.signal.removeEventListener('abort', onCanceled);
|
||||
}
|
||||
|
||||
let request = new XMLHttpRequest();
|
||||
|
||||
request.open(_config.method.toUpperCase(), _config.url, true);
|
||||
|
||||
// Set the request timeout in MS
|
||||
request.timeout = _config.timeout;
|
||||
|
||||
function onloadend() {
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
// Prepare the response
|
||||
const responseHeaders = AxiosHeaders.from(
|
||||
'getAllResponseHeaders' in request && request.getAllResponseHeaders()
|
||||
);
|
||||
const responseData =
|
||||
!responseType || responseType === 'text' || responseType === 'json'
|
||||
? request.responseText
|
||||
: request.response;
|
||||
const response = {
|
||||
data: responseData,
|
||||
status: request.status,
|
||||
statusText: request.statusText,
|
||||
headers: responseHeaders,
|
||||
config,
|
||||
request,
|
||||
};
|
||||
|
||||
settle(
|
||||
function _resolve(value) {
|
||||
resolve(value);
|
||||
done();
|
||||
},
|
||||
function _reject(err) {
|
||||
reject(err);
|
||||
done();
|
||||
},
|
||||
response
|
||||
);
|
||||
|
||||
// Clean up request
|
||||
request = null;
|
||||
}
|
||||
|
||||
if ('onloadend' in request) {
|
||||
// Use onloadend if available
|
||||
request.onloadend = onloadend;
|
||||
} else {
|
||||
// Listen for ready state to emulate onloadend
|
||||
request.onreadystatechange = function handleLoad() {
|
||||
if (!request || request.readyState !== 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The request errored out and we didn't get a response, this will be
|
||||
// handled by onerror instead
|
||||
// With one exception: request that using file: protocol, most browsers
|
||||
// will return status as 0 even though it's a successful request
|
||||
if (
|
||||
request.status === 0 &&
|
||||
!(request.responseURL && request.responseURL.startsWith('file:'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// readystate handler is calling before onerror or ontimeout handlers,
|
||||
// so we should call onloadend on the next 'tick'
|
||||
setTimeout(onloadend);
|
||||
};
|
||||
}
|
||||
|
||||
// Handle browser request cancellation (as opposed to a manual cancellation)
|
||||
request.onabort = function handleAbort() {
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
|
||||
reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
|
||||
done();
|
||||
|
||||
// Clean up request
|
||||
request = null;
|
||||
};
|
||||
|
||||
// Handle low level network errors
|
||||
request.onerror = function handleError(event) {
|
||||
// Browsers deliver a ProgressEvent in XHR onerror
|
||||
// (message may be empty; when present, surface it)
|
||||
// See https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/error_event
|
||||
const msg = event && event.message ? event.message : 'Network Error';
|
||||
const err = new AxiosError(msg, AxiosError.ERR_NETWORK, config, request);
|
||||
// attach the underlying event for consumers who want details
|
||||
err.event = event || null;
|
||||
reject(err);
|
||||
done();
|
||||
request = null;
|
||||
};
|
||||
|
||||
// Handle timeout
|
||||
request.ontimeout = function handleTimeout() {
|
||||
let timeoutErrorMessage = _config.timeout
|
||||
? 'timeout of ' + _config.timeout + 'ms exceeded'
|
||||
: 'timeout exceeded';
|
||||
const transitional = _config.transitional || transitionalDefaults;
|
||||
if (_config.timeoutErrorMessage) {
|
||||
timeoutErrorMessage = _config.timeoutErrorMessage;
|
||||
}
|
||||
reject(
|
||||
new AxiosError(
|
||||
timeoutErrorMessage,
|
||||
transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
|
||||
config,
|
||||
request
|
||||
)
|
||||
);
|
||||
done();
|
||||
|
||||
// Clean up request
|
||||
request = null;
|
||||
};
|
||||
|
||||
// Remove Content-Type if data is undefined
|
||||
requestData === undefined && requestHeaders.setContentType(null);
|
||||
|
||||
// Add headers to the request
|
||||
if ('setRequestHeader' in request) {
|
||||
utils.forEach(toByteStringHeaderObject(requestHeaders), function setRequestHeader(val, key) {
|
||||
request.setRequestHeader(key, val);
|
||||
});
|
||||
}
|
||||
|
||||
// Add withCredentials to request if needed
|
||||
if (!utils.isUndefined(_config.withCredentials)) {
|
||||
request.withCredentials = !!_config.withCredentials;
|
||||
}
|
||||
|
||||
// Add responseType to request if needed
|
||||
if (responseType && responseType !== 'json') {
|
||||
request.responseType = _config.responseType;
|
||||
}
|
||||
|
||||
// Handle progress if needed
|
||||
if (onDownloadProgress) {
|
||||
[downloadThrottled, flushDownload] = progressEventReducer(onDownloadProgress, true);
|
||||
request.addEventListener('progress', downloadThrottled);
|
||||
}
|
||||
|
||||
// Not all browsers support upload events
|
||||
if (onUploadProgress && request.upload) {
|
||||
[uploadThrottled, flushUpload] = progressEventReducer(onUploadProgress);
|
||||
|
||||
request.upload.addEventListener('progress', uploadThrottled);
|
||||
|
||||
request.upload.addEventListener('loadend', flushUpload);
|
||||
}
|
||||
|
||||
if (_config.cancelToken || _config.signal) {
|
||||
// Handle cancellation
|
||||
// eslint-disable-next-line func-names
|
||||
onCanceled = (cancel) => {
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
|
||||
request.abort();
|
||||
done();
|
||||
request = null;
|
||||
};
|
||||
|
||||
_config.cancelToken && _config.cancelToken.subscribe(onCanceled);
|
||||
if (_config.signal) {
|
||||
_config.signal.aborted
|
||||
? onCanceled()
|
||||
: _config.signal.addEventListener('abort', onCanceled);
|
||||
}
|
||||
}
|
||||
|
||||
const protocol = parseProtocol(_config.url);
|
||||
|
||||
if (protocol && !platform.protocols.includes(protocol)) {
|
||||
reject(
|
||||
new AxiosError(
|
||||
'Unsupported protocol ' + protocol + ':',
|
||||
AxiosError.ERR_BAD_REQUEST,
|
||||
config
|
||||
)
|
||||
);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the request
|
||||
request.send(requestData || null);
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user