@20syldev/api 4.8.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.
- package/README.md +84 -47
- package/package.json +2 -1
- package/.github/FUNDING.yml +0 -1
- package/.prettierrc +0 -7
- package/eslint.config.js +0 -24
- package/robots.txt +0 -74
- package/src/app.ts +0 -53
- package/src/config/env.ts +0 -87
- package/src/config/plans.ts +0 -61
- package/src/config/versions.ts +0 -121
- package/src/constants.ts +0 -61
- package/src/favicon.ico +0 -0
- package/src/middleware/cors.ts +0 -23
- package/src/middleware/error.ts +0 -16
- package/src/middleware/json.ts +0 -9
- package/src/middleware/logger.ts +0 -32
- package/src/middleware/ratelimit.ts +0 -89
- package/src/middleware/version.ts +0 -40
- package/src/modules/v3/algorithms.js +0 -218
- package/src/modules/v3/captcha.js +0 -60
- package/src/modules/v3/chat.js +0 -111
- package/src/modules/v3/color.js +0 -60
- package/src/modules/v3/convert.js +0 -39
- package/src/modules/v3/domain.js +0 -86
- package/src/modules/v3/hash.js +0 -16
- package/src/modules/v3/hyperplanning.js +0 -56
- package/src/modules/v3/levenshtein.js +0 -46
- package/src/modules/v3/personal.js +0 -139
- package/src/modules/v3/qrcode.js +0 -20
- package/src/modules/v3/tic_tac_toe.js +0 -242
- package/src/modules/v3/time.js +0 -98
- package/src/modules/v3/token.js +0 -54
- package/src/modules/v3/username.js +0 -94
- package/src/modules/v3/utils.js +0 -77
- package/src/modules/v3.js +0 -15
- package/src/modules/v4/address.ts +0 -412
- package/src/modules/v4/agent.ts +0 -104
- package/src/modules/v4/algorithms.ts +0 -276
- package/src/modules/v4/captcha.ts +0 -114
- package/src/modules/v4/chat.ts +0 -130
- package/src/modules/v4/color.ts +0 -75
- package/src/modules/v4/convert.ts +0 -127
- package/src/modules/v4/cron.ts +0 -221
- package/src/modules/v4/dice.ts +0 -46
- package/src/modules/v4/domain.ts +0 -86
- package/src/modules/v4/encode.ts +0 -241
- package/src/modules/v4/geo.ts +0 -64
- package/src/modules/v4/hash.ts +0 -32
- package/src/modules/v4/hyperplanning.ts +0 -93
- package/src/modules/v4/ip.ts +0 -134
- package/src/modules/v4/levenshtein.ts +0 -46
- package/src/modules/v4/palette.ts +0 -71
- package/src/modules/v4/password.ts +0 -379
- package/src/modules/v4/personal.ts +0 -140
- package/src/modules/v4/placeholder.ts +0 -197
- package/src/modules/v4/qrcode.ts +0 -121
- package/src/modules/v4/regex.ts +0 -67
- package/src/modules/v4/statistics.ts +0 -62
- package/src/modules/v4/text.ts +0 -330
- package/src/modules/v4/tic_tac_toe.ts +0 -264
- package/src/modules/v4/time.ts +0 -149
- package/src/modules/v4/token.ts +0 -53
- package/src/modules/v4/username.ts +0 -94
- package/src/modules/v4/validate.ts +0 -76
- package/src/modules/v4.ts +0 -29
- package/src/routes/delete.ts +0 -72
- package/src/routes/get.ts +0 -856
- package/src/routes/index.ts +0 -59
- package/src/routes/patch.ts +0 -45
- package/src/routes/post.ts +0 -209
- package/src/storage/index.ts +0 -16
- package/src/types/express.d.ts +0 -17
- package/src/types/storage.ts +0 -48
- package/src/utils/colors.ts +0 -91
- package/src/utils/helpers.ts +0 -90
- package/src/utils/response.ts +0 -28
- package/tests/integration/api.test.ts +0 -772
- package/tests/tsconfig.json +0 -3
- package/tests/unit/address.test.ts +0 -84
- package/tests/unit/agent.test.ts +0 -113
- package/tests/unit/algorithms.test.ts +0 -121
- package/tests/unit/captcha.test.ts +0 -62
- package/tests/unit/chat.test.ts +0 -54
- package/tests/unit/color.test.ts +0 -54
- package/tests/unit/convert.test.ts +0 -68
- package/tests/unit/cron.test.ts +0 -110
- package/tests/unit/dice.test.ts +0 -58
- package/tests/unit/domain.test.ts +0 -48
- package/tests/unit/encode.test.ts +0 -109
- package/tests/unit/geo.test.ts +0 -46
- package/tests/unit/hash.test.ts +0 -45
- package/tests/unit/hyperplanning.test.ts +0 -97
- package/tests/unit/ip.test.ts +0 -140
- package/tests/unit/levenshtein.test.ts +0 -35
- package/tests/unit/palette.test.ts +0 -54
- package/tests/unit/password.test.ts +0 -86
- package/tests/unit/personal.test.ts +0 -66
- package/tests/unit/placeholder.test.ts +0 -58
- package/tests/unit/qrcode.test.ts +0 -83
- package/tests/unit/regex.test.ts +0 -95
- package/tests/unit/statistics.test.ts +0 -58
- package/tests/unit/text.test.ts +0 -108
- package/tests/unit/tic_tac_toe.test.ts +0 -56
- package/tests/unit/time.test.ts +0 -115
- package/tests/unit/token.test.ts +0 -62
- package/tests/unit/username.test.ts +0 -35
- package/tests/unit/validate.test.ts +0 -72
- package/tsconfig.json +0 -17
- package/tsconfig.test.json +0 -9
package/tests/tsconfig.json
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import address from '../../src/modules/v4/address.js';
|
|
5
|
-
|
|
6
|
-
describe('address', () => {
|
|
7
|
-
describe('default behavior', () => {
|
|
8
|
-
test('returns an addresses array with one entry by default', () => {
|
|
9
|
-
const r = address();
|
|
10
|
-
assert.equal(r.addresses.length, 1);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('each address has required fields', () => {
|
|
14
|
-
const r = address();
|
|
15
|
-
const a = r.addresses[0]!;
|
|
16
|
-
assert.ok(a.street);
|
|
17
|
-
assert.ok(a.city);
|
|
18
|
-
assert.ok(a.zip);
|
|
19
|
-
assert.ok(a.state);
|
|
20
|
-
assert.ok(a.country);
|
|
21
|
-
assert.ok(a.countryCode);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('country filtering', () => {
|
|
26
|
-
test('fr returns a French address', () => {
|
|
27
|
-
const r = address('fr');
|
|
28
|
-
assert.equal(r.addresses[0]!.countryCode, 'FR');
|
|
29
|
-
assert.equal(r.addresses[0]!.country, 'France');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('us returns a US address', () => {
|
|
33
|
-
const r = address('us');
|
|
34
|
-
assert.equal(r.addresses[0]!.countryCode, 'US');
|
|
35
|
-
assert.equal(r.addresses[0]!.country, 'United States');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('uk returns a UK address', () => {
|
|
39
|
-
const r = address('uk');
|
|
40
|
-
assert.equal(r.addresses[0]!.countryCode, 'UK');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('de returns a German address', () => {
|
|
44
|
-
const r = address('de');
|
|
45
|
-
assert.equal(r.addresses[0]!.countryCode, 'DE');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('es returns a Spanish address', () => {
|
|
49
|
-
const r = address('es');
|
|
50
|
-
assert.equal(r.addresses[0]!.countryCode, 'ES');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('country code is case-insensitive', () => {
|
|
54
|
-
const r = address('FR');
|
|
55
|
-
assert.equal(r.addresses[0]!.countryCode, 'FR');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('count', () => {
|
|
60
|
-
test('count=5 returns 5 addresses', () => {
|
|
61
|
-
const r = address('fr', 5);
|
|
62
|
-
assert.equal(r.addresses.length, 5);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('count=10 returns 10 addresses', () => {
|
|
66
|
-
const r = address('us', 10);
|
|
67
|
-
assert.equal(r.addresses.length, 10);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe('errors', () => {
|
|
72
|
-
test('unknown country throws', () => {
|
|
73
|
-
assert.throws(() => address('xx'), /Unknown country code/);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('count=0 throws', () => {
|
|
77
|
-
assert.throws(() => address('fr', 0), /Count must be between/);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('count=11 throws', () => {
|
|
81
|
-
assert.throws(() => address('fr', 11), /Count must be between/);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
});
|
package/tests/unit/agent.test.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import agent from '../../src/modules/v4/agent.js';
|
|
5
|
-
|
|
6
|
-
const CHROME_MAC =
|
|
7
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|
8
|
-
const FIREFOX_WIN = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0';
|
|
9
|
-
const SAFARI_IPHONE =
|
|
10
|
-
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
|
|
11
|
-
const EDGE_WIN =
|
|
12
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0';
|
|
13
|
-
const GOOGLEBOT = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)';
|
|
14
|
-
const IPAD =
|
|
15
|
-
'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
|
|
16
|
-
|
|
17
|
-
describe('agent', () => {
|
|
18
|
-
describe('browser detection', () => {
|
|
19
|
-
test('Chrome on macOS', () => {
|
|
20
|
-
const r = agent(CHROME_MAC);
|
|
21
|
-
assert.equal(r.browser.name, 'Chrome');
|
|
22
|
-
assert.equal(r.browser.major, '120');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('Firefox on Windows', () => {
|
|
26
|
-
const r = agent(FIREFOX_WIN);
|
|
27
|
-
assert.equal(r.browser.name, 'Firefox');
|
|
28
|
-
assert.equal(r.browser.major, '121');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('Safari on iPhone', () => {
|
|
32
|
-
const r = agent(SAFARI_IPHONE);
|
|
33
|
-
assert.equal(r.browser.name, 'Safari');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('Edge detected before Chrome (both present in UA)', () => {
|
|
37
|
-
const r = agent(EDGE_WIN);
|
|
38
|
-
assert.equal(r.browser.name, 'Edge');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('unknown browser returns unknown', () => {
|
|
42
|
-
const r = agent('UnknownBrowser/1.0');
|
|
43
|
-
assert.equal(r.browser.name, 'unknown');
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('OS detection', () => {
|
|
48
|
-
test('macOS from Chrome UA', () => {
|
|
49
|
-
const r = agent(CHROME_MAC);
|
|
50
|
-
assert.equal(r.os.name, 'macOS');
|
|
51
|
-
assert.equal(r.os.version, '10.15.7');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('Windows 10/11 from Firefox UA', () => {
|
|
55
|
-
const r = agent(FIREFOX_WIN);
|
|
56
|
-
assert.equal(r.os.name, 'Windows 10/11');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('iOS from iPhone UA', () => {
|
|
60
|
-
const r = agent(SAFARI_IPHONE);
|
|
61
|
-
assert.equal(r.os.name, 'iOS');
|
|
62
|
-
assert.equal(r.os.version, '17.0');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('device detection', () => {
|
|
67
|
-
test('desktop from Chrome macOS', () => {
|
|
68
|
-
assert.equal(agent(CHROME_MAC).device.type, 'desktop');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('mobile from iPhone UA', () => {
|
|
72
|
-
assert.equal(agent(SAFARI_IPHONE).device.type, 'mobile');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('tablet from iPad UA', () => {
|
|
76
|
-
assert.equal(agent(IPAD).device.type, 'tablet');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('Apple vendor from iPhone', () => {
|
|
80
|
-
assert.equal(agent(SAFARI_IPHONE).device.vendor, 'Apple');
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('engine detection', () => {
|
|
85
|
-
test('Chrome uses Blink', () => {
|
|
86
|
-
assert.equal(agent(CHROME_MAC).engine.name, 'Blink');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('Firefox uses Gecko', () => {
|
|
90
|
-
assert.equal(agent(FIREFOX_WIN).engine.name, 'Gecko');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('Edge uses Blink', () => {
|
|
94
|
-
assert.equal(agent(EDGE_WIN).engine.name, 'Blink');
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
describe('bot detection', () => {
|
|
99
|
-
test('Googlebot is a bot', () => {
|
|
100
|
-
assert.equal(agent(GOOGLEBOT).bot, true);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('Chrome is not a bot', () => {
|
|
104
|
-
assert.equal(agent(CHROME_MAC).bot, false);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('errors', () => {
|
|
109
|
-
test('throws on empty string', () => {
|
|
110
|
-
assert.throws(() => agent(''), /required/);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
anagram,
|
|
6
|
-
factorial,
|
|
7
|
-
fibonacci,
|
|
8
|
-
gcd,
|
|
9
|
-
isprime,
|
|
10
|
-
palindrome,
|
|
11
|
-
primefactors,
|
|
12
|
-
reverse,
|
|
13
|
-
roman,
|
|
14
|
-
} from '../../src/modules/v4/algorithms.js';
|
|
15
|
-
|
|
16
|
-
describe('algorithms', () => {
|
|
17
|
-
describe('anagram', () => {
|
|
18
|
-
test('listen / silent', () => {
|
|
19
|
-
assert.equal(anagram('listen', 'silent'), true);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('hello / world', () => {
|
|
23
|
-
assert.equal(anagram('hello', 'world'), false);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('factorial', () => {
|
|
28
|
-
test('5! = 120', () => {
|
|
29
|
-
assert.equal(factorial(5), 120);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('0! = 1', () => {
|
|
33
|
-
assert.equal(factorial(0), 1);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('throws on negative', () => {
|
|
37
|
-
assert.throws(() => factorial(-1), /positive/);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('fibonacci', () => {
|
|
42
|
-
test('first 10 values', () => {
|
|
43
|
-
assert.deepEqual(fibonacci(10), [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('gcd', () => {
|
|
48
|
-
test('gcd(12, 18) = 6', () => {
|
|
49
|
-
assert.equal(gcd(12, 18), 6);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('gcd(7, 13) = 1', () => {
|
|
53
|
-
assert.equal(gcd(7, 13), 1);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('isprime', () => {
|
|
58
|
-
test('17 is prime', () => {
|
|
59
|
-
assert.equal(isprime(17), true);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('15 is not prime', () => {
|
|
63
|
-
assert.equal(isprime(15), false);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('palindrome', () => {
|
|
68
|
-
test('madam is palindrome', () => {
|
|
69
|
-
assert.equal(palindrome('madam'), true);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('hello is not', () => {
|
|
73
|
-
assert.equal(palindrome('hello'), false);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('primefactors', () => {
|
|
78
|
-
test('factors of 60', () => {
|
|
79
|
-
assert.deepEqual(primefactors(60), [2, 2, 3, 5]);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('reverse', () => {
|
|
84
|
-
test('reverses a string', () => {
|
|
85
|
-
assert.equal(reverse('abc'), 'cba');
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('roman', () => {
|
|
90
|
-
test('2024 → MMXXIV', () => {
|
|
91
|
-
assert.equal(roman('2024'), 'MMXXIV');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('MMXXIV → 2024', () => {
|
|
95
|
-
assert.equal(roman('MMXXIV'), 2024);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('1 → I', () => {
|
|
99
|
-
assert.equal(roman('1'), 'I');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('3999 → MMMCMXCIX', () => {
|
|
103
|
-
assert.equal(roman('3999'), 'MMMCMXCIX');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('round-trip 1..100', () => {
|
|
107
|
-
for (let i = 1; i <= 100; i++) {
|
|
108
|
-
const r = roman(String(i));
|
|
109
|
-
assert.equal(roman(r as string), i);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('throws on out of range', () => {
|
|
114
|
-
assert.throws(() => roman('4000'), /between 1 and 3999/);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test('throws on invalid roman', () => {
|
|
118
|
-
assert.throws(() => roman('XYZ'), /Invalid Roman/);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import captcha from '../../src/modules/v4/captcha.js';
|
|
5
|
-
|
|
6
|
-
const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
|
|
7
|
-
|
|
8
|
-
describe('captcha', () => {
|
|
9
|
-
test('returns PNG buffer with provided text', () => {
|
|
10
|
-
const result = captcha({ text: 'hello' });
|
|
11
|
-
assert.equal(result.contentType, 'image/png');
|
|
12
|
-
assert.ok(Buffer.isBuffer(result.body));
|
|
13
|
-
assert.ok(result.body.subarray(0, 4).equals(PNG_MAGIC));
|
|
14
|
-
assert.equal(result.text, 'hello');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('auto-generates text when none provided', () => {
|
|
18
|
-
const result = captcha({});
|
|
19
|
-
assert.equal(result.text.length, 6);
|
|
20
|
-
assert.ok(Buffer.isBuffer(result.body));
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('respects custom length', () => {
|
|
24
|
-
const result = captcha({ length: 10 });
|
|
25
|
-
assert.equal(result.text.length, 10);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('throws on invalid length', () => {
|
|
29
|
-
assert.throws(() => captcha({ length: 0 }), /length/);
|
|
30
|
-
assert.throws(() => captcha({ length: 99 }), /length/);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('accepts custom dimensions', () => {
|
|
34
|
-
const result = captcha({ text: 'AB', width: 200, height: 80 });
|
|
35
|
-
assert.ok(Buffer.isBuffer(result.body));
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('throws on invalid dimensions', () => {
|
|
39
|
-
assert.throws(() => captcha({ text: 'AB', width: 10 }), /width/);
|
|
40
|
-
assert.throws(() => captcha({ text: 'AB', height: 500 }), /height/);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('accepts noise levels', () => {
|
|
44
|
-
for (const noise of ['low', 'medium', 'high'] as const) {
|
|
45
|
-
const result = captcha({ text: 'test', noise });
|
|
46
|
-
assert.ok(Buffer.isBuffer(result.body));
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('throws on invalid noise', () => {
|
|
51
|
-
assert.throws(() => captcha({ text: 'test', noise: 'extreme' as 'low' }), /noise/);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('accepts custom colors', () => {
|
|
55
|
-
const result = captcha({ text: 'test', bg: 'f0f0f0', color: '333333' });
|
|
56
|
-
assert.ok(Buffer.isBuffer(result.body));
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('throws on invalid color', () => {
|
|
60
|
-
assert.throws(() => captcha({ text: 'test', bg: 'xyz' }), /color/i);
|
|
61
|
-
});
|
|
62
|
-
});
|
package/tests/unit/chat.test.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import chat from '../../src/modules/v4/chat.js';
|
|
5
|
-
import type { ChatStorage } from '../../src/types/storage.js';
|
|
6
|
-
|
|
7
|
-
function makeStorage(): ChatStorage {
|
|
8
|
-
return { messages: [], privateChats: {}, sessions: {}, rateLimits: {} };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe('chat', () => {
|
|
12
|
-
test('sends a public message', () => {
|
|
13
|
-
const storage = makeStorage();
|
|
14
|
-
const result = chat('message', { username: 'alice', message: 'hello', session: 'a1', storage });
|
|
15
|
-
assert.ok((result as { message: string }).message.includes('sent'));
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test('fetches public messages', () => {
|
|
19
|
-
const storage = makeStorage();
|
|
20
|
-
chat('message', { username: 'alice', message: 'test', session: 'a1', storage });
|
|
21
|
-
const result = chat('fetch', { username: 'bob', storage });
|
|
22
|
-
assert.ok(Array.isArray(result));
|
|
23
|
-
assert.equal((result as { message: string }[])[0]!.message, 'test');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('sends and retrieves private messages', () => {
|
|
27
|
-
const storage = makeStorage();
|
|
28
|
-
chat('message', { username: 'alice', message: 'secret', session: 'a1', token: 'tok1', storage });
|
|
29
|
-
const result = chat('private', { username: 'bob', token: 'tok1', storage });
|
|
30
|
-
assert.ok(Array.isArray(result));
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('clears a private chat', () => {
|
|
34
|
-
const storage = makeStorage();
|
|
35
|
-
chat('message', { username: 'alice', message: 'secret', session: 'a1', token: 'tok2', storage });
|
|
36
|
-
const result = chat('clear', { username: 'alice', session: 'a1', token: 'tok2', storage });
|
|
37
|
-
assert.ok((result as { message: string }).message.includes('cleared'));
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('throws on missing username', () => {
|
|
41
|
-
assert.throws(
|
|
42
|
-
() => chat('message', { username: '', message: 'hi', session: 's', storage: makeStorage() }),
|
|
43
|
-
/username/,
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('throws on invalid action', () => {
|
|
48
|
-
assert.throws(() => chat('delete', { username: 'alice', storage: makeStorage() }), /Invalid action/);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('throws on fetch with no messages', () => {
|
|
52
|
-
assert.throws(() => chat('fetch', { username: 'alice', storage: makeStorage() }), /No messages/);
|
|
53
|
-
});
|
|
54
|
-
});
|
package/tests/unit/color.test.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import color from '../../src/modules/v4/color.js';
|
|
5
|
-
|
|
6
|
-
describe('color', () => {
|
|
7
|
-
test('returns all 6 formats', () => {
|
|
8
|
-
const result = color();
|
|
9
|
-
assert.ok('hex' in result);
|
|
10
|
-
assert.ok('rgb' in result);
|
|
11
|
-
assert.ok('hsl' in result);
|
|
12
|
-
assert.ok('hsv' in result);
|
|
13
|
-
assert.ok('hwb' in result);
|
|
14
|
-
assert.ok('cmyk' in result);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('hex matches #RRGGBB', () => {
|
|
18
|
-
for (let i = 0; i < 20; i++) {
|
|
19
|
-
assert.match(color().hex, /^#[0-9a-f]{6}$/);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('rgb format', () => {
|
|
24
|
-
assert.match(color().rgb, /^rgb\(\d{1,3}, \d{1,3}, \d{1,3}\)$/);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('hsl format', () => {
|
|
28
|
-
assert.match(color().hsl, /^hsl\(\d+(\.\d+)?, \d+(\.\d+)?%, \d+(\.\d+)?%\)$/);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('cmyk format with 4 channels', () => {
|
|
32
|
-
assert.match(color().cmyk, /^cmyk\(/);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('accepts hex input', () => {
|
|
36
|
-
const result = color('#ff6600');
|
|
37
|
-
assert.equal(result.hex, '#ff6600');
|
|
38
|
-
assert.equal(result.rgb, 'rgb(255, 102, 0)');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('accepts hex without #', () => {
|
|
42
|
-
const result = color('ff6600');
|
|
43
|
-
assert.equal(result.hex, '#ff6600');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('throws on invalid hex', () => {
|
|
47
|
-
assert.throws(() => color('xyz'), /Invalid HEX/);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('HSV correct for equal channels', () => {
|
|
51
|
-
const result = color('#808000');
|
|
52
|
-
assert.match(result.hsv, /^hsv\(60\.0,/);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import convert from '../../src/modules/v4/convert.js';
|
|
5
|
-
|
|
6
|
-
describe('convert', () => {
|
|
7
|
-
test('celsius to fahrenheit', () => {
|
|
8
|
-
const result = convert(0, 'celsius', 'fahrenheit');
|
|
9
|
-
assert.equal(result.result, 32);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('celsius to kelvin', () => {
|
|
13
|
-
const result = convert(0, 'celsius', 'kelvin');
|
|
14
|
-
assert.equal(result.result, 273.15);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('fahrenheit to celsius', () => {
|
|
18
|
-
const result = convert(32, 'fahrenheit', 'celsius');
|
|
19
|
-
assert.equal(result.result, 0);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('kelvin to celsius', () => {
|
|
23
|
-
const result = convert(273.15, 'kelvin', 'celsius');
|
|
24
|
-
assert.equal(result.result, 0);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('case-insensitive units', () => {
|
|
28
|
-
const result = convert(100, 'CELSIUS', 'Fahrenheit');
|
|
29
|
-
assert.equal(result.result, 212);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('throws on invalid unit', () => {
|
|
33
|
-
assert.throws(() => convert(100, 'celsius', 'banana'), /Invalid conversion/);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('throws on absolute zero violation', () => {
|
|
37
|
-
assert.throws(() => convert(-300, 'celsius', 'kelvin'), /absolute zero/);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('km to mi', () => {
|
|
41
|
-
const result = convert(1, 'km', 'mi');
|
|
42
|
-
assert.ok(Math.abs(result.result - 0.621371) < 0.001);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('kg to lb', () => {
|
|
46
|
-
const result = convert(1, 'kg', 'lb');
|
|
47
|
-
assert.ok(Math.abs(result.result - 2.20462) < 0.001);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('kb to mb', () => {
|
|
51
|
-
const result = convert(1024, 'kb', 'mb');
|
|
52
|
-
assert.equal(result.result, 1);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('km/h to mph', () => {
|
|
56
|
-
const result = convert(100, 'km/h', 'mph');
|
|
57
|
-
assert.ok(Math.abs(result.result - 62.1371) < 0.01);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('negative values allowed for non-temperature', () => {
|
|
61
|
-
const result = convert(-5, 'km', 'mi');
|
|
62
|
-
assert.ok(result.result < 0);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('throws on non-numeric value', () => {
|
|
66
|
-
assert.throws(() => convert(NaN, 'celsius', 'kelvin'), /must be a number/);
|
|
67
|
-
});
|
|
68
|
-
});
|
package/tests/unit/cron.test.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import cron from '../../src/modules/v4/cron.js';
|
|
5
|
-
|
|
6
|
-
// Fixed reference point: a known Monday at 08:45 UTC
|
|
7
|
-
const FROM = '2026-01-05T08:45:00Z'; // Monday
|
|
8
|
-
|
|
9
|
-
describe('cron', () => {
|
|
10
|
-
describe('every minute', () => {
|
|
11
|
-
test('* * * * * returns 5 consecutive minutes', () => {
|
|
12
|
-
const r = cron('* * * * *', 5, FROM);
|
|
13
|
-
assert.equal(r.next.length, 5);
|
|
14
|
-
// First result should be the next minute after FROM
|
|
15
|
-
assert.ok(r.next[0]!.startsWith('2026-01-05T08:46'));
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test('description is "Every minute"', () => {
|
|
19
|
-
const r = cron('* * * * *', 1, FROM);
|
|
20
|
-
assert.equal(r.description, 'Every minute');
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe('every N minutes', () => {
|
|
25
|
-
test('*/15 * * * * returns results 15 minutes apart', () => {
|
|
26
|
-
const r = cron('*/15 * * * *', 3, FROM);
|
|
27
|
-
assert.equal(r.next.length, 3);
|
|
28
|
-
assert.ok(r.next[0]!.includes('T09:00'));
|
|
29
|
-
assert.ok(r.next[1]!.includes('T09:15'));
|
|
30
|
-
assert.ok(r.next[2]!.includes('T09:30'));
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('description mentions every 15 minutes', () => {
|
|
34
|
-
const r = cron('*/15 * * * *', 1, FROM);
|
|
35
|
-
assert.match(r.description, /Every 15 minutes/);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('at fixed time', () => {
|
|
40
|
-
test('0 9 * * 1-5 fires at 09:00 on weekdays', () => {
|
|
41
|
-
const r = cron('0 9 * * 1-5', 3, FROM);
|
|
42
|
-
// Next Monday 09:00 is same day
|
|
43
|
-
assert.ok(r.next[0]!.includes('T09:00'));
|
|
44
|
-
// All results should be on weekdays (Mon–Fri)
|
|
45
|
-
for (const date of r.next) {
|
|
46
|
-
const d = new Date(date);
|
|
47
|
-
const dow = d.getUTCDay();
|
|
48
|
-
assert.ok(dow >= 1 && dow <= 5, `Expected weekday, got ${dow}`);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('description includes Monday through Friday', () => {
|
|
53
|
-
const r = cron('0 9 * * 1-5', 1, FROM);
|
|
54
|
-
assert.match(r.description, /Monday through Friday/);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('plan example', () => {
|
|
59
|
-
test('*/15 9-17 * * 1-5 fires between 09:00 and 17:45 on weekdays', () => {
|
|
60
|
-
const r = cron('*/15 9-17 * * 1-5', 5, FROM);
|
|
61
|
-
assert.equal(r.next.length, 5);
|
|
62
|
-
for (const date of r.next) {
|
|
63
|
-
const d = new Date(date);
|
|
64
|
-
const hour = d.getUTCHours();
|
|
65
|
-
assert.ok(hour >= 9 && hour <= 17);
|
|
66
|
-
const dow = d.getUTCDay();
|
|
67
|
-
assert.ok(dow >= 1 && dow <= 5);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('description includes "Every 15 minutes"', () => {
|
|
72
|
-
const r = cron('*/15 9-17 * * 1-5', 1, FROM);
|
|
73
|
-
assert.match(r.description, /Every 15 minutes/);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('count', () => {
|
|
78
|
-
test('count=10 returns 10 results', () => {
|
|
79
|
-
const r = cron('* * * * *', 10, FROM);
|
|
80
|
-
assert.equal(r.next.length, 10);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test('count=1 returns 1 result', () => {
|
|
84
|
-
const r = cron('*/30 * * * *', 1, FROM);
|
|
85
|
-
assert.equal(r.next.length, 1);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('errors', () => {
|
|
90
|
-
test('missing expr throws', () => {
|
|
91
|
-
assert.throws(() => cron(''), /expression/);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('too few fields throws', () => {
|
|
95
|
-
assert.throws(() => cron('* * * *'), /5 fields/);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('invalid range throws', () => {
|
|
99
|
-
assert.throws(() => cron('60-70 * * * *'), /valid: 0-59/);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('invalid timezone throws', () => {
|
|
103
|
-
assert.throws(() => cron('* * * * *', 5, undefined, 'Mars/Olympus'), /timezone/);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('count out of range throws', () => {
|
|
107
|
-
assert.throws(() => cron('* * * * *', 21), /Count/);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
});
|