@moxon/api 6.0.0-pre
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/.prettierignore +5 -0
- package/LICENSE +28 -0
- package/README.md +277 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +55 -0
- package/dist/app.js.map +1 -0
- package/dist/config/env.d.ts +22 -0
- package/dist/config/env.js +36 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/plans.d.ts +26 -0
- package/dist/config/plans.js +44 -0
- package/dist/config/plans.js.map +1 -0
- package/dist/config/versions.d.ts +18 -0
- package/dist/config/versions.js +248 -0
- package/dist/config/versions.js.map +1 -0
- package/dist/constants.d.ts +48 -0
- package/dist/constants.js +75 -0
- package/dist/constants.js.map +1 -0
- package/dist/middleware/cors.d.ts +2 -0
- package/dist/middleware/cors.js +31 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/error.d.ts +4 -0
- package/dist/middleware/error.js +8 -0
- package/dist/middleware/error.js.map +1 -0
- package/dist/middleware/json.d.ts +2 -0
- package/dist/middleware/json.js +8 -0
- package/dist/middleware/json.js.map +1 -0
- package/dist/middleware/logger.d.ts +26 -0
- package/dist/middleware/logger.js +65 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/ratelimit.d.ts +2 -0
- package/dist/middleware/ratelimit.js +83 -0
- package/dist/middleware/ratelimit.js.map +1 -0
- package/dist/middleware/version.d.ts +3 -0
- package/dist/middleware/version.js +30 -0
- package/dist/middleware/version.js.map +1 -0
- package/dist/modules/v4/address.d.ts +20 -0
- package/dist/modules/v4/address.js +381 -0
- package/dist/modules/v4/address.js.map +1 -0
- package/dist/modules/v4/agent.d.ts +30 -0
- package/dist/modules/v4/agent.js +92 -0
- package/dist/modules/v4/agent.js.map +1 -0
- package/dist/modules/v4/algorithms.d.ts +90 -0
- package/dist/modules/v4/algorithms.js +279 -0
- package/dist/modules/v4/algorithms.js.map +1 -0
- package/dist/modules/v4/avatar.d.ts +23 -0
- package/dist/modules/v4/avatar.js +136 -0
- package/dist/modules/v4/avatar.js.map +1 -0
- package/dist/modules/v4/barcode.d.ts +27 -0
- package/dist/modules/v4/barcode.js +440 -0
- package/dist/modules/v4/barcode.js.map +1 -0
- package/dist/modules/v4/captcha.d.ts +22 -0
- package/dist/modules/v4/captcha.js +79 -0
- package/dist/modules/v4/captcha.js.map +1 -0
- package/dist/modules/v4/chat.d.ts +21 -0
- package/dist/modules/v4/chat.js +100 -0
- package/dist/modules/v4/chat.js.map +1 -0
- package/dist/modules/v4/color.d.ts +16 -0
- package/dist/modules/v4/color.js +59 -0
- package/dist/modules/v4/color.js.map +1 -0
- package/dist/modules/v4/convert.d.ts +15 -0
- package/dist/modules/v4/convert.js +116 -0
- package/dist/modules/v4/convert.js.map +1 -0
- package/dist/modules/v4/credit.d.ts +22 -0
- package/dist/modules/v4/credit.js +104 -0
- package/dist/modules/v4/credit.js.map +1 -0
- package/dist/modules/v4/cron.d.ts +17 -0
- package/dist/modules/v4/cron.js +202 -0
- package/dist/modules/v4/cron.js.map +1 -0
- package/dist/modules/v4/dice.d.ts +16 -0
- package/dist/modules/v4/dice.js +37 -0
- package/dist/modules/v4/dice.js.map +1 -0
- package/dist/modules/v4/domain.d.ts +6 -0
- package/dist/modules/v4/domain.js +83 -0
- package/dist/modules/v4/domain.js.map +1 -0
- package/dist/modules/v4/encode.d.ts +81 -0
- package/dist/modules/v4/encode.js +236 -0
- package/dist/modules/v4/encode.js.map +1 -0
- package/dist/modules/v4/geo.d.ts +30 -0
- package/dist/modules/v4/geo.js +51 -0
- package/dist/modules/v4/geo.js.map +1 -0
- package/dist/modules/v4/hash.d.ts +15 -0
- package/dist/modules/v4/hash.js +25 -0
- package/dist/modules/v4/hash.js.map +1 -0
- package/dist/modules/v4/hyperplanning.d.ts +19 -0
- package/dist/modules/v4/hyperplanning.js +80 -0
- package/dist/modules/v4/hyperplanning.js.map +1 -0
- package/dist/modules/v4/ip.d.ts +19 -0
- package/dist/modules/v4/ip.js +132 -0
- package/dist/modules/v4/ip.js.map +1 -0
- package/dist/modules/v4/levenshtein.d.ts +13 -0
- package/dist/modules/v4/levenshtein.js +37 -0
- package/dist/modules/v4/levenshtein.js.map +1 -0
- package/dist/modules/v4/palette.d.ts +19 -0
- package/dist/modules/v4/palette.js +56 -0
- package/dist/modules/v4/palette.js.map +1 -0
- package/dist/modules/v4/password.d.ts +25 -0
- package/dist/modules/v4/password.js +343 -0
- package/dist/modules/v4/password.js.map +1 -0
- package/dist/modules/v4/personal.d.ts +6 -0
- package/dist/modules/v4/personal.js +121 -0
- package/dist/modules/v4/personal.js.map +1 -0
- package/dist/modules/v4/placeholder.d.ts +30 -0
- package/dist/modules/v4/placeholder.js +160 -0
- package/dist/modules/v4/placeholder.js.map +1 -0
- package/dist/modules/v4/qrcode.d.ts +25 -0
- package/dist/modules/v4/qrcode.js +92 -0
- package/dist/modules/v4/qrcode.js.map +1 -0
- package/dist/modules/v4/regex.d.ts +23 -0
- package/dist/modules/v4/regex.js +48 -0
- package/dist/modules/v4/regex.js.map +1 -0
- package/dist/modules/v4/statistics.d.ts +20 -0
- package/dist/modules/v4/statistics.js +48 -0
- package/dist/modules/v4/statistics.js.map +1 -0
- package/dist/modules/v4/text.d.ts +43 -0
- package/dist/modules/v4/text.js +323 -0
- package/dist/modules/v4/text.js.map +1 -0
- package/dist/modules/v4/tic_tac_toe.d.ts +19 -0
- package/dist/modules/v4/tic_tac_toe.js +214 -0
- package/dist/modules/v4/tic_tac_toe.js.map +1 -0
- package/dist/modules/v4/time.d.ts +15 -0
- package/dist/modules/v4/time.js +124 -0
- package/dist/modules/v4/time.js.map +1 -0
- package/dist/modules/v4/token.d.ts +9 -0
- package/dist/modules/v4/token.js +45 -0
- package/dist/modules/v4/token.js.map +1 -0
- package/dist/modules/v4/username.d.ts +6 -0
- package/dist/modules/v4/username.js +92 -0
- package/dist/modules/v4/username.js.map +1 -0
- package/dist/modules/v4/validate.d.ts +34 -0
- package/dist/modules/v4/validate.js +75 -0
- package/dist/modules/v4/validate.js.map +1 -0
- package/dist/modules/v4.d.ts +32 -0
- package/dist/modules/v4.js +33 -0
- package/dist/modules/v4.js.map +1 -0
- package/dist/modules/v5/address.d.ts +5 -0
- package/dist/modules/v5/address.js +20 -0
- package/dist/modules/v5/address.js.map +1 -0
- package/dist/modules/v5/algorithms.d.ts +9 -0
- package/dist/modules/v5/algorithms.js +39 -0
- package/dist/modules/v5/algorithms.js.map +1 -0
- package/dist/modules/v5/asymmetric.d.ts +26 -0
- package/dist/modules/v5/asymmetric.js +74 -0
- package/dist/modules/v5/asymmetric.js.map +1 -0
- package/dist/modules/v5/barcode.d.ts +27 -0
- package/dist/modules/v5/barcode.js +424 -0
- package/dist/modules/v5/barcode.js.map +1 -0
- package/dist/modules/v5/base.d.ts +16 -0
- package/dist/modules/v5/base.js +41 -0
- package/dist/modules/v5/base.js.map +1 -0
- package/dist/modules/v5/captcha.d.ts +27 -0
- package/dist/modules/v5/captcha.js +136 -0
- package/dist/modules/v5/captcha.js.map +1 -0
- package/dist/modules/v5/case.d.ts +10 -0
- package/dist/modules/v5/case.js +57 -0
- package/dist/modules/v5/case.js.map +1 -0
- package/dist/modules/v5/chart.d.ts +53 -0
- package/dist/modules/v5/chart.js +385 -0
- package/dist/modules/v5/chart.js.map +1 -0
- package/dist/modules/v5/chat.d.ts +21 -0
- package/dist/modules/v5/chat.js +107 -0
- package/dist/modules/v5/chat.js.map +1 -0
- package/dist/modules/v5/csv.d.ts +29 -0
- package/dist/modules/v5/csv.js +143 -0
- package/dist/modules/v5/csv.js.map +1 -0
- package/dist/modules/v5/diff.d.ts +20 -0
- package/dist/modules/v5/diff.js +81 -0
- package/dist/modules/v5/diff.js.map +1 -0
- package/dist/modules/v5/evaluate.d.ts +14 -0
- package/dist/modules/v5/evaluate.js +284 -0
- package/dist/modules/v5/evaluate.js.map +1 -0
- package/dist/modules/v5/jwt.d.ts +11 -0
- package/dist/modules/v5/jwt.js +42 -0
- package/dist/modules/v5/jwt.js.map +1 -0
- package/dist/modules/v5/matrix.d.ts +77 -0
- package/dist/modules/v5/matrix.js +239 -0
- package/dist/modules/v5/matrix.js.map +1 -0
- package/dist/modules/v5/otp.d.ts +30 -0
- package/dist/modules/v5/otp.js +117 -0
- package/dist/modules/v5/otp.js.map +1 -0
- package/dist/modules/v5/pow.d.ts +29 -0
- package/dist/modules/v5/pow.js +49 -0
- package/dist/modules/v5/pow.js.map +1 -0
- package/dist/modules/v5/read.d.ts +20 -0
- package/dist/modules/v5/read.js +59 -0
- package/dist/modules/v5/read.js.map +1 -0
- package/dist/modules/v5/semver.d.ts +33 -0
- package/dist/modules/v5/semver.js +99 -0
- package/dist/modules/v5/semver.js.map +1 -0
- package/dist/modules/v5/symmetric.d.ts +15 -0
- package/dist/modules/v5/symmetric.js +88 -0
- package/dist/modules/v5/symmetric.js.map +1 -0
- package/dist/modules/v5/text.d.ts +5 -0
- package/dist/modules/v5/text.js +12 -0
- package/dist/modules/v5/text.js.map +1 -0
- package/dist/modules/v5/tic_tac_toe.d.ts +19 -0
- package/dist/modules/v5/tic_tac_toe.js +226 -0
- package/dist/modules/v5/tic_tac_toe.js.map +1 -0
- package/dist/modules/v5/url.d.ts +18 -0
- package/dist/modules/v5/url.js +51 -0
- package/dist/modules/v5/url.js.map +1 -0
- package/dist/modules/v5/uuid.d.ts +25 -0
- package/dist/modules/v5/uuid.js +40 -0
- package/dist/modules/v5/uuid.js.map +1 -0
- package/dist/modules/v5.d.ts +24 -0
- package/dist/modules/v5.js +25 -0
- package/dist/modules/v5.js.map +1 -0
- package/dist/modules/v6.d.ts +1 -0
- package/dist/modules/v6.js +2 -0
- package/dist/modules/v6.js.map +1 -0
- package/dist/routes/delete.d.ts +2 -0
- package/dist/routes/delete.js +73 -0
- package/dist/routes/delete.js.map +1 -0
- package/dist/routes/get.d.ts +2 -0
- package/dist/routes/get.js +857 -0
- package/dist/routes/get.js.map +1 -0
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.js +95 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/patch.d.ts +2 -0
- package/dist/routes/patch.js +45 -0
- package/dist/routes/patch.js.map +1 -0
- package/dist/routes/post.d.ts +2 -0
- package/dist/routes/post.js +604 -0
- package/dist/routes/post.js.map +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +14 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/types/storage.d.ts +52 -0
- package/dist/types/storage.js +2 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/utils/challenge.d.ts +40 -0
- package/dist/utils/challenge.js +75 -0
- package/dist/utils/challenge.js.map +1 -0
- package/dist/utils/colors.d.ts +44 -0
- package/dist/utils/colors.js +93 -0
- package/dist/utils/colors.js.map +1 -0
- package/dist/utils/helpers.d.ts +55 -0
- package/dist/utils/helpers.js +82 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/response.d.ts +9 -0
- package/dist/utils/response.js +24 -0
- package/dist/utils/response.js.map +1 -0
- package/package.json +90 -0
- package/robots.txt +74 -0
- package/src/favicon.ico +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface LogEntry {
|
|
2
|
+
timestamp: string;
|
|
3
|
+
method: string;
|
|
4
|
+
url: string;
|
|
5
|
+
status: number;
|
|
6
|
+
duration: string;
|
|
7
|
+
platform: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface ChatMessage {
|
|
10
|
+
username: string;
|
|
11
|
+
message: string;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ChatStorage {
|
|
15
|
+
messages: ChatMessage[];
|
|
16
|
+
privateChats: Record<string, ChatMessage[]>;
|
|
17
|
+
sessions: Record<string, {
|
|
18
|
+
user: string;
|
|
19
|
+
last: number;
|
|
20
|
+
}>;
|
|
21
|
+
rateLimits: Record<string, number[]>;
|
|
22
|
+
}
|
|
23
|
+
export interface TicTacToeMove {
|
|
24
|
+
username: string;
|
|
25
|
+
move: string;
|
|
26
|
+
session: string;
|
|
27
|
+
}
|
|
28
|
+
export interface TicTacToeGame {
|
|
29
|
+
moves: TicTacToeMove[];
|
|
30
|
+
players: string[];
|
|
31
|
+
private: boolean;
|
|
32
|
+
creation: number;
|
|
33
|
+
last?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface TicTacToeStorage {
|
|
36
|
+
games: Record<string, TicTacToeGame>;
|
|
37
|
+
sessions: Record<string, {
|
|
38
|
+
user: string;
|
|
39
|
+
last: number;
|
|
40
|
+
}>;
|
|
41
|
+
rateLimits: Record<string, number[]>;
|
|
42
|
+
}
|
|
43
|
+
export interface ChallengeStorage {
|
|
44
|
+
usedNonces: Map<string, number>;
|
|
45
|
+
}
|
|
46
|
+
export interface IpLimits {
|
|
47
|
+
[ip: string]: {
|
|
48
|
+
[hour: string]: {
|
|
49
|
+
[minute: string]: number;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/types/storage.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ChallengeStorage } from '../types/storage.js';
|
|
2
|
+
export interface ChallengeVerification {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
reason?: 'wrong' | 'expired' | 'used' | 'invalid';
|
|
5
|
+
}
|
|
6
|
+
export interface TokenPayload {
|
|
7
|
+
type: 'img' | 'pow';
|
|
8
|
+
nonce: string;
|
|
9
|
+
exp: number;
|
|
10
|
+
salt?: string;
|
|
11
|
+
difficulty?: number;
|
|
12
|
+
sig: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Signs a challenge into a stateless token. The expected answer never appears in
|
|
16
|
+
* the token itself, only in the HMAC, so it cannot be read back by the client.
|
|
17
|
+
*
|
|
18
|
+
* @param type - Challenge kind: img for a captcha, pow for a proof-of-work
|
|
19
|
+
* @param payload - Secret material bound to the token: the answer, or the salt and difficulty
|
|
20
|
+
* @param extra - Public fields to carry along, such as salt and difficulty
|
|
21
|
+
* @returns Base64url token carrying the nonce, expiry and signature
|
|
22
|
+
*/
|
|
23
|
+
export declare function signToken(type: 'img' | 'pow', payload: string, extra?: Record<string, unknown>): string;
|
|
24
|
+
/**
|
|
25
|
+
* Verifies a token against the payload it was signed with, then consumes its
|
|
26
|
+
* nonce so the same token cannot be replayed.
|
|
27
|
+
*
|
|
28
|
+
* @param token - Token issued by signToken
|
|
29
|
+
* @param payload - Payload to check the signature against
|
|
30
|
+
* @param storage - Store of already consumed nonces
|
|
31
|
+
* @returns Verification result, with a reason when it fails
|
|
32
|
+
*/
|
|
33
|
+
export declare function verifyToken(token: string, payload: string, storage: ChallengeStorage): ChallengeVerification;
|
|
34
|
+
/**
|
|
35
|
+
* Reads the public fields of a token without verifying its signature.
|
|
36
|
+
*
|
|
37
|
+
* @param token - Token issued by signToken
|
|
38
|
+
* @returns Decoded payload, or null if the token is malformed
|
|
39
|
+
*/
|
|
40
|
+
export declare function readToken(token: string): TokenPayload | null;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { env } from '../config/env.js';
|
|
3
|
+
import { CHALLENGE_TTL } from '../constants.js';
|
|
4
|
+
const secret = env.CHALLENGE_SECRET || randomBytes(32).toString('hex');
|
|
5
|
+
const sign = (type, nonce, exp, payload) => createHmac('sha256', secret).update(`${type}.${nonce}.${exp}.${payload}`).digest('hex');
|
|
6
|
+
function decode(token) {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(Buffer.from(token, 'base64url').toString('utf8'));
|
|
9
|
+
if (typeof parsed.nonce !== 'string' || typeof parsed.exp !== 'number' || typeof parsed.sig !== 'string') {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function prune(storage, now) {
|
|
19
|
+
for (const [nonce, exp] of storage.usedNonces) {
|
|
20
|
+
if (exp <= now)
|
|
21
|
+
storage.usedNonces.delete(nonce);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Signs a challenge into a stateless token. The expected answer never appears in
|
|
26
|
+
* the token itself, only in the HMAC, so it cannot be read back by the client.
|
|
27
|
+
*
|
|
28
|
+
* @param type - Challenge kind: img for a captcha, pow for a proof-of-work
|
|
29
|
+
* @param payload - Secret material bound to the token: the answer, or the salt and difficulty
|
|
30
|
+
* @param extra - Public fields to carry along, such as salt and difficulty
|
|
31
|
+
* @returns Base64url token carrying the nonce, expiry and signature
|
|
32
|
+
*/
|
|
33
|
+
export function signToken(type, payload, extra = {}) {
|
|
34
|
+
const nonce = randomBytes(16).toString('hex');
|
|
35
|
+
const exp = Date.now() + CHALLENGE_TTL;
|
|
36
|
+
const body = { type, nonce, exp, ...extra, sig: sign(type, nonce, exp, payload) };
|
|
37
|
+
return Buffer.from(JSON.stringify(body)).toString('base64url');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Verifies a token against the payload it was signed with, then consumes its
|
|
41
|
+
* nonce so the same token cannot be replayed.
|
|
42
|
+
*
|
|
43
|
+
* @param token - Token issued by signToken
|
|
44
|
+
* @param payload - Payload to check the signature against
|
|
45
|
+
* @param storage - Store of already consumed nonces
|
|
46
|
+
* @returns Verification result, with a reason when it fails
|
|
47
|
+
*/
|
|
48
|
+
export function verifyToken(token, payload, storage) {
|
|
49
|
+
const decoded = decode(token);
|
|
50
|
+
if (!decoded)
|
|
51
|
+
return { valid: false, reason: 'invalid' };
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
prune(storage, now);
|
|
54
|
+
if (decoded.exp <= now)
|
|
55
|
+
return { valid: false, reason: 'expired' };
|
|
56
|
+
if (storage.usedNonces.has(decoded.nonce))
|
|
57
|
+
return { valid: false, reason: 'used' };
|
|
58
|
+
const expected = Buffer.from(sign(decoded.type, decoded.nonce, decoded.exp, payload), 'hex');
|
|
59
|
+
const actual = Buffer.from(decoded.sig, 'hex');
|
|
60
|
+
if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) {
|
|
61
|
+
return { valid: false, reason: 'wrong' };
|
|
62
|
+
}
|
|
63
|
+
storage.usedNonces.set(decoded.nonce, decoded.exp);
|
|
64
|
+
return { valid: true };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Reads the public fields of a token without verifying its signature.
|
|
68
|
+
*
|
|
69
|
+
* @param token - Token issued by signToken
|
|
70
|
+
* @returns Decoded payload, or null if the token is malformed
|
|
71
|
+
*/
|
|
72
|
+
export function readToken(token) {
|
|
73
|
+
return decode(token);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=challenge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"challenge.js","sourceRoot":"","sources":["../../src/utils/challenge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a hex color string into an RGB tuple.
|
|
3
|
+
*
|
|
4
|
+
* @param hex - Hex color string (#RRGGBB or RRGGBB)
|
|
5
|
+
* @returns RGB tuple [r, g, b] with values 0-255
|
|
6
|
+
* @throws Error if the hex string is invalid
|
|
7
|
+
*/
|
|
8
|
+
export declare function hexToRgb(hex: string): [number, number, number];
|
|
9
|
+
/**
|
|
10
|
+
* Converts HSL values to an RGB tuple.
|
|
11
|
+
*
|
|
12
|
+
* @param h - Hue in degrees (0-360)
|
|
13
|
+
* @param s - Saturation (0-1)
|
|
14
|
+
* @param l - Lightness (0-1)
|
|
15
|
+
* @returns RGB tuple [r, g, b] with values 0-255
|
|
16
|
+
*/
|
|
17
|
+
export declare function hslToRgb(h: number, s: number, l: number): [number, number, number];
|
|
18
|
+
/**
|
|
19
|
+
* Validates and normalizes a hex color string, adding # if missing.
|
|
20
|
+
*
|
|
21
|
+
* @param value - Raw color value (with or without #)
|
|
22
|
+
* @param def - Default color if value is empty
|
|
23
|
+
* @returns Normalized hex color string with #
|
|
24
|
+
* @throws Error if the color format is invalid
|
|
25
|
+
*/
|
|
26
|
+
export declare function normalizeColor(value: string | undefined, def: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Formats an RGB tuple as a hex color string.
|
|
29
|
+
*
|
|
30
|
+
* @param r - Red channel (0-255)
|
|
31
|
+
* @param g - Green channel (0-255)
|
|
32
|
+
* @param b - Blue channel (0-255)
|
|
33
|
+
* @returns Hex color string (#rrggbb)
|
|
34
|
+
*/
|
|
35
|
+
export declare function rgbToHex(r: number, g: number, b: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* Converts RGB values to an HSL tuple.
|
|
38
|
+
*
|
|
39
|
+
* @param r - Red channel (0-255)
|
|
40
|
+
* @param g - Green channel (0-255)
|
|
41
|
+
* @param b - Blue channel (0-255)
|
|
42
|
+
* @returns HSL tuple [h (0-360), s (0-1), l (0-1)]
|
|
43
|
+
*/
|
|
44
|
+
export declare function rgbToHsl(r: number, g: number, b: number): [number, number, number];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a hex color string into an RGB tuple.
|
|
3
|
+
*
|
|
4
|
+
* @param hex - Hex color string (#RRGGBB or RRGGBB)
|
|
5
|
+
* @returns RGB tuple [r, g, b] with values 0-255
|
|
6
|
+
* @throws Error if the hex string is invalid
|
|
7
|
+
*/
|
|
8
|
+
export function hexToRgb(hex) {
|
|
9
|
+
const clean = hex.replace('#', '');
|
|
10
|
+
if (!/^[0-9a-fA-F]{6}$/.test(clean))
|
|
11
|
+
throw new Error('Invalid HEX color (use #RRGGBB)');
|
|
12
|
+
return [parseInt(clean.slice(0, 2), 16), parseInt(clean.slice(2, 4), 16), parseInt(clean.slice(4, 6), 16)];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Converts HSL values to an RGB tuple.
|
|
16
|
+
*
|
|
17
|
+
* @param h - Hue in degrees (0-360)
|
|
18
|
+
* @param s - Saturation (0-1)
|
|
19
|
+
* @param l - Lightness (0-1)
|
|
20
|
+
* @returns RGB tuple [r, g, b] with values 0-255
|
|
21
|
+
*/
|
|
22
|
+
export function hslToRgb(h, s, l) {
|
|
23
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
24
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
25
|
+
const m = l - c / 2;
|
|
26
|
+
let r1 = 0, g1 = 0, b1 = 0;
|
|
27
|
+
if (h < 60)
|
|
28
|
+
[r1, g1, b1] = [c, x, 0];
|
|
29
|
+
else if (h < 120)
|
|
30
|
+
[r1, g1, b1] = [x, c, 0];
|
|
31
|
+
else if (h < 180)
|
|
32
|
+
[r1, g1, b1] = [0, c, x];
|
|
33
|
+
else if (h < 240)
|
|
34
|
+
[r1, g1, b1] = [0, x, c];
|
|
35
|
+
else if (h < 300)
|
|
36
|
+
[r1, g1, b1] = [x, 0, c];
|
|
37
|
+
else
|
|
38
|
+
[r1, g1, b1] = [c, 0, x];
|
|
39
|
+
return [Math.round((r1 + m) * 255), Math.round((g1 + m) * 255), Math.round((b1 + m) * 255)];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validates and normalizes a hex color string, adding # if missing.
|
|
43
|
+
*
|
|
44
|
+
* @param value - Raw color value (with or without #)
|
|
45
|
+
* @param def - Default color if value is empty
|
|
46
|
+
* @returns Normalized hex color string with #
|
|
47
|
+
* @throws Error if the color format is invalid
|
|
48
|
+
*/
|
|
49
|
+
export function normalizeColor(value, def) {
|
|
50
|
+
if (!value)
|
|
51
|
+
return def;
|
|
52
|
+
const clean = value.startsWith('#') ? value : `#${value}`;
|
|
53
|
+
if (!/^#([0-9a-fA-F]{3}){1,2}$/.test(clean))
|
|
54
|
+
throw new Error('Invalid color (use hex like ff6600)');
|
|
55
|
+
return clean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Formats an RGB tuple as a hex color string.
|
|
59
|
+
*
|
|
60
|
+
* @param r - Red channel (0-255)
|
|
61
|
+
* @param g - Green channel (0-255)
|
|
62
|
+
* @param b - Blue channel (0-255)
|
|
63
|
+
* @returns Hex color string (#rrggbb)
|
|
64
|
+
*/
|
|
65
|
+
export function rgbToHex(r, g, b) {
|
|
66
|
+
return `#${[r, g, b].map((x) => x.toString(16).padStart(2, '0')).join('')}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Converts RGB values to an HSL tuple.
|
|
70
|
+
*
|
|
71
|
+
* @param r - Red channel (0-255)
|
|
72
|
+
* @param g - Green channel (0-255)
|
|
73
|
+
* @param b - Blue channel (0-255)
|
|
74
|
+
* @returns HSL tuple [h (0-360), s (0-1), l (0-1)]
|
|
75
|
+
*/
|
|
76
|
+
export function rgbToHsl(r, g, b) {
|
|
77
|
+
const r1 = r / 255, g1 = g / 255, b1 = b / 255;
|
|
78
|
+
const max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1);
|
|
79
|
+
const l = (max + min) / 2;
|
|
80
|
+
if (max === min)
|
|
81
|
+
return [0, 0, l];
|
|
82
|
+
const d = max - min;
|
|
83
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
84
|
+
let h = 0;
|
|
85
|
+
if (max === r1)
|
|
86
|
+
h = (g1 - b1) / d + (g1 < b1 ? 6 : 0);
|
|
87
|
+
else if (max === g1)
|
|
88
|
+
h = (b1 - r1) / d + 2;
|
|
89
|
+
else
|
|
90
|
+
h = (r1 - g1) / d + 4;
|
|
91
|
+
return [(h * 60 + 360) % 360, s, l];
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=colors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/utils/colors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks requests per user and throws if the rate limit is exceeded.
|
|
3
|
+
*
|
|
4
|
+
* @param rateLimits - Store of timestamps per user
|
|
5
|
+
* @param userId - Identifier of the requesting user
|
|
6
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
7
|
+
* @param window - Time window in milliseconds
|
|
8
|
+
* @param limit - Maximum requests allowed in the window
|
|
9
|
+
* @returns false if within limits
|
|
10
|
+
* @throws Error if rate limit is exceeded
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkRateLimit(rateLimits: Record<string, number[]>, userId: string, timestamp: number, window?: number, limit?: number): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Parses a space-separated environment variable into an array.
|
|
15
|
+
*
|
|
16
|
+
* @param key - The environment variable name
|
|
17
|
+
* @returns The parsed array, or null if not set
|
|
18
|
+
*/
|
|
19
|
+
export declare function envList(key: string): string[] | null;
|
|
20
|
+
/**
|
|
21
|
+
* Formats a Date to ISO string without timezone suffix.
|
|
22
|
+
*
|
|
23
|
+
* @param date - The date to format
|
|
24
|
+
* @returns ISO-formatted string without trailing Z
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatDate(date: Date): string;
|
|
27
|
+
/**
|
|
28
|
+
* Generates a random IPv4 address.
|
|
29
|
+
*
|
|
30
|
+
* @returns A string in the format "X.X.X.X"
|
|
31
|
+
*/
|
|
32
|
+
export declare function genIP(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Returns a random element from an array.
|
|
35
|
+
*
|
|
36
|
+
* @param arr - The source array
|
|
37
|
+
* @returns A random element
|
|
38
|
+
*/
|
|
39
|
+
export declare function random<T>(arr: T[]): T;
|
|
40
|
+
/**
|
|
41
|
+
* Returns a random integer between min and max (inclusive).
|
|
42
|
+
*
|
|
43
|
+
* @param min - Lower bound
|
|
44
|
+
* @param max - Upper bound
|
|
45
|
+
* @returns A random integer in [min, max]
|
|
46
|
+
*/
|
|
47
|
+
export declare function randomNumber(min: number, max: number): number;
|
|
48
|
+
/**
|
|
49
|
+
* Checks if a version string is at least the given minimum.
|
|
50
|
+
*
|
|
51
|
+
* @param version - Version string (e.g. "v4")
|
|
52
|
+
* @param min - Minimum version number
|
|
53
|
+
* @returns true if the version is >= min
|
|
54
|
+
*/
|
|
55
|
+
export declare function since(version: string, min: number): boolean;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks requests per user and throws if the rate limit is exceeded.
|
|
3
|
+
*
|
|
4
|
+
* @param rateLimits - Store of timestamps per user
|
|
5
|
+
* @param userId - Identifier of the requesting user
|
|
6
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
7
|
+
* @param window - Time window in milliseconds
|
|
8
|
+
* @param limit - Maximum requests allowed in the window
|
|
9
|
+
* @returns false if within limits
|
|
10
|
+
* @throws Error if rate limit is exceeded
|
|
11
|
+
*/
|
|
12
|
+
export function checkRateLimit(rateLimits, userId, timestamp, window = 10000, limit = 50) {
|
|
13
|
+
for (const key of Object.keys(rateLimits)) {
|
|
14
|
+
if (key !== userId && !rateLimits[key].some((ts) => timestamp - ts < window)) {
|
|
15
|
+
delete rateLimits[key];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
rateLimits[userId] = (rateLimits[userId] ?? []).filter((ts) => timestamp - ts < window);
|
|
19
|
+
if (rateLimits[userId].length >= limit) {
|
|
20
|
+
const remainingTime = Math.ceil((rateLimits[userId][0] + window - timestamp) / 1000);
|
|
21
|
+
throw new Error(`Rate limit exceeded. Try again in ${remainingTime} seconds.`);
|
|
22
|
+
}
|
|
23
|
+
rateLimits[userId].push(timestamp);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parses a space-separated environment variable into an array.
|
|
28
|
+
*
|
|
29
|
+
* @param key - The environment variable name
|
|
30
|
+
* @returns The parsed array, or null if not set
|
|
31
|
+
*/
|
|
32
|
+
export function envList(key) {
|
|
33
|
+
const v = process.env[key];
|
|
34
|
+
return v && v !== 'undefined' ? v.split(' ') : null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Formats a Date to ISO string without timezone suffix.
|
|
38
|
+
*
|
|
39
|
+
* @param date - The date to format
|
|
40
|
+
* @returns ISO-formatted string without trailing Z
|
|
41
|
+
*/
|
|
42
|
+
export function formatDate(date) {
|
|
43
|
+
return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().replace('Z', '');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generates a random IPv4 address.
|
|
47
|
+
*
|
|
48
|
+
* @returns A string in the format "X.X.X.X"
|
|
49
|
+
*/
|
|
50
|
+
export function genIP() {
|
|
51
|
+
return `${randomNumber(0, 255)}.${randomNumber(0, 255)}.${randomNumber(0, 255)}.${randomNumber(0, 255)}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns a random element from an array.
|
|
55
|
+
*
|
|
56
|
+
* @param arr - The source array
|
|
57
|
+
* @returns A random element
|
|
58
|
+
*/
|
|
59
|
+
export function random(arr) {
|
|
60
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns a random integer between min and max (inclusive).
|
|
64
|
+
*
|
|
65
|
+
* @param min - Lower bound
|
|
66
|
+
* @param max - Upper bound
|
|
67
|
+
* @returns A random integer in [min, max]
|
|
68
|
+
*/
|
|
69
|
+
export function randomNumber(min, max) {
|
|
70
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Checks if a version string is at least the given minimum.
|
|
74
|
+
*
|
|
75
|
+
* @param version - Version string (e.g. "v4")
|
|
76
|
+
* @param min - Minimum version number
|
|
77
|
+
* @returns true if the version is >= min
|
|
78
|
+
*/
|
|
79
|
+
export function since(version, min) {
|
|
80
|
+
return parseInt(version.replace('v', '')) >= min;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAC1B,UAAoC,EACpC,MAAc,EACd,SAAiB,EACjB,MAAM,GAAG,KAAK,EACd,KAAK,GAAG,EAAE;IAEV,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;YAC5E,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC;IAED,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC;IAExF,IAAI,UAAU,CAAC,MAAM,CAAE,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QACtC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAE,CAAC,CAAC,CAAE,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;QACvF,MAAM,IAAI,KAAK,CAAC,qCAAqC,aAAa,WAAW,CAAC,CAAC;IACnF,CAAC;IAED,UAAU,CAAC,MAAM,CAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,GAAW;IAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAU;IACjC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACtG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK;IACjB,OAAO,GAAG,YAAY,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,YAAY,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,YAAY,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,YAAY,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AAC7G,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAI,GAAQ;IAC9B,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAE,CAAC;AACxD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,GAAW;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CAAC,OAAe,EAAE,GAAW;IAC9C,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Sends a standardized JSON error response.
|
|
4
|
+
*
|
|
5
|
+
* @param res - Express response object
|
|
6
|
+
* @param status - HTTP status code
|
|
7
|
+
* @param message - Error description
|
|
8
|
+
*/
|
|
9
|
+
export declare function error(res: Response, status: number, message: string): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { STATUS_MESSAGES } from '../constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sends a standardized JSON error response.
|
|
4
|
+
*
|
|
5
|
+
* @param res - Express response object
|
|
6
|
+
* @param status - HTTP status code
|
|
7
|
+
* @param message - Error description
|
|
8
|
+
*/
|
|
9
|
+
export function error(res, status, message) {
|
|
10
|
+
const body = {
|
|
11
|
+
message: STATUS_MESSAGES[status] ?? 'Error',
|
|
12
|
+
error: message,
|
|
13
|
+
status,
|
|
14
|
+
};
|
|
15
|
+
if (typeof res.jsonResponse === 'function') {
|
|
16
|
+
res.status(status).jsonResponse(body);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
res.status(status)
|
|
20
|
+
.setHeader('Content-Type', 'application/json')
|
|
21
|
+
.send(JSON.stringify(body, null, 2));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../src/utils/response.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CAAC,GAAa,EAAE,MAAc,EAAE,OAAe;IAChE,MAAM,IAAI,GAAG;QACT,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC,IAAI,OAAO;QAC3C,KAAK,EAAE,OAAO;QACd,MAAM;KACT,CAAC;IAEF,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;QACzC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;aACb,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC;aAC7C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;AACL,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "6.0.0-pre",
|
|
3
|
+
"name": "@moxon/api",
|
|
4
|
+
"description": "Node.js API with multiple features. Check the documentation at https://docs.sylvain.sh",
|
|
5
|
+
"main": "./dist/modules/v6.js",
|
|
6
|
+
"types": "./dist/modules/v6.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"prepare": "npm run clean && tsc",
|
|
10
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
11
|
+
"dev": "tsx watch src/app.ts",
|
|
12
|
+
"build": "npm run clean && tsc",
|
|
13
|
+
"start": "node dist/app.js",
|
|
14
|
+
"check": "tsc --noEmit -p tsconfig.test.json && eslint src/ tests/ && prettier --check .",
|
|
15
|
+
"format": "prettier --write . && eslint --fix src/ tests/",
|
|
16
|
+
"test": "NODE_ENV=test DOCS_URL=https://docs.example.test node --import tsx --test tests/unit/*.test.ts tests/integration/*.test.ts"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=22.12.0"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/modules/v6.d.ts",
|
|
24
|
+
"default": "./dist/modules/v6.js"
|
|
25
|
+
},
|
|
26
|
+
"./server": {
|
|
27
|
+
"types": "./dist/app.d.ts",
|
|
28
|
+
"default": "./dist/app.js"
|
|
29
|
+
},
|
|
30
|
+
"./v4": {
|
|
31
|
+
"types": "./dist/modules/v4.d.ts",
|
|
32
|
+
"default": "./dist/modules/v4.js"
|
|
33
|
+
},
|
|
34
|
+
"./v5": {
|
|
35
|
+
"types": "./dist/modules/v5.d.ts",
|
|
36
|
+
"default": "./dist/modules/v5.js"
|
|
37
|
+
},
|
|
38
|
+
"./v6": {
|
|
39
|
+
"types": "./dist/modules/v6.d.ts",
|
|
40
|
+
"default": "./dist/modules/v6.js"
|
|
41
|
+
},
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@20syldev/logger.ts": "^1.0.2",
|
|
46
|
+
"@types/express": "^5.0.0",
|
|
47
|
+
"@types/node": "^22.19.17",
|
|
48
|
+
"canvas": "^3.1.0",
|
|
49
|
+
"cors": "^2.8.5",
|
|
50
|
+
"dotenv": "^16.4.7",
|
|
51
|
+
"express": "^5.1.0",
|
|
52
|
+
"ical.js": "^2.1.0",
|
|
53
|
+
"qrcode": "^1.5.4",
|
|
54
|
+
"random": "^5.3.0",
|
|
55
|
+
"uuid": "^14.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@eslint/js": "^9.24.0",
|
|
59
|
+
"@types/cors": "^2.8.17",
|
|
60
|
+
"@types/qrcode": "^1.5.6",
|
|
61
|
+
"@types/uuid": "^10.0.0",
|
|
62
|
+
"eslint": "^9.24.0",
|
|
63
|
+
"eslint-config-prettier": "^10.1.1",
|
|
64
|
+
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
65
|
+
"prettier": "^3.5.3",
|
|
66
|
+
"tsx": "^4.19.0",
|
|
67
|
+
"typescript": "^5.7.0",
|
|
68
|
+
"typescript-eslint": "^8.29.0"
|
|
69
|
+
},
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public"
|
|
72
|
+
},
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "git+https://github.com/20syldev/moxon.git"
|
|
76
|
+
},
|
|
77
|
+
"keywords": [
|
|
78
|
+
"api",
|
|
79
|
+
"express",
|
|
80
|
+
"utility",
|
|
81
|
+
"math",
|
|
82
|
+
"typescript"
|
|
83
|
+
],
|
|
84
|
+
"author": "Sylvain L.",
|
|
85
|
+
"license": "BSD 3-Clause",
|
|
86
|
+
"bugs": {
|
|
87
|
+
"url": "https://github.com/20syldev/moxon/issues"
|
|
88
|
+
},
|
|
89
|
+
"homepage": "https://api.sylvain.sh"
|
|
90
|
+
}
|