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,216 @@
1
+ /**
2
+ * 📜 WU-SCRIPT-EXECUTOR: Sistema de ejecución segura de scripts
3
+ * Basado en video-code - Ejecuta scripts en contexto sandbox aislado
4
+ */
5
+
6
+ export class WuScriptExecutor {
7
+ constructor() {
8
+ console.log('[WuScriptExecutor] 📜 Script execution system initialized');
9
+ }
10
+
11
+ /**
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
17
+ */
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;
24
+
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
+
33
+ // 🚀 Ejecutar con Function (más seguro que eval)
34
+ const fn = new Function(scriptText);
35
+ const result = fn();
36
+
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__;
46
+
47
+ console.error(`[WuScriptExecutor] ❌ Script execution failed for ${appName}:`, error);
48
+ throw error;
49
+ }
50
+ }
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`);
61
+
62
+ 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
+
83
+ } 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);
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ /**
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
99
+ */
100
+ execute(script, appName, globalProxy, strategy = 'function') {
101
+ if (strategy === 'eval') {
102
+ return this.executeWithEval(script, appName, globalProxy);
103
+ }
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}`);
118
+ }
119
+
120
+ /**
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
127
+ */
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}`);
136
+
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 });
143
+ }
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
+
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
179
+ }
180
+ }
181
+
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
+ };
195
+ }
196
+ }
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
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * 🛡️ WU-SNAPSHOT-SANDBOX: JavaScript Isolation con Snapshots
3
+ * Basado en video-code - Fallback para navegadores sin Proxy
4
+ *
5
+ * Este sandbox toma "fotos" del estado de window antes de montar
6
+ * y restaura el estado original al desmontar
7
+ */
8
+
9
+ export class WuSnapshotSandbox {
10
+ constructor(appName) {
11
+ this.appName = appName;
12
+ this.proxy = window; // En snapshot mode, proxy es window directamente
13
+ this.snapshot = new Map();
14
+ this.modifiedKeys = new Set();
15
+ this.active = false;
16
+
17
+ console.log(`[WuSnapshotSandbox] 📸 Creating snapshot sandbox for: ${appName}`);
18
+ }
19
+
20
+ /**
21
+ * Activar sandbox - Captura snapshot del window
22
+ */
23
+ 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}...`);
30
+
31
+ // 📸 Capturar estado actual de window
32
+ this.snapshot.clear();
33
+ this.modifiedKeys.clear();
34
+
35
+ // Iterar sobre todas las propiedades de window
36
+ for (const key in window) {
37
+ try {
38
+ // Guardar el valor actual
39
+ this.snapshot.set(key, window[key]);
40
+ } catch (error) {
41
+ // Algunas propiedades pueden ser inaccesibles
42
+ console.warn(`[WuSnapshotSandbox] ⚠️ Could not snapshot property: ${key}`);
43
+ }
44
+ }
45
+
46
+ this.active = true;
47
+ console.log(`[WuSnapshotSandbox] ✅ Snapshot captured with ${this.snapshot.size} properties`);
48
+
49
+ return this.proxy;
50
+ }
51
+
52
+ /**
53
+ * Desactivar sandbox - Restaura el snapshot original
54
+ */
55
+ deactivate() {
56
+ if (!this.active) {
57
+ console.warn(`[WuSnapshotSandbox] Sandbox not active for ${this.appName}`);
58
+ return;
59
+ }
60
+
61
+ console.log(`[WuSnapshotSandbox] 🔄 Restoring window snapshot for ${this.appName}...`);
62
+
63
+ let restoredCount = 0;
64
+ let deletedCount = 0;
65
+
66
+ // 🔄 Detectar y restaurar cambios
67
+ for (const key in window) {
68
+ try {
69
+ const currentValue = window[key];
70
+ const originalValue = this.snapshot.get(key);
71
+
72
+ // Si la propiedad cambió, restaurarla
73
+ if (currentValue !== originalValue) {
74
+ if (this.snapshot.has(key)) {
75
+ window[key] = originalValue;
76
+ restoredCount++;
77
+ } else {
78
+ // Nueva propiedad agregada por la app, eliminarla
79
+ try {
80
+ delete window[key];
81
+ deletedCount++;
82
+ } catch (error) {
83
+ // Algunas propiedades no se pueden eliminar
84
+ console.warn(`[WuSnapshotSandbox] ⚠️ Could not delete: ${key}`);
85
+ }
86
+ }
87
+ }
88
+ } catch (error) {
89
+ console.warn(`[WuSnapshotSandbox] ⚠️ Could not restore property: ${key}`);
90
+ }
91
+ }
92
+
93
+ // 🧹 Limpiar estado
94
+ this.snapshot.clear();
95
+ this.modifiedKeys.clear();
96
+ this.active = false;
97
+
98
+ console.log(`[WuSnapshotSandbox] ✅ Snapshot restored - ${restoredCount} restored, ${deletedCount} deleted`);
99
+ }
100
+
101
+ /**
102
+ * Obtener el proxy (en este caso, window)
103
+ */
104
+ getProxy() {
105
+ return this.active ? this.proxy : null;
106
+ }
107
+
108
+ /**
109
+ * Verificar si el sandbox está activo
110
+ */
111
+ isActive() {
112
+ return this.active;
113
+ }
114
+
115
+ /**
116
+ * Obtener estadísticas del sandbox
117
+ */
118
+ 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
+ return {
138
+ appName: this.appName,
139
+ active: this.active,
140
+ snapshotSize: this.snapshot.size,
141
+ modifiedProperties: modifiedProps,
142
+ modifiedCount: modifiedProps.length
143
+ };
144
+ }
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
+ }
@@ -0,0 +1,297 @@
1
+ /**
2
+ * 🚀 WU-STORE: Ultra-High Performance State Management
3
+ *
4
+ * Basado en patrones de Disruptor y Vert.x
5
+ * - Ring Buffer para zero-allocation
6
+ * - Lock-free operations
7
+ * - Event Bus para pub/sub
8
+ * - API minimalista: get(), set(), on()
9
+ */
10
+
11
+ export class WuStore {
12
+ constructor(bufferSize = 256) {
13
+ // Ring Buffer configuration
14
+ this.bufferSize = this.nextPowerOfTwo(bufferSize);
15
+ this.mask = this.bufferSize - 1;
16
+ this.buffer = new Array(this.bufferSize);
17
+ this.cursor = 0;
18
+
19
+ // State storage
20
+ this.state = {};
21
+
22
+ // Event listeners map: path -> Set of callbacks
23
+ this.listeners = new Map();
24
+
25
+ // Pattern listeners for wildcards
26
+ this.patternListeners = new Map();
27
+
28
+ // Performance metrics
29
+ this.metrics = {
30
+ reads: 0,
31
+ writes: 0,
32
+ notifications: 0
33
+ };
34
+
35
+ // Initialize ring buffer slots
36
+ for (let i = 0; i < this.bufferSize; i++) {
37
+ this.buffer[i] = { path: null, value: null, timestamp: 0 };
38
+ }
39
+
40
+ // No global pollution - proper library architecture
41
+ }
42
+
43
+ /**
44
+ * Get value from store
45
+ * @param {string} path - Dot notation path (e.g., 'user.name')
46
+ * @returns {*} Value at path or entire state if no path
47
+ */
48
+ get(path) {
49
+ this.metrics.reads++;
50
+
51
+ if (!path) return this.state;
52
+
53
+ // Fast path resolution with reduce
54
+ return path.split('.').reduce((obj, key) => obj?.[key], this.state);
55
+ }
56
+
57
+ /**
58
+ * Set value in store with Ring Buffer
59
+ * @param {string} path - Dot notation path
60
+ * @param {*} value - Value to set
61
+ * @returns {number} Sequence number
62
+ */
63
+ set(path, value) {
64
+ this.metrics.writes++;
65
+
66
+ // Write to ring buffer (lock-free)
67
+ const sequence = this.cursor++;
68
+ const index = sequence & this.mask;
69
+
70
+ // Reuse buffer slot (zero allocation)
71
+ const event = this.buffer[index];
72
+ event.path = path;
73
+ event.value = value;
74
+ event.timestamp = performance.now();
75
+
76
+ // Update state synchronously
77
+ this.updateState(path, value);
78
+
79
+ // Schedule async notifications (non-blocking)
80
+ queueMicrotask(() => {
81
+ this.notify(path, value);
82
+ this.notifyPatterns(path, value);
83
+ });
84
+
85
+ return sequence;
86
+ }
87
+
88
+ /**
89
+ * Subscribe to state changes
90
+ * @param {string} pattern - Path or pattern (supports * wildcard)
91
+ * @param {Function} callback - Callback function
92
+ * @returns {Function} Unsubscribe function
93
+ */
94
+ on(pattern, callback) {
95
+ // Check if pattern contains wildcards
96
+ if (pattern.includes('*')) {
97
+ // Pattern subscription
98
+ if (!this.patternListeners.has(pattern)) {
99
+ this.patternListeners.set(pattern, new Set());
100
+ }
101
+ this.patternListeners.get(pattern).add(callback);
102
+
103
+ // Return unsubscribe function
104
+ return () => {
105
+ const listeners = this.patternListeners.get(pattern);
106
+ if (listeners) {
107
+ listeners.delete(callback);
108
+ if (listeners.size === 0) {
109
+ this.patternListeners.delete(pattern);
110
+ }
111
+ }
112
+ };
113
+ } else {
114
+ // Direct path subscription
115
+ if (!this.listeners.has(pattern)) {
116
+ this.listeners.set(pattern, new Set());
117
+ }
118
+ this.listeners.get(pattern).add(callback);
119
+
120
+ // Return unsubscribe function
121
+ return () => {
122
+ const listeners = this.listeners.get(pattern);
123
+ if (listeners) {
124
+ listeners.delete(callback);
125
+ if (listeners.size === 0) {
126
+ this.listeners.delete(pattern);
127
+ }
128
+ }
129
+ };
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Batch set multiple values
135
+ * @param {Object} updates - Object with path:value pairs
136
+ */
137
+ batch(updates) {
138
+ const sequences = [];
139
+
140
+ for (const [path, value] of Object.entries(updates)) {
141
+ sequences.push(this.set(path, value));
142
+ }
143
+
144
+ return sequences;
145
+ }
146
+
147
+ /**
148
+ * Get current metrics
149
+ * @returns {Object} Performance metrics
150
+ */
151
+ getMetrics() {
152
+ return {
153
+ ...this.metrics,
154
+ bufferUtilization: (this.cursor % this.bufferSize) / this.bufferSize,
155
+ listenerCount: this.listeners.size + this.patternListeners.size
156
+ };
157
+ }
158
+
159
+ // Private methods
160
+
161
+ nextPowerOfTwo(n) {
162
+ return Math.pow(2, Math.ceil(Math.log2(n)));
163
+ }
164
+
165
+ updateState(path, value) {
166
+ if (!path) {
167
+ this.state = value;
168
+ return;
169
+ }
170
+
171
+ const keys = path.split('.');
172
+ const last = keys.pop();
173
+
174
+ // Create nested structure if needed
175
+ let target = this.state;
176
+ for (const key of keys) {
177
+ if (!(key in target) || typeof target[key] !== 'object') {
178
+ target[key] = {};
179
+ }
180
+ target = target[key];
181
+ }
182
+
183
+ target[last] = value;
184
+ }
185
+
186
+ notify(path, value) {
187
+ this.metrics.notifications++;
188
+
189
+ // Notify exact path listeners
190
+ const exactListeners = this.listeners.get(path);
191
+ if (exactListeners) {
192
+ exactListeners.forEach(callback => {
193
+ try {
194
+ callback(value, path);
195
+ } catch (error) {
196
+ console.error('[WuStore] Listener error:', error);
197
+ }
198
+ });
199
+ }
200
+
201
+ // Notify parent path listeners
202
+ const parts = path.split('.');
203
+ for (let i = parts.length - 1; i > 0; i--) {
204
+ const parentPath = parts.slice(0, i).join('.');
205
+ const parentListeners = this.listeners.get(parentPath);
206
+ if (parentListeners) {
207
+ const parentValue = this.get(parentPath);
208
+ parentListeners.forEach(callback => {
209
+ try {
210
+ callback(parentValue, parentPath);
211
+ } catch (error) {
212
+ console.error('[WuStore] Parent listener error:', error);
213
+ }
214
+ });
215
+ }
216
+ }
217
+ }
218
+
219
+ notifyPatterns(path, value) {
220
+ // Check all pattern listeners
221
+ for (const [pattern, listeners] of this.patternListeners) {
222
+ if (this.matchesPattern(path, pattern)) {
223
+ listeners.forEach(callback => {
224
+ try {
225
+ callback({ path, value });
226
+ } catch (error) {
227
+ console.error('[WuStore] Pattern listener error:', error);
228
+ }
229
+ });
230
+ }
231
+ }
232
+ }
233
+
234
+ matchesPattern(path, pattern) {
235
+ // Convert pattern to regex
236
+ // user.* matches user.name, user.email, etc.
237
+ // *.name matches user.name, post.name, etc.
238
+ // * matches everything
239
+
240
+ if (pattern === '*') return true;
241
+
242
+ const regexPattern = pattern
243
+ .split('.')
244
+ .map(part => part === '*' ? '[^.]+' : part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
245
+ .join('\\.');
246
+
247
+ const regex = new RegExp(`^${regexPattern}$`);
248
+ return regex.test(path);
249
+ }
250
+
251
+ /**
252
+ * Clear all state and listeners
253
+ */
254
+ clear() {
255
+ this.state = {};
256
+ this.listeners.clear();
257
+ this.patternListeners.clear();
258
+ this.cursor = 0;
259
+
260
+ // Clear buffer
261
+ for (let i = 0; i < this.bufferSize; i++) {
262
+ this.buffer[i].path = null;
263
+ this.buffer[i].value = null;
264
+ this.buffer[i].timestamp = 0;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Get recent events from ring buffer
270
+ * @param {number} count - Number of recent events
271
+ * @returns {Array} Recent events
272
+ */
273
+ getRecentEvents(count = 10) {
274
+ const events = [];
275
+ const start = Math.max(0, this.cursor - count);
276
+
277
+ for (let i = start; i < this.cursor && events.length < count; i++) {
278
+ const index = i & this.mask;
279
+ const event = this.buffer[index];
280
+ if (event.path) {
281
+ events.push({
282
+ path: event.path,
283
+ value: event.value,
284
+ timestamp: event.timestamp
285
+ });
286
+ }
287
+ }
288
+
289
+ return events.reverse();
290
+ }
291
+ }
292
+
293
+ // Create singleton instance
294
+ const store = new WuStore();
295
+
296
+ // Export both class and instance
297
+ export default store;