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/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 maybeUpdate = value => {
95
+ const update = value => {
93
96
  if (isPositiveNumber(value)) delayMs = value;
94
97
  };
95
98
  while (true) {
96
99
  try {
97
- maybeUpdate(await task());
100
+ update(await task());
98
101
  } catch (error) {
99
- if (onError) maybeUpdate(await onError(error));
102
+ if (onError) update(await onError(error));
100
103
  } finally {
101
104
  if (onFinally) {
102
105
  try {
103
- maybeUpdate(await onFinally());
106
+ update(await onFinally());
104
107
  } catch {}
105
108
  }
106
109
  await sleepMs(delayMs);
107
110
  }
108
111
  }
109
112
  }
110
- async function retry(callFn, maxAttempts, errorFn = null, {
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 callFn();
119
+ return await task();
117
120
  } catch (error) {
118
- if (errorFn) await errorFn(attempt, error);
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 typeof value === 'number' && Number.isFinite(value) && value > 0;
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 = 5000, interval = 100) {
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} milliseconds`));
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 (!object) return undefined;
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 dict = {};
317
+ const obj = {};
314
318
  const cookies = _setCookieParser.default.parse(response, {
315
319
  decodeValues
316
320
  });
317
321
  for (const cookie of cookies) {
318
- dict[cookie.name] = cookie.value;
322
+ obj[cookie.name] = cookie.value;
319
323
  }
320
- return dict;
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 _crypto.default.randomInt(2) === 0;
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[_crypto.default.randomInt(0, characters.length)];
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 (!object) return undefined;
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[_crypto.default.randomInt(0, values.length)];
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 maybeUpdate = value => {
47
+ const update = value => {
48
48
  if (isPositiveNumber(value)) delayMs = value;
49
49
  };
50
50
  while (true) {
51
51
  try {
52
- maybeUpdate(await task());
52
+ update(await task());
53
53
  } catch (error) {
54
- if (onError) maybeUpdate(await onError(error));
54
+ if (onError) update(await onError(error));
55
55
  } finally {
56
56
  if (onFinally) {
57
57
  try {
58
- maybeUpdate(await onFinally());
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(callFn, maxAttempts, errorFn = null, {
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 callFn();
71
+ return await task();
72
72
  } catch (error) {
73
- if (errorFn) await errorFn(attempt, error);
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 typeof value === 'number' && Number.isFinite(value) && value > 0;
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 = 5000, interval = 100) {
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} milliseconds`));
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 (!object) return undefined;
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 dict = {};
269
+ const obj = {};
269
270
  const cookies = setCookieParser.parse(response, {
270
271
  decodeValues
271
272
  });
272
273
  for (const cookie of cookies) {
273
- dict[cookie.name] = cookie.value;
274
+ obj[cookie.name] = cookie.value;
274
275
  }
275
- return dict;
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
  }