wu-framework 1.0.3 → 1.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wu-framework",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "🚀 Universal Microfrontends Framework - Framework agnostic, zero config, Shadow DOM isolation",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -102,7 +102,11 @@ export class WuCache {
102
102
  };
103
103
 
104
104
  // Verificar si necesitamos hacer espacio
105
- this.ensureSpace(entry.size);
105
+ const hasSpace = this.ensureSpace(entry.size);
106
+ if (hasSpace === false) {
107
+ console.warn(`[WuCache] ⚠️ Cannot cache item: ${key} (too large)`);
108
+ return false;
109
+ }
106
110
 
107
111
  // Guardar en memoria
108
112
  this.memoryCache.set(key, entry);
@@ -194,9 +198,28 @@ export class WuCache {
194
198
  ensureSpace(neededSize) {
195
199
  const maxSizeBytes = this.config.maxSize * 1024 * 1024;
196
200
 
201
+ // 🛡️ FIX: Validar que el item no sea más grande que el máximo permitido
202
+ if (neededSize > maxSizeBytes) {
203
+ console.warn(`[WuCache] ⚠️ Item size (${neededSize}) exceeds max cache size (${maxSizeBytes}). Skipping.`);
204
+ return false;
205
+ }
206
+
207
+ // 🛡️ FIX: Límite de iteraciones para evitar loop infinito
208
+ const maxIterations = this.config.maxItems + 10;
209
+ let iterations = 0;
210
+
197
211
  // Verificar si necesitamos limpiar
198
- while (this.stats.size + neededSize > maxSizeBytes ||
199
- this.memoryCache.size >= this.config.maxItems) {
212
+ while ((this.stats.size + neededSize > maxSizeBytes ||
213
+ this.memoryCache.size >= this.config.maxItems) &&
214
+ iterations < maxIterations) {
215
+
216
+ iterations++;
217
+
218
+ // 🛡️ FIX: Si el cache está vacío pero aún no hay espacio, salir
219
+ if (this.memoryCache.size === 0) {
220
+ console.warn('[WuCache] ⚠️ Cache empty but still no space. Breaking loop.');
221
+ break;
222
+ }
200
223
 
201
224
  // Encontrar entrada menos recientemente usada (LRU)
202
225
  let oldestKey = null;
@@ -217,6 +240,13 @@ export class WuCache {
217
240
  break;
218
241
  }
219
242
  }
243
+
244
+ // 🛡️ FIX: Log si alcanzamos el límite de iteraciones
245
+ if (iterations >= maxIterations) {
246
+ console.error(`[WuCache] 🚨 Max eviction iterations reached (${maxIterations}). Possible infinite loop prevented.`);
247
+ }
248
+
249
+ return true;
220
250
  }
221
251
 
222
252
  /**
@@ -104,56 +104,79 @@ export class WuCore {
104
104
  }
105
105
 
106
106
  /**
107
- * 🔍 SMART MUTATION OBSERVER: Real-time DOM monitoring for efficiency
107
+ * 🔍 SMART MUTATION OBSERVER: Observa SOLO los contenedores de apps montadas
108
+ * FIX: Ya no observa todo document.body (memory leak)
108
109
  */
109
110
  initializeMutationObserver() {
110
- if (!window.MutationObserver) return; // Fallback for older browsers
111
+ if (!window.MutationObserver) return;
111
112
 
112
- this.healthState.mutationObserver = new MutationObserver((mutations) => {
113
- let shouldCheckHealth = false;
113
+ // Map para trackear observers por contenedor (evita memory leak)
114
+ this.healthState.containerObservers = new Map();
115
+
116
+ // Callback compartido para todos los observers
117
+ this.healthState.mutationCallback = (mutations, observer) => {
118
+ let affectedAppName = null;
114
119
 
115
120
  for (const mutation of mutations) {
116
- // Only check health if it affects our mounted apps
117
- if (mutation.type === 'childList') {
118
- const removedNodes = Array.from(mutation.removedNodes);
119
- const affectedApps = removedNodes.some(node => {
120
- if (node.nodeType === Node.ELEMENT_NODE) {
121
- // Check if removed node contains any of our mounted apps
122
- for (const [appName, mounted] of this.mounted) {
123
- if (node.contains && node.contains(mounted.container)) {
124
- return true;
125
- }
126
- if (node === mounted.container) {
127
- return true;
128
- }
129
- }
121
+ if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
122
+ // Encontrar qué app fue afectada
123
+ for (const [appName, mounted] of this.mounted) {
124
+ const container = mounted.hostContainer || mounted.container;
125
+ if (mutation.target === container ||
126
+ mutation.target.contains?.(container)) {
127
+ affectedAppName = appName;
128
+ break;
130
129
  }
131
- return false;
132
- });
133
-
134
- if (affectedApps) {
135
- shouldCheckHealth = true;
136
- break;
137
130
  }
138
131
  }
132
+ if (affectedAppName) break;
139
133
  }
140
134
 
141
- // Debounced health check only when necessary
142
- if (shouldCheckHealth) {
135
+ if (affectedAppName) {
143
136
  clearTimeout(this.healthState.mutationCheckTimeout);
144
137
  this.healthState.mutationCheckTimeout = setTimeout(() => {
145
138
  this.performHealthCheck();
146
- }, 1000); // Wait 1 second to batch multiple mutations
139
+ }, 1000);
147
140
  }
148
- });
141
+ };
142
+
143
+ logger.wuDebug('🔍 MutationObserver system initialized (lazy per-container)');
144
+ }
145
+
146
+ /**
147
+ * 🔗 Observar contenedor específico cuando se monta una app
148
+ */
149
+ observeContainer(appName, container) {
150
+ if (!this.healthState.containerObservers || !container) return;
151
+
152
+ // No observar si ya existe
153
+ if (this.healthState.containerObservers.has(appName)) return;
149
154
 
150
- // Observe the entire document but only care about child changes
151
- this.healthState.mutationObserver.observe(document.body, {
155
+ const observer = new MutationObserver(this.healthState.mutationCallback);
156
+
157
+ // Observar solo el contenedor padre directo (no subtree profundo)
158
+ const parentToObserve = container.parentElement || container;
159
+ observer.observe(parentToObserve, {
152
160
  childList: true,
153
- subtree: true
161
+ subtree: false // ✅ Solo hijos directos, no todo el árbol
154
162
  });
155
163
 
156
- logger.wuDebug('🔍 MutationObserver initialized for smart DOM monitoring');
164
+ this.healthState.containerObservers.set(appName, observer);
165
+ logger.wuDebug(`🔍 Observing container for ${appName}`);
166
+ }
167
+
168
+ /**
169
+ * 🔓 Dejar de observar contenedor cuando se desmonta
170
+ */
171
+ unobserveContainer(appName) {
172
+ if (!this.healthState.containerObservers) return;
173
+
174
+ const observer = this.healthState.containerObservers.get(appName);
175
+ if (observer) {
176
+ observer.disconnect();
177
+ this.healthState.containerObservers.delete(appName);
178
+ logger.wuDebug(`🔓 Stopped observing container for ${appName}`);
179
+ }
157
180
  }
158
181
 
159
182
  /**
@@ -606,10 +629,14 @@ export class WuCore {
606
629
  poolSandbox,
607
630
  lifecycle,
608
631
  container: sandbox.container,
632
+ hostContainer: container,
609
633
  timestamp: Date.now(),
610
634
  state: 'stable'
611
635
  });
612
636
 
637
+ // 🔍 Observar contenedor para health monitoring (sin memory leak)
638
+ this.observeContainer(appName, container);
639
+
613
640
  // ⚡ End performance measurement
614
641
  const mountTime = this.performance.endMeasure('mount', appName);
615
642
 
@@ -1017,6 +1044,7 @@ export class WuCore {
1017
1044
  /**
1018
1045
  * 📦 MODULE LOADER: Advanced registration patterns
1019
1046
  * Handles asynchronous registration with timing synchronization
1047
+ * FIX: Verifica que definitions tenga el lifecycle después de cargar
1020
1048
  */
1021
1049
  async moduleLoader(moduleUrl, appName) {
1022
1050
  // ✅ Check if already registered
@@ -1027,16 +1055,37 @@ export class WuCore {
1027
1055
 
1028
1056
  console.log(`[Wu] 📡 Using event-based registration for ${appName}`);
1029
1057
 
1030
- // 🔔 Use event-based waiting (no polling!)
1031
- const registrationPromise = this.registry.waitForApp(appName, 10000);
1058
+ // 🚀 Load module first
1059
+ try {
1060
+ await import(/* @vite-ignore */ moduleUrl);
1061
+ } catch (loadError) {
1062
+ console.error(`[Wu] ❌ Failed to import module ${moduleUrl}:`, loadError);
1063
+ throw loadError;
1064
+ }
1032
1065
 
1033
- // 🚀 Load module asynchronously
1034
- const moduleLoadPromise = import(/* @vite-ignore */ moduleUrl);
1066
+ // 🛡️ FIX: Esperar a que wu.define() se ejecute con verificación real
1067
+ const maxWaitTime = 10000; // 10 segundos
1068
+ const checkInterval = 50; // Verificar cada 50ms
1069
+ const startTime = Date.now();
1070
+
1071
+ while (!this.definitions.has(appName)) {
1072
+ const elapsed = Date.now() - startTime;
1073
+
1074
+ if (elapsed >= maxWaitTime) {
1075
+ throw new Error(
1076
+ `App '${appName}' module loaded but wu.define() was not called within ${maxWaitTime}ms.\n\n` +
1077
+ `Make sure your module calls:\n` +
1078
+ ` wu.define('${appName}', { mount, unmount })\n\n` +
1079
+ `Or using window.wu:\n` +
1080
+ ` window.wu.define('${appName}', { mount, unmount })`
1081
+ );
1082
+ }
1035
1083
 
1036
- // 🌌 Wait for both operations to complete
1037
- await Promise.all([moduleLoadPromise, registrationPromise]);
1084
+ // Esperar un poco antes de verificar de nuevo
1085
+ await new Promise(resolve => setTimeout(resolve, checkInterval));
1086
+ }
1038
1087
 
1039
- console.log(`[Wu] ✅ App ${appName} loaded and registered via events`);
1088
+ console.log(`[Wu] ✅ App ${appName} loaded and registered (verified in definitions)`);
1040
1089
  }
1041
1090
 
1042
1091
  /**
@@ -1080,6 +1129,9 @@ export class WuCore {
1080
1129
  this.sandboxPool.release(appName);
1081
1130
  }
1082
1131
 
1132
+ // 🔓 Dejar de observar contenedor (evita memory leak)
1133
+ this.unobserveContainer(appName);
1134
+
1083
1135
  // Remover del registro de montadas
1084
1136
  this.mounted.delete(appName);
1085
1137
 
@@ -1243,10 +1295,12 @@ export class WuCore {
1243
1295
  this.healthState.monitor = null;
1244
1296
  }
1245
1297
 
1246
- // Limpiar MutationObserver
1247
- if (this.healthState.mutationObserver) {
1248
- this.healthState.mutationObserver.disconnect();
1249
- this.healthState.mutationObserver = null;
1298
+ // Limpiar todos los MutationObservers por contenedor
1299
+ if (this.healthState.containerObservers) {
1300
+ for (const [appName, observer] of this.healthState.containerObservers) {
1301
+ observer.disconnect();
1302
+ }
1303
+ this.healthState.containerObservers.clear();
1250
1304
  }
1251
1305
 
1252
1306
  // Limpiar timeouts pendientes
@@ -7,7 +7,8 @@ export class WuLogger {
7
7
  constructor() {
8
8
  // Detectar entorno automáticamente
9
9
  this.isDevelopment = this.detectEnvironment();
10
- this.logLevel = this.isDevelopment ? 'debug' : 'error';
10
+ // En desarrollo: warn (menos ruido), en producción: error
11
+ this.logLevel = this.isDevelopment ? 'warn' : 'error';
11
12
 
12
13
  this.levels = {
13
14
  debug: 0,
@@ -116,4 +117,19 @@ export const wuLog = {
116
117
  info: (...args) => logger.wuInfo(...args),
117
118
  warn: (...args) => logger.wuWarn(...args),
118
119
  error: (...args) => logger.wuError(...args)
119
- };
120
+ };
121
+
122
+ /**
123
+ * 🔇 Silenciar todos los logs de Wu Framework
124
+ * Útil en producción para eliminar todo el ruido
125
+ */
126
+ export function silenceAllLogs() {
127
+ logger.setLevel('silent');
128
+ }
129
+
130
+ /**
131
+ * 🔊 Restaurar logs (nivel debug)
132
+ */
133
+ export function enableAllLogs() {
134
+ logger.setLevel('debug');
135
+ }
@@ -1,52 +1,21 @@
1
1
  /**
2
- * 🔔 WU-REGISTRY: EVENT-BASED APP REGISTRATION SYSTEM
3
- *
4
- * Reemplaza el polling con Custom Events para mejor performance
2
+ * 🔔 WU-REGISTRY: APP REGISTRATION TRACKER
3
+ * Simplificado - solo trackea apps registradas
5
4
  */
6
5
 
7
6
  export class WuRegistry {
8
7
  constructor() {
9
8
  this.registeredApps = new Set();
10
- this.waitingPromises = new Map();
11
-
12
- // Event listeners para registro
13
- this.setupEventListeners();
14
-
15
- console.log('[WuRegistry] 🔔 Event-based registration system initialized');
16
- }
17
-
18
- /**
19
- * 📡 SETUP EVENT LISTENERS
20
- */
21
- setupEventListeners() {
22
- // Escuchar eventos de registro de apps
23
- window.addEventListener('wu:app:ready', (event) => {
24
- const { appName } = event.detail;
25
- console.log(`[WuRegistry] ✅ App registered via event: ${appName}`);
26
-
27
- this.registeredApps.add(appName);
28
-
29
- // Resolver promesas pendientes
30
- if (this.waitingPromises.has(appName)) {
31
- const { resolve } = this.waitingPromises.get(appName);
32
- resolve();
33
- this.waitingPromises.delete(appName);
34
- }
35
- });
36
-
37
- // API para que apps notifiquen que están listas (se agrega a window.wu cuando exista)
38
- // No crear window.wu aquí - eso lo hace index.js con la instancia completa
39
9
  this._setupNotifyReady();
40
10
  }
41
11
 
42
12
  /**
43
- * 🔔 Setup notifyReady API cuando window.wu esté disponible
13
+ * 🔔 Setup notifyReady API en window.wu
44
14
  */
45
15
  _setupNotifyReady() {
46
16
  const setupFn = () => {
47
17
  if (window.wu && !window.wu.notifyReady) {
48
18
  window.wu.notifyReady = (appName) => {
49
- console.log(`[WuRegistry] 📢 App ${appName} called notifyReady()`);
50
19
  const event = new CustomEvent('wu:app:ready', {
51
20
  detail: { appName, timestamp: Date.now() }
52
21
  });
@@ -55,89 +24,37 @@ export class WuRegistry {
55
24
  }
56
25
  };
57
26
 
58
- // Intentar setup inmediato
59
27
  setupFn();
60
-
61
- // Si window.wu no existe aún, esperar un tick
62
28
  if (!window.wu) {
63
29
  queueMicrotask(setupFn);
64
30
  }
65
31
  }
66
32
 
67
33
  /**
68
- * WAIT FOR APP: Promise-based waiting
69
- * @param {string} appName - Nombre de la app
70
- * @param {number} timeout - Timeout en ms (default: 10000)
71
- * @returns {Promise}
34
+ * MARK AS REGISTERED
72
35
  */
73
- waitForApp(appName, timeout = 10000) {
74
- // Si ya está registrada, resolver inmediatamente
75
- if (this.registeredApps.has(appName)) {
76
- console.log(`[WuRegistry] ⚡ App ${appName} already registered`);
77
- return Promise.resolve();
78
- }
79
-
80
- console.log(`[WuRegistry] ⏳ Waiting for app ${appName} to register...`);
81
-
82
- return new Promise((resolve, reject) => {
83
- // Guardar promesa
84
- this.waitingPromises.set(appName, { resolve, reject });
85
-
86
- // Timeout
87
- const timeoutId = setTimeout(() => {
88
- if (this.waitingPromises.has(appName)) {
89
- this.waitingPromises.delete(appName);
90
-
91
- const error = new Error(
92
- `App '${appName}' failed to register within ${timeout}ms.\n\n` +
93
- `Possible causes:\n` +
94
- ` - Module failed to load\n` +
95
- ` - wu.define() was not called\n` +
96
- ` - Check browser console for import errors\n\n` +
97
- `Make sure your app calls:\n` +
98
- ` wu.define('${appName}', { mount, unmount })`
99
- );
100
-
101
- reject(error);
102
- }
103
- }, timeout);
104
-
105
- // Limpiar timeout cuando se resuelva
106
- this.waitingPromises.get(appName).timeoutId = timeoutId;
107
- });
36
+ markAsRegistered(appName) {
37
+ this.registeredApps.add(appName);
108
38
  }
109
39
 
110
40
  /**
111
- * MARK AS REGISTERED
112
- * @param {string} appName
41
+ * IS REGISTERED
113
42
  */
114
- markAsRegistered(appName) {
115
- this.registeredApps.add(appName);
116
- console.log(`[WuRegistry] ✅ App ${appName} marked as registered`);
43
+ isRegistered(appName) {
44
+ return this.registeredApps.has(appName);
117
45
  }
118
46
 
119
47
  /**
120
48
  * ❌ UNREGISTER APP
121
- * @param {string} appName
122
49
  */
123
50
  unregister(appName) {
124
51
  this.registeredApps.delete(appName);
125
- console.log(`[WuRegistry] ❌ App ${appName} unregistered`);
126
52
  }
127
53
 
128
54
  /**
129
55
  * 🧹 CLEANUP
130
56
  */
131
57
  cleanup() {
132
- // Rechazar todas las promesas pendientes
133
- for (const [appName, { reject, timeoutId }] of this.waitingPromises) {
134
- clearTimeout(timeoutId);
135
- reject(new Error(`Registry cleanup: ${appName}`));
136
- }
137
-
138
- this.waitingPromises.clear();
139
58
  this.registeredApps.clear();
140
-
141
- console.log('[WuRegistry] 🧹 Registry cleaned up');
142
59
  }
143
60
  }
package/src/index.js CHANGED
@@ -103,7 +103,7 @@ if (typeof window !== 'undefined') {
103
103
 
104
104
  // Configurar propiedades si no existen
105
105
  if (!wu.version) {
106
- wu.version = '1.0.3';
106
+ wu.version = '1.0.5';
107
107
  console.log('🚀 Wu Framework loaded - Universal Microfrontends ready');
108
108
  wu.info = {
109
109
  name: 'Wu Framework',
@@ -126,6 +126,19 @@ if (typeof window !== 'undefined') {
126
126
  wu.once = (eventName, callback) => wu.eventBus.once(eventName, callback);
127
127
  wu.off = (eventName, callback) => wu.eventBus.off(eventName, callback);
128
128
  }
129
+
130
+ // 🔇 Exponer funciones de control de logs
131
+ // window.wu.silence() para silenciar, window.wu.verbose() para debug
132
+ if (!wu.silence) {
133
+ wu.silence = async () => {
134
+ const { silenceAllLogs } = await import('./core/wu-logger.js');
135
+ silenceAllLogs();
136
+ };
137
+ wu.verbose = async () => {
138
+ const { enableAllLogs } = await import('./core/wu-logger.js');
139
+ enableAllLogs();
140
+ };
141
+ }
129
142
  }
130
143
 
131
144
  // Exportar API principal
@@ -167,6 +180,7 @@ export {
167
180
  } from './core/wu-hooks.js';
168
181
  export { WuSandboxPool } from './core/wu-sandbox-pool.js';
169
182
  export { WuRegistry } from './core/wu-registry.js';
183
+ export { silenceAllLogs, enableAllLogs } from './core/wu-logger.js';
170
184
 
171
185
  /**
172
186
  * Utilidades de conveniencia para uso común
@@ -1,316 +0,0 @@
1
- /**
2
- * 🚀 WU-FRAMEWORK SIMPLIFIED API
3
- * Developer-friendly interface for universal microfrontends
4
- */
5
-
6
- import { wu as wuCore } from '../index.js'
7
- import { logger } from '../core/wu-logger.js'
8
-
9
- class WuSimpleAPI {
10
- constructor() {
11
- this.core = wuCore
12
- this.autoInitialized = false
13
- this.defaultConfig = {
14
- container: '#app',
15
- mode: 'auto', // auto, spa, microfrontend
16
- debug: false,
17
- timeout: 30000,
18
- retries: 3
19
- }
20
- }
21
-
22
- /**
23
- * 🌟 SMART MOUNT: Intelligent mounting with auto-discovery
24
- * wu.mount('dashboard').into('#container')
25
- * wu.mount('http://localhost:3001/dashboard')
26
- */
27
- mount(appNameOrUrl) {
28
- const mountAPI = {
29
- into: async (containerSelector = this.defaultConfig.container) => {
30
- await this._ensureInitialized()
31
-
32
- // 🔍 Detect if it's a URL or app name
33
- if (this._isUrl(appNameOrUrl)) {
34
- return await this._mountFromUrl(appNameOrUrl, containerSelector)
35
- } else {
36
- return await this._mountFromName(appNameOrUrl, containerSelector)
37
- }
38
- }
39
- }
40
-
41
- // Support direct call: wu.mount('dashboard', '#container')
42
- if (typeof arguments[1] === 'string') {
43
- return mountAPI.into(arguments[1])
44
- }
45
-
46
- return mountAPI
47
- }
48
-
49
- /**
50
- * 🚀 QUICK LOAD: One-liner for loading remote microfrontends
51
- * wu.load('http://localhost:3001/dashboard')
52
- * wu.load('http://localhost:3001', { name: 'dashboard' })
53
- */
54
- async load(url, options = {}) {
55
- await this._ensureInitialized()
56
-
57
- const appName = options.name || this._extractAppName(url)
58
- const container = options.container || this.defaultConfig.container
59
-
60
- // Auto-register and mount
61
- await this._registerFromUrl(url, appName)
62
- return await this.mount(appName).into(container)
63
- }
64
-
65
- /**
66
- * 🎯 BATCH OPERATIONS: Load multiple apps at once
67
- * wu.loadMany([
68
- * { name: 'dashboard', url: 'http://localhost:3001' },
69
- * { name: 'sidebar', url: 'http://localhost:3002' }
70
- * ])
71
- */
72
- async loadMany(apps) {
73
- await this._ensureInitialized()
74
-
75
- const results = []
76
- for (const app of apps) {
77
- try {
78
- await this._registerFromUrl(app.url, app.name)
79
- results.push({ name: app.name, status: 'registered' })
80
- } catch (error) {
81
- results.push({ name: app.name, status: 'failed', error: error.message })
82
- }
83
- }
84
-
85
- return results
86
- }
87
-
88
- /**
89
- * 🔧 CONFIGURATION: Flexible configuration methods
90
- * wu.config({ debug: true, timeout: 60000 })
91
- * wu.debug(true)
92
- * wu.timeout(30000)
93
- */
94
- config(options = {}) {
95
- Object.assign(this.defaultConfig, options)
96
-
97
- if (options.debug) {
98
- logger.wuDebug('🔧 Configuration updated:', this.defaultConfig)
99
- }
100
-
101
- return this
102
- }
103
-
104
- /**
105
- * 🐛 DEBUG MODE: Quick debug toggle
106
- * wu.debug(true)
107
- * wu.debug() // toggle
108
- */
109
- debug(enabled) {
110
- if (enabled === undefined) {
111
- this.defaultConfig.debug = !this.defaultConfig.debug
112
- } else {
113
- this.defaultConfig.debug = !!enabled
114
- }
115
-
116
- logger.wuInfo(`🐛 Debug mode: ${this.defaultConfig.debug ? 'ON' : 'OFF'}`)
117
- return this
118
- }
119
-
120
- /**
121
- * ⏱️ TIMEOUT: Set operation timeout
122
- * wu.timeout(60000) // 60 seconds
123
- */
124
- timeout(ms) {
125
- this.defaultConfig.timeout = ms
126
- logger.wuDebug(`⏱️ Timeout set to ${ms}ms`)
127
- return this
128
- }
129
-
130
- /**
131
- * 🔄 RETRIES: Set retry attempts
132
- * wu.retries(5)
133
- */
134
- retries(count) {
135
- this.defaultConfig.retries = count
136
- logger.wuDebug(`🔄 Retries set to ${count}`)
137
- return this
138
- }
139
-
140
- /**
141
- * 🎯 DEFAULT CONTAINER: Set default mounting container
142
- * wu.container('#main-app')
143
- */
144
- container(selector) {
145
- this.defaultConfig.container = selector
146
- logger.wuDebug(`🎯 Default container set to ${selector}`)
147
- return this
148
- }
149
-
150
- /**
151
- * 🌐 GLOBAL SETTINGS: Environment-based configuration
152
- * wu.development() // Sets debug: true, retries: 1
153
- * wu.production() // Sets debug: false, retries: 3
154
- */
155
- development() {
156
- return this.config({
157
- debug: true,
158
- retries: 1,
159
- timeout: 60000
160
- })
161
- }
162
-
163
- production() {
164
- return this.config({
165
- debug: false,
166
- retries: 3,
167
- timeout: 30000
168
- })
169
- }
170
-
171
- /**
172
- * 🚀 QUICK SETUP: Common configurations
173
- * wu.spa() // Single Page App mode
174
- * wu.micro() // Microfrontend mode
175
- */
176
- spa() {
177
- return this.config({
178
- mode: 'spa',
179
- container: '#app',
180
- debug: false
181
- })
182
- }
183
-
184
- micro() {
185
- return this.config({
186
- mode: 'microfrontend',
187
- debug: true,
188
- retries: 5
189
- })
190
- }
191
-
192
- /**
193
- * 📊 STATUS: Get framework and apps status
194
- * wu.status()
195
- */
196
- status() {
197
- const stats = this.core.getStats?.() || {}
198
-
199
- return {
200
- initialized: this.autoInitialized,
201
- apps: {
202
- registered: stats.registered || 0,
203
- mounted: stats.mounted || 0,
204
- available: stats.apps || []
205
- },
206
- health: this.core.quantumState?.dimensionalStability || 'unknown',
207
- config: this.defaultConfig
208
- }
209
- }
210
-
211
- /**
212
- * 🧹 CLEANUP: Clean shutdown
213
- * wu.destroy()
214
- */
215
- async destroy() {
216
- if (this.core.destroy) {
217
- await this.core.destroy()
218
- }
219
- this.autoInitialized = false
220
- logger.wuInfo('🧹 Framework destroyed')
221
- }
222
-
223
-
224
- // 🔒 PRIVATE METHODS
225
-
226
- async _ensureInitialized() {
227
- if (!this.autoInitialized) {
228
- await this._autoInitialize()
229
- }
230
- }
231
-
232
- async _autoInitialize() {
233
- if (this.defaultConfig.debug) {
234
- logger.wuDebug('🚀 Auto-initializing Wu Framework...')
235
- }
236
-
237
- // Smart initialization with minimal config
238
- await this.core.init({ apps: [] })
239
- this.autoInitialized = true
240
-
241
- if (this.defaultConfig.debug) {
242
- logger.wuDebug('✅ Wu Framework auto-initialized')
243
- }
244
- }
245
-
246
- _isUrl(str) {
247
- try {
248
- new URL(str)
249
- return true
250
- } catch {
251
- return str.includes('://') || str.startsWith('http')
252
- }
253
- }
254
-
255
- _extractAppName(url) {
256
- try {
257
- const urlObj = new URL(url)
258
- const pathSegments = urlObj.pathname.split('/').filter(Boolean)
259
- return pathSegments[pathSegments.length - 1] || urlObj.hostname.split('.')[0]
260
- } catch {
261
- return 'app-' + Date.now()
262
- }
263
- }
264
-
265
- async _registerFromUrl(url, appName) {
266
- const baseUrl = this._getBaseUrl(url)
267
-
268
- await this.core.registerApp({
269
- name: appName,
270
- url: baseUrl
271
- })
272
- }
273
-
274
- _getBaseUrl(url) {
275
- try {
276
- const urlObj = new URL(url)
277
- return `${urlObj.protocol}//${urlObj.host}`
278
- } catch {
279
- return url
280
- }
281
- }
282
-
283
- async _mountFromUrl(url, containerSelector) {
284
- const appName = this._extractAppName(url)
285
- await this._registerFromUrl(url, appName)
286
- return await this._mountFromName(appName, containerSelector)
287
- }
288
-
289
- async _mountFromName(appName, containerSelector) {
290
- try {
291
- await this.core.mount(appName, containerSelector)
292
-
293
- if (this.defaultConfig.debug) {
294
- logger.wuDebug(`✅ ${appName} mounted successfully in ${containerSelector}`)
295
- }
296
-
297
- return {
298
- app: appName,
299
- container: containerSelector,
300
- status: 'mounted',
301
- unmount: () => this.core.unmount(appName)
302
- }
303
-
304
- } catch (error) {
305
- console.error(`[Wu] ❌ Failed to mount ${appName}:`, error.message)
306
- throw new Error(`Failed to mount ${appName}: ${error.message}`)
307
- }
308
- }
309
- }
310
-
311
- // 🌟 Create singleton instance
312
- const wuSimple = new WuSimpleAPI()
313
-
314
- // 🎯 Export simple API
315
- export { wuSimple as wu }
316
- export default wuSimple