@20syldev/api 4.5.0 → 4.7.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 +11 -4
- package/eslint.config.js +4 -0
- package/package.json +3 -2
- package/src/app.ts +7 -6
- package/src/config/env.ts +1 -0
- package/src/config/versions.ts +10 -2
- package/src/middleware/error.ts +2 -1
- package/src/middleware/json.ts +1 -1
- package/src/middleware/logger.ts +2 -1
- package/src/middleware/ratelimit.ts +4 -3
- package/src/middleware/version.ts +2 -1
- package/src/modules/v3.js +1 -1
- package/src/modules/v4/address.ts +413 -0
- package/src/modules/v4/agent.ts +104 -0
- package/src/modules/v4/algorithms.ts +79 -0
- package/src/modules/v4/captcha.ts +8 -0
- package/src/modules/v4/chat.ts +10 -2
- package/src/modules/v4/color.ts +8 -1
- package/src/modules/v4/convert.ts +9 -0
- package/src/modules/v4/dice.ts +7 -0
- package/src/modules/v4/domain.ts +6 -1
- package/src/modules/v4/encode.ts +71 -0
- package/src/modules/v4/geo.ts +10 -0
- package/src/modules/v4/hash.ts +10 -1
- package/src/modules/v4/hyperplanning.ts +10 -1
- package/src/modules/v4/ip.ts +134 -0
- package/src/modules/v4/levenshtein.ts +8 -0
- package/src/modules/v4/palette.ts +9 -1
- package/src/modules/v4/password.ts +381 -0
- package/src/modules/v4/personal.ts +9 -16
- package/src/modules/v4/placeholder.ts +8 -0
- package/src/modules/v4/qrcode.ts +9 -1
- package/src/modules/v4/statistics.ts +7 -0
- package/src/modules/v4/text.ts +30 -0
- package/src/modules/v4/tic_tac_toe.ts +11 -2
- package/src/modules/v4/time.ts +11 -0
- package/src/modules/v4/token.ts +18 -8
- package/src/modules/v4/username.ts +5 -0
- package/src/modules/v4/validate.ts +21 -0
- package/src/modules/v4.ts +5 -1
- package/src/routes/delete.ts +2 -1
- package/src/routes/get.ts +115 -7
- package/src/routes/index.ts +4 -3
- package/src/routes/patch.ts +2 -1
- package/src/routes/post.ts +5 -4
- package/src/storage/index.ts +1 -1
- package/src/utils/response.ts +1 -0
- package/tests/integration/api.test.ts +142 -2
- package/tests/unit/address.test.ts +84 -0
- package/tests/unit/agent.test.ts +113 -0
- package/tests/unit/algorithms.test.ts +2 -1
- package/tests/unit/captcha.test.ts +2 -1
- package/tests/unit/chat.test.ts +2 -1
- package/tests/unit/color.test.ts +2 -1
- package/tests/unit/convert.test.ts +2 -1
- package/tests/unit/dice.test.ts +2 -1
- package/tests/unit/domain.test.ts +2 -1
- package/tests/unit/encode.test.ts +8 -7
- package/tests/unit/geo.test.ts +2 -1
- package/tests/unit/hash.test.ts +2 -1
- package/tests/unit/hyperplanning.test.ts +2 -1
- package/tests/unit/ip.test.ts +140 -0
- package/tests/unit/levenshtein.test.ts +2 -1
- package/tests/unit/palette.test.ts +2 -1
- package/tests/unit/password.test.ts +86 -0
- package/tests/unit/personal.test.ts +2 -1
- package/tests/unit/placeholder.test.ts +2 -1
- package/tests/unit/qrcode.test.ts +2 -1
- package/tests/unit/statistics.test.ts +2 -1
- package/tests/unit/text.test.ts +3 -2
- package/tests/unit/tic_tac_toe.test.ts +2 -1
- package/tests/unit/time.test.ts +2 -1
- package/tests/unit/token.test.ts +2 -1
- package/tests/unit/username.test.ts +2 -1
- package/tests/unit/validate.test.ts +3 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { MAX_STRING_LENGTH } from '../../constants.js';
|
|
2
|
+
|
|
3
|
+
export interface IpResult {
|
|
4
|
+
ip: string;
|
|
5
|
+
version: 'IPv4' | 'IPv6';
|
|
6
|
+
type: 'public' | 'private' | 'loopback' | 'link-local' | 'multicast' | 'broadcast';
|
|
7
|
+
class?: 'A' | 'B' | 'C' | 'D' | 'E';
|
|
8
|
+
range?: string;
|
|
9
|
+
binary: string;
|
|
10
|
+
decimal?: number;
|
|
11
|
+
reverse: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parses and analyzes an IPv4 address.
|
|
16
|
+
*
|
|
17
|
+
* @param raw - The IPv4 address string (e.g. "192.168.1.1")
|
|
18
|
+
* @returns Detailed information about the address
|
|
19
|
+
* @throws Error if the address is malformed
|
|
20
|
+
*/
|
|
21
|
+
function parseIPv4(raw: string): IpResult {
|
|
22
|
+
const parts = raw.split('.');
|
|
23
|
+
if (parts.length !== 4) throw new Error('Invalid IPv4 address');
|
|
24
|
+
const octets = parts.map(Number);
|
|
25
|
+
if (octets.some((o) => isNaN(o) || !Number.isInteger(o) || o < 0 || o > 255))
|
|
26
|
+
throw new Error('Invalid IPv4 address');
|
|
27
|
+
|
|
28
|
+
const [a, b, c, d] = octets as [number, number, number, number];
|
|
29
|
+
|
|
30
|
+
let cls: 'A' | 'B' | 'C' | 'D' | 'E';
|
|
31
|
+
if (a < 128) cls = 'A';
|
|
32
|
+
else if (a < 192) cls = 'B';
|
|
33
|
+
else if (a < 224) cls = 'C';
|
|
34
|
+
else if (a < 240) cls = 'D';
|
|
35
|
+
else cls = 'E';
|
|
36
|
+
|
|
37
|
+
let type: IpResult['type'] = 'public';
|
|
38
|
+
let range: string | undefined;
|
|
39
|
+
|
|
40
|
+
if (a === 127) {
|
|
41
|
+
type = 'loopback';
|
|
42
|
+
range = '127.0.0.0/8';
|
|
43
|
+
} else if (a === 10) {
|
|
44
|
+
type = 'private';
|
|
45
|
+
range = '10.0.0.0/8';
|
|
46
|
+
} else if (a === 172 && b >= 16 && b <= 31) {
|
|
47
|
+
type = 'private';
|
|
48
|
+
range = '172.16.0.0/12';
|
|
49
|
+
} else if (a === 192 && b === 168) {
|
|
50
|
+
type = 'private';
|
|
51
|
+
range = '192.168.0.0/16';
|
|
52
|
+
} else if (a === 169 && b === 254) {
|
|
53
|
+
type = 'link-local';
|
|
54
|
+
range = '169.254.0.0/16';
|
|
55
|
+
} else if (a >= 224 && a <= 239) {
|
|
56
|
+
type = 'multicast';
|
|
57
|
+
range = '224.0.0.0/4';
|
|
58
|
+
} else if (raw === '255.255.255.255') {
|
|
59
|
+
type = 'broadcast';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const binary = octets.map((o) => o.toString(2).padStart(8, '0')).join('.');
|
|
63
|
+
const decimal = ((a << 24) | (b << 16) | (c << 8) | d) >>> 0;
|
|
64
|
+
const reverse = [...octets].reverse().join('.') + '.in-addr.arpa';
|
|
65
|
+
|
|
66
|
+
const result: IpResult = { ip: raw, version: 'IPv4', type, class: cls, binary, decimal, reverse };
|
|
67
|
+
if (range) result.range = range;
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parses and analyzes an IPv6 address, expanding :: shorthand.
|
|
73
|
+
*
|
|
74
|
+
* @param raw - The IPv6 address string (e.g. "::1" or "fe80::1")
|
|
75
|
+
* @returns Detailed information about the address
|
|
76
|
+
* @throws Error if the address is malformed
|
|
77
|
+
*/
|
|
78
|
+
function parseIPv6(raw: string): IpResult {
|
|
79
|
+
let expanded = raw;
|
|
80
|
+
if (raw.includes('::')) {
|
|
81
|
+
const halves = raw.split('::');
|
|
82
|
+
const left = halves[0] ? halves[0].split(':') : [];
|
|
83
|
+
const right = halves[1] ? halves[1].split(':') : [];
|
|
84
|
+
const fill = Array(8 - left.length - right.length).fill('0');
|
|
85
|
+
expanded = [...left, ...fill, ...right].join(':');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const groups = expanded.split(':');
|
|
89
|
+
if (groups.length !== 8) throw new Error('Invalid IPv6 address');
|
|
90
|
+
const values = groups.map((g) => parseInt(g || '0', 16));
|
|
91
|
+
if (values.some((v) => isNaN(v) || v < 0 || v > 0xffff)) throw new Error('Invalid IPv6 address');
|
|
92
|
+
|
|
93
|
+
let type: IpResult['type'] = 'public';
|
|
94
|
+
let range: string | undefined;
|
|
95
|
+
|
|
96
|
+
if (raw === '::1' || values.every((v, i) => (i < 7 ? v === 0 : v === 1))) {
|
|
97
|
+
type = 'loopback';
|
|
98
|
+
range = '::1/128';
|
|
99
|
+
} else if ((values[0]! & 0xfe00) === 0xfc00) {
|
|
100
|
+
type = 'private';
|
|
101
|
+
range = 'fc00::/7';
|
|
102
|
+
} else if ((values[0]! & 0xffc0) === 0xfe80) {
|
|
103
|
+
type = 'link-local';
|
|
104
|
+
range = 'fe80::/10';
|
|
105
|
+
} else if ((values[0]! & 0xff00) === 0xff00) {
|
|
106
|
+
type = 'multicast';
|
|
107
|
+
range = 'ff00::/8';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const binary = values.map((v) => v.toString(2).padStart(16, '0')).join(':');
|
|
111
|
+
const hex = values.map((v) => v.toString(16).padStart(4, '0')).join('');
|
|
112
|
+
const reverse = hex.split('').reverse().join('.') + '.ip6.arpa';
|
|
113
|
+
|
|
114
|
+
const result: IpResult = { ip: expanded, version: 'IPv6', type, binary, reverse };
|
|
115
|
+
if (range) result.range = range;
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Analyzes an IPv4 or IPv6 address and returns its type, class, binary
|
|
121
|
+
* representation, decimal value, and reverse DNS notation.
|
|
122
|
+
*
|
|
123
|
+
* @param address - The IP address to analyze
|
|
124
|
+
* @returns Detailed breakdown of the address
|
|
125
|
+
* @throws Error if the address is missing or invalid
|
|
126
|
+
*/
|
|
127
|
+
export default function ip(address: string): IpResult {
|
|
128
|
+
if (!address || typeof address !== 'string') throw new Error('An IP address is required');
|
|
129
|
+
if (address.length > MAX_STRING_LENGTH) throw new Error('Address is too long');
|
|
130
|
+
|
|
131
|
+
if (address.includes(':')) return parseIPv6(address);
|
|
132
|
+
if (address.includes('.')) return parseIPv4(address);
|
|
133
|
+
throw new Error('Invalid IP address format');
|
|
134
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { MAX_LEVENSHTEIN_LENGTH } from '../../constants.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Computes the Levenshtein edit distance between two strings.
|
|
5
|
+
*
|
|
6
|
+
* @param str1 - First string
|
|
7
|
+
* @param str2 - Second string
|
|
8
|
+
* @returns Object containing both strings and the minimum number of single-character edits to transform one into the other
|
|
9
|
+
* @throws Error if either string is missing, not a string, or exceeds the maximum length
|
|
10
|
+
*/
|
|
3
11
|
export default function levenshtein(str1: string, str2: string): { str1: string; str2: string; distance: number } {
|
|
4
12
|
if (!str1 || typeof str1 !== 'string') {
|
|
5
13
|
throw new Error('Please provide a valid first string');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hexToRgb,
|
|
1
|
+
import { hexToRgb, hslToRgb, rgbToHex, rgbToHsl } from '../../utils/colors.js';
|
|
2
2
|
|
|
3
3
|
export interface PaletteColor {
|
|
4
4
|
hex: string;
|
|
@@ -30,6 +30,14 @@ function fromHueShifts(base: [number, number, number], shifts: number[]): Palett
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Generates a color palette from a base hex color using a specified harmony type.
|
|
35
|
+
*
|
|
36
|
+
* @param color - Base hex color (e.g. "#ff5733")
|
|
37
|
+
* @param type - Palette type: "complementary", "triadic", "analogous", "tetradic", or "split-complementary"
|
|
38
|
+
* @returns Object containing the base color and the derived palette colors in hex, RGB, and HSL
|
|
39
|
+
* @throws Error if color or type is missing, or the type is not one of the accepted values
|
|
40
|
+
*/
|
|
33
41
|
export default function palette(color: string, type: string): PaletteResult {
|
|
34
42
|
if (!color) throw new Error('A base color is required');
|
|
35
43
|
if (!type) throw new Error('A palette type is required');
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { randomInt } from 'crypto';
|
|
2
|
+
|
|
3
|
+
const MIN_LENGTH = 8;
|
|
4
|
+
const MAX_LENGTH = 128;
|
|
5
|
+
const MAX_COUNT = 20;
|
|
6
|
+
|
|
7
|
+
const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
8
|
+
const LOWER = 'abcdefghijklmnopqrstuvwxyz';
|
|
9
|
+
const DIGITS = '0123456789';
|
|
10
|
+
const SYMBOLS = '!@#$%^&*()-_=+[]{}|;:,.<>?';
|
|
11
|
+
|
|
12
|
+
const WORDLIST = [
|
|
13
|
+
'apple',
|
|
14
|
+
'brave',
|
|
15
|
+
'chair',
|
|
16
|
+
'dance',
|
|
17
|
+
'eagle',
|
|
18
|
+
'fancy',
|
|
19
|
+
'grace',
|
|
20
|
+
'house',
|
|
21
|
+
'ivory',
|
|
22
|
+
'joker',
|
|
23
|
+
'kneel',
|
|
24
|
+
'lemon',
|
|
25
|
+
'maple',
|
|
26
|
+
'noble',
|
|
27
|
+
'ocean',
|
|
28
|
+
'piano',
|
|
29
|
+
'queen',
|
|
30
|
+
'river',
|
|
31
|
+
'stone',
|
|
32
|
+
'tiger',
|
|
33
|
+
'ultra',
|
|
34
|
+
'vivid',
|
|
35
|
+
'waste',
|
|
36
|
+
'xenon',
|
|
37
|
+
'yacht',
|
|
38
|
+
'zebra',
|
|
39
|
+
'amber',
|
|
40
|
+
'blaze',
|
|
41
|
+
'coral',
|
|
42
|
+
'drift',
|
|
43
|
+
'ember',
|
|
44
|
+
'flora',
|
|
45
|
+
'globe',
|
|
46
|
+
'haven',
|
|
47
|
+
'input',
|
|
48
|
+
'jumpy',
|
|
49
|
+
'karma',
|
|
50
|
+
'lunar',
|
|
51
|
+
'magic',
|
|
52
|
+
'night',
|
|
53
|
+
'optic',
|
|
54
|
+
'proxy',
|
|
55
|
+
'quirk',
|
|
56
|
+
'radar',
|
|
57
|
+
'solar',
|
|
58
|
+
'trace',
|
|
59
|
+
'umbra',
|
|
60
|
+
'vapor',
|
|
61
|
+
'witch',
|
|
62
|
+
'xylem',
|
|
63
|
+
'yield',
|
|
64
|
+
'zippy',
|
|
65
|
+
'alpha',
|
|
66
|
+
'brisk',
|
|
67
|
+
'crane',
|
|
68
|
+
'depth',
|
|
69
|
+
'event',
|
|
70
|
+
'frost',
|
|
71
|
+
'grime',
|
|
72
|
+
'harsh',
|
|
73
|
+
'index',
|
|
74
|
+
'joint',
|
|
75
|
+
'knack',
|
|
76
|
+
'level',
|
|
77
|
+
'minus',
|
|
78
|
+
'nerve',
|
|
79
|
+
'onset',
|
|
80
|
+
'phase',
|
|
81
|
+
'quest',
|
|
82
|
+
'ratio',
|
|
83
|
+
'scope',
|
|
84
|
+
'trend',
|
|
85
|
+
'union',
|
|
86
|
+
'valve',
|
|
87
|
+
'weary',
|
|
88
|
+
'young',
|
|
89
|
+
'blunt',
|
|
90
|
+
'clean',
|
|
91
|
+
'drive',
|
|
92
|
+
'earth',
|
|
93
|
+
'fiber',
|
|
94
|
+
'grain',
|
|
95
|
+
'grind',
|
|
96
|
+
'horse',
|
|
97
|
+
'ideal',
|
|
98
|
+
'issue',
|
|
99
|
+
'judge',
|
|
100
|
+
'juice',
|
|
101
|
+
'knave',
|
|
102
|
+
'large',
|
|
103
|
+
'laser',
|
|
104
|
+
'light',
|
|
105
|
+
'limit',
|
|
106
|
+
'media',
|
|
107
|
+
'merit',
|
|
108
|
+
'metal',
|
|
109
|
+
'model',
|
|
110
|
+
'money',
|
|
111
|
+
'month',
|
|
112
|
+
'motor',
|
|
113
|
+
'mount',
|
|
114
|
+
'music',
|
|
115
|
+
'nerve',
|
|
116
|
+
'noise',
|
|
117
|
+
'north',
|
|
118
|
+
'noted',
|
|
119
|
+
'novel',
|
|
120
|
+
'nurse',
|
|
121
|
+
'oasis',
|
|
122
|
+
'offer',
|
|
123
|
+
'olive',
|
|
124
|
+
'omega',
|
|
125
|
+
'other',
|
|
126
|
+
'outer',
|
|
127
|
+
'owner',
|
|
128
|
+
'oxide',
|
|
129
|
+
'ozone',
|
|
130
|
+
'paint',
|
|
131
|
+
'panel',
|
|
132
|
+
'paper',
|
|
133
|
+
'patch',
|
|
134
|
+
'pause',
|
|
135
|
+
'peace',
|
|
136
|
+
'pearl',
|
|
137
|
+
'pedal',
|
|
138
|
+
'plain',
|
|
139
|
+
'plank',
|
|
140
|
+
'plant',
|
|
141
|
+
'plate',
|
|
142
|
+
'plaza',
|
|
143
|
+
'pluck',
|
|
144
|
+
'plumb',
|
|
145
|
+
'plume',
|
|
146
|
+
'plunge',
|
|
147
|
+
'point',
|
|
148
|
+
'polar',
|
|
149
|
+
'power',
|
|
150
|
+
'press',
|
|
151
|
+
'pride',
|
|
152
|
+
'prime',
|
|
153
|
+
'print',
|
|
154
|
+
'prism',
|
|
155
|
+
'prize',
|
|
156
|
+
'probe',
|
|
157
|
+
'prone',
|
|
158
|
+
'proof',
|
|
159
|
+
'prose',
|
|
160
|
+
'proud',
|
|
161
|
+
'prove',
|
|
162
|
+
'prowl',
|
|
163
|
+
'pulse',
|
|
164
|
+
'punch',
|
|
165
|
+
'pupil',
|
|
166
|
+
'purge',
|
|
167
|
+
'swift',
|
|
168
|
+
'sword',
|
|
169
|
+
'table',
|
|
170
|
+
'taint',
|
|
171
|
+
'tally',
|
|
172
|
+
'taste',
|
|
173
|
+
'teach',
|
|
174
|
+
'teeth',
|
|
175
|
+
'tempo',
|
|
176
|
+
'tense',
|
|
177
|
+
'tenth',
|
|
178
|
+
'terms',
|
|
179
|
+
'terra',
|
|
180
|
+
'thick',
|
|
181
|
+
'thing',
|
|
182
|
+
'think',
|
|
183
|
+
'thorn',
|
|
184
|
+
'three',
|
|
185
|
+
'throw',
|
|
186
|
+
'thumb',
|
|
187
|
+
'tidal',
|
|
188
|
+
'tight',
|
|
189
|
+
'tilde',
|
|
190
|
+
'timer',
|
|
191
|
+
'title',
|
|
192
|
+
'token',
|
|
193
|
+
'torch',
|
|
194
|
+
'total',
|
|
195
|
+
'touch',
|
|
196
|
+
'tough',
|
|
197
|
+
'tower',
|
|
198
|
+
'track',
|
|
199
|
+
'trade',
|
|
200
|
+
'trail',
|
|
201
|
+
'train',
|
|
202
|
+
'trait',
|
|
203
|
+
'tramp',
|
|
204
|
+
'tread',
|
|
205
|
+
'treat',
|
|
206
|
+
'trees',
|
|
207
|
+
'trial',
|
|
208
|
+
'tribe',
|
|
209
|
+
'trick',
|
|
210
|
+
'tried',
|
|
211
|
+
'troop',
|
|
212
|
+
'trove',
|
|
213
|
+
'truce',
|
|
214
|
+
'truck',
|
|
215
|
+
'truly',
|
|
216
|
+
'trunk',
|
|
217
|
+
'truth',
|
|
218
|
+
'tulip',
|
|
219
|
+
'tumor',
|
|
220
|
+
'tuner',
|
|
221
|
+
'tunic',
|
|
222
|
+
'tweak',
|
|
223
|
+
'twice',
|
|
224
|
+
'twirl',
|
|
225
|
+
'twist',
|
|
226
|
+
'typed',
|
|
227
|
+
'under',
|
|
228
|
+
'unify',
|
|
229
|
+
'unite',
|
|
230
|
+
'unity',
|
|
231
|
+
'until',
|
|
232
|
+
'upper',
|
|
233
|
+
'upset',
|
|
234
|
+
'urban',
|
|
235
|
+
'usage',
|
|
236
|
+
'valid',
|
|
237
|
+
'value',
|
|
238
|
+
'visor',
|
|
239
|
+
'vital',
|
|
240
|
+
'vivid',
|
|
241
|
+
'vocab',
|
|
242
|
+
'vocal',
|
|
243
|
+
'voice',
|
|
244
|
+
'voter',
|
|
245
|
+
'vault',
|
|
246
|
+
'venue',
|
|
247
|
+
'verse',
|
|
248
|
+
'watch',
|
|
249
|
+
'water',
|
|
250
|
+
'weave',
|
|
251
|
+
'wedge',
|
|
252
|
+
'weird',
|
|
253
|
+
'wheel',
|
|
254
|
+
'where',
|
|
255
|
+
'which',
|
|
256
|
+
'while',
|
|
257
|
+
'white',
|
|
258
|
+
'whole',
|
|
259
|
+
'wider',
|
|
260
|
+
'windy',
|
|
261
|
+
'wired',
|
|
262
|
+
'world',
|
|
263
|
+
'worse',
|
|
264
|
+
'worth',
|
|
265
|
+
'would',
|
|
266
|
+
'wound',
|
|
267
|
+
'write',
|
|
268
|
+
'wrong',
|
|
269
|
+
'wrote',
|
|
270
|
+
'years',
|
|
271
|
+
'yield',
|
|
272
|
+
'yours',
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
export interface PasswordResult {
|
|
276
|
+
passwords: string[];
|
|
277
|
+
type: string;
|
|
278
|
+
length: number;
|
|
279
|
+
strength: string;
|
|
280
|
+
entropy: number;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function computeStrength(entropy: number): string {
|
|
284
|
+
if (entropy < 40) return 'weak';
|
|
285
|
+
if (entropy < 60) return 'moderate';
|
|
286
|
+
if (entropy < 80) return 'strong';
|
|
287
|
+
return 'very_strong';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function generateRandom(length: number, charset: string): string {
|
|
291
|
+
return Array.from({ length }, () => charset[randomInt(charset.length)]).join('');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function generatePassphrase(wordCount: number, separator: string): string {
|
|
295
|
+
return Array.from({ length: wordCount }, () => WORDLIST[randomInt(WORDLIST.length)]).join(separator);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Generates one or more passwords using random characters or passphrase mode.
|
|
300
|
+
*
|
|
301
|
+
* @param type - Generation mode: "random" or "passphrase"
|
|
302
|
+
* @param length - Password length for random mode (8–128), or word count for passphrase mode (3–10)
|
|
303
|
+
* @param options - Character set options: uppercase, lowercase, digits, symbols, exclude, count, separator
|
|
304
|
+
* @returns Generated passwords with type, length, strength and entropy
|
|
305
|
+
* @throws Error if no charset is active, length is out of range, or count exceeds the maximum
|
|
306
|
+
*/
|
|
307
|
+
export default function password(
|
|
308
|
+
type: string = 'random',
|
|
309
|
+
length: number = 16,
|
|
310
|
+
options: {
|
|
311
|
+
uppercase?: boolean;
|
|
312
|
+
lowercase?: boolean;
|
|
313
|
+
digits?: boolean;
|
|
314
|
+
symbols?: boolean;
|
|
315
|
+
exclude?: string;
|
|
316
|
+
count?: number;
|
|
317
|
+
separator?: string;
|
|
318
|
+
} = {},
|
|
319
|
+
): PasswordResult {
|
|
320
|
+
const {
|
|
321
|
+
uppercase = true,
|
|
322
|
+
lowercase = true,
|
|
323
|
+
digits = true,
|
|
324
|
+
symbols = false,
|
|
325
|
+
exclude = '',
|
|
326
|
+
count = 1,
|
|
327
|
+
separator = '-',
|
|
328
|
+
} = options;
|
|
329
|
+
|
|
330
|
+
if (count < 1 || count > MAX_COUNT) {
|
|
331
|
+
throw new Error(`Count must be between 1 and ${MAX_COUNT}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (type === 'passphrase') {
|
|
335
|
+
const wordCount = Math.max(3, Math.min(10, length));
|
|
336
|
+
const entropy = Math.log2(Math.pow(WORDLIST.length, wordCount));
|
|
337
|
+
const passwords = Array.from({ length: count }, () => generatePassphrase(wordCount, separator));
|
|
338
|
+
return {
|
|
339
|
+
passwords,
|
|
340
|
+
type: 'passphrase',
|
|
341
|
+
length: wordCount,
|
|
342
|
+
strength: computeStrength(entropy),
|
|
343
|
+
entropy: Math.round(entropy * 10) / 10,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (type !== 'random') {
|
|
348
|
+
throw new Error('Type must be "random" or "passphrase"');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (isNaN(length) || length < MIN_LENGTH || length > MAX_LENGTH) {
|
|
352
|
+
throw new Error(`Length must be between ${MIN_LENGTH} and ${MAX_LENGTH}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let charset = '';
|
|
356
|
+
if (uppercase) charset += UPPER;
|
|
357
|
+
if (lowercase) charset += LOWER;
|
|
358
|
+
if (digits) charset += DIGITS;
|
|
359
|
+
if (symbols) charset += SYMBOLS;
|
|
360
|
+
|
|
361
|
+
if (exclude) {
|
|
362
|
+
for (const char of exclude) {
|
|
363
|
+
charset = charset.replaceAll(char, '');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (charset.length === 0) {
|
|
368
|
+
throw new Error('At least one character set must be enabled');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const entropy = Math.log2(Math.pow(charset.length, length));
|
|
372
|
+
const passwords = Array.from({ length: count }, () => generateRandom(length, charset));
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
passwords,
|
|
376
|
+
type: 'random',
|
|
377
|
+
length,
|
|
378
|
+
strength: computeStrength(entropy),
|
|
379
|
+
entropy: Math.round(entropy * 10) / 10,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
@@ -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;
|
|
@@ -13,6 +14,11 @@ interface CountryInfo {
|
|
|
13
14
|
lang: string;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Generates a fictional personal profile with realistic demographic and contact data.
|
|
19
|
+
*
|
|
20
|
+
* @returns Object containing name, email, phone, address, job, hobbies, and other personal attributes
|
|
21
|
+
*/
|
|
16
22
|
export default function personal(): Record<string, unknown> {
|
|
17
23
|
const people: Person[] = [
|
|
18
24
|
{ name: 'John Doe', social: 'john_doe', email: 'john@example.com', country: 'US' },
|
|
@@ -42,21 +48,6 @@ export default function personal(): Record<string, unknown> {
|
|
|
42
48
|
|
|
43
49
|
const jobs = ['Writer', 'Artist', 'Musician', 'Explorer', 'Scientist', 'Engineer', 'Athlete', 'Doctor'];
|
|
44
50
|
const hobbies = ['Reading', 'Traveling', 'Gaming', 'Cooking', 'Fitness', 'Music', 'Photography', 'Writing'];
|
|
45
|
-
const cities = [
|
|
46
|
-
'New York',
|
|
47
|
-
'Paris',
|
|
48
|
-
'London',
|
|
49
|
-
'Madrid',
|
|
50
|
-
'Berlin',
|
|
51
|
-
'Rome',
|
|
52
|
-
'Tokyo',
|
|
53
|
-
'Los Angeles',
|
|
54
|
-
'Sydney',
|
|
55
|
-
'São Paulo',
|
|
56
|
-
'Toronto',
|
|
57
|
-
];
|
|
58
|
-
const streets = ['Main St', '2nd Ave', 'Broadway', 'Park Lane', 'Elm St', 'Sunset Blvd', 'Maple St', 'Highland Rd'];
|
|
59
|
-
|
|
60
51
|
const card = Array.from({ length: 4 }, () => randomNumber(1000, 9999)).join(' ');
|
|
61
52
|
const cvc = randomNumber(100, 999);
|
|
62
53
|
const expiration = `${String(randomNumber(1, 12)).padStart(2, '0')}/${(new Date().getFullYear() + randomNumber(0, 3)).toString().slice(-2)}`;
|
|
@@ -64,6 +55,8 @@ export default function personal(): Record<string, unknown> {
|
|
|
64
55
|
const person = random(people);
|
|
65
56
|
const social = person.social;
|
|
66
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';
|
|
67
60
|
const countryInfo = countries[country]!;
|
|
68
61
|
const phone = countryInfo.tel;
|
|
69
62
|
const lang = countryInfo.lang;
|
|
@@ -124,7 +117,7 @@ export default function personal(): Record<string, unknown> {
|
|
|
124
117
|
card,
|
|
125
118
|
cvc,
|
|
126
119
|
expiration,
|
|
127
|
-
address:
|
|
120
|
+
address: addressFn(addrCountry).addresses[0]!,
|
|
128
121
|
birthday,
|
|
129
122
|
civil_status: civilStatus,
|
|
130
123
|
children,
|
|
@@ -157,6 +157,14 @@ function parseAnimate(value: string | undefined): AnimateMode {
|
|
|
157
157
|
throw new Error('animate must be one of: shimmer, pulse, none');
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Generates an SVG placeholder image or skeleton loader with configurable dimensions and style.
|
|
162
|
+
*
|
|
163
|
+
* @param type - Placeholder type: "image" or "skeleton"
|
|
164
|
+
* @param query - Query parameters including width, height, bg, color, text, rows, avatar, animate, speed, and radius
|
|
165
|
+
* @returns Object containing the SVG body, content type, and placeholder type
|
|
166
|
+
* @throws Error if any parameter is invalid or out of range
|
|
167
|
+
*/
|
|
160
168
|
export default function placeholder(type: string, query: Record<string, string | undefined>): PlaceholderResult {
|
|
161
169
|
const speed = query.speed ? parseFloat(query.speed) : 1.5;
|
|
162
170
|
if (isNaN(speed) || speed < 0.1 || speed > 10) throw new Error('speed must be between 0.1 and 10');
|
package/src/modules/v4/qrcode.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { toBuffer, toDataURL, toCanvas as qrToCanvas } from 'qrcode';
|
|
2
1
|
import { createCanvas, loadImage } from 'canvas';
|
|
2
|
+
import { toBuffer, toCanvas as qrToCanvas, toDataURL } from 'qrcode';
|
|
3
|
+
|
|
3
4
|
import { normalizeColor } from '../../utils/colors.js';
|
|
4
5
|
|
|
5
6
|
export interface QRCodeOptions {
|
|
@@ -48,6 +49,13 @@ async function fetchIcon(url: string): Promise<Buffer> {
|
|
|
48
49
|
return buffer;
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Generates a QR code image for a given URL, with optional icon overlay and color customization.
|
|
54
|
+
*
|
|
55
|
+
* @param options - QR code generation options including URL, size, margin, correction level, colors, and optional icon
|
|
56
|
+
* @returns Object containing the QR code as a PNG buffer or Base64 string, and the content type
|
|
57
|
+
* @throws Error if the URL is missing, any option is invalid, or the icon URL cannot be fetched
|
|
58
|
+
*/
|
|
51
59
|
export default async function qrcode(options: QRCodeOptions): Promise<QRCodeResult> {
|
|
52
60
|
const { url } = options;
|
|
53
61
|
if (!url || typeof url !== 'string') throw new Error('Please provide a valid URL');
|
|
@@ -13,6 +13,13 @@ export interface StatisticsResult {
|
|
|
13
13
|
stddev: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Computes descriptive statistics for a comma-separated list of numbers.
|
|
18
|
+
*
|
|
19
|
+
* @param values - Comma-separated numeric string (e.g. "1,2,3,4,5")
|
|
20
|
+
* @returns Object containing count, sum, min, max, range, mean, median, mode, variance, and standard deviation
|
|
21
|
+
* @throws Error if values is missing, contains non-numeric entries, or exceeds the maximum count
|
|
22
|
+
*/
|
|
16
23
|
export default function statistics(values: string): StatisticsResult {
|
|
17
24
|
if (!values) throw new Error('A list of values is required');
|
|
18
25
|
if (typeof values !== 'string') throw new Error('Values must be a comma-separated string');
|