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.
- package/README.md +773 -366
- package/package.json +34 -9
- package/src/adapters/angular.d.ts +154 -0
- package/src/adapters/angular.js +642 -0
- package/src/adapters/index.js +157 -0
- package/src/adapters/lit.d.ts +120 -0
- package/src/adapters/lit.js +726 -0
- package/src/adapters/preact.d.ts +108 -0
- package/src/adapters/preact.js +665 -0
- package/src/adapters/react.d.ts +212 -0
- package/src/adapters/react.js +513 -0
- package/src/adapters/solid.d.ts +101 -0
- package/src/adapters/solid.js +591 -0
- package/src/adapters/svelte.d.ts +166 -0
- package/src/adapters/svelte.js +803 -0
- package/src/adapters/vanilla.d.ts +179 -0
- package/src/adapters/vanilla.js +791 -0
- package/src/adapters/vue.d.ts +299 -0
- package/src/adapters/vue.js +570 -0
package/README.md
CHANGED
|
@@ -9,11 +9,13 @@ Wu Framework es la librería de microfrontends más simple y poderosa del mercad
|
|
|
9
9
|
| Característica | Module Federation | qiankun | **Wu Framework** |
|
|
10
10
|
|---|---|---|---|
|
|
11
11
|
| **Setup** | Webpack config complejo | Configuración manual | **Zero config** |
|
|
12
|
-
| **Framework Support** | React-first | Multi framework | **
|
|
12
|
+
| **Framework Support** | React-first | Multi framework | **8 frameworks nativos** |
|
|
13
13
|
| **Bundler Required** | Solo Webpack 5 | Agnóstico pero complejo | **Ninguno** |
|
|
14
14
|
| **CSS Isolation** | Básico | CSS hacks | **Shadow DOM nativo** |
|
|
15
|
+
| **JS Isolation** | ❌ | Proxy básico | **Proxy Sandbox completo** |
|
|
15
16
|
| **Runtime Config** | ❌ | Limitado | **✅ Completamente dinámico** |
|
|
16
|
-
| **
|
|
17
|
+
| **Self-Healing** | ❌ | ❌ | **✅ Auto-recovery** |
|
|
18
|
+
| **API Complexity** | Alta | Media | **1 línea de código** |
|
|
17
19
|
|
|
18
20
|
## 🎯 Instalación
|
|
19
21
|
|
|
@@ -23,15 +25,34 @@ npm install wu-framework
|
|
|
23
25
|
|
|
24
26
|
**¡Y ya está!** Sin webpack config, sin plugins, sin configuración.
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🎨 Frameworks Soportados
|
|
31
|
+
|
|
32
|
+
Wu Framework incluye **adapters nativos** para los frameworks más populares:
|
|
33
|
+
|
|
34
|
+
| Framework | Adapter | Registro |
|
|
35
|
+
|-----------|---------|----------|
|
|
36
|
+
| ⚛️ React | `wuReact` | `wuReact.register('app', App)` |
|
|
37
|
+
| 💚 Vue | `wuVue` | `wuVue.register('app', App)` |
|
|
38
|
+
| 🅰️ Angular | `wuAngular` | `wuAngular.register('app', AppModule)` |
|
|
39
|
+
| 🧡 Svelte | `wuSvelte` | `wuSvelte.register('app', App)` |
|
|
40
|
+
| ⚡ Preact | `wuPreact` | `wuPreact.register('app', App)` |
|
|
41
|
+
| 💎 Solid.js | `wuSolid` | `wuSolid.register('app', App)` |
|
|
42
|
+
| 🔥 Lit | `wuLit` | `wuLit.register('app', MyElement)` |
|
|
43
|
+
| 📦 Vanilla JS | `wuVanilla` | `wuVanilla.register('app', config)` |
|
|
44
|
+
|
|
45
|
+
---
|
|
27
46
|
|
|
28
|
-
|
|
47
|
+
## 🔥 Quick Start
|
|
48
|
+
|
|
49
|
+
### 1. Shell (Host Application)
|
|
29
50
|
|
|
30
51
|
```js
|
|
31
52
|
import { wu } from 'wu-framework';
|
|
32
53
|
|
|
33
|
-
// Inicializar
|
|
34
|
-
wu.init({
|
|
54
|
+
// Inicializar y montar microfrontends
|
|
55
|
+
await wu.init({
|
|
35
56
|
apps: [
|
|
36
57
|
{ name: 'header', url: 'http://localhost:3001' },
|
|
37
58
|
{ name: 'sidebar', url: 'http://localhost:3002' },
|
|
@@ -39,521 +60,907 @@ wu.init({
|
|
|
39
60
|
]
|
|
40
61
|
});
|
|
41
62
|
|
|
42
|
-
|
|
43
|
-
wu.mount('
|
|
44
|
-
wu.mount('
|
|
45
|
-
wu.mount('content', '#content-container');
|
|
63
|
+
await wu.mount('header', '#header-container');
|
|
64
|
+
await wu.mount('sidebar', '#sidebar-container');
|
|
65
|
+
await wu.mount('content', '#content-container');
|
|
46
66
|
```
|
|
47
67
|
|
|
48
|
-
### Micro App (
|
|
68
|
+
### 2. Micro App (1 línea de código)
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
**React:**
|
|
71
|
+
```tsx
|
|
72
|
+
import { wuReact } from 'wu-framework/adapters/react';
|
|
73
|
+
import App from './App';
|
|
52
74
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
mount: (container) => {
|
|
56
|
-
ReactDOM.render(<HeaderApp />, container);
|
|
57
|
-
},
|
|
58
|
-
unmount: (container) => {
|
|
59
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
75
|
+
wuReact.register('header', App);
|
|
76
|
+
```
|
|
62
77
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
unmount: (container) => {
|
|
69
|
-
// Vue cleanup
|
|
70
|
-
}
|
|
71
|
-
});
|
|
78
|
+
**Vue:**
|
|
79
|
+
```ts
|
|
80
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
81
|
+
import App from './App.vue';
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
wu.define('content', {
|
|
75
|
-
mount: (container) => {
|
|
76
|
-
container.innerHTML = `
|
|
77
|
-
<h1>Content App</h1>
|
|
78
|
-
<p>Pure vanilla JavaScript!</p>
|
|
79
|
-
`;
|
|
80
|
-
},
|
|
81
|
-
unmount: (container) => {
|
|
82
|
-
container.innerHTML = '';
|
|
83
|
-
}
|
|
84
|
-
});
|
|
83
|
+
wuVue.register('sidebar', App);
|
|
85
84
|
```
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"name": "header-app",
|
|
94
|
-
"entry": "index.js",
|
|
95
|
-
"wu": {
|
|
96
|
-
"exports": {
|
|
97
|
-
"HeaderComponent": "./components/Header",
|
|
98
|
-
"useAuth": "./hooks/useAuth"
|
|
99
|
-
},
|
|
100
|
-
"imports": [
|
|
101
|
-
"shared.Button",
|
|
102
|
-
"shared.Theme"
|
|
103
|
-
],
|
|
104
|
-
"routes": ["/header", "/nav"],
|
|
105
|
-
"permissions": ["localStorage", "eventBus"]
|
|
106
|
-
}
|
|
107
|
-
}
|
|
86
|
+
**Angular:**
|
|
87
|
+
```ts
|
|
88
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
89
|
+
import { AppModule } from './app/app.module';
|
|
90
|
+
|
|
91
|
+
wuAngular.register('content', AppModule);
|
|
108
92
|
```
|
|
109
93
|
|
|
110
|
-
|
|
94
|
+
**¡Eso es todo!** El adapter se encarga de todo: detección de contexto, modo standalone, cleanup automático.
|
|
111
95
|
|
|
112
|
-
|
|
113
|
-
// Exportar componentes
|
|
114
|
-
wu.define('shared', {
|
|
115
|
-
mount: (container) => {
|
|
116
|
-
// App shared con componentes reutilizables
|
|
117
|
-
}
|
|
118
|
-
});
|
|
96
|
+
---
|
|
119
97
|
|
|
120
|
-
|
|
121
|
-
const Button = await wu.use('shared.Button');
|
|
122
|
-
const Theme = await wu.use('shared.Theme');
|
|
123
|
-
```
|
|
98
|
+
## ⚛️ React Adapter
|
|
124
99
|
|
|
125
|
-
|
|
100
|
+
### Registro Básico
|
|
126
101
|
|
|
127
|
-
|
|
102
|
+
```tsx
|
|
103
|
+
import { wuReact } from 'wu-framework/adapters/react';
|
|
104
|
+
import App from './App';
|
|
128
105
|
|
|
106
|
+
wuReact.register('my-app', App);
|
|
129
107
|
```
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
108
|
+
|
|
109
|
+
### Con Opciones
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
wuReact.register('my-app', App, {
|
|
113
|
+
strictMode: true, // Envolver en React.StrictMode
|
|
114
|
+
props: { theme: 'dark' }, // Props iniciales
|
|
115
|
+
standalone: true, // Ejecutar independiente si Wu no está
|
|
116
|
+
standaloneContainer: '#root',
|
|
117
|
+
onMount: (container) => console.log('Mounted!'),
|
|
118
|
+
onUnmount: (container) => console.log('Unmounted!')
|
|
119
|
+
});
|
|
141
120
|
```
|
|
142
121
|
|
|
143
|
-
###
|
|
122
|
+
### Hooks para React
|
|
144
123
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
- ✅ **Performance nativo** - Sin overhead de hacks CSS
|
|
149
|
-
- ✅ **Debugging fácil** - Cada app en su propio DevTools tree
|
|
124
|
+
```tsx
|
|
125
|
+
import React from 'react';
|
|
126
|
+
import { createUseWuEvents, createUseWuStore } from 'wu-framework/adapters/react';
|
|
150
127
|
|
|
151
|
-
|
|
128
|
+
const useWuEvents = createUseWuEvents(React);
|
|
129
|
+
const useWuStore = createUseWuStore(React);
|
|
152
130
|
|
|
153
|
-
|
|
131
|
+
function MyComponent() {
|
|
132
|
+
const { emit, on } = useWuEvents();
|
|
133
|
+
const { state, setState } = useWuStore('user');
|
|
134
|
+
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
const unsub = on('cart:updated', (data) => {
|
|
137
|
+
console.log('Cart updated:', data);
|
|
138
|
+
});
|
|
139
|
+
return unsub;
|
|
140
|
+
}, [on]);
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<button onClick={() => emit('user:logout')}>
|
|
144
|
+
Logout {state?.name}
|
|
145
|
+
</button>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Cargar Microfrontends en React (Shell)
|
|
154
151
|
|
|
155
152
|
```tsx
|
|
156
|
-
// header-app/src/index.tsx
|
|
157
153
|
import React from 'react';
|
|
158
|
-
import
|
|
159
|
-
|
|
154
|
+
import { createWuSlot } from 'wu-framework/adapters/react';
|
|
155
|
+
|
|
156
|
+
const WuSlot = createWuSlot(React);
|
|
157
|
+
|
|
158
|
+
function Shell() {
|
|
159
|
+
return (
|
|
160
|
+
<div>
|
|
161
|
+
<WuSlot
|
|
162
|
+
name="header"
|
|
163
|
+
url="http://localhost:3001"
|
|
164
|
+
onLoad={() => console.log('Header loaded!')}
|
|
165
|
+
onError={(err) => console.error(err)}
|
|
166
|
+
/>
|
|
167
|
+
<WuSlot name="content" url="http://localhost:3002" />
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
```
|
|
160
172
|
|
|
161
|
-
|
|
162
|
-
<header className="app-header">
|
|
163
|
-
<h1>Header App (React)</h1>
|
|
164
|
-
</header>
|
|
165
|
-
);
|
|
173
|
+
---
|
|
166
174
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
## 💚 Vue Adapter
|
|
176
|
+
|
|
177
|
+
### Registro Básico
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
181
|
+
import App from './App.vue';
|
|
182
|
+
|
|
183
|
+
wuVue.register('my-app', App);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Con Plugins (Pinia, Router, etc.)
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
190
|
+
import { createPinia } from 'pinia';
|
|
191
|
+
import router from './router';
|
|
192
|
+
import App from './App.vue';
|
|
193
|
+
|
|
194
|
+
wuVue.register('my-app', App, {
|
|
195
|
+
setup: (app) => {
|
|
196
|
+
app.use(createPinia());
|
|
197
|
+
app.use(router);
|
|
173
198
|
}
|
|
174
199
|
});
|
|
175
200
|
```
|
|
176
201
|
|
|
177
|
-
###
|
|
202
|
+
### Composables para Vue
|
|
178
203
|
|
|
179
204
|
```vue
|
|
180
|
-
|
|
205
|
+
<script setup>
|
|
206
|
+
import { onMounted, onUnmounted } from 'vue';
|
|
207
|
+
import { useWuEvents, useWuStore } from 'wu-framework/adapters/vue';
|
|
208
|
+
|
|
209
|
+
const { emit, on, cleanup } = useWuEvents();
|
|
210
|
+
const { state, setState } = useWuStore('cart');
|
|
211
|
+
|
|
212
|
+
onMounted(() => {
|
|
213
|
+
on('user:login', (data) => console.log('Login:', data));
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
onUnmounted(() => cleanup());
|
|
217
|
+
</script>
|
|
218
|
+
|
|
181
219
|
<template>
|
|
182
|
-
<
|
|
183
|
-
<
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
<li v-for="item in items" :key="item.id">
|
|
187
|
-
{{ item.name }}
|
|
188
|
-
</li>
|
|
189
|
-
</ul>
|
|
190
|
-
</nav>
|
|
191
|
-
</aside>
|
|
220
|
+
<div>
|
|
221
|
+
<p>Items: {{ state?.items?.length }}</p>
|
|
222
|
+
<button @click="emit('cart:clear')">Clear Cart</button>
|
|
223
|
+
</div>
|
|
192
224
|
</template>
|
|
225
|
+
```
|
|
193
226
|
|
|
194
|
-
|
|
195
|
-
import { ref } from 'vue';
|
|
227
|
+
### Cargar Microfrontends en Vue (Shell)
|
|
196
228
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
{ id: 3, name: 'Contact' }
|
|
201
|
-
]);
|
|
229
|
+
```vue
|
|
230
|
+
<script setup>
|
|
231
|
+
import { WuSlot } from 'wu-framework/adapters/vue';
|
|
202
232
|
</script>
|
|
233
|
+
|
|
234
|
+
<template>
|
|
235
|
+
<div>
|
|
236
|
+
<WuSlot
|
|
237
|
+
name="header"
|
|
238
|
+
url="http://localhost:3001"
|
|
239
|
+
@load="onLoad"
|
|
240
|
+
@error="onError"
|
|
241
|
+
/>
|
|
242
|
+
<WuSlot name="sidebar" url="http://localhost:3002" />
|
|
243
|
+
</div>
|
|
244
|
+
</template>
|
|
203
245
|
```
|
|
204
246
|
|
|
205
|
-
|
|
206
|
-
|
|
247
|
+
### Plugin Global
|
|
248
|
+
|
|
249
|
+
```ts
|
|
207
250
|
import { createApp } from 'vue';
|
|
208
|
-
import {
|
|
209
|
-
import App from './App.vue';
|
|
251
|
+
import { wuVuePlugin } from 'wu-framework/adapters/vue';
|
|
210
252
|
|
|
211
|
-
|
|
253
|
+
const app = createApp(App);
|
|
254
|
+
app.use(wuVuePlugin); // Registra <WuSlot> globalmente
|
|
255
|
+
```
|
|
212
256
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 🅰️ Angular Adapter
|
|
260
|
+
|
|
261
|
+
### Registro con NgModule
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
265
|
+
import { AppModule } from './app/app.module';
|
|
266
|
+
|
|
267
|
+
wuAngular.register('my-app', AppModule);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Registro con Standalone Components (Angular 14+)
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
274
|
+
import { AppComponent } from './app/app.component';
|
|
275
|
+
import { appConfig } from './app/app.config';
|
|
276
|
+
|
|
277
|
+
wuAngular.registerStandalone('my-app', AppComponent, {
|
|
278
|
+
appConfig
|
|
224
279
|
});
|
|
225
280
|
```
|
|
226
281
|
|
|
227
|
-
###
|
|
282
|
+
### Servicio para Componentes
|
|
228
283
|
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
import {
|
|
232
|
-
|
|
233
|
-
|
|
284
|
+
```ts
|
|
285
|
+
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
286
|
+
import { createWuService } from 'wu-framework/adapters/angular';
|
|
287
|
+
|
|
288
|
+
@Component({
|
|
289
|
+
selector: 'app-root',
|
|
290
|
+
template: `<button (click)="sendEvent()">Send Event</button>`
|
|
291
|
+
})
|
|
292
|
+
export class AppComponent implements OnInit, OnDestroy {
|
|
293
|
+
private wuService = createWuService();
|
|
234
294
|
|
|
235
|
-
|
|
295
|
+
ngOnInit() {
|
|
296
|
+
this.wuService.on('user:login', (data) => {
|
|
297
|
+
console.log('User logged in:', data);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
236
300
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const appElement = document.createElement('app-root');
|
|
241
|
-
container.appendChild(appElement);
|
|
301
|
+
sendEvent() {
|
|
302
|
+
this.wuService.emit('app:ready', { timestamp: Date.now() });
|
|
303
|
+
}
|
|
242
304
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
},
|
|
246
|
-
unmount: (container: HTMLElement) => {
|
|
247
|
-
if (platformRef) {
|
|
248
|
-
platformRef.destroy();
|
|
249
|
-
platformRef = null;
|
|
250
|
-
}
|
|
251
|
-
container.innerHTML = '';
|
|
305
|
+
ngOnDestroy() {
|
|
306
|
+
this.wuService.destroy();
|
|
252
307
|
}
|
|
253
|
-
}
|
|
308
|
+
}
|
|
254
309
|
```
|
|
255
310
|
|
|
256
|
-
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 🧡 Svelte Adapter
|
|
314
|
+
|
|
315
|
+
### Registro Básico
|
|
257
316
|
|
|
258
317
|
```js
|
|
259
|
-
|
|
260
|
-
import { wu } from 'wu-framework';
|
|
318
|
+
import { wuSvelte } from 'wu-framework/adapters/svelte';
|
|
261
319
|
import App from './App.svelte';
|
|
262
320
|
|
|
263
|
-
|
|
321
|
+
wuSvelte.register('my-app', App);
|
|
322
|
+
```
|
|
264
323
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
324
|
+
### Svelte 5 (con runes)
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
import { wuSvelte } from 'wu-framework/adapters/svelte';
|
|
328
|
+
import App from './App.svelte';
|
|
329
|
+
|
|
330
|
+
wuSvelte.registerSvelte5('my-app', App);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Stores Reactivos
|
|
334
|
+
|
|
335
|
+
```svelte
|
|
336
|
+
<script>
|
|
337
|
+
import { createWuStore, useWuEvents } from 'wu-framework/adapters/svelte';
|
|
338
|
+
import { onDestroy } from 'svelte';
|
|
339
|
+
|
|
340
|
+
// Store reactivo (compatible con $store)
|
|
341
|
+
const userStore = createWuStore('user');
|
|
342
|
+
|
|
343
|
+
// Eventos
|
|
344
|
+
const { emit, on, cleanup } = useWuEvents();
|
|
345
|
+
|
|
346
|
+
on('cart:updated', (data) => {
|
|
347
|
+
console.log('Cart updated:', data);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
onDestroy(cleanup);
|
|
351
|
+
</script>
|
|
352
|
+
|
|
353
|
+
<p>Hello, {$userStore?.name}</p>
|
|
354
|
+
<button on:click={() => emit('user:logout')}>Logout</button>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## ⚡ Preact Adapter
|
|
360
|
+
|
|
361
|
+
### Registro Básico
|
|
362
|
+
|
|
363
|
+
```jsx
|
|
364
|
+
import { wuPreact } from 'wu-framework/adapters/preact';
|
|
365
|
+
import App from './App';
|
|
366
|
+
|
|
367
|
+
wuPreact.register('my-app', App);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Compatible con React (preact/compat)
|
|
371
|
+
|
|
372
|
+
```jsx
|
|
373
|
+
import { wuPreact } from 'wu-framework/adapters/preact';
|
|
374
|
+
import App from './App'; // Componente estilo React
|
|
375
|
+
|
|
376
|
+
wuPreact.registerCompat('my-app', App);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Hooks
|
|
380
|
+
|
|
381
|
+
```jsx
|
|
382
|
+
import { h } from 'preact';
|
|
383
|
+
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
|
|
384
|
+
import { createUseWuEvents, createUseWuStore } from 'wu-framework/adapters/preact';
|
|
385
|
+
|
|
386
|
+
const useWuEvents = createUseWuEvents({ useCallback, useEffect, useRef });
|
|
387
|
+
const useWuStore = createUseWuStore({ useState, useCallback, useEffect });
|
|
388
|
+
|
|
389
|
+
function MyComponent() {
|
|
390
|
+
const { emit, on } = useWuEvents();
|
|
391
|
+
const { state } = useWuStore('user');
|
|
392
|
+
|
|
393
|
+
return <div>Hello {state?.name}</div>;
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## 💎 Solid.js Adapter
|
|
400
|
+
|
|
401
|
+
### Registro Básico
|
|
402
|
+
|
|
403
|
+
```jsx
|
|
404
|
+
import { wuSolid } from 'wu-framework/adapters/solid';
|
|
405
|
+
import App from './App';
|
|
406
|
+
|
|
407
|
+
wuSolid.register('my-app', App);
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Stores Reactivos
|
|
411
|
+
|
|
412
|
+
```jsx
|
|
413
|
+
import { createWuStore, createWuEvent } from 'wu-framework/adapters/solid';
|
|
414
|
+
|
|
415
|
+
function MyComponent() {
|
|
416
|
+
// Store reactivo
|
|
417
|
+
const [user, setUser] = createWuStore('user');
|
|
418
|
+
|
|
419
|
+
// Eventos reactivos
|
|
420
|
+
const lastEvent = createWuEvent('notification:*');
|
|
421
|
+
|
|
422
|
+
return (
|
|
423
|
+
<div>
|
|
424
|
+
<p>Hello, {user()?.name}</p>
|
|
425
|
+
<Show when={lastEvent()}>
|
|
426
|
+
<p>Last notification: {lastEvent()?.data}</p>
|
|
427
|
+
</Show>
|
|
428
|
+
<button onClick={() => setUser('name', 'John')}>
|
|
429
|
+
Set Name
|
|
430
|
+
</button>
|
|
431
|
+
</div>
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 🔥 Lit Adapter (Web Components)
|
|
439
|
+
|
|
440
|
+
### Registro con LitElement
|
|
441
|
+
|
|
442
|
+
```js
|
|
443
|
+
import { LitElement, html, css } from 'lit';
|
|
444
|
+
import { wuLit } from 'wu-framework/adapters/lit';
|
|
445
|
+
|
|
446
|
+
class MyApp extends LitElement {
|
|
447
|
+
static styles = css`
|
|
448
|
+
:host { display: block; padding: 16px; }
|
|
449
|
+
`;
|
|
450
|
+
|
|
451
|
+
render() {
|
|
452
|
+
return html`<h1>Hello from Lit!</h1>`;
|
|
279
453
|
}
|
|
280
|
-
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
wuLit.register('my-app', MyApp);
|
|
281
457
|
```
|
|
282
458
|
|
|
283
|
-
|
|
459
|
+
### Con WuMixin
|
|
284
460
|
|
|
285
|
-
|
|
461
|
+
```js
|
|
462
|
+
import { LitElement, html } from 'lit';
|
|
463
|
+
import { WuMixin } from 'wu-framework/adapters/lit';
|
|
464
|
+
|
|
465
|
+
class MyApp extends WuMixin(LitElement) {
|
|
466
|
+
connectedCallback() {
|
|
467
|
+
super.connectedCallback();
|
|
468
|
+
|
|
469
|
+
// Usar eventos de Wu
|
|
470
|
+
this.wuOn('user:login', (data) => {
|
|
471
|
+
console.log('User logged in:', data);
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
handleClick() {
|
|
476
|
+
this.wuEmit('button:clicked', { id: 'my-button' });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
render() {
|
|
480
|
+
return html`
|
|
481
|
+
<button @click=${this.handleClick}>Click me</button>
|
|
482
|
+
`;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
wuLit.register('my-app', MyApp);
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Web Component Vanilla (sin Lit)
|
|
286
490
|
|
|
287
491
|
```js
|
|
288
|
-
import {
|
|
492
|
+
import { wuLit } from 'wu-framework/adapters/lit';
|
|
493
|
+
|
|
494
|
+
class MyComponent extends HTMLElement {
|
|
495
|
+
connectedCallback() {
|
|
496
|
+
this.attachShadow({ mode: 'open' });
|
|
497
|
+
this.shadowRoot.innerHTML = '<h1>Hello Web Component!</h1>';
|
|
498
|
+
}
|
|
499
|
+
}
|
|
289
500
|
|
|
290
|
-
|
|
291
|
-
|
|
501
|
+
wuLit.registerWebComponent('my-component', MyComponent);
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
---
|
|
292
505
|
|
|
293
|
-
|
|
294
|
-
await wu.mount('appName', '#container');
|
|
506
|
+
## 📦 Vanilla JS Adapter
|
|
295
507
|
|
|
296
|
-
|
|
297
|
-
await wu.unmount('appName');
|
|
508
|
+
### Registro con Objeto
|
|
298
509
|
|
|
299
|
-
|
|
300
|
-
|
|
510
|
+
```js
|
|
511
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
301
512
|
|
|
302
|
-
|
|
303
|
-
|
|
513
|
+
wuVanilla.register('my-app', {
|
|
514
|
+
state: { count: 0 },
|
|
304
515
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
516
|
+
render: (container, state) => {
|
|
517
|
+
container.innerHTML = `
|
|
518
|
+
<div>
|
|
519
|
+
<h1>Count: ${state.count}</h1>
|
|
520
|
+
<button id="increment">+</button>
|
|
521
|
+
</div>
|
|
522
|
+
`;
|
|
523
|
+
container.querySelector('#increment').onclick = () => {
|
|
524
|
+
state.count++;
|
|
525
|
+
// Re-render manual
|
|
526
|
+
};
|
|
527
|
+
},
|
|
308
528
|
|
|
309
|
-
|
|
310
|
-
|
|
529
|
+
destroy: (container) => {
|
|
530
|
+
container.innerHTML = '';
|
|
531
|
+
}
|
|
532
|
+
});
|
|
311
533
|
```
|
|
312
534
|
|
|
313
|
-
###
|
|
535
|
+
### Registro con Clase
|
|
314
536
|
|
|
315
537
|
```js
|
|
316
|
-
import {
|
|
538
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
317
539
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
540
|
+
class TodoApp {
|
|
541
|
+
constructor(container) {
|
|
542
|
+
this.container = container;
|
|
543
|
+
this.todos = [];
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
render() {
|
|
547
|
+
this.container.innerHTML = `
|
|
548
|
+
<ul>
|
|
549
|
+
${this.todos.map(t => `<li>${t}</li>`).join('')}
|
|
550
|
+
</ul>
|
|
551
|
+
<input type="text" id="input" />
|
|
552
|
+
<button id="add">Add</button>
|
|
553
|
+
`;
|
|
554
|
+
|
|
555
|
+
this.container.querySelector('#add').onclick = () => {
|
|
556
|
+
const input = this.container.querySelector('#input');
|
|
557
|
+
this.todos.push(input.value);
|
|
558
|
+
this.render();
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
destroy() {
|
|
563
|
+
this.container.innerHTML = '';
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
wuVanilla.registerClass('todo-app', TodoApp);
|
|
323
568
|
```
|
|
324
569
|
|
|
325
|
-
###
|
|
570
|
+
### Registro con Template
|
|
326
571
|
|
|
327
572
|
```js
|
|
328
|
-
import {
|
|
573
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
329
574
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
575
|
+
wuVanilla.registerTemplate('header', `
|
|
576
|
+
<header>
|
|
577
|
+
<h1>My Header</h1>
|
|
578
|
+
<nav>
|
|
579
|
+
<a href="/">Home</a>
|
|
580
|
+
<a href="/about">About</a>
|
|
581
|
+
</nav>
|
|
582
|
+
</header>
|
|
583
|
+
`);
|
|
584
|
+
|
|
585
|
+
// Template dinámico
|
|
586
|
+
wuVanilla.registerTemplate('greeting',
|
|
587
|
+
(data) => `<h1>Hello, ${data.name}!</h1>`,
|
|
588
|
+
{ data: { name: 'World' } }
|
|
589
|
+
);
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Componente Reactivo
|
|
593
|
+
|
|
594
|
+
```js
|
|
595
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
596
|
+
|
|
597
|
+
const Counter = wuVanilla.createComponent({
|
|
598
|
+
state: { count: 0 },
|
|
599
|
+
|
|
600
|
+
template: (state) => `
|
|
601
|
+
<div>
|
|
602
|
+
<h1>Count: ${state.count}</h1>
|
|
603
|
+
<button data-action="increment">+</button>
|
|
604
|
+
<button data-action="decrement">-</button>
|
|
605
|
+
</div>
|
|
606
|
+
`,
|
|
607
|
+
|
|
608
|
+
actions: {
|
|
609
|
+
increment: (state) => ({ count: state.count + 1 }),
|
|
610
|
+
decrement: (state) => ({ count: state.count - 1 })
|
|
611
|
+
}
|
|
612
|
+
});
|
|
333
613
|
|
|
334
|
-
|
|
335
|
-
await dev.reload('appName');
|
|
614
|
+
wuVanilla.register('counter', Counter);
|
|
336
615
|
```
|
|
337
616
|
|
|
338
|
-
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## 📡 Event Bus
|
|
339
620
|
|
|
340
|
-
|
|
621
|
+
Sistema de eventos para comunicación entre microfrontends con soporte para wildcards y replay.
|
|
341
622
|
|
|
342
623
|
```js
|
|
343
|
-
import {
|
|
624
|
+
import { emit, on, once, off, replayEvents } from 'wu-framework';
|
|
625
|
+
|
|
626
|
+
// Emitir evento
|
|
627
|
+
emit('user:login', { userId: 123, name: 'John' });
|
|
628
|
+
|
|
629
|
+
// Suscribirse a evento
|
|
630
|
+
const unsubscribe = on('user:login', (event) => {
|
|
631
|
+
console.log('User logged in:', event.data);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// Suscribirse con wildcards
|
|
635
|
+
on('user:*', (event) => {
|
|
636
|
+
console.log('User event:', event.name, event.data);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// Suscribirse una sola vez
|
|
640
|
+
once('app:ready', (event) => {
|
|
641
|
+
console.log('App is ready!');
|
|
642
|
+
});
|
|
344
643
|
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
{ name: 'sidebar', port: 3002 }
|
|
349
|
-
]));
|
|
644
|
+
// Desuscribirse
|
|
645
|
+
off('user:login', callback);
|
|
646
|
+
unsubscribe(); // También funciona
|
|
350
647
|
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
]));
|
|
648
|
+
// Replay eventos del historial
|
|
649
|
+
replayEvents('user:*', (event) => {
|
|
650
|
+
console.log('Past event:', event);
|
|
651
|
+
});
|
|
356
652
|
```
|
|
357
653
|
|
|
358
|
-
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## 🗄️ Store (State Management)
|
|
657
|
+
|
|
658
|
+
Store de alto rendimiento con pattern matching.
|
|
359
659
|
|
|
360
660
|
```js
|
|
361
|
-
import {
|
|
661
|
+
import {
|
|
662
|
+
getState,
|
|
663
|
+
setState,
|
|
664
|
+
onStateChange,
|
|
665
|
+
batchState,
|
|
666
|
+
clearState,
|
|
667
|
+
getStoreMetrics
|
|
668
|
+
} from 'wu-framework';
|
|
669
|
+
|
|
670
|
+
// Establecer estado (dot notation)
|
|
671
|
+
setState('user.name', 'John');
|
|
672
|
+
setState('user.preferences.theme', 'dark');
|
|
673
|
+
|
|
674
|
+
// Obtener estado
|
|
675
|
+
const name = getState('user.name'); // 'John'
|
|
676
|
+
const user = getState('user'); // { name: 'John', preferences: { theme: 'dark' } }
|
|
677
|
+
|
|
678
|
+
// Suscribirse a cambios
|
|
679
|
+
const unsubscribe = onStateChange('user.name', (value, path) => {
|
|
680
|
+
console.log(`${path} changed to:`, value);
|
|
681
|
+
});
|
|
362
682
|
|
|
363
|
-
//
|
|
364
|
-
|
|
365
|
-
console.log(
|
|
683
|
+
// Suscribirse con wildcards
|
|
684
|
+
onStateChange('user.*', (value, path) => {
|
|
685
|
+
console.log(`User property changed: ${path} =`, value);
|
|
366
686
|
});
|
|
367
687
|
|
|
368
|
-
|
|
369
|
-
|
|
688
|
+
// Batch updates (más eficiente)
|
|
689
|
+
batchState({
|
|
690
|
+
'user.name': 'Jane',
|
|
691
|
+
'user.email': 'jane@example.com',
|
|
692
|
+
'settings.notifications': true
|
|
370
693
|
});
|
|
694
|
+
|
|
695
|
+
// Métricas de performance
|
|
696
|
+
const metrics = getStoreMetrics();
|
|
371
697
|
```
|
|
372
698
|
|
|
373
|
-
|
|
699
|
+
---
|
|
374
700
|
|
|
375
|
-
|
|
701
|
+
## 🔌 Sistema de Plugins
|
|
376
702
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
- ✅ **Framework agnostic real** - No solo React
|
|
380
|
-
- ✅ **API más simple** - Menos boilerplate
|
|
703
|
+
```js
|
|
704
|
+
import { usePlugin, createPlugin, uninstallPlugin } from 'wu-framework';
|
|
381
705
|
|
|
382
|
-
|
|
706
|
+
const analyticsPlugin = createPlugin({
|
|
707
|
+
name: 'analytics',
|
|
708
|
+
permissions: ['events', 'store'],
|
|
383
709
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
710
|
+
install: (api, options) => {
|
|
711
|
+
api.on('app:mounted', (event) => {
|
|
712
|
+
trackEvent('mount', event.data.appName);
|
|
713
|
+
});
|
|
714
|
+
},
|
|
388
715
|
|
|
389
|
-
|
|
716
|
+
beforeMount: async (context) => {
|
|
717
|
+
console.log('Before mount:', context.appName);
|
|
718
|
+
},
|
|
390
719
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
720
|
+
afterMount: async (context) => {
|
|
721
|
+
console.log('Mounted in', context.mountTime, 'ms');
|
|
722
|
+
}
|
|
723
|
+
});
|
|
395
724
|
|
|
396
|
-
|
|
725
|
+
usePlugin(analyticsPlugin, { trackingId: 'UA-123456' });
|
|
726
|
+
```
|
|
397
727
|
|
|
398
|
-
|
|
728
|
+
---
|
|
399
729
|
|
|
400
|
-
|
|
730
|
+
## 🪝 Lifecycle Hooks
|
|
401
731
|
|
|
402
732
|
```js
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
733
|
+
import { useHook, createGuardHook, createTimedHook } from 'wu-framework';
|
|
734
|
+
|
|
735
|
+
// Hook básico
|
|
736
|
+
useHook('beforeMount', async (context, next) => {
|
|
737
|
+
console.log('Before mounting:', context.appName);
|
|
738
|
+
await next();
|
|
739
|
+
}, { name: 'my-hook', priority: 10 });
|
|
740
|
+
|
|
741
|
+
// Guard hook (puede cancelar)
|
|
742
|
+
const authGuard = createGuardHook(async (ctx) => {
|
|
743
|
+
return await isUserAuthenticated();
|
|
744
|
+
});
|
|
745
|
+
useHook('beforeMount', authGuard);
|
|
746
|
+
|
|
747
|
+
// Hook con timeout
|
|
748
|
+
const timedHook = createTimedHook(async (ctx) => {
|
|
749
|
+
await slowOperation();
|
|
750
|
+
}, 5000);
|
|
408
751
|
```
|
|
409
752
|
|
|
410
|
-
###
|
|
753
|
+
### Fases Disponibles
|
|
754
|
+
|
|
755
|
+
| Fase | Descripción |
|
|
756
|
+
|------|-------------|
|
|
757
|
+
| `beforeInit` | Antes de inicializar framework |
|
|
758
|
+
| `afterInit` | Después de inicializar |
|
|
759
|
+
| `beforeLoad` | Antes de cargar una app |
|
|
760
|
+
| `afterLoad` | Después de cargar |
|
|
761
|
+
| `beforeMount` | Antes de montar |
|
|
762
|
+
| `afterMount` | Después de montar |
|
|
763
|
+
| `beforeUnmount` | Antes de desmontar |
|
|
764
|
+
| `afterUnmount` | Después de desmontar |
|
|
765
|
+
|
|
766
|
+
---
|
|
767
|
+
|
|
768
|
+
## 🎯 Loading Strategies
|
|
411
769
|
|
|
412
770
|
```js
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
771
|
+
await wu.init({
|
|
772
|
+
apps: [
|
|
773
|
+
{ name: 'header', url: '...', strategy: 'lazy' }, // Default
|
|
774
|
+
{ name: 'sidebar', url: '...', strategy: 'eager' }, // Precarga
|
|
775
|
+
{ name: 'footer', url: '...', strategy: 'preload' }, // <link prefetch>
|
|
776
|
+
{ name: 'analytics', url: '...', strategy: 'idle' } // requestIdleCallback
|
|
777
|
+
]
|
|
778
|
+
});
|
|
418
779
|
```
|
|
419
780
|
|
|
420
|
-
|
|
781
|
+
---
|
|
421
782
|
|
|
422
|
-
|
|
423
|
-
<!-- Funciona directamente en el browser -->
|
|
424
|
-
<script type="module">
|
|
425
|
-
import { wu } from './node_modules/wu-framework/src/index.js';
|
|
783
|
+
## ⚡ Performance Monitoring
|
|
426
784
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
785
|
+
```js
|
|
786
|
+
import {
|
|
787
|
+
startMeasure,
|
|
788
|
+
endMeasure,
|
|
789
|
+
getPerformanceMetrics,
|
|
790
|
+
generatePerformanceReport
|
|
791
|
+
} from 'wu-framework';
|
|
792
|
+
|
|
793
|
+
// Medición personalizada
|
|
794
|
+
startMeasure('data-fetch', 'my-app');
|
|
795
|
+
await fetchData();
|
|
796
|
+
const duration = endMeasure('data-fetch', 'my-app');
|
|
797
|
+
|
|
798
|
+
// Reporte completo
|
|
799
|
+
const report = generatePerformanceReport();
|
|
431
800
|
```
|
|
432
801
|
|
|
433
|
-
|
|
802
|
+
---
|
|
434
803
|
|
|
435
|
-
|
|
804
|
+
## 🛡️ Error Handling
|
|
436
805
|
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
806
|
+
```js
|
|
807
|
+
import { registerErrorHandler, configureErrorBoundary } from 'wu-framework';
|
|
808
|
+
|
|
809
|
+
registerErrorHandler({
|
|
810
|
+
name: 'api-error',
|
|
811
|
+
canHandle: (error) => error.message.includes('API'),
|
|
812
|
+
handle: async (error, context) => {
|
|
813
|
+
if (context.retryCount < 3) {
|
|
814
|
+
return { recovered: true, action: 'retry' };
|
|
815
|
+
}
|
|
816
|
+
return { recovered: false, action: 'fallback' };
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
configureErrorBoundary({
|
|
821
|
+
maxRetries: 3,
|
|
822
|
+
retryDelay: 1000,
|
|
823
|
+
showErrorUI: true
|
|
824
|
+
});
|
|
442
825
|
```
|
|
443
826
|
|
|
827
|
+
---
|
|
828
|
+
|
|
829
|
+
## 🎯 API Simplificada (wu.app)
|
|
830
|
+
|
|
444
831
|
```js
|
|
445
|
-
// index.js
|
|
446
832
|
import { wu } from 'wu-framework';
|
|
447
833
|
|
|
448
|
-
wu.
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
{ name: 'content', url: 'http://localhost:3002' }
|
|
452
|
-
]
|
|
834
|
+
const header = wu.app('header', {
|
|
835
|
+
url: 'http://localhost:3001',
|
|
836
|
+
container: '#header-container'
|
|
453
837
|
});
|
|
454
838
|
|
|
455
|
-
|
|
456
|
-
|
|
839
|
+
await header.mount();
|
|
840
|
+
console.log(header.isMounted); // true
|
|
841
|
+
|
|
842
|
+
await header.unmount();
|
|
843
|
+
await header.remount();
|
|
844
|
+
await header.reload(); // Limpia cache
|
|
845
|
+
|
|
846
|
+
const health = await header.verify();
|
|
847
|
+
await header.destroy();
|
|
457
848
|
```
|
|
458
849
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
<html>
|
|
463
|
-
<head>
|
|
464
|
-
<title>Wu Framework Container</title>
|
|
465
|
-
</head>
|
|
466
|
-
<body>
|
|
467
|
-
<div id="header"></div>
|
|
468
|
-
<div id="content"></div>
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## 🏗️ Arquitectura
|
|
469
853
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
854
|
+
```
|
|
855
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
856
|
+
│ HOST APPLICATION │
|
|
857
|
+
├─────────────────────────────────────────────────────────────┤
|
|
858
|
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
|
|
859
|
+
│ │ #shadow-root │ │ #shadow-root │ │ #shadow-root│ │
|
|
860
|
+
│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌─────────┐ │ │
|
|
861
|
+
│ │ │ Header │ │ │ │ Sidebar │ │ │ │ Content │ │ │
|
|
862
|
+
│ │ │ (React) │ │ │ │ (Vue) │ │ │ │(Angular)│ │ │
|
|
863
|
+
│ │ └───────────┘ │ │ └───────────┘ │ │ └─────────┘ │ │
|
|
864
|
+
│ │ CSS Isolated │ │ CSS Isolated │ │ CSS Isolated│ │
|
|
865
|
+
│ │ JS Sandboxed │ │ JS Sandboxed │ │ JS Sandboxed│ │
|
|
866
|
+
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
|
|
867
|
+
├─────────────────────────────────────────────────────────────┤
|
|
868
|
+
│ WU FRAMEWORK CORE │
|
|
869
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
|
|
870
|
+
│ │ EventBus │ │ Store │ │ Plugins │ │ Sandbox Pool │ │
|
|
871
|
+
│ └──────────┘ └──────────┘ └──────────┘ └────────────────┘ │
|
|
872
|
+
└─────────────────────────────────────────────────────────────┘
|
|
473
873
|
```
|
|
474
874
|
|
|
475
|
-
|
|
875
|
+
---
|
|
476
876
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
877
|
+
## 📋 API Reference
|
|
878
|
+
|
|
879
|
+
### Core
|
|
880
|
+
|
|
881
|
+
```js
|
|
882
|
+
import { wu, init, mount, unmount, destroy, getStats } from 'wu-framework';
|
|
482
883
|
```
|
|
483
884
|
|
|
885
|
+
### Event Bus
|
|
886
|
+
|
|
484
887
|
```js
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
import ReactDOM from 'react-dom';
|
|
488
|
-
import { wu } from 'wu-framework';
|
|
888
|
+
import { emit, on, once, off, replayEvents, getEventBusStats } from 'wu-framework';
|
|
889
|
+
```
|
|
489
890
|
|
|
490
|
-
|
|
491
|
-
<header style={{ background: '#f0f0f0', padding: '1rem' }}>
|
|
492
|
-
<h1>My Header App</h1>
|
|
493
|
-
</header>
|
|
494
|
-
);
|
|
891
|
+
### Store
|
|
495
892
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
ReactDOM.render(<HeaderApp />, container);
|
|
499
|
-
},
|
|
500
|
-
unmount: (container) => {
|
|
501
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
502
|
-
}
|
|
503
|
-
});
|
|
893
|
+
```js
|
|
894
|
+
import { getState, setState, onStateChange, batchState, clearState } from 'wu-framework';
|
|
504
895
|
```
|
|
505
896
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
"entry": "index.js",
|
|
511
|
-
"wu": {
|
|
512
|
-
"exports": {
|
|
513
|
-
"HeaderComponent": "./components/Header"
|
|
514
|
-
},
|
|
515
|
-
"imports": [],
|
|
516
|
-
"routes": ["/header"]
|
|
517
|
-
}
|
|
518
|
-
}
|
|
897
|
+
### Plugins & Hooks
|
|
898
|
+
|
|
899
|
+
```js
|
|
900
|
+
import { usePlugin, createPlugin, useHook, removeHook } from 'wu-framework';
|
|
519
901
|
```
|
|
520
902
|
|
|
521
|
-
###
|
|
903
|
+
### Performance & Errors
|
|
522
904
|
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
905
|
+
```js
|
|
906
|
+
import { startMeasure, endMeasure, generatePerformanceReport } from 'wu-framework';
|
|
907
|
+
import { registerErrorHandler, configureErrorBoundary } from 'wu-framework';
|
|
908
|
+
```
|
|
527
909
|
|
|
528
|
-
|
|
529
|
-
cd header-app
|
|
530
|
-
npx serve -p 3001
|
|
910
|
+
### Adapters
|
|
531
911
|
|
|
532
|
-
|
|
912
|
+
```js
|
|
913
|
+
import { wuReact } from 'wu-framework/adapters/react';
|
|
914
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
915
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
916
|
+
import { wuSvelte } from 'wu-framework/adapters/svelte';
|
|
917
|
+
import { wuPreact } from 'wu-framework/adapters/preact';
|
|
918
|
+
import { wuSolid } from 'wu-framework/adapters/solid';
|
|
919
|
+
import { wuLit } from 'wu-framework/adapters/lit';
|
|
920
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
533
921
|
```
|
|
534
922
|
|
|
535
|
-
|
|
923
|
+
### Utilities
|
|
536
924
|
|
|
537
|
-
|
|
925
|
+
```js
|
|
926
|
+
import { presets, dev, events } from 'wu-framework';
|
|
538
927
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
4. Push branch (`git push origin feature/amazing-feature`)
|
|
543
|
-
5. Abrir Pull Request
|
|
928
|
+
// Presets
|
|
929
|
+
await wu.init(presets.development([{ name: 'app', port: 3001 }]));
|
|
930
|
+
await wu.init(presets.production([{ name: 'app', url: 'https://...' }]));
|
|
544
931
|
|
|
545
|
-
|
|
932
|
+
// Dev tools
|
|
933
|
+
dev.enableDebug();
|
|
934
|
+
dev.inspect();
|
|
935
|
+
await dev.reload('app');
|
|
936
|
+
|
|
937
|
+
// Silenciar logs
|
|
938
|
+
wu.silence();
|
|
939
|
+
wu.verbose();
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
---
|
|
546
943
|
|
|
547
|
-
|
|
944
|
+
## 🤝 Contributing
|
|
548
945
|
|
|
549
|
-
|
|
946
|
+
¡Las contribuciones son bienvenidas!
|
|
550
947
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
948
|
+
1. **Fork** el repositorio
|
|
949
|
+
2. **Crear** branch (`git checkout -b feature/amazing-feature`)
|
|
950
|
+
3. **Commit** tus cambios (`git commit -m 'Add amazing feature'`)
|
|
951
|
+
4. **Push** al branch (`git push origin feature/amazing-feature`)
|
|
952
|
+
5. **Abrir** un Pull Request
|
|
554
953
|
|
|
555
954
|
---
|
|
556
955
|
|
|
557
|
-
|
|
956
|
+
## 📄 License
|
|
957
|
+
|
|
958
|
+
MIT License - Copyright (c) 2025 Wu Framework Team
|
|
959
|
+
|
|
960
|
+
---
|
|
558
961
|
|
|
559
|
-
|
|
962
|
+
<p align="center">
|
|
963
|
+
<b>🚀 Wu Framework - Universal Microfrontends Made Simple</b>
|
|
964
|
+
<br><br>
|
|
965
|
+
<i>Zero dependencies • 8 Frameworks • Shadow DOM • Proxy Sandbox • Self-Healing</i>
|
|
966
|
+
</p>
|