wu-framework 1.1.15 → 1.1.17

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 (88) hide show
  1. package/README.md +52 -20
  2. package/dist/wu-framework.cjs.js +1 -1
  3. package/dist/wu-framework.cjs.js.map +1 -1
  4. package/dist/wu-framework.dev.js +15511 -15146
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js +1 -1
  7. package/dist/wu-framework.esm.js.map +1 -1
  8. package/dist/wu-framework.umd.js +1 -1
  9. package/dist/wu-framework.umd.js.map +1 -1
  10. package/package.json +166 -161
  11. package/src/adapters/angular/ai.js +30 -30
  12. package/src/adapters/angular/index.d.ts +154 -154
  13. package/src/adapters/angular/index.js +932 -932
  14. package/src/adapters/angular.d.ts +3 -3
  15. package/src/adapters/angular.js +3 -3
  16. package/src/adapters/index.js +168 -168
  17. package/src/adapters/lit/ai.js +20 -20
  18. package/src/adapters/lit/index.d.ts +120 -120
  19. package/src/adapters/lit/index.js +721 -721
  20. package/src/adapters/lit.d.ts +3 -3
  21. package/src/adapters/lit.js +3 -3
  22. package/src/adapters/preact/ai.js +33 -33
  23. package/src/adapters/preact/index.d.ts +108 -108
  24. package/src/adapters/preact/index.js +661 -661
  25. package/src/adapters/preact.d.ts +3 -3
  26. package/src/adapters/preact.js +3 -3
  27. package/src/adapters/react/index.js +48 -54
  28. package/src/adapters/react.d.ts +3 -3
  29. package/src/adapters/react.js +3 -3
  30. package/src/adapters/shared.js +64 -64
  31. package/src/adapters/solid/ai.js +32 -32
  32. package/src/adapters/solid/index.d.ts +101 -101
  33. package/src/adapters/solid/index.js +586 -586
  34. package/src/adapters/solid.d.ts +3 -3
  35. package/src/adapters/solid.js +3 -3
  36. package/src/adapters/svelte/ai.js +31 -31
  37. package/src/adapters/svelte/index.d.ts +166 -166
  38. package/src/adapters/svelte/index.js +798 -798
  39. package/src/adapters/svelte.d.ts +3 -3
  40. package/src/adapters/svelte.js +3 -3
  41. package/src/adapters/vanilla/ai.js +30 -30
  42. package/src/adapters/vanilla/index.d.ts +179 -179
  43. package/src/adapters/vanilla/index.js +785 -785
  44. package/src/adapters/vanilla.d.ts +3 -3
  45. package/src/adapters/vanilla.js +3 -3
  46. package/src/adapters/vue/ai.js +52 -52
  47. package/src/adapters/vue/index.d.ts +299 -299
  48. package/src/adapters/vue/index.js +610 -610
  49. package/src/adapters/vue.d.ts +3 -3
  50. package/src/adapters/vue.js +3 -3
  51. package/src/ai/wu-ai-actions.js +261 -261
  52. package/src/ai/wu-ai-agent.js +546 -546
  53. package/src/ai/wu-ai-browser-primitives.js +354 -354
  54. package/src/ai/wu-ai-browser.js +380 -380
  55. package/src/ai/wu-ai-context.js +332 -332
  56. package/src/ai/wu-ai-conversation.js +613 -613
  57. package/src/ai/wu-ai-orchestrate.js +1021 -1021
  58. package/src/ai/wu-ai-permissions.js +381 -381
  59. package/src/ai/wu-ai-provider.js +700 -700
  60. package/src/ai/wu-ai-schema.js +225 -225
  61. package/src/ai/wu-ai-triggers.js +396 -396
  62. package/src/ai/wu-ai.js +804 -804
  63. package/src/core/wu-app.js +236 -236
  64. package/src/core/wu-cache.js +498 -477
  65. package/src/core/wu-core.js +1412 -1398
  66. package/src/core/wu-error-boundary.js +396 -382
  67. package/src/core/wu-event-bus.js +390 -348
  68. package/src/core/wu-hooks.js +350 -350
  69. package/src/core/wu-html-parser.js +199 -190
  70. package/src/core/wu-iframe-sandbox.js +328 -328
  71. package/src/core/wu-loader.js +385 -273
  72. package/src/core/wu-logger.js +142 -134
  73. package/src/core/wu-manifest.js +532 -509
  74. package/src/core/wu-mcp-bridge.js +432 -432
  75. package/src/core/wu-overrides.js +510 -510
  76. package/src/core/wu-performance.js +228 -228
  77. package/src/core/wu-plugin.js +401 -348
  78. package/src/core/wu-prefetch.js +414 -414
  79. package/src/core/wu-proxy-sandbox.js +477 -476
  80. package/src/core/wu-sandbox.js +779 -779
  81. package/src/core/wu-script-executor.js +161 -113
  82. package/src/core/wu-snapshot-sandbox.js +227 -227
  83. package/src/core/wu-store.js +13 -3
  84. package/src/core/wu-strategies.js +256 -256
  85. package/src/core/wu-style-bridge.js +477 -477
  86. package/src/index.d.ts +317 -0
  87. package/src/index.js +234 -224
  88. package/src/utils/dependency-resolver.js +327 -327
@@ -1,785 +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;
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
+ const 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;