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/README.md +2 -6
- package/dist/general-CAhFOkv8.cjs +655 -0
- package/dist/general.cjs +44 -0
- package/dist/general.mjs +370 -0
- package/dist/node.cjs +170 -0
- package/dist/node.mjs +142 -0
- package/docs/{docs.md → README.md} +4 -5
- package/docs/{index.md → general.md} +3 -3
- package/docs/node.md +1 -1
- package/package.json +25 -24
- package/lib/cjs/index.cjs +0 -474
- package/lib/cjs/node.cjs +0 -199
- package/lib/esm/index.mjs +0 -426
- package/lib/esm/node.mjs +0 -169
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
|
-
}
|