wu-framework 1.1.15 โ 1.1.16
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/LICENSE +39 -39
- package/README.md +408 -408
- package/dist/wu-framework.cjs.js.map +1 -1
- package/dist/wu-framework.dev.js +15151 -15151
- package/dist/wu-framework.dev.js.map +1 -1
- package/dist/wu-framework.esm.js.map +1 -1
- package/dist/wu-framework.umd.js.map +1 -1
- package/integrations/astro/README.md +127 -127
- package/integrations/astro/WuApp.astro +63 -63
- package/integrations/astro/WuShell.astro +39 -39
- package/integrations/astro/index.js +68 -68
- package/integrations/astro/package.json +38 -38
- package/integrations/astro/types.d.ts +53 -53
- package/package.json +161 -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 +477 -477
- package/src/core/wu-core.js +1398 -1398
- package/src/core/wu-error-boundary.js +382 -382
- package/src/core/wu-event-bus.js +348 -348
- package/src/core/wu-hooks.js +350 -350
- package/src/core/wu-html-parser.js +190 -190
- package/src/core/wu-iframe-sandbox.js +328 -328
- package/src/core/wu-loader.js +272 -272
- package/src/core/wu-logger.js +134 -134
- package/src/core/wu-manifest.js +509 -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 +348 -348
- package/src/core/wu-prefetch.js +414 -414
- package/src/core/wu-proxy-sandbox.js +476 -476
- package/src/core/wu-sandbox.js +779 -779
- package/src/core/wu-script-executor.js +113 -113
- package/src/core/wu-snapshot-sandbox.js +227 -227
- package/src/core/wu-strategies.js +256 -256
- package/src/core/wu-style-bridge.js +477 -477
- package/src/index.js +224 -224
- package/src/utils/dependency-resolver.js +327 -327
package/src/core/wu-event-bus.js
CHANGED
|
@@ -1,348 +1,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
|
-
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
|
+
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
|
+
}
|