wu-framework 1.0.3 → 1.0.4
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 +33 -3
- package/src/core/wu-core.js +97 -43
- package/src/index.js +1 -1
package/package.json
CHANGED
package/src/core/wu-cache.js
CHANGED
|
@@ -102,7 +102,11 @@ export class WuCache {
|
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
// Verificar si necesitamos hacer espacio
|
|
105
|
-
this.ensureSpace(entry.size);
|
|
105
|
+
const hasSpace = this.ensureSpace(entry.size);
|
|
106
|
+
if (hasSpace === false) {
|
|
107
|
+
console.warn(`[WuCache] ⚠️ Cannot cache item: ${key} (too large)`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
106
110
|
|
|
107
111
|
// Guardar en memoria
|
|
108
112
|
this.memoryCache.set(key, entry);
|
|
@@ -194,9 +198,28 @@ export class WuCache {
|
|
|
194
198
|
ensureSpace(neededSize) {
|
|
195
199
|
const maxSizeBytes = this.config.maxSize * 1024 * 1024;
|
|
196
200
|
|
|
201
|
+
// 🛡️ FIX: Validar que el item no sea más grande que el máximo permitido
|
|
202
|
+
if (neededSize > maxSizeBytes) {
|
|
203
|
+
console.warn(`[WuCache] ⚠️ Item size (${neededSize}) exceeds max cache size (${maxSizeBytes}). Skipping.`);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 🛡️ FIX: Límite de iteraciones para evitar loop infinito
|
|
208
|
+
const maxIterations = this.config.maxItems + 10;
|
|
209
|
+
let iterations = 0;
|
|
210
|
+
|
|
197
211
|
// Verificar si necesitamos limpiar
|
|
198
|
-
while (this.stats.size + neededSize > maxSizeBytes ||
|
|
199
|
-
this.memoryCache.size >= this.config.maxItems)
|
|
212
|
+
while ((this.stats.size + neededSize > maxSizeBytes ||
|
|
213
|
+
this.memoryCache.size >= this.config.maxItems) &&
|
|
214
|
+
iterations < maxIterations) {
|
|
215
|
+
|
|
216
|
+
iterations++;
|
|
217
|
+
|
|
218
|
+
// 🛡️ FIX: Si el cache está vacío pero aún no hay espacio, salir
|
|
219
|
+
if (this.memoryCache.size === 0) {
|
|
220
|
+
console.warn('[WuCache] ⚠️ Cache empty but still no space. Breaking loop.');
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
200
223
|
|
|
201
224
|
// Encontrar entrada menos recientemente usada (LRU)
|
|
202
225
|
let oldestKey = null;
|
|
@@ -217,6 +240,13 @@ export class WuCache {
|
|
|
217
240
|
break;
|
|
218
241
|
}
|
|
219
242
|
}
|
|
243
|
+
|
|
244
|
+
// 🛡️ FIX: Log si alcanzamos el límite de iteraciones
|
|
245
|
+
if (iterations >= maxIterations) {
|
|
246
|
+
console.error(`[WuCache] 🚨 Max eviction iterations reached (${maxIterations}). Possible infinite loop prevented.`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return true;
|
|
220
250
|
}
|
|
221
251
|
|
|
222
252
|
/**
|
package/src/core/wu-core.js
CHANGED
|
@@ -104,56 +104,79 @@ export class WuCore {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* 🔍 SMART MUTATION OBSERVER:
|
|
107
|
+
* 🔍 SMART MUTATION OBSERVER: Observa SOLO los contenedores de apps montadas
|
|
108
|
+
* FIX: Ya no observa todo document.body (memory leak)
|
|
108
109
|
*/
|
|
109
110
|
initializeMutationObserver() {
|
|
110
|
-
if (!window.MutationObserver) return;
|
|
111
|
+
if (!window.MutationObserver) return;
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
// Map para trackear observers por contenedor (evita memory leak)
|
|
114
|
+
this.healthState.containerObservers = new Map();
|
|
115
|
+
|
|
116
|
+
// Callback compartido para todos los observers
|
|
117
|
+
this.healthState.mutationCallback = (mutations, observer) => {
|
|
118
|
+
let affectedAppName = null;
|
|
114
119
|
|
|
115
120
|
for (const mutation of mutations) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
if (node === mounted.container) {
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
121
|
+
if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
|
|
122
|
+
// Encontrar qué app fue afectada
|
|
123
|
+
for (const [appName, mounted] of this.mounted) {
|
|
124
|
+
const container = mounted.hostContainer || mounted.container;
|
|
125
|
+
if (mutation.target === container ||
|
|
126
|
+
mutation.target.contains?.(container)) {
|
|
127
|
+
affectedAppName = appName;
|
|
128
|
+
break;
|
|
130
129
|
}
|
|
131
|
-
return false;
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
if (affectedApps) {
|
|
135
|
-
shouldCheckHealth = true;
|
|
136
|
-
break;
|
|
137
130
|
}
|
|
138
131
|
}
|
|
132
|
+
if (affectedAppName) break;
|
|
139
133
|
}
|
|
140
134
|
|
|
141
|
-
|
|
142
|
-
if (shouldCheckHealth) {
|
|
135
|
+
if (affectedAppName) {
|
|
143
136
|
clearTimeout(this.healthState.mutationCheckTimeout);
|
|
144
137
|
this.healthState.mutationCheckTimeout = setTimeout(() => {
|
|
145
138
|
this.performHealthCheck();
|
|
146
|
-
}, 1000);
|
|
139
|
+
}, 1000);
|
|
147
140
|
}
|
|
148
|
-
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
logger.wuDebug('🔍 MutationObserver system initialized (lazy per-container)');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 🔗 Observar contenedor específico cuando se monta una app
|
|
148
|
+
*/
|
|
149
|
+
observeContainer(appName, container) {
|
|
150
|
+
if (!this.healthState.containerObservers || !container) return;
|
|
151
|
+
|
|
152
|
+
// No observar si ya existe
|
|
153
|
+
if (this.healthState.containerObservers.has(appName)) return;
|
|
149
154
|
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
const observer = new MutationObserver(this.healthState.mutationCallback);
|
|
156
|
+
|
|
157
|
+
// Observar solo el contenedor padre directo (no subtree profundo)
|
|
158
|
+
const parentToObserve = container.parentElement || container;
|
|
159
|
+
observer.observe(parentToObserve, {
|
|
152
160
|
childList: true,
|
|
153
|
-
subtree:
|
|
161
|
+
subtree: false // ✅ Solo hijos directos, no todo el árbol
|
|
154
162
|
});
|
|
155
163
|
|
|
156
|
-
|
|
164
|
+
this.healthState.containerObservers.set(appName, observer);
|
|
165
|
+
logger.wuDebug(`🔍 Observing container for ${appName}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 🔓 Dejar de observar contenedor cuando se desmonta
|
|
170
|
+
*/
|
|
171
|
+
unobserveContainer(appName) {
|
|
172
|
+
if (!this.healthState.containerObservers) return;
|
|
173
|
+
|
|
174
|
+
const observer = this.healthState.containerObservers.get(appName);
|
|
175
|
+
if (observer) {
|
|
176
|
+
observer.disconnect();
|
|
177
|
+
this.healthState.containerObservers.delete(appName);
|
|
178
|
+
logger.wuDebug(`🔓 Stopped observing container for ${appName}`);
|
|
179
|
+
}
|
|
157
180
|
}
|
|
158
181
|
|
|
159
182
|
/**
|
|
@@ -606,10 +629,14 @@ export class WuCore {
|
|
|
606
629
|
poolSandbox,
|
|
607
630
|
lifecycle,
|
|
608
631
|
container: sandbox.container,
|
|
632
|
+
hostContainer: container,
|
|
609
633
|
timestamp: Date.now(),
|
|
610
634
|
state: 'stable'
|
|
611
635
|
});
|
|
612
636
|
|
|
637
|
+
// 🔍 Observar contenedor para health monitoring (sin memory leak)
|
|
638
|
+
this.observeContainer(appName, container);
|
|
639
|
+
|
|
613
640
|
// ⚡ End performance measurement
|
|
614
641
|
const mountTime = this.performance.endMeasure('mount', appName);
|
|
615
642
|
|
|
@@ -1017,6 +1044,7 @@ export class WuCore {
|
|
|
1017
1044
|
/**
|
|
1018
1045
|
* 📦 MODULE LOADER: Advanced registration patterns
|
|
1019
1046
|
* Handles asynchronous registration with timing synchronization
|
|
1047
|
+
* FIX: Verifica que definitions tenga el lifecycle después de cargar
|
|
1020
1048
|
*/
|
|
1021
1049
|
async moduleLoader(moduleUrl, appName) {
|
|
1022
1050
|
// ✅ Check if already registered
|
|
@@ -1027,16 +1055,37 @@ export class WuCore {
|
|
|
1027
1055
|
|
|
1028
1056
|
console.log(`[Wu] 📡 Using event-based registration for ${appName}`);
|
|
1029
1057
|
|
|
1030
|
-
//
|
|
1031
|
-
|
|
1058
|
+
// 🚀 Load module first
|
|
1059
|
+
try {
|
|
1060
|
+
await import(/* @vite-ignore */ moduleUrl);
|
|
1061
|
+
} catch (loadError) {
|
|
1062
|
+
console.error(`[Wu] ❌ Failed to import module ${moduleUrl}:`, loadError);
|
|
1063
|
+
throw loadError;
|
|
1064
|
+
}
|
|
1032
1065
|
|
|
1033
|
-
//
|
|
1034
|
-
const
|
|
1066
|
+
// 🛡️ FIX: Esperar a que wu.define() se ejecute con verificación real
|
|
1067
|
+
const maxWaitTime = 10000; // 10 segundos
|
|
1068
|
+
const checkInterval = 50; // Verificar cada 50ms
|
|
1069
|
+
const startTime = Date.now();
|
|
1070
|
+
|
|
1071
|
+
while (!this.definitions.has(appName)) {
|
|
1072
|
+
const elapsed = Date.now() - startTime;
|
|
1073
|
+
|
|
1074
|
+
if (elapsed >= maxWaitTime) {
|
|
1075
|
+
throw new Error(
|
|
1076
|
+
`App '${appName}' module loaded but wu.define() was not called within ${maxWaitTime}ms.\n\n` +
|
|
1077
|
+
`Make sure your module calls:\n` +
|
|
1078
|
+
` wu.define('${appName}', { mount, unmount })\n\n` +
|
|
1079
|
+
`Or using window.wu:\n` +
|
|
1080
|
+
` window.wu.define('${appName}', { mount, unmount })`
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1035
1083
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1084
|
+
// Esperar un poco antes de verificar de nuevo
|
|
1085
|
+
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
1086
|
+
}
|
|
1038
1087
|
|
|
1039
|
-
console.log(`[Wu] ✅ App ${appName} loaded and registered
|
|
1088
|
+
console.log(`[Wu] ✅ App ${appName} loaded and registered (verified in definitions)`);
|
|
1040
1089
|
}
|
|
1041
1090
|
|
|
1042
1091
|
/**
|
|
@@ -1080,6 +1129,9 @@ export class WuCore {
|
|
|
1080
1129
|
this.sandboxPool.release(appName);
|
|
1081
1130
|
}
|
|
1082
1131
|
|
|
1132
|
+
// 🔓 Dejar de observar contenedor (evita memory leak)
|
|
1133
|
+
this.unobserveContainer(appName);
|
|
1134
|
+
|
|
1083
1135
|
// Remover del registro de montadas
|
|
1084
1136
|
this.mounted.delete(appName);
|
|
1085
1137
|
|
|
@@ -1243,10 +1295,12 @@ export class WuCore {
|
|
|
1243
1295
|
this.healthState.monitor = null;
|
|
1244
1296
|
}
|
|
1245
1297
|
|
|
1246
|
-
// Limpiar
|
|
1247
|
-
if (this.healthState.
|
|
1248
|
-
this.healthState.
|
|
1249
|
-
|
|
1298
|
+
// Limpiar todos los MutationObservers por contenedor
|
|
1299
|
+
if (this.healthState.containerObservers) {
|
|
1300
|
+
for (const [appName, observer] of this.healthState.containerObservers) {
|
|
1301
|
+
observer.disconnect();
|
|
1302
|
+
}
|
|
1303
|
+
this.healthState.containerObservers.clear();
|
|
1250
1304
|
}
|
|
1251
1305
|
|
|
1252
1306
|
// Limpiar timeouts pendientes
|
package/src/index.js
CHANGED
|
@@ -103,7 +103,7 @@ if (typeof window !== 'undefined') {
|
|
|
103
103
|
|
|
104
104
|
// Configurar propiedades si no existen
|
|
105
105
|
if (!wu.version) {
|
|
106
|
-
wu.version = '1.0.
|
|
106
|
+
wu.version = '1.0.4';
|
|
107
107
|
console.log('🚀 Wu Framework loaded - Universal Microfrontends ready');
|
|
108
108
|
wu.info = {
|
|
109
109
|
name: 'Wu Framework',
|