rezo 1.0.2 → 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 +19 -4
@@ -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_khxd8a = require('./crawler.cjs');
2
- exports.Crawler = _mod_khxd8a.Crawler;;
3
- const _mod_vm96it = require('./crawler-options.cjs');
4
- exports.CrawlerOptions = _mod_vm96it.CrawlerOptions;;
5
- const _mod_enfdda = require('../cache/file-cacher.cjs');
6
- exports.FileCacher = _mod_enfdda.FileCacher;;
7
- const _mod_p51bkd = require('../cache/url-store.cjs');
8
- exports.UrlStore = _mod_p51bkd.UrlStore;;
9
- const _mod_q47c3j = require('./addon/oxylabs/index.cjs');
10
- exports.Oxylabs = _mod_q47c3j.Oxylabs;;
11
- const _mod_rfrfyo = require('./addon/oxylabs/options.cjs');
12
- exports.OXYLABS_BROWSER_TYPES = _mod_rfrfyo.OXYLABS_BROWSER_TYPES;
13
- exports.OXYLABS_COMMON_LOCALES = _mod_rfrfyo.OXYLABS_COMMON_LOCALES;
14
- exports.OXYLABS_COMMON_GEO_LOCATIONS = _mod_rfrfyo.OXYLABS_COMMON_GEO_LOCATIONS;
15
- exports.OXYLABS_US_STATES = _mod_rfrfyo.OXYLABS_US_STATES;
16
- exports.OXYLABS_EUROPEAN_COUNTRIES = _mod_rfrfyo.OXYLABS_EUROPEAN_COUNTRIES;
17
- exports.OXYLABS_ASIAN_COUNTRIES = _mod_rfrfyo.OXYLABS_ASIAN_COUNTRIES;
18
- exports.getRandomOxylabsBrowserType = _mod_rfrfyo.getRandomBrowserType;
19
- exports.getRandomOxylabsLocale = _mod_rfrfyo.getRandomLocale;
20
- exports.getRandomOxylabsGeoLocation = _mod_rfrfyo.getRandomGeoLocation;;
21
- const _mod_5qyxa5 = require('./addon/decodo/index.cjs');
22
- exports.Decodo = _mod_5qyxa5.Decodo;;
23
- const _mod_38klqg = require('./addon/decodo/options.cjs');
24
- exports.DECODO_DEVICE_TYPES = _mod_38klqg.DECODO_DEVICE_TYPES;
25
- exports.DECODO_HEADLESS_MODES = _mod_38klqg.DECODO_HEADLESS_MODES;
26
- exports.DECODO_COMMON_LOCALES = _mod_38klqg.DECODO_COMMON_LOCALES;
27
- exports.DECODO_COMMON_COUNTRIES = _mod_38klqg.DECODO_COMMON_COUNTRIES;
28
- exports.DECODO_EUROPEAN_COUNTRIES = _mod_38klqg.DECODO_EUROPEAN_COUNTRIES;
29
- exports.DECODO_ASIAN_COUNTRIES = _mod_38klqg.DECODO_ASIAN_COUNTRIES;
30
- exports.DECODO_US_STATES = _mod_38klqg.DECODO_US_STATES;
31
- exports.DECODO_COMMON_CITIES = _mod_38klqg.DECODO_COMMON_CITIES;
32
- exports.getRandomDecodoDeviceType = _mod_38klqg.getRandomDeviceType;
33
- exports.getRandomDecodoLocale = _mod_38klqg.getRandomLocale;
34
- exports.getRandomDecodoCountry = _mod_38klqg.getRandomCountry;
35
- exports.getRandomDecodoCity = _mod_38klqg.getRandomCity;
36
- exports.generateDecodoSessionId = _mod_38klqg.generateSessionId;;
1
+ const _mod_er88yk = require('./crawler.cjs');
2
+ exports.Crawler = _mod_er88yk.Crawler;;
3
+ const _mod_fi3kva = require('./crawler-options.cjs');
4
+ exports.CrawlerOptions = _mod_fi3kva.CrawlerOptions;;
5
+ const _mod_pgkdtt = require('../cache/file-cacher.cjs');
6
+ exports.FileCacher = _mod_pgkdtt.FileCacher;;
7
+ const _mod_6h8hob = require('../cache/url-store.cjs');
8
+ exports.UrlStore = _mod_6h8hob.UrlStore;;
9
+ const _mod_9aknyh = require('./addon/oxylabs/index.cjs');
10
+ exports.Oxylabs = _mod_9aknyh.Oxylabs;;
11
+ const _mod_8drluh = require('./addon/oxylabs/options.cjs');
12
+ exports.OXYLABS_BROWSER_TYPES = _mod_8drluh.OXYLABS_BROWSER_TYPES;
13
+ exports.OXYLABS_COMMON_LOCALES = _mod_8drluh.OXYLABS_COMMON_LOCALES;
14
+ exports.OXYLABS_COMMON_GEO_LOCATIONS = _mod_8drluh.OXYLABS_COMMON_GEO_LOCATIONS;
15
+ exports.OXYLABS_US_STATES = _mod_8drluh.OXYLABS_US_STATES;
16
+ exports.OXYLABS_EUROPEAN_COUNTRIES = _mod_8drluh.OXYLABS_EUROPEAN_COUNTRIES;
17
+ exports.OXYLABS_ASIAN_COUNTRIES = _mod_8drluh.OXYLABS_ASIAN_COUNTRIES;
18
+ exports.getRandomOxylabsBrowserType = _mod_8drluh.getRandomBrowserType;
19
+ exports.getRandomOxylabsLocale = _mod_8drluh.getRandomLocale;
20
+ exports.getRandomOxylabsGeoLocation = _mod_8drluh.getRandomGeoLocation;;
21
+ const _mod_hco42s = require('./addon/decodo/index.cjs');
22
+ exports.Decodo = _mod_hco42s.Decodo;;
23
+ const _mod_ly422g = require('./addon/decodo/options.cjs');
24
+ exports.DECODO_DEVICE_TYPES = _mod_ly422g.DECODO_DEVICE_TYPES;
25
+ exports.DECODO_HEADLESS_MODES = _mod_ly422g.DECODO_HEADLESS_MODES;
26
+ exports.DECODO_COMMON_LOCALES = _mod_ly422g.DECODO_COMMON_LOCALES;
27
+ exports.DECODO_COMMON_COUNTRIES = _mod_ly422g.DECODO_COMMON_COUNTRIES;
28
+ exports.DECODO_EUROPEAN_COUNTRIES = _mod_ly422g.DECODO_EUROPEAN_COUNTRIES;
29
+ exports.DECODO_ASIAN_COUNTRIES = _mod_ly422g.DECODO_ASIAN_COUNTRIES;
30
+ exports.DECODO_US_STATES = _mod_ly422g.DECODO_US_STATES;
31
+ exports.DECODO_COMMON_CITIES = _mod_ly422g.DECODO_COMMON_CITIES;
32
+ exports.getRandomDecodoDeviceType = _mod_ly422g.getRandomDeviceType;
33
+ exports.getRandomDecodoLocale = _mod_ly422g.getRandomLocale;
34
+ exports.getRandomDecodoCountry = _mod_ly422g.getRandomCountry;
35
+ exports.getRandomDecodoCity = _mod_ly422g.getRandomCity;
36
+ exports.generateDecodoSessionId = _mod_ly422g.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_jzt7z5 = require('./manager.cjs');
5
+ exports.ProxyManager = _mod_jzt7z5.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;