jerkjs 2.5.8 → 2.6.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 (36) hide show
  1. package/CHANGELOG.md +162 -99
  2. package/README.md +113 -190
  3. package/RESULTADOS_WAF.md +63 -0
  4. package/doc-2.5/MANUAL_MODULOS_ADMIN.md +287 -0
  5. package/doc-2.5/QUEUE_CLI_MODULE_MANUAL.md +289 -0
  6. package/doc-2.5/QUEUE_SYSTEM_MANUAL.md +320 -0
  7. package/doc-2.5/ROUTE_CACHE_MODULE_MANUAL.md +205 -0
  8. package/doc-2.5/WAF_MODULE_MANUAL.md +229 -0
  9. package/index.js +12 -3
  10. package/jerk-admin-client/README.md +69 -0
  11. package/jerk-admin-client/package.json +23 -0
  12. package/jerk-admin-client.js +257 -0
  13. package/lib/admin/AdminExtension.js +74 -19
  14. package/lib/admin/modules/ControllerGeneratorModule.js +414 -0
  15. package/lib/admin/modules/QueueManagementModule.js +265 -0
  16. package/lib/admin/modules/RouteCacheModule.js +227 -0
  17. package/lib/admin/modules/RouteManagerModule.js +468 -0
  18. package/lib/admin/modules/STATS_MODULE_README.md +15 -0
  19. package/lib/admin/modules/ViewCacheStatsModule.js +92 -0
  20. package/lib/admin/modules/WAFModule.js +737 -0
  21. package/lib/core/server.js +72 -69
  22. package/lib/middleware/firewall.js +112 -17
  23. package/lib/mvc/viewEngine.js +69 -10
  24. package/lib/queue/GlobalQueueStorage.js +38 -0
  25. package/lib/queue/QueueSystem.js +451 -0
  26. package/lib/queue/admin_example.js +114 -0
  27. package/lib/queue/example.js +268 -0
  28. package/lib/queue/integration.js +109 -0
  29. package/lib/utils/globalViewCacheInfo.js +16 -0
  30. package/lib/utils/globalWAFStats.js +54 -0
  31. package/package.json +2 -2
  32. package/test-colors.js +46 -0
  33. package/test-help-alias.js +31 -0
  34. package/ESTADISTICAS_RENDIMIENTO.md +0 -106
  35. package/debug_hook.js +0 -11
  36. package/docs/CACHE_SYSTEM_MAP.md +0 -206
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Módulo de Gestión de Rutas para la Extensión de Administración de JERK Framework
3
+ * Módulo personalizado para crear y deshabilitar rutas de manera interactiva
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ class RouteManagerModule {
10
+ /**
11
+ * Constructor del módulo de gestión de rutas
12
+ * @param {Object} adminExtension - Instancia de la extensión de administración
13
+ */
14
+ constructor(adminExtension) {
15
+ this.adminExtension = adminExtension;
16
+ this.name = 'Route Manager Module';
17
+ this.description = 'Módulo para crear y deshabilitar rutas de manera interactiva';
18
+ this.commands = ['manage-routes', 'route-manager', 'route-mgmt'];
19
+
20
+ // Estado para el proceso interactivo
21
+ this.currentProcess = new Map(); // Almacena el estado de gestión por socket
22
+ }
23
+
24
+ /**
25
+ * Manejador para los comandos del módulo de gestión de rutas
26
+ * @param {string} command - Comando a ejecutar
27
+ * @param {Object} socket - Socket de la conexión
28
+ */
29
+ handleCommand(command, socket) {
30
+ switch (command) {
31
+ case 'manage-routes':
32
+ case 'route-manager':
33
+ case 'route-mgmt':
34
+ this.startRouteManagement(socket);
35
+ break;
36
+ default:
37
+ socket.write(`Comando desconocido para el módulo de gestión de rutas: ${command}\n`);
38
+ socket.write(`Comandos disponibles: manage-routes, route-manager, route-mgmt\n\n`);
39
+ socket.write(`> `);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Inicia el proceso interactivo de gestión de rutas
45
+ * @param {Object} socket - Socket de la conexión
46
+ */
47
+ startRouteManagement(socket) {
48
+ const sessionId = this.getSessionId(socket);
49
+
50
+ // Inicializar el estado de la sesión
51
+ this.currentProcess.set(sessionId, {
52
+ step: 'action',
53
+ routeData: {}
54
+ });
55
+
56
+ socket.write('\n=== Gestión de Rutas ===\n');
57
+ socket.write('¿Qué deseas hacer?\n');
58
+ socket.write('1. Crear nueva ruta\n');
59
+ socket.write('2. Deshabilitar ruta existente\n');
60
+ socket.write('3. Ver rutas registradas\n');
61
+ socket.write('4. Ver rutas activas\n');
62
+ socket.write('\nEscribe el número de la opción (1-4) o "cancel" para cancelar:\n> ');
63
+ }
64
+
65
+ /**
66
+ * Obtiene un ID único para la sesión del socket
67
+ * @param {Object} socket - Socket de la conexión
68
+ * @returns {string} - ID de sesión
69
+ */
70
+ getSessionId(socket) {
71
+ // Usar la dirección remota y puerto como identificador único
72
+ return `${socket.remoteAddress}:${socket.remotePort}`;
73
+ }
74
+
75
+ /**
76
+ * Procesa la entrada del usuario durante el proceso interactivo
77
+ * @param {Object} socket - Socket de la conexión
78
+ * @param {string} input - Entrada del usuario
79
+ */
80
+ processInput(socket, input) {
81
+ const sessionId = this.getSessionId(socket);
82
+ const processState = this.currentProcess.get(sessionId);
83
+
84
+ if (!processState) {
85
+ socket.write('No hay proceso activo. Usa "manage-routes" para iniciar.\n> ');
86
+ return;
87
+ }
88
+
89
+ const userInput = input.trim();
90
+
91
+ if (userInput.toLowerCase() === 'cancel') {
92
+ this.cancelProcess(socket, sessionId);
93
+ return;
94
+ }
95
+
96
+ switch (processState.step) {
97
+ case 'action':
98
+ this.handleActionSelection(socket, sessionId, userInput);
99
+ break;
100
+ case 'create_method':
101
+ this.handleCreateMethod(socket, sessionId, userInput);
102
+ break;
103
+ case 'create_path':
104
+ this.handleCreatePath(socket, sessionId, userInput);
105
+ break;
106
+ case 'create_controller':
107
+ this.handleCreateController(socket, sessionId, userInput);
108
+ break;
109
+ case 'create_handler':
110
+ this.handleCreateHandler(socket, sessionId, userInput);
111
+ break;
112
+ case 'create_confirm':
113
+ this.handleCreateConfirmation(socket, sessionId, userInput);
114
+ break;
115
+ case 'disable_select':
116
+ this.handleDisableSelection(socket, sessionId, userInput);
117
+ break;
118
+ case 'disable_confirm':
119
+ this.handleDisableConfirmation(socket, sessionId, userInput);
120
+ break;
121
+ case 'view_routes':
122
+ if (userInput.toLowerCase() === 'sí' || userInput.toLowerCase() === 'si' || userInput.toLowerCase() === 'yes') {
123
+ this.showRegisteredRoutes(socket);
124
+ } else {
125
+ socket.write('\nVolviendo al menú principal...\n> ');
126
+ this.resetProcess(socket);
127
+ }
128
+ break;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Maneja la selección de acción (crear, deshabilitar, ver rutas)
134
+ * @param {Object} socket - Socket de la conexión
135
+ * @param {string} sessionId - ID de la sesión
136
+ * @param {string} selection - Selección del usuario
137
+ */
138
+ handleActionSelection(socket, sessionId, selection) {
139
+ const processState = this.currentProcess.get(sessionId);
140
+
141
+ switch (selection) {
142
+ case '1':
143
+ processState.step = 'create_method';
144
+ processState.action = 'create';
145
+ socket.write('\n=== Crear Nueva Ruta ===\n');
146
+ socket.write('Selecciona el método HTTP:\n');
147
+ socket.write('1. GET\n2. POST\n3. PUT\n4. DELETE\n5. PATCH\n6. Otro\n');
148
+ socket.write('Escribe el número o el método directamente (GET, POST, etc.):\n> ');
149
+ break;
150
+ case '2':
151
+ processState.step = 'disable_select';
152
+ processState.action = 'disable';
153
+ this.listAvailableRoutes(socket);
154
+ break;
155
+ case '3':
156
+ this.showRegisteredRoutes(socket);
157
+ this.resetProcess(socket);
158
+ break;
159
+ case '4':
160
+ this.showActiveRoutes(socket);
161
+ this.resetProcess(socket);
162
+ break;
163
+ default:
164
+ socket.write('Opción inválida. Por favor, selecciona 1, 2, 3 o 4:\n> ');
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Muestra las rutas registradas
170
+ * @param {Object} socket - Socket de la conexión
171
+ */
172
+ showRegisteredRoutes(socket) {
173
+ if (this.adminExtension.registeredRoutes && this.adminExtension.registeredRoutes.length > 0) {
174
+ socket.write('\n=== Rutas Registradas ===\n');
175
+ this.adminExtension.registeredRoutes.forEach((route, index) => {
176
+ socket.write(`${index + 1}. ${route.method} ${route.path}\n`);
177
+ });
178
+ } else {
179
+ socket.write('\n=== Rutas Registradas ===\n');
180
+ socket.write('No hay rutas registradas.\n');
181
+ }
182
+ socket.write('\n> ');
183
+ }
184
+
185
+ /**
186
+ * Muestra las rutas activas
187
+ * @param {Object} socket - Socket de la conexión
188
+ */
189
+ showActiveRoutes(socket) {
190
+ if (this.adminExtension.activeRoutes && this.adminExtension.activeRoutes.size > 0) {
191
+ socket.write('\n=== Rutas Activas ===\n');
192
+ for (const [url, info] of this.adminExtension.activeRoutes) {
193
+ socket.write(`${info.method} ${url} - ${info.timestamp} (${info.remoteAddress})\n`);
194
+ }
195
+ } else {
196
+ socket.write('\n=== Rutas Activas ===\n');
197
+ socket.write('No hay rutas activas registradas.\n');
198
+ }
199
+ socket.write('\n> ');
200
+ }
201
+
202
+ /**
203
+ * Lista las rutas disponibles para deshabilitar
204
+ * @param {Object} socket - Socket de la conexión
205
+ */
206
+ listAvailableRoutes(socket) {
207
+ if (this.adminExtension.registeredRoutes && this.adminExtension.registeredRoutes.length > 0) {
208
+ socket.write('\n=== Rutas Disponibles para Deshabilitar ===\n');
209
+ this.adminExtension.registeredRoutes.forEach((route, index) => {
210
+ socket.write(`${index + 1}. ${route.method} ${route.path}\n`);
211
+ });
212
+ socket.write('\nSelecciona el número de la ruta que deseas deshabilitar:\n> ');
213
+ } else {
214
+ socket.write('\nNo hay rutas registradas para deshabilitar.\n');
215
+ socket.write('Volviendo al menú principal...\n> ');
216
+ this.resetProcess(socket);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Maneja la selección del método HTTP para crear ruta
222
+ * @param {Object} socket - Socket de la conexión
223
+ * @param {string} sessionId - ID de la sesión
224
+ * @param {string} method - Método HTTP seleccionado
225
+ */
226
+ handleCreateMethod(socket, sessionId, method) {
227
+ const processState = this.currentProcess.get(sessionId);
228
+ const methodMap = {
229
+ '1': 'GET', '2': 'POST', '3': 'PUT', '4': 'DELETE', '5': 'PATCH', '6': 'OTHER'
230
+ };
231
+
232
+ let selectedMethod;
233
+ if (methodMap[method]) {
234
+ selectedMethod = methodMap[method];
235
+ if (selectedMethod === 'OTHER') {
236
+ socket.write('Escribe el método HTTP personalizado:\n> ');
237
+ return;
238
+ }
239
+ } else {
240
+ selectedMethod = method.toUpperCase();
241
+ }
242
+
243
+ processState.routeData.method = selectedMethod;
244
+ processState.step = 'create_path';
245
+
246
+ socket.write(`\nMétodo seleccionado: ${selectedMethod}\n`);
247
+ socket.write('Introduce la ruta (ej: /api/users, /users/:id):\n> ');
248
+ }
249
+
250
+ /**
251
+ * Maneja la introducción de la ruta
252
+ * @param {Object} socket - Socket de la conexión
253
+ * @param {string} sessionId - ID de la sesión
254
+ * @param {string} routePath - Ruta introducida
255
+ */
256
+ handleCreatePath(socket, sessionId, routePath) {
257
+ const processState = this.currentProcess.get(sessionId);
258
+
259
+ if (!routePath || !routePath.startsWith('/')) {
260
+ socket.write('La ruta debe comenzar con "/". Por favor, introduce una ruta válida:\n> ');
261
+ return;
262
+ }
263
+
264
+ processState.routeData.path = routePath;
265
+ processState.step = 'create_controller';
266
+
267
+ socket.write(`\nRuta: ${routePath}\n`);
268
+ socket.write('Introduce la ruta al archivo del controlador (ej: ./controllers/UserController.js):\n> ');
269
+ }
270
+
271
+ /**
272
+ * Maneja la introducción del controlador
273
+ * @param {Object} socket - Socket de la conexión
274
+ * @param {string} sessionId - ID de la sesión
275
+ * @param {string} controller - Ruta al controlador
276
+ */
277
+ handleCreateController(socket, sessionId, controller) {
278
+ const processState = this.currentProcess.get(sessionId);
279
+
280
+ if (!controller) {
281
+ socket.write('La ruta al controlador no puede estar vacía. Por favor, introduce una ruta válida:\n> ');
282
+ return;
283
+ }
284
+
285
+ processState.routeData.controller = controller;
286
+ processState.step = 'create_handler';
287
+
288
+ socket.write(`\nControlador: ${controller}\n`);
289
+ socket.write('Introduce el nombre del método del controlador (ej: index, show, create):\n> ');
290
+ }
291
+
292
+ /**
293
+ * Maneja la introducción del handler
294
+ * @param {Object} socket - Socket de la conexión
295
+ * @param {string} sessionId - ID de la sesión
296
+ * @param {string} handler - Nombre del handler
297
+ */
298
+ handleCreateHandler(socket, sessionId, handler) {
299
+ const processState = this.currentProcess.get(sessionId);
300
+
301
+ if (!handler) {
302
+ socket.write('El nombre del handler no puede estar vacío. Por favor, introduce un nombre válido:\n> ');
303
+ return;
304
+ }
305
+
306
+ processState.routeData.handler = handler;
307
+ processState.step = 'create_confirm';
308
+
309
+ socket.write(`\nHandler: ${handler}\n`);
310
+ this.showRoutePreview(socket, processState.routeData);
311
+ }
312
+
313
+ /**
314
+ * Muestra una vista previa de la ruta que se va a crear
315
+ * @param {Object} socket - Socket de la conexión
316
+ * @param {Object} routeData - Datos de la ruta
317
+ */
318
+ showRoutePreview(socket, routeData) {
319
+ socket.write('\n=== Vista Previa de la Ruta ===\n');
320
+ socket.write(`Método: ${routeData.method}\n`);
321
+ socket.write(`Ruta: ${routeData.path}\n`);
322
+ socket.write(`Controlador: ${routeData.controller}\n`);
323
+ socket.write(`Handler: ${routeData.handler}\n\n`);
324
+
325
+ const routeEntry = {
326
+ path: routeData.path,
327
+ method: routeData.method,
328
+ controller: routeData.controller,
329
+ handler: routeData.handler,
330
+ contentType: "application/json"
331
+ };
332
+
333
+ socket.write('Entrada para routes.json:\n');
334
+ socket.write(JSON.stringify(routeEntry, null, 2) + '\n\n');
335
+
336
+ socket.write('¿Deseas crear esta ruta? (sí/no):\n> ');
337
+ }
338
+
339
+ /**
340
+ * Maneja la confirmación de creación de ruta
341
+ * @param {Object} socket - Socket de la conexión
342
+ * @param {string} sessionId - ID de la sesión
343
+ * @param {string} confirmation - Confirmación del usuario
344
+ */
345
+ handleCreateConfirmation(socket, sessionId, confirmation) {
346
+ const processState = this.currentProcess.get(sessionId);
347
+
348
+ if (confirmation.toLowerCase() === 'sí' || confirmation.toLowerCase() === 'si' || confirmation.toLowerCase() === 'yes') {
349
+ this.createRoute(socket, processState.routeData);
350
+ } else {
351
+ socket.write('\nCreación de ruta cancelada.\n> ');
352
+ this.resetProcess(socket);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Crea la ruta en el sistema
358
+ * @param {Object} socket - Socket de la conexión
359
+ * @param {Object} routeData - Datos de la ruta
360
+ */
361
+ createRoute(socket, routeData) {
362
+ // En un entorno real, aquí se agregaría la ruta al servidor
363
+ // Por ahora, solo mostramos un mensaje de confirmación
364
+
365
+ socket.write(`\n✅ Ruta "${routeData.method} ${routeData.path}" creada exitosamente.\n`);
366
+ socket.write(`- Controlador: ${routeData.controller}\n`);
367
+ socket.write(`- Handler: ${routeData.handler}\n`);
368
+
369
+ // Agregar a las rutas registradas
370
+ if (this.adminExtension.registeredRoutes) {
371
+ const newRoute = {
372
+ method: routeData.method,
373
+ path: routeData.path,
374
+ controller: routeData.controller,
375
+ handler: routeData.handler
376
+ };
377
+ this.adminExtension.registeredRoutes.push(newRoute);
378
+ }
379
+
380
+ socket.write('\n> ');
381
+ this.resetProcess(socket);
382
+ }
383
+
384
+ /**
385
+ * Maneja la selección de ruta para deshabilitar
386
+ * @param {Object} socket - Socket de la conexión
387
+ * @param {string} sessionId - ID de la sesión
388
+ * @param {string} selection - Selección del usuario
389
+ */
390
+ handleDisableSelection(socket, sessionId, selection) {
391
+ const processState = this.currentProcess.get(sessionId);
392
+
393
+ const routeIndex = parseInt(selection) - 1;
394
+
395
+ if (isNaN(routeIndex) || routeIndex < 0 || routeIndex >= this.adminExtension.registeredRoutes.length) {
396
+ socket.write('Número de ruta inválido. Por favor, selecciona un número válido:\n> ');
397
+ return;
398
+ }
399
+
400
+ processState.routeToDisable = this.adminExtension.registeredRoutes[routeIndex];
401
+ processState.step = 'disable_confirm';
402
+
403
+ socket.write(`\nRuta seleccionada para deshabilitar:\n`);
404
+ socket.write(`${processState.routeToDisable.method} ${processState.routeToDisable.path}\n`);
405
+ socket.write(`¿Estás seguro de que deseas deshabilitar esta ruta? (sí/no):\n> `);
406
+ }
407
+
408
+ /**
409
+ * Maneja la confirmación de deshabilitación de ruta
410
+ * @param {Object} socket - Socket de la conexión
411
+ * @param {string} sessionId - ID de la sesión
412
+ * @param {string} confirmation - Confirmación del usuario
413
+ */
414
+ handleDisableConfirmation(socket, sessionId, confirmation) {
415
+ const processState = this.currentProcess.get(sessionId);
416
+
417
+ if (confirmation.toLowerCase() === 'sí' || confirmation.toLowerCase() === 'si' || confirmation.toLowerCase() === 'yes') {
418
+ this.disableRoute(socket, processState.routeToDisable);
419
+ } else {
420
+ socket.write('\nDeshabilitación de ruta cancelada.\n> ');
421
+ this.resetProcess(socket);
422
+ }
423
+ }
424
+
425
+ /**
426
+ * Deshabilita la ruta del sistema
427
+ * @param {Object} socket - Socket de la conexión
428
+ * @param {Object} route - Ruta a deshabilitar
429
+ */
430
+ disableRoute(socket, route) {
431
+ // En un entorno real, aquí se removería la ruta del servidor
432
+ // Por ahora, solo mostramos un mensaje de confirmación
433
+
434
+ socket.write(`\n✅ Ruta "${route.method} ${route.path}" deshabilitada exitosamente.\n`);
435
+
436
+ // Remover de las rutas registradas
437
+ if (this.adminExtension.registeredRoutes) {
438
+ const index = this.adminExtension.registeredRoutes.indexOf(route);
439
+ if (index !== -1) {
440
+ this.adminExtension.registeredRoutes.splice(index, 1);
441
+ }
442
+ }
443
+
444
+ socket.write('\n> ');
445
+ this.resetProcess(socket);
446
+ }
447
+
448
+ /**
449
+ * Reinicia el proceso de gestión de rutas
450
+ * @param {Object} socket - Socket de la conexión
451
+ */
452
+ resetProcess(socket) {
453
+ const sessionId = this.getSessionId(socket);
454
+ this.currentProcess.delete(sessionId);
455
+ }
456
+
457
+ /**
458
+ * Cancela el proceso de gestión de rutas
459
+ * @param {Object} socket - Socket de la conexión
460
+ * @param {string} sessionId - ID de la sesión
461
+ */
462
+ cancelProcess(socket, sessionId) {
463
+ this.currentProcess.delete(sessionId);
464
+ socket.write('\nProceso cancelado.\n> ');
465
+ }
466
+ }
467
+
468
+ module.exports = RouteManagerModule;
@@ -63,6 +63,21 @@ Las estadísticas se actualizan directamente en el servidor en los siguientes pu
63
63
  - Al enviar la respuesta: actualiza `responseBytes`
64
64
  - Al procesar una ruta: actualiza `requestsProcessed`, `responsesSent`, `routeAccesses`, `endpointHits`
65
65
 
66
+ ## Sistema de Caché de Vistas
67
+
68
+ Además del sistema de estadísticas general, el framework también incluye un sistema de caché de vistas con estadísticas globales:
69
+
70
+ - `globalViewCacheInfo.viewCache`: Mapa de vistas en caché
71
+ - `globalViewCacheInfo.cacheStats`: Estadísticas de hits/misses
72
+ - `globalViewCacheInfo.viewDependencies`: Registro de dependencias entre vistas (inclusiones)
73
+
74
+ ### Estadísticas del Caché de Vistas
75
+
76
+ - Hits: Número de accesos exitosos al caché
77
+ - Misses: Número de veces que se leyó desde disco
78
+ - Tasa de aciertos: Porcentaje de accesos exitosos
79
+ - Dependencias: Registro de inclusiones entre vistas
80
+
66
81
  ## Instalación
67
82
 
68
83
  El módulo se carga automáticamente cuando se inicializa la extensión de administración, ya que está ubicado en el directorio `modules` de la extensión.
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Módulo de Estadísticas del Caché de Vistas para la Extensión de Administración de JERK Framework
3
+ * Módulo personalizado para mostrar estadísticas del caché de vistas
4
+ */
5
+
6
+ class ViewCacheStatsModule {
7
+ /**
8
+ * Constructor del módulo de estadísticas del caché de vistas
9
+ * @param {Object} adminExtension - Instancia de la extensión de administración
10
+ */
11
+ constructor(adminExtension) {
12
+ this.adminExtension = adminExtension;
13
+ this.name = 'View Cache Statistics Module';
14
+ this.description = 'Módulo para mostrar estadísticas del caché de vistas';
15
+ this.commands = ['view-cache'];
16
+
17
+ // Cargar el objeto global de estadísticas del caché de vistas
18
+ this.globalViewCacheInfo = require('../../utils/globalViewCacheInfo').globalViewCacheInfo;
19
+ }
20
+
21
+ /**
22
+ * Manejador para los comandos del módulo de estadísticas del caché de vistas
23
+ * @param {string} command - Comando a ejecutar
24
+ * @param {Object} socket - Socket de la conexión
25
+ */
26
+ handleCommand(command, socket) {
27
+ switch (command) {
28
+ case 'view-cache':
29
+ case 'vcache':
30
+ case 'view-stats':
31
+ this.showViewCacheStats(socket);
32
+ break;
33
+ default:
34
+ socket.write(`Comando desconocido para el módulo de estadísticas del caché de vistas: ${command}\n`);
35
+ socket.write(`Comandos disponibles: view-cache, vcache, view-stats\n\n`);
36
+ socket.write(`> `);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Muestra estadísticas del caché de vistas
42
+ * @param {Object} socket - Socket de la conexión
43
+ */
44
+ showViewCacheStats(socket) {
45
+ const { cacheStats, viewCache, viewDependencies } = this.globalViewCacheInfo;
46
+
47
+ // Codigos de color ANSI
48
+ const RESET = '\x1b[0m';
49
+ const BOLD = '\x1b[1m';
50
+ const GREEN = '\x1b[32m';
51
+ const YELLOW = '\x1b[33m';
52
+ const BLUE = '\x1b[34m';
53
+ const CYAN = '\x1b[36m';
54
+ const MAGENTA = '\x1b[35m';
55
+
56
+ socket.write(`\n${BOLD}${CYAN}=== Estadísticas del Caché de Vistas ===${RESET}\n`);
57
+ socket.write(`${GREEN}Vistas en caché:${RESET} ${viewCache.size}\n`);
58
+ socket.write(`${YELLOW}Hits (accesos exitosos):${RESET} ${cacheStats.hits}\n`);
59
+ socket.write(`${YELLOW}Misses (cargas desde disco):${RESET} ${cacheStats.misses}\n`);
60
+ socket.write(`${GREEN}Total de vistas únicas:${RESET} ${cacheStats.totalViews}\n`);
61
+
62
+ if (cacheStats.hits + cacheStats.misses > 0) {
63
+ const hitRate = (cacheStats.hits / (cacheStats.hits + cacheStats.misses) * 100).toFixed(2);
64
+ socket.write(`${BLUE}Tasa de aciertos:${RESET} ${hitRate}%\n`);
65
+ }
66
+
67
+ // Mostrar vistas más usadas (basado en el tamaño del caché)
68
+ if (viewCache.size > 0) {
69
+ socket.write(`\n${BOLD}${MAGENTA}Vistas en caché:${RESET}\n`);
70
+ for (const [viewPath, content] of viewCache) {
71
+ socket.write(` ${viewPath} (${content.length} chars)\n`);
72
+ }
73
+ }
74
+
75
+ // Mostrar dependencias de vistas
76
+ if (viewDependencies.size > 0) {
77
+ socket.write(`\n${BOLD}${MAGENTA}Dependencias de vistas (inclusiones):${RESET}\n`);
78
+ for (const [viewPath, dependencies] of viewDependencies) {
79
+ if (dependencies.size > 0) {
80
+ socket.write(` ${viewPath}:\n`);
81
+ for (const dependency of dependencies) {
82
+ socket.write(` - ${dependency}\n`);
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ socket.write(`\n${RESET}> `);
89
+ }
90
+ }
91
+
92
+ module.exports = ViewCacheStatsModule;