wu-framework 1.0.4 → 1.0.6
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/package.json +1 -1
- package/src/core/wu-cache.js +87 -15
- package/src/core/wu-event-bus.js +147 -58
- package/src/core/wu-logger.js +18 -2
- package/src/core/wu-manifest.js +139 -8
- package/src/core/wu-plugin.js +201 -71
- package/src/core/wu-registry.js +9 -92
- package/src/index.js +15 -1
- package/src/api/wu-simple.js +0 -316
package/package.json
CHANGED
package/src/core/wu-cache.js
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 💾 WU-CACHE: INTERNAL
|
|
2
|
+
* 💾 WU-CACHE: SECURE INTERNAL CACHING
|
|
3
3
|
*
|
|
4
|
-
* Sistema de caché INTERNO
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
* Features:
|
|
10
|
-
* - Cache persistente (localStorage/sessionStorage)
|
|
11
|
-
* - Cache en memoria (Map)
|
|
12
|
-
* - TTL (Time To Live) configurable
|
|
13
|
-
* - LRU (Least Recently Used) eviction
|
|
4
|
+
* Sistema de caché INTERNO con rate limiting
|
|
5
|
+
* - Rate limiting para prevenir abuso
|
|
6
|
+
* - Cache persistente y en memoria
|
|
7
|
+
* - TTL y LRU eviction
|
|
14
8
|
*
|
|
15
9
|
* ⚠️ USO INTERNO: No exponer en API pública
|
|
16
10
|
*/
|
|
@@ -22,15 +16,26 @@ export class WuCache {
|
|
|
22
16
|
maxItems: options.maxItems || 100,
|
|
23
17
|
defaultTTL: options.defaultTTL || 3600000, // 1 hour
|
|
24
18
|
persistent: options.persistent !== false,
|
|
25
|
-
storage: options.storage || 'memory',
|
|
19
|
+
storage: options.storage || 'memory',
|
|
26
20
|
compression: options.compression || false
|
|
27
21
|
};
|
|
28
22
|
|
|
23
|
+
// 🔐 Rate limiting configuration
|
|
24
|
+
this.rateLimiting = {
|
|
25
|
+
enabled: options.rateLimiting !== false,
|
|
26
|
+
maxOpsPerSecond: options.maxOpsPerSecond || 100,
|
|
27
|
+
windowMs: 1000, // 1 second window
|
|
28
|
+
cooldownMs: options.cooldownMs || 5000, // 5 second cooldown after limit
|
|
29
|
+
operations: [],
|
|
30
|
+
inCooldown: false,
|
|
31
|
+
cooldownUntil: 0
|
|
32
|
+
};
|
|
33
|
+
|
|
29
34
|
// Memory cache
|
|
30
35
|
this.memoryCache = new Map();
|
|
31
36
|
|
|
32
37
|
// LRU tracking
|
|
33
|
-
this.accessOrder = new Map();
|
|
38
|
+
this.accessOrder = new Map();
|
|
34
39
|
|
|
35
40
|
// Statistics
|
|
36
41
|
this.stats = {
|
|
@@ -38,10 +43,67 @@ export class WuCache {
|
|
|
38
43
|
misses: 0,
|
|
39
44
|
sets: 0,
|
|
40
45
|
evictions: 0,
|
|
41
|
-
size: 0
|
|
46
|
+
size: 0,
|
|
47
|
+
rateLimited: 0 // 🔐 Contador de operaciones rechazadas
|
|
42
48
|
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 🔐 CHECK RATE LIMIT: Verificar si la operación está permitida
|
|
53
|
+
* @returns {boolean} true si la operación está permitida
|
|
54
|
+
*/
|
|
55
|
+
_checkRateLimit() {
|
|
56
|
+
if (!this.rateLimiting.enabled) return true;
|
|
57
|
+
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
|
|
60
|
+
// Verificar si estamos en cooldown
|
|
61
|
+
if (this.rateLimiting.inCooldown) {
|
|
62
|
+
if (now < this.rateLimiting.cooldownUntil) {
|
|
63
|
+
this.stats.rateLimited++;
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
// Cooldown terminado
|
|
67
|
+
this.rateLimiting.inCooldown = false;
|
|
68
|
+
this.rateLimiting.operations = [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Limpiar operaciones antiguas (fuera de la ventana)
|
|
72
|
+
const windowStart = now - this.rateLimiting.windowMs;
|
|
73
|
+
this.rateLimiting.operations = this.rateLimiting.operations.filter(
|
|
74
|
+
ts => ts > windowStart
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Verificar límite
|
|
78
|
+
if (this.rateLimiting.operations.length >= this.rateLimiting.maxOpsPerSecond) {
|
|
79
|
+
// Activar cooldown
|
|
80
|
+
this.rateLimiting.inCooldown = true;
|
|
81
|
+
this.rateLimiting.cooldownUntil = now + this.rateLimiting.cooldownMs;
|
|
82
|
+
this.stats.rateLimited++;
|
|
83
|
+
console.warn(`[WuCache] 🚫 Rate limit exceeded. Cooldown for ${this.rateLimiting.cooldownMs}ms`);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
43
86
|
|
|
44
|
-
|
|
87
|
+
// Registrar operación
|
|
88
|
+
this.rateLimiting.operations.push(now);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 🔐 GET RATE LIMIT STATUS
|
|
94
|
+
*/
|
|
95
|
+
getRateLimitStatus() {
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
return {
|
|
98
|
+
enabled: this.rateLimiting.enabled,
|
|
99
|
+
inCooldown: this.rateLimiting.inCooldown,
|
|
100
|
+
cooldownRemaining: this.rateLimiting.inCooldown
|
|
101
|
+
? Math.max(0, this.rateLimiting.cooldownUntil - now)
|
|
102
|
+
: 0,
|
|
103
|
+
currentOps: this.rateLimiting.operations.length,
|
|
104
|
+
maxOps: this.rateLimiting.maxOpsPerSecond,
|
|
105
|
+
rateLimited: this.stats.rateLimited
|
|
106
|
+
};
|
|
45
107
|
}
|
|
46
108
|
|
|
47
109
|
/**
|
|
@@ -50,6 +112,11 @@ export class WuCache {
|
|
|
50
112
|
* @returns {*} Valor cacheado o null
|
|
51
113
|
*/
|
|
52
114
|
get(key) {
|
|
115
|
+
// 🔐 Check rate limit
|
|
116
|
+
if (!this._checkRateLimit()) {
|
|
117
|
+
return null; // Silently fail on rate limit
|
|
118
|
+
}
|
|
119
|
+
|
|
53
120
|
// 1. Buscar en memoria
|
|
54
121
|
if (this.memoryCache.has(key)) {
|
|
55
122
|
const entry = this.memoryCache.get(key);
|
|
@@ -92,6 +159,11 @@ export class WuCache {
|
|
|
92
159
|
* @returns {boolean}
|
|
93
160
|
*/
|
|
94
161
|
set(key, value, ttl) {
|
|
162
|
+
// 🔐 Check rate limit
|
|
163
|
+
if (!this._checkRateLimit()) {
|
|
164
|
+
return false; // Reject on rate limit
|
|
165
|
+
}
|
|
166
|
+
|
|
95
167
|
try {
|
|
96
168
|
const entry = {
|
|
97
169
|
key,
|
package/src/core/wu-event-bus.js
CHANGED
|
@@ -1,46 +1,156 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 📡 WU-EVENT-BUS:
|
|
2
|
+
* 📡 WU-EVENT-BUS: SECURE PUB/SUB SYSTEM
|
|
3
3
|
*
|
|
4
4
|
* Sistema de eventos para comunicación entre microfrontends
|
|
5
|
-
* - Pub/Sub pattern
|
|
5
|
+
* - Pub/Sub pattern con validación de origen
|
|
6
6
|
* - Event namespaces
|
|
7
7
|
* - Wildcards
|
|
8
8
|
* - Event replay
|
|
9
|
-
* -
|
|
9
|
+
* - Verificación de apps autorizadas
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
export class WuEventBus {
|
|
13
13
|
constructor() {
|
|
14
|
-
this.listeners = new Map();
|
|
15
|
-
this.history = [];
|
|
14
|
+
this.listeners = new Map();
|
|
15
|
+
this.history = [];
|
|
16
|
+
|
|
17
|
+
// 🔐 SEGURIDAD: Registro de apps autorizadas con tokens
|
|
18
|
+
this.authorizedApps = new Map(); // appName -> { token, permissions }
|
|
19
|
+
this.trustedEvents = new Set(['wu:*', 'system:*']); // Eventos del sistema
|
|
20
|
+
|
|
16
21
|
this.config = {
|
|
17
22
|
maxHistory: 100,
|
|
18
23
|
enableReplay: true,
|
|
19
24
|
enableWildcards: true,
|
|
20
|
-
logEvents: false
|
|
25
|
+
logEvents: false,
|
|
26
|
+
// 🔐 Opciones de seguridad
|
|
27
|
+
strictMode: false, // Si true, rechaza eventos de apps no autorizadas
|
|
28
|
+
validateOrigin: true // Valida que appName sea una app registrada
|
|
21
29
|
};
|
|
22
30
|
|
|
23
31
|
this.stats = {
|
|
24
32
|
emitted: 0,
|
|
25
|
-
subscriptions: 0
|
|
33
|
+
subscriptions: 0,
|
|
34
|
+
rejected: 0 // Eventos rechazados por seguridad
|
|
26
35
|
};
|
|
36
|
+
}
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
/**
|
|
39
|
+
* 🔐 REGISTER APP: Registrar app autorizada para emitir eventos
|
|
40
|
+
* @param {string} appName - Nombre de la app
|
|
41
|
+
* @param {Object} options - { permissions: ['event:*'], token }
|
|
42
|
+
* @returns {string} Token de autorización
|
|
43
|
+
*/
|
|
44
|
+
registerApp(appName, options = {}) {
|
|
45
|
+
const token = options.token || this._generateToken();
|
|
46
|
+
|
|
47
|
+
this.authorizedApps.set(appName, {
|
|
48
|
+
token,
|
|
49
|
+
permissions: options.permissions || ['*'], // Por defecto puede emitir todo
|
|
50
|
+
registeredAt: Date.now()
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return token;
|
|
29
54
|
}
|
|
30
55
|
|
|
31
56
|
/**
|
|
32
|
-
*
|
|
57
|
+
* 🔓 UNREGISTER APP: Desregistrar app
|
|
58
|
+
* @param {string} appName
|
|
59
|
+
*/
|
|
60
|
+
unregisterApp(appName) {
|
|
61
|
+
this.authorizedApps.delete(appName);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 🔐 VALIDATE ORIGIN: Verificar que el emisor está autorizado
|
|
66
|
+
* @param {string} eventName
|
|
67
|
+
* @param {string} appName
|
|
68
|
+
* @param {string} token
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
_validateOrigin(eventName, appName, token) {
|
|
72
|
+
// Eventos del sistema siempre permitidos
|
|
73
|
+
if (this._isSystemEvent(eventName)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Si no está en modo estricto, permitir todo
|
|
78
|
+
if (!this.config.strictMode) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Verificar que la app esté registrada
|
|
83
|
+
const appInfo = this.authorizedApps.get(appName);
|
|
84
|
+
if (!appInfo) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Verificar token si se proporciona
|
|
89
|
+
if (token && appInfo.token !== token) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Verificar permisos
|
|
94
|
+
return this._hasPermission(appInfo.permissions, eventName);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 🔐 HAS PERMISSION: Verificar si la app tiene permiso para el evento
|
|
99
|
+
*/
|
|
100
|
+
_hasPermission(permissions, eventName) {
|
|
101
|
+
if (permissions.includes('*')) return true;
|
|
102
|
+
|
|
103
|
+
return permissions.some(pattern => {
|
|
104
|
+
if (pattern === eventName) return true;
|
|
105
|
+
if (pattern.includes('*')) {
|
|
106
|
+
return this.matchesWildcard(eventName, pattern);
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 🔐 IS SYSTEM EVENT: Verificar si es un evento del sistema
|
|
114
|
+
*/
|
|
115
|
+
_isSystemEvent(eventName) {
|
|
116
|
+
return eventName.startsWith('wu:') ||
|
|
117
|
+
eventName.startsWith('system:') ||
|
|
118
|
+
eventName.startsWith('app:');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 🔐 GENERATE TOKEN: Generar token único
|
|
123
|
+
*/
|
|
124
|
+
_generateToken() {
|
|
125
|
+
return `wu_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 📢 EMIT: Emitir evento con validación de origen
|
|
33
130
|
* @param {string} eventName - Nombre del evento
|
|
34
131
|
* @param {*} data - Datos del evento
|
|
35
|
-
* @param {Object} options -
|
|
132
|
+
* @param {Object} options - { appName, timestamp, meta, token }
|
|
36
133
|
*/
|
|
37
134
|
emit(eventName, data, options = {}) {
|
|
135
|
+
const appName = options.appName || 'unknown';
|
|
136
|
+
|
|
137
|
+
// 🔐 Validar origen si está habilitado
|
|
138
|
+
if (this.config.validateOrigin && this.config.strictMode) {
|
|
139
|
+
if (!this._validateOrigin(eventName, appName, options.token)) {
|
|
140
|
+
this.stats.rejected++;
|
|
141
|
+
console.warn(`[WuEventBus] 🚫 Event rejected: ${eventName} from ${appName} (unauthorized)`);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
38
146
|
const event = {
|
|
39
147
|
name: eventName,
|
|
40
148
|
data,
|
|
41
149
|
timestamp: options.timestamp || Date.now(),
|
|
42
|
-
appName
|
|
43
|
-
meta: options.meta || {}
|
|
150
|
+
appName,
|
|
151
|
+
meta: options.meta || {},
|
|
152
|
+
// 🔐 Marcar si el origen fue verificado
|
|
153
|
+
verified: this.authorizedApps.has(appName)
|
|
44
154
|
};
|
|
45
155
|
|
|
46
156
|
// Agregar a historial
|
|
@@ -71,13 +181,11 @@ export class WuEventBus {
|
|
|
71
181
|
}
|
|
72
182
|
|
|
73
183
|
this.stats.emitted++;
|
|
184
|
+
return true;
|
|
74
185
|
}
|
|
75
186
|
|
|
76
187
|
/**
|
|
77
188
|
* 👂 ON: Suscribirse a evento
|
|
78
|
-
* @param {string} eventName - Nombre del evento (puede usar wildcards: 'app.*', '*.update')
|
|
79
|
-
* @param {Function} callback - Callback a ejecutar
|
|
80
|
-
* @returns {Function} Función para desuscribirse
|
|
81
189
|
*/
|
|
82
190
|
on(eventName, callback) {
|
|
83
191
|
if (!this.listeners.has(eventName)) {
|
|
@@ -87,48 +195,36 @@ export class WuEventBus {
|
|
|
87
195
|
this.listeners.get(eventName).add(callback);
|
|
88
196
|
this.stats.subscriptions++;
|
|
89
197
|
|
|
90
|
-
// Retornar función de desuscripción
|
|
91
198
|
return () => this.off(eventName, callback);
|
|
92
199
|
}
|
|
93
200
|
|
|
94
201
|
/**
|
|
95
202
|
* 🔇 OFF: Desuscribirse de evento
|
|
96
|
-
* @param {string} eventName - Nombre del evento
|
|
97
|
-
* @param {Function} callback - Callback a remover
|
|
98
203
|
*/
|
|
99
204
|
off(eventName, callback) {
|
|
100
205
|
const listeners = this.listeners.get(eventName);
|
|
101
206
|
if (listeners) {
|
|
102
207
|
listeners.delete(callback);
|
|
103
|
-
|
|
104
|
-
// Limpiar si no quedan listeners
|
|
105
208
|
if (listeners.size === 0) {
|
|
106
209
|
this.listeners.delete(eventName);
|
|
107
210
|
}
|
|
108
|
-
|
|
109
211
|
this.stats.subscriptions--;
|
|
110
212
|
}
|
|
111
213
|
}
|
|
112
214
|
|
|
113
215
|
/**
|
|
114
216
|
* 🎯 ONCE: Suscribirse una sola vez
|
|
115
|
-
* @param {string} eventName - Nombre del evento
|
|
116
|
-
* @param {Function} callback - Callback a ejecutar
|
|
117
|
-
* @returns {Function} Función para desuscribirse
|
|
118
217
|
*/
|
|
119
218
|
once(eventName, callback) {
|
|
120
219
|
const wrappedCallback = (event) => {
|
|
121
220
|
callback(event);
|
|
122
221
|
this.off(eventName, wrappedCallback);
|
|
123
222
|
};
|
|
124
|
-
|
|
125
223
|
return this.on(eventName, wrappedCallback);
|
|
126
224
|
}
|
|
127
225
|
|
|
128
226
|
/**
|
|
129
|
-
* 🌟 WILDCARD LISTENERS
|
|
130
|
-
* @param {string} eventName - Nombre del evento emitido
|
|
131
|
-
* @param {Object} event - Objeto del evento
|
|
227
|
+
* 🌟 WILDCARD LISTENERS
|
|
132
228
|
*/
|
|
133
229
|
notifyWildcardListeners(eventName, event) {
|
|
134
230
|
for (const [pattern, listeners] of this.listeners) {
|
|
@@ -145,41 +241,29 @@ export class WuEventBus {
|
|
|
145
241
|
}
|
|
146
242
|
|
|
147
243
|
/**
|
|
148
|
-
* 🎯 MATCHES WILDCARD
|
|
149
|
-
* @param {string} eventName - Nombre del evento
|
|
150
|
-
* @param {string} pattern - Patrón con wildcards
|
|
151
|
-
* @returns {boolean}
|
|
244
|
+
* 🎯 MATCHES WILDCARD
|
|
152
245
|
*/
|
|
153
246
|
matchesWildcard(eventName, pattern) {
|
|
154
|
-
// Si no hay wildcard, ya se procesó en listeners exactos
|
|
155
247
|
if (!pattern.includes('*')) return false;
|
|
156
|
-
|
|
157
|
-
// Convertir pattern a regex
|
|
158
248
|
const regexPattern = pattern
|
|
159
249
|
.replace(/\./g, '\\.')
|
|
160
250
|
.replace(/\*/g, '.*');
|
|
161
|
-
|
|
162
251
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
163
252
|
return regex.test(eventName);
|
|
164
253
|
}
|
|
165
254
|
|
|
166
255
|
/**
|
|
167
|
-
* 📝 ADD TO HISTORY
|
|
168
|
-
* @param {Object} event - Evento
|
|
256
|
+
* 📝 ADD TO HISTORY
|
|
169
257
|
*/
|
|
170
258
|
addToHistory(event) {
|
|
171
259
|
this.history.push(event);
|
|
172
|
-
|
|
173
|
-
// Mantener tamaño máximo
|
|
174
260
|
if (this.history.length > this.config.maxHistory) {
|
|
175
261
|
this.history.shift();
|
|
176
262
|
}
|
|
177
263
|
}
|
|
178
264
|
|
|
179
265
|
/**
|
|
180
|
-
* 🔄 REPLAY
|
|
181
|
-
* @param {string} eventNameOrPattern - Nombre o patrón de eventos a reproducir
|
|
182
|
-
* @param {Function} callback - Callback para cada evento
|
|
266
|
+
* 🔄 REPLAY
|
|
183
267
|
*/
|
|
184
268
|
replay(eventNameOrPattern, callback) {
|
|
185
269
|
const events = this.history.filter(event => {
|
|
@@ -189,8 +273,6 @@ export class WuEventBus {
|
|
|
189
273
|
return event.name === eventNameOrPattern;
|
|
190
274
|
});
|
|
191
275
|
|
|
192
|
-
console.log(`[WuEventBus] 🔄 Replaying ${events.length} events for ${eventNameOrPattern}`);
|
|
193
|
-
|
|
194
276
|
events.forEach(event => {
|
|
195
277
|
try {
|
|
196
278
|
callback(event);
|
|
@@ -201,13 +283,11 @@ export class WuEventBus {
|
|
|
201
283
|
}
|
|
202
284
|
|
|
203
285
|
/**
|
|
204
|
-
* 🧹 CLEAR HISTORY
|
|
205
|
-
* @param {string} eventNameOrPattern - Patrón de eventos a limpiar (opcional)
|
|
286
|
+
* 🧹 CLEAR HISTORY
|
|
206
287
|
*/
|
|
207
288
|
clearHistory(eventNameOrPattern) {
|
|
208
289
|
if (!eventNameOrPattern) {
|
|
209
290
|
this.history = [];
|
|
210
|
-
console.log('[WuEventBus] 🧹 Event history cleared');
|
|
211
291
|
return;
|
|
212
292
|
}
|
|
213
293
|
|
|
@@ -220,14 +300,14 @@ export class WuEventBus {
|
|
|
220
300
|
}
|
|
221
301
|
|
|
222
302
|
/**
|
|
223
|
-
* 📊 GET STATS
|
|
224
|
-
* @returns {Object}
|
|
303
|
+
* 📊 GET STATS
|
|
225
304
|
*/
|
|
226
305
|
getStats() {
|
|
227
306
|
return {
|
|
228
307
|
...this.stats,
|
|
229
308
|
activeListeners: this.listeners.size,
|
|
230
309
|
historySize: this.history.length,
|
|
310
|
+
authorizedApps: this.authorizedApps.size,
|
|
231
311
|
listenersByEvent: Array.from(this.listeners.entries()).map(([event, listeners]) => ({
|
|
232
312
|
event,
|
|
233
313
|
listeners: listeners.size
|
|
@@ -236,22 +316,31 @@ export class WuEventBus {
|
|
|
236
316
|
}
|
|
237
317
|
|
|
238
318
|
/**
|
|
239
|
-
* ⚙️ CONFIGURE
|
|
240
|
-
* @param {Object} config - Nueva configuración
|
|
319
|
+
* ⚙️ CONFIGURE
|
|
241
320
|
*/
|
|
242
321
|
configure(config) {
|
|
243
|
-
this.config = {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
322
|
+
this.config = { ...this.config, ...config };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 🔐 ENABLE STRICT MODE: Activar modo estricto de seguridad
|
|
327
|
+
*/
|
|
328
|
+
enableStrictMode() {
|
|
329
|
+
this.config.strictMode = true;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 🔓 DISABLE STRICT MODE
|
|
334
|
+
*/
|
|
335
|
+
disableStrictMode() {
|
|
336
|
+
this.config.strictMode = false;
|
|
247
337
|
}
|
|
248
338
|
|
|
249
339
|
/**
|
|
250
|
-
* 🗑️ REMOVE ALL
|
|
340
|
+
* 🗑️ REMOVE ALL
|
|
251
341
|
*/
|
|
252
342
|
removeAll() {
|
|
253
343
|
this.listeners.clear();
|
|
254
344
|
this.stats.subscriptions = 0;
|
|
255
|
-
console.log('[WuEventBus] 🗑️ All listeners removed');
|
|
256
345
|
}
|
|
257
346
|
}
|
package/src/core/wu-logger.js
CHANGED
|
@@ -7,7 +7,8 @@ export class WuLogger {
|
|
|
7
7
|
constructor() {
|
|
8
8
|
// Detectar entorno automáticamente
|
|
9
9
|
this.isDevelopment = this.detectEnvironment();
|
|
10
|
-
|
|
10
|
+
// En desarrollo: warn (menos ruido), en producción: error
|
|
11
|
+
this.logLevel = this.isDevelopment ? 'warn' : 'error';
|
|
11
12
|
|
|
12
13
|
this.levels = {
|
|
13
14
|
debug: 0,
|
|
@@ -116,4 +117,19 @@ export const wuLog = {
|
|
|
116
117
|
info: (...args) => logger.wuInfo(...args),
|
|
117
118
|
warn: (...args) => logger.wuWarn(...args),
|
|
118
119
|
error: (...args) => logger.wuError(...args)
|
|
119
|
-
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 🔇 Silenciar todos los logs de Wu Framework
|
|
124
|
+
* Útil en producción para eliminar todo el ruido
|
|
125
|
+
*/
|
|
126
|
+
export function silenceAllLogs() {
|
|
127
|
+
logger.setLevel('silent');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 🔊 Restaurar logs (nivel debug)
|
|
132
|
+
*/
|
|
133
|
+
export function enableAllLogs() {
|
|
134
|
+
logger.setLevel('debug');
|
|
135
|
+
}
|