millas 0.2.11 → 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 (74) hide show
  1. package/package.json +6 -5
  2. package/src/auth/Auth.js +13 -8
  3. package/src/auth/AuthController.js +45 -134
  4. package/src/auth/AuthMiddleware.js +12 -23
  5. package/src/auth/AuthUser.js +98 -0
  6. package/src/auth/RoleMiddleware.js +7 -17
  7. package/src/cli.js +1 -1
  8. package/src/commands/migrate.js +46 -31
  9. package/src/commands/serve.js +238 -38
  10. package/src/container/AppInitializer.js +158 -0
  11. package/src/container/Application.js +288 -183
  12. package/src/container/HttpServer.js +156 -0
  13. package/src/container/MillasApp.js +23 -280
  14. package/src/container/MillasConfig.js +163 -0
  15. package/src/controller/Controller.js +79 -300
  16. package/src/core/auth.js +9 -0
  17. package/src/core/db.js +8 -0
  18. package/src/core/foundation.js +67 -0
  19. package/src/core/http.js +11 -0
  20. package/src/core/mail.js +6 -0
  21. package/src/core/queue.js +7 -0
  22. package/src/core/validation.js +29 -0
  23. package/src/errors/ErrorRenderer.js +640 -0
  24. package/src/facades/Admin.js +49 -0
  25. package/src/facades/Auth.js +29 -0
  26. package/src/facades/Cache.js +28 -0
  27. package/src/facades/Database.js +43 -0
  28. package/src/facades/Events.js +25 -0
  29. package/src/facades/Facade.js +197 -0
  30. package/src/facades/Http.js +51 -0
  31. package/src/facades/Log.js +32 -0
  32. package/src/facades/Mail.js +35 -0
  33. package/src/facades/Queue.js +30 -0
  34. package/src/facades/Storage.js +25 -0
  35. package/src/facades/Url.js +53 -0
  36. package/src/http/HttpClient.js +673 -0
  37. package/src/http/MillasRequest.js +253 -0
  38. package/src/http/MillasResponse.js +196 -0
  39. package/src/http/RequestContext.js +176 -0
  40. package/src/http/ResponseDispatcher.js +51 -0
  41. package/src/http/UrlGenerator.js +375 -0
  42. package/src/http/WelcomePage.js +273 -0
  43. package/src/http/adapters/ExpressAdapter.js +315 -0
  44. package/src/http/adapters/HttpAdapter.js +168 -0
  45. package/src/http/adapters/index.js +9 -0
  46. package/src/http/helpers.js +164 -0
  47. package/src/http/index.js +13 -0
  48. package/src/index.js +5 -91
  49. package/src/logger/formatters/PrettyFormatter.js +15 -5
  50. package/src/logger/internal.js +76 -0
  51. package/src/logger/patchConsole.js +145 -0
  52. package/src/middleware/CorsMiddleware.js +22 -30
  53. package/src/middleware/LogMiddleware.js +27 -59
  54. package/src/middleware/Middleware.js +24 -15
  55. package/src/middleware/MiddlewarePipeline.js +30 -67
  56. package/src/middleware/MiddlewareRegistry.js +106 -0
  57. package/src/middleware/ThrottleMiddleware.js +22 -26
  58. package/src/orm/fields/index.js +124 -56
  59. package/src/orm/migration/ModelInspector.js +339 -336
  60. package/src/orm/model/Model.js +96 -6
  61. package/src/orm/query/QueryBuilder.js +141 -3
  62. package/src/providers/AuthServiceProvider.js +9 -5
  63. package/src/providers/CacheStorageServiceProvider.js +3 -1
  64. package/src/providers/EventServiceProvider.js +2 -1
  65. package/src/providers/LogServiceProvider.js +88 -17
  66. package/src/providers/MailServiceProvider.js +3 -2
  67. package/src/providers/ProviderRegistry.js +14 -1
  68. package/src/providers/QueueServiceProvider.js +3 -2
  69. package/src/providers/ServiceProvider.js +40 -8
  70. package/src/router/Router.js +121 -222
  71. package/src/scaffold/maker.js +24 -59
  72. package/src/scaffold/templates.js +21 -19
  73. package/src/validation/BaseValidator.js +193 -0
  74. package/src/validation/Validator.js +680 -0
@@ -1,278 +1,177 @@
1
1
  'use strict';
2
2
 
3
- const MiddlewareRegistry = require('./MiddlewareRegistry');
4
-
5
- // ── Welcome page — shown at / when no route is defined ────────────────────────
6
- const WELCOME_PAGE = `<!DOCTYPE html>
7
- <html lang="en">
8
- <head>
9
- <meta charset="UTF-8">
10
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
-
12
- <title>Welcome to Millas</title>
13
-
14
- <link rel="stylesheet"
15
- href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
16
- crossorigin="anonymous" referrerpolicy="no-referrer"/>
17
-
18
- <style>
19
-
20
- body{
21
- margin:0;
22
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
23
- background:#fff;
24
- color:#333;
25
- display:flex;
26
- align-items:center;
27
- justify-content:center;
28
- height:100vh;
29
- text-align:center;
30
- }
31
-
32
- .container{
33
- max-width:720px;
34
- padding:40px;
35
- }
36
-
37
- .icon{
38
- margin-bottom:20px;
39
- }
40
-
41
- h1{
42
- font-size:32px;
43
- margin-bottom:12px;
44
- }
45
-
46
- .subtitle{
47
- color:#555;
48
- font-size:16px;
49
- line-height:1.7;
50
- margin-bottom:25px;
51
- }
52
-
53
- code{
54
- background:#fff4ed;
55
- color:#ea580c;
56
- padding:4px 8px;
57
- border-radius:6px;
58
- font-size:13px;
59
- }
60
-
61
- .steps{
62
- margin-top:30px;
63
- text-align:left;
64
- display:inline-block;
65
- }
66
-
67
- .steps h3{
68
- margin-bottom:10px;
69
- font-size:16px;
70
- }
71
-
72
- .steps ul{
73
- margin:0;
74
- padding-left:20px;
75
- color:#555;
76
- }
77
-
78
- .links{
79
- margin-top:35px;
80
- display:flex;
81
- justify-content:center;
82
- gap:18px;
83
- flex-wrap:wrap;
84
- }
85
-
86
- .links a{
87
- color:#f97316;
88
- text-decoration:none;
89
- font-size:14px;
90
- display:flex;
91
- align-items:center;
92
- gap:6px;
93
- }
94
-
95
- .links a:hover{
96
- text-decoration:underline;
97
- }
98
-
99
- .footer{
100
- margin-top:35px;
101
- font-size:13px;
102
- color:#888;
103
- }
104
-
105
- </style>
106
- </head>
107
-
108
- <body>
109
-
110
- <div class="container">
111
-
112
- <div class="icon">
113
- <img src="https://www.saaspegasus.com/static/images/web/landing-page/rocket-laptop.39d6be7451e6.svg" alt="Millas" style="width:140px;height:auto;">
114
- </div>
115
-
116
- <h1>It worked!</h1>
117
-
118
- <p class="subtitle">
119
- Millas is installed and running correctly.
120
- </p>
121
-
122
- <div class="steps">
123
- <h3>Next steps</h3>
124
- <ul>
125
- <li>Create your first route in <code>routes/web.js</code></li>
126
- </ul>
127
- </div>
128
-
129
- <div class="links">
130
-
131
- <a href="/admin">
132
- <i class="fa-solid fa-gauge"></i>
133
- Admin panel
134
- </a>
135
-
136
- <a href="/api/health">
137
- <i class="fa-solid fa-heart-pulse"></i>
138
- Health check
139
- </a>
140
-
141
- <a href="https://github.com/millas-framework/millas" target="_blank">
142
- <i class="fa-brands fa-github"></i>
143
- GitHub
144
- </a>
145
-
146
- </div>
147
-
148
- <div class="footer">
149
- Millas v0.1.2
150
- </div>
151
-
152
- </div>
153
-
154
- </body>
155
- </html>`;
156
-
157
3
  /**
158
4
  * Router
159
5
  *
160
- * Takes a populated RouteRegistry and binds every route
161
- * onto a live Express app instance.
6
+ * Bridges the Millas RouteRegistry to any HttpAdapter.
7
+ * Zero knowledge of Express (or any HTTP engine) — it only calls
8
+ * the adapter interface defined in HttpAdapter.js.
162
9
  *
163
- * Also wraps async controller methods so unhandled promise
164
- * rejections are forwarded to Express error handlers.
10
+ * Responsibilities:
11
+ * - Resolve route handlers from the RouteRegistry
12
+ * - Resolve middleware aliases from the MiddlewareRegistry
13
+ * - Ask the adapter to mount each route
14
+ * - Ask the adapter to mount the welcome page, 404, and error handler
165
15
  */
166
16
  class Router {
167
- constructor(expressApp, registry, middlewareRegistry) {
168
- this._app = expressApp;
169
- this._registry = registry;
170
- this._mw = middlewareRegistry || new MiddlewareRegistry();
17
+ /**
18
+ * @param {import('../http/adapters/HttpAdapter')} adapter
19
+ * @param {import('./RouteRegistry')} registry
20
+ * @param {import('./MiddlewareRegistry')} middlewareRegistry
21
+ * @param {import('../container/Container')|null} container
22
+ */
23
+ constructor(adapter, registry, middlewareRegistry, container = null) {
24
+ this._adapter = adapter;
25
+ this._registry = registry;
26
+ this._mw = middlewareRegistry;
27
+ this._container = container;
171
28
  }
172
29
 
30
+ // ── Public API ─────────────────────────────────────────────────────────────
31
+
32
+ /**
33
+ * Mount all registered routes onto the adapter.
34
+ * Does NOT add fallbacks — call mountFallbacks() after all routes
35
+ * and any extra middleware (e.g. Admin panel) have been added.
36
+ */
173
37
  mountRoutes() {
174
- const routes = this._registry.all();
175
- for (const route of routes) {
38
+ for (const route of this._registry.all()) {
176
39
  this._bindRoute(route);
177
40
  }
178
41
  return this;
179
42
  }
180
43
 
44
+ /**
45
+ * Mount the 404 + error handlers.
46
+ * Must be called LAST — after all routes and the admin panel.
47
+ */
181
48
  mountFallbacks() {
182
- const hasRootRoute = this._registry.all().some(
183
- r => r.verb === 'GET' && r.path === '/'
184
- );
185
-
186
- if (!hasRootRoute) {
187
- this._app.get('/', (req, res) => {
188
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
189
- res.send(WELCOME_PAGE);
190
- });
191
- }
192
-
193
- this._app.use((req, res) => {
194
- res.status(404).json({
195
- error: 'Not Found',
196
- message: `Cannot ${req.method} ${req.path}`,
197
- status: 404,
198
- });
199
- });
200
-
201
- this._app.use((err, req, res, _next) => {
202
- const status = err.status || err.statusCode || 500;
203
- const message = err.message || 'Internal Server Error';
204
-
205
- if (status >= 500 && process.env.NODE_ENV !== 'production') {
206
- console.error(err.stack);
207
- }
208
-
209
- res.status(status).json({
210
- error: status >= 500 ? 'Internal Server Error' : message,
211
- message,
212
- status,
213
- ...(err.errors && { errors: err.errors }),
214
- ...(status >= 500 && process.env.NODE_ENV !== 'production' && { stack: err.stack }),
215
- });
216
- });
49
+ this._maybeInjectWelcome();
50
+ this._adapter.mountNotFound();
51
+ this._adapter.mountErrorHandler();
217
52
  return this;
218
53
  }
219
54
 
55
+ /**
56
+ * Mount routes + fallbacks in one call.
57
+ */
220
58
  mount() {
221
- const routes = this._registry.all();
222
- for (const route of routes) {
223
- this._bindRoute(route);
224
- }
225
- this.mountFallbacks();
59
+ this.mountRoutes();
60
+ this._maybeInjectWelcome();
61
+ this._adapter.mountNotFound();
62
+ this._adapter.mountErrorHandler();
226
63
  return this;
227
64
  }
228
65
 
66
+ // ── Private ────────────────────────────────────────────────────────────────
67
+
229
68
  _bindRoute(route) {
230
- const verb = route.verb.toLowerCase();
231
- const path = route.path;
232
- const mwHandlers = this._resolveMiddleware(route.middleware || []);
233
- const terminal = this._resolveHandler(route.handler, route.method);
234
- this._app[verb](path, ...mwHandlers, terminal);
69
+ const middlewareHandlers = this._resolveMiddleware(route.middleware || []);
70
+ const terminalHandler = this._resolveTerminalHandler(
71
+ route.handler,
72
+ route.method,
73
+ route.verb,
74
+ route.path,
75
+ route.name
76
+ );
77
+
78
+ this._adapter.mountRoute(route.verb, route.path, [
79
+ ...middlewareHandlers,
80
+ terminalHandler,
81
+ ]);
235
82
  }
236
83
 
237
84
  _resolveMiddleware(list) {
238
85
  return list.map(alias => {
239
86
  try {
240
- return this._mw.resolve(alias);
87
+ return this._mw.resolve(alias, this._adapter, this._container);
241
88
  } catch (err) {
242
- console.warn(`[Millas] Warning: ${err.message} — skipping.`);
243
- return (_req, _res, next) => next();
89
+ console.warn(`[Millas] Middleware warning: ${err.message} — skipping.`);
90
+ return this._mw.resolvePassthrough(this._adapter);
244
91
  }
245
92
  });
246
93
  }
247
94
 
248
- _resolveHandler(handler, method) {
95
+ _resolveTerminalHandler(handler, method, verb, path, routeName) {
96
+ const kernelFn = this._extractKernelFn(handler, method);
97
+ const displayName = this._buildDisplayName(handler, method, verb, path, routeName);
98
+ return this._adapter.wrapKernelHandler(kernelFn, displayName, this._container);
99
+ }
100
+
101
+ /**
102
+ * Pull the actual function out of the handler definition.
103
+ * Three forms:
104
+ * 1. Bare function/arrow: Route.get('/', () => jsonify({}))
105
+ * 2. Controller class + method: Route.get('/', UserController, 'index')
106
+ * 3. Controller instance + method: Route.get('/', controllerInstance, 'index')
107
+ */
108
+ _extractKernelFn(handler, method) {
249
109
  if (typeof handler === 'function' && !method) {
250
- return this._wrapAsync(handler);
110
+ return handler;
251
111
  }
112
+
252
113
  if (typeof handler === 'function' && typeof method === 'string') {
253
114
  const instance = new handler();
254
115
  if (typeof instance[method] !== 'function') {
255
- throw new Error(`Method "${method}" not found on controller "${handler.name}".`);
116
+ throw new Error(
117
+ `Method "${method}" not found on controller "${handler.name}".`
118
+ );
256
119
  }
257
- return this._wrapAsync(instance[method].bind(instance));
120
+ return instance[method].bind(instance);
258
121
  }
122
+
259
123
  if (typeof handler === 'object' && handler !== null && typeof method === 'string') {
260
124
  if (typeof handler[method] !== 'function') {
261
125
  throw new Error(`Method "${method}" not found on handler object.`);
262
126
  }
263
- return this._wrapAsync(handler[method].bind(handler));
127
+ return handler[method].bind(handler);
264
128
  }
129
+
265
130
  if (typeof handler === 'function') {
266
- return this._wrapAsync(handler);
131
+ return handler;
267
132
  }
133
+
268
134
  throw new Error(`Invalid route handler: ${JSON.stringify(handler)}`);
269
135
  }
270
136
 
271
- _wrapAsync(fn) {
272
- if (fn.constructor.name === 'AsyncFunction') {
273
- return (req, res, next) => fn(req, res, next).catch(next);
137
+ _buildDisplayName(handler, method, verb, path, routeName) {
138
+ if (routeName) return routeName;
139
+
140
+ if (typeof handler === 'function' && method) {
141
+ return `${handler.name}.${method}`;
142
+ }
143
+
144
+ if (
145
+ typeof handler === 'function' &&
146
+ handler.name &&
147
+ handler.name !== 'anonymous' &&
148
+ handler.name !== ''
149
+ ) {
150
+ return handler.name;
151
+ }
152
+
153
+ return verb && path ? `${verb.toUpperCase()} ${path}` : 'anonymous';
154
+ }
155
+
156
+ /**
157
+ * If no user-defined GET / route exists, ask the adapter to serve
158
+ * a developer-friendly welcome page.
159
+ * Only active outside production — silently skipped in prod.
160
+ */
161
+ _maybeInjectWelcome() {
162
+ if (process.env.NODE_ENV === 'production') return;
163
+
164
+ const hasRoot = this._registry.all().some(
165
+ r => r.verb === 'GET' && (r.path === '/' || r.path === '')
166
+ );
167
+
168
+ if (!hasRoot) {
169
+ let version = '';
170
+ try { version = require('../../package.json').version; } catch {}
171
+ this._adapter.mountWelcome(
172
+ this._adapter.makeWelcomeHandler(version)
173
+ );
274
174
  }
275
- return fn;
276
175
  }
277
176
  }
278
177
 
@@ -38,6 +38,7 @@ async function makeController(name, options = {}) {
38
38
  ? `'use strict';
39
39
 
40
40
  const { Controller } = require('millas');
41
+ const { string, email, number } = require('millas/validation');
41
42
 
42
43
  /**
43
44
  * ${className}
@@ -46,34 +47,32 @@ const { Controller } = require('millas');
46
47
  */
47
48
  class ${className} extends Controller {
48
49
  /** GET /${name.toLowerCase()}s */
49
- async index(req, res) {
50
- return this.ok(res, { data: [] });
50
+ async index({ query }) {
51
+ return this.ok({ data: [] });
51
52
  }
52
53
 
53
54
  /** GET /${name.toLowerCase()}s/:id */
54
- async show(req, res) {
55
- const id = this.param(req, 'id');
56
- return this.ok(res, { data: { id } });
55
+ async show({ params }) {
56
+ return this.ok({ data: { id: params.id } });
57
57
  }
58
58
 
59
59
  /** POST /${name.toLowerCase()}s */
60
- async store(req, res) {
61
- const data = await this.validate(req, {
62
- // 'name': 'required|string|max:255',
60
+ async store({ body }) {
61
+ const data = await body.validate({
62
+ // name: string().required().max(255),
63
63
  });
64
- return this.created(res, { data });
64
+ return this.created({ data });
65
65
  }
66
66
 
67
67
  /** PUT /${name.toLowerCase()}s/:id */
68
- async update(req, res) {
69
- const id = this.param(req, 'id');
70
- const data = this.except(req, ['id']);
71
- return this.ok(res, { data: { id, ...data } });
68
+ async update({ params, body }) {
69
+ const data = body.except(['id']);
70
+ return this.ok({ data: { id: params.id, ...data } });
72
71
  }
73
72
 
74
73
  /** DELETE /${name.toLowerCase()}s/:id */
75
- async destroy(req, res) {
76
- return this.noContent(res);
74
+ async destroy({ params }) {
75
+ return this.noContent();
77
76
  }
78
77
  }
79
78
 
@@ -87,8 +86,8 @@ const { Controller } = require('millas');
87
86
  * ${className}
88
87
  */
89
88
  class ${className} extends Controller {
90
- async index(req, res) {
91
- return this.ok(res, { message: 'Hello from ${className}' });
89
+ async index({ query }) {
90
+ return this.ok({ message: 'Hello from ${className}' });
92
91
  }
93
92
  }
94
93
 
@@ -105,58 +104,23 @@ async function makeModel(name, options = {}) {
105
104
 
106
105
  const content = `'use strict';
107
106
 
108
- const { Model, fields, HasMany, HasOne, BelongsTo, BelongsToMany } = require('millas');
107
+ const { Model, fields } = require('millas');
109
108
 
110
109
  /**
111
110
  * ${className} Model
112
111
  *
113
- * Table: ${tableName}
114
- *
115
- * Edit this file, then run:
116
- * millas makemigrations — generates migration files from your changes
117
- * millas migrate — applies them to the database
112
+ * Represents the "${tableName}" table.
113
+ * Run: millas makemigrations — to generate the migration.
114
+ * Run: millas migrate — to apply it.
118
115
  */
119
116
  class ${className} extends Model {
120
117
  static table = '${tableName}';
121
118
 
122
- // ── Fields ──────────────────────────────────────────────────────
123
119
  static fields = {
124
120
  id: fields.id(),
125
121
  created_at: fields.timestamp(),
126
122
  updated_at: fields.timestamp(),
127
- // deleted_at: fields.timestamp({ nullable: true }), // uncomment + set softDeletes = true
128
123
  };
129
-
130
- // ── Soft deletes ─────────────────────────────────────────────────
131
- // static softDeletes = true;
132
-
133
- // ── Relations ─────────────────────────────────────────────────────
134
- // static relations = {
135
- // posts: new HasMany(() => require('./Post'), 'user_id'),
136
- // profile: new HasOne(() => require('./Profile'), 'user_id'),
137
- // role: new BelongsTo(() => require('./Role'), 'role_id'),
138
- // tags: new BelongsToMany(() => require('./Tag'), '${tableName.replace(/s$/, '')}_tag', '${tableName.replace(/s$/, '')}_id', 'tag_id'),
139
- // };
140
-
141
- // ── Scopes ───────────────────────────────────────────────────────
142
- // static scopes = {
143
- // active: qb => qb.where('active', true),
144
- // recent: qb => qb.latest().limit(10),
145
- // byUser: (qb, userId) => qb.where('user_id', userId),
146
- // };
147
-
148
- // ── Validation ───────────────────────────────────────────────────
149
- // static validate(data) {
150
- // if (!data.name) throw new Error('name is required');
151
- // }
152
-
153
- // ── Lifecycle hooks ──────────────────────────────────────────────
154
- // static async beforeCreate(data) { return data; }
155
- // static async afterCreate(instance) {}
156
- // static async beforeUpdate(data) { return data; }
157
- // static async afterUpdate(instance) {}
158
- // static async beforeDelete(instance){}
159
- // static async afterDelete(instance) {}
160
124
  }
161
125
 
162
126
  module.exports = ${className};
@@ -194,10 +158,11 @@ class ${className} extends Middleware {
194
158
  * Handle the incoming request.
195
159
  * Call next() to continue, or return a response to halt the chain.
196
160
  */
197
- async handle(req, res, next) {
198
- // Your middleware logic here
161
+ async handle({ req }, next) {
162
+ // Destructure what you need: { params, body, query, user, headers, req }
163
+ // Return a MillasResponse to short-circuit, or call next() to continue.
199
164
 
200
- next();
165
+ return next();
201
166
  }
202
167
  }
203
168
 
@@ -12,6 +12,8 @@ function getProjectFiles(projectName) {
12
12
  scripts: {
13
13
  start: 'node bootstrap/app.js',
14
14
  dev: 'millas serve',
15
+ makemigrations: 'millas makemigration',
16
+ migrate: 'millas migrate',
15
17
  serve: 'millas serve',
16
18
  },
17
19
  dependencies: {
@@ -88,18 +90,17 @@ module.exports = {
88
90
 
89
91
  require('dotenv').config();
90
92
 
91
- const { MillasApp } = require('millas');
93
+ const { Millas } = require('millas');
92
94
  const AppServiceProvider = require('../providers/AppServiceProvider');
93
95
 
94
- const app = MillasApp.create()
96
+ module.exports = Millas.config()
95
97
  .providers([AppServiceProvider])
96
98
  .routes(Route => {
97
99
  require('../routes/web')(Route);
98
100
  require('../routes/api')(Route);
99
101
  })
100
- .withAdmin();
101
-
102
- module.exports = app.start();
102
+ .withAdmin()
103
+ .create();
103
104
  `,
104
105
 
105
106
  // ─── routes/web.js ────────────────────────────────────────────
@@ -130,12 +131,13 @@ module.exports = function (Route) {
130
131
  module.exports = function (Route) {
131
132
  Route.prefix('/api').group(() => {
132
133
 
133
- Route.get('/health', (req, res) => {
134
- res.json({ status: 'ok', timestamp: new Date().toISOString() });
135
- });
134
+ Route.get('/health', () =>
135
+ jsonify({ status: 'ok', timestamp: new Date().toISOString() })
136
+ );
136
137
 
137
- // Your API routes here
138
+ // Your API routes here:
138
139
  // Route.resource('/users', UserController);
140
+ // Route.auth('/auth');
139
141
 
140
142
  });
141
143
  };
@@ -241,29 +243,29 @@ module.exports = {
241
243
  // ─── providers/AppServiceProvider.js ──────────────────────────
242
244
  'providers/AppServiceProvider.js': `'use strict';
243
245
 
244
- const { ServiceProvider } = require('millas');
246
+ const { ServiceProvider } = require('millas/core/foundation');
245
247
 
246
248
  /**
247
249
  * AppServiceProvider
248
250
  *
249
251
  * Register and bootstrap your application services here.
250
252
  *
251
- * register() bind things into the container (synchronous)
252
- * boot() — called after all providers have registered (async OK)
253
+ * beforeBoot(container) runs first, before any register(). Synchronous.
254
+ * Good for patching globals, loading config.
255
+ * register(container) — bind singletons, factories, instances.
256
+ * boot(container, app) — all providers registered. Async OK.
257
+ * Good for routes, event listeners, admin resources.
253
258
  */
254
259
  class AppServiceProvider extends ServiceProvider {
255
260
  register(container) {
256
261
  // container.bind('UserService', UserService);
257
- // container.singleton('Mailer', MailService);
262
+ // container.singleton('Cache', CacheService);
258
263
  // container.instance('Config', require('../config/app'));
259
264
  }
260
265
 
261
266
  async boot(container, app) {
262
- // const { Log } = require('millas');
263
- // Log.tag('AppServiceProvider').i('Booted');
264
-
265
- // Register resources in the Admin panel:
266
- // const { Admin, AdminResource } = require('millas');
267
+ // Register Admin resources:
268
+ // const { Admin } = require('millas');
267
269
  // const Post = require('../app/models/Post');
268
270
  // Admin.register(Post);
269
271
  }
@@ -325,4 +327,4 @@ providers/ # Service providers
325
327
  };
326
328
  }
327
329
 
328
- module.exports = { getProjectFiles };
330
+ module.exports = { getProjectFiles };