millas 0.2.12-beta → 0.2.12-beta-1

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 (52) hide show
  1. package/package.json +3 -16
  2. package/src/auth/Auth.js +13 -8
  3. package/src/auth/AuthController.js +3 -1
  4. package/src/auth/AuthUser.js +98 -0
  5. package/src/cli.js +1 -1
  6. package/src/commands/serve.js +81 -110
  7. package/src/container/AppInitializer.js +158 -0
  8. package/src/container/Application.js +278 -253
  9. package/src/container/HttpServer.js +156 -0
  10. package/src/container/MillasApp.js +23 -280
  11. package/src/container/MillasConfig.js +163 -0
  12. package/src/core/auth.js +9 -0
  13. package/src/core/db.js +8 -0
  14. package/src/core/foundation.js +67 -0
  15. package/src/core/http.js +11 -0
  16. package/src/core/mail.js +6 -0
  17. package/src/core/queue.js +7 -0
  18. package/src/core/validation.js +29 -0
  19. package/src/facades/Admin.js +1 -1
  20. package/src/facades/Auth.js +22 -39
  21. package/src/facades/Cache.js +21 -10
  22. package/src/facades/Database.js +1 -1
  23. package/src/facades/Events.js +18 -17
  24. package/src/facades/Facade.js +197 -0
  25. package/src/facades/Http.js +42 -45
  26. package/src/facades/Log.js +25 -49
  27. package/src/facades/Mail.js +27 -32
  28. package/src/facades/Queue.js +22 -15
  29. package/src/facades/Storage.js +18 -10
  30. package/src/facades/Url.js +53 -0
  31. package/src/http/HttpClient.js +673 -0
  32. package/src/http/ResponseDispatcher.js +18 -111
  33. package/src/http/UrlGenerator.js +375 -0
  34. package/src/http/WelcomePage.js +273 -0
  35. package/src/http/adapters/ExpressAdapter.js +315 -0
  36. package/src/http/adapters/HttpAdapter.js +168 -0
  37. package/src/http/adapters/index.js +9 -0
  38. package/src/index.js +5 -144
  39. package/src/logger/formatters/PrettyFormatter.js +15 -5
  40. package/src/logger/internal.js +2 -2
  41. package/src/logger/patchConsole.js +91 -81
  42. package/src/middleware/MiddlewareRegistry.js +62 -82
  43. package/src/orm/migration/ModelInspector.js +339 -340
  44. package/src/providers/AuthServiceProvider.js +9 -5
  45. package/src/providers/CacheStorageServiceProvider.js +3 -1
  46. package/src/providers/EventServiceProvider.js +2 -1
  47. package/src/providers/LogServiceProvider.js +3 -2
  48. package/src/providers/MailServiceProvider.js +3 -2
  49. package/src/providers/QueueServiceProvider.js +3 -2
  50. package/src/router/Router.js +119 -152
  51. package/src/scaffold/templates.js +8 -7
  52. package/src/facades/Validation.js +0 -69
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
- const Container = require('./Container');
3
+ const Container = require('./Container');
4
4
  const ProviderRegistry = require('../providers/ProviderRegistry');
5
- const { Route, Router, MiddlewareRegistry } = require('../router');
6
- const CorsMiddleware = require('../middleware/CorsMiddleware');
5
+ const {Route, Router, MiddlewareRegistry} = require('../router');
6
+ const CorsMiddleware = require('../middleware/CorsMiddleware');
7
7
  const ThrottleMiddleware = require('../middleware/ThrottleMiddleware');
8
- const LogMiddleware = require('../middleware/LogMiddleware');
9
- const AuthMiddleware = require('../auth/AuthMiddleware');
8
+ const LogMiddleware = require('../middleware/LogMiddleware');
9
+ const AuthMiddleware = require('../auth/AuthMiddleware');
10
+ const Facade = require("../facades/Facade");
11
+ const UrlGenerator = require("../http/UrlGenerator");
10
12
 
11
13
  /**
12
14
  * Application
@@ -14,275 +16,298 @@ const AuthMiddleware = require('../auth/AuthMiddleware');
14
16
  * The central Millas kernel. Owns:
15
17
  * - The DI container
16
18
  * - The provider registry
17
- * - The Express app
19
+ * - The HttpAdapter (NOT Express directly — any adapter works)
18
20
  * - The route instance
19
21
  * - The middleware registry
20
22
  *
23
+ * The kernel is fully decoupled from Express. It talks to the HTTP layer
24
+ * exclusively through the HttpAdapter interface.
25
+ *
21
26
  * Usage in bootstrap/app.js:
22
27
  *
23
- * const { Application } = require('millas/src/container');
24
- * const app = new Application(expressApp);
25
- * app.providers([AppServiceProvider, DatabaseServiceProvider]);
28
+ * const { Application } = require('millas/src/container');
29
+ * const { ExpressAdapter } = require('millas/src/http/adapters');
30
+ * const express = require('express');
31
+ *
32
+ * const adapter = new ExpressAdapter(express());
33
+ * const app = new Application(adapter);
34
+ * app.providers([AppServiceProvider]);
26
35
  * await app.boot();
27
- * app.routes(route => {
28
- * require('../routes/web')(route);
29
- * require('../routes/api')(route);
30
- * });
36
+ * app.routes(Route => require('../routes/api')(Route));
31
37
  * app.mount();
32
38
  * app.listen();
33
39
  */
34
40
  class Application {
35
- constructor(expressApp) {
36
- this._express = expressApp;
37
- this._container = new Container();
38
- this._providers = new ProviderRegistry(this._container, expressApp);
39
- this._mwRegistry = new MiddlewareRegistry();
40
- this._route = new Route();
41
- this._booted = false;
42
-
43
- // Register core framework bindings
44
- this._registerCoreBindings();
45
- }
46
-
47
- // ─── Configuration ───────────────────────────────────────────────────────────
48
-
49
- /**
50
- * Add service providers.
51
- * @param {Array} providers
52
- */
53
- providers(providers = []) {
54
- this._providers.addMany(providers);
55
- return this;
56
- }
57
-
58
- /**
59
- * Register a middleware alias.
60
- *
61
- * app.middleware('auth', AuthMiddleware)
62
- * app.middleware('throttle', new ThrottleMiddleware({ max: 60 }))
63
- */
64
- middleware(alias, handler) {
65
- this._mwRegistry.register(alias, handler);
66
- return this;
67
- }
68
-
69
- /**
70
- * Define all routes via a callback.
71
- *
72
- * app.routes(route => {
73
- * require('../routes/web')(route);
74
- * require('../routes/api')(route);
75
- * });
76
- */
77
- routes(callback) {
78
- callback(this._route);
79
- return this;
80
- }
81
-
82
- // ─── Lifecycle ───────────────────────────────────────────────────────────────
83
-
84
- /**
85
- * Run the full provider lifecycle:
86
- * Phase 0 — beforeBoot (synchronous, global setup)
87
- * Phase 1 — register (synchronous, container bindings)
88
- * Phase 2 — boot (async, all bindings available)
89
- *
90
- * Emits: platform.booting platform.booted
91
- */
92
- async boot() {
93
- if (this._booted) return this;
94
-
95
- this._emitSync('platform.booting', { providers: this._providers.list() });
96
-
97
- await this._providers.boot();
98
- this._booted = true;
99
-
100
- this._emitSync('platform.booted', { providers: this._providers.list() });
101
-
102
- return this;
103
- }
104
-
105
- /**
106
- * Bind all routes onto the Express app.
107
- * Does NOT add 404/error fallbacks — call mountFallbacks() after
108
- * registering any extra middleware (e.g. Admin panel).
109
- *
110
- * Typical usage:
111
- * app.mountRoutes();
112
- * Admin.mount(expressApp); // admin routes added here
113
- * app.mountFallbacks(); // 404 + error handler last
114
- *
115
- * Or use app.mount() which does all three in one call.
116
- */
117
- mountRoutes() {
118
- this._router = new Router(this._express, this._route.getRegistry(), this._mwRegistry, this._container);
119
- this._router.mountRoutes();
120
- return this;
121
- }
122
-
123
- /**
124
- * Add 404 + global error handlers. Must be called LAST.
125
- */
126
- mountFallbacks() {
127
- if (!this._router) {
128
- this._router = new Router(this._express, this._route.getRegistry(), this._mwRegistry, this._container);
41
+ /**
42
+ * @param {import('../http/adapters/HttpAdapter')} adapter
43
+ */
44
+ constructor(adapter) {
45
+ this._adapter = adapter;
46
+ this._container = new Container();
47
+ this._providers = new ProviderRegistry(this._container, adapter.nativeApp || adapter);
48
+ this._mwRegistry = new MiddlewareRegistry();
49
+ this._route = new Route();
50
+ this._booted = false;
51
+
52
+ this._registerCoreBindings();
53
+ }
54
+
55
+ // ── Configuration ──────────────────────────────────────────────────────────
56
+
57
+ providers(providers = []) {
58
+ this._providers.addMany(providers);
59
+ return this;
60
+ }
61
+
62
+ /**
63
+ * Register a middleware alias.
64
+ *
65
+ * app.middleware('auth', AuthMiddleware)
66
+ * app.middleware('throttle', new ThrottleMiddleware({ max: 60 }))
67
+ */
68
+ middleware(alias, handler) {
69
+ this._mwRegistry.register(alias, handler);
70
+ return this;
71
+ }
72
+
73
+ /**
74
+ * Define all routes via a callback.
75
+ *
76
+ * app.routes(Route => {
77
+ * require('../routes/api')(Route);
78
+ * });
79
+ */
80
+ routes(callback) {
81
+ callback(this._route);
82
+ return this;
83
+ }
84
+
85
+ // ── Lifecycle ──────────────────────────────────────────────────────────────
86
+
87
+ /**
88
+ * Run the full provider lifecycle:
89
+ * Phase 0 — beforeBoot (sync, global setup)
90
+ * Phase 1 — register (sync, container bindings)
91
+ * Phase 2 boot (async, all bindings available)
92
+ */
93
+ async boot() {
94
+ if (this._booted) return this;
95
+
96
+ this._emitSync('platform.booting', {providers: this._providers.list()});
97
+
98
+ await this._providers.boot();
99
+ this._booted = true;
100
+
101
+ this._emitSync('platform.booted', {providers: this._providers.list()});
102
+
103
+ return this;
104
+ }
105
+
106
+ /**
107
+ * Mount all registered routes onto the adapter.
108
+ * Does NOT add fallbacks — call mountFallbacks() separately after
109
+ * any extra middleware (e.g. Admin panel) is mounted.
110
+ */
111
+ mountRoutes() {
112
+ this._router = new Router(
113
+ this._adapter,
114
+ this._route.getRegistry(),
115
+ this._mwRegistry,
116
+ this._container
117
+ );
118
+ this._router.mountRoutes();
119
+ return this;
129
120
  }
130
- this._router.mountFallbacks();
131
- return this;
132
- }
133
-
134
- /**
135
- * Bind routes + add 404/error handlers in one call.
136
- * Use this when NOT using the Admin panel, or when Admin
137
- * is mounted before this call.
138
- */
139
- mount() {
140
- const router = new Router(this._express, this._route.getRegistry(), this._mwRegistry, this._container);
141
- router.mount();
142
- return this;
143
- }
144
-
145
- /**
146
- * Start the HTTP server.
147
- * Emits: platform.listening
148
- */
149
- listen(port, host, callback) {
150
- const _port = port || parseInt(process.env.APP_PORT, 10) || 3000;
151
- const _host = host || process.env.MILLAS_HOST || 'localhost';
152
-
153
- const server = this._express.listen(_port, _host, () => {
154
- if (!process.env.MILLAS_ROUTE_LIST) {
155
- const chalk = _tryChalk();
121
+
122
+ /**
123
+ * Mount 404 + error handler. Must be called LAST.
124
+ */
125
+ mountFallbacks() {
126
+ if (!this._router) {
127
+ this._router = new Router(
128
+ this._adapter,
129
+ this._route.getRegistry(),
130
+ this._mwRegistry,
131
+ this._container
132
+ );
133
+ }
134
+ this._router.mountFallbacks();
135
+ return this;
136
+ }
137
+
138
+ /**
139
+ * Mount routes + fallbacks in one call.
140
+ */
141
+ mount() {
142
+ const router = new Router(
143
+ this._adapter,
144
+ this._route.getRegistry(),
145
+ this._mwRegistry,
146
+ this._container
147
+ );
148
+ router.mount();
149
+ return this;
150
+ }
151
+
152
+ /**
153
+ * Start the HTTP server via the adapter.
154
+ * Emits: platform.listening
155
+ */
156
+ async listen(port, host, callback) {
157
+ const _port = port
158
+ || parseInt(process.env.MILLAS_INTERNAL_PORT, 10)
159
+ || parseInt(process.env.APP_PORT, 10)
160
+ || 3000;
161
+ const _host = host || process.env.MILLAS_HOST || 'localhost';
162
+
163
+ await this._adapter.listen(_port, _host);
164
+
165
+ if (process.env.APP_ENV === "development" && process.env.MILLAS_START_UP && !process.env.MILLERS_NODE_ENV) {
166
+ this._printStartupLog(_host, _port);
167
+ }
168
+
169
+ this._emitSync('platform.listening', {port: _port, host: _host});
170
+
171
+ if (typeof callback === 'function') callback(_port, _host);
172
+
173
+ return this;
174
+ }
175
+
176
+ _printStartupLog(host, port) {
177
+ const chalk = _tryChalk();
156
178
  const routeCount = this._route.list().length;
179
+ const url = `http://${host}:${port}`;
157
180
 
158
- // Use process.stdout.write directly so these lines always appear
159
- // regardless of log level — even if console is patched and debug is off.
160
- process.stdout.write(
161
- ' ' + chalk.dim('›') + ' ' +
162
- chalk.white(routeCount + ' route' + (routeCount !== 1 ? 's' : '') + ' registered') +
163
- '\n'
181
+ // console.error(chalk.dim('›') + ' ' +
182
+ // chalk.dim('Listening on ') + chalk.bold.white(url)
183
+ // );
184
+ console.log(chalk.dim('›') + ' ' +
185
+ chalk.white(routeCount + ' route' + (routeCount !== 1 ? 's' : '') + ' registered')
164
186
  );
165
- process.stdout.write(
166
- ' ' + chalk.dim('') + ' ' +
167
- chalk.dim('Press ') + chalk.bold('Ctrl+C') + chalk.dim(' to stop') +
168
- '\n\n'
187
+ console.error(chalk.dim('›') + ' ' +
188
+ chalk.dim('Press ') + chalk.bold('Ctrl+C') + chalk.dim(' to stop')
169
189
  );
170
- }
171
-
172
- this._emitSync('platform.listening', { port: _port, host: _host });
173
-
174
- if (typeof callback === 'function') callback(_port, _host);
175
- });
176
-
177
- this._server = server;
178
- return this;
179
- }
180
-
181
- /**
182
- * Graceful shutdown — emits platform.shutting_down, then closes the server.
183
- * Call this instead of process.exit() for clean teardown.
184
- *
185
- * process.on('SIGTERM', () => app.shutdown());
186
- */
187
- async shutdown(code = 0) {
188
- this._emitSync('platform.shutting_down', {});
189
- if (this._server) {
190
- await new Promise(resolve => this._server.close(resolve));
191
190
  }
192
- process.exit(code);
193
- }
194
-
195
- // ─── Platform event bus ───────────────────────────────────────────────────
196
-
197
- /**
198
- * Listen to a platform lifecycle event.
199
- *
200
- * app.on('platform.booted', ({ providers }) => { ... });
201
- * app.on('platform.listening', ({ port, host }) => { ... });
202
- * app.on('platform.shutting_down', () => process.cleanup());
203
- */
204
- on(event, fn) {
205
- if (!this._platformListeners) this._platformListeners = new Map();
206
- if (!this._platformListeners.has(event)) this._platformListeners.set(event, []);
207
- this._platformListeners.get(event).push(fn);
208
- return this;
209
- }
210
-
211
- /** Synchronous platform event dispatch (fire-and-forget, no await). */
212
- _emitSync(event, data) {
213
- if (!this._platformListeners) return;
214
- const listeners = this._platformListeners.get(event) || [];
215
- for (const fn of listeners) {
216
- try { fn(data); } catch {}
191
+
192
+ /**
193
+ * Graceful shutdown.
194
+ */
195
+ async shutdown(code = 0) {
196
+ this._emitSync('platform.shutting_down', {});
197
+ await this._adapter.close();
198
+ process.exit(code);
199
+ }
200
+
201
+ // ── Platform event bus ─────────────────────────────────────────────────────
202
+
203
+ on(event, fn) {
204
+ if (!this._platformListeners) this._platformListeners = new Map();
205
+ if (!this._platformListeners.has(event)) this._platformListeners.set(event, []);
206
+ this._platformListeners.get(event).push(fn);
207
+ return this;
208
+ }
209
+
210
+ _emitSync(event, data) {
211
+ if (!this._platformListeners) return;
212
+ const listeners = this._platformListeners.get(event) || [];
213
+ for (const fn of listeners) {
214
+ try {
215
+ fn(data);
216
+ } catch {
217
+ }
218
+ }
219
+ }
220
+
221
+ // ── Container proxy ────────────────────────────────────────────────────────
222
+
223
+ bind(abstract, concrete) {
224
+ this._container.bind(abstract, concrete);
225
+ return this;
226
+ }
227
+
228
+ singleton(abstract, c) {
229
+ this._container.singleton(abstract, c);
230
+ return this;
231
+ }
232
+
233
+ instance(abstract, value) {
234
+ this._container.instance(abstract, value);
235
+ return this;
236
+ }
237
+
238
+ make(abstract, overrides) {
239
+ return this._container.make(abstract, overrides);
240
+ }
241
+
242
+ // ── Accessors ──────────────────────────────────────────────────────────────
243
+
244
+ get container() {
245
+ return this._container;
246
+ }
247
+
248
+ get route() {
249
+ return this._route;
250
+ }
251
+
252
+ get adapter() {
253
+ return this._adapter;
254
+ }
255
+
256
+ get mwRegistry() {
257
+ return this._mwRegistry;
258
+ }
259
+
260
+ /**
261
+ * express — backward-compatible escape hatch.
262
+ * Returns the native app from the adapter if available.
263
+ * Prefer adapter.nativeApp for new code.
264
+ */
265
+ get express() {
266
+ return this._adapter.nativeApp || this._adapter;
267
+ }
268
+
269
+ // ── Internal ───────────────────────────────────────────────────────────────
270
+
271
+ _registerCoreBindings() {
272
+ this._container.instance('app', this);
273
+ this._container.instance('container', this._container);
274
+
275
+ const Facade = require('../facades/Facade');
276
+ Facade.setContainer(this._container);
277
+ // Http client — always available, no provider needed
278
+ const {HttpClient} = require('../http/HttpClient');
279
+ this._container.instance('HttpClient', HttpClient);
280
+ this._container.alias('http', "HttpClient");
281
+
282
+ const UrlGenerator = require('../http/UrlGenerator');
283
+ const urlGenerator = new UrlGenerator({
284
+ baseUrl: process.env.APP_URL || '',
285
+ appKey: process.env.APP_KEY || '',
286
+ routeRegistry: this._route?.getRegistry?.() || null,
287
+ });
288
+ this._container.instance('url', urlGenerator);
289
+
290
+
291
+ this._mwRegistry.register('cors', new CorsMiddleware());
292
+ this._mwRegistry.register('throttle', new ThrottleMiddleware({max: 60, window: 60}));
293
+ this._mwRegistry.register('log', new LogMiddleware());
294
+ this._mwRegistry.register('auth', AuthMiddleware);
217
295
  }
218
- }
219
-
220
- // ─── Container Proxy ─────────────────────────────────────────────────────────
221
-
222
- /**
223
- * Bind a transient into the container.
224
- */
225
- bind(abstract, concrete) {
226
- this._container.bind(abstract, concrete);
227
- return this;
228
- }
229
-
230
- /**
231
- * Bind a singleton into the container.
232
- */
233
- singleton(abstract, concrete) {
234
- this._container.singleton(abstract, concrete);
235
- return this;
236
- }
237
-
238
- /**
239
- * Register a pre-built instance.
240
- */
241
- instance(abstract, value) {
242
- this._container.instance(abstract, value);
243
- return this;
244
- }
245
-
246
- /**
247
- * Resolve something from the container.
248
- */
249
- make(abstract, overrides) {
250
- return this._container.make(abstract, overrides);
251
- }
252
-
253
- // ─── Accessors ───────────────────────────────────────────────────────────────
254
-
255
- get container() { return this._container; }
256
- get route() { return this._route; }
257
- get express() { return this._express; }
258
- get mwRegistry() { return this._mwRegistry; }
259
-
260
- // ─── Internal ────────────────────────────────────────────────────────────────
261
-
262
- _registerCoreBindings() {
263
- // Register the application itself
264
- this._container.instance('app', this);
265
- this._container.instance('container', this._container);
266
-
267
- // Register built-in middleware
268
- this._mwRegistry.register('cors', new CorsMiddleware());
269
- this._mwRegistry.register('throttle', new ThrottleMiddleware({ max: 60, window: 60 }));
270
- this._mwRegistry.register('log', new LogMiddleware());
271
- this._mwRegistry.register('auth', AuthMiddleware);
272
- }
273
296
  }
274
297
 
275
298
  module.exports = Application;
276
299
 
277
300
  // ── Helpers ───────────────────────────────────────────────────────────────────
278
301
 
279
- // Safely require chalk — it ships with Millas but we guard in case of oddities
280
302
  function _tryChalk() {
281
- try { return require('chalk'); } catch {
282
- // Fallback: no-op chalk that returns strings unchanged
283
- const id = s => s;
284
- const p = new Proxy({}, { get: () => p, apply: (_, __, [s]) => String(s || '') });
285
- p.dim = id; p.bold = p; p.white = id;
286
- return p;
287
- }
288
- }
303
+ try {
304
+ return require('chalk');
305
+ } catch {
306
+ const id = s => s;
307
+ const p = new Proxy({}, {get: () => p, apply: (_, __, [s]) => String(s || '')});
308
+ p.dim = id;
309
+ p.bold = p;
310
+ p.white = id;
311
+ return p;
312
+ }
313
+ }