rezo 1.0.3 → 1.0.4

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 (64) hide show
  1. package/README.md +261 -0
  2. package/dist/adapters/curl.cjs +47 -1
  3. package/dist/adapters/curl.js +47 -1
  4. package/dist/adapters/entries/curl.cjs +31 -4
  5. package/dist/adapters/entries/curl.d.ts +2576 -847
  6. package/dist/adapters/entries/curl.js +29 -2
  7. package/dist/adapters/entries/fetch.cjs +31 -2
  8. package/dist/adapters/entries/fetch.d.ts +1753 -15
  9. package/dist/adapters/entries/fetch.js +29 -1
  10. package/dist/adapters/entries/http.cjs +31 -2
  11. package/dist/adapters/entries/http.d.ts +1774 -14
  12. package/dist/adapters/entries/http.js +29 -1
  13. package/dist/adapters/entries/http2.cjs +31 -4
  14. package/dist/adapters/entries/http2.d.ts +1748 -19
  15. package/dist/adapters/entries/http2.js +29 -2
  16. package/dist/adapters/entries/react-native.cjs +31 -2
  17. package/dist/adapters/entries/react-native.d.ts +1753 -14
  18. package/dist/adapters/entries/react-native.js +29 -1
  19. package/dist/adapters/entries/xhr.cjs +31 -2
  20. package/dist/adapters/entries/xhr.d.ts +1753 -15
  21. package/dist/adapters/entries/xhr.js +29 -1
  22. package/dist/adapters/fetch.cjs +24 -20
  23. package/dist/adapters/fetch.js +24 -20
  24. package/dist/adapters/http.cjs +69 -19
  25. package/dist/adapters/http.js +69 -19
  26. package/dist/adapters/http2.cjs +69 -19
  27. package/dist/adapters/http2.js +69 -19
  28. package/dist/adapters/index.cjs +6 -6
  29. package/dist/cache/index.cjs +13 -13
  30. package/dist/core/hooks.cjs +16 -0
  31. package/dist/core/hooks.js +16 -0
  32. package/dist/core/rezo.cjs +23 -1
  33. package/dist/core/rezo.js +23 -1
  34. package/dist/crawler.d.ts +528 -5
  35. package/dist/entries/crawler.cjs +5 -5
  36. package/dist/index.cjs +18 -16
  37. package/dist/index.d.ts +564 -5
  38. package/dist/index.js +1 -0
  39. package/dist/platform/browser.cjs +24 -2
  40. package/dist/platform/browser.d.ts +672 -10
  41. package/dist/platform/browser.js +24 -2
  42. package/dist/platform/bun.cjs +24 -2
  43. package/dist/platform/bun.d.ts +672 -10
  44. package/dist/platform/bun.js +24 -2
  45. package/dist/platform/deno.cjs +24 -2
  46. package/dist/platform/deno.d.ts +672 -10
  47. package/dist/platform/deno.js +24 -2
  48. package/dist/platform/node.cjs +24 -2
  49. package/dist/platform/node.d.ts +672 -10
  50. package/dist/platform/node.js +24 -2
  51. package/dist/platform/react-native.cjs +24 -2
  52. package/dist/platform/react-native.d.ts +672 -10
  53. package/dist/platform/react-native.js +24 -2
  54. package/dist/platform/worker.cjs +24 -2
  55. package/dist/platform/worker.d.ts +672 -10
  56. package/dist/platform/worker.js +24 -2
  57. package/dist/plugin/index.cjs +36 -36
  58. package/dist/proxy/index.cjs +2 -0
  59. package/dist/proxy/index.js +1 -0
  60. package/dist/proxy/manager.cjs +446 -0
  61. package/dist/proxy/manager.js +444 -0
  62. package/dist/utils/http-config.cjs +14 -3
  63. package/dist/utils/http-config.js +14 -3
  64. package/package.json +1 -3
package/README.md CHANGED
@@ -63,6 +63,7 @@ Rezo is a production-ready HTTP client library engineered for Node.js 22+ and un
63
63
  - [Advanced Usage](#advanced-usage)
64
64
  - [Cookie Management](#cookie-management)
65
65
  - [Proxy Configuration](#proxy-configuration)
66
+ - [Proxy Manager](#proxy-manager)
66
67
  - [Streaming](#streaming)
67
68
  - [File Downloads](#file-downloads)
68
69
  - [File Uploads](#file-uploads)
@@ -776,6 +777,266 @@ interface ProxyConfig {
776
777
  }
777
778
  ```
778
779
 
780
+ ### Proxy Manager
781
+
782
+ Rezo includes a powerful Proxy Manager for enterprise scenarios requiring proxy rotation, health monitoring, and intelligent failover. The Proxy Manager automatically rotates through a pool of proxies, tracks their health, and removes failing proxies from circulation.
783
+
784
+ #### Basic Usage
785
+
786
+ ```typescript
787
+ import rezo, { ProxyManager } from 'rezo';
788
+
789
+ // Create a proxy manager with a pool of proxies
790
+ const proxyManager = new ProxyManager({
791
+ proxies: [
792
+ { protocol: 'http', host: 'proxy1.example.com', port: 8080 },
793
+ { protocol: 'http', host: 'proxy2.example.com', port: 8080 },
794
+ { protocol: 'socks5', host: 'proxy3.example.com', port: 1080 }
795
+ ]
796
+ });
797
+
798
+ // Create client with proxy manager
799
+ const client = rezo.create({
800
+ proxyManager
801
+ });
802
+
803
+ // Requests automatically rotate through available proxies
804
+ await client.get('https://api.example.com/data');
805
+ ```
806
+
807
+ #### Rotation Strategies
808
+
809
+ ```typescript
810
+ import { ProxyManager } from 'rezo';
811
+
812
+ // Random rotation (default) - randomly selects from available proxies
813
+ const randomManager = new ProxyManager({
814
+ proxies: [...],
815
+ rotationStrategy: 'random'
816
+ });
817
+
818
+ // Sequential rotation - cycles through proxies in order
819
+ const sequentialManager = new ProxyManager({
820
+ proxies: [...],
821
+ rotationStrategy: 'sequential'
822
+ });
823
+
824
+ // Per-proxy limit - switches after N requests per proxy
825
+ const limitManager = new ProxyManager({
826
+ proxies: [...],
827
+ rotationStrategy: 'per-proxy-limit',
828
+ perProxyLimit: 100 // Switch after 100 requests
829
+ });
830
+ ```
831
+
832
+ #### URL Filtering (Whitelist/Blacklist)
833
+
834
+ Control which URLs use the proxy manager with glob patterns or regular expressions:
835
+
836
+ ```typescript
837
+ import { ProxyManager } from 'rezo';
838
+
839
+ const proxyManager = new ProxyManager({
840
+ proxies: [...],
841
+
842
+ // Only use proxies for these URL patterns (glob or regex)
843
+ whitelist: [
844
+ '*.example.com', // Glob pattern
845
+ 'https://api.service.io/*',
846
+ /^https:\/\/secure\./ // Regex pattern
847
+ ],
848
+
849
+ // Never use proxies for these URLs
850
+ blacklist: [
851
+ '*.internal.company.com',
852
+ 'https://localhost/*'
853
+ ]
854
+ });
855
+
856
+ const client = rezo.create({ proxyManager });
857
+
858
+ // Uses proxy (matches whitelist)
859
+ await client.get('https://api.example.com/data');
860
+
861
+ // Direct connection (matches blacklist)
862
+ await client.get('https://app.internal.company.com/status');
863
+ ```
864
+
865
+ #### Health Monitoring & Failover
866
+
867
+ The Proxy Manager automatically tracks proxy health and removes failing proxies:
868
+
869
+ ```typescript
870
+ import { ProxyManager } from 'rezo';
871
+
872
+ const proxyManager = new ProxyManager({
873
+ proxies: [...],
874
+
875
+ // Mark proxy as dead after 5 consecutive failures
876
+ maxFailures: 5,
877
+
878
+ // Re-enable dead proxies after 5 minutes cooldown
879
+ cooldownPeriod: 5 * 60 * 1000,
880
+
881
+ // Fail request if no proxies available (vs direct connection)
882
+ failWithoutProxy: true
883
+ });
884
+
885
+ // Check proxy pool status
886
+ const status = proxyManager.getStatus();
887
+ console.log('Active proxies:', status.active);
888
+ console.log('Disabled proxies:', status.disabled);
889
+ console.log('Total proxies:', status.total);
890
+
891
+ // Check if proxies are available
892
+ if (proxyManager.hasAvailableProxies()) {
893
+ await client.get('https://api.example.com/data');
894
+ }
895
+
896
+ // Reset all proxy states (re-enable all)
897
+ proxyManager.reset();
898
+ ```
899
+
900
+ #### Lifecycle Hooks
901
+
902
+ Monitor and react to proxy events with lifecycle hooks:
903
+
904
+ ```typescript
905
+ import { ProxyManager } from 'rezo';
906
+
907
+ const proxyManager = new ProxyManager({
908
+ proxies: [...],
909
+
910
+ hooks: {
911
+ // Called before selecting a proxy
912
+ beforeProxySelect: (url) => {
913
+ console.log('Selecting proxy for:', url);
914
+ },
915
+
916
+ // Called after proxy selection
917
+ afterProxySelect: (proxy, url) => {
918
+ console.log('Selected:', proxy.host);
919
+ },
920
+
921
+ // Called before reporting a proxy error
922
+ beforeProxyError: (proxy, error) => {
923
+ console.log('Proxy failed:', proxy.host, error.message);
924
+ },
925
+
926
+ // Called after error is recorded
927
+ afterProxyError: (proxy, error, failureCount) => {
928
+ console.log('Failure count:', failureCount);
929
+ },
930
+
931
+ // Called when proxy is disabled
932
+ afterProxyDisable: (proxy, reason) => {
933
+ console.log('Proxy disabled:', proxy.host, reason);
934
+ },
935
+
936
+ // Called when proxy is re-enabled
937
+ afterProxyEnable: (proxy) => {
938
+ console.log('Proxy re-enabled:', proxy.host);
939
+ },
940
+
941
+ // Called after rotation occurs
942
+ afterProxyRotate: (oldProxy, newProxy) => {
943
+ console.log('Rotated from', oldProxy?.host, 'to', newProxy.host);
944
+ }
945
+ }
946
+ });
947
+ ```
948
+
949
+ #### Instance-Level Hooks
950
+
951
+ You can also add proxy hooks at the instance level:
952
+
953
+ ```typescript
954
+ import rezo, { ProxyManager } from 'rezo';
955
+
956
+ const proxyManager = new ProxyManager({ proxies: [...] });
957
+
958
+ const client = rezo.create({
959
+ proxyManager,
960
+ hooks: {
961
+ afterProxySelect: (proxy, url) => {
962
+ console.log('Request will use proxy:', proxy.host);
963
+ },
964
+ afterProxyDisable: (proxy) => {
965
+ // Alert monitoring system
966
+ alertMonitoring('Proxy disabled', proxy.host);
967
+ }
968
+ }
969
+ });
970
+ ```
971
+
972
+ #### Manual Proxy Control
973
+
974
+ Override Proxy Manager for specific requests:
975
+
976
+ ```typescript
977
+ import rezo, { ProxyManager } from 'rezo';
978
+
979
+ const proxyManager = new ProxyManager({ proxies: [...] });
980
+ const client = rezo.create({ proxyManager });
981
+
982
+ // Use a specific proxy (bypasses Proxy Manager)
983
+ await client.get('https://api.example.com/data', {
984
+ proxy: {
985
+ protocol: 'http',
986
+ host: 'specific-proxy.example.com',
987
+ port: 8080
988
+ }
989
+ });
990
+
991
+ // Direct connection (bypasses Proxy Manager)
992
+ await client.get('https://api.example.com/data', {
993
+ useProxyManager: false
994
+ });
995
+ ```
996
+
997
+ #### Proxy Manager Configuration
998
+
999
+ ```typescript
1000
+ interface ProxyManagerConfig {
1001
+ // Array of proxy configurations
1002
+ proxies: ProxyConfig[];
1003
+
1004
+ // Rotation strategy: 'random' | 'sequential' | 'per-proxy-limit'
1005
+ rotationStrategy?: string;
1006
+
1007
+ // Requests per proxy before rotation (for 'per-proxy-limit')
1008
+ perProxyLimit?: number;
1009
+
1010
+ // URL patterns to use with proxies (glob or regex)
1011
+ whitelist?: (string | RegExp)[];
1012
+
1013
+ // URL patterns to bypass proxies (glob or regex)
1014
+ blacklist?: (string | RegExp)[];
1015
+
1016
+ // Consecutive failures before disabling proxy
1017
+ maxFailures?: number;
1018
+
1019
+ // Milliseconds before re-enabling disabled proxy
1020
+ cooldownPeriod?: number;
1021
+
1022
+ // Throw error if no proxy available (vs direct connection)
1023
+ failWithoutProxy?: boolean;
1024
+
1025
+ // Lifecycle hooks
1026
+ hooks?: ProxyManagerHooks;
1027
+ }
1028
+ ```
1029
+
1030
+ #### Adapter Support
1031
+
1032
+ | Adapter | Proxy Manager Support |
1033
+ |---------|----------------------|
1034
+ | HTTP | Full support |
1035
+ | HTTP/2 | Full support |
1036
+ | cURL | Full support |
1037
+ | Fetch | Not supported (browser security) |
1038
+ | React Native | Not supported (platform limitations) |
1039
+
779
1040
  ### Streaming
780
1041
 
781
1042
  Rezo provides powerful streaming capabilities for handling large data efficiently.
@@ -974,11 +974,27 @@ async function executeRequest(options, defaultOptions, jar) {
974
974
  if (!options.responseType) {
975
975
  options.responseType = "auto";
976
976
  }
977
- const d_options = await getDefaultConfig(defaultOptions);
977
+ const d_options = await getDefaultConfig(defaultOptions, defaultOptions._proxyManager);
978
978
  const configResult = prepareHTTPOptions(options, jar, { defaultOptions: d_options });
979
979
  const config = configResult.config;
980
980
  const originalRequest = configResult.fetchOptions;
981
+ const { proxyManager } = configResult;
981
982
  const perform = new RezoPerformance;
983
+ let selectedProxy = null;
984
+ if (proxyManager) {
985
+ const requestUrl = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url?.toString() || "";
986
+ selectedProxy = proxyManager.next(requestUrl);
987
+ if (selectedProxy) {
988
+ originalRequest.proxy = {
989
+ protocol: selectedProxy.protocol,
990
+ host: selectedProxy.host,
991
+ port: selectedProxy.port,
992
+ auth: selectedProxy.auth
993
+ };
994
+ } else if (proxyManager.config.failWithoutProxy) {
995
+ throw new RezoError("No proxy available: All proxies exhausted or URL did not match whitelist/blacklist", config, "UNQ_NO_PROXY_AVAILABLE", originalRequest);
996
+ }
997
+ }
982
998
  const isStream = options._isStream;
983
999
  const isDownload = options._isDownload || !!options.fileName || !!options.saveTo;
984
1000
  const isUpload = options._isUpload;
@@ -999,11 +1015,38 @@ async function executeRequest(options, defaultOptions, jar) {
999
1015
  if (eventEmitter) {
1000
1016
  eventEmitter.emit("initiated");
1001
1017
  }
1018
+ if (proxyManager && selectedProxy) {
1019
+ if (streamResponse) {
1020
+ streamResponse.on("finish", () => {
1021
+ proxyManager.reportSuccess(selectedProxy);
1022
+ });
1023
+ streamResponse.on("error", (err) => {
1024
+ proxyManager.reportFailure(selectedProxy, err);
1025
+ });
1026
+ } else if (downloadResponse) {
1027
+ downloadResponse.on("finish", () => {
1028
+ proxyManager.reportSuccess(selectedProxy);
1029
+ });
1030
+ downloadResponse.on("error", (err) => {
1031
+ proxyManager.reportFailure(selectedProxy, err);
1032
+ });
1033
+ } else if (uploadResponse) {
1034
+ uploadResponse.on("finish", () => {
1035
+ proxyManager.reportSuccess(selectedProxy);
1036
+ });
1037
+ uploadResponse.on("error", (err) => {
1038
+ proxyManager.reportFailure(selectedProxy, err);
1039
+ });
1040
+ }
1041
+ }
1002
1042
  const executor = new CurlExecutor;
1003
1043
  try {
1004
1044
  const result = await executor.execute(config, originalRequest, streamResponse, downloadResponse, uploadResponse);
1005
1045
  if (!streamResponse && !downloadResponse && !uploadResponse) {
1006
1046
  const response = result;
1047
+ if (proxyManager && selectedProxy) {
1048
+ proxyManager.reportSuccess(selectedProxy);
1049
+ }
1007
1050
  if (config.retry && response.status >= 400) {
1008
1051
  const maxRetries = config.retry.maxRetries || 0;
1009
1052
  const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
@@ -1021,6 +1064,9 @@ async function executeRequest(options, defaultOptions, jar) {
1021
1064
  }
1022
1065
  return result;
1023
1066
  } catch (error) {
1067
+ if (proxyManager && selectedProxy) {
1068
+ proxyManager.reportFailure(selectedProxy, error);
1069
+ }
1024
1070
  if (error instanceof RezoError) {
1025
1071
  throw error;
1026
1072
  }
@@ -974,11 +974,27 @@ export async function executeRequest(options, defaultOptions, jar) {
974
974
  if (!options.responseType) {
975
975
  options.responseType = "auto";
976
976
  }
977
- const d_options = await getDefaultConfig(defaultOptions);
977
+ const d_options = await getDefaultConfig(defaultOptions, defaultOptions._proxyManager);
978
978
  const configResult = prepareHTTPOptions(options, jar, { defaultOptions: d_options });
979
979
  const config = configResult.config;
980
980
  const originalRequest = configResult.fetchOptions;
981
+ const { proxyManager } = configResult;
981
982
  const perform = new RezoPerformance;
983
+ let selectedProxy = null;
984
+ if (proxyManager) {
985
+ const requestUrl = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url?.toString() || "";
986
+ selectedProxy = proxyManager.next(requestUrl);
987
+ if (selectedProxy) {
988
+ originalRequest.proxy = {
989
+ protocol: selectedProxy.protocol,
990
+ host: selectedProxy.host,
991
+ port: selectedProxy.port,
992
+ auth: selectedProxy.auth
993
+ };
994
+ } else if (proxyManager.config.failWithoutProxy) {
995
+ throw new RezoError("No proxy available: All proxies exhausted or URL did not match whitelist/blacklist", config, "UNQ_NO_PROXY_AVAILABLE", originalRequest);
996
+ }
997
+ }
982
998
  const isStream = options._isStream;
983
999
  const isDownload = options._isDownload || !!options.fileName || !!options.saveTo;
984
1000
  const isUpload = options._isUpload;
@@ -999,11 +1015,38 @@ export async function executeRequest(options, defaultOptions, jar) {
999
1015
  if (eventEmitter) {
1000
1016
  eventEmitter.emit("initiated");
1001
1017
  }
1018
+ if (proxyManager && selectedProxy) {
1019
+ if (streamResponse) {
1020
+ streamResponse.on("finish", () => {
1021
+ proxyManager.reportSuccess(selectedProxy);
1022
+ });
1023
+ streamResponse.on("error", (err) => {
1024
+ proxyManager.reportFailure(selectedProxy, err);
1025
+ });
1026
+ } else if (downloadResponse) {
1027
+ downloadResponse.on("finish", () => {
1028
+ proxyManager.reportSuccess(selectedProxy);
1029
+ });
1030
+ downloadResponse.on("error", (err) => {
1031
+ proxyManager.reportFailure(selectedProxy, err);
1032
+ });
1033
+ } else if (uploadResponse) {
1034
+ uploadResponse.on("finish", () => {
1035
+ proxyManager.reportSuccess(selectedProxy);
1036
+ });
1037
+ uploadResponse.on("error", (err) => {
1038
+ proxyManager.reportFailure(selectedProxy, err);
1039
+ });
1040
+ }
1041
+ }
1002
1042
  const executor = new CurlExecutor;
1003
1043
  try {
1004
1044
  const result = await executor.execute(config, originalRequest, streamResponse, downloadResponse, uploadResponse);
1005
1045
  if (!streamResponse && !downloadResponse && !uploadResponse) {
1006
1046
  const response = result;
1047
+ if (proxyManager && selectedProxy) {
1048
+ proxyManager.reportSuccess(selectedProxy);
1049
+ }
1007
1050
  if (config.retry && response.status >= 400) {
1008
1051
  const maxRetries = config.retry.maxRetries || 0;
1009
1052
  const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
@@ -1021,6 +1064,9 @@ export async function executeRequest(options, defaultOptions, jar) {
1021
1064
  }
1022
1065
  return result;
1023
1066
  } catch (error) {
1067
+ if (proxyManager && selectedProxy) {
1068
+ proxyManager.reportFailure(selectedProxy, error);
1069
+ }
1024
1070
  if (error instanceof RezoError) {
1025
1071
  throw error;
1026
1072
  }
@@ -1,4 +1,31 @@
1
- const _mod_w60egr = require('../curl.cjs');
2
- exports.executeCurlRequest = _mod_w60egr.executeRequest;;
3
- const _mod_jk2wno = require('../http.cjs');
4
- exports.executeHttpRequest = _mod_jk2wno.executeRequest;;
1
+ const { executeRequest } = require('../curl.cjs');
2
+ const { setGlobalAdapter, createRezoInstance, Rezo } = require('../../core/rezo.cjs');
3
+ const { RezoError, RezoErrorCode } = require('../../errors/rezo-error.cjs');
4
+ const { RezoHeaders } = require('../../utils/headers.cjs');
5
+ const { RezoFormData } = require('../../utils/form-data.cjs');
6
+ const { RezoCookieJar } = require('../../utils/cookies.cjs');
7
+ const { createDefaultHooks, mergeHooks } = require('../../core/hooks.cjs');
8
+ const packageJson = require("../../../package.json");
9
+
10
+ exports.Rezo = Rezo;
11
+ exports.RezoError = RezoError;
12
+ exports.RezoErrorCode = RezoErrorCode;
13
+ exports.RezoHeaders = RezoHeaders;
14
+ exports.RezoFormData = RezoFormData;
15
+ exports.RezoCookieJar = RezoCookieJar;
16
+ exports.createDefaultHooks = createDefaultHooks;
17
+ exports.mergeHooks = mergeHooks;
18
+ const isRezoError = exports.isRezoError = RezoError.isRezoError;
19
+ const Cancel = exports.Cancel = RezoError;
20
+ const CancelToken = exports.CancelToken = AbortController;
21
+ const isCancel = exports.isCancel = (error) => {
22
+ return error instanceof RezoError && error.code === "ECONNABORTED";
23
+ };
24
+ const all = exports.all = Promise.all.bind(Promise);
25
+ const spread = exports.spread = (callback) => (array) => callback(...array);
26
+ const VERSION = exports.VERSION = packageJson.version;
27
+ setGlobalAdapter(executeRequest);
28
+ const rezo = createRezoInstance(executeRequest);
29
+
30
+ exports.default = rezo;
31
+ module.exports = Object.assign(rezo, exports);