free-framework 4.5.7 → 4.6.0

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": "free-framework",
3
- "version": "4.5.7",
3
+ "version": "4.6.0",
4
4
  "description": "Professional Node.js engine for the .free language. Blazing-fast SSR, Islands Architecture, and built-in ORM.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -3,13 +3,13 @@
3
3
  * Enterprise Maintenance Mode Guard.
4
4
  */
5
5
 
6
- module.exports = function maintenanceMiddleware(req, res, next) {
6
+ module.exports = function maintenanceMiddleware(req, res) {
7
7
  const isMaintenance = process.env.MAINTENANCE_MODE === 'true';
8
8
  const bypassToken = process.env.MAINTENANCE_BYPASS;
9
9
 
10
10
  // Allow bypassing with a specific query token or if disabled
11
11
  if (!isMaintenance || (bypassToken && req.query.bypass === bypassToken)) {
12
- return next();
12
+ return;
13
13
  }
14
14
 
15
15
  // Identify if it's an API request or HTML
@@ -3,7 +3,7 @@
3
3
  * Enterprise-grade Security Headers Middleware.
4
4
  */
5
5
 
6
- module.exports = function securityMiddleware(req, res, next) {
6
+ module.exports = function securityMiddleware(req, res) {
7
7
  // 1. Core Security Headers
8
8
  res.setHeader('X-Powered-By', 'Free-Ultra/Enterprise');
9
9
  res.setHeader('X-Content-Type-Options', 'nosniff');
@@ -25,6 +25,4 @@ module.exports = function securityMiddleware(req, res, next) {
25
25
 
26
26
  // 4. Permissions Policy (Hardware restrictions)
27
27
  res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), interest-cohort=()');
28
-
29
- next();
30
28
  };
package/runtime/server.js CHANGED
@@ -21,11 +21,11 @@ class FreeServer {
21
21
  this.plugins = [];
22
22
  this.viewsPath = process.env.VIEWS_PATH || nodePath.join(process.cwd(), 'views');
23
23
 
24
- // Static Asset Serving - GET fallback for priority
24
+ // Static Asset Serving Middleware
25
25
  const publicPath = nodePath.join(process.cwd(), 'public');
26
- this.app.get('/*', (req, res, next) => {
26
+ this.app.use((req, res) => {
27
27
  const lookupPath = req.path.startsWith('/') ? req.path.substring(1) : req.path;
28
- if (!lookupPath) return next();
28
+ if (!lookupPath) return;
29
29
 
30
30
  const fullPath = nodePath.join(publicPath, lookupPath);
31
31
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
@@ -34,7 +34,6 @@ class FreeServer {
34
34
  const mimes = { '.js': 'application/javascript', '.css': 'text/css', '.png': 'image/png' };
35
35
  return res.type(mimes[ext] || 'application/javascript').send(content);
36
36
  }
37
- next();
38
37
  });
39
38
 
40
39
  this.app.get('/test-static', (req, res) => res.send('Static serving test'));
@@ -55,7 +54,7 @@ class FreeServer {
55
54
  this.app.use(maintenanceMiddleware);
56
55
  this.app.use(securityMiddleware);
57
56
 
58
- this.app.use((req, res, next) => {
57
+ this.app.use((req, res) => {
59
58
  const ip = req.ip || req.headers['x-forwarded-for'] || 'unknown';
60
59
  const reqCount = this.rateLimits.get(ip) || 0;
61
60
 
@@ -66,7 +65,6 @@ class FreeServer {
66
65
  }
67
66
 
68
67
  this.rateLimits.set(ip, reqCount + 1);
69
- next();
70
68
  });
71
69
  }
72
70
 
@@ -98,8 +96,58 @@ class FreeServer {
98
96
 
99
97
  await handler(req, res);
100
98
  } catch (e) {
101
- console.error(`[Free Engine] Error ${method} ${path}:`, e);
102
- res.status(e.status || 500).header('Content-Type', 'text/html').send('Internal Error');
99
+ if (process.env.NODE_ENV === 'production') {
100
+ console.error(`[Free Engine] Error ${method} ${path}:`, e);
101
+ res.status(e.status || 500).header('Content-Type', 'text/html').send('Internal Error');
102
+ } else {
103
+ // Premium Debug Page (Ignition Style)
104
+ const os = require('os');
105
+ const pkg = require('../package.json');
106
+ const debugTplPath = nodePath.join(__dirname, 'views/debug.html');
107
+
108
+ if (fs.existsSync(debugTplPath)) {
109
+ let html = fs.readFileSync(debugTplPath, 'utf8');
110
+
111
+ // Error Details
112
+ html = html.replace('{{errorName}}', e.name || 'Error');
113
+ html = html.replace('{{errorMessage}}', e.message || 'No message provided');
114
+
115
+ // Stack Trace with highlighting
116
+ const stack = (e.stack || '').split('\n').map(line => {
117
+ const isUserFile = line.includes(process.cwd()) && !line.includes('node_modules');
118
+ return `<span class="stack-line ${isUserFile ? 'highlight' : ''}">${line.replace(/&/g, '&amp;').replace(/</g, '&lt;')}</span>`;
119
+ }).join('');
120
+ html = html.replace('{{stackTrace}}', stack);
121
+
122
+ // Request Details
123
+ let reqHtml = `<tr><th>Method</th><td class="val-text">${req.method}</td></tr>`;
124
+ reqHtml += `<tr><th>URL</th><td class="val-text">${req.url}</td></tr>`;
125
+ Object.keys(req.headers).forEach(k => {
126
+ reqHtml += `<tr><th>Header: ${k}</th><td class="val-text">${req.headers[k]}</td></tr>`;
127
+ });
128
+ html = html.replace('{{requestDetails}}', reqHtml);
129
+
130
+ // Env details (Mask secrets)
131
+ let envHtml = '';
132
+ Object.keys(process.env).forEach(k => {
133
+ const isSecret = k.includes('KEY') || k.includes('SECRET') || k.includes('PASS');
134
+ const val = isSecret ? '********' : process.env[k];
135
+ envHtml += `<tr><th>${k}</th><td class="val-text">${val}</td></tr>`;
136
+ });
137
+ html = html.replace('{{envDetails}}', envHtml);
138
+
139
+ // Engine Stats
140
+ html = html.replace('{{version}}', pkg.version);
141
+ html = html.replace('{{nodeVersion}}', process.version);
142
+ html = html.replace('{{osInfo}}', `${os.type()} ${os.release()} (${os.arch()})`);
143
+ const mem = process.memoryUsage();
144
+ html = html.replace('{{memoryUsage}}', `${Math.round(mem.heapUsed / 1024 / 1024)}MB / ${Math.round(mem.heapTotal / 1024 / 1024)}MB`);
145
+
146
+ res.status(500).header('Content-Type', 'text/html').send(html);
147
+ } else {
148
+ res.status(500).send(`<h1>${e.name || 'Error'}</h1><pre>${e.stack}</pre>`);
149
+ }
150
+ }
103
151
  }
104
152
  };
105
153
 
@@ -122,7 +170,9 @@ class FreeServer {
122
170
  start(port = 3000) {
123
171
  this.app.get('/health', (req, res) => res.send('OK'));
124
172
  this.app.listen(port).then(() => {
125
- console.log(`⚡ Free Engine Ignite: http://localhost:${port}`);
173
+ const blueUnderline = "\x1b[34m\x1b[4m";
174
+ const reset = "\x1b[0m";
175
+ console.log(`⚡ Free Engine Ignite: ${blueUnderline}http://localhost:${port}${reset}`);
126
176
  });
127
177
  }
128
178
  }
@@ -0,0 +1,273 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Free Engine | Runtime Exception</title>
8
+ <link
9
+ href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;700;900&family=JetBrains+Mono:wght@400;700&display=swap"
10
+ rel="stylesheet">
11
+ <style>
12
+ :root {
13
+ --primary: #ff3e3e;
14
+ --primary-bg: #1a0b0b;
15
+ --bg: #050505;
16
+ --surface: #121212;
17
+ --text: #ffffff;
18
+ --text-dim: #888888;
19
+ --border: rgba(255, 255, 255, 0.1);
20
+ --code: #00ff88;
21
+ }
22
+
23
+ * {
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ body {
28
+ margin: 0;
29
+ font-family: 'Outfit', sans-serif;
30
+ background: var(--bg);
31
+ color: var(--text);
32
+ line-height: 1.6;
33
+ overflow-x: hidden;
34
+ }
35
+
36
+ header {
37
+ background: var(--primary-bg);
38
+ border-bottom: 2px solid var(--primary);
39
+ padding: 3rem 2rem;
40
+ position: relative;
41
+ overflow: hidden;
42
+ }
43
+
44
+ .glow {
45
+ position: absolute;
46
+ top: -100px;
47
+ right: -100px;
48
+ width: 400px;
49
+ height: 400px;
50
+ background: radial-gradient(circle, rgba(255, 62, 62, 0.2) 0%, transparent 70%);
51
+ z-index: 0;
52
+ pointer-events: none;
53
+ }
54
+
55
+ .container {
56
+ max-width: 1200px;
57
+ margin: 0 auto;
58
+ position: relative;
59
+ z-index: 1;
60
+ }
61
+
62
+ .badge {
63
+ display: inline-block;
64
+ background: var(--primary);
65
+ color: white;
66
+ padding: 0.2rem 0.8rem;
67
+ border-radius: 4px;
68
+ font-weight: 900;
69
+ font-size: 0.7rem;
70
+ letter-spacing: 1px;
71
+ text-transform: uppercase;
72
+ margin-bottom: 1rem;
73
+ }
74
+
75
+ h1 {
76
+ margin: 0;
77
+ font-size: 2.5rem;
78
+ font-weight: 900;
79
+ letter-spacing: -1px;
80
+ }
81
+
82
+ .message {
83
+ font-size: 1.3rem;
84
+ color: var(--text-dim);
85
+ margin-top: 0.5rem;
86
+ }
87
+
88
+ nav {
89
+ background: var(--surface);
90
+ border-bottom: 1px solid var(--border);
91
+ padding: 0;
92
+ display: flex;
93
+ position: sticky;
94
+ top: 0;
95
+ z-index: 100;
96
+ }
97
+
98
+ nav button {
99
+ background: transparent;
100
+ border: none;
101
+ color: var(--text-dim);
102
+ padding: 1rem 2rem;
103
+ font-family: inherit;
104
+ font-weight: 700;
105
+ cursor: pointer;
106
+ transition: 0.3s;
107
+ border-bottom: 2px solid transparent;
108
+ }
109
+
110
+ nav button.active {
111
+ color: var(--primary);
112
+ border-bottom-color: var(--primary);
113
+ background: rgba(255, 62, 62, 0.05);
114
+ }
115
+
116
+ section {
117
+ padding: 2rem;
118
+ display: none;
119
+ }
120
+
121
+ section.active {
122
+ display: block;
123
+ }
124
+
125
+ .stack-trace {
126
+ background: #000;
127
+ border-radius: 8px;
128
+ padding: 1rem;
129
+ overflow-x: auto;
130
+ border: 1px solid var(--border);
131
+ }
132
+
133
+ .stack-trace pre {
134
+ margin: 0;
135
+ font-family: 'JetBrains Mono', monospace;
136
+ font-size: 0.9rem;
137
+ color: #ddd;
138
+ }
139
+
140
+ .stack-line {
141
+ display: block;
142
+ padding: 0.2rem 0;
143
+ border-bottom: 1px solid rgba(255, 255, 255, 0.03);
144
+ }
145
+
146
+ .stack-line.highlight {
147
+ background: rgba(255, 62, 62, 0.1);
148
+ color: var(--primary);
149
+ font-weight: 700;
150
+ }
151
+
152
+ table {
153
+ width: 100%;
154
+ border-collapse: collapse;
155
+ margin-top: 1rem;
156
+ }
157
+
158
+ th,
159
+ td {
160
+ text-align: left;
161
+ padding: 1rem;
162
+ border-bottom: 1px solid var(--border);
163
+ }
164
+
165
+ th {
166
+ color: var(--text-dim);
167
+ width: 200px;
168
+ text-transform: uppercase;
169
+ font-size: 0.75rem;
170
+ letter-spacing: 1px;
171
+ }
172
+
173
+ .val-text {
174
+ font-family: 'JetBrains Mono', monospace;
175
+ color: var(--code);
176
+ }
177
+
178
+ .card {
179
+ background: var(--surface);
180
+ border: 1px solid var(--border);
181
+ border-radius: 12px;
182
+ padding: 2rem;
183
+ margin-bottom: 2rem;
184
+ }
185
+
186
+ h2 {
187
+ font-size: 1.5rem;
188
+ margin-top: 0;
189
+ }
190
+ </style>
191
+ </head>
192
+
193
+ <body>
194
+ <header>
195
+ <div class="glow"></div>
196
+ <div class="container">
197
+ <div class="badge">Runtime Exception</div>
198
+ <h1>{{errorName}}</h1>
199
+ <div class="message">{{errorMessage}}</div>
200
+ </div>
201
+ </header>
202
+
203
+ <nav>
204
+ <button onclick="showTab('stack')" id="btn-stack" class="active">Stack Trace</button>
205
+ <button onclick="showTab('request')" id="btn-request">Request</button>
206
+ <button onclick="showTab('env')" id="btn-env">Environment</button>
207
+ <button onclick="showTab('app')" id="btn-app">Engine</button>
208
+ </nav>
209
+
210
+ <div class="container">
211
+ <section id="tab-stack" class="active">
212
+ <div class="card">
213
+ <h2>Trace Analysis</h2>
214
+ <div class="stack-trace">
215
+ <pre>{{stackTrace}}</pre>
216
+ </div>
217
+ </div>
218
+ </section>
219
+
220
+ <section id="tab-request">
221
+ <div class="card">
222
+ <h2>HTTP Details</h2>
223
+ <table>
224
+ {{requestDetails}}
225
+ </table>
226
+ </div>
227
+ </section>
228
+
229
+ <section id="tab-env">
230
+ <div class="card">
231
+ <h2>Environment Variables</h2>
232
+ <table>
233
+ {{envDetails}}
234
+ </table>
235
+ </div>
236
+ </section>
237
+
238
+ <section id="tab-app">
239
+ <div class="card">
240
+ <h2>Engine Statistics</h2>
241
+ <table>
242
+ <tr>
243
+ <th>Engine</th>
244
+ <td class="val-text">Free Enterprise v{{version}}</td>
245
+ </tr>
246
+ <tr>
247
+ <th>Node Version</th>
248
+ <td class="val-text">{{nodeVersion}}</td>
249
+ </tr>
250
+ <tr>
251
+ <th>OS</th>
252
+ <td class="val-text">{{osInfo}}</td>
253
+ </tr>
254
+ <tr>
255
+ <th>Memory Usage</th>
256
+ <td class="val-text">{{memoryUsage}}</td>
257
+ </tr>
258
+ </table>
259
+ </div>
260
+ </section>
261
+ </div>
262
+
263
+ <script>
264
+ function showTab(id) {
265
+ document.querySelectorAll('section').forEach(s => s.classList.remove('active'));
266
+ document.querySelectorAll('nav button').forEach(b => b.classList.remove('active'));
267
+ document.getElementById('tab-' + id).classList.add('active');
268
+ document.getElementById('btn-' + id).classList.add('active');
269
+ }
270
+ </script>
271
+ </body>
272
+
273
+ </html>
@@ -1,13 +1,17 @@
1
1
  # Free Framework Core Template Configuration
2
2
  # Production-ready defaults for Database, Security, and Routing
3
3
 
4
- # ── Database (MySQL Recommended) ──────────────────────────────────────────
5
- DB_CLIENT=mysql2
6
- DB_HOST=127.0.0.1
7
- DB_PORT=3306
8
- DB_USER=root
9
- DB_PASS=
10
- DB_NAME=free_db_default
4
+ # ── Database (SQLite Default - Change as needed) ───────────────────────────
5
+ DB_CLIENT=sqlite3
6
+ DB_PATH=database/database.sqlite
7
+
8
+ # MySQL (Recommended for Production)
9
+ # DB_CLIENT=mysql2
10
+ # DB_HOST=127.0.0.1
11
+ # DB_PORT=3306
12
+ # DB_USER=root
13
+ # DB_PASS=
14
+ # DB_NAME=free_db_default
11
15
 
12
16
  # Connection Pooling Settings
13
17
  DB_POOL_MIN=5
@@ -14,7 +14,7 @@
14
14
 
15
15
  module.exports = {
16
16
  // The default database connection to use.
17
- default: process.env.DB_CLIENT || 'mysql2',
17
+ default: process.env.DB_CLIENT || 'sqlite3',
18
18
 
19
19
  connections: {
20
20
  // High-performance MySQL connection settings