wu-framework 1.1.14 → 1.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +39 -39
  2. package/README.md +408 -408
  3. package/dist/wu-framework.cjs.js.map +1 -1
  4. package/dist/wu-framework.dev.js +15151 -15151
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js.map +1 -1
  7. package/dist/wu-framework.umd.js.map +1 -1
  8. package/integrations/astro/README.md +127 -127
  9. package/integrations/astro/WuApp.astro +63 -63
  10. package/integrations/astro/WuShell.astro +39 -39
  11. package/integrations/astro/index.js +68 -68
  12. package/integrations/astro/package.json +38 -38
  13. package/integrations/astro/types.d.ts +53 -53
  14. package/package.json +161 -161
  15. package/src/adapters/angular/ai.js +30 -30
  16. package/src/adapters/angular/index.d.ts +154 -154
  17. package/src/adapters/angular/index.js +932 -932
  18. package/src/adapters/angular.d.ts +3 -3
  19. package/src/adapters/angular.js +3 -3
  20. package/src/adapters/index.js +168 -168
  21. package/src/adapters/lit/ai.js +20 -20
  22. package/src/adapters/lit/index.d.ts +120 -120
  23. package/src/adapters/lit/index.js +721 -721
  24. package/src/adapters/lit.d.ts +3 -3
  25. package/src/adapters/lit.js +3 -3
  26. package/src/adapters/preact/ai.js +33 -33
  27. package/src/adapters/preact/index.d.ts +108 -108
  28. package/src/adapters/preact/index.js +661 -661
  29. package/src/adapters/preact.d.ts +3 -3
  30. package/src/adapters/preact.js +3 -3
  31. package/src/adapters/react/index.js +48 -54
  32. package/src/adapters/react.d.ts +3 -3
  33. package/src/adapters/react.js +3 -3
  34. package/src/adapters/shared.js +64 -64
  35. package/src/adapters/solid/ai.js +32 -32
  36. package/src/adapters/solid/index.d.ts +101 -101
  37. package/src/adapters/solid/index.js +586 -586
  38. package/src/adapters/solid.d.ts +3 -3
  39. package/src/adapters/solid.js +3 -3
  40. package/src/adapters/svelte/ai.js +31 -31
  41. package/src/adapters/svelte/index.d.ts +166 -166
  42. package/src/adapters/svelte/index.js +798 -798
  43. package/src/adapters/svelte.d.ts +3 -3
  44. package/src/adapters/svelte.js +3 -3
  45. package/src/adapters/vanilla/ai.js +30 -30
  46. package/src/adapters/vanilla/index.d.ts +179 -179
  47. package/src/adapters/vanilla/index.js +785 -785
  48. package/src/adapters/vanilla.d.ts +3 -3
  49. package/src/adapters/vanilla.js +3 -3
  50. package/src/adapters/vue/ai.js +52 -52
  51. package/src/adapters/vue/index.d.ts +299 -299
  52. package/src/adapters/vue/index.js +610 -610
  53. package/src/adapters/vue.d.ts +3 -3
  54. package/src/adapters/vue.js +3 -3
  55. package/src/ai/wu-ai-actions.js +261 -261
  56. package/src/ai/wu-ai-agent.js +546 -546
  57. package/src/ai/wu-ai-browser-primitives.js +354 -354
  58. package/src/ai/wu-ai-browser.js +380 -380
  59. package/src/ai/wu-ai-context.js +332 -332
  60. package/src/ai/wu-ai-conversation.js +613 -613
  61. package/src/ai/wu-ai-orchestrate.js +1021 -1021
  62. package/src/ai/wu-ai-permissions.js +381 -381
  63. package/src/ai/wu-ai-provider.js +700 -700
  64. package/src/ai/wu-ai-schema.js +225 -225
  65. package/src/ai/wu-ai-triggers.js +396 -396
  66. package/src/ai/wu-ai.js +804 -804
  67. package/src/core/wu-app.js +236 -236
  68. package/src/core/wu-cache.js +477 -477
  69. package/src/core/wu-core.js +1398 -1398
  70. package/src/core/wu-error-boundary.js +382 -382
  71. package/src/core/wu-event-bus.js +348 -348
  72. package/src/core/wu-hooks.js +350 -350
  73. package/src/core/wu-html-parser.js +190 -190
  74. package/src/core/wu-iframe-sandbox.js +328 -328
  75. package/src/core/wu-loader.js +272 -272
  76. package/src/core/wu-logger.js +134 -134
  77. package/src/core/wu-manifest.js +509 -509
  78. package/src/core/wu-mcp-bridge.js +432 -432
  79. package/src/core/wu-overrides.js +510 -510
  80. package/src/core/wu-performance.js +228 -228
  81. package/src/core/wu-plugin.js +348 -348
  82. package/src/core/wu-prefetch.js +414 -414
  83. package/src/core/wu-proxy-sandbox.js +476 -476
  84. package/src/core/wu-sandbox.js +779 -779
  85. package/src/core/wu-script-executor.js +113 -113
  86. package/src/core/wu-snapshot-sandbox.js +227 -227
  87. package/src/core/wu-strategies.js +256 -256
  88. package/src/core/wu-style-bridge.js +477 -477
  89. package/src/index.js +224 -224
  90. package/src/utils/dependency-resolver.js +327 -327
@@ -1,721 +1,721 @@
1
- /**
2
- * 🚀 WU-FRAMEWORK LIT ADAPTER
3
- *
4
- * Simplifica la integración de Lit (Web Components) con Wu Framework.
5
- * Aprovecha los Web Components nativos con Shadow DOM incluido.
6
- *
7
- * @example
8
- * // Microfrontend (main.js)
9
- * import { wuLit } from 'wu-framework/adapters/lit';
10
- * import { MyApp } from './my-app';
11
- *
12
- * wuLit.register('my-app', MyApp);
13
- *
14
- * @example
15
- * // Usando LitElement
16
- * import { LitElement, html, css } from 'lit';
17
- *
18
- * class MyApp extends LitElement {
19
- * static styles = css`:host { display: block; }`;
20
- *
21
- * render() {
22
- * return html`<h1>Hello from Lit!</h1>`;
23
- * }
24
- * }
25
- *
26
- * wuLit.register('my-app', MyApp);
27
- */
28
-
29
- // Estado global del adapter
30
- const adapterState = {
31
- apps: new Map(),
32
- elements: new Map(),
33
- lit: null,
34
- initialized: false
35
- };
36
-
37
- /**
38
- * Obtiene la instancia de Wu Framework
39
- */
40
- function getWuInstance() {
41
- if (typeof window === 'undefined') return null;
42
-
43
- return window.wu
44
- || window.parent?.wu
45
- || window.top?.wu
46
- || null;
47
- }
48
-
49
- /**
50
- * Espera a que Wu Framework esté disponible
51
- */
52
- function waitForWu(timeout = 5000) {
53
- return new Promise((resolve, reject) => {
54
- const wu = getWuInstance();
55
- if (wu) {
56
- resolve(wu);
57
- return;
58
- }
59
-
60
- const startTime = Date.now();
61
-
62
- const handleWuReady = () => {
63
- cleanup();
64
- resolve(getWuInstance());
65
- };
66
-
67
- window.addEventListener('wu:ready', handleWuReady);
68
- window.addEventListener('wu:app:ready', handleWuReady);
69
-
70
- const checkInterval = setInterval(() => {
71
- const wu = getWuInstance();
72
- if (wu) {
73
- cleanup();
74
- resolve(wu);
75
- return;
76
- }
77
-
78
- if (Date.now() - startTime > timeout) {
79
- cleanup();
80
- reject(new Error(`Wu Framework not found after ${timeout}ms`));
81
- }
82
- }, 200);
83
-
84
- function cleanup() {
85
- clearInterval(checkInterval);
86
- window.removeEventListener('wu:ready', handleWuReady);
87
- window.removeEventListener('wu:app:ready', handleWuReady);
88
- }
89
- });
90
- }
91
-
92
- /**
93
- * Genera un nombre de tag válido para Custom Elements
94
- */
95
- function generateTagName(appName) {
96
- // Custom elements deben tener un guión
97
- if (appName.includes('-')) {
98
- return `wu-${appName}`;
99
- }
100
- return `wu-app-${appName}`;
101
- }
102
-
103
- /**
104
- * Registra un LitElement como microfrontend
105
- *
106
- * @param {string} appName - Nombre único del microfrontend
107
- * @param {typeof LitElement} ElementClass - Clase que extiende LitElement
108
- * @param {Object} options - Opciones adicionales
109
- * @param {string} options.tagName - Nombre del custom element (auto-generado si no se provee)
110
- * @param {Object} options.properties - Propiedades iniciales
111
- * @param {Function} options.onMount - Callback después de montar
112
- * @param {Function} options.onUnmount - Callback antes de desmontar
113
- * @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
114
- * @param {string} options.standaloneContainer - Selector para modo standalone (default: '#root')
115
- *
116
- * @example
117
- * import { LitElement, html } from 'lit';
118
- *
119
- * class HeaderApp extends LitElement {
120
- * render() {
121
- * return html`<header><h1>My Header</h1></header>`;
122
- * }
123
- * }
124
- *
125
- * wuLit.register('header', HeaderApp);
126
- */
127
- async function register(appName, ElementClass, options = {}) {
128
- const {
129
- tagName = null,
130
- properties = {},
131
- onMount = null,
132
- onUnmount = null,
133
- standalone = true,
134
- standaloneContainer = '#root'
135
- } = options;
136
-
137
- // Generar nombre de tag
138
- const customTagName = tagName || generateTagName(appName);
139
-
140
- // Registrar el Custom Element si no existe
141
- if (!customElements.get(customTagName)) {
142
- try {
143
- customElements.define(customTagName, ElementClass);
144
- console.log(`[WuLit] Custom element <${customTagName}> defined`);
145
- } catch (error) {
146
- console.error(`[WuLit] Failed to define custom element:`, error);
147
- throw error;
148
- }
149
- }
150
-
151
- // Guardar referencia de la clase
152
- adapterState.elements.set(appName, {
153
- ElementClass,
154
- tagName: customTagName
155
- });
156
-
157
- // Función de mount
158
- const mountApp = (container) => {
159
- if (!container) {
160
- console.error(`[WuLit] Mount failed for ${appName}: container is null`);
161
- return;
162
- }
163
-
164
- // Evitar doble mount
165
- if (adapterState.apps.has(appName)) {
166
- console.warn(`[WuLit] ${appName} already mounted, unmounting first`);
167
- unmountApp();
168
- }
169
-
170
- try {
171
- // Limpiar container
172
- container.innerHTML = '';
173
-
174
- // Crear elemento
175
- const element = document.createElement(customTagName);
176
-
177
- // Aplicar propiedades
178
- Object.entries(properties).forEach(([key, value]) => {
179
- element[key] = value;
180
- });
181
-
182
- // Inyectar información de Wu
183
- element.wuAppName = appName;
184
- element.wuInstance = getWuInstance();
185
-
186
- // Agregar al container
187
- container.appendChild(element);
188
-
189
- // Guardar referencia
190
- adapterState.apps.set(appName, {
191
- element,
192
- container,
193
- tagName: customTagName
194
- });
195
-
196
- console.log(`[WuLit] ✅ ${appName} (<${customTagName}>) mounted successfully`);
197
-
198
- if (onMount) {
199
- onMount(container, element);
200
- }
201
- } catch (error) {
202
- console.error(`[WuLit] Mount error for ${appName}:`, error);
203
- throw error;
204
- }
205
- };
206
-
207
- // Función de unmount
208
- const unmountApp = (container) => {
209
- const appData = adapterState.apps.get(appName);
210
-
211
- if (appData) {
212
- try {
213
- if (onUnmount) {
214
- onUnmount(appData.container, appData.element);
215
- }
216
-
217
- // Remover elemento
218
- if (appData.element && appData.element.parentNode) {
219
- appData.element.remove();
220
- }
221
-
222
- // Limpiar container
223
- appData.container.innerHTML = '';
224
-
225
- adapterState.apps.delete(appName);
226
-
227
- console.log(`[WuLit] ✅ ${appName} unmounted successfully`);
228
- } catch (error) {
229
- console.error(`[WuLit] Unmount error for ${appName}:`, error);
230
- }
231
- }
232
-
233
- if (container) {
234
- container.innerHTML = '';
235
- }
236
- };
237
-
238
- // Intentar registrar con Wu Framework
239
- try {
240
- const wu = await waitForWu(3000);
241
-
242
- wu.define(appName, {
243
- mount: mountApp,
244
- unmount: unmountApp
245
- });
246
-
247
- console.log(`[WuLit] ✅ ${appName} registered with Wu Framework`);
248
- return true;
249
-
250
- } catch (error) {
251
- console.warn(`[WuLit] Wu Framework not available for ${appName}`);
252
-
253
- if (standalone) {
254
- const containerElement = document.querySelector(standaloneContainer);
255
-
256
- if (containerElement) {
257
- console.log(`[WuLit] Running ${appName} in standalone mode`);
258
- mountApp(containerElement);
259
- return true;
260
- }
261
- }
262
-
263
- return false;
264
- }
265
- }
266
-
267
- /**
268
- * Registra un Web Component vanilla (sin Lit) como microfrontend
269
- *
270
- * @param {string} appName - Nombre del microfrontend
271
- * @param {typeof HTMLElement} ElementClass - Clase que extiende HTMLElement
272
- * @param {Object} options - Opciones
273
- *
274
- * @example
275
- * class MyWebComponent extends HTMLElement {
276
- * connectedCallback() {
277
- * this.attachShadow({ mode: 'open' });
278
- * this.shadowRoot.innerHTML = '<h1>Hello!</h1>';
279
- * }
280
- * }
281
- *
282
- * wuLit.registerWebComponent('my-component', MyWebComponent);
283
- */
284
- async function registerWebComponent(appName, ElementClass, options = {}) {
285
- // Usar el mismo registro pero para HTMLElement vanilla
286
- return register(appName, ElementClass, options);
287
- }
288
-
289
- /**
290
- * Crea un LitElement wrapper que carga un microfrontend
291
- *
292
- * @example
293
- * import { html, LitElement } from 'lit';
294
- * import { createWuSlotElement } from 'wu-framework/adapters/lit';
295
- *
296
- * const WuSlot = createWuSlotElement(LitElement, html);
297
- *
298
- * // Uso en otro componente
299
- * render() {
300
- * return html`<wu-slot name="header" url="http://localhost:3001"></wu-slot>`;
301
- * }
302
- */
303
- function createWuSlotElement(LitElement, html, css) {
304
- class WuSlotElement extends LitElement {
305
- static properties = {
306
- name: { type: String },
307
- url: { type: String },
308
- appName: { type: String, attribute: 'app-name' },
309
- fallbackText: { type: String, attribute: 'fallback-text' },
310
- loading: { type: Boolean, state: true },
311
- error: { type: String, state: true }
312
- };
313
-
314
- static styles = css ? css`
315
- :host {
316
- display: block;
317
- min-height: 100px;
318
- position: relative;
319
- }
320
-
321
- .wu-slot-loading {
322
- display: flex;
323
- align-items: center;
324
- justify-content: center;
325
- padding: 2rem;
326
- color: #666;
327
- }
328
-
329
- .wu-slot-error {
330
- padding: 1rem;
331
- border: 1px solid #f5c6cb;
332
- border-radius: 4px;
333
- background: #f8d7da;
334
- color: #721c24;
335
- }
336
-
337
- .wu-slot-error strong {
338
- display: block;
339
- margin-bottom: 0.5rem;
340
- }
341
-
342
- .wu-slot-content {
343
- width: 100%;
344
- height: 100%;
345
- }
346
- ` : [];
347
-
348
- constructor() {
349
- super();
350
- this.name = '';
351
- this.url = '';
352
- this.appName = null;
353
- this.fallbackText = null;
354
- this.loading = true;
355
- this.error = null;
356
- this._appInstance = null;
357
- }
358
-
359
- get actualAppName() {
360
- return this.appName || this.name;
361
- }
362
-
363
- async connectedCallback() {
364
- super.connectedCallback();
365
- await this.mountMicrofrontend();
366
- }
367
-
368
- disconnectedCallback() {
369
- super.disconnectedCallback();
370
- this.unmountMicrofrontend();
371
- }
372
-
373
- async mountMicrofrontend() {
374
- try {
375
- this.loading = true;
376
- this.error = null;
377
-
378
- const wu = getWuInstance();
379
- if (!wu) {
380
- throw new Error('Wu Framework not initialized');
381
- }
382
-
383
- // Esperar a que el componente se renderice
384
- await this.updateComplete;
385
-
386
- const contentSlot = this.shadowRoot.querySelector('.wu-slot-content');
387
- if (!contentSlot) return;
388
-
389
- const containerId = `wu-slot-${this.actualAppName}-${Date.now()}`;
390
- const innerContainer = document.createElement('div');
391
- innerContainer.id = containerId;
392
- innerContainer.style.cssText = 'width: 100%; height: 100%;';
393
-
394
- contentSlot.innerHTML = '';
395
- contentSlot.appendChild(innerContainer);
396
-
397
- const app = wu.app(this.actualAppName, {
398
- url: this.url,
399
- container: `#${containerId}`,
400
- autoInit: true
401
- });
402
-
403
- this._appInstance = app;
404
- await app.mount();
405
-
406
- this.loading = false;
407
- this.dispatchEvent(new CustomEvent('wu-load', {
408
- detail: { name: this.actualAppName, url: this.url },
409
- bubbles: true,
410
- composed: true
411
- }));
412
-
413
- } catch (err) {
414
- console.error(`[WuSlot] Error loading ${this.actualAppName}:`, err);
415
- this.error = err.message || 'Failed to load microfrontend';
416
- this.loading = false;
417
- this.dispatchEvent(new CustomEvent('wu-error', {
418
- detail: err,
419
- bubbles: true,
420
- composed: true
421
- }));
422
- }
423
- }
424
-
425
- async unmountMicrofrontend() {
426
- if (this._appInstance) {
427
- this.dispatchEvent(new CustomEvent('wu-unmount', {
428
- detail: { name: this.actualAppName },
429
- bubbles: true,
430
- composed: true
431
- }));
432
-
433
- try {
434
- await this._appInstance.unmount();
435
- } catch (e) {}
436
-
437
- this._appInstance = null;
438
- }
439
- }
440
-
441
- render() {
442
- if (this.error) {
443
- return html`
444
- <div class="wu-slot-error">
445
- <strong>Error loading ${this.name}</strong>
446
- <span>${this.error}</span>
447
- </div>
448
- `;
449
- }
450
-
451
- if (this.loading) {
452
- return html`
453
- <div class="wu-slot-loading">
454
- ${this.fallbackText || `Loading ${this.name}...`}
455
- </div>
456
- <div class="wu-slot-content"></div>
457
- `;
458
- }
459
-
460
- return html`<div class="wu-slot-content"></div>`;
461
- }
462
- }
463
-
464
- // Registrar el elemento
465
- if (!customElements.get('wu-slot')) {
466
- customElements.define('wu-slot', WuSlotElement);
467
- }
468
-
469
- return WuSlotElement;
470
- }
471
-
472
- /**
473
- * Mixin para agregar capacidades de Wu a cualquier LitElement
474
- *
475
- * @example
476
- * import { LitElement } from 'lit';
477
- * import { WuMixin } from 'wu-framework/adapters/lit';
478
- *
479
- * class MyElement extends WuMixin(LitElement) {
480
- * connectedCallback() {
481
- * super.connectedCallback();
482
- *
483
- * // Usar eventos de Wu
484
- * this.wuOn('user:login', (data) => {
485
- * console.log('User logged in:', data);
486
- * });
487
- * }
488
- *
489
- * handleClick() {
490
- * this.wuEmit('button:clicked', { id: this.id });
491
- * }
492
- * }
493
- */
494
- function WuMixin(Base) {
495
- return class extends Base {
496
- constructor() {
497
- super();
498
- this._wuSubscriptions = [];
499
- }
500
-
501
- get wu() {
502
- return getWuInstance();
503
- }
504
-
505
- // Event Bus methods
506
- wuEmit(event, data, options) {
507
- const wu = this.wu;
508
- if (wu?.eventBus) {
509
- wu.eventBus.emit(event, data, options);
510
- }
511
- }
512
-
513
- wuOn(event, callback) {
514
- const wu = this.wu;
515
- if (wu?.eventBus) {
516
- const unsubscribe = wu.eventBus.on(event, callback);
517
- this._wuSubscriptions.push(unsubscribe);
518
- return unsubscribe;
519
- }
520
- return () => {};
521
- }
522
-
523
- wuOnce(event, callback) {
524
- const wu = this.wu;
525
- if (wu?.eventBus) {
526
- return wu.eventBus.once(event, callback);
527
- }
528
- return () => {};
529
- }
530
-
531
- // Store methods
532
- wuGetState(path) {
533
- const wu = this.wu;
534
- return wu?.store?.get(path) || null;
535
- }
536
-
537
- wuSetState(path, value) {
538
- const wu = this.wu;
539
- if (wu?.store) {
540
- wu.store.set(path, value);
541
- }
542
- }
543
-
544
- wuOnStateChange(pattern, callback) {
545
- const wu = this.wu;
546
- if (wu?.store) {
547
- const unsubscribe = wu.store.on(pattern, callback);
548
- this._wuSubscriptions.push(unsubscribe);
549
- return unsubscribe;
550
- }
551
- return () => {};
552
- }
553
-
554
- // Cleanup
555
- disconnectedCallback() {
556
- super.disconnectedCallback();
557
- this._wuSubscriptions.forEach(unsub => unsub());
558
- this._wuSubscriptions = [];
559
- }
560
- };
561
- }
562
-
563
- /**
564
- * Decorador reactivo para propiedades conectadas al store de Wu
565
- *
566
- * @example
567
- * import { LitElement } from 'lit';
568
- * import { wuProperty } from 'wu-framework/adapters/lit';
569
- *
570
- * class MyElement extends LitElement {
571
- * @wuProperty('user.name')
572
- * userName;
573
- *
574
- * render() {
575
- * return html`<p>Hello, ${this.userName}</p>`;
576
- * }
577
- * }
578
- */
579
- function wuProperty(storePath) {
580
- return function(target, propertyKey) {
581
- const privateKey = `_wu_${propertyKey}`;
582
- let unsubscribe = null;
583
-
584
- Object.defineProperty(target, propertyKey, {
585
- get() {
586
- return this[privateKey];
587
- },
588
- set(value) {
589
- const wu = getWuInstance();
590
- if (wu?.store) {
591
- wu.store.set(storePath, value);
592
- }
593
- },
594
- configurable: true,
595
- enumerable: true
596
- });
597
-
598
- // Hook into connectedCallback
599
- const originalConnected = target.connectedCallback;
600
- target.connectedCallback = function() {
601
- if (originalConnected) originalConnected.call(this);
602
-
603
- const wu = getWuInstance();
604
- if (wu?.store) {
605
- // Set initial value
606
- this[privateKey] = wu.store.get(storePath);
607
-
608
- // Subscribe to changes
609
- unsubscribe = wu.store.on(storePath, (value) => {
610
- this[privateKey] = value;
611
- this.requestUpdate();
612
- });
613
- }
614
- };
615
-
616
- // Hook into disconnectedCallback
617
- const originalDisconnected = target.disconnectedCallback;
618
- target.disconnectedCallback = function() {
619
- if (originalDisconnected) originalDisconnected.call(this);
620
- if (unsubscribe) {
621
- unsubscribe();
622
- unsubscribe = null;
623
- }
624
- };
625
- };
626
- }
627
-
628
- /**
629
- * Helper para crear un Web Component simple sin Lit
630
- *
631
- * @example
632
- * const MyComponent = createSimpleElement({
633
- * name: 'my-component',
634
- * template: '<h1>Hello!</h1>',
635
- * styles: ':host { display: block; color: blue; }',
636
- * connectedCallback() {
637
- * console.log('Connected!');
638
- * }
639
- * });
640
- */
641
- function createSimpleElement(config) {
642
- const {
643
- name,
644
- template,
645
- styles = '',
646
- shadow = true,
647
- ...callbacks
648
- } = config;
649
-
650
- class SimpleElement extends HTMLElement {
651
- constructor() {
652
- super();
653
- if (shadow) {
654
- this.attachShadow({ mode: 'open' });
655
- }
656
- }
657
-
658
- connectedCallback() {
659
- const root = this.shadowRoot || this;
660
-
661
- if (styles) {
662
- const styleEl = document.createElement('style');
663
- styleEl.textContent = styles;
664
- root.appendChild(styleEl);
665
- }
666
-
667
- if (typeof template === 'function') {
668
- root.innerHTML += template(this);
669
- } else {
670
- root.innerHTML += template;
671
- }
672
-
673
- if (callbacks.connectedCallback) {
674
- callbacks.connectedCallback.call(this);
675
- }
676
- }
677
-
678
- disconnectedCallback() {
679
- if (callbacks.disconnectedCallback) {
680
- callbacks.disconnectedCallback.call(this);
681
- }
682
- }
683
-
684
- attributeChangedCallback(name, oldVal, newVal) {
685
- if (callbacks.attributeChangedCallback) {
686
- callbacks.attributeChangedCallback.call(this, name, oldVal, newVal);
687
- }
688
- }
689
- }
690
-
691
- if (callbacks.observedAttributes) {
692
- SimpleElement.observedAttributes = callbacks.observedAttributes;
693
- }
694
-
695
- if (!customElements.get(name)) {
696
- customElements.define(name, SimpleElement);
697
- }
698
-
699
- return SimpleElement;
700
- }
701
-
702
- // ============================================
703
- // AI INTEGRATION
704
- // ============================================
705
- export { WuAIMixin } from './ai.js';
706
- import { WuAIMixin } from './ai.js';
707
-
708
- // API pública del adapter
709
- export const wuLit = {
710
- register,
711
- registerWebComponent,
712
- createWuSlotElement,
713
- WuMixin,
714
- WuAIMixin,
715
- wuProperty,
716
- createSimpleElement,
717
- getWuInstance,
718
- waitForWu
719
- };
720
-
721
- export default wuLit;
1
+ /**
2
+ * 🚀 WU-FRAMEWORK LIT ADAPTER
3
+ *
4
+ * Simplifica la integración de Lit (Web Components) con Wu Framework.
5
+ * Aprovecha los Web Components nativos con Shadow DOM incluido.
6
+ *
7
+ * @example
8
+ * // Microfrontend (main.js)
9
+ * import { wuLit } from 'wu-framework/adapters/lit';
10
+ * import { MyApp } from './my-app';
11
+ *
12
+ * wuLit.register('my-app', MyApp);
13
+ *
14
+ * @example
15
+ * // Usando LitElement
16
+ * import { LitElement, html, css } from 'lit';
17
+ *
18
+ * class MyApp extends LitElement {
19
+ * static styles = css`:host { display: block; }`;
20
+ *
21
+ * render() {
22
+ * return html`<h1>Hello from Lit!</h1>`;
23
+ * }
24
+ * }
25
+ *
26
+ * wuLit.register('my-app', MyApp);
27
+ */
28
+
29
+ // Estado global del adapter
30
+ const adapterState = {
31
+ apps: new Map(),
32
+ elements: new Map(),
33
+ lit: null,
34
+ initialized: false
35
+ };
36
+
37
+ /**
38
+ * Obtiene la instancia de Wu Framework
39
+ */
40
+ function getWuInstance() {
41
+ if (typeof window === 'undefined') return null;
42
+
43
+ return window.wu
44
+ || window.parent?.wu
45
+ || window.top?.wu
46
+ || null;
47
+ }
48
+
49
+ /**
50
+ * Espera a que Wu Framework esté disponible
51
+ */
52
+ function waitForWu(timeout = 5000) {
53
+ return new Promise((resolve, reject) => {
54
+ const wu = getWuInstance();
55
+ if (wu) {
56
+ resolve(wu);
57
+ return;
58
+ }
59
+
60
+ const startTime = Date.now();
61
+
62
+ const handleWuReady = () => {
63
+ cleanup();
64
+ resolve(getWuInstance());
65
+ };
66
+
67
+ window.addEventListener('wu:ready', handleWuReady);
68
+ window.addEventListener('wu:app:ready', handleWuReady);
69
+
70
+ const checkInterval = setInterval(() => {
71
+ const wu = getWuInstance();
72
+ if (wu) {
73
+ cleanup();
74
+ resolve(wu);
75
+ return;
76
+ }
77
+
78
+ if (Date.now() - startTime > timeout) {
79
+ cleanup();
80
+ reject(new Error(`Wu Framework not found after ${timeout}ms`));
81
+ }
82
+ }, 200);
83
+
84
+ function cleanup() {
85
+ clearInterval(checkInterval);
86
+ window.removeEventListener('wu:ready', handleWuReady);
87
+ window.removeEventListener('wu:app:ready', handleWuReady);
88
+ }
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Genera un nombre de tag válido para Custom Elements
94
+ */
95
+ function generateTagName(appName) {
96
+ // Custom elements deben tener un guión
97
+ if (appName.includes('-')) {
98
+ return `wu-${appName}`;
99
+ }
100
+ return `wu-app-${appName}`;
101
+ }
102
+
103
+ /**
104
+ * Registra un LitElement como microfrontend
105
+ *
106
+ * @param {string} appName - Nombre único del microfrontend
107
+ * @param {typeof LitElement} ElementClass - Clase que extiende LitElement
108
+ * @param {Object} options - Opciones adicionales
109
+ * @param {string} options.tagName - Nombre del custom element (auto-generado si no se provee)
110
+ * @param {Object} options.properties - Propiedades iniciales
111
+ * @param {Function} options.onMount - Callback después de montar
112
+ * @param {Function} options.onUnmount - Callback antes de desmontar
113
+ * @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
114
+ * @param {string} options.standaloneContainer - Selector para modo standalone (default: '#root')
115
+ *
116
+ * @example
117
+ * import { LitElement, html } from 'lit';
118
+ *
119
+ * class HeaderApp extends LitElement {
120
+ * render() {
121
+ * return html`<header><h1>My Header</h1></header>`;
122
+ * }
123
+ * }
124
+ *
125
+ * wuLit.register('header', HeaderApp);
126
+ */
127
+ async function register(appName, ElementClass, options = {}) {
128
+ const {
129
+ tagName = null,
130
+ properties = {},
131
+ onMount = null,
132
+ onUnmount = null,
133
+ standalone = true,
134
+ standaloneContainer = '#root'
135
+ } = options;
136
+
137
+ // Generar nombre de tag
138
+ const customTagName = tagName || generateTagName(appName);
139
+
140
+ // Registrar el Custom Element si no existe
141
+ if (!customElements.get(customTagName)) {
142
+ try {
143
+ customElements.define(customTagName, ElementClass);
144
+ console.log(`[WuLit] Custom element <${customTagName}> defined`);
145
+ } catch (error) {
146
+ console.error(`[WuLit] Failed to define custom element:`, error);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ // Guardar referencia de la clase
152
+ adapterState.elements.set(appName, {
153
+ ElementClass,
154
+ tagName: customTagName
155
+ });
156
+
157
+ // Función de mount
158
+ const mountApp = (container) => {
159
+ if (!container) {
160
+ console.error(`[WuLit] Mount failed for ${appName}: container is null`);
161
+ return;
162
+ }
163
+
164
+ // Evitar doble mount
165
+ if (adapterState.apps.has(appName)) {
166
+ console.warn(`[WuLit] ${appName} already mounted, unmounting first`);
167
+ unmountApp();
168
+ }
169
+
170
+ try {
171
+ // Limpiar container
172
+ container.innerHTML = '';
173
+
174
+ // Crear elemento
175
+ const element = document.createElement(customTagName);
176
+
177
+ // Aplicar propiedades
178
+ Object.entries(properties).forEach(([key, value]) => {
179
+ element[key] = value;
180
+ });
181
+
182
+ // Inyectar información de Wu
183
+ element.wuAppName = appName;
184
+ element.wuInstance = getWuInstance();
185
+
186
+ // Agregar al container
187
+ container.appendChild(element);
188
+
189
+ // Guardar referencia
190
+ adapterState.apps.set(appName, {
191
+ element,
192
+ container,
193
+ tagName: customTagName
194
+ });
195
+
196
+ console.log(`[WuLit] ✅ ${appName} (<${customTagName}>) mounted successfully`);
197
+
198
+ if (onMount) {
199
+ onMount(container, element);
200
+ }
201
+ } catch (error) {
202
+ console.error(`[WuLit] Mount error for ${appName}:`, error);
203
+ throw error;
204
+ }
205
+ };
206
+
207
+ // Función de unmount
208
+ const unmountApp = (container) => {
209
+ const appData = adapterState.apps.get(appName);
210
+
211
+ if (appData) {
212
+ try {
213
+ if (onUnmount) {
214
+ onUnmount(appData.container, appData.element);
215
+ }
216
+
217
+ // Remover elemento
218
+ if (appData.element && appData.element.parentNode) {
219
+ appData.element.remove();
220
+ }
221
+
222
+ // Limpiar container
223
+ appData.container.innerHTML = '';
224
+
225
+ adapterState.apps.delete(appName);
226
+
227
+ console.log(`[WuLit] ✅ ${appName} unmounted successfully`);
228
+ } catch (error) {
229
+ console.error(`[WuLit] Unmount error for ${appName}:`, error);
230
+ }
231
+ }
232
+
233
+ if (container) {
234
+ container.innerHTML = '';
235
+ }
236
+ };
237
+
238
+ // Intentar registrar con Wu Framework
239
+ try {
240
+ const wu = await waitForWu(3000);
241
+
242
+ wu.define(appName, {
243
+ mount: mountApp,
244
+ unmount: unmountApp
245
+ });
246
+
247
+ console.log(`[WuLit] ✅ ${appName} registered with Wu Framework`);
248
+ return true;
249
+
250
+ } catch (error) {
251
+ console.warn(`[WuLit] Wu Framework not available for ${appName}`);
252
+
253
+ if (standalone) {
254
+ const containerElement = document.querySelector(standaloneContainer);
255
+
256
+ if (containerElement) {
257
+ console.log(`[WuLit] Running ${appName} in standalone mode`);
258
+ mountApp(containerElement);
259
+ return true;
260
+ }
261
+ }
262
+
263
+ return false;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Registra un Web Component vanilla (sin Lit) como microfrontend
269
+ *
270
+ * @param {string} appName - Nombre del microfrontend
271
+ * @param {typeof HTMLElement} ElementClass - Clase que extiende HTMLElement
272
+ * @param {Object} options - Opciones
273
+ *
274
+ * @example
275
+ * class MyWebComponent extends HTMLElement {
276
+ * connectedCallback() {
277
+ * this.attachShadow({ mode: 'open' });
278
+ * this.shadowRoot.innerHTML = '<h1>Hello!</h1>';
279
+ * }
280
+ * }
281
+ *
282
+ * wuLit.registerWebComponent('my-component', MyWebComponent);
283
+ */
284
+ async function registerWebComponent(appName, ElementClass, options = {}) {
285
+ // Usar el mismo registro pero para HTMLElement vanilla
286
+ return register(appName, ElementClass, options);
287
+ }
288
+
289
+ /**
290
+ * Crea un LitElement wrapper que carga un microfrontend
291
+ *
292
+ * @example
293
+ * import { html, LitElement } from 'lit';
294
+ * import { createWuSlotElement } from 'wu-framework/adapters/lit';
295
+ *
296
+ * const WuSlot = createWuSlotElement(LitElement, html);
297
+ *
298
+ * // Uso en otro componente
299
+ * render() {
300
+ * return html`<wu-slot name="header" url="http://localhost:3001"></wu-slot>`;
301
+ * }
302
+ */
303
+ function createWuSlotElement(LitElement, html, css) {
304
+ class WuSlotElement extends LitElement {
305
+ static properties = {
306
+ name: { type: String },
307
+ url: { type: String },
308
+ appName: { type: String, attribute: 'app-name' },
309
+ fallbackText: { type: String, attribute: 'fallback-text' },
310
+ loading: { type: Boolean, state: true },
311
+ error: { type: String, state: true }
312
+ };
313
+
314
+ static styles = css ? css`
315
+ :host {
316
+ display: block;
317
+ min-height: 100px;
318
+ position: relative;
319
+ }
320
+
321
+ .wu-slot-loading {
322
+ display: flex;
323
+ align-items: center;
324
+ justify-content: center;
325
+ padding: 2rem;
326
+ color: #666;
327
+ }
328
+
329
+ .wu-slot-error {
330
+ padding: 1rem;
331
+ border: 1px solid #f5c6cb;
332
+ border-radius: 4px;
333
+ background: #f8d7da;
334
+ color: #721c24;
335
+ }
336
+
337
+ .wu-slot-error strong {
338
+ display: block;
339
+ margin-bottom: 0.5rem;
340
+ }
341
+
342
+ .wu-slot-content {
343
+ width: 100%;
344
+ height: 100%;
345
+ }
346
+ ` : [];
347
+
348
+ constructor() {
349
+ super();
350
+ this.name = '';
351
+ this.url = '';
352
+ this.appName = null;
353
+ this.fallbackText = null;
354
+ this.loading = true;
355
+ this.error = null;
356
+ this._appInstance = null;
357
+ }
358
+
359
+ get actualAppName() {
360
+ return this.appName || this.name;
361
+ }
362
+
363
+ async connectedCallback() {
364
+ super.connectedCallback();
365
+ await this.mountMicrofrontend();
366
+ }
367
+
368
+ disconnectedCallback() {
369
+ super.disconnectedCallback();
370
+ this.unmountMicrofrontend();
371
+ }
372
+
373
+ async mountMicrofrontend() {
374
+ try {
375
+ this.loading = true;
376
+ this.error = null;
377
+
378
+ const wu = getWuInstance();
379
+ if (!wu) {
380
+ throw new Error('Wu Framework not initialized');
381
+ }
382
+
383
+ // Esperar a que el componente se renderice
384
+ await this.updateComplete;
385
+
386
+ const contentSlot = this.shadowRoot.querySelector('.wu-slot-content');
387
+ if (!contentSlot) return;
388
+
389
+ const containerId = `wu-slot-${this.actualAppName}-${Date.now()}`;
390
+ const innerContainer = document.createElement('div');
391
+ innerContainer.id = containerId;
392
+ innerContainer.style.cssText = 'width: 100%; height: 100%;';
393
+
394
+ contentSlot.innerHTML = '';
395
+ contentSlot.appendChild(innerContainer);
396
+
397
+ const app = wu.app(this.actualAppName, {
398
+ url: this.url,
399
+ container: `#${containerId}`,
400
+ autoInit: true
401
+ });
402
+
403
+ this._appInstance = app;
404
+ await app.mount();
405
+
406
+ this.loading = false;
407
+ this.dispatchEvent(new CustomEvent('wu-load', {
408
+ detail: { name: this.actualAppName, url: this.url },
409
+ bubbles: true,
410
+ composed: true
411
+ }));
412
+
413
+ } catch (err) {
414
+ console.error(`[WuSlot] Error loading ${this.actualAppName}:`, err);
415
+ this.error = err.message || 'Failed to load microfrontend';
416
+ this.loading = false;
417
+ this.dispatchEvent(new CustomEvent('wu-error', {
418
+ detail: err,
419
+ bubbles: true,
420
+ composed: true
421
+ }));
422
+ }
423
+ }
424
+
425
+ async unmountMicrofrontend() {
426
+ if (this._appInstance) {
427
+ this.dispatchEvent(new CustomEvent('wu-unmount', {
428
+ detail: { name: this.actualAppName },
429
+ bubbles: true,
430
+ composed: true
431
+ }));
432
+
433
+ try {
434
+ await this._appInstance.unmount();
435
+ } catch (e) {}
436
+
437
+ this._appInstance = null;
438
+ }
439
+ }
440
+
441
+ render() {
442
+ if (this.error) {
443
+ return html`
444
+ <div class="wu-slot-error">
445
+ <strong>Error loading ${this.name}</strong>
446
+ <span>${this.error}</span>
447
+ </div>
448
+ `;
449
+ }
450
+
451
+ if (this.loading) {
452
+ return html`
453
+ <div class="wu-slot-loading">
454
+ ${this.fallbackText || `Loading ${this.name}...`}
455
+ </div>
456
+ <div class="wu-slot-content"></div>
457
+ `;
458
+ }
459
+
460
+ return html`<div class="wu-slot-content"></div>`;
461
+ }
462
+ }
463
+
464
+ // Registrar el elemento
465
+ if (!customElements.get('wu-slot')) {
466
+ customElements.define('wu-slot', WuSlotElement);
467
+ }
468
+
469
+ return WuSlotElement;
470
+ }
471
+
472
+ /**
473
+ * Mixin para agregar capacidades de Wu a cualquier LitElement
474
+ *
475
+ * @example
476
+ * import { LitElement } from 'lit';
477
+ * import { WuMixin } from 'wu-framework/adapters/lit';
478
+ *
479
+ * class MyElement extends WuMixin(LitElement) {
480
+ * connectedCallback() {
481
+ * super.connectedCallback();
482
+ *
483
+ * // Usar eventos de Wu
484
+ * this.wuOn('user:login', (data) => {
485
+ * console.log('User logged in:', data);
486
+ * });
487
+ * }
488
+ *
489
+ * handleClick() {
490
+ * this.wuEmit('button:clicked', { id: this.id });
491
+ * }
492
+ * }
493
+ */
494
+ function WuMixin(Base) {
495
+ return class extends Base {
496
+ constructor() {
497
+ super();
498
+ this._wuSubscriptions = [];
499
+ }
500
+
501
+ get wu() {
502
+ return getWuInstance();
503
+ }
504
+
505
+ // Event Bus methods
506
+ wuEmit(event, data, options) {
507
+ const wu = this.wu;
508
+ if (wu?.eventBus) {
509
+ wu.eventBus.emit(event, data, options);
510
+ }
511
+ }
512
+
513
+ wuOn(event, callback) {
514
+ const wu = this.wu;
515
+ if (wu?.eventBus) {
516
+ const unsubscribe = wu.eventBus.on(event, callback);
517
+ this._wuSubscriptions.push(unsubscribe);
518
+ return unsubscribe;
519
+ }
520
+ return () => {};
521
+ }
522
+
523
+ wuOnce(event, callback) {
524
+ const wu = this.wu;
525
+ if (wu?.eventBus) {
526
+ return wu.eventBus.once(event, callback);
527
+ }
528
+ return () => {};
529
+ }
530
+
531
+ // Store methods
532
+ wuGetState(path) {
533
+ const wu = this.wu;
534
+ return wu?.store?.get(path) || null;
535
+ }
536
+
537
+ wuSetState(path, value) {
538
+ const wu = this.wu;
539
+ if (wu?.store) {
540
+ wu.store.set(path, value);
541
+ }
542
+ }
543
+
544
+ wuOnStateChange(pattern, callback) {
545
+ const wu = this.wu;
546
+ if (wu?.store) {
547
+ const unsubscribe = wu.store.on(pattern, callback);
548
+ this._wuSubscriptions.push(unsubscribe);
549
+ return unsubscribe;
550
+ }
551
+ return () => {};
552
+ }
553
+
554
+ // Cleanup
555
+ disconnectedCallback() {
556
+ super.disconnectedCallback();
557
+ this._wuSubscriptions.forEach(unsub => unsub());
558
+ this._wuSubscriptions = [];
559
+ }
560
+ };
561
+ }
562
+
563
+ /**
564
+ * Decorador reactivo para propiedades conectadas al store de Wu
565
+ *
566
+ * @example
567
+ * import { LitElement } from 'lit';
568
+ * import { wuProperty } from 'wu-framework/adapters/lit';
569
+ *
570
+ * class MyElement extends LitElement {
571
+ * @wuProperty('user.name')
572
+ * userName;
573
+ *
574
+ * render() {
575
+ * return html`<p>Hello, ${this.userName}</p>`;
576
+ * }
577
+ * }
578
+ */
579
+ function wuProperty(storePath) {
580
+ return function(target, propertyKey) {
581
+ const privateKey = `_wu_${propertyKey}`;
582
+ let unsubscribe = null;
583
+
584
+ Object.defineProperty(target, propertyKey, {
585
+ get() {
586
+ return this[privateKey];
587
+ },
588
+ set(value) {
589
+ const wu = getWuInstance();
590
+ if (wu?.store) {
591
+ wu.store.set(storePath, value);
592
+ }
593
+ },
594
+ configurable: true,
595
+ enumerable: true
596
+ });
597
+
598
+ // Hook into connectedCallback
599
+ const originalConnected = target.connectedCallback;
600
+ target.connectedCallback = function() {
601
+ if (originalConnected) originalConnected.call(this);
602
+
603
+ const wu = getWuInstance();
604
+ if (wu?.store) {
605
+ // Set initial value
606
+ this[privateKey] = wu.store.get(storePath);
607
+
608
+ // Subscribe to changes
609
+ unsubscribe = wu.store.on(storePath, (value) => {
610
+ this[privateKey] = value;
611
+ this.requestUpdate();
612
+ });
613
+ }
614
+ };
615
+
616
+ // Hook into disconnectedCallback
617
+ const originalDisconnected = target.disconnectedCallback;
618
+ target.disconnectedCallback = function() {
619
+ if (originalDisconnected) originalDisconnected.call(this);
620
+ if (unsubscribe) {
621
+ unsubscribe();
622
+ unsubscribe = null;
623
+ }
624
+ };
625
+ };
626
+ }
627
+
628
+ /**
629
+ * Helper para crear un Web Component simple sin Lit
630
+ *
631
+ * @example
632
+ * const MyComponent = createSimpleElement({
633
+ * name: 'my-component',
634
+ * template: '<h1>Hello!</h1>',
635
+ * styles: ':host { display: block; color: blue; }',
636
+ * connectedCallback() {
637
+ * console.log('Connected!');
638
+ * }
639
+ * });
640
+ */
641
+ function createSimpleElement(config) {
642
+ const {
643
+ name,
644
+ template,
645
+ styles = '',
646
+ shadow = true,
647
+ ...callbacks
648
+ } = config;
649
+
650
+ class SimpleElement extends HTMLElement {
651
+ constructor() {
652
+ super();
653
+ if (shadow) {
654
+ this.attachShadow({ mode: 'open' });
655
+ }
656
+ }
657
+
658
+ connectedCallback() {
659
+ const root = this.shadowRoot || this;
660
+
661
+ if (styles) {
662
+ const styleEl = document.createElement('style');
663
+ styleEl.textContent = styles;
664
+ root.appendChild(styleEl);
665
+ }
666
+
667
+ if (typeof template === 'function') {
668
+ root.innerHTML += template(this);
669
+ } else {
670
+ root.innerHTML += template;
671
+ }
672
+
673
+ if (callbacks.connectedCallback) {
674
+ callbacks.connectedCallback.call(this);
675
+ }
676
+ }
677
+
678
+ disconnectedCallback() {
679
+ if (callbacks.disconnectedCallback) {
680
+ callbacks.disconnectedCallback.call(this);
681
+ }
682
+ }
683
+
684
+ attributeChangedCallback(name, oldVal, newVal) {
685
+ if (callbacks.attributeChangedCallback) {
686
+ callbacks.attributeChangedCallback.call(this, name, oldVal, newVal);
687
+ }
688
+ }
689
+ }
690
+
691
+ if (callbacks.observedAttributes) {
692
+ SimpleElement.observedAttributes = callbacks.observedAttributes;
693
+ }
694
+
695
+ if (!customElements.get(name)) {
696
+ customElements.define(name, SimpleElement);
697
+ }
698
+
699
+ return SimpleElement;
700
+ }
701
+
702
+ // ============================================
703
+ // AI INTEGRATION
704
+ // ============================================
705
+ export { WuAIMixin } from './ai.js';
706
+ import { WuAIMixin } from './ai.js';
707
+
708
+ // API pública del adapter
709
+ export const wuLit = {
710
+ register,
711
+ registerWebComponent,
712
+ createWuSlotElement,
713
+ WuMixin,
714
+ WuAIMixin,
715
+ wuProperty,
716
+ createSimpleElement,
717
+ getWuInstance,
718
+ waitForWu
719
+ };
720
+
721
+ export default wuLit;