@20syldev/api 4.5.0 → 4.6.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 +9 -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 +7 -4
- 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/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/personal.ts +5 -0
- 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 +10 -1
- package/src/modules/v4/username.ts +5 -0
- package/src/modules/v4/validate.ts +21 -0
- package/src/modules/v4.ts +3 -1
- package/src/routes/delete.ts +2 -1
- package/src/routes/get.ts +57 -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 +87 -2
- 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/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
package/src/modules/v4/encode.ts
CHANGED
|
@@ -66,22 +66,50 @@ function checkText(value: string): void {
|
|
|
66
66
|
throw new Error(`Value must be less than ${MAX_STRING_LENGTH} characters long`);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Encodes a UTF-8 string to Base64.
|
|
71
|
+
*
|
|
72
|
+
* @param value - The string to encode
|
|
73
|
+
* @returns Base64-encoded string
|
|
74
|
+
* @throws Error if value is missing, not a string, or too long
|
|
75
|
+
*/
|
|
69
76
|
export function base64encode(value: string): string {
|
|
70
77
|
checkText(value);
|
|
71
78
|
return Buffer.from(value, 'utf-8').toString('base64');
|
|
72
79
|
}
|
|
73
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Decodes a Base64 string to UTF-8.
|
|
83
|
+
*
|
|
84
|
+
* @param value - The Base64 string to decode
|
|
85
|
+
* @returns Decoded UTF-8 string
|
|
86
|
+
* @throws Error if value is missing, not valid Base64, or too long
|
|
87
|
+
*/
|
|
74
88
|
export function base64decode(value: string): string {
|
|
75
89
|
checkText(value);
|
|
76
90
|
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value)) throw new Error('Invalid Base64 string');
|
|
77
91
|
return Buffer.from(value, 'base64').toString('utf-8');
|
|
78
92
|
}
|
|
79
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Percent-encodes a string for safe use in a URL.
|
|
96
|
+
*
|
|
97
|
+
* @param value - The string to encode
|
|
98
|
+
* @returns URL-encoded string
|
|
99
|
+
* @throws Error if value is missing, not a string, or too long
|
|
100
|
+
*/
|
|
80
101
|
export function urlencode(value: string): string {
|
|
81
102
|
checkText(value);
|
|
82
103
|
return encodeURIComponent(value);
|
|
83
104
|
}
|
|
84
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Decodes a percent-encoded URL string.
|
|
108
|
+
*
|
|
109
|
+
* @param value - The URL-encoded string to decode
|
|
110
|
+
* @returns Decoded string
|
|
111
|
+
* @throws Error if value is missing, not a valid URL-encoded string, or too long
|
|
112
|
+
*/
|
|
85
113
|
export function urldecode(value: string): string {
|
|
86
114
|
checkText(value);
|
|
87
115
|
try {
|
|
@@ -91,6 +119,13 @@ export function urldecode(value: string): string {
|
|
|
91
119
|
}
|
|
92
120
|
}
|
|
93
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Converts a plain text string to Morse code.
|
|
124
|
+
*
|
|
125
|
+
* @param value - The text to encode in Morse code
|
|
126
|
+
* @returns Morse code string where letters are separated by spaces and words by " / "
|
|
127
|
+
* @throws Error if value contains unsupported characters or is too long
|
|
128
|
+
*/
|
|
94
129
|
export function morse(value: string): string {
|
|
95
130
|
checkText(value);
|
|
96
131
|
return value
|
|
@@ -111,6 +146,13 @@ export function morse(value: string): string {
|
|
|
111
146
|
.join(' / ');
|
|
112
147
|
}
|
|
113
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Converts a Morse code string back to plain text.
|
|
151
|
+
*
|
|
152
|
+
* @param value - Morse code string (letters separated by spaces, words by " / ")
|
|
153
|
+
* @returns Decoded plain text string
|
|
154
|
+
* @throws Error if the Morse code contains an unrecognized sequence or is too long
|
|
155
|
+
*/
|
|
114
156
|
export function unmorse(value: string): string {
|
|
115
157
|
checkText(value);
|
|
116
158
|
return value
|
|
@@ -129,6 +171,13 @@ export function unmorse(value: string): string {
|
|
|
129
171
|
.join(' ');
|
|
130
172
|
}
|
|
131
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Applies the ROT13 substitution cipher to a string.
|
|
176
|
+
*
|
|
177
|
+
* @param value - The string to encode
|
|
178
|
+
* @returns ROT13-encoded string (applying it twice restores the original)
|
|
179
|
+
* @throws Error if value is missing, not a string, or too long
|
|
180
|
+
*/
|
|
132
181
|
export function rot13(value: string): string {
|
|
133
182
|
checkText(value);
|
|
134
183
|
return value.replace(/[a-zA-Z]/g, (c) => {
|
|
@@ -137,6 +186,14 @@ export function rot13(value: string): string {
|
|
|
137
186
|
});
|
|
138
187
|
}
|
|
139
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Applies the Caesar cipher by shifting alphabetic characters by a given amount.
|
|
191
|
+
*
|
|
192
|
+
* @param value - The string to encode
|
|
193
|
+
* @param shift - Number of positions to shift (can be negative for left shift)
|
|
194
|
+
* @returns Caesar-shifted string
|
|
195
|
+
* @throws Error if value is missing or shift is not a number
|
|
196
|
+
*/
|
|
140
197
|
export function caesar(value: string, shift: string): string {
|
|
141
198
|
checkText(value);
|
|
142
199
|
const n = Number(shift);
|
|
@@ -148,6 +205,13 @@ export function caesar(value: string, shift: string): string {
|
|
|
148
205
|
});
|
|
149
206
|
}
|
|
150
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Converts a string to its binary representation, one byte per character.
|
|
210
|
+
*
|
|
211
|
+
* @param value - The string to convert
|
|
212
|
+
* @returns Space-separated 8-bit binary groups (one per character)
|
|
213
|
+
* @throws Error if value is missing, not a string, or too long
|
|
214
|
+
*/
|
|
151
215
|
export function binary(value: string): string {
|
|
152
216
|
checkText(value);
|
|
153
217
|
return value
|
|
@@ -156,6 +220,13 @@ export function binary(value: string): string {
|
|
|
156
220
|
.join(' ');
|
|
157
221
|
}
|
|
158
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Converts a binary string back to plain text.
|
|
225
|
+
*
|
|
226
|
+
* @param value - Space-separated 8-bit binary groups
|
|
227
|
+
* @returns Decoded string
|
|
228
|
+
* @throws Error if value contains non-binary characters or groups are not 8 bits wide
|
|
229
|
+
*/
|
|
159
230
|
export function unbinary(value: string): string {
|
|
160
231
|
checkText(value);
|
|
161
232
|
if (!/^[01\s]+$/.test(value)) throw new Error('Invalid binary string');
|
package/src/modules/v4/geo.ts
CHANGED
|
@@ -23,6 +23,16 @@ function parseCoord(value: string, name: string, min: number, max: number): numb
|
|
|
23
23
|
return n;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Calculates the great-circle distance and bearing between two geographic coordinates.
|
|
28
|
+
*
|
|
29
|
+
* @param lat1 - Latitude of the first point (-90 to 90)
|
|
30
|
+
* @param lon1 - Longitude of the first point (-180 to 180)
|
|
31
|
+
* @param lat2 - Latitude of the second point (-90 to 90)
|
|
32
|
+
* @param lon2 - Longitude of the second point (-180 to 180)
|
|
33
|
+
* @returns Distance in km/miles/nautical miles, compass bearing, and both coordinates
|
|
34
|
+
* @throws Error if any coordinate is out of range or not a number
|
|
35
|
+
*/
|
|
26
36
|
export default function geo(lat1: string, lon1: string, lat2: string, lon2: string): GeoResult {
|
|
27
37
|
const a = parseCoord(lat1, 'lat1', -90, 90);
|
|
28
38
|
const b = parseCoord(lon1, 'lon1', -180, 180);
|
package/src/modules/v4/hash.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash, getHashes } from 'crypto';
|
|
2
2
|
|
|
3
3
|
export interface HashResult {
|
|
4
4
|
method: string;
|
|
@@ -8,6 +8,15 @@ export interface HashResult {
|
|
|
8
8
|
|
|
9
9
|
const ENCODINGS = new Set(['hex', 'base64']);
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Hashes a text string using the specified algorithm and encoding.
|
|
13
|
+
*
|
|
14
|
+
* @param text - The text to hash
|
|
15
|
+
* @param method - Hashing algorithm (e.g. "sha256", "md5")
|
|
16
|
+
* @param encoding - Output encoding: "hex" (default) or "base64"
|
|
17
|
+
* @returns Object containing the method, hash result, and encoding used
|
|
18
|
+
* @throws Error if text is missing, the method is unsupported, or the encoding is invalid
|
|
19
|
+
*/
|
|
11
20
|
export default function hash(text: string, method: string, encoding: string = 'hex'): HashResult {
|
|
12
21
|
if (!text) throw new Error('Text is required');
|
|
13
22
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { formatDate } from '../../utils/helpers.js';
|
|
2
1
|
import ical from 'ical.js';
|
|
3
2
|
|
|
3
|
+
import { formatDate } from '../../utils/helpers.js';
|
|
4
|
+
|
|
4
5
|
interface CalendarEvent {
|
|
5
6
|
summary: string[] | string;
|
|
6
7
|
subject?: string;
|
|
@@ -27,6 +28,14 @@ function blocked(hostname: string): boolean {
|
|
|
27
28
|
return false;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Fetches and parses an ICS calendar from a Hyperplanning URL, filtering out past events.
|
|
33
|
+
*
|
|
34
|
+
* @param url - HTTPS URL to the ICS calendar
|
|
35
|
+
* @param detail - Level of detail: "full" (subject/teacher/classes), "list" (summary/times), or default (summary only)
|
|
36
|
+
* @returns Array of calendar events sorted by start time, excluding past events
|
|
37
|
+
* @throws Error if the URL is invalid, uses HTTP, points to a private host, or does not return a valid ICS file
|
|
38
|
+
*/
|
|
30
39
|
export default async function hyperplanning(url: string, detail?: string): Promise<CalendarEvent[]> {
|
|
31
40
|
let parsed: URL;
|
|
32
41
|
try {
|
|
@@ -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');
|
|
@@ -13,6 +13,11 @@ interface CountryInfo {
|
|
|
13
13
|
lang: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Generates a fictional personal profile with realistic demographic and contact data.
|
|
18
|
+
*
|
|
19
|
+
* @returns Object containing name, email, phone, address, job, hobbies, and other personal attributes
|
|
20
|
+
*/
|
|
16
21
|
export default function personal(): Record<string, unknown> {
|
|
17
22
|
const people: Person[] = [
|
|
18
23
|
{ name: 'John Doe', social: 'john_doe', email: 'john@example.com', country: 'US' },
|
|
@@ -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');
|
package/src/modules/v4/text.ts
CHANGED
|
@@ -142,6 +142,13 @@ export interface TextStats {
|
|
|
142
142
|
mostFrequentChar: string;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Analyzes a text string and returns character, word, sentence, and reading time statistics.
|
|
147
|
+
*
|
|
148
|
+
* @param value - The text to analyze
|
|
149
|
+
* @returns Object containing character counts, word/sentence/paragraph counts, reading time, and most frequent character
|
|
150
|
+
* @throws Error if value is missing, not a string, or too long
|
|
151
|
+
*/
|
|
145
152
|
export function stats(value: string): TextStats {
|
|
146
153
|
checkText(value);
|
|
147
154
|
|
|
@@ -168,6 +175,13 @@ export function stats(value: string): TextStats {
|
|
|
168
175
|
return { characters, charactersNoSpaces, words, sentences, paragraphs, readingTime, mostFrequentChar };
|
|
169
176
|
}
|
|
170
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Converts a string to a URL-friendly slug.
|
|
180
|
+
*
|
|
181
|
+
* @param value - The string to slugify
|
|
182
|
+
* @returns Lowercase hyphenated slug with diacritics and special characters removed
|
|
183
|
+
* @throws Error if value is missing, not a string, or too long
|
|
184
|
+
*/
|
|
171
185
|
export function slug(value: string): string {
|
|
172
186
|
checkText(value);
|
|
173
187
|
return value
|
|
@@ -179,6 +193,14 @@ export function slug(value: string): string {
|
|
|
179
193
|
.replace(/[\s-]+/g, '-');
|
|
180
194
|
}
|
|
181
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Generates Lorem Ipsum placeholder text.
|
|
198
|
+
*
|
|
199
|
+
* @param type - Content type: "words", "sentences", or "paragraphs"
|
|
200
|
+
* @param count - Number of words, sentences, or paragraphs to generate
|
|
201
|
+
* @returns Generated Lorem Ipsum text
|
|
202
|
+
* @throws Error if count is out of range or type is not one of the accepted values
|
|
203
|
+
*/
|
|
182
204
|
export function lorem(type: string, count: string): string {
|
|
183
205
|
const n = Number(count) || 5;
|
|
184
206
|
if (n < 1 || n > 500) throw new Error('Count must be between 1 and 500');
|
|
@@ -283,6 +305,14 @@ function numberToEnglish(n: number): string {
|
|
|
283
305
|
throw new Error('Number must be less than 1 billion');
|
|
284
306
|
}
|
|
285
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Converts an integer to its written-out word form in French or English.
|
|
310
|
+
*
|
|
311
|
+
* @param value - The integer to convert (must be less than 1 billion)
|
|
312
|
+
* @param lang - Language: "fr" for French or "en" for English
|
|
313
|
+
* @returns The number written out in words
|
|
314
|
+
* @throws Error if value is not an integer, exceeds the maximum, or lang is not supported
|
|
315
|
+
*/
|
|
286
316
|
export function number(value: string, lang: string): string {
|
|
287
317
|
const n = Number(value);
|
|
288
318
|
if (isNaN(n)) throw new Error('Value must be a number');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from 'crypto';
|
|
2
|
-
|
|
3
|
-
import
|
|
2
|
+
|
|
3
|
+
import { GAME_CLEANUP_TTL, RATE_LIMIT_MAX, RATE_LIMIT_WINDOW, SESSION_TTL } from '../../constants.js';
|
|
4
|
+
import type { TicTacToeGame, TicTacToeMove, TicTacToeStorage } from '../../types/storage.js';
|
|
4
5
|
|
|
5
6
|
interface TicTacToeParams {
|
|
6
7
|
username?: string;
|
|
@@ -17,6 +18,14 @@ interface GameResult {
|
|
|
17
18
|
tie?: boolean;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Handles Tic-Tac-Toe game actions including playing a move, fetching game state, listing games, and forfeiting.
|
|
23
|
+
*
|
|
24
|
+
* @param action - The action to perform: "play", "fetch", "list", or "forfeit"
|
|
25
|
+
* @param params - Game parameters including username, move, session, game ID, and shared storage
|
|
26
|
+
* @returns Game state or action result depending on the action
|
|
27
|
+
* @throws Error if a required parameter is missing, the action is invalid, or a rate limit is exceeded
|
|
28
|
+
*/
|
|
20
29
|
export default function tic_tac_toe(action: string, params: TicTacToeParams): Record<string, unknown> {
|
|
21
30
|
const storage = params.storage;
|
|
22
31
|
|
package/src/modules/v4/time.ts
CHANGED
|
@@ -24,6 +24,17 @@ const validTimezones = ['UTC', 'America/New_York', 'Europe/Paris', 'Asia/Tokyo',
|
|
|
24
24
|
type TimeFormat = (typeof validFormats)[number];
|
|
25
25
|
type Timezone = (typeof validTimezones)[number];
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Returns the current or a random date/time in various formats and timezones.
|
|
29
|
+
*
|
|
30
|
+
* @param type - "live" for the current time or "random" for a random date within a range
|
|
31
|
+
* @param start - Optional start date for random mode (YYYY-MM-DD)
|
|
32
|
+
* @param end - Optional end date for random mode (YYYY-MM-DD)
|
|
33
|
+
* @param format - Optional specific format to return (e.g. "iso", "timestamp", "year")
|
|
34
|
+
* @param timezone - Optional timezone (e.g. "UTC", "Europe/Paris")
|
|
35
|
+
* @returns Object containing all time formats, or a single format if specified
|
|
36
|
+
* @throws Error if type, format, or timezone is invalid
|
|
37
|
+
*/
|
|
27
38
|
export default function time(
|
|
28
39
|
type: string = 'live',
|
|
29
40
|
start?: string,
|
package/src/modules/v4/token.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { randomBytes } from 'crypto';
|
|
2
2
|
import { v4 } from 'uuid';
|
|
3
|
-
import { MIN_TOKEN_LENGTH, MAX_TOKEN_LENGTH } from '../../constants.js';
|
|
4
3
|
|
|
4
|
+
import { MAX_TOKEN_LENGTH, MIN_TOKEN_LENGTH } from '../../constants.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates a cryptographically random token of the specified length and type.
|
|
8
|
+
*
|
|
9
|
+
* @param len - Token length (must be between 12 and 4096)
|
|
10
|
+
* @param type - Character set to use: "alpha", "alphanum", "base64", "hex", "num", "punct", "urlsafe", or "uuid"
|
|
11
|
+
* @returns The generated token string
|
|
12
|
+
* @throws Error if length is out of range or the type is not valid
|
|
13
|
+
*/
|
|
5
14
|
export default function token(len: number, type: string = 'alphanum'): string {
|
|
6
15
|
if (isNaN(len) || len < MIN_TOKEN_LENGTH) {
|
|
7
16
|
throw new Error('Length must be a number greater than or equal to 12');
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { random, randomNumber } from '../../utils/helpers.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Generates a random username combining adjectives, animals, and job titles.
|
|
5
|
+
*
|
|
6
|
+
* @returns Object containing the generated username, the number used, and the components (adjective, animal, job)
|
|
7
|
+
*/
|
|
3
8
|
export default function username(): Record<string, unknown> {
|
|
4
9
|
const adj = [
|
|
5
10
|
'Happy',
|
|
@@ -7,6 +7,13 @@ function checkValue(value: string): void {
|
|
|
7
7
|
throw new Error(`Value must be less than ${MAX_STRING_LENGTH} characters long`);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Validates a credit/debit card number using the Luhn algorithm.
|
|
12
|
+
*
|
|
13
|
+
* @param value - Card number string (digits, spaces, or dashes allowed)
|
|
14
|
+
* @returns Object containing the validity result and the sanitized digit string
|
|
15
|
+
* @throws Error if value is missing, contains non-digit characters, or has an invalid length
|
|
16
|
+
*/
|
|
10
17
|
export function luhn(value: string): { valid: boolean; value: string } {
|
|
11
18
|
checkValue(value);
|
|
12
19
|
const digits = value.replace(/\s|-/g, '');
|
|
@@ -28,6 +35,13 @@ export function luhn(value: string): { valid: boolean; value: string } {
|
|
|
28
35
|
return { valid: sum % 10 === 0, value: digits };
|
|
29
36
|
}
|
|
30
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Validates an IBAN using the mod-97 checksum algorithm.
|
|
40
|
+
*
|
|
41
|
+
* @param value - IBAN string (spaces allowed)
|
|
42
|
+
* @returns Object containing the validity result, sanitized IBAN, and country code
|
|
43
|
+
* @throws Error if value is missing, has an invalid format, or is out of length bounds
|
|
44
|
+
*/
|
|
31
45
|
export function iban(value: string): { valid: boolean; value: string; country?: string } {
|
|
32
46
|
checkValue(value);
|
|
33
47
|
const cleaned = value.replace(/\s/g, '').toUpperCase();
|
|
@@ -48,6 +62,13 @@ export function iban(value: string): { valid: boolean; value: string; country?:
|
|
|
48
62
|
return { valid: remainder === 1, value: cleaned, country: cleaned.slice(0, 2) };
|
|
49
63
|
}
|
|
50
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Validates an email address against a basic format check.
|
|
67
|
+
*
|
|
68
|
+
* @param value - The email address string to validate
|
|
69
|
+
* @returns Object containing the validity result and the original value
|
|
70
|
+
* @throws Error if value is missing, not a string, or too long
|
|
71
|
+
*/
|
|
51
72
|
export function email(value: string): { valid: boolean; value: string } {
|
|
52
73
|
checkValue(value);
|
|
53
74
|
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
package/src/modules/v4.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
export { default as agent } from './v4/agent.js';
|
|
1
2
|
export * as algorithms from './v4/algorithms.js';
|
|
2
|
-
export { default as chat } from './v4/chat.js';
|
|
3
3
|
export { default as captcha } from './v4/captcha.js';
|
|
4
|
+
export { default as chat } from './v4/chat.js';
|
|
4
5
|
export { default as color } from './v4/color.js';
|
|
5
6
|
export { default as convert } from './v4/convert.js';
|
|
6
7
|
export { default as dice } from './v4/dice.js';
|
|
@@ -9,6 +10,7 @@ export * as encode from './v4/encode.js';
|
|
|
9
10
|
export { default as geo } from './v4/geo.js';
|
|
10
11
|
export { default as hash } from './v4/hash.js';
|
|
11
12
|
export { default as hyperplanning } from './v4/hyperplanning.js';
|
|
13
|
+
export { default as ip } from './v4/ip.js';
|
|
12
14
|
export { default as levenshtein } from './v4/levenshtein.js';
|
|
13
15
|
export { default as palette } from './v4/palette.js';
|
|
14
16
|
export { default as personal } from './v4/personal.js';
|
package/src/routes/delete.ts
CHANGED