@20syldev/api 3.5.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +7 -0
- package/README.md +32 -33
- package/dist/app.js +36 -0
- package/dist/app.js.map +1 -0
- package/dist/config/env.js +73 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/versions.js +72 -0
- package/dist/config/versions.js.map +1 -0
- package/dist/constants.js +42 -0
- package/dist/constants.js.map +1 -0
- package/dist/middleware/cors.js +15 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/error.js +6 -0
- package/dist/middleware/error.js.map +1 -0
- package/dist/middleware/json.js +8 -0
- package/dist/middleware/json.js.map +1 -0
- package/dist/middleware/logger.js +26 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/ratelimit.js +58 -0
- package/dist/middleware/ratelimit.js.map +1 -0
- package/dist/middleware/version.js +30 -0
- package/dist/middleware/version.js.map +1 -0
- package/dist/modules/v3/algorithms.js +221 -0
- package/dist/modules/v3/algorithms.js.map +1 -0
- package/{modules → dist/modules}/v3/captcha.js +4 -13
- package/dist/modules/v3/captcha.js.map +1 -0
- package/dist/modules/v3/chat.js +103 -0
- package/dist/modules/v3/chat.js.map +1 -0
- package/{modules → dist/modules}/v3/color.js +12 -13
- package/dist/modules/v3/color.js.map +1 -0
- package/dist/modules/v3/convert.js +41 -0
- package/dist/modules/v3/convert.js.map +1 -0
- package/dist/modules/v3/domain.js +83 -0
- package/dist/modules/v3/domain.js.map +1 -0
- package/dist/modules/v3/hash.js +16 -0
- package/dist/modules/v3/hash.js.map +1 -0
- package/dist/modules/v3/hyperplanning.js +50 -0
- package/dist/modules/v3/hyperplanning.js.map +1 -0
- package/dist/modules/v3/levenshtein.js +38 -0
- package/dist/modules/v3/levenshtein.js.map +1 -0
- package/dist/modules/v3/personal.js +129 -0
- package/dist/modules/v3/personal.js.map +1 -0
- package/dist/modules/v3/qrcode.js +21 -0
- package/dist/modules/v3/qrcode.js.map +1 -0
- package/dist/modules/v3/tic_tac_toe.js +221 -0
- package/dist/modules/v3/tic_tac_toe.js.map +1 -0
- package/dist/modules/v3/time.js +88 -0
- package/dist/modules/v3/time.js.map +1 -0
- package/{modules → dist/modules}/v3/token.js +10 -11
- package/dist/modules/v3/token.js.map +1 -0
- package/dist/modules/v3/username.js +92 -0
- package/dist/modules/v3/username.js.map +1 -0
- package/dist/modules/v3/utils.js +70 -0
- package/dist/modules/v3/utils.js.map +1 -0
- package/dist/modules/v3.js +16 -0
- package/dist/modules/v3.js.map +1 -0
- package/dist/modules/v4/algorithms.js +200 -0
- package/dist/modules/v4/algorithms.js.map +1 -0
- package/dist/modules/v4/captcha.js +35 -0
- package/dist/modules/v4/captcha.js.map +1 -0
- package/dist/modules/v4/chat.js +92 -0
- package/dist/modules/v4/chat.js.map +1 -0
- package/dist/modules/v4/color.js +43 -0
- package/dist/modules/v4/color.js.map +1 -0
- package/dist/modules/v4/convert.js +33 -0
- package/dist/modules/v4/convert.js.map +1 -0
- package/dist/modules/v4/dice.js +30 -0
- package/dist/modules/v4/dice.js.map +1 -0
- package/dist/modules/v4/domain.js +78 -0
- package/dist/modules/v4/domain.js.map +1 -0
- package/dist/modules/v4/encode.js +165 -0
- package/dist/modules/v4/encode.js.map +1 -0
- package/dist/modules/v4/geo.js +41 -0
- package/dist/modules/v4/geo.js.map +1 -0
- package/dist/modules/v4/hash.js +9 -0
- package/dist/modules/v4/hash.js.map +1 -0
- package/dist/modules/v4/hyperplanning.js +42 -0
- package/dist/modules/v4/hyperplanning.js.map +1 -0
- package/dist/modules/v4/levenshtein.js +29 -0
- package/dist/modules/v4/levenshtein.js.map +1 -0
- package/dist/modules/v4/palette.js +89 -0
- package/dist/modules/v4/palette.js.map +1 -0
- package/dist/modules/v4/personal.js +125 -0
- package/dist/modules/v4/personal.js.map +1 -0
- package/dist/modules/v4/placeholder.js +96 -0
- package/dist/modules/v4/placeholder.js.map +1 -0
- package/dist/modules/v4/qrcode.js +12 -0
- package/dist/modules/v4/qrcode.js.map +1 -0
- package/dist/modules/v4/statistics.js +41 -0
- package/dist/modules/v4/statistics.js.map +1 -0
- package/dist/modules/v4/text.js +293 -0
- package/dist/modules/v4/text.js.map +1 -0
- package/dist/modules/v4/tic_tac_toe.js +206 -0
- package/dist/modules/v4/tic_tac_toe.js.map +1 -0
- package/dist/modules/v4/time.js +73 -0
- package/dist/modules/v4/time.js.map +1 -0
- package/dist/modules/v4/token.js +36 -0
- package/dist/modules/v4/token.js.map +1 -0
- package/dist/modules/v4/username.js +87 -0
- package/dist/modules/v4/username.js.map +1 -0
- package/dist/modules/v4/utils.js +26 -0
- package/dist/modules/v4/utils.js.map +1 -0
- package/dist/modules/v4/validate.js +54 -0
- package/dist/modules/v4/validate.js.map +1 -0
- package/dist/modules/v4.js +18 -0
- package/dist/modules/v4.js.map +1 -0
- package/dist/routes/delete.js +65 -0
- package/dist/routes/delete.js.map +1 -0
- package/dist/routes/get.js +441 -0
- package/dist/routes/get.js.map +1 -0
- package/dist/routes/index.js +50 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/patch.js +41 -0
- package/dist/routes/patch.js.map +1 -0
- package/dist/routes/post.js +185 -0
- package/dist/routes/post.js.map +1 -0
- package/dist/storage/index.js +13 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/types/storage.js +2 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/utils/response.js +10 -0
- package/dist/utils/response.js.map +1 -0
- package/dist/utils/version.js +3 -0
- package/dist/utils/version.js.map +1 -0
- package/docs/changelog.md +333 -0
- package/eslint.config.js +20 -0
- package/package.json +31 -15
- package/src/app.ts +48 -0
- package/src/config/env.ts +81 -0
- package/src/config/versions.ts +92 -0
- package/src/constants.ts +47 -0
- package/src/middleware/cors.ts +17 -0
- package/src/middleware/error.ts +7 -0
- package/src/middleware/json.ts +9 -0
- package/src/middleware/logger.ts +31 -0
- package/src/middleware/ratelimit.ts +66 -0
- package/src/middleware/version.ts +39 -0
- package/{modules → src/modules}/v3/algorithms.js +1 -1
- package/src/modules/v3/captcha.js +60 -0
- package/{modules → src/modules}/v3/chat.js +6 -4
- package/src/modules/v3/color.js +60 -0
- package/{modules → src/modules}/v3/convert.js +5 -5
- package/{modules → src/modules}/v3/domain.js +51 -4
- package/{modules → src/modules}/v3/hash.js +1 -1
- package/{modules → src/modules}/v3/hyperplanning.js +11 -10
- package/{modules → src/modules}/v3/levenshtein.js +3 -3
- package/{modules → src/modules}/v3/personal.js +20 -8
- package/{modules → src/modules}/v3/qrcode.js +1 -1
- package/{modules → src/modules}/v3/tic_tac_toe.js +24 -18
- package/{modules → src/modules}/v3/time.js +22 -11
- package/src/modules/v3/token.js +54 -0
- package/src/modules/v3/username.js +94 -0
- package/{modules → src/modules}/v3/utils.js +2 -2
- package/{modules → src/modules}/v3.js +1 -1
- package/src/modules/v4/algorithms.ts +197 -0
- package/src/modules/v4/captcha.ts +49 -0
- package/src/modules/v4/chat.ts +122 -0
- package/src/modules/v4/color.ts +64 -0
- package/src/modules/v4/convert.ts +38 -0
- package/src/modules/v4/dice.ts +39 -0
- package/src/modules/v4/domain.ts +81 -0
- package/src/modules/v4/encode.ts +170 -0
- package/src/modules/v4/geo.ts +53 -0
- package/src/modules/v4/hash.ts +9 -0
- package/src/modules/v4/hyperplanning.ts +58 -0
- package/src/modules/v4/levenshtein.ts +38 -0
- package/src/modules/v4/palette.ts +103 -0
- package/src/modules/v4/personal.ts +147 -0
- package/src/modules/v4/placeholder.ts +122 -0
- package/src/modules/v4/qrcode.ts +11 -0
- package/src/modules/v4/statistics.ts +55 -0
- package/src/modules/v4/text.ts +300 -0
- package/src/modules/v4/tic_tac_toe.ts +255 -0
- package/src/modules/v4/time.ts +92 -0
- package/src/modules/v4/token.ts +43 -0
- package/src/modules/v4/username.ts +89 -0
- package/src/modules/v4/utils.ts +38 -0
- package/src/modules/v4/validate.ts +55 -0
- package/src/modules/v4.ts +17 -0
- package/src/routes/delete.ts +72 -0
- package/src/routes/get.ts +508 -0
- package/src/routes/index.ts +58 -0
- package/src/routes/patch.ts +45 -0
- package/src/routes/post.ts +200 -0
- package/src/storage/index.ts +16 -0
- package/src/types/express.d.ts +17 -0
- package/src/types/storage.ts +48 -0
- package/src/utils/response.ts +11 -0
- package/src/utils/version.ts +5 -0
- package/tests/integration/api.test.ts +395 -0
- package/tests/tsconfig.json +3 -0
- package/tests/unit/algorithms.test.ts +120 -0
- package/tests/unit/color.test.ts +33 -0
- package/tests/unit/convert.test.ts +42 -0
- package/tests/unit/dice.test.ts +57 -0
- package/tests/unit/domain.test.ts +47 -0
- package/tests/unit/encode.test.ts +108 -0
- package/tests/unit/geo.test.ts +45 -0
- package/tests/unit/hash.test.ts +37 -0
- package/tests/unit/hyperplanning.test.ts +77 -0
- package/tests/unit/levenshtein.test.ts +34 -0
- package/tests/unit/palette.test.ts +53 -0
- package/tests/unit/personal.test.ts +65 -0
- package/tests/unit/statistics.test.ts +57 -0
- package/tests/unit/text.test.ts +107 -0
- package/tests/unit/time.test.ts +67 -0
- package/tests/unit/token.test.ts +61 -0
- package/tests/unit/username.test.ts +34 -0
- package/tests/unit/validate.test.ts +71 -0
- package/tsconfig.json +17 -0
- package/tsconfig.test.json +9 -0
- package/app.js +0 -865
- package/modules/v3/username.js +0 -32
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { MAX_STRING_LENGTH } from '../../constants.js';
|
|
2
|
+
|
|
3
|
+
const MORSE_TABLE: Record<string, string> = {
|
|
4
|
+
A: '.-',
|
|
5
|
+
B: '-...',
|
|
6
|
+
C: '-.-.',
|
|
7
|
+
D: '-..',
|
|
8
|
+
E: '.',
|
|
9
|
+
F: '..-.',
|
|
10
|
+
G: '--.',
|
|
11
|
+
H: '....',
|
|
12
|
+
I: '..',
|
|
13
|
+
J: '.---',
|
|
14
|
+
K: '-.-',
|
|
15
|
+
L: '.-..',
|
|
16
|
+
M: '--',
|
|
17
|
+
N: '-.',
|
|
18
|
+
O: '---',
|
|
19
|
+
P: '.--.',
|
|
20
|
+
Q: '--.-',
|
|
21
|
+
R: '.-.',
|
|
22
|
+
S: '...',
|
|
23
|
+
T: '-',
|
|
24
|
+
U: '..-',
|
|
25
|
+
V: '...-',
|
|
26
|
+
W: '.--',
|
|
27
|
+
X: '-..-',
|
|
28
|
+
Y: '-.--',
|
|
29
|
+
Z: '--..',
|
|
30
|
+
'0': '-----',
|
|
31
|
+
'1': '.----',
|
|
32
|
+
'2': '..---',
|
|
33
|
+
'3': '...--',
|
|
34
|
+
'4': '....-',
|
|
35
|
+
'5': '.....',
|
|
36
|
+
'6': '-....',
|
|
37
|
+
'7': '--...',
|
|
38
|
+
'8': '---..',
|
|
39
|
+
'9': '----.',
|
|
40
|
+
'.': '.-.-.-',
|
|
41
|
+
',': '--..--',
|
|
42
|
+
'?': '..--..',
|
|
43
|
+
"'": '.----.',
|
|
44
|
+
'!': '-.-.--',
|
|
45
|
+
'/': '-..-.',
|
|
46
|
+
'(': '-.--.',
|
|
47
|
+
')': '-.--.-',
|
|
48
|
+
'&': '.-...',
|
|
49
|
+
':': '---...',
|
|
50
|
+
';': '-.-.-.',
|
|
51
|
+
'=': '-...-',
|
|
52
|
+
'+': '.-.-.',
|
|
53
|
+
'-': '-....-',
|
|
54
|
+
_: '..--.-',
|
|
55
|
+
'"': '.-..-.',
|
|
56
|
+
$: '...-..-',
|
|
57
|
+
'@': '.--.-.',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const MORSE_REVERSE: Record<string, string> = Object.fromEntries(Object.entries(MORSE_TABLE).map(([k, v]) => [v, k]));
|
|
61
|
+
|
|
62
|
+
function checkText(value: string): void {
|
|
63
|
+
if (!value) throw new Error('A value is required');
|
|
64
|
+
if (typeof value !== 'string') throw new Error('Value must be a string');
|
|
65
|
+
if (value.length > MAX_STRING_LENGTH)
|
|
66
|
+
throw new Error(`Value must be less than ${MAX_STRING_LENGTH} characters long`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function base64encode(value: string): string {
|
|
70
|
+
checkText(value);
|
|
71
|
+
return Buffer.from(value, 'utf-8').toString('base64');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function base64decode(value: string): string {
|
|
75
|
+
checkText(value);
|
|
76
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value)) throw new Error('Invalid Base64 string');
|
|
77
|
+
return Buffer.from(value, 'base64').toString('utf-8');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function urlencode(value: string): string {
|
|
81
|
+
checkText(value);
|
|
82
|
+
return encodeURIComponent(value);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function urldecode(value: string): string {
|
|
86
|
+
checkText(value);
|
|
87
|
+
try {
|
|
88
|
+
return decodeURIComponent(value);
|
|
89
|
+
} catch {
|
|
90
|
+
throw new Error('Invalid URL-encoded string');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function morse(value: string): string {
|
|
95
|
+
checkText(value);
|
|
96
|
+
return value
|
|
97
|
+
.toUpperCase()
|
|
98
|
+
.split(' ')
|
|
99
|
+
.map((word) =>
|
|
100
|
+
word
|
|
101
|
+
.split('')
|
|
102
|
+
.map((c) => {
|
|
103
|
+
if (c === '\n' || c === '\t') return '';
|
|
104
|
+
const code = MORSE_TABLE[c];
|
|
105
|
+
if (!code) throw new Error(`Unsupported character in Morse code: '${c}'`);
|
|
106
|
+
return code;
|
|
107
|
+
})
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.join(' '),
|
|
110
|
+
)
|
|
111
|
+
.join(' / ');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function unmorse(value: string): string {
|
|
115
|
+
checkText(value);
|
|
116
|
+
return value
|
|
117
|
+
.split(' / ')
|
|
118
|
+
.map((word) =>
|
|
119
|
+
word
|
|
120
|
+
.split(' ')
|
|
121
|
+
.map((code) => {
|
|
122
|
+
if (!code) return '';
|
|
123
|
+
const char = MORSE_REVERSE[code];
|
|
124
|
+
if (!char) throw new Error(`Unsupported Morse sequence: '${code}'`);
|
|
125
|
+
return char;
|
|
126
|
+
})
|
|
127
|
+
.join(''),
|
|
128
|
+
)
|
|
129
|
+
.join(' ');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function rot13(value: string): string {
|
|
133
|
+
checkText(value);
|
|
134
|
+
return value.replace(/[a-zA-Z]/g, (c) => {
|
|
135
|
+
const base = c <= 'Z' ? 65 : 97;
|
|
136
|
+
return String.fromCharCode(((c.charCodeAt(0) - base + 13) % 26) + base);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function caesar(value: string, shift: string): string {
|
|
141
|
+
checkText(value);
|
|
142
|
+
const n = Number(shift);
|
|
143
|
+
if (isNaN(n)) throw new Error('Shift must be a number');
|
|
144
|
+
const s = ((n % 26) + 26) % 26;
|
|
145
|
+
return value.replace(/[a-zA-Z]/g, (c) => {
|
|
146
|
+
const base = c <= 'Z' ? 65 : 97;
|
|
147
|
+
return String.fromCharCode(((c.charCodeAt(0) - base + s) % 26) + base);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function binary(value: string): string {
|
|
152
|
+
checkText(value);
|
|
153
|
+
return value
|
|
154
|
+
.split('')
|
|
155
|
+
.map((c) => c.charCodeAt(0).toString(2).padStart(8, '0'))
|
|
156
|
+
.join(' ');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function unbinary(value: string): string {
|
|
160
|
+
checkText(value);
|
|
161
|
+
if (!/^[01\s]+$/.test(value)) throw new Error('Invalid binary string');
|
|
162
|
+
return value
|
|
163
|
+
.trim()
|
|
164
|
+
.split(/\s+/)
|
|
165
|
+
.map((b) => {
|
|
166
|
+
if (b.length !== 8) throw new Error('Each binary group must contain 8 bits');
|
|
167
|
+
return String.fromCharCode(parseInt(b, 2));
|
|
168
|
+
})
|
|
169
|
+
.join('');
|
|
170
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface GeoResult {
|
|
2
|
+
distance: { km: number; miles: number; nauticalMiles: number };
|
|
3
|
+
bearing: { degrees: number; cardinal: string };
|
|
4
|
+
from: { lat: number; lon: number };
|
|
5
|
+
to: { lat: number; lon: number };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const EARTH_RADIUS_KM = 6371;
|
|
9
|
+
const CARDINALS = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];
|
|
10
|
+
|
|
11
|
+
function toRad(deg: number): number {
|
|
12
|
+
return (deg * Math.PI) / 180;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function toDeg(rad: number): number {
|
|
16
|
+
return (rad * 180) / Math.PI;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseCoord(value: string, name: string, min: number, max: number): number {
|
|
20
|
+
const n = Number(value);
|
|
21
|
+
if (isNaN(n)) throw new Error(`${name} must be a number`);
|
|
22
|
+
if (n < min || n > max) throw new Error(`${name} must be between ${min} and ${max}`);
|
|
23
|
+
return n;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default function geo(lat1: string, lon1: string, lat2: string, lon2: string): GeoResult {
|
|
27
|
+
const a = parseCoord(lat1, 'lat1', -90, 90);
|
|
28
|
+
const b = parseCoord(lon1, 'lon1', -180, 180);
|
|
29
|
+
const c = parseCoord(lat2, 'lat2', -90, 90);
|
|
30
|
+
const d = parseCoord(lon2, 'lon2', -180, 180);
|
|
31
|
+
|
|
32
|
+
const dLat = toRad(c - a);
|
|
33
|
+
const dLon = toRad(d - b);
|
|
34
|
+
const sa = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(a)) * Math.cos(toRad(c)) * Math.sin(dLon / 2) ** 2;
|
|
35
|
+
const distance = 2 * EARTH_RADIUS_KM * Math.asin(Math.sqrt(sa));
|
|
36
|
+
|
|
37
|
+
const y = Math.sin(toRad(d - b)) * Math.cos(toRad(c));
|
|
38
|
+
const x =
|
|
39
|
+
Math.cos(toRad(a)) * Math.sin(toRad(c)) - Math.sin(toRad(a)) * Math.cos(toRad(c)) * Math.cos(toRad(d - b));
|
|
40
|
+
const bearing = (toDeg(Math.atan2(y, x)) + 360) % 360;
|
|
41
|
+
const cardinal = CARDINALS[Math.round(bearing / 22.5) % 16]!;
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
distance: {
|
|
45
|
+
km: +distance.toFixed(3),
|
|
46
|
+
miles: +(distance * 0.621371).toFixed(3),
|
|
47
|
+
nauticalMiles: +(distance * 0.539957).toFixed(3),
|
|
48
|
+
},
|
|
49
|
+
bearing: { degrees: +bearing.toFixed(2), cardinal },
|
|
50
|
+
from: { lat: a, lon: b },
|
|
51
|
+
to: { lat: c, lon: d },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getHashes, createHash } from 'crypto';
|
|
2
|
+
|
|
3
|
+
export default function hash(text: string, method: string): { method: string; hash: string } | { error: string } {
|
|
4
|
+
const methods = getHashes();
|
|
5
|
+
if (!methods.includes(method)) return { error: `Unsupported method. Use one of: ${methods.join(', ')}` };
|
|
6
|
+
|
|
7
|
+
const result = createHash(method).update(text).digest('hex');
|
|
8
|
+
return { method, hash: result };
|
|
9
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { formatDate } from './utils.js';
|
|
2
|
+
import ical from 'ical.js';
|
|
3
|
+
|
|
4
|
+
interface CalendarEvent {
|
|
5
|
+
summary: string[] | string;
|
|
6
|
+
subject?: string;
|
|
7
|
+
teacher?: string;
|
|
8
|
+
classes?: string[];
|
|
9
|
+
type?: string;
|
|
10
|
+
start: string;
|
|
11
|
+
end: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default async function hyperplanning(url: string, detail?: string): Promise<CalendarEvent[]> {
|
|
15
|
+
const response = await fetch(url);
|
|
16
|
+
|
|
17
|
+
if (!response.ok || !(response.headers.get('content-type') || '').includes('text/calendar')) {
|
|
18
|
+
throw new Error('Invalid ICS file format.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const events: CalendarEvent[] = new ical.Component(ical.parse(await response.text()))
|
|
22
|
+
.getAllSubcomponents('vevent')
|
|
23
|
+
.map((e: ical.Component) => {
|
|
24
|
+
const evt = new ical.Event(e);
|
|
25
|
+
const summary = (evt.summary || '').split(' ').filter((part: string) => part !== '-');
|
|
26
|
+
const start = formatDate(evt.startDate.toJSDate());
|
|
27
|
+
const end = formatDate(evt.endDate.toJSDate());
|
|
28
|
+
|
|
29
|
+
if (detail === 'full') {
|
|
30
|
+
const desc = (evt.description || '').split('\n').map((l: string) => l.trim());
|
|
31
|
+
const extract = (p: string) => (desc.find((l: string) => l.startsWith(p)) || '').replace(p, '').trim();
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
summary,
|
|
35
|
+
subject: extract('Matière :'),
|
|
36
|
+
teacher: extract('Enseignant :'),
|
|
37
|
+
classes: extract('Promotions :')
|
|
38
|
+
.split(', ')
|
|
39
|
+
.map((c: string) => c.trim()),
|
|
40
|
+
type: extract('Salle :') || undefined,
|
|
41
|
+
start,
|
|
42
|
+
end,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (detail === 'list') return { summary, start, end };
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
summary: evt.summary || '',
|
|
50
|
+
start,
|
|
51
|
+
end,
|
|
52
|
+
};
|
|
53
|
+
})
|
|
54
|
+
.sort((a: CalendarEvent, b: CalendarEvent) => new Date(a.start).getTime() - new Date(b.start).getTime())
|
|
55
|
+
.filter((e: CalendarEvent) => new Date(e.end) >= new Date());
|
|
56
|
+
|
|
57
|
+
return events;
|
|
58
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MAX_LEVENSHTEIN_LENGTH } from '../../constants.js';
|
|
2
|
+
|
|
3
|
+
export default function levenshtein(str1: string, str2: string): { str1: string; str2: string; distance: number } {
|
|
4
|
+
if (!str1 || typeof str1 !== 'string') {
|
|
5
|
+
throw new Error('Please provide a valid first string');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (!str2 || typeof str2 !== 'string') {
|
|
9
|
+
throw new Error('Please provide a valid second string');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (str1.length > MAX_LEVENSHTEIN_LENGTH) {
|
|
13
|
+
throw new Error('First string exceeds 1000 characters');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (str2.length > MAX_LEVENSHTEIN_LENGTH) {
|
|
17
|
+
throw new Error('Second string exceeds 1000 characters');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const m: number[][] = Array.from({ length: str1.length + 1 }, (_, i) => [i]);
|
|
21
|
+
for (let j = 0; j <= str2.length; j++) m[0]![j] = j;
|
|
22
|
+
|
|
23
|
+
for (let i = 1; i <= str1.length; i++) {
|
|
24
|
+
for (let j = 1; j <= str2.length; j++) {
|
|
25
|
+
m[i]![j] = Math.min(
|
|
26
|
+
m[i - 1]![j]! + 1,
|
|
27
|
+
m[i]![j - 1]! + 1,
|
|
28
|
+
m[i - 1]![j - 1]! + (str1[i - 1] !== str2[j - 1] ? 1 : 0),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
str1,
|
|
35
|
+
str2,
|
|
36
|
+
distance: m[str1.length]![str2.length]!,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export interface PaletteColor {
|
|
2
|
+
hex: string;
|
|
3
|
+
rgb: string;
|
|
4
|
+
hsl: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface PaletteResult {
|
|
8
|
+
base: PaletteColor;
|
|
9
|
+
type: string;
|
|
10
|
+
colors: PaletteColor[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function hexToRgb(hex: string): [number, number, number] {
|
|
14
|
+
const clean = hex.replace('#', '');
|
|
15
|
+
if (!/^[0-9a-fA-F]{6}$/.test(clean)) throw new Error('Invalid HEX color (use #RRGGBB)');
|
|
16
|
+
return [parseInt(clean.slice(0, 2), 16), parseInt(clean.slice(2, 4), 16), parseInt(clean.slice(4, 6), 16)];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
|
|
20
|
+
const r1 = r / 255,
|
|
21
|
+
g1 = g / 255,
|
|
22
|
+
b1 = b / 255;
|
|
23
|
+
const max = Math.max(r1, g1, b1),
|
|
24
|
+
min = Math.min(r1, g1, b1);
|
|
25
|
+
const l = (max + min) / 2;
|
|
26
|
+
|
|
27
|
+
if (max === min) return [0, 0, l];
|
|
28
|
+
|
|
29
|
+
const d = max - min;
|
|
30
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
31
|
+
let h = 0;
|
|
32
|
+
if (max === r1) h = (g1 - b1) / d + (g1 < b1 ? 6 : 0);
|
|
33
|
+
else if (max === g1) h = (b1 - r1) / d + 2;
|
|
34
|
+
else h = (r1 - g1) / d + 4;
|
|
35
|
+
|
|
36
|
+
return [(h * 60 + 360) % 360, s, l];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function hslToRgb(h: number, s: number, l: number): [number, number, number] {
|
|
40
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
41
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
42
|
+
const m = l - c / 2;
|
|
43
|
+
let r1 = 0,
|
|
44
|
+
g1 = 0,
|
|
45
|
+
b1 = 0;
|
|
46
|
+
if (h < 60) [r1, g1, b1] = [c, x, 0];
|
|
47
|
+
else if (h < 120) [r1, g1, b1] = [x, c, 0];
|
|
48
|
+
else if (h < 180) [r1, g1, b1] = [0, c, x];
|
|
49
|
+
else if (h < 240) [r1, g1, b1] = [0, x, c];
|
|
50
|
+
else if (h < 300) [r1, g1, b1] = [x, 0, c];
|
|
51
|
+
else [r1, g1, b1] = [c, 0, x];
|
|
52
|
+
return [Math.round((r1 + m) * 255), Math.round((g1 + m) * 255), Math.round((b1 + m) * 255)];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function format(r: number, g: number, b: number): PaletteColor {
|
|
56
|
+
const [h, s, l] = rgbToHsl(r, g, b);
|
|
57
|
+
return {
|
|
58
|
+
hex: `#${[r, g, b].map((x) => x.toString(16).padStart(2, '0')).join('')}`,
|
|
59
|
+
rgb: `rgb(${r}, ${g}, ${b})`,
|
|
60
|
+
hsl: `hsl(${h.toFixed(1)}, ${(s * 100).toFixed(1)}%, ${(l * 100).toFixed(1)}%)`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function fromHueShifts(base: [number, number, number], shifts: number[]): PaletteColor[] {
|
|
65
|
+
const [h, s, l] = base;
|
|
66
|
+
return shifts.map((shift) => {
|
|
67
|
+
const newH = (h + shift + 360) % 360;
|
|
68
|
+
const [r, g, b] = hslToRgb(newH, s, l);
|
|
69
|
+
return format(r, g, b);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default function palette(color: string, type: string): PaletteResult {
|
|
74
|
+
if (!color) throw new Error('A base color is required');
|
|
75
|
+
if (!type) throw new Error('A palette type is required');
|
|
76
|
+
|
|
77
|
+
const [r, g, b] = hexToRgb(color);
|
|
78
|
+
const base = format(r, g, b);
|
|
79
|
+
const hsl = rgbToHsl(r, g, b);
|
|
80
|
+
|
|
81
|
+
let colors: PaletteColor[];
|
|
82
|
+
switch (type) {
|
|
83
|
+
case 'complementary':
|
|
84
|
+
colors = fromHueShifts(hsl, [0, 180]);
|
|
85
|
+
break;
|
|
86
|
+
case 'triadic':
|
|
87
|
+
colors = fromHueShifts(hsl, [0, 120, 240]);
|
|
88
|
+
break;
|
|
89
|
+
case 'analogous':
|
|
90
|
+
colors = fromHueShifts(hsl, [-60, -30, 0, 30, 60]);
|
|
91
|
+
break;
|
|
92
|
+
case 'tetradic':
|
|
93
|
+
colors = fromHueShifts(hsl, [0, 90, 180, 270]);
|
|
94
|
+
break;
|
|
95
|
+
case 'split-complementary':
|
|
96
|
+
colors = fromHueShifts(hsl, [0, 150, 210]);
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
throw new Error('Type must be one of: complementary, triadic, analogous, tetradic, split-complementary');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { base, type, colors };
|
|
103
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { random, randomNumber } from './utils.js';
|
|
2
|
+
|
|
3
|
+
interface Person {
|
|
4
|
+
name: string;
|
|
5
|
+
social: string;
|
|
6
|
+
email: string;
|
|
7
|
+
country: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface CountryInfo {
|
|
11
|
+
tel: string;
|
|
12
|
+
code: string;
|
|
13
|
+
lang: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function personal(): Record<string, unknown> {
|
|
17
|
+
const people: Person[] = [
|
|
18
|
+
{ name: 'John Doe', social: 'john_doe', email: 'john@example.com', country: 'US' },
|
|
19
|
+
{ name: 'Jane Martin', social: 'jane_martin', email: 'jane@example.com', country: 'FR' },
|
|
20
|
+
{ name: 'Michael Johnson', social: 'mike_johnson', email: 'michael@example.com', country: 'UK' },
|
|
21
|
+
{ name: 'Emily Davis', social: 'emily_davis', email: 'emily@example.com', country: 'ES' },
|
|
22
|
+
{ name: 'Alexis Barbos', social: 'alexis_barbos', email: 'alexis@example.com', country: 'DE' },
|
|
23
|
+
{ name: 'Sarah Williams', social: 'sarah_williams', email: 'sarah@example.com', country: 'IT' },
|
|
24
|
+
{ name: 'Daniel Brown', social: 'daniel_brown', email: 'daniel@example.com', country: 'JP' },
|
|
25
|
+
{ name: 'Sophia Wilson', social: 'sophia_wilson', email: 'sophia@example.com', country: 'BR' },
|
|
26
|
+
{ name: 'James Taylor', social: 'james_taylor', email: 'james@example.com', country: 'CA' },
|
|
27
|
+
{ name: 'Olivia Thomas', social: 'olivia_thomas', email: 'olivia@example.com', country: 'AU' },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const countries: Record<string, CountryInfo> = {
|
|
31
|
+
US: { tel: '123-456-7890', code: '1', lang: 'English' },
|
|
32
|
+
FR: { tel: '06 78 90 12 34', code: '33', lang: 'French' },
|
|
33
|
+
UK: { tel: '7911 123456', code: '44', lang: 'English' },
|
|
34
|
+
ES: { tel: '678 901 234', code: '34', lang: 'Spanish' },
|
|
35
|
+
DE: { tel: '163 555 1584', code: '49', lang: 'German' },
|
|
36
|
+
IT: { tel: '345 678 9012', code: '39', lang: 'Italian' },
|
|
37
|
+
JP: { tel: '080-1234-5678', code: '81', lang: 'Japanese' },
|
|
38
|
+
BR: { tel: '(11) 98765-4321', code: '55', lang: 'Portuguese' },
|
|
39
|
+
CA: { tel: '416-123-4567', code: '1', lang: 'English' },
|
|
40
|
+
AU: { tel: '0412 345 678', code: '61', lang: 'English' },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const jobs = ['Writer', 'Artist', 'Musician', 'Explorer', 'Scientist', 'Engineer', 'Athlete', 'Doctor'];
|
|
44
|
+
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
|
+
const card = Array.from({ length: 4 }, () => randomNumber(1000, 9999)).join(' ');
|
|
61
|
+
const cvc = randomNumber(100, 999);
|
|
62
|
+
const expiration = `${String(randomNumber(1, 12)).padStart(2, '0')}/${(new Date().getFullYear() + randomNumber(0, 3)).toString().slice(-2)}`;
|
|
63
|
+
|
|
64
|
+
const person = random(people);
|
|
65
|
+
const social = person.social;
|
|
66
|
+
const country = person.country;
|
|
67
|
+
const countryInfo = countries[country]!;
|
|
68
|
+
const phone = countryInfo.tel;
|
|
69
|
+
const lang = countryInfo.lang;
|
|
70
|
+
|
|
71
|
+
const age = randomNumber(18, 67);
|
|
72
|
+
const birthday = new Date(Date.now() - randomNumber(18, 67) * 365.25 * 24 * 60 * 60 * 1000).toISOString();
|
|
73
|
+
|
|
74
|
+
const emergencyContacts: { name: string; relationship: string; phone: string }[] = [];
|
|
75
|
+
const yearIncome = randomNumber(20000, 100000);
|
|
76
|
+
const subscriptions: string[] = [];
|
|
77
|
+
const pets: string[] = [];
|
|
78
|
+
const vehicles: string[] = [];
|
|
79
|
+
let civilStatus = 'Single';
|
|
80
|
+
let children = 0;
|
|
81
|
+
|
|
82
|
+
if (age >= 21 && Math.random() > 0.7) civilStatus = 'Married';
|
|
83
|
+
if (civilStatus === 'Married' && age >= 25) children = randomNumber(0, 3);
|
|
84
|
+
|
|
85
|
+
while (emergencyContacts.length < randomNumber(1, 3)) {
|
|
86
|
+
let emergencyContact = random(people);
|
|
87
|
+
while (
|
|
88
|
+
emergencyContact.email === person.email ||
|
|
89
|
+
emergencyContacts.some((e) => e.name === emergencyContact.name)
|
|
90
|
+
) {
|
|
91
|
+
emergencyContact = random(people);
|
|
92
|
+
}
|
|
93
|
+
const emergencyPhone = `${randomNumber(100, 999)}-${randomNumber(100, 999)}-${randomNumber(1000, 9999)}`;
|
|
94
|
+
emergencyContacts.push({
|
|
95
|
+
name: emergencyContact.name,
|
|
96
|
+
relationship: random(['Spouse', 'Parent', 'Sibling', 'Friend']),
|
|
97
|
+
phone: `+${countryInfo.code} ${emergencyPhone}`,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
while (subscriptions.length < randomNumber(1, 3)) {
|
|
102
|
+
const subscription = random(['Netflix', 'Spotify', 'Amazon Prime', 'Disney+', 'Hulu']);
|
|
103
|
+
if (!subscriptions.includes(subscription)) subscriptions.push(subscription);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
while (pets.length < randomNumber(1, 3)) {
|
|
107
|
+
const pet = random(['Dog', 'Cat', 'Fish', 'Bird', 'None']);
|
|
108
|
+
if (!pets.includes(pet)) pets.push(pet);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
while (vehicles.length < randomNumber(1, 3)) {
|
|
112
|
+
const vehicle = random(['Car', 'Bike', 'Motorcycle', 'Bus', 'None']);
|
|
113
|
+
if (!vehicles.includes(vehicle)) vehicles.push(vehicle);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
name: person.name,
|
|
118
|
+
email: person.email,
|
|
119
|
+
localisation: country,
|
|
120
|
+
phone: `+${countryInfo.code} ${phone}`,
|
|
121
|
+
job: random(jobs),
|
|
122
|
+
hobbies: random(hobbies),
|
|
123
|
+
language: lang,
|
|
124
|
+
card,
|
|
125
|
+
cvc,
|
|
126
|
+
expiration,
|
|
127
|
+
address: `${randomNumber(1, 9999)} ${random(streets)}, ${random(cities)}`,
|
|
128
|
+
birthday,
|
|
129
|
+
civil_status: civilStatus,
|
|
130
|
+
children,
|
|
131
|
+
vehicle: vehicles,
|
|
132
|
+
social_profiles: {
|
|
133
|
+
twitter: `@${social}`,
|
|
134
|
+
facebook: `facebook.com/${social}`,
|
|
135
|
+
linkedin: `linkedin.com/in/${social}`,
|
|
136
|
+
instagram: `instagram.com/${social}`,
|
|
137
|
+
},
|
|
138
|
+
year_income: `${yearIncome} USD/year`,
|
|
139
|
+
month_income: `${(yearIncome / 12).toFixed(2)} USD/month`,
|
|
140
|
+
education: random(['High School', "Bachelor's", "Master's", 'PhD']),
|
|
141
|
+
work_experience: `${randomNumber(0, 20)} years`,
|
|
142
|
+
health_status: random(['Healthy', 'Minor Issues', 'Chronic Conditions']),
|
|
143
|
+
emergency_contacts: emergencyContacts,
|
|
144
|
+
subscriptions,
|
|
145
|
+
pets,
|
|
146
|
+
};
|
|
147
|
+
}
|