wu-framework 1.1.0 → 1.1.2

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 CHANGED
@@ -46,26 +46,36 @@ Wu Framework incluye **adapters nativos** para los frameworks más populares:
46
46
 
47
47
  ## 🔥 Quick Start
48
48
 
49
- ### 1. Shell (Host Application)
50
-
51
- ```js
52
- import { wu } from 'wu-framework';
53
-
54
- // Inicializar y montar microfrontends
55
- await wu.init({
56
- apps: [
57
- { name: 'header', url: 'http://localhost:3001' },
58
- { name: 'sidebar', url: 'http://localhost:3002' },
59
- { name: 'content', url: 'http://localhost:3003' }
60
- ]
61
- });
62
-
63
- await wu.mount('header', '#header-container');
64
- await wu.mount('sidebar', '#sidebar-container');
65
- await wu.mount('content', '#content-container');
49
+ ### 1. Micro App: Crear `wu.json`
50
+
51
+ Cada microfrontend necesita un archivo `wu.json` en su raíz:
52
+
53
+ ```json
54
+ {
55
+ "name": "header",
56
+ "entry": "index.js",
57
+ "wu": {
58
+ "exports": {
59
+ "NavBar": "components/NavBar.js",
60
+ "UserMenu": "components/UserMenu.js"
61
+ },
62
+ "imports": [],
63
+ "routes": ["/", "/home"],
64
+ "permissions": ["events", "store"]
65
+ }
66
+ }
66
67
  ```
67
68
 
68
- ### 2. Micro App (1 línea de código)
69
+ | Campo | Descripción |
70
+ |-------|-------------|
71
+ | `name` | Nombre único del microfrontend |
72
+ | `entry` | Archivo JS principal (default: `index.js`) |
73
+ | `wu.exports` | Componentes que expone a otros microfrontends |
74
+ | `wu.imports` | Componentes que importa de otros microfrontends |
75
+ | `wu.routes` | Rutas que maneja este microfrontend |
76
+ | `wu.permissions` | Permisos requeridos (`events`, `store`, `dom`) |
77
+
78
+ ### 2. Micro App: Registrar con Adapter (1 línea)
69
79
 
70
80
  **React:**
71
81
  ```tsx
@@ -93,6 +103,70 @@ wuAngular.register('content', AppModule);
93
103
 
94
104
  **¡Eso es todo!** El adapter se encarga de todo: detección de contexto, modo standalone, cleanup automático.
95
105
 
106
+ ### 3. Shell (Host Application)
107
+
108
+ **Desarrollo:**
109
+ ```js
110
+ import { wu } from 'wu-framework';
111
+
112
+ await wu.init({
113
+ apps: [
114
+ { name: 'header', url: 'http://localhost:3001' },
115
+ { name: 'sidebar', url: 'http://localhost:3002' },
116
+ { name: 'content', url: 'http://localhost:3003' }
117
+ ]
118
+ });
119
+
120
+ await wu.mount('header', '#header-container');
121
+ await wu.mount('sidebar', '#sidebar-container');
122
+ await wu.mount('content', '#content-container');
123
+ ```
124
+
125
+ **Producción:**
126
+ ```js
127
+ import { wu } from 'wu-framework';
128
+
129
+ await wu.init({
130
+ apps: [
131
+ { name: 'header', url: 'https://cdn.mycompany.com/mfe/header' },
132
+ { name: 'sidebar', url: 'https://cdn.mycompany.com/mfe/sidebar' },
133
+ { name: 'content', url: 'https://cdn.mycompany.com/mfe/content' }
134
+ ]
135
+ });
136
+
137
+ await wu.mount('header', '#header-container');
138
+ await wu.mount('sidebar', '#sidebar-container');
139
+ await wu.mount('content', '#content-container');
140
+ ```
141
+
142
+ **Con configuración dinámica:**
143
+ ```js
144
+ import { wu } from 'wu-framework';
145
+
146
+ const isDev = process.env.NODE_ENV !== 'production';
147
+
148
+ await wu.init({
149
+ apps: [
150
+ { name: 'header', url: isDev ? 'http://localhost:3001' : 'https://cdn.mycompany.com/mfe/header' },
151
+ { name: 'sidebar', url: isDev ? 'http://localhost:3002' : 'https://cdn.mycompany.com/mfe/sidebar' },
152
+ { name: 'content', url: isDev ? 'http://localhost:3003' : 'https://cdn.mycompany.com/mfe/content' }
153
+ ]
154
+ });
155
+ ```
156
+
157
+ ### Estructura de Archivos (Microfrontend)
158
+
159
+ ```
160
+ my-header-mfe/
161
+ ├── wu.json # ← Manifest requerido
162
+ ├── index.js # ← Entry point (registra con adapter)
163
+ ├── App.jsx # ← Componente principal
164
+ ├── components/
165
+ │ ├── NavBar.js
166
+ │ └── UserMenu.js
167
+ └── package.json
168
+ ```
169
+
96
170
  ---
97
171
 
98
172
  ## ⚛️ React Adapter
@@ -155,16 +229,20 @@ import { createWuSlot } from 'wu-framework/adapters/react';
155
229
 
156
230
  const WuSlot = createWuSlot(React);
157
231
 
232
+ // URLs pueden ser localhost (dev) o CDN (prod)
233
+ const HEADER_URL = process.env.REACT_APP_HEADER_URL || 'http://localhost:3001';
234
+ const CONTENT_URL = process.env.REACT_APP_CONTENT_URL || 'http://localhost:3002';
235
+
158
236
  function Shell() {
159
237
  return (
160
238
  <div>
161
239
  <WuSlot
162
240
  name="header"
163
- url="http://localhost:3001"
241
+ url={HEADER_URL}
164
242
  onLoad={() => console.log('Header loaded!')}
165
243
  onError={(err) => console.error(err)}
166
244
  />
167
- <WuSlot name="content" url="http://localhost:3002" />
245
+ <WuSlot name="content" url={CONTENT_URL} />
168
246
  </div>
169
247
  );
170
248
  }
@@ -229,17 +307,21 @@ onUnmounted(() => cleanup());
229
307
  ```vue
230
308
  <script setup>
231
309
  import { WuSlot } from 'wu-framework/adapters/vue';
310
+
311
+ // URLs: localhost en dev, CDN en prod
312
+ const headerUrl = import.meta.env.VITE_HEADER_URL || 'http://localhost:3001';
313
+ const sidebarUrl = import.meta.env.VITE_SIDEBAR_URL || 'http://localhost:3002';
232
314
  </script>
233
315
 
234
316
  <template>
235
317
  <div>
236
318
  <WuSlot
237
319
  name="header"
238
- url="http://localhost:3001"
320
+ :url="headerUrl"
239
321
  @load="onLoad"
240
322
  @error="onError"
241
323
  />
242
- <WuSlot name="sidebar" url="http://localhost:3002" />
324
+ <WuSlot name="sidebar" :url="sidebarUrl" />
243
325
  </div>
244
326
  </template>
245
327
  ```
@@ -770,14 +852,21 @@ const timedHook = createTimedHook(async (ctx) => {
770
852
  ```js
771
853
  await wu.init({
772
854
  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
855
+ { name: 'header', url: '/mfe/header', strategy: 'lazy' }, // Default - carga cuando se monta
856
+ { name: 'sidebar', url: '/mfe/sidebar', strategy: 'eager' }, // Precarga inmediata
857
+ { name: 'footer', url: '/mfe/footer', strategy: 'preload' }, // <link prefetch>
858
+ { name: 'analytics', url: '/mfe/analytics', strategy: 'idle' } // requestIdleCallback
777
859
  ]
778
860
  });
779
861
  ```
780
862
 
863
+ | Estrategia | Descripción | Uso recomendado |
864
+ |------------|-------------|-----------------|
865
+ | `lazy` | Carga cuando se monta (default) | Contenido below-the-fold |
866
+ | `eager` | Precarga inmediata | Componentes críticos |
867
+ | `preload` | Usa `<link rel="prefetch">` | Navegación anticipada |
868
+ | `idle` | Usa `requestIdleCallback` | Analytics, features secundarias |
869
+
781
870
  ---
782
871
 
783
872
  ## ⚡ Performance Monitoring
@@ -831,8 +920,9 @@ configureErrorBoundary({
831
920
  ```js
832
921
  import { wu } from 'wu-framework';
833
922
 
923
+ // URL puede ser localhost (dev) o CDN (prod)
834
924
  const header = wu.app('header', {
835
- url: 'http://localhost:3001',
925
+ url: 'http://localhost:3001', // o 'https://cdn.mycompany.com/mfe/header'
836
926
  container: '#header-container'
837
927
  });
838
928
 
@@ -849,6 +939,99 @@ await header.destroy();
849
939
 
850
940
  ---
851
941
 
942
+ ## 🚀 Deployment
943
+
944
+ ### Estructura de Producción
945
+
946
+ Cada microfrontend se despliega de forma independiente:
947
+
948
+ ```
949
+ https://cdn.mycompany.com/
950
+ ├── mfe/
951
+ │ ├── header/
952
+ │ │ ├── wu.json # Manifest
953
+ │ │ ├── index.js # Entry point
954
+ │ │ └── assets/
955
+ │ ├── sidebar/
956
+ │ │ ├── wu.json
957
+ │ │ ├── index.js
958
+ │ │ └── assets/
959
+ │ └── content/
960
+ │ ├── wu.json
961
+ │ ├── index.js
962
+ │ └── assets/
963
+ ```
964
+
965
+ ### Configuración por Entorno
966
+
967
+ ```js
968
+ // config.js
969
+ const config = {
970
+ development: {
971
+ header: 'http://localhost:3001',
972
+ sidebar: 'http://localhost:3002',
973
+ content: 'http://localhost:3003'
974
+ },
975
+ staging: {
976
+ header: 'https://staging-cdn.mycompany.com/mfe/header',
977
+ sidebar: 'https://staging-cdn.mycompany.com/mfe/sidebar',
978
+ content: 'https://staging-cdn.mycompany.com/mfe/content'
979
+ },
980
+ production: {
981
+ header: 'https://cdn.mycompany.com/mfe/header',
982
+ sidebar: 'https://cdn.mycompany.com/mfe/sidebar',
983
+ content: 'https://cdn.mycompany.com/mfe/content'
984
+ }
985
+ };
986
+
987
+ const env = process.env.NODE_ENV || 'development';
988
+ const urls = config[env];
989
+
990
+ await wu.init({
991
+ apps: [
992
+ { name: 'header', url: urls.header },
993
+ { name: 'sidebar', url: urls.sidebar },
994
+ { name: 'content', url: urls.content }
995
+ ]
996
+ });
997
+ ```
998
+
999
+ ### CORS Configuration
1000
+
1001
+ Los microfrontends deben permitir CORS desde el shell:
1002
+
1003
+ ```nginx
1004
+ # nginx.conf para CDN
1005
+ location /mfe/ {
1006
+ add_header Access-Control-Allow-Origin *;
1007
+ add_header Access-Control-Allow-Methods "GET, OPTIONS";
1008
+ add_header Access-Control-Allow-Headers "Content-Type";
1009
+ }
1010
+ ```
1011
+
1012
+ ### Versionado
1013
+
1014
+ ```json
1015
+ // wu.json con versión
1016
+ {
1017
+ "name": "header",
1018
+ "version": "1.2.0",
1019
+ "entry": "index.js"
1020
+ }
1021
+ ```
1022
+
1023
+ ```js
1024
+ // Shell con versiones específicas
1025
+ await wu.init({
1026
+ apps: [
1027
+ { name: 'header', url: 'https://cdn.mycompany.com/mfe/header/v1.2.0' },
1028
+ { name: 'sidebar', url: 'https://cdn.mycompany.com/mfe/sidebar/v2.0.0' }
1029
+ ]
1030
+ });
1031
+ ```
1032
+
1033
+ ---
1034
+
852
1035
  ## 🏗️ Arquitectura
853
1036
 
854
1037
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wu-framework",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "🚀 Universal Microfrontends Framework - 8 frameworks, zero config, Shadow DOM isolation",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -23,9 +23,153 @@ const adapterState = {
23
23
  apps: new Map(),
24
24
  Vue: null,
25
25
  createApp: null,
26
- initialized: false
26
+ initialized: false,
27
+ collectedStyles: new Map() // Estilos recolectados por app
27
28
  };
28
29
 
30
+ /**
31
+ * 🎨 STYLE COLLECTOR: Captura estilos inyectados dinámicamente
32
+ * Soluciona el problema de estilos no disponibles en Shadow DOM
33
+ *
34
+ * Estrategia:
35
+ * 1. Captura estilos existentes (incluyendo los de Vite HMR)
36
+ * 2. Filtra por URL base del MFE para identificar estilos relevantes
37
+ * 3. Observa nuevos estilos durante el mount
38
+ */
39
+ function createStyleCollector(appName) {
40
+ const styles = [];
41
+ const capturedIds = new Set();
42
+ let observer = null;
43
+
44
+ // Capturar estilos existentes antes del mount
45
+ // Incluye estilos inyectados por Vite via data-vite-dev-id
46
+ const captureExistingStyles = () => {
47
+ const styleTags = document.querySelectorAll('style:not([data-wu-shared])');
48
+ styleTags.forEach(style => {
49
+ const content = style.textContent;
50
+ const viteId = style.getAttribute('data-vite-dev-id');
51
+
52
+ // Evitar duplicados
53
+ const styleId = viteId || content?.substring(0, 100);
54
+ if (capturedIds.has(styleId)) return;
55
+
56
+ if (content && content.trim().length > 0) {
57
+ // Capturar todos los estilos que no sean de wu-framework
58
+ styles.push({
59
+ type: 'existing',
60
+ content: content,
61
+ viteId: viteId
62
+ });
63
+ capturedIds.add(styleId);
64
+ }
65
+ });
66
+ return styles.length;
67
+ };
68
+
69
+ // Observar nuevos estilos agregados durante el mount
70
+ const startObserving = () => {
71
+ if (!window.MutationObserver) return;
72
+
73
+ observer = new MutationObserver((mutations) => {
74
+ for (const mutation of mutations) {
75
+ if (mutation.type === 'childList') {
76
+ mutation.addedNodes.forEach(node => {
77
+ if (node.tagName === 'STYLE' && !node.hasAttribute('data-wu-shared')) {
78
+ const content = node.textContent;
79
+ const viteId = node.getAttribute('data-vite-dev-id');
80
+
81
+ // Evitar duplicados
82
+ const styleId = viteId || content?.substring(0, 100);
83
+ if (capturedIds.has(styleId)) return;
84
+
85
+ if (content && content.trim().length > 0) {
86
+ styles.push({
87
+ type: 'dynamic',
88
+ content: content,
89
+ viteId: viteId
90
+ });
91
+ capturedIds.add(styleId);
92
+ console.log(`[WuVue] 🎨 Captured dynamic style for ${appName}`);
93
+ }
94
+ }
95
+ });
96
+ }
97
+ }
98
+ });
99
+
100
+ observer.observe(document.head, { childList: true, subtree: true });
101
+ };
102
+
103
+ const stopObserving = () => {
104
+ if (observer) {
105
+ observer.disconnect();
106
+ observer = null;
107
+ }
108
+ };
109
+
110
+ const getStyles = () => [...styles];
111
+
112
+ return {
113
+ captureExistingStyles,
114
+ startObserving,
115
+ stopObserving,
116
+ getStyles
117
+ };
118
+ }
119
+
120
+ /**
121
+ * 🎨 INJECT STYLES TO SHADOW: Inyecta estilos capturados al Shadow DOM
122
+ */
123
+ function injectStylesToShadow(shadowRoot, styles, appName) {
124
+ if (!shadowRoot || !styles || styles.length === 0) return 0;
125
+
126
+ let injected = 0;
127
+
128
+ for (const style of styles) {
129
+ // Verificar si ya existe
130
+ const viteId = style.viteId;
131
+ if (viteId) {
132
+ const existing = shadowRoot.querySelector(`style[data-wu-vite-id="${viteId}"]`);
133
+ if (existing) continue;
134
+ }
135
+
136
+ const styleTag = document.createElement('style');
137
+ styleTag.textContent = style.content;
138
+ styleTag.setAttribute('data-wu-shared', 'true');
139
+ styleTag.setAttribute('data-wu-app', appName);
140
+ if (viteId) {
141
+ styleTag.setAttribute('data-wu-vite-id', viteId);
142
+ }
143
+
144
+ shadowRoot.insertBefore(styleTag, shadowRoot.firstChild);
145
+ injected++;
146
+ }
147
+
148
+ if (injected > 0) {
149
+ console.log(`[WuVue] 🎨 Injected ${injected} styles into Shadow DOM for ${appName}`);
150
+ }
151
+
152
+ return injected;
153
+ }
154
+
155
+ /**
156
+ * 🔍 GET SHADOW ROOT: Obtiene el Shadow Root del contenedor
157
+ */
158
+ function getShadowRoot(container) {
159
+ // Buscar shadow root en la jerarquía
160
+ let current = container;
161
+ while (current) {
162
+ if (current.getRootNode && current.getRootNode() instanceof ShadowRoot) {
163
+ return current.getRootNode();
164
+ }
165
+ if (current.shadowRoot) {
166
+ return current.shadowRoot;
167
+ }
168
+ current = current.parentElement;
169
+ }
170
+ return null;
171
+ }
172
+
29
173
  /**
30
174
  * Detecta y obtiene Vue del contexto global o lo importa
31
175
  */
@@ -121,6 +265,7 @@ function waitForWu(timeout = 5000) {
121
265
  * @param {Function} options.onUnmount - Callback antes de desmontar
122
266
  * @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
123
267
  * @param {string} options.standaloneContainer - Selector para modo standalone (default: '#app')
268
+ * @param {string|string[]} options.styles - CSS string o array de CSS strings para inyectar en Shadow DOM
124
269
  *
125
270
  * @example
126
271
  * // Básico
@@ -135,6 +280,12 @@ function waitForWu(timeout = 5000) {
135
280
  * app.component('MyGlobal', MyComponent);
136
281
  * }
137
282
  * });
283
+ *
284
+ * @example
285
+ * // Con estilos explícitos para Shadow DOM
286
+ * wuVue.register('my-app', App, {
287
+ * styles: `.header { background: blue; }`
288
+ * });
138
289
  */
139
290
  async function register(appName, RootComponent, options = {}) {
140
291
  const {
@@ -143,7 +294,8 @@ async function register(appName, RootComponent, options = {}) {
143
294
  onMount = null,
144
295
  onUnmount = null,
145
296
  standalone = true,
146
- standaloneContainer = '#app'
297
+ standaloneContainer = '#app',
298
+ styles = null // 🎨 CSS string o array de CSS strings para inyectar en Shadow DOM
147
299
  } = options;
148
300
 
149
301
  // Asegurar que Vue está disponible
@@ -155,6 +307,9 @@ async function register(appName, RootComponent, options = {}) {
155
307
 
156
308
  const { createApp } = adapterState;
157
309
 
310
+ // 🎨 Crear collector de estilos para esta app
311
+ const styleCollector = createStyleCollector(appName);
312
+
158
313
  // Función de mount interna
159
314
  const mountApp = (container) => {
160
315
  if (!container) {
@@ -169,6 +324,13 @@ async function register(appName, RootComponent, options = {}) {
169
324
  }
170
325
 
171
326
  try {
327
+ // 🎨 Capturar estilos existentes ANTES del mount
328
+ const existingStylesCount = styleCollector.captureExistingStyles();
329
+ console.log(`[WuVue] 🎨 Captured ${existingStylesCount} existing styles for ${appName}`);
330
+
331
+ // 🎨 Comenzar a observar nuevos estilos
332
+ styleCollector.startObserving();
333
+
172
334
  // Crear la aplicación Vue
173
335
  const app = createApp(RootComponent, props);
174
336
 
@@ -184,6 +346,33 @@ async function register(appName, RootComponent, options = {}) {
184
346
  // Montar
185
347
  app.mount(container);
186
348
 
349
+ // 🎨 Detener observación
350
+ styleCollector.stopObserving();
351
+
352
+ // 🎨 Inyectar estilos en Shadow DOM
353
+ const shadowRoot = getShadowRoot(container);
354
+ if (shadowRoot) {
355
+ // 1. Inyectar estilos declarados explícitamente (opción `styles`)
356
+ if (styles) {
357
+ const styleArray = Array.isArray(styles) ? styles : [styles];
358
+ const explicitStyles = styleArray.map(css => ({ type: 'explicit', content: css }));
359
+ injectStylesToShadow(shadowRoot, explicitStyles, appName);
360
+ console.log(`[WuVue] 🎨 Injected ${explicitStyles.length} explicit styles for ${appName}`);
361
+ }
362
+
363
+ // 2. Inyectar estilos capturados dinámicamente
364
+ const collectedStyles = styleCollector.getStyles();
365
+ console.log(`[WuVue] 🎨 Total styles collected for ${appName}:`, collectedStyles.length);
366
+
367
+ // Inyectar estilos capturados
368
+ injectStylesToShadow(shadowRoot, collectedStyles, appName);
369
+
370
+ // Guardar referencia de estilos para re-inyección si es necesario
371
+ adapterState.collectedStyles.set(appName, collectedStyles);
372
+ } else {
373
+ console.log(`[WuVue] ℹ️ No Shadow DOM detected for ${appName}, styles applied normally`);
374
+ }
375
+
187
376
  // Guardar referencia
188
377
  adapterState.apps.set(appName, { app, container });
189
378
 
@@ -193,6 +382,7 @@ async function register(appName, RootComponent, options = {}) {
193
382
  onMount(container, app);
194
383
  }
195
384
  } catch (error) {
385
+ styleCollector.stopObserving();
196
386
  console.error(`[WuVue] Mount error for ${appName}:`, error);
197
387
  throw error;
198
388
  }
@@ -9,8 +9,13 @@ export class WuStyleBridge {
9
9
  constructor() {
10
10
  this.sharedStyles = new Map();
11
11
  this.styleObserver = null;
12
+ this.appStyleModes = new Map(); // Per-app style isolation mode
12
13
  this.config = {
13
- // Librerías que se deben compartir automáticamente
14
+ // 🛡️ DEFAULT MODE: 'isolated' = cada MFE tiene sus propios estilos
15
+ // 'shared' = compartir estilos del padre
16
+ // 'auto' = compartir solo librerías específicas
17
+ defaultMode: 'isolated',
18
+ // Librerías que se deben compartir automáticamente (solo en modo 'auto')
14
19
  autoShareLibraries: [
15
20
  'element-plus',
16
21
  'vue-flow',
@@ -20,20 +25,37 @@ export class WuStyleBridge {
20
25
  'normalize.css',
21
26
  'reset.css'
22
27
  ],
23
- // Patrones de URLs a compartir
28
+ // Patrones de URLs a compartir (solo en modo 'auto' o 'shared')
24
29
  sharePatterns: [
25
30
  /\/node_modules\//,
26
31
  /\/@vite\/client/,
27
32
  /\/dist\/index\.css$/,
28
33
  /\/dist\/style\.css$/
29
34
  ],
30
- // Modo de compartición
31
- mode: 'auto', // 'auto' | 'manual' | 'all'
32
35
  // Caché de estilos
33
36
  cacheEnabled: true
34
37
  };
35
38
 
36
- console.log('[WuStyleBridge] 🎨 Style sharing system initialized');
39
+ console.log('[WuStyleBridge] 🎨 Style sharing system initialized (default: isolated)');
40
+ }
41
+
42
+ /**
43
+ * 🛡️ SET APP STYLE MODE: Configura el modo de estilos para una app específica
44
+ * @param {string} appName - Nombre de la app
45
+ * @param {'isolated' | 'shared' | 'auto'} mode - Modo de estilos
46
+ */
47
+ setAppStyleMode(appName, mode) {
48
+ this.appStyleModes.set(appName, mode);
49
+ console.log(`[WuStyleBridge] 🎨 Style mode for ${appName}: ${mode}`);
50
+ }
51
+
52
+ /**
53
+ * 🔍 GET APP STYLE MODE: Obtiene el modo de estilos de una app
54
+ * @param {string} appName - Nombre de la app
55
+ * @returns {'isolated' | 'shared' | 'auto'}
56
+ */
57
+ getAppStyleMode(appName) {
58
+ return this.appStyleModes.get(appName) || this.config.defaultMode;
37
59
  }
38
60
 
39
61
  /**
@@ -137,7 +159,7 @@ export class WuStyleBridge {
137
159
  }
138
160
 
139
161
  /**
140
- * 🌉 INYECTAR ESTILOS EN SHADOW DOM: Clona estilos al Shadow DOM
162
+ * 🌉 INYECTAR ESTILOS EN SHADOW DOM: Clona estilos al Shadow DOM según el modo
141
163
  * @param {ShadowRoot} shadowRoot - Shadow DOM donde inyectar
142
164
  * @param {string} appName - Nombre de la app
143
165
  * @returns {Promise<number>}
@@ -148,15 +170,31 @@ export class WuStyleBridge {
148
170
  return 0;
149
171
  }
150
172
 
151
- console.log(`[WuStyleBridge] 🌉 Injecting shared styles into ${appName}...`);
173
+ // 🛡️ CHECK STYLE MODE: Respetar el modo de aislamiento de la app
174
+ const styleMode = this.getAppStyleMode(appName);
175
+
176
+ if (styleMode === 'isolated') {
177
+ console.log(`[WuStyleBridge] 🛡️ ${appName} is in ISOLATED mode - no parent styles will be shared`);
178
+ return 0;
179
+ }
180
+
181
+ console.log(`[WuStyleBridge] 🌉 Injecting styles into ${appName} (mode: ${styleMode})...`);
152
182
 
153
183
  // Detectar estilos del documento
154
184
  const styles = this.detectDocumentStyles();
155
185
  let injectedCount = 0;
156
186
 
157
- // Inyectar cada estilo
187
+ // Inyectar cada estilo según el modo
158
188
  for (const style of styles) {
159
189
  try {
190
+ // 🎯 En modo 'auto', solo compartir librerías específicas
191
+ if (styleMode === 'auto') {
192
+ const shouldShare = this.shouldShareStyle(style.href || style.viteId || '');
193
+ if (!shouldShare) {
194
+ continue; // Skip this style
195
+ }
196
+ }
197
+
160
198
  switch (style.type) {
161
199
  case 'link':
162
200
  await this.injectLinkStyle(shadowRoot, style);