wu-framework 1.1.3 → 1.1.5

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.
@@ -56,9 +56,10 @@ export class WuSandbox {
56
56
  * 🔧 SMART SANDBOX: Advanced Shadow DOM creation with error recovery
57
57
  * @param {string} appName - Nombre de la aplicación
58
58
  * @param {HTMLElement} hostContainer - Contenedor host
59
+ * @param {Object} options - Opciones adicionales (styleMode, manifest, etc.)
59
60
  * @returns {Object} Sandbox con shadow root y container
60
61
  */
61
- create(appName, hostContainer) {
62
+ create(appName, hostContainer, options = {}) {
62
63
  console.log(`[WuSandbox] 🔨 Creating sandbox for: ${appName}`);
63
64
 
64
65
  try {
@@ -109,6 +110,9 @@ export class WuSandbox {
109
110
  const jsSandbox = this.createAdvancedJSSandbox(appName);
110
111
  const jsProxy = jsSandbox.activate(); // Activar sandbox y obtener proxy
111
112
 
113
+ // Verificar styleMode del manifest antes de inyectar estilos
114
+ const styleMode = options.styleMode || options.manifest?.styleMode;
115
+
112
116
  const sandbox = {
113
117
  appName,
114
118
  shadowRoot,
@@ -118,28 +122,124 @@ export class WuSandbox {
118
122
  jsSandbox, // NUEVO: ProxySandbox o SnapshotSandbox
119
123
  jsProxy, // NUEVO: Proxy para ejecutar scripts
120
124
  styles: baseStyles,
125
+ styleMode, // Guardar styleMode para uso futuro
126
+ manifest: options.manifest, // Guardar manifest completo
121
127
  created: Date.now(),
122
128
  sandbox_state: 'stable',
123
129
  recovery_count: 0
124
130
  };
125
131
 
126
- // 🎨 INJECT SHARED STYLES: Store promise for await in mount
127
- sandbox.stylesReady = this.styleBridge.injectStylesIntoShadow(shadowRoot, appName).then(count => {
128
- console.log(`[WuSandbox] 🎨 Shared ${count} styles with ${appName}`);
129
-
130
- // 🔄 Observar cambios dinámicos de estilos (HMR de Vite)
131
- this.styleBridge.observeStyleChanges(() => {
132
- console.log(`[WuSandbox] 🔄 Reinjecting styles for ${appName} due to changes`);
133
- this.styleBridge.injectStylesIntoShadow(shadowRoot, appName).catch(err => {
134
- console.warn(`[WuSandbox] ⚠️ Failed to reinject styles:`, err);
132
+ // 🎨 INJECT STYLES: Comportamiento según styleMode
133
+ // - "shared": Inyecta todos los estilos del documento padre
134
+ // - "isolated": NO inyecta estilos externos (encapsulamiento nativo Shadow DOM)
135
+ // - "fully-isolated": Inyecta SOLO estilos propios de la app
136
+
137
+ if (styleMode === 'isolated') {
138
+ // 🔒 MODO ISOLATED: Encapsulamiento nativo del Shadow DOM
139
+ // No se inyectan estilos externos - la app debe manejar sus propios estilos
140
+ console.log(`[WuSandbox] 🔒 Style mode "isolated" for ${appName}, using native Shadow DOM encapsulation`);
141
+ sandbox.stylesReady = Promise.resolve(0);
142
+ // No configurar observer de estilos - la app es responsable de sus propios estilos
143
+
144
+ } else if (styleMode === 'fully-isolated') {
145
+ console.log(`[WuSandbox] 🛡️ Style mode "fully-isolated" detected for ${appName}, using enhanced style injection`);
146
+ // Registrar esta app como fully-isolated en el style bridge para filtrar sus estilos
147
+ const appUrl = options.appUrl || (options.manifest?.name ? `/${options.manifest.name}/` : `/${appName}/`);
148
+ this.styleBridge.registerFullyIsolatedApp(appName, appUrl);
149
+
150
+ // Guardar appUrl en sandbox para uso en reinjectStyles
151
+ sandbox.appUrl = appUrl;
152
+
153
+ // Para fully-isolated, inyectar SOLO los estilos propios de la app en su Shadow DOM
154
+ // Guardamos referencia a this para usar en el observer
155
+ const self = this;
156
+
157
+ sandbox.stylesReady = new Promise((resolve) => {
158
+ let resolved = false;
159
+
160
+ const tryInject = async () => {
161
+ const count = await self.injectOwnStylesToShadow(shadowRoot, appName, appUrl);
162
+
163
+ if (count > 0) {
164
+ console.log(`[WuSandbox] 🎨 Injected ${count} own styles for ${appName} (fully-isolated)`);
165
+ if (!resolved) {
166
+ resolved = true;
167
+ resolve(count);
168
+ }
169
+ }
170
+
171
+ return count;
172
+ };
173
+
174
+ // Usar MutationObserver PERSISTENTE para detectar cuando se inyectan estilos del app
175
+ console.log(`[WuSandbox] 👀 Setting up style observer for ${appName} (fully-isolated)`);
176
+ const observer = new MutationObserver((mutations) => {
177
+ let newStyleCount = 0;
178
+ for (const m of mutations) {
179
+ if (m.type === 'childList') {
180
+ for (const n of m.addedNodes) {
181
+ if (n.nodeName === 'STYLE' || n.nodeName === 'LINK') {
182
+ newStyleCount++;
183
+ const viteId = n.getAttribute ? n.getAttribute('data-vite-dev-id') : null;
184
+ if (viteId && viteId.toLowerCase().includes(appName.toLowerCase())) {
185
+ console.log(`[WuSandbox] 🆕 New ${appName} style detected: ${viteId.split('/').pop()}`);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ if (newStyleCount > 0) {
192
+ console.log(`[WuSandbox] 🔄 ${newStyleCount} new styles detected in head, checking for ${appName}...`);
193
+ tryInject();
194
+ }
135
195
  });
196
+
197
+ // Observar cambios en el head DE FORMA PERSISTENTE
198
+ observer.observe(document.head, {
199
+ childList: true,
200
+ subtree: true
201
+ });
202
+
203
+ // Guardar referencia al observer para poder desconectarlo cuando se desmonte la app
204
+ sandbox.styleObserver = observer;
205
+
206
+ // Intento inicial con pequeño delay para que Vite procese los imports
207
+ setTimeout(async () => {
208
+ const count = await tryInject();
209
+ // Si después de 3 segundos no hay estilos, usar fallback
210
+ if (!resolved) {
211
+ setTimeout(() => {
212
+ if (!resolved) {
213
+ console.warn(`[WuSandbox] ⚠️ No own styles found for ${appName} after timeout, using FALLBACK`);
214
+ const fallbackCount = self.injectAllStylesToShadow(shadowRoot, appName);
215
+ console.log(`[WuSandbox] 🎨 FALLBACK: Injected ${fallbackCount} styles for ${appName}`);
216
+ resolved = true;
217
+ resolve(fallbackCount);
218
+ }
219
+ }, 3000);
220
+ }
221
+ }, 50);
136
222
  });
223
+ } else {
224
+ // 🌐 MODO SHARED (default): Inyectar todos los estilos compartidos del documento
225
+ console.log(`[WuSandbox] 🌐 Style mode "shared" for ${appName}, injecting all shared styles...`);
226
+ sandbox.stylesReady = this.styleBridge.injectStylesIntoShadow(shadowRoot, appName, styleMode).then(count => {
227
+ console.log(`[WuSandbox] 🎨 Shared ${count} styles with ${appName}`);
228
+
229
+ // 🔄 Observar cambios dinámicos de estilos (HMR de Vite)
230
+ this.styleBridge.observeStyleChanges(() => {
231
+ console.log(`[WuSandbox] 🔄 Reinjecting styles for ${appName} due to changes`);
232
+ this.styleBridge.injectStylesIntoShadow(shadowRoot, appName, styleMode).catch(err => {
233
+ console.warn(`[WuSandbox] ⚠️ Failed to reinject styles:`, err);
234
+ });
235
+ });
137
236
 
138
- return count;
139
- }).catch(error => {
140
- console.warn(`[WuSandbox] ⚠️ Failed to inject styles:`, error);
141
- return 0;
142
- });
237
+ return count;
238
+ }).catch(error => {
239
+ console.warn(`[WuSandbox] ⚠️ Failed to inject styles:`, error);
240
+ return 0;
241
+ });
242
+ }
143
243
 
144
244
  // 📊 Register in sandbox registry
145
245
  this.sandboxes.set(appName, sandbox);
@@ -710,11 +810,152 @@ export class WuSandbox {
710
810
  return;
711
811
  }
712
812
 
713
- console.log(`[WuSandbox] 🔄 Reinjecting styles for ${appName}...`);
813
+ const styleMode = sandbox.styleMode;
814
+
815
+ // 🔒 MODO ISOLATED: No reinyectar estilos - la app maneja sus propios estilos
816
+ if (styleMode === 'isolated') {
817
+ console.log(`[WuSandbox] 🔒 Skipping reinject for ${appName} (isolated mode - app manages own styles)`);
818
+ return;
819
+ }
820
+
821
+ // 🛡️ MODO FULLY-ISOLATED: Reinyectar SOLO estilos propios
822
+ if (styleMode === 'fully-isolated') {
823
+ console.log(`[WuSandbox] 🔄 Reinjecting OWN styles for ${appName} (fully-isolated)...`);
824
+ const appUrl = sandbox.appUrl || sandbox.manifest?.name ? `/${sandbox.manifest.name}/` : `/${appName}/`;
825
+ const count = await this.injectOwnStylesToShadow(sandbox.shadowRoot, appName, appUrl);
826
+ console.log(`[WuSandbox] ✅ Reinjected ${count} own styles for ${appName}`);
827
+ return;
828
+ }
829
+
830
+ // 🌐 MODO SHARED: Reinyectar todos los estilos compartidos
831
+ console.log(`[WuSandbox] 🔄 Reinjecting shared styles for ${appName}...`);
714
832
  const count = await this.styleBridge.injectStylesIntoShadow(
715
833
  sandbox.shadowRoot,
716
- appName
834
+ appName,
835
+ styleMode
717
836
  );
718
- console.log(`[WuSandbox] ✅ Reinjected ${count} styles`);
837
+ console.log(`[WuSandbox] ✅ Reinjected ${count} shared styles`);
838
+ }
839
+
840
+ /**
841
+ * 🎨 INYECTAR ESTILOS PROPIOS: Inyecta SOLO los estilos propios de una app en su Shadow DOM
842
+ * Usado para apps en modo fully-isolated
843
+ * @param {ShadowRoot} shadowRoot - Shadow DOM donde inyectar
844
+ * @param {string} appName - Nombre de la app
845
+ * @param {string} appUrl - URL base de la app
846
+ * @returns {Promise<number>} Número de estilos inyectados
847
+ */
848
+ async injectOwnStylesToShadow(shadowRoot, appName, appUrl) {
849
+ if (!shadowRoot) return 0;
850
+
851
+ let injectedCount = 0;
852
+
853
+ // Buscar TODOS los estilos en el head
854
+ const allStyles = document.querySelectorAll('style');
855
+ const normalizedAppName = appName.toLowerCase();
856
+
857
+ // Patrones para detectar estilos de esta app (Windows y Unix paths)
858
+ // IMPORTANTE: Debe coincidir SOLO con packages/appName/ al inicio del path del paquete
859
+ // NO debe coincidir con packages/shell/src/components/learning/ (eso es del shell)
860
+ const appPatterns = [
861
+ // Patrón específico: packages/learning/src o packages\learning\src
862
+ // El src/ es clave para asegurar que es el MFE, no un subdirectorio de otro package
863
+ new RegExp(`packages[/\\\\]${normalizedAppName}[/\\\\]src[/\\\\]`, 'i')
864
+ ];
865
+
866
+ console.log(`[WuSandbox] 🔍 Searching own styles for ${appName}, found ${allStyles.length} style tags in head`);
867
+
868
+ // Log para debug: mostrar estilos que contienen el nombre de la app
869
+ let matchingCount = 0;
870
+ for (const s of allStyles) {
871
+ const vid = s.getAttribute('data-vite-dev-id') || '';
872
+ if (vid.toLowerCase().includes(normalizedAppName)) {
873
+ matchingCount++;
874
+ }
875
+ }
876
+ if (matchingCount > 0) {
877
+ console.log(`[WuSandbox] 📋 Found ${matchingCount} styles potentially matching ${appName}`);
878
+ }
879
+
880
+ for (const style of allStyles) {
881
+ // NO saltar basándose en data-wu-injected del head - eso se pone en los clones del shadow DOM
882
+
883
+ const viteId = style.getAttribute('data-vite-dev-id') || '';
884
+ const normalizedViteId = viteId.replace(/\\/g, '/').toLowerCase();
885
+
886
+ // Verificar si el estilo pertenece a esta app
887
+ let belongsToApp = false;
888
+
889
+ // 1. Por data-vite-dev-id (la forma más confiable)
890
+ if (viteId) {
891
+ // Revisar si el path contiene packages/appName/
892
+ for (const pattern of appPatterns) {
893
+ if (pattern instanceof RegExp) {
894
+ if (pattern.test(viteId)) {
895
+ belongsToApp = true;
896
+ break;
897
+ }
898
+ } else {
899
+ if (normalizedViteId.includes(pattern.toLowerCase())) {
900
+ belongsToApp = true;
901
+ break;
902
+ }
903
+ }
904
+ }
905
+ }
906
+
907
+ if (belongsToApp) {
908
+ // Verificar si ya existe en el Shadow DOM
909
+ const existingStyle = shadowRoot.querySelector(`style[data-vite-dev-id="${viteId}"]`);
910
+
911
+ if (!existingStyle) {
912
+ const clonedStyle = style.cloneNode(true);
913
+ clonedStyle.setAttribute('data-wu-injected', 'true');
914
+ shadowRoot.insertBefore(clonedStyle, shadowRoot.firstChild);
915
+ injectedCount++;
916
+ const styleName = viteId.substring(viteId.lastIndexOf('/') + 1) || viteId.substring(viteId.lastIndexOf('\\') + 1);
917
+ console.log(`[WuSandbox] ✅ Injected own style for ${appName}: ${styleName}`);
918
+ }
919
+ }
920
+ }
921
+
922
+ console.log(`[WuSandbox] 🎨 Total own styles injected for ${appName}: ${injectedCount}`);
923
+ return injectedCount;
924
+ }
925
+
926
+ /**
927
+ * 🎨 FALLBACK: Inyectar estilos que contengan el nombre de la app
928
+ * Usado como último recurso cuando no se encuentran los estilos con el patrón exacto
929
+ * @param {ShadowRoot} shadowRoot - Shadow DOM donde inyectar
930
+ * @param {string} appName - Nombre de la app (para logging)
931
+ * @returns {number} Número de estilos inyectados
932
+ */
933
+ injectAllStylesToShadow(shadowRoot, appName) {
934
+ if (!shadowRoot) return 0;
935
+
936
+ let injectedCount = 0;
937
+ const normalizedAppName = appName.toLowerCase();
938
+
939
+ // Inyectar estilos que contengan el nombre de la app en su vite-id
940
+ const allStyles = document.querySelectorAll('style');
941
+ for (const style of allStyles) {
942
+ const viteId = style.getAttribute('data-vite-dev-id') || '';
943
+
944
+ // Solo inyectar si contiene el nombre de la app
945
+ if (!viteId.toLowerCase().includes(normalizedAppName)) continue;
946
+
947
+ // Verificar si ya existe en el shadow DOM
948
+ if (shadowRoot.querySelector(`style[data-vite-dev-id="${viteId}"]`)) continue;
949
+
950
+ const clonedStyle = style.cloneNode(true);
951
+ clonedStyle.setAttribute('data-wu-fallback', 'true');
952
+ shadowRoot.insertBefore(clonedStyle, shadowRoot.firstChild);
953
+ injectedCount++;
954
+ const styleName = viteId.split('/').pop() || viteId.split('\\').pop();
955
+ console.log(`[WuSandbox] 📦 FALLBACK injected: ${styleName}`);
956
+ }
957
+
958
+ console.log(`[WuSandbox] 🎨 FALLBACK: Total ${injectedCount} styles injected for ${appName}`);
959
+ return injectedCount;
719
960
  }
720
961
  }