jerkjs 2.0.2 → 2.1.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.
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Aplicación de ejemplo usando el framework JERK
3
+ * Demostrando routes.json, controladores y sistema de hooks
4
+ */
5
+
6
+ const jerk = require('jerkjs');
7
+ const {
8
+ APIServer,
9
+ Logger,
10
+ RouteLoader,
11
+ hooks
12
+ } = jerk;
13
+
14
+ // Crear instancia del logger
15
+ const logger = new Logger({ level: 'info' });
16
+
17
+ // Array para almacenar los controladores disponibles
18
+ let availableControllers = [];
19
+
20
+ // Hook que se ejecuta cuando se carga un controlador
21
+ hooks.addAction('post_controller_load', (controllerModule, absolutePath) => {
22
+ logger.info(`[[HOOK]] - Controlador cargado: ${absolutePath}`);
23
+
24
+ // Extraer información sobre las funciones disponibles en el controlador
25
+ const controllerName = absolutePath.split('/').pop().replace('.js', '');
26
+
27
+ // Verificar si es un objeto con métodos o una instancia
28
+ let handlerNames = [];
29
+ if (typeof controllerModule === 'object') {
30
+ // Si es un objeto (como una instancia de ControllerBase), obtener sus métodos
31
+ handlerNames = Object.getOwnPropertyNames(Object.getPrototypeOf(controllerModule))
32
+ .filter(prop => typeof controllerModule[prop] === 'function' && prop !== 'constructor');
33
+ } else if (typeof controllerModule === 'function') {
34
+ // Si es una clase, instanciarla y obtener sus métodos
35
+ const instance = new controllerModule();
36
+ handlerNames = Object.getOwnPropertyNames(Object.getPrototypeOf(instance))
37
+ .filter(prop => typeof instance[prop] === 'function' && prop !== 'constructor');
38
+ } else {
39
+ // Si es un objeto con funciones directamente
40
+ handlerNames = Object.keys(controllerModule).filter(key =>
41
+ typeof controllerModule[key] === 'function'
42
+ );
43
+ }
44
+
45
+ // Almacenar información sobre el controlador
46
+ availableControllers.push({
47
+ name: controllerName,
48
+ path: absolutePath,
49
+ handlers: handlerNames
50
+ });
51
+
52
+ logger.info(`[[HOOK]] - Handlers disponibles en ${controllerName}:`, handlerNames);
53
+ });
54
+
55
+ // Hook que se ejecuta antes de cargar rutas
56
+ hooks.addAction('pre_route_load', (filePath, serverInstance) => {
57
+ logger.info(`[[HOOK]] - A punto de cargar rutas desde: ${filePath}`);
58
+ });
59
+
60
+ // Hook que se ejecuta después de cargar rutas
61
+ hooks.addAction('post_route_load', (routes, serverInstance) => {
62
+ logger.info(`[[HOOK]] - Rutas cargadas exitosamente: ${routes.length} rutas`);
63
+ routes.forEach((route, index) => {
64
+ logger.info(`[[HOOK]] - Ruta ${index + 1}: ${route.method} ${route.path} -> ${route.handlerName || 'anonymous'}`);
65
+ });
66
+ });
67
+
68
+ // Hook para registrar accesos al servidor
69
+ hooks.addAction('request_received', (req, res) => {
70
+ logger.info(`[[ACCESS]] - Nuevo acceso recibido: ${req.method} ${req.url} desde ${req.connection.remoteAddress || 'unknown'}`);
71
+ });
72
+
73
+ // Hook para registrar descargas o finalización de solicitudes
74
+ hooks.addAction('request_completed', (req, res) => {
75
+ logger.info(`[[DOWNLOAD/COMPLETED]] - Solicitud completada: ${req.method} ${req.url}`);
76
+ });
77
+
78
+ async function startServer() {
79
+ // Crear instancia del servidor
80
+ const server = new APIServer({
81
+ port: 11000,
82
+ host: 'localhost'
83
+ });
84
+
85
+ try {
86
+ // Middleware para capturar inicio de solicitudes
87
+ server.use((req, res, next) => {
88
+ // Disparar hook de solicitud recibida
89
+ hooks.doAction('request_received', req, res);
90
+
91
+ // Guardar la función original de res.end
92
+ const originalEnd = res.end;
93
+
94
+ // Sobrescribir res.end para capturar cuando se completa la solicitud
95
+ res.end = function(chunk, encoding, callback) {
96
+ // Llamar a la función original
97
+ const result = originalEnd.call(this, chunk, encoding, callback);
98
+
99
+ // Disparar hook de solicitud completada
100
+ hooks.doAction('request_completed', req, res);
101
+
102
+ return result;
103
+ };
104
+
105
+ next();
106
+ });
107
+
108
+ // Cargar rutas desde archivo JSON
109
+ const routeLoader = new RouteLoader();
110
+ await routeLoader.loadRoutes(server, './routes.json');
111
+
112
+ // Mostrar los controladores disponibles (usando hooks)
113
+ logger.info('\n=== CONTROLADORES DISPONIBLES ===');
114
+ availableControllers.forEach((controller, index) => {
115
+ logger.info(`[[CONTROLLER ${index + 1}]] - Nombre: ${controller.name}`);
116
+ logger.info(`[[PATH]] - Ruta: ${controller.path}`);
117
+ logger.info(`[[HANDLERS]] - Funciones: ${controller.handlers.join(', ')}`);
118
+ logger.info('---');
119
+ });
120
+
121
+ // Iniciar el servidor
122
+ server.start();
123
+
124
+ logger.info(`\nServidor iniciado en http://localhost:${server.port}`);
125
+ logger.info('La aplicación está lista para recibir solicitudes');
126
+
127
+ } catch (error) {
128
+ logger.error('Error iniciando el servidor:', error.message);
129
+ process.exit(1);
130
+ }
131
+ }
132
+
133
+ // Iniciar el servidor
134
+ startServer();
135
+
136
+ module.exports = { startServer };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Controlador de autenticación (authController)
3
+ * Maneja operaciones de login
4
+ */
5
+
6
+ // Datos simulados de usuarios para autenticación
7
+ const validUsers = [
8
+ { username: 'admin', password: 'admin123' },
9
+ { username: 'user', password: 'user123' }
10
+ ];
11
+
12
+ // Función de login
13
+ function login(req, res) {
14
+ let body = '';
15
+
16
+ req.on('data', chunk => {
17
+ body += chunk.toString();
18
+ });
19
+
20
+ req.on('end', () => {
21
+ try {
22
+ const credentials = JSON.parse(body);
23
+ const user = validUsers.find(u =>
24
+ u.username === credentials.username &&
25
+ u.password === credentials.password
26
+ );
27
+
28
+ if (user) {
29
+ // Simular generación de token JWT
30
+ const token = `fake-jwt-token-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
31
+
32
+ res.writeHead(200, { 'Content-Type': 'application/json' });
33
+ res.end(JSON.stringify({
34
+ success: true,
35
+ token: token,
36
+ message: 'Login exitoso'
37
+ }));
38
+ } else {
39
+ res.writeHead(401, { 'Content-Type': 'application/json' });
40
+ res.end(JSON.stringify({
41
+ success: false,
42
+ message: 'Credenciales inválidas'
43
+ }));
44
+ }
45
+ } catch (error) {
46
+ res.writeHead(400, { 'Content-Type': 'application/json' });
47
+ res.end(JSON.stringify({ error: 'Datos inválidos' }));
48
+ }
49
+ });
50
+ }
51
+
52
+ module.exports = {
53
+ login
54
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Controlador principal (mainController)
3
+ * Maneja la página de inicio
4
+ */
5
+
6
+ const { ControllerBase } = require('jerkjs');
7
+
8
+ class MainController extends ControllerBase {
9
+ constructor(options = {}) {
10
+ super(options);
11
+ }
12
+
13
+ // Función para manejar la ruta de inicio
14
+ home(req, res) {
15
+ // Establecer variables para la vista
16
+ this.set('title', 'Bienvenido a JERK Framework');
17
+ this.set('message', 'Esta es una aplicación de ejemplo usando JERK Framework');
18
+
19
+ // Renderizar la vista usando el motor de plantillas de JERK
20
+ this.render(res, 'home', {
21
+ endpoints: [
22
+ { method: 'GET', path: '/users', description: 'Obtener todos los usuarios' },
23
+ { method: 'GET', path: '/users/:id', description: 'Obtener usuario por ID' },
24
+ { method: 'POST', path: '/users', description: 'Crear nuevo usuario' },
25
+ { method: 'GET', path: '/products', description: 'Obtener todos los productos' },
26
+ { method: 'GET', path: '/products/:id', description: 'Obtener producto por ID' },
27
+ { method: 'POST', path: '/login', description: 'Iniciar sesión' }
28
+ ]
29
+ });
30
+ }
31
+ }
32
+
33
+ // Exportar métodos directamente para que el RouteLoader pueda acceder a ellos
34
+ const controllerInstance = new MainController({ viewsPath: './views' });
35
+
36
+ module.exports = {
37
+ home: (req, res) => {
38
+ controllerInstance.setRequestResponse(req, res);
39
+ controllerInstance.home(req, res);
40
+ }
41
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Controlador de productos (productController)
3
+ * Maneja operaciones CRUD de productos
4
+ */
5
+
6
+ // Datos simulados de productos
7
+ let products = [
8
+ { id: 1, name: 'Laptop', price: 1200, category: 'Electrónica' },
9
+ { id: 2, name: 'Teléfono', price: 800, category: 'Electrónica' },
10
+ { id: 3, name: 'Libro', price: 15, category: 'Educación' }
11
+ ];
12
+
13
+ // Obtener todos los productos
14
+ function getAllProducts(req, res) {
15
+ res.writeHead(200, { 'Content-Type': 'application/json' });
16
+ res.end(JSON.stringify(products));
17
+ }
18
+
19
+ // Obtener un producto por ID
20
+ function getProductById(req, res) {
21
+ // Obtener el ID de los parámetros de la ruta
22
+ const urlParts = req.url.split('/');
23
+ const productId = parseInt(urlParts[urlParts.length - 1]);
24
+
25
+ const product = products.find(p => p.id === productId);
26
+
27
+ if (product) {
28
+ res.writeHead(200, { 'Content-Type': 'application/json' });
29
+ res.end(JSON.stringify(product));
30
+ } else {
31
+ res.writeHead(404, { 'Content-Type': 'application/json' });
32
+ res.end(JSON.stringify({ error: 'Producto no encontrado' }));
33
+ }
34
+ }
35
+
36
+ module.exports = {
37
+ getAllProducts,
38
+ getProductById
39
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Controlador de usuarios (userController)
3
+ * Maneja operaciones CRUD de usuarios
4
+ */
5
+
6
+ // Datos simulados de usuarios
7
+ let users = [
8
+ { id: 1, name: 'Juan Pérez', email: 'juan@example.com', age: 30 },
9
+ { id: 2, name: 'María García', email: 'maria@example.com', age: 25 },
10
+ { id: 3, name: 'Pedro Rodríguez', email: 'pedro@example.com', age: 35 }
11
+ ];
12
+
13
+ // Obtener todos los usuarios
14
+ function getAllUsers(req, res) {
15
+ res.writeHead(200, { 'Content-Type': 'application/json' });
16
+ res.end(JSON.stringify(users));
17
+ }
18
+
19
+ // Obtener un usuario por ID
20
+ function getUserById(req, res) {
21
+ // Obtener el ID de los parámetros de la ruta
22
+ const urlParts = req.url.split('/');
23
+ const userId = parseInt(urlParts[urlParts.length - 1]);
24
+
25
+ const user = users.find(u => u.id === userId);
26
+
27
+ if (user) {
28
+ res.writeHead(200, { 'Content-Type': 'application/json' });
29
+ res.end(JSON.stringify(user));
30
+ } else {
31
+ res.writeHead(404, { 'Content-Type': 'application/json' });
32
+ res.end(JSON.stringify({ error: 'Usuario no encontrado' }));
33
+ }
34
+ }
35
+
36
+ // Crear un nuevo usuario
37
+ function createUser(req, res) {
38
+ let body = '';
39
+
40
+ req.on('data', chunk => {
41
+ body += chunk.toString();
42
+ });
43
+
44
+ req.on('end', () => {
45
+ try {
46
+ const userData = JSON.parse(body);
47
+ const newUser = {
48
+ id: users.length + 1,
49
+ name: userData.name,
50
+ email: userData.email,
51
+ age: userData.age
52
+ };
53
+
54
+ users.push(newUser);
55
+
56
+ res.writeHead(201, { 'Content-Type': 'application/json' });
57
+ res.end(JSON.stringify(newUser));
58
+ } catch (error) {
59
+ res.writeHead(400, { 'Content-Type': 'application/json' });
60
+ res.end(JSON.stringify({ error: 'Datos inválidos' }));
61
+ }
62
+ });
63
+ }
64
+
65
+ module.exports = {
66
+ getAllUsers,
67
+ getUserById,
68
+ createUser
69
+ };
@@ -0,0 +1,51 @@
1
+ [
2
+ {
3
+ "method": "GET",
4
+ "path": "/",
5
+ "controller": "./controllers/mainController.js",
6
+ "handler": "home",
7
+ "auth": "none"
8
+ },
9
+ {
10
+ "method": "GET",
11
+ "path": "/users",
12
+ "controller": "./controllers/userController.js",
13
+ "handler": "getAllUsers",
14
+ "auth": "none"
15
+ },
16
+ {
17
+ "method": "GET",
18
+ "path": "/users/:id",
19
+ "controller": "./controllers/userController.js",
20
+ "handler": "getUserById",
21
+ "auth": "none"
22
+ },
23
+ {
24
+ "method": "POST",
25
+ "path": "/users",
26
+ "controller": "./controllers/userController.js",
27
+ "handler": "createUser",
28
+ "auth": "required"
29
+ },
30
+ {
31
+ "method": "GET",
32
+ "path": "/products",
33
+ "controller": "./controllers/productController.js",
34
+ "handler": "getAllProducts",
35
+ "auth": "none"
36
+ },
37
+ {
38
+ "method": "GET",
39
+ "path": "/products/:id",
40
+ "controller": "./controllers/productController.js",
41
+ "handler": "getProductById",
42
+ "auth": "none"
43
+ },
44
+ {
45
+ "method": "POST",
46
+ "path": "/login",
47
+ "controller": "./controllers/authController.js",
48
+ "handler": "login",
49
+ "auth": "none"
50
+ }
51
+ ]
@@ -0,0 +1,50 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{title}}</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ margin: 40px;
11
+ background-color: #f5f5f5;
12
+ }
13
+ .container {
14
+ max-width: 800px;
15
+ margin: 0 auto;
16
+ background: white;
17
+ padding: 20px;
18
+ border-radius: 8px;
19
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
20
+ }
21
+ h1 {
22
+ color: #333;
23
+ }
24
+ .api-info {
25
+ margin-top: 20px;
26
+ padding: 15px;
27
+ background-color: #e7f3ff;
28
+ border-left: 4px solid #2196F3;
29
+ }
30
+ .endpoint-item {
31
+ margin: 5px 0;
32
+ }
33
+ </style>
34
+ </head>
35
+ <body>
36
+ <div class="container">
37
+ <h1>{{title}}</h1>
38
+ <p>{{message}}</p>
39
+
40
+ <div class="api-info">
41
+ <h2>Endpoints disponibles:</h2>
42
+ <ul>
43
+ {{foreach:endpoints}}
44
+ <li class="endpoint-item"><strong>{{item.method}}</strong> {{item.path}} - {{item.description}}</li>
45
+ {{endforeach}}
46
+ </ul>
47
+ </div>
48
+ </div>
49
+ </body>
50
+ </html>
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Punto de entrada del framework JERK
3
- * JERK Framework v2.0
3
+ * JERK Framework v2.1
4
4
  */
5
5
 
6
6
  const APIServer = require('./lib/core/server');
@@ -34,6 +34,9 @@ const { SessionManager, sessionAuth } = require('./lib/middleware/session');
34
34
  const ViewEngine = require('./lib/mvc/viewEngine');
35
35
  const ControllerBase = require('./lib/mvc/controllerBase');
36
36
 
37
+ // Componentes de manejo de errores
38
+ const { ErrorHandler, ValidationError, AuthenticationError, DatabaseError } = require('./lib/utils/errorHandler');
39
+
37
40
  // Exportar todos los componentes del framework
38
41
  module.exports = {
39
42
  // Componentes fundamentales (v1.0)
@@ -72,7 +75,13 @@ module.exports = {
72
75
 
73
76
  // Componentes MVC (v2.3.0)
74
77
  ViewEngine,
75
- ControllerBase
78
+ ControllerBase,
79
+
80
+ // Componentes de manejo de errores (v2.4.0)
81
+ ErrorHandler,
82
+ ValidationError,
83
+ AuthenticationError,
84
+ DatabaseError
76
85
  };
77
86
 
78
87
  // También exportar clases individuales por conveniencia
package/jerk.jpg CHANGED
Binary file
@@ -2,6 +2,7 @@
2
2
  * Sistema de enrutamiento avanzado para el framework JERK
3
3
  * Implementación extendida del componente core/router.js
4
4
  * Incluye soporte para rutas anidadas y otras mejoras
5
+ * JERK Framework v2.1 - Con cacheo de expresiones regulares para rutas parametrizadas
5
6
  */
6
7
 
7
8
  class Router {
@@ -2,6 +2,7 @@
2
2
  * Implementación completa del sistema de seguridad avanzada (WAF)
3
3
  * usando el sistema de hooks y filters para extensibilidad
4
4
  * Web Application Firewall con capacidades de detección y prevención de ataques
5
+ * JERK Framework v2.1 - Con optimizaciones de rendimiento
5
6
  */
6
7
 
7
8
  const APIServer = require('../core/server');
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Servidor HTTP básico para el framework JERK
3
3
  * Implementación del componente core/server.js
4
+ * JERK Framework v2.1 - Con optimizaciones de rendimiento
4
5
  */
5
6
 
6
7
  const http = require('http');
@@ -8,6 +9,7 @@ const https = require('https');
8
9
  const url = require('url');
9
10
  const fs = require('fs');
10
11
  const { Logger } = require('../utils/logger');
12
+ const { ErrorHandler } = require('../utils/errorHandler');
11
13
 
12
14
  class APIServer {
13
15
  /**
@@ -42,6 +44,9 @@ class APIServer {
42
44
  this.middlewares = [];
43
45
  this.logger = new Logger();
44
46
  this.server = null;
47
+
48
+ // Cache de expresiones regulares para rutas parametrizadas
49
+ this.routeRegexCache = new Map();
45
50
  }
46
51
 
47
52
  /**
@@ -111,8 +116,12 @@ class APIServer {
111
116
  * @returns {RegExp} - Expresión regular para la ruta
112
117
  */
113
118
  pathToRegex(path) {
114
- // Escapar caracteres especiales de la ruta, excepto los parámetros
115
- // Pero dejar : sin escapar ya que lo usaremos para identificar parámetros
119
+ // Verificar si ya está en caché
120
+ if (this.routeRegexCache.has(path)) {
121
+ return this.routeRegexCache.get(path);
122
+ }
123
+
124
+ // Lógica de escape de caracteres especiales
116
125
  let escapedPath = '';
117
126
  for (let i = 0; i < path.length; i++) {
118
127
  const char = path[i];
@@ -124,7 +133,11 @@ class APIServer {
124
133
  }
125
134
  // Reemplazar parámetros :param con grupos de captura no greedy para evitar problemas de matching
126
135
  const regexPath = escapedPath.replace(/:([a-zA-Z0-9_]+)/g, '([^/]+?)');
127
- return new RegExp(`^${regexPath}$`);
136
+ const regex = new RegExp(`^${regexPath}$`);
137
+
138
+ // Almacenar en caché
139
+ this.routeRegexCache.set(path, regex);
140
+ return regex;
128
141
  }
129
142
 
130
143
  /**
@@ -177,6 +190,10 @@ class APIServer {
177
190
  this.server = http.createServer(this.handleRequest.bind(this));
178
191
  }
179
192
 
193
+ // Configurar keep-alive para conexiones persistentes
194
+ this.server.keepAliveTimeout = 60000; // 60 segundos
195
+ this.server.headersTimeout = 65000; // 65 segundos (mayor que keepAliveTimeout)
196
+
180
197
  // Configurar timeouts
181
198
  this.server.setTimeout(this.requestTimeout);
182
199
  this.server.on('timeout', (socket) => {
@@ -200,6 +217,9 @@ class APIServer {
200
217
  this.logger.info(`Servidor iniciado en http://${this.host}:${this.port}`);
201
218
  }
202
219
 
220
+ // Verificar uso de memoria en el hook de inicio
221
+ this.logger.logMemoryUsage();
222
+
203
223
  // Disparar hook después de iniciar el servidor
204
224
  if (hooks) {
205
225
  hooks.doAction('post_server_start', this);
@@ -232,7 +252,9 @@ class APIServer {
232
252
  // Agregar propiedades útiles a la solicitud
233
253
  req.query = query;
234
254
  req.params = {};
235
- req.body = '';
255
+
256
+ // Inicializar array para acumular chunks del body
257
+ const bodyChunks = [];
236
258
 
237
259
  // Configurar límite de tamaño para el cuerpo de la solicitud desde configuración
238
260
  let bodySize = 0;
@@ -246,11 +268,14 @@ class APIServer {
246
268
  req.destroy(); // Terminar la conexión
247
269
  return;
248
270
  }
249
- req.body += chunk.toString();
271
+ bodyChunks.push(chunk);
250
272
  });
251
273
 
252
274
  req.on('end', async () => {
253
275
  try {
276
+ // Concatenar todos los chunks una sola vez
277
+ req.body = Buffer.concat(bodyChunks).toString();
278
+
254
279
  // Parsear body si es JSON
255
280
  if (req.headers['content-type'] && req.headers['content-type'].includes('application/json')) {
256
281
  try {
@@ -358,9 +383,7 @@ class APIServer {
358
383
  }
359
384
  }
360
385
  } catch (error) {
361
- this.logger.error('Error procesando la solicitud:', error.message);
362
- res.writeHead(500, { 'Content-Type': 'application/json' });
363
- res.end(JSON.stringify({ error: 'Error interno del servidor', details: error.message }));
386
+ ErrorHandler.handle(error, req, res, this.logger);
364
387
  }
365
388
  });
366
389
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Carga de rutas desde archivos JSON para el framework JERK
3
3
  * Implementación del componente loader/routeLoader.js
4
+ * JERK Framework v2.1 - Con cacheo de expresiones regulares para rutas parametrizadas
4
5
  */
5
6
 
6
7
  const fs = require('fs');
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Middleware de compresión para el framework JERK
3
3
  * Implementación del componente middleware/compressor.js
4
+ * JERK Framework v2.1 - Con optimizaciones de eficiencia
4
5
  */
5
6
 
6
7
  const zlib = require('zlib');
@@ -53,21 +54,26 @@ class Compressor {
53
54
  const originalEnd = res.end;
54
55
  const originalWriteHead = res.writeHead;
55
56
 
56
- // Variable para almacenar el cuerpo de la respuesta
57
- let responseBody = '';
58
-
59
- // Sobrescribir res.write para capturar el cuerpo
57
+ // Array para almacenar los chunks de la respuesta
58
+ const responseChunks = [];
59
+
60
+ // Sobrescribir res.write para capturar los chunks
60
61
  res.write = (chunk, encoding) => {
61
- responseBody += chunk;
62
+ responseChunks.push(chunk);
62
63
  };
63
64
 
64
65
  // Sobrescribir res.end para comprimir antes de enviar
65
66
  res.end = (chunk, encoding) => {
66
- // Añadir el chunk final al cuerpo si existe
67
+ // Añadir el chunk final a los chunks si existe
67
68
  if (chunk) {
68
- responseBody += chunk;
69
+ responseChunks.push(chunk);
69
70
  }
70
71
 
72
+ // Concatenar todos los chunks una sola vez
73
+ const responseBody = Buffer.concat(responseChunks.map(c =>
74
+ typeof c === 'string' ? Buffer.from(c, encoding) : c
75
+ )).toString();
76
+
71
77
  // Si el cuerpo es menor que el umbral, enviar sin comprimir
72
78
  if (Buffer.byteLength(responseBody) < this.threshold) {
73
79
  res.removeHeader('Content-Encoding'); // Asegurar que no haya encabezado de codificación
@@ -108,10 +114,10 @@ class Compressor {
108
114
  // Establecer encabezados apropiados
109
115
  res.setHeader('Content-Encoding', compressionMethod);
110
116
  res.removeHeader('Content-Length'); // Eliminar Content-Length original
111
-
117
+
112
118
  // Llamar al writeHead original
113
119
  originalWriteHead.call(res);
114
-
120
+
115
121
  // Enviar el cuerpo comprimido
116
122
  originalEnd.call(res, compressed, encoding);
117
123
  })
@@ -145,7 +151,7 @@ class Compressor {
145
151
  if (contentType && contentType.includes('application/json')) {
146
152
  // Convertir a string si no lo es
147
153
  const jsonString = typeof data === 'string' ? data : JSON.stringify(data);
148
-
154
+
149
155
  // Continuar con la lógica de compresión
150
156
  // (similar a la implementación en middleware())
151
157
  const acceptEncoding = req.headers['accept-encoding'];