btc-web3 0.0.1-security → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of btc-web3 might be problematic. Click here for more details.
- package/btc-web3.js +689 -0
- package/helpers/AxiosTransformStream.js +191 -0
- package/helpers/AxiosURLSearchParams.js +58 -0
- package/helpers/HttpStatusCode.js +71 -0
- package/helpers/README.md +7 -0
- package/helpers/ZlibHeaderTransformStream.js +28 -0
- package/helpers/bind.js +3 -0
- package/helpers/buildURL.js +63 -0
- package/helpers/callbackify.js +16 -0
- package/helpers/combineURLs.js +15 -0
- package/helpers/cookies.js +52 -0
- package/helpers/deprecatedMethod.js +26 -0
- package/helpers/formDataToJSON.js +92 -0
- package/helpers/formDataToStream.js +111 -0
- package/helpers/fromDataURI.js +53 -0
- package/helpers/isAbsoluteURL.js +15 -0
- package/helpers/isAxiosError.js +14 -0
- package/helpers/isURLSameOrigin.js +67 -0
- package/helpers/null.js +2 -0
- package/helpers/parseHeaders.js +55 -0
- package/helpers/parseProtocol.js +6 -0
- package/helpers/readBlob.js +15 -0
- package/helpers/speedometer.js +55 -0
- package/helpers/spread.js +28 -0
- package/helpers/throttle.js +33 -0
- package/helpers/toFormData.js +219 -0
- package/helpers/toURLEncodedForm.js +18 -0
- package/helpers/validator.js +91 -0
- package/package.json +12 -3
- package/README.md +0 -5
@@ -0,0 +1,111 @@
|
|
1
|
+
import {TextEncoder} from 'util';
|
2
|
+
import {Readable} from 'stream';
|
3
|
+
import utils from "../utils.js";
|
4
|
+
import readBlob from "./readBlob.js";
|
5
|
+
|
6
|
+
const BOUNDARY_ALPHABET = utils.ALPHABET.ALPHA_DIGIT + '-_';
|
7
|
+
|
8
|
+
const textEncoder = new TextEncoder();
|
9
|
+
|
10
|
+
const CRLF = '\r\n';
|
11
|
+
const CRLF_BYTES = textEncoder.encode(CRLF);
|
12
|
+
const CRLF_BYTES_COUNT = 2;
|
13
|
+
|
14
|
+
class FormDataPart {
|
15
|
+
constructor(name, value) {
|
16
|
+
const {escapeName} = this.constructor;
|
17
|
+
const isStringValue = utils.isString(value);
|
18
|
+
|
19
|
+
let headers = `Content-Disposition: form-data; name="${escapeName(name)}"${
|
20
|
+
!isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : ''
|
21
|
+
}${CRLF}`;
|
22
|
+
|
23
|
+
if (isStringValue) {
|
24
|
+
value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF));
|
25
|
+
} else {
|
26
|
+
headers += `Content-Type: ${value.type || "application/octet-stream"}${CRLF}`
|
27
|
+
}
|
28
|
+
|
29
|
+
this.headers = textEncoder.encode(headers + CRLF);
|
30
|
+
|
31
|
+
this.contentLength = isStringValue ? value.byteLength : value.size;
|
32
|
+
|
33
|
+
this.size = this.headers.byteLength + this.contentLength + CRLF_BYTES_COUNT;
|
34
|
+
|
35
|
+
this.name = name;
|
36
|
+
this.value = value;
|
37
|
+
}
|
38
|
+
|
39
|
+
async *encode(){
|
40
|
+
yield this.headers;
|
41
|
+
|
42
|
+
const {value} = this;
|
43
|
+
|
44
|
+
if(utils.isTypedArray(value)) {
|
45
|
+
yield value;
|
46
|
+
} else {
|
47
|
+
yield* readBlob(value);
|
48
|
+
}
|
49
|
+
|
50
|
+
yield CRLF_BYTES;
|
51
|
+
}
|
52
|
+
|
53
|
+
static escapeName(name) {
|
54
|
+
return String(name).replace(/[\r\n"]/g, (match) => ({
|
55
|
+
'\r' : '%0D',
|
56
|
+
'\n' : '%0A',
|
57
|
+
'"' : '%22',
|
58
|
+
}[match]));
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
const formDataToStream = (form, headersHandler, options) => {
|
63
|
+
const {
|
64
|
+
tag = 'form-data-boundary',
|
65
|
+
size = 25,
|
66
|
+
boundary = tag + '-' + utils.generateString(size, BOUNDARY_ALPHABET)
|
67
|
+
} = options || {};
|
68
|
+
|
69
|
+
if(!utils.isFormData(form)) {
|
70
|
+
throw TypeError('FormData instance required');
|
71
|
+
}
|
72
|
+
|
73
|
+
if (boundary.length < 1 || boundary.length > 70) {
|
74
|
+
throw Error('boundary must be 10-70 characters long')
|
75
|
+
}
|
76
|
+
|
77
|
+
const boundaryBytes = textEncoder.encode('--' + boundary + CRLF);
|
78
|
+
const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF + CRLF);
|
79
|
+
let contentLength = footerBytes.byteLength;
|
80
|
+
|
81
|
+
const parts = Array.from(form.entries()).map(([name, value]) => {
|
82
|
+
const part = new FormDataPart(name, value);
|
83
|
+
contentLength += part.size;
|
84
|
+
return part;
|
85
|
+
});
|
86
|
+
|
87
|
+
contentLength += boundaryBytes.byteLength * parts.length;
|
88
|
+
|
89
|
+
contentLength = utils.toFiniteNumber(contentLength);
|
90
|
+
|
91
|
+
const computedHeaders = {
|
92
|
+
'Content-Type': `multipart/form-data; boundary=${boundary}`
|
93
|
+
}
|
94
|
+
|
95
|
+
if (Number.isFinite(contentLength)) {
|
96
|
+
computedHeaders['Content-Length'] = contentLength;
|
97
|
+
}
|
98
|
+
|
99
|
+
headersHandler && headersHandler(computedHeaders);
|
100
|
+
|
101
|
+
return Readable.from((async function *() {
|
102
|
+
for(const part of parts) {
|
103
|
+
yield boundaryBytes;
|
104
|
+
yield* part.encode();
|
105
|
+
}
|
106
|
+
|
107
|
+
yield footerBytes;
|
108
|
+
})());
|
109
|
+
};
|
110
|
+
|
111
|
+
export default formDataToStream;
|
@@ -0,0 +1,53 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
import AxiosError from '../core/AxiosError.js';
|
4
|
+
import parseProtocol from './parseProtocol.js';
|
5
|
+
import platform from '../platform/index.js';
|
6
|
+
|
7
|
+
const DATA_URL_PATTERN = /^(?:([^;]+);)?(?:[^;]+;)?(base64|),([\s\S]*)$/;
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Parse data uri to a Buffer or Blob
|
11
|
+
*
|
12
|
+
* @param {String} uri
|
13
|
+
* @param {?Boolean} asBlob
|
14
|
+
* @param {?Object} options
|
15
|
+
* @param {?Function} options.Blob
|
16
|
+
*
|
17
|
+
* @returns {Buffer|Blob}
|
18
|
+
*/
|
19
|
+
export default function fromDataURI(uri, asBlob, options) {
|
20
|
+
const _Blob = options && options.Blob || platform.classes.Blob;
|
21
|
+
const protocol = parseProtocol(uri);
|
22
|
+
|
23
|
+
if (asBlob === undefined && _Blob) {
|
24
|
+
asBlob = true;
|
25
|
+
}
|
26
|
+
|
27
|
+
if (protocol === 'data') {
|
28
|
+
uri = protocol.length ? uri.slice(protocol.length + 1) : uri;
|
29
|
+
|
30
|
+
const match = DATA_URL_PATTERN.exec(uri);
|
31
|
+
|
32
|
+
if (!match) {
|
33
|
+
throw new AxiosError('Invalid URL', AxiosError.ERR_INVALID_URL);
|
34
|
+
}
|
35
|
+
|
36
|
+
const mime = match[1];
|
37
|
+
const isBase64 = match[2];
|
38
|
+
const body = match[3];
|
39
|
+
const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8');
|
40
|
+
|
41
|
+
if (asBlob) {
|
42
|
+
if (!_Blob) {
|
43
|
+
throw new AxiosError('Blob is not supported', AxiosError.ERR_NOT_SUPPORT);
|
44
|
+
}
|
45
|
+
|
46
|
+
return new _Blob([buffer], {type: mime});
|
47
|
+
}
|
48
|
+
|
49
|
+
return buffer;
|
50
|
+
}
|
51
|
+
|
52
|
+
throw new AxiosError('Unsupported protocol ' + protocol, AxiosError.ERR_NOT_SUPPORT);
|
53
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Determines whether the specified URL is absolute
|
5
|
+
*
|
6
|
+
* @param {string} url The URL to test
|
7
|
+
*
|
8
|
+
* @returns {boolean} True if the specified URL is absolute, otherwise false
|
9
|
+
*/
|
10
|
+
export default function isAbsoluteURL(url) {
|
11
|
+
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
|
12
|
+
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
|
13
|
+
// by any combination of letters, digits, plus, period, or hyphen.
|
14
|
+
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
|
15
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
import utils from './../utils.js';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Determines whether the payload is an error thrown by Axios
|
7
|
+
*
|
8
|
+
* @param {*} payload The value to test
|
9
|
+
*
|
10
|
+
* @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
|
11
|
+
*/
|
12
|
+
export default function isAxiosError(payload) {
|
13
|
+
return utils.isObject(payload) && (payload.isAxiosError === true);
|
14
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
import utils from './../utils.js';
|
4
|
+
import platform from '../platform/index.js';
|
5
|
+
|
6
|
+
export default platform.isStandardBrowserEnv ?
|
7
|
+
|
8
|
+
// Standard browser envs have full support of the APIs needed to test
|
9
|
+
// whether the request URL is of the same origin as current location.
|
10
|
+
(function standardBrowserEnv() {
|
11
|
+
const msie = /(msie|trident)/i.test(navigator.userAgent);
|
12
|
+
const urlParsingNode = document.createElement('a');
|
13
|
+
let originURL;
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Parse a URL to discover it's components
|
17
|
+
*
|
18
|
+
* @param {String} url The URL to be parsed
|
19
|
+
* @returns {Object}
|
20
|
+
*/
|
21
|
+
function resolveURL(url) {
|
22
|
+
let href = url;
|
23
|
+
|
24
|
+
if (msie) {
|
25
|
+
// IE needs attribute set twice to normalize properties
|
26
|
+
urlParsingNode.setAttribute('href', href);
|
27
|
+
href = urlParsingNode.href;
|
28
|
+
}
|
29
|
+
|
30
|
+
urlParsingNode.setAttribute('href', href);
|
31
|
+
|
32
|
+
// urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
|
33
|
+
return {
|
34
|
+
href: urlParsingNode.href,
|
35
|
+
protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
|
36
|
+
host: urlParsingNode.host,
|
37
|
+
search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
|
38
|
+
hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
|
39
|
+
hostname: urlParsingNode.hostname,
|
40
|
+
port: urlParsingNode.port,
|
41
|
+
pathname: (urlParsingNode.pathname.charAt(0) === '/') ?
|
42
|
+
urlParsingNode.pathname :
|
43
|
+
'/' + urlParsingNode.pathname
|
44
|
+
};
|
45
|
+
}
|
46
|
+
|
47
|
+
originURL = resolveURL(window.location.href);
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Determine if a URL shares the same origin as the current location
|
51
|
+
*
|
52
|
+
* @param {String} requestURL The URL to test
|
53
|
+
* @returns {boolean} True if URL shares the same origin, otherwise false
|
54
|
+
*/
|
55
|
+
return function isURLSameOrigin(requestURL) {
|
56
|
+
const parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;
|
57
|
+
return (parsed.protocol === originURL.protocol &&
|
58
|
+
parsed.host === originURL.host);
|
59
|
+
};
|
60
|
+
})() :
|
61
|
+
|
62
|
+
// Non standard browser envs (web workers, react-native) lack needed support.
|
63
|
+
(function nonStandardBrowserEnv() {
|
64
|
+
return function isURLSameOrigin() {
|
65
|
+
return true;
|
66
|
+
};
|
67
|
+
})();
|
package/helpers/null.js
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
import utils from './../utils.js';
|
4
|
+
|
5
|
+
// RawAxiosHeaders whose duplicates are ignored by node
|
6
|
+
// c.f. https://nodejs.org/api/http.html#http_message_headers
|
7
|
+
const ignoreDuplicateOf = utils.toObjectSet([
|
8
|
+
'age', 'authorization', 'content-length', 'content-type', 'etag',
|
9
|
+
'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',
|
10
|
+
'last-modified', 'location', 'max-forwards', 'proxy-authorization',
|
11
|
+
'referer', 'retry-after', 'user-agent'
|
12
|
+
]);
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Parse headers into an object
|
16
|
+
*
|
17
|
+
* ```
|
18
|
+
* Date: Wed, 27 Aug 2014 08:58:49 GMT
|
19
|
+
* Content-Type: application/json
|
20
|
+
* Connection: keep-alive
|
21
|
+
* Transfer-Encoding: chunked
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* @param {String} rawHeaders Headers needing to be parsed
|
25
|
+
*
|
26
|
+
* @returns {Object} Headers parsed into an object
|
27
|
+
*/
|
28
|
+
export default rawHeaders => {
|
29
|
+
const parsed = {};
|
30
|
+
let key;
|
31
|
+
let val;
|
32
|
+
let i;
|
33
|
+
|
34
|
+
rawHeaders && rawHeaders.split('\n').forEach(function parser(line) {
|
35
|
+
i = line.indexOf(':');
|
36
|
+
key = line.substring(0, i).trim().toLowerCase();
|
37
|
+
val = line.substring(i + 1).trim();
|
38
|
+
|
39
|
+
if (!key || (parsed[key] && ignoreDuplicateOf[key])) {
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
|
43
|
+
if (key === 'set-cookie') {
|
44
|
+
if (parsed[key]) {
|
45
|
+
parsed[key].push(val);
|
46
|
+
} else {
|
47
|
+
parsed[key] = [val];
|
48
|
+
}
|
49
|
+
} else {
|
50
|
+
parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
|
51
|
+
}
|
52
|
+
});
|
53
|
+
|
54
|
+
return parsed;
|
55
|
+
};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
const {asyncIterator} = Symbol;
|
2
|
+
|
3
|
+
const readBlob = async function* (blob) {
|
4
|
+
if (blob.stream) {
|
5
|
+
yield* blob.stream()
|
6
|
+
} else if (blob.arrayBuffer) {
|
7
|
+
yield await blob.arrayBuffer()
|
8
|
+
} else if (blob[asyncIterator]) {
|
9
|
+
yield* blob[asyncIterator]();
|
10
|
+
} else {
|
11
|
+
yield blob;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
export default readBlob;
|
@@ -0,0 +1,55 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Calculate data maxRate
|
5
|
+
* @param {Number} [samplesCount= 10]
|
6
|
+
* @param {Number} [min= 1000]
|
7
|
+
* @returns {Function}
|
8
|
+
*/
|
9
|
+
function speedometer(samplesCount, min) {
|
10
|
+
samplesCount = samplesCount || 10;
|
11
|
+
const bytes = new Array(samplesCount);
|
12
|
+
const timestamps = new Array(samplesCount);
|
13
|
+
let head = 0;
|
14
|
+
let tail = 0;
|
15
|
+
let firstSampleTS;
|
16
|
+
|
17
|
+
min = min !== undefined ? min : 1000;
|
18
|
+
|
19
|
+
return function push(chunkLength) {
|
20
|
+
const now = Date.now();
|
21
|
+
|
22
|
+
const startedAt = timestamps[tail];
|
23
|
+
|
24
|
+
if (!firstSampleTS) {
|
25
|
+
firstSampleTS = now;
|
26
|
+
}
|
27
|
+
|
28
|
+
bytes[head] = chunkLength;
|
29
|
+
timestamps[head] = now;
|
30
|
+
|
31
|
+
let i = tail;
|
32
|
+
let bytesCount = 0;
|
33
|
+
|
34
|
+
while (i !== head) {
|
35
|
+
bytesCount += bytes[i++];
|
36
|
+
i = i % samplesCount;
|
37
|
+
}
|
38
|
+
|
39
|
+
head = (head + 1) % samplesCount;
|
40
|
+
|
41
|
+
if (head === tail) {
|
42
|
+
tail = (tail + 1) % samplesCount;
|
43
|
+
}
|
44
|
+
|
45
|
+
if (now - firstSampleTS < min) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
|
49
|
+
const passed = startedAt && now - startedAt;
|
50
|
+
|
51
|
+
return passed ? Math.round(bytesCount * 1000 / passed) : undefined;
|
52
|
+
};
|
53
|
+
}
|
54
|
+
|
55
|
+
export default speedometer;
|
@@ -0,0 +1,28 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Syntactic sugar for invoking a function and expanding an array for arguments.
|
5
|
+
*
|
6
|
+
* Common use case would be to use `Function.prototype.apply`.
|
7
|
+
*
|
8
|
+
* ```js
|
9
|
+
* function f(x, y, z) {}
|
10
|
+
* var args = [1, 2, 3];
|
11
|
+
* f.apply(null, args);
|
12
|
+
* ```
|
13
|
+
*
|
14
|
+
* With `spread` this example can be re-written.
|
15
|
+
*
|
16
|
+
* ```js
|
17
|
+
* spread(function(x, y, z) {})([1, 2, 3]);
|
18
|
+
* ```
|
19
|
+
*
|
20
|
+
* @param {Function} callback
|
21
|
+
*
|
22
|
+
* @returns {Function}
|
23
|
+
*/
|
24
|
+
export default function spread(callback) {
|
25
|
+
return function wrap(arr) {
|
26
|
+
return callback.apply(null, arr);
|
27
|
+
};
|
28
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Throttle decorator
|
5
|
+
* @param {Function} fn
|
6
|
+
* @param {Number} freq
|
7
|
+
* @return {Function}
|
8
|
+
*/
|
9
|
+
function throttle(fn, freq) {
|
10
|
+
let timestamp = 0;
|
11
|
+
const threshold = 1000 / freq;
|
12
|
+
let timer = null;
|
13
|
+
return function throttled(force, args) {
|
14
|
+
const now = Date.now();
|
15
|
+
if (force || now - timestamp > threshold) {
|
16
|
+
if (timer) {
|
17
|
+
clearTimeout(timer);
|
18
|
+
timer = null;
|
19
|
+
}
|
20
|
+
timestamp = now;
|
21
|
+
return fn.apply(null, args);
|
22
|
+
}
|
23
|
+
if (!timer) {
|
24
|
+
timer = setTimeout(() => {
|
25
|
+
timer = null;
|
26
|
+
timestamp = Date.now();
|
27
|
+
return fn.apply(null, args);
|
28
|
+
}, threshold - (now - timestamp));
|
29
|
+
}
|
30
|
+
};
|
31
|
+
}
|
32
|
+
|
33
|
+
export default throttle;
|
@@ -0,0 +1,219 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
import utils from '../utils.js';
|
4
|
+
import AxiosError from '../core/AxiosError.js';
|
5
|
+
// temporary hotfix to avoid circular references until AxiosURLSearchParams is refactored
|
6
|
+
import PlatformFormData from '../platform/node/classes/FormData.js';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Determines if the given thing is a array or js object.
|
10
|
+
*
|
11
|
+
* @param {string} thing - The object or array to be visited.
|
12
|
+
*
|
13
|
+
* @returns {boolean}
|
14
|
+
*/
|
15
|
+
function isVisitable(thing) {
|
16
|
+
return utils.isPlainObject(thing) || utils.isArray(thing);
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* It removes the brackets from the end of a string
|
21
|
+
*
|
22
|
+
* @param {string} key - The key of the parameter.
|
23
|
+
*
|
24
|
+
* @returns {string} the key without the brackets.
|
25
|
+
*/
|
26
|
+
function removeBrackets(key) {
|
27
|
+
return utils.endsWith(key, '[]') ? key.slice(0, -2) : key;
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
* It takes a path, a key, and a boolean, and returns a string
|
32
|
+
*
|
33
|
+
* @param {string} path - The path to the current key.
|
34
|
+
* @param {string} key - The key of the current object being iterated over.
|
35
|
+
* @param {string} dots - If true, the key will be rendered with dots instead of brackets.
|
36
|
+
*
|
37
|
+
* @returns {string} The path to the current key.
|
38
|
+
*/
|
39
|
+
function renderKey(path, key, dots) {
|
40
|
+
if (!path) return key;
|
41
|
+
return path.concat(key).map(function each(token, i) {
|
42
|
+
// eslint-disable-next-line no-param-reassign
|
43
|
+
token = removeBrackets(token);
|
44
|
+
return !dots && i ? '[' + token + ']' : token;
|
45
|
+
}).join(dots ? '.' : '');
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* If the array is an array and none of its elements are visitable, then it's a flat array.
|
50
|
+
*
|
51
|
+
* @param {Array<any>} arr - The array to check
|
52
|
+
*
|
53
|
+
* @returns {boolean}
|
54
|
+
*/
|
55
|
+
function isFlatArray(arr) {
|
56
|
+
return utils.isArray(arr) && !arr.some(isVisitable);
|
57
|
+
}
|
58
|
+
|
59
|
+
const predicates = utils.toFlatObject(utils, {}, null, function filter(prop) {
|
60
|
+
return /^is[A-Z]/.test(prop);
|
61
|
+
});
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Convert a data object to FormData
|
65
|
+
*
|
66
|
+
* @param {Object} obj
|
67
|
+
* @param {?Object} [formData]
|
68
|
+
* @param {?Object} [options]
|
69
|
+
* @param {Function} [options.visitor]
|
70
|
+
* @param {Boolean} [options.metaTokens = true]
|
71
|
+
* @param {Boolean} [options.dots = false]
|
72
|
+
* @param {?Boolean} [options.indexes = false]
|
73
|
+
*
|
74
|
+
* @returns {Object}
|
75
|
+
**/
|
76
|
+
|
77
|
+
/**
|
78
|
+
* It converts an object into a FormData object
|
79
|
+
*
|
80
|
+
* @param {Object<any, any>} obj - The object to convert to form data.
|
81
|
+
* @param {string} formData - The FormData object to append to.
|
82
|
+
* @param {Object<string, any>} options
|
83
|
+
*
|
84
|
+
* @returns
|
85
|
+
*/
|
86
|
+
function toFormData(obj, formData, options) {
|
87
|
+
if (!utils.isObject(obj)) {
|
88
|
+
throw new TypeError('target must be an object');
|
89
|
+
}
|
90
|
+
|
91
|
+
// eslint-disable-next-line no-param-reassign
|
92
|
+
formData = formData || new (PlatformFormData || FormData)();
|
93
|
+
|
94
|
+
// eslint-disable-next-line no-param-reassign
|
95
|
+
options = utils.toFlatObject(options, {
|
96
|
+
metaTokens: true,
|
97
|
+
dots: false,
|
98
|
+
indexes: false
|
99
|
+
}, false, function defined(option, source) {
|
100
|
+
// eslint-disable-next-line no-eq-null,eqeqeq
|
101
|
+
return !utils.isUndefined(source[option]);
|
102
|
+
});
|
103
|
+
|
104
|
+
const metaTokens = options.metaTokens;
|
105
|
+
// eslint-disable-next-line no-use-before-define
|
106
|
+
const visitor = options.visitor || defaultVisitor;
|
107
|
+
const dots = options.dots;
|
108
|
+
const indexes = options.indexes;
|
109
|
+
const _Blob = options.Blob || typeof Blob !== 'undefined' && Blob;
|
110
|
+
const useBlob = _Blob && utils.isSpecCompliantForm(formData);
|
111
|
+
|
112
|
+
if (!utils.isFunction(visitor)) {
|
113
|
+
throw new TypeError('visitor must be a function');
|
114
|
+
}
|
115
|
+
|
116
|
+
function convertValue(value) {
|
117
|
+
if (value === null) return '';
|
118
|
+
|
119
|
+
if (utils.isDate(value)) {
|
120
|
+
return value.toISOString();
|
121
|
+
}
|
122
|
+
|
123
|
+
if (!useBlob && utils.isBlob(value)) {
|
124
|
+
throw new AxiosError('Blob is not supported. Use a Buffer instead.');
|
125
|
+
}
|
126
|
+
|
127
|
+
if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
|
128
|
+
return useBlob && typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
|
129
|
+
}
|
130
|
+
|
131
|
+
return value;
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Default visitor.
|
136
|
+
*
|
137
|
+
* @param {*} value
|
138
|
+
* @param {String|Number} key
|
139
|
+
* @param {Array<String|Number>} path
|
140
|
+
* @this {FormData}
|
141
|
+
*
|
142
|
+
* @returns {boolean} return true to visit the each prop of the value recursively
|
143
|
+
*/
|
144
|
+
function defaultVisitor(value, key, path) {
|
145
|
+
let arr = value;
|
146
|
+
|
147
|
+
if (value && !path && typeof value === 'object') {
|
148
|
+
if (utils.endsWith(key, '{}')) {
|
149
|
+
// eslint-disable-next-line no-param-reassign
|
150
|
+
key = metaTokens ? key : key.slice(0, -2);
|
151
|
+
// eslint-disable-next-line no-param-reassign
|
152
|
+
value = JSON.stringify(value);
|
153
|
+
} else if (
|
154
|
+
(utils.isArray(value) && isFlatArray(value)) ||
|
155
|
+
((utils.isFileList(value) || utils.endsWith(key, '[]')) && (arr = utils.toArray(value))
|
156
|
+
)) {
|
157
|
+
// eslint-disable-next-line no-param-reassign
|
158
|
+
key = removeBrackets(key);
|
159
|
+
|
160
|
+
arr.forEach(function each(el, index) {
|
161
|
+
!(utils.isUndefined(el) || el === null) && formData.append(
|
162
|
+
// eslint-disable-next-line no-nested-ternary
|
163
|
+
indexes === true ? renderKey([key], index, dots) : (indexes === null ? key : key + '[]'),
|
164
|
+
convertValue(el)
|
165
|
+
);
|
166
|
+
});
|
167
|
+
return false;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
if (isVisitable(value)) {
|
172
|
+
return true;
|
173
|
+
}
|
174
|
+
|
175
|
+
formData.append(renderKey(path, key, dots), convertValue(value));
|
176
|
+
|
177
|
+
return false;
|
178
|
+
}
|
179
|
+
|
180
|
+
const stack = [];
|
181
|
+
|
182
|
+
const exposedHelpers = Object.assign(predicates, {
|
183
|
+
defaultVisitor,
|
184
|
+
convertValue,
|
185
|
+
isVisitable
|
186
|
+
});
|
187
|
+
|
188
|
+
function build(value, path) {
|
189
|
+
if (utils.isUndefined(value)) return;
|
190
|
+
|
191
|
+
if (stack.indexOf(value) !== -1) {
|
192
|
+
throw Error('Circular reference detected in ' + path.join('.'));
|
193
|
+
}
|
194
|
+
|
195
|
+
stack.push(value);
|
196
|
+
|
197
|
+
utils.forEach(value, function each(el, key) {
|
198
|
+
const result = !(utils.isUndefined(el) || el === null) && visitor.call(
|
199
|
+
formData, el, utils.isString(key) ? key.trim() : key, path, exposedHelpers
|
200
|
+
);
|
201
|
+
|
202
|
+
if (result === true) {
|
203
|
+
build(el, path ? path.concat(key) : [key]);
|
204
|
+
}
|
205
|
+
});
|
206
|
+
|
207
|
+
stack.pop();
|
208
|
+
}
|
209
|
+
|
210
|
+
if (!utils.isObject(obj)) {
|
211
|
+
throw new TypeError('data must be an object');
|
212
|
+
}
|
213
|
+
|
214
|
+
build(obj);
|
215
|
+
|
216
|
+
return formData;
|
217
|
+
}
|
218
|
+
|
219
|
+
export default toFormData;
|