wu-framework 1.0.0
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 +21 -0
- package/README.md +559 -0
- package/package.json +84 -0
- package/src/api/wu-simple.js +316 -0
- package/src/core/wu-app.js +192 -0
- package/src/core/wu-cache.js +374 -0
- package/src/core/wu-core.js +1296 -0
- package/src/core/wu-error-boundary.js +380 -0
- package/src/core/wu-event-bus.js +257 -0
- package/src/core/wu-hooks.js +348 -0
- package/src/core/wu-html-parser.js +280 -0
- package/src/core/wu-loader.js +271 -0
- package/src/core/wu-logger.js +119 -0
- package/src/core/wu-manifest.js +366 -0
- package/src/core/wu-performance.js +226 -0
- package/src/core/wu-plugin.js +213 -0
- package/src/core/wu-proxy-sandbox.js +153 -0
- package/src/core/wu-registry.js +130 -0
- package/src/core/wu-sandbox-pool.js +390 -0
- package/src/core/wu-sandbox.js +720 -0
- package/src/core/wu-script-executor.js +216 -0
- package/src/core/wu-snapshot-sandbox.js +184 -0
- package/src/core/wu-store.js +297 -0
- package/src/core/wu-strategies.js +241 -0
- package/src/core/wu-style-bridge.js +357 -0
- package/src/index.js +690 -0
- package/src/utils/dependency-resolver.js +326 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔌 WU-PLUGIN: EXTENSIBLE PLUGIN SYSTEM
|
|
3
|
+
*
|
|
4
|
+
* Sistema de plugins para extender wu-framework sin modificar el core
|
|
5
|
+
* - Plugin lifecycle (install, beforeMount, afterMount, etc.)
|
|
6
|
+
* - Dependency injection
|
|
7
|
+
* - Plugin communication
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class WuPluginSystem {
|
|
11
|
+
constructor(core) {
|
|
12
|
+
this.core = core;
|
|
13
|
+
this.plugins = new Map(); // name -> plugin instance
|
|
14
|
+
this.hooks = new Map(); // hook name -> [callbacks]
|
|
15
|
+
|
|
16
|
+
// Hooks disponibles
|
|
17
|
+
this.availableHooks = [
|
|
18
|
+
'beforeInit',
|
|
19
|
+
'afterInit',
|
|
20
|
+
'beforeMount',
|
|
21
|
+
'afterMount',
|
|
22
|
+
'beforeUnmount',
|
|
23
|
+
'afterUnmount',
|
|
24
|
+
'onError',
|
|
25
|
+
'onDestroy'
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
// Inicializar hooks
|
|
29
|
+
this.availableHooks.forEach(hook => {
|
|
30
|
+
this.hooks.set(hook, []);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log('[WuPlugin] 🔌 Plugin system initialized');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 📦 USE: Instalar plugin
|
|
38
|
+
* @param {Object|Function} plugin - Plugin o factory function
|
|
39
|
+
* @param {Object} options - Opciones del plugin
|
|
40
|
+
*/
|
|
41
|
+
use(plugin, options = {}) {
|
|
42
|
+
// Si es una función, ejecutarla para obtener el plugin
|
|
43
|
+
if (typeof plugin === 'function') {
|
|
44
|
+
plugin = plugin(this.core, options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Validar plugin
|
|
48
|
+
if (!plugin.name) {
|
|
49
|
+
throw new Error('[WuPlugin] Plugin must have a name property');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Verificar si ya está instalado
|
|
53
|
+
if (this.plugins.has(plugin.name)) {
|
|
54
|
+
console.warn(`[WuPlugin] Plugin "${plugin.name}" already installed`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Ejecutar install del plugin
|
|
59
|
+
if (plugin.install) {
|
|
60
|
+
plugin.install(this.core, options);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Registrar hooks del plugin
|
|
64
|
+
this.availableHooks.forEach(hookName => {
|
|
65
|
+
if (typeof plugin[hookName] === 'function') {
|
|
66
|
+
this.registerHook(hookName, plugin[hookName].bind(plugin));
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Guardar plugin
|
|
71
|
+
this.plugins.set(plugin.name, {
|
|
72
|
+
plugin,
|
|
73
|
+
options,
|
|
74
|
+
installedAt: Date.now()
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
console.log(`[WuPlugin] ✅ Plugin "${plugin.name}" installed`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 🪝 REGISTER HOOK: Registrar callback en un hook
|
|
82
|
+
* @param {string} hookName - Nombre del hook
|
|
83
|
+
* @param {Function} callback - Función callback
|
|
84
|
+
*/
|
|
85
|
+
registerHook(hookName, callback) {
|
|
86
|
+
if (!this.hooks.has(hookName)) {
|
|
87
|
+
console.warn(`[WuPlugin] Unknown hook: ${hookName}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.hooks.get(hookName).push(callback);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 🎯 CALL HOOK: Ejecutar todos los callbacks de un hook
|
|
96
|
+
* @param {string} hookName - Nombre del hook
|
|
97
|
+
* @param {*} context - Contexto a pasar a los callbacks
|
|
98
|
+
* @returns {Promise<boolean>} false si algún callback cancela la operación
|
|
99
|
+
*/
|
|
100
|
+
async callHook(hookName, context) {
|
|
101
|
+
const callbacks = this.hooks.get(hookName) || [];
|
|
102
|
+
|
|
103
|
+
for (const callback of callbacks) {
|
|
104
|
+
try {
|
|
105
|
+
const result = await callback(context);
|
|
106
|
+
|
|
107
|
+
// Si retorna false, cancelar operación
|
|
108
|
+
if (result === false) {
|
|
109
|
+
console.log(`[WuPlugin] Hook ${hookName} cancelled by plugin`);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(`[WuPlugin] Error in hook ${hookName}:`, error);
|
|
114
|
+
|
|
115
|
+
// Llamar hook de error
|
|
116
|
+
await this.callHook('onError', { hookName, error, context });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 🗑️ UNINSTALL: Desinstalar plugin
|
|
125
|
+
* @param {string} pluginName - Nombre del plugin
|
|
126
|
+
*/
|
|
127
|
+
uninstall(pluginName) {
|
|
128
|
+
const pluginData = this.plugins.get(pluginName);
|
|
129
|
+
|
|
130
|
+
if (!pluginData) {
|
|
131
|
+
console.warn(`[WuPlugin] Plugin "${pluginName}" not found`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const { plugin } = pluginData;
|
|
136
|
+
|
|
137
|
+
// Ejecutar uninstall si existe
|
|
138
|
+
if (plugin.uninstall) {
|
|
139
|
+
plugin.uninstall(this.core);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Remover hooks del plugin
|
|
143
|
+
this.availableHooks.forEach(hookName => {
|
|
144
|
+
if (typeof plugin[hookName] === 'function') {
|
|
145
|
+
const callbacks = this.hooks.get(hookName);
|
|
146
|
+
const index = callbacks.indexOf(plugin[hookName]);
|
|
147
|
+
if (index > -1) {
|
|
148
|
+
callbacks.splice(index, 1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Remover plugin
|
|
154
|
+
this.plugins.delete(pluginName);
|
|
155
|
+
|
|
156
|
+
console.log(`[WuPlugin] ✅ Plugin "${pluginName}" uninstalled`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 📋 GET PLUGIN: Obtener plugin instalado
|
|
161
|
+
* @param {string} pluginName - Nombre del plugin
|
|
162
|
+
* @returns {Object}
|
|
163
|
+
*/
|
|
164
|
+
getPlugin(pluginName) {
|
|
165
|
+
return this.plugins.get(pluginName)?.plugin;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 📊 GET STATS: Estadísticas de plugins
|
|
170
|
+
* @returns {Object}
|
|
171
|
+
*/
|
|
172
|
+
getStats() {
|
|
173
|
+
return {
|
|
174
|
+
totalPlugins: this.plugins.size,
|
|
175
|
+
plugins: Array.from(this.plugins.entries()).map(([name, data]) => ({
|
|
176
|
+
name,
|
|
177
|
+
installedAt: data.installedAt,
|
|
178
|
+
hasUninstall: typeof data.plugin.uninstall === 'function'
|
|
179
|
+
})),
|
|
180
|
+
hooks: Array.from(this.hooks.entries()).map(([name, callbacks]) => ({
|
|
181
|
+
name,
|
|
182
|
+
callbacks: callbacks.length
|
|
183
|
+
}))
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 🧹 CLEANUP: Limpiar todos los plugins
|
|
189
|
+
*/
|
|
190
|
+
cleanup() {
|
|
191
|
+
for (const [name] of this.plugins) {
|
|
192
|
+
this.uninstall(name);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('[WuPlugin] 🧹 All plugins cleaned up');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 📦 PLUGIN HELPER: Helper para crear plugins fácilmente
|
|
201
|
+
*/
|
|
202
|
+
export const createPlugin = (config) => {
|
|
203
|
+
return {
|
|
204
|
+
name: config.name,
|
|
205
|
+
install: config.install,
|
|
206
|
+
uninstall: config.uninstall,
|
|
207
|
+
beforeMount: config.beforeMount,
|
|
208
|
+
afterMount: config.afterMount,
|
|
209
|
+
beforeUnmount: config.beforeUnmount,
|
|
210
|
+
afterUnmount: config.afterUnmount,
|
|
211
|
+
onError: config.onError
|
|
212
|
+
};
|
|
213
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🛡️ WU-PROXY-SANDBOX: JavaScript Isolation con Proxy
|
|
3
|
+
* Basado en video-code - Aislamiento real de variables globales
|
|
4
|
+
*
|
|
5
|
+
* Este sandbox usa ES6 Proxy para interceptar accesos a window
|
|
6
|
+
* y aislar completamente el JavaScript entre micro-apps
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export class WuProxySandbox {
|
|
10
|
+
constructor(appName) {
|
|
11
|
+
this.appName = appName;
|
|
12
|
+
this.proxy = null;
|
|
13
|
+
this.fakeWindow = Object.create(null); // Namespace aislado
|
|
14
|
+
this.active = false;
|
|
15
|
+
this.modifiedKeys = new Set(); // Track de propiedades modificadas
|
|
16
|
+
|
|
17
|
+
console.log(`[WuProxySandbox] 🛡️ Creating proxy sandbox for: ${appName}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Activar sandbox - Crea el Proxy que aísla window
|
|
22
|
+
*/
|
|
23
|
+
activate() {
|
|
24
|
+
if (this.active) {
|
|
25
|
+
console.warn(`[WuProxySandbox] Sandbox already active for ${this.appName}`);
|
|
26
|
+
return this.proxy;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.proxy = new Proxy(window, {
|
|
30
|
+
get: (target, prop) => {
|
|
31
|
+
// 🔍 Primero buscar en el namespace aislado
|
|
32
|
+
if (prop in this.fakeWindow) {
|
|
33
|
+
return this.fakeWindow[prop];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 🎯 Propiedades del window real
|
|
37
|
+
const value = target[prop];
|
|
38
|
+
|
|
39
|
+
// ⚠️ CRÍTICO: Bind de funciones para mantener contexto
|
|
40
|
+
if (typeof value === 'function' && !this.isConstructor(value)) {
|
|
41
|
+
return value.bind(target);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return value;
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
set: (target, prop, value) => {
|
|
48
|
+
// 🛡️ AISLAMIENTO: Todo set va al namespace aislado
|
|
49
|
+
this.fakeWindow[prop] = value;
|
|
50
|
+
this.modifiedKeys.add(prop);
|
|
51
|
+
|
|
52
|
+
console.log(`[WuProxySandbox] 📝 ${this.appName} set: ${String(prop)}`);
|
|
53
|
+
return true;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
has: (target, prop) => {
|
|
57
|
+
// Verificar tanto en fakeWindow como en window real
|
|
58
|
+
return prop in this.fakeWindow || prop in target;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
deleteProperty: (target, prop) => {
|
|
62
|
+
// Solo permitir delete en el namespace aislado
|
|
63
|
+
if (prop in this.fakeWindow) {
|
|
64
|
+
delete this.fakeWindow[prop];
|
|
65
|
+
this.modifiedKeys.delete(prop);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
this.active = true;
|
|
73
|
+
console.log(`[WuProxySandbox] ✅ Proxy sandbox activated for ${this.appName}`);
|
|
74
|
+
|
|
75
|
+
return this.proxy;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Desactivar sandbox - Limpia el namespace aislado
|
|
80
|
+
*/
|
|
81
|
+
deactivate() {
|
|
82
|
+
if (!this.active) {
|
|
83
|
+
console.warn(`[WuProxySandbox] Sandbox not active for ${this.appName}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(`[WuProxySandbox] 🧹 Deactivating sandbox for ${this.appName}`);
|
|
88
|
+
|
|
89
|
+
// 🧹 Limpiar todas las propiedades modificadas
|
|
90
|
+
this.fakeWindow = Object.create(null);
|
|
91
|
+
this.modifiedKeys.clear();
|
|
92
|
+
this.proxy = null;
|
|
93
|
+
this.active = false;
|
|
94
|
+
|
|
95
|
+
console.log(`[WuProxySandbox] ✅ Sandbox deactivated for ${this.appName}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Verificar si un valor es un constructor (class)
|
|
100
|
+
*/
|
|
101
|
+
isConstructor(fn) {
|
|
102
|
+
try {
|
|
103
|
+
// Detectar constructores/classes por su prototipo
|
|
104
|
+
return fn.prototype && fn.prototype.constructor === fn;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Obtener el proxy activo (o null si no está activo)
|
|
112
|
+
*/
|
|
113
|
+
getProxy() {
|
|
114
|
+
return this.active ? this.proxy : null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Verificar si el sandbox está activo
|
|
119
|
+
*/
|
|
120
|
+
isActive() {
|
|
121
|
+
return this.active;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Obtener estadísticas del sandbox
|
|
126
|
+
*/
|
|
127
|
+
getStats() {
|
|
128
|
+
return {
|
|
129
|
+
appName: this.appName,
|
|
130
|
+
active: this.active,
|
|
131
|
+
modifiedKeys: Array.from(this.modifiedKeys),
|
|
132
|
+
isolatedPropsCount: Object.keys(this.fakeWindow).length
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Limpiar propiedades específicas del namespace
|
|
138
|
+
*/
|
|
139
|
+
clearProperty(prop) {
|
|
140
|
+
if (prop in this.fakeWindow) {
|
|
141
|
+
delete this.fakeWindow[prop];
|
|
142
|
+
this.modifiedKeys.delete(prop);
|
|
143
|
+
console.log(`[WuProxySandbox] 🗑️ Cleared property: ${String(prop)}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Obtener todas las propiedades aisladas
|
|
149
|
+
*/
|
|
150
|
+
getIsolatedProperties() {
|
|
151
|
+
return { ...this.fakeWindow };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔔 WU-REGISTRY: EVENT-BASED APP REGISTRATION SYSTEM
|
|
3
|
+
*
|
|
4
|
+
* Reemplaza el polling con Custom Events para mejor performance
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export class WuRegistry {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.registeredApps = new Set();
|
|
10
|
+
this.waitingPromises = new Map();
|
|
11
|
+
|
|
12
|
+
// Event listeners para registro
|
|
13
|
+
this.setupEventListeners();
|
|
14
|
+
|
|
15
|
+
console.log('[WuRegistry] 🔔 Event-based registration system initialized');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 📡 SETUP EVENT LISTENERS
|
|
20
|
+
*/
|
|
21
|
+
setupEventListeners() {
|
|
22
|
+
// Escuchar eventos de registro de apps
|
|
23
|
+
window.addEventListener('wu:app:ready', (event) => {
|
|
24
|
+
const { appName } = event.detail;
|
|
25
|
+
console.log(`[WuRegistry] ✅ App registered via event: ${appName}`);
|
|
26
|
+
|
|
27
|
+
this.registeredApps.add(appName);
|
|
28
|
+
|
|
29
|
+
// Resolver promesas pendientes
|
|
30
|
+
if (this.waitingPromises.has(appName)) {
|
|
31
|
+
const { resolve } = this.waitingPromises.get(appName);
|
|
32
|
+
resolve();
|
|
33
|
+
this.waitingPromises.delete(appName);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Exponer API global para microfrontends
|
|
38
|
+
if (!window.wu) {
|
|
39
|
+
window.wu = {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// API para que apps notifiquen que están listas
|
|
43
|
+
window.wu.notifyReady = (appName) => {
|
|
44
|
+
console.log(`[WuRegistry] 📢 App ${appName} called notifyReady()`);
|
|
45
|
+
|
|
46
|
+
const event = new CustomEvent('wu:app:ready', {
|
|
47
|
+
detail: { appName, timestamp: Date.now() }
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
window.dispatchEvent(event);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* ⏳ WAIT FOR APP: Promise-based waiting
|
|
56
|
+
* @param {string} appName - Nombre de la app
|
|
57
|
+
* @param {number} timeout - Timeout en ms (default: 10000)
|
|
58
|
+
* @returns {Promise}
|
|
59
|
+
*/
|
|
60
|
+
waitForApp(appName, timeout = 10000) {
|
|
61
|
+
// Si ya está registrada, resolver inmediatamente
|
|
62
|
+
if (this.registeredApps.has(appName)) {
|
|
63
|
+
console.log(`[WuRegistry] ⚡ App ${appName} already registered`);
|
|
64
|
+
return Promise.resolve();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(`[WuRegistry] ⏳ Waiting for app ${appName} to register...`);
|
|
68
|
+
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
// Guardar promesa
|
|
71
|
+
this.waitingPromises.set(appName, { resolve, reject });
|
|
72
|
+
|
|
73
|
+
// Timeout
|
|
74
|
+
const timeoutId = setTimeout(() => {
|
|
75
|
+
if (this.waitingPromises.has(appName)) {
|
|
76
|
+
this.waitingPromises.delete(appName);
|
|
77
|
+
|
|
78
|
+
const error = new Error(
|
|
79
|
+
`App '${appName}' failed to register within ${timeout}ms.\n\n` +
|
|
80
|
+
`Possible causes:\n` +
|
|
81
|
+
` - Module failed to load\n` +
|
|
82
|
+
` - wu.define() was not called\n` +
|
|
83
|
+
` - Check browser console for import errors\n\n` +
|
|
84
|
+
`Make sure your app calls:\n` +
|
|
85
|
+
` wu.define('${appName}', { mount, unmount })`
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
reject(error);
|
|
89
|
+
}
|
|
90
|
+
}, timeout);
|
|
91
|
+
|
|
92
|
+
// Limpiar timeout cuando se resuelva
|
|
93
|
+
this.waitingPromises.get(appName).timeoutId = timeoutId;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* ✅ MARK AS REGISTERED
|
|
99
|
+
* @param {string} appName
|
|
100
|
+
*/
|
|
101
|
+
markAsRegistered(appName) {
|
|
102
|
+
this.registeredApps.add(appName);
|
|
103
|
+
console.log(`[WuRegistry] ✅ App ${appName} marked as registered`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* ❌ UNREGISTER APP
|
|
108
|
+
* @param {string} appName
|
|
109
|
+
*/
|
|
110
|
+
unregister(appName) {
|
|
111
|
+
this.registeredApps.delete(appName);
|
|
112
|
+
console.log(`[WuRegistry] ❌ App ${appName} unregistered`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 🧹 CLEANUP
|
|
117
|
+
*/
|
|
118
|
+
cleanup() {
|
|
119
|
+
// Rechazar todas las promesas pendientes
|
|
120
|
+
for (const [appName, { reject, timeoutId }] of this.waitingPromises) {
|
|
121
|
+
clearTimeout(timeoutId);
|
|
122
|
+
reject(new Error(`Registry cleanup: ${appName}`));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.waitingPromises.clear();
|
|
126
|
+
this.registeredApps.clear();
|
|
127
|
+
|
|
128
|
+
console.log('[WuRegistry] 🧹 Registry cleaned up');
|
|
129
|
+
}
|
|
130
|
+
}
|