@gcmdev/faxios 0.1.0 → 0.2.1
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 +1 -0
- package/README.md +40 -1774
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/adapters/fetch-helpers.d.ts +1 -0
- 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 +23 -3
- package/dist/lib/adapters/fetch.js.map +1 -1
- package/dist/lib/cancel/CanceledError.d.ts +1 -0
- package/dist/lib/cancel/CanceledError.d.ts.map +1 -0
- package/dist/lib/cancel/isCancel.d.ts +3 -1
- package/dist/lib/cancel/isCancel.d.ts.map +1 -0
- package/dist/lib/cancel/isCancel.js +3 -0
- package/dist/lib/cancel/isCancel.js.map +1 -1
- package/dist/lib/core/Faxios.d.ts +6 -17
- package/dist/lib/core/Faxios.d.ts.map +1 -0
- package/dist/lib/core/Faxios.js +144 -76
- package/dist/lib/core/Faxios.js.map +1 -1
- package/dist/lib/core/FaxiosError.d.ts +19 -6
- package/dist/lib/core/FaxiosError.d.ts.map +1 -0
- package/dist/lib/core/FaxiosError.js +16 -1
- package/dist/lib/core/FaxiosError.js.map +1 -1
- package/dist/lib/core/FaxiosHeaders.d.ts +12 -8
- package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -0
- package/dist/lib/core/FaxiosHeaders.js +1 -0
- package/dist/lib/core/FaxiosHeaders.js.map +1 -1
- 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 +19 -2
- 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 +1 -0
- package/dist/lib/core/dispatchRequest.d.ts.map +1 -0
- package/dist/lib/core/dispatchRequest.js +4 -0
- package/dist/lib/core/dispatchRequest.js.map +1 -1
- package/dist/lib/core/mergeConfig.d.ts +1 -0
- package/dist/lib/core/mergeConfig.d.ts.map +1 -0
- package/dist/lib/core/prepareRequest.d.ts +1 -0
- package/dist/lib/core/prepareRequest.d.ts.map +1 -0
- package/dist/lib/core/prepareRequest.js +1 -20
- package/dist/lib/core/prepareRequest.js.map +1 -1
- package/dist/lib/core/route.d.ts +11 -2
- package/dist/lib/core/route.d.ts.map +1 -0
- 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 +1 -0
- package/dist/lib/core/transformData.d.ts.map +1 -0
- package/dist/lib/core/validateSchema.d.ts +1 -0
- 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/index.js +3 -3
- package/dist/lib/defaults/index.js.map +1 -1
- package/dist/lib/defaults/transitional.d.ts +1 -1
- package/dist/lib/defaults/transitional.d.ts.map +1 -0
- package/dist/lib/defaults/transitional.js +0 -1
- package/dist/lib/defaults/transitional.js.map +1 -1
- 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/env/data.js.map +1 -1
- package/dist/lib/faxios.d.ts +115 -48
- package/dist/lib/faxios.d.ts.map +1 -0
- package/dist/lib/faxios.js +14 -2
- package/dist/lib/faxios.js.map +1 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts +3 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts.map +1 -0
- package/dist/lib/helpers/FaxiosURLSearchParams.js.map +1 -1
- 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/buildURL.js +7 -1
- package/dist/lib/helpers/buildURL.js.map +1 -1
- package/dist/lib/helpers/cloneConfig.d.ts +4 -0
- package/dist/lib/helpers/cloneConfig.d.ts.map +1 -0
- package/dist/lib/helpers/cloneConfig.js +21 -0
- package/dist/lib/helpers/cloneConfig.js.map +1 -0
- 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/composeSignals.js +8 -1
- package/dist/lib/helpers/composeSignals.js.map +1 -1
- package/dist/lib/helpers/cookies.d.ts +1 -0
- 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 +1 -0
- package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -0
- 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 +8 -2
- package/dist/lib/helpers/isFaxiosError.d.ts.map +1 -0
- package/dist/lib/helpers/isFaxiosError.js +5 -1
- package/dist/lib/helpers/isFaxiosError.js.map +1 -1
- 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 +1 -0
- 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/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/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 +1 -0
- package/dist/lib/helpers/validator.d.ts.map +1 -0
- package/dist/lib/platform.d.ts +2 -1
- package/dist/lib/platform.d.ts.map +1 -0
- package/dist/lib/plugins/attempt.d.ts +2 -0
- package/dist/lib/plugins/attempt.d.ts.map +1 -0
- package/dist/lib/plugins/attempt.js +4 -0
- package/dist/lib/plugins/attempt.js.map +1 -0
- package/dist/lib/plugins/authBearer.d.ts +31 -0
- package/dist/lib/plugins/authBearer.d.ts.map +1 -0
- package/dist/lib/plugins/authBearer.js +99 -0
- package/dist/lib/plugins/authBearer.js.map +1 -0
- package/dist/lib/plugins/definePlugin.d.ts +22 -0
- package/dist/lib/plugins/definePlugin.d.ts.map +1 -0
- package/dist/lib/plugins/definePlugin.js +14 -0
- package/dist/lib/plugins/definePlugin.js.map +1 -0
- package/dist/lib/plugins/retry.d.ts +66 -0
- package/dist/lib/plugins/retry.d.ts.map +1 -0
- package/dist/lib/plugins/retry.js +168 -0
- package/dist/lib/plugins/retry.js.map +1 -0
- package/dist/lib/plugins/timing.d.ts +32 -0
- package/dist/lib/plugins/timing.d.ts.map +1 -0
- package/dist/lib/plugins/timing.js +53 -0
- package/dist/lib/plugins/timing.js.map +1 -0
- package/dist/lib/types/standard-schema.d.ts +1 -0
- package/dist/lib/types/standard-schema.d.ts.map +1 -0
- package/dist/lib/types.d.ts +125 -34
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/package.json +42 -19
- package/skills/configure-requests/SKILL.md +380 -0
- package/skills/configure-requests/references/axios-differences.md +67 -0
- package/skills/configure-requests/references/plugins.md +127 -0
- package/src/index.ts +57 -0
- package/src/lib/adapters/fetch-helpers.ts +167 -0
- package/src/lib/adapters/fetch.ts +810 -0
- package/src/lib/cancel/CanceledError.ts +23 -0
- package/src/lib/cancel/isCancel.ts +10 -0
- package/src/lib/core/Faxios.ts +513 -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 +175 -0
- package/src/lib/core/prepareRequest.ts +155 -0
- package/src/lib/core/route.ts +68 -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 +23 -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 +13 -0
- package/src/lib/helpers/throttle.ts +45 -0
- package/src/lib/helpers/toFormData.ts +296 -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 +124 -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 +932 -0
- package/dist/lib/core/InterceptorManager.d.ts +0 -9
- package/dist/lib/core/InterceptorManager.js +0 -28
- package/dist/lib/core/InterceptorManager.js.map +0 -1
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
2
|
+
import FaxiosHeaders from "../core/FaxiosHeaders.js";
|
|
3
|
+
import prepareRequest from "../core/prepareRequest.js";
|
|
4
|
+
import { settle } from "../core/settle.js";
|
|
5
|
+
import { VERSION } from "../env/data.js";
|
|
6
|
+
import composeSignals from "../helpers/composeSignals.js";
|
|
7
|
+
import estimateDataURLDecodedBytes from "../helpers/estimateDataURLDecodedBytes.js";
|
|
8
|
+
import {
|
|
9
|
+
progressEventReducer,
|
|
10
|
+
progressEventDecorator,
|
|
11
|
+
asyncDecorator
|
|
12
|
+
} from "../helpers/progressEventReducer.js";
|
|
13
|
+
import { toByteStringHeaderObject } from "../helpers/sanitizeHeaderValue.js";
|
|
14
|
+
import { trackStream } from "../helpers/trackStream.js";
|
|
15
|
+
import platform from "../platform.js";
|
|
16
|
+
import type {
|
|
17
|
+
InternalFaxiosRequestConfig,
|
|
18
|
+
FaxiosRequestHeaders
|
|
19
|
+
} from "../types.js";
|
|
20
|
+
import utils from "../utils.js";
|
|
21
|
+
import { checkDeclaredContentLength, checkMaterializedSize, handleFetchCaughtError, cleanFormDataContentType, encodeBodyIfNeeded } from "./fetch-helpers.js";
|
|
22
|
+
import type { ComposedSignal, PendingBodyErrorRef } from "./fetch-helpers.js";
|
|
23
|
+
|
|
24
|
+
// btoa is a global in Node 16+ and browsers; accessed via globalThis for no-DOM lib compat
|
|
25
|
+
const _btoa: (data: string) => string = (
|
|
26
|
+
globalThis as unknown as Record<string, unknown>
|
|
27
|
+
)["btoa"] as (data: string) => string;
|
|
28
|
+
type AnyConstructor = new (...args: Array<unknown>) => unknown;
|
|
29
|
+
type FetchFn = (input: unknown, init?: unknown) => Promise<AnyResponse>;
|
|
30
|
+
type AnyResponse = {
|
|
31
|
+
body: unknown;
|
|
32
|
+
headers: {
|
|
33
|
+
has: (name: string) => boolean;
|
|
34
|
+
get: (name: string) => string | null;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
status: number;
|
|
38
|
+
statusText: string;
|
|
39
|
+
arrayBuffer: () => Promise<ArrayBuffer>;
|
|
40
|
+
[key: string]: unknown;
|
|
41
|
+
};
|
|
42
|
+
type AnyRequest = {
|
|
43
|
+
headers: {
|
|
44
|
+
has: (name: string) => boolean;
|
|
45
|
+
get: (name: string) => string | null;
|
|
46
|
+
};
|
|
47
|
+
body: { cancel: () => Promise<void>; } | null;
|
|
48
|
+
arrayBuffer: () => Promise<ArrayBuffer>;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
};
|
|
51
|
+
type AnyReadableStream = {
|
|
52
|
+
cancel: () => Promise<void>;
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
};
|
|
55
|
+
type AnyTextEncoder = { encode: (str: string) => Uint8Array; };
|
|
56
|
+
|
|
57
|
+
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
|
58
|
+
|
|
59
|
+
const { isFunction } = utils;
|
|
60
|
+
|
|
61
|
+
// Server runtimes default User-Agent to their own (e.g. "node"), so name faxios there.
|
|
62
|
+
// Browsers send their own User-Agent; setting one makes every cross-origin request
|
|
63
|
+
// need a CORS preflight that most APIs don't allow.
|
|
64
|
+
const setsUserAgent = !platform.hasStandardBrowserEnv && !platform.hasStandardBrowserWebWorkerEnv;
|
|
65
|
+
const applyDefaultUserAgent = (headers: FaxiosRequestHeaders): void => {
|
|
66
|
+
if (setsUserAgent) headers.set("User-Agent", "faxios/" + VERSION, false);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Encode a UTF-8 string to a Latin-1 byte string for use with btoa().
|
|
71
|
+
* This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} str The string to encode
|
|
74
|
+
*
|
|
75
|
+
* @returns {string} UTF-8 bytes as a Latin-1 string
|
|
76
|
+
*/
|
|
77
|
+
const encodeUTF8 = (str: string): string =>
|
|
78
|
+
encodeURIComponent(str).replace(
|
|
79
|
+
/%([0-9A-F]{2})/gi,
|
|
80
|
+
(_: string, hex: string) => String.fromCharCode(parseInt(hex, 16))
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Node's WHATWG URL parser returns `username` and `password` percent-encoded.
|
|
84
|
+
// Decode before composing the `auth` option so credentials such as
|
|
85
|
+
// `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the
|
|
86
|
+
// original value for malformed input so a bad encoding never throws.
|
|
87
|
+
const decodeURIComponentSafe = (value: unknown): unknown => {
|
|
88
|
+
if (!utils.isString(value)) {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
return decodeURIComponent(value as string);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const test = (
|
|
101
|
+
fn: (...args: Array<unknown>) => unknown,
|
|
102
|
+
...args: Array<unknown>
|
|
103
|
+
): boolean => {
|
|
104
|
+
try {
|
|
105
|
+
return !!fn(...args);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const maybeWithAuthCredentials = (url: string): boolean => {
|
|
113
|
+
const protocolIndex = url.indexOf("://");
|
|
114
|
+
const urlToCheck = protocolIndex !== -1 ? url.slice(protocolIndex + 3) : url;
|
|
115
|
+
// Only `@` indicates credentials in the authority; `:` alone is a port separator.
|
|
116
|
+
return urlToCheck.includes("@");
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// eslint-disable-next-line sonarjs/function-return-type
|
|
120
|
+
const factory = (env: Record<string, unknown>) => {
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
122
|
+
const globalObject: Record<string, unknown> = utils.global ?? globalThis;
|
|
123
|
+
const ReadableStream = globalObject["ReadableStream"] as
|
|
124
|
+
| (AnyConstructor & { prototype: AnyReadableStream; })
|
|
125
|
+
| undefined;
|
|
126
|
+
const TextEncoder = globalObject["TextEncoder"] as
|
|
127
|
+
| (new () => AnyTextEncoder)
|
|
128
|
+
| undefined;
|
|
129
|
+
|
|
130
|
+
env = utils.merge.call(
|
|
131
|
+
{
|
|
132
|
+
skipUndefined: true,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
Request: globalObject["Request"],
|
|
136
|
+
Response: globalObject["Response"],
|
|
137
|
+
},
|
|
138
|
+
env
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const {
|
|
142
|
+
fetch: envFetch,
|
|
143
|
+
Request,
|
|
144
|
+
Response,
|
|
145
|
+
} = env as {
|
|
146
|
+
fetch?: FetchFn;
|
|
147
|
+
Request?: AnyConstructor;
|
|
148
|
+
Response?: AnyConstructor;
|
|
149
|
+
};
|
|
150
|
+
const _globalFetch = (globalThis as unknown as Record<string, unknown>)[
|
|
151
|
+
"fetch"
|
|
152
|
+
];
|
|
153
|
+
const isFetchSupported = envFetch
|
|
154
|
+
? isFunction(envFetch)
|
|
155
|
+
: isFunction(_globalFetch);
|
|
156
|
+
const isRequestSupported = isFunction(Request);
|
|
157
|
+
const isResponseSupported = isFunction(Response);
|
|
158
|
+
|
|
159
|
+
if (!isFetchSupported) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const isReadableStreamSupported = isFunction(ReadableStream);
|
|
164
|
+
|
|
165
|
+
const encodeText: (str: string) => Promise<Uint8Array> | Uint8Array =
|
|
166
|
+
typeof TextEncoder === "function"
|
|
167
|
+
? (
|
|
168
|
+
(encoder: AnyTextEncoder) => (str: string) =>
|
|
169
|
+
encoder.encode(str)
|
|
170
|
+
)(new TextEncoder())
|
|
171
|
+
: async (str: string) =>
|
|
172
|
+
new Uint8Array(
|
|
173
|
+
await (
|
|
174
|
+
new (Request as AnyConstructor)(str) as AnyRequest
|
|
175
|
+
).arrayBuffer()
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// A Node stream body reaches fetch twice: once in `new Request(url, init)` and again in
|
|
179
|
+
// `fetch(request, init)`. undici (Node 26.11+) starts reading an async iterable in both,
|
|
180
|
+
// so the first chunk goes to the discarded copy. One web stream is read only once.
|
|
181
|
+
// Web stream bodies must yield bytes, so string chunks (e.g. `Readable.from(["a"])`) are encoded.
|
|
182
|
+
const from = (ReadableStream as { from?: (source: unknown) => unknown; } | undefined)?.from;
|
|
183
|
+
const streamFrom = typeof from === "function" ? from.bind(ReadableStream) : undefined;
|
|
184
|
+
async function* toByteChunks(source: AsyncIterable<unknown>) {
|
|
185
|
+
for await (const chunk of source) yield typeof chunk === "string" ? await encodeText(chunk) : chunk;
|
|
186
|
+
}
|
|
187
|
+
const toWebStream = (body: unknown): unknown =>
|
|
188
|
+
(streamFrom && utils.isStream(body) && !utils.isFormData(body)
|
|
189
|
+
? streamFrom(toByteChunks(body as AsyncIterable<unknown>))
|
|
190
|
+
: body);
|
|
191
|
+
|
|
192
|
+
const supportsRequestStream =
|
|
193
|
+
isRequestSupported &&
|
|
194
|
+
isReadableStreamSupported &&
|
|
195
|
+
test(() => {
|
|
196
|
+
let duplexAccessed = false;
|
|
197
|
+
|
|
198
|
+
const request = new (Request as AnyConstructor)(platform.origin, {
|
|
199
|
+
body: new (ReadableStream as AnyConstructor)(),
|
|
200
|
+
method: "POST",
|
|
201
|
+
get duplex() {
|
|
202
|
+
duplexAccessed = true;
|
|
203
|
+
return "half";
|
|
204
|
+
},
|
|
205
|
+
}) as AnyRequest;
|
|
206
|
+
|
|
207
|
+
const hasContentType = request.headers.has("Content-Type");
|
|
208
|
+
|
|
209
|
+
if (request.body != null) {
|
|
210
|
+
request.body.cancel().catch(() => {});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
214
|
+
return duplexAccessed && !hasContentType;
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const supportsResponseStream =
|
|
218
|
+
isResponseSupported &&
|
|
219
|
+
isReadableStreamSupported &&
|
|
220
|
+
test(() =>
|
|
221
|
+
utils.isReadableStream!(
|
|
222
|
+
(new (Response as AnyConstructor)("") as AnyResponse).body
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const resolvers: Record<
|
|
227
|
+
string,
|
|
228
|
+
((res: AnyResponse, config?: unknown) => unknown) | false
|
|
229
|
+
> = {
|
|
230
|
+
stream: supportsResponseStream && ((res: AnyResponse) => res.body),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
234
|
+
isFetchSupported &&
|
|
235
|
+
(() => {
|
|
236
|
+
[ "text", "arrayBuffer", "blob", "formData", "stream" ].forEach(type => {
|
|
237
|
+
!resolvers[type] &&
|
|
238
|
+
(resolvers[type] = (res: AnyResponse, config?: unknown) => {
|
|
239
|
+
const method = (res as Record<string, unknown>)[type];
|
|
240
|
+
|
|
241
|
+
if (method) {
|
|
242
|
+
return (method as (this: AnyResponse) => unknown).call(res);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
throw new FaxiosError(
|
|
246
|
+
`Response type '${type}' is not supported`,
|
|
247
|
+
FaxiosError.ERR_NOT_SUPPORT,
|
|
248
|
+
config as InternalFaxiosRequestConfig
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
})();
|
|
253
|
+
|
|
254
|
+
const getBodyLength = async (body: unknown): Promise<number | undefined> => {
|
|
255
|
+
if (body == null) {
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (utils.isBlob(body)) {
|
|
260
|
+
return (body as { size: number; }).size;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (utils.isSpecCompliantForm(body)) {
|
|
264
|
+
const _request = new (Request as AnyConstructor)(platform.origin, {
|
|
265
|
+
method: "POST",
|
|
266
|
+
body,
|
|
267
|
+
}) as AnyRequest;
|
|
268
|
+
return (await _request.arrayBuffer()).byteLength;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
|
|
272
|
+
return (body as ArrayBufferView | ArrayBuffer).byteLength;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (utils.isURLSearchParams(body)) {
|
|
276
|
+
body = body + "";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (utils.isString(body)) {
|
|
280
|
+
return (await encodeText(body as string)).byteLength;
|
|
281
|
+
}
|
|
282
|
+
return undefined;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const resolveBodyLength = async (
|
|
286
|
+
headers: FaxiosRequestHeaders,
|
|
287
|
+
body: unknown
|
|
288
|
+
): Promise<number | undefined> => {
|
|
289
|
+
const length = utils.toFiniteNumber(headers.getContentLength());
|
|
290
|
+
|
|
291
|
+
return length == null ? getBodyLength(body) : length;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const normalizeCredentials = (wc: string | boolean): string => {
|
|
295
|
+
if (utils.isString(wc)) return wc as string;
|
|
296
|
+
return wc ? "include" : "omit";
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// HTTP basic authentication: extract auth from config or URL, set Authorization header,
|
|
300
|
+
// strip credentials from URL. Returns the (possibly updated) URL.
|
|
301
|
+
const applyAuthToRequest = (
|
|
302
|
+
url: string,
|
|
303
|
+
headers: FaxiosRequestHeaders,
|
|
304
|
+
own: (key: string) => unknown
|
|
305
|
+
): string => {
|
|
306
|
+
let auth: { username: unknown; password: unknown; } | undefined;
|
|
307
|
+
const configAuth = own("auth");
|
|
308
|
+
if (configAuth) {
|
|
309
|
+
auth = {
|
|
310
|
+
username: utils.getSafeProp(configAuth, "username") || "",
|
|
311
|
+
password: utils.getSafeProp(configAuth, "password") || "",
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (maybeWithAuthCredentials(url)) {
|
|
315
|
+
const parsedURL = new URL(url, platform.origin);
|
|
316
|
+
if (!auth && (parsedURL.username || parsedURL.password)) {
|
|
317
|
+
auth = {
|
|
318
|
+
username: decodeURIComponentSafe(parsedURL.username),
|
|
319
|
+
password: decodeURIComponentSafe(parsedURL.password),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (parsedURL.username || parsedURL.password) {
|
|
323
|
+
parsedURL.username = "";
|
|
324
|
+
parsedURL.password = "";
|
|
325
|
+
url = parsedURL.href;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (auth) {
|
|
329
|
+
headers.delete("authorization");
|
|
330
|
+
headers.set(
|
|
331
|
+
"Authorization",
|
|
332
|
+
"Basic " +
|
|
333
|
+
_btoa(
|
|
334
|
+
encodeUTF8(
|
|
335
|
+
String(auth.username || "") + ":" + String(auth.password || "")
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return url;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// Pre-flight size checks: data: URL content-length and known-size body maxBodyLength.
|
|
344
|
+
// Returns requestContentLength if computed, else undefined.
|
|
345
|
+
const checkPreflightLimits = async (
|
|
346
|
+
url: string,
|
|
347
|
+
data: unknown,
|
|
348
|
+
method: string,
|
|
349
|
+
hasMaxContentLength: boolean,
|
|
350
|
+
maxContentLength: number,
|
|
351
|
+
hasMaxBodyLength: boolean,
|
|
352
|
+
maxBodyLength: number,
|
|
353
|
+
config: InternalFaxiosRequestConfig,
|
|
354
|
+
request: unknown
|
|
355
|
+
): Promise<number | undefined> => {
|
|
356
|
+
// Enforce maxContentLength for data: URLs up-front so we never materialize
|
|
357
|
+
// an oversized payload. The HTTP adapter applies the same check.
|
|
358
|
+
if (
|
|
359
|
+
hasMaxContentLength &&
|
|
360
|
+
typeof url === "string" &&
|
|
361
|
+
url.startsWith("data:")
|
|
362
|
+
) {
|
|
363
|
+
const estimated = estimateDataURLDecodedBytes(url);
|
|
364
|
+
if (estimated > maxContentLength) {
|
|
365
|
+
throw new FaxiosError(
|
|
366
|
+
"maxContentLength size of " + maxContentLength + " exceeded",
|
|
367
|
+
FaxiosError.ERR_BAD_RESPONSE,
|
|
368
|
+
config,
|
|
369
|
+
request
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Enforce maxBodyLength against known-size bodies before dispatch using
|
|
374
|
+
// the body's actual size — never a caller-declared Content-Length.
|
|
375
|
+
if (hasMaxBodyLength && method !== "get" && method !== "head") {
|
|
376
|
+
const outboundLength = await getBodyLength(data);
|
|
377
|
+
if (typeof outboundLength === "number" && isFinite(outboundLength)) {
|
|
378
|
+
if (outboundLength > maxBodyLength) {
|
|
379
|
+
throw new FaxiosError(
|
|
380
|
+
"Request body larger than maxBodyLength limit",
|
|
381
|
+
FaxiosError.ERR_BAD_REQUEST,
|
|
382
|
+
config,
|
|
383
|
+
request
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return outboundLength;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return undefined;
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// Wrap a Request body with upload progress tracking and optional maxBodyLength enforcement.
|
|
393
|
+
const applyUploadProgress = (
|
|
394
|
+
_request: AnyRequest,
|
|
395
|
+
data: unknown,
|
|
396
|
+
headers: FaxiosRequestHeaders,
|
|
397
|
+
requestContentLength: number | undefined,
|
|
398
|
+
onUploadProgress: unknown,
|
|
399
|
+
_trackStream: (stream: unknown, onProgress?: (bytes: number) => void, flush?: () => void) => unknown
|
|
400
|
+
): unknown => {
|
|
401
|
+
let contentTypeHeader: string | null;
|
|
402
|
+
if (
|
|
403
|
+
utils.isFormData(data) &&
|
|
404
|
+
(contentTypeHeader = _request.headers.get("content-type"))
|
|
405
|
+
) {
|
|
406
|
+
headers.setContentType(contentTypeHeader);
|
|
407
|
+
}
|
|
408
|
+
if (_request.body) {
|
|
409
|
+
const [ onProgress, flush ] = onUploadProgress
|
|
410
|
+
? progressEventDecorator(
|
|
411
|
+
requestContentLength,
|
|
412
|
+
progressEventReducer(
|
|
413
|
+
asyncDecorator(
|
|
414
|
+
onUploadProgress as (...args: Array<unknown>) => unknown
|
|
415
|
+
),
|
|
416
|
+
false
|
|
417
|
+
)
|
|
418
|
+
)
|
|
419
|
+
: [];
|
|
420
|
+
return _trackStream(_request.body, onProgress, flush);
|
|
421
|
+
}
|
|
422
|
+
return data;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// Set up request body streaming with upload progress and/or maxBodyLength enforcement.
|
|
426
|
+
const buildUploadStream = async (
|
|
427
|
+
data: unknown,
|
|
428
|
+
url: string,
|
|
429
|
+
method: string,
|
|
430
|
+
headers: FaxiosRequestHeaders,
|
|
431
|
+
onUploadProgress: unknown,
|
|
432
|
+
mustEnforceStreamBody: boolean,
|
|
433
|
+
requestContentLength: number | undefined,
|
|
434
|
+
hasMaxBodyLength: boolean,
|
|
435
|
+
maxBodyLength: number,
|
|
436
|
+
config: InternalFaxiosRequestConfig,
|
|
437
|
+
request: unknown,
|
|
438
|
+
pendingBodyErrorRef: PendingBodyErrorRef
|
|
439
|
+
): Promise<{ data: unknown; requestContentLength: number | undefined; }> => {
|
|
440
|
+
const _makeBodyLengthError = () =>
|
|
441
|
+
new FaxiosError(
|
|
442
|
+
"Request body larger than maxBodyLength limit",
|
|
443
|
+
FaxiosError.ERR_BAD_REQUEST,
|
|
444
|
+
config,
|
|
445
|
+
request
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const _trackStream = (
|
|
449
|
+
stream: unknown,
|
|
450
|
+
onProgress?: (bytes: number) => void,
|
|
451
|
+
flush?: () => void
|
|
452
|
+
) =>
|
|
453
|
+
trackStream(
|
|
454
|
+
stream,
|
|
455
|
+
DEFAULT_CHUNK_SIZE,
|
|
456
|
+
(loadedBytes: number) => {
|
|
457
|
+
if (hasMaxBodyLength && loadedBytes > maxBodyLength) {
|
|
458
|
+
pendingBodyErrorRef.value = _makeBodyLengthError();
|
|
459
|
+
throw pendingBodyErrorRef.value;
|
|
460
|
+
}
|
|
461
|
+
onProgress && onProgress(loadedBytes);
|
|
462
|
+
},
|
|
463
|
+
flush
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
if (
|
|
467
|
+
supportsRequestStream &&
|
|
468
|
+
method !== "get" &&
|
|
469
|
+
method !== "head" &&
|
|
470
|
+
(onUploadProgress || mustEnforceStreamBody)
|
|
471
|
+
) {
|
|
472
|
+
requestContentLength =
|
|
473
|
+
requestContentLength == null
|
|
474
|
+
? await resolveBodyLength(headers, data)
|
|
475
|
+
: requestContentLength;
|
|
476
|
+
|
|
477
|
+
// A declared length of 0 is only trusted to skip the wrap when we are
|
|
478
|
+
// not enforcing a stream limit (which must not rely on that header).
|
|
479
|
+
if (requestContentLength !== 0 || mustEnforceStreamBody) {
|
|
480
|
+
const _request = new (Request as AnyConstructor)(url, {
|
|
481
|
+
method: "POST",
|
|
482
|
+
body: data,
|
|
483
|
+
duplex: "half",
|
|
484
|
+
}) as AnyRequest;
|
|
485
|
+
data = applyUploadProgress(_request, data, headers, requestContentLength, onUploadProgress, _trackStream);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
else if (
|
|
489
|
+
mustEnforceStreamBody &&
|
|
490
|
+
!isRequestSupported &&
|
|
491
|
+
isReadableStreamSupported &&
|
|
492
|
+
method !== "get" &&
|
|
493
|
+
method !== "head"
|
|
494
|
+
) {
|
|
495
|
+
data = _trackStream(data);
|
|
496
|
+
}
|
|
497
|
+
else if (
|
|
498
|
+
mustEnforceStreamBody &&
|
|
499
|
+
isRequestSupported &&
|
|
500
|
+
!supportsRequestStream &&
|
|
501
|
+
method !== "get" &&
|
|
502
|
+
method !== "head"
|
|
503
|
+
) {
|
|
504
|
+
throw new FaxiosError(
|
|
505
|
+
"Stream request bodies are not supported by the current fetch implementation",
|
|
506
|
+
FaxiosError.ERR_NOT_SUPPORT,
|
|
507
|
+
config,
|
|
508
|
+
request
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return { data, requestContentLength };
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// Wrap the response body stream with download progress tracking and maxContentLength enforcement.
|
|
516
|
+
const buildDownloadStream = (
|
|
517
|
+
response: AnyResponse,
|
|
518
|
+
responseHeaders: FaxiosHeaders,
|
|
519
|
+
onDownloadProgress: unknown,
|
|
520
|
+
hasMaxContentLength: boolean,
|
|
521
|
+
maxContentLength: number,
|
|
522
|
+
isStreamResponse: boolean,
|
|
523
|
+
unsubscribe: (() => void) | false | undefined,
|
|
524
|
+
config: InternalFaxiosRequestConfig,
|
|
525
|
+
request: unknown
|
|
526
|
+
): AnyResponse => {
|
|
527
|
+
const needsWrapping = onDownloadProgress || hasMaxContentLength || (isStreamResponse && unsubscribe);
|
|
528
|
+
if (!supportsResponseStream || !response.body || !needsWrapping) {
|
|
529
|
+
return response;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const options: Record<string, unknown> = {};
|
|
533
|
+
[ "status", "statusText", "headers" ].forEach(prop => {
|
|
534
|
+
options[prop] = response[prop];
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
const responseContentLength = utils.toFiniteNumber(
|
|
538
|
+
(responseHeaders.getContentLength as () => unknown)()
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
const [ onProgress, flush ] = onDownloadProgress
|
|
542
|
+
? progressEventDecorator(
|
|
543
|
+
responseContentLength,
|
|
544
|
+
progressEventReducer(
|
|
545
|
+
asyncDecorator(
|
|
546
|
+
onDownloadProgress as (...args: Array<unknown>) => unknown
|
|
547
|
+
),
|
|
548
|
+
true
|
|
549
|
+
)
|
|
550
|
+
)
|
|
551
|
+
: [];
|
|
552
|
+
|
|
553
|
+
let bytesRead = 0;
|
|
554
|
+
const onChunkProgress = (loadedBytes: number): void => {
|
|
555
|
+
if (hasMaxContentLength) {
|
|
556
|
+
bytesRead = loadedBytes;
|
|
557
|
+
if (bytesRead > maxContentLength) {
|
|
558
|
+
throw new FaxiosError(
|
|
559
|
+
"maxContentLength size of " + maxContentLength + " exceeded",
|
|
560
|
+
FaxiosError.ERR_BAD_RESPONSE,
|
|
561
|
+
config,
|
|
562
|
+
request
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
onProgress && onProgress(loadedBytes);
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
return new (Response as AnyConstructor)(
|
|
570
|
+
trackStream(
|
|
571
|
+
response.body,
|
|
572
|
+
DEFAULT_CHUNK_SIZE,
|
|
573
|
+
onChunkProgress,
|
|
574
|
+
() => {
|
|
575
|
+
flush && flush();
|
|
576
|
+
unsubscribe && unsubscribe();
|
|
577
|
+
}
|
|
578
|
+
),
|
|
579
|
+
options
|
|
580
|
+
) as AnyResponse;
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const unsubscribeOnComplete = (isStreamResponse: boolean, unsubscribe: (() => void) | undefined) => {
|
|
584
|
+
if (!isStreamResponse) unsubscribe?.();
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
return async (config: InternalFaxiosRequestConfig) => {
|
|
588
|
+
const _resolved = prepareRequest(config) as InternalFaxiosRequestConfig & {
|
|
589
|
+
fetchOptions?: Record<string, unknown>;
|
|
590
|
+
withCredentials?: string | boolean;
|
|
591
|
+
};
|
|
592
|
+
let {
|
|
593
|
+
url,
|
|
594
|
+
method,
|
|
595
|
+
data,
|
|
596
|
+
signal,
|
|
597
|
+
timeout,
|
|
598
|
+
timeoutErrorMessage,
|
|
599
|
+
onDownloadProgress,
|
|
600
|
+
onUploadProgress,
|
|
601
|
+
responseType,
|
|
602
|
+
fetchOptions,
|
|
603
|
+
maxContentLength,
|
|
604
|
+
maxBodyLength,
|
|
605
|
+
} = _resolved;
|
|
606
|
+
let headers = _resolved.headers;
|
|
607
|
+
let withCredentials: string | boolean =
|
|
608
|
+
_resolved.withCredentials ?? "same-origin";
|
|
609
|
+
|
|
610
|
+
const hasMaxContentLength =
|
|
611
|
+
utils.isNumber(maxContentLength) && maxContentLength! > -1;
|
|
612
|
+
const hasMaxBodyLength =
|
|
613
|
+
utils.isNumber(maxBodyLength) && maxBodyLength! > -1;
|
|
614
|
+
const own = (key: string): unknown =>
|
|
615
|
+
utils.hasOwnProp(config, key)
|
|
616
|
+
? (config as unknown as Record<string, unknown>)[key]
|
|
617
|
+
: undefined;
|
|
618
|
+
|
|
619
|
+
const _fetch: FetchFn = (envFetch || (globalThis as unknown as Record<string, unknown>)["fetch"]) as FetchFn;
|
|
620
|
+
|
|
621
|
+
if (timeout != null && !Number.isFinite(Number(timeout))) {
|
|
622
|
+
throw new FaxiosError(
|
|
623
|
+
"error trying to parse `config.timeout` to int",
|
|
624
|
+
FaxiosError.ERR_BAD_OPTION_VALUE,
|
|
625
|
+
config
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
responseType = (
|
|
630
|
+
responseType ? (responseType + "").toLowerCase() : "text"
|
|
631
|
+
) as typeof responseType;
|
|
632
|
+
|
|
633
|
+
const composedSignal = composeSignals(
|
|
634
|
+
[ signal ],
|
|
635
|
+
timeout,
|
|
636
|
+
timeoutErrorMessage
|
|
637
|
+
) as ComposedSignal | undefined;
|
|
638
|
+
|
|
639
|
+
const unsubscribe = composedSignal?.unsubscribe?.bind(composedSignal);
|
|
640
|
+
|
|
641
|
+
let request: Request | null = null;
|
|
642
|
+
const pendingBodyErrorRef: PendingBodyErrorRef = { value: null };
|
|
643
|
+
|
|
644
|
+
try {
|
|
645
|
+
url = applyAuthToRequest(url!, headers, own);
|
|
646
|
+
|
|
647
|
+
const preflightContentLength = await checkPreflightLimits(
|
|
648
|
+
url,
|
|
649
|
+
data,
|
|
650
|
+
method!,
|
|
651
|
+
hasMaxContentLength,
|
|
652
|
+
maxContentLength!,
|
|
653
|
+
hasMaxBodyLength,
|
|
654
|
+
maxBodyLength!,
|
|
655
|
+
config,
|
|
656
|
+
request
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
// A streamed body under maxBodyLength must be counted as fetch consumes
|
|
660
|
+
// it; its size is never trusted from a caller-declared Content-Length.
|
|
661
|
+
const isStreamLike = utils.isReadableStream!(data) || utils.isStream(data);
|
|
662
|
+
const mustEnforceStreamBody = hasMaxBodyLength && isStreamLike;
|
|
663
|
+
|
|
664
|
+
({ data } = await buildUploadStream(
|
|
665
|
+
data,
|
|
666
|
+
url,
|
|
667
|
+
method!,
|
|
668
|
+
headers,
|
|
669
|
+
onUploadProgress,
|
|
670
|
+
mustEnforceStreamBody,
|
|
671
|
+
preflightContentLength,
|
|
672
|
+
hasMaxBodyLength,
|
|
673
|
+
maxBodyLength!,
|
|
674
|
+
config,
|
|
675
|
+
request,
|
|
676
|
+
pendingBodyErrorRef
|
|
677
|
+
));
|
|
678
|
+
|
|
679
|
+
withCredentials = normalizeCredentials(withCredentials);
|
|
680
|
+
|
|
681
|
+
// Cloudflare Workers throws when credentials are defined
|
|
682
|
+
// see https://github.com/cloudflare/workerd/issues/902
|
|
683
|
+
const isCredentialsSupported =
|
|
684
|
+
isRequestSupported &&
|
|
685
|
+
Request != null &&
|
|
686
|
+
"credentials" in (Request.prototype as object);
|
|
687
|
+
|
|
688
|
+
cleanFormDataContentType(data, headers);
|
|
689
|
+
|
|
690
|
+
applyDefaultUserAgent(headers);
|
|
691
|
+
|
|
692
|
+
const serializedHeaders = toByteStringHeaderObject(headers.normalize(false));
|
|
693
|
+
|
|
694
|
+
// Browsers auto-add "text/plain;charset=UTF-8" for string bodies when no Content-Type is set.
|
|
695
|
+
// Encoding as Uint8Array prevents the browser from injecting a content-type.
|
|
696
|
+
// Case-insensitive check: toByteStringHeaderObject may normalize to lowercase.
|
|
697
|
+
data = toWebStream(await encodeBodyIfNeeded(data, serializedHeaders, encodeText));
|
|
698
|
+
|
|
699
|
+
const resolvedOptions = {
|
|
700
|
+
...fetchOptions,
|
|
701
|
+
signal: composedSignal,
|
|
702
|
+
method: (method as string).toUpperCase(),
|
|
703
|
+
headers: serializedHeaders,
|
|
704
|
+
body: data,
|
|
705
|
+
duplex: "half",
|
|
706
|
+
credentials: isCredentialsSupported ? withCredentials : undefined,
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
request = isRequestSupported
|
|
710
|
+
? new (Request as AnyConstructor)(url, resolvedOptions) as Request
|
|
711
|
+
: null;
|
|
712
|
+
|
|
713
|
+
// Pass resolvedOptions (all own-properties) as the fetch init even when a
|
|
714
|
+
// Request object is supplied: undici re-reads RequestInit fields (method,
|
|
715
|
+
// headers, body, credentials) off Object.prototype when init is absent,
|
|
716
|
+
// so a polluted Object.prototype.method would override the Request's verb.
|
|
717
|
+
// An explicit own-property init neutralizes every such gadget.
|
|
718
|
+
let response = await (isRequestSupported
|
|
719
|
+
? _fetch(request, resolvedOptions)
|
|
720
|
+
: _fetch(url, resolvedOptions));
|
|
721
|
+
|
|
722
|
+
const responseHeaders = FaxiosHeaders.from(response.headers);
|
|
723
|
+
|
|
724
|
+
checkDeclaredContentLength(responseHeaders, hasMaxContentLength, maxContentLength!, config, request);
|
|
725
|
+
|
|
726
|
+
const isStreamResponse =
|
|
727
|
+
supportsResponseStream &&
|
|
728
|
+
(responseType === "stream" || responseType === "response");
|
|
729
|
+
|
|
730
|
+
response = buildDownloadStream(
|
|
731
|
+
response,
|
|
732
|
+
responseHeaders,
|
|
733
|
+
onDownloadProgress,
|
|
734
|
+
hasMaxContentLength,
|
|
735
|
+
maxContentLength!,
|
|
736
|
+
isStreamResponse,
|
|
737
|
+
unsubscribe,
|
|
738
|
+
config,
|
|
739
|
+
request
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
responseType = responseType || "text";
|
|
743
|
+
|
|
744
|
+
const resolverKey = utils.findKey(resolvers, responseType) || "text";
|
|
745
|
+
const resolver = resolvers[resolverKey];
|
|
746
|
+
const responseData = await (resolver && resolver(response, config));
|
|
747
|
+
|
|
748
|
+
checkMaterializedSize(
|
|
749
|
+
responseData,
|
|
750
|
+
hasMaxContentLength,
|
|
751
|
+
maxContentLength!,
|
|
752
|
+
isStreamResponse,
|
|
753
|
+
supportsResponseStream,
|
|
754
|
+
config,
|
|
755
|
+
request
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
unsubscribeOnComplete(isStreamResponse, unsubscribe);
|
|
759
|
+
|
|
760
|
+
return await new Promise((resolve, reject) => {
|
|
761
|
+
settle(resolve, reject, {
|
|
762
|
+
data: responseData,
|
|
763
|
+
headers: FaxiosHeaders.from(response.headers),
|
|
764
|
+
status: response.status,
|
|
765
|
+
statusText: response.statusText,
|
|
766
|
+
config,
|
|
767
|
+
request,
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
catch (err) {
|
|
772
|
+
unsubscribe && unsubscribe();
|
|
773
|
+
throw handleFetchCaughtError(err, composedSignal, pendingBodyErrorRef, config, request);
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
const seedCache = new Map<unknown, unknown>();
|
|
779
|
+
|
|
780
|
+
export const getFetch = (config?: { env?: Record<string, unknown>; }) => {
|
|
781
|
+
const env: Record<string, unknown> = (config && config.env) || {};
|
|
782
|
+
const { fetch, Request, Response } = env as {
|
|
783
|
+
fetch?: unknown;
|
|
784
|
+
Request?: unknown;
|
|
785
|
+
Response?: unknown;
|
|
786
|
+
};
|
|
787
|
+
const seeds: Array<unknown> = [ Request, Response, fetch ];
|
|
788
|
+
|
|
789
|
+
const len = seeds.length;
|
|
790
|
+
let i = len;
|
|
791
|
+
let seed: unknown;
|
|
792
|
+
let target: unknown;
|
|
793
|
+
let map: Map<unknown, unknown> = seedCache;
|
|
794
|
+
|
|
795
|
+
while (i--) {
|
|
796
|
+
seed = seeds[i];
|
|
797
|
+
target = map.get(seed);
|
|
798
|
+
|
|
799
|
+
if (target === undefined) {
|
|
800
|
+
target = i ? new Map<unknown, unknown>() : factory(env);
|
|
801
|
+
map.set(seed, target);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
map = target as Map<unknown, unknown>;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return target;
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
getFetch();
|