@nitronjs/framework 0.2.0 → 0.2.2

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 CHANGED
@@ -1,102 +1,138 @@
1
1
  # NitronJS
2
2
 
3
- A modern and extensible Node.js MVC framework built on Fastify. NitronJS focuses on clean architecture, modular structure, and developer productivity, offering built-in routing, middleware, configuration management, CLI tooling, and native React integration for scalable full-stack applications.
3
+ A modern full-stack Node.js framework with React Server Components, Islands Architecture, and built-in tooling. Built on React and Fastify for high performance and developer productivity.
4
4
 
5
- ## Features
5
+ ## Why NitronJS?
6
6
 
7
- - **MVC Architecture** - Clean separation of concerns with Models, Views, and Controllers
8
- - **React SSR** - Built-in React server-side rendering with TypeScript support
9
- - **Fast & Lightweight** - Built on Fastify for high performance
10
- - **CLI Tools** - Powerful command-line interface for scaffolding and development
11
- - **Database ORM** - Intuitive query builder and model system for MySQL
12
- - **Hot Reload** - Development mode with automatic reloading
13
- - **Middleware System** - Flexible middleware pipeline with route-specific middleware
14
- - **Session Management** - Built-in session support with multiple drivers
15
- - **Security** - CSRF protection, authentication, and input validation
16
- - **Modern Build System** - esbuild + PostCSS + Tailwind CSS integration
7
+ - **Zero Config** - Start building immediately. No webpack, no babel config, no boilerplate
8
+ - **Server Components by Default** - All components render on the server. Add `"use client"` only where needed
9
+ - **Islands Architecture** - Client components hydrate independently, keeping your pages fast
10
+ - **Instant HMR** - Changes reflect immediately without losing state
11
+ - **Full-Stack** - Database ORM, authentication, sessions, validation all built-in
17
12
 
18
13
  ## Installation
19
14
 
20
- Create a new NitronJS project:
21
-
22
15
  ```bash
23
16
  npx @nitronjs/framework my-app
24
17
  cd my-app
18
+ npm run dev
25
19
  ```
26
20
 
27
- Dependencies are installed automatically during project creation.
21
+ Your app will be running at `http://localhost:3000`
28
22
 
29
- ## Quick Start
23
+ ## Core Concepts
30
24
 
31
- 1. **Configure your database** in `config/database.js`
25
+ ### Server Components (Default)
32
26
 
33
- 2. **Run migrations**:
34
- ```bash
35
- npx njs migrate --seed
36
- ```
27
+ Every `.tsx` file in `resources/views/` is a Server Component by default. They run on the server, have full access to your database, file system, and never ship JavaScript to the browser.
37
28
 
38
- 3. **Start development server**:
39
- ```bash
40
- npm run dev
29
+ ```tsx
30
+ // resources/views/Site/Home.tsx
31
+ import User from '@/app/Models/User.js';
32
+
33
+ export default async function Home() {
34
+ const users = await User.get(); // Direct database access
35
+
36
+ return (
37
+ <div>
38
+ <h1>Users</h1>
39
+ <ul>
40
+ {users.map(user => (
41
+ <li key={user.id}>{user.name}</li>
42
+ ))}
43
+ </ul>
44
+ </div>
45
+ );
46
+ }
41
47
  ```
42
48
 
43
- Your application will be running at `http://localhost:3000`
49
+ ### Client Components
44
50
 
45
- ## CLI Commands
51
+ Add `"use client"` at the top of a file to make it interactive. These components hydrate on the browser and can use React hooks.
46
52
 
47
- NitronJS comes with a powerful CLI tool (`njs`):
53
+ ```tsx
54
+ // resources/views/Components/Counter.tsx
55
+ "use client";
56
+ import { useState } from 'react';
48
57
 
49
- ### Development & Build
50
- ```bash
51
- njs dev # Start development server with hot reload
52
- njs build # Build views for production
53
- njs start # Start production server
54
- ```
58
+ export default function Counter() {
59
+ const [count, setCount] = useState(0);
55
60
 
56
- ### Database
57
- ```bash
58
- njs migrate # Run pending migrations
59
- njs migrate:fresh # Drop all tables and re-run migrations
60
- njs migrate:fresh --seed # Drop, migrate, and seed
61
- njs seed # Run database seeders
61
+ return (
62
+ <button onClick={() => setCount(count + 1)}>
63
+ Count: {count}
64
+ </button>
65
+ );
66
+ }
62
67
  ```
63
68
 
64
- ### Code Generation
65
- ```bash
66
- njs make:controller <name> # Generate a new controller
67
- njs make:model <name> # Generate a new model
68
- njs make:middleware <name> # Generate a new middleware
69
- njs make:migration <name> # Generate a new migration
70
- njs make:seeder <name> # Generate a new seeder
69
+ Use client components inside server components:
70
+
71
+ ```tsx
72
+ // resources/views/Site/Home.tsx (Server Component)
73
+ import Counter from '../Components/Counter';
74
+
75
+ export default function Home() {
76
+ return (
77
+ <div>
78
+ <h1>Welcome</h1>
79
+ <Counter /> {/* Hydrates as an island */}
80
+ </div>
81
+ );
82
+ }
71
83
  ```
72
84
 
73
- ### Utilities
74
- ```bash
75
- njs storage:link # Create symbolic link for storage
85
+ ### Layouts
86
+
87
+ Create `Layout.tsx` files to wrap pages. Layouts are discovered automatically by walking up the directory tree.
88
+
89
+ ```
90
+ resources/views/
91
+ ├── Layout.tsx # Root layout (wraps everything)
92
+ ├── Site/
93
+ │ └── Home.tsx # Uses root Layout
94
+ └── Admin/
95
+ ├── Layout.tsx # Admin layout (nested inside root)
96
+ └── Dashboard.tsx # Uses both layouts
76
97
  ```
77
98
 
78
- ## Project Structure
99
+ ```tsx
100
+ // resources/views/Layout.tsx
101
+ export default function RootLayout({ children }) {
102
+ return (
103
+ <html lang="en">
104
+ <head>
105
+ <meta charSet="UTF-8" />
106
+ <title>My App</title>
107
+ </head>
108
+ <body>
109
+ {children}
110
+ </body>
111
+ </html>
112
+ );
113
+ }
114
+ ```
79
115
 
116
+ ```tsx
117
+ // resources/views/Admin/Layout.tsx
118
+ export default function AdminLayout({ children }) {
119
+ return (
120
+ <div className="admin-wrapper">
121
+ <nav>Admin Navigation</nav>
122
+ <main>{children}</main>
123
+ </div>
124
+ );
125
+ }
80
126
  ```
81
- my-app/
82
- ├── app/
83
- │ ├── Controllers/ # Request handlers
84
- │ ├── Middlewares/ # Custom middleware
85
- │ └── Models/ # Database models
86
- ├── config/ # Configuration files
87
- │ ├── app.js
88
- │ ├── database.js
89
- │ └── server.js
90
- ├── database/
91
- │ ├── migrations/ # Database migrations
92
- │ └── seeders/ # Database seeders
93
- ├── public/ # Static assets
94
- ├── resources/
95
- │ ├── css/ # Stylesheets
96
- │ └── views/ # React components (TSX)
97
- ├── routes/
98
- │ └── web.js # Route definitions
99
- └── storage/ # File storage
127
+
128
+ Disable layout for a specific page:
129
+
130
+ ```tsx
131
+ export const layout = false;
132
+
133
+ export default function FullscreenPage() {
134
+ return <div>No layout wrapper</div>;
135
+ }
100
136
  ```
101
137
 
102
138
  ## Routing
@@ -106,88 +142,86 @@ Define routes in `routes/web.js`:
106
142
  ```javascript
107
143
  import { Route } from '@nitronjs/framework';
108
144
  import HomeController from '#app/Controllers/HomeController.js';
145
+ import UserController from '#app/Controllers/UserController.js';
109
146
 
147
+ // Basic routes
110
148
  Route.get('/', HomeController.index).name('home');
111
- Route.get('/about', HomeController.about);
112
- Route.post('/contact', HomeController.contact);
113
- ```
149
+ Route.get('/about', HomeController.about).name('about');
114
150
 
115
- ### Route Groups & Middleware
151
+ // Route parameters
152
+ Route.get('/users/:id', UserController.show).name('user.show');
116
153
 
117
- ```javascript
154
+ // Route groups with middleware
118
155
  Route.prefix('/admin').middleware('auth').group(() => {
119
- Route.get('/dashboard', AdminController.dashboard);
120
- Route.get('/users', AdminController.users);
156
+ Route.get('/dashboard', AdminController.dashboard).name('admin.dashboard');
157
+ Route.get('/users', AdminController.users).name('admin.users');
121
158
  });
159
+
160
+ // RESTful routes
161
+ Route.post('/users', UserController.store);
162
+ Route.put('/users/:id', UserController.update);
163
+ Route.delete('/users/:id', UserController.destroy);
122
164
  ```
123
165
 
124
- ## Controllers
166
+ Generate URLs using route names:
125
167
 
126
- Create controllers with `njs make:controller`:
168
+ ```tsx
169
+ <a href={route('user.show', { id: 1 })}>View User</a>
170
+ ```
171
+
172
+ ## Controllers
127
173
 
128
174
  ```javascript
129
- class HomeController {
130
- static async index(request, reply) {
131
- return reply.view('Site/Home', {
132
- title: 'Welcome'
133
- });
134
- }
135
-
136
- static async contact(request, reply) {
137
- const { name, email } = request.body;
138
- // Handle contact form
139
- return reply.redirect('/thank-you');
175
+ // app/Controllers/UserController.js
176
+ class UserController {
177
+ static async index(req, res) {
178
+ const users = await User.get();
179
+
180
+ return res.view('User/Index', { users });
140
181
  }
141
- }
142
182
 
143
- export default HomeController;
144
- ```
183
+ static async show(req, res) {
184
+ const user = await User.find(req.params.id);
145
185
 
146
- ## Views (React SSR)
186
+ if (!user) {
187
+ return res.status(404).view('Errors/NotFound');
188
+ }
147
189
 
148
- Create React views in `resources/views/`:
190
+ return res.view('User/Show', { user });
191
+ }
149
192
 
150
- ```tsx
151
- // resources/views/Site/Home.tsx
152
- export default function Home({ title, items }) {
153
- return (
154
- <html>
155
- <head>
156
- <title>{title}</title>
157
- <link rel="stylesheet" href="/css/site_style.css" />
158
- </head>
159
- <body>
160
- <h1>{title}</h1>
161
- <ul>
162
- {items.map(item => (
163
- <li key={item.id}>{item.name}</li>
164
- ))}
165
- </ul>
166
- </body>
167
- </html>
168
- );
193
+ static async store(req, res) {
194
+ const { name, email } = req.body;
195
+
196
+ const user = new User();
197
+ user.name = name;
198
+ user.email = email;
199
+ await user.save();
200
+
201
+ return res.redirect(route('user.show', { id: user.id }));
202
+ }
169
203
  }
170
- ```
171
204
 
172
- Views are automatically compiled and bundled with your application.
205
+ export default UserController;
206
+ ```
173
207
 
174
- ## Models
208
+ ## Database
175
209
 
176
- Create models with `njs make:model`:
210
+ ### Models
177
211
 
178
212
  ```javascript
213
+ // app/Models/User.js
179
214
  import { Model } from '@nitronjs/framework';
180
215
 
181
216
  export default class User extends Model {
182
217
  static table = 'users';
183
- static primaryKey = 'id';
184
218
  }
185
219
  ```
186
220
 
187
- ### Query Examples
221
+ ### Queries
188
222
 
189
223
  ```javascript
190
- // Find all users
224
+ // Find all
191
225
  const users = await User.get();
192
226
 
193
227
  // Find by ID
@@ -195,63 +229,40 @@ const user = await User.find(1);
195
229
 
196
230
  // Where clauses
197
231
  const admins = await User.where('role', '=', 'admin').get();
232
+ const active = await User.where('active', true).get();
198
233
 
199
- // Create new record
234
+ // Chaining
235
+ const results = await User
236
+ .where('role', '=', 'admin')
237
+ .where('active', true)
238
+ .orderBy('created_at', 'desc')
239
+ .limit(10)
240
+ .get();
241
+
242
+ // First result
243
+ const user = await User.where('email', '=', 'john@example.com').first();
244
+
245
+ // Create
200
246
  const user = new User();
201
247
  user.name = 'John';
202
248
  user.email = 'john@example.com';
203
249
  await user.save();
204
250
 
205
251
  // Update
206
- await User.where('id', '=', 1).update({
207
- name: 'Jane'
208
- });
252
+ await User.where('id', '=', 1).update({ name: 'Jane' });
209
253
 
210
254
  // Delete
211
255
  await User.where('id', '=', 1).delete();
212
256
  ```
213
257
 
214
- ## Middleware
215
-
216
- Create middleware with `njs make:middleware`:
217
-
218
- ```javascript
219
- class CheckAge {
220
- static async handler(req, res) {
221
- const age = req.query.age;
222
-
223
- if (age < 18) {
224
- return res.status(403).send('Access denied');
225
- }
226
-
227
- // If no return, continues to next middleware/handler
228
- }
229
- }
230
-
231
- export default CheckAge;
232
- ```
233
-
234
- Register middleware in `app/Kernel.js`:
235
-
236
- ```javascript
237
- export default {
238
- routeMiddlewares: {
239
- "check-age": CheckAge,
240
- }
241
- };
242
- ```
243
-
244
- Apply to routes:
258
+ ### Migrations
245
259
 
246
- ```javascript
247
- Route.middleware('check-age').get('/restricted', Controller.method);
260
+ ```bash
261
+ npm run make:migration create_users_table
248
262
  ```
249
263
 
250
- ## Database Migrations
251
-
252
- Create migrations with `njs make:migration`:
253
-
254
264
  ```javascript
265
+ // database/migrations/2024_01_01_000000_create_users_table.js
255
266
  import { Schema } from '@nitronjs/framework';
256
267
 
257
268
  class CreateUsersTable {
@@ -265,7 +276,7 @@ class CreateUsersTable {
265
276
  table.timestamp('updated_at').nullable();
266
277
  });
267
278
  }
268
-
279
+
269
280
  static async down() {
270
281
  await Schema.dropIfExists('users');
271
282
  }
@@ -274,84 +285,67 @@ class CreateUsersTable {
274
285
  export default CreateUsersTable;
275
286
  ```
276
287
 
277
- ## Configuration
278
-
279
- All configuration files are in the `config/` directory:
280
-
281
- - **app.js** - Application settings (name, env, debug mode)
282
- - **database.js** - Database connections
283
- - **server.js** - Server configuration (port, host, CORS)
284
- - **session.js** - Session management
285
- - **auth.js** - Authentication settings
286
- - **hash.js** - Hashing algorithm configuration
288
+ Run migrations:
287
289
 
288
- Access config values:
289
-
290
- ```javascript
291
- import { Config } from '@nitronjs/framework';
292
-
293
- const appName = Config.get('app.name');
294
- const dbHost = Config.get('database.host');
290
+ ```bash
291
+ npm run migrate # Run pending migrations
292
+ npm run migrate:fresh # Drop all tables and re-run
293
+ npm run migrate:fresh --seed # Drop, migrate, and seed
295
294
  ```
296
295
 
297
- ## Session Management
296
+ ## Authentication
298
297
 
299
298
  ```javascript
300
- // Set session data
301
- req.session.set('user', { id: 1, name: 'John' });
299
+ // In a controller or middleware
300
+ class AuthController {
301
+ static async login(req, res) {
302
+ const { email, password } = req.body;
302
303
 
303
- // Get session data
304
- const user = req.session.get('user');
304
+ const success = await req.auth.attempt({ email, password });
305
305
 
306
- // Check if session has value
307
- if (req.session.get('user')) {
308
- // User is logged in
309
- }
310
-
311
- // Remove session data
312
- req.session.set('user', null);
306
+ if (success) {
307
+ return req.redirect('/dashboard');
308
+ }
313
309
 
314
- // Get all session data
315
- const allData = req.session.all();
310
+ return req.view('Auth/Login', {
311
+ error: 'Invalid credentials'
312
+ });
313
+ }
316
314
 
317
- // Regenerate session (security - after login)
318
- req.session.regenerate();
315
+ static async logout(req, res) {
316
+ await req.auth.logout();
319
317
 
320
- // Flash messages (one-time)
321
- req.session.flash('message', 'Success!');
322
- const message = req.session.getFlash('message');
318
+ return res.redirect('/');
319
+ }
320
+ }
323
321
 
324
- // CSRF token
325
- const token = req.session.getCsrfToken();
322
+ // Check authentication
323
+ if (req.auth.check()) {
324
+ const user = await req.auth.user();
325
+ }
326
326
  ```
327
327
 
328
- ## Authentication
329
-
330
- Built-in authentication system (request-based):
328
+ ## Session
331
329
 
332
330
  ```javascript
333
- // Attempt login (inside controller or middleware)
334
- const success = await req.auth.attempt({
335
- email: 'admin@example.com',
336
- password: 'secret'
337
- });
338
- // veya belirli bir guard ile:
339
- // const success = await req.auth.guard('user').attempt({ email, password });
331
+ // Set value
332
+ req.session.set('key', 'value');
340
333
 
341
- // Kullanıcıya erişim
342
- const user = await req.auth.user();
343
- // veya
344
- // const user = await req.auth.guard('user').user();
334
+ // Get value
335
+ const value = req.session.get('key');
345
336
 
346
- // Giriş yapmış mı?
347
- if (req.auth.check()) {
348
- // Authenticated
349
- }
337
+ // Flash messages (one-time)
338
+ req.session.flash('success', 'Profile updated!');
339
+ const message = req.session.getFlash('success');
340
+
341
+ // All data
342
+ const data = req.session.all();
343
+
344
+ // Regenerate session ID (after login)
345
+ req.session.regenerate();
350
346
 
351
- // Çıkış
352
- await req.auth.logout();
353
- // veya
354
- // await req.auth.guard('user').logout();
347
+ // CSRF token
348
+ const token = req.session.getCsrfToken();
355
349
  ```
356
350
 
357
351
  ## Validation
@@ -360,9 +354,10 @@ await req.auth.logout();
360
354
  import { Validator } from '@nitronjs/framework';
361
355
 
362
356
  const validation = Validator.make(req.body, {
357
+ name: 'required|string|min:2|max:100',
363
358
  email: 'required|email',
364
359
  password: 'required|string|min:8',
365
- age: 'required|numeric|min:18'
360
+ age: 'numeric|min:18'
366
361
  });
367
362
 
368
363
  if (validation.fails()) {
@@ -372,58 +367,141 @@ if (validation.fails()) {
372
367
  }
373
368
  ```
374
369
 
375
- ## Environment Variables
370
+ ## Middleware
376
371
 
377
- Create a `.env` file in your project root:
372
+ Create custom middleware:
378
373
 
379
- ```env
380
- APP_NAME=nitronjs
381
- APP_DEV=true
382
- APP_DEBUG=true
383
- APP_PORT=3000
384
- APP_URL=http://localhost
374
+ ```javascript
375
+ // app/Middlewares/CheckAge.js
376
+ class CheckAge {
377
+ static async handler(req, res) {
378
+ if (req.query.age < 18) {
379
+ return res.status(403).send('Access denied');
380
+ }
381
+ // No return = continue to next handler
382
+ }
383
+ }
385
384
 
386
- DATABASE_DRIVER=mysql
387
- DATABASE_HOST=127.0.0.1
388
- DATABASE_PORT=3306
389
- DATABASE_NAME=nitronjs
390
- DATABASE_USERNAME=root
391
- DATABASE_PASSWORD=
385
+ export default CheckAge;
392
386
  ```
393
387
 
394
- ## Response Methods
388
+ Register in `app/Kernel.js`:
395
389
 
396
390
  ```javascript
397
- // Render view
398
- return res.view('Site/Home', { title: 'Welcome' });
391
+ export default {
392
+ routeMiddlewares: {
393
+ 'check-age': CheckAge,
394
+ 'auth': AuthMiddleware,
395
+ }
396
+ };
397
+ ```
399
398
 
400
- // JSON response
401
- return res.json({ success: true, data: users });
399
+ Apply to routes:
400
+
401
+ ```javascript
402
+ Route.middleware('check-age').get('/restricted', Controller.method);
403
+ Route.middleware('auth', 'check-age').get('/admin', AdminController.index);
404
+ ```
402
405
 
403
- // Redirect
404
- return res.redirect('/dashboard');
406
+ ## CSS & Tailwind
405
407
 
406
- // Status code
407
- return res.status(404).send('Not Found');
408
+ Put your CSS files in `resources/css/`. Tailwind CSS is automatically detected and processed.
408
409
 
409
- // Download file
410
- return res.download('/path/to/file.pdf');
410
+ ```css
411
+ /* resources/css/app.css */
412
+ @import "tailwindcss";
411
413
  ```
412
414
 
413
- ## Testing Your Application
415
+ CSS files are automatically linked in your views:
414
416
 
415
- Build and run in production mode:
417
+ ```tsx
418
+ // CSS from resources/css/ is available at /storage/css/
419
+ <link rel="stylesheet" href="/storage/css/app.css" />
420
+ ```
421
+
422
+ ## CLI Commands
416
423
 
417
424
  ```bash
418
- npm run build
419
- npm run start
425
+ # Development
426
+ npm run dev # Start dev server with HMR
427
+ npm run build # Build for production
428
+ npm run start # Start production server
429
+
430
+ # Database
431
+ npm run migrate # Run migrations
432
+ npm run migrate:fresh # Fresh migration
433
+ npm run seed # Run seeders
434
+
435
+ # Code Generation
436
+ npm run make:controller <name>
437
+ npm run make:model <name>
438
+ npm run make:middleware <name>
439
+ npm run make:migration <name>
440
+ npm run make:seeder <name>
441
+
442
+ # Utilities
443
+ npm run storage:link # Create storage symlink
444
+ ```
445
+
446
+ ## Project Structure
447
+
448
+ ```
449
+ my-app/
450
+ ├── app/
451
+ │ ├── Controllers/ # Request handlers
452
+ │ ├── Middlewares/ # Custom middleware
453
+ │ └── Models/ # Database models
454
+ ├── config/ # Configuration files
455
+ │ ├── app.js
456
+ │ ├── database.js
457
+ │ └── server.js
458
+ ├── database/
459
+ │ ├── migrations/ # Database migrations
460
+ │ └── seeders/ # Database seeders
461
+ ├── public/ # Static assets
462
+ ├── resources/
463
+ │ ├── css/ # Stylesheets
464
+ │ └── views/ # React components (TSX)
465
+ ├── routes/
466
+ │ └── web.js # Route definitions
467
+ ├── storage/ # File storage
468
+ └── .env # Environment variables
469
+ ```
470
+
471
+ ## Configuration
472
+
473
+ Access configuration values:
474
+
475
+ ```javascript
476
+ import { Config } from '@nitronjs/framework';
477
+
478
+ const appName = Config.get('app.name');
479
+ const dbHost = Config.get('database.host', 'localhost'); // with default
480
+ ```
481
+
482
+ Environment variables in `.env`:
483
+
484
+ ```env
485
+ APP_NAME=MyApp
486
+ APP_DEV=true
487
+ APP_PORT=3000
488
+
489
+ DATABASE_HOST=127.0.0.1
490
+ DATABASE_PORT=3306
491
+ DATABASE_NAME=myapp
492
+ DATABASE_USERNAME=root
493
+ DATABASE_PASSWORD=
420
494
  ```
421
495
 
422
496
  ## Requirements
423
497
 
424
- - Node.js 18.x or higher
498
+ - Node.js 18+
425
499
  - MySQL 5.7+ or MariaDB 10.3+
426
500
 
501
+ ## Documentation
502
+
503
+ Full documentation available at [nitronjs.dev](https://nitronjs.dev/docs)
504
+
427
505
  ## License
428
506
 
429
507
  ISC
@@ -5,12 +5,8 @@ class MigrationRepository {
5
5
  static table = 'migrations';
6
6
 
7
7
  static async tableExists() {
8
- try {
9
- await DB.table(this.table).limit(1).get();
10
- return true;
11
- } catch {
12
- return false;
13
- }
8
+ const [rows] = await DB.rawQuery(`SHOW TABLES LIKE '${this.table}'`);
9
+ return rows.length > 0;
14
10
  }
15
11
 
16
12
  static async getExecuted() {
@@ -2,7 +2,6 @@ import DB from './DB.js';
2
2
 
3
3
  class Model {
4
4
  static table = null;
5
- static primaryKey = 'id';
6
5
 
7
6
  constructor(attrs = {}) {
8
7
  Object.defineProperty(this, '_attributes', { value: {}, writable: true });
@@ -63,7 +62,7 @@ class Model {
63
62
  throw new Error(`Model ${this.name} must define a static 'table' property`);
64
63
  }
65
64
 
66
- const row = await DB.table(this.table).where(this.primaryKey, id).first();
65
+ const row = await DB.table(this.table).where("id", id).first();
67
66
 
68
67
  if (!row) return null;
69
68
 
@@ -112,15 +111,15 @@ class Model {
112
111
  const data = {};
113
112
 
114
113
  for (const [key, value] of Object.entries(this._attributes)) {
115
- if (value !== undefined && (key !== constructor.primaryKey || !this._exists)) {
114
+ if (value !== undefined && (key !== "id" || !this._exists)) {
116
115
  data[key] = value;
117
116
  }
118
117
  }
119
118
 
120
119
  if (this._exists) {
121
- const primaryKeyValue = this._attributes[constructor.primaryKey];
120
+ const primaryKeyValue = this._attributes["id"];
122
121
  await DB.table(constructor.table)
123
- .where(constructor.primaryKey, primaryKeyValue)
122
+ .where("id", primaryKeyValue)
124
123
  .update(data);
125
124
 
126
125
  Object.assign(this._attributes, data);
@@ -128,7 +127,7 @@ class Model {
128
127
  }
129
128
  else {
130
129
  const id = await DB.table(constructor.table).insert(data);
131
- this._attributes[constructor.primaryKey] = id;
130
+ this._attributes["id"] = id;
132
131
 
133
132
  this._original = { ...this._attributes };
134
133
  this._exists = true;
@@ -139,14 +138,14 @@ class Model {
139
138
 
140
139
  async delete() {
141
140
  const constructor = this.constructor;
142
- const primaryKeyValue = this._attributes[constructor.primaryKey];
141
+ const primaryKeyValue = this._attributes["id"];
143
142
 
144
143
  if (!this._exists) {
145
144
  throw new Error('Cannot delete a model that does not exist');
146
145
  }
147
146
 
148
147
  await DB.table(constructor.table)
149
- .where(constructor.primaryKey, primaryKeyValue)
148
+ .where("id", primaryKeyValue)
150
149
  .delete();
151
150
 
152
151
  this._exists = false;
@@ -5,12 +5,8 @@ class SeederRepository {
5
5
  static table = 'seeders';
6
6
 
7
7
  static async tableExists() {
8
- try {
9
- await DB.table(this.table).limit(1).get();
10
- return true;
11
- } catch {
12
- return false;
13
- }
8
+ const [rows] = await DB.rawQuery(`SHOW TABLES LIKE '${this.table}'`);
9
+ return rows.length > 0;
14
10
  }
15
11
 
16
12
  static async getExecuted() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitronjs/framework",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "NitronJS is a modern and extensible Node.js MVC framework built on Fastify. It focuses on clean architecture, modular structure, and developer productivity, offering built-in routing, middleware, configuration management, CLI tooling, and native React integration for scalable full-stack applications.",
5
5
  "bin": {
6
6
  "njs": "./cli/njs.js"
@@ -1,31 +1,7 @@
1
1
  class HomeController {
2
-
2
+
3
3
  static async index(req, res) {
4
- return res.view("Site/Home", {
5
- title: "Welcome to NitronJS",
6
- features: [
7
- {
8
- icon: "⚡",
9
- title: "Lightning Fast",
10
- description: "Server-side rendering with React for blazing fast page loads"
11
- },
12
- {
13
- icon: "🔥",
14
- title: "Hot Module Reload",
15
- description: "See your changes instantly without losing application state"
16
- },
17
- {
18
- icon: "🛡️",
19
- title: "Secure by Default",
20
- description: "Built-in CSRF protection, secure sessions, and CSP headers"
21
- },
22
- {
23
- icon: "📦",
24
- title: "MVC Architecture",
25
- description: "Clean separation of concerns with Models, Views, and Controllers"
26
- }
27
- ]
28
- });
4
+ return res.view("Site/Home");
29
5
  }
30
6
 
31
7
  }
@@ -1,290 +1,86 @@
1
- type Feature = {
2
- icon: string;
3
- title: string;
4
- description: string;
5
- };
6
-
7
1
  type HomeProps = {
8
- title?: string;
9
- features?: Feature[];
2
+ version?: string;
10
3
  };
11
4
 
12
- export default function Home({ title = "Welcome to NitronJS", features = [] }: HomeProps) {
5
+ export default function Home({ version = "0.2.0" }: HomeProps) {
13
6
  return (
14
7
  <main style={{
15
8
  minHeight: "100vh",
16
- background: "linear-gradient(135deg, #0f0f23 0%, #1a1a3e 50%, #0f172a 100%)",
17
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif",
9
+ background: "#0a0a0a",
10
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
18
11
  color: "#e4e4e7",
19
- overflow: "hidden"
12
+ display: "flex",
13
+ flexDirection: "column",
14
+ alignItems: "center",
15
+ justifyContent: "center",
16
+ padding: "2rem",
17
+ boxSizing: "border-box"
20
18
  }}>
21
- {/* Background decoration */}
22
19
  <div style={{
23
- position: "fixed",
24
- top: 0,
25
- left: 0,
26
- right: 0,
27
- bottom: 0,
28
- background: "radial-gradient(circle at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 50%), radial-gradient(circle at 80% 80%, rgba(139, 92, 246, 0.1) 0%, transparent 50%)",
29
- pointerEvents: "none"
30
- }} />
31
-
32
- {/* Hero Section */}
33
- <section style={{
34
- minHeight: "100vh",
20
+ width: 64,
21
+ height: 64,
22
+ background: "#fff",
23
+ borderRadius: 14,
35
24
  display: "flex",
36
- flexDirection: "column",
37
25
  alignItems: "center",
38
26
  justifyContent: "center",
39
- padding: "2rem",
40
- position: "relative",
41
- textAlign: "center"
27
+ marginBottom: "2rem"
42
28
  }}>
43
- {/* Logo */}
44
- <div style={{
45
- width: 80,
46
- height: 80,
47
- background: "linear-gradient(135deg, #6366f1, #8b5cf6)",
48
- borderRadius: 20,
49
- display: "flex",
50
- alignItems: "center",
51
- justifyContent: "center",
52
- marginBottom: "2rem",
53
- boxShadow: "0 20px 60px rgba(99, 102, 241, 0.4)"
54
- }}>
55
- <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2">
56
- <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
57
- </svg>
58
- </div>
59
-
60
- {/* Title */}
61
- <h1 style={{
62
- fontSize: "clamp(2.5rem, 8vw, 4.5rem)",
63
- fontWeight: 800,
64
- margin: 0,
65
- background: "linear-gradient(135deg, #fff 0%, #a1a1aa 100%)",
66
- WebkitBackgroundClip: "text",
67
- WebkitTextFillColor: "transparent",
68
- letterSpacing: "-0.02em",
69
- lineHeight: 1.1
70
- }}>
71
- {title}
72
- </h1>
29
+ <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#0a0a0a" strokeWidth="2.5">
30
+ <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
31
+ </svg>
32
+ </div>
73
33
 
74
- {/* Subtitle */}
75
- <p style={{
76
- fontSize: "clamp(1rem, 2.5vw, 1.25rem)",
77
- color: "#71717a",
78
- maxWidth: 600,
79
- margin: "1.5rem 0 2.5rem",
80
- lineHeight: 1.7
81
- }}>
82
- Build modern, full-stack web applications with React SSR,
83
- MVC architecture, and developer-friendly tooling.
84
- </p>
34
+ <h1 style={{
35
+ fontSize: "2.5rem",
36
+ fontWeight: 700,
37
+ margin: 0,
38
+ color: "#fff",
39
+ letterSpacing: "-0.02em"
40
+ }}>
41
+ Welcome to NitronJS
42
+ </h1>
85
43
 
86
- {/* CTA Buttons */}
87
- <div style={{
88
- display: "flex",
89
- gap: "1rem",
90
- flexWrap: "wrap",
91
- justifyContent: "center"
92
- }}>
93
- <a href="https://nitronjs.dev/docs" style={{
94
- display: "inline-flex",
95
- alignItems: "center",
96
- gap: "0.5rem",
97
- padding: "0.875rem 1.75rem",
98
- background: "linear-gradient(135deg, #6366f1, #8b5cf6)",
99
- color: "#fff",
100
- textDecoration: "none",
101
- fontWeight: 600,
102
- fontSize: "0.95rem",
103
- borderRadius: 12,
104
- boxShadow: "0 4px 20px rgba(99, 102, 241, 0.4)",
105
- transition: "transform 0.2s, box-shadow 0.2s"
106
- }}>
107
- Get Started
108
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
109
- <path d="M5 12h14M12 5l7 7-7 7"/>
110
- </svg>
111
- </a>
112
- <a href="https://github.com/nicatdursunlu/nitronjs" style={{
113
- display: "inline-flex",
114
- alignItems: "center",
115
- gap: "0.5rem",
116
- padding: "0.875rem 1.75rem",
117
- background: "rgba(255, 255, 255, 0.05)",
118
- color: "#e4e4e7",
119
- textDecoration: "none",
120
- fontWeight: 600,
121
- fontSize: "0.95rem",
122
- borderRadius: 12,
123
- border: "1px solid rgba(255, 255, 255, 0.1)",
124
- transition: "background 0.2s, border-color 0.2s"
125
- }}>
126
- <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
127
- <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/>
128
- </svg>
129
- GitHub
130
- </a>
131
- </div>
44
+ <p style={{
45
+ fontSize: "1.1rem",
46
+ color: "#71717a",
47
+ maxWidth: 400,
48
+ margin: "1.5rem 0 2rem",
49
+ lineHeight: 1.6,
50
+ textAlign: "center"
51
+ }}>
52
+ Your application is ready. Start building by editing the files in <code style={{ color: "#a1a1aa", background: "#1a1a1a", padding: "2px 6px", borderRadius: 4 }}>resources/views</code>
53
+ </p>
132
54
 
133
- {/* Scroll indicator */}
134
- <div style={{
135
- position: "absolute",
136
- bottom: "2rem",
137
- display: "flex",
138
- flexDirection: "column",
55
+ <a
56
+ href="https://nitronjs.dev/docs"
57
+ style={{
58
+ display: "inline-flex",
139
59
  alignItems: "center",
140
60
  gap: "0.5rem",
141
- color: "#52525b",
142
- fontSize: "0.75rem"
143
- }}>
144
- <span>Scroll to explore</span>
145
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
146
- <path d="M12 5v14M5 12l7 7 7-7"/>
147
- </svg>
148
- </div>
149
- </section>
150
-
151
- {/* Features Section */}
152
- <section style={{
153
- padding: "6rem 2rem",
154
- maxWidth: 1200,
155
- margin: "0 auto"
156
- }}>
157
- <h2 style={{
158
- fontSize: "clamp(1.75rem, 4vw, 2.5rem)",
159
- fontWeight: 700,
160
- textAlign: "center",
161
- margin: "0 0 1rem",
162
- color: "#fff"
163
- }}>
164
- Why NitronJS?
165
- </h2>
166
- <p style={{
167
- textAlign: "center",
168
- color: "#71717a",
169
- maxWidth: 500,
170
- margin: "0 auto 4rem",
171
- fontSize: "1.1rem"
172
- }}>
173
- Everything you need to build production-ready applications
174
- </p>
61
+ padding: "0.75rem 1.5rem",
62
+ background: "#fff",
63
+ color: "#0a0a0a",
64
+ textDecoration: "none",
65
+ fontWeight: 600,
66
+ fontSize: "0.9rem",
67
+ borderRadius: 8
68
+ }}
69
+ >
70
+ Documentation
71
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
72
+ <path d="M5 12h14M12 5l7 7-7 7"/>
73
+ </svg>
74
+ </a>
175
75
 
176
- <div style={{
177
- display: "grid",
178
- gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))",
179
- gap: "1.5rem"
180
- }}>
181
- {features.map((feature, index) => (
182
- <div key={index} style={{
183
- padding: "2rem",
184
- background: "linear-gradient(145deg, rgba(255,255,255,0.03), rgba(255,255,255,0.01))",
185
- border: "1px solid rgba(255,255,255,0.06)",
186
- borderRadius: 16,
187
- transition: "transform 0.2s, border-color 0.2s"
188
- }}>
189
- <div style={{
190
- width: 48,
191
- height: 48,
192
- background: "linear-gradient(135deg, rgba(99,102,241,0.2), rgba(139,92,246,0.1))",
193
- borderRadius: 12,
194
- display: "flex",
195
- alignItems: "center",
196
- justifyContent: "center",
197
- fontSize: "1.5rem",
198
- marginBottom: "1.25rem"
199
- }}>
200
- {feature.icon}
201
- </div>
202
- <h3 style={{
203
- fontSize: "1.125rem",
204
- fontWeight: 600,
205
- margin: "0 0 0.75rem",
206
- color: "#fff"
207
- }}>
208
- {feature.title}
209
- </h3>
210
- <p style={{
211
- fontSize: "0.9rem",
212
- color: "#71717a",
213
- margin: 0,
214
- lineHeight: 1.6
215
- }}>
216
- {feature.description}
217
- </p>
218
- </div>
219
- ))}
220
- </div>
221
- </section>
222
-
223
- {/* Code Example Section */}
224
- <section style={{
225
- padding: "4rem 2rem 6rem",
226
- maxWidth: 800,
227
- margin: "0 auto"
228
- }}>
229
- <h2 style={{
230
- fontSize: "clamp(1.5rem, 3vw, 2rem)",
231
- fontWeight: 700,
232
- textAlign: "center",
233
- margin: "0 0 2rem",
234
- color: "#fff"
235
- }}>
236
- Start Building
237
- </h2>
238
- <div style={{
239
- background: "#0d0d1a",
240
- border: "1px solid rgba(255,255,255,0.08)",
241
- borderRadius: 16,
242
- overflow: "hidden"
243
- }}>
244
- <div style={{
245
- padding: "1rem 1.25rem",
246
- borderBottom: "1px solid rgba(255,255,255,0.06)",
247
- display: "flex",
248
- alignItems: "center",
249
- gap: "0.5rem"
250
- }}>
251
- <span style={{ width: 12, height: 12, borderRadius: "50%", background: "#ef4444" }} />
252
- <span style={{ width: 12, height: 12, borderRadius: "50%", background: "#fbbf24" }} />
253
- <span style={{ width: 12, height: 12, borderRadius: "50%", background: "#4ade80" }} />
254
- <span style={{ marginLeft: "auto", color: "#52525b", fontSize: "0.8rem" }}>Terminal</span>
255
- </div>
256
- <pre style={{
257
- margin: 0,
258
- padding: "1.5rem",
259
- fontSize: "0.9rem",
260
- lineHeight: 1.8,
261
- overflowX: "auto"
262
- }}>
263
- <code style={{ color: "#a1a1aa" }}>
264
- <span style={{ color: "#71717a" }}># Create a new project</span>{"\n"}
265
- <span style={{ color: "#4ade80" }}>npx</span> @nitronjs/framework my-app{"\n\n"}
266
- <span style={{ color: "#71717a" }}># Start development server</span>{"\n"}
267
- <span style={{ color: "#4ade80" }}>cd</span> my-app{"\n"}
268
- <span style={{ color: "#4ade80" }}>npm</span> run dev
269
- </code>
270
- </pre>
271
- </div>
272
- </section>
273
-
274
- {/* Footer */}
275
- <footer style={{
276
- padding: "3rem 2rem",
277
- borderTop: "1px solid rgba(255,255,255,0.05)",
278
- textAlign: "center"
76
+ <div style={{
77
+ position: "fixed",
78
+ bottom: "1.5rem",
79
+ color: "#3f3f46",
80
+ fontSize: "0.8rem"
279
81
  }}>
280
- <p style={{
281
- margin: 0,
282
- color: "#52525b",
283
- fontSize: "0.875rem"
284
- }}>
285
- Built with ⚡ NitronJS — The Modern Full-Stack Framework
286
- </p>
287
- </footer>
82
+ v{version}
83
+ </div>
288
84
  </main>
289
85
  );
290
86
  }