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
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🚀 WU-FRAMEWORK VANILLA JS ADAPTER
|
|
3
|
+
*
|
|
4
|
+
* El adapter más simple - Para JavaScript puro sin frameworks.
|
|
5
|
+
* Ideal para microfrontends ligeros o legacy code.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Microfrontend (main.js)
|
|
9
|
+
* import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
10
|
+
*
|
|
11
|
+
* wuVanilla.register('my-app', {
|
|
12
|
+
* render: (container) => {
|
|
13
|
+
* container.innerHTML = '<h1>Hello World</h1>';
|
|
14
|
+
* }
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Con clase
|
|
19
|
+
* class MyApp {
|
|
20
|
+
* constructor(container) {
|
|
21
|
+
* this.container = container;
|
|
22
|
+
* }
|
|
23
|
+
* render() {
|
|
24
|
+
* this.container.innerHTML = '<h1>My App</h1>';
|
|
25
|
+
* }
|
|
26
|
+
* destroy() {
|
|
27
|
+
* this.container.innerHTML = '';
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* wuVanilla.registerClass('my-app', MyApp);
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// Estado global del adapter
|
|
35
|
+
const adapterState = {
|
|
36
|
+
apps: new Map(),
|
|
37
|
+
instances: new Map()
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Obtiene la instancia de Wu Framework
|
|
42
|
+
*/
|
|
43
|
+
function getWuInstance() {
|
|
44
|
+
if (typeof window === 'undefined') return null;
|
|
45
|
+
|
|
46
|
+
return window.wu
|
|
47
|
+
|| window.parent?.wu
|
|
48
|
+
|| window.top?.wu
|
|
49
|
+
|| null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Espera a que Wu Framework esté disponible
|
|
54
|
+
*/
|
|
55
|
+
function waitForWu(timeout = 5000) {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const wu = getWuInstance();
|
|
58
|
+
if (wu) {
|
|
59
|
+
resolve(wu);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const startTime = Date.now();
|
|
64
|
+
|
|
65
|
+
const handleWuReady = () => {
|
|
66
|
+
cleanup();
|
|
67
|
+
resolve(getWuInstance());
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
window.addEventListener('wu:ready', handleWuReady);
|
|
71
|
+
window.addEventListener('wu:app:ready', handleWuReady);
|
|
72
|
+
|
|
73
|
+
const checkInterval = setInterval(() => {
|
|
74
|
+
const wu = getWuInstance();
|
|
75
|
+
if (wu) {
|
|
76
|
+
cleanup();
|
|
77
|
+
resolve(wu);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (Date.now() - startTime > timeout) {
|
|
82
|
+
cleanup();
|
|
83
|
+
reject(new Error(`Wu Framework not found after ${timeout}ms`));
|
|
84
|
+
}
|
|
85
|
+
}, 200);
|
|
86
|
+
|
|
87
|
+
function cleanup() {
|
|
88
|
+
clearInterval(checkInterval);
|
|
89
|
+
window.removeEventListener('wu:ready', handleWuReady);
|
|
90
|
+
window.removeEventListener('wu:app:ready', handleWuReady);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Registra una app Vanilla JS como microfrontend
|
|
97
|
+
*
|
|
98
|
+
* @param {string} appName - Nombre único del microfrontend
|
|
99
|
+
* @param {Object} config - Configuración de la app
|
|
100
|
+
* @param {Function} config.render - Función para renderizar (recibe container)
|
|
101
|
+
* @param {Function} [config.destroy] - Función para limpiar (recibe container)
|
|
102
|
+
* @param {Function} [config.init] - Función de inicialización (recibe container)
|
|
103
|
+
* @param {Object} [config.state] - Estado inicial
|
|
104
|
+
* @param {Object} options - Opciones adicionales
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* wuVanilla.register('counter', {
|
|
108
|
+
* state: { count: 0 },
|
|
109
|
+
* init: (container) => {
|
|
110
|
+
* console.log('Initializing...');
|
|
111
|
+
* },
|
|
112
|
+
* render: (container, state) => {
|
|
113
|
+
* container.innerHTML = `
|
|
114
|
+
* <div>
|
|
115
|
+
* <h1>Count: ${state.count}</h1>
|
|
116
|
+
* <button id="increment">+</button>
|
|
117
|
+
* </div>
|
|
118
|
+
* `;
|
|
119
|
+
* container.querySelector('#increment').onclick = () => {
|
|
120
|
+
* state.count++;
|
|
121
|
+
* // Re-render
|
|
122
|
+
* };
|
|
123
|
+
* },
|
|
124
|
+
* destroy: (container) => {
|
|
125
|
+
* container.innerHTML = '';
|
|
126
|
+
* }
|
|
127
|
+
* });
|
|
128
|
+
*/
|
|
129
|
+
async function register(appName, config, options = {}) {
|
|
130
|
+
const {
|
|
131
|
+
render,
|
|
132
|
+
destroy = null,
|
|
133
|
+
init = null,
|
|
134
|
+
state = {}
|
|
135
|
+
} = config;
|
|
136
|
+
|
|
137
|
+
const {
|
|
138
|
+
onMount = null,
|
|
139
|
+
onUnmount = null,
|
|
140
|
+
standalone = true,
|
|
141
|
+
standaloneContainer = '#app'
|
|
142
|
+
} = options;
|
|
143
|
+
|
|
144
|
+
if (!render || typeof render !== 'function') {
|
|
145
|
+
throw new Error(`[WuVanilla] render function is required for ${appName}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Estado local de la app
|
|
149
|
+
let appState = { ...state };
|
|
150
|
+
|
|
151
|
+
// Función de mount
|
|
152
|
+
const mountApp = (container) => {
|
|
153
|
+
if (!container) {
|
|
154
|
+
console.error(`[WuVanilla] Mount failed for ${appName}: container is null`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Evitar doble mount
|
|
159
|
+
if (adapterState.apps.has(appName)) {
|
|
160
|
+
console.warn(`[WuVanilla] ${appName} already mounted, unmounting first`);
|
|
161
|
+
unmountApp();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// Limpiar container
|
|
166
|
+
container.innerHTML = '';
|
|
167
|
+
|
|
168
|
+
// Ejecutar init si existe
|
|
169
|
+
if (init && typeof init === 'function') {
|
|
170
|
+
init(container, appState);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Renderizar
|
|
174
|
+
render(container, appState);
|
|
175
|
+
|
|
176
|
+
// Guardar referencia
|
|
177
|
+
adapterState.apps.set(appName, {
|
|
178
|
+
container,
|
|
179
|
+
config,
|
|
180
|
+
state: appState
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
console.log(`[WuVanilla] ✅ ${appName} mounted successfully`);
|
|
184
|
+
|
|
185
|
+
if (onMount) {
|
|
186
|
+
onMount(container, appState);
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error(`[WuVanilla] Mount error for ${appName}:`, error);
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Función de unmount
|
|
195
|
+
const unmountApp = (container) => {
|
|
196
|
+
const appData = adapterState.apps.get(appName);
|
|
197
|
+
|
|
198
|
+
if (appData) {
|
|
199
|
+
try {
|
|
200
|
+
if (onUnmount) {
|
|
201
|
+
onUnmount(appData.container, appData.state);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Ejecutar destroy si existe
|
|
205
|
+
if (destroy && typeof destroy === 'function') {
|
|
206
|
+
destroy(appData.container, appData.state);
|
|
207
|
+
} else {
|
|
208
|
+
// Cleanup por defecto
|
|
209
|
+
appData.container.innerHTML = '';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
adapterState.apps.delete(appName);
|
|
213
|
+
|
|
214
|
+
console.log(`[WuVanilla] ✅ ${appName} unmounted successfully`);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error(`[WuVanilla] Unmount error for ${appName}:`, error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (container) {
|
|
221
|
+
container.innerHTML = '';
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Intentar registrar con Wu Framework
|
|
226
|
+
try {
|
|
227
|
+
const wu = await waitForWu(3000);
|
|
228
|
+
|
|
229
|
+
wu.define(appName, {
|
|
230
|
+
mount: mountApp,
|
|
231
|
+
unmount: unmountApp
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
console.log(`[WuVanilla] ✅ ${appName} registered with Wu Framework`);
|
|
235
|
+
return true;
|
|
236
|
+
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.warn(`[WuVanilla] Wu Framework not available for ${appName}`);
|
|
239
|
+
|
|
240
|
+
if (standalone) {
|
|
241
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
242
|
+
|
|
243
|
+
if (containerElement) {
|
|
244
|
+
console.log(`[WuVanilla] Running ${appName} in standalone mode`);
|
|
245
|
+
mountApp(containerElement);
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Registra una clase como microfrontend
|
|
256
|
+
*
|
|
257
|
+
* @param {string} appName - Nombre único del microfrontend
|
|
258
|
+
* @param {Function} AppClass - Clase con constructor(container) y métodos render/destroy
|
|
259
|
+
* @param {Object} options - Opciones adicionales
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* class TodoApp {
|
|
263
|
+
* constructor(container) {
|
|
264
|
+
* this.container = container;
|
|
265
|
+
* this.todos = [];
|
|
266
|
+
* }
|
|
267
|
+
*
|
|
268
|
+
* render() {
|
|
269
|
+
* this.container.innerHTML = `
|
|
270
|
+
* <ul>${this.todos.map(t => `<li>${t}</li>`).join('')}</ul>
|
|
271
|
+
* `;
|
|
272
|
+
* }
|
|
273
|
+
*
|
|
274
|
+
* addTodo(text) {
|
|
275
|
+
* this.todos.push(text);
|
|
276
|
+
* this.render();
|
|
277
|
+
* }
|
|
278
|
+
*
|
|
279
|
+
* destroy() {
|
|
280
|
+
* this.container.innerHTML = '';
|
|
281
|
+
* this.todos = [];
|
|
282
|
+
* }
|
|
283
|
+
* }
|
|
284
|
+
*
|
|
285
|
+
* wuVanilla.registerClass('todo-app', TodoApp);
|
|
286
|
+
*/
|
|
287
|
+
async function registerClass(appName, AppClass, options = {}) {
|
|
288
|
+
const {
|
|
289
|
+
constructorArgs = [],
|
|
290
|
+
onMount = null,
|
|
291
|
+
onUnmount = null,
|
|
292
|
+
standalone = true,
|
|
293
|
+
standaloneContainer = '#app'
|
|
294
|
+
} = options;
|
|
295
|
+
|
|
296
|
+
// Función de mount
|
|
297
|
+
const mountApp = (container) => {
|
|
298
|
+
if (!container) {
|
|
299
|
+
console.error(`[WuVanilla] Mount failed for ${appName}: container is null`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Evitar doble mount
|
|
304
|
+
if (adapterState.instances.has(appName)) {
|
|
305
|
+
console.warn(`[WuVanilla] ${appName} already mounted, unmounting first`);
|
|
306
|
+
unmountApp();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
container.innerHTML = '';
|
|
311
|
+
|
|
312
|
+
// Crear instancia de la clase
|
|
313
|
+
const instance = new AppClass(container, ...constructorArgs);
|
|
314
|
+
|
|
315
|
+
// Llamar render si existe
|
|
316
|
+
if (instance.render && typeof instance.render === 'function') {
|
|
317
|
+
instance.render();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Guardar instancia
|
|
321
|
+
adapterState.instances.set(appName, {
|
|
322
|
+
instance,
|
|
323
|
+
container
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
console.log(`[WuVanilla] ✅ ${appName} (class) mounted successfully`);
|
|
327
|
+
|
|
328
|
+
if (onMount) {
|
|
329
|
+
onMount(container, instance);
|
|
330
|
+
}
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error(`[WuVanilla] Mount error for ${appName}:`, error);
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// Función de unmount
|
|
338
|
+
const unmountApp = (container) => {
|
|
339
|
+
const appData = adapterState.instances.get(appName);
|
|
340
|
+
|
|
341
|
+
if (appData) {
|
|
342
|
+
try {
|
|
343
|
+
if (onUnmount) {
|
|
344
|
+
onUnmount(appData.container, appData.instance);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Llamar destroy si existe
|
|
348
|
+
if (appData.instance.destroy && typeof appData.instance.destroy === 'function') {
|
|
349
|
+
appData.instance.destroy();
|
|
350
|
+
} else {
|
|
351
|
+
appData.container.innerHTML = '';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
adapterState.instances.delete(appName);
|
|
355
|
+
|
|
356
|
+
console.log(`[WuVanilla] ✅ ${appName} (class) unmounted successfully`);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error(`[WuVanilla] Unmount error for ${appName}:`, error);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (container) {
|
|
363
|
+
container.innerHTML = '';
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// Intentar registrar con Wu Framework
|
|
368
|
+
try {
|
|
369
|
+
const wu = await waitForWu(3000);
|
|
370
|
+
|
|
371
|
+
wu.define(appName, {
|
|
372
|
+
mount: mountApp,
|
|
373
|
+
unmount: unmountApp
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
console.log(`[WuVanilla] ✅ ${appName} (class) registered with Wu Framework`);
|
|
377
|
+
return true;
|
|
378
|
+
|
|
379
|
+
} catch (error) {
|
|
380
|
+
console.warn(`[WuVanilla] Wu Framework not available for ${appName}`);
|
|
381
|
+
|
|
382
|
+
if (standalone) {
|
|
383
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
384
|
+
|
|
385
|
+
if (containerElement) {
|
|
386
|
+
console.log(`[WuVanilla] Running ${appName} in standalone mode`);
|
|
387
|
+
mountApp(containerElement);
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Registra un template HTML como microfrontend
|
|
398
|
+
*
|
|
399
|
+
* @param {string} appName - Nombre único del microfrontend
|
|
400
|
+
* @param {string|Function} template - HTML string o función que retorna HTML
|
|
401
|
+
* @param {Object} options - Opciones adicionales
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* // Template estático
|
|
405
|
+
* wuVanilla.registerTemplate('header', '<header><h1>My Header</h1></header>');
|
|
406
|
+
*
|
|
407
|
+
* // Template dinámico
|
|
408
|
+
* wuVanilla.registerTemplate('greeting', (data) => `<h1>Hello ${data.name}!</h1>`, {
|
|
409
|
+
* data: { name: 'World' }
|
|
410
|
+
* });
|
|
411
|
+
*/
|
|
412
|
+
async function registerTemplate(appName, template, options = {}) {
|
|
413
|
+
const {
|
|
414
|
+
data = {},
|
|
415
|
+
scripts = [],
|
|
416
|
+
styles = [],
|
|
417
|
+
onMount = null,
|
|
418
|
+
onUnmount = null,
|
|
419
|
+
standalone = true,
|
|
420
|
+
standaloneContainer = '#app'
|
|
421
|
+
} = options;
|
|
422
|
+
|
|
423
|
+
const mountApp = (container) => {
|
|
424
|
+
if (!container) {
|
|
425
|
+
console.error(`[WuVanilla] Mount failed for ${appName}: container is null`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
container.innerHTML = '';
|
|
431
|
+
|
|
432
|
+
// Inyectar estilos
|
|
433
|
+
if (styles.length > 0) {
|
|
434
|
+
const styleEl = document.createElement('style');
|
|
435
|
+
styleEl.textContent = styles.join('\n');
|
|
436
|
+
styleEl.setAttribute('data-wu-app', appName);
|
|
437
|
+
container.appendChild(styleEl);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Crear wrapper
|
|
441
|
+
const wrapper = document.createElement('div');
|
|
442
|
+
wrapper.setAttribute('data-wu-template', appName);
|
|
443
|
+
|
|
444
|
+
// Renderizar template
|
|
445
|
+
if (typeof template === 'function') {
|
|
446
|
+
wrapper.innerHTML = template(data);
|
|
447
|
+
} else {
|
|
448
|
+
wrapper.innerHTML = template;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
container.appendChild(wrapper);
|
|
452
|
+
|
|
453
|
+
// Ejecutar scripts
|
|
454
|
+
scripts.forEach(scriptFn => {
|
|
455
|
+
if (typeof scriptFn === 'function') {
|
|
456
|
+
scriptFn(container, data);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
adapterState.apps.set(appName, { container, template, data });
|
|
461
|
+
|
|
462
|
+
console.log(`[WuVanilla] ✅ ${appName} (template) mounted successfully`);
|
|
463
|
+
|
|
464
|
+
if (onMount) {
|
|
465
|
+
onMount(container, data);
|
|
466
|
+
}
|
|
467
|
+
} catch (error) {
|
|
468
|
+
console.error(`[WuVanilla] Mount error for ${appName}:`, error);
|
|
469
|
+
throw error;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const unmountApp = (container) => {
|
|
474
|
+
const appData = adapterState.apps.get(appName);
|
|
475
|
+
|
|
476
|
+
if (appData) {
|
|
477
|
+
if (onUnmount) {
|
|
478
|
+
onUnmount(appData.container, appData.data);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
appData.container.innerHTML = '';
|
|
482
|
+
adapterState.apps.delete(appName);
|
|
483
|
+
|
|
484
|
+
console.log(`[WuVanilla] ✅ ${appName} (template) unmounted successfully`);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (container) {
|
|
488
|
+
container.innerHTML = '';
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
const wu = await waitForWu(3000);
|
|
494
|
+
|
|
495
|
+
wu.define(appName, {
|
|
496
|
+
mount: mountApp,
|
|
497
|
+
unmount: unmountApp
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
console.log(`[WuVanilla] ✅ ${appName} (template) registered with Wu Framework`);
|
|
501
|
+
return true;
|
|
502
|
+
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.warn(`[WuVanilla] Wu Framework not available for ${appName}`);
|
|
505
|
+
|
|
506
|
+
if (standalone) {
|
|
507
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
508
|
+
if (containerElement) {
|
|
509
|
+
console.log(`[WuVanilla] Running ${appName} in standalone mode`);
|
|
510
|
+
mountApp(containerElement);
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Helper para crear un componente reactivo simple
|
|
521
|
+
*
|
|
522
|
+
* @param {Object} config - Configuración del componente
|
|
523
|
+
* @returns {Object} Componente con métodos de estado
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* const Counter = wuVanilla.createComponent({
|
|
527
|
+
* state: { count: 0 },
|
|
528
|
+
* template: (state) => `
|
|
529
|
+
* <div>
|
|
530
|
+
* <h1>Count: ${state.count}</h1>
|
|
531
|
+
* <button data-action="increment">+</button>
|
|
532
|
+
* <button data-action="decrement">-</button>
|
|
533
|
+
* </div>
|
|
534
|
+
* `,
|
|
535
|
+
* actions: {
|
|
536
|
+
* increment: (state) => ({ count: state.count + 1 }),
|
|
537
|
+
* decrement: (state) => ({ count: state.count - 1 })
|
|
538
|
+
* }
|
|
539
|
+
* });
|
|
540
|
+
*
|
|
541
|
+
* wuVanilla.register('counter', Counter);
|
|
542
|
+
*/
|
|
543
|
+
function createComponent(config) {
|
|
544
|
+
const { state: initialState = {}, template, actions = {}, onInit, onDestroy } = config;
|
|
545
|
+
|
|
546
|
+
let currentState = { ...initialState };
|
|
547
|
+
let container = null;
|
|
548
|
+
let mounted = false;
|
|
549
|
+
|
|
550
|
+
const setState = (newState) => {
|
|
551
|
+
currentState = { ...currentState, ...newState };
|
|
552
|
+
if (mounted && container) {
|
|
553
|
+
render(container, currentState);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const render = (cont, state) => {
|
|
558
|
+
const html = template(state);
|
|
559
|
+
|
|
560
|
+
// Preservar focus si es posible
|
|
561
|
+
const activeId = document.activeElement?.id;
|
|
562
|
+
|
|
563
|
+
cont.innerHTML = html;
|
|
564
|
+
|
|
565
|
+
// Restaurar focus
|
|
566
|
+
if (activeId) {
|
|
567
|
+
const el = cont.querySelector(`#${activeId}`);
|
|
568
|
+
if (el) el.focus();
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Bind actions
|
|
572
|
+
cont.querySelectorAll('[data-action]').forEach(el => {
|
|
573
|
+
const actionName = el.getAttribute('data-action');
|
|
574
|
+
if (actions[actionName]) {
|
|
575
|
+
el.addEventListener('click', () => {
|
|
576
|
+
const result = actions[actionName](currentState, el);
|
|
577
|
+
if (result) {
|
|
578
|
+
setState(result);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
return {
|
|
586
|
+
state: currentState,
|
|
587
|
+
|
|
588
|
+
init: (cont) => {
|
|
589
|
+
container = cont;
|
|
590
|
+
if (onInit) onInit(cont, currentState);
|
|
591
|
+
},
|
|
592
|
+
|
|
593
|
+
render: (cont, state) => {
|
|
594
|
+
container = cont;
|
|
595
|
+
currentState = state || currentState;
|
|
596
|
+
mounted = true;
|
|
597
|
+
render(cont, currentState);
|
|
598
|
+
},
|
|
599
|
+
|
|
600
|
+
destroy: (cont) => {
|
|
601
|
+
if (onDestroy) onDestroy(cont, currentState);
|
|
602
|
+
mounted = false;
|
|
603
|
+
container = null;
|
|
604
|
+
cont.innerHTML = '';
|
|
605
|
+
},
|
|
606
|
+
|
|
607
|
+
// Exponer setState para uso externo
|
|
608
|
+
setState,
|
|
609
|
+
getState: () => currentState
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Helper para usar eventos de Wu Framework
|
|
615
|
+
*/
|
|
616
|
+
function useWuEvents() {
|
|
617
|
+
const subscriptions = [];
|
|
618
|
+
|
|
619
|
+
return {
|
|
620
|
+
emit: (event, data, options) => {
|
|
621
|
+
const wu = getWuInstance();
|
|
622
|
+
if (wu?.eventBus) {
|
|
623
|
+
wu.eventBus.emit(event, data, options);
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
|
|
627
|
+
on: (event, callback) => {
|
|
628
|
+
const wu = getWuInstance();
|
|
629
|
+
if (wu?.eventBus) {
|
|
630
|
+
const unsubscribe = wu.eventBus.on(event, callback);
|
|
631
|
+
subscriptions.push(unsubscribe);
|
|
632
|
+
return unsubscribe;
|
|
633
|
+
}
|
|
634
|
+
return () => {};
|
|
635
|
+
},
|
|
636
|
+
|
|
637
|
+
once: (event, callback) => {
|
|
638
|
+
const wu = getWuInstance();
|
|
639
|
+
if (wu?.eventBus) {
|
|
640
|
+
return wu.eventBus.once(event, callback);
|
|
641
|
+
}
|
|
642
|
+
return () => {};
|
|
643
|
+
},
|
|
644
|
+
|
|
645
|
+
off: (event, callback) => {
|
|
646
|
+
const wu = getWuInstance();
|
|
647
|
+
if (wu?.eventBus) {
|
|
648
|
+
wu.eventBus.off(event, callback);
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
|
|
652
|
+
cleanup: () => {
|
|
653
|
+
subscriptions.forEach(unsub => unsub());
|
|
654
|
+
subscriptions.length = 0;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Helper para usar el Store de Wu Framework
|
|
661
|
+
*/
|
|
662
|
+
function useWuStore(namespace = '') {
|
|
663
|
+
return {
|
|
664
|
+
get: (path = '') => {
|
|
665
|
+
const wu = getWuInstance();
|
|
666
|
+
if (wu?.store) {
|
|
667
|
+
const fullPath = namespace ? (path ? `${namespace}.${path}` : namespace) : path;
|
|
668
|
+
return wu.store.get(fullPath);
|
|
669
|
+
}
|
|
670
|
+
return null;
|
|
671
|
+
},
|
|
672
|
+
|
|
673
|
+
set: (path, value) => {
|
|
674
|
+
const wu = getWuInstance();
|
|
675
|
+
if (wu?.store) {
|
|
676
|
+
const fullPath = namespace ? `${namespace}.${path}` : path;
|
|
677
|
+
wu.store.set(fullPath, value);
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
|
|
681
|
+
onChange: (pattern, callback) => {
|
|
682
|
+
const wu = getWuInstance();
|
|
683
|
+
if (wu?.store) {
|
|
684
|
+
const fullPattern = namespace ? `${namespace}.${pattern}` : pattern;
|
|
685
|
+
return wu.store.on(fullPattern, callback);
|
|
686
|
+
}
|
|
687
|
+
return () => {};
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Crea un WuSlot en JavaScript puro
|
|
694
|
+
*/
|
|
695
|
+
function createWuSlot(target, props) {
|
|
696
|
+
const { name, url, fallbackText = null, onLoad = null, onError = null } = props;
|
|
697
|
+
|
|
698
|
+
const container = document.createElement('div');
|
|
699
|
+
container.className = 'wu-slot';
|
|
700
|
+
container.style.cssText = 'min-height: 100px; position: relative;';
|
|
701
|
+
container.setAttribute('data-wu-app', name);
|
|
702
|
+
container.setAttribute('data-wu-url', url);
|
|
703
|
+
|
|
704
|
+
// Loading state
|
|
705
|
+
container.innerHTML = `
|
|
706
|
+
<div style="display: flex; align-items: center; justify-content: center; padding: 2rem; color: #666;">
|
|
707
|
+
${fallbackText || `Loading ${name}...`}
|
|
708
|
+
</div>
|
|
709
|
+
`;
|
|
710
|
+
|
|
711
|
+
target.appendChild(container);
|
|
712
|
+
|
|
713
|
+
let appInstance = null;
|
|
714
|
+
|
|
715
|
+
const mount = async () => {
|
|
716
|
+
try {
|
|
717
|
+
const wu = getWuInstance();
|
|
718
|
+
if (!wu) throw new Error('Wu Framework not initialized');
|
|
719
|
+
|
|
720
|
+
const containerId = `wu-slot-${name}-${Date.now()}`;
|
|
721
|
+
const innerContainer = document.createElement('div');
|
|
722
|
+
innerContainer.id = containerId;
|
|
723
|
+
innerContainer.style.cssText = 'width: 100%; height: 100%;';
|
|
724
|
+
|
|
725
|
+
container.innerHTML = '';
|
|
726
|
+
container.appendChild(innerContainer);
|
|
727
|
+
|
|
728
|
+
const app = wu.app(name, {
|
|
729
|
+
url,
|
|
730
|
+
container: `#${containerId}`,
|
|
731
|
+
autoInit: true
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
appInstance = app;
|
|
735
|
+
await app.mount();
|
|
736
|
+
|
|
737
|
+
if (onLoad) onLoad({ name, url });
|
|
738
|
+
|
|
739
|
+
} catch (err) {
|
|
740
|
+
container.innerHTML = `
|
|
741
|
+
<div style="padding: 1rem; border: 1px solid #f5c6cb; border-radius: 4px; background: #f8d7da; color: #721c24;">
|
|
742
|
+
<strong>Error loading ${name}</strong>
|
|
743
|
+
<p style="margin: 0.5rem 0 0 0;">${err.message}</p>
|
|
744
|
+
</div>
|
|
745
|
+
`;
|
|
746
|
+
if (onError) onError(err);
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
const destroy = async () => {
|
|
751
|
+
if (appInstance) {
|
|
752
|
+
try {
|
|
753
|
+
await appInstance.unmount();
|
|
754
|
+
} catch (e) {}
|
|
755
|
+
appInstance = null;
|
|
756
|
+
}
|
|
757
|
+
container.remove();
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
mount();
|
|
761
|
+
|
|
762
|
+
return { container, destroy };
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// ============================================
|
|
766
|
+
// AI INTEGRATION
|
|
767
|
+
// ============================================
|
|
768
|
+
export { useWuAI } from './ai.js';
|
|
769
|
+
import { useWuAI } from './ai.js';
|
|
770
|
+
|
|
771
|
+
// API pública del adapter
|
|
772
|
+
export const wuVanilla = {
|
|
773
|
+
register,
|
|
774
|
+
registerClass,
|
|
775
|
+
registerTemplate,
|
|
776
|
+
createComponent,
|
|
777
|
+
createWuSlot,
|
|
778
|
+
useWuEvents,
|
|
779
|
+
useWuStore,
|
|
780
|
+
useWuAI,
|
|
781
|
+
getWuInstance,
|
|
782
|
+
waitForWu
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
export default wuVanilla;
|