enerthya.dev-web-common 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/constants/httpStatus.js +23 -0
- package/esm/constants/index.js +3 -0
- package/esm/constants/routes.js +72 -0
- package/esm/index.js +15 -0
- package/esm/structures/ApiResponse.js +86 -0
- package/esm/structures/index.js +8 -0
- package/esm/utils/ParamValidator.js +150 -0
- package/esm/utils/UrlUtils.js +75 -0
- package/esm/utils/index.js +3 -0
- package/package.json +12 -16
- package/src/utils/UrlUtils.js +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP_STATUS — Named HTTP status codes used across the Enerthya stack.
|
|
3
|
+
*
|
|
4
|
+
* Use these instead of magic numbers in route handlers and client error checks.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* res.status(HTTP_STATUS.UNAUTHORIZED).json(createError(401, "Not authenticated"));
|
|
8
|
+
*/
|
|
9
|
+
export const HTTP_STATUS = {
|
|
10
|
+
OK: 200,
|
|
11
|
+
CREATED: 201,
|
|
12
|
+
NO_CONTENT: 204,
|
|
13
|
+
BAD_REQUEST: 400,
|
|
14
|
+
UNAUTHORIZED: 401,
|
|
15
|
+
FORBIDDEN: 403,
|
|
16
|
+
NOT_FOUND: 404,
|
|
17
|
+
CONFLICT: 409,
|
|
18
|
+
UNPROCESSABLE: 422,
|
|
19
|
+
TOO_MANY_REQUESTS: 429,
|
|
20
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
21
|
+
SERVICE_UNAVAILABLE: 503,
|
|
22
|
+
};
|
|
23
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API_ROUTES — Central source of truth for all API route paths.
|
|
3
|
+
*
|
|
4
|
+
* Every route is defined once here and imported everywhere else.
|
|
5
|
+
* Prevents strings from diverging between server/routes/ and client/src/.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { API_ROUTES } = require("enerthya.dev-web-common");
|
|
9
|
+
* API_ROUTES.GUILD_CONFIG("123456") // => "/guilds/123456/config"
|
|
10
|
+
* API_ROUTES.AUTH.USER // => "/auth/user"
|
|
11
|
+
*
|
|
12
|
+
* All paths are relative (no leading "/api/") — the client's axios baseURL
|
|
13
|
+
* already includes "/api", and the server mounts on "/api" as well.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const API_ROUTES = {
|
|
17
|
+
// ─── Auth ────────────────────────────────────────────────────────────────
|
|
18
|
+
AUTH: {
|
|
19
|
+
/** GET — OAuth2 login redirect */
|
|
20
|
+
LOGIN: "/auth/login",
|
|
21
|
+
/** GET — OAuth2 callback */
|
|
22
|
+
CALLBACK: "/auth/callback",
|
|
23
|
+
/** POST — Logout and destroy session */
|
|
24
|
+
LOGOUT: "/auth/logout",
|
|
25
|
+
/** GET — Current authenticated user */
|
|
26
|
+
USER: "/auth/user",
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// ─── Guilds ──────────────────────────────────────────────────────────────
|
|
30
|
+
/** GET — All guilds the user manages */
|
|
31
|
+
GUILDS: "/guilds",
|
|
32
|
+
/**
|
|
33
|
+
* GET / POST — Guild config
|
|
34
|
+
* @param {string} id - Discord guild ID
|
|
35
|
+
*/
|
|
36
|
+
GUILD_CONFIG: (id) => `/guilds/${id}/config`,
|
|
37
|
+
/**
|
|
38
|
+
* GET — Warns for a guild
|
|
39
|
+
* @param {string} id - Discord guild ID
|
|
40
|
+
*/
|
|
41
|
+
GUILD_WARNS: (id) => `/guilds/${id}/warns`,
|
|
42
|
+
/**
|
|
43
|
+
* DELETE — Remove a specific warn
|
|
44
|
+
* @param {string} id - Discord guild ID
|
|
45
|
+
* @param {string} warnId - Warn document ID
|
|
46
|
+
*/
|
|
47
|
+
GUILD_WARN: (id, warnId) => `/guilds/${id}/warns/${warnId}`,
|
|
48
|
+
/**
|
|
49
|
+
* GET — Command-level permission settings for a guild
|
|
50
|
+
* @param {string} id - Discord guild ID
|
|
51
|
+
*/
|
|
52
|
+
GUILD_COMMAND_SETTINGS: (id) => `/guilds/${id}/command-settings`,
|
|
53
|
+
|
|
54
|
+
// ─── Daily ───────────────────────────────────────────────────────────────
|
|
55
|
+
DAILY: {
|
|
56
|
+
/** GET — Daily status (coins, streak, next claim) */
|
|
57
|
+
STATUS: "/daily",
|
|
58
|
+
/** GET — Captcha for daily claim */
|
|
59
|
+
CAPTCHA: "/daily/captcha",
|
|
60
|
+
/** POST — Claim daily reward */
|
|
61
|
+
CLAIM: "/daily/claim",
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// ─── Meta ─────────────────────────────────────────────────────────────────
|
|
65
|
+
/** GET — Bot + shard status */
|
|
66
|
+
STATUS: "/status",
|
|
67
|
+
/** GET — All registered commands */
|
|
68
|
+
COMMANDS: "/commands",
|
|
69
|
+
/** GET — Dev-only diagnostics */
|
|
70
|
+
DEV: "/dev",
|
|
71
|
+
};
|
|
72
|
+
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* enerthya.dev-web-common — ESM build
|
|
3
|
+
* Bundlers e Vite usam este arquivo via campo "exports"."import" no package.json.
|
|
4
|
+
* Node.js (bot/server) continua usando src/index.js via "require".
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { API_ROUTES, HTTP_STATUS } from './constants/index.js';
|
|
8
|
+
export { UrlUtils, ParamValidator } from './utils/index.js';
|
|
9
|
+
export {
|
|
10
|
+
createSuccess,
|
|
11
|
+
createError,
|
|
12
|
+
isApiSuccess,
|
|
13
|
+
isApiError,
|
|
14
|
+
API_ERRORS,
|
|
15
|
+
} from './structures/index.js';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response factories — Standardised API response shapes.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Loritta's `loritta-website:web-common` typed response wrappers.
|
|
5
|
+
*
|
|
6
|
+
* All server routes should use these factories so the client always receives
|
|
7
|
+
* a predictable envelope. The client uses `isApiError` / `isApiSuccess` to
|
|
8
|
+
* branch on the result without checking HTTP status codes manually.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // In an Express route handler:
|
|
12
|
+
* res.json(createSuccess({ guilds: [...] }));
|
|
13
|
+
* res.status(403).json(createError(403, "Forbidden", "MISSING_PERMISSION"));
|
|
14
|
+
*
|
|
15
|
+
* // In a React component:
|
|
16
|
+
* const data = await axios.get(...);
|
|
17
|
+
* if (isApiError(data.data)) { ... }
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Builds a successful API response envelope.
|
|
22
|
+
*
|
|
23
|
+
* @template T
|
|
24
|
+
* @param {T} data - Payload to embed in the response.
|
|
25
|
+
* @returns {{ ok: true, data: T }}
|
|
26
|
+
*/
|
|
27
|
+
export function createSuccess(data) {
|
|
28
|
+
return { ok: true, data };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Builds an error API response envelope.
|
|
33
|
+
*
|
|
34
|
+
* @param {number} status - HTTP status code (for reference in the body).
|
|
35
|
+
* @param {string} message - Human-readable error message.
|
|
36
|
+
* @param {string} [code] - Optional machine-readable error code (UPPER_SNAKE).
|
|
37
|
+
* @returns {{ ok: false, status: number, message: string, code?: string }}
|
|
38
|
+
*/
|
|
39
|
+
export function createError(status, message, code) {
|
|
40
|
+
const response = { ok: false, status, message };
|
|
41
|
+
if (code !== undefined) response.code = code;
|
|
42
|
+
return response;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if the value looks like a successful API response.
|
|
47
|
+
*
|
|
48
|
+
* @param {unknown} value
|
|
49
|
+
* @returns {boolean}
|
|
50
|
+
*/
|
|
51
|
+
export function isApiSuccess(value) {
|
|
52
|
+
return (
|
|
53
|
+
value !== null &&
|
|
54
|
+
typeof value === "object" &&
|
|
55
|
+
value.ok === true &&
|
|
56
|
+
"data" in value
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Returns true if the value looks like an error API response.
|
|
62
|
+
*
|
|
63
|
+
* @param {unknown} value
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
export function isApiError(value) {
|
|
67
|
+
return (
|
|
68
|
+
value !== null &&
|
|
69
|
+
typeof value === "object" &&
|
|
70
|
+
value.ok === false &&
|
|
71
|
+
typeof value.message === "string"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Common pre-built error responses.
|
|
77
|
+
* Use these constants to avoid typos in repeated error messages.
|
|
78
|
+
*/
|
|
79
|
+
export const API_ERRORS = {
|
|
80
|
+
NOT_AUTHENTICATED: createError(401, "Not authenticated", "NOT_AUTHENTICATED"),
|
|
81
|
+
FORBIDDEN: createError(403, "Access forbidden", "FORBIDDEN"),
|
|
82
|
+
NOT_FOUND: createError(404, "Resource not found", "NOT_FOUND"),
|
|
83
|
+
INTERNAL: createError(500, "Internal server error", "INTERNAL_SERVER_ERROR"),
|
|
84
|
+
BOT_OFFLINE: createError(503, "Bot is offline or unreachable", "BOT_OFFLINE"),
|
|
85
|
+
};
|
|
86
|
+
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ParamValidator — Validates route parameters and request bodies.
|
|
3
|
+
*
|
|
4
|
+
* Used in Express route handlers to validate inputs before touching
|
|
5
|
+
* the database. Returns structured results instead of throwing, so
|
|
6
|
+
* routes can decide how to respond.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const { ParamValidator } = require("enerthya.dev-web-common");
|
|
10
|
+
*
|
|
11
|
+
* const result = ParamValidator.guildId(req.params.id);
|
|
12
|
+
* if (!result.valid) return res.status(400).json(createError(400, result.error));
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const ParamValidator = {
|
|
16
|
+
/**
|
|
17
|
+
* Validates a Discord guild ID (snowflake).
|
|
18
|
+
* @param {unknown} id
|
|
19
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
20
|
+
*/
|
|
21
|
+
guildId(id) {
|
|
22
|
+
if (typeof id !== "string" || !/^\d{17,20}$/.test(id)) {
|
|
23
|
+
return { valid: false, error: "Invalid guild ID — must be a 17-20 digit Discord snowflake" };
|
|
24
|
+
}
|
|
25
|
+
return { valid: true };
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validates a MongoDB ObjectId string (24 hex chars).
|
|
30
|
+
* @param {unknown} id
|
|
31
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
32
|
+
*/
|
|
33
|
+
objectId(id) {
|
|
34
|
+
if (typeof id !== "string" || !/^[a-f0-9]{24}$/i.test(id)) {
|
|
35
|
+
return { valid: false, error: "Invalid ID — must be a 24-character hex string" };
|
|
36
|
+
}
|
|
37
|
+
return { valid: true };
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Validates a required string field in a request body.
|
|
42
|
+
* @param {unknown} value
|
|
43
|
+
* @param {string} fieldName - Used in the error message.
|
|
44
|
+
* @param {{ minLength?: number, maxLength?: number }} [options]
|
|
45
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
46
|
+
*/
|
|
47
|
+
requiredString(value, fieldName, options = {}) {
|
|
48
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
49
|
+
return { valid: false, error: `"${fieldName}" is required and must be a non-empty string` };
|
|
50
|
+
}
|
|
51
|
+
const { minLength, maxLength } = options;
|
|
52
|
+
if (minLength !== undefined && value.length < minLength) {
|
|
53
|
+
return { valid: false, error: `"${fieldName}" must be at least ${minLength} characters` };
|
|
54
|
+
}
|
|
55
|
+
if (maxLength !== undefined && value.length > maxLength) {
|
|
56
|
+
return { valid: false, error: `"${fieldName}" must be at most ${maxLength} characters` };
|
|
57
|
+
}
|
|
58
|
+
return { valid: true };
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Validates an optional string field — passes if undefined/null, fails if wrong type.
|
|
63
|
+
* @param {unknown} value
|
|
64
|
+
* @param {string} fieldName
|
|
65
|
+
* @param {{ maxLength?: number }} [options]
|
|
66
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
67
|
+
*/
|
|
68
|
+
optionalString(value, fieldName, options = {}) {
|
|
69
|
+
if (value === undefined || value === null) return { valid: true };
|
|
70
|
+
if (typeof value !== "string") {
|
|
71
|
+
return { valid: false, error: `"${fieldName}" must be a string or null` };
|
|
72
|
+
}
|
|
73
|
+
const { maxLength } = options;
|
|
74
|
+
if (maxLength !== undefined && value.length > maxLength) {
|
|
75
|
+
return { valid: false, error: `"${fieldName}" must be at most ${maxLength} characters` };
|
|
76
|
+
}
|
|
77
|
+
return { valid: true };
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validates a boolean field.
|
|
82
|
+
* @param {unknown} value
|
|
83
|
+
* @param {string} fieldName
|
|
84
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
85
|
+
*/
|
|
86
|
+
boolean(value, fieldName) {
|
|
87
|
+
if (typeof value !== "boolean") {
|
|
88
|
+
return { valid: false, error: `"${fieldName}" must be a boolean (true or false)` };
|
|
89
|
+
}
|
|
90
|
+
return { valid: true };
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validates a non-negative integer field.
|
|
95
|
+
* @param {unknown} value
|
|
96
|
+
* @param {string} fieldName
|
|
97
|
+
* @param {{ max?: number }} [options]
|
|
98
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
99
|
+
*/
|
|
100
|
+
nonNegativeInt(value, fieldName, options = {}) {
|
|
101
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
102
|
+
return { valid: false, error: `"${fieldName}" must be a non-negative integer` };
|
|
103
|
+
}
|
|
104
|
+
const { max } = options;
|
|
105
|
+
if (max !== undefined && value > max) {
|
|
106
|
+
return { valid: false, error: `"${fieldName}" must be at most ${max}` };
|
|
107
|
+
}
|
|
108
|
+
return { valid: true };
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validates a hex color string (e.g. "#5865F2" or "#FFF").
|
|
113
|
+
* @param {unknown} value
|
|
114
|
+
* @param {string} fieldName
|
|
115
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
116
|
+
*/
|
|
117
|
+
hexColor(value, fieldName) {
|
|
118
|
+
if (typeof value !== "string" || !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value)) {
|
|
119
|
+
return { valid: false, error: `"${fieldName}" must be a valid hex color (e.g. #5865F2)` };
|
|
120
|
+
}
|
|
121
|
+
return { valid: true };
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Validates a Discord snowflake (channel ID, role ID, user ID, etc.).
|
|
126
|
+
* Alias for guildId — same format.
|
|
127
|
+
* @param {unknown} id
|
|
128
|
+
* @param {string} [fieldName]
|
|
129
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
130
|
+
*/
|
|
131
|
+
snowflake(id, fieldName = "ID") {
|
|
132
|
+
if (typeof id !== "string" || !/^\d{17,20}$/.test(id)) {
|
|
133
|
+
return { valid: false, error: `"${fieldName}" must be a valid Discord snowflake ID` };
|
|
134
|
+
}
|
|
135
|
+
return { valid: true };
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Runs multiple validators and returns the first failure, or { valid: true }.
|
|
140
|
+
* @param {...{ valid: boolean, error?: string }} results
|
|
141
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
142
|
+
*/
|
|
143
|
+
all(...results) {
|
|
144
|
+
for (const result of results) {
|
|
145
|
+
if (!result.valid) return result;
|
|
146
|
+
}
|
|
147
|
+
return { valid: true };
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UrlUtils — URL building and parsing helpers for the Enerthya dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Centralises all path construction so route strings never drift between
|
|
5
|
+
* the server and the client.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* UrlUtils.guildBase("123456") // "/dashboard/123456"
|
|
9
|
+
* UrlUtils.guildModule("123456", "xp-rates") // "/dashboard/123456/xp-rates"
|
|
10
|
+
* UrlUtils.parseGuildId("/dashboard/123456/overview") // "123456"
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const DASHBOARD_PREFIX = "/dashboard";
|
|
14
|
+
|
|
15
|
+
export const UrlUtils = {
|
|
16
|
+
/**
|
|
17
|
+
* Root path for a guild in the dashboard.
|
|
18
|
+
* @param {string} guildId
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
guildBase(guildId) {
|
|
22
|
+
if (!guildId || typeof guildId !== "string") {
|
|
23
|
+
throw new TypeError("guildId must be a non-empty string");
|
|
24
|
+
}
|
|
25
|
+
return `${DASHBOARD_PREFIX}/${guildId}`;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Path to a specific module page inside a guild.
|
|
30
|
+
* @param {string} guildId
|
|
31
|
+
* @param {string} module - e.g. "welcome", "xp-rates", "suggestion"
|
|
32
|
+
* @returns {string}
|
|
33
|
+
*/
|
|
34
|
+
guildModule(guildId, module) {
|
|
35
|
+
if (!module || typeof module !== "string") {
|
|
36
|
+
throw new TypeError("module must be a non-empty string");
|
|
37
|
+
}
|
|
38
|
+
return `${UrlUtils.guildBase(guildId)}/${module}`;
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extracts the guild ID from a dashboard URL path.
|
|
43
|
+
* Returns null if the path is not a valid guild path.
|
|
44
|
+
* @param {string} pathname
|
|
45
|
+
* @returns {string|null}
|
|
46
|
+
*/
|
|
47
|
+
parseGuildId(pathname) {
|
|
48
|
+
if (typeof pathname !== "string") return null;
|
|
49
|
+
const match = pathname.match(/^\/dashboard\/(\d{17,20})/);
|
|
50
|
+
return match ? match[1] : null;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns true if the given string looks like a valid Discord snowflake ID.
|
|
55
|
+
* @param {string|unknown} id
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
isValidSnowflake(id) {
|
|
59
|
+
return typeof id === "string" && /^\d{17,20}$/.test(id);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Builds a full absolute URL given a base and a relative path.
|
|
64
|
+
* Safely handles trailing slashes on the base.
|
|
65
|
+
* @param {string} base - e.g. "https://enerthya.dev"
|
|
66
|
+
* @param {string} path - e.g. "/dashboard/123456/welcome"
|
|
67
|
+
* @returns {string}
|
|
68
|
+
*/
|
|
69
|
+
absolute(base, path) {
|
|
70
|
+
const cleanBase = base.replace(/\/+$/, "");
|
|
71
|
+
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
72
|
+
return `${cleanBase}${cleanPath}`;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
package/package.json
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "enerthya.dev-web-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Shared web layer for the Enerthya ecosystem: API routes, response envelopes, URL helpers and validators. Inspired by Loritta's web-common and dashboard-common modules.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
"module": "esm/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./esm/index.js",
|
|
10
|
+
"require": "./src/index.js",
|
|
11
|
+
"default": "./esm/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"keywords": ["enerthya", "discord", "web", "api", "routes", "common"],
|
|
14
15
|
"author": "Enerthya",
|
|
15
16
|
"license": "MIT",
|
|
16
|
-
"engines": {
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
"files": [
|
|
20
|
-
"src/",
|
|
21
|
-
"README.md"
|
|
22
|
-
],
|
|
17
|
+
"engines": { "node": ">=18.0.0" },
|
|
18
|
+
"files": ["src/", "esm/", "README.md"],
|
|
23
19
|
"dependencies": {}
|
|
24
20
|
}
|
package/src/utils/UrlUtils.js
CHANGED
|
@@ -28,7 +28,7 @@ const UrlUtils = {
|
|
|
28
28
|
/**
|
|
29
29
|
* Path to a specific module page inside a guild.
|
|
30
30
|
* @param {string} guildId
|
|
31
|
-
* @param {string} module - e.g. "welcome", "xp-rates", "
|
|
31
|
+
* @param {string} module - e.g. "welcome", "xp-rates", "suggestion"
|
|
32
32
|
* @returns {string}
|
|
33
33
|
*/
|
|
34
34
|
guildModule(guildId, module) {
|