@20syldev/api 4.6.0 → 4.8.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 +5 -3
- package/dist/app.js +6 -6
- package/dist/app.js.map +1 -1
- package/dist/config/env.js.map +1 -1
- package/dist/config/versions.js +22 -3
- package/dist/config/versions.js.map +1 -1
- package/dist/constants.js +27 -12
- package/dist/constants.js.map +1 -1
- package/dist/middleware/error.js +2 -2
- package/dist/middleware/error.js.map +1 -1
- package/dist/middleware/logger.js.map +1 -1
- package/dist/middleware/ratelimit.js +2 -2
- package/dist/middleware/ratelimit.js.map +1 -1
- package/dist/middleware/version.js.map +1 -1
- package/dist/modules/v3.js +1 -1
- package/dist/modules/v3.js.map +1 -1
- package/dist/modules/v4/address.js +381 -0
- package/dist/modules/v4/address.js.map +1 -0
- package/dist/modules/v4/agent.js +92 -0
- package/dist/modules/v4/agent.js.map +1 -0
- package/dist/modules/v4/algorithms.js +79 -0
- package/dist/modules/v4/algorithms.js.map +1 -1
- package/dist/modules/v4/avatar.js +136 -0
- package/dist/modules/v4/avatar.js.map +1 -0
- package/dist/modules/v4/barcode.js +440 -0
- package/dist/modules/v4/barcode.js.map +1 -0
- package/dist/modules/v4/captcha.js +7 -0
- package/dist/modules/v4/captcha.js.map +1 -1
- package/dist/modules/v4/chat.js +9 -1
- package/dist/modules/v4/chat.js.map +1 -1
- package/dist/modules/v4/color.js +8 -1
- package/dist/modules/v4/color.js.map +1 -1
- package/dist/modules/v4/convert.js +9 -0
- package/dist/modules/v4/convert.js.map +1 -1
- package/dist/modules/v4/credit.js +104 -0
- package/dist/modules/v4/credit.js.map +1 -0
- package/dist/modules/v4/cron.js +202 -0
- package/dist/modules/v4/cron.js.map +1 -0
- package/dist/modules/v4/dice.js +7 -0
- package/dist/modules/v4/dice.js.map +1 -1
- package/dist/modules/v4/domain.js +6 -1
- package/dist/modules/v4/domain.js.map +1 -1
- package/dist/modules/v4/encode.js +71 -0
- package/dist/modules/v4/encode.js.map +1 -1
- package/dist/modules/v4/geo.js +11 -1
- package/dist/modules/v4/geo.js.map +1 -1
- package/dist/modules/v4/hash.js +10 -1
- package/dist/modules/v4/hash.js.map +1 -1
- package/dist/modules/v4/hyperplanning.js +9 -1
- package/dist/modules/v4/hyperplanning.js.map +1 -1
- package/dist/modules/v4/ip.js +132 -0
- package/dist/modules/v4/ip.js.map +1 -0
- package/dist/modules/v4/levenshtein.js +8 -0
- package/dist/modules/v4/levenshtein.js.map +1 -1
- package/dist/modules/v4/palette.js +9 -1
- package/dist/modules/v4/palette.js.map +1 -1
- package/dist/modules/v4/password.js +343 -0
- package/dist/modules/v4/password.js.map +1 -0
- package/dist/modules/v4/personal.js +14 -18
- package/dist/modules/v4/personal.js.map +1 -1
- package/dist/modules/v4/placeholder.js +8 -0
- package/dist/modules/v4/placeholder.js.map +1 -1
- package/dist/modules/v4/qrcode.js +10 -3
- package/dist/modules/v4/qrcode.js.map +1 -1
- package/dist/modules/v4/regex.js +48 -0
- package/dist/modules/v4/regex.js.map +1 -0
- package/dist/modules/v4/statistics.js +7 -0
- package/dist/modules/v4/statistics.js.map +1 -1
- package/dist/modules/v4/text.js +30 -0
- package/dist/modules/v4/text.js.map +1 -1
- package/dist/modules/v4/tic_tac_toe.js +9 -1
- package/dist/modules/v4/tic_tac_toe.js.map +1 -1
- package/dist/modules/v4/time.js +62 -11
- package/dist/modules/v4/time.js.map +1 -1
- package/dist/modules/v4/token.js +17 -8
- package/dist/modules/v4/token.js.map +1 -1
- package/dist/modules/v4/username.js +5 -0
- package/dist/modules/v4/username.js.map +1 -1
- package/dist/modules/v4/validate.js +21 -0
- package/dist/modules/v4/validate.js.map +1 -1
- package/dist/modules/v4.js +10 -1
- package/dist/modules/v4.js.map +1 -1
- package/dist/routes/delete.js.map +1 -1
- package/dist/routes/get.js +208 -48
- package/dist/routes/get.js.map +1 -1
- package/dist/routes/index.js +2 -2
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/patch.js.map +1 -1
- package/dist/routes/post.js +2 -2
- package/dist/routes/post.js.map +1 -1
- package/dist/utils/response.js.map +1 -1
- package/package.json +1 -1
- package/src/config/versions.ts +8 -1
- package/src/constants.ts +27 -12
- package/src/modules/v4/address.ts +412 -0
- package/src/modules/v4/cron.ts +221 -0
- package/src/modules/v4/geo.ts +2 -1
- package/src/modules/v4/password.ts +379 -0
- package/src/modules/v4/personal.ts +4 -16
- package/src/modules/v4/qrcode.ts +2 -2
- package/src/modules/v4/regex.ts +67 -0
- package/src/modules/v4/time.ts +63 -17
- package/src/modules/v4/token.ts +8 -7
- package/src/modules/v4.ts +4 -0
- package/src/routes/get.ts +125 -46
- package/tests/integration/api.test.ts +111 -0
- package/tests/unit/address.test.ts +84 -0
- package/tests/unit/cron.test.ts +110 -0
- package/tests/unit/password.test.ts +86 -0
- package/tests/unit/regex.test.ts +95 -0
- package/tests/unit/time.test.ts +47 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { randomInt } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import { MAX_PASSWORD_COUNT, MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH } from '../../constants.js';
|
|
4
|
+
|
|
5
|
+
const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
6
|
+
const LOWER = 'abcdefghijklmnopqrstuvwxyz';
|
|
7
|
+
const DIGITS = '0123456789';
|
|
8
|
+
const SYMBOLS = '!@#$%^&*()-_=+[]{}|;:,.<>?';
|
|
9
|
+
|
|
10
|
+
const WORDLIST = [
|
|
11
|
+
'apple',
|
|
12
|
+
'brave',
|
|
13
|
+
'chair',
|
|
14
|
+
'dance',
|
|
15
|
+
'eagle',
|
|
16
|
+
'fancy',
|
|
17
|
+
'grace',
|
|
18
|
+
'house',
|
|
19
|
+
'ivory',
|
|
20
|
+
'joker',
|
|
21
|
+
'kneel',
|
|
22
|
+
'lemon',
|
|
23
|
+
'maple',
|
|
24
|
+
'noble',
|
|
25
|
+
'ocean',
|
|
26
|
+
'piano',
|
|
27
|
+
'queen',
|
|
28
|
+
'river',
|
|
29
|
+
'stone',
|
|
30
|
+
'tiger',
|
|
31
|
+
'ultra',
|
|
32
|
+
'vivid',
|
|
33
|
+
'waste',
|
|
34
|
+
'xenon',
|
|
35
|
+
'yacht',
|
|
36
|
+
'zebra',
|
|
37
|
+
'amber',
|
|
38
|
+
'blaze',
|
|
39
|
+
'coral',
|
|
40
|
+
'drift',
|
|
41
|
+
'ember',
|
|
42
|
+
'flora',
|
|
43
|
+
'globe',
|
|
44
|
+
'haven',
|
|
45
|
+
'input',
|
|
46
|
+
'jumpy',
|
|
47
|
+
'karma',
|
|
48
|
+
'lunar',
|
|
49
|
+
'magic',
|
|
50
|
+
'night',
|
|
51
|
+
'optic',
|
|
52
|
+
'proxy',
|
|
53
|
+
'quirk',
|
|
54
|
+
'radar',
|
|
55
|
+
'solar',
|
|
56
|
+
'trace',
|
|
57
|
+
'umbra',
|
|
58
|
+
'vapor',
|
|
59
|
+
'witch',
|
|
60
|
+
'xylem',
|
|
61
|
+
'yield',
|
|
62
|
+
'zippy',
|
|
63
|
+
'alpha',
|
|
64
|
+
'brisk',
|
|
65
|
+
'crane',
|
|
66
|
+
'depth',
|
|
67
|
+
'event',
|
|
68
|
+
'frost',
|
|
69
|
+
'grime',
|
|
70
|
+
'harsh',
|
|
71
|
+
'index',
|
|
72
|
+
'joint',
|
|
73
|
+
'knack',
|
|
74
|
+
'level',
|
|
75
|
+
'minus',
|
|
76
|
+
'nerve',
|
|
77
|
+
'onset',
|
|
78
|
+
'phase',
|
|
79
|
+
'quest',
|
|
80
|
+
'ratio',
|
|
81
|
+
'scope',
|
|
82
|
+
'trend',
|
|
83
|
+
'union',
|
|
84
|
+
'valve',
|
|
85
|
+
'weary',
|
|
86
|
+
'young',
|
|
87
|
+
'blunt',
|
|
88
|
+
'clean',
|
|
89
|
+
'drive',
|
|
90
|
+
'earth',
|
|
91
|
+
'fiber',
|
|
92
|
+
'grain',
|
|
93
|
+
'grind',
|
|
94
|
+
'horse',
|
|
95
|
+
'ideal',
|
|
96
|
+
'issue',
|
|
97
|
+
'judge',
|
|
98
|
+
'juice',
|
|
99
|
+
'knave',
|
|
100
|
+
'large',
|
|
101
|
+
'laser',
|
|
102
|
+
'light',
|
|
103
|
+
'limit',
|
|
104
|
+
'media',
|
|
105
|
+
'merit',
|
|
106
|
+
'metal',
|
|
107
|
+
'model',
|
|
108
|
+
'money',
|
|
109
|
+
'month',
|
|
110
|
+
'motor',
|
|
111
|
+
'mount',
|
|
112
|
+
'music',
|
|
113
|
+
'nerve',
|
|
114
|
+
'noise',
|
|
115
|
+
'north',
|
|
116
|
+
'noted',
|
|
117
|
+
'novel',
|
|
118
|
+
'nurse',
|
|
119
|
+
'oasis',
|
|
120
|
+
'offer',
|
|
121
|
+
'olive',
|
|
122
|
+
'omega',
|
|
123
|
+
'other',
|
|
124
|
+
'outer',
|
|
125
|
+
'owner',
|
|
126
|
+
'oxide',
|
|
127
|
+
'ozone',
|
|
128
|
+
'paint',
|
|
129
|
+
'panel',
|
|
130
|
+
'paper',
|
|
131
|
+
'patch',
|
|
132
|
+
'pause',
|
|
133
|
+
'peace',
|
|
134
|
+
'pearl',
|
|
135
|
+
'pedal',
|
|
136
|
+
'plain',
|
|
137
|
+
'plank',
|
|
138
|
+
'plant',
|
|
139
|
+
'plate',
|
|
140
|
+
'plaza',
|
|
141
|
+
'pluck',
|
|
142
|
+
'plumb',
|
|
143
|
+
'plume',
|
|
144
|
+
'plunge',
|
|
145
|
+
'point',
|
|
146
|
+
'polar',
|
|
147
|
+
'power',
|
|
148
|
+
'press',
|
|
149
|
+
'pride',
|
|
150
|
+
'prime',
|
|
151
|
+
'print',
|
|
152
|
+
'prism',
|
|
153
|
+
'prize',
|
|
154
|
+
'probe',
|
|
155
|
+
'prone',
|
|
156
|
+
'proof',
|
|
157
|
+
'prose',
|
|
158
|
+
'proud',
|
|
159
|
+
'prove',
|
|
160
|
+
'prowl',
|
|
161
|
+
'pulse',
|
|
162
|
+
'punch',
|
|
163
|
+
'pupil',
|
|
164
|
+
'purge',
|
|
165
|
+
'swift',
|
|
166
|
+
'sword',
|
|
167
|
+
'table',
|
|
168
|
+
'taint',
|
|
169
|
+
'tally',
|
|
170
|
+
'taste',
|
|
171
|
+
'teach',
|
|
172
|
+
'teeth',
|
|
173
|
+
'tempo',
|
|
174
|
+
'tense',
|
|
175
|
+
'tenth',
|
|
176
|
+
'terms',
|
|
177
|
+
'terra',
|
|
178
|
+
'thick',
|
|
179
|
+
'thing',
|
|
180
|
+
'think',
|
|
181
|
+
'thorn',
|
|
182
|
+
'three',
|
|
183
|
+
'throw',
|
|
184
|
+
'thumb',
|
|
185
|
+
'tidal',
|
|
186
|
+
'tight',
|
|
187
|
+
'tilde',
|
|
188
|
+
'timer',
|
|
189
|
+
'title',
|
|
190
|
+
'token',
|
|
191
|
+
'torch',
|
|
192
|
+
'total',
|
|
193
|
+
'touch',
|
|
194
|
+
'tough',
|
|
195
|
+
'tower',
|
|
196
|
+
'track',
|
|
197
|
+
'trade',
|
|
198
|
+
'trail',
|
|
199
|
+
'train',
|
|
200
|
+
'trait',
|
|
201
|
+
'tramp',
|
|
202
|
+
'tread',
|
|
203
|
+
'treat',
|
|
204
|
+
'trees',
|
|
205
|
+
'trial',
|
|
206
|
+
'tribe',
|
|
207
|
+
'trick',
|
|
208
|
+
'tried',
|
|
209
|
+
'troop',
|
|
210
|
+
'trove',
|
|
211
|
+
'truce',
|
|
212
|
+
'truck',
|
|
213
|
+
'truly',
|
|
214
|
+
'trunk',
|
|
215
|
+
'truth',
|
|
216
|
+
'tulip',
|
|
217
|
+
'tumor',
|
|
218
|
+
'tuner',
|
|
219
|
+
'tunic',
|
|
220
|
+
'tweak',
|
|
221
|
+
'twice',
|
|
222
|
+
'twirl',
|
|
223
|
+
'twist',
|
|
224
|
+
'typed',
|
|
225
|
+
'under',
|
|
226
|
+
'unify',
|
|
227
|
+
'unite',
|
|
228
|
+
'unity',
|
|
229
|
+
'until',
|
|
230
|
+
'upper',
|
|
231
|
+
'upset',
|
|
232
|
+
'urban',
|
|
233
|
+
'usage',
|
|
234
|
+
'valid',
|
|
235
|
+
'value',
|
|
236
|
+
'visor',
|
|
237
|
+
'vital',
|
|
238
|
+
'vivid',
|
|
239
|
+
'vocab',
|
|
240
|
+
'vocal',
|
|
241
|
+
'voice',
|
|
242
|
+
'voter',
|
|
243
|
+
'vault',
|
|
244
|
+
'venue',
|
|
245
|
+
'verse',
|
|
246
|
+
'watch',
|
|
247
|
+
'water',
|
|
248
|
+
'weave',
|
|
249
|
+
'wedge',
|
|
250
|
+
'weird',
|
|
251
|
+
'wheel',
|
|
252
|
+
'where',
|
|
253
|
+
'which',
|
|
254
|
+
'while',
|
|
255
|
+
'white',
|
|
256
|
+
'whole',
|
|
257
|
+
'wider',
|
|
258
|
+
'windy',
|
|
259
|
+
'wired',
|
|
260
|
+
'world',
|
|
261
|
+
'worse',
|
|
262
|
+
'worth',
|
|
263
|
+
'would',
|
|
264
|
+
'wound',
|
|
265
|
+
'write',
|
|
266
|
+
'wrong',
|
|
267
|
+
'wrote',
|
|
268
|
+
'years',
|
|
269
|
+
'yield',
|
|
270
|
+
'yours',
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
export interface PasswordResult {
|
|
274
|
+
passwords: string[];
|
|
275
|
+
type: string;
|
|
276
|
+
length: number;
|
|
277
|
+
strength: string;
|
|
278
|
+
entropy: number;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function computeStrength(entropy: number): string {
|
|
282
|
+
if (entropy < 40) return 'weak';
|
|
283
|
+
if (entropy < 60) return 'moderate';
|
|
284
|
+
if (entropy < 80) return 'strong';
|
|
285
|
+
return 'very_strong';
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function generateRandom(length: number, charset: string): string {
|
|
289
|
+
return Array.from({ length }, () => charset[randomInt(charset.length)]).join('');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function generatePassphrase(wordCount: number, separator: string): string {
|
|
293
|
+
return Array.from({ length: wordCount }, () => WORDLIST[randomInt(WORDLIST.length)]).join(separator);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Generates one or more passwords using random characters or passphrase mode.
|
|
298
|
+
*
|
|
299
|
+
* @param type - Generation mode: "random" or "passphrase"
|
|
300
|
+
* @param length - Password length for random mode (8–128), or word count for passphrase mode (3–10)
|
|
301
|
+
* @param options - Character set options: uppercase, lowercase, digits, symbols, exclude, count, separator
|
|
302
|
+
* @returns Generated passwords with type, length, strength and entropy
|
|
303
|
+
* @throws Error if no charset is active, length is out of range, or count exceeds the maximum
|
|
304
|
+
*/
|
|
305
|
+
export default function password(
|
|
306
|
+
type: string = 'random',
|
|
307
|
+
length: number = 16,
|
|
308
|
+
options: {
|
|
309
|
+
uppercase?: boolean;
|
|
310
|
+
lowercase?: boolean;
|
|
311
|
+
digits?: boolean;
|
|
312
|
+
symbols?: boolean;
|
|
313
|
+
exclude?: string;
|
|
314
|
+
count?: number;
|
|
315
|
+
separator?: string;
|
|
316
|
+
} = {},
|
|
317
|
+
): PasswordResult {
|
|
318
|
+
const {
|
|
319
|
+
uppercase = true,
|
|
320
|
+
lowercase = true,
|
|
321
|
+
digits = true,
|
|
322
|
+
symbols = false,
|
|
323
|
+
exclude = '',
|
|
324
|
+
count = 1,
|
|
325
|
+
separator = '-',
|
|
326
|
+
} = options;
|
|
327
|
+
|
|
328
|
+
if (count < 1 || count > MAX_PASSWORD_COUNT) {
|
|
329
|
+
throw new Error(`Count must be between 1 and ${MAX_PASSWORD_COUNT}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (type === 'passphrase') {
|
|
333
|
+
const wordCount = Math.max(3, Math.min(10, length));
|
|
334
|
+
const entropy = Math.log2(Math.pow(WORDLIST.length, wordCount));
|
|
335
|
+
const passwords = Array.from({ length: count }, () => generatePassphrase(wordCount, separator));
|
|
336
|
+
return {
|
|
337
|
+
passwords,
|
|
338
|
+
type: 'passphrase',
|
|
339
|
+
length: wordCount,
|
|
340
|
+
strength: computeStrength(entropy),
|
|
341
|
+
entropy: Math.round(entropy * 10) / 10,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (type !== 'random') {
|
|
346
|
+
throw new Error('Type must be "random" or "passphrase"');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (isNaN(length) || length < MIN_PASSWORD_LENGTH || length > MAX_PASSWORD_LENGTH) {
|
|
350
|
+
throw new Error(`Length must be between ${MIN_PASSWORD_LENGTH} and ${MAX_PASSWORD_LENGTH}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let charset = '';
|
|
354
|
+
if (uppercase) charset += UPPER;
|
|
355
|
+
if (lowercase) charset += LOWER;
|
|
356
|
+
if (digits) charset += DIGITS;
|
|
357
|
+
if (symbols) charset += SYMBOLS;
|
|
358
|
+
|
|
359
|
+
if (exclude) {
|
|
360
|
+
for (const char of exclude) {
|
|
361
|
+
charset = charset.replaceAll(char, '');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (charset.length === 0) {
|
|
366
|
+
throw new Error('At least one character set must be enabled');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const entropy = Math.log2(Math.pow(charset.length, length));
|
|
370
|
+
const passwords = Array.from({ length: count }, () => generateRandom(length, charset));
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
passwords,
|
|
374
|
+
type: 'random',
|
|
375
|
+
length,
|
|
376
|
+
strength: computeStrength(entropy),
|
|
377
|
+
entropy: Math.round(entropy * 10) / 10,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { random, randomNumber } from '../../utils/helpers.js';
|
|
2
|
+
import addressFn from './address.js';
|
|
2
3
|
|
|
3
4
|
interface Person {
|
|
4
5
|
name: string;
|
|
@@ -47,21 +48,6 @@ export default function personal(): Record<string, unknown> {
|
|
|
47
48
|
|
|
48
49
|
const jobs = ['Writer', 'Artist', 'Musician', 'Explorer', 'Scientist', 'Engineer', 'Athlete', 'Doctor'];
|
|
49
50
|
const hobbies = ['Reading', 'Traveling', 'Gaming', 'Cooking', 'Fitness', 'Music', 'Photography', 'Writing'];
|
|
50
|
-
const cities = [
|
|
51
|
-
'New York',
|
|
52
|
-
'Paris',
|
|
53
|
-
'London',
|
|
54
|
-
'Madrid',
|
|
55
|
-
'Berlin',
|
|
56
|
-
'Rome',
|
|
57
|
-
'Tokyo',
|
|
58
|
-
'Los Angeles',
|
|
59
|
-
'Sydney',
|
|
60
|
-
'São Paulo',
|
|
61
|
-
'Toronto',
|
|
62
|
-
];
|
|
63
|
-
const streets = ['Main St', '2nd Ave', 'Broadway', 'Park Lane', 'Elm St', 'Sunset Blvd', 'Maple St', 'Highland Rd'];
|
|
64
|
-
|
|
65
51
|
const card = Array.from({ length: 4 }, () => randomNumber(1000, 9999)).join(' ');
|
|
66
52
|
const cvc = randomNumber(100, 999);
|
|
67
53
|
const expiration = `${String(randomNumber(1, 12)).padStart(2, '0')}/${(new Date().getFullYear() + randomNumber(0, 3)).toString().slice(-2)}`;
|
|
@@ -69,6 +55,8 @@ export default function personal(): Record<string, unknown> {
|
|
|
69
55
|
const person = random(people);
|
|
70
56
|
const social = person.social;
|
|
71
57
|
const country = person.country;
|
|
58
|
+
const countryCodes: Record<string, string> = { US: 'us', FR: 'fr', UK: 'uk', ES: 'es', DE: 'de' };
|
|
59
|
+
const addrCountry = countryCodes[country] ?? 'us';
|
|
72
60
|
const countryInfo = countries[country]!;
|
|
73
61
|
const phone = countryInfo.tel;
|
|
74
62
|
const lang = countryInfo.lang;
|
|
@@ -129,7 +117,7 @@ export default function personal(): Record<string, unknown> {
|
|
|
129
117
|
card,
|
|
130
118
|
cvc,
|
|
131
119
|
expiration,
|
|
132
|
-
address:
|
|
120
|
+
address: addressFn(addrCountry).addresses[0]!,
|
|
133
121
|
birthday,
|
|
134
122
|
civil_status: civilStatus,
|
|
135
123
|
children,
|
package/src/modules/v4/qrcode.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createCanvas, loadImage } from 'canvas';
|
|
2
2
|
import { toBuffer, toCanvas as qrToCanvas, toDataURL } from 'qrcode';
|
|
3
3
|
|
|
4
|
+
import { MAX_QRCODE_LOGO_BYTES } from '../../constants.js';
|
|
4
5
|
import { normalizeColor } from '../../utils/colors.js';
|
|
5
6
|
|
|
6
7
|
export interface QRCodeOptions {
|
|
@@ -24,7 +25,6 @@ export interface QRCodeResult {
|
|
|
24
25
|
|
|
25
26
|
const CORRECTIONS = new Set(['L', 'M', 'Q', 'H']);
|
|
26
27
|
const FORMATS = new Set(['png', 'base64']);
|
|
27
|
-
const MAX_LOGO_BYTES = 2 * 1024 * 1024;
|
|
28
28
|
|
|
29
29
|
function clamp(value: number | undefined, name: string, def: number, min: number, max: number): number {
|
|
30
30
|
if (value === undefined) return def;
|
|
@@ -44,7 +44,7 @@ async function fetchIcon(url: string): Promise<Buffer> {
|
|
|
44
44
|
if (!contentType.startsWith('image/')) throw new Error('Icon URL must point to an image');
|
|
45
45
|
|
|
46
46
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
47
|
-
if (buffer.length >
|
|
47
|
+
if (buffer.length > MAX_QRCODE_LOGO_BYTES) throw new Error('Icon must be under 2MB');
|
|
48
48
|
|
|
49
49
|
return buffer;
|
|
50
50
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { MAX_PATTERN_LENGTH, MAX_REGEX_MATCHES, MAX_STRING_LENGTH } from '../../constants.js';
|
|
2
|
+
|
|
3
|
+
const VALID_FLAGS = new Set(['g', 'i', 'm', 's', 'u']);
|
|
4
|
+
|
|
5
|
+
export interface RegexMatch {
|
|
6
|
+
match: string;
|
|
7
|
+
index: number;
|
|
8
|
+
groups: string[];
|
|
9
|
+
namedGroups: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface RegexResult {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
pattern: string;
|
|
15
|
+
flags: string;
|
|
16
|
+
matches: RegexMatch[];
|
|
17
|
+
count: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Tests a regular expression pattern against a text and returns structured match results.
|
|
22
|
+
*
|
|
23
|
+
* @param pattern - The regex pattern to test (max 200 characters)
|
|
24
|
+
* @param text - The text to match against (max 1000 characters)
|
|
25
|
+
* @param flags - Optional regex flags; only g, i, m, s, u are accepted (g is always forced)
|
|
26
|
+
* @returns Match results with groups and named groups, or { valid: false } if the pattern is invalid
|
|
27
|
+
* @throws Error if pattern or text is missing or exceeds the maximum length
|
|
28
|
+
*/
|
|
29
|
+
export default function regex(pattern: string, text: string, flags: string = 'g'): RegexResult {
|
|
30
|
+
if (!pattern) {
|
|
31
|
+
throw new Error('Please provide a pattern (?pattern={regex})');
|
|
32
|
+
}
|
|
33
|
+
if (!text) {
|
|
34
|
+
throw new Error('Please provide a text (&text={string})');
|
|
35
|
+
}
|
|
36
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
37
|
+
throw new Error(`Pattern must be under ${MAX_PATTERN_LENGTH} characters`);
|
|
38
|
+
}
|
|
39
|
+
if (text.length > MAX_STRING_LENGTH) {
|
|
40
|
+
throw new Error(`Text must be under ${MAX_STRING_LENGTH} characters`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cleanedFlags = [...new Set(['g', ...flags.split('').filter((f) => VALID_FLAGS.has(f))])].join('');
|
|
44
|
+
|
|
45
|
+
let re: RegExp;
|
|
46
|
+
try {
|
|
47
|
+
re = new RegExp(pattern, cleanedFlags);
|
|
48
|
+
} catch {
|
|
49
|
+
return { valid: false, pattern, flags: cleanedFlags, matches: [], count: 0 };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const matches: RegexMatch[] = [];
|
|
53
|
+
let result: RegExpExecArray | null;
|
|
54
|
+
|
|
55
|
+
while ((result = re.exec(text)) !== null && matches.length < MAX_REGEX_MATCHES) {
|
|
56
|
+
matches.push({
|
|
57
|
+
match: result[0],
|
|
58
|
+
index: result.index,
|
|
59
|
+
groups: result.slice(1).map((g) => g ?? ''),
|
|
60
|
+
namedGroups: result.groups ? { ...result.groups } : {},
|
|
61
|
+
});
|
|
62
|
+
// Prevent infinite loop on zero-length matches
|
|
63
|
+
if (result[0].length === 0) re.lastIndex++;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { valid: true, pattern, flags: cleanedFlags, matches, count: matches.length };
|
|
67
|
+
}
|
package/src/modules/v4/time.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { MAX_COUNTDOWN_YEARS } from '../../constants.js';
|
|
2
|
+
|
|
3
|
+
const VALID_FORMATS = [
|
|
2
4
|
'iso',
|
|
3
5
|
'utc',
|
|
4
6
|
'timestamp',
|
|
@@ -19,21 +21,23 @@ const validFormats = [
|
|
|
19
21
|
'timezoneOffset',
|
|
20
22
|
] as const;
|
|
21
23
|
|
|
22
|
-
const
|
|
24
|
+
export const VALID_TIMEZONES = ['UTC', 'America/New_York', 'Europe/Paris', 'Asia/Tokyo', 'Australia/Sydney'] as const;
|
|
23
25
|
|
|
24
|
-
type TimeFormat = (typeof
|
|
25
|
-
type Timezone = (typeof
|
|
26
|
+
type TimeFormat = (typeof VALID_FORMATS)[number];
|
|
27
|
+
type Timezone = (typeof VALID_TIMEZONES)[number];
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
|
-
* Returns the current or a random date/time in various formats and timezones
|
|
30
|
+
* Returns the current or a random date/time in various formats and timezones,
|
|
31
|
+
* or computes a countdown/elapsed time to/from a target date.
|
|
29
32
|
*
|
|
30
|
-
* @param type - "live" for the current time
|
|
33
|
+
* @param type - "live" for the current time, "random" for a random date, or "countdown" for a time diff
|
|
31
34
|
* @param start - Optional start date for random mode (YYYY-MM-DD)
|
|
32
35
|
* @param end - Optional end date for random mode (YYYY-MM-DD)
|
|
33
36
|
* @param format - Optional specific format to return (e.g. "iso", "timestamp", "year")
|
|
34
37
|
* @param timezone - Optional timezone (e.g. "UTC", "Europe/Paris")
|
|
35
|
-
* @
|
|
36
|
-
* @
|
|
38
|
+
* @param target - Required for countdown mode: target date in ISO 8601 or YYYY-MM-DD format
|
|
39
|
+
* @returns Object containing all time formats, a single format, or a countdown result
|
|
40
|
+
* @throws Error if type, format, timezone, or target is invalid
|
|
37
41
|
*/
|
|
38
42
|
export default function time(
|
|
39
43
|
type: string = 'live',
|
|
@@ -41,9 +45,55 @@ export default function time(
|
|
|
41
45
|
end?: string,
|
|
42
46
|
format?: string,
|
|
43
47
|
timezone?: string,
|
|
48
|
+
target?: string,
|
|
44
49
|
): Record<string, unknown> {
|
|
45
|
-
if (type !== 'live' && type !== 'random') {
|
|
46
|
-
throw new Error('Please provide a valid type (live or
|
|
50
|
+
if (type !== 'live' && type !== 'random' && type !== 'countdown') {
|
|
51
|
+
throw new Error('Please provide a valid type (live, random or countdown)');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (timezone && !VALID_TIMEZONES.includes(timezone as Timezone)) {
|
|
55
|
+
throw new Error(`Please provide a valid timezone. Options: ${VALID_TIMEZONES.join(', ')}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (type === 'countdown') {
|
|
59
|
+
if (!target) {
|
|
60
|
+
throw new Error('Please provide a target date (?target=YYYY-MM-DD)');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const targetDate = new Date(target);
|
|
64
|
+
if (isNaN(targetDate.getTime())) {
|
|
65
|
+
throw new Error('Please provide a valid target date (ISO 8601 or YYYY-MM-DD)');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const now = new Date();
|
|
69
|
+
const maxMs = MAX_COUNTDOWN_YEARS * 365.25 * 24 * 3600 * 1000;
|
|
70
|
+
|
|
71
|
+
if (Math.abs(targetDate.getTime() - now.getTime()) > maxMs) {
|
|
72
|
+
throw new Error(`Target date must be within ${MAX_COUNTDOWN_YEARS} years from now`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const direction: 'future' | 'past' = targetDate > now ? 'future' : 'past';
|
|
76
|
+
const diffMs = Math.abs(targetDate.getTime() - now.getTime());
|
|
77
|
+
const total_seconds = Math.floor(diffMs / 1000);
|
|
78
|
+
|
|
79
|
+
const days = Math.floor(total_seconds / 86400);
|
|
80
|
+
const hours = Math.floor((total_seconds % 86400) / 3600);
|
|
81
|
+
const minutes = Math.floor((total_seconds % 3600) / 60);
|
|
82
|
+
const seconds = total_seconds % 60;
|
|
83
|
+
|
|
84
|
+
const parts: string[] = [];
|
|
85
|
+
if (days > 0) parts.push(`${days} day${days !== 1 ? 's' : ''}`);
|
|
86
|
+
if (hours > 0) parts.push(`${hours} hour${hours !== 1 ? 's' : ''}`);
|
|
87
|
+
if (minutes > 0) parts.push(`${minutes} minute${minutes !== 1 ? 's' : ''}`);
|
|
88
|
+
if (seconds > 0) parts.push(`${seconds} second${seconds !== 1 ? 's' : ''}`);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
target: targetDate.toISOString(),
|
|
92
|
+
now: now.toISOString(),
|
|
93
|
+
direction,
|
|
94
|
+
remaining: { total_seconds, days, hours, minutes, seconds },
|
|
95
|
+
human: parts.length > 0 ? parts.join(', ') : '0 seconds',
|
|
96
|
+
};
|
|
47
97
|
}
|
|
48
98
|
|
|
49
99
|
if (start && !Date.parse(start)) {
|
|
@@ -54,12 +104,8 @@ export default function time(
|
|
|
54
104
|
throw new Error('Please provide a valid end date (YYYY-MM-DD)');
|
|
55
105
|
}
|
|
56
106
|
|
|
57
|
-
if (format && !
|
|
58
|
-
throw new Error(`Please provide a valid format. Options: ${
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (timezone && !validTimezones.includes(timezone as Timezone)) {
|
|
62
|
-
throw new Error(`Please provide a valid timezone. Options: ${validTimezones.join(', ')}`);
|
|
107
|
+
if (format && !VALID_FORMATS.includes(format as TimeFormat)) {
|
|
108
|
+
throw new Error(`Please provide a valid format. Options: ${VALID_FORMATS.join(', ')}`);
|
|
63
109
|
}
|
|
64
110
|
|
|
65
111
|
const getTimeFormats = (date: Date, tz: string): Record<string, unknown> => {
|
|
@@ -89,7 +135,7 @@ export default function time(
|
|
|
89
135
|
const startDate = start ? new Date(start).getTime() : new Date('1900-01-01').getTime();
|
|
90
136
|
const endDate = end ? new Date(end).getTime() : new Date('2100-12-31').getTime();
|
|
91
137
|
const randomDate = new Date(startDate + Math.random() * (endDate - startDate));
|
|
92
|
-
const tz = timezone ||
|
|
138
|
+
const tz = timezone || VALID_TIMEZONES[Math.floor(Math.random() * VALID_TIMEZONES.length)]!;
|
|
93
139
|
const formats = getTimeFormats(randomDate, tz);
|
|
94
140
|
|
|
95
141
|
return format ? { date: formats[format] } : formats;
|
package/src/modules/v4/token.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { randomBytes } from 'crypto';
|
|
1
|
+
import { randomBytes, randomInt } from 'crypto';
|
|
2
2
|
import { v4 } from 'uuid';
|
|
3
3
|
|
|
4
4
|
import { MAX_TOKEN_LENGTH, MIN_TOKEN_LENGTH } from '../../constants.js';
|
|
@@ -20,11 +20,8 @@ export default function token(len: number, type: string = 'alphanum'): string {
|
|
|
20
20
|
throw new Error('Length cannot exceed 4096');
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const genToken = (chars: string, length: number): string =>
|
|
24
|
-
|
|
25
|
-
return chars[Math.floor(Math.random() * chars.length)];
|
|
26
|
-
}).join('');
|
|
27
|
-
};
|
|
23
|
+
const genToken = (chars: string, length: number): string =>
|
|
24
|
+
Array.from({ length }, () => chars[randomInt(chars.length)]).join('');
|
|
28
25
|
|
|
29
26
|
const tokenTypes: Record<string, () => string> = {
|
|
30
27
|
alpha: () => genToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', len),
|
|
@@ -40,7 +37,11 @@ export default function token(len: number, type: string = 'alphanum'): string {
|
|
|
40
37
|
num: () => genToken('0123456789', len),
|
|
41
38
|
punct: () => genToken('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~', len),
|
|
42
39
|
urlsafe: () => genToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_', len),
|
|
43
|
-
uuid: () =>
|
|
40
|
+
uuid: () => {
|
|
41
|
+
let result = '';
|
|
42
|
+
while (result.length < len) result += v4().replace(/-/g, '');
|
|
43
|
+
return result.slice(0, len);
|
|
44
|
+
},
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
const lowerType = type.toLowerCase();
|
package/src/modules/v4.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
export { default as address } from './v4/address.js';
|
|
1
2
|
export { default as agent } from './v4/agent.js';
|
|
2
3
|
export * as algorithms from './v4/algorithms.js';
|
|
3
4
|
export { default as captcha } from './v4/captcha.js';
|
|
4
5
|
export { default as chat } from './v4/chat.js';
|
|
5
6
|
export { default as color } from './v4/color.js';
|
|
6
7
|
export { default as convert } from './v4/convert.js';
|
|
8
|
+
export { default as cron } from './v4/cron.js';
|
|
7
9
|
export { default as dice } from './v4/dice.js';
|
|
8
10
|
export { default as domain } from './v4/domain.js';
|
|
9
11
|
export * as encode from './v4/encode.js';
|
|
@@ -13,9 +15,11 @@ export { default as hyperplanning } from './v4/hyperplanning.js';
|
|
|
13
15
|
export { default as ip } from './v4/ip.js';
|
|
14
16
|
export { default as levenshtein } from './v4/levenshtein.js';
|
|
15
17
|
export { default as palette } from './v4/palette.js';
|
|
18
|
+
export { default as password } from './v4/password.js';
|
|
16
19
|
export { default as personal } from './v4/personal.js';
|
|
17
20
|
export { default as placeholder } from './v4/placeholder.js';
|
|
18
21
|
export { default as qrcode } from './v4/qrcode.js';
|
|
22
|
+
export { default as regex } from './v4/regex.js';
|
|
19
23
|
export { default as statistics } from './v4/statistics.js';
|
|
20
24
|
export * as text from './v4/text.js';
|
|
21
25
|
export { default as tic_tac_toe } from './v4/tic_tac_toe.js';
|