wu-framework 1.1.6 → 1.1.8

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.
Files changed (90) hide show
  1. package/README.md +511 -977
  2. package/dist/wu-framework.cjs.js +3 -1
  3. package/dist/wu-framework.cjs.js.map +1 -0
  4. package/dist/wu-framework.dev.js +7533 -2761
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js +3 -0
  7. package/dist/wu-framework.esm.js.map +1 -0
  8. package/dist/wu-framework.umd.js +3 -1
  9. package/dist/wu-framework.umd.js.map +1 -0
  10. package/integrations/astro/README.md +127 -0
  11. package/integrations/astro/WuApp.astro +63 -0
  12. package/integrations/astro/WuShell.astro +39 -0
  13. package/integrations/astro/index.js +68 -0
  14. package/integrations/astro/package.json +38 -0
  15. package/integrations/astro/types.d.ts +53 -0
  16. package/package.json +94 -74
  17. package/src/adapters/angular/ai.js +30 -0
  18. package/src/adapters/angular/index.d.ts +154 -0
  19. package/src/adapters/angular/index.js +932 -0
  20. package/src/adapters/angular.d.ts +3 -154
  21. package/src/adapters/angular.js +3 -813
  22. package/src/adapters/index.js +35 -24
  23. package/src/adapters/lit/ai.js +20 -0
  24. package/src/adapters/lit/index.d.ts +120 -0
  25. package/src/adapters/lit/index.js +721 -0
  26. package/src/adapters/lit.d.ts +3 -120
  27. package/src/adapters/lit.js +3 -726
  28. package/src/adapters/preact/ai.js +33 -0
  29. package/src/adapters/preact/index.d.ts +108 -0
  30. package/src/adapters/preact/index.js +661 -0
  31. package/src/adapters/preact.d.ts +3 -108
  32. package/src/adapters/preact.js +3 -665
  33. package/src/adapters/react/ai.js +135 -0
  34. package/src/adapters/react/index.d.ts +246 -0
  35. package/src/adapters/react/index.js +689 -0
  36. package/src/adapters/react.d.ts +3 -212
  37. package/src/adapters/react.js +3 -513
  38. package/src/adapters/shared.js +64 -0
  39. package/src/adapters/solid/ai.js +32 -0
  40. package/src/adapters/solid/index.d.ts +101 -0
  41. package/src/adapters/solid/index.js +586 -0
  42. package/src/adapters/solid.d.ts +3 -101
  43. package/src/adapters/solid.js +3 -591
  44. package/src/adapters/svelte/ai.js +31 -0
  45. package/src/adapters/svelte/index.d.ts +166 -0
  46. package/src/adapters/svelte/index.js +798 -0
  47. package/src/adapters/svelte.d.ts +3 -166
  48. package/src/adapters/svelte.js +3 -803
  49. package/src/adapters/vanilla/ai.js +30 -0
  50. package/src/adapters/vanilla/index.d.ts +179 -0
  51. package/src/adapters/vanilla/index.js +785 -0
  52. package/src/adapters/vanilla.d.ts +3 -179
  53. package/src/adapters/vanilla.js +3 -791
  54. package/src/adapters/vue/ai.js +52 -0
  55. package/src/adapters/vue/index.d.ts +299 -0
  56. package/src/adapters/vue/index.js +608 -0
  57. package/src/adapters/vue.d.ts +3 -299
  58. package/src/adapters/vue.js +3 -611
  59. package/src/ai/wu-ai-actions.js +261 -0
  60. package/src/ai/wu-ai-browser.js +663 -0
  61. package/src/ai/wu-ai-context.js +332 -0
  62. package/src/ai/wu-ai-conversation.js +554 -0
  63. package/src/ai/wu-ai-permissions.js +381 -0
  64. package/src/ai/wu-ai-provider.js +605 -0
  65. package/src/ai/wu-ai-schema.js +225 -0
  66. package/src/ai/wu-ai-triggers.js +396 -0
  67. package/src/ai/wu-ai.js +474 -0
  68. package/src/core/wu-app.js +50 -8
  69. package/src/core/wu-cache.js +1 -1
  70. package/src/core/wu-core.js +645 -677
  71. package/src/core/wu-html-parser.js +121 -211
  72. package/src/core/wu-iframe-sandbox.js +328 -0
  73. package/src/core/wu-mcp-bridge.js +647 -0
  74. package/src/core/wu-overrides.js +510 -0
  75. package/src/core/wu-prefetch.js +414 -0
  76. package/src/core/wu-proxy-sandbox.js +398 -75
  77. package/src/core/wu-sandbox.js +86 -268
  78. package/src/core/wu-script-executor.js +79 -182
  79. package/src/core/wu-snapshot-sandbox.js +149 -106
  80. package/src/core/wu-strategies.js +13 -0
  81. package/src/core/wu-style-bridge.js +0 -2
  82. package/src/index.js +139 -665
  83. package/dist/wu-framework.hex.js +0 -23
  84. package/dist/wu-framework.min.js +0 -1
  85. package/dist/wu-framework.obf.js +0 -1
  86. package/scripts/build-protected.js +0 -366
  87. package/scripts/build.js +0 -212
  88. package/scripts/rollup-plugin-hex.js +0 -143
  89. package/src/core/wu-registry.js +0 -60
  90. package/src/core/wu-sandbox-pool.js +0 -390
@@ -1,216 +1,113 @@
1
1
  /**
2
- * 📜 WU-SCRIPT-EXECUTOR: Sistema de ejecución segura de scripts
3
- * Basado en video-code - Ejecuta scripts en contexto sandbox aislado
2
+ * WU-SCRIPT-EXECUTOR: Execute scripts inside a Proxy sandbox.
3
+ *
4
+ * Two isolation levels:
5
+ * - strictGlobal: true → with(proxy) { code } — all global access goes through proxy
6
+ * - strictGlobal: false → (function(window){ code })(proxy) — only explicit window.xxx
7
+ *
8
+ * This is what makes the sandbox REAL instead of decorative.
9
+ * Without this, import() runs code in global scope and the proxy is just a cleanup tracker.
10
+ * With this, code receives the proxy as "window" and every setTimeout, addEventListener,
11
+ * document.querySelector, localStorage access goes through the proxy's traps.
4
12
  */
5
13
 
14
+ import { logger } from './wu-logger.js';
15
+
6
16
  export class WuScriptExecutor {
7
- constructor() {
8
- console.log('[WuScriptExecutor] 📜 Script execution system initialized');
9
- }
10
17
 
11
18
  /**
12
- * Ejecutar script usando Function constructor (más seguro que eval)
13
- * @param {string} script - Código JavaScript a ejecutar
14
- * @param {string} appName - Nombre de la app
15
- * @param {Proxy} globalProxy - Proxy sandbox como window
16
- * @returns {*} Resultado de la ejecución
19
+ * Execute a script string inside the proxy sandbox.
20
+ *
21
+ * @param {string} scriptText - JavaScript code to execute
22
+ * @param {string} appName - App identifier (for logging)
23
+ * @param {Proxy} proxy - The activated proxy sandbox
24
+ * @param {Object} [options]
25
+ * @param {boolean} [options.strictGlobal=true] - Use with(proxy) for maximum isolation
26
+ * @param {string} [options.sourceUrl=''] - Source URL for devtools (//# sourceURL)
27
+ * @returns {*} Return value of the executed code
17
28
  */
18
- executeWithFunction(script, appName, globalProxy) {
19
- console.log(`[WuScriptExecutor] 🚀 Executing script for ${appName} using Function`);
20
-
21
- try {
22
- // 🌟 Almacenar proxy globalmente de forma temporal
23
- window.__WU_PROXY_TEMP__ = globalProxy;
29
+ execute(scriptText, appName, proxy, options = {}) {
30
+ const { strictGlobal = true, sourceUrl = '' } = options;
24
31
 
25
- // 🎯 Crear función que recibe window como parámetro
26
- const scriptText = `
27
- return ((window) => {
28
- ${script}
29
- return window['${appName}'];
30
- })(window.__WU_PROXY_TEMP__);
31
- `;
32
+ if (!scriptText || !scriptText.trim()) return;
32
33
 
33
- // 🚀 Ejecutar con Function (más seguro que eval)
34
- const fn = new Function(scriptText);
35
- const result = fn();
34
+ const sourceComment = sourceUrl ? `\n//# sourceURL=wu-sandbox:///${appName}/${sourceUrl}\n` : '';
36
35
 
37
- // 🧹 Limpiar proxy temporal
38
- delete window.__WU_PROXY_TEMP__;
39
-
40
- console.log(`[WuScriptExecutor] ✅ Script executed successfully for ${appName}`);
41
- return result;
42
-
43
- } catch (error) {
44
- // 🧹 Limpiar en caso de error
45
- delete window.__WU_PROXY_TEMP__;
36
+ let wrappedCode;
46
37
 
47
- console.error(`[WuScriptExecutor] ❌ Script execution failed for ${appName}:`, error);
48
- throw error;
49
- }
38
+ if (strictGlobal) {
39
+ // MAXIMUM ISOLATION
40
+ // with(window) makes ALL unqualified identifiers (setTimeout, fetch, document, etc.)
41
+ // resolve through the proxy's has/get traps, not the real window.
42
+ // Note: 'use strict' inside the with block becomes a no-op string expression,
43
+ // so bundled code with strict mode still works.
44
+ wrappedCode = `;(function(window, self, globalThis, top, parent) {
45
+ with(window) {
46
+ ;${scriptText}${sourceComment}
50
47
  }
51
-
52
- /**
53
- * Ejecutar script usando eval (fallback)
54
- * @param {string} script - Código JavaScript a ejecutar
55
- * @param {string} appName - Nombre de la app
56
- * @param {Proxy} globalProxy - Proxy sandbox como window
57
- * @returns {*} Resultado de la ejecución
58
- */
59
- executeWithEval(script, appName, globalProxy) {
60
- console.log(`[WuScriptExecutor] 🚀 Executing script for ${appName} using eval`);
48
+ }).call(proxy, proxy, proxy, proxy, proxy, proxy);`;
49
+ } else {
50
+ // IIFE ONLY only explicit window.xxx goes through proxy
51
+ wrappedCode = `;(function(window, self, globalThis, top, parent) {
52
+ ;${scriptText}${sourceComment}
53
+ }).call(proxy, proxy, proxy, proxy, proxy, proxy);`;
54
+ }
61
55
 
62
56
  try {
63
- // 🌟 Almacenar proxy globalmente de forma temporal
64
- window.__WU_PROXY_TEMP__ = globalProxy;
65
-
66
- // 🎯 IIFE que recibe window como parámetro
67
- const scriptText = `
68
- ((window) => {
69
- ${script}
70
- return window['${appName}'];
71
- })(window.__WU_PROXY_TEMP__);
72
- `;
73
-
74
- // ⚠️ Usar eval (menos seguro pero compatible)
75
- const result = eval(scriptText);
76
-
77
- // 🧹 Limpiar proxy temporal
78
- delete window.__WU_PROXY_TEMP__;
79
-
80
- console.log(`[WuScriptExecutor] ✅ Script executed successfully for ${appName}`);
81
- return result;
82
-
57
+ // new Function('proxy', code) creates a function with 'proxy' as the single param.
58
+ // This avoids polluting scope — the only bridge to the sandbox is the proxy argument.
59
+ const fn = new Function('proxy', wrappedCode);
60
+ return fn(proxy);
83
61
  } catch (error) {
84
- // 🧹 Limpiar en caso de error
85
- delete window.__WU_PROXY_TEMP__;
86
-
87
- console.error(`[WuScriptExecutor] Script execution failed for ${appName}:`, error);
62
+ // If strictGlobal failed (rare edge case with with-statement), retry without it
63
+ if (strictGlobal) {
64
+ logger.wuWarn(`[ScriptExecutor] strictGlobal failed for ${appName}, retrying without with(): ${error.message}`);
65
+ return this.execute(scriptText, appName, proxy, { ...options, strictGlobal: false });
66
+ }
67
+ logger.wuError(`[ScriptExecutor] Execution failed for ${appName}:`, error);
88
68
  throw error;
89
69
  }
90
70
  }
91
71
 
92
72
  /**
93
- * Ejecutar script con estrategia automática (intenta Function primero, luego eval)
94
- * @param {string} script - Código JavaScript a ejecutar
95
- * @param {string} appName - Nombre de la app
96
- * @param {Proxy} globalProxy - Proxy sandbox como window
97
- * @param {string} strategy - 'function' | 'eval' | 'auto'
98
- * @returns {*} Resultado de la ejecución
73
+ * Fetch script content from a URL.
74
+ * @param {string} url - Script URL
75
+ * @returns {Promise<string>} Script text
99
76
  */
100
- execute(script, appName, globalProxy, strategy = 'function') {
101
- if (strategy === 'eval') {
102
- return this.executeWithEval(script, appName, globalProxy);
77
+ async fetchScript(url) {
78
+ const response = await fetch(url);
79
+ if (!response.ok) {
80
+ throw new Error(`Failed to fetch script ${url}: HTTP ${response.status}`);
103
81
  }
104
-
105
- if (strategy === 'function' || strategy === 'auto') {
106
- try {
107
- return this.executeWithFunction(script, appName, globalProxy);
108
- } catch (error) {
109
- if (strategy === 'auto') {
110
- console.warn(`[WuScriptExecutor] ⚠️ Function failed, falling back to eval`);
111
- return this.executeWithEval(script, appName, globalProxy);
112
- }
113
- throw error;
114
- }
115
- }
116
-
117
- throw new Error(`Unknown execution strategy: ${strategy}`);
82
+ return response.text();
118
83
  }
119
84
 
120
85
  /**
121
- * Ejecutar múltiples scripts en secuencia
122
- * @param {Array<string>} scripts - Array de scripts
123
- * @param {string} appName - Nombre de la app
124
- * @param {Proxy} globalProxy - Proxy sandbox
125
- * @param {string} strategy - Estrategia de ejecución
126
- * @returns {Array<*>} Resultados de ejecución
86
+ * Execute an array of scripts in sequence inside the proxy.
87
+ * External scripts (with src) are fetched first.
88
+ *
89
+ * @param {Array<{content?: string, src?: string}>} scripts
90
+ * @param {string} appName
91
+ * @param {Proxy} proxy
92
+ * @param {Object} [options]
127
93
  */
128
- async executeMultiple(scripts, appName, globalProxy, strategy = 'function') {
129
- console.log(`[WuScriptExecutor] 📚 Executing ${scripts.length} scripts for ${appName}`);
130
-
131
- const results = [];
132
-
133
- for (let i = 0; i < scripts.length; i++) {
134
- const script = scripts[i];
135
- console.log(`[WuScriptExecutor] 📜 Executing script ${i + 1}/${scripts.length}`);
94
+ async executeAll(scripts, appName, proxy, options = {}) {
95
+ for (const script of scripts) {
96
+ let text = script.content;
136
97
 
137
- try {
138
- const result = this.execute(script, appName, globalProxy, strategy);
139
- results.push({ success: true, result });
140
- } catch (error) {
141
- console.error(`[WuScriptExecutor] ❌ Script ${i + 1} failed:`, error);
142
- results.push({ success: false, error });
98
+ if (!text && script.src) {
99
+ logger.wuDebug(`[ScriptExecutor] Fetching external script: ${script.src}`);
100
+ text = await this.fetchScript(script.src);
143
101
  }
144
- }
145
-
146
- const successCount = results.filter(r => r.success).length;
147
- console.log(`[WuScriptExecutor] ✅ Executed ${successCount}/${scripts.length} scripts successfully`);
148
-
149
- return results;
150
- }
151
102
 
152
- /**
153
- * Validar script antes de ejecutar
154
- * @param {string} script - Código a validar
155
- * @returns {boolean} True si el script parece válido
156
- */
157
- validateScript(script) {
158
- if (!script || typeof script !== 'string') {
159
- return false;
160
- }
161
-
162
- if (script.trim().length === 0) {
163
- return false;
164
- }
165
-
166
- // Detectar código potencialmente peligroso
167
- const dangerousPatterns = [
168
- /__proto__/,
169
- /constructor.*prototype/,
170
- /document\.write/,
171
- /eval\(/,
172
- /new\s+Function\(/
173
- ];
174
-
175
- for (const pattern of dangerousPatterns) {
176
- if (pattern.test(script)) {
177
- console.warn(`[WuScriptExecutor] ⚠️ Potentially dangerous code detected`);
178
- // No bloquear, solo advertir
103
+ if (text && text.trim()) {
104
+ this.execute(text, appName, proxy, {
105
+ ...options,
106
+ sourceUrl: script.src || options.sourceUrl || ''
107
+ });
179
108
  }
180
109
  }
181
110
 
182
- return true;
183
- }
184
-
185
- /**
186
- * Obtener estadísticas del executor
187
- */
188
- getStats() {
189
- return {
190
- executor: 'WuScriptExecutor',
191
- capabilities: ['function', 'eval', 'auto'],
192
- defaultStrategy: 'function',
193
- validation: true
194
- };
111
+ logger.wuDebug(`[ScriptExecutor] Executed ${scripts.length} scripts for ${appName}`);
195
112
  }
196
113
  }
197
-
198
- /**
199
- * 🎯 EXPORTS DE CONVENIENCIA
200
- */
201
-
202
- /**
203
- * Ejecutar script de forma segura
204
- */
205
- export function executeScript(script, appName, globalProxy, strategy = 'function') {
206
- const executor = new WuScriptExecutor();
207
- return executor.execute(script, appName, globalProxy, strategy);
208
- }
209
-
210
- /**
211
- * Ejecutar múltiples scripts
212
- */
213
- export async function executeScripts(scripts, appName, globalProxy, strategy = 'function') {
214
- const executor = new WuScriptExecutor();
215
- return await executor.executeMultiple(scripts, appName, globalProxy, strategy);
216
- }
@@ -1,184 +1,227 @@
1
1
  /**
2
- * 🛡️ WU-SNAPSHOT-SANDBOX: JavaScript Isolation con Snapshots
3
- * Basado en video-code - Fallback para navegadores sin Proxy
2
+ * WU-SNAPSHOT-SANDBOX: JavaScript Isolation via Snapshots
3
+ * Fallback for browsers without Proxy support.
4
4
  *
5
- * Este sandbox toma "fotos" del estado de window antes de montar
6
- * y restaura el estado original al desmontar
5
+ * Takes a snapshot of window state before mount,
6
+ * restores original state on deactivate.
7
+ * Also tracks timers and event listeners for cleanup.
7
8
  */
8
9
 
10
+ import { logger } from './wu-logger.js';
11
+
9
12
  export class WuSnapshotSandbox {
10
13
  constructor(appName) {
11
14
  this.appName = appName;
12
- this.proxy = window; // En snapshot mode, proxy es window directamente
15
+ this.proxy = window;
13
16
  this.snapshot = new Map();
14
17
  this.modifiedKeys = new Set();
15
18
  this.active = false;
16
19
 
17
- console.log(`[WuSnapshotSandbox] 📸 Creating snapshot sandbox for: ${appName}`);
20
+ // Side-effect tracking (same as ProxySandbox)
21
+ this._timers = new Set();
22
+ this._intervals = new Set();
23
+ this._rafs = new Set();
24
+ this._eventListeners = [];
25
+
26
+ // Window patching state
27
+ this._patched = false;
28
+ this._originals = null;
18
29
  }
19
30
 
20
31
  /**
21
- * Activar sandbox - Captura snapshot del window
32
+ * Activate sandbox - capture window snapshot and start tracking.
22
33
  */
23
34
  activate() {
24
- if (this.active) {
25
- console.warn(`[WuSnapshotSandbox] Sandbox already active for ${this.appName}`);
26
- return this.proxy;
27
- }
28
-
29
- console.log(`[WuSnapshotSandbox] 📸 Taking window snapshot for ${this.appName}...`);
35
+ if (this.active) return this.proxy;
30
36
 
31
- // 📸 Capturar estado actual de window
32
37
  this.snapshot.clear();
33
38
  this.modifiedKeys.clear();
34
39
 
35
- // Iterar sobre todas las propiedades de window
40
+ // Capture current window state
36
41
  for (const key in window) {
37
42
  try {
38
- // Guardar el valor actual
39
43
  this.snapshot.set(key, window[key]);
40
- } catch (error) {
41
- // Algunas propiedades pueden ser inaccesibles
42
- console.warn(`[WuSnapshotSandbox] ⚠️ Could not snapshot property: ${key}`);
44
+ } catch {
45
+ // Some properties may be inaccessible
43
46
  }
44
47
  }
45
48
 
46
49
  this.active = true;
47
- console.log(`[WuSnapshotSandbox] Snapshot captured with ${this.snapshot.size} properties`);
48
-
50
+ logger.wuDebug(`[SnapshotSandbox] Activated for ${this.appName} (${this.snapshot.size} props)`);
49
51
  return this.proxy;
50
52
  }
51
53
 
52
54
  /**
53
- * Desactivar sandbox - Restaura el snapshot original
55
+ * Deactivate sandbox - restore snapshot AND clean side effects.
54
56
  */
55
57
  deactivate() {
56
- if (!this.active) {
57
- console.warn(`[WuSnapshotSandbox] Sandbox not active for ${this.appName}`);
58
- return;
58
+ if (!this.active) return;
59
+
60
+ // Unpatch window if patched
61
+ this.unpatchWindow();
62
+
63
+ // --- Clean tracked timers ---
64
+ for (const id of this._timers) {
65
+ try { clearTimeout(id); } catch {}
66
+ }
67
+ for (const id of this._intervals) {
68
+ try { clearInterval(id); } catch {}
59
69
  }
70
+ for (const id of this._rafs) {
71
+ try { cancelAnimationFrame(id); } catch {}
72
+ }
73
+
74
+ const timerCount = this._timers.size + this._intervals.size + this._rafs.size;
75
+ this._timers.clear();
76
+ this._intervals.clear();
77
+ this._rafs.clear();
60
78
 
61
- console.log(`[WuSnapshotSandbox] 🔄 Restoring window snapshot for ${this.appName}...`);
79
+ // --- Clean tracked event listeners ---
80
+ const listenerCount = this._eventListeners.length;
81
+ for (const { target, event, handler, options } of this._eventListeners) {
82
+ try { target.removeEventListener(event, handler, options); } catch {}
83
+ }
84
+ this._eventListeners = [];
62
85
 
86
+ // --- Restore window snapshot ---
63
87
  let restoredCount = 0;
64
88
  let deletedCount = 0;
65
89
 
66
- // 🔄 Detectar y restaurar cambios
67
90
  for (const key in window) {
68
91
  try {
69
92
  const currentValue = window[key];
70
93
  const originalValue = this.snapshot.get(key);
71
94
 
72
- // Si la propiedad cambió, restaurarla
73
95
  if (currentValue !== originalValue) {
74
96
  if (this.snapshot.has(key)) {
75
97
  window[key] = originalValue;
76
98
  restoredCount++;
77
99
  } else {
78
- // Nueva propiedad agregada por la app, eliminarla
79
100
  try {
80
101
  delete window[key];
81
102
  deletedCount++;
82
- } catch (error) {
83
- // Algunas propiedades no se pueden eliminar
84
- console.warn(`[WuSnapshotSandbox] ⚠️ Could not delete: ${key}`);
85
- }
103
+ } catch {}
86
104
  }
87
105
  }
88
- } catch (error) {
89
- console.warn(`[WuSnapshotSandbox] ⚠️ Could not restore property: ${key}`);
90
- }
106
+ } catch {}
91
107
  }
92
108
 
93
- // 🧹 Limpiar estado
94
109
  this.snapshot.clear();
95
110
  this.modifiedKeys.clear();
96
111
  this.active = false;
97
112
 
98
- console.log(`[WuSnapshotSandbox] Snapshot restored - ${restoredCount} restored, ${deletedCount} deleted`);
113
+ if (timerCount > 0 || listenerCount > 0) {
114
+ logger.wuDebug(
115
+ `[SnapshotSandbox] ${this.appName} cleanup: ${timerCount} timers, ${listenerCount} listeners, ${restoredCount} restored, ${deletedCount} deleted`
116
+ );
117
+ }
118
+ logger.wuDebug(`[SnapshotSandbox] Deactivated for ${this.appName}`);
99
119
  }
100
120
 
101
- /**
102
- * Obtener el proxy (en este caso, window)
103
- */
121
+ // ================================================================
122
+ // WINDOW PATCHING - same interface as ProxySandbox
123
+ // ================================================================
124
+
125
+ patchWindow() {
126
+ if (this._patched) return;
127
+
128
+ const self = this;
129
+
130
+ // Capture in local closure — survives unpatch safely
131
+ const originals = {
132
+ setTimeout: window.setTimeout,
133
+ clearTimeout: window.clearTimeout,
134
+ setInterval: window.setInterval,
135
+ clearInterval: window.clearInterval,
136
+ requestAnimationFrame: window.requestAnimationFrame,
137
+ cancelAnimationFrame: window.cancelAnimationFrame,
138
+ addEventListener: window.addEventListener,
139
+ removeEventListener: window.removeEventListener
140
+ };
141
+
142
+ this._originals = originals;
143
+
144
+ window.setTimeout = function(fn, delay, ...args) {
145
+ const id = originals.setTimeout.call(window, fn, delay, ...args);
146
+ if (self._patched) self._timers.add(id);
147
+ return id;
148
+ };
149
+ window.clearTimeout = function(id) {
150
+ self._timers.delete(id);
151
+ return originals.clearTimeout.call(window, id);
152
+ };
153
+ window.setInterval = function(fn, delay, ...args) {
154
+ const id = originals.setInterval.call(window, fn, delay, ...args);
155
+ if (self._patched) self._intervals.add(id);
156
+ return id;
157
+ };
158
+ window.clearInterval = function(id) {
159
+ self._intervals.delete(id);
160
+ return originals.clearInterval.call(window, id);
161
+ };
162
+ window.requestAnimationFrame = function(fn) {
163
+ const id = originals.requestAnimationFrame.call(window, fn);
164
+ if (self._patched) self._rafs.add(id);
165
+ return id;
166
+ };
167
+ window.cancelAnimationFrame = function(id) {
168
+ self._rafs.delete(id);
169
+ return originals.cancelAnimationFrame.call(window, id);
170
+ };
171
+
172
+ window.addEventListener = function(event, handler, options) {
173
+ if (self._patched) self._eventListeners.push({ target: window, event, handler, options });
174
+ return originals.addEventListener.call(window, event, handler, options);
175
+ };
176
+ window.removeEventListener = function(event, handler, options) {
177
+ self._eventListeners = self._eventListeners.filter(
178
+ l => !(l.target === window && l.event === event && l.handler === handler)
179
+ );
180
+ return originals.removeEventListener.call(window, event, handler, options);
181
+ };
182
+
183
+ this._patched = true;
184
+ logger.wuDebug(`[SnapshotSandbox] Window patched for ${this.appName}`);
185
+ }
186
+
187
+ unpatchWindow() {
188
+ if (!this._patched || !this._originals) return;
189
+
190
+ window.setTimeout = this._originals.setTimeout;
191
+ window.clearTimeout = this._originals.clearTimeout;
192
+ window.setInterval = this._originals.setInterval;
193
+ window.clearInterval = this._originals.clearInterval;
194
+ window.requestAnimationFrame = this._originals.requestAnimationFrame;
195
+ window.cancelAnimationFrame = this._originals.cancelAnimationFrame;
196
+ window.addEventListener = this._originals.addEventListener;
197
+ window.removeEventListener = this._originals.removeEventListener;
198
+
199
+ this._patched = false;
200
+ logger.wuDebug(`[SnapshotSandbox] Window unpatched for ${this.appName}`);
201
+ }
202
+
203
+ // ================================================================
204
+ // UTILITIES
205
+ // ================================================================
206
+
104
207
  getProxy() {
105
208
  return this.active ? this.proxy : null;
106
209
  }
107
210
 
108
- /**
109
- * Verificar si el sandbox está activo
110
- */
111
211
  isActive() {
112
212
  return this.active;
113
213
  }
114
214
 
115
- /**
116
- * Obtener estadísticas del sandbox
117
- */
118
215
  getStats() {
119
- const modifiedProps = [];
120
-
121
- if (this.active) {
122
- // Detectar qué propiedades han cambiado
123
- for (const key in window) {
124
- try {
125
- const currentValue = window[key];
126
- const originalValue = this.snapshot.get(key);
127
-
128
- if (currentValue !== originalValue) {
129
- modifiedProps.push(key);
130
- }
131
- } catch (error) {
132
- // Ignorar errores de acceso
133
- }
134
- }
135
- }
136
-
137
216
  return {
138
217
  appName: this.appName,
139
218
  active: this.active,
219
+ patched: this._patched,
140
220
  snapshotSize: this.snapshot.size,
141
- modifiedProperties: modifiedProps,
142
- modifiedCount: modifiedProps.length
221
+ trackedTimers: this._timers.size,
222
+ trackedIntervals: this._intervals.size,
223
+ trackedRAFs: this._rafs.size,
224
+ trackedEventListeners: this._eventListeners.length
143
225
  };
144
226
  }
145
-
146
- /**
147
- * Verificar si una propiedad fue modificada
148
- */
149
- isPropertyModified(prop) {
150
- if (!this.active) return false;
151
-
152
- try {
153
- const currentValue = window[prop];
154
- const originalValue = this.snapshot.get(prop);
155
- return currentValue !== originalValue;
156
- } catch {
157
- return false;
158
- }
159
- }
160
-
161
- /**
162
- * Obtener todas las propiedades modificadas
163
- */
164
- getModifiedProperties() {
165
- const modified = {};
166
-
167
- if (this.active) {
168
- for (const key in window) {
169
- try {
170
- if (this.isPropertyModified(key)) {
171
- modified[key] = {
172
- original: this.snapshot.get(key),
173
- current: window[key]
174
- };
175
- }
176
- } catch (error) {
177
- // Ignorar errores de acceso
178
- }
179
- }
180
- }
181
-
182
- return modified;
183
- }
184
227
  }
@@ -75,6 +75,19 @@ export class WuLoadingStrategy {
75
75
  }
76
76
  });
77
77
 
78
+ // Speculate: Uses Speculation Rules API (Chrome 121+) with fallbacks
79
+ this.register('speculate', {
80
+ shouldPreload: true,
81
+ priority: 'medium',
82
+ load: async (appName, config) => {
83
+ if (this.core.prefetcher) {
84
+ await this.core.prefetcher.prefetch(appName, {
85
+ eagerness: config.eagerness || 'moderate'
86
+ });
87
+ }
88
+ }
89
+ });
90
+
78
91
  // Idle: Carga cuando el navegador está idle
79
92
  this.register('idle', {
80
93
  shouldPreload: false,
@@ -30,7 +30,6 @@
30
30
 
31
31
  export class WuStyleBridge {
32
32
  constructor() {
33
- this.sharedStyles = new Map();
34
33
  this.styleObserver = null;
35
34
  this.fullyIsolatedApps = new Map(); // Mapa de appName -> appUrl para apps con fully-isolated
36
35
  this.config = {
@@ -454,7 +453,6 @@ export class WuStyleBridge {
454
453
  this.styleObserver = null;
455
454
  }
456
455
 
457
- this.sharedStyles.clear();
458
456
  console.log('[WuStyleBridge] 🧹 StyleBridge cleaned up');
459
457
  }
460
458