rezo 1.0.12 → 1.0.14

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 (44) hide show
  1. package/dist/adapters/curl.cjs +89 -22
  2. package/dist/adapters/curl.js +89 -22
  3. package/dist/adapters/entries/curl.d.ts +8 -0
  4. package/dist/adapters/entries/fetch.d.ts +8 -0
  5. package/dist/adapters/entries/http.d.ts +8 -0
  6. package/dist/adapters/entries/http2.d.ts +8 -0
  7. package/dist/adapters/entries/react-native.d.ts +8 -0
  8. package/dist/adapters/entries/xhr.d.ts +8 -0
  9. package/dist/adapters/fetch.cjs +128 -58
  10. package/dist/adapters/fetch.js +128 -58
  11. package/dist/adapters/http.cjs +43 -13
  12. package/dist/adapters/http.js +43 -13
  13. package/dist/adapters/http2.cjs +136 -58
  14. package/dist/adapters/http2.js +136 -58
  15. package/dist/adapters/index.cjs +6 -6
  16. package/dist/cache/index.cjs +13 -13
  17. package/dist/core/rezo.cjs +27 -6
  18. package/dist/core/rezo.js +27 -6
  19. package/dist/crawler.d.ts +8 -0
  20. package/dist/entries/crawler.cjs +5 -5
  21. package/dist/index.cjs +24 -24
  22. package/dist/index.d.ts +8 -0
  23. package/dist/platform/browser.d.ts +8 -0
  24. package/dist/platform/bun.d.ts +8 -0
  25. package/dist/platform/deno.d.ts +8 -0
  26. package/dist/platform/node.d.ts +8 -0
  27. package/dist/platform/react-native.d.ts +8 -0
  28. package/dist/platform/worker.d.ts +8 -0
  29. package/dist/plugin/index.cjs +36 -36
  30. package/dist/proxy/index.cjs +2 -2
  31. package/dist/queue/index.cjs +8 -8
  32. package/dist/responses/buildError.cjs +5 -1
  33. package/dist/responses/buildError.js +5 -1
  34. package/dist/responses/buildResponse.cjs +2 -0
  35. package/dist/responses/buildResponse.js +2 -0
  36. package/dist/utils/compression.cjs +6 -6
  37. package/dist/utils/compression.js +6 -6
  38. package/dist/utils/form-data.cjs +64 -7
  39. package/dist/utils/form-data.js +64 -7
  40. package/dist/utils/headers.cjs +17 -0
  41. package/dist/utils/headers.js +17 -1
  42. package/dist/utils/http-config.cjs +39 -4
  43. package/dist/utils/http-config.js +39 -4
  44. package/package.json +1 -1
@@ -6,8 +6,8 @@ const { spawn, execSync } = require("node:child_process");
6
6
  const { Readable } = require("node:stream");
7
7
  const { EventEmitter } = require("node:events");
8
8
  const { RezoError } = require('../errors/rezo-error.cjs');
9
- const { buildSmartError } = require('../responses/buildError.cjs');
10
- const { Cookie } = require('../utils/cookies.cjs');
9
+ const { buildSmartError, builErrorFromResponse } = require('../responses/buildError.cjs');
10
+ const { RezoCookieJar, Cookie } = require('../utils/cookies.cjs');
11
11
  const RezoFormData = require('../utils/form-data.cjs');
12
12
  const { existsSync } = require("node:fs");
13
13
  const { getDefaultConfig, prepareHTTPOptions } = require('../utils/http-config.cjs');
@@ -16,6 +16,24 @@ const { StreamResponse } = require('../responses/stream.cjs');
16
16
  const { DownloadResponse } = require('../responses/download.cjs');
17
17
  const { UploadResponse } = require('../responses/upload.cjs');
18
18
  const { RezoPerformance } = require('../utils/tools.cjs');
19
+ function mergeRequestAndResponseCookies(requestCookies, responseCookies) {
20
+ if (!requestCookies || requestCookies.length === 0) {
21
+ return responseCookies;
22
+ }
23
+ if (responseCookies.length === 0) {
24
+ return requestCookies;
25
+ }
26
+ const cookieMap = new Map;
27
+ for (const cookie of requestCookies) {
28
+ const key = `${cookie.key}|${cookie.domain || ""}`;
29
+ cookieMap.set(key, cookie);
30
+ }
31
+ for (const cookie of responseCookies) {
32
+ const key = `${cookie.key}|${cookie.domain || ""}`;
33
+ cookieMap.set(key, cookie);
34
+ }
35
+ return Array.from(cookieMap.values());
36
+ }
19
37
 
20
38
  class CurlCapabilities {
21
39
  static instance;
@@ -261,7 +279,7 @@ class CurlCommandBuilder {
261
279
  this.buildDownloadOptions(config, originalRequest, createdTempFiles);
262
280
  this.buildHeaders(config);
263
281
  this.buildRedirectOptions(config, originalRequest);
264
- this.buildRequestBody(config, createdTempFiles);
282
+ this.buildRequestBody(config, originalRequest, createdTempFiles);
265
283
  if (originalRequest.onUploadProgress || originalRequest.onDownloadProgress) {
266
284
  this.addArg("--progress-bar");
267
285
  }
@@ -1290,11 +1308,11 @@ class CurlCommandBuilder {
1290
1308
  this.addArg("--max-redirs", "0");
1291
1309
  }
1292
1310
  }
1293
- buildRequestBody(config, tempFiles) {
1294
- if (!config.data) {
1311
+ buildRequestBody(config, originalRequest, tempFiles) {
1312
+ const data = originalRequest.body ?? config.data;
1313
+ if (!data) {
1295
1314
  return;
1296
1315
  }
1297
- const data = config.data;
1298
1316
  if (typeof data === "string") {
1299
1317
  this.addArg("-d", data);
1300
1318
  } else if (Buffer.isBuffer(data)) {
@@ -1304,7 +1322,6 @@ class CurlCommandBuilder {
1304
1322
  this.addArg("--data-binary", `@${dataFile}`);
1305
1323
  } else if (data instanceof RezoFormData) {
1306
1324
  const formData = data;
1307
- const knownLength = formData.getLengthSync?.() || 0;
1308
1325
  const formDataFile = this.tempFiles.createTempFile("formdata", ".txt");
1309
1326
  const formBuffer = formData.getBuffer?.();
1310
1327
  if (formBuffer) {
@@ -1315,6 +1332,7 @@ class CurlCommandBuilder {
1315
1332
  if (boundary) {
1316
1333
  this.addArg("-H", `Content-Type: multipart/form-data; boundary=${boundary}`);
1317
1334
  }
1335
+ this.addArg("-H", `Content-Length: ${formBuffer.length}`);
1318
1336
  }
1319
1337
  } else if (data instanceof Readable) {
1320
1338
  this.addArg("-d", "@-");
@@ -1324,7 +1342,8 @@ class CurlCommandBuilder {
1324
1342
  }
1325
1343
  buildWriteOutFormat() {
1326
1344
  return [
1327
- "\\n---CURL_STATS_START---",
1345
+ `
1346
+ ---CURL_STATS_START---`,
1328
1347
  "http_code:%{http_code}",
1329
1348
  "time_namelookup:%{time_namelookup}",
1330
1349
  "time_connect:%{time_connect}",
@@ -1344,7 +1363,8 @@ class CurlCommandBuilder {
1344
1363
  "ssl_verify_result:%{ssl_verify_result}",
1345
1364
  "content_type:%{content_type}",
1346
1365
  "---CURL_STATS_END---"
1347
- ].join("\\n");
1366
+ ].join(`
1367
+ `);
1348
1368
  }
1349
1369
  }
1350
1370
 
@@ -1381,7 +1401,24 @@ class CurlResponseParser {
1381
1401
  const statusText = this.getStatusText(status);
1382
1402
  const headers = this.parseHeaders(headerSection);
1383
1403
  const rezoHeaders = new RezoHeaders(headers);
1384
- const cookies = this.parseCookies(headers);
1404
+ const responseCookies = this.parseCookies(headers);
1405
+ const mergedCookieArray = mergeRequestAndResponseCookies(config.requestCookies, responseCookies.array);
1406
+ let cookies;
1407
+ if (mergedCookieArray.length > 0) {
1408
+ const mergedJar = new RezoCookieJar(mergedCookieArray, config.url || "");
1409
+ cookies = mergedJar.cookies();
1410
+ } else {
1411
+ cookies = {
1412
+ array: [],
1413
+ serialized: [],
1414
+ netscape: `# Netscape HTTP Cookie File
1415
+ # This file was generated by Rezo HTTP client
1416
+ # Based on uniqhtt cookie implementation
1417
+ `,
1418
+ string: "",
1419
+ setCookiesString: []
1420
+ };
1421
+ }
1385
1422
  let data;
1386
1423
  const contentType = stats["content_type"] || rezoHeaders.get("content-type") || "";
1387
1424
  const responseType = config.responseType || originalRequest.responseType || "auto";
@@ -1408,6 +1445,25 @@ class CurlResponseParser {
1408
1445
  durationMs: totalMs
1409
1446
  };
1410
1447
  config.timing = timing;
1448
+ config.status = status;
1449
+ config.statusText = statusText;
1450
+ const isSecure = config.url?.startsWith("https") || false;
1451
+ config.adapterUsed = "curl";
1452
+ config.isSecure = isSecure;
1453
+ config.finalUrl = stats["redirect_url"] || config.url || "";
1454
+ if (!config.network) {
1455
+ config.network = {};
1456
+ }
1457
+ config.network.protocol = isSecure ? "https" : "http";
1458
+ config.network.httpVersion = headerSection.match(/HTTP\/([\d.]+)/)?.[1] || "1.1";
1459
+ if (!config.transfer) {
1460
+ config.transfer = { requestSize: 0, responseSize: 0, headerSize: 0, bodySize: 0 };
1461
+ }
1462
+ config.transfer.requestSize = parseInt(stats["size_upload"]) || 0;
1463
+ config.transfer.responseSize = parseInt(stats["size_download"]) || responseBody.length;
1464
+ config.transfer.bodySize = responseBody.length;
1465
+ config.transfer.headerSize = headerSection.length;
1466
+ config.responseCookies = cookies;
1411
1467
  const urls = [config.url];
1412
1468
  if (stats["redirect_url"]) {
1413
1469
  urls.push(stats["redirect_url"]);
@@ -1507,7 +1563,7 @@ class CurlExecutor {
1507
1563
  }
1508
1564
  buildFinalUrl(config) {
1509
1565
  let url = config.url;
1510
- if (config.baseURL) {
1566
+ if (config.baseURL && !url.startsWith("http://") && !url.startsWith("https://")) {
1511
1567
  url = config.baseURL.replace(/\/$/, "") + "/" + url.replace(/^\//, "");
1512
1568
  }
1513
1569
  if (config.params && Object.keys(config.params).length > 0) {
@@ -1596,6 +1652,13 @@ class CurlExecutor {
1596
1652
  curl.on("close", (code) => {
1597
1653
  try {
1598
1654
  if (code !== 0 && code !== null) {
1655
+ if (code === 22 && stdout) {
1656
+ try {
1657
+ const response = CurlResponseParser.parse(stdout, stderr, config, originalRequest);
1658
+ resolve(response);
1659
+ return;
1660
+ } catch {}
1661
+ }
1599
1662
  const errorCode = this.mapCurlErrorCode(code);
1600
1663
  const errorMessage = this.buildDetailedErrorMessage(code, stderr, config);
1601
1664
  const rezoError = new RezoError(errorMessage, config, errorCode);
@@ -1843,19 +1906,23 @@ async function executeRequest(options, defaultOptions, jar) {
1843
1906
  if (proxyManager && selectedProxy) {
1844
1907
  proxyManager.reportSuccess(selectedProxy);
1845
1908
  }
1846
- if (config.retry && response.status >= 400) {
1847
- const maxRetries = config.retry.maxRetries || 0;
1848
- const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
1849
- if (config.retryAttempts < maxRetries && statusCodes.includes(response.status)) {
1850
- const retryDelay = config.retry.retryDelay || 0;
1851
- const incrementDelay = config.retry.incrementDelay || false;
1852
- const delay = incrementDelay ? retryDelay * (config.retryAttempts + 1) : retryDelay;
1853
- if (delay > 0) {
1854
- await new Promise((resolve) => setTimeout(resolve, delay));
1909
+ if (response.status >= 400) {
1910
+ const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, originalRequest);
1911
+ if (config.retry) {
1912
+ const maxRetries = config.retry.maxRetries || 0;
1913
+ const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
1914
+ if (config.retryAttempts < maxRetries && statusCodes.includes(response.status)) {
1915
+ const retryDelay = config.retry.retryDelay || 0;
1916
+ const incrementDelay = config.retry.incrementDelay || false;
1917
+ const delay = incrementDelay ? retryDelay * (config.retryAttempts + 1) : retryDelay;
1918
+ if (delay > 0) {
1919
+ await new Promise((resolve) => setTimeout(resolve, delay));
1920
+ }
1921
+ config.retryAttempts++;
1922
+ return executeRequest(options, defaultOptions, jar);
1855
1923
  }
1856
- config.retryAttempts++;
1857
- return executeRequest(options, defaultOptions, jar);
1858
1924
  }
1925
+ throw httpError;
1859
1926
  }
1860
1927
  }
1861
1928
  return result;
@@ -6,8 +6,8 @@ import { spawn, execSync } from "node:child_process";
6
6
  import { Readable } from "node:stream";
7
7
  import { EventEmitter } from "node:events";
8
8
  import { RezoError } from '../errors/rezo-error.js';
9
- import { buildSmartError } from '../responses/buildError.js';
10
- import { Cookie } from '../utils/cookies.js';
9
+ import { buildSmartError, builErrorFromResponse } from '../responses/buildError.js';
10
+ import { RezoCookieJar, Cookie } from '../utils/cookies.js';
11
11
  import RezoFormData from '../utils/form-data.js';
12
12
  import { existsSync } from "node:fs";
13
13
  import { getDefaultConfig, prepareHTTPOptions } from '../utils/http-config.js';
@@ -16,6 +16,24 @@ import { StreamResponse } from '../responses/stream.js';
16
16
  import { DownloadResponse } from '../responses/download.js';
17
17
  import { UploadResponse } from '../responses/upload.js';
18
18
  import { RezoPerformance } from '../utils/tools.js';
19
+ function mergeRequestAndResponseCookies(requestCookies, responseCookies) {
20
+ if (!requestCookies || requestCookies.length === 0) {
21
+ return responseCookies;
22
+ }
23
+ if (responseCookies.length === 0) {
24
+ return requestCookies;
25
+ }
26
+ const cookieMap = new Map;
27
+ for (const cookie of requestCookies) {
28
+ const key = `${cookie.key}|${cookie.domain || ""}`;
29
+ cookieMap.set(key, cookie);
30
+ }
31
+ for (const cookie of responseCookies) {
32
+ const key = `${cookie.key}|${cookie.domain || ""}`;
33
+ cookieMap.set(key, cookie);
34
+ }
35
+ return Array.from(cookieMap.values());
36
+ }
19
37
 
20
38
  class CurlCapabilities {
21
39
  static instance;
@@ -261,7 +279,7 @@ class CurlCommandBuilder {
261
279
  this.buildDownloadOptions(config, originalRequest, createdTempFiles);
262
280
  this.buildHeaders(config);
263
281
  this.buildRedirectOptions(config, originalRequest);
264
- this.buildRequestBody(config, createdTempFiles);
282
+ this.buildRequestBody(config, originalRequest, createdTempFiles);
265
283
  if (originalRequest.onUploadProgress || originalRequest.onDownloadProgress) {
266
284
  this.addArg("--progress-bar");
267
285
  }
@@ -1290,11 +1308,11 @@ class CurlCommandBuilder {
1290
1308
  this.addArg("--max-redirs", "0");
1291
1309
  }
1292
1310
  }
1293
- buildRequestBody(config, tempFiles) {
1294
- if (!config.data) {
1311
+ buildRequestBody(config, originalRequest, tempFiles) {
1312
+ const data = originalRequest.body ?? config.data;
1313
+ if (!data) {
1295
1314
  return;
1296
1315
  }
1297
- const data = config.data;
1298
1316
  if (typeof data === "string") {
1299
1317
  this.addArg("-d", data);
1300
1318
  } else if (Buffer.isBuffer(data)) {
@@ -1304,7 +1322,6 @@ class CurlCommandBuilder {
1304
1322
  this.addArg("--data-binary", `@${dataFile}`);
1305
1323
  } else if (data instanceof RezoFormData) {
1306
1324
  const formData = data;
1307
- const knownLength = formData.getLengthSync?.() || 0;
1308
1325
  const formDataFile = this.tempFiles.createTempFile("formdata", ".txt");
1309
1326
  const formBuffer = formData.getBuffer?.();
1310
1327
  if (formBuffer) {
@@ -1315,6 +1332,7 @@ class CurlCommandBuilder {
1315
1332
  if (boundary) {
1316
1333
  this.addArg("-H", `Content-Type: multipart/form-data; boundary=${boundary}`);
1317
1334
  }
1335
+ this.addArg("-H", `Content-Length: ${formBuffer.length}`);
1318
1336
  }
1319
1337
  } else if (data instanceof Readable) {
1320
1338
  this.addArg("-d", "@-");
@@ -1324,7 +1342,8 @@ class CurlCommandBuilder {
1324
1342
  }
1325
1343
  buildWriteOutFormat() {
1326
1344
  return [
1327
- "\\n---CURL_STATS_START---",
1345
+ `
1346
+ ---CURL_STATS_START---`,
1328
1347
  "http_code:%{http_code}",
1329
1348
  "time_namelookup:%{time_namelookup}",
1330
1349
  "time_connect:%{time_connect}",
@@ -1344,7 +1363,8 @@ class CurlCommandBuilder {
1344
1363
  "ssl_verify_result:%{ssl_verify_result}",
1345
1364
  "content_type:%{content_type}",
1346
1365
  "---CURL_STATS_END---"
1347
- ].join("\\n");
1366
+ ].join(`
1367
+ `);
1348
1368
  }
1349
1369
  }
1350
1370
 
@@ -1381,7 +1401,24 @@ class CurlResponseParser {
1381
1401
  const statusText = this.getStatusText(status);
1382
1402
  const headers = this.parseHeaders(headerSection);
1383
1403
  const rezoHeaders = new RezoHeaders(headers);
1384
- const cookies = this.parseCookies(headers);
1404
+ const responseCookies = this.parseCookies(headers);
1405
+ const mergedCookieArray = mergeRequestAndResponseCookies(config.requestCookies, responseCookies.array);
1406
+ let cookies;
1407
+ if (mergedCookieArray.length > 0) {
1408
+ const mergedJar = new RezoCookieJar(mergedCookieArray, config.url || "");
1409
+ cookies = mergedJar.cookies();
1410
+ } else {
1411
+ cookies = {
1412
+ array: [],
1413
+ serialized: [],
1414
+ netscape: `# Netscape HTTP Cookie File
1415
+ # This file was generated by Rezo HTTP client
1416
+ # Based on uniqhtt cookie implementation
1417
+ `,
1418
+ string: "",
1419
+ setCookiesString: []
1420
+ };
1421
+ }
1385
1422
  let data;
1386
1423
  const contentType = stats["content_type"] || rezoHeaders.get("content-type") || "";
1387
1424
  const responseType = config.responseType || originalRequest.responseType || "auto";
@@ -1408,6 +1445,25 @@ class CurlResponseParser {
1408
1445
  durationMs: totalMs
1409
1446
  };
1410
1447
  config.timing = timing;
1448
+ config.status = status;
1449
+ config.statusText = statusText;
1450
+ const isSecure = config.url?.startsWith("https") || false;
1451
+ config.adapterUsed = "curl";
1452
+ config.isSecure = isSecure;
1453
+ config.finalUrl = stats["redirect_url"] || config.url || "";
1454
+ if (!config.network) {
1455
+ config.network = {};
1456
+ }
1457
+ config.network.protocol = isSecure ? "https" : "http";
1458
+ config.network.httpVersion = headerSection.match(/HTTP\/([\d.]+)/)?.[1] || "1.1";
1459
+ if (!config.transfer) {
1460
+ config.transfer = { requestSize: 0, responseSize: 0, headerSize: 0, bodySize: 0 };
1461
+ }
1462
+ config.transfer.requestSize = parseInt(stats["size_upload"]) || 0;
1463
+ config.transfer.responseSize = parseInt(stats["size_download"]) || responseBody.length;
1464
+ config.transfer.bodySize = responseBody.length;
1465
+ config.transfer.headerSize = headerSection.length;
1466
+ config.responseCookies = cookies;
1411
1467
  const urls = [config.url];
1412
1468
  if (stats["redirect_url"]) {
1413
1469
  urls.push(stats["redirect_url"]);
@@ -1507,7 +1563,7 @@ class CurlExecutor {
1507
1563
  }
1508
1564
  buildFinalUrl(config) {
1509
1565
  let url = config.url;
1510
- if (config.baseURL) {
1566
+ if (config.baseURL && !url.startsWith("http://") && !url.startsWith("https://")) {
1511
1567
  url = config.baseURL.replace(/\/$/, "") + "/" + url.replace(/^\//, "");
1512
1568
  }
1513
1569
  if (config.params && Object.keys(config.params).length > 0) {
@@ -1596,6 +1652,13 @@ class CurlExecutor {
1596
1652
  curl.on("close", (code) => {
1597
1653
  try {
1598
1654
  if (code !== 0 && code !== null) {
1655
+ if (code === 22 && stdout) {
1656
+ try {
1657
+ const response = CurlResponseParser.parse(stdout, stderr, config, originalRequest);
1658
+ resolve(response);
1659
+ return;
1660
+ } catch {}
1661
+ }
1599
1662
  const errorCode = this.mapCurlErrorCode(code);
1600
1663
  const errorMessage = this.buildDetailedErrorMessage(code, stderr, config);
1601
1664
  const rezoError = new RezoError(errorMessage, config, errorCode);
@@ -1843,19 +1906,23 @@ export async function executeRequest(options, defaultOptions, jar) {
1843
1906
  if (proxyManager && selectedProxy) {
1844
1907
  proxyManager.reportSuccess(selectedProxy);
1845
1908
  }
1846
- if (config.retry && response.status >= 400) {
1847
- const maxRetries = config.retry.maxRetries || 0;
1848
- const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
1849
- if (config.retryAttempts < maxRetries && statusCodes.includes(response.status)) {
1850
- const retryDelay = config.retry.retryDelay || 0;
1851
- const incrementDelay = config.retry.incrementDelay || false;
1852
- const delay = incrementDelay ? retryDelay * (config.retryAttempts + 1) : retryDelay;
1853
- if (delay > 0) {
1854
- await new Promise((resolve) => setTimeout(resolve, delay));
1909
+ if (response.status >= 400) {
1910
+ const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, originalRequest);
1911
+ if (config.retry) {
1912
+ const maxRetries = config.retry.maxRetries || 0;
1913
+ const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
1914
+ if (config.retryAttempts < maxRetries && statusCodes.includes(response.status)) {
1915
+ const retryDelay = config.retry.retryDelay || 0;
1916
+ const incrementDelay = config.retry.incrementDelay || false;
1917
+ const delay = incrementDelay ? retryDelay * (config.retryAttempts + 1) : retryDelay;
1918
+ if (delay > 0) {
1919
+ await new Promise((resolve) => setTimeout(resolve, delay));
1920
+ }
1921
+ config.retryAttempts++;
1922
+ return executeRequest(options, defaultOptions, jar);
1855
1923
  }
1856
- config.retryAttempts++;
1857
- return executeRequest(options, defaultOptions, jar);
1858
1924
  }
1925
+ throw httpError;
1859
1926
  }
1860
1927
  }
1861
1928
  return result;
@@ -314,11 +314,19 @@ export declare class RezoFormData extends NodeFormData {
314
314
  toBuffer(): Buffer;
315
315
  /**
316
316
  * Create RezoFormData from object
317
+ * Properly handles nested objects by JSON.stringify-ing them
317
318
  * @param {Record<string, any>} obj - Object to convert
318
319
  * @param {Options} options - Optional RezoFormData options
319
320
  * @returns {RezoFormData}
320
321
  */
321
322
  static fromObject(obj: Record<string, any>, options?: Options): RezoFormData;
323
+ /**
324
+ * Helper to append a value to FormData with proper type handling
325
+ * @param {RezoFormData} formData - The form data to append to
326
+ * @param {string} key - The field name
327
+ * @param {any} value - The value to append
328
+ */
329
+ private static appendValue;
322
330
  /**
323
331
  * Convert to URL query string
324
332
  * Warning: File, Blob, and binary data will be omitted
@@ -314,11 +314,19 @@ export declare class RezoFormData extends NodeFormData {
314
314
  toBuffer(): Buffer;
315
315
  /**
316
316
  * Create RezoFormData from object
317
+ * Properly handles nested objects by JSON.stringify-ing them
317
318
  * @param {Record<string, any>} obj - Object to convert
318
319
  * @param {Options} options - Optional RezoFormData options
319
320
  * @returns {RezoFormData}
320
321
  */
321
322
  static fromObject(obj: Record<string, any>, options?: Options): RezoFormData;
323
+ /**
324
+ * Helper to append a value to FormData with proper type handling
325
+ * @param {RezoFormData} formData - The form data to append to
326
+ * @param {string} key - The field name
327
+ * @param {any} value - The value to append
328
+ */
329
+ private static appendValue;
322
330
  /**
323
331
  * Convert to URL query string
324
332
  * Warning: File, Blob, and binary data will be omitted
@@ -314,11 +314,19 @@ export declare class RezoFormData extends NodeFormData {
314
314
  toBuffer(): Buffer;
315
315
  /**
316
316
  * Create RezoFormData from object
317
+ * Properly handles nested objects by JSON.stringify-ing them
317
318
  * @param {Record<string, any>} obj - Object to convert
318
319
  * @param {Options} options - Optional RezoFormData options
319
320
  * @returns {RezoFormData}
320
321
  */
321
322
  static fromObject(obj: Record<string, any>, options?: Options): RezoFormData;
323
+ /**
324
+ * Helper to append a value to FormData with proper type handling
325
+ * @param {RezoFormData} formData - The form data to append to
326
+ * @param {string} key - The field name
327
+ * @param {any} value - The value to append
328
+ */
329
+ private static appendValue;
322
330
  /**
323
331
  * Convert to URL query string
324
332
  * Warning: File, Blob, and binary data will be omitted
@@ -314,11 +314,19 @@ export declare class RezoFormData extends NodeFormData {
314
314
  toBuffer(): Buffer;
315
315
  /**
316
316
  * Create RezoFormData from object
317
+ * Properly handles nested objects by JSON.stringify-ing them
317
318
  * @param {Record<string, any>} obj - Object to convert
318
319
  * @param {Options} options - Optional RezoFormData options
319
320
  * @returns {RezoFormData}
320
321
  */
321
322
  static fromObject(obj: Record<string, any>, options?: Options): RezoFormData;
323
+ /**
324
+ * Helper to append a value to FormData with proper type handling
325
+ * @param {RezoFormData} formData - The form data to append to
326
+ * @param {string} key - The field name
327
+ * @param {any} value - The value to append
328
+ */
329
+ private static appendValue;
322
330
  /**
323
331
  * Convert to URL query string
324
332
  * Warning: File, Blob, and binary data will be omitted
@@ -314,11 +314,19 @@ export declare class RezoFormData extends NodeFormData {
314
314
  toBuffer(): Buffer;
315
315
  /**
316
316
  * Create RezoFormData from object
317
+ * Properly handles nested objects by JSON.stringify-ing them
317
318
  * @param {Record<string, any>} obj - Object to convert
318
319
  * @param {Options} options - Optional RezoFormData options
319
320
  * @returns {RezoFormData}
320
321
  */
321
322
  static fromObject(obj: Record<string, any>, options?: Options): RezoFormData;
323
+ /**
324
+ * Helper to append a value to FormData with proper type handling
325
+ * @param {RezoFormData} formData - The form data to append to
326
+ * @param {string} key - The field name
327
+ * @param {any} value - The value to append
328
+ */
329
+ private static appendValue;
322
330
  /**
323
331
  * Convert to URL query string
324
332
  * Warning: File, Blob, and binary data will be omitted
@@ -314,11 +314,19 @@ export declare class RezoFormData extends NodeFormData {
314
314
  toBuffer(): Buffer;
315
315
  /**
316
316
  * Create RezoFormData from object
317
+ * Properly handles nested objects by JSON.stringify-ing them
317
318
  * @param {Record<string, any>} obj - Object to convert
318
319
  * @param {Options} options - Optional RezoFormData options
319
320
  * @returns {RezoFormData}
320
321
  */
321
322
  static fromObject(obj: Record<string, any>, options?: Options): RezoFormData;
323
+ /**
324
+ * Helper to append a value to FormData with proper type handling
325
+ * @param {RezoFormData} formData - The form data to append to
326
+ * @param {string} key - The field name
327
+ * @param {any} value - The value to append
328
+ */
329
+ private static appendValue;
322
330
  /**
323
331
  * Convert to URL query string
324
332
  * Warning: File, Blob, and binary data will be omitted