insitu-js 1.1.0 → 1.2.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,235 @@
1
+ # Insitu Framework Modifications - BlackCoffee
2
+
3
+ Este documento detalla las modificaciones realizadas al framework Insitu para soportar el sistema de templates 404 personalizados en BlackCoffee.
4
+
5
+ ---
6
+
7
+ ## [2026-02-16] - Custom Static 404 Response Filter
8
+
9
+ ### Resumen
10
+
11
+ Se añadió el filtro `customize_static_404_response` al manejador de archivos estáticos del servidor Insitu. Este filtro permite interceptar las respuestas 404 para archivos estáticos y reemplazar la respuesta JSON por defecto con un template HTML personalizado.
12
+
13
+ ### Archivos Modificados
14
+
15
+ #### `lib/core/server.js`
16
+
17
+ **Ubicación:** Líneas ~456-520, ~545-565, ~615-630
18
+
19
+ **Cambios realizados:**
20
+
21
+ 1. **Manejador de error ENOENT (archivo no encontrado)**
22
+ ```javascript
23
+ // ANTES:
24
+ if (error.code === 'ENOENT') {
25
+ this.sendNotFoundResponse(res, error.path);
26
+ StaticFileHooksHandler.handleStaticFileNotFound(this, error.path, req, res);
27
+ }
28
+
29
+ // DESPUÉS:
30
+ if (error.code === 'ENOENT') {
31
+ if (this.hooks) {
32
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
33
+ shouldSendDefaultResponse: true,
34
+ filePath: error.path,
35
+ req,
36
+ res
37
+ }, error.path, req, res);
38
+
39
+ if (hookResult.shouldSendDefaultResponse !== false) {
40
+ this.sendNotFoundResponse(res, error.path);
41
+ }
42
+ } else {
43
+ this.sendNotFoundResponse(res, error.path);
44
+ }
45
+
46
+ StaticFileHooksHandler.handleStaticFileNotFound(this, error.path, req, res);
47
+ }
48
+ ```
49
+
50
+ 2. **Directorio no encontrado (dentro del try-catch de stat)**
51
+ ```javascript
52
+ // Se añadió el filtro customize_static_404_response antes de sendJsonResponse
53
+ if (this.hooks) {
54
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
55
+ shouldSendDefaultResponse: true,
56
+ filePath: dirPath,
57
+ req,
58
+ res
59
+ }, dirPath, req, res);
60
+
61
+ if (hookResult.shouldSendDefaultResponse !== false) {
62
+ this.sendJsonResponse(res, 404, { error: 'Directorio no encontrado', path: dirPath });
63
+ }
64
+ } else {
65
+ this.sendJsonResponse(res, 404, { error: 'Directorio no encontrado', path: dirPath });
66
+ }
67
+ ```
68
+
69
+ 3. **Archivo no encontrado (error de stat)**
70
+ ```javascript
71
+ // Se añadió el filtro customize_static_404_response antes de sendNotFoundResponse
72
+ if (this.hooks) {
73
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
74
+ shouldSendDefaultResponse: true,
75
+ filePath: physicalPath,
76
+ req,
77
+ res
78
+ }, physicalPath, req, res);
79
+
80
+ if (hookResult.shouldSendDefaultResponse !== false) {
81
+ this.sendNotFoundResponse(res, physicalPath);
82
+ }
83
+ } else {
84
+ this.sendNotFoundResponse(res, physicalPath);
85
+ }
86
+ ```
87
+
88
+ 4. **Directorio sin archivo índice**
89
+ ```javascript
90
+ // Se añadió el filtro customize_static_404_response antes de sendNotFoundResponse
91
+ if (this.hooks) {
92
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
93
+ shouldSendDefaultResponse: true,
94
+ filePath: dirPath + ' (no index file)',
95
+ req,
96
+ res
97
+ }, dirPath, req, res);
98
+
99
+ if (hookResult.shouldSendDefaultResponse !== false) {
100
+ this.sendNotFoundResponse(res);
101
+ }
102
+ } else {
103
+ this.sendNotFoundResponse(res);
104
+ }
105
+ ```
106
+
107
+ ### Nuevo Hook Registrado
108
+
109
+ **Nombre:** `customize_static_404_response`
110
+
111
+ **Tipo:** Filter
112
+
113
+ **Parámetros:**
114
+ - `data` (Object): Datos del filtro
115
+ - `shouldSendDefaultResponse` (boolean): Si true, envía la respuesta JSON por defecto
116
+ - `filePath` (string): Ruta del archivo no encontrado
117
+ - `req` (Object): Objeto de solicitud HTTP
118
+ - `res` (Object): Objeto de respuesta HTTP
119
+ - `filePath` (string): Ruta del archivo (parámetro directo)
120
+ - `req` (Object): Objeto de solicitud HTTP
121
+ - `res` (Object): Objeto de respuesta HTTP
122
+
123
+ **Comportamiento:**
124
+ - Si `data.shouldSendDefaultResponse` se establece en `false`, NO se envía la respuesta JSON por defecto
125
+ - El filtro puede enviar una respuesta personalizada (ej: template HTML) directamente
126
+ - Si el filtro no modifica `shouldSendDefaultResponse`, se envía la respuesta JSON estándar
127
+
128
+ ### Ejemplo de Uso
129
+
130
+ ```javascript
131
+ const { hooks } = require('insitu-js');
132
+
133
+ // Registrar filtro para servir template HTML 404
134
+ hooks.addFilter('customize_static_404_response', (data, filePath, req, res) => {
135
+ const template = load404Template();
136
+
137
+ if (template && req && res) {
138
+ const errorData = {
139
+ type: 'file',
140
+ path: filePath,
141
+ method: req.method,
142
+ url: req.url,
143
+ timestamp: new Date().toISOString()
144
+ };
145
+
146
+ // Inyectar datos en el template
147
+ let html = template.replace(
148
+ 'window.errorData = {};',
149
+ `window.errorData = ${JSON.stringify(errorData)};`
150
+ );
151
+
152
+ // Enviar respuesta HTML personalizada
153
+ res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
154
+ res.end(html);
155
+
156
+ // Evitar respuesta por defecto
157
+ data.shouldSendDefaultResponse = false;
158
+ }
159
+
160
+ return data;
161
+ }, 5); // Prioridad alta
162
+ ```
163
+
164
+ ### Compatibilidad
165
+
166
+ - **Retrocompatible:** Sí. Si no hay filtros registrados, el comportamiento es idéntico al original
167
+ - **Requiere:** Sistema de hooks de Insitu (disponible en v1.1.0+)
168
+ - **Afecta:** Solo a rutas estáticas (archivos servidos con `static: { dir: '...' }`)
169
+
170
+ ### Puntos de Ejecución
171
+
172
+ El filtro se ejecuta en 4 puntos del flujo de archivos estáticos:
173
+
174
+ ```
175
+ Solicitud → createStaticFileHandler()
176
+
177
+ ┌─ Try-catch principal
178
+ │ ├─ fs.promises.access() falla
179
+ │ │ └─ [HOOK] ENOENT handler (línea ~617)
180
+ │ │
181
+ │ ├─ stats.isDirectory() = true
182
+ │ │ └─ [HOOK] Directory not found (línea ~456)
183
+ │ │
184
+ │ └─ Catch statErr
185
+ │ └─ [HOOK] Stat error (línea ~487)
186
+
187
+ └─ Es directorio, buscar index
188
+ └─ No hay index file
189
+ └─ [HOOK] No index file (línea ~545)
190
+ ```
191
+
192
+ ### Testing
193
+
194
+ Para verificar que el hook funciona correctamente:
195
+
196
+ ```bash
197
+ # 1. Iniciar servidor con hooks registrados
198
+ node server.js
199
+
200
+ # 2. Solicitar archivo inexistente
201
+ curl -k https://localhost:9791/nonexistent.css
202
+
203
+ # 3. Verificar respuesta HTML (no JSON)
204
+ # Expected: <!DOCTYPE html>... (template 404)
205
+ # Not expected: {"error":"Archivo no encontrado"...}
206
+ ```
207
+
208
+ ### Notas de Implementación
209
+
210
+ 1. **Orden de ejecución:** El filtro se registra con prioridad 5 (alta) para ejecutarse antes que cualquier otro filtro que pueda modificar la respuesta
211
+
212
+ 2. **Manejo de undefined:** Los parámetros `req` y `res` pueden llegar como `undefined` en algunos contextos. Los filtros deben validar antes de usarlos
213
+
214
+ 3. **Fallback:** Si el filtro falla o no envía respuesta, `shouldSendDefaultResponse` permanece en `true` y se envía la respuesta JSON estándar
215
+
216
+ 4. **Performance:** El template debe cachearse para evitar lectura de archivo en cada 404
217
+
218
+ ### Referencias
219
+
220
+ - Hook existente similar: `customize_404_response` (para rutas dinámicas, línea ~1088)
221
+ - Handler estático: `StaticFileHooksHandler.handleStaticFileNotFound()` (línea ~84 de static-file-hooks-handler.js)
222
+
223
+ ---
224
+
225
+ ## Historial de Versiones
226
+
227
+ | Fecha | Versión Insitu | Cambio |
228
+ |-------|---------------|--------|
229
+ | 2026-02-16 | 1.1.0 | Añadido filtro `customize_static_404_response` en 4 puntos |
230
+
231
+ ---
232
+
233
+ **Documentación creada para:** BlackCoffee v1.2.0
234
+ **Autor:** Benjamin Sanchez Cardenas
235
+ **Repositorio:** https://gitlab.com/bytedogssyndicate1/bc2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,98 @@ El versionado sigue SemVer (MAJOR.MINOR.PATCH).
5
5
 
6
6
  ---
7
7
 
8
+ ## [1.2.0] - 2026-02-16
9
+
10
+ ### Custom Static 404 Response Filter - Insitu Framework
11
+
12
+ #### Nuevo Hook: `customize_static_404_response`
13
+
14
+ Se añadió el filtro `customize_static_404_response` al manejador de archivos estáticos del servidor Insitu. Este filtro permite interceptar las respuestas 404 para archivos estáticos y reemplazar la respuesta JSON por defecto con un template HTML personalizado.
15
+
16
+ #### Puntos de Aplicación en `lib/core/server.js`
17
+
18
+ El filtro se ejecuta en 4 puntos del flujo de archivos estáticos:
19
+
20
+ 1. **Manejador de error ENOENT (archivo no encontrado)** - Línea ~617
21
+ - Se ejecuta cuando `fs.promises.access()` falla con error ENOENT
22
+ - Permite personalizar la respuesta antes de enviar `sendNotFoundResponse()`
23
+
24
+ 2. **Directorio no encontrado (dentro del try-catch de stat)** - Línea ~456
25
+ - Se ejecuta cuando `stats.isDirectory()` es true pero el directorio no existe
26
+ - Permite personalizar la respuesta antes de enviar el error de directorio
27
+
28
+ 3. **Archivo no encontrado (error de stat)** - Línea ~487
29
+ - Se ejecuta cuando ocurre un error al obtener estadísticas del archivo
30
+ - Permite personalizar la respuesta antes de enviar `sendNotFoundResponse()`
31
+
32
+ 4. **Directorio sin archivo índice** - Línea ~545
33
+ - Se ejecuta cuando un directorio no contiene archivo índice
34
+ - Permite personalizar la respuesta antes de enviar `sendNotFoundResponse()`
35
+
36
+ #### Parámetros del Hook
37
+
38
+ - `data` (Object): Datos del filtro
39
+ - `shouldSendDefaultResponse` (boolean): Si true, envía la respuesta JSON por defecto
40
+ - `filePath` (string): Ruta del archivo no encontrado
41
+ - `req` (Object): Objeto de solicitud HTTP
42
+ - `res` (Object): Objeto de respuesta HTTP
43
+ - `filePath` (string): Ruta del archivo (parámetro directo)
44
+ - `req` (Object): Objeto de solicitud HTTP
45
+ - `res` (Object): Objeto de respuesta HTTP
46
+
47
+ #### Comportamiento
48
+
49
+ - Si `data.shouldSendDefaultResponse` se establece en `false`, NO se envía la respuesta JSON por defecto
50
+ - El filtro puede enviar una respuesta personalizada (ej: template HTML) directamente
51
+ - Si el filtro no modifica `shouldSendDefaultResponse`, se envía la respuesta JSON estándar
52
+
53
+ #### Ejemplo de Uso
54
+
55
+ ```javascript
56
+ const { hooks } = require('insitu-js');
57
+
58
+ hooks.addFilter('customize_static_404_response', (data, filePath, req, res) => {
59
+ const template = load404Template();
60
+
61
+ if (template && req && res) {
62
+ const errorData = {
63
+ type: 'file',
64
+ path: filePath,
65
+ method: req.method,
66
+ url: req.url,
67
+ timestamp: new Date().toISOString()
68
+ };
69
+
70
+ let html = template.replace(
71
+ 'window.errorData = {};',
72
+ `window.errorData = ${JSON.stringify(errorData)};`
73
+ );
74
+
75
+ res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
76
+ res.end(html);
77
+
78
+ data.shouldSendDefaultResponse = false;
79
+ }
80
+
81
+ return data;
82
+ }, 5);
83
+ ```
84
+
85
+ #### Compatibilidad
86
+
87
+ - **Retrocompatible:** Sí. Si no hay filtros registrados, el comportamiento es idéntico al original
88
+ - **Requiere:** Sistema de hooks de Insitu (disponible en v1.1.0+)
89
+ - **Afecta:** Solo a rutas estáticas (archivos servidos con `static: { dir: '...' }`)
90
+
91
+ #### Beneficios Obtenidos
92
+
93
+ - Capacidad de servir templates HTML personalizados para errores 404 en archivos estáticos
94
+ - Mejor experiencia de usuario con páginas de error estilizadas
95
+ - Flexibilidad para incluir datos de error dinámicos en la respuesta
96
+ - Mantenimiento del comportamiento por defecto cuando no hay filtros registrados
97
+
98
+ ---
99
+
8
100
  ## [1.0.2] - 2026-02-16
9
101
 
10
102
  ### Mejoras en Presentación Visual, Corrección de Dependencias Circulares y Hooks de Seguimiento - Insitu Framework
@@ -453,20 +453,62 @@ class APIServer {
453
453
  }
454
454
 
455
455
  // Si es directorio pero no hay archivo índice
456
- this.sendJsonResponse(res, 404, { error: 'Directorio no encontrado', path: dirPath });
456
+ // Permitir que los filtros personalicen la respuesta 404
457
+ if (this.hooks) {
458
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
459
+ shouldSendDefaultResponse: true,
460
+ filePath: dirPath,
461
+ req,
462
+ res
463
+ }, dirPath, req, res);
464
+
465
+ if (hookResult.shouldSendDefaultResponse !== false) {
466
+ this.sendJsonResponse(res, 404, { error: 'Directorio no encontrado', path: dirPath });
467
+ }
468
+ } else {
469
+ this.sendJsonResponse(res, 404, { error: 'Directorio no encontrado', path: dirPath });
470
+ }
457
471
 
458
472
  StaticFileHooksHandler.handleStaticDirectoryNoIndex(this, dirPath, req, res);
459
473
  return;
460
474
  } else {
461
475
  // No es directorio ni archivo existente
462
- this.sendNotFoundResponse(res, physicalPath);
476
+ // Permitir que los filtros personalicen la respuesta 404
477
+ if (this.hooks) {
478
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
479
+ shouldSendDefaultResponse: true,
480
+ filePath: physicalPath,
481
+ req,
482
+ res
483
+ }, physicalPath, req, res);
484
+
485
+ if (hookResult.shouldSendDefaultResponse !== false) {
486
+ this.sendNotFoundResponse(res, physicalPath);
487
+ }
488
+ } else {
489
+ this.sendNotFoundResponse(res, physicalPath);
490
+ }
463
491
 
464
492
  StaticFileHooksHandler.handleStaticFileNotFound(this, physicalPath, req, res);
465
493
  return;
466
494
  }
467
495
  } catch (statErr) {
468
496
  // No es directorio ni archivo
469
- this.sendNotFoundResponse(res, physicalPath);
497
+ // Permitir que los filtros personalicen la respuesta 404
498
+ if (this.hooks) {
499
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
500
+ shouldSendDefaultResponse: true,
501
+ filePath: physicalPath,
502
+ req,
503
+ res
504
+ }, physicalPath, req, res);
505
+
506
+ if (hookResult.shouldSendDefaultResponse !== false) {
507
+ this.sendNotFoundResponse(res, physicalPath);
508
+ }
509
+ } else {
510
+ this.sendNotFoundResponse(res, physicalPath);
511
+ }
470
512
 
471
513
  StaticFileHooksHandler.handleStaticFileNotFound(this, physicalPath, req, res);
472
514
  return;
@@ -501,7 +543,21 @@ class APIServer {
501
543
  }
502
544
 
503
545
  // Si es directorio pero no hay archivo índice
504
- this.sendNotFoundResponse(res);
546
+ // Permitir que los filtros personalicen la respuesta 404
547
+ if (this.hooks) {
548
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
549
+ shouldSendDefaultResponse: true,
550
+ filePath: dirPath + ' (no index file)',
551
+ req,
552
+ res
553
+ }, dirPath, req, res);
554
+
555
+ if (hookResult.shouldSendDefaultResponse !== false) {
556
+ this.sendNotFoundResponse(res);
557
+ }
558
+ } else {
559
+ this.sendNotFoundResponse(res);
560
+ }
505
561
 
506
562
  StaticFileHooksHandler.handleStaticDirectoryNoIndex(this, dirPath, req, res);
507
563
  return;
@@ -558,7 +614,21 @@ class APIServer {
558
614
  StaticFileHooksHandler.handleStaticFileServed(this, physicalPath, req, res);
559
615
  } catch (error) {
560
616
  if (error.code === 'ENOENT') {
561
- this.sendNotFoundResponse(res, error.path);
617
+ // Permitir que los filtros personalicen la respuesta 404 para archivos estáticos
618
+ if (this.hooks) {
619
+ const hookResult = this.hooks.applyFilters('customize_static_404_response', {
620
+ shouldSendDefaultResponse: true,
621
+ filePath: error.path,
622
+ req,
623
+ res
624
+ }, error.path, req, res);
625
+
626
+ if (hookResult.shouldSendDefaultResponse !== false) {
627
+ this.sendNotFoundResponse(res, error.path);
628
+ }
629
+ } else {
630
+ this.sendNotFoundResponse(res, error.path);
631
+ }
562
632
 
563
633
  StaticFileHooksHandler.handleStaticFileNotFound(this, error.path, req, res);
564
634
  } else if (error.code === 'EACCES') {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "insitu-js",
3
- "version": "1.1.0",
4
- "description": "Insitu Framework v1.0.2 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, template engine, integration with qbuilderjs, complete MVC architecture with models, enhanced route loading from directory, improved model loading system, administration extension, queue management system, route cache management module, and fixed routing issues with static and parametrized routes",
3
+ "version": "1.2.0",
4
+ "description": "Insitu Framework v1.2.0 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, template engine, integration with qbuilderjs, complete MVC architecture with models, enhanced route loading from directory, improved model loading system, administration extension, queue management system, route cache management module, fixed routing issues with static and parametrized routes, and custom static 404 response filter for personalized HTML templates",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1",