slicejs-web-framework 3.4.3 → 3.5.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.
Files changed (27) hide show
  1. package/.opencode/opencode.json +14 -14
  2. package/Slice/Components/Structural/ContextManager/ContextManager.js +422 -422
  3. package/Slice/Components/Structural/ContextManager/ContextManagerDebugger.js +468 -468
  4. package/Slice/Components/Structural/Controller/Controller.js +1406 -1198
  5. package/Slice/Components/Structural/Debugger/Debugger.js +1503 -1503
  6. package/Slice/Components/Structural/EventManager/EventManager.js +651 -366
  7. package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +368 -30
  8. package/Slice/Components/Structural/LeakInspector/LeakInspector.js +296 -0
  9. package/Slice/Components/Structural/Logger/Logger.js +225 -225
  10. package/Slice/Components/Structural/Router/Router.js +775 -775
  11. package/Slice/Components/Structural/StylesManager/StylesManager.js +82 -82
  12. package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +106 -106
  13. package/Slice/Slice.js +663 -627
  14. package/Slice/tests/build-singleton.test.js +244 -244
  15. package/Slice/tests/bundle-v2-runtime-contract.test.js +738 -738
  16. package/Slice/tests/context-patch-use.test.js +95 -95
  17. package/Slice/tests/event-emitters.test.js +79 -0
  18. package/Slice/tests/event-registry.test.js +141 -0
  19. package/Slice/tests/fixtures/real-runtime-loader.mjs +21 -21
  20. package/Slice/tests/leak-orphans.test.js +131 -0
  21. package/Slice/tests/public-env-typed-accessors.test.js +66 -66
  22. package/api/index.js +308 -281
  23. package/api/utils/publicEnvResolver.js +123 -123
  24. package/package.json +44 -44
  25. package/sliceConfig.schema.json +10 -1
  26. package/src/sliceConfig.json +5 -1
  27. package/types/index.d.ts +12 -0
package/api/index.js CHANGED
@@ -1,281 +1,308 @@
1
- // api/index.js - Seguridad automática sin configuración
2
- import express from 'slicejs-web-framework/api/framework/express.js';
3
- import path from 'path';
4
- import fs from 'fs';
5
- import { fileURLToPath } from 'url';
6
- import { dirname } from 'path';
7
- import {
8
- securityMiddleware,
9
- sliceFrameworkProtection,
10
- suspiciousRequestLogger
11
- } from './middleware/securityMiddleware.js';
12
- import { createPublicEnvProvider } from './utils/publicEnvResolver.js';
13
-
14
- const __filename = fileURLToPath(import.meta.url);
15
- const __dirname = dirname(__filename);
16
- import sliceConfig from '../src/sliceConfig.json' with { type: 'json' };
17
-
18
- let server;
19
- const app = express();
20
-
21
- // Parsear argumentos de línea de comandos
22
- const args = process.argv.slice(2);
23
-
24
- const runMode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
25
- const folderDeployed = runMode === 'production' ? 'dist' : 'src';
26
- const publicEnvProvider = createPublicEnvProvider({
27
- mode: runMode,
28
- envFilePath: path.join(__dirname, '..', '.env')
29
- });
30
-
31
- // Obtener puerto desde process.env.PORT con fallback a sliceConfig.json
32
- const PORT = process.env.PORT || sliceConfig.server?.port || 3001;
33
-
34
- // ==============================================
35
- // MIDDLEWARES DE SEGURIDAD (APLICAR PRIMERO)
36
- // ==============================================
37
-
38
- // 1. Logger de peticiones sospechosas (solo observación, no bloquea)
39
- app.use(suspiciousRequestLogger());
40
-
41
- // 2. Protección del framework - TOTALMENTE AUTOMÁTICA
42
- // Detecta automáticamente el dominio desde los headers
43
- // Funciona en localhost, IP, y cualquier dominio
44
- app.use(sliceFrameworkProtection());
45
-
46
- // 3. Middleware de seguridad general
47
- app.use(securityMiddleware({
48
- allowedExtensions: [
49
- '.js', '.css', '.html', '.json',
50
- '.svg', '.png', '.jpg', '.jpeg', '.gif',
51
- '.woff', '.woff2', '.ttf', '.ico'
52
- ],
53
- blockedPaths: [
54
- '/node_modules',
55
- '/package.json',
56
- '/package-lock.json',
57
- '/.env',
58
- '/.git',
59
- '/api/middleware'
60
- ],
61
- allowPublicAssets: true
62
- }));
63
-
64
- // ==============================================
65
- // MIDDLEWARES DE APLICACIÓN
66
- // ==============================================
67
-
68
- // Middleware global para archivos JavaScript con MIME types correctos
69
- app.use((req, res, next) => {
70
- if (req.path.endsWith('.js')) {
71
- // Forzar headers correctos para TODOS los archivos .js
72
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
73
- }
74
- next();
75
- });
76
-
77
- // Middleware para parsear JSON y formularios
78
- app.use(express.json());
79
- app.use(express.urlencoded({ extended: true }));
80
-
81
- // Configurar headers de CORS
82
- app.use((req, res, next) => {
83
- res.header('Access-Control-Allow-Origin', '*');
84
- res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
85
- res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
86
-
87
- if (req.method === 'OPTIONS') {
88
- res.sendStatus(200);
89
- } else {
90
- next();
91
- }
92
- });
93
-
94
- // ==============================================
95
- // RUNTIME MODE ENDPOINT
96
- // ==============================================
97
-
98
- app.get('/slice-env.json', (req, res) => {
99
- const payload = publicEnvProvider.getPayload();
100
- res.setHeader('Cache-Control', 'no-store');
101
- res.setHeader('Pragma', 'no-cache');
102
- res.setHeader('Expires', '0');
103
- res.json(payload);
104
- });
105
-
106
- // ==============================================
107
- // ARCHIVOS ESTÁTICOS (DESPUÉS DE SEGURIDAD)
108
- // ==============================================
109
-
110
- if (runMode === 'production') {
111
- app.get('/Slice/Slice.js', (req, res) => {
112
- const slicePath = path.join(__dirname, '..', 'node_modules', 'slicejs-web-framework', 'Slice', 'Slice.js');
113
- try {
114
- if (fs.existsSync(slicePath)) {
115
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
116
- return res.send(fs.readFileSync(slicePath, 'utf8'));
117
- }
118
- } catch (error) {
119
- console.error(`Error reading Slice.js:`, error);
120
- return res.status(500).send('Error loading framework');
121
- }
122
- return res.status(404).send('Slice.js not found');
123
- });
124
-
125
- app.use('/Slice', (req, res) => res.status(404).send('Not found'));
126
- app.use('/Components', (req, res) => res.status(404).send('Not found'));
127
- }
128
-
129
- // Middleware personalizado para archivos de bundles con MIME types correctos
130
- // ⚠️ DEBE IR ANTES del middleware general para tener prioridad
131
- app.use('/bundles/', (req, res, next) => {
132
- // Solo procesar archivos .js
133
- if (req.path.endsWith('.js')) {
134
- const filePath = path.join(__dirname, `../${folderDeployed}`, 'bundles', req.path);
135
- console.log(`📂 Processing bundle: ${req.path} -> ${filePath}`);
136
-
137
- // Verificar que el archivo existe
138
- if (fs.existsSync(filePath)) {
139
- try {
140
- // Leer y servir el archivo con headers correctos
141
- const fileContent = fs.readFileSync(filePath, 'utf8');
142
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
143
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); // No cachear para permitir actualizaciones en tiempo real
144
- res.setHeader('Pragma', 'no-cache');
145
- res.setHeader('Expires', '0');
146
- console.log(`✅ Serving bundle: ${req.path} (${fileContent.length} bytes, ${Buffer.byteLength(fileContent, 'utf8')} bytes UTF-8)`);
147
- return res.send(fileContent);
148
- } catch (error) {
149
- console.error(`Error reading bundle file:`, error);
150
- return res.status(500).send('Error reading bundle file');
151
- }
152
- } else {
153
- console.log(`❌ Bundle file not found: ${filePath}`);
154
- return res.status(404).send('Bundle file not found');
155
- }
156
- }
157
-
158
- // Para archivos no .js, continuar con el middleware estático normal
159
- next();
160
- });
161
-
162
- // Servir otros archivos de bundles (JSON, CSS, etc.) con el middleware estático normal
163
- app.use('/bundles/', express.static(path.join(__dirname, `../${folderDeployed}`, 'bundles')));
164
- console.log(`📦 Serving bundles from /${folderDeployed}/bundles`);
165
-
166
- // Servir framework Slice.js (solo development)
167
- if (runMode === 'development') {
168
- app.use('/Slice/', express.static(path.join(__dirname, '..', 'node_modules', 'slicejs-web-framework', 'Slice')));
169
- }
170
-
171
- // Servir archivos estáticos del proyecto con allowlist
172
- const publicFolders = Array.isArray(sliceConfig.publicFolders) ? sliceConfig.publicFolders : [];
173
- const normalizedPublicFolders = publicFolders
174
- .filter((entry) => typeof entry === 'string')
175
- .map((entry) => entry.trim())
176
- .filter((entry) => entry.length > 0)
177
- .map((entry) => (entry.startsWith('/') ? entry : `/${entry}`));
178
-
179
- if (runMode === 'development') {
180
- app.use(express.static(path.join(__dirname, `../${folderDeployed}`)));
181
- } else {
182
- app.use('/App', express.static(path.join(__dirname, `../${folderDeployed}`, 'App')));
183
- const serveStaticFile = (req, res, filePath, contentType, fileName) => {
184
- try {
185
- if (fs.existsSync(filePath)) {
186
- res.setHeader('Content-Type', contentType);
187
- return res.send(fs.readFileSync(filePath, 'utf8'));
188
- }
189
- } catch (error) {
190
- console.error(`Error reading ${fileName}:`, error);
191
- return res.status(500).send(`Error reading ${fileName}`);
192
- }
193
- return res.status(404).send(`${fileName} not found`);
194
- };
195
-
196
- app.get('/manifest.json', (req, res) => {
197
- const manifestPath = path.join(__dirname, `../${folderDeployed}`, 'manifest.json');
198
- serveStaticFile(req, res, manifestPath, 'application/json; charset=utf-8', 'manifest.json');
199
- });
200
- app.get('/service-worker.js', (req, res) => {
201
- const workerPath = path.join(__dirname, `../${folderDeployed}`, 'service-worker.js');
202
- serveStaticFile(req, res, workerPath, 'application/javascript; charset=utf-8', 'service-worker.js');
203
- });
204
- app.get('/routes.js', (req, res) => {
205
- const routesPath = path.join(__dirname, `../${folderDeployed}`, 'routes.js');
206
- serveStaticFile(req, res, routesPath, 'application/javascript; charset=utf-8', 'routes.js');
207
- });
208
- app.get('/sliceConfig.json', (req, res) => {
209
- const configPath = path.join(__dirname, `../${folderDeployed}`, 'sliceConfig.json');
210
- serveStaticFile(req, res, configPath, 'application/json; charset=utf-8', 'sliceConfig.json');
211
- });
212
- for (const folder of normalizedPublicFolders) {
213
- app.use(folder, express.static(path.join(__dirname, `../${folderDeployed}`, folder)));
214
- }
215
- app.use('/bundles/', express.static(path.join(__dirname, `../${folderDeployed}`, 'bundles')));
216
- app.use('/dist/', express.static(path.join(__dirname, '..', 'dist')));
217
- }
218
-
219
- // ==============================================
220
- // RUTAS DE API
221
- // ==============================================
222
-
223
- // Ruta de ejemplo para API
224
- app.get('/api/status', (req, res) => {
225
- res.json({
226
- status: 'ok',
227
- mode: runMode,
228
- folder: folderDeployed,
229
- timestamp: new Date().toISOString(),
230
- framework: 'Slice.js',
231
- version: '2.0.0',
232
- security: {
233
- enabled: true,
234
- mode: 'automatic',
235
- description: 'Zero-config security - works with any domain'
236
- }
237
- });
238
- });
239
-
240
-
241
- // ==============================================
242
- // SPA FALLBACK
243
- // ==============================================
244
-
245
- // SPA fallback - servir index.html para rutas no encontradas
246
- app.get('*', (req, res) => {
247
- const indexPath = path.join(__dirname, `../${folderDeployed}`, "App", 'index.html');
248
- res.sendFile(indexPath, (err) => {
249
- if (err) {
250
- console.error(`[SPA Fallback] Error sending ${indexPath}:`, err);
251
- res.status(500).send(`<h1>500 - Internal Server Error</h1><p>Could not serve application file</p>`);
252
- }
253
- });
254
- });
255
-
256
- // ==============================================
257
- // INICIO DEL SERVIDOR
258
- // ==============================================
259
-
260
- function startServer() {
261
- server = app.listen(PORT, () => {
262
- console.log(`🔒 Security middleware: active (zero-config, automatic)`);
263
- console.log(`🚀 Slice.js server running on port ${PORT}`);
264
- });
265
- }
266
-
267
- // Manejar cierre del proceso
268
- process.on('SIGINT', () => {
269
- console.log('\n🛑 Slice server stopped');
270
- process.exit(0);
271
- });
272
-
273
- process.on('SIGTERM', () => {
274
- console.log('\n🛑 Server terminated');
275
- process.exit(0);
276
- });
277
-
278
- // Iniciar servidor
279
- startServer();
280
-
281
- export default app;
1
+ // api/index.js - Seguridad automática sin configuración
2
+ import express from 'slicejs-web-framework/api/framework/express.js';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname } from 'path';
7
+ import {
8
+ securityMiddleware,
9
+ sliceFrameworkProtection,
10
+ suspiciousRequestLogger
11
+ } from './middleware/securityMiddleware.js';
12
+ import { createPublicEnvProvider } from './utils/publicEnvResolver.js';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+ import sliceConfig from '../src/sliceConfig.json' with { type: 'json' };
17
+
18
+ let server;
19
+ const app = express();
20
+
21
+ // Parsear argumentos de línea de comandos
22
+ const args = process.argv.slice(2);
23
+
24
+ const runMode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
25
+ const folderDeployed = runMode === 'production' ? 'dist' : 'src';
26
+ const publicEnvProvider = createPublicEnvProvider({
27
+ mode: runMode,
28
+ envFilePath: path.join(__dirname, '..', '.env')
29
+ });
30
+
31
+ // Obtener puerto desde process.env.PORT con fallback a sliceConfig.json
32
+ const PORT = process.env.PORT || sliceConfig.server?.port || 3001;
33
+
34
+ // ==============================================
35
+ // MIDDLEWARES DE SEGURIDAD (APLICAR PRIMERO)
36
+ // ==============================================
37
+
38
+ // 1. Logger de peticiones sospechosas (solo observación, no bloquea)
39
+ app.use(suspiciousRequestLogger());
40
+
41
+ // 2. Protección del framework - TOTALMENTE AUTOMÁTICA
42
+ // Detecta automáticamente el dominio desde los headers
43
+ // Funciona en localhost, IP, y cualquier dominio
44
+ app.use(sliceFrameworkProtection());
45
+
46
+ // 3. Middleware de seguridad general
47
+ app.use(securityMiddleware({
48
+ allowedExtensions: [
49
+ '.js', '.css', '.html', '.json',
50
+ '.svg', '.png', '.jpg', '.jpeg', '.gif',
51
+ '.woff', '.woff2', '.ttf', '.ico'
52
+ ],
53
+ blockedPaths: [
54
+ '/node_modules',
55
+ '/package.json',
56
+ '/package-lock.json',
57
+ '/.env',
58
+ '/.git',
59
+ '/api/middleware'
60
+ ],
61
+ allowPublicAssets: true
62
+ }));
63
+
64
+ // ==============================================
65
+ // MIDDLEWARES DE APLICACIÓN
66
+ // ==============================================
67
+
68
+ // Middleware global para archivos JavaScript con MIME types correctos
69
+ app.use((req, res, next) => {
70
+ if (req.path.endsWith('.js')) {
71
+ // Forzar headers correctos para TODOS los archivos .js
72
+ res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
73
+ }
74
+ next();
75
+ });
76
+
77
+ // Middleware para parsear JSON y formularios
78
+ app.use(express.json());
79
+ app.use(express.urlencoded({ extended: true }));
80
+
81
+ // Configurar headers de CORS
82
+ app.use((req, res, next) => {
83
+ res.header('Access-Control-Allow-Origin', '*');
84
+ res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
85
+ res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
86
+
87
+ if (req.method === 'OPTIONS') {
88
+ res.sendStatus(200);
89
+ } else {
90
+ next();
91
+ }
92
+ });
93
+
94
+ // ==============================================
95
+ // RUNTIME MODE ENDPOINT
96
+ // ==============================================
97
+
98
+ app.get('/slice-env.json', (req, res) => {
99
+ const payload = publicEnvProvider.getPayload();
100
+ res.setHeader('Cache-Control', 'no-store');
101
+ res.setHeader('Pragma', 'no-cache');
102
+ res.setHeader('Expires', '0');
103
+ res.json(payload);
104
+ });
105
+
106
+ // ==============================================
107
+ // PWA — manifest + service worker (dev y prod)
108
+ // ==============================================
109
+ // Despacha los archivos PWA desde la carpeta desplegada (src en dev, dist en
110
+ // prod) si existen; 404 si no, así no estorba cuando la app no es PWA.
111
+ // El service worker se sirve SIN caché (para que sus actualizaciones lleguen) y
112
+ // con Service-Worker-Allowed:/ para permitir scope raíz.
113
+ const servePwaFile = (res, fileName, contentType, extraHeaders = {}) => {
114
+ const filePath = path.join(__dirname, `../${folderDeployed}`, fileName);
115
+ try {
116
+ if (fs.existsSync(filePath)) {
117
+ res.setHeader('Content-Type', contentType);
118
+ for (const [key, value] of Object.entries(extraHeaders)) res.setHeader(key, value);
119
+ return res.send(fs.readFileSync(filePath, 'utf8'));
120
+ }
121
+ } catch (error) {
122
+ console.error(`Error reading ${fileName}:`, error);
123
+ return res.status(500).send(`Error reading ${fileName}`);
124
+ }
125
+ return res.status(404).send(`${fileName} not found`);
126
+ };
127
+
128
+ app.get('/service-worker.js', (req, res) => {
129
+ servePwaFile(res, 'service-worker.js', 'application/javascript; charset=utf-8', {
130
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
131
+ 'Service-Worker-Allowed': '/',
132
+ });
133
+ });
134
+
135
+ app.get('/manifest.json', (req, res) => {
136
+ servePwaFile(res, 'manifest.json', 'application/manifest+json; charset=utf-8');
137
+ });
138
+
139
+ // ==============================================
140
+ // ARCHIVOS ESTÁTICOS (DESPUÉS DE SEGURIDAD)
141
+ // ==============================================
142
+
143
+ if (runMode === 'production') {
144
+ app.get('/Slice/Slice.js', (req, res) => {
145
+ const slicePath = path.join(__dirname, '..', 'node_modules', 'slicejs-web-framework', 'Slice', 'Slice.js');
146
+ try {
147
+ if (fs.existsSync(slicePath)) {
148
+ res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
149
+ return res.send(fs.readFileSync(slicePath, 'utf8'));
150
+ }
151
+ } catch (error) {
152
+ console.error(`Error reading Slice.js:`, error);
153
+ return res.status(500).send('Error loading framework');
154
+ }
155
+ return res.status(404).send('Slice.js not found');
156
+ });
157
+
158
+ app.use('/Slice', (req, res) => res.status(404).send('Not found'));
159
+ app.use('/Components', (req, res) => res.status(404).send('Not found'));
160
+ }
161
+
162
+ // Middleware personalizado para archivos de bundles con MIME types correctos
163
+ // ⚠️ DEBE IR ANTES del middleware general para tener prioridad
164
+ app.use('/bundles/', (req, res, next) => {
165
+ // Solo procesar archivos .js
166
+ if (req.path.endsWith('.js')) {
167
+ const filePath = path.join(__dirname, `../${folderDeployed}`, 'bundles', req.path);
168
+ console.log(`📂 Processing bundle: ${req.path} -> ${filePath}`);
169
+
170
+ // Verificar que el archivo existe
171
+ if (fs.existsSync(filePath)) {
172
+ try {
173
+ // Leer y servir el archivo con headers correctos
174
+ const fileContent = fs.readFileSync(filePath, 'utf8');
175
+ res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
176
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); // No cachear para permitir actualizaciones en tiempo real
177
+ res.setHeader('Pragma', 'no-cache');
178
+ res.setHeader('Expires', '0');
179
+ console.log(`✅ Serving bundle: ${req.path} (${fileContent.length} bytes, ${Buffer.byteLength(fileContent, 'utf8')} bytes UTF-8)`);
180
+ return res.send(fileContent);
181
+ } catch (error) {
182
+ console.error(`Error reading bundle file:`, error);
183
+ return res.status(500).send('Error reading bundle file');
184
+ }
185
+ } else {
186
+ console.log(`❌ Bundle file not found: ${filePath}`);
187
+ return res.status(404).send('Bundle file not found');
188
+ }
189
+ }
190
+
191
+ // Para archivos no .js, continuar con el middleware estático normal
192
+ next();
193
+ });
194
+
195
+ // Servir otros archivos de bundles (JSON, CSS, etc.) con el middleware estático normal
196
+ app.use('/bundles/', express.static(path.join(__dirname, `../${folderDeployed}`, 'bundles')));
197
+ console.log(`📦 Serving bundles from /${folderDeployed}/bundles`);
198
+
199
+ // Servir framework Slice.js (solo development)
200
+ if (runMode === 'development') {
201
+ app.use('/Slice/', express.static(path.join(__dirname, '..', 'node_modules', 'slicejs-web-framework', 'Slice')));
202
+ }
203
+
204
+ // Servir archivos estáticos del proyecto con allowlist
205
+ const publicFolders = Array.isArray(sliceConfig.publicFolders) ? sliceConfig.publicFolders : [];
206
+ const normalizedPublicFolders = publicFolders
207
+ .filter((entry) => typeof entry === 'string')
208
+ .map((entry) => entry.trim())
209
+ .filter((entry) => entry.length > 0)
210
+ .map((entry) => (entry.startsWith('/') ? entry : `/${entry}`));
211
+
212
+ if (runMode === 'development') {
213
+ app.use(express.static(path.join(__dirname, `../${folderDeployed}`)));
214
+ } else {
215
+ app.use('/App', express.static(path.join(__dirname, `../${folderDeployed}`, 'App')));
216
+ const serveStaticFile = (req, res, filePath, contentType, fileName) => {
217
+ try {
218
+ if (fs.existsSync(filePath)) {
219
+ res.setHeader('Content-Type', contentType);
220
+ return res.send(fs.readFileSync(filePath, 'utf8'));
221
+ }
222
+ } catch (error) {
223
+ console.error(`Error reading ${fileName}:`, error);
224
+ return res.status(500).send(`Error reading ${fileName}`);
225
+ }
226
+ return res.status(404).send(`${fileName} not found`);
227
+ };
228
+
229
+ // (manifest.json y service-worker.js los sirve el bloque PWA compartido de arriba,
230
+ // con headers correctos en dev y prod.)
231
+ app.get('/routes.js', (req, res) => {
232
+ const routesPath = path.join(__dirname, `../${folderDeployed}`, 'routes.js');
233
+ serveStaticFile(req, res, routesPath, 'application/javascript; charset=utf-8', 'routes.js');
234
+ });
235
+ app.get('/sliceConfig.json', (req, res) => {
236
+ const configPath = path.join(__dirname, `../${folderDeployed}`, 'sliceConfig.json');
237
+ serveStaticFile(req, res, configPath, 'application/json; charset=utf-8', 'sliceConfig.json');
238
+ });
239
+ for (const folder of normalizedPublicFolders) {
240
+ app.use(folder, express.static(path.join(__dirname, `../${folderDeployed}`, folder)));
241
+ }
242
+ app.use('/bundles/', express.static(path.join(__dirname, `../${folderDeployed}`, 'bundles')));
243
+ app.use('/dist/', express.static(path.join(__dirname, '..', 'dist')));
244
+ }
245
+
246
+ // ==============================================
247
+ // RUTAS DE API
248
+ // ==============================================
249
+
250
+ // Ruta de ejemplo para API
251
+ app.get('/api/status', (req, res) => {
252
+ res.json({
253
+ status: 'ok',
254
+ mode: runMode,
255
+ folder: folderDeployed,
256
+ timestamp: new Date().toISOString(),
257
+ framework: 'Slice.js',
258
+ version: '2.0.0',
259
+ security: {
260
+ enabled: true,
261
+ mode: 'automatic',
262
+ description: 'Zero-config security - works with any domain'
263
+ }
264
+ });
265
+ });
266
+
267
+
268
+ // ==============================================
269
+ // SPA FALLBACK
270
+ // ==============================================
271
+
272
+ // SPA fallback - servir index.html para rutas no encontradas
273
+ app.get('*', (req, res) => {
274
+ const indexPath = path.join(__dirname, `../${folderDeployed}`, "App", 'index.html');
275
+ res.sendFile(indexPath, (err) => {
276
+ if (err) {
277
+ console.error(`[SPA Fallback] Error sending ${indexPath}:`, err);
278
+ res.status(500).send(`<h1>500 - Internal Server Error</h1><p>Could not serve application file</p>`);
279
+ }
280
+ });
281
+ });
282
+
283
+ // ==============================================
284
+ // INICIO DEL SERVIDOR
285
+ // ==============================================
286
+
287
+ function startServer() {
288
+ server = app.listen(PORT, () => {
289
+ console.log(`🔒 Security middleware: active (zero-config, automatic)`);
290
+ console.log(`🚀 Slice.js server running on port ${PORT}`);
291
+ });
292
+ }
293
+
294
+ // Manejar cierre del proceso
295
+ process.on('SIGINT', () => {
296
+ console.log('\n🛑 Slice server stopped');
297
+ process.exit(0);
298
+ });
299
+
300
+ process.on('SIGTERM', () => {
301
+ console.log('\n🛑 Server terminated');
302
+ process.exit(0);
303
+ });
304
+
305
+ // Iniciar servidor
306
+ startServer();
307
+
308
+ export default app;