melperjs 16.0.0 → 17.0.0
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 +3 -3
- package/docs/docs.md +27 -0
- package/docs/index.md +403 -19
- package/docs/node.md +9 -61
- package/lib/cjs/index.cjs +138 -15
- package/lib/cjs/node.cjs +6 -108
- package/lib/esm/index.mjs +135 -15
- package/lib/esm/node.mjs +7 -106
- package/package.json +2 -2
- package/docs/general.md +0 -363
package/lib/cjs/index.cjs
CHANGED
|
@@ -20,10 +20,13 @@ exports.isTransientHttpCode = isTransientHttpCode;
|
|
|
20
20
|
exports.isValidURL = isValidURL;
|
|
21
21
|
exports.limitString = limitString;
|
|
22
22
|
exports.mulberry32 = mulberry32;
|
|
23
|
+
exports.normalizeProxy = normalizeProxy;
|
|
23
24
|
exports.objectStringify = objectStringify;
|
|
25
|
+
exports.parseProxy = parseProxy;
|
|
24
26
|
exports.pascalCase = pascalCase;
|
|
25
27
|
exports.promiseSilent = promiseSilent;
|
|
26
28
|
exports.promiseTimeout = promiseTimeout;
|
|
29
|
+
exports.proxyValue = proxyValue;
|
|
27
30
|
exports.randomBoolean = randomBoolean;
|
|
28
31
|
exports.randomElement = randomElement;
|
|
29
32
|
exports.randomHex = randomHex;
|
|
@@ -89,33 +92,33 @@ function promiseSilent(promise) {
|
|
|
89
92
|
}
|
|
90
93
|
async function forever(delayMs, task, onError = null, onFinally = null) {
|
|
91
94
|
if (!isPositiveNumber(delayMs)) throw new Error("delayMs must be a positive number");
|
|
92
|
-
const
|
|
95
|
+
const update = value => {
|
|
93
96
|
if (isPositiveNumber(value)) delayMs = value;
|
|
94
97
|
};
|
|
95
98
|
while (true) {
|
|
96
99
|
try {
|
|
97
|
-
|
|
100
|
+
update(await task());
|
|
98
101
|
} catch (error) {
|
|
99
|
-
if (onError)
|
|
102
|
+
if (onError) update(await onError(error));
|
|
100
103
|
} finally {
|
|
101
104
|
if (onFinally) {
|
|
102
105
|
try {
|
|
103
|
-
|
|
106
|
+
update(await onFinally());
|
|
104
107
|
} catch {}
|
|
105
108
|
}
|
|
106
109
|
await sleepMs(delayMs);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
|
-
async function retry(
|
|
113
|
+
async function retry(task, maxAttempts = 1, onError = null, {
|
|
111
114
|
delayMs = 0,
|
|
112
115
|
backoffFactor = 1
|
|
113
116
|
} = {}) {
|
|
114
117
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
115
118
|
try {
|
|
116
|
-
return await
|
|
119
|
+
return await task();
|
|
117
120
|
} catch (error) {
|
|
118
|
-
if (
|
|
121
|
+
if (onError) await onError(error, attempt);
|
|
119
122
|
if (attempt >= maxAttempts) throw error;
|
|
120
123
|
if (delayMs > 0) await sleepMs(delayMs * backoffFactor ** (attempt - 1));
|
|
121
124
|
}
|
|
@@ -146,7 +149,7 @@ function isInt32(value) {
|
|
|
146
149
|
return Number.isInteger(value) && value >= CONSTANTS.INT32_MIN && value <= CONSTANTS.INT32_MAX;
|
|
147
150
|
}
|
|
148
151
|
function isPositiveNumber(value) {
|
|
149
|
-
return
|
|
152
|
+
return Number.isFinite(value) && value > 0;
|
|
150
153
|
}
|
|
151
154
|
function coerceObjectNumbers(object) {
|
|
152
155
|
for (const key of Object.keys(object)) {
|
|
@@ -178,7 +181,7 @@ function findNodeByKey(key, node, pair = null) {
|
|
|
178
181
|
}
|
|
179
182
|
return null;
|
|
180
183
|
}
|
|
181
|
-
function waitForProperty(object, property, timeout
|
|
184
|
+
function waitForProperty(object, property, timeout, interval = 100) {
|
|
182
185
|
return new Promise((resolve, reject) => {
|
|
183
186
|
if (Object.hasOwn(object, property)) {
|
|
184
187
|
resolve(object[property]);
|
|
@@ -191,7 +194,7 @@ function waitForProperty(object, property, timeout = 5000, interval = 100) {
|
|
|
191
194
|
resolve(object[property]);
|
|
192
195
|
} else if (Date.now() - startTime >= timeout) {
|
|
193
196
|
clearInterval(checkProperty);
|
|
194
|
-
reject(new Error(`Property "${property}" did not appear within ${timeout}
|
|
197
|
+
reject(new Error(`Property "${property}" did not appear within ${timeout}ms`));
|
|
195
198
|
}
|
|
196
199
|
}, interval);
|
|
197
200
|
});
|
|
@@ -246,7 +249,7 @@ function randomHex(length) {
|
|
|
246
249
|
}
|
|
247
250
|
return result;
|
|
248
251
|
}
|
|
249
|
-
function randomInteger(min, max) {
|
|
252
|
+
function randomInteger(min, max = undefined) {
|
|
250
253
|
if (typeof max === 'undefined') {
|
|
251
254
|
max = min;
|
|
252
255
|
min = 0;
|
|
@@ -267,6 +270,7 @@ function randomUuid(useDashes = true) {
|
|
|
267
270
|
return useDashes ? uuid : uuid.replaceAll("-", "");
|
|
268
271
|
}
|
|
269
272
|
function randomWeighted(object) {
|
|
273
|
+
if (checkEmpty(object)) return undefined;
|
|
270
274
|
const elements = Object.keys(object);
|
|
271
275
|
const weights = Object.values(object);
|
|
272
276
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
@@ -280,7 +284,7 @@ function randomWeighted(object) {
|
|
|
280
284
|
}
|
|
281
285
|
}
|
|
282
286
|
function randomElement(object) {
|
|
283
|
-
if (
|
|
287
|
+
if (checkEmpty(object)) return undefined;
|
|
284
288
|
const values = Array.isArray(object) ? object : Object.values(object);
|
|
285
289
|
if (values.length === 0) return undefined;
|
|
286
290
|
return values[Math.floor(Math.random() * values.length)];
|
|
@@ -310,14 +314,14 @@ function seedHex(seed, length) {
|
|
|
310
314
|
return result.slice(0, length);
|
|
311
315
|
}
|
|
312
316
|
function cookiesFromResponse(response, decodeValues = false) {
|
|
313
|
-
const
|
|
317
|
+
const obj = {};
|
|
314
318
|
const cookies = _setCookieParser.default.parse(response, {
|
|
315
319
|
decodeValues
|
|
316
320
|
});
|
|
317
321
|
for (const cookie of cookies) {
|
|
318
|
-
|
|
322
|
+
obj[cookie.name] = cookie.value;
|
|
319
323
|
}
|
|
320
|
-
return
|
|
324
|
+
return obj;
|
|
321
325
|
}
|
|
322
326
|
function cookiesToHeader(cookies) {
|
|
323
327
|
if (!cookies) return "";
|
|
@@ -348,4 +352,123 @@ function getResponseError(error, limit = 200) {
|
|
|
348
352
|
response = error.response.data;
|
|
349
353
|
}
|
|
350
354
|
return limitString(response || error.message, limit).trim();
|
|
355
|
+
}
|
|
356
|
+
function normalizeProxy(proxy, protocol = "http") {
|
|
357
|
+
proxy = proxy?.trim();
|
|
358
|
+
if (!proxy) return null;
|
|
359
|
+
const schemeMatch = proxy.match(/^([a-z][a-z0-9+.-]*):\/\/(.+)$/i);
|
|
360
|
+
if (schemeMatch) {
|
|
361
|
+
protocol = schemeMatch[1];
|
|
362
|
+
proxy = schemeMatch[2];
|
|
363
|
+
}
|
|
364
|
+
let auth = "";
|
|
365
|
+
let body = proxy;
|
|
366
|
+
const atIdx = body.lastIndexOf("@");
|
|
367
|
+
if (atIdx !== -1) {
|
|
368
|
+
auth = body.slice(0, atIdx) + "@";
|
|
369
|
+
body = body.slice(atIdx + 1);
|
|
370
|
+
}
|
|
371
|
+
if (!auth) {
|
|
372
|
+
/* Note: when host is single-token (e.g. "localhost") AND password is all-digit port-shaped,
|
|
373
|
+
the heuristic stays ambiguous; prefer `user:pass@host:port` for those cases. */
|
|
374
|
+
const parts = body.split(":");
|
|
375
|
+
const isPort = s => /^\d+$/.test(s) && +s >= 1 && +s <= 65535;
|
|
376
|
+
const isHost = s => s.includes(".") || /[a-z]/i.test(s);
|
|
377
|
+
if (parts.length === 4) {
|
|
378
|
+
if (isPort(parts[3]) && !isPort(parts[1])) {
|
|
379
|
+
// user:pass:host:port
|
|
380
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
381
|
+
body = `${parts[2]}:${parts[3]}`;
|
|
382
|
+
} else if (isPort(parts[1]) && !isPort(parts[3])) {
|
|
383
|
+
// host:port:user:pass
|
|
384
|
+
auth = `${parts[2]}:${parts[3]}@`;
|
|
385
|
+
body = `${parts[0]}:${parts[1]}`;
|
|
386
|
+
} else if (isHost(parts[2]) && !isHost(parts[0])) {
|
|
387
|
+
// user:pass:host:port (ambiguous; host detected at parts[2])
|
|
388
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
389
|
+
body = `${parts[2]}:${parts[3]}`;
|
|
390
|
+
} else {
|
|
391
|
+
// host:port:user:pass (ambiguous fallback)
|
|
392
|
+
auth = `${parts[2]}:${parts[3]}@`;
|
|
393
|
+
body = `${parts[0]}:${parts[1]}`;
|
|
394
|
+
}
|
|
395
|
+
} else if (parts.length === 5) {
|
|
396
|
+
if (isPort(parts[3]) && isPort(parts[4]) && !isPort(parts[1])) {
|
|
397
|
+
// user:pass:host:portStart:portEnd
|
|
398
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
399
|
+
body = `${parts[2]}:${parts[3]}:${parts[4]}`;
|
|
400
|
+
} else if (isPort(parts[1]) && isPort(parts[2]) && !isPort(parts[3])) {
|
|
401
|
+
// host:portStart:portEnd:user:pass
|
|
402
|
+
auth = `${parts[3]}:${parts[4]}@`;
|
|
403
|
+
body = `${parts[0]}:${parts[1]}:${parts[2]}`;
|
|
404
|
+
} else if (isHost(parts[2]) && !isHost(parts[0])) {
|
|
405
|
+
// user:pass:host:portStart:portEnd (ambiguous; host detected at parts[2])
|
|
406
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
407
|
+
body = `${parts[2]}:${parts[3]}:${parts[4]}`;
|
|
408
|
+
} else {
|
|
409
|
+
// host:portStart:portEnd:user:pass (ambiguous fallback)
|
|
410
|
+
auth = `${parts[3]}:${parts[4]}@`;
|
|
411
|
+
body = `${parts[0]}:${parts[1]}:${parts[2]}`;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const parts = body.split(":");
|
|
416
|
+
if (parts.length === 3) {
|
|
417
|
+
const start = Number(parts[1]);
|
|
418
|
+
const end = Number(parts[2]);
|
|
419
|
+
if (Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end) {
|
|
420
|
+
body = `${parts[0]}:${randomInteger(start, end + 1)}`;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return `${protocol}://${auth}${body}`;
|
|
424
|
+
}
|
|
425
|
+
function parseProxy(proxy, protocol = "http") {
|
|
426
|
+
const normalized = normalizeProxy(proxy, protocol);
|
|
427
|
+
if (!normalized) return null;
|
|
428
|
+
const [scheme, rest] = normalized.split("://");
|
|
429
|
+
const atIdx = rest.lastIndexOf("@");
|
|
430
|
+
const authPart = atIdx === -1 ? null : rest.slice(0, atIdx);
|
|
431
|
+
const hostPart = atIdx === -1 ? rest : rest.slice(atIdx + 1);
|
|
432
|
+
const [host, port] = hostPart.split(":");
|
|
433
|
+
const result = {
|
|
434
|
+
protocol: scheme,
|
|
435
|
+
host,
|
|
436
|
+
port: parseInt(port, 10)
|
|
437
|
+
};
|
|
438
|
+
if (authPart !== null) {
|
|
439
|
+
const colonIdx = authPart.indexOf(":");
|
|
440
|
+
const [username, password] = colonIdx === -1 ? [authPart, ""] : [authPart.slice(0, colonIdx), authPart.slice(colonIdx + 1)];
|
|
441
|
+
result.auth = {
|
|
442
|
+
username,
|
|
443
|
+
password
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
function proxyValue(rawProxy, replacements = {}) {
|
|
449
|
+
const list = splitTrim(rawProxy || "");
|
|
450
|
+
if (list.length === 0) return null;
|
|
451
|
+
const picked = list[randomInteger(0, list.length)];
|
|
452
|
+
let result = normalizeProxy(picked);
|
|
453
|
+
if (!result) return null;
|
|
454
|
+
if (result.includes("{")) {
|
|
455
|
+
const {
|
|
456
|
+
SESSION,
|
|
457
|
+
...rest
|
|
458
|
+
} = replacements;
|
|
459
|
+
let sessionValue;
|
|
460
|
+
if (SESSION === undefined) {
|
|
461
|
+
sessionValue = randomHex(8);
|
|
462
|
+
} else if (typeof SESSION === "function") {
|
|
463
|
+
sessionValue = SESSION();
|
|
464
|
+
} else {
|
|
465
|
+
sessionValue = seedHex(String(SESSION), 8);
|
|
466
|
+
}
|
|
467
|
+
result = result.replace("{SESSION}", sessionValue);
|
|
468
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
469
|
+
const v = typeof value === "function" ? value() : String(value);
|
|
470
|
+
result = result.replace(`{${key}}`, v);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
351
474
|
}
|
package/lib/cjs/node.cjs
CHANGED
|
@@ -14,9 +14,6 @@ exports.gitVersion = gitVersion;
|
|
|
14
14
|
exports.hash = hash;
|
|
15
15
|
exports.hostIp = hostIp;
|
|
16
16
|
exports.md5 = md5;
|
|
17
|
-
exports.normalizeProxy = normalizeProxy;
|
|
18
|
-
exports.parseProxy = parseProxy;
|
|
19
|
-
exports.proxyValue = proxyValue;
|
|
20
17
|
exports.readJsonFile = readJsonFile;
|
|
21
18
|
exports.readJsonFileSync = readJsonFileSync;
|
|
22
19
|
exports.secureRandomBoolean = secureRandomBoolean;
|
|
@@ -42,7 +39,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
42
39
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
43
40
|
const execAsync = (0, _util.promisify)(_child_process.exec);
|
|
44
41
|
function secureRandomBoolean() {
|
|
45
|
-
return
|
|
42
|
+
return secureRandomInteger(2) === 1;
|
|
46
43
|
}
|
|
47
44
|
function secureRandomString(length, useNumbers = true, useUppercase = false) {
|
|
48
45
|
let characters = _index.CONSTANTS.LOWER_CASE;
|
|
@@ -50,14 +47,14 @@ function secureRandomString(length, useNumbers = true, useUppercase = false) {
|
|
|
50
47
|
if (useNumbers) characters += _index.CONSTANTS.NUMBERS;
|
|
51
48
|
let result = '';
|
|
52
49
|
for (let i = 0; i < length; i++) {
|
|
53
|
-
result += characters[
|
|
50
|
+
result += characters[secureRandomInteger(0, characters.length)];
|
|
54
51
|
}
|
|
55
52
|
return result;
|
|
56
53
|
}
|
|
57
54
|
function secureRandomHex(length) {
|
|
58
55
|
return _crypto.default.randomBytes(Math.ceil(length / 2)).toString('hex').slice(0, length);
|
|
59
56
|
}
|
|
60
|
-
function secureRandomInteger(min, max) {
|
|
57
|
+
function secureRandomInteger(min, max = undefined) {
|
|
61
58
|
return _crypto.default.randomInt(min, max);
|
|
62
59
|
}
|
|
63
60
|
function secureRandomUuid(useDashes = true) {
|
|
@@ -65,6 +62,7 @@ function secureRandomUuid(useDashes = true) {
|
|
|
65
62
|
return useDashes ? uuid : uuid.replaceAll("-", "");
|
|
66
63
|
}
|
|
67
64
|
function secureRandomWeighted(object) {
|
|
65
|
+
if ((0, _index.checkEmpty)(object)) return undefined;
|
|
68
66
|
const elements = Object.keys(object);
|
|
69
67
|
const weights = Object.values(object);
|
|
70
68
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
@@ -78,10 +76,10 @@ function secureRandomWeighted(object) {
|
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
78
|
function secureRandomElement(object) {
|
|
81
|
-
if (
|
|
79
|
+
if ((0, _index.checkEmpty)(object)) return undefined;
|
|
82
80
|
const values = Array.isArray(object) ? object : Object.values(object);
|
|
83
81
|
if (values.length === 0) return undefined;
|
|
84
|
-
return values[
|
|
82
|
+
return values[secureRandomInteger(0, values.length)];
|
|
85
83
|
}
|
|
86
84
|
function uuidFromSeed(seed, useDashes = true) {
|
|
87
85
|
const hash = _crypto.default.createHash('md5').update(seed).digest();
|
|
@@ -127,106 +125,6 @@ function bcryptVerify(plainText, hash, {
|
|
|
127
125
|
}
|
|
128
126
|
return _bcryptjs.default.compareSync(input, hash);
|
|
129
127
|
}
|
|
130
|
-
function normalizeProxy(proxy, protocol = "http") {
|
|
131
|
-
proxy = proxy?.trim();
|
|
132
|
-
if (!proxy) return null;
|
|
133
|
-
const schemeMatch = proxy.match(/^([a-z][a-z0-9+.-]*):\/\/(.+)$/i);
|
|
134
|
-
if (schemeMatch) {
|
|
135
|
-
protocol = schemeMatch[1];
|
|
136
|
-
proxy = schemeMatch[2];
|
|
137
|
-
}
|
|
138
|
-
let auth = "";
|
|
139
|
-
let body = proxy;
|
|
140
|
-
const atIdx = body.lastIndexOf("@");
|
|
141
|
-
if (atIdx !== -1) {
|
|
142
|
-
auth = body.slice(0, atIdx) + "@";
|
|
143
|
-
body = body.slice(atIdx + 1);
|
|
144
|
-
}
|
|
145
|
-
if (!auth) {
|
|
146
|
-
// Note: when the password itself is all-digit and port-shaped (e.g. "admin:1234:host:port"),
|
|
147
|
-
// the heuristic cannot distinguish auth-first from host-first ordering and may pick the wrong branch.
|
|
148
|
-
const parts = body.split(":");
|
|
149
|
-
const isPort = s => /^\d+$/.test(s) && +s >= 1 && +s <= 65535;
|
|
150
|
-
if (parts.length === 4) {
|
|
151
|
-
if (isPort(parts[3]) && !isPort(parts[1])) {
|
|
152
|
-
// user:pass:host:port
|
|
153
|
-
auth = `${parts[0]}:${parts[1]}@`;
|
|
154
|
-
body = `${parts[2]}:${parts[3]}`;
|
|
155
|
-
} else {
|
|
156
|
-
// host:port:user:pass (default)
|
|
157
|
-
auth = `${parts[2]}:${parts[3]}@`;
|
|
158
|
-
body = `${parts[0]}:${parts[1]}`;
|
|
159
|
-
}
|
|
160
|
-
} else if (parts.length === 5) {
|
|
161
|
-
if (isPort(parts[3]) && isPort(parts[4]) && !isPort(parts[1])) {
|
|
162
|
-
// user:pass:host:portStart:portEnd
|
|
163
|
-
auth = `${parts[0]}:${parts[1]}@`;
|
|
164
|
-
body = `${parts[2]}:${parts[3]}:${parts[4]}`;
|
|
165
|
-
} else {
|
|
166
|
-
// host:portStart:portEnd:user:pass (default)
|
|
167
|
-
auth = `${parts[3]}:${parts[4]}@`;
|
|
168
|
-
body = `${parts[0]}:${parts[1]}:${parts[2]}`;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const parts = body.split(":");
|
|
173
|
-
if (parts.length === 3) {
|
|
174
|
-
const start = Number(parts[1]);
|
|
175
|
-
const end = Number(parts[2]);
|
|
176
|
-
if (Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end) {
|
|
177
|
-
body = `${parts[0]}:${_crypto.default.randomInt(start, end + 1)}`;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return `${protocol}://${auth}${body}`;
|
|
181
|
-
}
|
|
182
|
-
function parseProxy(proxy, protocol = "http") {
|
|
183
|
-
const normalized = normalizeProxy(proxy, protocol);
|
|
184
|
-
if (!normalized) return null;
|
|
185
|
-
const [scheme, rest] = normalized.split("://");
|
|
186
|
-
const atIdx = rest.lastIndexOf("@");
|
|
187
|
-
const authPart = atIdx === -1 ? null : rest.slice(0, atIdx);
|
|
188
|
-
const hostPart = atIdx === -1 ? rest : rest.slice(atIdx + 1);
|
|
189
|
-
const [host, port] = hostPart.split(":");
|
|
190
|
-
const result = {
|
|
191
|
-
protocol: scheme,
|
|
192
|
-
host,
|
|
193
|
-
port: parseInt(port, 10)
|
|
194
|
-
};
|
|
195
|
-
if (authPart !== null) {
|
|
196
|
-
const colonIdx = authPart.indexOf(":");
|
|
197
|
-
const [username, password] = colonIdx === -1 ? [authPart, ""] : [authPart.slice(0, colonIdx), authPart.slice(colonIdx + 1)];
|
|
198
|
-
result.auth = {
|
|
199
|
-
username,
|
|
200
|
-
password
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
function proxyValue(rawProxy, replacements = {}) {
|
|
206
|
-
const list = (0, _index.splitTrim)(rawProxy || "");
|
|
207
|
-
if (list.length === 0) return null;
|
|
208
|
-
const picked = list[(0, _index.randomInteger)(0, list.length)];
|
|
209
|
-
const {
|
|
210
|
-
SESSION,
|
|
211
|
-
...rest
|
|
212
|
-
} = replacements;
|
|
213
|
-
let sessionValue;
|
|
214
|
-
if (SESSION === undefined) {
|
|
215
|
-
sessionValue = (0, _index.randomHex)(8);
|
|
216
|
-
} else if (typeof SESSION === "function") {
|
|
217
|
-
sessionValue = SESSION();
|
|
218
|
-
} else {
|
|
219
|
-
sessionValue = (0, _index.seedHex)(String(SESSION), 8);
|
|
220
|
-
}
|
|
221
|
-
let result = normalizeProxy(picked);
|
|
222
|
-
if (!result) return null;
|
|
223
|
-
result = result.replace("{SESSION}", sessionValue);
|
|
224
|
-
for (const [key, value] of Object.entries(rest)) {
|
|
225
|
-
const v = typeof value === "function" ? value() : String(value);
|
|
226
|
-
result = result.replace(`{${key}}`, v);
|
|
227
|
-
}
|
|
228
|
-
return result;
|
|
229
|
-
}
|
|
230
128
|
async function readJsonFile(filePath, defaultValue = {}) {
|
|
231
129
|
try {
|
|
232
130
|
const data = await _fs.promises.readFile(filePath, 'utf8');
|
package/lib/esm/index.mjs
CHANGED
|
@@ -44,33 +44,33 @@ export function promiseSilent(promise) {
|
|
|
44
44
|
}
|
|
45
45
|
export async function forever(delayMs, task, onError = null, onFinally = null) {
|
|
46
46
|
if (!isPositiveNumber(delayMs)) throw new Error("delayMs must be a positive number");
|
|
47
|
-
const
|
|
47
|
+
const update = value => {
|
|
48
48
|
if (isPositiveNumber(value)) delayMs = value;
|
|
49
49
|
};
|
|
50
50
|
while (true) {
|
|
51
51
|
try {
|
|
52
|
-
|
|
52
|
+
update(await task());
|
|
53
53
|
} catch (error) {
|
|
54
|
-
if (onError)
|
|
54
|
+
if (onError) update(await onError(error));
|
|
55
55
|
} finally {
|
|
56
56
|
if (onFinally) {
|
|
57
57
|
try {
|
|
58
|
-
|
|
58
|
+
update(await onFinally());
|
|
59
59
|
} catch {}
|
|
60
60
|
}
|
|
61
61
|
await sleepMs(delayMs);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
export async function retry(
|
|
65
|
+
export async function retry(task, maxAttempts = 1, onError = null, {
|
|
66
66
|
delayMs = 0,
|
|
67
67
|
backoffFactor = 1
|
|
68
68
|
} = {}) {
|
|
69
69
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
70
70
|
try {
|
|
71
|
-
return await
|
|
71
|
+
return await task();
|
|
72
72
|
} catch (error) {
|
|
73
|
-
if (
|
|
73
|
+
if (onError) await onError(error, attempt);
|
|
74
74
|
if (attempt >= maxAttempts) throw error;
|
|
75
75
|
if (delayMs > 0) await sleepMs(delayMs * backoffFactor ** (attempt - 1));
|
|
76
76
|
}
|
|
@@ -101,7 +101,7 @@ export function isInt32(value) {
|
|
|
101
101
|
return Number.isInteger(value) && value >= CONSTANTS.INT32_MIN && value <= CONSTANTS.INT32_MAX;
|
|
102
102
|
}
|
|
103
103
|
export function isPositiveNumber(value) {
|
|
104
|
-
return
|
|
104
|
+
return Number.isFinite(value) && value > 0;
|
|
105
105
|
}
|
|
106
106
|
export function coerceObjectNumbers(object) {
|
|
107
107
|
for (const key of Object.keys(object)) {
|
|
@@ -133,7 +133,7 @@ export function findNodeByKey(key, node, pair = null) {
|
|
|
133
133
|
}
|
|
134
134
|
return null;
|
|
135
135
|
}
|
|
136
|
-
export function waitForProperty(object, property, timeout
|
|
136
|
+
export function waitForProperty(object, property, timeout, interval = 100) {
|
|
137
137
|
return new Promise((resolve, reject) => {
|
|
138
138
|
if (Object.hasOwn(object, property)) {
|
|
139
139
|
resolve(object[property]);
|
|
@@ -146,7 +146,7 @@ export function waitForProperty(object, property, timeout = 5000, interval = 100
|
|
|
146
146
|
resolve(object[property]);
|
|
147
147
|
} else if (Date.now() - startTime >= timeout) {
|
|
148
148
|
clearInterval(checkProperty);
|
|
149
|
-
reject(new Error(`Property "${property}" did not appear within ${timeout}
|
|
149
|
+
reject(new Error(`Property "${property}" did not appear within ${timeout}ms`));
|
|
150
150
|
}
|
|
151
151
|
}, interval);
|
|
152
152
|
});
|
|
@@ -201,7 +201,7 @@ export function randomHex(length) {
|
|
|
201
201
|
}
|
|
202
202
|
return result;
|
|
203
203
|
}
|
|
204
|
-
export function randomInteger(min, max) {
|
|
204
|
+
export function randomInteger(min, max = undefined) {
|
|
205
205
|
if (typeof max === 'undefined') {
|
|
206
206
|
max = min;
|
|
207
207
|
min = 0;
|
|
@@ -222,6 +222,7 @@ export function randomUuid(useDashes = true) {
|
|
|
222
222
|
return useDashes ? uuid : uuid.replaceAll("-", "");
|
|
223
223
|
}
|
|
224
224
|
export function randomWeighted(object) {
|
|
225
|
+
if (checkEmpty(object)) return undefined;
|
|
225
226
|
const elements = Object.keys(object);
|
|
226
227
|
const weights = Object.values(object);
|
|
227
228
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
@@ -235,7 +236,7 @@ export function randomWeighted(object) {
|
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
export function randomElement(object) {
|
|
238
|
-
if (
|
|
239
|
+
if (checkEmpty(object)) return undefined;
|
|
239
240
|
const values = Array.isArray(object) ? object : Object.values(object);
|
|
240
241
|
if (values.length === 0) return undefined;
|
|
241
242
|
return values[Math.floor(Math.random() * values.length)];
|
|
@@ -265,14 +266,14 @@ export function seedHex(seed, length) {
|
|
|
265
266
|
return result.slice(0, length);
|
|
266
267
|
}
|
|
267
268
|
export function cookiesFromResponse(response, decodeValues = false) {
|
|
268
|
-
const
|
|
269
|
+
const obj = {};
|
|
269
270
|
const cookies = setCookieParser.parse(response, {
|
|
270
271
|
decodeValues
|
|
271
272
|
});
|
|
272
273
|
for (const cookie of cookies) {
|
|
273
|
-
|
|
274
|
+
obj[cookie.name] = cookie.value;
|
|
274
275
|
}
|
|
275
|
-
return
|
|
276
|
+
return obj;
|
|
276
277
|
}
|
|
277
278
|
export function cookiesToHeader(cookies) {
|
|
278
279
|
if (!cookies) return "";
|
|
@@ -303,4 +304,123 @@ export function getResponseError(error, limit = 200) {
|
|
|
303
304
|
response = error.response.data;
|
|
304
305
|
}
|
|
305
306
|
return limitString(response || error.message, limit).trim();
|
|
307
|
+
}
|
|
308
|
+
export function normalizeProxy(proxy, protocol = "http") {
|
|
309
|
+
proxy = proxy?.trim();
|
|
310
|
+
if (!proxy) return null;
|
|
311
|
+
const schemeMatch = proxy.match(/^([a-z][a-z0-9+.-]*):\/\/(.+)$/i);
|
|
312
|
+
if (schemeMatch) {
|
|
313
|
+
protocol = schemeMatch[1];
|
|
314
|
+
proxy = schemeMatch[2];
|
|
315
|
+
}
|
|
316
|
+
let auth = "";
|
|
317
|
+
let body = proxy;
|
|
318
|
+
const atIdx = body.lastIndexOf("@");
|
|
319
|
+
if (atIdx !== -1) {
|
|
320
|
+
auth = body.slice(0, atIdx) + "@";
|
|
321
|
+
body = body.slice(atIdx + 1);
|
|
322
|
+
}
|
|
323
|
+
if (!auth) {
|
|
324
|
+
/* Note: when host is single-token (e.g. "localhost") AND password is all-digit port-shaped,
|
|
325
|
+
the heuristic stays ambiguous; prefer `user:pass@host:port` for those cases. */
|
|
326
|
+
const parts = body.split(":");
|
|
327
|
+
const isPort = s => /^\d+$/.test(s) && +s >= 1 && +s <= 65535;
|
|
328
|
+
const isHost = s => s.includes(".") || /[a-z]/i.test(s);
|
|
329
|
+
if (parts.length === 4) {
|
|
330
|
+
if (isPort(parts[3]) && !isPort(parts[1])) {
|
|
331
|
+
// user:pass:host:port
|
|
332
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
333
|
+
body = `${parts[2]}:${parts[3]}`;
|
|
334
|
+
} else if (isPort(parts[1]) && !isPort(parts[3])) {
|
|
335
|
+
// host:port:user:pass
|
|
336
|
+
auth = `${parts[2]}:${parts[3]}@`;
|
|
337
|
+
body = `${parts[0]}:${parts[1]}`;
|
|
338
|
+
} else if (isHost(parts[2]) && !isHost(parts[0])) {
|
|
339
|
+
// user:pass:host:port (ambiguous; host detected at parts[2])
|
|
340
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
341
|
+
body = `${parts[2]}:${parts[3]}`;
|
|
342
|
+
} else {
|
|
343
|
+
// host:port:user:pass (ambiguous fallback)
|
|
344
|
+
auth = `${parts[2]}:${parts[3]}@`;
|
|
345
|
+
body = `${parts[0]}:${parts[1]}`;
|
|
346
|
+
}
|
|
347
|
+
} else if (parts.length === 5) {
|
|
348
|
+
if (isPort(parts[3]) && isPort(parts[4]) && !isPort(parts[1])) {
|
|
349
|
+
// user:pass:host:portStart:portEnd
|
|
350
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
351
|
+
body = `${parts[2]}:${parts[3]}:${parts[4]}`;
|
|
352
|
+
} else if (isPort(parts[1]) && isPort(parts[2]) && !isPort(parts[3])) {
|
|
353
|
+
// host:portStart:portEnd:user:pass
|
|
354
|
+
auth = `${parts[3]}:${parts[4]}@`;
|
|
355
|
+
body = `${parts[0]}:${parts[1]}:${parts[2]}`;
|
|
356
|
+
} else if (isHost(parts[2]) && !isHost(parts[0])) {
|
|
357
|
+
// user:pass:host:portStart:portEnd (ambiguous; host detected at parts[2])
|
|
358
|
+
auth = `${parts[0]}:${parts[1]}@`;
|
|
359
|
+
body = `${parts[2]}:${parts[3]}:${parts[4]}`;
|
|
360
|
+
} else {
|
|
361
|
+
// host:portStart:portEnd:user:pass (ambiguous fallback)
|
|
362
|
+
auth = `${parts[3]}:${parts[4]}@`;
|
|
363
|
+
body = `${parts[0]}:${parts[1]}:${parts[2]}`;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const parts = body.split(":");
|
|
368
|
+
if (parts.length === 3) {
|
|
369
|
+
const start = Number(parts[1]);
|
|
370
|
+
const end = Number(parts[2]);
|
|
371
|
+
if (Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end) {
|
|
372
|
+
body = `${parts[0]}:${randomInteger(start, end + 1)}`;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return `${protocol}://${auth}${body}`;
|
|
376
|
+
}
|
|
377
|
+
export function parseProxy(proxy, protocol = "http") {
|
|
378
|
+
const normalized = normalizeProxy(proxy, protocol);
|
|
379
|
+
if (!normalized) return null;
|
|
380
|
+
const [scheme, rest] = normalized.split("://");
|
|
381
|
+
const atIdx = rest.lastIndexOf("@");
|
|
382
|
+
const authPart = atIdx === -1 ? null : rest.slice(0, atIdx);
|
|
383
|
+
const hostPart = atIdx === -1 ? rest : rest.slice(atIdx + 1);
|
|
384
|
+
const [host, port] = hostPart.split(":");
|
|
385
|
+
const result = {
|
|
386
|
+
protocol: scheme,
|
|
387
|
+
host,
|
|
388
|
+
port: parseInt(port, 10)
|
|
389
|
+
};
|
|
390
|
+
if (authPart !== null) {
|
|
391
|
+
const colonIdx = authPart.indexOf(":");
|
|
392
|
+
const [username, password] = colonIdx === -1 ? [authPart, ""] : [authPart.slice(0, colonIdx), authPart.slice(colonIdx + 1)];
|
|
393
|
+
result.auth = {
|
|
394
|
+
username,
|
|
395
|
+
password
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
export function proxyValue(rawProxy, replacements = {}) {
|
|
401
|
+
const list = splitTrim(rawProxy || "");
|
|
402
|
+
if (list.length === 0) return null;
|
|
403
|
+
const picked = list[randomInteger(0, list.length)];
|
|
404
|
+
let result = normalizeProxy(picked);
|
|
405
|
+
if (!result) return null;
|
|
406
|
+
if (result.includes("{")) {
|
|
407
|
+
const {
|
|
408
|
+
SESSION,
|
|
409
|
+
...rest
|
|
410
|
+
} = replacements;
|
|
411
|
+
let sessionValue;
|
|
412
|
+
if (SESSION === undefined) {
|
|
413
|
+
sessionValue = randomHex(8);
|
|
414
|
+
} else if (typeof SESSION === "function") {
|
|
415
|
+
sessionValue = SESSION();
|
|
416
|
+
} else {
|
|
417
|
+
sessionValue = seedHex(String(SESSION), 8);
|
|
418
|
+
}
|
|
419
|
+
result = result.replace("{SESSION}", sessionValue);
|
|
420
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
421
|
+
const v = typeof value === "function" ? value() : String(value);
|
|
422
|
+
result = result.replace(`{${key}}`, v);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return result;
|
|
306
426
|
}
|