jerkjs 2.1.5 → 2.1.7
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/BUG_REPORTE_COMPRESION.txt +72 -0
- package/CHANGELOG.md +16 -0
- package/README.md +2 -2
- package/docs/HOOK-2.0.md +512 -0
- package/lib/core/hooks.js +421 -59
- package/lib/middleware/compressor.js +87 -18
- package/package.json +3 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Reporte de Bug: Falta de Header Content-Encoding en Respuestas Comprimidas
|
|
2
|
+
|
|
3
|
+
## Descripción del Problema
|
|
4
|
+
|
|
5
|
+
El middleware de compresión del framework JERK no estaba enviando el header `Content-Encoding: gzip` (o `Content-Encoding: deflate`) cuando se comprimían las respuestas. Esto causaba que los navegadores y clientes HTTP no pudieran descomprimir automáticamente las respuestas comprimidas, resultando en contenido ilegible para el usuario final.
|
|
6
|
+
|
|
7
|
+
## Causa Raíz
|
|
8
|
+
|
|
9
|
+
El problema se producía porque los controladores en el framework JERK llamaban directamente a `res.writeHead()` antes de que el middleware de compresión tuviera la oportunidad de modificar los headers. Cuando un controlador llama a `res.writeHead()`, los encabezados se envían inmediatamente al cliente, y una vez enviados, no se pueden modificar posteriormente.
|
|
10
|
+
|
|
11
|
+
En el código del controlador de ejemplo:
|
|
12
|
+
```javascript
|
|
13
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
14
|
+
res.end(JSON.stringify(largeData));
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
La llamada a `writeHead` se ejecutaba antes de que el middleware de compresión pudiera agregar el header `Content-Encoding`.
|
|
18
|
+
|
|
19
|
+
## Solución Implementada
|
|
20
|
+
|
|
21
|
+
Se modificó el middleware de compresión (`lib/middleware/compressor.js`) para implementar un sistema de captura y posposición de encabezados:
|
|
22
|
+
|
|
23
|
+
### 1. Interceptación de writeHead
|
|
24
|
+
Se sobreescribió el método `res.writeHead()` para capturar los encabezados originales pero no enviarlos inmediatamente:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
res.writeHead = (code, headers) => {
|
|
28
|
+
statusCode = code;
|
|
29
|
+
if (typeof headers === 'object' && headers !== null) {
|
|
30
|
+
responseHeaders = { ...headers };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// NO LLAMAR A ORIGINAL WRITEHEAD AÚN - ESPERAR A VER SI NECESITAMOS COMPRIMIR
|
|
34
|
+
// Marcar que writeHead ha sido llamado pero diferir el envío real
|
|
35
|
+
res.__hasCalledWriteHead = true;
|
|
36
|
+
res.__pendingStatusCode = code;
|
|
37
|
+
res.__pendingHeaders = { ...headers };
|
|
38
|
+
|
|
39
|
+
return res;
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Envío Condicionado de Encabezados
|
|
44
|
+
Cuando se determina si se debe comprimir la respuesta, se combinan los encabezados originales con el header `Content-Encoding` y se envían juntos:
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
if (res.__hasCalledWriteHead && res.__pendingHeaders) {
|
|
48
|
+
// Combinar los encabezados pendientes con el encabezado de compresión
|
|
49
|
+
const combinedHeaders = { ...res.__pendingHeaders, ...headersToSend };
|
|
50
|
+
originalWriteHead.call(res, res.__pendingStatusCode || statusCode, combinedHeaders);
|
|
51
|
+
} else {
|
|
52
|
+
originalWriteHead.call(res, statusCode, headersToSend);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Resultado
|
|
57
|
+
|
|
58
|
+
Después de la corrección:
|
|
59
|
+
- Las respuestas comprimidas ahora incluyen correctamente el header `Content-Encoding: gzip`
|
|
60
|
+
- Los navegadores pueden descomprimir automáticamente las respuestas
|
|
61
|
+
- El contenido se muestra correctamente al usuario
|
|
62
|
+
- Se mantiene la compatibilidad con los controladores existentes
|
|
63
|
+
|
|
64
|
+
## Archivos Modificados
|
|
65
|
+
|
|
66
|
+
- `lib/middleware/compressor.js`: Implementación de la solución
|
|
67
|
+
|
|
68
|
+
## Pruebas Realizadas
|
|
69
|
+
|
|
70
|
+
- Se verificó que el header `Content-Encoding: gzip` aparece en las respuestas cuando se solicita compresión
|
|
71
|
+
- Se confirmó que la respuesta se puede descomprimir correctamente usando herramientas como `zcat`
|
|
72
|
+
- Se probó con múltiples endpoints para asegurar la consistencia del comportamiento
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## v2.1.7 - 19 de enero de 2026
|
|
4
|
+
|
|
5
|
+
### Correcciones
|
|
6
|
+
|
|
7
|
+
- **Fix de header Content-Encoding faltante en respuestas comprimidas**: Se corrigió un bug crítico donde el middleware de compresión no enviaba el header `Content-Encoding: gzip` cuando los controladores llamaban directamente a `res.writeHead()` antes de que el middleware pudiera modificar los encabezados. La solución implementa un sistema de captura y posposición de encabezados que asegura que `Content-Encoding` se incluya correctamente en las respuestas comprimidas, permitiendo que los navegadores descompriman automáticamente el contenido.
|
|
8
|
+
|
|
9
|
+
## v2.1.6 - 18 de enero de 2026
|
|
10
|
+
|
|
11
|
+
### Actualizaciones
|
|
12
|
+
|
|
13
|
+
- **Actualización de la biblioteca Hooked Lib a versión 2.0**: Se ha actualizado la biblioteca de hooks existente (@lib/core/hooks.js) a la versión 2.0, que ahora permite implementar patrones de extensibilidad mediante hooks y filtros, similar a los utilizados en WordPress. Incluye soporte para namespaces (namespace::hookName), prioridades, identificadores de callbacks, operaciones síncronas y asíncronas (doActionAsync, applyFiltersAsync), y validación de entradas.
|
|
14
|
+
|
|
15
|
+
### Mejoras
|
|
16
|
+
|
|
17
|
+
- **Sistema de extensibilidad mejorado**: La actualización a Hooked Lib 2.0 proporciona capacidades avanzadas de hooks y filtros con soporte para namespaces, operaciones asíncronas, y gestión flexible de prioridades e identificadores de callbacks.
|
|
18
|
+
|
|
3
19
|
## v2.1.5 - 17 de enero de 2026
|
|
4
20
|
|
|
5
21
|
### Nuevas características
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# JERK Framework v2.1.
|
|
1
|
+
# JERK Framework v2.1.7
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
@@ -228,4 +228,4 @@ Apache 2.0
|
|
|
228
228
|
|
|
229
229
|
## Documentación del Framework
|
|
230
230
|
|
|
231
|
-
Para una descripción detallada de la arquitectura y componentes del framework, consulta el archivo [JERK_FRAMEWORK_DOCUMENTATION.md](JERK_FRAMEWORK_DOCUMENTATION.md).
|
|
231
|
+
Para una descripción detallada de la arquitectura y componentes del framework, consulta el archivo [JERK_FRAMEWORK_DOCUMENTATION.md](JERK_FRAMEWORK_DOCUMENTATION.md).
|
package/docs/HOOK-2.0.md
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
# Hooked Lib 2.0
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
## Description / Descripción
|
|
6
|
+
|
|
7
|
+
Hooked Lib is a lightweight library that allows implementing extensibility patterns through hooks and filters, similar to those used in WordPress. It enables developers to extend the functionality of their applications without modifying the core code.
|
|
8
|
+
|
|
9
|
+
Hooked Lib es una biblioteca ligera que permite implementar patrones de extensibilidad mediante hooks y filtros, similar a los utilizados en WordPress. Permite a los desarrolladores extender la funcionalidad de sus aplicaciones sin modificar el código base.
|
|
10
|
+
|
|
11
|
+
**Official Page / Página oficial:** https://jerk.page.gd/
|
|
12
|
+
|
|
13
|
+
## Installation / Instalación
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Using npm
|
|
17
|
+
# Usando npm
|
|
18
|
+
npm install jerk-hooked-lib
|
|
19
|
+
|
|
20
|
+
# Or copy the hooks.js file to your project
|
|
21
|
+
# O copia el archivo hooks.js a tu proyecto
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Basic Usage / Uso Básico
|
|
25
|
+
|
|
26
|
+
### Import the library / Importar la biblioteca
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
// After installing via npm
|
|
30
|
+
// Después de instalar vía npm
|
|
31
|
+
const HookSystem = require('jerk-hooked-lib');
|
|
32
|
+
const hooks = new HookSystem();
|
|
33
|
+
|
|
34
|
+
// Or using ES6 modules
|
|
35
|
+
// O usando módulos ES6
|
|
36
|
+
import HookSystem from 'jerk-hooked-lib';
|
|
37
|
+
const hooks = new HookSystem();
|
|
38
|
+
|
|
39
|
+
// Or if you copied the hooks.js file to your project
|
|
40
|
+
// O si copiaste el archivo hooks.js a tu proyecto
|
|
41
|
+
const HookSystem = require('./hooks.js');
|
|
42
|
+
const hooks = new HookSystem();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Register and execute actions / Registrar y ejecutar acciones
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
// Register an action / Registrar una acción
|
|
49
|
+
hooks.addAction('greeting', function(name) {
|
|
50
|
+
console.log(`Hello, ${name}!`);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Execute the action / Ejecutar la acción
|
|
54
|
+
hooks.doAction('greeting', 'John');
|
|
55
|
+
// Output: Hello, John! / Salida: Hello, John!
|
|
56
|
+
|
|
57
|
+
// Registrar una acción
|
|
58
|
+
hooks.addAction('saludo', function(nombre) {
|
|
59
|
+
console.log(`¡Hola, ${nombre}!`);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Ejecutar la acción
|
|
63
|
+
hooks.doAction('saludo', 'Juan');
|
|
64
|
+
// Salida: ¡Hola, Juan!
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Register and apply filters / Registrar y aplicar filtros
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
// Register a filter / Registrar un filtro
|
|
71
|
+
hooks.addFilter('uppercase', function(text) {
|
|
72
|
+
return text.toUpperCase();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Apply the filter / Aplicar el filtro
|
|
76
|
+
let result = hooks.applyFilters('uppercase', 'hello world');
|
|
77
|
+
// result = 'HELLO WORLD'
|
|
78
|
+
|
|
79
|
+
// Registrar un filtro
|
|
80
|
+
hooks.addFilter('mayusculas', function(texto) {
|
|
81
|
+
return texto.toUpperCase();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Aplicar el filtro
|
|
85
|
+
let resultado = hooks.applyFilters('mayusculas', 'hola mundo');
|
|
86
|
+
// resultado = 'HOLA MUNDO'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Full API / API Completa
|
|
90
|
+
|
|
91
|
+
### addAction(hookName, callback, priority = 10, acceptedArgs = 1, identifier = null)
|
|
92
|
+
|
|
93
|
+
Registers a new action (hook) that can be executed later.
|
|
94
|
+
|
|
95
|
+
Registra una nueva acción (hook) que se puede ejecutar posteriormente.
|
|
96
|
+
|
|
97
|
+
- `hookName`: Name of the hook (can include namespace like namespace::name) / Nombre del hook (puede incluir namespace como namespace::nombre)
|
|
98
|
+
- `callback`: Function to execute when the hook is called / Función a ejecutar cuando se llame al hook
|
|
99
|
+
- `priority`: Priority (lower executes first), defaults to 10 / Prioridad (más bajo se ejecuta primero), por defecto 10
|
|
100
|
+
- `acceptedArgs`: Number of arguments the function accepts, defaults to 1 / Número de argumentos que acepta la función, por defecto 1
|
|
101
|
+
- `identifier`: Optional identifier for the callback (allows removing by name) / Identificador opcional para el callback (permite eliminarlo por nombre)
|
|
102
|
+
|
|
103
|
+
### doAction(hookName, ...args)
|
|
104
|
+
|
|
105
|
+
Executes all registered actions for a specific hook.
|
|
106
|
+
|
|
107
|
+
Ejecuta todas las acciones registradas para un hook específico.
|
|
108
|
+
|
|
109
|
+
- `hookName`: Name of the hook to execute (can include namespace like namespace::name) / Nombre del hook a ejecutar (puede incluir namespace como namespace::nombre)
|
|
110
|
+
- `...args`: Arguments to pass to the callback functions / Argumentos a pasar a las funciones callback
|
|
111
|
+
|
|
112
|
+
### doActionAsync(hookName, ...args)
|
|
113
|
+
|
|
114
|
+
Executes all registered actions for a specific hook asynchronously.
|
|
115
|
+
|
|
116
|
+
Ejecuta todas las acciones registradas para un hook específico de forma asíncrona.
|
|
117
|
+
|
|
118
|
+
- `hookName`: Name of the hook to execute (can include namespace like namespace::name) / Nombre del hook a ejecutar (puede incluir namespace como namespace::nombre)
|
|
119
|
+
- `...args`: Arguments to pass to the callback functions / Argumentos a pasar a las funciones callback
|
|
120
|
+
|
|
121
|
+
### addFilter(hookName, callback, priority = 10, acceptedArgs = 1, identifier = null)
|
|
122
|
+
|
|
123
|
+
Registers a new filter that can transform a value.
|
|
124
|
+
|
|
125
|
+
Registra un nuevo filtro que puede transformar un valor.
|
|
126
|
+
|
|
127
|
+
- `hookName`: Name of the filter (can include namespace like namespace::name) / Nombre del filtro (puede incluir namespace como namespace::nombre)
|
|
128
|
+
- `callback`: Function that transforms the value / Función que transforma el valor
|
|
129
|
+
- `priority`: Priority (lower executes first), defaults to 10 / Prioridad (más bajo se ejecuta primero), por defecto 10
|
|
130
|
+
- `acceptedArgs`: Number of arguments the function accepts, defaults to 1 / Número de argumentos que acepta la función, por defecto 1
|
|
131
|
+
- `identifier`: Optional identifier for the callback (allows removing by name) / Identificador opcional para el callback (permite eliminarlo por nombre)
|
|
132
|
+
|
|
133
|
+
### applyFilters(hookName, value, ...additionalArgs)
|
|
134
|
+
|
|
135
|
+
Applies all registered filters to a value.
|
|
136
|
+
|
|
137
|
+
Aplica todos los filtros registrados a un valor.
|
|
138
|
+
|
|
139
|
+
- `hookName`: Name of the filter to apply (can include namespace like namespace::name) / Nombre del filtro a aplicar (puede incluir namespace como namespace::nombre)
|
|
140
|
+
- `value`: Value to transform / Valor a transformar
|
|
141
|
+
- `...additionalArgs`: Additional arguments for the filters / Argumentos adicionales para los filtros
|
|
142
|
+
|
|
143
|
+
### applyFiltersAsync(hookName, value, ...additionalArgs)
|
|
144
|
+
|
|
145
|
+
Applies all registered filters to a value asynchronously.
|
|
146
|
+
|
|
147
|
+
Aplica todos los filtros registrados a un valor de forma asíncrona.
|
|
148
|
+
|
|
149
|
+
- `hookName`: Name of the filter to apply (can include namespace like namespace::name) / Nombre del filtro a aplicar (puede incluir namespace como namespace::nombre)
|
|
150
|
+
- `value`: Value to transform / Valor a transformar
|
|
151
|
+
- `...additionalArgs`: Additional arguments for the filters / Argumentos adicionales para los filtros
|
|
152
|
+
|
|
153
|
+
### hasAction(hookName)
|
|
154
|
+
|
|
155
|
+
Checks if there are registered actions for a specific hook.
|
|
156
|
+
|
|
157
|
+
Verifica si hay acciones registradas para un hook específico.
|
|
158
|
+
|
|
159
|
+
- `hookName`: Name of the hook to check (can include namespace like namespace::name) / Nombre del hook a verificar (puede incluir namespace como namespace::nombre)
|
|
160
|
+
|
|
161
|
+
### hasFilter(hookName)
|
|
162
|
+
|
|
163
|
+
Checks if there are registered filters for a specific hook.
|
|
164
|
+
|
|
165
|
+
Verifica si hay filtros registrados para un hook específico.
|
|
166
|
+
|
|
167
|
+
- `hookName`: Name of the hook to check (can include namespace like namespace::name) / Nombre del hook a verificar (puede incluir namespace como namespace::nombre)
|
|
168
|
+
|
|
169
|
+
### actionsCount(hookName)
|
|
170
|
+
|
|
171
|
+
Gets the number of registered actions for a specific hook.
|
|
172
|
+
|
|
173
|
+
Obtiene el número de acciones registradas para un hook específico.
|
|
174
|
+
|
|
175
|
+
- `hookName`: Name of the hook (can include namespace like namespace::name) / Nombre del hook (puede incluir namespace como namespace::nombre)
|
|
176
|
+
|
|
177
|
+
### filtersCount(hookName)
|
|
178
|
+
|
|
179
|
+
Gets the number of registered filters for a specific hook.
|
|
180
|
+
|
|
181
|
+
Obtiene el número de filtros registrados para un hook específico.
|
|
182
|
+
|
|
183
|
+
- `hookName`: Name of the hook (can include namespace like namespace::name) / Nombre del hook (puede incluir namespace como namespace::nombre)
|
|
184
|
+
|
|
185
|
+
### removeAction(hookName, callbackOrIdentifier, priority = 10)
|
|
186
|
+
|
|
187
|
+
Removes a specific action.
|
|
188
|
+
|
|
189
|
+
Elimina una acción específica.
|
|
190
|
+
|
|
191
|
+
- `hookName`: Name of the hook (can include namespace like namespace::name) / Nombre del hook (puede incluir namespace como namespace::nombre)
|
|
192
|
+
- `callbackOrIdentifier`: Callback function or identifier to remove / Función callback o identificador a eliminar
|
|
193
|
+
- `priority`: Callback priority, defaults to 10 (optional if using identifier exclusively) / Prioridad del callback, por defecto 10 (opcional si se usa identificador exclusivamente)
|
|
194
|
+
|
|
195
|
+
### removeFilter(hookName, callbackOrIdentifier, priority = 10)
|
|
196
|
+
|
|
197
|
+
Removes a specific filter.
|
|
198
|
+
|
|
199
|
+
Elimina un filtro específico.
|
|
200
|
+
|
|
201
|
+
- `hookName`: Name of the filter (can include namespace like namespace::name) / Nombre del filtro (puede incluir namespace como namespace::nombre)
|
|
202
|
+
- `callbackOrIdentifier`: Callback function or identifier to remove / Función callback o identificador a eliminar
|
|
203
|
+
- `priority`: Callback priority, defaults to 10 (optional if using identifier exclusively) / Prioridad del callback, por defecto 10 (opcional si se usa identificador exclusivamente)
|
|
204
|
+
|
|
205
|
+
### Removal by identifier / Eliminación por identificador
|
|
206
|
+
|
|
207
|
+
When calling `removeAction` or `removeFilter` with only two parameters (hookName and identifier), all callbacks with that identifier will be removed at all priorities.
|
|
208
|
+
|
|
209
|
+
Cuando se llama a `removeAction` o `removeFilter` solo con dos parámetros (hookName e identificador), se eliminarán todos los callbacks con ese identificador en todas las prioridades.
|
|
210
|
+
|
|
211
|
+
Example / Ejemplo:
|
|
212
|
+
```javascript
|
|
213
|
+
// Remove all callbacks with identifier 'my_callback' regardless of priority
|
|
214
|
+
// Eliminar todos los callbacks con identificador 'mi_callback' sin importar la prioridad
|
|
215
|
+
hooks.removeAction('my_event', 'my_callback');
|
|
216
|
+
hooks.removeFilter('my_filter', 'my_identifier');
|
|
217
|
+
|
|
218
|
+
hooks.removeAction('mi_evento', 'mi_callback');
|
|
219
|
+
hooks.removeFilter('mi_filtro', 'mi_identificador');
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### removeAllActions(hookName)
|
|
223
|
+
|
|
224
|
+
Removes all actions of a specific hook (if a name is provided) or all actions (if no name is provided).
|
|
225
|
+
|
|
226
|
+
Elimina todas las acciones de un hook específico (si se proporciona el nombre) o todas las acciones (si no se proporciona nombre).
|
|
227
|
+
|
|
228
|
+
- `hookName`: Name of the hook (optional, can include namespace like namespace::name) / Nombre del hook (opcional, puede incluir namespace como namespace::nombre)
|
|
229
|
+
|
|
230
|
+
### removeAllFilters(hookName)
|
|
231
|
+
|
|
232
|
+
Removes all filters of a specific hook (if a name is provided) or all filters (if no name is provided).
|
|
233
|
+
|
|
234
|
+
Elimina todos los filtros de un hook específico (si se proporciona el nombre) o todos los filtros (si no se proporciona nombre).
|
|
235
|
+
|
|
236
|
+
- `hookName`: Name of the filter (optional, can include namespace like namespace::name) / Nombre del filtro (opcional, puede incluir namespace como namespace::nombre)
|
|
237
|
+
|
|
238
|
+
## Advanced Examples / Ejemplos Avanzados
|
|
239
|
+
|
|
240
|
+
### Priorities / Prioridades
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
// Register actions with different priorities
|
|
244
|
+
// Registrar acciones con diferentes prioridades
|
|
245
|
+
hooks.addAction('event', function() {
|
|
246
|
+
console.log('Will execute second (priority 5)');
|
|
247
|
+
}, 5);
|
|
248
|
+
|
|
249
|
+
hooks.addAction('event', function() {
|
|
250
|
+
console.log('Will execute first (priority 1)');
|
|
251
|
+
}, 1);
|
|
252
|
+
|
|
253
|
+
hooks.addAction('event', function() {
|
|
254
|
+
console.log('Will execute third (priority 10, default)');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
hooks.doAction('event');
|
|
258
|
+
// Output:
|
|
259
|
+
// Will execute first (priority 1)
|
|
260
|
+
// Will execute second (priority 5)
|
|
261
|
+
// Will execute third (priority 10, default)
|
|
262
|
+
|
|
263
|
+
// Registrar acciones con diferentes prioridades
|
|
264
|
+
hooks.addAction('evento', function() {
|
|
265
|
+
console.log('Se ejecutará segundo (prioridad 5)');
|
|
266
|
+
}, 5);
|
|
267
|
+
|
|
268
|
+
hooks.addAction('evento', function() {
|
|
269
|
+
console.log('Se ejecutará primero (prioridad 1)');
|
|
270
|
+
}, 1);
|
|
271
|
+
|
|
272
|
+
hooks.addAction('evento', function() {
|
|
273
|
+
console.log('Se ejecutará tercero (prioridad 10, por defecto)');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
hooks.doAction('evento');
|
|
277
|
+
// Salida:
|
|
278
|
+
// Se ejecutará primero (prioridad 1)
|
|
279
|
+
// Se ejecutará segundo (prioridad 5)
|
|
280
|
+
// Se ejecutará tercero (prioridad 10, por defecto)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Multiple arguments / Múltiples argumentos
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
// Action that accepts multiple arguments
|
|
287
|
+
// Acción que acepta múltiples argumentos
|
|
288
|
+
hooks.addAction('user_event', function(user, action, date) {
|
|
289
|
+
console.log(`${user} performed ${action} on ${date}`);
|
|
290
|
+
}, 10, 3); // Priority 10, accepts 3 arguments / Prioridad 10, acepta 3 argumentos
|
|
291
|
+
|
|
292
|
+
hooks.doAction('user_event', 'Peter', 'logged in', '2023-05-15');
|
|
293
|
+
// Output: Peter performed logged in on 2023-05-15 / Salida: Peter performed logged in on 2023-05-15
|
|
294
|
+
|
|
295
|
+
// Filtro con múltiples argumentos
|
|
296
|
+
hooks.addAction('evento_usuario', function(usuario, accion, fecha) {
|
|
297
|
+
console.log(`${usuario} realizó ${accion} el ${fecha}`);
|
|
298
|
+
}, 10, 3); // Prioridad 10, acepta 3 argumentos
|
|
299
|
+
|
|
300
|
+
hooks.doAction('evento_usuario', 'Pedro', 'inicio sesión', '2023-05-15');
|
|
301
|
+
// Salida: Pedro realizó inicio sesión el 2023-05-15
|
|
302
|
+
|
|
303
|
+
// Filter with multiple arguments / Filtro con múltiples argumentos
|
|
304
|
+
hooks.addFilter('calculate_discount', function(price, percentage) {
|
|
305
|
+
return price * (1 - percentage / 100);
|
|
306
|
+
}, 10, 2); // Priority 10, accepts 2 arguments / Prioridad 10, acepta 2 argumentos
|
|
307
|
+
|
|
308
|
+
let discountedPrice = hooks.applyFilters('calculate_discount', 100, 10);
|
|
309
|
+
// discountedPrice = 90
|
|
310
|
+
|
|
311
|
+
// Filtro con múltiples argumentos
|
|
312
|
+
hooks.addFilter('calcular_descuento', function(precio, porcentaje) {
|
|
313
|
+
return precio * (1 - porcentaje / 100);
|
|
314
|
+
}, 10, 2); // Prioridad 10, acepta 2 argumentos
|
|
315
|
+
|
|
316
|
+
let precioConDescuento = hooks.applyFilters('calcular_descuento', 100, 10);
|
|
317
|
+
// precioConDescuento = 90
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Filter chain / Cadena de filtros
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
// Register multiple filters to transform a value
|
|
324
|
+
// Registrar varios filtros para transformar un valor
|
|
325
|
+
hooks.addFilter('process_text', function(text) {
|
|
326
|
+
return text.trim();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
hooks.addFilter('process_text', function(text) {
|
|
330
|
+
return text.toLowerCase();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
hooks.addFilter('process_text', function(text) {
|
|
334
|
+
return text.replace(/\s+/g, '_');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
let result = hooks.applyFilters('process_text', ' EXAMPLE TEXT ');
|
|
338
|
+
// result = 'example_text'
|
|
339
|
+
|
|
340
|
+
// Registrar varios filtros para transformar un valor
|
|
341
|
+
hooks.addFilter('procesar_texto', function(texto) {
|
|
342
|
+
return texto.trim();
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
hooks.addFilter('procesar_texto', function(texto) {
|
|
346
|
+
return texto.toLowerCase();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
hooks.addFilter('procesar_texto', function(texto) {
|
|
350
|
+
return texto.replace(/\s+/g, '_');
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
let resultado = hooks.applyFilters('procesar_texto', ' TEXTO de EJEMPLO ');
|
|
354
|
+
// resultado = 'texto_de_ejemplo'
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Named callback identification / Identificación de callbacks con nombres
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
// Register an action with identifier
|
|
361
|
+
// Registrar una acción con identificador
|
|
362
|
+
hooks.addAction('event', function() {
|
|
363
|
+
console.log('Callback 1');
|
|
364
|
+
}, 10, 1, 'callback_one');
|
|
365
|
+
|
|
366
|
+
hooks.addAction('event', function() {
|
|
367
|
+
console.log('Callback 2');
|
|
368
|
+
}, 5, 1, 'callback_two');
|
|
369
|
+
|
|
370
|
+
// Remove by identifier
|
|
371
|
+
// Eliminar por identificador
|
|
372
|
+
hooks.removeAction('event', 'callback_one', 10);
|
|
373
|
+
|
|
374
|
+
hooks.doAction('event');
|
|
375
|
+
// Output: Callback 2
|
|
376
|
+
|
|
377
|
+
// Registrar una acción con identificador
|
|
378
|
+
hooks.addAction('evento', function() {
|
|
379
|
+
console.log('Callback 1');
|
|
380
|
+
}, 10, 1, 'callback_uno');
|
|
381
|
+
|
|
382
|
+
hooks.addAction('evento', function() {
|
|
383
|
+
console.log('Callback 2');
|
|
384
|
+
}, 5, 1, 'callback_dos');
|
|
385
|
+
|
|
386
|
+
// Eliminar por identificador
|
|
387
|
+
hooks.removeAction('evento', 'callback_uno', 10);
|
|
388
|
+
|
|
389
|
+
hooks.doAction('evento');
|
|
390
|
+
// Salida: Callback 2
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Asynchronous operations / Operaciones asíncronas
|
|
394
|
+
|
|
395
|
+
```javascript
|
|
396
|
+
// Asynchronous action
|
|
397
|
+
// Acción asíncrona
|
|
398
|
+
hooks.addAction('async_event', async function(name) {
|
|
399
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
400
|
+
console.log(`Hello ${name} after 1 second`);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Execute asynchronously
|
|
404
|
+
// Ejecutar de forma asíncrona
|
|
405
|
+
await hooks.doActionAsync('async_event', 'John');
|
|
406
|
+
|
|
407
|
+
// Asynchronous filter
|
|
408
|
+
// Filtro asíncrono
|
|
409
|
+
hooks.addFilter('async_filter', async function(value) {
|
|
410
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
411
|
+
return value.toUpperCase();
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Apply asynchronously
|
|
415
|
+
// Aplicar de forma asíncrona
|
|
416
|
+
const result = await hooks.applyFiltersAsync('async_filter', 'hello');
|
|
417
|
+
// result = 'HELLO'
|
|
418
|
+
|
|
419
|
+
// Acción asíncrona
|
|
420
|
+
hooks.addAction('evento_async', async function(nombre) {
|
|
421
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
422
|
+
console.log(`Hola ${nombre} después de 1 segundo`);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Ejecutar de forma asíncrona
|
|
426
|
+
await hooks.doActionAsync('evento_async', 'Juan');
|
|
427
|
+
|
|
428
|
+
// Filtro asíncrono
|
|
429
|
+
hooks.addFilter('filtrar_async', async function(valor) {
|
|
430
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
431
|
+
return valor.toUpperCase();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Aplicar de forma asíncrona
|
|
435
|
+
const resultado = await hooks.applyFiltersAsync('filtrar_async', 'hola');
|
|
436
|
+
// resultado = 'HOLA'
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Namespaces
|
|
440
|
+
|
|
441
|
+
```javascript
|
|
442
|
+
// Register hooks with namespaces
|
|
443
|
+
// Registrar hooks con namespaces
|
|
444
|
+
hooks.addAction('user_module::registration', function(user) {
|
|
445
|
+
console.log(`User registered: ${user}`);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
hooks.addFilter('product_module::price', function(price) {
|
|
449
|
+
return price * 0.9; // 10% discount / 10% de descuento
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// Execute hooks with namespaces
|
|
453
|
+
// Ejecutar hooks con namespaces
|
|
454
|
+
hooks.doAction('user_module::registration', 'Anna');
|
|
455
|
+
hooks.applyFilters('product_module::price', 100);
|
|
456
|
+
|
|
457
|
+
// Registrar hooks con namespaces
|
|
458
|
+
hooks.addAction('modulo_usuarios::registro', function(usuario) {
|
|
459
|
+
console.log(`Usuario registrado: ${usuario}`);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
hooks.addFilter('modulo_productos::precio', function(precio) {
|
|
463
|
+
return precio * 0.9; // 10% de descuento
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Ejecutar hooks con namespaces
|
|
467
|
+
hooks.doAction('modulo_usuarios::registro', 'Ana');
|
|
468
|
+
hooks.applyFilters('modulo_productos::precio', 100);
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Input Validation / Validación de Entradas
|
|
472
|
+
|
|
473
|
+
The library includes comprehensive input validation:
|
|
474
|
+
|
|
475
|
+
La biblioteca incluye validación exhaustiva de entradas:
|
|
476
|
+
|
|
477
|
+
- `hookName` must be a non-empty string / `hookName` debe ser una cadena no vacía
|
|
478
|
+
- `callback` must be a function / `callback` debe ser una función
|
|
479
|
+
- `priority` must be a non-negative integer / `priority` debe ser un número entero no negativo
|
|
480
|
+
- `acceptedArgs` must be a non-negative integer / `acceptedArgs` debe ser un número entero no negativo
|
|
481
|
+
- `identifier` must be a string or null / `identifier` debe ser una cadena o null
|
|
482
|
+
|
|
483
|
+
## Error Handling / Gestión de Errores
|
|
484
|
+
|
|
485
|
+
Callbacks that throw errors are individually captured to prevent interruptions in the execution of other hooks.
|
|
486
|
+
|
|
487
|
+
Los callbacks que lanzan errores son capturados individualmente para evitar interrupciones en la ejecución de otros hooks.
|
|
488
|
+
|
|
489
|
+
## Examples / Ejemplos
|
|
490
|
+
|
|
491
|
+
You can find detailed examples in the [examples/](./examples/) directory:
|
|
492
|
+
|
|
493
|
+
Puedes encontrar ejemplos detallados en el directorio [examples/](./examples/):
|
|
494
|
+
|
|
495
|
+
- [examples_basic.js](./examples/ejemplos_basicos.js) - Basic usage of addAction and doAction / Uso básico de addAction y doAction
|
|
496
|
+
- [examples_filters.js](./examples/ejemplos_filtros.js) - Usage of addFilter and applyFilters / Uso de addFilter y applyFilters
|
|
497
|
+
- [examples_priorities.js](./examples/ejemplos_prioridades.js) - Handling priorities in hooks / Manejo de prioridades en hooks
|
|
498
|
+
- [examples_utilities.js](./examples/ejemplos_utilidad.js) - Utility functions like hasAction, hasFilter, etc. / Funciones de utilidad como hasAction, hasFilter, etc.
|
|
499
|
+
- [examples_removal_v2.js](./examples/ejemplos_eliminacion_v2.js) - Hook removal functions / Funciones de eliminación de hooks
|
|
500
|
+
- [practical_example.js](./examples/ejemplo_practico.js) - Complete example of real-world application / Ejemplo completo de aplicación real
|
|
501
|
+
- [examples_new_features.js](./examples/ejemplos_nuevas_funcionalidades.js) - Demonstration of new features (namespaces, asynchronous operations, etc.) / Demostración de las nuevas funcionalidades (namespaces, operaciones asíncronas, etc.)
|
|
502
|
+
- [example_remove_by_id.js](./examples/ejemplo_eliminar_por_id.js) - Example of the new identifier-based removal functionality / Ejemplo de la nueva funcionalidad de eliminación por identificador
|
|
503
|
+
|
|
504
|
+
## Contribution / Contribución
|
|
505
|
+
|
|
506
|
+
Contributions are welcome. If you find any issues or have improvement suggestions, please open an issue or submit a pull request.
|
|
507
|
+
|
|
508
|
+
Las contribuciones son bienvenidas. Si encuentras algún problema o tienes sugerencias de mejora, por favor abre un issue o envía un pull request.
|
|
509
|
+
|
|
510
|
+
## License / Licencia
|
|
511
|
+
|
|
512
|
+
Apache 2.0
|