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.
@@ -0,0 +1,720 @@
1
+ /**
2
+ * 🛡️ WU-SANDBOX: ADVANCED ISOLATION SYSTEM
3
+ * Shadow DOM + Proxy Sandbox + Script Execution + HTML Parsing
4
+ * Combina lo mejor de video-code con Shadow DOM nativo
5
+ */
6
+
7
+ import { WuStyleBridge } from './wu-style-bridge.js';
8
+ import { WuProxySandbox } from './wu-proxy-sandbox.js';
9
+ import { WuSnapshotSandbox } from './wu-snapshot-sandbox.js';
10
+ import { WuScriptExecutor } from './wu-script-executor.js';
11
+ import { WuHtmlParser } from './wu-html-parser.js';
12
+
13
+ export class WuSandbox {
14
+ constructor() {
15
+ // Registros existentes
16
+ this.sandboxes = new Map();
17
+ this.globalState = new Map();
18
+ this.styleBridge = new WuStyleBridge();
19
+
20
+ // 🚀 NUEVOS SISTEMAS INTEGRADOS
21
+ this.jsSandboxes = new Map(); // ProxySandbox o SnapshotSandbox por app
22
+ this.scriptExecutor = new WuScriptExecutor();
23
+ this.htmlParser = new WuHtmlParser();
24
+ this.sandboxStrategy = this.detectSandboxStrategy();
25
+
26
+ console.log(`[WuSandbox] 🛡️ Advanced isolation system initialized (strategy: ${this.sandboxStrategy})`);
27
+ }
28
+
29
+ /**
30
+ * Detectar estrategia de sandbox óptima
31
+ * @returns {'proxy' | 'snapshot'} Estrategia a usar
32
+ */
33
+ detectSandboxStrategy() {
34
+ // Verificar si Proxy está disponible
35
+ if (typeof Proxy !== 'undefined') {
36
+ try {
37
+ // Test básico de Proxy
38
+ const testProxy = new Proxy({}, {
39
+ get(target, prop) { return target[prop]; }
40
+ });
41
+ testProxy.test = 'value';
42
+
43
+ console.log('[WuSandbox] ✅ Proxy available - using ProxySandbox strategy');
44
+ return 'proxy';
45
+ } catch (error) {
46
+ console.warn('[WuSandbox] ⚠️ Proxy not working - falling back to SnapshotSandbox');
47
+ return 'snapshot';
48
+ }
49
+ }
50
+
51
+ console.warn('[WuSandbox] ⚠️ Proxy not available - using SnapshotSandbox strategy');
52
+ return 'snapshot';
53
+ }
54
+
55
+ /**
56
+ * 🔧 SMART SANDBOX: Advanced Shadow DOM creation with error recovery
57
+ * @param {string} appName - Nombre de la aplicación
58
+ * @param {HTMLElement} hostContainer - Contenedor host
59
+ * @returns {Object} Sandbox con shadow root y container
60
+ */
61
+ create(appName, hostContainer) {
62
+ console.log(`[WuSandbox] 🔨 Creating sandbox for: ${appName}`);
63
+
64
+ try {
65
+ // 🔧 SHADOW DOM VERIFICATION
66
+ if (!hostContainer.attachShadow) {
67
+ throw new Error('Shadow DOM not supported in this browser');
68
+ }
69
+
70
+ // 🛠️ SMART CLEANUP: Handle existing shadow roots
71
+ let shadowRoot;
72
+ if (hostContainer.shadowRoot) {
73
+ console.log(`[WuSandbox] 🔄 Existing shadow root detected, performing cleanup...`);
74
+
75
+ // Clear existing shadow root content
76
+ hostContainer.shadowRoot.innerHTML = '';
77
+ shadowRoot = hostContainer.shadowRoot;
78
+
79
+ console.log(`[WuSandbox] ✨ Existing shadow root cleaned and reused`);
80
+ } else {
81
+ // Create new Shadow DOM
82
+ shadowRoot = hostContainer.attachShadow({
83
+ mode: 'open',
84
+ delegatesFocus: true
85
+ });
86
+
87
+ console.log(`[WuSandbox] 🌟 New shadow root created`);
88
+ }
89
+
90
+ // 🎯 Create app container with advanced features
91
+ const appContainer = document.createElement('div');
92
+ appContainer.id = `wu-app-${appName}`;
93
+ appContainer.className = 'wu-app-root';
94
+ appContainer.setAttribute('data-wu-enhanced', 'true');
95
+ appContainer.setAttribute('data-wu-timestamp', Date.now().toString());
96
+
97
+ // 🎨 Enhanced base styles with advanced properties
98
+ const baseStyles = document.createElement('style');
99
+ baseStyles.textContent = this.generateSandboxStyles(appName);
100
+
101
+ // 🌟 Assemble enhanced Shadow DOM
102
+ shadowRoot.appendChild(baseStyles);
103
+ shadowRoot.appendChild(appContainer);
104
+
105
+ // 🚀 Create isolated JavaScript scope (LEGACY - backward compatibility)
106
+ const jsScope = this.createJSScope(appName);
107
+
108
+ // 🛡️ NUEVO: Crear JS Sandbox avanzado (ProxySandbox o SnapshotSandbox)
109
+ const jsSandbox = this.createAdvancedJSSandbox(appName);
110
+ const jsProxy = jsSandbox.activate(); // Activar sandbox y obtener proxy
111
+
112
+ const sandbox = {
113
+ appName,
114
+ shadowRoot,
115
+ container: appContainer,
116
+ hostContainer,
117
+ jsScope, // LEGACY: mantener para compatibilidad
118
+ jsSandbox, // NUEVO: ProxySandbox o SnapshotSandbox
119
+ jsProxy, // NUEVO: Proxy para ejecutar scripts
120
+ styles: baseStyles,
121
+ created: Date.now(),
122
+ sandbox_state: 'stable',
123
+ recovery_count: 0
124
+ };
125
+
126
+ // 🎨 INJECT SHARED STYLES: Store promise for await in mount
127
+ sandbox.stylesReady = this.styleBridge.injectStylesIntoShadow(shadowRoot, appName).then(count => {
128
+ console.log(`[WuSandbox] 🎨 Shared ${count} styles with ${appName}`);
129
+
130
+ // 🔄 Observar cambios dinámicos de estilos (HMR de Vite)
131
+ this.styleBridge.observeStyleChanges(() => {
132
+ console.log(`[WuSandbox] 🔄 Reinjecting styles for ${appName} due to changes`);
133
+ this.styleBridge.injectStylesIntoShadow(shadowRoot, appName).catch(err => {
134
+ console.warn(`[WuSandbox] ⚠️ Failed to reinject styles:`, err);
135
+ });
136
+ });
137
+
138
+ return count;
139
+ }).catch(error => {
140
+ console.warn(`[WuSandbox] ⚠️ Failed to inject styles:`, error);
141
+ return 0;
142
+ });
143
+
144
+ // 📊 Register in sandbox registry
145
+ this.sandboxes.set(appName, sandbox);
146
+
147
+ console.log(`[WuSandbox] ✅ Enhanced sandbox created for ${appName}`);
148
+ return sandbox;
149
+
150
+ } catch (error) {
151
+ console.error(`[WuSandbox] ❌ Failed to create sandbox for ${appName}:`, error);
152
+
153
+ // 🔧 FALLBACK RECOVERY: Create fallback sandbox when Shadow DOM fails
154
+ if (error.message.includes('Shadow root cannot be created')) {
155
+ return this.createFallbackSandbox(appName, hostContainer);
156
+ }
157
+
158
+ throw error;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * 🔧 FALLBACK SANDBOX: Create fallback container when Shadow DOM is not available
164
+ */
165
+ createFallbackSandbox(appName, hostContainer) {
166
+ console.log(`[WuSandbox] 🔧 Creating fallback sandbox for ${appName}...`);
167
+
168
+ try {
169
+ // 🛠️ Complete shadow DOM reset
170
+ if (hostContainer.shadowRoot) {
171
+ hostContainer.shadowRoot.innerHTML = '';
172
+ }
173
+
174
+ // 🌟 Create minimal container without shadow DOM if necessary
175
+ const fallbackContainer = document.createElement('div');
176
+ fallbackContainer.id = `wu-app-${appName}`;
177
+ fallbackContainer.className = 'wu-app-root wu-fallback';
178
+ fallbackContainer.style.cssText = `
179
+ width: 100%;
180
+ height: 100%;
181
+ isolation: isolate;
182
+ contain: layout style paint;
183
+ `;
184
+
185
+ // 🧹 Clear host container
186
+ hostContainer.innerHTML = '';
187
+ hostContainer.appendChild(fallbackContainer);
188
+
189
+ const healedSandbox = {
190
+ appName,
191
+ shadowRoot: null, // No shadow DOM in fallback mode
192
+ container: fallbackContainer,
193
+ hostContainer,
194
+ jsScope: this.createJSScope(appName),
195
+ styles: null,
196
+ created: Date.now(),
197
+ sandbox_state: 'fallback_mode',
198
+ recovery_count: 1,
199
+ fallback_mode: true
200
+ };
201
+
202
+ this.sandboxes.set(appName, healedSandbox);
203
+
204
+ console.log(`[WuSandbox] ✨ Fallback sandbox created successfully for ${appName}`);
205
+ return healedSandbox;
206
+
207
+ } catch (healingError) {
208
+ console.error(`[WuSandbox] 💥 Fallback sandbox creation failed:`, healingError);
209
+ throw healingError;
210
+ }
211
+ }
212
+
213
+ /**
214
+ * 🎨 SANDBOX STYLES: Generate CSS styles for Shadow DOM isolation
215
+ */
216
+ generateSandboxStyles(appName) {
217
+ return `
218
+ /* Wu Framework - Shadow DOM Isolation Styles */
219
+ :host {
220
+ display: block;
221
+ width: 100%;
222
+ height: 100%;
223
+ box-sizing: border-box;
224
+ contain: layout style paint;
225
+ --wu-sandbox-active: true;
226
+ --wu-isolation-state: stable;
227
+ }
228
+
229
+ .wu-app-root {
230
+ width: 100%;
231
+ height: 100%;
232
+ box-sizing: border-box;
233
+ isolation: isolate;
234
+ position: relative;
235
+ overflow: hidden;
236
+ }
237
+
238
+ /* Loading animation for sandbox initialization */
239
+ .wu-app-root[data-wu-loading="true"] {
240
+ background: linear-gradient(45deg,
241
+ rgba(74, 144, 226, 0.1) 0%,
242
+ rgba(80, 227, 194, 0.1) 100%);
243
+ animation: sandboxPulse 2s ease-in-out infinite;
244
+ }
245
+
246
+ @keyframes sandboxPulse {
247
+ 0%, 100% { opacity: 0.8; }
248
+ 50% { opacity: 1; }
249
+ }
250
+
251
+ /* CSS reset for shadow DOM stability */
252
+ * {
253
+ box-sizing: border-box;
254
+ }
255
+
256
+ /* CSS custom properties for sandbox */
257
+ :host {
258
+ --wu-app-name: "${appName}";
259
+ --wu-isolation: true;
260
+ --wu-creation-timestamp: ${Date.now()};
261
+ }
262
+
263
+ /* 🛡️ Debug mode enhancements */
264
+ :host([wu-debug]) {
265
+ border: 2px dashed #4a90e2;
266
+ background: rgba(74, 144, 226, 0.05);
267
+ box-shadow: 0 0 10px rgba(74, 144, 226, 0.3);
268
+ }
269
+
270
+ :host([wu-debug])::before {
271
+ content: "Wu Framework: " attr(wu-app);
272
+ position: absolute;
273
+ top: 0;
274
+ left: 0;
275
+ background: linear-gradient(45deg, #4a90e2, #50e3c2);
276
+ color: white;
277
+ padding: 4px 8px;
278
+ font-size: 11px;
279
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
280
+ z-index: 10000;
281
+ border-radius: 0 0 4px 0;
282
+ font-weight: 600;
283
+ }
284
+
285
+ /* Sandbox state indicators */
286
+ :host([data-sandbox-state="stable"]) {
287
+ --wu-isolation-state: stable;
288
+ }
289
+
290
+ :host([data-sandbox-state="healing"]) {
291
+ --wu-dimensional-stability: healing;
292
+ animation: sandboxHealing 1s ease-in-out infinite;
293
+ }
294
+
295
+ @keyframes sandboxHealing {
296
+ 0%, 100% { filter: hue-rotate(0deg); }
297
+ 50% { filter: hue-rotate(180deg); }
298
+ }
299
+ `;
300
+ }
301
+
302
+ /**
303
+ * 🛡️ Crear JS Sandbox avanzado (ProxySandbox o SnapshotSandbox)
304
+ * @param {string} appName - Nombre de la app
305
+ * @returns {WuProxySandbox|WuSnapshotSandbox} Sandbox JS
306
+ */
307
+ createAdvancedJSSandbox(appName) {
308
+ let jsSandbox;
309
+
310
+ if (this.sandboxStrategy === 'proxy') {
311
+ jsSandbox = new WuProxySandbox(appName);
312
+ console.log(`[WuSandbox] 🛡️ Created ProxySandbox for ${appName}`);
313
+ } else {
314
+ jsSandbox = new WuSnapshotSandbox(appName);
315
+ console.log(`[WuSandbox] 📸 Created SnapshotSandbox for ${appName}`);
316
+ }
317
+
318
+ // Registrar sandbox
319
+ this.jsSandboxes.set(appName, jsSandbox);
320
+
321
+ return jsSandbox;
322
+ }
323
+
324
+ /**
325
+ * 📜 Ejecutar script en sandbox aislado
326
+ * @param {string} script - Código JavaScript
327
+ * @param {string} appName - Nombre de la app
328
+ * @param {string} strategy - 'function' | 'eval' | 'auto'
329
+ * @returns {*} Resultado de la ejecución
330
+ */
331
+ executeScript(script, appName, strategy = 'function') {
332
+ const sandbox = this.sandboxes.get(appName);
333
+ if (!sandbox || !sandbox.jsProxy) {
334
+ throw new Error(`Sandbox not found for ${appName}`);
335
+ }
336
+
337
+ console.log(`[WuSandbox] 📜 Executing script for ${appName}`);
338
+ return this.scriptExecutor.execute(script, appName, sandbox.jsProxy, strategy);
339
+ }
340
+
341
+ /**
342
+ * 📄 Parsear HTML de micro-app
343
+ * @param {string} html - HTML a parsear
344
+ * @param {string} appName - Nombre de la app
345
+ * @param {string} baseUrl - URL base
346
+ * @returns {Object} Resultado del parsing
347
+ */
348
+ parseHtml(html, appName, baseUrl) {
349
+ console.log(`[WuSandbox] 📄 Parsing HTML for ${appName}`);
350
+ return this.htmlParser.parse(html, appName, baseUrl);
351
+ }
352
+
353
+ /**
354
+ * 📄 Parsear HTML desde URL
355
+ * @param {string} url - URL de la app
356
+ * @param {string} appName - Nombre de la app
357
+ * @returns {Promise<Object>} Resultado del parsing
358
+ */
359
+ async parseHtmlFromUrl(url, appName) {
360
+ console.log(`[WuSandbox] 🌐 Parsing HTML from URL for ${appName}`);
361
+ return await this.htmlParser.parseFromUrl(url, appName);
362
+ }
363
+
364
+ /**
365
+ * Obtener estilos base para el sandbox
366
+ * @param {string} appName - Nombre de la aplicación
367
+ * @returns {string} CSS base
368
+ */
369
+ getBaseStyles(appName) {
370
+ return `
371
+ /* Wu Framework - Shadow DOM Base Styles */
372
+ :host {
373
+ display: block;
374
+ width: 100%;
375
+ height: 100%;
376
+ box-sizing: border-box;
377
+ contain: layout style paint;
378
+ }
379
+
380
+ .wu-app-root {
381
+ width: 100%;
382
+ height: 100%;
383
+ box-sizing: border-box;
384
+ isolation: isolate;
385
+ }
386
+
387
+ /* Reset básico para evitar conflictos */
388
+ * {
389
+ box-sizing: border-box;
390
+ }
391
+
392
+ /* Variables CSS aisladas para la app */
393
+ :host {
394
+ --wu-app-name: "${appName}";
395
+ --wu-isolation: true;
396
+ }
397
+
398
+ /* Prevenir scroll del host cuando no es necesario */
399
+ :host([wu-no-scroll]) {
400
+ overflow: hidden;
401
+ }
402
+
403
+ /* Estilos para debugging en desarrollo */
404
+ :host([wu-debug]) {
405
+ border: 2px dashed #ff6b6b;
406
+ background: rgba(255, 107, 107, 0.1);
407
+ }
408
+
409
+ :host([wu-debug])::before {
410
+ content: "Wu App: " attr(wu-app);
411
+ position: absolute;
412
+ top: 0;
413
+ left: 0;
414
+ background: #ff6b6b;
415
+ color: white;
416
+ padding: 2px 6px;
417
+ font-size: 10px;
418
+ font-family: monospace;
419
+ z-index: 10000;
420
+ }
421
+ `;
422
+ }
423
+
424
+ /**
425
+ * Crear scope aislado para JavaScript
426
+ * @param {string} appName - Nombre de la aplicación
427
+ * @returns {Proxy} Proxy que aísla el scope global
428
+ */
429
+ createJSScope(appName) {
430
+ // Crear namespace aislado para la app
431
+ const appGlobals = Object.create(null);
432
+
433
+ // Proxy que intercepta acceso a variables globales
434
+ return new Proxy(window, {
435
+ set(target, prop, value) {
436
+ // Variables específicas de la app van al namespace aislado
437
+ if (this.isAppSpecific(prop)) {
438
+ appGlobals[prop] = value;
439
+ console.log(`[WuSandbox] 📝 App ${appName} set isolated global: ${String(prop)}`);
440
+ return true;
441
+ }
442
+
443
+ // Variables seguras pueden ir al global real
444
+ if (this.isSafeGlobal(prop)) {
445
+ target[prop] = value;
446
+ return true;
447
+ }
448
+
449
+ // Por defecto, aislar en el namespace de la app
450
+ appGlobals[prop] = value;
451
+ return true;
452
+ },
453
+
454
+ get(target, prop) {
455
+ // Primero buscar en namespace aislado
456
+ if (prop in appGlobals) {
457
+ return appGlobals[prop];
458
+ }
459
+
460
+ // Luego en el global real
461
+ return target[prop];
462
+ },
463
+
464
+ has(target, prop) {
465
+ return prop in appGlobals || prop in target;
466
+ },
467
+
468
+ ownKeys(target) {
469
+ return [...Object.keys(appGlobals), ...Object.keys(target)];
470
+ },
471
+
472
+ // Métodos auxiliares
473
+ isAppSpecific(prop) {
474
+ const appSpecificPatterns = [
475
+ /^app[A-Z]/, // appData, appConfig, etc.
476
+ /^[a-z]+App$/, // myApp, headerApp, etc.
477
+ /^__[a-zA-Z]/, // variables privadas
478
+ /^_[A-Z]/ // variables de librerías
479
+ ];
480
+
481
+ return appSpecificPatterns.some(pattern => pattern.test(String(prop)));
482
+ },
483
+
484
+ isSafeGlobal(prop) {
485
+ const safeGlobals = new Set([
486
+ 'console', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
487
+ 'fetch', 'URL', 'URLSearchParams', 'FormData', 'Blob',
488
+ 'Date', 'Math', 'JSON', 'Promise', 'Array', 'Object', 'String', 'Number'
489
+ ]);
490
+
491
+ return safeGlobals.has(String(prop));
492
+ }
493
+ });
494
+ }
495
+
496
+ /**
497
+ * Agregar estilos personalizados al sandbox
498
+ * @param {string} appName - Nombre de la aplicación
499
+ * @param {string} css - CSS a agregar
500
+ */
501
+ addStyles(appName, css) {
502
+ const sandbox = this.sandboxes.get(appName);
503
+ if (!sandbox) {
504
+ console.warn(`[WuSandbox] Sandbox not found for: ${appName}`);
505
+ return;
506
+ }
507
+
508
+ const styleElement = document.createElement('style');
509
+ styleElement.textContent = css;
510
+ styleElement.setAttribute('wu-custom-styles', '');
511
+
512
+ sandbox.shadowRoot.appendChild(styleElement);
513
+ console.log(`[WuSandbox] 🎨 Custom styles added to ${appName}`);
514
+ }
515
+
516
+ /**
517
+ * Cargar estilos externos en el sandbox
518
+ * @param {string} appName - Nombre de la aplicación
519
+ * @param {string} href - URL del CSS
520
+ */
521
+ loadExternalStyles(appName, href) {
522
+ const sandbox = this.sandboxes.get(appName);
523
+ if (!sandbox) {
524
+ console.warn(`[WuSandbox] Sandbox not found for: ${appName}`);
525
+ return;
526
+ }
527
+
528
+ const linkElement = document.createElement('link');
529
+ linkElement.rel = 'stylesheet';
530
+ linkElement.href = href;
531
+ linkElement.setAttribute('wu-external-styles', '');
532
+
533
+ sandbox.shadowRoot.appendChild(linkElement);
534
+ console.log(`[WuSandbox] 🔗 External styles loaded in ${appName}: ${href}`);
535
+ }
536
+
537
+ /**
538
+ * Establecer modo debug para un sandbox
539
+ * @param {string} appName - Nombre de la aplicación
540
+ * @param {boolean} enabled - Activar/desactivar debug
541
+ */
542
+ setDebugMode(appName, enabled = true) {
543
+ const sandbox = this.sandboxes.get(appName);
544
+ if (!sandbox) {
545
+ console.warn(`[WuSandbox] Sandbox not found for: ${appName}`);
546
+ return;
547
+ }
548
+
549
+ if (enabled) {
550
+ sandbox.hostContainer.setAttribute('wu-debug', '');
551
+ sandbox.hostContainer.setAttribute('wu-app', appName);
552
+ } else {
553
+ sandbox.hostContainer.removeAttribute('wu-debug');
554
+ sandbox.hostContainer.removeAttribute('wu-app');
555
+ }
556
+
557
+ console.log(`[WuSandbox] 🐛 Debug mode ${enabled ? 'enabled' : 'disabled'} for ${appName}`);
558
+ }
559
+
560
+ /**
561
+ * Limpiar y destruir sandbox
562
+ * @param {Object} sandbox - Sandbox a limpiar
563
+ */
564
+ cleanup(sandbox) {
565
+ if (!sandbox) return;
566
+
567
+ const { appName, shadowRoot, hostContainer, jsSandbox } = sandbox;
568
+
569
+ console.log(`[WuSandbox] 🧹 Cleaning up sandbox for: ${appName}`);
570
+
571
+ try {
572
+ // 🛡️ NUEVO: Desactivar JS Sandbox
573
+ if (jsSandbox && jsSandbox.isActive()) {
574
+ jsSandbox.deactivate();
575
+ console.log(`[WuSandbox] 🛡️ JS Sandbox deactivated for ${appName}`);
576
+ }
577
+
578
+ // Limpiar eventos y observers
579
+ this.cleanupEventListeners(sandbox);
580
+
581
+ // Limpiar contenido del Shadow DOM
582
+ if (shadowRoot) {
583
+ shadowRoot.innerHTML = '';
584
+ }
585
+
586
+ // Remover atributos del host
587
+ if (hostContainer) {
588
+ hostContainer.removeAttribute('wu-debug');
589
+ hostContainer.removeAttribute('wu-app');
590
+ hostContainer.removeAttribute('wu-no-scroll');
591
+ }
592
+
593
+ // Remover del registro
594
+ this.sandboxes.delete(appName);
595
+ this.jsSandboxes.delete(appName);
596
+
597
+ console.log(`[WuSandbox] ✅ Sandbox cleaned up: ${appName}`);
598
+
599
+ } catch (error) {
600
+ console.error(`[WuSandbox] ❌ Error cleaning up sandbox ${appName}:`, error);
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Limpiar event listeners del sandbox
606
+ * @param {Object} sandbox - Sandbox a limpiar
607
+ */
608
+ cleanupEventListeners(sandbox) {
609
+ // Remover todos los event listeners del Shadow DOM
610
+ const { shadowRoot } = sandbox;
611
+ if (!shadowRoot) return;
612
+
613
+ // Clonar nodos para remover todos los event listeners
614
+ const elements = shadowRoot.querySelectorAll('*');
615
+ elements.forEach(element => {
616
+ if (element.cloneNode) {
617
+ const clone = element.cloneNode(true);
618
+ element.parentNode?.replaceChild(clone, element);
619
+ }
620
+ });
621
+ }
622
+
623
+ /**
624
+ * Obtener información de un sandbox
625
+ * @param {string} appName - Nombre de la aplicación
626
+ * @returns {Object} Información del sandbox
627
+ */
628
+ getSandboxInfo(appName) {
629
+ const sandbox = this.sandboxes.get(appName);
630
+ if (!sandbox) return null;
631
+
632
+ return {
633
+ appName: sandbox.appName,
634
+ created: sandbox.created,
635
+ hasContainer: !!sandbox.container,
636
+ hasShadowRoot: !!sandbox.shadowRoot,
637
+ elementCount: sandbox.shadowRoot?.children?.length || 0,
638
+ uptime: Date.now() - sandbox.created
639
+ };
640
+ }
641
+
642
+ /**
643
+ * Obtener estadísticas de todos los sandboxes
644
+ */
645
+ getStats() {
646
+ return {
647
+ strategy: this.sandboxStrategy,
648
+ total: this.sandboxes.size,
649
+ sandboxes: Array.from(this.sandboxes.keys()),
650
+ jsSandboxes: Array.from(this.jsSandboxes.keys()),
651
+ htmlCacheSize: this.htmlParser.getStats().cacheSize,
652
+ scriptExecutor: this.scriptExecutor.getStats(),
653
+ details: Array.from(this.sandboxes.entries()).map(([name, sandbox]) => ({
654
+ name,
655
+ uptime: Date.now() - sandbox.created,
656
+ elements: sandbox.shadowRoot?.children?.length || 0,
657
+ hasJsSandbox: !!sandbox.jsSandbox,
658
+ jsSandboxActive: sandbox.jsSandbox?.isActive() || false
659
+ }))
660
+ };
661
+ }
662
+
663
+ /**
664
+ * Limpiar todos los sandboxes
665
+ */
666
+ cleanupAll() {
667
+ console.log(`[WuSandbox] 🧹 Cleaning up all ${this.sandboxes.size} sandboxes...`);
668
+
669
+ for (const [appName, sandbox] of this.sandboxes) {
670
+ this.cleanup(sandbox);
671
+ }
672
+
673
+ this.globalState.clear();
674
+
675
+ // Limpiar StyleBridge
676
+ if (this.styleBridge) {
677
+ this.styleBridge.cleanup();
678
+ }
679
+
680
+ console.log(`[WuSandbox] ✅ All sandboxes cleaned up`);
681
+ }
682
+
683
+ /**
684
+ * 🎨 CONFIGURAR STYLE BRIDGE: Configura el sistema de compartición de estilos
685
+ * @param {Object} config - Configuración del StyleBridge
686
+ */
687
+ configureStyleSharing(config) {
688
+ if (this.styleBridge) {
689
+ this.styleBridge.configure(config);
690
+ console.log(`[WuSandbox] 🎨 StyleBridge configured`);
691
+ }
692
+ }
693
+
694
+ /**
695
+ * 📊 OBTENER ESTADÍSTICAS DE ESTILOS: Info sobre estilos compartidos
696
+ * @returns {Object}
697
+ */
698
+ getStyleStats() {
699
+ return this.styleBridge ? this.styleBridge.getStats() : null;
700
+ }
701
+
702
+ /**
703
+ * 🔄 RE-INYECTAR ESTILOS: Vuelve a inyectar estilos en un sandbox
704
+ * @param {string} appName - Nombre de la aplicación
705
+ */
706
+ async reinjectStyles(appName) {
707
+ const sandbox = this.sandboxes.get(appName);
708
+ if (!sandbox || !sandbox.shadowRoot) {
709
+ console.warn(`[WuSandbox] Cannot reinject styles for ${appName}`);
710
+ return;
711
+ }
712
+
713
+ console.log(`[WuSandbox] 🔄 Reinjecting styles for ${appName}...`);
714
+ const count = await this.styleBridge.injectStylesIntoShadow(
715
+ sandbox.shadowRoot,
716
+ appName
717
+ );
718
+ console.log(`[WuSandbox] ✅ Reinjected ${count} styles`);
719
+ }
720
+ }