rezo 1.0.0
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 +202 -0
- package/README.md +1507 -0
- package/assets/icon.svg +37 -0
- package/assets/logo-dark.svg +47 -0
- package/assets/logo.svg +58 -0
- package/dist/adapters/curl.cjs +1034 -0
- package/dist/adapters/curl.js +1031 -0
- package/dist/adapters/entries/curl.cjs +4 -0
- package/dist/adapters/entries/curl.d.ts +2136 -0
- package/dist/adapters/entries/curl.js +2 -0
- package/dist/adapters/entries/fetch.cjs +2 -0
- package/dist/adapters/entries/fetch.d.ts +2127 -0
- package/dist/adapters/entries/fetch.js +1 -0
- package/dist/adapters/entries/http.cjs +2 -0
- package/dist/adapters/entries/http.d.ts +2126 -0
- package/dist/adapters/entries/http.js +1 -0
- package/dist/adapters/entries/http2.cjs +4 -0
- package/dist/adapters/entries/http2.d.ts +2136 -0
- package/dist/adapters/entries/http2.js +2 -0
- package/dist/adapters/entries/react-native.cjs +2 -0
- package/dist/adapters/entries/react-native.d.ts +2126 -0
- package/dist/adapters/entries/react-native.js +1 -0
- package/dist/adapters/entries/xhr.cjs +2 -0
- package/dist/adapters/entries/xhr.d.ts +2127 -0
- package/dist/adapters/entries/xhr.js +1 -0
- package/dist/adapters/fetch.cjs +740 -0
- package/dist/adapters/fetch.js +739 -0
- package/dist/adapters/http.cjs +1153 -0
- package/dist/adapters/http.js +1151 -0
- package/dist/adapters/http2.cjs +957 -0
- package/dist/adapters/http2.js +956 -0
- package/dist/adapters/index.cjs +6 -0
- package/dist/adapters/index.js +7 -0
- package/dist/adapters/picker.cjs +342 -0
- package/dist/adapters/picker.js +331 -0
- package/dist/adapters/react-native.cjs +545 -0
- package/dist/adapters/react-native.js +544 -0
- package/dist/adapters/xhr.cjs +622 -0
- package/dist/adapters/xhr.js +621 -0
- package/dist/cache/dns-cache.cjs +118 -0
- package/dist/cache/dns-cache.js +113 -0
- package/dist/cache/file-cacher.cjs +264 -0
- package/dist/cache/file-cacher.js +261 -0
- package/dist/cache/index.cjs +13 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/lru-cache.cjs +96 -0
- package/dist/cache/lru-cache.js +93 -0
- package/dist/cache/response-cache.cjs +314 -0
- package/dist/cache/response-cache.js +310 -0
- package/dist/cache/url-store.cjs +288 -0
- package/dist/cache/url-store.js +285 -0
- package/dist/core/hooks.cjs +133 -0
- package/dist/core/hooks.js +120 -0
- package/dist/core/rezo.cjs +464 -0
- package/dist/core/rezo.js +458 -0
- package/dist/crawler.d.ts +6255 -0
- package/dist/dom/index.cjs +1 -0
- package/dist/dom/index.d.ts +23 -0
- package/dist/dom/index.js +1 -0
- package/dist/entries/crawler.cjs +5 -0
- package/dist/entries/crawler.js +2 -0
- package/dist/errors/rezo-error.cjs +722 -0
- package/dist/errors/rezo-error.js +716 -0
- package/dist/index.cjs +34 -0
- package/dist/index.d.ts +3335 -0
- package/dist/index.js +26 -0
- package/dist/platform/browser.cjs +9 -0
- package/dist/platform/browser.d.ts +3203 -0
- package/dist/platform/browser.js +7 -0
- package/dist/platform/bun.cjs +9 -0
- package/dist/platform/bun.d.ts +3203 -0
- package/dist/platform/bun.js +7 -0
- package/dist/platform/deno.cjs +9 -0
- package/dist/platform/deno.d.ts +3203 -0
- package/dist/platform/deno.js +7 -0
- package/dist/platform/node.cjs +9 -0
- package/dist/platform/node.d.ts +3203 -0
- package/dist/platform/node.js +7 -0
- package/dist/platform/react-native.cjs +9 -0
- package/dist/platform/react-native.d.ts +3203 -0
- package/dist/platform/react-native.js +7 -0
- package/dist/platform/worker.cjs +9 -0
- package/dist/platform/worker.d.ts +3203 -0
- package/dist/platform/worker.js +7 -0
- package/dist/plugin/addon/decodo/index.cjs +1 -0
- package/dist/plugin/addon/decodo/index.js +1 -0
- package/dist/plugin/addon/decodo/options.cjs +1 -0
- package/dist/plugin/addon/decodo/options.js +1 -0
- package/dist/plugin/addon/oxylabs/index.cjs +1 -0
- package/dist/plugin/addon/oxylabs/index.js +1 -0
- package/dist/plugin/addon/oxylabs/options.cjs +1 -0
- package/dist/plugin/addon/oxylabs/options.js +1 -0
- package/dist/plugin/crawler-options.cjs +1 -0
- package/dist/plugin/crawler-options.js +1 -0
- package/dist/plugin/crawler.cjs +519 -0
- package/dist/plugin/crawler.js +517 -0
- package/dist/plugin/index.cjs +36 -0
- package/dist/plugin/index.js +32 -0
- package/dist/proxy/index.cjs +142 -0
- package/dist/proxy/index.js +139 -0
- package/dist/responses/buildError.cjs +452 -0
- package/dist/responses/buildError.js +441 -0
- package/dist/responses/buildResponse.cjs +365 -0
- package/dist/responses/buildResponse.js +361 -0
- package/dist/responses/download.cjs +54 -0
- package/dist/responses/download.js +52 -0
- package/dist/responses/stream.cjs +60 -0
- package/dist/responses/stream.js +58 -0
- package/dist/responses/upload.cjs +54 -0
- package/dist/responses/upload.js +52 -0
- package/dist/types/cookies.cjs +394 -0
- package/dist/types/cookies.js +391 -0
- package/dist/types/download.cjs +10 -0
- package/dist/types/download.js +10 -0
- package/dist/types/rezo-request.cjs +131 -0
- package/dist/types/rezo-request.js +131 -0
- package/dist/utils/agent-merger.cjs +111 -0
- package/dist/utils/agent-merger.js +108 -0
- package/dist/utils/compression.cjs +84 -0
- package/dist/utils/compression.js +82 -0
- package/dist/utils/cookies.cjs +514 -0
- package/dist/utils/cookies.js +511 -0
- package/dist/utils/data-operations.cjs +75 -0
- package/dist/utils/data-operations.js +73 -0
- package/dist/utils/form-data.cjs +164 -0
- package/dist/utils/form-data.js +161 -0
- package/dist/utils/headers.cjs +162 -0
- package/dist/utils/headers.js +161 -0
- package/dist/utils/http-config.cjs +723 -0
- package/dist/utils/http-config.js +718 -0
- package/dist/utils/index.cjs +8 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/tools.cjs +18 -0
- package/dist/utils/tools.js +15 -0
- package/package.json +172 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import { RezoError } from '../errors/rezo-error.js';
|
|
2
|
+
import { buildSmartError, builErrorFromResponse } from '../responses/buildError.js';
|
|
3
|
+
import RezoFormData from '../utils/form-data.js';
|
|
4
|
+
import { getDefaultConfig, prepareHTTPOptions } from '../utils/http-config.js';
|
|
5
|
+
import { RezoHeaders } from '../utils/headers.js';
|
|
6
|
+
import { RezoURLSearchParams } from '../utils/data-operations.js';
|
|
7
|
+
import { StreamResponse } from '../responses/stream.js';
|
|
8
|
+
import { DownloadResponse } from '../responses/download.js';
|
|
9
|
+
import { UploadResponse } from '../responses/upload.js';
|
|
10
|
+
import { RezoPerformance } from '../utils/tools.js';
|
|
11
|
+
import { ResponseCache } from '../cache/response-cache.js';
|
|
12
|
+
const Environment = {
|
|
13
|
+
isReactNative: typeof navigator !== "undefined" && navigator.product === "ReactNative",
|
|
14
|
+
isExpo: typeof globalThis.expo !== "undefined",
|
|
15
|
+
hasFetch: typeof fetch !== "undefined",
|
|
16
|
+
hasBlob: typeof Blob !== "undefined",
|
|
17
|
+
hasFormData: typeof FormData !== "undefined",
|
|
18
|
+
hasAbortController: typeof AbortController !== "undefined"
|
|
19
|
+
};
|
|
20
|
+
const responseCacheInstances = new Map;
|
|
21
|
+
function getCacheConfigKey(option) {
|
|
22
|
+
if (option === true)
|
|
23
|
+
return "default";
|
|
24
|
+
if (option === false)
|
|
25
|
+
return "disabled";
|
|
26
|
+
const cfg = option;
|
|
27
|
+
return JSON.stringify({
|
|
28
|
+
cacheDir: cfg.cacheDir || null,
|
|
29
|
+
ttl: cfg.ttl || 300000,
|
|
30
|
+
maxEntries: cfg.maxEntries || 500,
|
|
31
|
+
methods: cfg.methods || ["GET", "HEAD"],
|
|
32
|
+
respectHeaders: cfg.respectHeaders !== false
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function getResponseCache(option) {
|
|
36
|
+
const key = getCacheConfigKey(option);
|
|
37
|
+
let cache = responseCacheInstances.get(key);
|
|
38
|
+
if (!cache) {
|
|
39
|
+
cache = new ResponseCache(option);
|
|
40
|
+
responseCacheInstances.set(key, cache);
|
|
41
|
+
}
|
|
42
|
+
return cache;
|
|
43
|
+
}
|
|
44
|
+
function parseCacheControlFromHeaders(headers) {
|
|
45
|
+
const cacheControl = headers["cache-control"] || "";
|
|
46
|
+
return {
|
|
47
|
+
noCache: cacheControl.includes("no-cache"),
|
|
48
|
+
mustRevalidate: cacheControl.includes("must-revalidate")
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function buildCachedRezoResponse(cached, config) {
|
|
52
|
+
const headers = new RezoHeaders(cached.headers);
|
|
53
|
+
return {
|
|
54
|
+
data: cached.data,
|
|
55
|
+
status: cached.status,
|
|
56
|
+
statusText: cached.statusText,
|
|
57
|
+
headers,
|
|
58
|
+
finalUrl: cached.url,
|
|
59
|
+
urls: [cached.url],
|
|
60
|
+
contentType: cached.headers["content-type"],
|
|
61
|
+
contentLength: parseInt(cached.headers["content-length"] || "0", 10) || 0,
|
|
62
|
+
cookies: {
|
|
63
|
+
array: [],
|
|
64
|
+
serialized: [],
|
|
65
|
+
netscape: "",
|
|
66
|
+
string: "",
|
|
67
|
+
setCookiesString: []
|
|
68
|
+
},
|
|
69
|
+
config: {
|
|
70
|
+
...config,
|
|
71
|
+
url: cached.url,
|
|
72
|
+
method: "GET",
|
|
73
|
+
headers,
|
|
74
|
+
adapterUsed: "react-native",
|
|
75
|
+
fromCache: true
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function buildUrlTree(config, finalUrl) {
|
|
80
|
+
const urls = [];
|
|
81
|
+
if (config.rawUrl) {
|
|
82
|
+
urls.push(config.rawUrl);
|
|
83
|
+
} else if (config.url) {
|
|
84
|
+
const urlStr = typeof config.url === "string" ? config.url : String(config.url);
|
|
85
|
+
urls.push(urlStr);
|
|
86
|
+
}
|
|
87
|
+
if (finalUrl && (urls.length === 0 || urls[0] !== finalUrl)) {
|
|
88
|
+
urls.push(finalUrl);
|
|
89
|
+
}
|
|
90
|
+
return urls.length > 0 ? urls : [finalUrl];
|
|
91
|
+
}
|
|
92
|
+
function sanitizeConfig(config) {
|
|
93
|
+
const sanitized = { ...config };
|
|
94
|
+
delete sanitized.data;
|
|
95
|
+
return sanitized;
|
|
96
|
+
}
|
|
97
|
+
function fromFetchHeaders(headers) {
|
|
98
|
+
const record = {};
|
|
99
|
+
headers.forEach((value, key) => {
|
|
100
|
+
record[key.toLowerCase()] = value;
|
|
101
|
+
});
|
|
102
|
+
return new RezoHeaders(record);
|
|
103
|
+
}
|
|
104
|
+
function toFetchHeaders(headers) {
|
|
105
|
+
const fetchHeaders = new Headers;
|
|
106
|
+
if (!headers)
|
|
107
|
+
return fetchHeaders;
|
|
108
|
+
if (headers instanceof RezoHeaders) {
|
|
109
|
+
for (const [key, value] of headers.entries()) {
|
|
110
|
+
if (value !== undefined && value !== null) {
|
|
111
|
+
fetchHeaders.set(key, String(value));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
116
|
+
if (value !== undefined && value !== null) {
|
|
117
|
+
fetchHeaders.set(key, String(value));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return fetchHeaders;
|
|
122
|
+
}
|
|
123
|
+
async function prepareBody(body) {
|
|
124
|
+
if (!body)
|
|
125
|
+
return;
|
|
126
|
+
if (body instanceof URLSearchParams || body instanceof RezoURLSearchParams) {
|
|
127
|
+
return body.toString();
|
|
128
|
+
}
|
|
129
|
+
if (typeof FormData !== "undefined" && body instanceof FormData) {
|
|
130
|
+
return body;
|
|
131
|
+
}
|
|
132
|
+
if (body instanceof RezoFormData) {
|
|
133
|
+
const nativeForm = body.toNativeFormData();
|
|
134
|
+
if (nativeForm) {
|
|
135
|
+
return nativeForm;
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (typeof body === "object" && !(body instanceof ArrayBuffer) && !(body instanceof Uint8Array)) {
|
|
140
|
+
return JSON.stringify(body);
|
|
141
|
+
}
|
|
142
|
+
return body;
|
|
143
|
+
}
|
|
144
|
+
export async function executeRequest(options, defaultOptions, jar) {
|
|
145
|
+
if (!Environment.hasFetch) {
|
|
146
|
+
throw new Error("Fetch API is not available in this React Native environment");
|
|
147
|
+
}
|
|
148
|
+
if (!options.responseType) {
|
|
149
|
+
options.responseType = "auto";
|
|
150
|
+
}
|
|
151
|
+
const d_options = await getDefaultConfig(defaultOptions);
|
|
152
|
+
const configResult = prepareHTTPOptions(options, jar, { defaultOptions: d_options });
|
|
153
|
+
let mainConfig = configResult.config;
|
|
154
|
+
const fetchOptions = configResult.fetchOptions;
|
|
155
|
+
const perform = new RezoPerformance;
|
|
156
|
+
const cacheOption = options.cache;
|
|
157
|
+
const method = (options.method || "GET").toUpperCase();
|
|
158
|
+
const requestUrl = typeof fetchOptions.url === "string" ? fetchOptions.url : fetchOptions.url?.toString() || "";
|
|
159
|
+
let cache;
|
|
160
|
+
let requestHeaders;
|
|
161
|
+
let cachedEntry;
|
|
162
|
+
if (cacheOption) {
|
|
163
|
+
cache = getResponseCache(cacheOption);
|
|
164
|
+
requestHeaders = fetchOptions.headers instanceof RezoHeaders ? Object.fromEntries(fetchOptions.headers.entries()) : fetchOptions.headers;
|
|
165
|
+
cachedEntry = cache.get(method, requestUrl, requestHeaders);
|
|
166
|
+
if (cachedEntry) {
|
|
167
|
+
const cacheControl = parseCacheControlFromHeaders(cachedEntry.headers);
|
|
168
|
+
if (!cacheControl.noCache && !cacheControl.mustRevalidate) {
|
|
169
|
+
return buildCachedRezoResponse(cachedEntry, mainConfig);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const conditionalHeaders = cache.getConditionalHeaders(method, requestUrl, requestHeaders);
|
|
173
|
+
if (conditionalHeaders) {
|
|
174
|
+
const headers = fetchOptions.headers instanceof RezoHeaders ? fetchOptions.headers : new RezoHeaders(fetchOptions.headers || {});
|
|
175
|
+
if (conditionalHeaders.etag) {
|
|
176
|
+
headers.set("If-None-Match", conditionalHeaders.etag);
|
|
177
|
+
}
|
|
178
|
+
if (conditionalHeaders.lastModified) {
|
|
179
|
+
headers.set("If-Modified-Since", conditionalHeaders.lastModified);
|
|
180
|
+
}
|
|
181
|
+
fetchOptions.headers = headers;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const isStream = options._isStream;
|
|
185
|
+
const isDownload = options._isDownload || !!options.fileName || !!options.saveTo;
|
|
186
|
+
const isUpload = options._isUpload;
|
|
187
|
+
let streamResponse;
|
|
188
|
+
let downloadResponse;
|
|
189
|
+
let uploadResponse;
|
|
190
|
+
if (isStream) {
|
|
191
|
+
streamResponse = new StreamResponse;
|
|
192
|
+
} else if (isDownload) {
|
|
193
|
+
const fileName = options.fileName || options.saveTo || "";
|
|
194
|
+
const url = typeof fetchOptions.url === "string" ? fetchOptions.url : fetchOptions.url?.toString() || "";
|
|
195
|
+
downloadResponse = new DownloadResponse(fileName, url);
|
|
196
|
+
} else if (isUpload) {
|
|
197
|
+
const url = typeof fetchOptions.url === "string" ? fetchOptions.url : fetchOptions.url?.toString() || "";
|
|
198
|
+
uploadResponse = new UploadResponse(url);
|
|
199
|
+
}
|
|
200
|
+
const res = executeFetchRequest(fetchOptions, mainConfig, options, perform, streamResponse, downloadResponse, uploadResponse);
|
|
201
|
+
if (streamResponse) {
|
|
202
|
+
return streamResponse;
|
|
203
|
+
} else if (downloadResponse) {
|
|
204
|
+
return downloadResponse;
|
|
205
|
+
} else if (uploadResponse) {
|
|
206
|
+
return uploadResponse;
|
|
207
|
+
}
|
|
208
|
+
const response = await res;
|
|
209
|
+
if (cache && !isStream && !isDownload && !isUpload) {
|
|
210
|
+
if (response.status === 304 && cachedEntry) {
|
|
211
|
+
const responseHeaders = response.headers instanceof RezoHeaders ? Object.fromEntries(response.headers.entries()) : response.headers;
|
|
212
|
+
const updatedCached = cache.updateRevalidated(method, requestUrl, responseHeaders, requestHeaders);
|
|
213
|
+
if (updatedCached) {
|
|
214
|
+
return buildCachedRezoResponse(updatedCached, mainConfig);
|
|
215
|
+
}
|
|
216
|
+
return buildCachedRezoResponse(cachedEntry, mainConfig);
|
|
217
|
+
}
|
|
218
|
+
if (response.status >= 200 && response.status < 300) {
|
|
219
|
+
cache.set(method, requestUrl, response, requestHeaders);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return response;
|
|
223
|
+
}
|
|
224
|
+
async function executeFetchRequest(fetchOptions, config, options, perform, streamResult, downloadResult, uploadResult) {
|
|
225
|
+
let retries = 0;
|
|
226
|
+
const retryDelay = config?.retry?.retryDelay || 0;
|
|
227
|
+
const maxRetries = config?.retry?.maxRetries || 0;
|
|
228
|
+
const incrementDelay = config?.retry?.incrementDelay || false;
|
|
229
|
+
const statusCodes = config?.retry?.statusCodes;
|
|
230
|
+
const timing = {
|
|
231
|
+
startTime: performance.now(),
|
|
232
|
+
startTimestamp: Date.now()
|
|
233
|
+
};
|
|
234
|
+
const ABSOLUTE_MAX_ATTEMPTS = 50;
|
|
235
|
+
let totalAttempts = 0;
|
|
236
|
+
config.setSignal();
|
|
237
|
+
const eventEmitter = streamResult || downloadResult || uploadResult;
|
|
238
|
+
if (eventEmitter) {
|
|
239
|
+
eventEmitter.emit("initiated");
|
|
240
|
+
}
|
|
241
|
+
while (true) {
|
|
242
|
+
totalAttempts++;
|
|
243
|
+
if (totalAttempts > ABSOLUTE_MAX_ATTEMPTS) {
|
|
244
|
+
const error = builErrorFromResponse(`Absolute maximum attempts (${ABSOLUTE_MAX_ATTEMPTS}) exceeded.`, { status: 0, statusText: "Max Attempts Exceeded" }, config, fetchOptions);
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const response = await executeSingleRequest(config, fetchOptions, timing, streamResult, downloadResult, uploadResult);
|
|
249
|
+
if (response instanceof RezoError) {
|
|
250
|
+
config.errors.push({
|
|
251
|
+
attempt: config.retryAttempts + 1,
|
|
252
|
+
error: response,
|
|
253
|
+
duration: perform.now()
|
|
254
|
+
});
|
|
255
|
+
perform.reset();
|
|
256
|
+
if (!config.retry) {
|
|
257
|
+
throw response;
|
|
258
|
+
}
|
|
259
|
+
if (config.retry.condition) {
|
|
260
|
+
const isPassed = await config.retry.condition(response);
|
|
261
|
+
if (typeof isPassed === "boolean" && isPassed === false) {
|
|
262
|
+
throw response;
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
if (statusCodes && !statusCodes.includes(response.status || 0)) {
|
|
266
|
+
throw response;
|
|
267
|
+
}
|
|
268
|
+
if (maxRetries <= retries) {
|
|
269
|
+
throw response;
|
|
270
|
+
}
|
|
271
|
+
retries++;
|
|
272
|
+
if (retryDelay > 0) {
|
|
273
|
+
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
config.retryAttempts++;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
return response;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (error instanceof RezoError) {
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
throw buildSmartError(config, fetchOptions, error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async function executeSingleRequest(config, fetchOptions, timing, streamResult, downloadResult, uploadResult) {
|
|
289
|
+
try {
|
|
290
|
+
const { fullUrl, body } = fetchOptions;
|
|
291
|
+
const url = fullUrl || (typeof fetchOptions.url === "string" ? fetchOptions.url : fetchOptions.url?.toString() || "");
|
|
292
|
+
const isSecure = url.startsWith("https:");
|
|
293
|
+
config.adapterUsed = "react-native";
|
|
294
|
+
config.isSecure = isSecure;
|
|
295
|
+
config.finalUrl = url;
|
|
296
|
+
config.network.protocol = isSecure ? "https" : "http";
|
|
297
|
+
config.timing.startTimestamp = timing.startTimestamp;
|
|
298
|
+
const reqHeaders = fetchOptions.headers instanceof RezoHeaders ? fetchOptions.headers.toObject() : fetchOptions.headers || {};
|
|
299
|
+
const headers = toFetchHeaders(reqHeaders);
|
|
300
|
+
const eventEmitter = streamResult || downloadResult || uploadResult;
|
|
301
|
+
if (eventEmitter) {
|
|
302
|
+
const startEvent = {
|
|
303
|
+
url,
|
|
304
|
+
method: fetchOptions.method.toUpperCase(),
|
|
305
|
+
headers: new RezoHeaders(reqHeaders),
|
|
306
|
+
timestamp: timing.startTime,
|
|
307
|
+
timeout: fetchOptions.timeout,
|
|
308
|
+
maxRedirects: config.maxRedirects
|
|
309
|
+
};
|
|
310
|
+
eventEmitter.emit("start", startEvent);
|
|
311
|
+
}
|
|
312
|
+
const abortController = new AbortController;
|
|
313
|
+
let timeoutId;
|
|
314
|
+
if (config.timeout) {
|
|
315
|
+
timeoutId = setTimeout(() => {
|
|
316
|
+
abortController.abort();
|
|
317
|
+
}, config.timeout);
|
|
318
|
+
}
|
|
319
|
+
if (config.signal) {
|
|
320
|
+
config.signal.addEventListener("abort", () => {
|
|
321
|
+
abortController.abort();
|
|
322
|
+
if (timeoutId)
|
|
323
|
+
clearTimeout(timeoutId);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
const preparedBody = await prepareBody(body);
|
|
327
|
+
const fetchInit = {
|
|
328
|
+
method: fetchOptions.method.toUpperCase(),
|
|
329
|
+
headers,
|
|
330
|
+
body: preparedBody,
|
|
331
|
+
signal: abortController.signal
|
|
332
|
+
};
|
|
333
|
+
let response;
|
|
334
|
+
try {
|
|
335
|
+
response = await fetch(url, fetchInit);
|
|
336
|
+
} catch (err) {
|
|
337
|
+
if (timeoutId)
|
|
338
|
+
clearTimeout(timeoutId);
|
|
339
|
+
if (err.name === "AbortError") {
|
|
340
|
+
const error = buildSmartError(config, fetchOptions, new Error(`Request timeout after ${config.timeout}ms`));
|
|
341
|
+
if (eventEmitter)
|
|
342
|
+
eventEmitter.emit("error", error);
|
|
343
|
+
return error;
|
|
344
|
+
}
|
|
345
|
+
throw err;
|
|
346
|
+
} finally {
|
|
347
|
+
if (timeoutId)
|
|
348
|
+
clearTimeout(timeoutId);
|
|
349
|
+
}
|
|
350
|
+
if (!config.timing.ttfbMs) {
|
|
351
|
+
timing.firstByteTime = performance.now();
|
|
352
|
+
config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
|
|
353
|
+
}
|
|
354
|
+
const status = response.status;
|
|
355
|
+
const statusText = response.statusText;
|
|
356
|
+
const responseHeaders = fromFetchHeaders(response.headers);
|
|
357
|
+
const contentType = response.headers.get("content-type") || "";
|
|
358
|
+
const contentLength = response.headers.get("content-length");
|
|
359
|
+
const cookies = {
|
|
360
|
+
array: [],
|
|
361
|
+
serialized: [],
|
|
362
|
+
netscape: "",
|
|
363
|
+
string: "",
|
|
364
|
+
setCookiesString: []
|
|
365
|
+
};
|
|
366
|
+
config.responseCookies = cookies;
|
|
367
|
+
if (eventEmitter) {
|
|
368
|
+
const headersEvent = {
|
|
369
|
+
status,
|
|
370
|
+
statusText,
|
|
371
|
+
headers: responseHeaders,
|
|
372
|
+
contentType,
|
|
373
|
+
contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
|
|
374
|
+
cookies: cookies.array,
|
|
375
|
+
timing: {
|
|
376
|
+
firstByte: config.timing.ttfbMs,
|
|
377
|
+
total: performance.now() - timing.startTime
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
eventEmitter.emit("headers", headersEvent);
|
|
381
|
+
eventEmitter.emit("status", status, statusText);
|
|
382
|
+
eventEmitter.emit("cookies", cookies.array);
|
|
383
|
+
if (downloadResult) {
|
|
384
|
+
downloadResult.status = status;
|
|
385
|
+
downloadResult.statusText = statusText;
|
|
386
|
+
} else if (uploadResult) {
|
|
387
|
+
uploadResult.status = status;
|
|
388
|
+
uploadResult.statusText = statusText;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
let responseData;
|
|
392
|
+
let bodySize = 0;
|
|
393
|
+
const responseType = config.responseType || fetchOptions.responseType || "auto";
|
|
394
|
+
if (responseType === "blob") {
|
|
395
|
+
const blob = await response.blob();
|
|
396
|
+
responseData = blob;
|
|
397
|
+
bodySize = blob.size;
|
|
398
|
+
} else if (responseType === "arrayBuffer" || responseType === "buffer") {
|
|
399
|
+
const buffer = await response.arrayBuffer();
|
|
400
|
+
responseData = buffer;
|
|
401
|
+
bodySize = buffer.byteLength;
|
|
402
|
+
} else if (responseType === "text") {
|
|
403
|
+
const text = await response.text();
|
|
404
|
+
responseData = text;
|
|
405
|
+
bodySize = text.length;
|
|
406
|
+
} else if (responseType === "json") {
|
|
407
|
+
try {
|
|
408
|
+
responseData = await response.json();
|
|
409
|
+
bodySize = JSON.stringify(responseData || "").length;
|
|
410
|
+
} catch {
|
|
411
|
+
const text = await response.text();
|
|
412
|
+
responseData = text;
|
|
413
|
+
bodySize = text.length;
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
if (contentType.includes("application/json")) {
|
|
417
|
+
try {
|
|
418
|
+
responseData = await response.json();
|
|
419
|
+
bodySize = JSON.stringify(responseData || "").length;
|
|
420
|
+
} catch {
|
|
421
|
+
const text = await response.text();
|
|
422
|
+
responseData = text;
|
|
423
|
+
bodySize = text.length;
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
const text = await response.text();
|
|
427
|
+
responseData = text;
|
|
428
|
+
bodySize = text.length;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
config.timing.endTimestamp = Date.now();
|
|
432
|
+
config.timing.durationMs = performance.now() - timing.startTime;
|
|
433
|
+
config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
|
|
434
|
+
config.transfer.bodySize = bodySize;
|
|
435
|
+
config.transfer.responseSize = bodySize;
|
|
436
|
+
if (status >= 400) {
|
|
437
|
+
const error = builErrorFromResponse(`HTTP Error ${status}: ${statusText}`, {
|
|
438
|
+
status,
|
|
439
|
+
statusText,
|
|
440
|
+
headers: responseHeaders,
|
|
441
|
+
data: responseData
|
|
442
|
+
}, config, fetchOptions);
|
|
443
|
+
if (eventEmitter)
|
|
444
|
+
eventEmitter.emit("error", error);
|
|
445
|
+
return error;
|
|
446
|
+
}
|
|
447
|
+
const finalResponse = {
|
|
448
|
+
data: responseData,
|
|
449
|
+
status,
|
|
450
|
+
statusText,
|
|
451
|
+
headers: responseHeaders,
|
|
452
|
+
cookies,
|
|
453
|
+
config,
|
|
454
|
+
contentType,
|
|
455
|
+
contentLength: bodySize,
|
|
456
|
+
finalUrl: url,
|
|
457
|
+
urls: buildUrlTree(config, url)
|
|
458
|
+
};
|
|
459
|
+
if (streamResult) {
|
|
460
|
+
const streamFinishEvent = {
|
|
461
|
+
status,
|
|
462
|
+
statusText,
|
|
463
|
+
headers: responseHeaders,
|
|
464
|
+
contentType,
|
|
465
|
+
contentLength: bodySize,
|
|
466
|
+
finalUrl: url,
|
|
467
|
+
cookies,
|
|
468
|
+
urls: buildUrlTree(config, url),
|
|
469
|
+
timing: {
|
|
470
|
+
total: config.timing.durationMs || 0,
|
|
471
|
+
firstByte: config.timing.ttfbMs,
|
|
472
|
+
download: config.timing.transferMs
|
|
473
|
+
},
|
|
474
|
+
config: sanitizeConfig(config)
|
|
475
|
+
};
|
|
476
|
+
streamResult.emit("finish", streamFinishEvent);
|
|
477
|
+
streamResult.emit("done", streamFinishEvent);
|
|
478
|
+
streamResult.emit("end");
|
|
479
|
+
streamResult._markFinished();
|
|
480
|
+
}
|
|
481
|
+
if (downloadResult) {
|
|
482
|
+
const downloadFinishEvent = {
|
|
483
|
+
status,
|
|
484
|
+
statusText,
|
|
485
|
+
headers: responseHeaders,
|
|
486
|
+
contentType,
|
|
487
|
+
contentLength: bodySize,
|
|
488
|
+
finalUrl: url,
|
|
489
|
+
cookies,
|
|
490
|
+
urls: buildUrlTree(config, url),
|
|
491
|
+
fileName: config.fileName || "",
|
|
492
|
+
fileSize: bodySize,
|
|
493
|
+
timing: {
|
|
494
|
+
total: config.timing.durationMs || 0,
|
|
495
|
+
firstByte: config.timing.ttfbMs,
|
|
496
|
+
download: config.timing.transferMs || 0
|
|
497
|
+
},
|
|
498
|
+
averageSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
|
|
499
|
+
config: sanitizeConfig(config)
|
|
500
|
+
};
|
|
501
|
+
downloadResult.emit("finish", downloadFinishEvent);
|
|
502
|
+
downloadResult.emit("done", downloadFinishEvent);
|
|
503
|
+
downloadResult._markFinished();
|
|
504
|
+
}
|
|
505
|
+
if (uploadResult) {
|
|
506
|
+
const uploadFinishEvent = {
|
|
507
|
+
response: {
|
|
508
|
+
status,
|
|
509
|
+
statusText,
|
|
510
|
+
headers: responseHeaders,
|
|
511
|
+
data: responseData,
|
|
512
|
+
contentType,
|
|
513
|
+
contentLength: bodySize
|
|
514
|
+
},
|
|
515
|
+
finalUrl: url,
|
|
516
|
+
cookies,
|
|
517
|
+
urls: buildUrlTree(config, url),
|
|
518
|
+
uploadSize: config.transfer.requestSize || 0,
|
|
519
|
+
timing: {
|
|
520
|
+
total: config.timing.durationMs || 0,
|
|
521
|
+
upload: config.timing.transferMs || 0,
|
|
522
|
+
waiting: config.timing.ttfbMs || 0,
|
|
523
|
+
download: config.timing.transferMs
|
|
524
|
+
},
|
|
525
|
+
averageUploadSpeed: config.timing.transferMs ? (config.transfer.requestSize || 0) / config.timing.transferMs * 1000 : 0,
|
|
526
|
+
averageDownloadSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
|
|
527
|
+
config: sanitizeConfig(config)
|
|
528
|
+
};
|
|
529
|
+
uploadResult.emit("finish", uploadFinishEvent);
|
|
530
|
+
uploadResult.emit("done", uploadFinishEvent);
|
|
531
|
+
uploadResult._markFinished();
|
|
532
|
+
}
|
|
533
|
+
return finalResponse;
|
|
534
|
+
} catch (error) {
|
|
535
|
+
const rezoError = buildSmartError(config, fetchOptions, error);
|
|
536
|
+
const eventEmitter = streamResult || downloadResult || uploadResult;
|
|
537
|
+
if (eventEmitter) {
|
|
538
|
+
eventEmitter.emit("error", rezoError);
|
|
539
|
+
}
|
|
540
|
+
return rezoError;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export { Environment };
|