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.
@@ -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.5
1
+ # JERK Framework v2.1.7
2
2
 
3
3
  ![JERK Framework Logo](jerk.jpg)
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).
@@ -0,0 +1,512 @@
1
+ # Hooked Lib 2.0
2
+
3
+ ![Hooked Lib Logo](./hooked.png)
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