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.
Files changed (88) hide show
  1. package/README.md +52 -20
  2. package/dist/wu-framework.cjs.js +1 -1
  3. package/dist/wu-framework.cjs.js.map +1 -1
  4. package/dist/wu-framework.dev.js +15511 -15146
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js +1 -1
  7. package/dist/wu-framework.esm.js.map +1 -1
  8. package/dist/wu-framework.umd.js +1 -1
  9. package/dist/wu-framework.umd.js.map +1 -1
  10. package/package.json +166 -161
  11. package/src/adapters/angular/ai.js +30 -30
  12. package/src/adapters/angular/index.d.ts +154 -154
  13. package/src/adapters/angular/index.js +932 -932
  14. package/src/adapters/angular.d.ts +3 -3
  15. package/src/adapters/angular.js +3 -3
  16. package/src/adapters/index.js +168 -168
  17. package/src/adapters/lit/ai.js +20 -20
  18. package/src/adapters/lit/index.d.ts +120 -120
  19. package/src/adapters/lit/index.js +721 -721
  20. package/src/adapters/lit.d.ts +3 -3
  21. package/src/adapters/lit.js +3 -3
  22. package/src/adapters/preact/ai.js +33 -33
  23. package/src/adapters/preact/index.d.ts +108 -108
  24. package/src/adapters/preact/index.js +661 -661
  25. package/src/adapters/preact.d.ts +3 -3
  26. package/src/adapters/preact.js +3 -3
  27. package/src/adapters/react/index.js +48 -54
  28. package/src/adapters/react.d.ts +3 -3
  29. package/src/adapters/react.js +3 -3
  30. package/src/adapters/shared.js +64 -64
  31. package/src/adapters/solid/ai.js +32 -32
  32. package/src/adapters/solid/index.d.ts +101 -101
  33. package/src/adapters/solid/index.js +586 -586
  34. package/src/adapters/solid.d.ts +3 -3
  35. package/src/adapters/solid.js +3 -3
  36. package/src/adapters/svelte/ai.js +31 -31
  37. package/src/adapters/svelte/index.d.ts +166 -166
  38. package/src/adapters/svelte/index.js +798 -798
  39. package/src/adapters/svelte.d.ts +3 -3
  40. package/src/adapters/svelte.js +3 -3
  41. package/src/adapters/vanilla/ai.js +30 -30
  42. package/src/adapters/vanilla/index.d.ts +179 -179
  43. package/src/adapters/vanilla/index.js +785 -785
  44. package/src/adapters/vanilla.d.ts +3 -3
  45. package/src/adapters/vanilla.js +3 -3
  46. package/src/adapters/vue/ai.js +52 -52
  47. package/src/adapters/vue/index.d.ts +299 -299
  48. package/src/adapters/vue/index.js +610 -610
  49. package/src/adapters/vue.d.ts +3 -3
  50. package/src/adapters/vue.js +3 -3
  51. package/src/ai/wu-ai-actions.js +261 -261
  52. package/src/ai/wu-ai-agent.js +546 -546
  53. package/src/ai/wu-ai-browser-primitives.js +354 -354
  54. package/src/ai/wu-ai-browser.js +380 -380
  55. package/src/ai/wu-ai-context.js +332 -332
  56. package/src/ai/wu-ai-conversation.js +613 -613
  57. package/src/ai/wu-ai-orchestrate.js +1021 -1021
  58. package/src/ai/wu-ai-permissions.js +381 -381
  59. package/src/ai/wu-ai-provider.js +700 -700
  60. package/src/ai/wu-ai-schema.js +225 -225
  61. package/src/ai/wu-ai-triggers.js +396 -396
  62. package/src/ai/wu-ai.js +804 -804
  63. package/src/core/wu-app.js +236 -236
  64. package/src/core/wu-cache.js +498 -477
  65. package/src/core/wu-core.js +1412 -1398
  66. package/src/core/wu-error-boundary.js +396 -382
  67. package/src/core/wu-event-bus.js +390 -348
  68. package/src/core/wu-hooks.js +350 -350
  69. package/src/core/wu-html-parser.js +199 -190
  70. package/src/core/wu-iframe-sandbox.js +328 -328
  71. package/src/core/wu-loader.js +385 -273
  72. package/src/core/wu-logger.js +142 -134
  73. package/src/core/wu-manifest.js +532 -509
  74. package/src/core/wu-mcp-bridge.js +432 -432
  75. package/src/core/wu-overrides.js +510 -510
  76. package/src/core/wu-performance.js +228 -228
  77. package/src/core/wu-plugin.js +401 -348
  78. package/src/core/wu-prefetch.js +414 -414
  79. package/src/core/wu-proxy-sandbox.js +477 -476
  80. package/src/core/wu-sandbox.js +779 -779
  81. package/src/core/wu-script-executor.js +161 -113
  82. package/src/core/wu-snapshot-sandbox.js +227 -227
  83. package/src/core/wu-store.js +13 -3
  84. package/src/core/wu-strategies.js +256 -256
  85. package/src/core/wu-style-bridge.js +477 -477
  86. package/src/index.d.ts +317 -0
  87. package/src/index.js +234 -224
  88. package/src/utils/dependency-resolver.js +327 -327
@@ -1,348 +1,390 @@
1
- /**
2
- * ๐Ÿ“ก WU-EVENT-BUS: SECURE PUB/SUB SYSTEM
3
- *
4
- * Sistema de eventos para comunicaciรณn entre microfrontends
5
- * - Pub/Sub pattern con validaciรณn de origen
6
- * - Event namespaces
7
- * - Wildcards
8
- * - Event replay
9
- * - Verificaciรณn de apps autorizadas
10
- */
11
- import { logger } from './wu-logger.js';
12
-
13
-
14
- export class WuEventBus {
15
- constructor() {
16
- this.listeners = new Map();
17
- this.history = [];
18
-
19
- // ๐Ÿ” SEGURIDAD: Registro de apps autorizadas con tokens
20
- this.authorizedApps = new Map(); // appName -> { token, permissions }
21
- this.trustedEvents = new Set(['wu:*', 'system:*']); // Eventos del sistema
22
-
23
- this.config = {
24
- maxHistory: 100,
25
- enableReplay: true,
26
- enableWildcards: true,
27
- logEvents: false,
28
- // ๐Ÿ” Opciones de seguridad
29
- strictMode: false, // Si true, rechaza eventos de apps no autorizadas
30
- validateOrigin: true // Valida que appName sea una app registrada
31
- };
32
-
33
- this.stats = {
34
- emitted: 0,
35
- subscriptions: 0,
36
- rejected: 0 // Eventos rechazados por seguridad
37
- };
38
- }
39
-
40
- /**
41
- * ๐Ÿ” REGISTER APP: Registrar app autorizada para emitir eventos
42
- * @param {string} appName - Nombre de la app
43
- * @param {Object} options - { permissions: ['event:*'], token }
44
- * @returns {string} Token de autorizaciรณn
45
- */
46
- registerApp(appName, options = {}) {
47
- const token = options.token || this._generateToken();
48
-
49
- this.authorizedApps.set(appName, {
50
- token,
51
- permissions: options.permissions || ['*'], // Por defecto puede emitir todo
52
- registeredAt: Date.now()
53
- });
54
-
55
- return token;
56
- }
57
-
58
- /**
59
- * ๐Ÿ”“ UNREGISTER APP: Desregistrar app
60
- * @param {string} appName
61
- */
62
- unregisterApp(appName) {
63
- this.authorizedApps.delete(appName);
64
- }
65
-
66
- /**
67
- * ๐Ÿ” VALIDATE ORIGIN: Verificar que el emisor estรก autorizado
68
- * @param {string} eventName
69
- * @param {string} appName
70
- * @param {string} token
71
- * @returns {boolean}
72
- */
73
- _validateOrigin(eventName, appName, token) {
74
- // Eventos del sistema siempre permitidos
75
- if (this._isSystemEvent(eventName)) {
76
- return true;
77
- }
78
-
79
- // Si no estรก en modo estricto, permitir todo
80
- if (!this.config.strictMode) {
81
- return true;
82
- }
83
-
84
- // Verificar que la app estรฉ registrada
85
- const appInfo = this.authorizedApps.get(appName);
86
- if (!appInfo) {
87
- return false;
88
- }
89
-
90
- // Verificar token si se proporciona
91
- if (token && appInfo.token !== token) {
92
- return false;
93
- }
94
-
95
- // Verificar permisos
96
- return this._hasPermission(appInfo.permissions, eventName);
97
- }
98
-
99
- /**
100
- * ๐Ÿ” HAS PERMISSION: Verificar si la app tiene permiso para el evento
101
- */
102
- _hasPermission(permissions, eventName) {
103
- if (permissions.includes('*')) return true;
104
-
105
- return permissions.some(pattern => {
106
- if (pattern === eventName) return true;
107
- if (pattern.includes('*')) {
108
- return this.matchesWildcard(eventName, pattern);
109
- }
110
- return false;
111
- });
112
- }
113
-
114
- /**
115
- * ๐Ÿ” IS SYSTEM EVENT: Verificar si es un evento del sistema
116
- */
117
- _isSystemEvent(eventName) {
118
- return eventName.startsWith('wu:') ||
119
- eventName.startsWith('system:') ||
120
- eventName.startsWith('app:');
121
- }
122
-
123
- /**
124
- * ๐Ÿ” GENERATE TOKEN: Generar token รบnico
125
- */
126
- _generateToken() {
127
- return `wu_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
128
- }
129
-
130
- /**
131
- * ๐Ÿ“ข EMIT: Emitir evento con validaciรณn de origen
132
- * @param {string} eventName - Nombre del evento
133
- * @param {*} data - Datos del evento
134
- * @param {Object} options - { appName, timestamp, meta, token }
135
- */
136
- emit(eventName, data, options = {}) {
137
- const appName = options.appName || 'unknown';
138
-
139
- // ๐Ÿ” Validar origen si estรก habilitado
140
- if (this.config.validateOrigin && this.config.strictMode) {
141
- if (!this._validateOrigin(eventName, appName, options.token)) {
142
- this.stats.rejected++;
143
- logger.warn(`[WuEventBus] ๐Ÿšซ Event rejected: ${eventName} from ${appName} (unauthorized)`);
144
- return false;
145
- }
146
- }
147
-
148
- const event = {
149
- name: eventName,
150
- data,
151
- timestamp: options.timestamp || Date.now(),
152
- appName,
153
- meta: options.meta || {},
154
- // ๐Ÿ” Marcar si el origen fue verificado
155
- verified: this.authorizedApps.has(appName)
156
- };
157
-
158
- // Agregar a historial
159
- if (this.config.enableReplay) {
160
- this.addToHistory(event);
161
- }
162
-
163
- // Log si estรก habilitado
164
- if (this.config.logEvents) {
165
- logger.debug(`[WuEventBus] ๐Ÿ“ข ${eventName}`, data);
166
- }
167
-
168
- // Notificar listeners exactos
169
- const exactListeners = this.listeners.get(eventName);
170
- if (exactListeners) {
171
- exactListeners.forEach(callback => {
172
- try {
173
- callback(event);
174
- } catch (error) {
175
- console.error(`[WuEventBus] โŒ Error in listener for ${eventName}:`, error);
176
- }
177
- });
178
- }
179
-
180
- // Notificar listeners con wildcards
181
- if (this.config.enableWildcards) {
182
- this.notifyWildcardListeners(eventName, event);
183
- }
184
-
185
- this.stats.emitted++;
186
- return true;
187
- }
188
-
189
- /**
190
- * ๐Ÿ‘‚ ON: Suscribirse a evento
191
- */
192
- on(eventName, callback) {
193
- if (!this.listeners.has(eventName)) {
194
- this.listeners.set(eventName, new Set());
195
- }
196
-
197
- this.listeners.get(eventName).add(callback);
198
- this.stats.subscriptions++;
199
-
200
- return () => this.off(eventName, callback);
201
- }
202
-
203
- /**
204
- * ๐Ÿ”‡ OFF: Desuscribirse de evento
205
- */
206
- off(eventName, callback) {
207
- const listeners = this.listeners.get(eventName);
208
- if (listeners) {
209
- listeners.delete(callback);
210
- if (listeners.size === 0) {
211
- this.listeners.delete(eventName);
212
- }
213
- this.stats.subscriptions--;
214
- }
215
- }
216
-
217
- /**
218
- * ๐ŸŽฏ ONCE: Suscribirse una sola vez
219
- */
220
- once(eventName, callback) {
221
- const wrappedCallback = (event) => {
222
- callback(event);
223
- this.off(eventName, wrappedCallback);
224
- };
225
- return this.on(eventName, wrappedCallback);
226
- }
227
-
228
- /**
229
- * ๐ŸŒŸ WILDCARD LISTENERS
230
- */
231
- notifyWildcardListeners(eventName, event) {
232
- for (const [pattern, listeners] of this.listeners) {
233
- if (this.matchesWildcard(eventName, pattern)) {
234
- listeners.forEach(callback => {
235
- try {
236
- callback(event);
237
- } catch (error) {
238
- console.error(`[WuEventBus] โŒ Error in wildcard listener for ${pattern}:`, error);
239
- }
240
- });
241
- }
242
- }
243
- }
244
-
245
- /**
246
- * ๐ŸŽฏ MATCHES WILDCARD
247
- */
248
- matchesWildcard(eventName, pattern) {
249
- if (!pattern.includes('*')) return false;
250
- const regexPattern = pattern
251
- .replace(/\./g, '\\.')
252
- .replace(/\*/g, '.*');
253
- const regex = new RegExp(`^${regexPattern}$`);
254
- return regex.test(eventName);
255
- }
256
-
257
- /**
258
- * ๐Ÿ“ ADD TO HISTORY
259
- */
260
- addToHistory(event) {
261
- this.history.push(event);
262
- if (this.history.length > this.config.maxHistory) {
263
- this.history.shift();
264
- }
265
- }
266
-
267
- /**
268
- * ๐Ÿ”„ REPLAY
269
- */
270
- replay(eventNameOrPattern, callback) {
271
- const events = this.history.filter(event => {
272
- if (eventNameOrPattern.includes('*')) {
273
- return this.matchesWildcard(event.name, eventNameOrPattern);
274
- }
275
- return event.name === eventNameOrPattern;
276
- });
277
-
278
- events.forEach(event => {
279
- try {
280
- callback(event);
281
- } catch (error) {
282
- console.error(`[WuEventBus] โŒ Error replaying event:`, error);
283
- }
284
- });
285
- }
286
-
287
- /**
288
- * ๐Ÿงน CLEAR HISTORY
289
- */
290
- clearHistory(eventNameOrPattern) {
291
- if (!eventNameOrPattern) {
292
- this.history = [];
293
- return;
294
- }
295
-
296
- this.history = this.history.filter(event => {
297
- if (eventNameOrPattern.includes('*')) {
298
- return !this.matchesWildcard(event.name, eventNameOrPattern);
299
- }
300
- return event.name !== eventNameOrPattern;
301
- });
302
- }
303
-
304
- /**
305
- * ๐Ÿ“Š GET STATS
306
- */
307
- getStats() {
308
- return {
309
- ...this.stats,
310
- activeListeners: this.listeners.size,
311
- historySize: this.history.length,
312
- authorizedApps: this.authorizedApps.size,
313
- listenersByEvent: Array.from(this.listeners.entries()).map(([event, listeners]) => ({
314
- event,
315
- listeners: listeners.size
316
- }))
317
- };
318
- }
319
-
320
- /**
321
- * โš™๏ธ CONFIGURE
322
- */
323
- configure(config) {
324
- this.config = { ...this.config, ...config };
325
- }
326
-
327
- /**
328
- * ๐Ÿ” ENABLE STRICT MODE: Activar modo estricto de seguridad
329
- */
330
- enableStrictMode() {
331
- this.config.strictMode = true;
332
- }
333
-
334
- /**
335
- * ๐Ÿ”“ DISABLE STRICT MODE
336
- */
337
- disableStrictMode() {
338
- this.config.strictMode = false;
339
- }
340
-
341
- /**
342
- * ๐Ÿ—‘๏ธ REMOVE ALL
343
- */
344
- removeAll() {
345
- this.listeners.clear();
346
- this.stats.subscriptions = 0;
347
- }
348
- }
1
+ /**
2
+ * ๐Ÿ“ก WU-EVENT-BUS: SECURE PUB/SUB SYSTEM
3
+ *
4
+ * Sistema de eventos para comunicaciรณn entre microfrontends
5
+ * - Pub/Sub pattern con validaciรณn de origen
6
+ * - Event namespaces
7
+ * - Wildcards
8
+ * - Event replay
9
+ * - Verificaciรณn de apps autorizadas
10
+ */
11
+ import { logger } from './wu-logger.js';
12
+
13
+ /**
14
+ * @typedef {Object} WuEvent
15
+ * @property {string} name - Event name
16
+ * @property {*} data - Event payload
17
+ * @property {number} timestamp - Event timestamp
18
+ * @property {string} appName - Source app name
19
+ * @property {Object} meta - Additional metadata
20
+ * @property {boolean} verified - Whether origin was verified
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object} WuEventBusConfig
25
+ * @property {number} [maxHistory=100] - Maximum events in history
26
+ * @property {boolean} [enableReplay=true] - Enable event replay
27
+ * @property {boolean} [enableWildcards=true] - Enable wildcard matching
28
+ * @property {boolean} [logEvents=false] - Log all events
29
+ * @property {boolean} [strictMode=false] - Reject unauthorized events
30
+ * @property {boolean} [validateOrigin=true] - Validate event origins
31
+ */
32
+
33
+ export class WuEventBus {
34
+ constructor() {
35
+ this.listeners = new Map();
36
+ this.history = [];
37
+
38
+ // ๐Ÿ” SEGURIDAD: Registro de apps autorizadas con tokens
39
+ this.authorizedApps = new Map(); // appName -> { token, permissions }
40
+ this.trustedEvents = new Set(['wu:*', 'system:*']); // Eventos del sistema
41
+
42
+ // Auto-detect production environment for strictMode default
43
+ const isProduction = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
44
+
45
+ this.config = {
46
+ maxHistory: 100,
47
+ enableReplay: true,
48
+ enableWildcards: true,
49
+ logEvents: false,
50
+ // ๐Ÿ” Opciones de seguridad
51
+ strictMode: isProduction, // Auto-enabled in production, permissive in development
52
+ validateOrigin: true // Valida que appName sea una app registrada
53
+ };
54
+
55
+ this._permissiveWarned = false;
56
+
57
+ this.stats = {
58
+ emitted: 0,
59
+ subscriptions: 0,
60
+ rejected: 0 // Eventos rechazados por seguridad
61
+ };
62
+ }
63
+
64
+ /**
65
+ * ๐Ÿ” REGISTER APP: Registrar app autorizada para emitir eventos
66
+ * @param {string} appName - Nombre de la app
67
+ * @param {Object} options - { permissions: ['event:*'], token }
68
+ * @returns {string} Token de autorizaciรณn
69
+ */
70
+ registerApp(appName, options = {}) {
71
+ const token = options.token || this._generateToken();
72
+
73
+ this.authorizedApps.set(appName, {
74
+ token,
75
+ permissions: options.permissions || ['*'], // Por defecto puede emitir todo
76
+ registeredAt: Date.now()
77
+ });
78
+
79
+ return token;
80
+ }
81
+
82
+ /**
83
+ * ๐Ÿ”“ UNREGISTER APP: Desregistrar app
84
+ * @param {string} appName
85
+ */
86
+ unregisterApp(appName) {
87
+ this.authorizedApps.delete(appName);
88
+ }
89
+
90
+ /**
91
+ * ๐Ÿ” VALIDATE ORIGIN: Verificar que el emisor estรก autorizado
92
+ * @param {string} eventName
93
+ * @param {string} appName
94
+ * @param {string} token
95
+ * @returns {boolean}
96
+ */
97
+ _validateOrigin(eventName, appName, token) {
98
+ // Eventos del sistema siempre permitidos
99
+ if (this._isSystemEvent(eventName)) {
100
+ return true;
101
+ }
102
+
103
+ // Si no estรก en modo estricto, permitir todo
104
+ if (!this.config.strictMode) {
105
+ return true;
106
+ }
107
+
108
+ // Verificar que la app estรฉ registrada
109
+ const appInfo = this.authorizedApps.get(appName);
110
+ if (!appInfo) {
111
+ return false;
112
+ }
113
+
114
+ // Verificar token si se proporciona
115
+ if (token && appInfo.token !== token) {
116
+ return false;
117
+ }
118
+
119
+ // Verificar permisos
120
+ return this._hasPermission(appInfo.permissions, eventName);
121
+ }
122
+
123
+ /**
124
+ * ๐Ÿ” HAS PERMISSION: Verificar si la app tiene permiso para el evento
125
+ */
126
+ _hasPermission(permissions, eventName) {
127
+ if (permissions.includes('*')) return true;
128
+
129
+ return permissions.some(pattern => {
130
+ if (pattern === eventName) return true;
131
+ if (pattern.includes('*')) {
132
+ return this.matchesWildcard(eventName, pattern);
133
+ }
134
+ return false;
135
+ });
136
+ }
137
+
138
+ /**
139
+ * ๐Ÿ” IS SYSTEM EVENT: Verificar si es un evento del sistema
140
+ */
141
+ _isSystemEvent(eventName) {
142
+ return eventName.startsWith('wu:') ||
143
+ eventName.startsWith('system:') ||
144
+ eventName.startsWith('app:');
145
+ }
146
+
147
+ /**
148
+ * ๐Ÿ” GENERATE TOKEN: Generar token รบnico
149
+ */
150
+ _generateToken() {
151
+ return `wu_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
152
+ }
153
+
154
+ /**
155
+ * WARN PERMISSIVE MODE: Log a one-time warning when strictMode is off
156
+ * Alerts developers that events are flowing without authorization checks
157
+ */
158
+ _warnPermissiveMode() {
159
+ if (this._permissiveWarned) return;
160
+ this._permissiveWarned = true;
161
+ logger.warn(
162
+ '[WuEventBus] strictMode is disabled. Events are emitted without authorization checks. ' +
163
+ 'Enable strictMode for production by calling enableStrictMode() or setting NODE_ENV=production.'
164
+ );
165
+ }
166
+
167
+ /**
168
+ * ๐Ÿ“ข EMIT: Emitir evento con validaciรณn de origen
169
+ * @param {string} eventName - Nombre del evento
170
+ * @param {*} data - Datos del evento
171
+ * @param {Object} options - { appName, timestamp, meta, token }
172
+ */
173
+ emit(eventName, data, options = {}) {
174
+ const appName = options.appName || 'unknown';
175
+
176
+ // Warn once if running in permissive mode (strictMode off)
177
+ if (!this.config.strictMode) {
178
+ this._warnPermissiveMode();
179
+ }
180
+
181
+ // ๐Ÿ” Validar origen si estรก habilitado
182
+ if (this.config.validateOrigin && this.config.strictMode) {
183
+ if (!this._validateOrigin(eventName, appName, options.token)) {
184
+ this.stats.rejected++;
185
+ logger.warn(`[WuEventBus] ๐Ÿšซ Event rejected: ${eventName} from ${appName} (unauthorized)`);
186
+ return false;
187
+ }
188
+ }
189
+
190
+ const event = {
191
+ name: eventName,
192
+ data,
193
+ timestamp: options.timestamp || Date.now(),
194
+ appName,
195
+ meta: options.meta || {},
196
+ // ๐Ÿ” Marcar si el origen fue verificado
197
+ verified: this.authorizedApps.has(appName)
198
+ };
199
+
200
+ // Agregar a historial
201
+ if (this.config.enableReplay) {
202
+ this.addToHistory(event);
203
+ }
204
+
205
+ // Log si estรก habilitado
206
+ if (this.config.logEvents) {
207
+ logger.debug(`[WuEventBus] ๐Ÿ“ข ${eventName}`, data);
208
+ }
209
+
210
+ // Notificar listeners exactos
211
+ const exactListeners = this.listeners.get(eventName);
212
+ if (exactListeners) {
213
+ exactListeners.forEach(callback => {
214
+ try {
215
+ callback(event);
216
+ } catch (error) {
217
+ console.error(`[WuEventBus] โŒ Error in listener for ${eventName}:`, error);
218
+ }
219
+ });
220
+ }
221
+
222
+ // Notificar listeners con wildcards
223
+ if (this.config.enableWildcards) {
224
+ this.notifyWildcardListeners(eventName, event);
225
+ }
226
+
227
+ this.stats.emitted++;
228
+ return true;
229
+ }
230
+
231
+ /**
232
+ * ๐Ÿ‘‚ ON: Suscribirse a evento
233
+ */
234
+ on(eventName, callback) {
235
+ if (!this.listeners.has(eventName)) {
236
+ this.listeners.set(eventName, new Set());
237
+ }
238
+
239
+ this.listeners.get(eventName).add(callback);
240
+ this.stats.subscriptions++;
241
+
242
+ return () => this.off(eventName, callback);
243
+ }
244
+
245
+ /**
246
+ * ๐Ÿ”‡ OFF: Desuscribirse de evento
247
+ */
248
+ off(eventName, callback) {
249
+ const listeners = this.listeners.get(eventName);
250
+ if (listeners) {
251
+ listeners.delete(callback);
252
+ if (listeners.size === 0) {
253
+ this.listeners.delete(eventName);
254
+ }
255
+ this.stats.subscriptions--;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * ๐ŸŽฏ ONCE: Suscribirse una sola vez
261
+ */
262
+ once(eventName, callback) {
263
+ const wrappedCallback = (event) => {
264
+ callback(event);
265
+ this.off(eventName, wrappedCallback);
266
+ };
267
+ return this.on(eventName, wrappedCallback);
268
+ }
269
+
270
+ /**
271
+ * ๐ŸŒŸ WILDCARD LISTENERS
272
+ */
273
+ notifyWildcardListeners(eventName, event) {
274
+ for (const [pattern, listeners] of this.listeners) {
275
+ if (this.matchesWildcard(eventName, pattern)) {
276
+ listeners.forEach(callback => {
277
+ try {
278
+ callback(event);
279
+ } catch (error) {
280
+ console.error(`[WuEventBus] โŒ Error in wildcard listener for ${pattern}:`, error);
281
+ }
282
+ });
283
+ }
284
+ }
285
+ }
286
+
287
+ /**
288
+ * ๐ŸŽฏ MATCHES WILDCARD
289
+ */
290
+ matchesWildcard(eventName, pattern) {
291
+ if (!pattern.includes('*')) return false;
292
+ const regexPattern = pattern
293
+ .replace(/\./g, '\\.')
294
+ .replace(/\*/g, '.*');
295
+ const regex = new RegExp(`^${regexPattern}$`);
296
+ return regex.test(eventName);
297
+ }
298
+
299
+ /**
300
+ * ๐Ÿ“ ADD TO HISTORY
301
+ */
302
+ addToHistory(event) {
303
+ this.history.push(event);
304
+ if (this.history.length > this.config.maxHistory) {
305
+ this.history.shift();
306
+ }
307
+ }
308
+
309
+ /**
310
+ * ๐Ÿ”„ REPLAY
311
+ */
312
+ replay(eventNameOrPattern, callback) {
313
+ const events = this.history.filter(event => {
314
+ if (eventNameOrPattern.includes('*')) {
315
+ return this.matchesWildcard(event.name, eventNameOrPattern);
316
+ }
317
+ return event.name === eventNameOrPattern;
318
+ });
319
+
320
+ events.forEach(event => {
321
+ try {
322
+ callback(event);
323
+ } catch (error) {
324
+ console.error(`[WuEventBus] โŒ Error replaying event:`, error);
325
+ }
326
+ });
327
+ }
328
+
329
+ /**
330
+ * ๐Ÿงน CLEAR HISTORY
331
+ */
332
+ clearHistory(eventNameOrPattern) {
333
+ if (!eventNameOrPattern) {
334
+ this.history = [];
335
+ return;
336
+ }
337
+
338
+ this.history = this.history.filter(event => {
339
+ if (eventNameOrPattern.includes('*')) {
340
+ return !this.matchesWildcard(event.name, eventNameOrPattern);
341
+ }
342
+ return event.name !== eventNameOrPattern;
343
+ });
344
+ }
345
+
346
+ /**
347
+ * ๐Ÿ“Š GET STATS
348
+ */
349
+ getStats() {
350
+ return {
351
+ ...this.stats,
352
+ activeListeners: this.listeners.size,
353
+ historySize: this.history.length,
354
+ authorizedApps: this.authorizedApps.size,
355
+ listenersByEvent: Array.from(this.listeners.entries()).map(([event, listeners]) => ({
356
+ event,
357
+ listeners: listeners.size
358
+ }))
359
+ };
360
+ }
361
+
362
+ /**
363
+ * โš™๏ธ CONFIGURE
364
+ */
365
+ configure(config) {
366
+ this.config = { ...this.config, ...config };
367
+ }
368
+
369
+ /**
370
+ * ๐Ÿ” ENABLE STRICT MODE: Activar modo estricto de seguridad
371
+ */
372
+ enableStrictMode() {
373
+ this.config.strictMode = true;
374
+ }
375
+
376
+ /**
377
+ * ๐Ÿ”“ DISABLE STRICT MODE
378
+ */
379
+ disableStrictMode() {
380
+ this.config.strictMode = false;
381
+ }
382
+
383
+ /**
384
+ * ๐Ÿ—‘๏ธ REMOVE ALL
385
+ */
386
+ removeAll() {
387
+ this.listeners.clear();
388
+ this.stats.subscriptions = 0;
389
+ }
390
+ }