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.
- package/README.md +511 -977
- package/dist/wu-framework.cjs.js +3 -1
- package/dist/wu-framework.cjs.js.map +1 -0
- package/dist/wu-framework.dev.js +7533 -2761
- package/dist/wu-framework.dev.js.map +1 -1
- package/dist/wu-framework.esm.js +3 -0
- package/dist/wu-framework.esm.js.map +1 -0
- package/dist/wu-framework.umd.js +3 -1
- package/dist/wu-framework.umd.js.map +1 -0
- package/integrations/astro/README.md +127 -0
- package/integrations/astro/WuApp.astro +63 -0
- package/integrations/astro/WuShell.astro +39 -0
- package/integrations/astro/index.js +68 -0
- package/integrations/astro/package.json +38 -0
- package/integrations/astro/types.d.ts +53 -0
- package/package.json +94 -74
- package/src/adapters/angular/ai.js +30 -0
- package/src/adapters/angular/index.d.ts +154 -0
- package/src/adapters/angular/index.js +932 -0
- package/src/adapters/angular.d.ts +3 -154
- package/src/adapters/angular.js +3 -813
- package/src/adapters/index.js +35 -24
- package/src/adapters/lit/ai.js +20 -0
- package/src/adapters/lit/index.d.ts +120 -0
- package/src/adapters/lit/index.js +721 -0
- package/src/adapters/lit.d.ts +3 -120
- package/src/adapters/lit.js +3 -726
- package/src/adapters/preact/ai.js +33 -0
- package/src/adapters/preact/index.d.ts +108 -0
- package/src/adapters/preact/index.js +661 -0
- package/src/adapters/preact.d.ts +3 -108
- package/src/adapters/preact.js +3 -665
- package/src/adapters/react/ai.js +135 -0
- package/src/adapters/react/index.d.ts +246 -0
- package/src/adapters/react/index.js +689 -0
- package/src/adapters/react.d.ts +3 -212
- package/src/adapters/react.js +3 -513
- package/src/adapters/shared.js +64 -0
- package/src/adapters/solid/ai.js +32 -0
- package/src/adapters/solid/index.d.ts +101 -0
- package/src/adapters/solid/index.js +586 -0
- package/src/adapters/solid.d.ts +3 -101
- package/src/adapters/solid.js +3 -591
- package/src/adapters/svelte/ai.js +31 -0
- package/src/adapters/svelte/index.d.ts +166 -0
- package/src/adapters/svelte/index.js +798 -0
- package/src/adapters/svelte.d.ts +3 -166
- package/src/adapters/svelte.js +3 -803
- package/src/adapters/vanilla/ai.js +30 -0
- package/src/adapters/vanilla/index.d.ts +179 -0
- package/src/adapters/vanilla/index.js +785 -0
- package/src/adapters/vanilla.d.ts +3 -179
- package/src/adapters/vanilla.js +3 -791
- package/src/adapters/vue/ai.js +52 -0
- package/src/adapters/vue/index.d.ts +299 -0
- package/src/adapters/vue/index.js +608 -0
- package/src/adapters/vue.d.ts +3 -299
- package/src/adapters/vue.js +3 -611
- package/src/ai/wu-ai-actions.js +261 -0
- package/src/ai/wu-ai-browser.js +663 -0
- package/src/ai/wu-ai-context.js +332 -0
- package/src/ai/wu-ai-conversation.js +554 -0
- package/src/ai/wu-ai-permissions.js +381 -0
- package/src/ai/wu-ai-provider.js +605 -0
- package/src/ai/wu-ai-schema.js +225 -0
- package/src/ai/wu-ai-triggers.js +396 -0
- package/src/ai/wu-ai.js +474 -0
- package/src/core/wu-app.js +50 -8
- package/src/core/wu-cache.js +1 -1
- package/src/core/wu-core.js +645 -677
- package/src/core/wu-html-parser.js +121 -211
- package/src/core/wu-iframe-sandbox.js +328 -0
- package/src/core/wu-mcp-bridge.js +647 -0
- package/src/core/wu-overrides.js +510 -0
- package/src/core/wu-prefetch.js +414 -0
- package/src/core/wu-proxy-sandbox.js +398 -75
- package/src/core/wu-sandbox.js +86 -268
- package/src/core/wu-script-executor.js +79 -182
- package/src/core/wu-snapshot-sandbox.js +149 -106
- package/src/core/wu-strategies.js +13 -0
- package/src/core/wu-style-bridge.js +0 -2
- package/src/index.js +139 -665
- package/dist/wu-framework.hex.js +0 -23
- package/dist/wu-framework.min.js +0 -1
- package/dist/wu-framework.obf.js +0 -1
- package/scripts/build-protected.js +0 -366
- package/scripts/build.js +0 -212
- package/scripts/rollup-plugin-hex.js +0 -143
- package/src/core/wu-registry.js +0 -60
- package/src/core/wu-sandbox-pool.js +0 -390
|
@@ -1,216 +1,113 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param {string}
|
|
15
|
-
* @param {
|
|
16
|
-
* @
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34
|
-
const fn = new Function(scriptText);
|
|
35
|
-
const result = fn();
|
|
34
|
+
const sourceComment = sourceUrl ? `\n//# sourceURL=wu-sandbox:///${appName}/${sourceUrl}\n` : '';
|
|
36
35
|
|
|
37
|
-
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
*
|
|
94
|
-
* @param {string}
|
|
95
|
-
* @
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* @param {
|
|
125
|
-
* @param {string}
|
|
126
|
-
* @
|
|
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
|
|
129
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* WU-SNAPSHOT-SANDBOX: JavaScript Isolation via Snapshots
|
|
3
|
+
* Fallback for browsers without Proxy support.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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;
|
|
15
|
+
this.proxy = window;
|
|
13
16
|
this.snapshot = new Map();
|
|
14
17
|
this.modifiedKeys = new Set();
|
|
15
18
|
this.active = false;
|
|
16
19
|
|
|
17
|
-
|
|
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
|
-
*
|
|
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
|
-
//
|
|
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
|
|
41
|
-
//
|
|
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
|
-
|
|
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
|
-
*
|
|
55
|
+
* Deactivate sandbox - restore snapshot AND clean side effects.
|
|
54
56
|
*/
|
|
55
57
|
deactivate() {
|
|
56
|
-
if (!this.active)
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
83
|
-
// Algunas propiedades no se pueden eliminar
|
|
84
|
-
console.warn(`[WuSnapshotSandbox] ⚠️ Could not delete: ${key}`);
|
|
85
|
-
}
|
|
103
|
+
} catch {}
|
|
86
104
|
}
|
|
87
105
|
}
|
|
88
|
-
} catch
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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
|
|