@gcmdev/faxios 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/adapters/fetch-helpers.d.ts +2 -1
- package/dist/lib/adapters/fetch-helpers.d.ts.map +1 -0
- package/dist/lib/adapters/fetch.d.ts +1 -0
- package/dist/lib/adapters/fetch.d.ts.map +1 -0
- package/dist/lib/adapters/fetch.js +86 -26
- package/dist/lib/adapters/fetch.js.map +1 -1
- package/dist/lib/cancel/CanceledError.d.ts +2 -1
- package/dist/lib/cancel/CanceledError.d.ts.map +1 -0
- package/dist/lib/cancel/isCancel.d.ts +1 -0
- package/dist/lib/cancel/isCancel.d.ts.map +1 -0
- package/dist/lib/core/Faxios.d.ts +4 -3
- package/dist/lib/core/Faxios.d.ts.map +1 -0
- package/dist/lib/core/Faxios.js +8 -4
- package/dist/lib/core/Faxios.js.map +1 -1
- package/dist/lib/core/FaxiosError.d.ts +3 -2
- package/dist/lib/core/FaxiosError.d.ts.map +1 -0
- package/dist/lib/core/FaxiosHeaders.d.ts +9 -8
- package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -0
- package/dist/lib/core/buildFullPath.d.ts +1 -0
- package/dist/lib/core/buildFullPath.d.ts.map +1 -0
- package/dist/lib/core/define.d.ts +20 -3
- package/dist/lib/core/define.d.ts.map +1 -0
- package/dist/lib/core/define.js.map +1 -1
- package/dist/lib/core/dispatchRequest.d.ts +2 -1
- package/dist/lib/core/dispatchRequest.d.ts.map +1 -0
- package/dist/lib/core/mergeConfig.d.ts +3 -2
- package/dist/lib/core/mergeConfig.d.ts.map +1 -0
- package/dist/lib/core/mergeConfig.js +40 -2
- package/dist/lib/core/mergeConfig.js.map +1 -1
- package/dist/lib/core/prepareRequest.d.ts +1 -0
- package/dist/lib/core/prepareRequest.d.ts.map +1 -0
- package/dist/lib/core/route.d.ts +10 -1
- package/dist/lib/core/route.d.ts.map +1 -0
- package/dist/lib/core/route.js +2 -5
- package/dist/lib/core/route.js.map +1 -1
- package/dist/lib/core/settle.d.ts +1 -0
- package/dist/lib/core/settle.d.ts.map +1 -0
- package/dist/lib/core/transformData.d.ts +3 -2
- package/dist/lib/core/transformData.d.ts.map +1 -0
- package/dist/lib/core/validateSchema.d.ts +3 -2
- package/dist/lib/core/validateSchema.d.ts.map +1 -0
- package/dist/lib/defaults/index.d.ts +1 -0
- package/dist/lib/defaults/index.d.ts.map +1 -0
- package/dist/lib/defaults/transitional.d.ts +1 -0
- package/dist/lib/defaults/transitional.d.ts.map +1 -0
- package/dist/lib/env/data.d.ts +2 -1
- package/dist/lib/env/data.d.ts.map +1 -0
- package/dist/lib/env/data.js +1 -1
- package/dist/lib/faxios.d.ts +59 -38
- package/dist/lib/faxios.d.ts.map +1 -0
- package/dist/lib/faxios.js.map +1 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts +1 -0
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts.map +1 -0
- package/dist/lib/helpers/HttpStatusCode.d.ts +1 -0
- package/dist/lib/helpers/HttpStatusCode.d.ts.map +1 -0
- package/dist/lib/helpers/buildURL.d.ts +1 -0
- package/dist/lib/helpers/buildURL.d.ts.map +1 -0
- package/dist/lib/helpers/cloneConfig.d.ts +1 -1
- package/dist/lib/helpers/cloneConfig.d.ts.map +1 -0
- package/dist/lib/helpers/cloneConfig.js +2 -2
- package/dist/lib/helpers/cloneConfig.js.map +1 -1
- package/dist/lib/helpers/combineURLs.d.ts +1 -0
- package/dist/lib/helpers/combineURLs.d.ts.map +1 -0
- package/dist/lib/helpers/composeSignals.d.ts +1 -0
- package/dist/lib/helpers/composeSignals.d.ts.map +1 -0
- package/dist/lib/helpers/cookies.d.ts +2 -1
- package/dist/lib/helpers/cookies.d.ts.map +1 -0
- package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts +1 -0
- package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts.map +1 -0
- package/dist/lib/helpers/formDataToJSON.d.ts +2 -1
- package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -0
- package/dist/lib/helpers/formDataToJSON.js +1 -1
- package/dist/lib/helpers/formDataToJSON.js.map +1 -1
- package/dist/lib/helpers/isAbsoluteURL.d.ts +1 -0
- package/dist/lib/helpers/isAbsoluteURL.d.ts.map +1 -0
- package/dist/lib/helpers/isFaxiosError.d.ts +1 -0
- package/dist/lib/helpers/isFaxiosError.d.ts.map +1 -0
- package/dist/lib/helpers/isURLSameOrigin.d.ts +1 -0
- package/dist/lib/helpers/isURLSameOrigin.d.ts.map +1 -0
- package/dist/lib/helpers/parseHeaders.d.ts +2 -1
- package/dist/lib/helpers/parseHeaders.d.ts.map +1 -0
- package/dist/lib/helpers/progressEventReducer.d.ts +1 -0
- package/dist/lib/helpers/progressEventReducer.d.ts.map +1 -0
- package/dist/lib/helpers/sanitizeHeaderValue.d.ts +1 -0
- package/dist/lib/helpers/sanitizeHeaderValue.d.ts.map +1 -0
- package/dist/lib/helpers/speedometer.d.ts +1 -0
- package/dist/lib/helpers/speedometer.d.ts.map +1 -0
- package/dist/lib/helpers/substitutePathParams.d.ts +1 -0
- package/dist/lib/helpers/substitutePathParams.d.ts.map +1 -0
- package/dist/lib/helpers/substitutePathParams.js +11 -3
- package/dist/lib/helpers/substitutePathParams.js.map +1 -1
- package/dist/lib/helpers/throttle.d.ts +1 -0
- package/dist/lib/helpers/throttle.d.ts.map +1 -0
- package/dist/lib/helpers/toFormData.d.ts +1 -0
- package/dist/lib/helpers/toFormData.d.ts.map +1 -0
- package/dist/lib/helpers/toFormData.js +4 -2
- package/dist/lib/helpers/toFormData.js.map +1 -1
- package/dist/lib/helpers/toURLEncodedForm.d.ts +1 -0
- package/dist/lib/helpers/toURLEncodedForm.d.ts.map +1 -0
- package/dist/lib/helpers/trackStream.d.ts +1 -0
- package/dist/lib/helpers/trackStream.d.ts.map +1 -0
- package/dist/lib/helpers/validator.d.ts +2 -1
- package/dist/lib/helpers/validator.d.ts.map +1 -0
- package/dist/lib/platform.d.ts +1 -0
- package/dist/lib/platform.d.ts.map +1 -0
- package/dist/lib/plugins/attempt.d.ts +1 -0
- package/dist/lib/plugins/attempt.d.ts.map +1 -0
- package/dist/lib/plugins/authBearer.d.ts +10 -5
- package/dist/lib/plugins/authBearer.d.ts.map +1 -0
- package/dist/lib/plugins/authBearer.js +6 -0
- package/dist/lib/plugins/authBearer.js.map +1 -1
- package/dist/lib/plugins/definePlugin.d.ts +4 -3
- package/dist/lib/plugins/definePlugin.d.ts.map +1 -0
- package/dist/lib/plugins/retry.d.ts +24 -23
- package/dist/lib/plugins/retry.d.ts.map +1 -0
- package/dist/lib/plugins/timing.d.ts +9 -8
- package/dist/lib/plugins/timing.d.ts.map +1 -0
- package/dist/lib/types/standard-schema.d.ts +7 -6
- package/dist/lib/types/standard-schema.d.ts.map +1 -0
- package/dist/lib/types.d.ts +104 -92
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +5 -3
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +9 -2
- package/dist/lib/utils.js.map +1 -1
- package/package.json +8 -4
- package/skills/configure-requests/SKILL.md +53 -8
- package/skills/configure-requests/references/plugins.md +54 -6
- package/src/index.ts +57 -0
- package/src/lib/adapters/fetch-helpers.ts +167 -0
- package/src/lib/adapters/fetch.ts +871 -0
- package/src/lib/cancel/CanceledError.ts +23 -0
- package/src/lib/cancel/isCancel.ts +10 -0
- package/src/lib/core/Faxios.ts +517 -0
- package/src/lib/core/FaxiosError.ts +283 -0
- package/src/lib/core/FaxiosHeaders.ts +529 -0
- package/src/lib/core/buildFullPath.ts +51 -0
- package/src/lib/core/define.ts +127 -0
- package/src/lib/core/dispatchRequest.ts +171 -0
- package/src/lib/core/mergeConfig.ts +213 -0
- package/src/lib/core/prepareRequest.ts +155 -0
- package/src/lib/core/route.ts +65 -0
- package/src/lib/core/settle.ts +20 -0
- package/src/lib/core/transformData.ts +23 -0
- package/src/lib/core/validateSchema.ts +53 -0
- package/src/lib/defaults/index.ts +221 -0
- package/src/lib/defaults/transitional.ts +9 -0
- package/src/lib/env/data.ts +1 -0
- package/src/lib/faxios.ts +306 -0
- package/src/lib/helpers/FaxiosURLSearchParams.ts +52 -0
- package/src/lib/helpers/HttpStatusCode.ts +77 -0
- package/src/lib/helpers/buildURL.ts +77 -0
- package/src/lib/helpers/cloneConfig.ts +22 -0
- package/src/lib/helpers/combineURLs.ts +11 -0
- package/src/lib/helpers/composeSignals.ts +74 -0
- package/src/lib/helpers/cookies.ts +68 -0
- package/src/lib/helpers/deprecatedMethod.ts +32 -0
- package/src/lib/helpers/estimateDataURLDecodedBytes.ts +95 -0
- package/src/lib/helpers/formDataToJSON.ts +122 -0
- package/src/lib/helpers/isAbsoluteURL.ts +13 -0
- package/src/lib/helpers/isFaxiosError.ts +19 -0
- package/src/lib/helpers/isURLSameOrigin.ts +16 -0
- package/src/lib/helpers/parseHeaders.ts +76 -0
- package/src/lib/helpers/progressEventReducer.ts +57 -0
- package/src/lib/helpers/sanitizeHeaderValue.ts +61 -0
- package/src/lib/helpers/speedometer.ts +55 -0
- package/src/lib/helpers/substitutePathParams.ts +23 -0
- package/src/lib/helpers/throttle.ts +45 -0
- package/src/lib/helpers/toFormData.ts +298 -0
- package/src/lib/helpers/toURLEncodedForm.ts +14 -0
- package/src/lib/helpers/trackStream.ts +123 -0
- package/src/lib/helpers/validator.ts +114 -0
- package/src/lib/platform.ts +44 -0
- package/src/lib/plugins/attempt.ts +3 -0
- package/src/lib/plugins/authBearer.ts +129 -0
- package/src/lib/plugins/definePlugin.ts +34 -0
- package/src/lib/plugins/retry.ts +245 -0
- package/src/lib/plugins/timing.ts +76 -0
- package/src/lib/types/standard-schema.ts +58 -0
- package/src/lib/types.ts +496 -0
- package/src/lib/utils.ts +941 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import utils from "../utils.js";
|
|
4
|
+
|
|
5
|
+
// RawFaxiosHeaders 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",
|
|
9
|
+
"authorization",
|
|
10
|
+
"content-length",
|
|
11
|
+
"content-type",
|
|
12
|
+
"etag",
|
|
13
|
+
"expires",
|
|
14
|
+
"from",
|
|
15
|
+
"host",
|
|
16
|
+
"if-modified-since",
|
|
17
|
+
"if-unmodified-since",
|
|
18
|
+
"last-modified",
|
|
19
|
+
"location",
|
|
20
|
+
"max-forwards",
|
|
21
|
+
"proxy-authorization",
|
|
22
|
+
"referer",
|
|
23
|
+
"retry-after",
|
|
24
|
+
"user-agent",
|
|
25
|
+
], "");
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse headers into an object
|
|
29
|
+
*
|
|
30
|
+
* ```
|
|
31
|
+
* Date: Wed, 27 Aug 2014 08:58:49 GMT
|
|
32
|
+
* Content-Type: application/json
|
|
33
|
+
* Connection: keep-alive
|
|
34
|
+
* Transfer-Encoding: chunked
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param {String} rawHeaders Headers needing to be parsed
|
|
38
|
+
*
|
|
39
|
+
* @returns {Object} Headers parsed into an object
|
|
40
|
+
*/
|
|
41
|
+
export default (rawHeaders: string): Record<string, Array<string> | string> => {
|
|
42
|
+
const parsed: Record<string, Array<string> | string> = {};
|
|
43
|
+
let key: string;
|
|
44
|
+
let val: string;
|
|
45
|
+
let i: number;
|
|
46
|
+
|
|
47
|
+
rawHeaders &&
|
|
48
|
+
rawHeaders.split("\n")
|
|
49
|
+
.forEach(function parser(line) {
|
|
50
|
+
// eslint-disable-next-line big-o/no-array-lookup-in-loop
|
|
51
|
+
i = line.indexOf(":");
|
|
52
|
+
key = line.substring(0, i).trim()
|
|
53
|
+
.toLowerCase();
|
|
54
|
+
val = line.substring(i + 1).trim();
|
|
55
|
+
|
|
56
|
+
if (!key || (parsed[key] !== undefined && ignoreDuplicateOf[key] !== undefined)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (key === "set-cookie") {
|
|
61
|
+
const existing = parsed[key];
|
|
62
|
+
if (existing) {
|
|
63
|
+
(existing as Array<string>).push(val);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
parsed[key] = [ val ];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const existing = parsed[key] as string | undefined;
|
|
71
|
+
parsed[key] = existing ? existing + ", " + val : val;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return parsed;
|
|
76
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FaxiosProgressEvent } from "../types.js";
|
|
2
|
+
import utils from "../utils.js";
|
|
3
|
+
import speedometer from "./speedometer.js";
|
|
4
|
+
import throttle from "./throttle.js";
|
|
5
|
+
|
|
6
|
+
export const progressEventReducer = (listener: (data: FaxiosProgressEvent) => void, isDownloadStream: boolean, freq = 3) => {
|
|
7
|
+
let bytesNotified = 0;
|
|
8
|
+
const _speedometer = speedometer(50, 250);
|
|
9
|
+
|
|
10
|
+
return throttle((e: unknown) => {
|
|
11
|
+
if (!e || typeof (e as Record<string, unknown>).loaded !== "number") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const ev = e as { lengthComputable?: boolean; loaded: number; total?: number; };
|
|
15
|
+
const rawLoaded = ev.loaded;
|
|
16
|
+
const total = ev.lengthComputable ? ev.total : undefined;
|
|
17
|
+
const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded;
|
|
18
|
+
const progressBytes = Math.max(0, loaded - bytesNotified);
|
|
19
|
+
const rate = _speedometer(progressBytes);
|
|
20
|
+
|
|
21
|
+
bytesNotified = Math.max(bytesNotified, loaded);
|
|
22
|
+
|
|
23
|
+
const data: FaxiosProgressEvent = {
|
|
24
|
+
loaded,
|
|
25
|
+
total,
|
|
26
|
+
progress: total ? loaded / total : undefined,
|
|
27
|
+
bytes: progressBytes,
|
|
28
|
+
rate: rate ? rate : undefined,
|
|
29
|
+
estimated: rate && total ? (total - loaded) / rate : undefined,
|
|
30
|
+
event: e,
|
|
31
|
+
lengthComputable: total != null,
|
|
32
|
+
download: isDownloadStream ? true : undefined,
|
|
33
|
+
upload: isDownloadStream ? undefined : true,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
listener(data);
|
|
37
|
+
}, freq);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const progressEventDecorator = (total: number | undefined, throttled: ReturnType<typeof throttle>): [(loaded: number) => void, () => void] => {
|
|
41
|
+
const lengthComputable = total != null;
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
(loaded: number) =>
|
|
45
|
+
throttled[0]({
|
|
46
|
+
lengthComputable,
|
|
47
|
+
total,
|
|
48
|
+
loaded,
|
|
49
|
+
}),
|
|
50
|
+
throttled[1],
|
|
51
|
+
];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const asyncDecorator =
|
|
55
|
+
(fn: (...args: Array<unknown>) => unknown) =>
|
|
56
|
+
(...args: Array<unknown>) =>
|
|
57
|
+
utils.asap(() => fn(...args));
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import utils from "../utils.js";
|
|
4
|
+
|
|
5
|
+
function trimSPorHTAB(str: string): string {
|
|
6
|
+
let start = 0;
|
|
7
|
+
let end = str.length;
|
|
8
|
+
|
|
9
|
+
while (start < end) {
|
|
10
|
+
const code = str.charCodeAt(start);
|
|
11
|
+
|
|
12
|
+
if (code !== 0x09 && code !== 0x20) {
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
start += 1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
while (end > start) {
|
|
20
|
+
const code = str.charCodeAt(end - 1);
|
|
21
|
+
|
|
22
|
+
if (code !== 0x09 && code !== 0x20) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
end -= 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return start === 0 && end === str.length ? str : str.slice(start, end);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The control-code ranges are intentional: header sanitization strips C0/DEL bytes.
|
|
33
|
+
// eslint-disable-next-line no-control-regex
|
|
34
|
+
const INVALID_UNICODE_HEADER_VALUE_CHARS = new RegExp("[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+", "g");
|
|
35
|
+
// eslint-disable-next-line no-control-regex, sonarjs/no-control-regex
|
|
36
|
+
const INVALID_BYTE_STRING_HEADER_VALUE_CHARS = new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+", "g");
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line sonarjs/function-return-type
|
|
39
|
+
function sanitizeValue(value: unknown, invalidChars: RegExp): Array<string> | string {
|
|
40
|
+
if (utils.isArray(value)) {
|
|
41
|
+
return (value as Array<unknown>).map(item => sanitizeValue(item, invalidChars) as string);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return trimSPorHTAB(String(value).replace(invalidChars, ""));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const sanitizeHeaderValue = (value: unknown) =>
|
|
48
|
+
sanitizeValue(value, INVALID_UNICODE_HEADER_VALUE_CHARS);
|
|
49
|
+
|
|
50
|
+
const sanitizeByteStringHeaderValue = (value: unknown) =>
|
|
51
|
+
sanitizeValue(value, INVALID_BYTE_STRING_HEADER_VALUE_CHARS);
|
|
52
|
+
|
|
53
|
+
export function toByteStringHeaderObject(headers: { toJSON: () => Record<string, unknown>; }) {
|
|
54
|
+
const byteStringHeaders: Record<string, unknown> = Object.create(null);
|
|
55
|
+
|
|
56
|
+
utils.forEach(headers.toJSON(), (value: unknown, header: unknown) => {
|
|
57
|
+
byteStringHeaders[String(header)] = sanitizeByteStringHeaderValue(value);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return byteStringHeaders;
|
|
61
|
+
}
|
|
@@ -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?: number, min?: number): (chunkLength: number) => number | undefined {
|
|
10
|
+
samplesCount = samplesCount || 10;
|
|
11
|
+
const bytes: Array<number> = new Array(samplesCount);
|
|
12
|
+
const timestamps: Array<number> = new Array(samplesCount);
|
|
13
|
+
let head = 0;
|
|
14
|
+
let tail = 0;
|
|
15
|
+
let firstSampleTS: number | undefined;
|
|
16
|
+
|
|
17
|
+
min = min !== undefined ? min : 1000;
|
|
18
|
+
|
|
19
|
+
return function push(chunkLength: number): number | undefined {
|
|
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,23 @@
|
|
|
1
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
2
|
+
|
|
3
|
+
// `.` and `..` (raw or percent-encoded) would become a dot segment that the URL parser resolves,
|
|
4
|
+
// moving the request to another path.
|
|
5
|
+
const DOT_SEGMENT = /^(?:\.|%2e){1,2}$/i;
|
|
6
|
+
|
|
7
|
+
/** Expects raw (unencoded) param values — applies encodeURIComponent here. */
|
|
8
|
+
export function substitutePathParams(url: string, params: Record<string, unknown>): string {
|
|
9
|
+
return url.replace(/\{([^{}]+)\}/g, (_match, key: string) => {
|
|
10
|
+
if (!Object.prototype.hasOwnProperty.call(params, key)) {
|
|
11
|
+
throw new FaxiosError(`Path param "${key}" not found in pathParams`, FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
12
|
+
}
|
|
13
|
+
const val = params[key];
|
|
14
|
+
if (val == null) {
|
|
15
|
+
throw new FaxiosError(`Path param "${key}" is null or undefined`, FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
16
|
+
}
|
|
17
|
+
const str = String(val);
|
|
18
|
+
if (DOT_SEGMENT.test(str)) {
|
|
19
|
+
throw new FaxiosError(`Path param "${key}" must not be "." or ".." (or a percent-encoded form)`, FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
20
|
+
}
|
|
21
|
+
return encodeURIComponent(str);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Throttle decorator
|
|
3
|
+
* @param {Function} fn
|
|
4
|
+
* @param {Number} freq
|
|
5
|
+
* @return {Function}
|
|
6
|
+
*/
|
|
7
|
+
function throttle(fn: (...args: Array<unknown>) => void, freq: number): [(...args: Array<unknown>) => void, () => void] {
|
|
8
|
+
let timestamp = 0;
|
|
9
|
+
const threshold = 1000 / freq;
|
|
10
|
+
let lastArgs: Array<unknown> | null = null;
|
|
11
|
+
let timer: null | ReturnType<typeof setTimeout> = null;
|
|
12
|
+
|
|
13
|
+
const invoke = (args: Array<unknown>, now: number = Date.now()) => {
|
|
14
|
+
timestamp = now;
|
|
15
|
+
lastArgs = null;
|
|
16
|
+
if (timer) {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
timer = null;
|
|
19
|
+
}
|
|
20
|
+
fn(...args);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const throttled = (...args: Array<unknown>) => {
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
const passed = now - timestamp;
|
|
26
|
+
if (passed >= threshold) {
|
|
27
|
+
invoke(args, now);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
lastArgs = args;
|
|
31
|
+
if (!timer) {
|
|
32
|
+
timer = setTimeout(() => {
|
|
33
|
+
timer = null;
|
|
34
|
+
invoke(lastArgs!);
|
|
35
|
+
}, threshold - passed);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const flush = () => { if (lastArgs) invoke(lastArgs); };
|
|
41
|
+
|
|
42
|
+
return [ throttled, flush ];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default throttle;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
4
|
+
// temporary hotfix to avoid circular references until FaxiosURLSearchParams is refactored
|
|
5
|
+
import type { FormDataVisitorHelpers, GenericFormData, SerializerVisitor } from "../types.js";
|
|
6
|
+
import utils from "../utils.js";
|
|
7
|
+
|
|
8
|
+
// Default nesting limit shared with the inverse transform (formDataToJSON) so
|
|
9
|
+
// the FormData <-> JSON round-trip stays symmetric.
|
|
10
|
+
export const DEFAULT_FORM_DATA_MAX_DEPTH = 100;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Determines if the given thing is a array or js object.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} thing - The object or array to be visited.
|
|
16
|
+
*
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
function isVisitable(thing: unknown): boolean {
|
|
20
|
+
return utils.isPlainObject(thing) || utils.isArray(thing);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* It removes the brackets from the end of a string
|
|
25
|
+
*
|
|
26
|
+
* @param {string} key - The key of the parameter.
|
|
27
|
+
*
|
|
28
|
+
* @returns {string} the key without the brackets.
|
|
29
|
+
*/
|
|
30
|
+
function removeBrackets(key: string): string {
|
|
31
|
+
return key.endsWith("[]") ? key.slice(0, -2) : key;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* It takes a path, a key, and a boolean, and returns a string
|
|
36
|
+
*
|
|
37
|
+
* @param {string} path - The path to the current key.
|
|
38
|
+
* @param {string} key - The key of the current object being iterated over.
|
|
39
|
+
* @param {string} dots - If true, the key will be rendered with dots instead of brackets.
|
|
40
|
+
*
|
|
41
|
+
* @returns {string} The path to the current key.
|
|
42
|
+
*/
|
|
43
|
+
function renderKey(path: Array<number | string> | null | undefined, key: number | string, dots: unknown): string {
|
|
44
|
+
if (!path) return String(key);
|
|
45
|
+
return path
|
|
46
|
+
.concat(key)
|
|
47
|
+
.map(function each(token: number | string, i: number) {
|
|
48
|
+
token = removeBrackets(String(token));
|
|
49
|
+
return !dots && i ? "[" + token + "]" : token;
|
|
50
|
+
})
|
|
51
|
+
.join(dots ? "." : "");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* If the array is an array and none of its elements are visitable, then it's a flat array.
|
|
56
|
+
*
|
|
57
|
+
* @param {Array<any>} arr - The array to check
|
|
58
|
+
*
|
|
59
|
+
* @returns {boolean}
|
|
60
|
+
*/
|
|
61
|
+
function isFlatArray(arr: unknown): boolean {
|
|
62
|
+
return utils.isArray(arr) && !(arr as Array<unknown>).some(isVisitable);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const predicates = utils.toFlatObject(utils, {}, null as unknown as false, function filter(prop: string) {
|
|
66
|
+
return /^is[A-Z]/.test(prop);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Convert a data object to FormData
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} obj
|
|
73
|
+
* @param {?Object} [formData]
|
|
74
|
+
* @param {?Object} [options]
|
|
75
|
+
* @param {Function} [options.visitor]
|
|
76
|
+
* @param {Boolean} [options.metaTokens = true]
|
|
77
|
+
* @param {Boolean} [options.dots = false]
|
|
78
|
+
* @param {?Boolean} [options.indexes = false]
|
|
79
|
+
*
|
|
80
|
+
* @returns {Object}
|
|
81
|
+
**/
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* It converts an object into a FormData object
|
|
85
|
+
*
|
|
86
|
+
* @param {Object<any, any>} obj - The object to convert to form data.
|
|
87
|
+
* @param {string} formData - The FormData object to append to.
|
|
88
|
+
* @param {Object<string, any>} options
|
|
89
|
+
*
|
|
90
|
+
* @returns
|
|
91
|
+
*/
|
|
92
|
+
function toFormData(obj: unknown, formData?: GenericFormData | null, options?: Record<string, unknown>): GenericFormData {
|
|
93
|
+
if (!utils.isObject(obj)) {
|
|
94
|
+
throw new TypeError("target must be an object");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const FormDataCtor = (globalThis as Record<string, unknown>)["FormData"] as (new () => GenericFormData) | undefined;
|
|
98
|
+
if (!formData) {
|
|
99
|
+
if (!FormDataCtor) throw new FaxiosError("FormData is not available in this environment", FaxiosError.ERR_NOT_SUPPORT);
|
|
100
|
+
formData = new FormDataCtor();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Null-prototype destination: `visitor`, `maxDepth` and `Blob` are read below, and a polluted
|
|
104
|
+
// Object.prototype must not supply them.
|
|
105
|
+
options = utils.toFlatObject(
|
|
106
|
+
options,
|
|
107
|
+
Object.assign(Object.create(null) as Record<string, unknown>, {
|
|
108
|
+
metaTokens: true,
|
|
109
|
+
dots: false,
|
|
110
|
+
indexes: false,
|
|
111
|
+
}),
|
|
112
|
+
false,
|
|
113
|
+
function defined(option: string, source: unknown) {
|
|
114
|
+
return !utils.isUndefined((source as Record<string, unknown>)[option]);
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const metaTokens = options["metaTokens"];
|
|
119
|
+
|
|
120
|
+
const visitor = (options["visitor"] || defaultVisitor) as SerializerVisitor;
|
|
121
|
+
const dots = options["dots"];
|
|
122
|
+
const indexes = options["indexes"];
|
|
123
|
+
const _Blob = options["Blob"] || (typeof (globalThis as Record<string, unknown>)["Blob"] !== "undefined" && (globalThis as Record<string, unknown>)["Blob"]);
|
|
124
|
+
const maxDepth =
|
|
125
|
+
options["maxDepth"] === undefined
|
|
126
|
+
? DEFAULT_FORM_DATA_MAX_DEPTH
|
|
127
|
+
: (options["maxDepth"] as number);
|
|
128
|
+
const useBlob = _Blob && utils.isSpecCompliantForm(formData);
|
|
129
|
+
const stack: Array<unknown> = [];
|
|
130
|
+
|
|
131
|
+
if (!utils.isFunction(visitor)) {
|
|
132
|
+
throw new TypeError("visitor must be a function");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function convertValue(value: unknown): unknown {
|
|
136
|
+
if (value === null) return "";
|
|
137
|
+
|
|
138
|
+
if (utils.isDate(value)) {
|
|
139
|
+
return (value as Date).toISOString();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (utils.isBoolean(value)) {
|
|
143
|
+
return String(value);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!useBlob && utils.isBlob(value)) {
|
|
147
|
+
throw new FaxiosError("Blob is not supported. Use a Buffer instead.");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
|
|
151
|
+
const BlobCtor = (globalThis as Record<string, unknown>)["Blob"] as (new (parts: Array<unknown>) => unknown) | undefined;
|
|
152
|
+
if (useBlob && typeof BlobCtor === "function") {
|
|
153
|
+
return new BlobCtor([ value ]);
|
|
154
|
+
}
|
|
155
|
+
throw new FaxiosError("Blob is required for binary FormData values in this environment", FaxiosError.ERR_NOT_SUPPORT);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function throwIfMaxDepthExceeded(depth: number): void {
|
|
162
|
+
if (depth > (maxDepth)) {
|
|
163
|
+
throw new FaxiosError(
|
|
164
|
+
"Object is too deeply nested (" +
|
|
165
|
+
depth +
|
|
166
|
+
" levels). Max depth: " +
|
|
167
|
+
maxDepth,
|
|
168
|
+
FaxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function stringifyWithDepthLimit(value: unknown, depth: number): string {
|
|
174
|
+
if (maxDepth === Infinity) {
|
|
175
|
+
return JSON.stringify(value);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const ancestors: Array<unknown> = [];
|
|
179
|
+
|
|
180
|
+
return JSON.stringify(value, function limitDepth(this: unknown, _key: string, currentValue: unknown): unknown {
|
|
181
|
+
if (!utils.isObject(currentValue)) {
|
|
182
|
+
return currentValue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
while (ancestors.length && ancestors[ancestors.length - 1] !== this) {
|
|
186
|
+
ancestors.pop();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
ancestors.push(currentValue);
|
|
190
|
+
throwIfMaxDepthExceeded(depth + ancestors.length - 1);
|
|
191
|
+
|
|
192
|
+
return currentValue;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Default visitor.
|
|
198
|
+
*
|
|
199
|
+
* @param {*} value
|
|
200
|
+
* @param {String|Number} key
|
|
201
|
+
* @param {Array<String|Number>} path
|
|
202
|
+
* @this {FormData}
|
|
203
|
+
*
|
|
204
|
+
* @returns {boolean} return true to visit the each prop of the value recursively
|
|
205
|
+
*/
|
|
206
|
+
function defaultVisitor(this: GenericFormData, value: unknown, key: number | string, path: Array<number | string> | null, _helpers?: FormDataVisitorHelpers): boolean {
|
|
207
|
+
let arr: unknown = value;
|
|
208
|
+
|
|
209
|
+
if (utils.isReactNative(formData) && utils.isReactNativeBlob(value)) {
|
|
210
|
+
(formData as GenericFormData).append(renderKey(path, key, dots), convertValue(value));
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (value && !path && typeof value === "object") {
|
|
215
|
+
if (String(key).endsWith("{}")) {
|
|
216
|
+
key = metaTokens ? key : String(key).slice(0, -2);
|
|
217
|
+
|
|
218
|
+
value = stringifyWithDepthLimit(value, 1);
|
|
219
|
+
}
|
|
220
|
+
else if (
|
|
221
|
+
(utils.isArray(value) && isFlatArray(value)) ||
|
|
222
|
+
((utils.isFileList(value) || String(key).endsWith("[]")) &&
|
|
223
|
+
(arr = utils.toArray(value)))
|
|
224
|
+
) {
|
|
225
|
+
key = removeBrackets(String(key));
|
|
226
|
+
|
|
227
|
+
(arr as Array<unknown>).forEach(function each(el: unknown, index: number) {
|
|
228
|
+
!utils.isUndefined(el) && el !== null &&
|
|
229
|
+
(formData as GenericFormData).append(
|
|
230
|
+
/* eslint-disable sonarjs/no-nested-conditional */
|
|
231
|
+
indexes === true
|
|
232
|
+
? renderKey([ String(key) ], index, dots)
|
|
233
|
+
: indexes === null
|
|
234
|
+
? String(key)
|
|
235
|
+
: String(key) + "[]",
|
|
236
|
+
/* eslint-enable sonarjs/no-nested-conditional */
|
|
237
|
+
convertValue(el)
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (isVisitable(value)) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
(formData as GenericFormData).append(renderKey(path, key, dots), convertValue(value));
|
|
249
|
+
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const exposedHelpers: FormDataVisitorHelpers = Object.assign(predicates, {
|
|
254
|
+
defaultVisitor: defaultVisitor,
|
|
255
|
+
convertValue,
|
|
256
|
+
isVisitable,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
function build(value: unknown, path?: Array<number | string>, depth = 0): void {
|
|
260
|
+
if (utils.isUndefined(value)) return;
|
|
261
|
+
|
|
262
|
+
throwIfMaxDepthExceeded(depth);
|
|
263
|
+
|
|
264
|
+
if (stack.indexOf(value) !== -1) {
|
|
265
|
+
throw new Error("Circular reference detected in " + (path ?? []).join("."));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
stack.push(value);
|
|
269
|
+
|
|
270
|
+
utils.forEach(value, function each(el: unknown, key: unknown) {
|
|
271
|
+
const result =
|
|
272
|
+
!utils.isUndefined(el) && el !== null &&
|
|
273
|
+
visitor.call(
|
|
274
|
+
formData as GenericFormData,
|
|
275
|
+
el,
|
|
276
|
+
utils.isString(key) ? (key as string).trim() : key as number | string,
|
|
277
|
+
path ?? null,
|
|
278
|
+
exposedHelpers
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
if (result === true) {
|
|
282
|
+
build(el, path ? path.concat(key as number | string) : [ key as number | string ], depth + 1);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
stack.pop();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!utils.isObject(obj)) {
|
|
290
|
+
throw new TypeError("data must be an object");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
build(obj);
|
|
294
|
+
|
|
295
|
+
return formData;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export default toFormData;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import platform from "../platform.js";
|
|
4
|
+
import type { GenericFormData } from "../types.js";
|
|
5
|
+
import toFormData from "./toFormData.js";
|
|
6
|
+
|
|
7
|
+
export default function toURLEncodedForm(data: unknown, options?: Record<string, unknown>) {
|
|
8
|
+
// ponytail: web-standard platform always provides global URLSearchParams
|
|
9
|
+
return toFormData(
|
|
10
|
+
data,
|
|
11
|
+
new (platform.classes.URLSearchParams as new () => object)() as unknown as GenericFormData,
|
|
12
|
+
{ ...options }
|
|
13
|
+
);
|
|
14
|
+
}
|