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.
- package/README.md +261 -0
- package/dist/adapters/curl.cjs +47 -1
- package/dist/adapters/curl.js +47 -1
- package/dist/adapters/entries/curl.cjs +31 -4
- package/dist/adapters/entries/curl.d.ts +2576 -847
- package/dist/adapters/entries/curl.js +29 -2
- package/dist/adapters/entries/fetch.cjs +31 -2
- package/dist/adapters/entries/fetch.d.ts +1753 -15
- package/dist/adapters/entries/fetch.js +29 -1
- package/dist/adapters/entries/http.cjs +31 -2
- package/dist/adapters/entries/http.d.ts +1774 -14
- package/dist/adapters/entries/http.js +29 -1
- package/dist/adapters/entries/http2.cjs +31 -4
- package/dist/adapters/entries/http2.d.ts +1748 -19
- package/dist/adapters/entries/http2.js +29 -2
- package/dist/adapters/entries/react-native.cjs +31 -2
- package/dist/adapters/entries/react-native.d.ts +1753 -14
- package/dist/adapters/entries/react-native.js +29 -1
- package/dist/adapters/entries/xhr.cjs +31 -2
- package/dist/adapters/entries/xhr.d.ts +1753 -15
- package/dist/adapters/entries/xhr.js +29 -1
- package/dist/adapters/fetch.cjs +24 -20
- package/dist/adapters/fetch.js +24 -20
- package/dist/adapters/http.cjs +69 -19
- package/dist/adapters/http.js +69 -19
- package/dist/adapters/http2.cjs +69 -19
- package/dist/adapters/http2.js +69 -19
- package/dist/adapters/index.cjs +6 -6
- package/dist/cache/index.cjs +13 -13
- package/dist/core/hooks.cjs +16 -0
- package/dist/core/hooks.js +16 -0
- package/dist/core/rezo.cjs +23 -1
- package/dist/core/rezo.js +23 -1
- package/dist/crawler.d.ts +528 -5
- package/dist/entries/crawler.cjs +5 -5
- package/dist/index.cjs +18 -16
- package/dist/index.d.ts +564 -5
- package/dist/index.js +1 -0
- package/dist/platform/browser.cjs +24 -2
- package/dist/platform/browser.d.ts +672 -10
- package/dist/platform/browser.js +24 -2
- package/dist/platform/bun.cjs +24 -2
- package/dist/platform/bun.d.ts +672 -10
- package/dist/platform/bun.js +24 -2
- package/dist/platform/deno.cjs +24 -2
- package/dist/platform/deno.d.ts +672 -10
- package/dist/platform/deno.js +24 -2
- package/dist/platform/node.cjs +24 -2
- package/dist/platform/node.d.ts +672 -10
- package/dist/platform/node.js +24 -2
- package/dist/platform/react-native.cjs +24 -2
- package/dist/platform/react-native.d.ts +672 -10
- package/dist/platform/react-native.js +24 -2
- package/dist/platform/worker.cjs +24 -2
- package/dist/platform/worker.d.ts +672 -10
- package/dist/platform/worker.js +24 -2
- package/dist/plugin/index.cjs +36 -36
- package/dist/proxy/index.cjs +2 -0
- package/dist/proxy/index.js +1 -0
- package/dist/proxy/manager.cjs +446 -0
- package/dist/proxy/manager.js +444 -0
- package/dist/utils/http-config.cjs +14 -3
- package/dist/utils/http-config.js +14 -3
- package/package.json +19 -4
package/dist/platform/worker.js
CHANGED
|
@@ -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;
|
package/dist/plugin/index.cjs
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
const
|
|
2
|
-
exports.Crawler =
|
|
3
|
-
const
|
|
4
|
-
exports.CrawlerOptions =
|
|
5
|
-
const
|
|
6
|
-
exports.FileCacher =
|
|
7
|
-
const
|
|
8
|
-
exports.UrlStore =
|
|
9
|
-
const
|
|
10
|
-
exports.Oxylabs =
|
|
11
|
-
const
|
|
12
|
-
exports.OXYLABS_BROWSER_TYPES =
|
|
13
|
-
exports.OXYLABS_COMMON_LOCALES =
|
|
14
|
-
exports.OXYLABS_COMMON_GEO_LOCATIONS =
|
|
15
|
-
exports.OXYLABS_US_STATES =
|
|
16
|
-
exports.OXYLABS_EUROPEAN_COUNTRIES =
|
|
17
|
-
exports.OXYLABS_ASIAN_COUNTRIES =
|
|
18
|
-
exports.getRandomOxylabsBrowserType =
|
|
19
|
-
exports.getRandomOxylabsLocale =
|
|
20
|
-
exports.getRandomOxylabsGeoLocation =
|
|
21
|
-
const
|
|
22
|
-
exports.Decodo =
|
|
23
|
-
const
|
|
24
|
-
exports.DECODO_DEVICE_TYPES =
|
|
25
|
-
exports.DECODO_HEADLESS_MODES =
|
|
26
|
-
exports.DECODO_COMMON_LOCALES =
|
|
27
|
-
exports.DECODO_COMMON_COUNTRIES =
|
|
28
|
-
exports.DECODO_EUROPEAN_COUNTRIES =
|
|
29
|
-
exports.DECODO_ASIAN_COUNTRIES =
|
|
30
|
-
exports.DECODO_US_STATES =
|
|
31
|
-
exports.DECODO_COMMON_CITIES =
|
|
32
|
-
exports.getRandomDecodoDeviceType =
|
|
33
|
-
exports.getRandomDecodoLocale =
|
|
34
|
-
exports.getRandomDecodoCountry =
|
|
35
|
-
exports.getRandomDecodoCity =
|
|
36
|
-
exports.generateDecodoSessionId =
|
|
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;;
|
package/dist/proxy/index.cjs
CHANGED
|
@@ -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 {
|
package/dist/proxy/index.js
CHANGED
|
@@ -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;
|