wu-framework 1.1.15 → 1.1.17
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/README.md +52 -20
- package/dist/wu-framework.cjs.js +1 -1
- package/dist/wu-framework.cjs.js.map +1 -1
- package/dist/wu-framework.dev.js +15511 -15146
- package/dist/wu-framework.dev.js.map +1 -1
- package/dist/wu-framework.esm.js +1 -1
- package/dist/wu-framework.esm.js.map +1 -1
- package/dist/wu-framework.umd.js +1 -1
- package/dist/wu-framework.umd.js.map +1 -1
- package/package.json +166 -161
- package/src/adapters/angular/ai.js +30 -30
- package/src/adapters/angular/index.d.ts +154 -154
- package/src/adapters/angular/index.js +932 -932
- package/src/adapters/angular.d.ts +3 -3
- package/src/adapters/angular.js +3 -3
- package/src/adapters/index.js +168 -168
- package/src/adapters/lit/ai.js +20 -20
- package/src/adapters/lit/index.d.ts +120 -120
- package/src/adapters/lit/index.js +721 -721
- package/src/adapters/lit.d.ts +3 -3
- package/src/adapters/lit.js +3 -3
- package/src/adapters/preact/ai.js +33 -33
- package/src/adapters/preact/index.d.ts +108 -108
- package/src/adapters/preact/index.js +661 -661
- package/src/adapters/preact.d.ts +3 -3
- package/src/adapters/preact.js +3 -3
- package/src/adapters/react/index.js +48 -54
- package/src/adapters/react.d.ts +3 -3
- package/src/adapters/react.js +3 -3
- package/src/adapters/shared.js +64 -64
- package/src/adapters/solid/ai.js +32 -32
- package/src/adapters/solid/index.d.ts +101 -101
- package/src/adapters/solid/index.js +586 -586
- package/src/adapters/solid.d.ts +3 -3
- package/src/adapters/solid.js +3 -3
- package/src/adapters/svelte/ai.js +31 -31
- package/src/adapters/svelte/index.d.ts +166 -166
- package/src/adapters/svelte/index.js +798 -798
- package/src/adapters/svelte.d.ts +3 -3
- package/src/adapters/svelte.js +3 -3
- package/src/adapters/vanilla/ai.js +30 -30
- package/src/adapters/vanilla/index.d.ts +179 -179
- package/src/adapters/vanilla/index.js +785 -785
- package/src/adapters/vanilla.d.ts +3 -3
- package/src/adapters/vanilla.js +3 -3
- package/src/adapters/vue/ai.js +52 -52
- package/src/adapters/vue/index.d.ts +299 -299
- package/src/adapters/vue/index.js +610 -610
- package/src/adapters/vue.d.ts +3 -3
- package/src/adapters/vue.js +3 -3
- package/src/ai/wu-ai-actions.js +261 -261
- package/src/ai/wu-ai-agent.js +546 -546
- package/src/ai/wu-ai-browser-primitives.js +354 -354
- package/src/ai/wu-ai-browser.js +380 -380
- package/src/ai/wu-ai-context.js +332 -332
- package/src/ai/wu-ai-conversation.js +613 -613
- package/src/ai/wu-ai-orchestrate.js +1021 -1021
- package/src/ai/wu-ai-permissions.js +381 -381
- package/src/ai/wu-ai-provider.js +700 -700
- package/src/ai/wu-ai-schema.js +225 -225
- package/src/ai/wu-ai-triggers.js +396 -396
- package/src/ai/wu-ai.js +804 -804
- package/src/core/wu-app.js +236 -236
- package/src/core/wu-cache.js +498 -477
- package/src/core/wu-core.js +1412 -1398
- package/src/core/wu-error-boundary.js +396 -382
- package/src/core/wu-event-bus.js +390 -348
- package/src/core/wu-hooks.js +350 -350
- package/src/core/wu-html-parser.js +199 -190
- package/src/core/wu-iframe-sandbox.js +328 -328
- package/src/core/wu-loader.js +385 -273
- package/src/core/wu-logger.js +142 -134
- package/src/core/wu-manifest.js +532 -509
- package/src/core/wu-mcp-bridge.js +432 -432
- package/src/core/wu-overrides.js +510 -510
- package/src/core/wu-performance.js +228 -228
- package/src/core/wu-plugin.js +401 -348
- package/src/core/wu-prefetch.js +414 -414
- package/src/core/wu-proxy-sandbox.js +477 -476
- package/src/core/wu-sandbox.js +779 -779
- package/src/core/wu-script-executor.js +161 -113
- package/src/core/wu-snapshot-sandbox.js +227 -227
- package/src/core/wu-store.js +13 -3
- package/src/core/wu-strategies.js +256 -256
- package/src/core/wu-style-bridge.js +477 -477
- package/src/index.d.ts +317 -0
- package/src/index.js +234 -224
- package/src/utils/dependency-resolver.js +327 -327
package/src/core/wu-hooks.js
CHANGED
|
@@ -1,350 +1,350 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 🪝 WU-HOOKS: LIFECYCLE MIDDLEWARE SYSTEM
|
|
3
|
-
*
|
|
4
|
-
* Sistema de hooks basado en middleware pattern para control fino:
|
|
5
|
-
* - Middleware chain con next()
|
|
6
|
-
* - Puede cancelar operaciones (no llamar next)
|
|
7
|
-
* - Puede modificar contexto
|
|
8
|
-
* - Prioridad de hooks
|
|
9
|
-
* - Async/await support
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { logger } from './wu-logger.js';
|
|
13
|
-
|
|
14
|
-
export class WuLifecycleHooks {
|
|
15
|
-
constructor(core) {
|
|
16
|
-
this.core = core;
|
|
17
|
-
this.hooks = new Map();
|
|
18
|
-
this.executionLog = [];
|
|
19
|
-
this.maxLogSize = 100;
|
|
20
|
-
|
|
21
|
-
// Lifecycle phases disponibles
|
|
22
|
-
this.lifecyclePhases = [
|
|
23
|
-
'beforeInit', // Antes de inicializar framework
|
|
24
|
-
'afterInit', // Después de inicializar
|
|
25
|
-
'beforeLoad', // Antes de cargar una app
|
|
26
|
-
'afterLoad', // Después de cargar
|
|
27
|
-
'beforeMount', // Antes de montar
|
|
28
|
-
'afterMount', // Después de montar
|
|
29
|
-
'beforeUnmount', // Antes de desmontar
|
|
30
|
-
'afterUnmount', // Después de desmontar
|
|
31
|
-
'beforeDestroy', // Antes de destruir framework
|
|
32
|
-
'afterDestroy' // Después de destruir
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
// Inicializar hooks
|
|
36
|
-
this.lifecyclePhases.forEach(phase => {
|
|
37
|
-
this.hooks.set(phase, []);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
logger.debug('[WuHooks] 🪝 Lifecycle hooks initialized');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 📦 USE: Registrar middleware hook
|
|
45
|
-
* @param {string} phase - Fase del lifecycle
|
|
46
|
-
* @param {Function} middleware - Función middleware (context, next)
|
|
47
|
-
* @param {Object} options - { priority, name }
|
|
48
|
-
*/
|
|
49
|
-
use(phase, middleware, options = {}) {
|
|
50
|
-
if (!this.hooks.has(phase)) {
|
|
51
|
-
throw new Error(`[WuHooks] Unknown lifecycle phase: ${phase}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (typeof middleware !== 'function') {
|
|
55
|
-
throw new Error('[WuHooks] Middleware must be a function');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const hook = {
|
|
59
|
-
middleware,
|
|
60
|
-
name: options.name || `hook_${Date.now()}`,
|
|
61
|
-
priority: options.priority || 0,
|
|
62
|
-
registeredAt: Date.now()
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const hooks = this.hooks.get(phase);
|
|
66
|
-
hooks.push(hook);
|
|
67
|
-
|
|
68
|
-
// Ordenar por prioridad (mayor primero)
|
|
69
|
-
hooks.sort((a, b) => b.priority - a.priority);
|
|
70
|
-
|
|
71
|
-
logger.debug(`[WuHooks] Hook "${hook.name}" registered for ${phase} (priority: ${hook.priority})`);
|
|
72
|
-
|
|
73
|
-
// Retornar función para desregistrar
|
|
74
|
-
return () => this.remove(phase, hook.name);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 🗑️ REMOVE: Remover hook
|
|
79
|
-
* @param {string} phase - Fase del lifecycle
|
|
80
|
-
* @param {string} name - Nombre del hook
|
|
81
|
-
*/
|
|
82
|
-
remove(phase, name) {
|
|
83
|
-
if (!this.hooks.has(phase)) return;
|
|
84
|
-
|
|
85
|
-
const hooks = this.hooks.get(phase);
|
|
86
|
-
const index = hooks.findIndex(h => h.name === name);
|
|
87
|
-
|
|
88
|
-
if (index > -1) {
|
|
89
|
-
hooks.splice(index, 1);
|
|
90
|
-
logger.debug(`[WuHooks] Hook "${name}" removed from ${phase}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 🎯 EXECUTE: Ejecutar middleware chain
|
|
96
|
-
* @param {string} phase - Fase del lifecycle
|
|
97
|
-
* @param {Object} context - Contexto a pasar
|
|
98
|
-
* @returns {Promise<Object>} Contexto modificado o { cancelled: true }
|
|
99
|
-
*/
|
|
100
|
-
async execute(phase, context = {}) {
|
|
101
|
-
const hooks = this.hooks.get(phase);
|
|
102
|
-
|
|
103
|
-
if (!hooks || hooks.length === 0) {
|
|
104
|
-
return context;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
logger.debug(`[WuHooks] Executing ${hooks.length} hooks for ${phase}`);
|
|
108
|
-
|
|
109
|
-
// Log para debugging
|
|
110
|
-
const executionEntry = {
|
|
111
|
-
phase,
|
|
112
|
-
timestamp: Date.now(),
|
|
113
|
-
hooksCount: hooks.length,
|
|
114
|
-
hookNames: hooks.map(h => h.name)
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
let currentContext = { ...context };
|
|
118
|
-
let cancelled = false;
|
|
119
|
-
|
|
120
|
-
// Crear cadena de middleware
|
|
121
|
-
const executeChain = async (index) => {
|
|
122
|
-
// Si llegamos al final de la cadena, retornar contexto
|
|
123
|
-
if (index >= hooks.length) {
|
|
124
|
-
return currentContext;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const hook = hooks[index];
|
|
128
|
-
const startTime = Date.now();
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
let nextCalled = false;
|
|
132
|
-
|
|
133
|
-
// Función next
|
|
134
|
-
const next = async (modifiedContext) => {
|
|
135
|
-
nextCalled = true;
|
|
136
|
-
|
|
137
|
-
// Si se pasa un contexto modificado, usarlo
|
|
138
|
-
if (modifiedContext !== undefined) {
|
|
139
|
-
currentContext = { ...currentContext, ...modifiedContext };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Continuar con siguiente hook
|
|
143
|
-
return await executeChain(index + 1);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
// Ejecutar middleware
|
|
147
|
-
await hook.middleware(currentContext, next);
|
|
148
|
-
|
|
149
|
-
// Si no se llamó next(), la operación fue cancelada
|
|
150
|
-
if (!nextCalled) {
|
|
151
|
-
logger.debug(`[WuHooks] Hook "${hook.name}" cancelled execution`);
|
|
152
|
-
cancelled = true;
|
|
153
|
-
return { cancelled: true };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const duration = Date.now() - startTime;
|
|
157
|
-
logger.debug(`[WuHooks] Hook "${hook.name}" executed in ${duration}ms`);
|
|
158
|
-
|
|
159
|
-
} catch (error) {
|
|
160
|
-
console.error(`[WuHooks] Error in hook "${hook.name}":`, error);
|
|
161
|
-
|
|
162
|
-
// Si hay error, pasar al siguiente hook
|
|
163
|
-
return await executeChain(index + 1);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return currentContext;
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
// Ejecutar cadena
|
|
170
|
-
const result = await executeChain(0);
|
|
171
|
-
|
|
172
|
-
// Completar log
|
|
173
|
-
executionEntry.duration = Date.now() - executionEntry.timestamp;
|
|
174
|
-
executionEntry.cancelled = cancelled;
|
|
175
|
-
executionEntry.success = !cancelled;
|
|
176
|
-
|
|
177
|
-
this.executionLog.push(executionEntry);
|
|
178
|
-
|
|
179
|
-
// Mantener límite de log
|
|
180
|
-
if (this.executionLog.length > this.maxLogSize) {
|
|
181
|
-
this.executionLog.shift();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return result;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* 🚀 HELPER: Registrar hook para múltiples fases
|
|
189
|
-
* @param {Array<string>} phases - Fases del lifecycle
|
|
190
|
-
* @param {Function} middleware - Función middleware
|
|
191
|
-
* @param {Object} options - Opciones
|
|
192
|
-
* @returns {Function} Función para desregistrar de todas las fases
|
|
193
|
-
*/
|
|
194
|
-
useMultiple(phases, middleware, options = {}) {
|
|
195
|
-
const unregisterFns = phases.map(phase =>
|
|
196
|
-
this.use(phase, middleware, { ...options, name: `${options.name}_${phase}` })
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
// Retornar función que desregistra de todas las fases
|
|
200
|
-
return () => unregisterFns.forEach(fn => fn());
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* 📋 GET HOOKS: Obtener hooks registrados
|
|
205
|
-
* @param {string} phase - Fase del lifecycle (opcional)
|
|
206
|
-
* @returns {Object|Array}
|
|
207
|
-
*/
|
|
208
|
-
getHooks(phase) {
|
|
209
|
-
if (phase) {
|
|
210
|
-
return this.hooks.get(phase) || [];
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Retornar todos los hooks
|
|
214
|
-
const allHooks = {};
|
|
215
|
-
this.hooks.forEach((hooks, phase) => {
|
|
216
|
-
allHooks[phase] = hooks.map(h => ({
|
|
217
|
-
name: h.name,
|
|
218
|
-
priority: h.priority,
|
|
219
|
-
registeredAt: h.registeredAt
|
|
220
|
-
}));
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
return allHooks;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* 📊 GET STATS: Estadísticas de hooks
|
|
228
|
-
* @returns {Object}
|
|
229
|
-
*/
|
|
230
|
-
getStats() {
|
|
231
|
-
const totalHooks = Array.from(this.hooks.values())
|
|
232
|
-
.reduce((sum, hooks) => sum + hooks.length, 0);
|
|
233
|
-
|
|
234
|
-
const executionsByPhase = {};
|
|
235
|
-
this.executionLog.forEach(entry => {
|
|
236
|
-
executionsByPhase[entry.phase] = (executionsByPhase[entry.phase] || 0) + 1;
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
const avgDuration = this.executionLog.length > 0
|
|
240
|
-
? this.executionLog.reduce((sum, entry) => sum + entry.duration, 0) / this.executionLog.length
|
|
241
|
-
: 0;
|
|
242
|
-
|
|
243
|
-
const cancelledCount = this.executionLog.filter(entry => entry.cancelled).length;
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
totalHooks,
|
|
247
|
-
totalExecutions: this.executionLog.length,
|
|
248
|
-
executionsByPhase,
|
|
249
|
-
avgDuration: Math.round(avgDuration),
|
|
250
|
-
cancelledCount,
|
|
251
|
-
recentExecutions: this.executionLog.slice(-10)
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* 🧹 CLEANUP: Limpiar todos los hooks
|
|
257
|
-
* @param {string} phase - Fase específica (opcional)
|
|
258
|
-
*/
|
|
259
|
-
cleanup(phase) {
|
|
260
|
-
if (phase) {
|
|
261
|
-
this.hooks.set(phase, []);
|
|
262
|
-
logger.debug(`[WuHooks] Hooks cleaned for ${phase}`);
|
|
263
|
-
} else {
|
|
264
|
-
this.lifecyclePhases.forEach(p => {
|
|
265
|
-
this.hooks.set(p, []);
|
|
266
|
-
});
|
|
267
|
-
this.executionLog = [];
|
|
268
|
-
logger.debug('[WuHooks] 🧹 All hooks cleaned');
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* 🔧 HELPER: Crear middleware hooks fácilmente
|
|
275
|
-
*/
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Crear hook simple que siempre llama next
|
|
279
|
-
* @param {Function} fn - Función a ejecutar
|
|
280
|
-
* @returns {Function} Middleware function
|
|
281
|
-
*/
|
|
282
|
-
export const createSimpleHook = (fn) => {
|
|
283
|
-
return async (context, next) => {
|
|
284
|
-
await fn(context);
|
|
285
|
-
await next();
|
|
286
|
-
};
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Crear hook condicional
|
|
291
|
-
* @param {Function} condition - Función de condición (context) => boolean
|
|
292
|
-
* @param {Function} fn - Función a ejecutar si condición es true
|
|
293
|
-
* @returns {Function} Middleware function
|
|
294
|
-
*/
|
|
295
|
-
export const createConditionalHook = (condition, fn) => {
|
|
296
|
-
return async (context, next) => {
|
|
297
|
-
if (await condition(context)) {
|
|
298
|
-
await fn(context);
|
|
299
|
-
}
|
|
300
|
-
await next();
|
|
301
|
-
};
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Crear hook que puede cancelar operación
|
|
306
|
-
* @param {Function} shouldContinue - Función que retorna true para continuar
|
|
307
|
-
* @returns {Function} Middleware function
|
|
308
|
-
*/
|
|
309
|
-
export const createGuardHook = (shouldContinue) => {
|
|
310
|
-
return async (context, next) => {
|
|
311
|
-
if (await shouldContinue(context)) {
|
|
312
|
-
await next();
|
|
313
|
-
}
|
|
314
|
-
// Si no retorna true, no llama next() y cancela
|
|
315
|
-
};
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Crear hook que modifica contexto
|
|
320
|
-
* @param {Function} transformer - Función que transforma el contexto
|
|
321
|
-
* @returns {Function} Middleware function
|
|
322
|
-
*/
|
|
323
|
-
export const createTransformHook = (transformer) => {
|
|
324
|
-
return async (context, next) => {
|
|
325
|
-
const modified = await transformer(context);
|
|
326
|
-
await next(modified);
|
|
327
|
-
};
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Crear hook con timeout
|
|
332
|
-
* @param {Function} fn - Función a ejecutar
|
|
333
|
-
* @param {number} timeout - Timeout en ms
|
|
334
|
-
* @returns {Function} Middleware function
|
|
335
|
-
*/
|
|
336
|
-
export const createTimedHook = (fn, timeout = 5000) => {
|
|
337
|
-
return async (context, next) => {
|
|
338
|
-
const timeoutPromise = new Promise((_, reject) =>
|
|
339
|
-
setTimeout(() => reject(new Error('Hook timeout')), timeout)
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
try {
|
|
343
|
-
await Promise.race([fn(context), timeoutPromise]);
|
|
344
|
-
await next();
|
|
345
|
-
} catch (error) {
|
|
346
|
-
console.error('[WuHooks] Timed hook failed:', error);
|
|
347
|
-
await next(); // Continuar a pesar del error
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* 🪝 WU-HOOKS: LIFECYCLE MIDDLEWARE SYSTEM
|
|
3
|
+
*
|
|
4
|
+
* Sistema de hooks basado en middleware pattern para control fino:
|
|
5
|
+
* - Middleware chain con next()
|
|
6
|
+
* - Puede cancelar operaciones (no llamar next)
|
|
7
|
+
* - Puede modificar contexto
|
|
8
|
+
* - Prioridad de hooks
|
|
9
|
+
* - Async/await support
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { logger } from './wu-logger.js';
|
|
13
|
+
|
|
14
|
+
export class WuLifecycleHooks {
|
|
15
|
+
constructor(core) {
|
|
16
|
+
this.core = core;
|
|
17
|
+
this.hooks = new Map();
|
|
18
|
+
this.executionLog = [];
|
|
19
|
+
this.maxLogSize = 100;
|
|
20
|
+
|
|
21
|
+
// Lifecycle phases disponibles
|
|
22
|
+
this.lifecyclePhases = [
|
|
23
|
+
'beforeInit', // Antes de inicializar framework
|
|
24
|
+
'afterInit', // Después de inicializar
|
|
25
|
+
'beforeLoad', // Antes de cargar una app
|
|
26
|
+
'afterLoad', // Después de cargar
|
|
27
|
+
'beforeMount', // Antes de montar
|
|
28
|
+
'afterMount', // Después de montar
|
|
29
|
+
'beforeUnmount', // Antes de desmontar
|
|
30
|
+
'afterUnmount', // Después de desmontar
|
|
31
|
+
'beforeDestroy', // Antes de destruir framework
|
|
32
|
+
'afterDestroy' // Después de destruir
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// Inicializar hooks
|
|
36
|
+
this.lifecyclePhases.forEach(phase => {
|
|
37
|
+
this.hooks.set(phase, []);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
logger.debug('[WuHooks] 🪝 Lifecycle hooks initialized');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 📦 USE: Registrar middleware hook
|
|
45
|
+
* @param {string} phase - Fase del lifecycle
|
|
46
|
+
* @param {Function} middleware - Función middleware (context, next)
|
|
47
|
+
* @param {Object} options - { priority, name }
|
|
48
|
+
*/
|
|
49
|
+
use(phase, middleware, options = {}) {
|
|
50
|
+
if (!this.hooks.has(phase)) {
|
|
51
|
+
throw new Error(`[WuHooks] Unknown lifecycle phase: ${phase}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof middleware !== 'function') {
|
|
55
|
+
throw new Error('[WuHooks] Middleware must be a function');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const hook = {
|
|
59
|
+
middleware,
|
|
60
|
+
name: options.name || `hook_${Date.now()}`,
|
|
61
|
+
priority: options.priority || 0,
|
|
62
|
+
registeredAt: Date.now()
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const hooks = this.hooks.get(phase);
|
|
66
|
+
hooks.push(hook);
|
|
67
|
+
|
|
68
|
+
// Ordenar por prioridad (mayor primero)
|
|
69
|
+
hooks.sort((a, b) => b.priority - a.priority);
|
|
70
|
+
|
|
71
|
+
logger.debug(`[WuHooks] Hook "${hook.name}" registered for ${phase} (priority: ${hook.priority})`);
|
|
72
|
+
|
|
73
|
+
// Retornar función para desregistrar
|
|
74
|
+
return () => this.remove(phase, hook.name);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 🗑️ REMOVE: Remover hook
|
|
79
|
+
* @param {string} phase - Fase del lifecycle
|
|
80
|
+
* @param {string} name - Nombre del hook
|
|
81
|
+
*/
|
|
82
|
+
remove(phase, name) {
|
|
83
|
+
if (!this.hooks.has(phase)) return;
|
|
84
|
+
|
|
85
|
+
const hooks = this.hooks.get(phase);
|
|
86
|
+
const index = hooks.findIndex(h => h.name === name);
|
|
87
|
+
|
|
88
|
+
if (index > -1) {
|
|
89
|
+
hooks.splice(index, 1);
|
|
90
|
+
logger.debug(`[WuHooks] Hook "${name}" removed from ${phase}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 🎯 EXECUTE: Ejecutar middleware chain
|
|
96
|
+
* @param {string} phase - Fase del lifecycle
|
|
97
|
+
* @param {Object} context - Contexto a pasar
|
|
98
|
+
* @returns {Promise<Object>} Contexto modificado o { cancelled: true }
|
|
99
|
+
*/
|
|
100
|
+
async execute(phase, context = {}) {
|
|
101
|
+
const hooks = this.hooks.get(phase);
|
|
102
|
+
|
|
103
|
+
if (!hooks || hooks.length === 0) {
|
|
104
|
+
return context;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
logger.debug(`[WuHooks] Executing ${hooks.length} hooks for ${phase}`);
|
|
108
|
+
|
|
109
|
+
// Log para debugging
|
|
110
|
+
const executionEntry = {
|
|
111
|
+
phase,
|
|
112
|
+
timestamp: Date.now(),
|
|
113
|
+
hooksCount: hooks.length,
|
|
114
|
+
hookNames: hooks.map(h => h.name)
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
let currentContext = { ...context };
|
|
118
|
+
let cancelled = false;
|
|
119
|
+
|
|
120
|
+
// Crear cadena de middleware
|
|
121
|
+
const executeChain = async (index) => {
|
|
122
|
+
// Si llegamos al final de la cadena, retornar contexto
|
|
123
|
+
if (index >= hooks.length) {
|
|
124
|
+
return currentContext;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const hook = hooks[index];
|
|
128
|
+
const startTime = Date.now();
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
let nextCalled = false;
|
|
132
|
+
|
|
133
|
+
// Función next
|
|
134
|
+
const next = async (modifiedContext) => {
|
|
135
|
+
nextCalled = true;
|
|
136
|
+
|
|
137
|
+
// Si se pasa un contexto modificado, usarlo
|
|
138
|
+
if (modifiedContext !== undefined) {
|
|
139
|
+
currentContext = { ...currentContext, ...modifiedContext };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Continuar con siguiente hook
|
|
143
|
+
return await executeChain(index + 1);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Ejecutar middleware
|
|
147
|
+
await hook.middleware(currentContext, next);
|
|
148
|
+
|
|
149
|
+
// Si no se llamó next(), la operación fue cancelada
|
|
150
|
+
if (!nextCalled) {
|
|
151
|
+
logger.debug(`[WuHooks] Hook "${hook.name}" cancelled execution`);
|
|
152
|
+
cancelled = true;
|
|
153
|
+
return { cancelled: true };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const duration = Date.now() - startTime;
|
|
157
|
+
logger.debug(`[WuHooks] Hook "${hook.name}" executed in ${duration}ms`);
|
|
158
|
+
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error(`[WuHooks] Error in hook "${hook.name}":`, error);
|
|
161
|
+
|
|
162
|
+
// Si hay error, pasar al siguiente hook
|
|
163
|
+
return await executeChain(index + 1);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return currentContext;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Ejecutar cadena
|
|
170
|
+
const result = await executeChain(0);
|
|
171
|
+
|
|
172
|
+
// Completar log
|
|
173
|
+
executionEntry.duration = Date.now() - executionEntry.timestamp;
|
|
174
|
+
executionEntry.cancelled = cancelled;
|
|
175
|
+
executionEntry.success = !cancelled;
|
|
176
|
+
|
|
177
|
+
this.executionLog.push(executionEntry);
|
|
178
|
+
|
|
179
|
+
// Mantener límite de log
|
|
180
|
+
if (this.executionLog.length > this.maxLogSize) {
|
|
181
|
+
this.executionLog.shift();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 🚀 HELPER: Registrar hook para múltiples fases
|
|
189
|
+
* @param {Array<string>} phases - Fases del lifecycle
|
|
190
|
+
* @param {Function} middleware - Función middleware
|
|
191
|
+
* @param {Object} options - Opciones
|
|
192
|
+
* @returns {Function} Función para desregistrar de todas las fases
|
|
193
|
+
*/
|
|
194
|
+
useMultiple(phases, middleware, options = {}) {
|
|
195
|
+
const unregisterFns = phases.map(phase =>
|
|
196
|
+
this.use(phase, middleware, { ...options, name: `${options.name}_${phase}` })
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Retornar función que desregistra de todas las fases
|
|
200
|
+
return () => unregisterFns.forEach(fn => fn());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 📋 GET HOOKS: Obtener hooks registrados
|
|
205
|
+
* @param {string} phase - Fase del lifecycle (opcional)
|
|
206
|
+
* @returns {Object|Array}
|
|
207
|
+
*/
|
|
208
|
+
getHooks(phase) {
|
|
209
|
+
if (phase) {
|
|
210
|
+
return this.hooks.get(phase) || [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Retornar todos los hooks
|
|
214
|
+
const allHooks = {};
|
|
215
|
+
this.hooks.forEach((hooks, phase) => {
|
|
216
|
+
allHooks[phase] = hooks.map(h => ({
|
|
217
|
+
name: h.name,
|
|
218
|
+
priority: h.priority,
|
|
219
|
+
registeredAt: h.registeredAt
|
|
220
|
+
}));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return allHooks;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 📊 GET STATS: Estadísticas de hooks
|
|
228
|
+
* @returns {Object}
|
|
229
|
+
*/
|
|
230
|
+
getStats() {
|
|
231
|
+
const totalHooks = Array.from(this.hooks.values())
|
|
232
|
+
.reduce((sum, hooks) => sum + hooks.length, 0);
|
|
233
|
+
|
|
234
|
+
const executionsByPhase = {};
|
|
235
|
+
this.executionLog.forEach(entry => {
|
|
236
|
+
executionsByPhase[entry.phase] = (executionsByPhase[entry.phase] || 0) + 1;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const avgDuration = this.executionLog.length > 0
|
|
240
|
+
? this.executionLog.reduce((sum, entry) => sum + entry.duration, 0) / this.executionLog.length
|
|
241
|
+
: 0;
|
|
242
|
+
|
|
243
|
+
const cancelledCount = this.executionLog.filter(entry => entry.cancelled).length;
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
totalHooks,
|
|
247
|
+
totalExecutions: this.executionLog.length,
|
|
248
|
+
executionsByPhase,
|
|
249
|
+
avgDuration: Math.round(avgDuration),
|
|
250
|
+
cancelledCount,
|
|
251
|
+
recentExecutions: this.executionLog.slice(-10)
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 🧹 CLEANUP: Limpiar todos los hooks
|
|
257
|
+
* @param {string} phase - Fase específica (opcional)
|
|
258
|
+
*/
|
|
259
|
+
cleanup(phase) {
|
|
260
|
+
if (phase) {
|
|
261
|
+
this.hooks.set(phase, []);
|
|
262
|
+
logger.debug(`[WuHooks] Hooks cleaned for ${phase}`);
|
|
263
|
+
} else {
|
|
264
|
+
this.lifecyclePhases.forEach(p => {
|
|
265
|
+
this.hooks.set(p, []);
|
|
266
|
+
});
|
|
267
|
+
this.executionLog = [];
|
|
268
|
+
logger.debug('[WuHooks] 🧹 All hooks cleaned');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 🔧 HELPER: Crear middleware hooks fácilmente
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Crear hook simple que siempre llama next
|
|
279
|
+
* @param {Function} fn - Función a ejecutar
|
|
280
|
+
* @returns {Function} Middleware function
|
|
281
|
+
*/
|
|
282
|
+
export const createSimpleHook = (fn) => {
|
|
283
|
+
return async (context, next) => {
|
|
284
|
+
await fn(context);
|
|
285
|
+
await next();
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Crear hook condicional
|
|
291
|
+
* @param {Function} condition - Función de condición (context) => boolean
|
|
292
|
+
* @param {Function} fn - Función a ejecutar si condición es true
|
|
293
|
+
* @returns {Function} Middleware function
|
|
294
|
+
*/
|
|
295
|
+
export const createConditionalHook = (condition, fn) => {
|
|
296
|
+
return async (context, next) => {
|
|
297
|
+
if (await condition(context)) {
|
|
298
|
+
await fn(context);
|
|
299
|
+
}
|
|
300
|
+
await next();
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Crear hook que puede cancelar operación
|
|
306
|
+
* @param {Function} shouldContinue - Función que retorna true para continuar
|
|
307
|
+
* @returns {Function} Middleware function
|
|
308
|
+
*/
|
|
309
|
+
export const createGuardHook = (shouldContinue) => {
|
|
310
|
+
return async (context, next) => {
|
|
311
|
+
if (await shouldContinue(context)) {
|
|
312
|
+
await next();
|
|
313
|
+
}
|
|
314
|
+
// Si no retorna true, no llama next() y cancela
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Crear hook que modifica contexto
|
|
320
|
+
* @param {Function} transformer - Función que transforma el contexto
|
|
321
|
+
* @returns {Function} Middleware function
|
|
322
|
+
*/
|
|
323
|
+
export const createTransformHook = (transformer) => {
|
|
324
|
+
return async (context, next) => {
|
|
325
|
+
const modified = await transformer(context);
|
|
326
|
+
await next(modified);
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Crear hook con timeout
|
|
332
|
+
* @param {Function} fn - Función a ejecutar
|
|
333
|
+
* @param {number} timeout - Timeout en ms
|
|
334
|
+
* @returns {Function} Middleware function
|
|
335
|
+
*/
|
|
336
|
+
export const createTimedHook = (fn, timeout = 5000) => {
|
|
337
|
+
return async (context, next) => {
|
|
338
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
339
|
+
setTimeout(() => reject(new Error('Hook timeout')), timeout)
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
await Promise.race([fn(context), timeoutPromise]);
|
|
344
|
+
await next();
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.error('[WuHooks] Timed hook failed:', error);
|
|
347
|
+
await next(); // Continuar a pesar del error
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
};
|