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,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 💾 WU-CACHE: INTERNAL FRAMEWORK CACHING
|
|
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
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ USO INTERNO: No exponer en API pública
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export class WuCache {
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.config = {
|
|
21
|
+
maxSize: options.maxSize || 50, // MB
|
|
22
|
+
maxItems: options.maxItems || 100,
|
|
23
|
+
defaultTTL: options.defaultTTL || 3600000, // 1 hour
|
|
24
|
+
persistent: options.persistent !== false,
|
|
25
|
+
storage: options.storage || 'memory', // 'memory' | 'localStorage' | 'sessionStorage'
|
|
26
|
+
compression: options.compression || false
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Memory cache
|
|
30
|
+
this.memoryCache = new Map();
|
|
31
|
+
|
|
32
|
+
// LRU tracking
|
|
33
|
+
this.accessOrder = new Map(); // key -> timestamp
|
|
34
|
+
|
|
35
|
+
// Statistics
|
|
36
|
+
this.stats = {
|
|
37
|
+
hits: 0,
|
|
38
|
+
misses: 0,
|
|
39
|
+
sets: 0,
|
|
40
|
+
evictions: 0,
|
|
41
|
+
size: 0
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
console.log('[WuCache] 💾 Advanced cache system initialized');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 🔍 GET: Obtener valor del cache
|
|
49
|
+
* @param {string} key - Clave
|
|
50
|
+
* @returns {*} Valor cacheado o null
|
|
51
|
+
*/
|
|
52
|
+
get(key) {
|
|
53
|
+
// 1. Buscar en memoria
|
|
54
|
+
if (this.memoryCache.has(key)) {
|
|
55
|
+
const entry = this.memoryCache.get(key);
|
|
56
|
+
|
|
57
|
+
// Verificar TTL
|
|
58
|
+
if (this.isExpired(entry)) {
|
|
59
|
+
this.delete(key);
|
|
60
|
+
this.stats.misses++;
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Actualizar acceso (LRU)
|
|
65
|
+
this.accessOrder.set(key, Date.now());
|
|
66
|
+
this.stats.hits++;
|
|
67
|
+
|
|
68
|
+
return entry.value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 2. Buscar en storage persistente
|
|
72
|
+
if (this.config.persistent) {
|
|
73
|
+
const stored = this.getFromStorage(key);
|
|
74
|
+
if (stored) {
|
|
75
|
+
// Restaurar a memoria
|
|
76
|
+
this.memoryCache.set(key, stored);
|
|
77
|
+
this.accessOrder.set(key, Date.now());
|
|
78
|
+
this.stats.hits++;
|
|
79
|
+
return stored.value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.stats.misses++;
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 💾 SET: Guardar valor en cache
|
|
89
|
+
* @param {string} key - Clave
|
|
90
|
+
* @param {*} value - Valor
|
|
91
|
+
* @param {number} ttl - Time to live (ms)
|
|
92
|
+
* @returns {boolean}
|
|
93
|
+
*/
|
|
94
|
+
set(key, value, ttl) {
|
|
95
|
+
try {
|
|
96
|
+
const entry = {
|
|
97
|
+
key,
|
|
98
|
+
value,
|
|
99
|
+
timestamp: Date.now(),
|
|
100
|
+
ttl: ttl || this.config.defaultTTL,
|
|
101
|
+
size: this.estimateSize(value)
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Verificar si necesitamos hacer espacio
|
|
105
|
+
this.ensureSpace(entry.size);
|
|
106
|
+
|
|
107
|
+
// Guardar en memoria
|
|
108
|
+
this.memoryCache.set(key, entry);
|
|
109
|
+
this.accessOrder.set(key, Date.now());
|
|
110
|
+
|
|
111
|
+
// Guardar en storage persistente
|
|
112
|
+
if (this.config.persistent) {
|
|
113
|
+
this.saveToStorage(key, entry);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.stats.sets++;
|
|
117
|
+
this.stats.size += entry.size;
|
|
118
|
+
|
|
119
|
+
return true;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.warn('[WuCache] ⚠️ Failed to set cache:', error);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 🗑️ DELETE: Eliminar del cache
|
|
128
|
+
* @param {string} key - Clave
|
|
129
|
+
*/
|
|
130
|
+
delete(key) {
|
|
131
|
+
const entry = this.memoryCache.get(key);
|
|
132
|
+
if (entry) {
|
|
133
|
+
this.stats.size -= entry.size;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.memoryCache.delete(key);
|
|
137
|
+
this.accessOrder.delete(key);
|
|
138
|
+
|
|
139
|
+
if (this.config.persistent) {
|
|
140
|
+
this.deleteFromStorage(key);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 🧹 CLEAR: Limpiar todo el cache
|
|
146
|
+
*/
|
|
147
|
+
clear() {
|
|
148
|
+
this.memoryCache.clear();
|
|
149
|
+
this.accessOrder.clear();
|
|
150
|
+
this.stats.size = 0;
|
|
151
|
+
|
|
152
|
+
if (this.config.persistent) {
|
|
153
|
+
this.clearStorage();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log('[WuCache] 🧹 Cache cleared');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* ⏰ IS EXPIRED: Verificar si entrada expiró
|
|
161
|
+
* @param {Object} entry - Entrada del cache
|
|
162
|
+
* @returns {boolean}
|
|
163
|
+
*/
|
|
164
|
+
isExpired(entry) {
|
|
165
|
+
if (!entry.ttl) return false;
|
|
166
|
+
return Date.now() - entry.timestamp > entry.ttl;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 📏 ESTIMATE SIZE: Estimar tamaño de un valor
|
|
171
|
+
* @param {*} value - Valor
|
|
172
|
+
* @returns {number} Tamaño en bytes
|
|
173
|
+
*/
|
|
174
|
+
estimateSize(value) {
|
|
175
|
+
if (typeof value === 'string') {
|
|
176
|
+
return value.length * 2; // UTF-16
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (typeof value === 'object') {
|
|
180
|
+
try {
|
|
181
|
+
return JSON.stringify(value).length * 2;
|
|
182
|
+
} catch {
|
|
183
|
+
return 1000; // Estimación por defecto
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return 100; // Tamaño por defecto para primitivos
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 🎯 ENSURE SPACE: Asegurar espacio en cache (LRU eviction)
|
|
192
|
+
* @param {number} neededSize - Tamaño necesario
|
|
193
|
+
*/
|
|
194
|
+
ensureSpace(neededSize) {
|
|
195
|
+
const maxSizeBytes = this.config.maxSize * 1024 * 1024;
|
|
196
|
+
|
|
197
|
+
// Verificar si necesitamos limpiar
|
|
198
|
+
while (this.stats.size + neededSize > maxSizeBytes ||
|
|
199
|
+
this.memoryCache.size >= this.config.maxItems) {
|
|
200
|
+
|
|
201
|
+
// Encontrar entrada menos recientemente usada (LRU)
|
|
202
|
+
let oldestKey = null;
|
|
203
|
+
let oldestTime = Infinity;
|
|
204
|
+
|
|
205
|
+
for (const [key, time] of this.accessOrder) {
|
|
206
|
+
if (time < oldestTime) {
|
|
207
|
+
oldestTime = time;
|
|
208
|
+
oldestKey = key;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (oldestKey) {
|
|
213
|
+
console.log(`[WuCache] 🗑️ Evicting LRU entry: ${oldestKey}`);
|
|
214
|
+
this.delete(oldestKey);
|
|
215
|
+
this.stats.evictions++;
|
|
216
|
+
} else {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 💽 GET FROM STORAGE: Obtener del storage persistente
|
|
224
|
+
* @param {string} key - Clave
|
|
225
|
+
* @returns {Object|null}
|
|
226
|
+
*/
|
|
227
|
+
getFromStorage(key) {
|
|
228
|
+
try {
|
|
229
|
+
const storage = this.getStorage();
|
|
230
|
+
const stored = storage.getItem(`wu_cache_${key}`);
|
|
231
|
+
|
|
232
|
+
if (stored) {
|
|
233
|
+
return JSON.parse(stored);
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.warn('[WuCache] ⚠️ Failed to get from storage:', error);
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 💾 SAVE TO STORAGE: Guardar en storage persistente
|
|
243
|
+
* @param {string} key - Clave
|
|
244
|
+
* @param {Object} entry - Entrada
|
|
245
|
+
*/
|
|
246
|
+
saveToStorage(key, entry) {
|
|
247
|
+
try {
|
|
248
|
+
const storage = this.getStorage();
|
|
249
|
+
storage.setItem(`wu_cache_${key}`, JSON.stringify(entry));
|
|
250
|
+
} catch (error) {
|
|
251
|
+
// Storage lleno, limpiar entradas antiguas
|
|
252
|
+
console.warn('[WuCache] ⚠️ Storage full, cleaning old entries');
|
|
253
|
+
this.cleanOldStorageEntries();
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
storage.setItem(`wu_cache_${key}`, JSON.stringify(entry));
|
|
257
|
+
} catch {
|
|
258
|
+
console.warn('[WuCache] ⚠️ Failed to save to storage after cleanup');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 🗑️ DELETE FROM STORAGE: Eliminar del storage
|
|
265
|
+
* @param {string} key - Clave
|
|
266
|
+
*/
|
|
267
|
+
deleteFromStorage(key) {
|
|
268
|
+
try {
|
|
269
|
+
const storage = this.getStorage();
|
|
270
|
+
storage.removeItem(`wu_cache_${key}`);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.warn('[WuCache] ⚠️ Failed to delete from storage:', error);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* 🧹 CLEAR STORAGE: Limpiar storage
|
|
278
|
+
*/
|
|
279
|
+
clearStorage() {
|
|
280
|
+
try {
|
|
281
|
+
const storage = this.getStorage();
|
|
282
|
+
const keys = Object.keys(storage);
|
|
283
|
+
|
|
284
|
+
keys.forEach(key => {
|
|
285
|
+
if (key.startsWith('wu_cache_')) {
|
|
286
|
+
storage.removeItem(key);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.warn('[WuCache] ⚠️ Failed to clear storage:', error);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 🧹 CLEAN OLD STORAGE ENTRIES: Limpiar entradas antiguas del storage
|
|
296
|
+
*/
|
|
297
|
+
cleanOldStorageEntries() {
|
|
298
|
+
try {
|
|
299
|
+
const storage = this.getStorage();
|
|
300
|
+
const keys = Object.keys(storage);
|
|
301
|
+
const entries = [];
|
|
302
|
+
|
|
303
|
+
// Recopilar todas las entradas con timestamp
|
|
304
|
+
keys.forEach(key => {
|
|
305
|
+
if (key.startsWith('wu_cache_')) {
|
|
306
|
+
try {
|
|
307
|
+
const entry = JSON.parse(storage.getItem(key));
|
|
308
|
+
entries.push({ key, timestamp: entry.timestamp });
|
|
309
|
+
} catch {}
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Ordenar por timestamp (más antiguas primero)
|
|
314
|
+
entries.sort((a, b) => a.timestamp - b.timestamp);
|
|
315
|
+
|
|
316
|
+
// Eliminar 25% de entradas más antiguas
|
|
317
|
+
const toRemove = Math.ceil(entries.length * 0.25);
|
|
318
|
+
for (let i = 0; i < toRemove; i++) {
|
|
319
|
+
storage.removeItem(entries[i].key);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
console.log(`[WuCache] 🧹 Cleaned ${toRemove} old storage entries`);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.warn('[WuCache] ⚠️ Failed to clean old storage entries:', error);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 💽 GET STORAGE: Obtener instancia de storage
|
|
330
|
+
* @returns {Storage}
|
|
331
|
+
*/
|
|
332
|
+
getStorage() {
|
|
333
|
+
if (this.config.storage === 'localStorage') {
|
|
334
|
+
return window.localStorage;
|
|
335
|
+
} else if (this.config.storage === 'sessionStorage') {
|
|
336
|
+
return window.sessionStorage;
|
|
337
|
+
}
|
|
338
|
+
// Fallback a memoria
|
|
339
|
+
return {
|
|
340
|
+
getItem: () => null,
|
|
341
|
+
setItem: () => {},
|
|
342
|
+
removeItem: () => {},
|
|
343
|
+
clear: () => {}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 📊 GET STATS: Obtener estadísticas del cache
|
|
349
|
+
* @returns {Object}
|
|
350
|
+
*/
|
|
351
|
+
getStats() {
|
|
352
|
+
const hitRate = this.stats.hits + this.stats.misses > 0
|
|
353
|
+
? (this.stats.hits / (this.stats.hits + this.stats.misses) * 100).toFixed(2)
|
|
354
|
+
: 0;
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
...this.stats,
|
|
358
|
+
hitRate: `${hitRate}%`,
|
|
359
|
+
items: this.memoryCache.size,
|
|
360
|
+
sizeMB: (this.stats.size / 1024 / 1024).toFixed(2)
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* ⚙️ CONFIGURE: Actualizar configuración
|
|
366
|
+
* @param {Object} config - Nueva configuración
|
|
367
|
+
*/
|
|
368
|
+
configure(config) {
|
|
369
|
+
this.config = {
|
|
370
|
+
...this.config,
|
|
371
|
+
...config
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|