wu-framework 1.1.4 β 1.1.6
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 +1115 -395
- package/dist/wu-framework.cjs.js +1 -0
- package/dist/wu-framework.dev.js +8534 -0
- package/dist/wu-framework.dev.js.map +1 -0
- package/dist/wu-framework.hex.js +23 -0
- package/dist/wu-framework.min.js +1 -0
- package/dist/wu-framework.obf.js +1 -0
- package/dist/wu-framework.umd.js +1 -0
- package/package.json +32 -6
- package/scripts/build-protected.js +366 -0
- package/scripts/build.js +212 -0
- package/scripts/rollup-plugin-hex.js +143 -0
- package/src/adapters/angular.js +171 -0
- package/src/adapters/vue.js +41 -0
- package/src/core/wu-core.js +14 -32
- package/src/core/wu-manifest.js +13 -31
- package/src/core/wu-sandbox.js +259 -18
- package/src/core/wu-style-bridge.js +118 -312
package/README.md
CHANGED
|
@@ -1,553 +1,1273 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Wu Framework
|
|
2
2
|
|
|
3
3
|
**Universal Microfrontends Made Simple**
|
|
4
4
|
|
|
5
|
-
Wu Framework es
|
|
5
|
+
Wu Framework es la libreria de microfrontends mas simple y poderosa del mercado. Framework agnostic, zero config, Shadow DOM nativo.
|
|
6
|
+
|
|
7
|
+
## Por que Wu Framework?
|
|
8
|
+
|
|
9
|
+
| Caracteristica | Module Federation | qiankun | single-spa | **Wu Framework** |
|
|
10
|
+
|---|---|---|---|---|
|
|
11
|
+
| **Setup** | Webpack config complejo | Configuracion manual | Config extensa | **Zero config** |
|
|
12
|
+
| **Framework Support** | React-first | Multi framework | Multi framework | **8 frameworks nativos** |
|
|
13
|
+
| **Bundler Required** | Solo Webpack 5 | Agnostico | Agnostico | **Ninguno requerido** |
|
|
14
|
+
| **CSS Isolation** | Basico | CSS hacks | Manual | **Shadow DOM nativo** |
|
|
15
|
+
| **JS Isolation** | No | Proxy basico | No | **Proxy + Snapshot Sandbox** |
|
|
16
|
+
| **Style Modes** | No | No | No | **3 modos (shared/isolated/fully-isolated)** |
|
|
17
|
+
| **Runtime Config** | No | Limitado | Limitado | **Completamente dinamico** |
|
|
18
|
+
| **Self-Healing** | No | No | No | **Auto-recovery** |
|
|
19
|
+
| **Build Protection** | No | No | No | **Minify + Obfuscate + Base64** |
|
|
20
|
+
| **API Complexity** | Alta | Media | Alta | **1 linea de codigo** |
|
|
21
|
+
|
|
22
|
+
## Instalacion
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install wu-framework
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Sin webpack config, sin plugins, sin configuracion adicional.
|
|
6
29
|
|
|
7
30
|
---
|
|
8
31
|
|
|
9
|
-
##
|
|
32
|
+
## Frameworks Soportados
|
|
10
33
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
34
|
+
Wu Framework incluye **adapters nativos** para los frameworks mas populares:
|
|
35
|
+
|
|
36
|
+
| Framework | Adapter | Registro |
|
|
37
|
+
|-----------|---------|----------|
|
|
38
|
+
| React | `wuReact` | `wuReact.register('app', App)` |
|
|
39
|
+
| Vue | `wuVue` | `wuVue.register('app', App)` |
|
|
40
|
+
| Angular | `wuAngular` | `wuAngular.register('app', AppModule)` |
|
|
41
|
+
| Angular Standalone | `wuAngular` | `wuAngular.registerStandalone('app', Component)` |
|
|
42
|
+
| Angular Elements | `wuAngular` | `wuAngular.registerElement('app', Component)` |
|
|
43
|
+
| Svelte | `wuSvelte` | `wuSvelte.register('app', App)` |
|
|
44
|
+
| Preact | `wuPreact` | `wuPreact.register('app', App)` |
|
|
45
|
+
| Solid.js | `wuSolid` | `wuSolid.register('app', App)` |
|
|
46
|
+
| Lit | `wuLit` | `wuLit.register('app', MyElement)` |
|
|
47
|
+
| Vanilla JS | `wuVanilla` | `wuVanilla.register('app', config)` |
|
|
21
48
|
|
|
22
49
|
---
|
|
23
50
|
|
|
24
|
-
##
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
### 1. Micro App: Crear `wu.json`
|
|
25
54
|
|
|
26
|
-
|
|
55
|
+
Cada microfrontend necesita un archivo `wu.json` en su raiz:
|
|
27
56
|
|
|
28
57
|
```json
|
|
29
|
-
// header/wu.json - Aislamiento total (bloquea CSS vars del padre)
|
|
30
58
|
{
|
|
31
59
|
"name": "header",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
60
|
+
"version": "1.0.0",
|
|
61
|
+
"entry": "index.js",
|
|
62
|
+
"styleMode": "shared",
|
|
34
63
|
"wu": {
|
|
64
|
+
"exports": {
|
|
65
|
+
"NavBar": "components/NavBar.js",
|
|
66
|
+
"UserMenu": "components/UserMenu.js"
|
|
67
|
+
},
|
|
68
|
+
"imports": [],
|
|
69
|
+
"routes": ["/", "/home"],
|
|
35
70
|
"permissions": ["events", "store"]
|
|
36
71
|
}
|
|
37
72
|
}
|
|
38
73
|
```
|
|
39
74
|
|
|
75
|
+
| Campo | Descripcion |
|
|
76
|
+
|-------|-------------|
|
|
77
|
+
| `name` | Nombre unico del microfrontend |
|
|
78
|
+
| `version` | Version del microfrontend |
|
|
79
|
+
| `entry` | Archivo JS principal (default: `index.js`) |
|
|
80
|
+
| `styleMode` | Modo de estilos: `shared`, `isolated`, `fully-isolated` |
|
|
81
|
+
| `wu.exports` | Componentes que expone a otros microfrontends |
|
|
82
|
+
| `wu.imports` | Componentes que importa de otros microfrontends |
|
|
83
|
+
| `wu.routes` | Rutas que maneja este microfrontend |
|
|
84
|
+
| `wu.permissions` | Permisos requeridos (`events`, `store`, `dom`) |
|
|
85
|
+
|
|
86
|
+
### 2. Micro App: Registrar con Adapter (1 linea)
|
|
87
|
+
|
|
88
|
+
**React:**
|
|
89
|
+
```tsx
|
|
90
|
+
import { wuReact } from 'wu-framework/adapters/react';
|
|
91
|
+
import App from './App';
|
|
92
|
+
|
|
93
|
+
wuReact.register('header', App);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Vue:**
|
|
97
|
+
```ts
|
|
98
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
99
|
+
import App from './App.vue';
|
|
100
|
+
|
|
101
|
+
wuVue.register('sidebar', App);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Angular:**
|
|
105
|
+
```ts
|
|
106
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
107
|
+
import { AppModule } from './app/app.module';
|
|
108
|
+
|
|
109
|
+
wuAngular.register('content', AppModule);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
El adapter se encarga de todo: deteccion de contexto, modo standalone, cleanup automatico.
|
|
113
|
+
|
|
114
|
+
### 3. Shell (Host Application)
|
|
115
|
+
|
|
116
|
+
**Desarrollo:**
|
|
117
|
+
```js
|
|
118
|
+
import { wu } from 'wu-framework';
|
|
119
|
+
|
|
120
|
+
await wu.init({
|
|
121
|
+
apps: [
|
|
122
|
+
{ name: 'header', url: 'http://localhost:3001' },
|
|
123
|
+
{ name: 'sidebar', url: 'http://localhost:3002' },
|
|
124
|
+
{ name: 'content', url: 'http://localhost:3003' }
|
|
125
|
+
]
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
await wu.mount('header', '#header-container');
|
|
129
|
+
await wu.mount('sidebar', '#sidebar-container');
|
|
130
|
+
await wu.mount('content', '#content-container');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Produccion:**
|
|
134
|
+
```js
|
|
135
|
+
import { wu } from 'wu-framework';
|
|
136
|
+
|
|
137
|
+
await wu.init({
|
|
138
|
+
apps: [
|
|
139
|
+
{ name: 'header', url: 'https://cdn.mycompany.com/mfe/header' },
|
|
140
|
+
{ name: 'sidebar', url: 'https://cdn.mycompany.com/mfe/sidebar' },
|
|
141
|
+
{ name: 'content', url: 'https://cdn.mycompany.com/mfe/content' }
|
|
142
|
+
]
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Estructura de Archivos (Microfrontend)
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
my-header-mfe/
|
|
150
|
+
βββ wu.json # Manifest requerido
|
|
151
|
+
βββ index.js # Entry point (registra con adapter)
|
|
152
|
+
βββ App.jsx # Componente principal
|
|
153
|
+
βββ components/
|
|
154
|
+
β βββ NavBar.js
|
|
155
|
+
β βββ UserMenu.js
|
|
156
|
+
βββ package.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Modos de Inyeccion de Estilos
|
|
162
|
+
|
|
163
|
+
Wu Framework soporta **3 modos de inyeccion de estilos** en Shadow DOM:
|
|
164
|
+
|
|
165
|
+
| Modo | Descripcion | Riesgo Colision |
|
|
166
|
+
|------|-------------|-----------------|
|
|
167
|
+
| `shared` | Inyecta TODOS los estilos del documento padre | Alto |
|
|
168
|
+
| `isolated` | NO inyecta estilos externos (Shadow DOM nativo) | Ninguno |
|
|
169
|
+
| `fully-isolated` | Inyecta SOLO estilos propios de la app | Ninguno |
|
|
170
|
+
|
|
171
|
+
### Configuracion en wu.json
|
|
172
|
+
|
|
40
173
|
```json
|
|
41
|
-
// container/wu.json - Aislamiento normal (hereda CSS vars)
|
|
42
174
|
{
|
|
43
|
-
"name": "
|
|
44
|
-
"
|
|
45
|
-
"entry": "src/main.tsx",
|
|
46
|
-
"styleMode": "isolated",
|
|
47
|
-
"wu": {
|
|
48
|
-
"permissions": ["events", "store"]
|
|
49
|
-
}
|
|
175
|
+
"name": "my-app",
|
|
176
|
+
"styleMode": "isolated"
|
|
50
177
|
}
|
|
51
178
|
```
|
|
52
179
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
180
|
+
### shared (default)
|
|
181
|
+
|
|
182
|
+
- Inyecta estilos de librerias (Element Plus, Tailwind, Vue Flow, etc.)
|
|
183
|
+
- Inyecta estilos globales del documento
|
|
184
|
+
- Observer HMR para cambios en desarrollo
|
|
185
|
+
- **Ideal para:** Apps que comparten un design system comun
|
|
186
|
+
|
|
187
|
+
### isolated
|
|
188
|
+
|
|
189
|
+
- NO inyecta ningun estilo externo
|
|
190
|
+
- Usa encapsulamiento nativo del Shadow DOM
|
|
191
|
+
- La app debe incluir sus propios estilos (CSS-in-JS, scoped, imports)
|
|
192
|
+
- **Ideal para:** Apps con estilos completamente independientes
|
|
193
|
+
|
|
194
|
+
### fully-isolated
|
|
195
|
+
|
|
196
|
+
- Detecta y inyecta SOLO estilos propios de la app especifica
|
|
197
|
+
- Patron de deteccion: `packages/appName/src/`
|
|
198
|
+
- MutationObserver persistente para HMR de Vite
|
|
199
|
+
- **Ideal para:** Apps que necesitan sus estilos pero no los globales
|
|
59
200
|
|
|
60
201
|
---
|
|
61
202
|
|
|
62
|
-
##
|
|
203
|
+
## React Adapter
|
|
63
204
|
|
|
64
|
-
###
|
|
205
|
+
### Registro Basico
|
|
65
206
|
|
|
66
207
|
```tsx
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const devUrls = {
|
|
71
|
-
header: 'http://localhost:3001',
|
|
72
|
-
content: 'http://localhost:3003'
|
|
73
|
-
}
|
|
208
|
+
import { wuReact } from 'wu-framework/adapters/react';
|
|
209
|
+
import App from './App';
|
|
74
210
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
content: import.meta.env.VITE_CONTENT_URL || 'https://cdn.example.com/mfe/content'
|
|
78
|
-
}
|
|
211
|
+
wuReact.register('my-app', App);
|
|
212
|
+
```
|
|
79
213
|
|
|
80
|
-
|
|
214
|
+
### Con Opciones
|
|
81
215
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
216
|
+
```tsx
|
|
217
|
+
wuReact.register('my-app', App, {
|
|
218
|
+
strictMode: true, // Envolver en React.StrictMode
|
|
219
|
+
props: { theme: 'dark' }, // Props iniciales
|
|
220
|
+
standalone: true, // Ejecutar independiente si Wu no esta
|
|
221
|
+
standaloneContainer: '#root',
|
|
222
|
+
onMount: (container) => console.log('Mounted!'),
|
|
223
|
+
onUnmount: (container) => console.log('Unmounted!')
|
|
224
|
+
});
|
|
86
225
|
```
|
|
87
226
|
|
|
227
|
+
### Hooks para React
|
|
228
|
+
|
|
88
229
|
```tsx
|
|
89
|
-
|
|
90
|
-
import {
|
|
91
|
-
import { wu } from 'wu-framework'
|
|
92
|
-
import { createUseWuEvents, createUseWuStore } from 'wu-framework/adapters/react'
|
|
93
|
-
import React from 'react'
|
|
94
|
-
import { enabledApps } from '../config/mfe.config'
|
|
95
|
-
|
|
96
|
-
// Crear hooks reactivos para el shell
|
|
97
|
-
export const useWuEvents = createUseWuEvents(React)
|
|
98
|
-
export const useWuStore = createUseWuStore(React)
|
|
99
|
-
|
|
100
|
-
export function useWuFramework() {
|
|
101
|
-
const [state, setState] = useState({
|
|
102
|
-
initialized: false,
|
|
103
|
-
loading: true,
|
|
104
|
-
error: null as Error | null
|
|
105
|
-
})
|
|
106
|
-
const initRef = useRef(false)
|
|
230
|
+
import React from 'react';
|
|
231
|
+
import { createUseWuEvents, createUseWuStore } from 'wu-framework/adapters/react';
|
|
107
232
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await wu.init({
|
|
115
|
-
apps: enabledApps.map(({ name, url }) => ({ name, url }))
|
|
116
|
-
})
|
|
117
|
-
setState({ initialized: true, loading: false, error: null })
|
|
118
|
-
} catch (error) {
|
|
119
|
-
setState({ initialized: false, loading: false, error: error as Error })
|
|
120
|
-
}
|
|
121
|
-
}
|
|
233
|
+
const useWuEvents = createUseWuEvents(React);
|
|
234
|
+
const useWuStore = createUseWuStore(React);
|
|
235
|
+
|
|
236
|
+
function MyComponent() {
|
|
237
|
+
const { emit, on } = useWuEvents();
|
|
238
|
+
const { state, setState } = useWuStore('user');
|
|
122
239
|
|
|
123
|
-
|
|
124
|
-
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
const unsub = on('cart:updated', (data) => {
|
|
242
|
+
console.log('Cart updated:', data);
|
|
243
|
+
});
|
|
244
|
+
return unsub;
|
|
245
|
+
}, [on]);
|
|
125
246
|
|
|
126
|
-
return
|
|
247
|
+
return (
|
|
248
|
+
<button onClick={() => emit('user:logout')}>
|
|
249
|
+
Logout {state?.name}
|
|
250
|
+
</button>
|
|
251
|
+
);
|
|
127
252
|
}
|
|
128
253
|
```
|
|
129
254
|
|
|
130
|
-
|
|
131
|
-
// shell/src/App.tsx
|
|
132
|
-
import { useWuFramework, useWuEvents } from './hooks/useWuFramework'
|
|
133
|
-
import WuSlot from './components/WuSlot'
|
|
134
|
-
import ToastContainer from './components/ToastContainer'
|
|
255
|
+
### Cargar Microfrontends en React (Shell)
|
|
135
256
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
257
|
+
```tsx
|
|
258
|
+
import React from 'react';
|
|
259
|
+
import { createWuSlot } from 'wu-framework/adapters/react';
|
|
139
260
|
|
|
140
|
-
|
|
141
|
-
if (initialized) {
|
|
142
|
-
emit('shell:ready', { timestamp: Date.now() })
|
|
143
|
-
}
|
|
144
|
-
}, [initialized, emit])
|
|
261
|
+
const WuSlot = createWuSlot(React);
|
|
145
262
|
|
|
146
|
-
|
|
147
|
-
if (error) return <div>Error: {error.message}</div>
|
|
263
|
+
const HEADER_URL = process.env.REACT_APP_HEADER_URL || 'http://localhost:3001';
|
|
148
264
|
|
|
265
|
+
function Shell() {
|
|
149
266
|
return (
|
|
150
|
-
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
267
|
+
<div>
|
|
268
|
+
<WuSlot
|
|
269
|
+
name="header"
|
|
270
|
+
url={HEADER_URL}
|
|
271
|
+
onLoad={() => console.log('Header loaded!')}
|
|
272
|
+
onError={(err) => console.error(err)}
|
|
273
|
+
/>
|
|
274
|
+
</div>
|
|
275
|
+
);
|
|
156
276
|
}
|
|
157
277
|
```
|
|
158
278
|
|
|
159
|
-
|
|
279
|
+
---
|
|
160
280
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
},
|
|
171
|
-
"permissions": ["events", "store"]
|
|
172
|
-
}
|
|
173
|
-
}
|
|
281
|
+
## Vue Adapter
|
|
282
|
+
|
|
283
|
+
### Registro Basico
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
287
|
+
import App from './App.vue';
|
|
288
|
+
|
|
289
|
+
wuVue.register('my-app', App);
|
|
174
290
|
```
|
|
175
291
|
|
|
292
|
+
### Con Plugins (Pinia, Router, etc.)
|
|
293
|
+
|
|
176
294
|
```ts
|
|
177
|
-
|
|
178
|
-
import {
|
|
179
|
-
import
|
|
180
|
-
import App from './App.vue'
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
})
|
|
295
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
296
|
+
import { createPinia } from 'pinia';
|
|
297
|
+
import router from './router';
|
|
298
|
+
import App from './App.vue';
|
|
299
|
+
|
|
300
|
+
wuVue.register('my-app', App, {
|
|
301
|
+
setup: (app) => {
|
|
302
|
+
app.use(createPinia());
|
|
303
|
+
app.use(router);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
189
306
|
```
|
|
190
307
|
|
|
191
|
-
|
|
192
|
-
<!-- header/src/components/NavBar.vue -->
|
|
193
|
-
<script setup lang="ts">
|
|
194
|
-
import { ref, onMounted, onUnmounted } from 'vue'
|
|
195
|
-
import { useWuEvents, useWuStore } from 'wu-framework/adapters/vue'
|
|
308
|
+
### Composables para Vue
|
|
196
309
|
|
|
197
|
-
|
|
198
|
-
|
|
310
|
+
```vue
|
|
311
|
+
<script setup>
|
|
312
|
+
import { onMounted, onUnmounted } from 'vue';
|
|
313
|
+
import { useWuEvents, useWuStore } from 'wu-framework/adapters/vue';
|
|
199
314
|
|
|
200
|
-
const
|
|
201
|
-
|
|
315
|
+
const { emit, on, cleanup } = useWuEvents();
|
|
316
|
+
const { state, setState } = useWuStore('cart');
|
|
202
317
|
|
|
203
318
|
onMounted(() => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
setState({ count: 0, items: [] })
|
|
207
|
-
}
|
|
319
|
+
on('user:login', (data) => console.log('Login:', data));
|
|
320
|
+
});
|
|
208
321
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
notificationCount.value++
|
|
212
|
-
|
|
213
|
-
// Actualizar store compartido
|
|
214
|
-
const current = notificationState.value || { count: 0, items: [] }
|
|
215
|
-
setState({
|
|
216
|
-
count: current.count + 1,
|
|
217
|
-
items: [...current.items, {
|
|
218
|
-
id: Date.now(),
|
|
219
|
-
message: event.data?.message,
|
|
220
|
-
type: event.data?.type || 'info'
|
|
221
|
-
}]
|
|
222
|
-
})
|
|
223
|
-
})
|
|
224
|
-
})
|
|
322
|
+
onUnmounted(() => cleanup());
|
|
323
|
+
</script>
|
|
225
324
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
325
|
+
<template>
|
|
326
|
+
<div>
|
|
327
|
+
<p>Items: {{ state?.items?.length }}</p>
|
|
328
|
+
<button @click="emit('cart:clear')">Clear Cart</button>
|
|
329
|
+
</div>
|
|
330
|
+
</template>
|
|
331
|
+
```
|
|
229
332
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
333
|
+
### Cargar Microfrontends en Vue (Shell)
|
|
334
|
+
|
|
335
|
+
```vue
|
|
336
|
+
<script setup>
|
|
337
|
+
import { WuSlot } from 'wu-framework/adapters/vue';
|
|
338
|
+
|
|
339
|
+
const headerUrl = import.meta.env.VITE_HEADER_URL || 'http://localhost:3001';
|
|
233
340
|
</script>
|
|
234
341
|
|
|
235
342
|
<template>
|
|
236
|
-
<
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
</
|
|
343
|
+
<div>
|
|
344
|
+
<WuSlot
|
|
345
|
+
name="header"
|
|
346
|
+
:url="headerUrl"
|
|
347
|
+
@load="onLoad"
|
|
348
|
+
@error="onError"
|
|
349
|
+
/>
|
|
350
|
+
</div>
|
|
244
351
|
</template>
|
|
245
352
|
```
|
|
246
353
|
|
|
247
|
-
###
|
|
354
|
+
### Plugin Global
|
|
248
355
|
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
356
|
+
```ts
|
|
357
|
+
import { createApp } from 'vue';
|
|
358
|
+
import { wuVuePlugin } from 'wu-framework/adapters/vue';
|
|
359
|
+
|
|
360
|
+
const app = createApp(App);
|
|
361
|
+
app.use(wuVuePlugin); // Registra <WuSlot> globalmente
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Angular Adapter
|
|
367
|
+
|
|
368
|
+
### Registro con NgModule
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
372
|
+
import { AppModule } from './app/app.module';
|
|
373
|
+
|
|
374
|
+
wuAngular.register('my-app', AppModule);
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Registro con Standalone Components (Angular 14+)
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
381
|
+
import { AppComponent } from './app/app.component';
|
|
382
|
+
import { appConfig } from './app/app.config';
|
|
383
|
+
|
|
384
|
+
wuAngular.registerStandalone('my-app', AppComponent, {
|
|
385
|
+
appConfig
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Registro con Angular Elements (Web Components)
|
|
390
|
+
|
|
391
|
+
Para proyectos que usan Angular Elements para crear Web Components:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
395
|
+
import { AppComponent } from './app/app.component';
|
|
396
|
+
import { appConfig } from './app/app.config';
|
|
397
|
+
|
|
398
|
+
wuAngular.registerElement('my-app', AppComponent, {
|
|
399
|
+
elementTag: 'my-app-element', // Tag del custom element
|
|
400
|
+
appConfig, // Configuracion de Angular
|
|
401
|
+
standaloneContainer: '#root', // Container para modo standalone
|
|
402
|
+
onMount: (container, element) => {
|
|
403
|
+
console.log('Angular Element mounted');
|
|
404
|
+
},
|
|
405
|
+
onUnmount: (container, element) => {
|
|
406
|
+
console.log('Angular Element unmounted');
|
|
261
407
|
}
|
|
262
|
-
}
|
|
408
|
+
});
|
|
263
409
|
```
|
|
264
410
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
411
|
+
El adapter se encarga de:
|
|
412
|
+
- Crear la aplicacion Angular
|
|
413
|
+
- Registrar el Custom Element con `customElements.define()`
|
|
414
|
+
- Gestionar el ciclo de vida mount/unmount
|
|
415
|
+
- Soportar modo standalone si Wu Framework no esta disponible
|
|
269
416
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
417
|
+
### Servicio para Componentes
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
421
|
+
import { createWuService } from 'wu-framework/adapters/angular';
|
|
422
|
+
|
|
423
|
+
@Component({
|
|
424
|
+
selector: 'app-root',
|
|
425
|
+
template: `<button (click)="sendEvent()">Send Event</button>`
|
|
276
426
|
})
|
|
277
|
-
|
|
427
|
+
export class AppComponent implements OnInit, OnDestroy {
|
|
428
|
+
private wuService = createWuService();
|
|
278
429
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
export function Dashboard() {
|
|
285
|
-
const { emit } = useWuEvents()
|
|
286
|
-
const [sent, setSent] = useState(0)
|
|
287
|
-
|
|
288
|
-
// Enviar notificaciΓ³n que el Header (Vue) y Shell (React) recibirΓ‘n
|
|
289
|
-
const sendNotification = (type: 'success' | 'warning' | 'error' | 'info', message: string) => {
|
|
290
|
-
emit('notification:new', { message, type })
|
|
291
|
-
setSent(prev => prev + 1)
|
|
430
|
+
ngOnInit() {
|
|
431
|
+
this.wuService.on('user:login', (data) => {
|
|
432
|
+
console.log('User logged in:', data);
|
|
433
|
+
});
|
|
292
434
|
}
|
|
293
435
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
β
Success
|
|
302
|
-
</button>
|
|
303
|
-
<button onClick={() => sendNotification('warning', 'Check your settings')}>
|
|
304
|
-
β οΈ Warning
|
|
305
|
-
</button>
|
|
306
|
-
<button onClick={() => sendNotification('error', 'Something went wrong!')}>
|
|
307
|
-
β Error
|
|
308
|
-
</button>
|
|
309
|
-
<button onClick={() => sendNotification('info', 'New message received')}>
|
|
310
|
-
π¬ Info
|
|
311
|
-
</button>
|
|
312
|
-
</div>
|
|
313
|
-
</div>
|
|
314
|
-
)
|
|
436
|
+
sendEvent() {
|
|
437
|
+
this.wuService.emit('app:ready', { timestamp: Date.now() });
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
ngOnDestroy() {
|
|
441
|
+
this.wuService.destroy();
|
|
442
|
+
}
|
|
315
443
|
}
|
|
316
444
|
```
|
|
317
445
|
|
|
318
|
-
|
|
446
|
+
---
|
|
319
447
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
448
|
+
## Svelte Adapter
|
|
449
|
+
|
|
450
|
+
### Registro Basico
|
|
451
|
+
|
|
452
|
+
```js
|
|
453
|
+
import { wuSvelte } from 'wu-framework/adapters/svelte';
|
|
454
|
+
import App from './App.svelte';
|
|
455
|
+
|
|
456
|
+
wuSvelte.register('my-app', App);
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Svelte 5 (con runes)
|
|
460
|
+
|
|
461
|
+
```js
|
|
462
|
+
import { wuSvelte } from 'wu-framework/adapters/svelte';
|
|
463
|
+
import App from './App.svelte';
|
|
464
|
+
|
|
465
|
+
wuSvelte.registerSvelte5('my-app', App);
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Stores Reactivos
|
|
469
|
+
|
|
470
|
+
```svelte
|
|
471
|
+
<script>
|
|
472
|
+
import { createWuStore, useWuEvents } from 'wu-framework/adapters/svelte';
|
|
473
|
+
import { onDestroy } from 'svelte';
|
|
474
|
+
|
|
475
|
+
const userStore = createWuStore('user');
|
|
476
|
+
const { emit, on, cleanup } = useWuEvents();
|
|
477
|
+
|
|
478
|
+
on('cart:updated', (data) => {
|
|
479
|
+
console.log('Cart updated:', data);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
onDestroy(cleanup);
|
|
483
|
+
</script>
|
|
484
|
+
|
|
485
|
+
<p>Hello, {$userStore?.name}</p>
|
|
486
|
+
<button on:click={() => emit('user:logout')}>Logout</button>
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Preact Adapter
|
|
492
|
+
|
|
493
|
+
### Registro Basico
|
|
494
|
+
|
|
495
|
+
```jsx
|
|
496
|
+
import { wuPreact } from 'wu-framework/adapters/preact';
|
|
497
|
+
import App from './App';
|
|
498
|
+
|
|
499
|
+
wuPreact.register('my-app', App);
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Compatible con React (preact/compat)
|
|
503
|
+
|
|
504
|
+
```jsx
|
|
505
|
+
import { wuPreact } from 'wu-framework/adapters/preact';
|
|
506
|
+
import App from './App';
|
|
507
|
+
|
|
508
|
+
wuPreact.registerCompat('my-app', App);
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Hooks
|
|
512
|
+
|
|
513
|
+
```jsx
|
|
514
|
+
import { h } from 'preact';
|
|
515
|
+
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
|
|
516
|
+
import { createUseWuEvents, createUseWuStore } from 'wu-framework/adapters/preact';
|
|
517
|
+
|
|
518
|
+
const useWuEvents = createUseWuEvents({ useCallback, useEffect, useRef });
|
|
519
|
+
const useWuStore = createUseWuStore({ useState, useCallback, useEffect });
|
|
520
|
+
|
|
521
|
+
function MyComponent() {
|
|
522
|
+
const { emit, on } = useWuEvents();
|
|
523
|
+
const { state } = useWuStore('user');
|
|
524
|
+
|
|
525
|
+
return <div>Hello {state?.name}</div>;
|
|
329
526
|
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
---
|
|
330
530
|
|
|
331
|
-
|
|
332
|
-
const [toasts, setToasts] = useState<Toast[]>([])
|
|
333
|
-
const { on } = useWuEvents()
|
|
531
|
+
## Solid.js Adapter
|
|
334
532
|
|
|
335
|
-
|
|
336
|
-
const id = `toast-${Date.now()}`
|
|
337
|
-
setToasts(prev => [...prev, { id, message, type }])
|
|
338
|
-
setTimeout(() => setToasts(prev => prev.filter(t => t.id !== id)), 5000)
|
|
339
|
-
}, [])
|
|
533
|
+
### Registro Basico
|
|
340
534
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
535
|
+
```jsx
|
|
536
|
+
import { wuSolid } from 'wu-framework/adapters/solid';
|
|
537
|
+
import App from './App';
|
|
538
|
+
|
|
539
|
+
wuSolid.register('my-app', App);
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Stores Reactivos
|
|
543
|
+
|
|
544
|
+
```jsx
|
|
545
|
+
import { createWuStore, createWuEvent } from 'wu-framework/adapters/solid';
|
|
546
|
+
|
|
547
|
+
function MyComponent() {
|
|
548
|
+
const [user, setUser] = createWuStore('user');
|
|
549
|
+
const lastEvent = createWuEvent('notification:*');
|
|
349
550
|
|
|
350
551
|
return (
|
|
351
|
-
<div
|
|
352
|
-
{
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
))}
|
|
552
|
+
<div>
|
|
553
|
+
<p>Hello, {user()?.name}</p>
|
|
554
|
+
<Show when={lastEvent()}>
|
|
555
|
+
<p>Last notification: {lastEvent()?.data}</p>
|
|
556
|
+
</Show>
|
|
557
|
+
<button onClick={() => setUser('name', 'John')}>
|
|
558
|
+
Set Name
|
|
559
|
+
</button>
|
|
357
560
|
</div>
|
|
358
|
-
)
|
|
561
|
+
);
|
|
359
562
|
}
|
|
360
563
|
```
|
|
361
564
|
|
|
362
565
|
---
|
|
363
566
|
|
|
364
|
-
##
|
|
567
|
+
## Lit Adapter (Web Components)
|
|
365
568
|
|
|
366
|
-
###
|
|
569
|
+
### Registro con LitElement
|
|
367
570
|
|
|
368
|
-
```
|
|
369
|
-
import {
|
|
571
|
+
```js
|
|
572
|
+
import { LitElement, html, css } from 'lit';
|
|
573
|
+
import { wuLit } from 'wu-framework/adapters/lit';
|
|
370
574
|
|
|
371
|
-
|
|
372
|
-
|
|
575
|
+
class MyApp extends LitElement {
|
|
576
|
+
static styles = css`
|
|
577
|
+
:host { display: block; padding: 16px; }
|
|
578
|
+
`;
|
|
373
579
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
580
|
+
render() {
|
|
581
|
+
return html`<h1>Hello from Lit!</h1>`;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
378
584
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
585
|
+
wuLit.register('my-app', MyApp);
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Con WuMixin
|
|
383
589
|
|
|
384
|
-
|
|
385
|
-
|
|
590
|
+
```js
|
|
591
|
+
import { LitElement, html } from 'lit';
|
|
592
|
+
import { WuMixin } from 'wu-framework/adapters/lit';
|
|
386
593
|
|
|
387
|
-
|
|
388
|
-
|
|
594
|
+
class MyApp extends WuMixin(LitElement) {
|
|
595
|
+
connectedCallback() {
|
|
596
|
+
super.connectedCallback();
|
|
597
|
+
this.wuOn('user:login', (data) => {
|
|
598
|
+
console.log('User logged in:', data);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
handleClick() {
|
|
603
|
+
this.wuEmit('button:clicked', { id: 'my-button' });
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
render() {
|
|
607
|
+
return html`
|
|
608
|
+
<button @click=${this.handleClick}>Click me</button>
|
|
609
|
+
`;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
wuLit.register('my-app', MyApp);
|
|
389
614
|
```
|
|
390
615
|
|
|
391
|
-
###
|
|
616
|
+
### Web Component Vanilla (sin Lit)
|
|
392
617
|
|
|
393
|
-
```
|
|
394
|
-
import {
|
|
618
|
+
```js
|
|
619
|
+
import { wuLit } from 'wu-framework/adapters/lit';
|
|
395
620
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
621
|
+
class MyComponent extends HTMLElement {
|
|
622
|
+
connectedCallback() {
|
|
623
|
+
this.attachShadow({ mode: 'open' });
|
|
624
|
+
this.shadowRoot.innerHTML = '<h1>Hello Web Component!</h1>';
|
|
625
|
+
}
|
|
626
|
+
}
|
|
399
627
|
|
|
400
|
-
|
|
401
|
-
|
|
628
|
+
wuLit.registerWebComponent('my-component', MyComponent);
|
|
629
|
+
```
|
|
402
630
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
## Vanilla JS Adapter
|
|
634
|
+
|
|
635
|
+
### Registro con Objeto
|
|
636
|
+
|
|
637
|
+
```js
|
|
638
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
639
|
+
|
|
640
|
+
wuVanilla.register('my-app', {
|
|
641
|
+
state: { count: 0 },
|
|
642
|
+
|
|
643
|
+
render: (container, state) => {
|
|
644
|
+
container.innerHTML = `
|
|
645
|
+
<div>
|
|
646
|
+
<h1>Count: ${state.count}</h1>
|
|
647
|
+
<button id="increment">+</button>
|
|
648
|
+
</div>
|
|
649
|
+
`;
|
|
650
|
+
container.querySelector('#increment').onclick = () => {
|
|
651
|
+
state.count++;
|
|
652
|
+
};
|
|
653
|
+
},
|
|
654
|
+
|
|
655
|
+
destroy: (container) => {
|
|
656
|
+
container.innerHTML = '';
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Registro con Clase
|
|
662
|
+
|
|
663
|
+
```js
|
|
664
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
665
|
+
|
|
666
|
+
class TodoApp {
|
|
667
|
+
constructor(container) {
|
|
668
|
+
this.container = container;
|
|
669
|
+
this.todos = [];
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
render() {
|
|
673
|
+
this.container.innerHTML = `
|
|
674
|
+
<ul>
|
|
675
|
+
${this.todos.map(t => `<li>${t}</li>`).join('')}
|
|
676
|
+
</ul>
|
|
677
|
+
<input type="text" id="input" />
|
|
678
|
+
<button id="add">Add</button>
|
|
679
|
+
`;
|
|
680
|
+
|
|
681
|
+
this.container.querySelector('#add').onclick = () => {
|
|
682
|
+
const input = this.container.querySelector('#input');
|
|
683
|
+
this.todos.push(input.value);
|
|
684
|
+
this.render();
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
destroy() {
|
|
689
|
+
this.container.innerHTML = '';
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
wuVanilla.registerClass('todo-app', TodoApp);
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### Registro con Template
|
|
697
|
+
|
|
698
|
+
```js
|
|
699
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
700
|
+
|
|
701
|
+
wuVanilla.registerTemplate('header', `
|
|
702
|
+
<header>
|
|
703
|
+
<h1>My Header</h1>
|
|
704
|
+
<nav>
|
|
705
|
+
<a href="/">Home</a>
|
|
706
|
+
<a href="/about">About</a>
|
|
707
|
+
</nav>
|
|
708
|
+
</header>
|
|
709
|
+
`);
|
|
710
|
+
|
|
711
|
+
// Template dinamico
|
|
712
|
+
wuVanilla.registerTemplate('greeting',
|
|
713
|
+
(data) => `<h1>Hello, ${data.name}!</h1>`,
|
|
714
|
+
{ data: { name: 'World' } }
|
|
715
|
+
);
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### Componente Reactivo
|
|
719
|
+
|
|
720
|
+
```js
|
|
721
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
722
|
+
|
|
723
|
+
const Counter = wuVanilla.createComponent({
|
|
724
|
+
state: { count: 0 },
|
|
725
|
+
|
|
726
|
+
template: (state) => `
|
|
727
|
+
<div>
|
|
728
|
+
<h1>Count: ${state.count}</h1>
|
|
729
|
+
<button data-action="increment">+</button>
|
|
730
|
+
<button data-action="decrement">-</button>
|
|
731
|
+
</div>
|
|
732
|
+
`,
|
|
733
|
+
|
|
734
|
+
actions: {
|
|
735
|
+
increment: (state) => ({ count: state.count + 1 }),
|
|
736
|
+
decrement: (state) => ({ count: state.count - 1 })
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
wuVanilla.register('counter', Counter);
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
---
|
|
407
744
|
|
|
408
|
-
|
|
745
|
+
## Event Bus
|
|
746
|
+
|
|
747
|
+
Sistema de eventos para comunicacion entre microfrontends con soporte para wildcards y replay.
|
|
748
|
+
|
|
749
|
+
```js
|
|
750
|
+
import { emit, on, once, off, replayEvents } from 'wu-framework';
|
|
751
|
+
|
|
752
|
+
// Emitir evento
|
|
753
|
+
emit('user:login', { userId: 123, name: 'John' });
|
|
754
|
+
|
|
755
|
+
// Suscribirse a evento
|
|
756
|
+
const unsubscribe = on('user:login', (event) => {
|
|
757
|
+
console.log('User logged in:', event.data);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
// Suscribirse con wildcards
|
|
761
|
+
on('user:*', (event) => {
|
|
762
|
+
console.log('User event:', event.name, event.data);
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
// Suscribirse una sola vez
|
|
766
|
+
once('app:ready', (event) => {
|
|
767
|
+
console.log('App is ready!');
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// Desuscribirse
|
|
771
|
+
off('user:login', callback);
|
|
772
|
+
unsubscribe();
|
|
773
|
+
|
|
774
|
+
// Replay eventos del historial
|
|
775
|
+
replayEvents('user:*', (event) => {
|
|
776
|
+
console.log('Past event:', event);
|
|
777
|
+
});
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
782
|
+
## Store (State Management)
|
|
783
|
+
|
|
784
|
+
Store de alto rendimiento con pattern matching.
|
|
785
|
+
|
|
786
|
+
```js
|
|
787
|
+
import {
|
|
788
|
+
getState,
|
|
789
|
+
setState,
|
|
790
|
+
onStateChange,
|
|
791
|
+
batchState,
|
|
792
|
+
clearState,
|
|
793
|
+
getStoreMetrics
|
|
794
|
+
} from 'wu-framework';
|
|
795
|
+
|
|
796
|
+
// Establecer estado (dot notation)
|
|
797
|
+
setState('user.name', 'John');
|
|
798
|
+
setState('user.preferences.theme', 'dark');
|
|
799
|
+
|
|
800
|
+
// Obtener estado
|
|
801
|
+
const name = getState('user.name'); // 'John'
|
|
802
|
+
const user = getState('user'); // { name: 'John', preferences: { theme: 'dark' } }
|
|
803
|
+
|
|
804
|
+
// Suscribirse a cambios
|
|
805
|
+
const unsubscribe = onStateChange('user.name', (value, path) => {
|
|
806
|
+
console.log(`${path} changed to:`, value);
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
// Suscribirse con wildcards
|
|
810
|
+
onStateChange('user.*', (value, path) => {
|
|
811
|
+
console.log(`User property changed: ${path} =`, value);
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
// Batch updates (mas eficiente)
|
|
409
815
|
batchState({
|
|
410
|
-
'user.name': '
|
|
411
|
-
'
|
|
412
|
-
|
|
816
|
+
'user.name': 'Jane',
|
|
817
|
+
'user.email': 'jane@example.com',
|
|
818
|
+
'settings.notifications': true
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
// Metricas de performance
|
|
822
|
+
const metrics = getStoreMetrics();
|
|
413
823
|
```
|
|
414
824
|
|
|
415
|
-
|
|
825
|
+
---
|
|
416
826
|
|
|
417
|
-
|
|
418
|
-
```tsx
|
|
419
|
-
import { createUseWuEvents, createUseWuStore } from 'wu-framework/adapters/react'
|
|
827
|
+
## Sistema de Plugins
|
|
420
828
|
|
|
421
|
-
|
|
422
|
-
|
|
829
|
+
```js
|
|
830
|
+
import { usePlugin, createPlugin, uninstallPlugin } from 'wu-framework';
|
|
423
831
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
832
|
+
const analyticsPlugin = createPlugin({
|
|
833
|
+
name: 'analytics',
|
|
834
|
+
permissions: ['events', 'store'],
|
|
427
835
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
836
|
+
install: (api, options) => {
|
|
837
|
+
api.on('app:mounted', (event) => {
|
|
838
|
+
trackEvent('mount', event.data.appName);
|
|
839
|
+
});
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
beforeMount: async (context) => {
|
|
843
|
+
console.log('Before mount:', context.appName);
|
|
844
|
+
},
|
|
845
|
+
|
|
846
|
+
afterMount: async (context) => {
|
|
847
|
+
console.log('Mounted in', context.mountTime, 'ms');
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
usePlugin(analyticsPlugin, { trackingId: 'UA-123456' });
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
---
|
|
855
|
+
|
|
856
|
+
## Lifecycle Hooks
|
|
857
|
+
|
|
858
|
+
```js
|
|
859
|
+
import { useHook, createGuardHook, createTimedHook } from 'wu-framework';
|
|
860
|
+
|
|
861
|
+
// Hook basico
|
|
862
|
+
useHook('beforeMount', async (context, next) => {
|
|
863
|
+
console.log('Before mounting:', context.appName);
|
|
864
|
+
await next();
|
|
865
|
+
}, { name: 'my-hook', priority: 10 });
|
|
866
|
+
|
|
867
|
+
// Guard hook (puede cancelar)
|
|
868
|
+
const authGuard = createGuardHook(async (ctx) => {
|
|
869
|
+
return await isUserAuthenticated();
|
|
870
|
+
});
|
|
871
|
+
useHook('beforeMount', authGuard);
|
|
872
|
+
|
|
873
|
+
// Hook con timeout
|
|
874
|
+
const timedHook = createTimedHook(async (ctx) => {
|
|
875
|
+
await slowOperation();
|
|
876
|
+
}, 5000);
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
### Fases Disponibles
|
|
880
|
+
|
|
881
|
+
| Fase | Descripcion |
|
|
882
|
+
|------|-------------|
|
|
883
|
+
| `beforeInit` | Antes de inicializar framework |
|
|
884
|
+
| `afterInit` | Despues de inicializar |
|
|
885
|
+
| `beforeLoad` | Antes de cargar una app |
|
|
886
|
+
| `afterLoad` | Despues de cargar |
|
|
887
|
+
| `beforeMount` | Antes de montar |
|
|
888
|
+
| `afterMount` | Despues de montar |
|
|
889
|
+
| `beforeUnmount` | Antes de desmontar |
|
|
890
|
+
| `afterUnmount` | Despues de desmontar |
|
|
891
|
+
|
|
892
|
+
---
|
|
893
|
+
|
|
894
|
+
## Loading Strategies
|
|
895
|
+
|
|
896
|
+
```js
|
|
897
|
+
await wu.init({
|
|
898
|
+
apps: [
|
|
899
|
+
{ name: 'header', url: '/mfe/header', strategy: 'lazy' },
|
|
900
|
+
{ name: 'sidebar', url: '/mfe/sidebar', strategy: 'eager' },
|
|
901
|
+
{ name: 'footer', url: '/mfe/footer', strategy: 'preload' },
|
|
902
|
+
{ name: 'analytics', url: '/mfe/analytics', strategy: 'idle' }
|
|
903
|
+
]
|
|
904
|
+
});
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
| Estrategia | Descripcion | Uso recomendado |
|
|
908
|
+
|------------|-------------|-----------------|
|
|
909
|
+
| `lazy` | Carga cuando se monta (default) | Contenido below-the-fold |
|
|
910
|
+
| `eager` | Precarga inmediata | Componentes criticos |
|
|
911
|
+
| `preload` | Usa `<link rel="prefetch">` | Navegacion anticipada |
|
|
912
|
+
| `idle` | Usa `requestIdleCallback` | Analytics, features secundarias |
|
|
913
|
+
|
|
914
|
+
---
|
|
431
915
|
|
|
432
|
-
|
|
916
|
+
## Performance Monitoring
|
|
917
|
+
|
|
918
|
+
```js
|
|
919
|
+
import {
|
|
920
|
+
startMeasure,
|
|
921
|
+
endMeasure,
|
|
922
|
+
getPerformanceMetrics,
|
|
923
|
+
generatePerformanceReport
|
|
924
|
+
} from 'wu-framework';
|
|
925
|
+
|
|
926
|
+
// Medicion personalizada
|
|
927
|
+
startMeasure('data-fetch', 'my-app');
|
|
928
|
+
await fetchData();
|
|
929
|
+
const duration = endMeasure('data-fetch', 'my-app');
|
|
930
|
+
|
|
931
|
+
// Reporte completo
|
|
932
|
+
const report = generatePerformanceReport();
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
---
|
|
936
|
+
|
|
937
|
+
## Error Handling
|
|
938
|
+
|
|
939
|
+
```js
|
|
940
|
+
import { registerErrorHandler, configureErrorBoundary } from 'wu-framework';
|
|
941
|
+
|
|
942
|
+
registerErrorHandler({
|
|
943
|
+
name: 'api-error',
|
|
944
|
+
canHandle: (error) => error.message.includes('API'),
|
|
945
|
+
handle: async (error, context) => {
|
|
946
|
+
if (context.retryCount < 3) {
|
|
947
|
+
return { recovered: true, action: 'retry' };
|
|
948
|
+
}
|
|
949
|
+
return { recovered: false, action: 'fallback' };
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
configureErrorBoundary({
|
|
954
|
+
maxRetries: 3,
|
|
955
|
+
retryDelay: 1000,
|
|
956
|
+
showErrorUI: true
|
|
957
|
+
});
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
---
|
|
961
|
+
|
|
962
|
+
## API Simplificada (wu.app)
|
|
963
|
+
|
|
964
|
+
```js
|
|
965
|
+
import { wu } from 'wu-framework';
|
|
966
|
+
|
|
967
|
+
const header = wu.app('header', {
|
|
968
|
+
url: 'http://localhost:3001',
|
|
969
|
+
container: '#header-container'
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
await header.mount();
|
|
973
|
+
console.log(header.isMounted); // true
|
|
974
|
+
|
|
975
|
+
await header.unmount();
|
|
976
|
+
await header.remount();
|
|
977
|
+
await header.reload(); // Limpia cache
|
|
978
|
+
|
|
979
|
+
const health = await header.verify();
|
|
980
|
+
await header.destroy();
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
---
|
|
984
|
+
|
|
985
|
+
## Sistema de Build
|
|
986
|
+
|
|
987
|
+
Wu Framework incluye un sistema de build completo para proteger el codigo.
|
|
988
|
+
|
|
989
|
+
### Comandos Disponibles
|
|
990
|
+
|
|
991
|
+
```bash
|
|
992
|
+
# Instalar dependencias
|
|
993
|
+
npm install
|
|
994
|
+
|
|
995
|
+
# Build completo (todos los formatos)
|
|
996
|
+
npm run build
|
|
997
|
+
|
|
998
|
+
# Builds individuales
|
|
999
|
+
npm run build:dev # Desarrollo (sin minificar)
|
|
1000
|
+
npm run build:prod # Produccion (minificado)
|
|
1001
|
+
npm run build:obf # Ofuscado
|
|
1002
|
+
npm run build:umd # UMD para browsers
|
|
1003
|
+
npm run build:cjs # CommonJS para Node
|
|
1004
|
+
|
|
1005
|
+
# Build protegido (pipeline completo)
|
|
1006
|
+
npm run build:protected # Bundle -> Minify -> Obfuscate -> Base64
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### Pipeline de Proteccion
|
|
1010
|
+
|
|
1011
|
+
```
|
|
1012
|
+
Source Code
|
|
1013
|
+
|
|
|
1014
|
+
v
|
|
1015
|
+
[1] Bundle (Rollup) -> Empaqueta todos los modulos
|
|
1016
|
+
|
|
|
1017
|
+
v
|
|
1018
|
+
[2] Minify (Terser) -> Reduce ~60% del tamanio
|
|
1019
|
+
|
|
|
1020
|
+
v
|
|
1021
|
+
[3] Obfuscate -> Codigo ilegible
|
|
1022
|
+
|
|
|
1023
|
+
v
|
|
1024
|
+
[4] Base64 Encode -> Capa adicional de proteccion
|
|
1025
|
+
|
|
|
1026
|
+
v
|
|
1027
|
+
Output: wu-framework.protected.js (~250KB)
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
### Archivos Generados
|
|
1031
|
+
|
|
1032
|
+
| Archivo | Formato | Uso |
|
|
1033
|
+
|---------|---------|-----|
|
|
1034
|
+
| `wu-framework.dev.js` | ES Module | Desarrollo |
|
|
1035
|
+
| `wu-framework.min.js` | ES Module | Produccion |
|
|
1036
|
+
| `wu-framework.obf.js` | ES Module | Produccion protegida |
|
|
1037
|
+
| `wu-framework.protected.js` | ES Module | Maxima proteccion |
|
|
1038
|
+
| `wu-framework.umd.js` | UMD | Browsers via CDN |
|
|
1039
|
+
| `wu-framework.cjs.js` | CommonJS | Node.js |
|
|
1040
|
+
|
|
1041
|
+
---
|
|
1042
|
+
|
|
1043
|
+
## Sandbox System
|
|
1044
|
+
|
|
1045
|
+
Wu Framework usa dos estrategias de aislamiento JS:
|
|
1046
|
+
|
|
1047
|
+
### Proxy Sandbox (Default)
|
|
1048
|
+
|
|
1049
|
+
- Usa ES6 Proxy para interceptar acceso a globales
|
|
1050
|
+
- Aislamiento completo sin side effects
|
|
1051
|
+
- Requiere soporte de Proxy (navegadores modernos)
|
|
1052
|
+
|
|
1053
|
+
### Snapshot Sandbox (Fallback)
|
|
1054
|
+
|
|
1055
|
+
- Toma snapshots del estado global
|
|
1056
|
+
- Restaura al desmontar
|
|
1057
|
+
- Compatible con navegadores legacy
|
|
1058
|
+
|
|
1059
|
+
```js
|
|
1060
|
+
// El framework detecta automaticamente la mejor estrategia
|
|
1061
|
+
const stats = wu.sandbox.getStats();
|
|
1062
|
+
console.log(stats.strategy); // 'proxy' o 'snapshot'
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
---
|
|
1066
|
+
|
|
1067
|
+
## Deployment
|
|
1068
|
+
|
|
1069
|
+
### Estructura de Produccion
|
|
1070
|
+
|
|
1071
|
+
```
|
|
1072
|
+
https://cdn.mycompany.com/
|
|
1073
|
+
βββ mfe/
|
|
1074
|
+
β βββ header/
|
|
1075
|
+
β β βββ wu.json
|
|
1076
|
+
β β βββ index.js
|
|
1077
|
+
β β βββ assets/
|
|
1078
|
+
β βββ sidebar/
|
|
1079
|
+
β β βββ wu.json
|
|
1080
|
+
β β βββ index.js
|
|
1081
|
+
β β βββ assets/
|
|
1082
|
+
β βββ content/
|
|
1083
|
+
β βββ wu.json
|
|
1084
|
+
β βββ index.js
|
|
1085
|
+
β βββ assets/
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
### Configuracion por Entorno
|
|
1089
|
+
|
|
1090
|
+
```js
|
|
1091
|
+
const config = {
|
|
1092
|
+
development: {
|
|
1093
|
+
header: 'http://localhost:3001',
|
|
1094
|
+
sidebar: 'http://localhost:3002',
|
|
1095
|
+
content: 'http://localhost:3003'
|
|
1096
|
+
},
|
|
1097
|
+
staging: {
|
|
1098
|
+
header: 'https://staging-cdn.mycompany.com/mfe/header',
|
|
1099
|
+
sidebar: 'https://staging-cdn.mycompany.com/mfe/sidebar',
|
|
1100
|
+
content: 'https://staging-cdn.mycompany.com/mfe/content'
|
|
1101
|
+
},
|
|
1102
|
+
production: {
|
|
1103
|
+
header: 'https://cdn.mycompany.com/mfe/header',
|
|
1104
|
+
sidebar: 'https://cdn.mycompany.com/mfe/sidebar',
|
|
1105
|
+
content: 'https://cdn.mycompany.com/mfe/content'
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
const env = process.env.NODE_ENV || 'development';
|
|
1110
|
+
const urls = config[env];
|
|
1111
|
+
|
|
1112
|
+
await wu.init({
|
|
1113
|
+
apps: [
|
|
1114
|
+
{ name: 'header', url: urls.header },
|
|
1115
|
+
{ name: 'sidebar', url: urls.sidebar },
|
|
1116
|
+
{ name: 'content', url: urls.content }
|
|
1117
|
+
]
|
|
1118
|
+
});
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
### CORS Configuration
|
|
1122
|
+
|
|
1123
|
+
```nginx
|
|
1124
|
+
location /mfe/ {
|
|
1125
|
+
add_header Access-Control-Allow-Origin *;
|
|
1126
|
+
add_header Access-Control-Allow-Methods "GET, OPTIONS";
|
|
1127
|
+
add_header Access-Control-Allow-Headers "Content-Type";
|
|
433
1128
|
}
|
|
434
1129
|
```
|
|
435
1130
|
|
|
436
|
-
|
|
437
|
-
```vue
|
|
438
|
-
<script setup>
|
|
439
|
-
import { useWuEvents, useWuStore } from 'wu-framework/adapters/vue'
|
|
1131
|
+
### Versionado
|
|
440
1132
|
|
|
441
|
-
|
|
442
|
-
|
|
1133
|
+
```json
|
|
1134
|
+
{
|
|
1135
|
+
"name": "header",
|
|
1136
|
+
"version": "1.2.0",
|
|
1137
|
+
"entry": "index.js"
|
|
1138
|
+
}
|
|
1139
|
+
```
|
|
443
1140
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
1141
|
+
```js
|
|
1142
|
+
await wu.init({
|
|
1143
|
+
apps: [
|
|
1144
|
+
{ name: 'header', url: 'https://cdn.mycompany.com/mfe/header/v1.2.0' },
|
|
1145
|
+
{ name: 'sidebar', url: 'https://cdn.mycompany.com/mfe/sidebar/v2.0.0' }
|
|
1146
|
+
]
|
|
1147
|
+
});
|
|
448
1148
|
```
|
|
449
1149
|
|
|
450
1150
|
---
|
|
451
1151
|
|
|
452
|
-
##
|
|
1152
|
+
## Arquitectura
|
|
453
1153
|
|
|
454
1154
|
```
|
|
455
1155
|
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
456
|
-
β
|
|
457
|
-
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
458
|
-
β β ToastContainer (escucha notification:new) β β
|
|
459
|
-
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
1156
|
+
β HOST APPLICATION β
|
|
460
1157
|
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
|
|
461
|
-
β βββββββββββββββββββ
|
|
462
|
-
β β #shadow-root β
|
|
463
|
-
β β βββββββββββββ β
|
|
464
|
-
β β β Header β β
|
|
465
|
-
β β β (
|
|
466
|
-
β β β
|
|
467
|
-
β β
|
|
468
|
-
β β
|
|
469
|
-
β
|
|
470
|
-
β βββββββββββββββββββ βββββββββββββββββββββββ β
|
|
1158
|
+
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββ β
|
|
1159
|
+
β β #shadow-root β β #shadow-root β β #shadow-rootβ β
|
|
1160
|
+
β β βββββββββββββ β β βββββββββββββ β β βββββββββββ β β
|
|
1161
|
+
β β β Header β β β β Sidebar β β β β Content β β β
|
|
1162
|
+
β β β (React) β β β β (Vue) β β β β(Angular)β β β
|
|
1163
|
+
β β βββββββββββββ β β βββββββββββββ β β βββββββββββ β β
|
|
1164
|
+
β β CSS Isolated β β CSS Isolated β β CSS Isolatedβ β
|
|
1165
|
+
β β JS Sandboxed β β JS Sandboxed β β JS Sandboxedβ β
|
|
1166
|
+
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββ β
|
|
471
1167
|
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
|
|
472
|
-
β WU FRAMEWORK CORE
|
|
473
|
-
β ββββββββββββ ββββββββββββ
|
|
474
|
-
β β EventBus β β Store β β
|
|
475
|
-
β ββββββββββββ ββββββββββββ
|
|
1168
|
+
β WU FRAMEWORK CORE β
|
|
1169
|
+
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββββ β
|
|
1170
|
+
β β EventBus β β Store β β Plugins β β Sandbox Pool β β
|
|
1171
|
+
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββββ β
|
|
1172
|
+
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββββ β
|
|
1173
|
+
β β Loader β β Manifest β β Hooks β β Style Bridge β β
|
|
1174
|
+
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββββ β
|
|
476
1175
|
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
477
1176
|
```
|
|
478
1177
|
|
|
479
|
-
**Flujo de datos:**
|
|
480
|
-
1. React Dashboard (content) llama `emit('notification:new', {...})`
|
|
481
|
-
2. Wu EventBus propaga a todos los suscriptores
|
|
482
|
-
3. Vue NavBar (header) incrementa badge
|
|
483
|
-
4. React ToastContainer (shell) muestra toast flotante
|
|
484
|
-
5. Store compartido actualiza `notifications.count`
|
|
485
|
-
|
|
486
1178
|
---
|
|
487
1179
|
|
|
488
|
-
##
|
|
1180
|
+
## API Reference
|
|
489
1181
|
|
|
490
|
-
|
|
491
|
-
|-----------|---------|----------|
|
|
492
|
-
| βοΈ React | `wuReact` | `wuReact.register('app', App)` |
|
|
493
|
-
| π Vue 3 | `wuVue` | `wuVue.register('app', App)` |
|
|
494
|
-
| π
°οΈ Angular | `wuAngular` | `wuAngular.register('app', AppModule)` |
|
|
495
|
-
| π§‘ Svelte | `wuSvelte` | `wuSvelte.register('app', App)` |
|
|
496
|
-
| β‘ Preact | `wuPreact` | `wuPreact.register('app', App)` |
|
|
497
|
-
| π Solid.js | `wuSolid` | `wuSolid.register('app', App)` |
|
|
498
|
-
| π₯ Lit | `wuLit` | `wuLit.register('app', MyElement)` |
|
|
499
|
-
| π¦ Vanilla | `wuVanilla` | `wuVanilla.register('app', config)` |
|
|
1182
|
+
### Core
|
|
500
1183
|
|
|
501
|
-
|
|
1184
|
+
```js
|
|
1185
|
+
import { wu, init, mount, unmount, destroy, getStats } from 'wu-framework';
|
|
1186
|
+
```
|
|
502
1187
|
|
|
503
|
-
|
|
1188
|
+
### Event Bus
|
|
504
1189
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
| **WuSandbox** | Proxy Sandbox para JS isolation, cleanup automΓ‘tico |
|
|
509
|
-
| **WuEventBus** | Wildcards, replay, mΓ©tricas |
|
|
510
|
-
| **WuStore** | Dot notation, wildcard subscriptions, batch |
|
|
511
|
-
| **WuCache** | Cache de manifests y mΓ³dulos |
|
|
512
|
-
| **WuPerformance** | Tiempos de mount/unmount, reportes |
|
|
513
|
-
| **WuErrorBoundary** | Auto-retry, fallback UI |
|
|
514
|
-
| **WuPluginSystem** | Lifecycle hooks, middleware |
|
|
1190
|
+
```js
|
|
1191
|
+
import { emit, on, once, off, replayEvents, getEventBusStats } from 'wu-framework';
|
|
1192
|
+
```
|
|
515
1193
|
|
|
516
|
-
|
|
1194
|
+
### Store
|
|
517
1195
|
|
|
518
|
-
|
|
1196
|
+
```js
|
|
1197
|
+
import { getState, setState, onStateChange, batchState, clearState } from 'wu-framework';
|
|
1198
|
+
```
|
|
519
1199
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
1200
|
+
### Plugins & Hooks
|
|
1201
|
+
|
|
1202
|
+
```js
|
|
1203
|
+
import { usePlugin, createPlugin, useHook, removeHook } from 'wu-framework';
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
### Performance & Errors
|
|
1207
|
+
|
|
1208
|
+
```js
|
|
1209
|
+
import { startMeasure, endMeasure, generatePerformanceReport } from 'wu-framework';
|
|
1210
|
+
import { registerErrorHandler, configureErrorBoundary } from 'wu-framework';
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
### Style Management
|
|
1214
|
+
|
|
1215
|
+
```js
|
|
1216
|
+
import { configureStyleSharing, reinjectStyles, getStyleStats } from 'wu-framework';
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
### Adapters
|
|
1220
|
+
|
|
1221
|
+
```js
|
|
1222
|
+
import { wuReact } from 'wu-framework/adapters/react';
|
|
1223
|
+
import { wuVue } from 'wu-framework/adapters/vue';
|
|
1224
|
+
import { wuAngular } from 'wu-framework/adapters/angular';
|
|
1225
|
+
import { wuSvelte } from 'wu-framework/adapters/svelte';
|
|
1226
|
+
import { wuPreact } from 'wu-framework/adapters/preact';
|
|
1227
|
+
import { wuSolid } from 'wu-framework/adapters/solid';
|
|
1228
|
+
import { wuLit } from 'wu-framework/adapters/lit';
|
|
1229
|
+
import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
1230
|
+
```
|
|
523
1231
|
|
|
524
|
-
|
|
525
|
-
import { emit, on, once, off, replayEvents } from 'wu-framework'
|
|
1232
|
+
### Utilities
|
|
526
1233
|
|
|
527
|
-
|
|
528
|
-
import {
|
|
1234
|
+
```js
|
|
1235
|
+
import { presets, dev, events } from 'wu-framework';
|
|
529
1236
|
|
|
530
|
-
//
|
|
531
|
-
|
|
1237
|
+
// Presets
|
|
1238
|
+
await wu.init(presets.development([{ name: 'app', port: 3001 }]));
|
|
1239
|
+
await wu.init(presets.production([{ name: 'app', url: 'https://...' }]));
|
|
532
1240
|
|
|
533
|
-
//
|
|
534
|
-
|
|
1241
|
+
// Dev tools
|
|
1242
|
+
dev.enableDebug();
|
|
1243
|
+
dev.inspect();
|
|
1244
|
+
await dev.reload('app');
|
|
535
1245
|
|
|
536
|
-
//
|
|
537
|
-
|
|
538
|
-
|
|
1246
|
+
// Silenciar logs
|
|
1247
|
+
wu.silence();
|
|
1248
|
+
wu.verbose();
|
|
539
1249
|
```
|
|
540
1250
|
|
|
541
1251
|
---
|
|
542
1252
|
|
|
543
|
-
##
|
|
1253
|
+
## Contributing
|
|
1254
|
+
|
|
1255
|
+
Las contribuciones son bienvenidas!
|
|
1256
|
+
|
|
1257
|
+
1. **Fork** el repositorio
|
|
1258
|
+
2. **Crear** branch (`git checkout -b feature/amazing-feature`)
|
|
1259
|
+
3. **Commit** tus cambios (`git commit -m 'Add amazing feature'`)
|
|
1260
|
+
4. **Push** al branch (`git push origin feature/amazing-feature`)
|
|
1261
|
+
5. **Abrir** un Pull Request
|
|
1262
|
+
|
|
1263
|
+
---
|
|
1264
|
+
|
|
1265
|
+
## License
|
|
544
1266
|
|
|
545
1267
|
MIT License - Copyright (c) 2025 Wu Framework Team
|
|
546
1268
|
|
|
547
1269
|
---
|
|
548
1270
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
<i>Shadow DOM β’ 4 CSS Isolation Modes β’ Cross-MFE Events & Store β’ Zero Config</i>
|
|
553
|
-
</p>
|
|
1271
|
+
**Wu Framework - Universal Microfrontends Made Simple**
|
|
1272
|
+
|
|
1273
|
+
*Zero dependencies - 8 Frameworks - Shadow DOM - Proxy Sandbox - Self-Healing - Build Protection*
|