wu-framework 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * 🚀 WU-FRAMEWORK SOLID.JS ADAPTER - TypeScript Declarations
3
+ */
4
+
5
+ import type { WuCore } from '../core/wu-core';
6
+
7
+ // Solid types (generics to avoid hard dependency)
8
+ type Component<P = {}> = (props: P) => any;
9
+ type Accessor<T> = () => T;
10
+ type Setter<T> = (value: T | ((prev: T) => T)) => T;
11
+
12
+ /**
13
+ * Opciones de registro Solid
14
+ */
15
+ export interface SolidRegisterOptions {
16
+ /** Props iniciales */
17
+ props?: Record<string, any>;
18
+ /** Callback después de montar */
19
+ onMount?: (container: HTMLElement) => void;
20
+ /** Callback antes de desmontar */
21
+ onUnmount?: (container: HTMLElement) => void;
22
+ /** Permitir ejecución standalone */
23
+ standalone?: boolean;
24
+ /** Selector para modo standalone */
25
+ standaloneContainer?: string;
26
+ }
27
+
28
+ /**
29
+ * Props del componente WuSlot
30
+ */
31
+ export interface WuSlotProps {
32
+ name: string;
33
+ url: string;
34
+ appName?: string;
35
+ fallback?: string;
36
+ onLoad?: (data: { name: string; url: string }) => void;
37
+ onError?: (error: Error) => void;
38
+ class?: string;
39
+ style?: Record<string, string | number>;
40
+ }
41
+
42
+ /**
43
+ * Resultado de createWuStore
44
+ */
45
+ export type WuStoreResult<T = any> = [
46
+ Accessor<T>,
47
+ (path: string, value: any) => void
48
+ ];
49
+
50
+ /**
51
+ * Helper de eventos Wu para Solid
52
+ */
53
+ export interface UseWuEventsResult {
54
+ emit: (event: string, data?: any, options?: any) => void;
55
+ on: (event: string, callback: (data: any) => void) => () => void;
56
+ once: (event: string, callback: (data: any) => void) => () => void;
57
+ off: (event: string, callback: (data: any) => void) => void;
58
+ }
59
+
60
+ /**
61
+ * Contexto de Wu
62
+ */
63
+ export interface WuContextResult {
64
+ WuProvider: Component<{ children?: any }>;
65
+ useWu: () => WuCore | null;
66
+ WuContext: any;
67
+ }
68
+
69
+ export function register(
70
+ appName: string,
71
+ Component: Component,
72
+ options?: SolidRegisterOptions
73
+ ): Promise<boolean>;
74
+
75
+ export function createWuSlot(): Component<WuSlotProps>;
76
+
77
+ export function createWuStore<T = any>(namespace?: string): WuStoreResult<T>;
78
+
79
+ export function createWuEvent<T = any>(eventPattern: string): Accessor<T | null>;
80
+
81
+ export function useWuEvents(): UseWuEventsResult;
82
+
83
+ export function createWuContext(): WuContextResult;
84
+
85
+ export function getWuInstance(): WuCore | null;
86
+
87
+ export function waitForWu(timeout?: number): Promise<WuCore>;
88
+
89
+ export interface WuSolidAdapter {
90
+ register: typeof register;
91
+ createWuSlot: typeof createWuSlot;
92
+ createWuStore: typeof createWuStore;
93
+ createWuEvent: typeof createWuEvent;
94
+ useWuEvents: typeof useWuEvents;
95
+ createWuContext: typeof createWuContext;
96
+ getWuInstance: typeof getWuInstance;
97
+ waitForWu: typeof waitForWu;
98
+ }
99
+
100
+ export const wuSolid: WuSolidAdapter;
101
+ export default wuSolid;
@@ -0,0 +1,591 @@
1
+ /**
2
+ * 🚀 WU-FRAMEWORK SOLID.JS ADAPTER
3
+ *
4
+ * Simplifica la integración de Solid.js con Wu Framework.
5
+ * Aprovecha la reactividad fine-grained de Solid para microfrontends eficientes.
6
+ *
7
+ * @example
8
+ * // Microfrontend (main.jsx)
9
+ * import { wuSolid } from 'wu-framework/adapters/solid';
10
+ * import App from './App';
11
+ *
12
+ * wuSolid.register('my-app', App);
13
+ *
14
+ * @example
15
+ * // Shell (cargar microfrontend)
16
+ * import { WuSlot } from 'wu-framework/adapters/solid';
17
+ *
18
+ * <WuSlot name="my-app" url="http://localhost:3001" />
19
+ */
20
+
21
+ // Estado global del adapter
22
+ const adapterState = {
23
+ apps: new Map(),
24
+ solid: null,
25
+ solidWeb: null,
26
+ initialized: false
27
+ };
28
+
29
+ /**
30
+ * Detecta y obtiene Solid del contexto global o lo importa
31
+ */
32
+ async function ensureSolid() {
33
+ if (adapterState.initialized) return true;
34
+
35
+ try {
36
+ // Intentar obtener de window
37
+ if (typeof window !== 'undefined' && window.Solid) {
38
+ adapterState.solid = window.Solid;
39
+ adapterState.solidWeb = window.SolidWeb;
40
+ adapterState.initialized = true;
41
+ return true;
42
+ }
43
+
44
+ // Intentar import dinámico
45
+ const [solid, solidWeb] = await Promise.all([
46
+ import('solid-js'),
47
+ import('solid-js/web')
48
+ ]);
49
+
50
+ adapterState.solid = solid;
51
+ adapterState.solidWeb = solidWeb;
52
+ adapterState.initialized = true;
53
+ return true;
54
+
55
+ } catch (error) {
56
+ console.error('[WuSolid] Failed to load Solid:', error);
57
+ return false;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Obtiene la instancia de Wu Framework
63
+ */
64
+ function getWuInstance() {
65
+ if (typeof window === 'undefined') return null;
66
+
67
+ return window.wu
68
+ || window.parent?.wu
69
+ || window.top?.wu
70
+ || null;
71
+ }
72
+
73
+ /**
74
+ * Espera a que Wu Framework esté disponible
75
+ */
76
+ function waitForWu(timeout = 5000) {
77
+ return new Promise((resolve, reject) => {
78
+ const wu = getWuInstance();
79
+ if (wu) {
80
+ resolve(wu);
81
+ return;
82
+ }
83
+
84
+ const startTime = Date.now();
85
+
86
+ const handleWuReady = () => {
87
+ cleanup();
88
+ resolve(getWuInstance());
89
+ };
90
+
91
+ window.addEventListener('wu:ready', handleWuReady);
92
+ window.addEventListener('wu:app:ready', handleWuReady);
93
+
94
+ const checkInterval = setInterval(() => {
95
+ const wu = getWuInstance();
96
+ if (wu) {
97
+ cleanup();
98
+ resolve(wu);
99
+ return;
100
+ }
101
+
102
+ if (Date.now() - startTime > timeout) {
103
+ cleanup();
104
+ reject(new Error(`Wu Framework not found after ${timeout}ms`));
105
+ }
106
+ }, 200);
107
+
108
+ function cleanup() {
109
+ clearInterval(checkInterval);
110
+ window.removeEventListener('wu:ready', handleWuReady);
111
+ window.removeEventListener('wu:app:ready', handleWuReady);
112
+ }
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Registra un componente Solid como microfrontend
118
+ *
119
+ * @param {string} appName - Nombre único del microfrontend
120
+ * @param {Function} Component - Componente Solid principal
121
+ * @param {Object} options - Opciones adicionales
122
+ * @param {Object} options.props - Props iniciales para el componente
123
+ * @param {Function} options.onMount - Callback después de montar
124
+ * @param {Function} options.onUnmount - Callback antes de desmontar
125
+ * @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
126
+ * @param {string} options.standaloneContainer - Selector para modo standalone (default: '#root')
127
+ *
128
+ * @example
129
+ * // Básico
130
+ * wuSolid.register('my-app', App);
131
+ *
132
+ * @example
133
+ * // Con props
134
+ * wuSolid.register('my-app', App, {
135
+ * props: { apiUrl: 'https://api.example.com' },
136
+ * onMount: (container) => console.log('Mounted!')
137
+ * });
138
+ */
139
+ async function register(appName, Component, options = {}) {
140
+ const {
141
+ props = {},
142
+ onMount = null,
143
+ onUnmount = null,
144
+ standalone = true,
145
+ standaloneContainer = '#root'
146
+ } = options;
147
+
148
+ // Asegurar que Solid está disponible
149
+ const hasSolid = await ensureSolid();
150
+ if (!hasSolid) {
151
+ console.error(`[WuSolid] Cannot register ${appName}: Solid not available`);
152
+ return false;
153
+ }
154
+
155
+ const { render } = adapterState.solidWeb;
156
+
157
+ let disposeApp = null;
158
+
159
+ // Función de mount interna
160
+ const mountApp = (container) => {
161
+ if (!container) {
162
+ console.error(`[WuSolid] Mount failed for ${appName}: container is null`);
163
+ return;
164
+ }
165
+
166
+ // Evitar doble mount
167
+ if (adapterState.apps.has(appName)) {
168
+ console.warn(`[WuSolid] ${appName} already mounted, unmounting first`);
169
+ unmountApp();
170
+ }
171
+
172
+ try {
173
+ // Limpiar container
174
+ container.innerHTML = '';
175
+
176
+ // Renderizar componente Solid
177
+ // render() retorna una función dispose
178
+ disposeApp = render(
179
+ () => Component({
180
+ ...props,
181
+ wuAppName: appName,
182
+ wuInstance: getWuInstance()
183
+ }),
184
+ container
185
+ );
186
+
187
+ // Guardar referencia
188
+ adapterState.apps.set(appName, {
189
+ container,
190
+ Component,
191
+ dispose: disposeApp
192
+ });
193
+
194
+ console.log(`[WuSolid] ✅ ${appName} mounted successfully`);
195
+
196
+ if (onMount) {
197
+ onMount(container);
198
+ }
199
+ } catch (error) {
200
+ console.error(`[WuSolid] Mount error for ${appName}:`, error);
201
+ throw error;
202
+ }
203
+ };
204
+
205
+ // Función de unmount interna
206
+ const unmountApp = (container) => {
207
+ const appData = adapterState.apps.get(appName);
208
+
209
+ if (appData) {
210
+ try {
211
+ if (onUnmount) {
212
+ onUnmount(appData.container);
213
+ }
214
+
215
+ // Dispose de Solid (limpia todas las reactividades)
216
+ if (appData.dispose && typeof appData.dispose === 'function') {
217
+ appData.dispose();
218
+ }
219
+
220
+ // Limpiar DOM
221
+ appData.container.innerHTML = '';
222
+
223
+ adapterState.apps.delete(appName);
224
+
225
+ console.log(`[WuSolid] ✅ ${appName} unmounted successfully`);
226
+ } catch (error) {
227
+ console.error(`[WuSolid] Unmount error for ${appName}:`, error);
228
+ }
229
+ }
230
+
231
+ if (container) {
232
+ container.innerHTML = '';
233
+ }
234
+ };
235
+
236
+ // Intentar registrar con Wu Framework
237
+ try {
238
+ const wu = await waitForWu(3000);
239
+
240
+ wu.define(appName, {
241
+ mount: mountApp,
242
+ unmount: unmountApp
243
+ });
244
+
245
+ console.log(`[WuSolid] ✅ ${appName} registered with Wu Framework`);
246
+ return true;
247
+
248
+ } catch (error) {
249
+ console.warn(`[WuSolid] Wu Framework not available for ${appName}`);
250
+
251
+ if (standalone) {
252
+ const containerElement = document.querySelector(standaloneContainer);
253
+
254
+ if (containerElement) {
255
+ console.log(`[WuSolid] Running ${appName} in standalone mode`);
256
+ mountApp(containerElement);
257
+ return true;
258
+ }
259
+ }
260
+
261
+ return false;
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Crea un componente WuSlot para Solid
267
+ *
268
+ * @example
269
+ * import { WuSlot } from 'wu-framework/adapters/solid';
270
+ *
271
+ * function Shell() {
272
+ * return (
273
+ * <div>
274
+ * <WuSlot name="header" url="http://localhost:3001" />
275
+ * <WuSlot name="content" url="http://localhost:3002" />
276
+ * </div>
277
+ * );
278
+ * }
279
+ */
280
+ function createWuSlot() {
281
+ // Esta función debe ser llamada dentro del contexto de Solid
282
+ return async function WuSlot(props) {
283
+ const {
284
+ name,
285
+ url,
286
+ appName = null,
287
+ fallback = null,
288
+ onLoad = null,
289
+ onError = null,
290
+ class: className = '',
291
+ style = {}
292
+ } = props;
293
+
294
+ const { createSignal, onMount, onCleanup } = adapterState.solid;
295
+
296
+ const [loading, setLoading] = createSignal(true);
297
+ const [error, setError] = createSignal(null);
298
+
299
+ let containerRef;
300
+ let appInstance = null;
301
+
302
+ const actualAppName = appName || name;
303
+
304
+ onMount(async () => {
305
+ try {
306
+ const wu = getWuInstance();
307
+ if (!wu) throw new Error('Wu Framework not initialized');
308
+
309
+ const containerId = `wu-slot-${actualAppName}-${Date.now()}`;
310
+ const innerContainer = document.createElement('div');
311
+ innerContainer.id = containerId;
312
+ innerContainer.style.cssText = 'width: 100%; height: 100%;';
313
+
314
+ containerRef.innerHTML = '';
315
+ containerRef.appendChild(innerContainer);
316
+
317
+ const app = wu.app(actualAppName, {
318
+ url,
319
+ container: `#${containerId}`,
320
+ autoInit: true
321
+ });
322
+
323
+ appInstance = app;
324
+ await app.mount();
325
+
326
+ setLoading(false);
327
+ if (onLoad) onLoad({ name: actualAppName, url });
328
+
329
+ } catch (err) {
330
+ console.error(`[WuSlot] Error loading ${actualAppName}:`, err);
331
+ setError(err.message || 'Failed to load microfrontend');
332
+ setLoading(false);
333
+ if (onError) onError(err);
334
+ }
335
+ });
336
+
337
+ onCleanup(async () => {
338
+ if (appInstance) {
339
+ try {
340
+ await appInstance.unmount();
341
+ } catch (e) {}
342
+ appInstance = null;
343
+ }
344
+ });
345
+
346
+ // Retornar JSX de Solid
347
+ return (() => {
348
+ const el = document.createElement('div');
349
+ el.className = `wu-slot ${loading() ? 'wu-slot-loading' : ''} ${error() ? 'wu-slot-error' : ''} ${className}`;
350
+ el.style.cssText = 'min-height: 100px; position: relative;';
351
+ el.setAttribute('data-wu-app', actualAppName);
352
+ el.setAttribute('data-wu-url', url);
353
+
354
+ Object.assign(el.style, style);
355
+
356
+ containerRef = el;
357
+
358
+ if (error()) {
359
+ el.innerHTML = `
360
+ <div style="padding: 1rem; border: 1px solid #f5c6cb; border-radius: 4px; background: #f8d7da; color: #721c24;">
361
+ <strong>Error loading ${name}</strong>
362
+ <p style="margin: 0.5rem 0 0 0;">${error()}</p>
363
+ </div>
364
+ `;
365
+ } else if (loading()) {
366
+ el.innerHTML = fallback || `
367
+ <div style="display: flex; align-items: center; justify-content: center; padding: 2rem; color: #666;">
368
+ Loading ${name}...
369
+ </div>
370
+ `;
371
+ }
372
+
373
+ return el;
374
+ })();
375
+ };
376
+ }
377
+
378
+ /**
379
+ * Crea un store de Wu compatible con la reactividad de Solid
380
+ *
381
+ * @param {string} namespace - Namespace en el store de Wu
382
+ * @returns {Array} [state, setState] similar a createSignal
383
+ *
384
+ * @example
385
+ * import { createWuStore } from 'wu-framework/adapters/solid';
386
+ *
387
+ * function MyComponent() {
388
+ * const [user, setUser] = createWuStore('user');
389
+ *
390
+ * return (
391
+ * <div>
392
+ * <p>Name: {user()?.name}</p>
393
+ * <button onClick={() => setUser('name', 'John')}>
394
+ * Set Name
395
+ * </button>
396
+ * </div>
397
+ * );
398
+ * }
399
+ */
400
+ function createWuStore(namespace = '') {
401
+ const { createSignal, onCleanup } = adapterState.solid;
402
+
403
+ const wu = getWuInstance();
404
+ const initialValue = wu?.store?.get(namespace) || null;
405
+
406
+ const [state, setState] = createSignal(initialValue);
407
+
408
+ // Suscribirse a cambios en Wu Store
409
+ if (wu?.store) {
410
+ const pattern = namespace ? `${namespace}.*` : '*';
411
+ const unsubscribe = wu.store.on(pattern, () => {
412
+ setState(wu.store.get(namespace));
413
+ });
414
+
415
+ onCleanup(unsubscribe);
416
+ }
417
+
418
+ // Función para actualizar el store
419
+ const setWuState = (path, value) => {
420
+ if (wu?.store) {
421
+ const fullPath = namespace ? `${namespace}.${path}` : path;
422
+ wu.store.set(fullPath, value);
423
+ }
424
+ };
425
+
426
+ return [state, setWuState];
427
+ }
428
+
429
+ /**
430
+ * Crea un signal reactivo basado en eventos de Wu
431
+ *
432
+ * @param {string} eventPattern - Patrón de eventos
433
+ * @returns {Function} Signal con el último evento
434
+ *
435
+ * @example
436
+ * import { createWuEvent } from 'wu-framework/adapters/solid';
437
+ *
438
+ * function MyComponent() {
439
+ * const lastUserEvent = createWuEvent('user:*');
440
+ *
441
+ * return (
442
+ * <Show when={lastUserEvent()}>
443
+ * <p>Last event: {lastUserEvent()?.name}</p>
444
+ * </Show>
445
+ * );
446
+ * }
447
+ */
448
+ function createWuEvent(eventPattern) {
449
+ const { createSignal, onCleanup } = adapterState.solid;
450
+
451
+ const [event, setEvent] = createSignal(null);
452
+
453
+ const wu = getWuInstance();
454
+ if (wu?.eventBus) {
455
+ const unsubscribe = wu.eventBus.on(eventPattern, (e) => {
456
+ setEvent(e);
457
+ });
458
+
459
+ onCleanup(unsubscribe);
460
+ }
461
+
462
+ return event;
463
+ }
464
+
465
+ /**
466
+ * Hook para usar el EventBus de Wu Framework en Solid
467
+ *
468
+ * @example
469
+ * import { useWuEvents } from 'wu-framework/adapters/solid';
470
+ *
471
+ * function MyComponent() {
472
+ * const { emit, on } = useWuEvents();
473
+ *
474
+ * onMount(() => {
475
+ * on('user:login', (data) => console.log('User logged in:', data));
476
+ * });
477
+ *
478
+ * return (
479
+ * <button onClick={() => emit('user:logout', { reason: 'manual' })}>
480
+ * Logout
481
+ * </button>
482
+ * );
483
+ * }
484
+ */
485
+ function useWuEvents() {
486
+ const { onCleanup } = adapterState.solid;
487
+ const subscriptions = [];
488
+
489
+ const emit = (event, data, options) => {
490
+ const wu = getWuInstance();
491
+ if (wu?.eventBus) {
492
+ wu.eventBus.emit(event, data, options);
493
+ }
494
+ };
495
+
496
+ const on = (event, callback) => {
497
+ const wu = getWuInstance();
498
+ if (wu?.eventBus) {
499
+ const unsubscribe = wu.eventBus.on(event, callback);
500
+ subscriptions.push(unsubscribe);
501
+ return unsubscribe;
502
+ }
503
+ return () => {};
504
+ };
505
+
506
+ const once = (event, callback) => {
507
+ const wu = getWuInstance();
508
+ if (wu?.eventBus) {
509
+ return wu.eventBus.once(event, callback);
510
+ }
511
+ return () => {};
512
+ };
513
+
514
+ const off = (event, callback) => {
515
+ const wu = getWuInstance();
516
+ if (wu?.eventBus) {
517
+ wu.eventBus.off(event, callback);
518
+ }
519
+ };
520
+
521
+ // Cleanup automático
522
+ onCleanup(() => {
523
+ subscriptions.forEach(unsub => unsub());
524
+ });
525
+
526
+ return { emit, on, once, off };
527
+ }
528
+
529
+ /**
530
+ * Contexto de Wu para Solid
531
+ * Permite acceder a Wu desde cualquier componente hijo
532
+ *
533
+ * @example
534
+ * import { WuProvider, useWu } from 'wu-framework/adapters/solid';
535
+ *
536
+ * // En el root
537
+ * <WuProvider>
538
+ * <App />
539
+ * </WuProvider>
540
+ *
541
+ * // En cualquier componente hijo
542
+ * function MyComponent() {
543
+ * const wu = useWu();
544
+ * // ...
545
+ * }
546
+ */
547
+ function createWuContext() {
548
+ const { createContext, useContext } = adapterState.solid;
549
+
550
+ const WuContext = createContext(null);
551
+
552
+ function WuProvider(props) {
553
+ const wu = getWuInstance();
554
+ return WuContext.Provider({
555
+ value: wu,
556
+ children: props.children
557
+ });
558
+ }
559
+
560
+ function useWu() {
561
+ return useContext(WuContext) || getWuInstance();
562
+ }
563
+
564
+ return { WuProvider, useWu, WuContext };
565
+ }
566
+
567
+ // API pública del adapter
568
+ export const wuSolid = {
569
+ register,
570
+ createWuSlot,
571
+ createWuStore,
572
+ createWuEvent,
573
+ useWuEvents,
574
+ createWuContext,
575
+ getWuInstance,
576
+ waitForWu
577
+ };
578
+
579
+ // Named exports
580
+ export {
581
+ register,
582
+ createWuSlot,
583
+ createWuStore,
584
+ createWuEvent,
585
+ useWuEvents,
586
+ createWuContext,
587
+ getWuInstance,
588
+ waitForWu
589
+ };
590
+
591
+ export default wuSolid;