@20syldev/api 4.9.0 → 5.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otp.js","sourceRoot":"","sources":["../../../src/modules/v5/otp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAkBjD,iBAAiB;AACjB,MAAM,YAAY,GAAG,kCAAkC,CAAC;AAExD,SAAS,YAAY,CAAC,MAAc;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QACxB,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAC5B,IAAI,IAAI,CAAC,CAAC;QACV,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,CAAC;YACV,MAAM,IAAI,YAAY,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACpD,CAAC;IACL,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACjC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACzD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC5D,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;QAC3B,IAAI,IAAI,CAAC,CAAC;QACV,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YACZ,IAAI,IAAI,CAAC,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACxC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,eAAe;AACf,SAAS,YAAY,CAAC,MAAc,EAAE,OAAe,EAAE,SAAiB,EAAE,MAAc;IACpF,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAErC,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,GAAG,IAAI,CAAC;IAC7C,MAAM,MAAM,GACR,CAAC,CAAC,IAAI,CAAC,MAAM,CAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAClC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,GAAG,IAAI,CAAC,CAAC;IAE/B,MAAM,GAAG,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM,CAAC;IAClC,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,eAAe;AACf,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC/D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACrC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,GAAG,CACvB,MAAc,EACd,UASI,EAAE;IAEN,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAExE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IAEpC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,GAAG,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrF,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACxE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAEhF,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;QACvC,MAAM,SAAS,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,kBAAkB,kBAAkB,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,WAAW,MAAM,WAAW,kBAAkB,CAAC,MAAM,CAAC,cAAc,SAAS,CAAC,WAAW,EAAE,WAAW,MAAM,WAAW,MAAM,EAAE,CAAC;QACrN,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC/E,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/C,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QACxB,MAAM,OAAO,GACT,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;QAC7G,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACjE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QAC7G,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IACvC,CAAC;IAED,sBAAsB;IACtB,IAAI,CAAC,OAAO,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAClE,MAAM,cAAc,GAChB,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;IAE7G,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACxF,IAAI,IAAI,KAAK,OAAO,CAAC,IAAI;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IACvF,CAAC;IACD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACxF,IAAI,IAAI,KAAK,OAAO,CAAC,IAAI;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC7D,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AACtC,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto';
2
+ import { MAX_STRING_LENGTH } from '../../constants.js';
3
+ const ALGORITHMS = {
4
+ 'aes-256-gcm': { keyLength: 32, ivLength: 12, authTag: true },
5
+ 'aes-256-cbc': { keyLength: 32, ivLength: 16, authTag: false },
6
+ 'aes-128-gcm': { keyLength: 16, ivLength: 12, authTag: true },
7
+ };
8
+ /**
9
+ * Encrypts or decrypts text using AES with key derivation via scrypt.
10
+ *
11
+ * @param action - "encrypt" or "decrypt"
12
+ * @param text - Text to encrypt, or base64 blob to decrypt
13
+ * @param key - User-provided key (min 8 characters)
14
+ * @param algorithm - Cipher algorithm (default: aes-256-gcm)
15
+ * @returns Object containing action, algorithm, and base64 result
16
+ */
17
+ export default function encrypt(action, text, key, algorithm = 'aes-256-gcm') {
18
+ if (!action || (action !== 'encrypt' && action !== 'decrypt'))
19
+ throw new Error('Action must be "encrypt" or "decrypt"');
20
+ if (!text)
21
+ throw new Error('Text is required');
22
+ if (!key)
23
+ throw new Error('Key is required');
24
+ if (key.length < 8)
25
+ throw new Error('Key must be at least 8 characters');
26
+ if (action === 'encrypt' && text.length > MAX_STRING_LENGTH)
27
+ throw new Error(`Text exceeds maximum length of ${MAX_STRING_LENGTH}`);
28
+ const algo = ALGORITHMS[algorithm];
29
+ if (!algo)
30
+ throw new Error(`Unsupported algorithm. Use one of: ${Object.keys(ALGORITHMS).join(', ')}`);
31
+ if (action === 'encrypt') {
32
+ const salt = randomBytes(16);
33
+ const iv = randomBytes(algo.ivLength);
34
+ const derivedKey = scryptSync(key, salt, algo.keyLength);
35
+ const cipher = createCipheriv(algorithm, derivedKey, iv);
36
+ const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
37
+ let blob;
38
+ if (algo.authTag) {
39
+ const tag = cipher.getAuthTag();
40
+ blob = Buffer.concat([salt, iv, tag, encrypted]);
41
+ }
42
+ else {
43
+ blob = Buffer.concat([salt, iv, encrypted]);
44
+ }
45
+ return { action, algorithm, result: blob.toString('base64') };
46
+ }
47
+ else {
48
+ const data = Buffer.from(text, 'base64');
49
+ const salt = data.subarray(0, 16);
50
+ const iv = data.subarray(16, 16 + algo.ivLength);
51
+ let tagEnd = 16 + algo.ivLength;
52
+ let tag = null;
53
+ if (algo.authTag) {
54
+ tag = data.subarray(tagEnd, tagEnd + 16);
55
+ tagEnd += 16;
56
+ }
57
+ const ciphertext = data.subarray(tagEnd);
58
+ try {
59
+ const derivedKey = scryptSync(key, salt, algo.keyLength);
60
+ const decipher = createDecipheriv(algorithm, derivedKey, iv);
61
+ if (tag)
62
+ decipher.setAuthTag(tag);
63
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
64
+ return { action, algorithm, result: decrypted.toString('utf8') };
65
+ }
66
+ catch {
67
+ throw new Error('Invalid key or corrupted data');
68
+ }
69
+ }
70
+ }
71
+ //# sourceMappingURL=symmetric.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symmetric.js","sourceRoot":"","sources":["../../../src/modules/v5/symmetric.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { address, agent, algorithms, avatar, barcode, captcha, chat, color, convert, credit, cron, dice, domain, encode, geo, hash, hyperplanning, ip, levenshtein, palette, password, personal, placeholder, qrcode, regex, statistics, text, tic_tac_toe, time, token, username, validate, } from './v4.js';
2
+ export * as chart from './v5/chart.js';
3
+ export { default as evaluate } from './v5/evaluate.js';
4
+ export * as matrix from './v5/matrix.js';
5
+ export { default as otp } from './v5/otp.js';
6
+ export { default as symmetric } from './v5/symmetric.js';
7
+ //# sourceMappingURL=v5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v5.js","sourceRoot":"","sources":["../../src/modules/v5.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,OAAO,EACP,KAAK,EACL,UAAU,EACV,MAAM,EACN,OAAO,EACP,OAAO,EACP,IAAI,EACJ,KAAK,EACL,OAAO,EACP,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,MAAM,EACN,GAAG,EACH,IAAI,EACJ,aAAa,EACb,EAAE,EACF,WAAW,EACX,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,MAAM,EACN,KAAK,EACL,UAAU,EACV,IAAI,EACJ,WAAW,EACX,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,QAAQ,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
@@ -4,7 +4,7 @@ import { error } from '../utils/response.js';
4
4
  const router = Router();
5
5
  // Clear a private chat
6
6
  router.delete('/:version/chat/:token', (req, res) => {
7
- if (req.version !== 'v4') {
7
+ if (parseInt(req.version.slice(1)) < 4) {
8
8
  error(res, 405, 'DELETE is only supported in v4+.', `${req.version}/chat`);
9
9
  return;
10
10
  }
@@ -33,7 +33,7 @@ router.delete('/:version/chat/:token', (req, res) => {
33
33
  });
34
34
  // Forfeit a tic-tac-toe game
35
35
  router.delete('/:version/tic-tac-toe/:game', (req, res) => {
36
- if (req.version !== 'v4') {
36
+ if (parseInt(req.version.slice(1)) < 4) {
37
37
  error(res, 405, 'DELETE is only supported in v4+.', `${req.version}/tic-tac-toe`);
38
38
  return;
39
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/routes/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE7C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;AAExB,uBAAuB;AACvB,MAAM,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACnE,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QACvB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,kCAAkC,EAAE,GAAG,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC;QAC3E,OAAO;IACX,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAe,CAAC;IACzC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAI,GAAG,CAAC,IAA+B,IAAI,EAAE,CAAC;IAEzE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,kDAAkD,CAAC,CAAC;QACpE,OAAO;IACX,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,mDAAmD,CAAC,CAAC;QACrE,OAAO;IACX,CAAC;IAED,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YACpC,QAAQ;YACR,KAAK;YACL,OAAO;YACP,OAAO,EAAE,WAAW;SACvB,CAAC,CAAC;QACH,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,KAAK,CAAC,GAAG,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,6BAA6B;AAC7B,MAAM,CAAC,MAAM,CAAC,6BAA6B,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACzE,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QACvB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,kCAAkC,EAAE,GAAG,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC;QAClF,OAAO;IACX,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAc,CAAC;IACvC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAI,GAAG,CAAC,IAA+B,IAAI,EAAE,CAAC;IAEzE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,kDAAkD,CAAC,CAAC;QACpE,OAAO;IACX,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,mDAAmD,CAAC,CAAC;QACrE,OAAO;IACX,CAAC;IAED,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE;YAC7C,QAAQ;YACR,OAAO;YACP,IAAI;YACJ,OAAO,EAAE,gBAAgB;SAC5B,CAAC,CAAC;QACH,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,KAAK,CAAC,GAAG,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/routes/delete.ts"],"names":[],"mappings":"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"}
@@ -41,6 +41,36 @@ router.get('/:version', (req, res) => {
41
41
  endpoints,
42
42
  });
43
43
  });
44
+ // Generate a fictional postal address
45
+ router.get('/:version/address', (req, res) => {
46
+ const { country, count } = req.query;
47
+ const { version } = req.params;
48
+ const addressFn = req.module.address;
49
+ if (!addressFn) {
50
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/address`);
51
+ return;
52
+ }
53
+ const parsedCount = count !== undefined ? parseInt(count, 10) : 1;
54
+ try {
55
+ const result = addressFn(country, parsedCount);
56
+ res.jsonResponse(result);
57
+ }
58
+ catch (err) {
59
+ error(res, 400, err.message, `${req.version}/address`);
60
+ }
61
+ });
62
+ // Parse a User-Agent string
63
+ router.get('/:version/agent', (req, res) => {
64
+ const ua = req.query.ua ?? req.headers['user-agent'] ?? '';
65
+ try {
66
+ const agentFn = req.module.agent;
67
+ const result = agentFn(ua);
68
+ res.jsonResponse(result);
69
+ }
70
+ catch (err) {
71
+ error(res, 400, err.message, `${req.version}/agent`);
72
+ }
73
+ });
44
74
  // Algorithms
45
75
  router.get('/:version/algorithms', (req, res) => {
46
76
  const { method, value, value2 } = req.query;
@@ -58,6 +88,56 @@ router.get('/:version/algorithms', (req, res) => {
58
88
  error(res, 400, err.message, `${req.version}/algorithms`);
59
89
  }
60
90
  });
91
+ // Generate an identicon or pixel-art avatar from a seed
92
+ router.get('/:version/avatar', (req, res) => {
93
+ const avatarFn = req.module.avatar;
94
+ if (!avatarFn) {
95
+ error(res, 404, `Endpoint not available in ${req.version}.`, `${req.latest}/avatar`);
96
+ return;
97
+ }
98
+ const { seed, size, type, bg, format } = req.query;
99
+ try {
100
+ const { contentType, body } = avatarFn({
101
+ seed: seed,
102
+ size: size !== undefined ? parseInt(size, 10) : undefined,
103
+ type: type,
104
+ bg: bg,
105
+ format: format,
106
+ });
107
+ res.type(contentType).send(body);
108
+ }
109
+ catch (err) {
110
+ error(res, 400, err.message, `${req.version}/avatar`);
111
+ }
112
+ });
113
+ // Generate a barcode image
114
+ router.get('/:version/barcode', (req, res) => {
115
+ const barcodeFn = req.module.barcode;
116
+ if (!barcodeFn) {
117
+ error(res, 404, `Endpoint not available in ${req.version}.`, `${req.latest}/barcode`);
118
+ return;
119
+ }
120
+ const { data, type, width, height, format, color, bg } = req.query;
121
+ if (!data) {
122
+ error(res, 400, 'Please provide data to encode (?data={string})', `${req.version}/barcode`);
123
+ return;
124
+ }
125
+ try {
126
+ const { contentType, body } = barcodeFn({
127
+ data: data,
128
+ type: type,
129
+ width: width !== undefined ? parseInt(width, 10) : undefined,
130
+ height: height !== undefined ? parseInt(height, 10) : undefined,
131
+ format: format,
132
+ color: color,
133
+ bg: bg,
134
+ });
135
+ res.type(contentType).send(body);
136
+ }
137
+ catch (err) {
138
+ error(res, 400, err.message, `${req.version}/barcode`);
139
+ }
140
+ });
61
141
  // Generate captcha
62
142
  router.get('/:version/captcha', (req, res) => {
63
143
  try {
@@ -89,6 +169,10 @@ router.get('/:version/captcha', (req, res) => {
89
169
  error(res, 400, err.message, `${req.version}/captcha`);
90
170
  }
91
171
  });
172
+ // GET chart error
173
+ router.get('/:version/chart', (_req, res) => {
174
+ error(res, 405, 'This endpoint only supports POST requests.');
175
+ });
92
176
  // Display stored data
93
177
  router.get('/:version/chat', (req, res) => {
94
178
  try {
@@ -154,124 +238,12 @@ router.get('/:version/convert', (req, res) => {
154
238
  error(res, 400, err.message, `${req.version}/convert`);
155
239
  }
156
240
  });
157
- // Generate a fictional postal address
158
- router.get('/:version/address', (req, res) => {
159
- const { country, count } = req.query;
160
- const { version } = req.params;
161
- const addressFn = req.module.address;
162
- if (!addressFn) {
163
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/address`);
164
- return;
165
- }
166
- const parsedCount = count !== undefined ? parseInt(count, 10) : 1;
167
- try {
168
- const result = addressFn(country, parsedCount);
169
- res.jsonResponse(result);
170
- }
171
- catch (err) {
172
- error(res, 400, err.message, `${req.version}/address`);
173
- }
174
- });
175
- // Echo request headers
176
- router.get('/:version/headers', (req, res) => {
177
- const redacted = new Set(['authorization', 'cookie', 'set-cookie', 'proxy-authorization']);
178
- let headers = {};
179
- for (const [k, v] of Object.entries(req.headers)) {
180
- headers[k] = redacted.has(k) ? '[redacted]' : v;
181
- }
182
- const filter = req.query.filter;
183
- if (filter) {
184
- const keys = new Set(filter.split(',').map((k) => k.trim().toLowerCase()));
185
- headers = Object.fromEntries(Object.entries(headers).filter(([k]) => keys.has(k)));
186
- }
187
- res.jsonResponse({
188
- count: Object.keys(headers).length,
189
- headers,
190
- ip: req.ip,
191
- method: req.method,
192
- url: req.originalUrl,
193
- });
194
- });
195
- // Analyze an IP address
196
- router.get('/:version/ip', (req, res) => {
197
- const address = req.query.address ?? req.ip ?? '';
198
- try {
199
- const ipFn = req.module.ip;
200
- const result = ipFn(address);
201
- res.jsonResponse(result);
202
- }
203
- catch (err) {
204
- error(res, 400, err.message, `${req.version}/ip`);
205
- }
206
- });
207
- // Parse a User-Agent string
208
- router.get('/:version/agent', (req, res) => {
209
- const ua = req.query.ua ?? req.headers['user-agent'] ?? '';
210
- try {
211
- const agentFn = req.module.agent;
212
- const result = agentFn(ua);
213
- res.jsonResponse(result);
214
- }
215
- catch (err) {
216
- error(res, 400, err.message, `${req.version}/agent`);
217
- }
218
- });
219
- // Generate an identicon or pixel-art avatar from a seed
220
- router.get('/:version/avatar', (req, res) => {
221
- const avatarFn = req.module.avatar;
222
- if (!avatarFn) {
223
- error(res, 404, `Endpoint not available in ${req.version}.`, `${req.version}/avatar`);
224
- return;
225
- }
226
- const { seed, size, type, bg, format } = req.query;
227
- try {
228
- const { contentType, body } = avatarFn({
229
- seed: seed,
230
- size: size !== undefined ? parseInt(size, 10) : undefined,
231
- type: type,
232
- bg: bg,
233
- format: format,
234
- });
235
- res.type(contentType).send(body);
236
- }
237
- catch (err) {
238
- error(res, 400, err.message, `${req.version}/avatar`);
239
- }
240
- });
241
- // Generate a barcode image
242
- router.get('/:version/barcode', (req, res) => {
243
- const barcodeFn = req.module.barcode;
244
- if (!barcodeFn) {
245
- error(res, 404, `Endpoint not available in ${req.version}.`, `${req.version}/barcode`);
246
- return;
247
- }
248
- const { data, type, width, height, format, color, bg } = req.query;
249
- if (!data) {
250
- error(res, 400, 'Please provide data to encode (?data={string})', `${req.version}/barcode`);
251
- return;
252
- }
253
- try {
254
- const { contentType, body } = barcodeFn({
255
- data: data,
256
- type: type,
257
- width: width !== undefined ? parseInt(width, 10) : undefined,
258
- height: height !== undefined ? parseInt(height, 10) : undefined,
259
- format: format,
260
- color: color,
261
- bg: bg,
262
- });
263
- res.type(contentType).send(body);
264
- }
265
- catch (err) {
266
- error(res, 400, err.message, `${req.version}/barcode`);
267
- }
268
- });
269
241
  // Generate fictitious credit card numbers
270
242
  router.get('/:version/credit', (req, res) => {
271
243
  const creditFn = req.module
272
244
  .credit;
273
245
  if (!creditFn) {
274
- error(res, 404, `Endpoint not available in ${req.version}.`, `${req.version}/credit`);
246
+ error(res, 404, `Endpoint not available in ${req.version}.`, `${req.latest}/credit`);
275
247
  return;
276
248
  }
277
249
  const { brand, count, format } = req.query;
@@ -283,23 +255,13 @@ router.get('/:version/credit', (req, res) => {
283
255
  error(res, 400, err.message, `${req.version}/credit`);
284
256
  }
285
257
  });
286
- // Generate domain informations
287
- router.get('/:version/domain', (req, res) => {
288
- try {
289
- const result = req.module.domain();
290
- res.jsonResponse(result);
291
- }
292
- catch (err) {
293
- error(res, 400, err.message, `${req.version}/domain`);
294
- }
295
- });
296
258
  // Parse a cron expression and compute next execution dates
297
259
  router.get('/:version/cron', (req, res) => {
298
260
  const { expr, count, from, timezone } = req.query;
299
261
  const { version } = req.params;
300
262
  const cronFn = req.module.cron;
301
263
  if (!cronFn) {
302
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/cron`);
264
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/cron`);
303
265
  return;
304
266
  }
305
267
  if (!expr) {
@@ -320,7 +282,7 @@ router.get('/:version/dice', (req, res) => {
320
282
  const { version } = req.params;
321
283
  const dice = req.module.dice;
322
284
  if (!dice) {
323
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/dice`);
285
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/dice`);
324
286
  return;
325
287
  }
326
288
  if (!roll) {
@@ -335,13 +297,23 @@ router.get('/:version/dice', (req, res) => {
335
297
  error(res, 400, err.message, `${req.version}/dice`);
336
298
  }
337
299
  });
300
+ // Generate domain informations
301
+ router.get('/:version/domain', (req, res) => {
302
+ try {
303
+ const result = req.module.domain();
304
+ res.jsonResponse(result);
305
+ }
306
+ catch (err) {
307
+ error(res, 400, err.message, `${req.version}/domain`);
308
+ }
309
+ });
338
310
  // Encode / decode text
339
311
  router.get('/:version/encode', (req, res) => {
340
312
  const { method, text, shift } = req.query;
341
313
  const { version } = req.params;
342
314
  const encode = req.module.encode;
343
315
  if (!encode) {
344
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/encode`);
316
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/encode`);
345
317
  return;
346
318
  }
347
319
  if (!method || !Object.hasOwn(encode, method)) {
@@ -356,13 +328,34 @@ router.get('/:version/encode', (req, res) => {
356
328
  error(res, 400, err.message, `${req.version}/encode`);
357
329
  }
358
330
  });
331
+ // Evaluate a math expression
332
+ router.get('/:version/evaluate', (req, res) => {
333
+ const { expr, precision } = req.query;
334
+ const { version } = req.params;
335
+ const evaluateFn = req.module.evaluate;
336
+ if (!evaluateFn) {
337
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/evaluate`);
338
+ return;
339
+ }
340
+ if (!expr || typeof expr !== 'string') {
341
+ error(res, 400, 'Please provide a math expression (?expr={expression})', `${version}/evaluate`);
342
+ return;
343
+ }
344
+ try {
345
+ const result = evaluateFn(expr, precision !== undefined ? parseInt(precision, 10) : undefined);
346
+ res.jsonResponse(result);
347
+ }
348
+ catch (err) {
349
+ error(res, 400, err.message, `${req.version}/evaluate`);
350
+ }
351
+ });
359
352
  // Geographic distance and bearing between two coordinates
360
353
  router.get('/:version/geo', (req, res) => {
361
354
  const { lat1, lon1, lat2, lon2 } = req.query;
362
355
  const { version } = req.params;
363
356
  const geo = req.module.geo;
364
357
  if (!geo) {
365
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/geo`);
358
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/geo`);
366
359
  return;
367
360
  }
368
361
  if (lat1 === undefined || lon1 === undefined || lat2 === undefined || lon2 === undefined) {
@@ -377,14 +370,34 @@ router.get('/:version/geo', (req, res) => {
377
370
  error(res, 400, err.message, `${req.version}/geo`);
378
371
  }
379
372
  });
380
- // GET planning error
381
- router.get('/:version/hyperplanning', (_req, res) => {
382
- error(res, 405, 'This endpoint only supports POST requests.');
383
- });
384
373
  // GET hash error
385
374
  router.get('/:version/hash', (_req, res) => {
386
375
  error(res, 405, 'This endpoint only supports POST requests.');
387
376
  });
377
+ // Echo request headers
378
+ router.get('/:version/headers', (req, res) => {
379
+ const redacted = new Set(['authorization', 'cookie', 'set-cookie', 'proxy-authorization']);
380
+ let headers = {};
381
+ for (const [k, v] of Object.entries(req.headers)) {
382
+ headers[k] = redacted.has(k) ? '[redacted]' : v;
383
+ }
384
+ const filter = req.query.filter;
385
+ if (filter) {
386
+ const keys = new Set(filter.split(',').map((k) => k.trim().toLowerCase()));
387
+ headers = Object.fromEntries(Object.entries(headers).filter(([k]) => keys.has(k)));
388
+ }
389
+ res.jsonResponse({
390
+ count: Object.keys(headers).length,
391
+ headers,
392
+ ip: req.ip,
393
+ method: req.method,
394
+ url: req.originalUrl,
395
+ });
396
+ });
397
+ // GET planning error
398
+ router.get('/:version/hyperplanning', (_req, res) => {
399
+ error(res, 405, 'This endpoint only supports POST requests.');
400
+ });
388
401
  // Display API informations
389
402
  router.get('/:version/infos', (req, res) => {
390
403
  const endpoints = Object.values(versions[req.version].endpoints).flat();
@@ -397,6 +410,18 @@ router.get('/:version/infos', (req, res) => {
397
410
  creation: 'November 25th 2024',
398
411
  });
399
412
  });
413
+ // Analyze an IP address
414
+ router.get('/:version/ip', (req, res) => {
415
+ const address = req.query.address ?? req.ip ?? '';
416
+ try {
417
+ const ipFn = req.module.ip;
418
+ const result = ipFn(address);
419
+ res.jsonResponse(result);
420
+ }
421
+ catch (err) {
422
+ error(res, 400, err.message, `${req.version}/ip`);
423
+ }
424
+ });
400
425
  // Calculate Levenshtein distance
401
426
  router.get('/:version/levenshtein', (req, res) => {
402
427
  const { str1, str2 } = req.query;
@@ -416,13 +441,21 @@ router.get('/:version/levenshtein', (req, res) => {
416
441
  error(res, 400, err.message, `${req.version}/levenshtein`);
417
442
  }
418
443
  });
444
+ // GET matrix error
445
+ router.get('/:version/matrix', (_req, res) => {
446
+ error(res, 405, 'This endpoint only supports POST requests.');
447
+ });
448
+ // GET otp error
449
+ router.get('/:version/otp', (_req, res) => {
450
+ error(res, 405, 'This endpoint only supports POST requests.');
451
+ });
419
452
  // Generate a color palette from a base color
420
453
  router.get('/:version/palette', (req, res) => {
421
454
  const { color, type } = req.query;
422
455
  const { version } = req.params;
423
456
  const palette = req.module.palette;
424
457
  if (!palette) {
425
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/palette`);
458
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/palette`);
426
459
  return;
427
460
  }
428
461
  if (!color) {
@@ -447,7 +480,7 @@ router.get('/:version/password', (req, res) => {
447
480
  const { version } = req.params;
448
481
  const passwordFn = req.module.password;
449
482
  if (!passwordFn) {
450
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/password`);
483
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/password`);
451
484
  return;
452
485
  }
453
486
  const parseBool = (v, def) => (v === undefined ? def : v !== 'false');
@@ -483,7 +516,7 @@ router.get('/:version/placeholder', (req, res) => {
483
516
  const { version } = req.params;
484
517
  const placeholder = req.module.placeholder;
485
518
  if (!placeholder) {
486
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/placeholder`);
519
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/placeholder`);
487
520
  return;
488
521
  }
489
522
  try {
@@ -539,7 +572,7 @@ router.get('/:version/regex', (req, res) => {
539
572
  const { version } = req.params;
540
573
  const regexFn = req.module.regex;
541
574
  if (!regexFn) {
542
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/regex`);
575
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/regex`);
543
576
  return;
544
577
  }
545
578
  if (!pattern) {
@@ -564,7 +597,7 @@ router.get('/:version/statistics', (req, res) => {
564
597
  const { version } = req.params;
565
598
  const statistics = req.module.statistics;
566
599
  if (!statistics) {
567
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/statistics`);
600
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/statistics`);
568
601
  return;
569
602
  }
570
603
  if (!values) {
@@ -579,13 +612,17 @@ router.get('/:version/statistics', (req, res) => {
579
612
  error(res, 400, err.message, `${req.version}/statistics`);
580
613
  }
581
614
  });
615
+ // GET symmetric error
616
+ router.get('/:version/symmetric', (_req, res) => {
617
+ error(res, 405, 'This endpoint only supports POST requests.');
618
+ });
582
619
  // Text utilities (slug, stats, lorem, number)
583
620
  router.get('/:version/text', (req, res) => {
584
621
  const { method, value, type, count, lang, text } = req.query;
585
622
  const { version } = req.params;
586
623
  const textMod = req.module.text;
587
624
  if (!textMod) {
588
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/text`);
625
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/text`);
589
626
  return;
590
627
  }
591
628
  if (!method || !Object.hasOwn(textMod, method)) {
@@ -614,31 +651,6 @@ router.get('/:version/text', (req, res) => {
614
651
  error(res, 400, err.message, `${req.version}/text`);
615
652
  }
616
653
  });
617
- // Validate data (luhn, iban, email)
618
- router.get('/:version/validate', (req, res) => {
619
- const { type, value } = req.query;
620
- const { version } = req.params;
621
- const validate = req.module.validate;
622
- if (!validate) {
623
- error(res, 404, `Endpoint not available in ${version}.`, `${version}/validate`);
624
- return;
625
- }
626
- if (!type || !Object.hasOwn(validate, type)) {
627
- error(res, 400, 'Please provide a valid type (?type={luhn|iban|email})', `${version}/validate`);
628
- return;
629
- }
630
- if (!value) {
631
- error(res, 400, 'Please provide a value (&value={value})', `${version}/validate`);
632
- return;
633
- }
634
- try {
635
- const result = validate[type](value);
636
- res.jsonResponse(result);
637
- }
638
- catch (err) {
639
- error(res, 400, err.message, `${req.version}/validate`);
640
- }
641
- });
642
654
  // GET tic-tac-toe errors
643
655
  router.get('/:version/tic-tac-toe', (_req, res) => {
644
656
  error(res, 405, 'This endpoint only supports POST requests.');
@@ -675,6 +687,31 @@ router.get('/:version/username', (req, res) => {
675
687
  error(res, 400, err.message, `${req.version}/username`);
676
688
  }
677
689
  });
690
+ // Validate data (luhn, iban, email)
691
+ router.get('/:version/validate', (req, res) => {
692
+ const { type, value } = req.query;
693
+ const { version } = req.params;
694
+ const validate = req.module.validate;
695
+ if (!validate) {
696
+ error(res, 404, `Endpoint not available in ${version}.`, `${req.latest}/validate`);
697
+ return;
698
+ }
699
+ if (!type || !Object.hasOwn(validate, type)) {
700
+ error(res, 400, 'Please provide a valid type (?type={luhn|iban|email})', `${version}/validate`);
701
+ return;
702
+ }
703
+ if (!value) {
704
+ error(res, 400, 'Please provide a value (&value={value})', `${version}/validate`);
705
+ return;
706
+ }
707
+ try {
708
+ const result = validate[type](value);
709
+ res.jsonResponse(result);
710
+ }
711
+ catch (err) {
712
+ error(res, 400, err.message, `${req.version}/validate`);
713
+ }
714
+ });
678
715
  // Display informations for owner's website
679
716
  router.get('/:version/website', async (req, res) => {
680
717
  const currentTime = Date.now();