mirror of
https://github.com/tenrok/axios.git
synced 2026-06-20 20:00:40 +03:00
feat(adapter): add fetch adapter; (#6371)
This commit is contained in:
@@ -15,7 +15,8 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [12.x, 14.x, 16.x, 18.x, 20.x]
|
node-version: [12.x, 14.x, 16.x, 18.x, 20.x, 21.x]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|||||||
+5
-2
@@ -268,7 +268,8 @@ declare namespace axios {
|
|||||||
| 'document'
|
| 'document'
|
||||||
| 'json'
|
| 'json'
|
||||||
| 'text'
|
| 'text'
|
||||||
| 'stream';
|
| 'stream'
|
||||||
|
| 'formdata';
|
||||||
|
|
||||||
type responseEncoding =
|
type responseEncoding =
|
||||||
| 'ascii' | 'ASCII'
|
| 'ascii' | 'ASCII'
|
||||||
@@ -353,11 +354,12 @@ declare namespace axios {
|
|||||||
upload?: boolean;
|
upload?: boolean;
|
||||||
download?: boolean;
|
download?: boolean;
|
||||||
event?: BrowserProgressEvent;
|
event?: BrowserProgressEvent;
|
||||||
|
lengthComputable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Milliseconds = number;
|
type Milliseconds = number;
|
||||||
|
|
||||||
type AxiosAdapterName = 'xhr' | 'http' | string;
|
type AxiosAdapterName = 'fetch' | 'xhr' | 'http' | string;
|
||||||
|
|
||||||
type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;
|
type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;
|
||||||
|
|
||||||
@@ -415,6 +417,7 @@ declare namespace axios {
|
|||||||
lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |
|
lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |
|
||||||
((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);
|
((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);
|
||||||
withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);
|
withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);
|
||||||
|
fetchOptions?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alias
|
// Alias
|
||||||
|
|||||||
Vendored
+5
-2
@@ -209,7 +209,8 @@ export type ResponseType =
|
|||||||
| 'document'
|
| 'document'
|
||||||
| 'json'
|
| 'json'
|
||||||
| 'text'
|
| 'text'
|
||||||
| 'stream';
|
| 'stream'
|
||||||
|
| 'formdata';
|
||||||
|
|
||||||
export type responseEncoding =
|
export type responseEncoding =
|
||||||
| 'ascii' | 'ASCII'
|
| 'ascii' | 'ASCII'
|
||||||
@@ -294,11 +295,12 @@ export interface AxiosProgressEvent {
|
|||||||
upload?: boolean;
|
upload?: boolean;
|
||||||
download?: boolean;
|
download?: boolean;
|
||||||
event?: BrowserProgressEvent;
|
event?: BrowserProgressEvent;
|
||||||
|
lengthComputable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Milliseconds = number;
|
type Milliseconds = number;
|
||||||
|
|
||||||
type AxiosAdapterName = 'xhr' | 'http' | string;
|
type AxiosAdapterName = 'fetch' | 'xhr' | 'http' | string;
|
||||||
|
|
||||||
type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;
|
type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;
|
||||||
|
|
||||||
@@ -356,6 +358,7 @@ export interface AxiosRequestConfig<D = any> {
|
|||||||
lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |
|
lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |
|
||||||
((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);
|
((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);
|
||||||
withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);
|
withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);
|
||||||
|
fetchOptions?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alias
|
// Alias
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import utils from '../utils.js';
|
import utils from '../utils.js';
|
||||||
import httpAdapter from './http.js';
|
import httpAdapter from './http.js';
|
||||||
import xhrAdapter from './xhr.js';
|
import xhrAdapter from './xhr.js';
|
||||||
|
import fetchAdapter from './fetch.js';
|
||||||
import AxiosError from "../core/AxiosError.js";
|
import AxiosError from "../core/AxiosError.js";
|
||||||
|
|
||||||
const knownAdapters = {
|
const knownAdapters = {
|
||||||
http: httpAdapter,
|
http: httpAdapter,
|
||||||
xhr: xhrAdapter
|
xhr: xhrAdapter,
|
||||||
|
fetch: fetchAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.forEach(knownAdapters, (fn, value) => {
|
utils.forEach(knownAdapters, (fn, value) => {
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
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 from "../helpers/progressEventReducer.js";
|
||||||
|
import resolveConfig from "../helpers/resolveConfig.js";
|
||||||
|
import settle from "../core/settle.js";
|
||||||
|
|
||||||
|
const fetchProgressDecorator = (total, fn) => {
|
||||||
|
const lengthComputable = total != null;
|
||||||
|
return (loaded) => setTimeout(() => fn({
|
||||||
|
lengthComputable,
|
||||||
|
total,
|
||||||
|
loaded
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFetchSupported = typeof fetch !== 'undefined';
|
||||||
|
|
||||||
|
const supportsRequestStreams = isFetchSupported && (() => {
|
||||||
|
let duplexAccessed = false;
|
||||||
|
|
||||||
|
const hasContentType = new Request(platform.origin, {
|
||||||
|
body: new ReadableStream(),
|
||||||
|
method: 'POST',
|
||||||
|
get duplex() {
|
||||||
|
duplexAccessed = true;
|
||||||
|
return 'half';
|
||||||
|
},
|
||||||
|
}).headers.has('Content-Type');
|
||||||
|
|
||||||
|
return duplexAccessed && !hasContentType;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||||
|
|
||||||
|
const resolvers = {
|
||||||
|
stream: (res) => res.body
|
||||||
|
};
|
||||||
|
|
||||||
|
isFetchSupported && ['text', 'arrayBuffer', 'blob', 'formData'].forEach(type => [
|
||||||
|
resolvers[type] = utils.isFunction(Response.prototype[type]) ? (res) => res[type]() : (_, config) => {
|
||||||
|
throw new AxiosError(`Response type ${type} is not supported`, AxiosError.ERR_NOT_SUPPORT, config);
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const getBodyLength = async (body) => {
|
||||||
|
if(utils.isBlob(body)) {
|
||||||
|
return body.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(utils.isSpecCompliantForm(body)) {
|
||||||
|
return (await new Request(body).arrayBuffer()).byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(utils.isArrayBufferView(body)) {
|
||||||
|
return body.byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(utils.isURLSearchParams(body)) {
|
||||||
|
body = body + '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(utils.isString(body)) {
|
||||||
|
return (await new TextEncoder().encode(body)).byteLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveBodyLength = async (headers, body) => {
|
||||||
|
const length = utils.toFiniteNumber(headers.getContentLength());
|
||||||
|
|
||||||
|
return length == null ? getBodyLength(body) : length;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async (config) => {
|
||||||
|
let {
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
data,
|
||||||
|
signal,
|
||||||
|
cancelToken,
|
||||||
|
timeout,
|
||||||
|
onDownloadProgress,
|
||||||
|
onUploadProgress,
|
||||||
|
responseType,
|
||||||
|
headers,
|
||||||
|
withCredentials = 'same-origin',
|
||||||
|
fetchOptions
|
||||||
|
} = resolveConfig(config);
|
||||||
|
|
||||||
|
responseType = responseType ? (responseType + '').toLowerCase() : 'text';
|
||||||
|
|
||||||
|
let [composedSignal, stopTimeout] = (signal || cancelToken || timeout) ?
|
||||||
|
composeSignals([signal, cancelToken], timeout) : [];
|
||||||
|
|
||||||
|
let finished, request;
|
||||||
|
|
||||||
|
const onFinish = () => {
|
||||||
|
!finished && setTimeout(() => {
|
||||||
|
composedSignal && composedSignal.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (onUploadProgress && supportsRequestStreams && method !== 'get' && method !== 'head') {
|
||||||
|
let requestContentLength = await resolveBodyLength(headers, data);
|
||||||
|
|
||||||
|
let _request = new Request(url, {
|
||||||
|
method,
|
||||||
|
body: data,
|
||||||
|
duplex: "half"
|
||||||
|
});
|
||||||
|
|
||||||
|
let contentTypeHeader;
|
||||||
|
|
||||||
|
if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
|
||||||
|
headers.setContentType(contentTypeHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, fetchProgressDecorator(
|
||||||
|
requestContentLength,
|
||||||
|
progressEventReducer(onUploadProgress)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!utils.isString(withCredentials)) {
|
||||||
|
withCredentials = withCredentials ? 'cors' : 'omit';
|
||||||
|
}
|
||||||
|
|
||||||
|
request = new Request(url, {
|
||||||
|
...fetchOptions,
|
||||||
|
signal: composedSignal,
|
||||||
|
method,
|
||||||
|
headers: headers.normalize().toJSON(),
|
||||||
|
body: data,
|
||||||
|
duplex: "half",
|
||||||
|
withCredentials
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await fetch(request);
|
||||||
|
|
||||||
|
const isStreamResponse = responseType === 'stream' || responseType === 'response';
|
||||||
|
|
||||||
|
if (onDownloadProgress || isStreamResponse) {
|
||||||
|
const options = {};
|
||||||
|
|
||||||
|
Object.getOwnPropertyNames(response).forEach(prop => {
|
||||||
|
options[prop] = response[prop];
|
||||||
|
});
|
||||||
|
|
||||||
|
const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
|
||||||
|
|
||||||
|
response = new Response(
|
||||||
|
trackStream(response.body, DEFAULT_CHUNK_SIZE, onDownloadProgress && fetchProgressDecorator(
|
||||||
|
responseContentLength,
|
||||||
|
progressEventReducer(onDownloadProgress, true)
|
||||||
|
), isStreamResponse && onFinish),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseType = responseType || 'text';
|
||||||
|
|
||||||
|
let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](response, config);
|
||||||
|
|
||||||
|
!isStreamResponse && onFinish();
|
||||||
|
|
||||||
|
stopTimeout && stopTimeout();
|
||||||
|
|
||||||
|
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) {
|
||||||
|
onFinish();
|
||||||
|
|
||||||
|
let {code} = err;
|
||||||
|
|
||||||
|
if (err.name === 'NetworkError') {
|
||||||
|
code = AxiosError.ERR_NETWORK;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw AxiosError.from(err, code, config, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+31
-101
@@ -1,93 +1,39 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import utils from './../utils.js';
|
import utils from './../utils.js';
|
||||||
import settle from './../core/settle.js';
|
import settle from './../core/settle.js';
|
||||||
import cookies from './../helpers/cookies.js';
|
|
||||||
import buildURL from './../helpers/buildURL.js';
|
|
||||||
import buildFullPath from '../core/buildFullPath.js';
|
|
||||||
import isURLSameOrigin from './../helpers/isURLSameOrigin.js';
|
|
||||||
import transitionalDefaults from '../defaults/transitional.js';
|
import transitionalDefaults from '../defaults/transitional.js';
|
||||||
import AxiosError from '../core/AxiosError.js';
|
import AxiosError from '../core/AxiosError.js';
|
||||||
import CanceledError from '../cancel/CanceledError.js';
|
import CanceledError from '../cancel/CanceledError.js';
|
||||||
import parseProtocol from '../helpers/parseProtocol.js';
|
import parseProtocol from '../helpers/parseProtocol.js';
|
||||||
import platform from '../platform/index.js';
|
import platform from '../platform/index.js';
|
||||||
import AxiosHeaders from '../core/AxiosHeaders.js';
|
import AxiosHeaders from '../core/AxiosHeaders.js';
|
||||||
import speedometer from '../helpers/speedometer.js';
|
import progressEventReducer from '../helpers/progressEventReducer.js';
|
||||||
|
import resolveConfig from "../helpers/resolveConfig.js";
|
||||||
function progressEventReducer(listener, isDownloadStream) {
|
|
||||||
let bytesNotified = 0;
|
|
||||||
const _speedometer = speedometer(50, 250);
|
|
||||||
|
|
||||||
return e => {
|
|
||||||
const loaded = e.loaded;
|
|
||||||
const total = e.lengthComputable ? e.total : undefined;
|
|
||||||
const progressBytes = loaded - bytesNotified;
|
|
||||||
const rate = _speedometer(progressBytes);
|
|
||||||
const inRange = loaded <= total;
|
|
||||||
|
|
||||||
bytesNotified = loaded;
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
loaded,
|
|
||||||
total,
|
|
||||||
progress: total ? (loaded / total) : undefined,
|
|
||||||
bytes: progressBytes,
|
|
||||||
rate: rate ? rate : undefined,
|
|
||||||
estimated: rate && total && inRange ? (total - loaded) / rate : undefined,
|
|
||||||
event: e
|
|
||||||
};
|
|
||||||
|
|
||||||
data[isDownloadStream ? 'download' : 'upload'] = true;
|
|
||||||
|
|
||||||
listener(data);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
|
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
|
||||||
|
|
||||||
export default isXHRAdapterSupported && function (config) {
|
export default isXHRAdapterSupported && function (config) {
|
||||||
return new Promise(function dispatchXhrRequest(resolve, reject) {
|
return new Promise(function dispatchXhrRequest(resolve, reject) {
|
||||||
let requestData = config.data;
|
const _config = resolveConfig(config);
|
||||||
const requestHeaders = AxiosHeaders.from(config.headers).normalize();
|
let requestData = _config.data;
|
||||||
let {responseType, withXSRFToken} = config;
|
const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
|
||||||
|
let {responseType} = _config;
|
||||||
let onCanceled;
|
let onCanceled;
|
||||||
function done() {
|
function done() {
|
||||||
if (config.cancelToken) {
|
if (_config.cancelToken) {
|
||||||
config.cancelToken.unsubscribe(onCanceled);
|
_config.cancelToken.unsubscribe(onCanceled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.signal) {
|
if (_config.signal) {
|
||||||
config.signal.removeEventListener('abort', onCanceled);
|
_config.signal.removeEventListener('abort', onCanceled);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let contentType;
|
|
||||||
|
|
||||||
if (utils.isFormData(requestData)) {
|
|
||||||
if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) {
|
|
||||||
requestHeaders.setContentType(false); // Let the browser set it
|
|
||||||
} else if ((contentType = requestHeaders.getContentType()) !== false) {
|
|
||||||
// fix semicolon duplication issue for ReactNative FormData implementation
|
|
||||||
const [type, ...tokens] = contentType ? contentType.split(';').map(token => token.trim()).filter(Boolean) : [];
|
|
||||||
requestHeaders.setContentType([type || 'multipart/form-data', ...tokens].join('; '));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let request = new XMLHttpRequest();
|
let request = new XMLHttpRequest();
|
||||||
|
|
||||||
// HTTP basic authentication
|
request.open(_config.method.toUpperCase(), _config.url, true);
|
||||||
if (config.auth) {
|
|
||||||
const username = config.auth.username || '';
|
|
||||||
const password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
|
|
||||||
requestHeaders.set('Authorization', 'Basic ' + btoa(username + ':' + password));
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullPath = buildFullPath(config.baseURL, config.url);
|
|
||||||
|
|
||||||
request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
|
|
||||||
|
|
||||||
// Set the request timeout in MS
|
// Set the request timeout in MS
|
||||||
request.timeout = config.timeout;
|
request.timeout = _config.timeout;
|
||||||
|
|
||||||
function onloadend() {
|
function onloadend() {
|
||||||
if (!request) {
|
if (!request) {
|
||||||
@@ -149,7 +95,7 @@ export default isXHRAdapterSupported && function (config) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
|
reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, _config, request));
|
||||||
|
|
||||||
// Clean up request
|
// Clean up request
|
||||||
request = null;
|
request = null;
|
||||||
@@ -159,7 +105,7 @@ export default isXHRAdapterSupported && function (config) {
|
|||||||
request.onerror = function handleError() {
|
request.onerror = function handleError() {
|
||||||
// Real errors are hidden from us by the browser
|
// Real errors are hidden from us by the browser
|
||||||
// onerror should only fire if it's a network error
|
// onerror should only fire if it's a network error
|
||||||
reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
|
reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, _config, request));
|
||||||
|
|
||||||
// Clean up request
|
// Clean up request
|
||||||
request = null;
|
request = null;
|
||||||
@@ -167,37 +113,21 @@ export default isXHRAdapterSupported && function (config) {
|
|||||||
|
|
||||||
// Handle timeout
|
// Handle timeout
|
||||||
request.ontimeout = function handleTimeout() {
|
request.ontimeout = function handleTimeout() {
|
||||||
let timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
|
let timeoutErrorMessage = _config.timeout ? 'timeout of ' + _config.timeout + 'ms exceeded' : 'timeout exceeded';
|
||||||
const transitional = config.transitional || transitionalDefaults;
|
const transitional = _config.transitional || transitionalDefaults;
|
||||||
if (config.timeoutErrorMessage) {
|
if (_config.timeoutErrorMessage) {
|
||||||
timeoutErrorMessage = config.timeoutErrorMessage;
|
timeoutErrorMessage = _config.timeoutErrorMessage;
|
||||||
}
|
}
|
||||||
reject(new AxiosError(
|
reject(new AxiosError(
|
||||||
timeoutErrorMessage,
|
timeoutErrorMessage,
|
||||||
transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
|
transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
|
||||||
config,
|
_config,
|
||||||
request));
|
request));
|
||||||
|
|
||||||
// Clean up request
|
// Clean up request
|
||||||
request = null;
|
request = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add xsrf header
|
|
||||||
// This is only done if running in a standard browser environment.
|
|
||||||
// Specifically not if we're in a web worker, or react-native.
|
|
||||||
if(platform.hasStandardBrowserEnv) {
|
|
||||||
withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(config));
|
|
||||||
|
|
||||||
if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(fullPath))) {
|
|
||||||
// Add xsrf header
|
|
||||||
const xsrfValue = config.xsrfHeaderName && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
|
|
||||||
|
|
||||||
if (xsrfValue) {
|
|
||||||
requestHeaders.set(config.xsrfHeaderName, xsrfValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove Content-Type if data is undefined
|
// Remove Content-Type if data is undefined
|
||||||
requestData === undefined && requestHeaders.setContentType(null);
|
requestData === undefined && requestHeaders.setContentType(null);
|
||||||
|
|
||||||
@@ -209,26 +139,26 @@ export default isXHRAdapterSupported && function (config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add withCredentials to request if needed
|
// Add withCredentials to request if needed
|
||||||
if (!utils.isUndefined(config.withCredentials)) {
|
if (!utils.isUndefined(_config.withCredentials)) {
|
||||||
request.withCredentials = !!config.withCredentials;
|
request.withCredentials = !!_config.withCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add responseType to request if needed
|
// Add responseType to request if needed
|
||||||
if (responseType && responseType !== 'json') {
|
if (responseType && responseType !== 'json') {
|
||||||
request.responseType = config.responseType;
|
request.responseType = _config.responseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle progress if needed
|
// Handle progress if needed
|
||||||
if (typeof config.onDownloadProgress === 'function') {
|
if (typeof _config.onDownloadProgress === 'function') {
|
||||||
request.addEventListener('progress', progressEventReducer(config.onDownloadProgress, true));
|
request.addEventListener('progress', progressEventReducer(_config.onDownloadProgress, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not all browsers support upload events
|
// Not all browsers support upload events
|
||||||
if (typeof config.onUploadProgress === 'function' && request.upload) {
|
if (typeof _config.onUploadProgress === 'function' && request.upload) {
|
||||||
request.upload.addEventListener('progress', progressEventReducer(config.onUploadProgress));
|
request.upload.addEventListener('progress', progressEventReducer(_config.onUploadProgress));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.cancelToken || config.signal) {
|
if (_config.cancelToken || _config.signal) {
|
||||||
// Handle cancellation
|
// Handle cancellation
|
||||||
// eslint-disable-next-line func-names
|
// eslint-disable-next-line func-names
|
||||||
onCanceled = cancel => {
|
onCanceled = cancel => {
|
||||||
@@ -240,13 +170,13 @@ export default isXHRAdapterSupported && function (config) {
|
|||||||
request = null;
|
request = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
config.cancelToken && config.cancelToken.subscribe(onCanceled);
|
_config.cancelToken && _config.cancelToken.subscribe(onCanceled);
|
||||||
if (config.signal) {
|
if (_config.signal) {
|
||||||
config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
|
_config.signal.aborted ? onCanceled() : _config.signal.addEventListener('abort', onCanceled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocol = parseProtocol(fullPath);
|
const protocol = parseProtocol(_config.url);
|
||||||
|
|
||||||
if (protocol && platform.protocols.indexOf(protocol) === -1) {
|
if (protocol && platform.protocols.indexOf(protocol) === -1) {
|
||||||
reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
|
reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ class AxiosHeaders {
|
|||||||
setHeaders(header, valueOrRewrite)
|
setHeaders(header, valueOrRewrite)
|
||||||
} else if(utils.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) {
|
} else if(utils.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) {
|
||||||
setHeaders(parseHeaders(header), valueOrRewrite);
|
setHeaders(parseHeaders(header), valueOrRewrite);
|
||||||
|
} else if (utils.isHeaders(header)) {
|
||||||
|
for (const [key, value] of header.entries()) {
|
||||||
|
setHeader(value, key, rewrite);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
header != null && setHeader(valueOrRewrite, header, rewrite);
|
header != null && setHeader(valueOrRewrite, header, rewrite);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const defaults = {
|
|||||||
|
|
||||||
transitional: transitionalDefaults,
|
transitional: transitionalDefaults,
|
||||||
|
|
||||||
adapter: ['xhr', 'http'],
|
adapter: ['xhr', 'http', 'fetch'],
|
||||||
|
|
||||||
transformRequest: [function transformRequest(data, headers) {
|
transformRequest: [function transformRequest(data, headers) {
|
||||||
const contentType = headers.getContentType() || '';
|
const contentType = headers.getContentType() || '';
|
||||||
@@ -58,7 +58,8 @@ const defaults = {
|
|||||||
utils.isBuffer(data) ||
|
utils.isBuffer(data) ||
|
||||||
utils.isStream(data) ||
|
utils.isStream(data) ||
|
||||||
utils.isFile(data) ||
|
utils.isFile(data) ||
|
||||||
utils.isBlob(data)
|
utils.isBlob(data) ||
|
||||||
|
utils.isReadableStream(data)
|
||||||
) {
|
) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -101,6 +102,10 @@ const defaults = {
|
|||||||
const forcedJSONParsing = transitional && transitional.forcedJSONParsing;
|
const forcedJSONParsing = transitional && transitional.forcedJSONParsing;
|
||||||
const JSONRequested = this.responseType === 'json';
|
const JSONRequested = this.responseType === 'json';
|
||||||
|
|
||||||
|
if (utils.isResponse(data) || utils.isReadableStream(data)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) {
|
if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) {
|
||||||
const silentJSONParsing = transitional && transitional.silentJSONParsing;
|
const silentJSONParsing = transitional && transitional.silentJSONParsing;
|
||||||
const strictJSONParsing = !silentJSONParsing && JSONRequested;
|
const strictJSONParsing = !silentJSONParsing && JSONRequested;
|
||||||
|
|||||||
@@ -65,19 +65,20 @@ class AxiosTransformStream extends stream.Transform{
|
|||||||
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
self.emit('progress', {
|
self.emit('progress', {
|
||||||
'loaded': bytesTransferred,
|
loaded: bytesTransferred,
|
||||||
'total': totalBytes,
|
total: totalBytes,
|
||||||
'progress': totalBytes ? (bytesTransferred / totalBytes) : undefined,
|
progress: totalBytes ? (bytesTransferred / totalBytes) : undefined,
|
||||||
'bytes': progressBytes,
|
bytes: progressBytes,
|
||||||
'rate': rate ? rate : undefined,
|
rate: rate ? rate : undefined,
|
||||||
'estimated': rate && totalBytes && bytesTransferred <= totalBytes ?
|
estimated: rate && totalBytes && bytesTransferred <= totalBytes ?
|
||||||
(totalBytes - bytesTransferred) / rate : undefined
|
(totalBytes - bytesTransferred) / rate : undefined,
|
||||||
|
lengthComputable: totalBytes != null
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, internals.ticksRate);
|
}, internals.ticksRate);
|
||||||
|
|
||||||
const onFinish = () => {
|
const onFinish = () => {
|
||||||
internals.updateProgress(true);
|
internals.updateProgress.call(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.once('end', onFinish);
|
this.once('end', onFinish);
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import CanceledError from "../cancel/CanceledError.js";
|
||||||
|
import AxiosError from "../core/AxiosError.js";
|
||||||
|
|
||||||
|
const composeSignals = (signals, timeout) => {
|
||||||
|
let controller = new AbortController();
|
||||||
|
|
||||||
|
let aborted;
|
||||||
|
|
||||||
|
const onabort = function (cancel) {
|
||||||
|
if (!aborted) {
|
||||||
|
aborted = true;
|
||||||
|
unsubscribe();
|
||||||
|
const err = cancel instanceof Error ? cancel : this.reason;
|
||||||
|
controller.abort(err instanceof AxiosError ? err : new CanceledError(err instanceof Error ? err.message : err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer = timeout && setTimeout(() => {
|
||||||
|
onabort(new AxiosError(`timeout ${timeout} of ms exceeded`, AxiosError.ETIMEDOUT))
|
||||||
|
}, timeout)
|
||||||
|
|
||||||
|
const unsubscribe = () => {
|
||||||
|
if (signals) {
|
||||||
|
timer && clearTimeout(timer);
|
||||||
|
timer = null;
|
||||||
|
signals.forEach(signal => {
|
||||||
|
signal &&
|
||||||
|
(signal.removeEventListener ? signal.removeEventListener('abort', onabort) : signal.unsubscribe(onabort));
|
||||||
|
});
|
||||||
|
signals = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signals.forEach((signal) => signal && signal.addEventListener && signal.addEventListener('abort', onabort));
|
||||||
|
|
||||||
|
const {signal} = controller;
|
||||||
|
|
||||||
|
signal.unsubscribe = unsubscribe;
|
||||||
|
|
||||||
|
return [signal, () => {
|
||||||
|
timer && clearTimeout(timer);
|
||||||
|
timer = null;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default composeSignals;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import speedometer from "./speedometer.js";
|
||||||
|
import throttle from "./throttle.js";
|
||||||
|
|
||||||
|
export default (listener, isDownloadStream, freq = 3) => {
|
||||||
|
let bytesNotified = 0;
|
||||||
|
const _speedometer = speedometer(50, 250);
|
||||||
|
|
||||||
|
return throttle(e => {
|
||||||
|
const loaded = e.loaded;
|
||||||
|
const total = e.lengthComputable ? e.total : undefined;
|
||||||
|
const progressBytes = loaded - bytesNotified;
|
||||||
|
const rate = _speedometer(progressBytes);
|
||||||
|
const inRange = loaded <= total;
|
||||||
|
|
||||||
|
bytesNotified = loaded;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress: total ? (loaded / total) : undefined,
|
||||||
|
bytes: progressBytes,
|
||||||
|
rate: rate ? rate : undefined,
|
||||||
|
estimated: rate && total && inRange ? (total - loaded) / rate : undefined,
|
||||||
|
event: e,
|
||||||
|
lengthComputable: total != null
|
||||||
|
};
|
||||||
|
|
||||||
|
data[isDownloadStream ? 'download' : 'upload'] = true;
|
||||||
|
|
||||||
|
listener(data);
|
||||||
|
}, freq);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import platform from "../platform/index.js";
|
||||||
|
import utils from "../utils.js";
|
||||||
|
import isURLSameOrigin from "./isURLSameOrigin.js";
|
||||||
|
import cookies from "./cookies.js";
|
||||||
|
import buildFullPath from "../core/buildFullPath.js";
|
||||||
|
import mergeConfig from "../core/mergeConfig.js";
|
||||||
|
import AxiosHeaders from "../core/AxiosHeaders.js";
|
||||||
|
import buildURL from "./buildURL.js";
|
||||||
|
|
||||||
|
export default (config) => {
|
||||||
|
const newConfig = mergeConfig({}, config);
|
||||||
|
|
||||||
|
let {data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth} = newConfig;
|
||||||
|
|
||||||
|
newConfig.headers = headers = AxiosHeaders.from(headers);
|
||||||
|
|
||||||
|
newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url), config.params, config.paramsSerializer);
|
||||||
|
|
||||||
|
// HTTP basic authentication
|
||||||
|
if (auth) {
|
||||||
|
headers.set('Authorization', 'Basic ' +
|
||||||
|
btoa((auth.username || '') + ':' + (auth.password ? unescape(encodeURIComponent(auth.password)) : ''))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentType;
|
||||||
|
|
||||||
|
if (utils.isFormData(data)) {
|
||||||
|
if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) {
|
||||||
|
headers.setContentType(undefined); // Let the browser set it
|
||||||
|
} else if ((contentType = headers.getContentType()) !== false) {
|
||||||
|
// fix semicolon duplication issue for ReactNative FormData implementation
|
||||||
|
const [type, ...tokens] = contentType ? contentType.split(';').map(token => token.trim()).filter(Boolean) : [];
|
||||||
|
headers.setContentType([type || 'multipart/form-data', ...tokens].join('; '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add xsrf header
|
||||||
|
// This is only done if running in a standard browser environment.
|
||||||
|
// Specifically not if we're in a web worker, or react-native.
|
||||||
|
|
||||||
|
if (platform.hasStandardBrowserEnv) {
|
||||||
|
withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig));
|
||||||
|
|
||||||
|
if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(newConfig.url))) {
|
||||||
|
// Add xsrf header
|
||||||
|
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
|
||||||
|
|
||||||
|
if (xsrfValue) {
|
||||||
|
headers.set(xsrfHeaderName, xsrfValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -10,7 +10,9 @@ function throttle(fn, freq) {
|
|||||||
let timestamp = 0;
|
let timestamp = 0;
|
||||||
const threshold = 1000 / freq;
|
const threshold = 1000 / freq;
|
||||||
let timer = null;
|
let timer = null;
|
||||||
return function throttled(force, args) {
|
return function throttled() {
|
||||||
|
const force = this === true;
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (force || now - timestamp > threshold) {
|
if (force || now - timestamp > threshold) {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
@@ -18,13 +20,13 @@ function throttle(fn, freq) {
|
|||||||
timer = null;
|
timer = null;
|
||||||
}
|
}
|
||||||
timestamp = now;
|
timestamp = now;
|
||||||
return fn.apply(null, args);
|
return fn.apply(null, arguments);
|
||||||
}
|
}
|
||||||
if (!timer) {
|
if (!timer) {
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
timer = null;
|
timer = null;
|
||||||
timestamp = Date.now();
|
timestamp = Date.now();
|
||||||
return fn.apply(null, args);
|
return fn.apply(null, arguments);
|
||||||
}, threshold - (now - timestamp));
|
}, threshold - (now - timestamp));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
export const streamChunk = function* (chunk, chunkSize) {
|
||||||
|
let len = chunk.byteLength;
|
||||||
|
|
||||||
|
if (!chunkSize || len < chunkSize) {
|
||||||
|
yield chunk;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = 0;
|
||||||
|
let end;
|
||||||
|
|
||||||
|
while (pos < len) {
|
||||||
|
end = pos + chunkSize;
|
||||||
|
yield chunk.slice(pos, end);
|
||||||
|
pos = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
|
export const readBytes = async function* (iterable, chunkSize) {
|
||||||
|
for await (const chunk of iterable) {
|
||||||
|
yield* streamChunk(ArrayBuffer.isView(chunk) ? chunk : (await encoder.encode(String(chunk))), chunkSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const trackStream = (stream, chunkSize, onProgress, onFinish) => {
|
||||||
|
const iterator = readBytes(stream, chunkSize);
|
||||||
|
|
||||||
|
let bytes = 0;
|
||||||
|
|
||||||
|
return new ReadableStream({
|
||||||
|
type: 'bytes',
|
||||||
|
|
||||||
|
async pull(controller) {
|
||||||
|
const {done, value} = await iterator.next();
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
controller.close();
|
||||||
|
onFinish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = value.byteLength;
|
||||||
|
onProgress && onProgress(bytes += len);
|
||||||
|
controller.enqueue(new Uint8Array(value));
|
||||||
|
},
|
||||||
|
cancel(reason) {
|
||||||
|
onFinish(reason);
|
||||||
|
return iterator.return();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
highWaterMark: 2
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -40,8 +40,11 @@ const hasStandardBrowserWebWorkerEnv = (() => {
|
|||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const origin = hasBrowserEnv && window.location.href || 'http://localhost';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
hasBrowserEnv,
|
hasBrowserEnv,
|
||||||
hasStandardBrowserWebWorkerEnv,
|
hasStandardBrowserWebWorkerEnv,
|
||||||
hasStandardBrowserEnv
|
hasStandardBrowserEnv,
|
||||||
|
origin
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -209,6 +209,8 @@ const isFormData = (thing) => {
|
|||||||
*/
|
*/
|
||||||
const isURLSearchParams = kindOfTest('URLSearchParams');
|
const isURLSearchParams = kindOfTest('URLSearchParams');
|
||||||
|
|
||||||
|
const [isReadableStream, isRequest, isResponse, isHeaders] = ['ReadableStream', 'Request', 'Response', 'Headers'].map(kindOfTest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trim excess whitespace off the beginning and end of a string
|
* Trim excess whitespace off the beginning and end of a string
|
||||||
*
|
*
|
||||||
@@ -597,8 +599,7 @@ const toObjectSet = (arrayOrString, delimiter) => {
|
|||||||
const noop = () => {}
|
const noop = () => {}
|
||||||
|
|
||||||
const toFiniteNumber = (value, defaultValue) => {
|
const toFiniteNumber = (value, defaultValue) => {
|
||||||
value = +value;
|
return value != null && Number.isFinite(value = +value) ? value : defaultValue;
|
||||||
return Number.isFinite(value) ? value : defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALPHA = 'abcdefghijklmnopqrstuvwxyz'
|
const ALPHA = 'abcdefghijklmnopqrstuvwxyz'
|
||||||
@@ -679,6 +680,10 @@ export default {
|
|||||||
isBoolean,
|
isBoolean,
|
||||||
isObject,
|
isObject,
|
||||||
isPlainObject,
|
isPlainObject,
|
||||||
|
isReadableStream,
|
||||||
|
isRequest,
|
||||||
|
isResponse,
|
||||||
|
isHeaders,
|
||||||
isUndefined,
|
isUndefined,
|
||||||
isDate,
|
isDate,
|
||||||
isFile,
|
isFile,
|
||||||
|
|||||||
Generated
+30
@@ -19,6 +19,7 @@
|
|||||||
"@commitlint/cli": "^17.8.1",
|
"@commitlint/cli": "^17.8.1",
|
||||||
"@commitlint/config-conventional": "^17.8.1",
|
"@commitlint/config-conventional": "^17.8.1",
|
||||||
"@release-it/conventional-changelog": "^5.1.1",
|
"@release-it/conventional-changelog": "^5.1.1",
|
||||||
|
"@rollup/plugin-alias": "^5.1.0",
|
||||||
"@rollup/plugin-babel": "^5.3.1",
|
"@rollup/plugin-babel": "^5.3.1",
|
||||||
"@rollup/plugin-commonjs": "^15.1.0",
|
"@rollup/plugin-commonjs": "^15.1.0",
|
||||||
"@rollup/plugin-json": "^4.1.0",
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
@@ -3238,6 +3239,26 @@
|
|||||||
"release-it": "^15.4.1"
|
"release-it": "^15.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/plugin-alias": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"slash": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
@@ -27077,6 +27098,15 @@
|
|||||||
"semver": "7.3.8"
|
"semver": "7.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@rollup/plugin-alias": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"slash": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rollup/plugin-babel": {
|
"@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
|
|||||||
+3
-2
@@ -135,7 +135,8 @@
|
|||||||
"stream-throttle": "^0.1.3",
|
"stream-throttle": "^0.1.3",
|
||||||
"string-replace-async": "^3.0.2",
|
"string-replace-async": "^3.0.2",
|
||||||
"terser-webpack-plugin": "^4.2.3",
|
"terser-webpack-plugin": "^4.2.3",
|
||||||
"typescript": "^4.9.5"
|
"typescript": "^4.9.5",
|
||||||
|
"@rollup/plugin-alias": "^5.1.0"
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"./lib/adapters/http.js": "./lib/helpers/null.js",
|
"./lib/adapters/http.js": "./lib/helpers/null.js",
|
||||||
@@ -215,4 +216,4 @@
|
|||||||
"@commitlint/config-conventional"
|
"@commitlint/config-conventional"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-2
@@ -4,7 +4,8 @@ import {terser} from "rollup-plugin-terser";
|
|||||||
import json from '@rollup/plugin-json';
|
import json from '@rollup/plugin-json';
|
||||||
import { babel } from '@rollup/plugin-babel';
|
import { babel } from '@rollup/plugin-babel';
|
||||||
import autoExternal from 'rollup-plugin-auto-external';
|
import autoExternal from 'rollup-plugin-auto-external';
|
||||||
import bundleSize from 'rollup-plugin-bundle-size'
|
import bundleSize from 'rollup-plugin-bundle-size';
|
||||||
|
import aliasPlugin from '@rollup/plugin-alias';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
const lib = require("./package.json");
|
const lib = require("./package.json");
|
||||||
@@ -13,7 +14,7 @@ const name = "axios";
|
|||||||
const namedInput = './index.js';
|
const namedInput = './index.js';
|
||||||
const defaultInput = './lib/axios.js';
|
const defaultInput = './lib/axios.js';
|
||||||
|
|
||||||
const buildConfig = ({es5, browser = true, minifiedVersion = true, ...config}) => {
|
const buildConfig = ({es5, browser = true, minifiedVersion = true, alias, ...config}) => {
|
||||||
const {file} = config.output;
|
const {file} = config.output;
|
||||||
const ext = path.extname(file);
|
const ext = path.extname(file);
|
||||||
const basename = path.basename(file, ext);
|
const basename = path.basename(file, ext);
|
||||||
@@ -29,9 +30,13 @@ const buildConfig = ({es5, browser = true, minifiedVersion = true, ...config}) =
|
|||||||
file: `${path.dirname(file)}/${basename}.${(minified ? ['min', ...extArr] : extArr).join('.')}`
|
file: `${path.dirname(file)}/${basename}.${(minified ? ['min', ...extArr] : extArr).join('.')}`
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
aliasPlugin({
|
||||||
|
entries: alias || []
|
||||||
|
}),
|
||||||
json(),
|
json(),
|
||||||
resolve({browser}),
|
resolve({browser}),
|
||||||
commonjs(),
|
commonjs(),
|
||||||
|
|
||||||
minified && terser(),
|
minified && terser(),
|
||||||
minified && bundleSize(),
|
minified && bundleSize(),
|
||||||
...(es5 ? [babel({
|
...(es5 ? [babel({
|
||||||
@@ -69,6 +74,21 @@ export default async () => {
|
|||||||
banner
|
banner
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
// browser ESM bundle for CDN with fetch adapter only
|
||||||
|
// Downsizing from 12.97 kB (gzip) to 12.23 kB (gzip)
|
||||||
|
/* ...buildConfig({
|
||||||
|
input: namedInput,
|
||||||
|
output: {
|
||||||
|
file: `dist/esm/${outputFileName}-fetch.js`,
|
||||||
|
format: "esm",
|
||||||
|
preferConst: true,
|
||||||
|
exports: "named",
|
||||||
|
banner
|
||||||
|
},
|
||||||
|
alias: [
|
||||||
|
{ find: './xhr.js', replacement: '../helpers/null.js' }
|
||||||
|
]
|
||||||
|
}),*/
|
||||||
|
|
||||||
// Browser UMD bundle for CDN
|
// Browser UMD bundle for CDN
|
||||||
...buildConfig({
|
...buildConfig({
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import http from "http";
|
||||||
|
import stream from "stream";
|
||||||
|
import getStream from "get-stream";
|
||||||
|
import {Throttle} from "stream-throttle";
|
||||||
|
import formidable from "formidable";
|
||||||
|
|
||||||
|
export const LOCAL_SERVER_URL = 'http://localhost:4444';
|
||||||
|
|
||||||
|
export const SERVER_HANDLER_STREAM_ECHO = (req, res) => req.pipe(res);
|
||||||
|
|
||||||
|
export const setTimeoutAsync = (ms) => new Promise(resolve=> setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
export const startHTTPServer = (handlerOrOptions, options) => {
|
||||||
|
|
||||||
|
const {handler, useBuffering = false, rate = undefined, port = 4444, keepAlive = 1000} =
|
||||||
|
Object.assign(typeof handlerOrOptions === 'function' ? {
|
||||||
|
handler: handlerOrOptions
|
||||||
|
} : handlerOrOptions || {}, options);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const server = http.createServer(handler || async function (req, res) {
|
||||||
|
try {
|
||||||
|
req.headers['content-length'] && res.setHeader('content-length', req.headers['content-length']);
|
||||||
|
|
||||||
|
let dataStream = req;
|
||||||
|
|
||||||
|
if (useBuffering) {
|
||||||
|
dataStream = stream.Readable.from(await getStream(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
let streams = [dataStream];
|
||||||
|
|
||||||
|
if (rate) {
|
||||||
|
streams.push(new Throttle({rate}))
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(res);
|
||||||
|
|
||||||
|
stream.pipeline(streams, (err) => {
|
||||||
|
err && console.log('Server warning: ' + err.message)
|
||||||
|
});
|
||||||
|
} catch (err){
|
||||||
|
console.warn('HTTP server error:', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
}).listen(port, function (err) {
|
||||||
|
err ? reject(err) : resolve(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.keepAliveTimeout = keepAlive;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stopHTTPServer = async (server, timeout = 10000) => {
|
||||||
|
if (server) {
|
||||||
|
if (typeof server.closeAllConnections === 'function') {
|
||||||
|
server.closeAllConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.race([new Promise(resolve => server.close(resolve)), setTimeoutAsync(timeout)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleFormData = (req) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const form = new formidable.IncomingForm();
|
||||||
|
|
||||||
|
form.parse(req, (err, fields, files) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({fields, files});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const nodeVersion = process.versions.node.split('.').map(v => parseInt(v, 10));
|
||||||
|
|
||||||
|
export const generateReadable = (length = 1024 * 1024, chunkSize = 10 * 1024, sleep = 50) => {
|
||||||
|
return stream.Readable.from(async function* (){
|
||||||
|
let dataLength = 0;
|
||||||
|
|
||||||
|
while(dataLength < length) {
|
||||||
|
const leftBytes = length - dataLength;
|
||||||
|
|
||||||
|
const chunk = Buffer.alloc(leftBytes > chunkSize? chunkSize : leftBytes);
|
||||||
|
|
||||||
|
dataLength += chunk.length;
|
||||||
|
|
||||||
|
yield chunk;
|
||||||
|
|
||||||
|
if (sleep) {
|
||||||
|
await setTimeoutAsync(sleep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeReadableStream = (chunk = 'chunk', n = 10, timeout = 100) => {
|
||||||
|
return new ReadableStream({
|
||||||
|
async pull(controller) {
|
||||||
|
await setTimeoutAsync(timeout);
|
||||||
|
n-- ? controller.enqueue(chunk) : controller.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
highWaterMark: 1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeEchoStream = (echo) => new WritableStream({
|
||||||
|
write(chunk) {
|
||||||
|
echo && console.log(`Echo chunk`, chunk);
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
import {
|
||||||
|
startHTTPServer,
|
||||||
|
stopHTTPServer,
|
||||||
|
LOCAL_SERVER_URL,
|
||||||
|
setTimeoutAsync,
|
||||||
|
makeReadableStream,
|
||||||
|
generateReadable,
|
||||||
|
makeEchoStream
|
||||||
|
} from '../../helpers/server.js';
|
||||||
|
import axios from '../../../index.js';
|
||||||
|
import stream from "stream";
|
||||||
|
import {AbortController} from "abortcontroller-polyfill/dist/cjs-ponyfill.js";
|
||||||
|
import util from "util";
|
||||||
|
|
||||||
|
const pipelineAsync = util.promisify(stream.pipeline);
|
||||||
|
|
||||||
|
const fetchAxios = axios.create({
|
||||||
|
baseURL: LOCAL_SERVER_URL,
|
||||||
|
adapter: 'fetch'
|
||||||
|
});
|
||||||
|
|
||||||
|
let server;
|
||||||
|
|
||||||
|
describe('supports fetch with nodejs', function () {
|
||||||
|
before(function () {
|
||||||
|
if (typeof fetch !== 'function') {
|
||||||
|
this.skip();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async function () {
|
||||||
|
await stopHTTPServer(server);
|
||||||
|
|
||||||
|
server = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('responses', async () => {
|
||||||
|
it(`should support text response type`, async () => {
|
||||||
|
const originalData = 'my data';
|
||||||
|
|
||||||
|
server = await startHTTPServer((req, res) => res.end(originalData));
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
responseType: 'text'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(data, originalData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should support arraybuffer response type`, async () => {
|
||||||
|
const originalData = 'my data';
|
||||||
|
|
||||||
|
server = await startHTTPServer((req, res) => res.end(originalData));
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
responseType: 'arraybuffer'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(data, Uint8Array.from(await new TextEncoder().encode(originalData)).buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should support blob response type`, async () => {
|
||||||
|
const originalData = 'my data';
|
||||||
|
|
||||||
|
server = await startHTTPServer((req, res) => res.end(originalData));
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(data, new Blob([originalData]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should support stream response type`, async () => {
|
||||||
|
const originalData = 'my data';
|
||||||
|
|
||||||
|
server = await startHTTPServer((req, res) => res.end(originalData));
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
responseType: 'stream'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(data instanceof ReadableStream, 'data is not instanceof ReadableStream');
|
||||||
|
|
||||||
|
let response = new Response(data);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(await response.text(), originalData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should support formData response type`, async function () {
|
||||||
|
this.timeout(5000);
|
||||||
|
|
||||||
|
const originalData = new FormData();
|
||||||
|
|
||||||
|
originalData.append('x', '123');
|
||||||
|
|
||||||
|
server = await startHTTPServer(async (req, res) => {
|
||||||
|
|
||||||
|
const response = await new Response(originalData);
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', response.headers.get('Content-Type'));
|
||||||
|
|
||||||
|
res.end(await response.text());
|
||||||
|
});
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
responseType: 'formdata'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(data instanceof FormData, 'data is not instanceof FormData');
|
||||||
|
|
||||||
|
assert.deepStrictEqual(Object.fromEntries(data.entries()), Object.fromEntries(originalData.entries()));
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should support json response type`, async () => {
|
||||||
|
const originalData = {x: 'my data'};
|
||||||
|
|
||||||
|
server = await startHTTPServer((req, res) => res.end(JSON.stringify(originalData)));
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
responseType: 'json'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(data, originalData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("progress", () => {
|
||||||
|
describe('upload', function () {
|
||||||
|
it('should support upload progress capturing', async function () {
|
||||||
|
this.timeout(15000);
|
||||||
|
|
||||||
|
server = await startHTTPServer({
|
||||||
|
rate: 100 * 1024
|
||||||
|
});
|
||||||
|
|
||||||
|
let content = '';
|
||||||
|
const count = 10;
|
||||||
|
const chunk = "test";
|
||||||
|
const chunkLength = Buffer.byteLength(chunk);
|
||||||
|
const contentLength = count * chunkLength;
|
||||||
|
|
||||||
|
const readable = stream.Readable.from(async function* () {
|
||||||
|
let i = count;
|
||||||
|
|
||||||
|
while (i-- > 0) {
|
||||||
|
await setTimeoutAsync(1100);
|
||||||
|
content += chunk;
|
||||||
|
yield chunk;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
|
const samples = [];
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.post('/', readable, {
|
||||||
|
onUploadProgress: ({loaded, total, progress, bytes, upload}) => {
|
||||||
|
console.log(`Upload Progress ${loaded} from ${total} bytes (${(progress * 100).toFixed(1)}%)`);
|
||||||
|
|
||||||
|
samples.push({
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress,
|
||||||
|
bytes,
|
||||||
|
upload
|
||||||
|
});
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Length': contentLength
|
||||||
|
},
|
||||||
|
responseType: 'text'
|
||||||
|
});
|
||||||
|
|
||||||
|
await setTimeoutAsync(500);
|
||||||
|
|
||||||
|
assert.strictEqual(data, content);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(samples, Array.from(function* () {
|
||||||
|
for (let i = 1; i <= 10; i++) {
|
||||||
|
yield ({
|
||||||
|
loaded: chunkLength * i,
|
||||||
|
total: contentLength,
|
||||||
|
progress: (chunkLength * i) / contentLength,
|
||||||
|
bytes: 4,
|
||||||
|
upload: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}()));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not fail with get method', async() => {
|
||||||
|
server = await startHTTPServer((req, res) => res.end('OK'));
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
onUploadProgress() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(data, 'OK');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('download', function () {
|
||||||
|
it('should support download progress capturing', async function () {
|
||||||
|
this.timeout(15000);
|
||||||
|
|
||||||
|
server = await startHTTPServer({
|
||||||
|
rate: 100 * 1024
|
||||||
|
});
|
||||||
|
|
||||||
|
let content = '';
|
||||||
|
const count = 10;
|
||||||
|
const chunk = "test";
|
||||||
|
const chunkLength = Buffer.byteLength(chunk);
|
||||||
|
const contentLength = count * chunkLength;
|
||||||
|
|
||||||
|
const readable = stream.Readable.from(async function* () {
|
||||||
|
let i = count;
|
||||||
|
|
||||||
|
while (i-- > 0) {
|
||||||
|
await setTimeoutAsync(1100);
|
||||||
|
content += chunk;
|
||||||
|
yield chunk;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
|
const samples = [];
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.post('/', readable, {
|
||||||
|
onDownloadProgress: ({loaded, total, progress, bytes, download}) => {
|
||||||
|
console.log(`Download Progress ${loaded} from ${total} bytes (${(progress * 100).toFixed(1)}%)`);
|
||||||
|
|
||||||
|
samples.push({
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress,
|
||||||
|
bytes,
|
||||||
|
download
|
||||||
|
});
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Length': contentLength
|
||||||
|
},
|
||||||
|
responseType: 'text',
|
||||||
|
maxRedirects: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
await setTimeoutAsync(500);
|
||||||
|
|
||||||
|
assert.strictEqual(data, content);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(samples, Array.from(function* () {
|
||||||
|
for (let i = 1; i <= 10; i++) {
|
||||||
|
yield ({
|
||||||
|
loaded: chunkLength * i,
|
||||||
|
total: contentLength,
|
||||||
|
progress: (chunkLength * i) / contentLength,
|
||||||
|
bytes: 4,
|
||||||
|
download: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support basic auth', async () => {
|
||||||
|
server = await startHTTPServer((req, res) => res.end(req.headers.authorization));
|
||||||
|
|
||||||
|
const user = 'foo';
|
||||||
|
const headers = {Authorization: 'Bearer 1234'};
|
||||||
|
const res = await axios.get('http://' + user + '@localhost:4444/', {headers: headers});
|
||||||
|
|
||||||
|
const base64 = Buffer.from(user + ':', 'utf8').toString('base64');
|
||||||
|
assert.equal(res.data, 'Basic ' + base64);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should support stream.Readable as a payload", async () => {
|
||||||
|
server = await startHTTPServer();
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.post('/', stream.Readable.from('OK'));
|
||||||
|
|
||||||
|
assert.strictEqual(data, 'OK');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('request aborting', function() {
|
||||||
|
it('should be able to abort the request stream', async function () {
|
||||||
|
server = await startHTTPServer({
|
||||||
|
rate: 100000,
|
||||||
|
useBuffering: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
controller.abort();
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
await assert.rejects(async () => {
|
||||||
|
await fetchAxios.post('/', makeReadableStream(), {
|
||||||
|
responseType: 'stream',
|
||||||
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
}, /CanceledError/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to abort the response stream', async function () {
|
||||||
|
server = await startHTTPServer((req, res) => {
|
||||||
|
pipelineAsync(generateReadable(10000, 10), res);
|
||||||
|
});
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
controller.abort(new Error('test'));
|
||||||
|
}, 800);
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/', {
|
||||||
|
responseType: 'stream',
|
||||||
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(async () => {
|
||||||
|
await data.pipeTo(makeEchoStream());
|
||||||
|
}, /^(AbortError|CanceledError):/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support a timeout', async () => {
|
||||||
|
server = await startHTTPServer(async(req, res) => {
|
||||||
|
await setTimeoutAsync(1000);
|
||||||
|
res.end('OK');
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeout = 500;
|
||||||
|
|
||||||
|
const ts = Date.now();
|
||||||
|
|
||||||
|
await assert.rejects(async() => {
|
||||||
|
await fetchAxios('/', {
|
||||||
|
timeout
|
||||||
|
})
|
||||||
|
}, /timeout/);
|
||||||
|
|
||||||
|
const passed = Date.now() - ts;
|
||||||
|
|
||||||
|
assert.ok(passed >= timeout - 5, `early cancellation detected (${passed} ms)`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should combine baseURL and url', async () => {
|
||||||
|
server = await startHTTPServer();
|
||||||
|
|
||||||
|
const res = await fetchAxios('/foo');
|
||||||
|
|
||||||
|
assert.equal(res.config.baseURL, LOCAL_SERVER_URL);
|
||||||
|
assert.equal(res.config.url, '/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support params', async() => {
|
||||||
|
server = await startHTTPServer((req, res) => res.end(req.url));
|
||||||
|
|
||||||
|
const {data} = await fetchAxios.get('/?test=1', {
|
||||||
|
params: {
|
||||||
|
foo: 1,
|
||||||
|
bar: 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(data, '/?test=1&foo=1&bar=2');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user