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,661 +1,661 @@
1
- /**
2
- * 🚀 WU-FRAMEWORK PREACT ADAPTER
3
- *
4
- * Simplifica la integración de Preact con Wu Framework.
5
- * Compatible con Preact 10+ y preact/compat para proyectos React migrados.
6
- *
7
- * @example
8
- * // Microfrontend (main.jsx)
9
- * import { wuPreact } from 'wu-framework/adapters/preact';
10
- * import App from './App';
11
- *
12
- * wuPreact.register('my-app', App);
13
- *
14
- * @example
15
- * // Shell (cargar microfrontend)
16
- * import { createWuSlot } from 'wu-framework/adapters/preact';
17
- * import { h } from 'preact';
18
- *
19
- * const WuSlot = createWuSlot(h);
20
- * <WuSlot name="my-app" url="http://localhost:3001" />
21
- */
22
-
23
- // Estado global del adapter
24
- const adapterState = {
25
- apps: new Map(),
26
- preact: null,
27
- render: null,
28
- h: null,
29
- initialized: false
30
- };
31
-
32
- /**
33
- * Detecta y obtiene Preact del contexto global o lo importa
34
- */
35
- async function ensurePreact() {
36
- if (adapterState.initialized) return true;
37
-
38
- try {
39
- // Intentar obtener de window
40
- if (typeof window !== 'undefined' && window.preact) {
41
- adapterState.preact = window.preact;
42
- adapterState.render = window.preact.render;
43
- adapterState.h = window.preact.h;
44
- adapterState.initialized = true;
45
- return true;
46
- }
47
-
48
- // Intentar import dinámico
49
- const preact = await import('preact');
50
- adapterState.preact = preact;
51
- adapterState.render = preact.render;
52
- adapterState.h = preact.h;
53
- adapterState.initialized = true;
54
- return true;
55
-
56
- } catch (error) {
57
- console.error('[WuPreact] Failed to load Preact:', error);
58
- return false;
59
- }
60
- }
61
-
62
- /**
63
- * Obtiene la instancia de Wu Framework
64
- */
65
- function getWuInstance() {
66
- if (typeof window === 'undefined') return null;
67
-
68
- return window.wu
69
- || window.parent?.wu
70
- || window.top?.wu
71
- || null;
72
- }
73
-
74
- /**
75
- * Espera a que Wu Framework esté disponible
76
- */
77
- function waitForWu(timeout = 5000) {
78
- return new Promise((resolve, reject) => {
79
- const wu = getWuInstance();
80
- if (wu) {
81
- resolve(wu);
82
- return;
83
- }
84
-
85
- const startTime = Date.now();
86
-
87
- const handleWuReady = () => {
88
- cleanup();
89
- resolve(getWuInstance());
90
- };
91
-
92
- window.addEventListener('wu:ready', handleWuReady);
93
- window.addEventListener('wu:app:ready', handleWuReady);
94
-
95
- const checkInterval = setInterval(() => {
96
- const wu = getWuInstance();
97
- if (wu) {
98
- cleanup();
99
- resolve(wu);
100
- return;
101
- }
102
-
103
- if (Date.now() - startTime > timeout) {
104
- cleanup();
105
- reject(new Error(`Wu Framework not found after ${timeout}ms`));
106
- }
107
- }, 200);
108
-
109
- function cleanup() {
110
- clearInterval(checkInterval);
111
- window.removeEventListener('wu:ready', handleWuReady);
112
- window.removeEventListener('wu:app:ready', handleWuReady);
113
- }
114
- });
115
- }
116
-
117
- /**
118
- * Registra un componente Preact como microfrontend
119
- *
120
- * @param {string} appName - Nombre único del microfrontend
121
- * @param {Function} Component - Componente Preact principal
122
- * @param {Object} options - Opciones adicionales
123
- * @param {Object} options.props - Props iniciales para el componente
124
- * @param {Function} options.onMount - Callback después de montar
125
- * @param {Function} options.onUnmount - Callback antes de desmontar
126
- * @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
127
- * @param {string} options.standaloneContainer - Selector para modo standalone (default: '#app')
128
- *
129
- * @example
130
- * // Básico
131
- * wuPreact.register('my-app', App);
132
- *
133
- * @example
134
- * // Con props
135
- * wuPreact.register('my-app', App, {
136
- * props: { apiUrl: 'https://api.example.com' },
137
- * onMount: (container) => console.log('Mounted!')
138
- * });
139
- */
140
- async function register(appName, Component, options = {}) {
141
- const {
142
- props = {},
143
- onMount = null,
144
- onUnmount = null,
145
- standalone = true,
146
- standaloneContainer = '#app'
147
- } = options;
148
-
149
- // Asegurar que Preact está disponible
150
- const hasPreact = await ensurePreact();
151
- if (!hasPreact) {
152
- console.error(`[WuPreact] Cannot register ${appName}: Preact not available`);
153
- return false;
154
- }
155
-
156
- const { render, h } = adapterState;
157
-
158
- // Función de mount interna
159
- const mountApp = (container) => {
160
- if (!container) {
161
- console.error(`[WuPreact] Mount failed for ${appName}: container is null`);
162
- return;
163
- }
164
-
165
- // Evitar doble mount
166
- if (adapterState.apps.has(appName)) {
167
- console.warn(`[WuPreact] ${appName} already mounted, unmounting first`);
168
- unmountApp();
169
- }
170
-
171
- try {
172
- // Limpiar container
173
- container.innerHTML = '';
174
-
175
- // Renderizar componente
176
- render(
177
- h(Component, {
178
- ...props,
179
- wuAppName: appName,
180
- wuInstance: getWuInstance()
181
- }),
182
- container
183
- );
184
-
185
- // Guardar referencia
186
- adapterState.apps.set(appName, { container, Component });
187
-
188
- console.log(`[WuPreact] ✅ ${appName} mounted successfully`);
189
-
190
- if (onMount) {
191
- onMount(container);
192
- }
193
- } catch (error) {
194
- console.error(`[WuPreact] Mount error for ${appName}:`, error);
195
- throw error;
196
- }
197
- };
198
-
199
- // Función de unmount interna
200
- const unmountApp = (container) => {
201
- const appData = adapterState.apps.get(appName);
202
-
203
- if (appData) {
204
- try {
205
- if (onUnmount) {
206
- onUnmount(appData.container);
207
- }
208
-
209
- // Unmount en Preact: renderizar null
210
- render(null, appData.container);
211
-
212
- adapterState.apps.delete(appName);
213
-
214
- console.log(`[WuPreact] ✅ ${appName} unmounted successfully`);
215
- } catch (error) {
216
- console.error(`[WuPreact] 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(`[WuPreact] ✅ ${appName} registered with Wu Framework`);
235
- return true;
236
-
237
- } catch (error) {
238
- console.warn(`[WuPreact] Wu Framework not available for ${appName}`);
239
-
240
- if (standalone) {
241
- const containerElement = document.querySelector(standaloneContainer);
242
-
243
- if (containerElement) {
244
- console.log(`[WuPreact] Running ${appName} in standalone mode`);
245
- mountApp(containerElement);
246
- return true;
247
- }
248
- }
249
-
250
- return false;
251
- }
252
- }
253
-
254
- /**
255
- * Registra usando preact/compat (para proyectos migrados de React)
256
- *
257
- * @example
258
- * import { wuPreact } from 'wu-framework/adapters/preact';
259
- * import App from './App'; // Componente React-like
260
- *
261
- * wuPreact.registerCompat('my-app', App);
262
- */
263
- async function registerCompat(appName, Component, options = {}) {
264
- const {
265
- props = {},
266
- onMount = null,
267
- onUnmount = null,
268
- standalone = true,
269
- standaloneContainer = '#app'
270
- } = options;
271
-
272
- let render, h;
273
-
274
- try {
275
- // Intentar cargar preact/compat
276
- const compat = await import('preact/compat');
277
- render = compat.render;
278
- h = compat.createElement;
279
- } catch (e) {
280
- console.error('[WuPreact] preact/compat not available');
281
- // Fallback a registro normal
282
- return register(appName, Component, options);
283
- }
284
-
285
- const mountApp = (container) => {
286
- if (!container) return;
287
-
288
- if (adapterState.apps.has(appName)) {
289
- unmountApp();
290
- }
291
-
292
- try {
293
- container.innerHTML = '';
294
-
295
- render(
296
- h(Component, {
297
- ...props,
298
- wuAppName: appName,
299
- wuInstance: getWuInstance()
300
- }),
301
- container
302
- );
303
-
304
- adapterState.apps.set(appName, { container, Component, isCompat: true });
305
-
306
- console.log(`[WuPreact] ✅ ${appName} (compat) mounted successfully`);
307
-
308
- if (onMount) onMount(container);
309
- } catch (error) {
310
- console.error(`[WuPreact] Mount error for ${appName}:`, error);
311
- throw error;
312
- }
313
- };
314
-
315
- const unmountApp = (container) => {
316
- const appData = adapterState.apps.get(appName);
317
-
318
- if (appData) {
319
- try {
320
- if (onUnmount) onUnmount(appData.container);
321
- render(null, appData.container);
322
- adapterState.apps.delete(appName);
323
- console.log(`[WuPreact] ✅ ${appName} (compat) unmounted successfully`);
324
- } catch (error) {
325
- console.error(`[WuPreact] Unmount error for ${appName}:`, error);
326
- }
327
- }
328
-
329
- if (container) container.innerHTML = '';
330
- };
331
-
332
- try {
333
- const wu = await waitForWu(3000);
334
- wu.define(appName, { mount: mountApp, unmount: unmountApp });
335
- console.log(`[WuPreact] ✅ ${appName} (compat) registered with Wu Framework`);
336
- return true;
337
- } catch (error) {
338
- if (standalone) {
339
- const containerElement = document.querySelector(standaloneContainer);
340
- if (containerElement) {
341
- mountApp(containerElement);
342
- return true;
343
- }
344
- }
345
- return false;
346
- }
347
- }
348
-
349
- /**
350
- * Crea un componente WuSlot para Preact
351
- *
352
- * @param {Function} h - Función h de Preact
353
- * @returns {Function} Componente WuSlot
354
- *
355
- * @example
356
- * import { h } from 'preact';
357
- * import { createWuSlot } from 'wu-framework/adapters/preact';
358
- *
359
- * const WuSlot = createWuSlot(h);
360
- *
361
- * function Shell() {
362
- * return (
363
- * <div>
364
- * <WuSlot name="header" url="http://localhost:3001" />
365
- * </div>
366
- * );
367
- * }
368
- */
369
- function createWuSlot(h) {
370
- // Importar hooks de Preact
371
- let useState, useEffect, useRef, useCallback;
372
-
373
- try {
374
- const hooks = require('preact/hooks');
375
- useState = hooks.useState;
376
- useEffect = hooks.useEffect;
377
- useRef = hooks.useRef;
378
- useCallback = hooks.useCallback;
379
- } catch (e) {
380
- // Si no hay hooks, crear versión sin hooks
381
- return function WuSlotBasic(props) {
382
- const { name, url } = props;
383
- return h('div', {
384
- class: 'wu-slot',
385
- 'data-wu-app': name,
386
- 'data-wu-url': url,
387
- style: 'min-height: 100px;',
388
- ref: (el) => {
389
- if (el && !el._mounted) {
390
- el._mounted = true;
391
- mountSlot(el, name, url);
392
- }
393
- }
394
- }, `Loading ${name}...`);
395
- };
396
- }
397
-
398
- return function WuSlot(props) {
399
- const {
400
- name,
401
- url,
402
- appName = null,
403
- fallback = null,
404
- onLoad = null,
405
- onError = null,
406
- className = '',
407
- style = {}
408
- } = props;
409
-
410
- const containerRef = useRef(null);
411
- const appInstanceRef = useRef(null);
412
- const [loading, setLoading] = useState(true);
413
- const [error, setError] = useState(null);
414
-
415
- const actualAppName = appName || name;
416
-
417
- const mountMicrofrontend = useCallback(async () => {
418
- if (!containerRef.current) return;
419
-
420
- try {
421
- setLoading(true);
422
- setError(null);
423
-
424
- const wu = getWuInstance();
425
- if (!wu) throw new Error('Wu Framework not initialized');
426
-
427
- const containerId = `wu-slot-${actualAppName}-${Date.now()}`;
428
- const innerContainer = document.createElement('div');
429
- innerContainer.id = containerId;
430
- innerContainer.style.cssText = 'width: 100%; height: 100%;';
431
-
432
- containerRef.current.innerHTML = '';
433
- containerRef.current.appendChild(innerContainer);
434
-
435
- const app = wu.app(actualAppName, {
436
- url,
437
- container: `#${containerId}`,
438
- autoInit: true
439
- });
440
-
441
- appInstanceRef.current = app;
442
- await app.mount();
443
-
444
- setLoading(false);
445
- if (onLoad) onLoad({ name: actualAppName, url });
446
-
447
- } catch (err) {
448
- console.error(`[WuSlot] Error loading ${actualAppName}:`, err);
449
- setError(err.message || 'Failed to load microfrontend');
450
- setLoading(false);
451
- if (onError) onError(err);
452
- }
453
- }, [actualAppName, url, onLoad, onError]);
454
-
455
- useEffect(() => {
456
- mountMicrofrontend();
457
-
458
- return () => {
459
- if (appInstanceRef.current) {
460
- appInstanceRef.current.unmount().catch(console.warn);
461
- appInstanceRef.current = null;
462
- }
463
- };
464
- }, [mountMicrofrontend]);
465
-
466
- if (error) {
467
- return h('div', {
468
- class: `wu-slot wu-slot-error ${className}`,
469
- style: {
470
- padding: '1rem',
471
- border: '1px solid #f5c6cb',
472
- borderRadius: '4px',
473
- backgroundColor: '#f8d7da',
474
- color: '#721c24',
475
- ...style
476
- }
477
- }, [
478
- h('strong', null, `Error loading ${name}`),
479
- h('p', { style: { margin: '0.5rem 0 0 0' } }, error)
480
- ]);
481
- }
482
-
483
- return h('div', {
484
- ref: containerRef,
485
- class: `wu-slot ${loading ? 'wu-slot-loading' : 'wu-slot-loaded'} ${className}`,
486
- style: {
487
- minHeight: '100px',
488
- position: 'relative',
489
- ...style
490
- },
491
- 'data-wu-app': actualAppName,
492
- 'data-wu-url': url
493
- }, loading && (fallback || h('div', {
494
- style: {
495
- display: 'flex',
496
- alignItems: 'center',
497
- justifyContent: 'center',
498
- padding: '2rem',
499
- color: '#666'
500
- }
501
- }, `Loading ${name}...`)));
502
- };
503
- }
504
-
505
- /**
506
- * Hook para usar el EventBus de Wu Framework en Preact
507
- *
508
- * @example
509
- * import { createUseWuEvents } from 'wu-framework/adapters/preact';
510
- * import { useState, useEffect, useRef, useCallback } from 'preact/hooks';
511
- *
512
- * const useWuEvents = createUseWuEvents({ useCallback, useEffect, useRef });
513
- *
514
- * function MyComponent() {
515
- * const { emit, on } = useWuEvents();
516
- *
517
- * useEffect(() => {
518
- * return on('user:login', (data) => console.log(data));
519
- * }, [on]);
520
- * }
521
- */
522
- function createUseWuEvents(hooks) {
523
- const { useCallback, useEffect, useRef } = hooks;
524
-
525
- return function useWuEvents() {
526
- const subscriptionsRef = useRef([]);
527
-
528
- const emit = useCallback((event, data, options) => {
529
- const wu = getWuInstance();
530
- if (wu?.eventBus) {
531
- wu.eventBus.emit(event, data, options);
532
- }
533
- }, []);
534
-
535
- const on = useCallback((event, callback) => {
536
- const wu = getWuInstance();
537
- if (wu?.eventBus) {
538
- const unsubscribe = wu.eventBus.on(event, callback);
539
- subscriptionsRef.current.push(unsubscribe);
540
- return unsubscribe;
541
- }
542
- return () => {};
543
- }, []);
544
-
545
- const once = useCallback((event, callback) => {
546
- const wu = getWuInstance();
547
- if (wu?.eventBus) {
548
- return wu.eventBus.once(event, callback);
549
- }
550
- return () => {};
551
- }, []);
552
-
553
- useEffect(() => {
554
- return () => {
555
- subscriptionsRef.current.forEach(unsub => unsub());
556
- subscriptionsRef.current = [];
557
- };
558
- }, []);
559
-
560
- return { emit, on, once };
561
- };
562
- }
563
-
564
- /**
565
- * Hook para usar el Store de Wu Framework en Preact
566
- *
567
- * @example
568
- * const useWuStore = createUseWuStore({ useState, useCallback, useEffect });
569
- *
570
- * function MyComponent() {
571
- * const { state, setState, getState } = useWuStore('user');
572
- * // ...
573
- * }
574
- */
575
- function createUseWuStore(hooks) {
576
- const { useState, useCallback, useEffect } = hooks;
577
-
578
- return function useWuStore(namespace = '') {
579
- const [state, setLocalState] = useState(() => {
580
- const wu = getWuInstance();
581
- return wu?.store?.get(namespace) || null;
582
- });
583
-
584
- const setState = useCallback((path, value) => {
585
- const wu = getWuInstance();
586
- if (wu?.store) {
587
- const fullPath = namespace ? `${namespace}.${path}` : path;
588
- wu.store.set(fullPath, value);
589
- }
590
- }, [namespace]);
591
-
592
- const getState = useCallback((path = '') => {
593
- const wu = getWuInstance();
594
- if (wu?.store) {
595
- const fullPath = namespace ? (path ? `${namespace}.${path}` : namespace) : path;
596
- return wu.store.get(fullPath);
597
- }
598
- return null;
599
- }, [namespace]);
600
-
601
- useEffect(() => {
602
- const wu = getWuInstance();
603
- if (!wu?.store) return;
604
-
605
- const pattern = namespace ? `${namespace}.*` : '*';
606
- const unsubscribe = wu.store.on(pattern, () => {
607
- setLocalState(wu.store.get(namespace));
608
- });
609
-
610
- return unsubscribe;
611
- }, [namespace]);
612
-
613
- return { state, setState, getState };
614
- };
615
- }
616
-
617
- // Helper interno para montar slot sin hooks
618
- async function mountSlot(container, name, url) {
619
- try {
620
- const wu = getWuInstance();
621
- if (!wu) return;
622
-
623
- const containerId = `wu-slot-${name}-${Date.now()}`;
624
- const innerContainer = document.createElement('div');
625
- innerContainer.id = containerId;
626
- innerContainer.style.cssText = 'width: 100%; height: 100%;';
627
-
628
- container.innerHTML = '';
629
- container.appendChild(innerContainer);
630
-
631
- const app = wu.app(name, {
632
- url,
633
- container: `#${containerId}`,
634
- autoInit: true
635
- });
636
-
637
- await app.mount();
638
- } catch (err) {
639
- container.innerHTML = `<div style="color: red;">Error: ${err.message}</div>`;
640
- }
641
- }
642
-
643
- // ============================================
644
- // AI INTEGRATION
645
- // ============================================
646
- export { createUseWuAI } from './ai.js';
647
- import { createUseWuAI } from './ai.js';
648
-
649
- // API pública del adapter
650
- export const wuPreact = {
651
- register,
652
- registerCompat,
653
- createWuSlot,
654
- createUseWuEvents,
655
- createUseWuStore,
656
- createUseWuAI,
657
- getWuInstance,
658
- waitForWu
659
- };
660
-
661
- export default wuPreact;
1
+ /**
2
+ * 🚀 WU-FRAMEWORK PREACT ADAPTER
3
+ *
4
+ * Simplifica la integración de Preact con Wu Framework.
5
+ * Compatible con Preact 10+ y preact/compat para proyectos React migrados.
6
+ *
7
+ * @example
8
+ * // Microfrontend (main.jsx)
9
+ * import { wuPreact } from 'wu-framework/adapters/preact';
10
+ * import App from './App';
11
+ *
12
+ * wuPreact.register('my-app', App);
13
+ *
14
+ * @example
15
+ * // Shell (cargar microfrontend)
16
+ * import { createWuSlot } from 'wu-framework/adapters/preact';
17
+ * import { h } from 'preact';
18
+ *
19
+ * const WuSlot = createWuSlot(h);
20
+ * <WuSlot name="my-app" url="http://localhost:3001" />
21
+ */
22
+
23
+ // Estado global del adapter
24
+ const adapterState = {
25
+ apps: new Map(),
26
+ preact: null,
27
+ render: null,
28
+ h: null,
29
+ initialized: false
30
+ };
31
+
32
+ /**
33
+ * Detecta y obtiene Preact del contexto global o lo importa
34
+ */
35
+ async function ensurePreact() {
36
+ if (adapterState.initialized) return true;
37
+
38
+ try {
39
+ // Intentar obtener de window
40
+ if (typeof window !== 'undefined' && window.preact) {
41
+ adapterState.preact = window.preact;
42
+ adapterState.render = window.preact.render;
43
+ adapterState.h = window.preact.h;
44
+ adapterState.initialized = true;
45
+ return true;
46
+ }
47
+
48
+ // Intentar import dinámico
49
+ const preact = await import('preact');
50
+ adapterState.preact = preact;
51
+ adapterState.render = preact.render;
52
+ adapterState.h = preact.h;
53
+ adapterState.initialized = true;
54
+ return true;
55
+
56
+ } catch (error) {
57
+ console.error('[WuPreact] Failed to load Preact:', error);
58
+ return false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Obtiene la instancia de Wu Framework
64
+ */
65
+ function getWuInstance() {
66
+ if (typeof window === 'undefined') return null;
67
+
68
+ return window.wu
69
+ || window.parent?.wu
70
+ || window.top?.wu
71
+ || null;
72
+ }
73
+
74
+ /**
75
+ * Espera a que Wu Framework esté disponible
76
+ */
77
+ function waitForWu(timeout = 5000) {
78
+ return new Promise((resolve, reject) => {
79
+ const wu = getWuInstance();
80
+ if (wu) {
81
+ resolve(wu);
82
+ return;
83
+ }
84
+
85
+ const startTime = Date.now();
86
+
87
+ const handleWuReady = () => {
88
+ cleanup();
89
+ resolve(getWuInstance());
90
+ };
91
+
92
+ window.addEventListener('wu:ready', handleWuReady);
93
+ window.addEventListener('wu:app:ready', handleWuReady);
94
+
95
+ const checkInterval = setInterval(() => {
96
+ const wu = getWuInstance();
97
+ if (wu) {
98
+ cleanup();
99
+ resolve(wu);
100
+ return;
101
+ }
102
+
103
+ if (Date.now() - startTime > timeout) {
104
+ cleanup();
105
+ reject(new Error(`Wu Framework not found after ${timeout}ms`));
106
+ }
107
+ }, 200);
108
+
109
+ function cleanup() {
110
+ clearInterval(checkInterval);
111
+ window.removeEventListener('wu:ready', handleWuReady);
112
+ window.removeEventListener('wu:app:ready', handleWuReady);
113
+ }
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Registra un componente Preact como microfrontend
119
+ *
120
+ * @param {string} appName - Nombre único del microfrontend
121
+ * @param {Function} Component - Componente Preact principal
122
+ * @param {Object} options - Opciones adicionales
123
+ * @param {Object} options.props - Props iniciales para el componente
124
+ * @param {Function} options.onMount - Callback después de montar
125
+ * @param {Function} options.onUnmount - Callback antes de desmontar
126
+ * @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
127
+ * @param {string} options.standaloneContainer - Selector para modo standalone (default: '#app')
128
+ *
129
+ * @example
130
+ * // Básico
131
+ * wuPreact.register('my-app', App);
132
+ *
133
+ * @example
134
+ * // Con props
135
+ * wuPreact.register('my-app', App, {
136
+ * props: { apiUrl: 'https://api.example.com' },
137
+ * onMount: (container) => console.log('Mounted!')
138
+ * });
139
+ */
140
+ async function register(appName, Component, options = {}) {
141
+ const {
142
+ props = {},
143
+ onMount = null,
144
+ onUnmount = null,
145
+ standalone = true,
146
+ standaloneContainer = '#app'
147
+ } = options;
148
+
149
+ // Asegurar que Preact está disponible
150
+ const hasPreact = await ensurePreact();
151
+ if (!hasPreact) {
152
+ console.error(`[WuPreact] Cannot register ${appName}: Preact not available`);
153
+ return false;
154
+ }
155
+
156
+ const { render, h } = adapterState;
157
+
158
+ // Función de mount interna
159
+ const mountApp = (container) => {
160
+ if (!container) {
161
+ console.error(`[WuPreact] Mount failed for ${appName}: container is null`);
162
+ return;
163
+ }
164
+
165
+ // Evitar doble mount
166
+ if (adapterState.apps.has(appName)) {
167
+ console.warn(`[WuPreact] ${appName} already mounted, unmounting first`);
168
+ unmountApp();
169
+ }
170
+
171
+ try {
172
+ // Limpiar container
173
+ container.innerHTML = '';
174
+
175
+ // Renderizar componente
176
+ render(
177
+ h(Component, {
178
+ ...props,
179
+ wuAppName: appName,
180
+ wuInstance: getWuInstance()
181
+ }),
182
+ container
183
+ );
184
+
185
+ // Guardar referencia
186
+ adapterState.apps.set(appName, { container, Component });
187
+
188
+ console.log(`[WuPreact] ✅ ${appName} mounted successfully`);
189
+
190
+ if (onMount) {
191
+ onMount(container);
192
+ }
193
+ } catch (error) {
194
+ console.error(`[WuPreact] Mount error for ${appName}:`, error);
195
+ throw error;
196
+ }
197
+ };
198
+
199
+ // Función de unmount interna
200
+ const unmountApp = (container) => {
201
+ const appData = adapterState.apps.get(appName);
202
+
203
+ if (appData) {
204
+ try {
205
+ if (onUnmount) {
206
+ onUnmount(appData.container);
207
+ }
208
+
209
+ // Unmount en Preact: renderizar null
210
+ render(null, appData.container);
211
+
212
+ adapterState.apps.delete(appName);
213
+
214
+ console.log(`[WuPreact] ✅ ${appName} unmounted successfully`);
215
+ } catch (error) {
216
+ console.error(`[WuPreact] 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(`[WuPreact] ✅ ${appName} registered with Wu Framework`);
235
+ return true;
236
+
237
+ } catch (error) {
238
+ console.warn(`[WuPreact] Wu Framework not available for ${appName}`);
239
+
240
+ if (standalone) {
241
+ const containerElement = document.querySelector(standaloneContainer);
242
+
243
+ if (containerElement) {
244
+ console.log(`[WuPreact] Running ${appName} in standalone mode`);
245
+ mountApp(containerElement);
246
+ return true;
247
+ }
248
+ }
249
+
250
+ return false;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Registra usando preact/compat (para proyectos migrados de React)
256
+ *
257
+ * @example
258
+ * import { wuPreact } from 'wu-framework/adapters/preact';
259
+ * import App from './App'; // Componente React-like
260
+ *
261
+ * wuPreact.registerCompat('my-app', App);
262
+ */
263
+ async function registerCompat(appName, Component, options = {}) {
264
+ const {
265
+ props = {},
266
+ onMount = null,
267
+ onUnmount = null,
268
+ standalone = true,
269
+ standaloneContainer = '#app'
270
+ } = options;
271
+
272
+ let render, h;
273
+
274
+ try {
275
+ // Intentar cargar preact/compat
276
+ const compat = await import('preact/compat');
277
+ render = compat.render;
278
+ h = compat.createElement;
279
+ } catch (e) {
280
+ console.error('[WuPreact] preact/compat not available');
281
+ // Fallback a registro normal
282
+ return register(appName, Component, options);
283
+ }
284
+
285
+ const mountApp = (container) => {
286
+ if (!container) return;
287
+
288
+ if (adapterState.apps.has(appName)) {
289
+ unmountApp();
290
+ }
291
+
292
+ try {
293
+ container.innerHTML = '';
294
+
295
+ render(
296
+ h(Component, {
297
+ ...props,
298
+ wuAppName: appName,
299
+ wuInstance: getWuInstance()
300
+ }),
301
+ container
302
+ );
303
+
304
+ adapterState.apps.set(appName, { container, Component, isCompat: true });
305
+
306
+ console.log(`[WuPreact] ✅ ${appName} (compat) mounted successfully`);
307
+
308
+ if (onMount) onMount(container);
309
+ } catch (error) {
310
+ console.error(`[WuPreact] Mount error for ${appName}:`, error);
311
+ throw error;
312
+ }
313
+ };
314
+
315
+ const unmountApp = (container) => {
316
+ const appData = adapterState.apps.get(appName);
317
+
318
+ if (appData) {
319
+ try {
320
+ if (onUnmount) onUnmount(appData.container);
321
+ render(null, appData.container);
322
+ adapterState.apps.delete(appName);
323
+ console.log(`[WuPreact] ✅ ${appName} (compat) unmounted successfully`);
324
+ } catch (error) {
325
+ console.error(`[WuPreact] Unmount error for ${appName}:`, error);
326
+ }
327
+ }
328
+
329
+ if (container) container.innerHTML = '';
330
+ };
331
+
332
+ try {
333
+ const wu = await waitForWu(3000);
334
+ wu.define(appName, { mount: mountApp, unmount: unmountApp });
335
+ console.log(`[WuPreact] ✅ ${appName} (compat) registered with Wu Framework`);
336
+ return true;
337
+ } catch (error) {
338
+ if (standalone) {
339
+ const containerElement = document.querySelector(standaloneContainer);
340
+ if (containerElement) {
341
+ mountApp(containerElement);
342
+ return true;
343
+ }
344
+ }
345
+ return false;
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Crea un componente WuSlot para Preact
351
+ *
352
+ * @param {Function} h - Función h de Preact
353
+ * @returns {Function} Componente WuSlot
354
+ *
355
+ * @example
356
+ * import { h } from 'preact';
357
+ * import { createWuSlot } from 'wu-framework/adapters/preact';
358
+ *
359
+ * const WuSlot = createWuSlot(h);
360
+ *
361
+ * function Shell() {
362
+ * return (
363
+ * <div>
364
+ * <WuSlot name="header" url="http://localhost:3001" />
365
+ * </div>
366
+ * );
367
+ * }
368
+ */
369
+ function createWuSlot(h) {
370
+ // Importar hooks de Preact
371
+ let useState, useEffect, useRef, useCallback;
372
+
373
+ try {
374
+ const hooks = require('preact/hooks');
375
+ useState = hooks.useState;
376
+ useEffect = hooks.useEffect;
377
+ useRef = hooks.useRef;
378
+ useCallback = hooks.useCallback;
379
+ } catch (e) {
380
+ // Si no hay hooks, crear versión sin hooks
381
+ return function WuSlotBasic(props) {
382
+ const { name, url } = props;
383
+ return h('div', {
384
+ class: 'wu-slot',
385
+ 'data-wu-app': name,
386
+ 'data-wu-url': url,
387
+ style: 'min-height: 100px;',
388
+ ref: (el) => {
389
+ if (el && !el._mounted) {
390
+ el._mounted = true;
391
+ mountSlot(el, name, url);
392
+ }
393
+ }
394
+ }, `Loading ${name}...`);
395
+ };
396
+ }
397
+
398
+ return function WuSlot(props) {
399
+ const {
400
+ name,
401
+ url,
402
+ appName = null,
403
+ fallback = null,
404
+ onLoad = null,
405
+ onError = null,
406
+ className = '',
407
+ style = {}
408
+ } = props;
409
+
410
+ const containerRef = useRef(null);
411
+ const appInstanceRef = useRef(null);
412
+ const [loading, setLoading] = useState(true);
413
+ const [error, setError] = useState(null);
414
+
415
+ const actualAppName = appName || name;
416
+
417
+ const mountMicrofrontend = useCallback(async () => {
418
+ if (!containerRef.current) return;
419
+
420
+ try {
421
+ setLoading(true);
422
+ setError(null);
423
+
424
+ const wu = getWuInstance();
425
+ if (!wu) throw new Error('Wu Framework not initialized');
426
+
427
+ const containerId = `wu-slot-${actualAppName}-${Date.now()}`;
428
+ const innerContainer = document.createElement('div');
429
+ innerContainer.id = containerId;
430
+ innerContainer.style.cssText = 'width: 100%; height: 100%;';
431
+
432
+ containerRef.current.innerHTML = '';
433
+ containerRef.current.appendChild(innerContainer);
434
+
435
+ const app = wu.app(actualAppName, {
436
+ url,
437
+ container: `#${containerId}`,
438
+ autoInit: true
439
+ });
440
+
441
+ appInstanceRef.current = app;
442
+ await app.mount();
443
+
444
+ setLoading(false);
445
+ if (onLoad) onLoad({ name: actualAppName, url });
446
+
447
+ } catch (err) {
448
+ console.error(`[WuSlot] Error loading ${actualAppName}:`, err);
449
+ setError(err.message || 'Failed to load microfrontend');
450
+ setLoading(false);
451
+ if (onError) onError(err);
452
+ }
453
+ }, [actualAppName, url, onLoad, onError]);
454
+
455
+ useEffect(() => {
456
+ mountMicrofrontend();
457
+
458
+ return () => {
459
+ if (appInstanceRef.current) {
460
+ appInstanceRef.current.unmount().catch(console.warn);
461
+ appInstanceRef.current = null;
462
+ }
463
+ };
464
+ }, [mountMicrofrontend]);
465
+
466
+ if (error) {
467
+ return h('div', {
468
+ class: `wu-slot wu-slot-error ${className}`,
469
+ style: {
470
+ padding: '1rem',
471
+ border: '1px solid #f5c6cb',
472
+ borderRadius: '4px',
473
+ backgroundColor: '#f8d7da',
474
+ color: '#721c24',
475
+ ...style
476
+ }
477
+ }, [
478
+ h('strong', null, `Error loading ${name}`),
479
+ h('p', { style: { margin: '0.5rem 0 0 0' } }, error)
480
+ ]);
481
+ }
482
+
483
+ return h('div', {
484
+ ref: containerRef,
485
+ class: `wu-slot ${loading ? 'wu-slot-loading' : 'wu-slot-loaded'} ${className}`,
486
+ style: {
487
+ minHeight: '100px',
488
+ position: 'relative',
489
+ ...style
490
+ },
491
+ 'data-wu-app': actualAppName,
492
+ 'data-wu-url': url
493
+ }, loading && (fallback || h('div', {
494
+ style: {
495
+ display: 'flex',
496
+ alignItems: 'center',
497
+ justifyContent: 'center',
498
+ padding: '2rem',
499
+ color: '#666'
500
+ }
501
+ }, `Loading ${name}...`)));
502
+ };
503
+ }
504
+
505
+ /**
506
+ * Hook para usar el EventBus de Wu Framework en Preact
507
+ *
508
+ * @example
509
+ * import { createUseWuEvents } from 'wu-framework/adapters/preact';
510
+ * import { useState, useEffect, useRef, useCallback } from 'preact/hooks';
511
+ *
512
+ * const useWuEvents = createUseWuEvents({ useCallback, useEffect, useRef });
513
+ *
514
+ * function MyComponent() {
515
+ * const { emit, on } = useWuEvents();
516
+ *
517
+ * useEffect(() => {
518
+ * return on('user:login', (data) => console.log(data));
519
+ * }, [on]);
520
+ * }
521
+ */
522
+ function createUseWuEvents(hooks) {
523
+ const { useCallback, useEffect, useRef } = hooks;
524
+
525
+ return function useWuEvents() {
526
+ const subscriptionsRef = useRef([]);
527
+
528
+ const emit = useCallback((event, data, options) => {
529
+ const wu = getWuInstance();
530
+ if (wu?.eventBus) {
531
+ wu.eventBus.emit(event, data, options);
532
+ }
533
+ }, []);
534
+
535
+ const on = useCallback((event, callback) => {
536
+ const wu = getWuInstance();
537
+ if (wu?.eventBus) {
538
+ const unsubscribe = wu.eventBus.on(event, callback);
539
+ subscriptionsRef.current.push(unsubscribe);
540
+ return unsubscribe;
541
+ }
542
+ return () => {};
543
+ }, []);
544
+
545
+ const once = useCallback((event, callback) => {
546
+ const wu = getWuInstance();
547
+ if (wu?.eventBus) {
548
+ return wu.eventBus.once(event, callback);
549
+ }
550
+ return () => {};
551
+ }, []);
552
+
553
+ useEffect(() => {
554
+ return () => {
555
+ subscriptionsRef.current.forEach(unsub => unsub());
556
+ subscriptionsRef.current = [];
557
+ };
558
+ }, []);
559
+
560
+ return { emit, on, once };
561
+ };
562
+ }
563
+
564
+ /**
565
+ * Hook para usar el Store de Wu Framework en Preact
566
+ *
567
+ * @example
568
+ * const useWuStore = createUseWuStore({ useState, useCallback, useEffect });
569
+ *
570
+ * function MyComponent() {
571
+ * const { state, setState, getState } = useWuStore('user');
572
+ * // ...
573
+ * }
574
+ */
575
+ function createUseWuStore(hooks) {
576
+ const { useState, useCallback, useEffect } = hooks;
577
+
578
+ return function useWuStore(namespace = '') {
579
+ const [state, setLocalState] = useState(() => {
580
+ const wu = getWuInstance();
581
+ return wu?.store?.get(namespace) || null;
582
+ });
583
+
584
+ const setState = useCallback((path, value) => {
585
+ const wu = getWuInstance();
586
+ if (wu?.store) {
587
+ const fullPath = namespace ? `${namespace}.${path}` : path;
588
+ wu.store.set(fullPath, value);
589
+ }
590
+ }, [namespace]);
591
+
592
+ const getState = useCallback((path = '') => {
593
+ const wu = getWuInstance();
594
+ if (wu?.store) {
595
+ const fullPath = namespace ? (path ? `${namespace}.${path}` : namespace) : path;
596
+ return wu.store.get(fullPath);
597
+ }
598
+ return null;
599
+ }, [namespace]);
600
+
601
+ useEffect(() => {
602
+ const wu = getWuInstance();
603
+ if (!wu?.store) return;
604
+
605
+ const pattern = namespace ? `${namespace}.*` : '*';
606
+ const unsubscribe = wu.store.on(pattern, () => {
607
+ setLocalState(wu.store.get(namespace));
608
+ });
609
+
610
+ return unsubscribe;
611
+ }, [namespace]);
612
+
613
+ return { state, setState, getState };
614
+ };
615
+ }
616
+
617
+ // Helper interno para montar slot sin hooks
618
+ async function mountSlot(container, name, url) {
619
+ try {
620
+ const wu = getWuInstance();
621
+ if (!wu) return;
622
+
623
+ const containerId = `wu-slot-${name}-${Date.now()}`;
624
+ const innerContainer = document.createElement('div');
625
+ innerContainer.id = containerId;
626
+ innerContainer.style.cssText = 'width: 100%; height: 100%;';
627
+
628
+ container.innerHTML = '';
629
+ container.appendChild(innerContainer);
630
+
631
+ const app = wu.app(name, {
632
+ url,
633
+ container: `#${containerId}`,
634
+ autoInit: true
635
+ });
636
+
637
+ await app.mount();
638
+ } catch (err) {
639
+ container.innerHTML = `<div style="color: red;">Error: ${err.message}</div>`;
640
+ }
641
+ }
642
+
643
+ // ============================================
644
+ // AI INTEGRATION
645
+ // ============================================
646
+ export { createUseWuAI } from './ai.js';
647
+ import { createUseWuAI } from './ai.js';
648
+
649
+ // API pública del adapter
650
+ export const wuPreact = {
651
+ register,
652
+ registerCompat,
653
+ createWuSlot,
654
+ createUseWuEvents,
655
+ createUseWuStore,
656
+ createUseWuAI,
657
+ getWuInstance,
658
+ waitForWu
659
+ };
660
+
661
+ export default wuPreact;