@20syldev/api 4.7.0 → 4.9.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 (191) hide show
  1. package/README.md +84 -47
  2. package/dist/app.js +6 -6
  3. package/dist/app.js.map +1 -1
  4. package/dist/config/env.js.map +1 -1
  5. package/dist/config/versions.js +22 -3
  6. package/dist/config/versions.js.map +1 -1
  7. package/dist/constants.js +27 -12
  8. package/dist/constants.js.map +1 -1
  9. package/dist/middleware/error.js +2 -2
  10. package/dist/middleware/error.js.map +1 -1
  11. package/dist/middleware/logger.js.map +1 -1
  12. package/dist/middleware/ratelimit.js +2 -2
  13. package/dist/middleware/ratelimit.js.map +1 -1
  14. package/dist/middleware/version.js.map +1 -1
  15. package/dist/modules/v3.js +1 -1
  16. package/dist/modules/v3.js.map +1 -1
  17. package/{src/modules/v4/address.ts → dist/modules/v4/address.js} +14 -46
  18. package/dist/modules/v4/address.js.map +1 -0
  19. package/{src/modules/v4/agent.ts → dist/modules/v4/agent.js} +26 -38
  20. package/dist/modules/v4/agent.js.map +1 -0
  21. package/dist/modules/v4/algorithms.js +79 -0
  22. package/dist/modules/v4/algorithms.js.map +1 -1
  23. package/dist/modules/v4/avatar.js +136 -0
  24. package/dist/modules/v4/avatar.js.map +1 -0
  25. package/dist/modules/v4/barcode.js +440 -0
  26. package/dist/modules/v4/barcode.js.map +1 -0
  27. package/dist/modules/v4/captcha.js +7 -0
  28. package/dist/modules/v4/captcha.js.map +1 -1
  29. package/dist/modules/v4/chat.js +9 -1
  30. package/dist/modules/v4/chat.js.map +1 -1
  31. package/dist/modules/v4/color.js +8 -1
  32. package/dist/modules/v4/color.js.map +1 -1
  33. package/dist/modules/v4/convert.js +9 -0
  34. package/dist/modules/v4/convert.js.map +1 -1
  35. package/dist/modules/v4/credit.js +104 -0
  36. package/dist/modules/v4/credit.js.map +1 -0
  37. package/dist/modules/v4/cron.js +202 -0
  38. package/dist/modules/v4/cron.js.map +1 -0
  39. package/dist/modules/v4/dice.js +7 -0
  40. package/dist/modules/v4/dice.js.map +1 -1
  41. package/dist/modules/v4/domain.js +6 -1
  42. package/dist/modules/v4/domain.js.map +1 -1
  43. package/dist/modules/v4/encode.js +71 -0
  44. package/dist/modules/v4/encode.js.map +1 -1
  45. package/dist/modules/v4/geo.js +11 -1
  46. package/dist/modules/v4/geo.js.map +1 -1
  47. package/dist/modules/v4/hash.js +10 -1
  48. package/dist/modules/v4/hash.js.map +1 -1
  49. package/dist/modules/v4/hyperplanning.js +9 -1
  50. package/dist/modules/v4/hyperplanning.js.map +1 -1
  51. package/{src/modules/v4/ip.ts → dist/modules/v4/ip.js} +58 -60
  52. package/dist/modules/v4/ip.js.map +1 -0
  53. package/dist/modules/v4/levenshtein.js +8 -0
  54. package/dist/modules/v4/levenshtein.js.map +1 -1
  55. package/dist/modules/v4/palette.js +9 -1
  56. package/dist/modules/v4/palette.js.map +1 -1
  57. package/{src/modules/v4/password.ts → dist/modules/v4/password.js} +25 -63
  58. package/dist/modules/v4/password.js.map +1 -0
  59. package/dist/modules/v4/personal.js +14 -18
  60. package/dist/modules/v4/personal.js.map +1 -1
  61. package/dist/modules/v4/placeholder.js +8 -0
  62. package/dist/modules/v4/placeholder.js.map +1 -1
  63. package/dist/modules/v4/qrcode.js +10 -3
  64. package/dist/modules/v4/qrcode.js.map +1 -1
  65. package/dist/modules/v4/regex.js +48 -0
  66. package/dist/modules/v4/regex.js.map +1 -0
  67. package/dist/modules/v4/statistics.js +7 -0
  68. package/dist/modules/v4/statistics.js.map +1 -1
  69. package/dist/modules/v4/text.js +30 -0
  70. package/dist/modules/v4/text.js.map +1 -1
  71. package/dist/modules/v4/tic_tac_toe.js +9 -1
  72. package/dist/modules/v4/tic_tac_toe.js.map +1 -1
  73. package/dist/modules/v4/time.js +62 -11
  74. package/dist/modules/v4/time.js.map +1 -1
  75. package/dist/modules/v4/token.js +17 -8
  76. package/dist/modules/v4/token.js.map +1 -1
  77. package/dist/modules/v4/username.js +5 -0
  78. package/dist/modules/v4/username.js.map +1 -1
  79. package/dist/modules/v4/validate.js +21 -0
  80. package/dist/modules/v4/validate.js.map +1 -1
  81. package/dist/modules/v4.js +10 -1
  82. package/dist/modules/v4.js.map +1 -1
  83. package/dist/routes/delete.js.map +1 -1
  84. package/dist/routes/get.js +208 -48
  85. package/dist/routes/get.js.map +1 -1
  86. package/dist/routes/index.js +2 -2
  87. package/dist/routes/index.js.map +1 -1
  88. package/dist/routes/patch.js.map +1 -1
  89. package/dist/routes/post.js +2 -2
  90. package/dist/routes/post.js.map +1 -1
  91. package/dist/utils/response.js.map +1 -1
  92. package/package.json +2 -1
  93. package/.github/FUNDING.yml +0 -1
  94. package/.prettierrc +0 -7
  95. package/eslint.config.js +0 -24
  96. package/robots.txt +0 -74
  97. package/src/app.ts +0 -53
  98. package/src/config/env.ts +0 -87
  99. package/src/config/plans.ts +0 -61
  100. package/src/config/versions.ts +0 -119
  101. package/src/constants.ts +0 -46
  102. package/src/favicon.ico +0 -0
  103. package/src/middleware/cors.ts +0 -23
  104. package/src/middleware/error.ts +0 -16
  105. package/src/middleware/json.ts +0 -9
  106. package/src/middleware/logger.ts +0 -32
  107. package/src/middleware/ratelimit.ts +0 -89
  108. package/src/middleware/version.ts +0 -40
  109. package/src/modules/v3/algorithms.js +0 -218
  110. package/src/modules/v3/captcha.js +0 -60
  111. package/src/modules/v3/chat.js +0 -111
  112. package/src/modules/v3/color.js +0 -60
  113. package/src/modules/v3/convert.js +0 -39
  114. package/src/modules/v3/domain.js +0 -86
  115. package/src/modules/v3/hash.js +0 -16
  116. package/src/modules/v3/hyperplanning.js +0 -56
  117. package/src/modules/v3/levenshtein.js +0 -46
  118. package/src/modules/v3/personal.js +0 -139
  119. package/src/modules/v3/qrcode.js +0 -20
  120. package/src/modules/v3/tic_tac_toe.js +0 -242
  121. package/src/modules/v3/time.js +0 -98
  122. package/src/modules/v3/token.js +0 -54
  123. package/src/modules/v3/username.js +0 -94
  124. package/src/modules/v3/utils.js +0 -77
  125. package/src/modules/v3.js +0 -15
  126. package/src/modules/v4/algorithms.ts +0 -276
  127. package/src/modules/v4/captcha.ts +0 -114
  128. package/src/modules/v4/chat.ts +0 -130
  129. package/src/modules/v4/color.ts +0 -75
  130. package/src/modules/v4/convert.ts +0 -127
  131. package/src/modules/v4/dice.ts +0 -46
  132. package/src/modules/v4/domain.ts +0 -86
  133. package/src/modules/v4/encode.ts +0 -241
  134. package/src/modules/v4/geo.ts +0 -63
  135. package/src/modules/v4/hash.ts +0 -32
  136. package/src/modules/v4/hyperplanning.ts +0 -93
  137. package/src/modules/v4/levenshtein.ts +0 -46
  138. package/src/modules/v4/palette.ts +0 -71
  139. package/src/modules/v4/personal.ts +0 -140
  140. package/src/modules/v4/placeholder.ts +0 -197
  141. package/src/modules/v4/qrcode.ts +0 -121
  142. package/src/modules/v4/statistics.ts +0 -62
  143. package/src/modules/v4/text.ts +0 -330
  144. package/src/modules/v4/tic_tac_toe.ts +0 -264
  145. package/src/modules/v4/time.ts +0 -103
  146. package/src/modules/v4/token.ts +0 -53
  147. package/src/modules/v4/username.ts +0 -94
  148. package/src/modules/v4/validate.ts +0 -76
  149. package/src/modules/v4.ts +0 -27
  150. package/src/routes/delete.ts +0 -72
  151. package/src/routes/get.ts +0 -835
  152. package/src/routes/index.ts +0 -59
  153. package/src/routes/patch.ts +0 -45
  154. package/src/routes/post.ts +0 -209
  155. package/src/storage/index.ts +0 -16
  156. package/src/types/express.d.ts +0 -17
  157. package/src/types/storage.ts +0 -48
  158. package/src/utils/colors.ts +0 -91
  159. package/src/utils/helpers.ts +0 -90
  160. package/src/utils/response.ts +0 -28
  161. package/tests/integration/api.test.ts +0 -716
  162. package/tests/tsconfig.json +0 -3
  163. package/tests/unit/address.test.ts +0 -84
  164. package/tests/unit/agent.test.ts +0 -113
  165. package/tests/unit/algorithms.test.ts +0 -121
  166. package/tests/unit/captcha.test.ts +0 -62
  167. package/tests/unit/chat.test.ts +0 -54
  168. package/tests/unit/color.test.ts +0 -54
  169. package/tests/unit/convert.test.ts +0 -68
  170. package/tests/unit/dice.test.ts +0 -58
  171. package/tests/unit/domain.test.ts +0 -48
  172. package/tests/unit/encode.test.ts +0 -109
  173. package/tests/unit/geo.test.ts +0 -46
  174. package/tests/unit/hash.test.ts +0 -45
  175. package/tests/unit/hyperplanning.test.ts +0 -97
  176. package/tests/unit/ip.test.ts +0 -140
  177. package/tests/unit/levenshtein.test.ts +0 -35
  178. package/tests/unit/palette.test.ts +0 -54
  179. package/tests/unit/password.test.ts +0 -86
  180. package/tests/unit/personal.test.ts +0 -66
  181. package/tests/unit/placeholder.test.ts +0 -58
  182. package/tests/unit/qrcode.test.ts +0 -83
  183. package/tests/unit/statistics.test.ts +0 -58
  184. package/tests/unit/text.test.ts +0 -108
  185. package/tests/unit/tic_tac_toe.test.ts +0 -56
  186. package/tests/unit/time.test.ts +0 -68
  187. package/tests/unit/token.test.ts +0 -62
  188. package/tests/unit/username.test.ts +0 -35
  189. package/tests/unit/validate.test.ts +0 -72
  190. package/tsconfig.json +0 -17
  191. package/tsconfig.test.json +0 -9
@@ -1,58 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import placeholder from '../../src/modules/v4/placeholder.js';
5
-
6
- describe('placeholder', () => {
7
- test('image returns SVG with text', () => {
8
- const result = placeholder('image', { width: '200', height: '100', text: 'Hello' });
9
- assert.equal(result.type, 'image');
10
- assert.equal(result.contentType, 'image/svg+xml');
11
- assert.match(result.body, /<svg/);
12
- assert.match(result.body, /Hello/);
13
- });
14
-
15
- test('skeleton returns SVG with animation', () => {
16
- const result = placeholder('skeleton', { width: '300', height: '200' });
17
- assert.equal(result.type, 'skeleton');
18
- assert.equal(result.contentType, 'image/svg+xml');
19
- assert.match(result.body, /<svg/);
20
- });
21
-
22
- test('default dimensions 800x600', () => {
23
- const result = placeholder('image', {});
24
- assert.match(result.body, /width="800"/);
25
- assert.match(result.body, /height="600"/);
26
- });
27
-
28
- test('skeleton with avatar', () => {
29
- const result = placeholder('skeleton', { avatar: 'circle' });
30
- assert.match(result.body, /<circle/);
31
- });
32
-
33
- test('skeleton with rows', () => {
34
- const result = placeholder('skeleton', { rows: '5' });
35
- assert.match(result.body, /<rect/);
36
- });
37
-
38
- test('animate none removes animation', () => {
39
- const result = placeholder('skeleton', { animate: 'none' });
40
- assert.ok(!result.body.includes('<animate'));
41
- });
42
-
43
- test('throws on invalid type', () => {
44
- assert.throws(() => placeholder('video', {}), /Type must be one of/);
45
- });
46
-
47
- test('throws on invalid width', () => {
48
- assert.throws(() => placeholder('image', { width: '5000' }), /width/);
49
- });
50
-
51
- test('throws on invalid avatar shape', () => {
52
- assert.throws(() => placeholder('skeleton', { avatar: 'hexagon' }), /avatar/);
53
- });
54
-
55
- test('throws on invalid speed', () => {
56
- assert.throws(() => placeholder('skeleton', { speed: '99' }), /speed/);
57
- });
58
- });
@@ -1,83 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import qrcode from '../../src/modules/v4/qrcode.js';
5
-
6
- const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
7
-
8
- describe('qrcode', () => {
9
- test('returns PNG buffer by default', async () => {
10
- const result = await qrcode({ url: 'https://example.com' });
11
- assert.equal(result.contentType, 'image/png');
12
- assert.ok(Buffer.isBuffer(result.body));
13
- assert.ok((result.body as Buffer).subarray(0, 4).equals(PNG_MAGIC));
14
- });
15
-
16
- test('returns data URL with format=base64', async () => {
17
- const result = await qrcode({ url: 'https://example.com', format: 'base64' });
18
- assert.equal(result.contentType, 'application/json');
19
- assert.match(result.body as string, /^data:image\/png;base64,/);
20
- });
21
-
22
- test('throws on invalid format', async () => {
23
- await assert.rejects(() => qrcode({ url: 'https://example.com', format: 'jpg' as 'png' }), /format/);
24
- });
25
-
26
- test('throws on missing url', async () => {
27
- await assert.rejects(() => qrcode({ url: '' }));
28
- });
29
-
30
- test('accepts custom size', async () => {
31
- const result = await qrcode({ url: 'https://example.com', size: 400 });
32
- assert.equal(result.contentType, 'image/png');
33
- });
34
-
35
- test('throws on invalid size', async () => {
36
- await assert.rejects(() => qrcode({ url: 'https://example.com', size: 9999 }), /size/);
37
- });
38
-
39
- test('accepts custom colors', async () => {
40
- const result = await qrcode({ url: 'https://example.com', dark: 'ff0000', light: 'ffffff' });
41
- assert.equal(result.contentType, 'image/png');
42
- });
43
-
44
- test('throws on invalid dark color', async () => {
45
- await assert.rejects(() => qrcode({ url: 'https://example.com', dark: 'xyz' }), /color/i);
46
- });
47
-
48
- test('throws on invalid light color', async () => {
49
- await assert.rejects(() => qrcode({ url: 'https://example.com', light: 'nope' }), /color/i);
50
- });
51
-
52
- test('accepts correction level', async () => {
53
- const result = await qrcode({ url: 'https://example.com', correction: 'H' });
54
- assert.equal(result.contentType, 'image/png');
55
- });
56
-
57
- test('throws on invalid correction level', async () => {
58
- await assert.rejects(() => qrcode({ url: 'https://example.com', correction: 'X' as 'L' }), /correction/);
59
- });
60
-
61
- test('accepts custom margin', async () => {
62
- const result = await qrcode({ url: 'https://example.com', margin: 0 });
63
- assert.equal(result.contentType, 'image/png');
64
- });
65
-
66
- test('throws on invalid margin', async () => {
67
- await assert.rejects(() => qrcode({ url: 'https://example.com', margin: 99 }), /margin/);
68
- });
69
-
70
- test('throws on non-HTTPS icon URL', async () => {
71
- await assert.rejects(
72
- () => qrcode({ url: 'https://example.com', icon: 'http://example.com/icon.png' }),
73
- /HTTPS/,
74
- );
75
- });
76
-
77
- test('throws on unreachable icon URL', async () => {
78
- await assert.rejects(
79
- () => qrcode({ url: 'https://example.com', icon: 'https://localhost:1/nope.png' }),
80
- /Failed to fetch icon|fetch/i,
81
- );
82
- });
83
- });
@@ -1,58 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import statistics from '../../src/modules/v4/statistics.js';
5
-
6
- describe('statistics', () => {
7
- test('basic computation on integers', () => {
8
- const result = statistics('1,2,3,4,5');
9
- assert.equal(result.count, 5);
10
- assert.equal(result.sum, 15);
11
- assert.equal(result.min, 1);
12
- assert.equal(result.max, 5);
13
- assert.equal(result.range, 4);
14
- assert.equal(result.mean, 3);
15
- assert.equal(result.median, 3);
16
- });
17
-
18
- test('median on even count', () => {
19
- const result = statistics('1,2,3,4');
20
- assert.equal(result.median, 2.5);
21
- });
22
-
23
- test('mode is empty when all values unique', () => {
24
- const result = statistics('1,2,3,4,5');
25
- assert.deepEqual(result.mode, []);
26
- });
27
-
28
- test('mode detects most frequent value', () => {
29
- const result = statistics('1,2,2,3,3,3');
30
- assert.deepEqual(result.mode, [3]);
31
- });
32
-
33
- test('multiple modes', () => {
34
- const result = statistics('1,1,2,2,3');
35
- assert.deepEqual(result.mode.sort(), [1, 2]);
36
- });
37
-
38
- test('stddev of identical values is 0', () => {
39
- const result = statistics('5,5,5,5');
40
- assert.equal(result.stddev, 0);
41
- assert.equal(result.variance, 0);
42
- });
43
-
44
- test('handles negative and decimal values', () => {
45
- const result = statistics('-1.5,0,1.5');
46
- assert.equal(result.mean, 0);
47
- assert.equal(result.min, -1.5);
48
- assert.equal(result.max, 1.5);
49
- });
50
-
51
- test('throws on non-numeric values', () => {
52
- assert.throws(() => statistics('1,abc,3'), /must contain only numbers/);
53
- });
54
-
55
- test('throws on missing values', () => {
56
- assert.throws(() => statistics(''), /required/);
57
- });
58
- });
@@ -1,108 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import { lorem, number, slug, stats } from '../../src/modules/v4/text.js';
5
-
6
- describe('text', () => {
7
- describe('slug', () => {
8
- test('basic slug', () => {
9
- assert.equal(slug('Hello World'), 'hello-world');
10
- });
11
-
12
- test('strips diacritics', () => {
13
- assert.equal(slug('Crème Brûlée'), 'creme-brulee');
14
- });
15
-
16
- test('strips special chars', () => {
17
- assert.equal(slug('Mon Article #1 !'), 'mon-article-1');
18
- });
19
-
20
- test('collapses multiple spaces', () => {
21
- assert.equal(slug('a b c'), 'a-b-c');
22
- });
23
- });
24
-
25
- describe('stats', () => {
26
- test('counts characters and words', () => {
27
- const result = stats('Hello world');
28
- assert.equal(result.characters, 11);
29
- assert.equal(result.charactersNoSpaces, 10);
30
- assert.equal(result.words, 2);
31
- });
32
-
33
- test('counts sentences', () => {
34
- const result = stats('Hi. How are you? Fine!');
35
- assert.equal(result.sentences, 3);
36
- });
37
-
38
- test('reading time format', () => {
39
- const result = stats('a '.repeat(200).trim());
40
- assert.match(result.readingTime, /min/);
41
- });
42
-
43
- test('short text returns seconds', () => {
44
- const result = stats('hello world');
45
- assert.match(result.readingTime, /s$/);
46
- });
47
- });
48
-
49
- describe('lorem', () => {
50
- test('words count', () => {
51
- const result = lorem('words', '10');
52
- assert.equal(result.split(' ').length, 10);
53
- });
54
-
55
- test('sentences end with period', () => {
56
- const result = lorem('sentences', '3');
57
- const sentences = result.split('. ');
58
- assert.equal(sentences.length, 3);
59
- });
60
-
61
- test('paragraphs separated by double newline', () => {
62
- const result = lorem('paragraphs', '2');
63
- assert.equal(result.split('\n\n').length, 2);
64
- });
65
-
66
- test('throws on invalid type', () => {
67
- assert.throws(() => lorem('verses', '5'), /Type must be one of/);
68
- });
69
- });
70
-
71
- describe('number', () => {
72
- test('English: 42', () => {
73
- assert.equal(number('42', 'en'), 'forty-two');
74
- });
75
-
76
- test('English: 100', () => {
77
- assert.equal(number('100', 'en'), 'one hundred');
78
- });
79
-
80
- test('English: 1234', () => {
81
- assert.equal(number('1234', 'en'), 'one thousand two hundred thirty-four');
82
- });
83
-
84
- test('French: 21', () => {
85
- assert.equal(number('21', 'fr'), 'vingt et un');
86
- });
87
-
88
- test('French: 80', () => {
89
- assert.equal(number('80', 'fr'), 'quatre-vingts');
90
- });
91
-
92
- test('French: 0', () => {
93
- assert.equal(number('0', 'fr'), 'zéro');
94
- });
95
-
96
- test('English: negative', () => {
97
- assert.equal(number('-5', 'en'), 'minus five');
98
- });
99
-
100
- test('throws on invalid lang', () => {
101
- assert.throws(() => number('42', 'es'), /Lang must be one of/);
102
- });
103
-
104
- test('throws on non-integer', () => {
105
- assert.throws(() => number('3.14', 'en'), /must be an integer/);
106
- });
107
- });
108
- });
@@ -1,56 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import tic_tac_toe from '../../src/modules/v4/tic_tac_toe.js';
5
- import type { TicTacToeStorage } from '../../src/types/storage.js';
6
-
7
- function makeStorage(): TicTacToeStorage {
8
- return { games: {}, sessions: {}, rateLimits: {} };
9
- }
10
-
11
- describe('tic_tac_toe', () => {
12
- test('list returns empty games array', () => {
13
- const result = tic_tac_toe('list', { storage: makeStorage() });
14
- assert.ok(Array.isArray(result.games));
15
- });
16
-
17
- test('play creates a game and makes a move', () => {
18
- const storage = makeStorage();
19
- const result = tic_tac_toe('play', { username: 'alice', move: '1-1', session: 'a1', game: 'TEST1', storage });
20
- assert.ok((result.message as string).includes('Move sent'));
21
- });
22
-
23
- test('fetch returns game state', () => {
24
- const storage = makeStorage();
25
- tic_tac_toe('play', { username: 'alice', move: '1-1', session: 'a1', game: 'TEST2', storage });
26
- const result = tic_tac_toe('fetch', { username: 'alice', game: 'TEST2', storage });
27
- assert.equal(result.id, 'TEST2');
28
- });
29
-
30
- test('forfeit throws on non-player', () => {
31
- const storage = makeStorage();
32
- tic_tac_toe('play', { username: 'alice', move: '1-1', session: 'a1', game: 'TEST3', storage });
33
- assert.throws(
34
- () => tic_tac_toe('forfeit', { username: 'charlie', session: 'c1', game: 'TEST3', storage }),
35
- /not a player/,
36
- );
37
- });
38
-
39
- test('throws on missing username', () => {
40
- assert.throws(
41
- () => tic_tac_toe('play', { move: '1-1', session: 's', game: 'G', storage: makeStorage() }),
42
- /username/,
43
- );
44
- });
45
-
46
- test('throws on missing move', () => {
47
- assert.throws(
48
- () => tic_tac_toe('play', { username: 'alice', session: 's', game: 'G', storage: makeStorage() }),
49
- /move/i,
50
- );
51
- });
52
-
53
- test('throws on invalid action', () => {
54
- assert.throws(() => tic_tac_toe('reset', { username: 'alice', storage: makeStorage() }), /Invalid action/);
55
- });
56
- });
@@ -1,68 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import time from '../../src/modules/v4/time.js';
5
-
6
- describe('time', () => {
7
- test('live returns full set of formats', () => {
8
- const result = time('live');
9
- const fields = [
10
- 'iso',
11
- 'utc',
12
- 'timestamp',
13
- 'locale',
14
- 'date',
15
- 'time',
16
- 'year',
17
- 'month',
18
- 'day',
19
- 'hour',
20
- 'minute',
21
- 'second',
22
- 'ms',
23
- 'dayOfWeek',
24
- 'dayOfYear',
25
- 'weekNumber',
26
- 'timezone',
27
- 'timezoneOffset',
28
- ];
29
- for (const f of fields) {
30
- assert.ok(f in result, `missing field: ${f}`);
31
- }
32
- });
33
-
34
- test('iso is parseable', () => {
35
- const result = time('live');
36
- assert.ok(!isNaN(Date.parse(result.iso as string)));
37
- });
38
-
39
- test('format=year returns only date', () => {
40
- const result = time('live', undefined, undefined, 'year');
41
- assert.ok('date' in result);
42
- assert.equal(typeof result.date, 'number');
43
- });
44
-
45
- test('random within range', () => {
46
- const result = time('random', '2020-01-01', '2020-12-31');
47
- const ts = result.timestamp as number;
48
- assert.ok(ts >= new Date('2020-01-01').getTime());
49
- assert.ok(ts <= new Date('2020-12-31').getTime());
50
- });
51
-
52
- test('throws on invalid type', () => {
53
- assert.throws(() => time('foo'), /valid type/);
54
- });
55
-
56
- test('throws on invalid format', () => {
57
- assert.throws(() => time('live', undefined, undefined, 'fakeformat'), /valid format/);
58
- });
59
-
60
- test('throws on invalid timezone', () => {
61
- assert.throws(() => time('live', undefined, undefined, undefined, 'Mars/Olympus'), /valid timezone/);
62
- });
63
-
64
- test('Europe/Paris timezone', () => {
65
- const result = time('live', undefined, undefined, undefined, 'Europe/Paris');
66
- assert.equal(result.timezone, 'Europe/Paris');
67
- });
68
- });
@@ -1,62 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import token from '../../src/modules/v4/token.js';
5
-
6
- describe('token', () => {
7
- test('alpha length', () => {
8
- const t = token(24, 'alpha');
9
- assert.equal(t.length, 24);
10
- assert.match(t, /^[a-zA-Z]+$/);
11
- });
12
-
13
- test('alphanum length', () => {
14
- const t = token(32, 'alphanum');
15
- assert.equal(t.length, 32);
16
- assert.match(t, /^[a-zA-Z0-9]+$/);
17
- });
18
-
19
- test('hex contains only hex chars', () => {
20
- const t = token(16, 'hex');
21
- assert.equal(t.length, 16);
22
- assert.match(t, /^[0-9a-f]+$/);
23
- });
24
-
25
- test('num contains only digits', () => {
26
- const t = token(20, 'num');
27
- assert.match(t, /^\d+$/);
28
- });
29
-
30
- test('urlsafe chars only', () => {
31
- const t = token(40, 'urlsafe');
32
- assert.match(t, /^[a-zA-Z0-9_-]+$/);
33
- });
34
-
35
- test('uuid type produces hex', () => {
36
- const t = token(32, 'uuid');
37
- assert.equal(t.length, 32);
38
- });
39
-
40
- test('default type alphanum', () => {
41
- const t = token(20);
42
- assert.match(t, /^[a-zA-Z0-9]+$/);
43
- });
44
-
45
- test('throws below minimum length', () => {
46
- assert.throws(() => token(5, 'alpha'), /greater than or equal to 12/);
47
- });
48
-
49
- test('throws above maximum length', () => {
50
- assert.throws(() => token(5000, 'alpha'), /4096/);
51
- });
52
-
53
- test('throws on invalid type', () => {
54
- assert.throws(() => token(20, 'martian'), /Invalid token type/);
55
- });
56
-
57
- test('two tokens are different (entropy check)', () => {
58
- const a = token(32, 'alphanum');
59
- const b = token(32, 'alphanum');
60
- assert.notEqual(a, b);
61
- });
62
- });
@@ -1,35 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import username from '../../src/modules/v4/username.js';
5
-
6
- describe('username', () => {
7
- test('returns expected fields', () => {
8
- const result = username();
9
- assert.ok('username' in result);
10
- assert.ok('number' in result);
11
- assert.ok('adjective' in result);
12
- assert.ok('animal' in result);
13
- assert.ok('job' in result);
14
- });
15
-
16
- test('username is a non-empty string', () => {
17
- const result = username();
18
- assert.equal(typeof result.username, 'string');
19
- assert.ok((result.username as string).length > 0);
20
- });
21
-
22
- test('number is between 0 and 99', () => {
23
- const result = username();
24
- const n = result.number as number;
25
- assert.ok(n >= 0 && n <= 99);
26
- });
27
-
28
- test('produces varied usernames', () => {
29
- const seen = new Set<string>();
30
- for (let i = 0; i < 10; i++) {
31
- seen.add(username().username as string);
32
- }
33
- assert.ok(seen.size > 1);
34
- });
35
- });
@@ -1,72 +0,0 @@
1
- import { strict as assert } from 'node:assert';
2
- import { describe, test } from 'node:test';
3
-
4
- import { email, iban, luhn } from '../../src/modules/v4/validate.js';
5
-
6
- describe('validate', () => {
7
- describe('luhn', () => {
8
- test('valid Visa test card', () => {
9
- const result = luhn('4111111111111111');
10
- assert.equal(result.valid, true);
11
- });
12
-
13
- test('invalid card number', () => {
14
- const result = luhn('4111111111111112');
15
- assert.equal(result.valid, false);
16
- });
17
-
18
- test('strips spaces and dashes', () => {
19
- const result = luhn('4111-1111 1111-1111');
20
- assert.equal(result.valid, true);
21
- assert.equal(result.value, '4111111111111111');
22
- });
23
-
24
- test('throws on non-digit', () => {
25
- assert.throws(() => luhn('4111ABC11111111'), /only digits/);
26
- });
27
-
28
- test('throws on too short', () => {
29
- assert.throws(() => luhn('411111'), /between 12 and 19/);
30
- });
31
- });
32
-
33
- describe('iban', () => {
34
- test('valid French IBAN', () => {
35
- const result = iban('FR1420041010050500013M02606');
36
- assert.equal(result.valid, true);
37
- assert.equal(result.country, 'FR');
38
- });
39
-
40
- test('invalid checksum', () => {
41
- const result = iban('FR1420041010050500013M02607');
42
- assert.equal(result.valid, false);
43
- });
44
-
45
- test('strips spaces', () => {
46
- const result = iban('FR14 2004 1010 0505 0001 3M02 606');
47
- assert.equal(result.valid, true);
48
- });
49
-
50
- test('throws on bad format', () => {
51
- assert.throws(() => iban('1234'), /Invalid IBAN format/);
52
- });
53
- });
54
-
55
- describe('email', () => {
56
- test('valid email', () => {
57
- assert.equal(email('hello@example.com').valid, true);
58
- });
59
-
60
- test('rejects missing domain', () => {
61
- assert.equal(email('hello@').valid, false);
62
- });
63
-
64
- test('rejects missing @', () => {
65
- assert.equal(email('helloexample.com').valid, false);
66
- });
67
-
68
- test('rejects spaces', () => {
69
- assert.equal(email('hello world@example.com').valid, false);
70
- });
71
- });
72
- });
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "allowJs": true,
7
- "strict": true,
8
- "noUncheckedIndexedAccess": true,
9
- "outDir": "dist",
10
- "rootDir": "src",
11
- "declaration": false,
12
- "sourceMap": true,
13
- "esModuleInterop": true,
14
- "skipLibCheck": true
15
- },
16
- "include": ["src"]
17
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "rootDir": ".",
5
- "noEmit": true,
6
- "types": ["node"]
7
- },
8
- "include": ["src", "tests"]
9
- }