@20syldev/api 3.5.0 → 4.0.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.
Files changed (180) hide show
  1. package/.prettierrc +7 -0
  2. package/README.md +32 -33
  3. package/dist/app.js +36 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/config/env.js +73 -0
  6. package/dist/config/env.js.map +1 -0
  7. package/dist/config/versions.js +72 -0
  8. package/dist/config/versions.js.map +1 -0
  9. package/dist/constants.js +42 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/middleware/cors.js +15 -0
  12. package/dist/middleware/cors.js.map +1 -0
  13. package/dist/middleware/error.js +6 -0
  14. package/dist/middleware/error.js.map +1 -0
  15. package/dist/middleware/json.js +8 -0
  16. package/dist/middleware/json.js.map +1 -0
  17. package/dist/middleware/logger.js +26 -0
  18. package/dist/middleware/logger.js.map +1 -0
  19. package/dist/middleware/ratelimit.js +58 -0
  20. package/dist/middleware/ratelimit.js.map +1 -0
  21. package/dist/middleware/version.js +30 -0
  22. package/dist/middleware/version.js.map +1 -0
  23. package/dist/modules/v3/algorithms.js +221 -0
  24. package/dist/modules/v3/algorithms.js.map +1 -0
  25. package/{modules → dist/modules}/v3/captcha.js +4 -13
  26. package/dist/modules/v3/captcha.js.map +1 -0
  27. package/dist/modules/v3/chat.js +103 -0
  28. package/dist/modules/v3/chat.js.map +1 -0
  29. package/{modules → dist/modules}/v3/color.js +12 -13
  30. package/dist/modules/v3/color.js.map +1 -0
  31. package/dist/modules/v3/convert.js +41 -0
  32. package/dist/modules/v3/convert.js.map +1 -0
  33. package/dist/modules/v3/domain.js +83 -0
  34. package/dist/modules/v3/domain.js.map +1 -0
  35. package/dist/modules/v3/hash.js +16 -0
  36. package/dist/modules/v3/hash.js.map +1 -0
  37. package/dist/modules/v3/hyperplanning.js +50 -0
  38. package/dist/modules/v3/hyperplanning.js.map +1 -0
  39. package/dist/modules/v3/levenshtein.js +38 -0
  40. package/dist/modules/v3/levenshtein.js.map +1 -0
  41. package/dist/modules/v3/personal.js +129 -0
  42. package/dist/modules/v3/personal.js.map +1 -0
  43. package/dist/modules/v3/qrcode.js +21 -0
  44. package/dist/modules/v3/qrcode.js.map +1 -0
  45. package/dist/modules/v3/tic_tac_toe.js +221 -0
  46. package/dist/modules/v3/tic_tac_toe.js.map +1 -0
  47. package/dist/modules/v3/time.js +88 -0
  48. package/dist/modules/v3/time.js.map +1 -0
  49. package/{modules → dist/modules}/v3/token.js +10 -11
  50. package/dist/modules/v3/token.js.map +1 -0
  51. package/dist/modules/v3/username.js +92 -0
  52. package/dist/modules/v3/username.js.map +1 -0
  53. package/dist/modules/v3/utils.js +70 -0
  54. package/dist/modules/v3/utils.js.map +1 -0
  55. package/dist/modules/v3.js +16 -0
  56. package/dist/modules/v3.js.map +1 -0
  57. package/dist/modules/v4/algorithms.js +200 -0
  58. package/dist/modules/v4/algorithms.js.map +1 -0
  59. package/dist/modules/v4/captcha.js +35 -0
  60. package/dist/modules/v4/captcha.js.map +1 -0
  61. package/dist/modules/v4/chat.js +92 -0
  62. package/dist/modules/v4/chat.js.map +1 -0
  63. package/dist/modules/v4/color.js +43 -0
  64. package/dist/modules/v4/color.js.map +1 -0
  65. package/dist/modules/v4/convert.js +33 -0
  66. package/dist/modules/v4/convert.js.map +1 -0
  67. package/dist/modules/v4/dice.js +30 -0
  68. package/dist/modules/v4/dice.js.map +1 -0
  69. package/dist/modules/v4/domain.js +78 -0
  70. package/dist/modules/v4/domain.js.map +1 -0
  71. package/dist/modules/v4/encode.js +165 -0
  72. package/dist/modules/v4/encode.js.map +1 -0
  73. package/dist/modules/v4/geo.js +41 -0
  74. package/dist/modules/v4/geo.js.map +1 -0
  75. package/dist/modules/v4/hash.js +9 -0
  76. package/dist/modules/v4/hash.js.map +1 -0
  77. package/dist/modules/v4/hyperplanning.js +42 -0
  78. package/dist/modules/v4/hyperplanning.js.map +1 -0
  79. package/dist/modules/v4/levenshtein.js +29 -0
  80. package/dist/modules/v4/levenshtein.js.map +1 -0
  81. package/dist/modules/v4/palette.js +89 -0
  82. package/dist/modules/v4/palette.js.map +1 -0
  83. package/dist/modules/v4/personal.js +125 -0
  84. package/dist/modules/v4/personal.js.map +1 -0
  85. package/dist/modules/v4/placeholder.js +96 -0
  86. package/dist/modules/v4/placeholder.js.map +1 -0
  87. package/dist/modules/v4/qrcode.js +12 -0
  88. package/dist/modules/v4/qrcode.js.map +1 -0
  89. package/dist/modules/v4/statistics.js +41 -0
  90. package/dist/modules/v4/statistics.js.map +1 -0
  91. package/dist/modules/v4/text.js +293 -0
  92. package/dist/modules/v4/text.js.map +1 -0
  93. package/dist/modules/v4/tic_tac_toe.js +206 -0
  94. package/dist/modules/v4/tic_tac_toe.js.map +1 -0
  95. package/dist/modules/v4/time.js +73 -0
  96. package/dist/modules/v4/time.js.map +1 -0
  97. package/dist/modules/v4/token.js +36 -0
  98. package/dist/modules/v4/token.js.map +1 -0
  99. package/dist/modules/v4/username.js +87 -0
  100. package/dist/modules/v4/username.js.map +1 -0
  101. package/dist/modules/v4/utils.js +26 -0
  102. package/dist/modules/v4/utils.js.map +1 -0
  103. package/dist/modules/v4/validate.js +54 -0
  104. package/dist/modules/v4/validate.js.map +1 -0
  105. package/dist/modules/v4.js +18 -0
  106. package/dist/modules/v4.js.map +1 -0
  107. package/dist/routes/delete.js +65 -0
  108. package/dist/routes/delete.js.map +1 -0
  109. package/dist/routes/get.js +441 -0
  110. package/dist/routes/get.js.map +1 -0
  111. package/dist/routes/index.js +50 -0
  112. package/dist/routes/index.js.map +1 -0
  113. package/dist/routes/patch.js +41 -0
  114. package/dist/routes/patch.js.map +1 -0
  115. package/dist/routes/post.js +185 -0
  116. package/dist/routes/post.js.map +1 -0
  117. package/dist/storage/index.js +13 -0
  118. package/dist/storage/index.js.map +1 -0
  119. package/dist/types/storage.js +2 -0
  120. package/dist/types/storage.js.map +1 -0
  121. package/dist/utils/response.js +10 -0
  122. package/dist/utils/response.js.map +1 -0
  123. package/dist/utils/version.js +3 -0
  124. package/dist/utils/version.js.map +1 -0
  125. package/eslint.config.js +20 -0
  126. package/package.json +30 -15
  127. package/src/app.ts +44 -0
  128. package/src/config/env.ts +81 -0
  129. package/src/config/versions.ts +80 -0
  130. package/src/constants.ts +28 -0
  131. package/src/middleware/cors.ts +17 -0
  132. package/src/middleware/error.ts +7 -0
  133. package/src/middleware/json.ts +9 -0
  134. package/src/middleware/logger.ts +31 -0
  135. package/src/middleware/ratelimit.ts +66 -0
  136. package/src/middleware/version.ts +39 -0
  137. package/{modules → src/modules}/v3/algorithms.js +1 -1
  138. package/src/modules/v3/captcha.js +60 -0
  139. package/{modules → src/modules}/v3/chat.js +6 -4
  140. package/src/modules/v3/color.js +60 -0
  141. package/{modules → src/modules}/v3/convert.js +5 -5
  142. package/{modules → src/modules}/v3/domain.js +51 -4
  143. package/{modules → src/modules}/v3/hash.js +1 -1
  144. package/{modules → src/modules}/v3/hyperplanning.js +11 -10
  145. package/{modules → src/modules}/v3/levenshtein.js +3 -3
  146. package/{modules → src/modules}/v3/personal.js +20 -8
  147. package/{modules → src/modules}/v3/qrcode.js +1 -1
  148. package/{modules → src/modules}/v3/tic_tac_toe.js +24 -18
  149. package/{modules → src/modules}/v3/time.js +22 -11
  150. package/src/modules/v3/token.js +54 -0
  151. package/src/modules/v3/username.js +94 -0
  152. package/{modules → src/modules}/v3/utils.js +2 -2
  153. package/{modules → src/modules}/v3.js +1 -1
  154. package/src/modules/v4/algorithms.ts +155 -0
  155. package/src/modules/v4/captcha.ts +49 -0
  156. package/src/modules/v4/chat.ts +99 -0
  157. package/src/modules/v4/color.ts +64 -0
  158. package/src/modules/v4/convert.ts +38 -0
  159. package/src/modules/v4/domain.ts +81 -0
  160. package/src/modules/v4/hash.ts +9 -0
  161. package/src/modules/v4/hyperplanning.ts +58 -0
  162. package/src/modules/v4/levenshtein.ts +38 -0
  163. package/src/modules/v4/personal.ts +147 -0
  164. package/src/modules/v4/qrcode.ts +11 -0
  165. package/src/modules/v4/tic_tac_toe.ts +223 -0
  166. package/src/modules/v4/time.ts +92 -0
  167. package/src/modules/v4/token.ts +43 -0
  168. package/src/modules/v4/username.ts +89 -0
  169. package/src/modules/v4/utils.ts +38 -0
  170. package/src/modules/v4.ts +15 -0
  171. package/src/routes/get.ts +461 -0
  172. package/src/routes/index.ts +58 -0
  173. package/src/routes/post.ts +200 -0
  174. package/src/storage/index.ts +16 -0
  175. package/src/types/express.d.ts +17 -0
  176. package/src/types/storage.ts +48 -0
  177. package/src/utils/response.ts +11 -0
  178. package/tsconfig.json +17 -0
  179. package/app.js +0 -865
  180. package/modules/v3/username.js +0 -32
@@ -0,0 +1,81 @@
1
+ import { random, genIP } from './utils.js';
2
+
3
+ export default function domain(): Record<string, unknown> {
4
+ const subdomains = [
5
+ 'fr.',
6
+ 'en.',
7
+ 'docs.',
8
+ 'api.',
9
+ 'projects.',
10
+ 'app.',
11
+ 'web.',
12
+ 'info.',
13
+ 'dev.',
14
+ 'shop.',
15
+ 'blog.',
16
+ 'support.',
17
+ 'mail.',
18
+ 'forum.',
19
+ ];
20
+ const domains = [
21
+ 'example',
22
+ 'site',
23
+ 'test',
24
+ 'demo',
25
+ 'page',
26
+ 'store',
27
+ 'portfolio',
28
+ 'platform',
29
+ 'hub',
30
+ 'network',
31
+ 'service',
32
+ 'cloud',
33
+ 'solutions',
34
+ 'company',
35
+ ];
36
+ const tlds = [
37
+ '.com',
38
+ '.fr',
39
+ '.eu',
40
+ '.dev',
41
+ '.net',
42
+ '.org',
43
+ '.io',
44
+ '.tech',
45
+ '.biz',
46
+ '.info',
47
+ '.co',
48
+ '.app',
49
+ '.store',
50
+ '.online',
51
+ '.shop',
52
+ '.tv',
53
+ ];
54
+
55
+ const domainName = `${random(domains)}${random(tlds)}`;
56
+ const fulldomain = `${random(subdomains)}${domainName}`;
57
+
58
+ const ips = Array.from({ length: Math.floor(Math.random() * 3) + 1 }, genIP);
59
+ const dns = Array.from({ length: Math.floor(Math.random() * 5) + 1 }, genIP);
60
+
61
+ return {
62
+ domain: domainName,
63
+ full_domain: fulldomain,
64
+ ip_address: ips,
65
+ ssl_certified: Math.random() > 0.5,
66
+ hosting_provider: random(['AWS', 'Bluehost', 'DigitalOcean', 'GitHub', 'HostGator', 'Render', 'SiteGround']),
67
+ dns_servers: dns,
68
+ dns_provider: random(['AWS Route 53', 'Cloudflare', 'GoDaddy', 'Google DNS', 'Namecheap']),
69
+ traffic: `${Math.floor(Math.random() * 10000)} visits/day`,
70
+ seo_score: Math.floor(Math.random() * 100),
71
+ page_rank: Math.floor(Math.random() * 10),
72
+ country: random(['Australia', 'Canada', 'France', 'Germany', 'India', 'Japan', 'UK', 'USA']),
73
+ website_type: random(['Blog', 'Community', 'Corporate', 'Educational', 'E-commerce', 'Personal', 'Portfolio']),
74
+ random_name: domainName.split('.')[0],
75
+ random_subdomain: fulldomain.split('.')[0],
76
+ random_tld: domainName.split('.').pop(),
77
+ backlinks_count: Math.floor(Math.random() * 1000),
78
+ creation_date: new Date(Date.now() - Math.floor(Math.random() * 10000000000)).toISOString(),
79
+ expiration_date: new Date(Date.now() + Math.floor(Math.random() * 10000000000)).toISOString(),
80
+ };
81
+ }
@@ -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,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
+ }
@@ -0,0 +1,11 @@
1
+ import { toDataURL } from 'qrcode';
2
+
3
+ export default async function qrcode(url: string): Promise<string> {
4
+ if (!url || typeof url !== 'string') throw new Error('Please provide a valid URL');
5
+
6
+ try {
7
+ return await toDataURL(url);
8
+ } catch (error) {
9
+ throw new Error(`Failed to generate QR code: ${(error as Error).message}`);
10
+ }
11
+ }
@@ -0,0 +1,223 @@
1
+ import { randomBytes } from 'crypto';
2
+ import { SESSION_TTL, GAME_CLEANUP_TTL, RATE_LIMIT_WINDOW, RATE_LIMIT_MAX } from '../../constants.js';
3
+ import type { TicTacToeStorage, TicTacToeMove, TicTacToeGame } from '../../types/storage.js';
4
+
5
+ interface TicTacToeParams {
6
+ username?: string;
7
+ move?: string;
8
+ session?: string;
9
+ game?: string;
10
+ private?: boolean;
11
+ storage: TicTacToeStorage;
12
+ }
13
+
14
+ interface GameResult {
15
+ winner?: string | null;
16
+ loser?: string | null;
17
+ tie?: boolean;
18
+ }
19
+
20
+ export default function tic_tac_toe(action: string, params: TicTacToeParams): Record<string, unknown> {
21
+ const storage = params.storage;
22
+
23
+ storage.games ??= {};
24
+ storage.sessions ??= {};
25
+ storage.rateLimits ??= {};
26
+
27
+ const { games, sessions, rateLimits } = storage;
28
+
29
+ if (action === 'list') return listGames(games);
30
+
31
+ if (!params.username) throw new Error('Please provide a username');
32
+
33
+ const u = params.username.toLowerCase();
34
+ const now = Date.now();
35
+
36
+ rateLimits[u] = (rateLimits[u] ?? []).filter((ts) => now - ts < RATE_LIMIT_WINDOW);
37
+ if (rateLimits[u]!.length > RATE_LIMIT_MAX) {
38
+ const remainingTime = Math.ceil((rateLimits[u]![0]! + RATE_LIMIT_WINDOW - now) / 1000);
39
+ throw new Error(`Rate limit exceeded. Try again in ${remainingTime} seconds.`);
40
+ }
41
+ rateLimits[u]!.push(now);
42
+
43
+ if (action === 'play') {
44
+ return playMove(params, games, sessions, u, now);
45
+ } else if (action === 'fetch') {
46
+ return fetchGame(params, games, u);
47
+ } else {
48
+ throw new Error('Invalid action. Use "play", "fetch", or "list"');
49
+ }
50
+ }
51
+
52
+ function playMove(
53
+ params: TicTacToeParams,
54
+ games: Record<string, TicTacToeGame>,
55
+ sessions: Record<string, { user: string; last: number }>,
56
+ u: string,
57
+ now: number,
58
+ ): Record<string, unknown> {
59
+ const { move, session, game } = params;
60
+ const validMoves = ['1-1', '1-2', '1-3', '2-1', '2-2', '2-3', '3-1', '3-2', '3-3'];
61
+
62
+ if (!move) throw new Error('Please provide a valid move');
63
+ if (!session) throw new Error('Please provide a valid session ID');
64
+ if (!game) throw new Error('Please provide a valid game ID');
65
+ if (!validMoves.includes(move)) throw new Error('Invalid move. Please provide a valid move (e.g., 1-1, 2-2, 3-3).');
66
+
67
+ if (sessions[u] && sessions[u].user !== session) {
68
+ throw new Error('Session ID mismatch');
69
+ }
70
+
71
+ games[game] = games[game] ?? {
72
+ moves: [],
73
+ players: [],
74
+ private: params.private || false,
75
+ creation: Date.now(),
76
+ };
77
+ const moves = games[game]!.moves;
78
+
79
+ const players = [...new Set(moves.map((play) => play.username))];
80
+ if (players.length >= 2 && !players.includes(params.username!)) {
81
+ throw new Error('Game is full, you can only watch.');
82
+ }
83
+
84
+ if (moves.length > 0 && moves[moves.length - 1]!.username === params.username) {
85
+ throw new Error('Please wait for the other player to make a move.');
86
+ }
87
+
88
+ if (moves.some((play) => play.move === move)) {
89
+ throw new Error('Move already made. Please choose a different move.');
90
+ }
91
+
92
+ const play: TicTacToeMove = { username: params.username!, move, session };
93
+ moves.push(play);
94
+
95
+ const result = checkGame(moves);
96
+ if (result.winner || result.tie) {
97
+ setTimeout(() => delete games[game!], GAME_CLEANUP_TTL);
98
+ return {
99
+ message: `Move sent successfully. ${result.winner ? result.winner + ' wins. ' + result.loser + ' loses.' : "It's a tie."}`,
100
+ ...result,
101
+ };
102
+ }
103
+
104
+ setTimeout(() => delete games[game!], SESSION_TTL);
105
+
106
+ sessions[u] = sessions[u] ?? { user: session, last: now };
107
+ sessions[u]!.last = now;
108
+ setTimeout(() => {
109
+ if (sessions[u] && now - sessions[u].last >= SESSION_TTL) delete sessions[u];
110
+ }, SESSION_TTL);
111
+
112
+ return { message: 'Move sent successfully' };
113
+ }
114
+
115
+ function fetchGame(params: TicTacToeParams, games: Record<string, TicTacToeGame>, u: string): Record<string, unknown> {
116
+ const id = params.game || generateGameId();
117
+ if (!games[id]) {
118
+ games[id] = {
119
+ moves: [],
120
+ players: [],
121
+ private: params.private || false,
122
+ creation: Date.now(),
123
+ };
124
+ }
125
+ if (!games[id]!.players.includes(u)) {
126
+ games[id]!.players.push(u);
127
+ }
128
+
129
+ const moves = games[id]!.moves;
130
+ const players = games[id]!.players;
131
+ const privateGame = games[id]!.private;
132
+ const lastPlayer = moves.length ? moves[moves.length - 1]!.username : null;
133
+ const turn = players.find((p) => p !== lastPlayer) || players[0];
134
+ const status = players.length >= 2 ? 'ready' : 'waiting';
135
+ const result = moves.length ? checkGame(moves) : {};
136
+
137
+ return {
138
+ id,
139
+ moves,
140
+ players,
141
+ turn,
142
+ status,
143
+ private: privateGame,
144
+ ...result,
145
+ };
146
+ }
147
+
148
+ function listGames(games: Record<string, TicTacToeGame>): Record<string, unknown> {
149
+ const publicGames: Record<string, unknown>[] = [];
150
+ const now = Date.now();
151
+
152
+ for (const [gameId, game] of Object.entries(games)) {
153
+ if (!game.private) {
154
+ const result = game.moves.length ? checkGame(game.moves) : {};
155
+ const isFinished = result.winner || result.tie;
156
+
157
+ if (!isFinished) {
158
+ const lastPlayer = game.moves.length ? game.moves[game.moves.length - 1]!.username : null;
159
+ const turn = game.players.find((p) => p !== lastPlayer) || game.players[0];
160
+ const status = game.players.length >= 2 ? 'ready' : 'waiting';
161
+
162
+ publicGames.push({
163
+ id: gameId,
164
+ players: game.players,
165
+ playersCount: game.players.length,
166
+ moves: game.moves.length,
167
+ turn,
168
+ status,
169
+ creation: game.creation || now,
170
+ });
171
+ }
172
+ }
173
+ }
174
+
175
+ publicGames.sort((a, b) => (b.creation as number) - (a.creation as number));
176
+
177
+ return {
178
+ message: 'Public games available',
179
+ count: publicGames.length,
180
+ games: publicGames,
181
+ };
182
+ }
183
+
184
+ function generateGameId(): string {
185
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
186
+ return Array.from(randomBytes(5))
187
+ .map((b) => chars[b % chars.length])
188
+ .join('');
189
+ }
190
+
191
+ function checkGame(moves: TicTacToeMove[]): GameResult {
192
+ const board: (string | null)[][] = Array(3)
193
+ .fill(null)
194
+ .map(() => Array(3).fill(null) as (string | null)[]);
195
+ const playerSymbols: Record<string, string> = {};
196
+ const playersOrder: string[] = [];
197
+
198
+ moves.forEach(({ username, move }) => {
199
+ if (!playerSymbols[username]) {
200
+ playersOrder.push(username);
201
+ playerSymbols[username] = playersOrder.length === 1 ? 'X' : 'O';
202
+ }
203
+ const [row, col] = move.split('-').map(Number) as [number, number];
204
+ board[row - 1]![col - 1] = playerSymbols[username]!;
205
+ });
206
+
207
+ const checkWinner = (symbol: string): boolean => {
208
+ for (let i = 0; i < 3; i++) {
209
+ if (board[i]![0] === symbol && board[i]![1] === symbol && board[i]![2] === symbol) return true;
210
+ if (board[0]![i] === symbol && board[1]![i] === symbol && board[2]![i] === symbol) return true;
211
+ }
212
+ if (board[0]![0] === symbol && board[1]![1] === symbol && board[2]![2] === symbol) return true;
213
+ if (board[0]![2] === symbol && board[1]![1] === symbol && board[2]![0] === symbol) return true;
214
+ return false;
215
+ };
216
+
217
+ const winner = Object.keys(playerSymbols).find((player) => checkWinner(playerSymbols[player]!)) ?? null;
218
+ const isTie = !winner && moves.length === 9;
219
+ const loser =
220
+ winner && playersOrder.length === 2 ? (playersOrder.find((player) => player !== winner) ?? null) : null;
221
+
222
+ return { winner, loser, tie: isTie };
223
+ }
@@ -0,0 +1,92 @@
1
+ const validFormats = [
2
+ 'iso',
3
+ 'utc',
4
+ 'timestamp',
5
+ 'locale',
6
+ 'date',
7
+ 'time',
8
+ 'year',
9
+ 'month',
10
+ 'day',
11
+ 'hour',
12
+ 'minute',
13
+ 'second',
14
+ 'ms',
15
+ 'dayOfWeek',
16
+ 'dayOfYear',
17
+ 'weekNumber',
18
+ 'timezone',
19
+ 'timezoneOffset',
20
+ ] as const;
21
+
22
+ const validTimezones = ['UTC', 'America/New_York', 'Europe/Paris', 'Asia/Tokyo', 'Australia/Sydney'] as const;
23
+
24
+ type TimeFormat = (typeof validFormats)[number];
25
+ type Timezone = (typeof validTimezones)[number];
26
+
27
+ export default function time(
28
+ type: string = 'live',
29
+ start?: string,
30
+ end?: string,
31
+ format?: string,
32
+ timezone?: string,
33
+ ): Record<string, unknown> {
34
+ if (type !== 'live' && type !== 'random') {
35
+ throw new Error('Please provide a valid type (live or random)');
36
+ }
37
+
38
+ if (start && !Date.parse(start)) {
39
+ throw new Error('Please provide a valid start date (YYYY-MM-DD)');
40
+ }
41
+
42
+ if (end && !Date.parse(end)) {
43
+ throw new Error('Please provide a valid end date (YYYY-MM-DD)');
44
+ }
45
+
46
+ if (format && !validFormats.includes(format as TimeFormat)) {
47
+ throw new Error(`Please provide a valid format. Options: ${validFormats.join(', ')}`);
48
+ }
49
+
50
+ if (timezone && !validTimezones.includes(timezone as Timezone)) {
51
+ throw new Error(`Please provide a valid timezone. Options: ${validTimezones.join(', ')}`);
52
+ }
53
+
54
+ const getTimeFormats = (date: Date, tz: string): Record<string, unknown> => {
55
+ return {
56
+ iso: date.toISOString(),
57
+ utc: date.toUTCString(),
58
+ timestamp: date.getTime(),
59
+ locale: date.toLocaleString('en-US', { timeZone: tz, timeZoneName: 'long' }),
60
+ date: date.toLocaleDateString('en-US', { timeZone: tz }),
61
+ time: date.toLocaleTimeString('en-US', { timeZone: tz }),
62
+ year: date.getFullYear(),
63
+ month: date.getMonth() + 1,
64
+ day: date.getDate(),
65
+ hour: date.getHours(),
66
+ minute: date.getMinutes(),
67
+ second: date.getSeconds(),
68
+ ms: date.getMilliseconds(),
69
+ dayOfWeek: date.getDay(),
70
+ dayOfYear: Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000),
71
+ weekNumber: Math.ceil(((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000 + 1) / 7),
72
+ timezone: tz,
73
+ timezoneOffset: date.getTimezoneOffset(),
74
+ };
75
+ };
76
+
77
+ if (type === 'random') {
78
+ const startDate = start ? new Date(start).getTime() : new Date('1900-01-01').getTime();
79
+ const endDate = end ? new Date(end).getTime() : new Date('2100-12-31').getTime();
80
+ const randomDate = new Date(startDate + Math.random() * (endDate - startDate));
81
+ const tz = timezone || validTimezones[Math.floor(Math.random() * validTimezones.length)]!;
82
+ const formats = getTimeFormats(randomDate, tz);
83
+
84
+ return format ? { date: formats[format] } : formats;
85
+ }
86
+
87
+ const now = new Date();
88
+ const tz = timezone || 'UTC';
89
+ const formats = getTimeFormats(now, tz);
90
+
91
+ return format ? { date: formats[format] } : formats;
92
+ }