millas 0.2.25 → 0.2.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "millas",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "A modern batteries-included backend framework for Node.js — built on Express, inspired by Laravel, Django, and FastAPI",
5
5
  "main": "src/index.js",
6
6
  "exports": {
package/src/cli.js CHANGED
@@ -1,8 +1,4 @@
1
1
  'use strict';
2
-
3
- // Load .env before anything else so all commands have access to env vars
4
- require('dotenv').config();
5
-
6
2
  // Set CLI mode globally for all commands
7
3
  process.env.MILLAS_CLI_MODE = 'true';
8
4
 
@@ -8,40 +8,6 @@ const {fork} = require('child_process');
8
8
  const chokidar = require('chokidar');
9
9
  const patchConsole = require("../logger/patchConsole");
10
10
  const Logger = require("../logger/internal");
11
- // ── ASCII banner ───────────────────────────────────────────────────────────────
12
-
13
- const BANNER_LINES = [
14
- ' ███╗ ███╗██╗██╗ ██╗ █████╗ ███████╗',
15
- ' ████╗ ████║██║██║ ██║ ██╔══██╗██╔════╝',
16
- ' ██╔████╔██║██║██║ ██║ ███████║███████╗',
17
- ' ██║╚██╔╝██║██║██║ ██║ ██╔══██║╚════██║',
18
- ' ██║ ╚═╝ ██║██║███████╗███████╗██║ ██║███████║',
19
- ' ╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝',
20
- ];
21
-
22
- function printBanner(host, port) {
23
- const env = process.env.NODE_ENV || 'development';
24
- const ver = 'v' + (require('../../package.json').version || '0.1.0');
25
- const url = `http://${host}:${port}`;
26
- const hr = chalk.dim(' ' + '─'.repeat(54));
27
- const envColour = env === 'production' ? chalk.red
28
- : env === 'staging' ? chalk.yellow
29
- : chalk.green;
30
-
31
- process.stdout.write('\n');
32
- for (const line of BANNER_LINES) {
33
- process.stdout.write(chalk.bold.cyan(line) + '\n');
34
- }
35
- process.stdout.write('\n' + hr + '\n');
36
- process.stdout.write(
37
- ' ' + chalk.dim(ver.padEnd(8)) +
38
- chalk.dim('│') + ' ' + envColour('⬤ ' + env) + ' ' +
39
- chalk.dim('│') + ' ' + chalk.bold.white(url) + '\n'
40
- );
41
- process.stdout.write(hr + '\n\n');
42
- }
43
-
44
- // ── Constants ─────────────────────────────────────────────────────────────────
45
11
 
46
12
  const WATCH_DIRS = ['app', 'routes', 'config', 'bootstrap', 'providers', 'middleware'];
47
13
  const WATCH_EXTS = new Set(['.js', '.mjs', '.cjs', '.json', '.njk', '.env']);
@@ -81,11 +47,14 @@ class HotReloader {
81
47
  extra["MILLAS_START_UP"] = true
82
48
  this._initialised = true
83
49
  }
50
+
84
51
  this._child = fork(this._bootstrap, [], {
85
52
  env: {
86
53
  ...extra,
87
54
  MILLAS_CHILD: '1',
88
55
  DEBUG: process.env.APP_DEBUG,
56
+ MILLAS_HOST: process.env.MILLAS_HOST,
57
+ MILLAS_PORT: process.env.MILLAS_PORT,
89
58
  },
90
59
  stdio: 'inherit',
91
60
  });
@@ -199,7 +168,7 @@ module.exports = function (program) {
199
168
  .command('serve')
200
169
  .description('Start the development server with hot reload')
201
170
  .option('-p, --port <port>', 'Port to listen on')
202
- .option('-h, --host <host>', 'Host to bind to', 'localhost')
171
+ .option('-h, --host <host>', 'Host to bind to')
203
172
  .option('--no-reload', 'Disable hot reload (run once, like production)')
204
173
  .action((options) => {
205
174
 
@@ -214,18 +183,19 @@ module.exports = function (program) {
214
183
  process.exit(1);
215
184
  }
216
185
 
217
- const publicPort = parseInt(options.port ||process.env.APP_PORT, 10) || 3000;
218
- const publicHost = options.host || 'localhost';
186
+ const publicPort = parseInt(options.port ||process.env.APP_PORT, 10);
187
+ const publicHost = options.host;
219
188
 
220
- const env = {
221
- NODE_ENV: process.env.APP_ENV || 'development',
222
- MILLERS_NODE_ENV: true,
223
- MILLAS_HOST: publicHost,
224
- MILLAS_PORT: String(publicPort),
225
- };
189
+ const env = Object.fromEntries(
190
+ Object.entries({
191
+ NODE_ENV: process.env.APP_ENV,
192
+ MILLERS_NODE_ENV: true,
193
+ MILLAS_HOST: publicHost,
194
+ MILLAS_PORT: String(publicPort),
195
+ }).filter(([, v]) => v !== undefined)
196
+ );
226
197
 
227
- Object.assign(process.env, env);
228
- printBanner(publicHost, publicPort);
198
+ Object.assign(process.env, env)
229
199
 
230
200
  if (options.reload !== false) {
231
201
  new HotReloader(appBootstrap, publicPort, publicHost).start();
@@ -174,7 +174,7 @@ class Application {
174
174
  || parseInt(process.env.MILLAS_INTERNAL_PORT, 10)
175
175
  || parseInt(process.env.APP_PORT, 10)
176
176
  || 3000;
177
- const _host = host || process.env.MILLAS_HOST || 'localhost';
177
+ const _host = host || 'localhost';
178
178
 
179
179
  await this._adapter.listen(_port, _host);
180
180
 
@@ -190,7 +190,9 @@ class Application {
190
190
  }
191
191
 
192
192
  _printStartupLog(host, port) {
193
+
193
194
  const chalk = _tryChalk();
195
+ printBanner(host,port,chalk)
194
196
  const routeCount = this._route.list().length;
195
197
  const url = `http://${host}:${port}`;
196
198
 
@@ -344,4 +346,35 @@ function _tryChalk() {
344
346
  p.white = id;
345
347
  return p;
346
348
  }
349
+ }
350
+
351
+ const BANNER_LINES = [
352
+ ' ███╗ ███╗██╗██╗ ██╗ █████╗ ███████╗',
353
+ ' ████╗ ████║██║██║ ██║ ██╔══██╗██╔════╝',
354
+ ' ██╔████╔██║██║██║ ██║ ███████║███████╗',
355
+ ' ██║╚██╔╝██║██║██║ ██║ ██╔══██║╚════██║',
356
+ ' ██║ ╚═╝ ██║██║███████╗███████╗██║ ██║███████║',
357
+ ' ╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝',
358
+ ];
359
+
360
+ function printBanner(host, port,chalk) {
361
+ const env = process.env.NODE_ENV || 'development';
362
+ const ver = 'v' + (require('../../package.json').version || '0.1.0');
363
+ const url = `http://${host}:${port}`;
364
+ const hr = chalk.dim(' ' + '─'.repeat(54));
365
+ const envColour = env === 'production' ? chalk.red
366
+ : env === 'staging' ? chalk.yellow
367
+ : chalk.green;
368
+
369
+ process.stdout.write('\n');
370
+ for (const line of BANNER_LINES) {
371
+ process.stdout.write(chalk.bold.cyan(line) + '\n');
372
+ }
373
+ process.stdout.write('\n' + hr + '\n');
374
+ process.stdout.write(
375
+ ' ' + chalk.dim(ver.padEnd(8)) +
376
+ chalk.dim('│') + ' ' + envColour('⬤ ' + env) + ' ' +
377
+ chalk.dim('│') + ' ' + chalk.bold.white(url) + '\n'
378
+ );
379
+ process.stdout.write(hr + '\n\n');
347
380
  }
@@ -51,7 +51,7 @@ class HttpServer {
51
51
  const port = this._resolvePort();
52
52
  const host = this._options.host
53
53
  || process.env.MILLAS_HOST
54
- || process.env.HOST
54
+ || process.env.APP_HOST
55
55
  || 'localhost';
56
56
 
57
57
  // Boot providers if not already done
@@ -569,6 +569,10 @@ class Model {
569
569
  return new QueryBuilder(this._db(), this).select(...cols);
570
570
  }
571
571
 
572
+ static selectRaw(sql, bindings = []) {
573
+ return new QueryBuilder(this._db(), this).selectRaw(sql, bindings);
574
+ }
575
+
572
576
  static distinct(...cols) {
573
577
  return new QueryBuilder(this._db(), this).distinct(...cols);
574
578
  }
@@ -78,6 +78,12 @@ class RouteEntry {
78
78
  * Add extra middleware to this specific route after registration.
79
79
  * Middleware aliases are appended to the existing list.
80
80
  *
81
+ * Supports Laravel-style parameters:
82
+ * .middleware('auth')
83
+ * .middleware(['auth', 'throttle'])
84
+ * .middleware('verifyAction:payment_method_delete')
85
+ * .middleware(['auth', 'verifyAction:payment_method_delete'])
86
+ *
81
87
  * @param {string|string[]} middleware
82
88
  * @returns {RouteEntry}
83
89
  */
@@ -86,6 +92,18 @@ class RouteEntry {
86
92
  this._entry.middleware = [...(this._entry.middleware || []), ...list];
87
93
  return this;
88
94
  }
95
+
96
+ /**
97
+ * Set route name (Laravel-style).
98
+ * Route.get('/users', UserController, 'index').name('users.index')
99
+ *
100
+ * @param {string} name
101
+ * @returns {RouteEntry}
102
+ */
103
+ name(name) {
104
+ this._entry.name = name;
105
+ return this;
106
+ }
89
107
  }
90
108
 
91
109
  module.exports = RouteEntry;