rezo 1.0.3 → 1.0.5

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
@@ -1,7 +1,29 @@
1
1
  import { executeRequest } from '../adapters/fetch.js';
2
2
  import { setGlobalAdapter, createRezoInstance, Rezo } from '../core/rezo.js';
3
+ import { RezoError, RezoErrorCode } from '../errors/rezo-error.js';
4
+ import { RezoHeaders } from '../utils/headers.js';
5
+ import { RezoFormData } from '../utils/form-data.js';
6
+ import { RezoCookieJar } from '../utils/cookies.js';
7
+ import { createDefaultHooks, mergeHooks } from '../core/hooks.js';
8
+ import packageJson from "../../package.json" with { type: 'json' };
9
+
10
+ export { Rezo };
11
+ export { RezoError };
12
+ export { RezoErrorCode };
13
+ export { RezoHeaders };
14
+ export { RezoFormData };
15
+ export { RezoCookieJar };
16
+ export { createDefaultHooks };
17
+ export { mergeHooks };
18
+ export const isRezoError = RezoError.isRezoError;
19
+ export const Cancel = RezoError;
20
+ export const CancelToken = AbortController;
21
+ export const isCancel = (error) => {
22
+ return error instanceof RezoError && error.code === "ECONNABORTED";
23
+ };
24
+ export const all = Promise.all.bind(Promise);
25
+ export const spread = (callback) => (array) => callback(...array);
26
+ export const VERSION = packageJson.version;
3
27
  setGlobalAdapter(executeRequest);
4
28
  const rezo = createRezoInstance(executeRequest);
5
-
6
- export { Rezo, rezo };
7
29
  export default rezo;
@@ -1,36 +1,36 @@
1
- const _mod_usiz2l = require('./crawler.cjs');
2
- exports.Crawler = _mod_usiz2l.Crawler;;
3
- const _mod_rfrrzc = require('./crawler-options.cjs');
4
- exports.CrawlerOptions = _mod_rfrrzc.CrawlerOptions;;
5
- const _mod_bf3njs = require('../cache/file-cacher.cjs');
6
- exports.FileCacher = _mod_bf3njs.FileCacher;;
7
- const _mod_rqok7a = require('../cache/url-store.cjs');
8
- exports.UrlStore = _mod_rqok7a.UrlStore;;
9
- const _mod_lx03tn = require('./addon/oxylabs/index.cjs');
10
- exports.Oxylabs = _mod_lx03tn.Oxylabs;;
11
- const _mod_ubpw3g = require('./addon/oxylabs/options.cjs');
12
- exports.OXYLABS_BROWSER_TYPES = _mod_ubpw3g.OXYLABS_BROWSER_TYPES;
13
- exports.OXYLABS_COMMON_LOCALES = _mod_ubpw3g.OXYLABS_COMMON_LOCALES;
14
- exports.OXYLABS_COMMON_GEO_LOCATIONS = _mod_ubpw3g.OXYLABS_COMMON_GEO_LOCATIONS;
15
- exports.OXYLABS_US_STATES = _mod_ubpw3g.OXYLABS_US_STATES;
16
- exports.OXYLABS_EUROPEAN_COUNTRIES = _mod_ubpw3g.OXYLABS_EUROPEAN_COUNTRIES;
17
- exports.OXYLABS_ASIAN_COUNTRIES = _mod_ubpw3g.OXYLABS_ASIAN_COUNTRIES;
18
- exports.getRandomOxylabsBrowserType = _mod_ubpw3g.getRandomBrowserType;
19
- exports.getRandomOxylabsLocale = _mod_ubpw3g.getRandomLocale;
20
- exports.getRandomOxylabsGeoLocation = _mod_ubpw3g.getRandomGeoLocation;;
21
- const _mod_owiwaz = require('./addon/decodo/index.cjs');
22
- exports.Decodo = _mod_owiwaz.Decodo;;
23
- const _mod_poab1t = require('./addon/decodo/options.cjs');
24
- exports.DECODO_DEVICE_TYPES = _mod_poab1t.DECODO_DEVICE_TYPES;
25
- exports.DECODO_HEADLESS_MODES = _mod_poab1t.DECODO_HEADLESS_MODES;
26
- exports.DECODO_COMMON_LOCALES = _mod_poab1t.DECODO_COMMON_LOCALES;
27
- exports.DECODO_COMMON_COUNTRIES = _mod_poab1t.DECODO_COMMON_COUNTRIES;
28
- exports.DECODO_EUROPEAN_COUNTRIES = _mod_poab1t.DECODO_EUROPEAN_COUNTRIES;
29
- exports.DECODO_ASIAN_COUNTRIES = _mod_poab1t.DECODO_ASIAN_COUNTRIES;
30
- exports.DECODO_US_STATES = _mod_poab1t.DECODO_US_STATES;
31
- exports.DECODO_COMMON_CITIES = _mod_poab1t.DECODO_COMMON_CITIES;
32
- exports.getRandomDecodoDeviceType = _mod_poab1t.getRandomDeviceType;
33
- exports.getRandomDecodoLocale = _mod_poab1t.getRandomLocale;
34
- exports.getRandomDecodoCountry = _mod_poab1t.getRandomCountry;
35
- exports.getRandomDecodoCity = _mod_poab1t.getRandomCity;
36
- exports.generateDecodoSessionId = _mod_poab1t.generateSessionId;;
1
+ const _mod_9qwi8e = require('./crawler.cjs');
2
+ exports.Crawler = _mod_9qwi8e.Crawler;;
3
+ const _mod_kpl4t4 = require('./crawler-options.cjs');
4
+ exports.CrawlerOptions = _mod_kpl4t4.CrawlerOptions;;
5
+ const _mod_o6bmw3 = require('../cache/file-cacher.cjs');
6
+ exports.FileCacher = _mod_o6bmw3.FileCacher;;
7
+ const _mod_9vlknl = require('../cache/url-store.cjs');
8
+ exports.UrlStore = _mod_9vlknl.UrlStore;;
9
+ const _mod_31b8nd = require('./addon/oxylabs/index.cjs');
10
+ exports.Oxylabs = _mod_31b8nd.Oxylabs;;
11
+ const _mod_63rr5e = require('./addon/oxylabs/options.cjs');
12
+ exports.OXYLABS_BROWSER_TYPES = _mod_63rr5e.OXYLABS_BROWSER_TYPES;
13
+ exports.OXYLABS_COMMON_LOCALES = _mod_63rr5e.OXYLABS_COMMON_LOCALES;
14
+ exports.OXYLABS_COMMON_GEO_LOCATIONS = _mod_63rr5e.OXYLABS_COMMON_GEO_LOCATIONS;
15
+ exports.OXYLABS_US_STATES = _mod_63rr5e.OXYLABS_US_STATES;
16
+ exports.OXYLABS_EUROPEAN_COUNTRIES = _mod_63rr5e.OXYLABS_EUROPEAN_COUNTRIES;
17
+ exports.OXYLABS_ASIAN_COUNTRIES = _mod_63rr5e.OXYLABS_ASIAN_COUNTRIES;
18
+ exports.getRandomOxylabsBrowserType = _mod_63rr5e.getRandomBrowserType;
19
+ exports.getRandomOxylabsLocale = _mod_63rr5e.getRandomLocale;
20
+ exports.getRandomOxylabsGeoLocation = _mod_63rr5e.getRandomGeoLocation;;
21
+ const _mod_h4ktbx = require('./addon/decodo/index.cjs');
22
+ exports.Decodo = _mod_h4ktbx.Decodo;;
23
+ const _mod_t9ve3n = require('./addon/decodo/options.cjs');
24
+ exports.DECODO_DEVICE_TYPES = _mod_t9ve3n.DECODO_DEVICE_TYPES;
25
+ exports.DECODO_HEADLESS_MODES = _mod_t9ve3n.DECODO_HEADLESS_MODES;
26
+ exports.DECODO_COMMON_LOCALES = _mod_t9ve3n.DECODO_COMMON_LOCALES;
27
+ exports.DECODO_COMMON_COUNTRIES = _mod_t9ve3n.DECODO_COMMON_COUNTRIES;
28
+ exports.DECODO_EUROPEAN_COUNTRIES = _mod_t9ve3n.DECODO_EUROPEAN_COUNTRIES;
29
+ exports.DECODO_ASIAN_COUNTRIES = _mod_t9ve3n.DECODO_ASIAN_COUNTRIES;
30
+ exports.DECODO_US_STATES = _mod_t9ve3n.DECODO_US_STATES;
31
+ exports.DECODO_COMMON_CITIES = _mod_t9ve3n.DECODO_COMMON_CITIES;
32
+ exports.getRandomDecodoDeviceType = _mod_t9ve3n.getRandomDeviceType;
33
+ exports.getRandomDecodoLocale = _mod_t9ve3n.getRandomLocale;
34
+ exports.getRandomDecodoCountry = _mod_t9ve3n.getRandomCountry;
35
+ exports.getRandomDecodoCity = _mod_t9ve3n.getRandomCity;
36
+ exports.generateDecodoSessionId = _mod_t9ve3n.generateSessionId;;
@@ -1,6 +1,8 @@
1
1
  const { SocksProxyAgent: RezoSocksProxy } = require("socks-proxy-agent");
2
2
  const { HttpsProxyAgent: RezoHttpsSocks } = require("https-proxy-agent");
3
3
  const { HttpProxyAgent: RezoHttpSocks } = require("http-proxy-agent");
4
+ const _mod_4nmwf9 = require('./manager.cjs');
5
+ exports.ProxyManager = _mod_4nmwf9.ProxyManager;;
4
6
  function createOptions(uri, opts) {
5
7
  if (uri instanceof URL || typeof uri === "string") {
6
8
  return {
@@ -1,6 +1,7 @@
1
1
  import { SocksProxyAgent as RezoSocksProxy } from "socks-proxy-agent";
2
2
  import { HttpsProxyAgent as RezoHttpsSocks } from "https-proxy-agent";
3
3
  import { HttpProxyAgent as RezoHttpSocks } from "http-proxy-agent";
4
+ export { ProxyManager } from './manager.js';
4
5
  function createOptions(uri, opts) {
5
6
  if (uri instanceof URL || typeof uri === "string") {
6
7
  return {
@@ -0,0 +1,446 @@
1
+ function generateProxyId() {
2
+ return `proxy_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
3
+ }
4
+
5
+ class ProxyManager {
6
+ config;
7
+ states = new Map;
8
+ currentIndex = 0;
9
+ currentProxyRequests = 0;
10
+ lastSelectedProxy = null;
11
+ cooldownTimers = new Map;
12
+ _totalRequests = 0;
13
+ _totalSuccesses = 0;
14
+ _totalFailures = 0;
15
+ hooks = {
16
+ beforeProxySelect: [],
17
+ afterProxySelect: [],
18
+ beforeProxyError: [],
19
+ afterProxyError: [],
20
+ beforeProxyDisable: [],
21
+ afterProxyDisable: [],
22
+ afterProxyRotate: [],
23
+ afterProxyEnable: []
24
+ };
25
+ constructor(config) {
26
+ this.config = {
27
+ failWithoutProxy: true,
28
+ autoDisableDeadProxies: false,
29
+ maxFailures: 3,
30
+ retryWithNextProxy: false,
31
+ maxProxyRetries: 3,
32
+ ...config
33
+ };
34
+ for (const proxy of this.config.proxies) {
35
+ if (!proxy.id) {
36
+ proxy.id = generateProxyId();
37
+ }
38
+ this.states.set(proxy.id, this.createInitialState(proxy));
39
+ }
40
+ }
41
+ createInitialState(proxy) {
42
+ return {
43
+ proxy,
44
+ requestCount: 0,
45
+ failureCount: 0,
46
+ successCount: 0,
47
+ totalFailures: 0,
48
+ isActive: true
49
+ };
50
+ }
51
+ shouldProxy(url) {
52
+ const { whitelist, blacklist } = this.config;
53
+ if (whitelist && whitelist.length > 0) {
54
+ const matches = whitelist.some((pattern) => this.matchPattern(pattern, url));
55
+ if (!matches) {
56
+ return false;
57
+ }
58
+ }
59
+ if (blacklist && blacklist.length > 0) {
60
+ const matches = blacklist.some((pattern) => this.matchPattern(pattern, url));
61
+ if (matches) {
62
+ return false;
63
+ }
64
+ }
65
+ return true;
66
+ }
67
+ matchPattern(pattern, url) {
68
+ if (pattern instanceof RegExp) {
69
+ return pattern.test(url);
70
+ }
71
+ try {
72
+ const urlObj = new URL(url);
73
+ const hostname = urlObj.hostname;
74
+ if (hostname === pattern) {
75
+ return true;
76
+ }
77
+ if (hostname.endsWith("." + pattern)) {
78
+ return true;
79
+ }
80
+ if (pattern.startsWith("*.")) {
81
+ const domain = pattern.slice(2);
82
+ if (hostname === domain || hostname.endsWith("." + domain)) {
83
+ return true;
84
+ }
85
+ }
86
+ return false;
87
+ } catch {
88
+ return url.includes(pattern);
89
+ }
90
+ }
91
+ getActive() {
92
+ this.processExpiredCooldowns();
93
+ return Array.from(this.states.values()).filter((state) => state.isActive).map((state) => state.proxy);
94
+ }
95
+ getDisabled() {
96
+ return Array.from(this.states.values()).filter((state) => !state.isActive && !state.reenableAt).map((state) => state.proxy);
97
+ }
98
+ getCooldown() {
99
+ const now = Date.now();
100
+ return Array.from(this.states.values()).filter((state) => !state.isActive && state.reenableAt && state.reenableAt > now).map((state) => state.proxy);
101
+ }
102
+ processExpiredCooldowns() {
103
+ const now = Date.now();
104
+ for (const state of this.states.values()) {
105
+ if (!state.isActive && state.reenableAt && state.reenableAt <= now) {
106
+ this.enableProxy(state.proxy, "cooldown-expired");
107
+ }
108
+ }
109
+ }
110
+ next(url) {
111
+ this._totalRequests++;
112
+ const activeProxies = this.getActive();
113
+ this.runBeforeProxySelectHooksSync({
114
+ url,
115
+ proxies: activeProxies,
116
+ isRetry: false,
117
+ retryCount: 0
118
+ });
119
+ if (!this.shouldProxy(url)) {
120
+ this.runAfterProxySelectHooksSync({
121
+ url,
122
+ proxy: null,
123
+ reason: this.config.blacklist?.some((p) => this.matchPattern(p, url)) ? "blacklist-match" : "whitelist-no-match"
124
+ });
125
+ return null;
126
+ }
127
+ if (activeProxies.length === 0) {
128
+ this.runAfterProxySelectHooksSync({
129
+ url,
130
+ proxy: null,
131
+ reason: "no-proxies-available"
132
+ });
133
+ return null;
134
+ }
135
+ const selected = this.selectProxy(activeProxies);
136
+ if (selected) {
137
+ const state = this.states.get(selected.id);
138
+ if (state) {
139
+ state.requestCount++;
140
+ if (this.config.rotation === "per-proxy-limit") {
141
+ const limit = this.config.limit;
142
+ if (state.requestCount >= limit) {
143
+ this.disableProxy(selected, "limit-reached");
144
+ }
145
+ }
146
+ }
147
+ if (this.lastSelectedProxy && this.lastSelectedProxy.id !== selected.id) {
148
+ this.runAfterProxyRotateHooks({
149
+ from: this.lastSelectedProxy,
150
+ to: selected,
151
+ reason: "scheduled"
152
+ });
153
+ }
154
+ this.lastSelectedProxy = selected;
155
+ }
156
+ this.runAfterProxySelectHooksSync({
157
+ url,
158
+ proxy: selected,
159
+ reason: selected ? "selected" : "no-proxies-available"
160
+ });
161
+ return selected;
162
+ }
163
+ select(url) {
164
+ this._totalRequests++;
165
+ if (!this.shouldProxy(url)) {
166
+ const { whitelist, blacklist } = this.config;
167
+ const reason = blacklist?.some((p) => this.matchPattern(p, url)) ? "blacklist-match" : "whitelist-no-match";
168
+ return { proxy: null, reason };
169
+ }
170
+ const activeProxies = this.getActive();
171
+ if (activeProxies.length === 0) {
172
+ return { proxy: null, reason: "no-proxies-available" };
173
+ }
174
+ const selected = this.selectProxy(activeProxies);
175
+ if (selected) {
176
+ const state = this.states.get(selected.id);
177
+ if (state) {
178
+ state.requestCount++;
179
+ if (this.config.rotation === "per-proxy-limit") {
180
+ const limit = this.config.limit;
181
+ if (state.requestCount >= limit) {
182
+ this.disableProxy(selected, "limit-reached");
183
+ }
184
+ }
185
+ }
186
+ this.lastSelectedProxy = selected;
187
+ return { proxy: selected, reason: "selected" };
188
+ }
189
+ return { proxy: null, reason: "no-proxies-available" };
190
+ }
191
+ selectProxy(activeProxies) {
192
+ if (activeProxies.length === 0) {
193
+ return null;
194
+ }
195
+ const rotation = this.config.rotation;
196
+ if (rotation === "random") {
197
+ const index = Math.floor(Math.random() * activeProxies.length);
198
+ return activeProxies[index];
199
+ }
200
+ if (rotation === "sequential") {
201
+ const requestsPerProxy = this.config.requestsPerProxy ?? 1;
202
+ if (this.currentProxyRequests >= requestsPerProxy) {
203
+ this.currentIndex = (this.currentIndex + 1) % activeProxies.length;
204
+ this.currentProxyRequests = 0;
205
+ }
206
+ if (this.currentIndex >= activeProxies.length) {
207
+ this.currentIndex = 0;
208
+ }
209
+ this.currentProxyRequests++;
210
+ return activeProxies[this.currentIndex];
211
+ }
212
+ if (rotation === "per-proxy-limit") {
213
+ for (const proxy of activeProxies) {
214
+ const state = this.states.get(proxy.id);
215
+ if (state && state.isActive) {
216
+ return proxy;
217
+ }
218
+ }
219
+ return null;
220
+ }
221
+ return activeProxies[0];
222
+ }
223
+ reportSuccess(proxy) {
224
+ this._totalSuccesses++;
225
+ const state = this.states.get(proxy.id);
226
+ if (state) {
227
+ state.successCount++;
228
+ state.failureCount = 0;
229
+ state.lastSuccessAt = Date.now();
230
+ }
231
+ }
232
+ reportFailure(proxy, error, url) {
233
+ this._totalFailures++;
234
+ const state = this.states.get(proxy.id);
235
+ if (!state)
236
+ return;
237
+ const willBeDisabled = !!(this.config.autoDisableDeadProxies && state.failureCount + 1 >= (this.config.maxFailures ?? 3));
238
+ this.runBeforeProxyErrorHooksSync({
239
+ proxy,
240
+ error,
241
+ url: url || "",
242
+ failureCount: state.failureCount + 1,
243
+ willBeDisabled
244
+ });
245
+ state.failureCount++;
246
+ state.totalFailures++;
247
+ state.lastFailureAt = Date.now();
248
+ state.lastError = error.message;
249
+ let action = "continue";
250
+ if (this.config.autoDisableDeadProxies) {
251
+ const maxFailures = this.config.maxFailures ?? 3;
252
+ if (state.failureCount >= maxFailures) {
253
+ this.disableProxy(proxy, "dead");
254
+ action = "disabled";
255
+ }
256
+ }
257
+ this.runAfterProxyErrorHooksSync({
258
+ proxy,
259
+ error,
260
+ action
261
+ });
262
+ }
263
+ disableProxy(proxy, reason = "manual") {
264
+ const state = this.states.get(proxy.id);
265
+ if (!state || !state.isActive)
266
+ return;
267
+ state.isActive = false;
268
+ state.disabledReason = reason;
269
+ state.disabledAt = Date.now();
270
+ const { cooldown } = this.config;
271
+ let hasCooldown = false;
272
+ let reenableAt;
273
+ if (cooldown?.enabled && cooldown.durationMs > 0) {
274
+ hasCooldown = true;
275
+ reenableAt = Date.now() + cooldown.durationMs;
276
+ state.reenableAt = reenableAt;
277
+ const timerId = setTimeout(() => {
278
+ this.enableProxy(proxy, "cooldown-expired");
279
+ this.cooldownTimers.delete(proxy.id);
280
+ }, cooldown.durationMs);
281
+ this.cooldownTimers.set(proxy.id, timerId);
282
+ }
283
+ this.runAfterProxyDisableHooks({
284
+ proxy,
285
+ reason,
286
+ hasCooldown,
287
+ reenableAt
288
+ });
289
+ }
290
+ enableProxy(proxy, reason = "manual") {
291
+ const state = this.states.get(proxy.id);
292
+ if (!state || state.isActive)
293
+ return;
294
+ state.isActive = true;
295
+ state.failureCount = 0;
296
+ state.disabledReason = undefined;
297
+ state.disabledAt = undefined;
298
+ state.reenableAt = undefined;
299
+ const timerId = this.cooldownTimers.get(proxy.id);
300
+ if (timerId) {
301
+ clearTimeout(timerId);
302
+ this.cooldownTimers.delete(proxy.id);
303
+ }
304
+ this.runAfterProxyEnableHooks({
305
+ proxy,
306
+ reason
307
+ });
308
+ }
309
+ add(proxies) {
310
+ const toAdd = Array.isArray(proxies) ? proxies : [proxies];
311
+ for (const proxy of toAdd) {
312
+ if (!proxy.id) {
313
+ proxy.id = generateProxyId();
314
+ }
315
+ if (!this.states.has(proxy.id)) {
316
+ this.states.set(proxy.id, this.createInitialState(proxy));
317
+ this.config.proxies.push(proxy);
318
+ }
319
+ }
320
+ }
321
+ remove(proxies) {
322
+ const toRemove = Array.isArray(proxies) ? proxies : [proxies];
323
+ for (const proxy of toRemove) {
324
+ if (proxy.id) {
325
+ const timerId = this.cooldownTimers.get(proxy.id);
326
+ if (timerId) {
327
+ clearTimeout(timerId);
328
+ this.cooldownTimers.delete(proxy.id);
329
+ }
330
+ this.states.delete(proxy.id);
331
+ const index = this.config.proxies.findIndex((p) => p.id === proxy.id);
332
+ if (index !== -1) {
333
+ this.config.proxies.splice(index, 1);
334
+ }
335
+ }
336
+ }
337
+ }
338
+ reset() {
339
+ for (const timerId of this.cooldownTimers.values()) {
340
+ clearTimeout(timerId);
341
+ }
342
+ this.cooldownTimers.clear();
343
+ for (const state of this.states.values()) {
344
+ state.requestCount = 0;
345
+ state.failureCount = 0;
346
+ state.isActive = true;
347
+ state.disabledReason = undefined;
348
+ state.disabledAt = undefined;
349
+ state.reenableAt = undefined;
350
+ }
351
+ this.currentIndex = 0;
352
+ this.currentProxyRequests = 0;
353
+ this.lastSelectedProxy = null;
354
+ }
355
+ getStatus() {
356
+ this.processExpiredCooldowns();
357
+ return {
358
+ active: this.getActive(),
359
+ disabled: this.getDisabled(),
360
+ cooldown: this.getCooldown(),
361
+ total: this.states.size,
362
+ rotation: this.config.rotation,
363
+ totalRequests: this._totalRequests,
364
+ totalSuccesses: this._totalSuccesses,
365
+ totalFailures: this._totalFailures
366
+ };
367
+ }
368
+ getProxyState(proxy) {
369
+ return this.states.get(proxy.id);
370
+ }
371
+ hasAvailableProxies() {
372
+ return this.getActive().length > 0;
373
+ }
374
+ destroy() {
375
+ for (const timerId of this.cooldownTimers.values()) {
376
+ clearTimeout(timerId);
377
+ }
378
+ this.cooldownTimers.clear();
379
+ this.states.clear();
380
+ }
381
+ runBeforeProxySelectHooksSync(context) {
382
+ for (const hook of this.hooks.beforeProxySelect) {
383
+ try {
384
+ hook(context);
385
+ } catch (error) {
386
+ console.error("[ProxyManager] beforeProxySelect hook error:", error);
387
+ }
388
+ }
389
+ }
390
+ runAfterProxySelectHooksSync(context) {
391
+ for (const hook of this.hooks.afterProxySelect) {
392
+ try {
393
+ hook(context);
394
+ } catch (error) {
395
+ console.error("[ProxyManager] afterProxySelect hook error:", error);
396
+ }
397
+ }
398
+ }
399
+ runBeforeProxyErrorHooksSync(context) {
400
+ for (const hook of this.hooks.beforeProxyError) {
401
+ try {
402
+ hook(context);
403
+ } catch (error) {
404
+ console.error("[ProxyManager] beforeProxyError hook error:", error);
405
+ }
406
+ }
407
+ }
408
+ runAfterProxyErrorHooksSync(context) {
409
+ for (const hook of this.hooks.afterProxyError) {
410
+ try {
411
+ hook(context);
412
+ } catch (error) {
413
+ console.error("[ProxyManager] afterProxyError hook error:", error);
414
+ }
415
+ }
416
+ }
417
+ runAfterProxyRotateHooks(context) {
418
+ for (const hook of this.hooks.afterProxyRotate) {
419
+ try {
420
+ hook(context);
421
+ } catch (error) {
422
+ console.error("[ProxyManager] afterProxyRotate hook error:", error);
423
+ }
424
+ }
425
+ }
426
+ runAfterProxyDisableHooks(context) {
427
+ for (const hook of this.hooks.afterProxyDisable) {
428
+ try {
429
+ hook(context);
430
+ } catch (error) {
431
+ console.error("[ProxyManager] afterProxyDisable hook error:", error);
432
+ }
433
+ }
434
+ }
435
+ runAfterProxyEnableHooks(context) {
436
+ for (const hook of this.hooks.afterProxyEnable) {
437
+ try {
438
+ hook(context);
439
+ } catch (error) {
440
+ console.error("[ProxyManager] afterProxyEnable hook error:", error);
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ exports.ProxyManager = ProxyManager;