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.
Files changed (135) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1507 -0
  3. package/assets/icon.svg +37 -0
  4. package/assets/logo-dark.svg +47 -0
  5. package/assets/logo.svg +58 -0
  6. package/dist/adapters/curl.cjs +1034 -0
  7. package/dist/adapters/curl.js +1031 -0
  8. package/dist/adapters/entries/curl.cjs +4 -0
  9. package/dist/adapters/entries/curl.d.ts +2136 -0
  10. package/dist/adapters/entries/curl.js +2 -0
  11. package/dist/adapters/entries/fetch.cjs +2 -0
  12. package/dist/adapters/entries/fetch.d.ts +2127 -0
  13. package/dist/adapters/entries/fetch.js +1 -0
  14. package/dist/adapters/entries/http.cjs +2 -0
  15. package/dist/adapters/entries/http.d.ts +2126 -0
  16. package/dist/adapters/entries/http.js +1 -0
  17. package/dist/adapters/entries/http2.cjs +4 -0
  18. package/dist/adapters/entries/http2.d.ts +2136 -0
  19. package/dist/adapters/entries/http2.js +2 -0
  20. package/dist/adapters/entries/react-native.cjs +2 -0
  21. package/dist/adapters/entries/react-native.d.ts +2126 -0
  22. package/dist/adapters/entries/react-native.js +1 -0
  23. package/dist/adapters/entries/xhr.cjs +2 -0
  24. package/dist/adapters/entries/xhr.d.ts +2127 -0
  25. package/dist/adapters/entries/xhr.js +1 -0
  26. package/dist/adapters/fetch.cjs +740 -0
  27. package/dist/adapters/fetch.js +739 -0
  28. package/dist/adapters/http.cjs +1153 -0
  29. package/dist/adapters/http.js +1151 -0
  30. package/dist/adapters/http2.cjs +957 -0
  31. package/dist/adapters/http2.js +956 -0
  32. package/dist/adapters/index.cjs +6 -0
  33. package/dist/adapters/index.js +7 -0
  34. package/dist/adapters/picker.cjs +342 -0
  35. package/dist/adapters/picker.js +331 -0
  36. package/dist/adapters/react-native.cjs +545 -0
  37. package/dist/adapters/react-native.js +544 -0
  38. package/dist/adapters/xhr.cjs +622 -0
  39. package/dist/adapters/xhr.js +621 -0
  40. package/dist/cache/dns-cache.cjs +118 -0
  41. package/dist/cache/dns-cache.js +113 -0
  42. package/dist/cache/file-cacher.cjs +264 -0
  43. package/dist/cache/file-cacher.js +261 -0
  44. package/dist/cache/index.cjs +13 -0
  45. package/dist/cache/index.js +5 -0
  46. package/dist/cache/lru-cache.cjs +96 -0
  47. package/dist/cache/lru-cache.js +93 -0
  48. package/dist/cache/response-cache.cjs +314 -0
  49. package/dist/cache/response-cache.js +310 -0
  50. package/dist/cache/url-store.cjs +288 -0
  51. package/dist/cache/url-store.js +285 -0
  52. package/dist/core/hooks.cjs +133 -0
  53. package/dist/core/hooks.js +120 -0
  54. package/dist/core/rezo.cjs +464 -0
  55. package/dist/core/rezo.js +458 -0
  56. package/dist/crawler.d.ts +6255 -0
  57. package/dist/dom/index.cjs +1 -0
  58. package/dist/dom/index.d.ts +23 -0
  59. package/dist/dom/index.js +1 -0
  60. package/dist/entries/crawler.cjs +5 -0
  61. package/dist/entries/crawler.js +2 -0
  62. package/dist/errors/rezo-error.cjs +722 -0
  63. package/dist/errors/rezo-error.js +716 -0
  64. package/dist/index.cjs +34 -0
  65. package/dist/index.d.ts +3335 -0
  66. package/dist/index.js +26 -0
  67. package/dist/platform/browser.cjs +9 -0
  68. package/dist/platform/browser.d.ts +3203 -0
  69. package/dist/platform/browser.js +7 -0
  70. package/dist/platform/bun.cjs +9 -0
  71. package/dist/platform/bun.d.ts +3203 -0
  72. package/dist/platform/bun.js +7 -0
  73. package/dist/platform/deno.cjs +9 -0
  74. package/dist/platform/deno.d.ts +3203 -0
  75. package/dist/platform/deno.js +7 -0
  76. package/dist/platform/node.cjs +9 -0
  77. package/dist/platform/node.d.ts +3203 -0
  78. package/dist/platform/node.js +7 -0
  79. package/dist/platform/react-native.cjs +9 -0
  80. package/dist/platform/react-native.d.ts +3203 -0
  81. package/dist/platform/react-native.js +7 -0
  82. package/dist/platform/worker.cjs +9 -0
  83. package/dist/platform/worker.d.ts +3203 -0
  84. package/dist/platform/worker.js +7 -0
  85. package/dist/plugin/addon/decodo/index.cjs +1 -0
  86. package/dist/plugin/addon/decodo/index.js +1 -0
  87. package/dist/plugin/addon/decodo/options.cjs +1 -0
  88. package/dist/plugin/addon/decodo/options.js +1 -0
  89. package/dist/plugin/addon/oxylabs/index.cjs +1 -0
  90. package/dist/plugin/addon/oxylabs/index.js +1 -0
  91. package/dist/plugin/addon/oxylabs/options.cjs +1 -0
  92. package/dist/plugin/addon/oxylabs/options.js +1 -0
  93. package/dist/plugin/crawler-options.cjs +1 -0
  94. package/dist/plugin/crawler-options.js +1 -0
  95. package/dist/plugin/crawler.cjs +519 -0
  96. package/dist/plugin/crawler.js +517 -0
  97. package/dist/plugin/index.cjs +36 -0
  98. package/dist/plugin/index.js +32 -0
  99. package/dist/proxy/index.cjs +142 -0
  100. package/dist/proxy/index.js +139 -0
  101. package/dist/responses/buildError.cjs +452 -0
  102. package/dist/responses/buildError.js +441 -0
  103. package/dist/responses/buildResponse.cjs +365 -0
  104. package/dist/responses/buildResponse.js +361 -0
  105. package/dist/responses/download.cjs +54 -0
  106. package/dist/responses/download.js +52 -0
  107. package/dist/responses/stream.cjs +60 -0
  108. package/dist/responses/stream.js +58 -0
  109. package/dist/responses/upload.cjs +54 -0
  110. package/dist/responses/upload.js +52 -0
  111. package/dist/types/cookies.cjs +394 -0
  112. package/dist/types/cookies.js +391 -0
  113. package/dist/types/download.cjs +10 -0
  114. package/dist/types/download.js +10 -0
  115. package/dist/types/rezo-request.cjs +131 -0
  116. package/dist/types/rezo-request.js +131 -0
  117. package/dist/utils/agent-merger.cjs +111 -0
  118. package/dist/utils/agent-merger.js +108 -0
  119. package/dist/utils/compression.cjs +84 -0
  120. package/dist/utils/compression.js +82 -0
  121. package/dist/utils/cookies.cjs +514 -0
  122. package/dist/utils/cookies.js +511 -0
  123. package/dist/utils/data-operations.cjs +75 -0
  124. package/dist/utils/data-operations.js +73 -0
  125. package/dist/utils/form-data.cjs +164 -0
  126. package/dist/utils/form-data.js +161 -0
  127. package/dist/utils/headers.cjs +162 -0
  128. package/dist/utils/headers.js +161 -0
  129. package/dist/utils/http-config.cjs +723 -0
  130. package/dist/utils/http-config.js +718 -0
  131. package/dist/utils/index.cjs +8 -0
  132. package/dist/utils/index.js +8 -0
  133. package/dist/utils/tools.cjs +18 -0
  134. package/dist/utils/tools.js +15 -0
  135. package/package.json +172 -0
@@ -0,0 +1,1034 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+ const os = require("node:os");
4
+ const crypto = require("node:crypto");
5
+ const { spawn, execSync } = require("node:child_process");
6
+ const { Readable } = require("node:stream");
7
+ const { EventEmitter } = require("node:events");
8
+ const { RezoError } = require('../errors/rezo-error.cjs');
9
+ const { buildSmartError } = require('../responses/buildError.cjs');
10
+ const { Cookie } = require('../utils/cookies.cjs');
11
+ const RezoFormData = require('../utils/form-data.cjs');
12
+ const { existsSync } = require("node:fs");
13
+ const { getDefaultConfig, prepareHTTPOptions } = require('../utils/http-config.cjs');
14
+ const { RezoHeaders } = require('../utils/headers.cjs');
15
+ const { StreamResponse } = require('../responses/stream.cjs');
16
+ const { DownloadResponse } = require('../responses/download.cjs');
17
+ const { UploadResponse } = require('../responses/upload.cjs');
18
+ const { RezoPerformance } = require('../utils/tools.cjs');
19
+
20
+ class CurlCapabilities {
21
+ static instance;
22
+ version = "";
23
+ features = new Set;
24
+ protocols = new Set;
25
+ isInitialized = false;
26
+ isAvailable = { status: false, message: "Initializing" };
27
+ static getInstance() {
28
+ if (!CurlCapabilities.instance) {
29
+ CurlCapabilities.instance = new CurlCapabilities;
30
+ }
31
+ return CurlCapabilities.instance;
32
+ }
33
+ async initialize() {
34
+ if (this.isInitialized) {
35
+ return;
36
+ }
37
+ try {
38
+ this.isAvailable = this.checkCurlAvailability();
39
+ if (!this.isAvailable.status) {
40
+ throw new Error(this.isAvailable.message);
41
+ }
42
+ const { version, features, protocols } = await this.detectCapabilities();
43
+ this.version = version;
44
+ this.features = new Set(features);
45
+ this.protocols = new Set(protocols);
46
+ this.isInitialized = true;
47
+ } catch (error) {
48
+ throw new Error(`cURL not available: ${error.message}`);
49
+ }
50
+ }
51
+ checkCurlAvailability() {
52
+ try {
53
+ const output = execSync("curl --version", { encoding: "utf8", timeout: 5000 });
54
+ return output.includes("curl") ? { status: true } : this.getCurlInstallationMessage();
55
+ } catch {
56
+ return this.getCurlInstallationMessage();
57
+ }
58
+ }
59
+ getCurlInstallationMessage() {
60
+ let message = "cURL is not installed. ";
61
+ const platform = os.platform();
62
+ if (platform === "darwin") {
63
+ message += "Install cURL via Homebrew with 'brew install curl' or use 'xcode-select --install' to install command line tools.";
64
+ } else if (platform === "win32") {
65
+ message += "Install cURL by downloading it from https://curl.se/windows/ or use a package manager like Chocolatey with 'choco install curl'.";
66
+ } else if (platform === "linux") {
67
+ const isDebian = existsSync("/etc/debian_version");
68
+ const isRedHat = existsSync("/etc/redhat-release");
69
+ const isArch = existsSync("/etc/arch-release");
70
+ if (isDebian) {
71
+ message += "Install cURL with 'sudo apt-get install curl'.";
72
+ } else if (isRedHat) {
73
+ message += "Install cURL with 'sudo dnf install curl' or 'sudo yum install curl'.";
74
+ } else if (isArch) {
75
+ message += "Install cURL with 'sudo pacman -S curl'.";
76
+ } else {
77
+ message += "Install cURL using your distribution's package manager.";
78
+ }
79
+ } else {
80
+ message += "Please install cURL from https://curl.se/download.html";
81
+ }
82
+ return { status: false, message };
83
+ }
84
+ async detectCapabilities() {
85
+ return new Promise((resolve, reject) => {
86
+ const curl = spawn("curl", ["--version"]);
87
+ let output = "";
88
+ curl.stdout.on("data", (chunk) => {
89
+ output += chunk.toString();
90
+ });
91
+ curl.on("error", (error) => {
92
+ reject(new Error(`cURL not found: ${error.message}`));
93
+ });
94
+ curl.on("close", (code) => {
95
+ if (code !== 0) {
96
+ reject(new Error(`cURL version check failed with code ${code}`));
97
+ return;
98
+ }
99
+ const lines = output.split(`
100
+ `);
101
+ const versionLine = lines[0] || "";
102
+ const versionMatch = versionLine.match(/curl\s+([\d.]+)/);
103
+ const version = versionMatch ? versionMatch[1] : "unknown";
104
+ const features = [];
105
+ const protocols = [];
106
+ let inFeatures = false;
107
+ let inProtocols = false;
108
+ for (const line of lines) {
109
+ const trimmedLine = line.trim();
110
+ if (trimmedLine.startsWith("Features:")) {
111
+ inFeatures = true;
112
+ inProtocols = false;
113
+ const featuresText = trimmedLine.replace("Features:", "").trim();
114
+ if (featuresText) {
115
+ features.push(...featuresText.split(/\s+/));
116
+ }
117
+ } else if (trimmedLine.startsWith("Protocols:")) {
118
+ inFeatures = false;
119
+ inProtocols = true;
120
+ const protocolsText = trimmedLine.replace("Protocols:", "").trim();
121
+ if (protocolsText) {
122
+ protocols.push(...protocolsText.split(/\s+/));
123
+ }
124
+ } else if (inFeatures && trimmedLine) {
125
+ features.push(...trimmedLine.split(/\s+/));
126
+ } else if (inProtocols && trimmedLine) {
127
+ protocols.push(...trimmedLine.split(/\s+/));
128
+ }
129
+ }
130
+ resolve({ version, features, protocols });
131
+ });
132
+ });
133
+ }
134
+ hasFeature(feature) {
135
+ return this.features.has(feature);
136
+ }
137
+ hasProtocol(protocol) {
138
+ return this.protocols.has(protocol);
139
+ }
140
+ getVersion() {
141
+ return this.version;
142
+ }
143
+ supportsHttp2() {
144
+ return this.hasFeature("HTTP2");
145
+ }
146
+ supportsHttp3() {
147
+ return this.hasFeature("HTTP3") || this.hasFeature("QUIC");
148
+ }
149
+ isAvailableStatus() {
150
+ return this.isAvailable;
151
+ }
152
+ throwIfNotAvailable() {
153
+ if (!this.isAvailable.status) {
154
+ throw new Error(this.isAvailable.message);
155
+ }
156
+ }
157
+ }
158
+
159
+ class CurlProgressTracker extends EventEmitter {
160
+ totalBytes = 0;
161
+ downloadedBytes = 0;
162
+ uploadedBytes = 0;
163
+ downloadSpeed = 0;
164
+ uploadSpeed = 0;
165
+ timeRemaining = 0;
166
+ startTime = Date.now();
167
+ parseProgress(line) {
168
+ const progressMatch = line.match(/\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+([\d:-]+)\s+([\d:-]+)\s+([\d:-]+)\s+(\d+)/);
169
+ if (progressMatch) {
170
+ const [, totalPercent, total, receivedPercent, received, xferPercent, xfer, avgDload, avgUpload, timeTotal, timeSpent, timeLeft, currentSpeed] = progressMatch;
171
+ this.totalBytes = parseInt(total);
172
+ this.downloadedBytes = parseInt(received);
173
+ this.uploadedBytes = parseInt(xfer);
174
+ this.downloadSpeed = parseInt(avgDload);
175
+ this.uploadSpeed = parseInt(avgUpload);
176
+ const progress = {
177
+ total: this.totalBytes,
178
+ loaded: this.downloadedBytes + this.uploadedBytes,
179
+ percentage: this.totalBytes > 0 ? Math.round(this.downloadedBytes / this.totalBytes * 100) : 0,
180
+ speed: parseInt(currentSpeed) || this.downloadSpeed,
181
+ averageSpeed: this.downloadSpeed,
182
+ estimatedTime: this.parseTime(timeLeft) * 1000,
183
+ timestamp: Date.now()
184
+ };
185
+ this.emit("progress", progress);
186
+ }
187
+ }
188
+ parseTime(timeStr) {
189
+ if (timeStr === "--:--:--") {
190
+ return 0;
191
+ }
192
+ const parts = timeStr.split(":").map(Number);
193
+ if (parts.length === 3) {
194
+ return parts[0] * 3600 + parts[1] * 60 + parts[2];
195
+ }
196
+ return 0;
197
+ }
198
+ }
199
+
200
+ class TempFileManager {
201
+ tempFiles = new Set;
202
+ createTempFile(prefix = "rezo-curl", extension = ".tmp") {
203
+ const tempDir = os.tmpdir();
204
+ const fileName = `${prefix}-${crypto.randomUUID()}${extension}`;
205
+ const filePath = path.join(tempDir, fileName);
206
+ this.tempFiles.add(filePath);
207
+ return filePath;
208
+ }
209
+ cleanup() {
210
+ for (const filePath of this.tempFiles) {
211
+ try {
212
+ if (fs.existsSync(filePath)) {
213
+ fs.unlinkSync(filePath);
214
+ }
215
+ } catch (error) {}
216
+ }
217
+ this.tempFiles.clear();
218
+ }
219
+ }
220
+
221
+ class CurlCommandBuilder {
222
+ args = [];
223
+ tempFiles;
224
+ capabilities;
225
+ constructor(tempFiles, capabilities) {
226
+ this.tempFiles = tempFiles;
227
+ this.capabilities = capabilities;
228
+ }
229
+ build(config, originalRequest) {
230
+ this.args = [];
231
+ const createdTempFiles = [];
232
+ this.addArg("-i");
233
+ this.addArg("--show-error");
234
+ this.addArg("--fail-with-body");
235
+ const isVerbose = originalRequest.verbose || config.debug;
236
+ if (isVerbose) {
237
+ this.addArg("-v");
238
+ } else {
239
+ this.addArg("-s");
240
+ }
241
+ if (config.method && config.method !== "GET") {
242
+ this.addArg("-X", config.method.toUpperCase());
243
+ }
244
+ if (config.http2 && this.capabilities.supportsHttp2()) {
245
+ this.addArg("--http2");
246
+ } else {
247
+ this.addArg("--http1.1");
248
+ }
249
+ this.buildTimeouts(config);
250
+ this.buildAuthentication(config, originalRequest);
251
+ this.buildSSLConfig(config, originalRequest);
252
+ this.buildProxyConfig(config);
253
+ const cookieJar = this.buildCookieConfig(config);
254
+ if (cookieJar) {
255
+ createdTempFiles.push(cookieJar);
256
+ }
257
+ if (config.compression?.enabled !== false) {
258
+ this.addArg("--compressed");
259
+ }
260
+ this.buildConnectionOptions(config, originalRequest);
261
+ this.buildDownloadOptions(config, originalRequest, createdTempFiles);
262
+ this.buildHeaders(config);
263
+ this.buildRedirectOptions(config, originalRequest);
264
+ this.buildRequestBody(config, createdTempFiles);
265
+ if (originalRequest.onUploadProgress || originalRequest.onDownloadProgress) {
266
+ this.addArg("--progress-bar");
267
+ }
268
+ this.addArg("-w", this.buildWriteOutFormat());
269
+ return { args: this.args, tempFiles: createdTempFiles, cookieJar };
270
+ }
271
+ addArg(arg, value) {
272
+ this.args.push(arg);
273
+ if (value !== undefined) {
274
+ this.args.push(this.escapeShellArg(value));
275
+ }
276
+ }
277
+ escapeShellArg(str) {
278
+ return str.replace(/["\\$`!]/g, "\\$&");
279
+ }
280
+ buildTimeouts(config) {
281
+ if (config.timeout) {
282
+ this.addArg("--max-time", Math.ceil(config.timeout / 1000).toString());
283
+ }
284
+ }
285
+ buildAuthentication(config, originalRequest) {
286
+ if (!config.auth) {
287
+ return;
288
+ }
289
+ const auth = config.auth;
290
+ const authType = auth.type || "basic";
291
+ switch (authType) {
292
+ case "basic":
293
+ if (auth.username && auth.password) {
294
+ this.addArg("--basic");
295
+ this.addArg("-u", `${auth.username}:${auth.password}`);
296
+ }
297
+ break;
298
+ case "digest":
299
+ if (auth.username && auth.password) {
300
+ this.addArg("--digest");
301
+ this.addArg("-u", `${auth.username}:${auth.password}`);
302
+ }
303
+ break;
304
+ case "ntlm":
305
+ if (auth.username && auth.password) {
306
+ this.addArg("--ntlm");
307
+ this.addArg("-u", `${auth.username}:${auth.password}`);
308
+ }
309
+ break;
310
+ case "bearer":
311
+ if (auth.token) {
312
+ this.addArg("-H", `Authorization: Bearer ${auth.token}`);
313
+ }
314
+ break;
315
+ case "oauth1":
316
+ case "aws4":
317
+ case "custom":
318
+ if (auth.custom) {
319
+ for (const [key, value] of Object.entries(auth.custom)) {
320
+ this.addArg("-H", `${key}: ${value}`);
321
+ }
322
+ }
323
+ break;
324
+ }
325
+ }
326
+ buildSSLConfig(config, originalRequest) {
327
+ if (config.rejectUnauthorized === false || originalRequest.rejectUnauthorized === false) {
328
+ this.addArg("-k");
329
+ }
330
+ const sslOptions = originalRequest.ssl;
331
+ if (!sslOptions) {
332
+ return;
333
+ }
334
+ if (sslOptions.cert) {
335
+ if (typeof sslOptions.cert === "string") {
336
+ this.addArg("--cert", sslOptions.cert);
337
+ } else {
338
+ const certFile = this.tempFiles.createTempFile("cert", ".pem");
339
+ fs.writeFileSync(certFile, sslOptions.cert);
340
+ this.addArg("--cert", certFile);
341
+ }
342
+ }
343
+ if (sslOptions.key) {
344
+ if (typeof sslOptions.key === "string") {
345
+ this.addArg("--key", sslOptions.key);
346
+ } else {
347
+ const keyFile = this.tempFiles.createTempFile("key", ".pem");
348
+ fs.writeFileSync(keyFile, sslOptions.key);
349
+ this.addArg("--key", keyFile);
350
+ }
351
+ }
352
+ if (sslOptions.ca) {
353
+ const caData = Array.isArray(sslOptions.ca) ? sslOptions.ca.join(`
354
+ `) : sslOptions.ca;
355
+ if (typeof caData === "string") {
356
+ this.addArg("--cacert", caData);
357
+ } else {
358
+ const caFile = this.tempFiles.createTempFile("ca", ".pem");
359
+ fs.writeFileSync(caFile, caData);
360
+ this.addArg("--cacert", caFile);
361
+ }
362
+ }
363
+ if (sslOptions.passphrase) {
364
+ this.addArg("--pass", sslOptions.passphrase);
365
+ }
366
+ if (sslOptions.ciphers) {
367
+ this.addArg("--ciphers", sslOptions.ciphers);
368
+ }
369
+ }
370
+ buildProxyConfig(config) {
371
+ if (!config.proxy) {
372
+ return;
373
+ }
374
+ const proxy = config.proxy;
375
+ if (typeof proxy === "string") {
376
+ try {
377
+ const url = new URL(proxy);
378
+ this.configureProxyByProtocol(url.protocol.replace(":", ""), url.hostname, url.port, url.username, url.password);
379
+ } catch (error) {
380
+ this.addArg("--proxy", proxy);
381
+ }
382
+ return;
383
+ }
384
+ const proxyOpts = proxy;
385
+ this.configureProxyByProtocol(proxyOpts.protocol, proxyOpts.host, proxyOpts.port?.toString(), proxyOpts.auth?.username, proxyOpts.auth?.password);
386
+ }
387
+ configureProxyByProtocol(protocol, host, port, username, password) {
388
+ const portNum = port || this.getDefaultProxyPort(protocol);
389
+ const hostPort = `${host}:${portNum}`;
390
+ const auth = username && password ? `${username}:${password}` : undefined;
391
+ switch (protocol.toLowerCase()) {
392
+ case "socks":
393
+ case "socks5":
394
+ case "socks5h":
395
+ this.addArg("--socks5", hostPort);
396
+ if (auth) {
397
+ this.addArg("--socks5-user", auth);
398
+ }
399
+ break;
400
+ case "socks4":
401
+ case "socks4a":
402
+ this.addArg("--socks4", hostPort);
403
+ if (username) {
404
+ this.addArg("--socks4-user", username);
405
+ }
406
+ break;
407
+ case "http":
408
+ case "https":
409
+ const proxyUrl = `${protocol}://${host}:${portNum}`;
410
+ this.addArg("--proxy", proxyUrl);
411
+ if (auth) {
412
+ this.addArg("--proxy-user", auth);
413
+ }
414
+ break;
415
+ default:
416
+ const defaultUrl = `http://${host}:${portNum}`;
417
+ this.addArg("--proxy", defaultUrl);
418
+ if (auth) {
419
+ this.addArg("--proxy-user", auth);
420
+ }
421
+ break;
422
+ }
423
+ }
424
+ getDefaultProxyPort(protocol) {
425
+ switch (protocol.toLowerCase()) {
426
+ case "socks":
427
+ case "socks4":
428
+ case "socks4a":
429
+ case "socks5":
430
+ case "socks5h":
431
+ return "1080";
432
+ case "https":
433
+ return "443";
434
+ case "http":
435
+ default:
436
+ return "8080";
437
+ }
438
+ }
439
+ buildCookieConfig(config) {
440
+ if (!config.enableCookieJar) {
441
+ return;
442
+ }
443
+ const cookieJarFile = this.tempFiles.createTempFile("cookies", ".txt");
444
+ fs.writeFileSync(cookieJarFile, "");
445
+ this.addArg("-b", cookieJarFile);
446
+ this.addArg("-c", cookieJarFile);
447
+ if (config.requestCookies && config.requestCookies.length > 0) {
448
+ const cookieString = config.requestCookies.map((c) => `${c.key}=${c.value}`).join("; ");
449
+ this.addArg("-b", cookieString);
450
+ }
451
+ return cookieJarFile;
452
+ }
453
+ buildConnectionOptions(config, originalRequest) {
454
+ if (originalRequest.keepAlive === false) {
455
+ this.addArg("-H", "Connection: close");
456
+ } else {
457
+ this.addArg("-H", "Connection: keep-alive");
458
+ }
459
+ }
460
+ buildDownloadOptions(config, originalRequest, tempFiles) {
461
+ const saveTo = originalRequest.saveTo || config.fileName;
462
+ if (saveTo) {
463
+ this.addArg("-o", saveTo);
464
+ }
465
+ }
466
+ buildHeaders(config) {
467
+ if (!config.headers) {
468
+ return;
469
+ }
470
+ const headers = config.headers;
471
+ if (headers instanceof RezoHeaders) {
472
+ for (const [key, value] of headers.entries()) {
473
+ if (value !== undefined && value !== null) {
474
+ this.addArg("-H", `${key}: ${value}`);
475
+ }
476
+ }
477
+ } else if (typeof headers === "object") {
478
+ for (const [key, value] of Object.entries(headers)) {
479
+ if (value !== undefined && value !== null) {
480
+ const headerValue = Array.isArray(value) ? value.join(", ") : String(value);
481
+ this.addArg("-H", `${key}: ${headerValue}`);
482
+ }
483
+ }
484
+ }
485
+ }
486
+ buildRedirectOptions(config, originalRequest) {
487
+ const followRedirects = originalRequest.followRedirects !== false;
488
+ if (followRedirects) {
489
+ this.addArg("-L");
490
+ if (config.maxRedirects && config.maxRedirects > 0) {
491
+ this.addArg("--max-redirs", config.maxRedirects.toString());
492
+ }
493
+ } else {
494
+ this.addArg("--max-redirs", "0");
495
+ }
496
+ }
497
+ buildRequestBody(config, tempFiles) {
498
+ if (!config.data) {
499
+ return;
500
+ }
501
+ const data = config.data;
502
+ if (typeof data === "string") {
503
+ this.addArg("-d", data);
504
+ } else if (Buffer.isBuffer(data)) {
505
+ const dataFile = this.tempFiles.createTempFile("data", ".bin");
506
+ fs.writeFileSync(dataFile, data);
507
+ tempFiles.push(dataFile);
508
+ this.addArg("--data-binary", `@${dataFile}`);
509
+ } else if (data instanceof RezoFormData) {
510
+ const formData = data;
511
+ const knownLength = formData.getLengthSync?.() || 0;
512
+ const formDataFile = this.tempFiles.createTempFile("formdata", ".txt");
513
+ const formBuffer = formData.getBuffer?.();
514
+ if (formBuffer) {
515
+ fs.writeFileSync(formDataFile, formBuffer);
516
+ tempFiles.push(formDataFile);
517
+ this.addArg("--data-binary", `@${formDataFile}`);
518
+ const boundary = formData.getBoundary?.();
519
+ if (boundary) {
520
+ this.addArg("-H", `Content-Type: multipart/form-data; boundary=${boundary}`);
521
+ }
522
+ }
523
+ } else if (data instanceof Readable) {
524
+ this.addArg("-d", "@-");
525
+ } else if (typeof data === "object") {
526
+ this.addArg("-d", JSON.stringify(data));
527
+ }
528
+ }
529
+ buildWriteOutFormat() {
530
+ return [
531
+ "\\n---CURL_STATS_START---",
532
+ "http_code:%{http_code}",
533
+ "time_namelookup:%{time_namelookup}",
534
+ "time_connect:%{time_connect}",
535
+ "time_appconnect:%{time_appconnect}",
536
+ "time_pretransfer:%{time_pretransfer}",
537
+ "time_starttransfer:%{time_starttransfer}",
538
+ "time_total:%{time_total}",
539
+ "size_download:%{size_download}",
540
+ "size_upload:%{size_upload}",
541
+ "speed_download:%{speed_download}",
542
+ "speed_upload:%{speed_upload}",
543
+ "remote_ip:%{remote_ip}",
544
+ "remote_port:%{remote_port}",
545
+ "local_ip:%{local_ip}",
546
+ "local_port:%{local_port}",
547
+ "redirect_url:%{redirect_url}",
548
+ "ssl_verify_result:%{ssl_verify_result}",
549
+ "content_type:%{content_type}",
550
+ "---CURL_STATS_END---"
551
+ ].join("\\n");
552
+ }
553
+ }
554
+
555
+ class CurlResponseParser {
556
+ static parse(stdout, stderr, config, originalRequest) {
557
+ const statsMarker = "---CURL_STATS_START---";
558
+ const statsEndMarker = "---CURL_STATS_END---";
559
+ let body = stdout;
560
+ let stats = {};
561
+ const statsStart = stdout.indexOf(statsMarker);
562
+ const statsEnd = stdout.indexOf(statsEndMarker);
563
+ if (statsStart !== -1 && statsEnd !== -1) {
564
+ const statsSection = stdout.slice(statsStart + statsMarker.length, statsEnd);
565
+ body = stdout.slice(0, statsStart);
566
+ for (const line of statsSection.split(`
567
+ `)) {
568
+ const [key, value] = line.split(":");
569
+ if (key && value !== undefined) {
570
+ stats[key.trim()] = value.trim();
571
+ }
572
+ }
573
+ }
574
+ const headerBodySplit = body.indexOf(`\r
575
+ \r
576
+ `);
577
+ let headerSection = "";
578
+ let responseBody = body;
579
+ if (headerBodySplit !== -1) {
580
+ headerSection = body.slice(0, headerBodySplit);
581
+ responseBody = body.slice(headerBodySplit + 4);
582
+ }
583
+ const statusMatch = headerSection.match(/HTTP\/[\d.]+ (\d+)/);
584
+ const status = statusMatch ? parseInt(statusMatch[1]) : parseInt(stats["http_code"]) || 200;
585
+ const statusText = this.getStatusText(status);
586
+ const headers = this.parseHeaders(headerSection);
587
+ const rezoHeaders = new RezoHeaders(headers);
588
+ const cookies = this.parseCookies(headers);
589
+ let data;
590
+ const contentType = stats["content_type"] || rezoHeaders.get("content-type") || "";
591
+ const responseType = config.responseType || originalRequest.responseType || "auto";
592
+ if (responseType === "json" || responseType === "auto" && contentType.includes("application/json")) {
593
+ try {
594
+ data = JSON.parse(responseBody.trim());
595
+ } catch {
596
+ data = responseBody;
597
+ }
598
+ } else if (responseType === "buffer" || responseType === "binary" || responseType === "arrayBuffer") {
599
+ data = Buffer.from(responseBody);
600
+ } else {
601
+ data = responseBody;
602
+ }
603
+ const totalMs = parseFloat(stats["time_total"]) * 1000 || 0;
604
+ const timing = {
605
+ startTimestamp: config.timing?.startTimestamp || Date.now(),
606
+ endTimestamp: Date.now(),
607
+ dnsMs: parseFloat(stats["time_namelookup"]) * 1000 || 0,
608
+ tcpMs: (parseFloat(stats["time_connect"]) - parseFloat(stats["time_namelookup"])) * 1000 || 0,
609
+ tlsMs: (parseFloat(stats["time_appconnect"]) - parseFloat(stats["time_connect"])) * 1000 || 0,
610
+ ttfbMs: parseFloat(stats["time_starttransfer"]) * 1000 || 0,
611
+ transferMs: (parseFloat(stats["time_total"]) - parseFloat(stats["time_starttransfer"])) * 1000 || 0,
612
+ durationMs: totalMs
613
+ };
614
+ config.timing = timing;
615
+ const urls = [config.url];
616
+ if (stats["redirect_url"]) {
617
+ urls.push(stats["redirect_url"]);
618
+ }
619
+ return {
620
+ data,
621
+ status,
622
+ statusText,
623
+ headers: rezoHeaders,
624
+ cookies,
625
+ config,
626
+ contentType: contentType || undefined,
627
+ contentLength: parseInt(stats["size_download"]) || responseBody.length,
628
+ finalUrl: stats["redirect_url"] || config.url,
629
+ urls
630
+ };
631
+ }
632
+ static parseHeaders(headerSection) {
633
+ const headers = {};
634
+ const headerLines = headerSection.split(`
635
+ `).slice(1);
636
+ for (const line of headerLines) {
637
+ const colonIndex = line.indexOf(":");
638
+ if (colonIndex > 0) {
639
+ const key = line.slice(0, colonIndex).trim().toLowerCase();
640
+ const value = line.slice(colonIndex + 1).trim();
641
+ const existing = headers[key];
642
+ if (existing !== undefined) {
643
+ headers[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
644
+ } else {
645
+ headers[key] = value;
646
+ }
647
+ }
648
+ }
649
+ return headers;
650
+ }
651
+ static getStatusText(status) {
652
+ const statusTexts = {
653
+ 200: "OK",
654
+ 201: "Created",
655
+ 204: "No Content",
656
+ 301: "Moved Permanently",
657
+ 302: "Found",
658
+ 304: "Not Modified",
659
+ 400: "Bad Request",
660
+ 401: "Unauthorized",
661
+ 403: "Forbidden",
662
+ 404: "Not Found",
663
+ 405: "Method Not Allowed",
664
+ 429: "Too Many Requests",
665
+ 500: "Internal Server Error",
666
+ 502: "Bad Gateway",
667
+ 503: "Service Unavailable",
668
+ 504: "Gateway Timeout"
669
+ };
670
+ return statusTexts[status] || "Unknown";
671
+ }
672
+ static parseCookies(headers) {
673
+ const setCookieHeaders = headers["set-cookie"];
674
+ const cookieArray = [];
675
+ if (setCookieHeaders) {
676
+ if (Array.isArray(setCookieHeaders)) {
677
+ cookieArray.push(...setCookieHeaders);
678
+ } else {
679
+ cookieArray.push(setCookieHeaders);
680
+ }
681
+ }
682
+ const parsedCookies = cookieArray.map((c) => Cookie.parse(c)).filter(Boolean);
683
+ return {
684
+ array: parsedCookies,
685
+ serialized: parsedCookies.map((c) => c.toJSON()),
686
+ netscape: "",
687
+ string: parsedCookies.map((c) => `${c.key}=${c.value}`).join("; "),
688
+ setCookiesString: cookieArray
689
+ };
690
+ }
691
+ }
692
+
693
+ class CurlExecutor {
694
+ tempFileManager;
695
+ capabilities;
696
+ constructor() {
697
+ this.tempFileManager = new TempFileManager;
698
+ this.capabilities = CurlCapabilities.getInstance();
699
+ }
700
+ async execute(config, originalRequest, streamResult, downloadResult, uploadResult) {
701
+ await this.capabilities.initialize();
702
+ const builder = new CurlCommandBuilder(this.tempFileManager, this.capabilities);
703
+ const { args, tempFiles, cookieJar } = builder.build(config, originalRequest);
704
+ const finalUrl = this.buildFinalUrl(config);
705
+ args.push(finalUrl);
706
+ try {
707
+ return await this.executeCurlCommand(args, config, originalRequest, tempFiles, cookieJar, streamResult, downloadResult, uploadResult);
708
+ } finally {
709
+ this.tempFileManager.cleanup();
710
+ }
711
+ }
712
+ buildFinalUrl(config) {
713
+ let url = config.url;
714
+ if (config.baseURL) {
715
+ url = config.baseURL.replace(/\/$/, "") + "/" + url.replace(/^\//, "");
716
+ }
717
+ if (config.params && Object.keys(config.params).length > 0) {
718
+ const urlParams = new URLSearchParams;
719
+ for (const [key, value] of Object.entries(config.params)) {
720
+ if (value !== undefined && value !== null) {
721
+ if (Array.isArray(value)) {
722
+ for (const item of value) {
723
+ urlParams.append(key, String(item));
724
+ }
725
+ } else {
726
+ urlParams.append(key, String(value));
727
+ }
728
+ }
729
+ }
730
+ const paramString = urlParams.toString();
731
+ if (paramString) {
732
+ url += (url.includes("?") ? "&" : "?") + paramString;
733
+ }
734
+ }
735
+ return url;
736
+ }
737
+ async executeCurlCommand(args, config, originalRequest, tempFiles, cookieJar, streamResult, downloadResult, uploadResult) {
738
+ return new Promise((resolve, reject) => {
739
+ const isStreaming = !!streamResult;
740
+ const isDownload = !!downloadResult;
741
+ const isUpload = !!uploadResult;
742
+ const curl = spawn("curl", args, {
743
+ stdio: ["pipe", "pipe", "pipe"]
744
+ });
745
+ let stdout = "";
746
+ let stderr = "";
747
+ const progressTracker = new CurlProgressTracker;
748
+ const startTime = performance.now();
749
+ if (originalRequest.onDownloadProgress) {
750
+ progressTracker.on("progress", (progress) => {
751
+ originalRequest.onDownloadProgress(progress);
752
+ if (streamResult) {
753
+ streamResult.emit("progress", progress);
754
+ }
755
+ if (downloadResult) {
756
+ downloadResult.emit("progress", progress);
757
+ }
758
+ });
759
+ }
760
+ if (originalRequest.onUploadProgress) {
761
+ progressTracker.on("progress", (progress) => {
762
+ if (progress.loaded > 0) {
763
+ originalRequest.onUploadProgress(progress);
764
+ if (uploadResult) {
765
+ uploadResult.emit("progress", progress);
766
+ }
767
+ }
768
+ });
769
+ }
770
+ curl.stdout.on("data", (chunk) => {
771
+ if (isStreaming && streamResult) {
772
+ streamResult.emit("data", chunk);
773
+ stdout += chunk.toString();
774
+ } else {
775
+ stdout += chunk.toString();
776
+ }
777
+ });
778
+ curl.stderr.on("data", (chunk) => {
779
+ const data = chunk.toString();
780
+ stderr += data;
781
+ const lines = data.split(`
782
+ `);
783
+ for (const line of lines) {
784
+ progressTracker.parseProgress(line);
785
+ }
786
+ });
787
+ curl.on("error", (error) => {
788
+ const rezoError = buildSmartError(config, originalRequest, error);
789
+ if (streamResult) {
790
+ streamResult.emit("error", rezoError);
791
+ }
792
+ if (downloadResult) {
793
+ downloadResult.emit("error", rezoError);
794
+ }
795
+ if (uploadResult) {
796
+ uploadResult.emit("error", rezoError);
797
+ }
798
+ reject(rezoError);
799
+ });
800
+ curl.on("close", (code) => {
801
+ try {
802
+ if (code !== 0 && code !== null) {
803
+ const errorCode = this.mapCurlErrorCode(code);
804
+ const errorMessage = this.buildDetailedErrorMessage(code, stderr, config);
805
+ const rezoError = new RezoError(errorMessage, config, errorCode);
806
+ if (streamResult)
807
+ streamResult.emit("error", rezoError);
808
+ if (downloadResult)
809
+ downloadResult.emit("error", rezoError);
810
+ if (uploadResult)
811
+ uploadResult.emit("error", rezoError);
812
+ reject(rezoError);
813
+ return;
814
+ }
815
+ const response = CurlResponseParser.parse(stdout, stderr, config, originalRequest);
816
+ if (isStreaming && streamResult) {
817
+ const finishEvent = {
818
+ status: response.status,
819
+ statusText: response.statusText,
820
+ headers: response.headers,
821
+ contentType: response.contentType,
822
+ contentLength: response.contentLength,
823
+ finalUrl: response.finalUrl,
824
+ cookies: response.cookies,
825
+ urls: response.urls,
826
+ timing: {
827
+ total: performance.now() - startTime,
828
+ firstByte: config.timing?.ttfbMs,
829
+ dns: config.timing?.dnsMs,
830
+ tcp: config.timing?.tcpMs,
831
+ tls: config.timing?.tlsMs,
832
+ download: config.timing?.transferMs
833
+ },
834
+ config
835
+ };
836
+ streamResult.emit("finish", finishEvent);
837
+ streamResult.emit("done", finishEvent);
838
+ resolve(streamResult);
839
+ return;
840
+ }
841
+ if (isDownload && downloadResult) {
842
+ const fileName = config.fileName || originalRequest.saveTo || "";
843
+ const fileSize = response.contentLength;
844
+ const finishEvent = {
845
+ status: response.status,
846
+ statusText: response.statusText,
847
+ headers: response.headers,
848
+ contentType: response.contentType,
849
+ contentLength: fileSize,
850
+ finalUrl: response.finalUrl,
851
+ cookies: response.cookies,
852
+ urls: response.urls,
853
+ fileName,
854
+ fileSize,
855
+ timing: {
856
+ total: performance.now() - startTime,
857
+ firstByte: config.timing?.ttfbMs,
858
+ dns: config.timing?.dnsMs,
859
+ tcp: config.timing?.tcpMs,
860
+ tls: config.timing?.tlsMs,
861
+ download: performance.now() - startTime
862
+ },
863
+ averageSpeed: fileSize / ((performance.now() - startTime) / 1000),
864
+ config
865
+ };
866
+ downloadResult.emit("finish", finishEvent);
867
+ downloadResult.emit("done", finishEvent);
868
+ resolve(downloadResult);
869
+ return;
870
+ }
871
+ if (isUpload && uploadResult) {
872
+ const finishEvent = {
873
+ response: {
874
+ status: response.status,
875
+ statusText: response.statusText,
876
+ headers: response.headers,
877
+ data: response.data,
878
+ contentType: response.contentType,
879
+ contentLength: response.contentLength
880
+ },
881
+ finalUrl: response.finalUrl,
882
+ cookies: response.cookies,
883
+ urls: response.urls,
884
+ uploadSize: config.transfer?.requestSize || 0,
885
+ timing: {
886
+ total: performance.now() - startTime,
887
+ dns: config.timing?.dnsMs,
888
+ tcp: config.timing?.tcpMs,
889
+ tls: config.timing?.tlsMs,
890
+ upload: performance.now() - startTime,
891
+ waiting: 0
892
+ },
893
+ averageUploadSpeed: (config.transfer?.requestSize || 0) / ((performance.now() - startTime) / 1000),
894
+ config
895
+ };
896
+ uploadResult.emit("finish", finishEvent);
897
+ uploadResult.emit("done", finishEvent);
898
+ resolve(uploadResult);
899
+ return;
900
+ }
901
+ resolve(response);
902
+ } catch (error) {
903
+ const rezoError = buildSmartError(config, originalRequest, error);
904
+ if (streamResult)
905
+ streamResult.emit("error", rezoError);
906
+ if (downloadResult)
907
+ downloadResult.emit("error", rezoError);
908
+ if (uploadResult)
909
+ uploadResult.emit("error", rezoError);
910
+ reject(rezoError);
911
+ }
912
+ });
913
+ if (config.signal) {
914
+ const abortHandler = () => {
915
+ curl.kill("SIGKILL");
916
+ const abortError = RezoError.createAbortError("Request aborted", config);
917
+ if (streamResult)
918
+ streamResult.emit("error", abortError);
919
+ if (downloadResult)
920
+ downloadResult.emit("error", abortError);
921
+ if (uploadResult)
922
+ uploadResult.emit("error", abortError);
923
+ reject(abortError);
924
+ };
925
+ if (config.signal.aborted) {
926
+ abortHandler();
927
+ return;
928
+ }
929
+ config.signal.addEventListener("abort", abortHandler);
930
+ }
931
+ if (config.data && config.data instanceof Readable) {
932
+ config.data.pipe(curl.stdin);
933
+ } else {
934
+ curl.stdin.end();
935
+ }
936
+ });
937
+ }
938
+ mapCurlErrorCode(code) {
939
+ const errorMap = {
940
+ 1: "ERR_INVALID_PROTOCOL",
941
+ 2: "REZ_UNKNOWN_ERROR",
942
+ 3: "ERR_INVALID_URL",
943
+ 5: "REZ_PROXY_CONNECTION_FAILED",
944
+ 6: "ENOTFOUND",
945
+ 7: "ECONNREFUSED",
946
+ 22: "REZ_HTTP_ERROR",
947
+ 28: "ETIMEDOUT",
948
+ 35: "ERR_TLS_HANDSHAKE_TIMEOUT",
949
+ 51: "ERR_TLS_CERT_ALTNAME_INVALID",
950
+ 52: "ERR_STREAM_DESTROYED",
951
+ 55: "ERR_STREAM_PREMATURE_CLOSE",
952
+ 56: "ERR_STREAM_DESTROYED",
953
+ 58: "REZ_PROXY_AUTHENTICATION_FAILED",
954
+ 60: "CERT_HAS_EXPIRED",
955
+ 77: "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
956
+ 91: "REZ_SOCKS_PROTOCOL_ERROR",
957
+ 97: "REZ_PROXY_TARGET_UNREACHABLE"
958
+ };
959
+ return errorMap[code] || "REZ_UNKNOWN_ERROR";
960
+ }
961
+ buildDetailedErrorMessage(code, stderr, config) {
962
+ const baseMessage = `cURL request failed (exit code ${code})`;
963
+ const stderrLines = stderr.split(`
964
+ `).filter((line) => line.trim());
965
+ const errorLines = stderrLines.filter((line) => line.includes("curl:") || line.includes("error:") || line.includes("failed:") || line.includes("timeout") || line.includes("refused") || line.includes("not found"));
966
+ if (errorLines.length > 0) {
967
+ const primaryError = errorLines[0].replace(/^curl:\s*\(\d+\)\s*/, "").trim();
968
+ return `${baseMessage}: ${primaryError}`;
969
+ }
970
+ return `${baseMessage} for ${config.method?.toUpperCase() || "GET"} ${config.url}`;
971
+ }
972
+ }
973
+ async function executeRequest(options, defaultOptions, jar) {
974
+ if (!options.responseType) {
975
+ options.responseType = "auto";
976
+ }
977
+ const d_options = await getDefaultConfig(defaultOptions);
978
+ const configResult = prepareHTTPOptions(options, jar, { defaultOptions: d_options });
979
+ const config = configResult.config;
980
+ const originalRequest = configResult.fetchOptions;
981
+ const perform = new RezoPerformance;
982
+ const isStream = options._isStream;
983
+ const isDownload = options._isDownload || !!options.fileName || !!options.saveTo;
984
+ const isUpload = options._isUpload;
985
+ let streamResponse;
986
+ let downloadResponse;
987
+ let uploadResponse;
988
+ if (isStream) {
989
+ streamResponse = new StreamResponse;
990
+ } else if (isDownload) {
991
+ const fileName = options.fileName || options.saveTo || "";
992
+ const url = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url.toString();
993
+ downloadResponse = new DownloadResponse(fileName, url);
994
+ } else if (isUpload) {
995
+ const url = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url.toString();
996
+ uploadResponse = new UploadResponse(url);
997
+ }
998
+ const eventEmitter = streamResponse || downloadResponse || uploadResponse;
999
+ if (eventEmitter) {
1000
+ eventEmitter.emit("initiated");
1001
+ }
1002
+ const executor = new CurlExecutor;
1003
+ try {
1004
+ const result = await executor.execute(config, originalRequest, streamResponse, downloadResponse, uploadResponse);
1005
+ if (!streamResponse && !downloadResponse && !uploadResponse) {
1006
+ const response = result;
1007
+ if (config.retry && response.status >= 400) {
1008
+ const maxRetries = config.retry.maxRetries || 0;
1009
+ const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
1010
+ if (config.retryAttempts < maxRetries && statusCodes.includes(response.status)) {
1011
+ const retryDelay = config.retry.retryDelay || 0;
1012
+ const incrementDelay = config.retry.incrementDelay || false;
1013
+ const delay = incrementDelay ? retryDelay * (config.retryAttempts + 1) : retryDelay;
1014
+ if (delay > 0) {
1015
+ await new Promise((resolve) => setTimeout(resolve, delay));
1016
+ }
1017
+ config.retryAttempts++;
1018
+ return executeRequest(options, defaultOptions, jar);
1019
+ }
1020
+ }
1021
+ }
1022
+ return result;
1023
+ } catch (error) {
1024
+ if (error instanceof RezoError) {
1025
+ throw error;
1026
+ }
1027
+ throw buildSmartError(config, originalRequest, error);
1028
+ }
1029
+ }
1030
+
1031
+ exports.CurlCapabilities = CurlCapabilities;
1032
+ exports.CurlExecutor = CurlExecutor;
1033
+ exports.CurlCommandBuilder = CurlCommandBuilder;
1034
+ exports.executeRequest = executeRequest;