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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wu-framework",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "🚀 Universal Microfrontends Framework - Framework agnostic, zero config, Shadow DOM isolation",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,16 +1,10 @@
1
1
  /**
2
- * 💾 WU-CACHE: INTERNAL FRAMEWORK CACHING
2
+ * 💾 WU-CACHE: SECURE INTERNAL CACHING
3
3
  *
4
- * Sistema de caché INTERNO para recursos del framework:
5
- * - Manifests de microfrontends
6
- * - Módulos cargados
7
- * - Configuraciones de apps
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', // 'memory' | 'localStorage' | 'sessionStorage'
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(); // key -> timestamp
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
- console.log('[WuCache] 💾 Advanced cache system initialized');
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,
@@ -1,46 +1,156 @@
1
1
  /**
2
- * 📡 WU-EVENT-BUS: ADVANCED PUB/SUB SYSTEM
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
- * - Performance optimizado
9
+ * - Verificación de apps autorizadas
10
10
  */
11
11
 
12
12
  export class WuEventBus {
13
13
  constructor() {
14
- this.listeners = new Map(); // eventName -> Set<callback>
15
- this.history = []; // Event history para replay
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
- console.log('[WuEventBus] 📡 Advanced event bus initialized');
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
- * 📢 EMIT: Emitir evento
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 - Opciones { appName, timestamp, meta }
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: options.appName || 'unknown',
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: Notificar listeners con wildcards
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: Verificar si evento coincide con patrón 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: Agregar evento al historial
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: Reproducir eventos del historial
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: Limpiar historial de eventos
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: Obtener estadísticas
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: Configurar event bus
240
- * @param {Object} config - Nueva configuración
319
+ * ⚙️ CONFIGURE
241
320
  */
242
321
  configure(config) {
243
- this.config = {
244
- ...this.config,
245
- ...config
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: Remover todos los listeners
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
  }
@@ -7,7 +7,8 @@ export class WuLogger {
7
7
  constructor() {
8
8
  // Detectar entorno automáticamente
9
9
  this.isDevelopment = this.detectEnvironment();
10
- this.logLevel = this.isDevelopment ? 'debug' : 'error';
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
+ }