kythia-core 0.10.1-beta → 0.11.0-beta
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 +87 -1
- package/package.json +11 -1
- package/src/Kythia.js +9 -7
- package/src/KythiaClient.js +1 -1
- package/src/cli/Command.js +68 -0
- package/src/cli/commands/CacheClearCommand.js +136 -0
- package/src/cli/commands/LangCheckCommand.js +367 -0
- package/src/cli/commands/LangTranslateCommand.js +336 -0
- package/src/cli/commands/MakeMigrationCommand.js +82 -0
- package/src/cli/commands/MakeModelCommand.js +81 -0
- package/src/cli/commands/MigrateCommand.js +259 -0
- package/src/cli/commands/NamespaceCommand.js +112 -0
- package/src/cli/commands/StructureCommand.js +70 -0
- package/src/cli/commands/UpversionCommand.js +94 -0
- package/src/cli/index.js +69 -0
- package/src/cli/utils/db.js +117 -0
- package/src/database/KythiaMigrator.js +1 -1
- package/src/database/KythiaModel.js +7 -8
- package/src/database/KythiaSequelize.js +1 -1
- package/src/database/KythiaStorage.js +1 -1
- package/src/database/ModelLoader.js +1 -1
- package/src/managers/AddonManager.js +10 -1
- package/src/managers/EventManager.js +1 -1
- package/src/managers/InteractionManager.js +2 -2
- package/src/managers/ShutdownManager.js +1 -1
- package/.husky/pre-commit +0 -4
- package/biome.json +0 -40
- package/bun.lock +0 -445
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ⚡ Kythia CLI Entry Point
|
|
5
|
+
*
|
|
6
|
+
* @file src/cli/index.js
|
|
7
|
+
* @copyright © 2025 kenndeclouv
|
|
8
|
+
* @assistant chaa & graa
|
|
9
|
+
* @version 0.11.0-beta
|
|
10
|
+
*
|
|
11
|
+
* @description
|
|
12
|
+
* The main bootstrap entry point for the Kythia CLI.
|
|
13
|
+
* It dynamically scans, loads, and registers all command classes found in the
|
|
14
|
+
* `commands` directory, orchestrating the `commander` program execution.
|
|
15
|
+
*
|
|
16
|
+
* ✨ Core Features:
|
|
17
|
+
* - Dynamic Loading: Automatically finds new commands without manual import.
|
|
18
|
+
* - Class-Based Architecture: Supports standard `Command` class structure.
|
|
19
|
+
* - Error Handling: Gracefully handles malformed commands during boot.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const { version } = require('../../package.json');
|
|
23
|
+
const { Command } = require('commander');
|
|
24
|
+
const BaseCommand = require('./Command');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
const pc = require('picocolors');
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
|
|
29
|
+
const program = new Command();
|
|
30
|
+
|
|
31
|
+
program
|
|
32
|
+
.name('kythia')
|
|
33
|
+
.description(pc.cyan('🌸 Kythia Framework CLI'))
|
|
34
|
+
.version(version);
|
|
35
|
+
|
|
36
|
+
const commandsDir = path.join(__dirname, 'commands');
|
|
37
|
+
|
|
38
|
+
if (fs.existsSync(commandsDir)) {
|
|
39
|
+
const commandFiles = fs
|
|
40
|
+
.readdirSync(commandsDir)
|
|
41
|
+
.filter((file) => file.endsWith('.js'));
|
|
42
|
+
|
|
43
|
+
for (const file of commandFiles) {
|
|
44
|
+
const filePath = path.join(commandsDir, file);
|
|
45
|
+
try {
|
|
46
|
+
const CommandClass = require(filePath);
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
typeof CommandClass === 'function' &&
|
|
50
|
+
CommandClass.prototype instanceof BaseCommand
|
|
51
|
+
) {
|
|
52
|
+
const commandInstance = new CommandClass();
|
|
53
|
+
commandInstance.register(program);
|
|
54
|
+
} else if (typeof CommandClass.register === 'function') {
|
|
55
|
+
CommandClass.register(program);
|
|
56
|
+
} else {
|
|
57
|
+
console.warn(
|
|
58
|
+
pc.yellow(
|
|
59
|
+
`⚠️ Skipped ${file}: Not a valid Command class or missing 'register' function.`,
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error(pc.red(`❌ Failed to load command ${file}:`), err);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔌 CLI Database & Migration Bootstrapper
|
|
3
|
+
*
|
|
4
|
+
* @file src/cli/utils/db.js
|
|
5
|
+
* @copyright © 2025 kenndeclouv
|
|
6
|
+
* @assistant chaa & graa
|
|
7
|
+
* @version 0.11.0-beta
|
|
8
|
+
*
|
|
9
|
+
* @description
|
|
10
|
+
* Initializes the database connection and migration engine (Umzug) specifically
|
|
11
|
+
* for CLI operations. It dynamically loads the project configuration and
|
|
12
|
+
* discovers migration files across all addons.
|
|
13
|
+
*
|
|
14
|
+
* ✨ Core Features:
|
|
15
|
+
* - Dynamic Config: Loads `kythia.config.js` from the user's project root.
|
|
16
|
+
* - Auto-Discovery: Scans `addons` directory for migration files.
|
|
17
|
+
* - Pretty Logging: Custom console output using `picocolors` for migration status.
|
|
18
|
+
* - Singleton-like: Exports shared instances of Sequelize and Umzug.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
require('@dotenvx/dotenvx/config');
|
|
22
|
+
const fs = require('node:fs');
|
|
23
|
+
const path = require('node:path');
|
|
24
|
+
const { Umzug } = require('umzug');
|
|
25
|
+
const createSequelizeInstance = require('../../database/KythiaSequelize');
|
|
26
|
+
const KythiaStorage = require('../../database/KythiaStorage');
|
|
27
|
+
const pc = require('picocolors');
|
|
28
|
+
|
|
29
|
+
const configPath = path.resolve(process.cwd(), 'kythia.config.js');
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(configPath)) {
|
|
32
|
+
console.error('❌ kythia.config.js not found in root directory!');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const config = require(configPath);
|
|
37
|
+
|
|
38
|
+
const logger = {
|
|
39
|
+
info: () => {},
|
|
40
|
+
error: console.error,
|
|
41
|
+
debug: () => {},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const sequelize = createSequelizeInstance(config, logger);
|
|
45
|
+
|
|
46
|
+
function getMigrationFiles() {
|
|
47
|
+
const rootDir = process.cwd();
|
|
48
|
+
const addonsDir = path.join(rootDir, 'addons');
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(addonsDir)) return [];
|
|
51
|
+
|
|
52
|
+
const migrationFiles = [];
|
|
53
|
+
const addonFolders = fs
|
|
54
|
+
.readdirSync(addonsDir)
|
|
55
|
+
.filter((f) => fs.statSync(path.join(addonsDir, f)).isDirectory());
|
|
56
|
+
|
|
57
|
+
for (const addon of addonFolders) {
|
|
58
|
+
const migrationDir = path.join(addonsDir, addon, 'database', 'migrations');
|
|
59
|
+
if (fs.existsSync(migrationDir)) {
|
|
60
|
+
const files = fs
|
|
61
|
+
.readdirSync(migrationDir)
|
|
62
|
+
.filter((f) => f.endsWith('.js'))
|
|
63
|
+
.map((f) => ({
|
|
64
|
+
name: f,
|
|
65
|
+
path: path.join(migrationDir, f),
|
|
66
|
+
}));
|
|
67
|
+
migrationFiles.push(...files);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return migrationFiles.sort((a, b) => a.name.localeCompare(b.name));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const storage = new KythiaStorage({ sequelize });
|
|
74
|
+
|
|
75
|
+
const umzugLogger = {
|
|
76
|
+
info: (event) => {
|
|
77
|
+
if (typeof event === 'object') {
|
|
78
|
+
if (event.event === 'migrated') {
|
|
79
|
+
console.log(
|
|
80
|
+
pc.green(`✅ Migrated: ${event.name} `) +
|
|
81
|
+
pc.gray(`(${event.durationSeconds}s)`),
|
|
82
|
+
);
|
|
83
|
+
} else if (event.event === 'reverting') {
|
|
84
|
+
console.log(pc.yellow(`↩️ Reverting: ${event.name}`));
|
|
85
|
+
} else if (event.event === 'reverted') {
|
|
86
|
+
console.log(
|
|
87
|
+
pc.red(`❌ Reverted: ${event.name} `) +
|
|
88
|
+
pc.gray(`(${event.durationSeconds}s)`),
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
console.log(pc.dim(event));
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
warn: (msg) => console.warn(pc.yellow(`⚠️ ${msg}`)),
|
|
96
|
+
error: (msg) => console.error(pc.red(`🔥 ${msg}`)),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const umzug = new Umzug({
|
|
100
|
+
migrations: getMigrationFiles().map((m) => ({
|
|
101
|
+
name: m.name,
|
|
102
|
+
path: m.path,
|
|
103
|
+
up: async ({ context }) => {
|
|
104
|
+
const migration = require(m.path);
|
|
105
|
+
return migration.up(context, require('sequelize').DataTypes);
|
|
106
|
+
},
|
|
107
|
+
down: async ({ context }) => {
|
|
108
|
+
const migration = require(m.path);
|
|
109
|
+
return migration.down(context, require('sequelize').DataTypes);
|
|
110
|
+
},
|
|
111
|
+
})),
|
|
112
|
+
context: sequelize.getQueryInterface(),
|
|
113
|
+
storage: storage,
|
|
114
|
+
logger: umzugLogger,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
module.exports = { sequelize, umzug, storage };
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file src/database/KythiaModel.js
|
|
5
5
|
* @copyright © 2025 kenndeclouv
|
|
6
6
|
* @assistant chaa & graa
|
|
7
|
-
* @version 0.
|
|
7
|
+
* @version 0.11.0-beta
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
10
|
* Caching layer for Sequelize Models, now sharding-aware. When config.db.redis.shard === true,
|
|
@@ -32,7 +32,7 @@ const REDIS_ERROR_TOLERANCE_INTERVAL_MS = 10 * 1000;
|
|
|
32
32
|
|
|
33
33
|
function safeStringify(obj, logger) {
|
|
34
34
|
try {
|
|
35
|
-
return JSON.stringify(obj, (value) =>
|
|
35
|
+
return JSON.stringify(obj, (_key, value) =>
|
|
36
36
|
typeof value === 'bigint' ? value.toString() : value,
|
|
37
37
|
);
|
|
38
38
|
} catch (err) {
|
|
@@ -799,23 +799,22 @@ class KythiaModel extends Model {
|
|
|
799
799
|
}
|
|
800
800
|
}
|
|
801
801
|
|
|
802
|
-
/**
|
|
803
|
-
* 🔴 (Private) Retrieves and deserializes an entry specifically from Redis.
|
|
804
|
-
*/
|
|
805
802
|
static async _redisGetCachedEntry(cacheKey, includeOptions) {
|
|
806
803
|
try {
|
|
807
804
|
const result = await this.redis.get(cacheKey);
|
|
805
|
+
|
|
808
806
|
if (result === null || result === undefined)
|
|
809
807
|
return { hit: false, data: undefined };
|
|
810
808
|
|
|
811
809
|
this.cacheStats.redisHits++;
|
|
812
|
-
if (result === NEGATIVE_CACHE_PLACEHOLDER)
|
|
810
|
+
if (result === NEGATIVE_CACHE_PLACEHOLDER) {
|
|
813
811
|
return { hit: true, data: null };
|
|
812
|
+
}
|
|
814
813
|
|
|
815
814
|
const parsedData = safeParse(result, this.logger);
|
|
816
815
|
|
|
817
|
-
if (
|
|
818
|
-
return { hit:
|
|
816
|
+
if (parsedData === null) {
|
|
817
|
+
return { hit: false, data: undefined };
|
|
819
818
|
}
|
|
820
819
|
|
|
821
820
|
const includeAsArray = includeOptions
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file src/managers/AddonManager.js
|
|
5
5
|
* @copyright © 2025 kenndeclouv
|
|
6
6
|
* @assistant chaa & graa
|
|
7
|
-
* @version 0.
|
|
7
|
+
* @version 0.11.0-beta
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
10
|
* Handles all addon loading, command registration, and component management.
|
|
@@ -466,6 +466,7 @@ class AddonManager {
|
|
|
466
466
|
async _loadTopLevelCommandGroup(
|
|
467
467
|
commandsPath,
|
|
468
468
|
addon,
|
|
469
|
+
addonPermissionDefaults,
|
|
469
470
|
commandNamesSet,
|
|
470
471
|
commandDataForDeployment,
|
|
471
472
|
) {
|
|
@@ -476,6 +477,14 @@ class AddonManager {
|
|
|
476
477
|
commandDef = this._instantiateBaseCommand(commandDef);
|
|
477
478
|
}
|
|
478
479
|
|
|
480
|
+
const category = addon.name;
|
|
481
|
+
const categoryDefaults = addonPermissionDefaults[category] || {};
|
|
482
|
+
|
|
483
|
+
commandDef = {
|
|
484
|
+
...categoryDefaults,
|
|
485
|
+
...commandDef,
|
|
486
|
+
};
|
|
487
|
+
|
|
479
488
|
const mainBuilder = this._createBuilderFromData(
|
|
480
489
|
commandDef.data,
|
|
481
490
|
SlashCommandBuilder,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file src/managers/InteractionManager.js
|
|
5
5
|
* @copyright © 2025 kenndeclouv
|
|
6
6
|
* @assistant chaa & graa
|
|
7
|
-
* @version 0.
|
|
7
|
+
* @version 0.11.0-beta
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
10
|
* Handles all Discord interaction events including slash commands, buttons, modals,
|
|
@@ -763,7 +763,7 @@ class InteractionManager {
|
|
|
763
763
|
.setLabel(
|
|
764
764
|
await this.t(interaction, 'common.error.button.contact.owner'),
|
|
765
765
|
)
|
|
766
|
-
.setURL(`discord
|
|
766
|
+
.setURL(`https://discord.com/users/${ownerFirstId}`),
|
|
767
767
|
),
|
|
768
768
|
),
|
|
769
769
|
];
|
package/.husky/pre-commit
DELETED
package/biome.json
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.3.7/schema.json",
|
|
3
|
-
"vcs": {
|
|
4
|
-
"enabled": true,
|
|
5
|
-
"clientKind": "git",
|
|
6
|
-
"useIgnoreFile": true
|
|
7
|
-
},
|
|
8
|
-
"files": {
|
|
9
|
-
"ignoreUnknown": false
|
|
10
|
-
},
|
|
11
|
-
"formatter": {
|
|
12
|
-
"enabled": true,
|
|
13
|
-
"indentStyle": "tab"
|
|
14
|
-
},
|
|
15
|
-
"linter": {
|
|
16
|
-
"enabled": true,
|
|
17
|
-
"rules": {
|
|
18
|
-
"recommended": true,
|
|
19
|
-
"correctness": {
|
|
20
|
-
"noUnusedVariables": "error"
|
|
21
|
-
},
|
|
22
|
-
"complexity": {
|
|
23
|
-
"noThisInStatic": "off"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"javascript": {
|
|
28
|
-
"formatter": {
|
|
29
|
-
"quoteStyle": "single"
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"assist": {
|
|
33
|
-
"enabled": true,
|
|
34
|
-
"actions": {
|
|
35
|
-
"source": {
|
|
36
|
-
"organizeImports": "off"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|