@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,104 @@
|
|
|
1
|
+
import { MAX_STRING_LENGTH } from '../../constants.js';
|
|
2
|
+
|
|
3
|
+
export interface UserAgentResult {
|
|
4
|
+
raw: string;
|
|
5
|
+
browser: { name: string; version: string; major: string };
|
|
6
|
+
os: { name: string; version: string };
|
|
7
|
+
device: { type: 'mobile' | 'tablet' | 'desktop'; vendor: string };
|
|
8
|
+
engine: { name: string; version: string };
|
|
9
|
+
bot: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const WINDOWS_MAP: Record<string, string> = {
|
|
13
|
+
'10.0': 'Windows 10/11',
|
|
14
|
+
'6.3': 'Windows 8.1',
|
|
15
|
+
'6.2': 'Windows 8',
|
|
16
|
+
'6.1': 'Windows 7',
|
|
17
|
+
'6.0': 'Windows Vista',
|
|
18
|
+
'5.1': 'Windows XP',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parses a User-Agent string and extracts browser, OS, device, engine
|
|
23
|
+
* and bot information.
|
|
24
|
+
*
|
|
25
|
+
* @param ua - The User-Agent string to parse
|
|
26
|
+
* @returns Structured breakdown of the User-Agent
|
|
27
|
+
* @throws Error if the User-Agent string is missing or too long
|
|
28
|
+
*/
|
|
29
|
+
export default function agent(ua: string): UserAgentResult {
|
|
30
|
+
if (!ua || typeof ua !== 'string') throw new Error('A User-Agent string is required');
|
|
31
|
+
if (ua.length > MAX_STRING_LENGTH) throw new Error(`User-Agent must be less than ${MAX_STRING_LENGTH} characters`);
|
|
32
|
+
|
|
33
|
+
const unknown = { name: 'unknown', version: 'unknown', major: 'unknown' };
|
|
34
|
+
|
|
35
|
+
const bot = /bot|crawl|spider|lighthouse|headless|prerender|slurp|bingpreview/i.test(ua);
|
|
36
|
+
|
|
37
|
+
// Browser — order matters: Edge/Opera before Chrome
|
|
38
|
+
let browser = { ...unknown };
|
|
39
|
+
const browserRules: [RegExp, string][] = [
|
|
40
|
+
[/Edg\/(\d+[\d.]*)/i, 'Edge'],
|
|
41
|
+
[/OPR\/(\d+[\d.]*)/i, 'Opera'],
|
|
42
|
+
[/SamsungBrowser\/(\d+[\d.]*)/i, 'Samsung Internet'],
|
|
43
|
+
[/Chrome\/(\d+[\d.]*)/i, 'Chrome'],
|
|
44
|
+
[/Firefox\/(\d+[\d.]*)/i, 'Firefox'],
|
|
45
|
+
[/Version\/([\d.]+).*Safari/i, 'Safari'],
|
|
46
|
+
[/MSIE ([\d.]+)/i, 'Internet Explorer'],
|
|
47
|
+
[/Trident\/.*rv:([\d.]+)/i, 'Internet Explorer'],
|
|
48
|
+
];
|
|
49
|
+
for (const [re, name] of browserRules) {
|
|
50
|
+
const m = ua.match(re);
|
|
51
|
+
if (m) {
|
|
52
|
+
browser = { name, version: m[1]!, major: m[1]!.split('.')[0]! };
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// OS
|
|
58
|
+
let os = { name: 'unknown', version: 'unknown' };
|
|
59
|
+
const osRules: [RegExp, (m: RegExpMatchArray) => typeof os][] = [
|
|
60
|
+
[/iPhone OS ([\d_]+)/i, (m) => ({ name: 'iOS', version: m[1]!.replace(/_/g, '.') })],
|
|
61
|
+
[/iPad.*OS ([\d_]+)/i, (m) => ({ name: 'iPadOS', version: m[1]!.replace(/_/g, '.') })],
|
|
62
|
+
[/Android ([\d.]+)/i, (m) => ({ name: 'Android', version: m[1]! })],
|
|
63
|
+
[/Windows NT ([\d.]+)/i, (m) => ({ name: WINDOWS_MAP[m[1]!] ?? 'Windows', version: m[1]! })],
|
|
64
|
+
[/Mac OS X ([\d_.]+)/i, (m) => ({ name: 'macOS', version: m[1]!.replace(/_/g, '.') })],
|
|
65
|
+
[/Linux/i, () => ({ name: 'Linux', version: 'unknown' })],
|
|
66
|
+
];
|
|
67
|
+
for (const [re, build] of osRules) {
|
|
68
|
+
const m = ua.match(re);
|
|
69
|
+
if (m) {
|
|
70
|
+
os = build(m);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Device
|
|
76
|
+
const isMobile = /Mobi|Android|iPhone|iPod/i.test(ua) && !/iPad/i.test(ua);
|
|
77
|
+
const isTablet = /iPad/i.test(ua) || (/Android/i.test(ua) && !/Mobile/i.test(ua));
|
|
78
|
+
const deviceType: 'mobile' | 'tablet' | 'desktop' = isMobile ? 'mobile' : isTablet ? 'tablet' : 'desktop';
|
|
79
|
+
|
|
80
|
+
let vendor = 'unknown';
|
|
81
|
+
if (/iPhone|iPad|Macintosh/i.test(ua)) vendor = 'Apple';
|
|
82
|
+
else if (/Samsung/i.test(ua)) vendor = 'Samsung';
|
|
83
|
+
else if (/Pixel|Nexus/i.test(ua)) vendor = 'Google';
|
|
84
|
+
else if (/Huawei/i.test(ua)) vendor = 'Huawei';
|
|
85
|
+
|
|
86
|
+
// Engine
|
|
87
|
+
let engine = { name: 'unknown', version: 'unknown' };
|
|
88
|
+
const engineRules: [RegExp, string][] = [
|
|
89
|
+
[/AppleWebKit\/([\d.]+)/i, 'WebKit'],
|
|
90
|
+
[/Gecko\/([\d.]+)/i, 'Gecko'],
|
|
91
|
+
[/Trident\/([\d.]+)/i, 'Trident'],
|
|
92
|
+
];
|
|
93
|
+
for (const [re, name] of engineRules) {
|
|
94
|
+
const m = ua.match(re);
|
|
95
|
+
if (m) {
|
|
96
|
+
// Chrome and Edge use Blink, a WebKit fork
|
|
97
|
+
const engineName = name === 'WebKit' && /Chrome|Edg/i.test(ua) ? 'Blink' : name;
|
|
98
|
+
engine = { name: engineName, version: m[1]! };
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { raw: ua, browser, os, device: { type: deviceType, vendor }, engine, bot };
|
|
104
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { MAX_FACTORIAL, MAX_GCD_VALUE, MAX_PRIME_LIST, MAX_STRING_LENGTH, ROMAN_VALUES } from '../../constants.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Checks whether two strings are anagrams of each other.
|
|
5
|
+
*
|
|
6
|
+
* @param value - First string
|
|
7
|
+
* @param value2 - Second string
|
|
8
|
+
* @returns True if the strings are anagrams, false otherwise
|
|
9
|
+
* @throws Error if either value is missing, not a string, or out of length bounds
|
|
10
|
+
*/
|
|
3
11
|
export function anagram(value: string, value2: string): boolean {
|
|
4
12
|
if (!value) throw new Error('First value is required');
|
|
5
13
|
if (!value2) throw new Error('Second value is required');
|
|
@@ -16,6 +24,13 @@ export function anagram(value: string, value2: string): boolean {
|
|
|
16
24
|
return value.split('').sort().join('') === value2.split('').sort().join('');
|
|
17
25
|
}
|
|
18
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Sorts a comma-separated list of numbers using the bubble sort algorithm.
|
|
29
|
+
*
|
|
30
|
+
* @param value - Comma-separated string of numbers (e.g. "3,1,2")
|
|
31
|
+
* @returns Array of numbers sorted in ascending order
|
|
32
|
+
* @throws Error if value is missing, non-numeric, or contains fewer than two elements
|
|
33
|
+
*/
|
|
19
34
|
export function bubblesort(value: string): number[] {
|
|
20
35
|
if (!value) throw new Error('A value is required');
|
|
21
36
|
|
|
@@ -36,6 +51,13 @@ export function bubblesort(value: string): number[] {
|
|
|
36
51
|
return arr;
|
|
37
52
|
}
|
|
38
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Computes the factorial of a non-negative integer.
|
|
56
|
+
*
|
|
57
|
+
* @param value - The integer to compute the factorial of
|
|
58
|
+
* @returns The factorial of the given number
|
|
59
|
+
* @throws Error if value is not a number, is negative, or exceeds the maximum allowed
|
|
60
|
+
*/
|
|
39
61
|
export function factorial(value: string | number): number {
|
|
40
62
|
const num = Number(value);
|
|
41
63
|
if (isNaN(num)) throw new Error('Value must be a number');
|
|
@@ -45,6 +67,13 @@ export function factorial(value: string | number): number {
|
|
|
45
67
|
return num <= 1 ? 1 : num * factorial(num - 1);
|
|
46
68
|
}
|
|
47
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Returns the first n numbers of the Fibonacci sequence.
|
|
72
|
+
*
|
|
73
|
+
* @param value - How many Fibonacci numbers to generate
|
|
74
|
+
* @returns Array of Fibonacci numbers starting from 0
|
|
75
|
+
* @throws Error if value is not a number, is negative, or exceeds 1000
|
|
76
|
+
*/
|
|
48
77
|
export function fibonacci(value: string | number): number[] {
|
|
49
78
|
const num = Number(value);
|
|
50
79
|
if (isNaN(num)) throw new Error('Value must be a number');
|
|
@@ -60,6 +89,14 @@ export function fibonacci(value: string | number): number[] {
|
|
|
60
89
|
return fib.slice(0, num);
|
|
61
90
|
}
|
|
62
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Computes the greatest common divisor of two positive integers using the Euclidean algorithm.
|
|
94
|
+
*
|
|
95
|
+
* @param value - First positive integer
|
|
96
|
+
* @param value2 - Second positive integer
|
|
97
|
+
* @returns The greatest common divisor
|
|
98
|
+
* @throws Error if either value is not a positive number or exceeds the maximum allowed
|
|
99
|
+
*/
|
|
63
100
|
export function gcd(value: string | number, value2: string | number): number {
|
|
64
101
|
let a = Number(value);
|
|
65
102
|
let b = Number(value2);
|
|
@@ -80,6 +117,13 @@ export function gcd(value: string | number, value2: string | number): number {
|
|
|
80
117
|
return a;
|
|
81
118
|
}
|
|
82
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Determines whether a given number is prime.
|
|
122
|
+
*
|
|
123
|
+
* @param value - The number to test
|
|
124
|
+
* @returns True if the number is prime, false otherwise
|
|
125
|
+
* @throws Error if value is not a positive number or exceeds the maximum allowed
|
|
126
|
+
*/
|
|
83
127
|
export function isprime(value: string | number): boolean {
|
|
84
128
|
const num = Number(value);
|
|
85
129
|
if (isNaN(num)) throw new Error('Value must be a number');
|
|
@@ -93,6 +137,13 @@ export function isprime(value: string | number): boolean {
|
|
|
93
137
|
return true;
|
|
94
138
|
}
|
|
95
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Checks whether a string reads the same forwards and backwards.
|
|
142
|
+
*
|
|
143
|
+
* @param value - The string to test
|
|
144
|
+
* @returns True if the string is a palindrome, false otherwise
|
|
145
|
+
* @throws Error if value is missing, not a string, or out of length bounds
|
|
146
|
+
*/
|
|
96
147
|
export function palindrome(value: string): boolean {
|
|
97
148
|
if (!value) throw new Error('A value is required');
|
|
98
149
|
|
|
@@ -104,6 +155,13 @@ export function palindrome(value: string): boolean {
|
|
|
104
155
|
return value === value.split('').reverse().join('');
|
|
105
156
|
}
|
|
106
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Returns the prime factorization of a given integer.
|
|
160
|
+
*
|
|
161
|
+
* @param value - An integer greater than 1
|
|
162
|
+
* @returns Array of prime factors in ascending order
|
|
163
|
+
* @throws Error if value is not a number, less than 2, or exceeds the maximum allowed
|
|
164
|
+
*/
|
|
107
165
|
export function primefactors(value: string | number): number[] {
|
|
108
166
|
let num = Number(value);
|
|
109
167
|
if (isNaN(num)) throw new Error('Value must be a number');
|
|
@@ -122,6 +180,13 @@ export function primefactors(value: string | number): number[] {
|
|
|
122
180
|
return factors;
|
|
123
181
|
}
|
|
124
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Returns all prime numbers up to and including the given limit.
|
|
185
|
+
*
|
|
186
|
+
* @param value - The upper bound (inclusive)
|
|
187
|
+
* @returns Array of prime numbers up to the given limit
|
|
188
|
+
* @throws Error if value is not a number, less than 2, or exceeds the maximum allowed
|
|
189
|
+
*/
|
|
125
190
|
export function primelist(value: string | number): number[] {
|
|
126
191
|
const num = Number(value);
|
|
127
192
|
if (isNaN(num)) throw new Error('Value must be a number');
|
|
@@ -146,6 +211,13 @@ export function primelist(value: string | number): number[] {
|
|
|
146
211
|
return primes;
|
|
147
212
|
}
|
|
148
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Reverses a string.
|
|
216
|
+
*
|
|
217
|
+
* @param value - The string to reverse
|
|
218
|
+
* @returns The reversed string
|
|
219
|
+
* @throws Error if value is missing or not a string
|
|
220
|
+
*/
|
|
149
221
|
export function reverse(value: string): string {
|
|
150
222
|
if (!value) throw new Error('A value is required');
|
|
151
223
|
|
|
@@ -154,6 +226,13 @@ export function reverse(value: string): string {
|
|
|
154
226
|
return value.split('').reverse().join('');
|
|
155
227
|
}
|
|
156
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Converts an integer to its Roman numeral representation, or a Roman numeral to an integer.
|
|
231
|
+
*
|
|
232
|
+
* @param value - An integer (1–3999) or a Roman numeral string
|
|
233
|
+
* @returns The converted value — a Roman numeral string if input was numeric, or an integer if input was a Roman numeral
|
|
234
|
+
* @throws Error if value is missing, out of range, or not a valid Roman numeral
|
|
235
|
+
*/
|
|
157
236
|
export function roman(value: string): number | string {
|
|
158
237
|
if (!value) throw new Error('A value is required');
|
|
159
238
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createCanvas } from 'canvas';
|
|
2
|
+
|
|
2
3
|
import { normalizeColor } from '../../utils/colors.js';
|
|
3
4
|
|
|
4
5
|
export interface CaptchaOptions {
|
|
@@ -36,6 +37,13 @@ function clamp(value: number | undefined, name: string, def: number, min: number
|
|
|
36
37
|
return Math.floor(value);
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Generates a CAPTCHA image with random or custom text and configurable noise.
|
|
42
|
+
*
|
|
43
|
+
* @param options - Captcha configuration options
|
|
44
|
+
* @returns Object containing the PNG buffer, content type, and the challenge text
|
|
45
|
+
* @throws Error if any option is out of the accepted range
|
|
46
|
+
*/
|
|
39
47
|
export default function captcha(options: CaptchaOptions): CaptchaResult {
|
|
40
48
|
const text = options.text || generateText(clamp(options.length, 'length', 6, 1, 20));
|
|
41
49
|
const height = clamp(options.height, 'height', 120, 50, 400);
|
package/src/modules/v4/chat.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { checkRateLimit } from '../../utils/helpers.js';
|
|
2
1
|
import { SESSION_TTL } from '../../constants.js';
|
|
3
|
-
import type {
|
|
2
|
+
import type { ChatMessage, ChatStorage } from '../../types/storage.js';
|
|
3
|
+
import { checkRateLimit } from '../../utils/helpers.js';
|
|
4
4
|
|
|
5
5
|
interface ChatParams {
|
|
6
6
|
username: string;
|
|
@@ -11,6 +11,14 @@ interface ChatParams {
|
|
|
11
11
|
storage: ChatStorage;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Handles real-time chat actions including sending, fetching, and clearing messages.
|
|
16
|
+
*
|
|
17
|
+
* @param action - The action to perform: "message", "private", "fetch", or "clear"
|
|
18
|
+
* @param params - Chat parameters including username, message, session, and shared storage
|
|
19
|
+
* @returns The result of the action — a message list, a sent confirmation, or a status message
|
|
20
|
+
* @throws Error if a required parameter is missing or the action is invalid
|
|
21
|
+
*/
|
|
14
22
|
export default function chat(action: string, params: ChatParams): ChatMessage[] | ChatMessage | { message: string } {
|
|
15
23
|
const storage = params.storage;
|
|
16
24
|
|
package/src/modules/v4/color.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hexToRgb,
|
|
1
|
+
import { hexToRgb, rgbToHex, rgbToHsl } from '../../utils/colors.js';
|
|
2
2
|
|
|
3
3
|
export interface ColorResult {
|
|
4
4
|
hex: string;
|
|
@@ -41,6 +41,13 @@ function rgbToCmyk(r: number, g: number, b: number): [number, number, number, nu
|
|
|
41
41
|
return [c * 100, m * 100, y * 100, k * 100];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Converts a hex color to all major color space representations, or generates a random color.
|
|
46
|
+
*
|
|
47
|
+
* @param hex - Optional hex color string (e.g. "#ff5733"); generates a random color if omitted
|
|
48
|
+
* @returns Color in hex, RGB, HSL, HSV, HWB, and CMYK formats
|
|
49
|
+
* @throws Error if the hex string is invalid
|
|
50
|
+
*/
|
|
44
51
|
export default function color(hex?: string): ColorResult {
|
|
45
52
|
let r: number, g: number, b: number;
|
|
46
53
|
|
|
@@ -97,6 +97,15 @@ const conversions: Record<string, Record<string, ConversionFn>> = {
|
|
|
97
97
|
knots: { 'km/h': (v) => v * 1.852, mph: (v) => v * 1.15078, 'm/s': (v) => v * 0.514444 },
|
|
98
98
|
};
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Converts a numeric value from one unit to another.
|
|
102
|
+
*
|
|
103
|
+
* @param value - The numeric value to convert
|
|
104
|
+
* @param from - The source unit (e.g. "km", "celsius", "kg")
|
|
105
|
+
* @param to - The target unit (e.g. "mi", "fahrenheit", "lb")
|
|
106
|
+
* @returns Object with source unit, target unit, original value, and converted result
|
|
107
|
+
* @throws Error if the conversion pair is unsupported, the value is NaN, or the temperature is below absolute zero
|
|
108
|
+
*/
|
|
100
109
|
export default function convert(
|
|
101
110
|
value: number,
|
|
102
111
|
from: string,
|
package/src/modules/v4/dice.ts
CHANGED
|
@@ -7,6 +7,13 @@ export interface DiceResult {
|
|
|
7
7
|
total: number;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Rolls dice using standard tabletop notation and returns individual results with a total.
|
|
12
|
+
*
|
|
13
|
+
* @param roll - Dice notation string (e.g. "2d6+3", "d20", "4d8-1")
|
|
14
|
+
* @returns Roll breakdown including count, sides, modifier, individual results, and total
|
|
15
|
+
* @throws Error if the notation is missing, invalid, or out of bounds
|
|
16
|
+
*/
|
|
10
17
|
export default function dice(roll: string): DiceResult {
|
|
11
18
|
if (!roll) throw new Error('A roll notation is required (e.g. 2d6+3)');
|
|
12
19
|
if (typeof roll !== 'string') throw new Error('Roll must be a string');
|
package/src/modules/v4/domain.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { genIP, random } from '../../utils/helpers.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Generates a randomized fictional domain profile with metadata.
|
|
5
|
+
*
|
|
6
|
+
* @returns Object containing domain name, IP addresses, DNS info, SEO score, and other domain attributes
|
|
7
|
+
*/
|
|
3
8
|
export default function domain(): Record<string, unknown> {
|
|
4
9
|
const subdomains = [
|
|
5
10
|
'fr.',
|
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 {
|