melperjs 17.0.0 → 17.1.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/esm/index.mjs DELETED
@@ -1,426 +0,0 @@
1
- import xss from "xss";
2
- import setCookieParser from "set-cookie-parser";
3
- import camelCase from "lodash/camelCase.js";
4
- import upperFirst from "lodash/upperFirst.js";
5
- import isEmpty from "lodash/isEmpty.js";
6
- import shuffle from "lodash/shuffle.js";
7
- export const CONSTANTS = {
8
- LOWER_CASE: "abcdefghijklmnopqrstuvwxyz",
9
- UPPER_CASE: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
10
- HEXADECIMAL: "0123456789abcdef",
11
- NUMBERS: "0123456789",
12
- INT32_MIN: -2147483648,
13
- INT32_MAX: 2147483647
14
- };
15
- const NUMBER_PATTERN = /^-?\d+(\.\d+)?(e[+-]?\d+)?$/i;
16
- const INTEGER_PATTERN = /^-?\d+$/;
17
- export function Exception(message, response = {}, name = null) {
18
- const error = new Error(message);
19
- error.name = name || "Exception";
20
- error.response = response;
21
- if (checkEmpty(response)) {
22
- error.response = {};
23
- }
24
- return error;
25
- }
26
- export function time() {
27
- return Math.floor(Date.now() / 1000);
28
- }
29
- export function sleepMs(milliseconds) {
30
- return new Promise(resolve => setTimeout(resolve, milliseconds));
31
- }
32
- export function sleep(seconds) {
33
- return sleepMs(seconds * 1000);
34
- }
35
- export function promiseTimeout(milliseconds, promise) {
36
- let timer;
37
- const timeout = new Promise((_, reject) => {
38
- timer = setTimeout(() => reject(new Error(`Promise timed out after ${milliseconds}ms`)), milliseconds);
39
- });
40
- return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
41
- }
42
- export function promiseSilent(promise) {
43
- return promise.then(() => {}).catch(() => {});
44
- }
45
- export async function forever(delayMs, task, onError = null, onFinally = null) {
46
- if (!isPositiveNumber(delayMs)) throw new Error("delayMs must be a positive number");
47
- const update = value => {
48
- if (isPositiveNumber(value)) delayMs = value;
49
- };
50
- while (true) {
51
- try {
52
- update(await task());
53
- } catch (error) {
54
- if (onError) update(await onError(error));
55
- } finally {
56
- if (onFinally) {
57
- try {
58
- update(await onFinally());
59
- } catch {}
60
- }
61
- await sleepMs(delayMs);
62
- }
63
- }
64
- }
65
- export async function retry(task, maxAttempts = 1, onError = null, {
66
- delayMs = 0,
67
- backoffFactor = 1
68
- } = {}) {
69
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
70
- try {
71
- return await task();
72
- } catch (error) {
73
- if (onError) await onError(error, attempt);
74
- if (attempt >= maxAttempts) throw error;
75
- if (delayMs > 0) await sleepMs(delayMs * backoffFactor ** (attempt - 1));
76
- }
77
- }
78
- }
79
- export function isValidURL(url) {
80
- try {
81
- new URL(url);
82
- return true;
83
- } catch {
84
- return false;
85
- }
86
- }
87
- export function splitTrim(string, separator = null) {
88
- return string.split(separator ?? /\r?\n/).map(item => item.trim()).filter(Boolean);
89
- }
90
- export function checkEmpty(value) {
91
- if (typeof value === "number") return value === 0;
92
- return isEmpty(value);
93
- }
94
- export function pascalCase(string) {
95
- return upperFirst(camelCase(string));
96
- }
97
- export function titleCase(string, separator = " ") {
98
- return (string || "").split(separator).map(upperFirst).join(separator);
99
- }
100
- export function isInt32(value) {
101
- return Number.isInteger(value) && value >= CONSTANTS.INT32_MIN && value <= CONSTANTS.INT32_MAX;
102
- }
103
- export function isPositiveNumber(value) {
104
- return Number.isFinite(value) && value > 0;
105
- }
106
- export function coerceObjectNumbers(object) {
107
- for (const key of Object.keys(object)) {
108
- const value = object[key];
109
- if (typeof value === 'string' && NUMBER_PATTERN.test(value)) {
110
- object[key] = parseFloat(value);
111
- }
112
- }
113
- return object;
114
- }
115
- export function coerceObjectIntegers(object) {
116
- for (const key of Object.keys(object)) {
117
- const value = object[key];
118
- if (typeof value === 'string' && INTEGER_PATTERN.test(value)) {
119
- object[key] = parseInt(value);
120
- }
121
- }
122
- return object;
123
- }
124
- export function findNodeByKey(key, node, pair = null) {
125
- if (node && typeof node === 'object') {
126
- if (Object.hasOwn(node, key) && (pair === null || node[key] === pair)) {
127
- return node;
128
- }
129
- for (const childKey of Object.keys(node)) {
130
- const result = findNodeByKey(key, node[childKey], pair);
131
- if (result) return result;
132
- }
133
- }
134
- return null;
135
- }
136
- export function waitForProperty(object, property, timeout, interval = 100) {
137
- return new Promise((resolve, reject) => {
138
- if (Object.hasOwn(object, property)) {
139
- resolve(object[property]);
140
- return;
141
- }
142
- const startTime = Date.now();
143
- const checkProperty = setInterval(() => {
144
- if (Object.hasOwn(object, property)) {
145
- clearInterval(checkProperty);
146
- resolve(object[property]);
147
- } else if (Date.now() - startTime >= timeout) {
148
- clearInterval(checkProperty);
149
- reject(new Error(`Property "${property}" did not appear within ${timeout}ms`));
150
- }
151
- }, interval);
152
- });
153
- }
154
- export function shuffleObject(object) {
155
- return Object.fromEntries(shuffle(Object.entries(object)));
156
- }
157
- export function objectStringify(object) {
158
- for (const key of Object.keys(object)) {
159
- const value = object[key];
160
- if (value !== null && typeof value === 'object') {
161
- objectStringify(value);
162
- } else {
163
- object[key] = String(value);
164
- }
165
- }
166
- return object;
167
- }
168
- export function limitString(string, limit = 35, omission = "...") {
169
- string = string || "";
170
- if (string.length <= limit) return string;
171
- return string.slice(0, limit - omission.length) + omission;
172
- }
173
- export function safeString(string) {
174
- return xss(string || "", {
175
- whiteList: {},
176
- stripIgnoreTag: true,
177
- stripIgnoreTagBody: ["script", "style", "iframe", "object", "embed", "form"],
178
- css: false
179
- });
180
- }
181
- export function shuffleString(string) {
182
- return shuffle(string.split('')).join('');
183
- }
184
- export function randomBoolean() {
185
- return Math.random() < 0.5;
186
- }
187
- export function randomString(length, useNumbers = true, useUppercase = false) {
188
- let characters = CONSTANTS.LOWER_CASE;
189
- if (useUppercase) characters += CONSTANTS.UPPER_CASE;
190
- if (useNumbers) characters += CONSTANTS.NUMBERS;
191
- let result = '';
192
- for (let i = 0; i < length; i++) {
193
- result += characters[Math.random() * characters.length | 0];
194
- }
195
- return result;
196
- }
197
- export function randomHex(length) {
198
- let result = '';
199
- for (let i = 0; i < length; i++) {
200
- result += CONSTANTS.HEXADECIMAL[Math.random() * 16 | 0];
201
- }
202
- return result;
203
- }
204
- export function randomInteger(min, max = undefined) {
205
- if (typeof max === 'undefined') {
206
- max = min;
207
- min = 0;
208
- }
209
- if (typeof min !== 'number' || typeof max !== 'number') {
210
- throw new Error('min and max must be numerical values');
211
- }
212
- if (max <= min) {
213
- throw new Error('max must be greater than min');
214
- }
215
- return Math.floor(Math.random() * (max - min)) + min;
216
- }
217
- export function randomUuid(useDashes = true) {
218
- const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
219
- const r = Math.random() * 16 | 0;
220
- return (c === 'x' ? r : r & 0x3 | 0x8).toString(16);
221
- });
222
- return useDashes ? uuid : uuid.replaceAll("-", "");
223
- }
224
- export function randomWeighted(object) {
225
- if (checkEmpty(object)) return undefined;
226
- const elements = Object.keys(object);
227
- const weights = Object.values(object);
228
- const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
229
- const randomNum = Math.random() * totalWeight;
230
- let weightSum = 0;
231
- for (let i = 0; i < elements.length; i++) {
232
- weightSum += weights[i];
233
- if (randomNum < weightSum) {
234
- return elements[i];
235
- }
236
- }
237
- }
238
- export function randomElement(object) {
239
- if (checkEmpty(object)) return undefined;
240
- const values = Array.isArray(object) ? object : Object.values(object);
241
- if (values.length === 0) return undefined;
242
- return values[Math.floor(Math.random() * values.length)];
243
- }
244
- export function mulberry32(seed) {
245
- if (typeof seed === "string") {
246
- let h = 0;
247
- for (let i = 0; i < seed.length; i++) {
248
- h = Math.imul(h ^ seed.charCodeAt(i), 2654435761);
249
- }
250
- seed = h >>> 0;
251
- }
252
- return function () {
253
- seed = seed + 0x6D2B79F5 | 0;
254
- let t = seed;
255
- t = Math.imul(t ^ t >>> 15, t | 1);
256
- t ^= t + Math.imul(t ^ t >>> 7, t | 61);
257
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
258
- };
259
- }
260
- export function seedHex(seed, length) {
261
- const rng = mulberry32(String(seed));
262
- let result = '';
263
- while (result.length < length) {
264
- result += Math.floor(rng() * 0x100000000).toString(16).padStart(8, '0');
265
- }
266
- return result.slice(0, length);
267
- }
268
- export function cookiesFromResponse(response, decodeValues = false) {
269
- const obj = {};
270
- const cookies = setCookieParser.parse(response, {
271
- decodeValues
272
- });
273
- for (const cookie of cookies) {
274
- obj[cookie.name] = cookie.value;
275
- }
276
- return obj;
277
- }
278
- export function cookiesToHeader(cookies) {
279
- if (!cookies) return "";
280
- return Object.entries(cookies).filter(([, value]) => value !== null && value !== undefined).map(([key, value]) => `${key}=${value}`).join("; ");
281
- }
282
- export function cookiesFromHeader(header) {
283
- const cookies = {};
284
- if (!header) return cookies;
285
- header.split(';').forEach(cookie => {
286
- const trimmed = cookie.trim();
287
- if (!trimmed.includes('=')) return;
288
- const [key, ...valueParts] = trimmed.split('=');
289
- const trimmedKey = key.trim();
290
- if (trimmedKey) {
291
- cookies[trimmedKey] = valueParts.join('=').trim();
292
- }
293
- });
294
- return cookies;
295
- }
296
- export function isTransientHttpCode(httpCode) {
297
- return !httpCode || isNaN(httpCode) || httpCode === 100 || httpCode === 402 || httpCode === 407 || 460 <= httpCode && httpCode < 470 || 500 <= httpCode;
298
- }
299
- export function getResponseError(error, limit = 200) {
300
- let response;
301
- if (error?.response?.status && error.response.data) {
302
- response = `${error.response.status}|${error.response.data}`;
303
- } else if (error?.response?.data) {
304
- response = error.response.data;
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;
426
- }
package/lib/esm/node.mjs DELETED
@@ -1,169 +0,0 @@
1
- import fs from "fs";
2
- import { promises as fsp } from "fs";
3
- import path from "path";
4
- import crypto from "crypto";
5
- import { networkInterfaces } from "os";
6
- import { exec, execFileSync } from "child_process";
7
- import { promisify } from "util";
8
- import bcrypt from "bcryptjs";
9
- import { CONSTANTS, checkEmpty } from "./index.mjs";
10
- const execAsync = promisify(exec);
11
- export function secureRandomBoolean() {
12
- return secureRandomInteger(2) === 1;
13
- }
14
- export function secureRandomString(length, useNumbers = true, useUppercase = false) {
15
- let characters = CONSTANTS.LOWER_CASE;
16
- if (useUppercase) characters += CONSTANTS.UPPER_CASE;
17
- if (useNumbers) characters += CONSTANTS.NUMBERS;
18
- let result = '';
19
- for (let i = 0; i < length; i++) {
20
- result += characters[secureRandomInteger(0, characters.length)];
21
- }
22
- return result;
23
- }
24
- export function secureRandomHex(length) {
25
- return crypto.randomBytes(Math.ceil(length / 2)).toString('hex').slice(0, length);
26
- }
27
- export function secureRandomInteger(min, max = undefined) {
28
- return crypto.randomInt(min, max);
29
- }
30
- export function secureRandomUuid(useDashes = true) {
31
- const uuid = crypto.randomUUID();
32
- return useDashes ? uuid : uuid.replaceAll("-", "");
33
- }
34
- export function secureRandomWeighted(object) {
35
- if (checkEmpty(object)) return undefined;
36
- const elements = Object.keys(object);
37
- const weights = Object.values(object);
38
- const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
39
- const randomNum = secureRandomInteger(0, totalWeight);
40
- let weightSum = 0;
41
- for (let i = 0; i < elements.length; i++) {
42
- weightSum += weights[i];
43
- if (randomNum < weightSum) {
44
- return elements[i];
45
- }
46
- }
47
- }
48
- export function secureRandomElement(object) {
49
- if (checkEmpty(object)) return undefined;
50
- const values = Array.isArray(object) ? object : Object.values(object);
51
- if (values.length === 0) return undefined;
52
- return values[secureRandomInteger(0, values.length)];
53
- }
54
- export function uuidFromSeed(seed, useDashes = true) {
55
- const hash = crypto.createHash('md5').update(seed).digest();
56
- hash[6] = hash[6] & 0x0f | 0x30;
57
- hash[8] = hash[8] & 0x3f | 0x80;
58
- const hex = hash.toString('hex');
59
- if (!useDashes) return hex;
60
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
61
- }
62
- export function hash(algorithm, data) {
63
- return crypto.createHash(algorithm).update(data).digest("hex");
64
- }
65
- export function md5(data) {
66
- return hash("md5", data);
67
- }
68
- export function sha256(data) {
69
- return hash("sha256", data);
70
- }
71
- export function base64Encode(data) {
72
- return Buffer.from(data).toString('base64');
73
- }
74
- export function base64Decode(data, encoding = 'utf8') {
75
- return Buffer.from(data, 'base64').toString(encoding);
76
- }
77
- export function bcryptHash(plainText, {
78
- key = "",
79
- strength = 12,
80
- preHash = true
81
- } = {}) {
82
- let input = plainText + key;
83
- if (preHash) {
84
- input = sha256(input);
85
- }
86
- return bcrypt.hashSync(input, strength);
87
- }
88
- export function bcryptVerify(plainText, hash, {
89
- key = "",
90
- preHash = true
91
- } = {}) {
92
- let input = plainText + key;
93
- if (preHash) {
94
- input = sha256(input);
95
- }
96
- return bcrypt.compareSync(input, hash);
97
- }
98
- export async function readJsonFile(filePath, defaultValue = {}) {
99
- try {
100
- const data = await fsp.readFile(filePath, 'utf8');
101
- return JSON.parse(data);
102
- } catch {
103
- return defaultValue;
104
- }
105
- }
106
- export function readJsonFileSync(filePath, defaultValue = {}) {
107
- try {
108
- const data = fs.readFileSync(filePath, 'utf8');
109
- return JSON.parse(data);
110
- } catch {
111
- return defaultValue;
112
- }
113
- }
114
- export function writeJsonFile(filePath, data) {
115
- const jsonData = JSON.stringify(data);
116
- return fsp.writeFile(filePath, jsonData, 'utf8');
117
- }
118
- export function writeJsonFileSync(filePath, data) {
119
- const jsonData = JSON.stringify(data);
120
- return fs.writeFileSync(filePath, jsonData, 'utf8');
121
- }
122
- export async function clearDirectory(directoryPath, keepDir = true) {
123
- await fsp.rm(directoryPath, {
124
- recursive: true,
125
- force: true
126
- });
127
- if (keepDir) await fsp.mkdir(directoryPath, {
128
- recursive: true
129
- });
130
- }
131
- export function createNumberedDirs(mainDirectory, start = 0, end = 9) {
132
- fs.mkdirSync(mainDirectory, {
133
- recursive: true
134
- });
135
- for (let i = start; i <= end; i++) {
136
- fs.mkdirSync(path.join(mainDirectory, `${i}`), {
137
- recursive: true
138
- });
139
- }
140
- }
141
- export async function executeCommand(command) {
142
- const {
143
- stdout
144
- } = await execAsync(command);
145
- return stdout.trim();
146
- }
147
- export function hostIp() {
148
- for (const list of Object.values(networkInterfaces())) {
149
- for (const alias of list) {
150
- if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.address.startsWith('192.168.') && !alias.internal) {
151
- return alias.address;
152
- }
153
- }
154
- }
155
- return '127.0.0.1';
156
- }
157
- export function gitVersion() {
158
- try {
159
- const raw = execFileSync('git', ['show', '-s', '--format=%ct', 'HEAD'], {
160
- encoding: 'utf8'
161
- }).trim();
162
- const timestamp = parseInt(raw, 10);
163
- if (isNaN(timestamp)) return "1.0";
164
- const iso = new Date(timestamp * 1000).toISOString();
165
- return `${iso.slice(2, 4)}${iso.slice(5, 7)}${iso.slice(8, 10)}.${iso.slice(11, 13)}${iso.slice(14, 16)}`;
166
- } catch {
167
- return "1.0";
168
- }
169
- }