@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/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<a href="https://api.sylvain.sh"><img src="https://api.sylvain.sh/favicon.ico" alt="Logo" width="25%" height="auto"/></a>
|
|
3
3
|
|
|
4
4
|
# API Personnelle
|
|
5
|
-
[](https://github.com/20syldev/api/releases/latest)
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -32,10 +32,10 @@ Pour démarrer un serveur local avec tous les endpoints :
|
|
|
32
32
|
$ npm run build && npm start
|
|
33
33
|
```
|
|
34
34
|
```console
|
|
35
|
-
> @20syldev/api@4.
|
|
35
|
+
> @20syldev/api@4.6.0 build
|
|
36
36
|
> tsc
|
|
37
37
|
|
|
38
|
-
> @20syldev/api@4.
|
|
38
|
+
> @20syldev/api@4.6.0 start
|
|
39
39
|
> node dist/app.js
|
|
40
40
|
|
|
41
41
|
API is running on
|
|
@@ -88,6 +88,7 @@ L'API v4 expose plusieurs modules que vous pouvez importer :
|
|
|
88
88
|
|
|
89
89
|
```js
|
|
90
90
|
import {
|
|
91
|
+
agent, // Analyse de User-Agent (navigateur, OS, appareil)
|
|
91
92
|
algorithms, // Fonctions algorithmiques diverses
|
|
92
93
|
captcha, // Génération d'images captcha
|
|
93
94
|
chat, // Système de chat temporaire
|
|
@@ -96,15 +97,19 @@ import {
|
|
|
96
97
|
dice, // Lanceur de dés RPG
|
|
97
98
|
domain, // Informations de domaine aléatoires
|
|
98
99
|
encode, // Encodage / décodage (base64, morse, rot13, caesar, binaire)
|
|
100
|
+
geo, // Informations de géolocalisation
|
|
99
101
|
hash, // Hachage de texte
|
|
100
102
|
hyperplanning, // Analyse de calendriers
|
|
103
|
+
ip, // Analyse d'adresses IPv4/IPv6
|
|
101
104
|
levenshtein, // Distance entre chaînes
|
|
105
|
+
palette, // Génération de palettes de couleurs
|
|
102
106
|
personal, // Informations personnelles aléatoires
|
|
107
|
+
placeholder, // Génération d'images placeholder
|
|
103
108
|
qrcode, // Génération de QR codes
|
|
104
109
|
statistics, // Statistiques descriptives
|
|
110
|
+
text, // Utilitaires texte (slug, stats, lorem, nombre en lettres)
|
|
105
111
|
tic_tac_toe, // Jeu de morpion
|
|
106
112
|
time, // Informations temporelles
|
|
107
|
-
text, // Utilitaires texte (slug, stats, lorem, nombre en lettres)
|
|
108
113
|
token, // Génération de jetons sécurisés
|
|
109
114
|
username, // Génération de noms d'utilisateur
|
|
110
115
|
validate // Validation (Luhn, IBAN, email)
|
package/eslint.config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import eslint from '@eslint/js';
|
|
2
|
+
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
|
2
3
|
import tseslint from 'typescript-eslint';
|
|
3
4
|
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
4
5
|
|
|
@@ -7,7 +8,10 @@ export default tseslint.config(
|
|
|
7
8
|
...tseslint.configs.strict,
|
|
8
9
|
eslintConfigPrettier,
|
|
9
10
|
{
|
|
11
|
+
plugins: { 'simple-import-sort': simpleImportSort },
|
|
10
12
|
rules: {
|
|
13
|
+
'simple-import-sort/imports': 'error',
|
|
14
|
+
'simple-import-sort/exports': 'error',
|
|
11
15
|
'@typescript-eslint/no-explicit-any': 'warn',
|
|
12
16
|
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
13
17
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.
|
|
2
|
+
"version": "4.6.0",
|
|
3
3
|
"name": "@20syldev/api",
|
|
4
4
|
"description": "Node.js API with multiple features. Check the documentation at https://docs.sylvain.sh",
|
|
5
5
|
"main": "dist/app.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"ical.js": "^2.1.0",
|
|
33
33
|
"qrcode": "^1.5.4",
|
|
34
34
|
"random": "^5.3.0",
|
|
35
|
-
"uuid": "^
|
|
35
|
+
"uuid": "^14.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@eslint/js": "^9.24.0",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@types/uuid": "^10.0.0",
|
|
44
44
|
"eslint": "^9.24.0",
|
|
45
45
|
"eslint-config-prettier": "^10.1.1",
|
|
46
|
+
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
46
47
|
"npm-check-updates": "^18.0.1",
|
|
47
48
|
"prettier": "^3.5.3",
|
|
48
49
|
"tsx": "^4.19.0",
|
package/src/app.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
+
|
|
2
3
|
import { env } from './config/env.js';
|
|
3
4
|
import { setupCors } from './middleware/cors.js';
|
|
5
|
+
import { errorHandler } from './middleware/error.js';
|
|
4
6
|
import { jsonResponseMiddleware } from './middleware/json.js';
|
|
5
|
-
import { rateLimitMiddleware } from './middleware/ratelimit.js';
|
|
6
7
|
import { loggerMiddleware } from './middleware/logger.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
8
|
+
import { rateLimitMiddleware } from './middleware/ratelimit.js';
|
|
9
|
+
import { endpointCheckMiddleware, versionCheckMiddleware } from './middleware/version.js';
|
|
10
|
+
import deleteRoutes from './routes/delete.js';
|
|
10
11
|
import getRoutes from './routes/get.js';
|
|
11
|
-
import
|
|
12
|
+
import indexRoutes from './routes/index.js';
|
|
12
13
|
import patchRoutes from './routes/patch.js';
|
|
13
|
-
import
|
|
14
|
+
import postRoutes from './routes/post.js';
|
|
14
15
|
|
|
15
16
|
const app = express();
|
|
16
17
|
|
package/src/config/env.ts
CHANGED
package/src/config/versions.ts
CHANGED
|
@@ -78,6 +78,7 @@ const v3 = {
|
|
|
78
78
|
|
|
79
79
|
const v4 = {
|
|
80
80
|
get: merge(v3.get, [
|
|
81
|
+
{ name: 'agent', path: '/agent(&ua={string})' },
|
|
81
82
|
{
|
|
82
83
|
name: 'captcha',
|
|
83
84
|
path: '/captcha(&text={text}&length={n}&width={px}&height={px}&noise={low|medium|high}&bg={hex}&color={hex})',
|
|
@@ -87,15 +88,17 @@ const v4 = {
|
|
|
87
88
|
{ name: 'dice', path: '/dice?roll={NdX+M}' },
|
|
88
89
|
{ name: 'encode', path: '/encode?method={method}&text={text}(&shift={shift})' },
|
|
89
90
|
{ name: 'geo', path: '/geo?lat1={lat}&lon1={lon}&lat2={lat}&lon2={lon}' },
|
|
91
|
+
{ name: 'headers', path: '/headers(&filter={header1,header2})' },
|
|
92
|
+
{ name: 'ip', path: '/ip(&address={ip})' },
|
|
90
93
|
{ name: 'palette', path: '/palette?color={#hex}&type={type}' },
|
|
91
|
-
{
|
|
92
|
-
name: 'qrcode',
|
|
93
|
-
path: '/qrcode?url={URL}(&size={px}&margin={n}&correction={L|M|Q|H}&dark={hex}&light={hex}&icon={URL}&iconSize={px}&iconPadding={px}&iconRadius={px}&format={png|base64})',
|
|
94
|
-
},
|
|
95
94
|
{
|
|
96
95
|
name: 'placeholder',
|
|
97
96
|
path: '/placeholder?type={image|skeleton}&width={w}&height={h}(&bg={hex}&color={hex}&text={text}&rows={n}&avatar={bool})',
|
|
98
97
|
},
|
|
98
|
+
{
|
|
99
|
+
name: 'qrcode',
|
|
100
|
+
path: '/qrcode?url={URL}(&size={px}&margin={n}&correction={L|M|Q|H}&dark={hex}&light={hex}&icon={URL}&iconSize={px}&iconPadding={px}&iconRadius={px}&format={png|base64})',
|
|
101
|
+
},
|
|
99
102
|
{ name: 'statistics', path: '/statistics?values={n1,n2,n3,...}' },
|
|
100
103
|
{ name: 'text', path: '/text?method={method}(&value={value}&type={type}&count={count}&lang={lang})' },
|
|
101
104
|
{ name: 'validate', path: '/validate?type={type}&value={value}' },
|
package/src/middleware/error.ts
CHANGED
package/src/middleware/json.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Request, Response
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
2
|
|
|
3
3
|
export function jsonResponseMiddleware(_req: Request, res: Response, next: NextFunction): void {
|
|
4
4
|
res.setHeader('Content-Type', 'application/json');
|
package/src/middleware/logger.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createLogger } from '@20syldev/logger.ts';
|
|
2
|
-
import type { Request, Response
|
|
2
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
3
|
+
|
|
3
4
|
import { MAX_LOG_ENTRIES } from '../constants.js';
|
|
4
5
|
|
|
5
6
|
export const logger = createLogger({
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Request, Response
|
|
2
|
-
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
|
|
3
|
+
import { getPlan, globalLimit } from '../config/plans.js';
|
|
3
4
|
import { RATE_LIMIT_WINDOW, SESSION_TTL } from '../constants.js';
|
|
5
|
+
import { ipLimits } from '../storage/index.js';
|
|
4
6
|
import { error } from '../utils/response.js';
|
|
5
|
-
import { getPlan, globalLimit } from '../config/plans.js';
|
|
6
7
|
|
|
7
8
|
let requests = 0;
|
|
8
9
|
let resetTime = Date.now() + SESSION_TTL;
|
package/src/modules/v3.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * as algorithms from './v3/algorithms.js';
|
|
2
|
-
export { default as chat } from './v3/chat.js';
|
|
3
2
|
export { default as captcha } from './v3/captcha.js';
|
|
3
|
+
export { default as chat } from './v3/chat.js';
|
|
4
4
|
export { default as color } from './v3/color.js';
|
|
5
5
|
export { default as convert } from './v3/convert.js';
|
|
6
6
|
export { default as domain } from './v3/domain.js';
|
|
@@ -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.',
|