millas 0.2.27 → 0.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/bin/millas.js +12 -2
  2. package/package.json +2 -1
  3. package/src/cli.js +117 -20
  4. package/src/commands/call.js +1 -1
  5. package/src/commands/createsuperuser.js +137 -182
  6. package/src/commands/key.js +61 -83
  7. package/src/commands/lang.js +423 -515
  8. package/src/commands/make.js +88 -62
  9. package/src/commands/migrate.js +200 -279
  10. package/src/commands/new.js +55 -50
  11. package/src/commands/route.js +78 -80
  12. package/src/commands/schedule.js +52 -150
  13. package/src/commands/serve.js +158 -191
  14. package/src/console/AppCommand.js +106 -0
  15. package/src/console/BaseCommand.js +726 -0
  16. package/src/console/CommandContext.js +66 -0
  17. package/src/console/CommandRegistry.js +88 -0
  18. package/src/console/Style.js +123 -0
  19. package/src/console/index.js +12 -3
  20. package/src/container/AppInitializer.js +10 -0
  21. package/src/container/Application.js +2 -0
  22. package/src/facades/DB.js +195 -0
  23. package/src/index.js +2 -1
  24. package/src/scaffold/maker.js +102 -42
  25. package/src/schematics/Collection.js +28 -0
  26. package/src/schematics/SchematicEngine.js +122 -0
  27. package/src/schematics/Template.js +99 -0
  28. package/src/schematics/index.js +7 -0
  29. package/src/templates/command/default.template.js +14 -0
  30. package/src/templates/command/schema.json +19 -0
  31. package/src/templates/controller/default.template.js +10 -0
  32. package/src/templates/controller/resource.template.js +59 -0
  33. package/src/templates/controller/schema.json +30 -0
  34. package/src/templates/job/default.template.js +11 -0
  35. package/src/templates/job/schema.json +19 -0
  36. package/src/templates/middleware/default.template.js +11 -0
  37. package/src/templates/middleware/schema.json +19 -0
  38. package/src/templates/migration/default.template.js +14 -0
  39. package/src/templates/migration/schema.json +19 -0
  40. package/src/templates/model/default.template.js +14 -0
  41. package/src/templates/model/migration.template.js +17 -0
  42. package/src/templates/model/schema.json +30 -0
  43. package/src/templates/service/default.template.js +12 -0
  44. package/src/templates/service/schema.json +19 -0
  45. package/src/templates/shape/default.template.js +11 -0
  46. package/src/templates/shape/schema.json +19 -0
  47. package/src/validation/BaseValidator.js +3 -0
  48. package/src/validation/types.js +3 -3
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Command Context
5
+ * Encapsulates all dependencies and configuration for CLI commands
6
+ */
7
+ class CommandContext {
8
+ constructor(options = {}) {
9
+ this.program = options.program;
10
+ this.container = options.container || null;
11
+ this.config = options.config || {};
12
+ this.logger = options.logger || console;
13
+ this.cwd = options.cwd || process.cwd();
14
+ }
15
+
16
+ /**
17
+ * Set the DI container (lazy loaded after app bootstrap)
18
+ */
19
+ setContainer(container) {
20
+ this.container = container;
21
+ return this;
22
+ }
23
+
24
+ /**
25
+ * Set configuration
26
+ */
27
+ setConfig(config) {
28
+ this.config = config;
29
+ return this;
30
+ }
31
+
32
+ /**
33
+ * Get a service from the container
34
+ */
35
+ resolve(serviceName) {
36
+ if (!this.container) {
37
+ throw new Error('Container not initialized. Cannot resolve services.');
38
+ }
39
+ return this.container.resolve(serviceName);
40
+ }
41
+
42
+ /**
43
+ * Check if running inside a Millas project
44
+ */
45
+ isMillasProject() {
46
+ const fs = require('fs');
47
+ const path = require('path');
48
+
49
+ const pkgPath = path.join(this.cwd, 'package.json');
50
+ if (!fs.existsSync(pkgPath)) {
51
+ return false;
52
+ }
53
+
54
+ try {
55
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
56
+ // Check for millas marker in package.json
57
+ return pkg.millas === true ||
58
+ (pkg.dependencies && 'millas' in pkg.dependencies) ||
59
+ (pkg.devDependencies && 'millas' in pkg.devDependencies);
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+ }
65
+
66
+ module.exports = CommandContext;
@@ -0,0 +1,88 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const chalk = require('chalk');
6
+
7
+ /**
8
+ * Command Registry
9
+ * Auto-discovers and registers all commands
10
+ */
11
+ class CommandRegistry {
12
+ constructor(context) {
13
+ this.context = context;
14
+ this.commands = new Map();
15
+ }
16
+
17
+ /**
18
+ * Discover and load all commands from a directory
19
+ */
20
+ async discoverCommands(commandsDir) {
21
+ if (!require('fs').existsSync(commandsDir)) {
22
+ return;
23
+ }
24
+
25
+ const fs = require('fs').promises;
26
+ const files = (await fs.readdir(commandsDir))
27
+ .filter(file => file.endsWith('.js') && file !== 'index.js');
28
+
29
+ for (const file of files) {
30
+ const commandPath = path.join(commandsDir, file);
31
+ try {
32
+ await this.loadCommand(commandPath);
33
+ } catch (err) {
34
+ console.log(err)
35
+ console.error(chalk.yellow(` ⚠ Failed to load command: ${file}`));
36
+ if (process.env.APP_DEBUG) {
37
+ console.error(err);
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Load a single command file
45
+ */
46
+ async loadCommand(commandPath) {
47
+ const CommandClass = require(commandPath);
48
+ const BaseCommand = require('./BaseCommand');
49
+
50
+ // Support both class-based and function-based commands
51
+ if (typeof CommandClass === 'function') {
52
+ // Check if it's a class extending BaseCommand
53
+ if (CommandClass.prototype instanceof BaseCommand) {
54
+ const commandInstance = new CommandClass(this.context);
55
+ await commandInstance.register(); // Now async
56
+ this.commands.set(commandPath, commandInstance);
57
+ }
58
+ // Legacy function-based command (backward compatibility)
59
+ // else if (CommandClass.length === 1) { // expects (program) argument
60
+ // CommandClass(this.context.program);
61
+ // this.commands.set(commandPath, CommandClass);
62
+ // }
63
+ // Invalid command export
64
+ // else {
65
+ // throw new Error(`Invalid command export in ${commandPath}. Must extend BaseCommand or export function(program).`);
66
+ // }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Register commands from app/commands/ (user-defined commands)
72
+ */
73
+ async discoverUserCommands() {
74
+ const userCommandsDir = path.join(this.context.cwd, 'app', 'commands');
75
+ if (require('fs').existsSync(userCommandsDir)) {
76
+ await this.discoverCommands(userCommandsDir);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Get all registered commands
82
+ */
83
+ getCommands() {
84
+ return Array.from(this.commands.values());
85
+ }
86
+ }
87
+
88
+ module.exports = CommandRegistry;
@@ -0,0 +1,123 @@
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+
5
+ /**
6
+ * Bootstrap-inspired styling system for CLI output
7
+ * Provides consistent theming across all commands
8
+ */
9
+ class Style {
10
+ // Bootstrap-style variants
11
+ success(text) {
12
+ return chalk.green(text);
13
+ }
14
+
15
+ danger(text) {
16
+ return chalk.red(text);
17
+ }
18
+
19
+ warning(text) {
20
+ return chalk.yellow(text);
21
+ }
22
+
23
+ info(text) {
24
+ return chalk.cyan(text);
25
+ }
26
+
27
+ primary(text) {
28
+ return chalk.blue(text);
29
+ }
30
+
31
+ secondary(text) {
32
+ return chalk.gray(text);
33
+ }
34
+
35
+ muted(text) {
36
+ return chalk.dim(text);
37
+ }
38
+
39
+ light(text) {
40
+ return chalk.white(text);
41
+ }
42
+
43
+ dark(text) {
44
+ return chalk.black(text);
45
+ }
46
+
47
+ // Text styles
48
+ bold(text) {
49
+ return chalk.bold(text);
50
+ }
51
+
52
+ italic(text) {
53
+ return chalk.italic(text);
54
+ }
55
+
56
+ underline(text) {
57
+ return chalk.underline(text);
58
+ }
59
+
60
+ // HTTP method colors
61
+ method(verb) {
62
+ const colors = {
63
+ GET: chalk.green,
64
+ POST: chalk.blue,
65
+ PUT: chalk.yellow,
66
+ PATCH: chalk.magenta,
67
+ DELETE: chalk.red,
68
+ };
69
+ return colors[verb] || chalk.white;
70
+ }
71
+
72
+ // Status indicators
73
+ checkmark(text = '') {
74
+ return chalk.green(`✔ ${text}`);
75
+ }
76
+
77
+ cross(text = '') {
78
+ return chalk.red(`✖ ${text}`);
79
+ }
80
+
81
+ bullet(text = '') {
82
+ return chalk.cyan(`• ${text}`);
83
+ }
84
+
85
+ arrow(text = '') {
86
+ return chalk.cyan(`→ ${text}`);
87
+ }
88
+
89
+ // Borders and separators
90
+ line(length = 80, char = '─') {
91
+ return chalk.gray(char.repeat(length));
92
+ }
93
+
94
+ // Badges
95
+ badge(text, variant = 'primary') {
96
+ const styles = {
97
+ success: chalk.bgGreen.black,
98
+ danger: chalk.bgRed.white,
99
+ warning: chalk.bgYellow.black,
100
+ info: chalk.bgCyan.black,
101
+ primary: chalk.bgBlue.white,
102
+ secondary: chalk.bgGray.white,
103
+ };
104
+ const style = styles[variant] || styles.primary;
105
+ return style(` ${text} `);
106
+ }
107
+
108
+ // Code/path highlighting
109
+ code(text) {
110
+ return chalk.cyan(text);
111
+ }
112
+
113
+ path(text) {
114
+ return chalk.cyan(text);
115
+ }
116
+
117
+ // Key-value pairs
118
+ kv(key, value) {
119
+ return `${chalk.dim(key)}: ${chalk.white(value)}`;
120
+ }
121
+ }
122
+
123
+ module.exports = Style;
@@ -1,6 +1,15 @@
1
1
  'use strict';
2
2
 
3
- const Command = require('./Command');
4
- const CommandLoader = require('./CommandLoader');
3
+ const Command = require('./Command');
4
+ const CommandLoader = require('./CommandLoader');
5
+ const BaseCommand = require('./BaseCommand');
6
+ const CommandContext = require('./CommandContext');
7
+ const CommandRegistry = require('./CommandRegistry');
5
8
 
6
- module.exports = { Command, CommandLoader };
9
+ module.exports = {
10
+ Command,
11
+ CommandLoader,
12
+ BaseCommand,
13
+ CommandContext,
14
+ CommandRegistry
15
+ };
@@ -72,6 +72,16 @@ class AppInitializer {
72
72
  * @returns {Application} the booted kernel
73
73
  */
74
74
  async bootKernel() {
75
+ // Load .env if not running via CLI (CLI loads it in src/cli.js)
76
+ if (!process.env.MILLAS_CLI_MODE) {
77
+ const path = require('path');
78
+ const fs = require('fs');
79
+ const envPath = path.resolve(process.cwd(), '.env');
80
+ if (fs.existsSync(envPath)) {
81
+ require('dotenv').config({ path: envPath, override: false });
82
+ }
83
+ }
84
+
75
85
  const cfg = this._config;
76
86
  const basePath = cfg.basePath || process.cwd();
77
87
 
@@ -176,7 +176,9 @@ class Application {
176
176
  || 3000;
177
177
  const _host = host || 'localhost';
178
178
 
179
+
179
180
  await this._adapter.listen(_port, _host);
181
+ console.log(_host,_port)
180
182
 
181
183
  if (process.env.APP_ENV === "development" && process.env.MILLAS_START_UP && !process.env.MILLERS_NODE_ENV) {
182
184
  this._printStartupLog(_host, _port);
@@ -0,0 +1,195 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ /**
7
+ * DB Facade - Django-style database access
8
+ *
9
+ * Auto-configures on first use by loading config/database.js
10
+ *
11
+ * Usage:
12
+ * const DB = require('millas/src/facades/DB');
13
+ *
14
+ * // Query builder
15
+ * const users = await DB.table('users').where('active', true).get();
16
+ *
17
+ * // Raw queries
18
+ * const result = await DB.select('SELECT * FROM users WHERE id = ?', [1]);
19
+ *
20
+ * // Transactions
21
+ * await DB.transaction(async (trx) => {
22
+ * await trx('users').insert({ name: 'John' });
23
+ * await trx('posts').insert({ title: 'Hello' });
24
+ * });
25
+ *
26
+ * // Direct connection
27
+ * const db = DB.connection();
28
+ * await db('users').select('*');
29
+ */
30
+ class DBFacade {
31
+ constructor() {
32
+ this._manager = null;
33
+ this._configured = false;
34
+ }
35
+
36
+ /**
37
+ * Get the DatabaseManager instance (auto-configures if needed)
38
+ */
39
+ _getManager() {
40
+ if (!this._configured) {
41
+ this._configure();
42
+ }
43
+ return this._manager;
44
+ }
45
+
46
+ /**
47
+ * Auto-configure by loading config/database.js
48
+ */
49
+ _configure() {
50
+ if (this._configured) return;
51
+
52
+ // Try to find config/database.js from current working directory
53
+ const configPath = path.resolve(process.cwd(), 'config/database.js');
54
+
55
+ if (!fs.existsSync(configPath)) {
56
+ throw new Error(
57
+ 'config/database.js not found. Make sure you are in a Millas project directory.'
58
+ );
59
+ }
60
+
61
+ const config = require(configPath);
62
+ this._manager = require('../orm/drivers/DatabaseManager');
63
+ this._manager.configure(config);
64
+ this._configured = true;
65
+ }
66
+
67
+ /**
68
+ * Get a database connection
69
+ * @param {string} name - Connection name (optional, uses default if not provided)
70
+ */
71
+ connection(name) {
72
+ return this._getManager().connection(name);
73
+ }
74
+
75
+ /**
76
+ * Get the default connection
77
+ */
78
+ get db() {
79
+ return this._getManager().db;
80
+ }
81
+
82
+ /**
83
+ * Query builder for a table (Laravel: DB::table('users'))
84
+ * @param {string} tableName
85
+ */
86
+ table(tableName) {
87
+ return this._getManager().table(tableName);
88
+ }
89
+
90
+ /**
91
+ * Execute raw SQL SELECT
92
+ * @param {string} sql
93
+ * @param {Array} bindings
94
+ */
95
+ async select(sql, bindings = []) {
96
+ return this._getManager().select(sql, bindings);
97
+ }
98
+
99
+ /**
100
+ * Execute INSERT
101
+ * @param {string} sql
102
+ * @param {Array} bindings
103
+ */
104
+ async insert(sql, bindings = []) {
105
+ return this._getManager().insert(sql, bindings);
106
+ }
107
+
108
+ /**
109
+ * Execute UPDATE
110
+ * @param {string} sql
111
+ * @param {Array} bindings
112
+ */
113
+ async update(sql, bindings = []) {
114
+ return this._getManager().update(sql, bindings);
115
+ }
116
+
117
+ /**
118
+ * Execute DELETE
119
+ * @param {string} sql
120
+ * @param {Array} bindings
121
+ */
122
+ async delete(sql, bindings = []) {
123
+ return this._getManager().delete(sql, bindings);
124
+ }
125
+
126
+ /**
127
+ * Execute raw SQL
128
+ * @param {string} sql
129
+ * @param {Array} bindings
130
+ */
131
+ async raw(sql, bindings = []) {
132
+ return this._getManager().raw(sql, bindings);
133
+ }
134
+
135
+ /**
136
+ * Run queries in a transaction
137
+ * @param {Function} callback
138
+ */
139
+ async transaction(callback) {
140
+ return this._getManager().transaction(callback);
141
+ }
142
+
143
+ /**
144
+ * Begin a transaction manually
145
+ */
146
+ async beginTransaction() {
147
+ return this._getManager().beginTransaction();
148
+ }
149
+
150
+ /**
151
+ * Execute a statement
152
+ * @param {string} sql
153
+ * @param {Array} bindings
154
+ */
155
+ async statement(sql, bindings = []) {
156
+ return this._getManager().statement(sql, bindings);
157
+ }
158
+
159
+ /**
160
+ * Execute unprepared statement
161
+ * @param {string} sql
162
+ */
163
+ async unprepared(sql) {
164
+ return this._getManager().unprepared(sql);
165
+ }
166
+
167
+ /**
168
+ * Get schema builder
169
+ */
170
+ get schema() {
171
+ return this._getManager().schema;
172
+ }
173
+
174
+ /**
175
+ * Close all database connections
176
+ */
177
+ async closeAll() {
178
+ if (this._manager) {
179
+ await this._manager.closeAll();
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Close a specific connection
185
+ * @param {string} name
186
+ */
187
+ async close(name) {
188
+ if (this._manager) {
189
+ await this._manager.close(name);
190
+ }
191
+ }
192
+ }
193
+
194
+ // Export singleton instance
195
+ module.exports = new DBFacade();
package/src/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const Millas = require('./container/MillasApp');
4
+ const DB = require('./facades/DB');
4
5
 
5
6
  /**
6
7
  * @module millas
7
8
  */
8
- module.exports = { Millas };
9
+ module.exports = { Millas, DB };