wu-framework 1.1.15 → 1.1.17

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.
Files changed (88) hide show
  1. package/README.md +52 -20
  2. package/dist/wu-framework.cjs.js +1 -1
  3. package/dist/wu-framework.cjs.js.map +1 -1
  4. package/dist/wu-framework.dev.js +15511 -15146
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js +1 -1
  7. package/dist/wu-framework.esm.js.map +1 -1
  8. package/dist/wu-framework.umd.js +1 -1
  9. package/dist/wu-framework.umd.js.map +1 -1
  10. package/package.json +166 -161
  11. package/src/adapters/angular/ai.js +30 -30
  12. package/src/adapters/angular/index.d.ts +154 -154
  13. package/src/adapters/angular/index.js +932 -932
  14. package/src/adapters/angular.d.ts +3 -3
  15. package/src/adapters/angular.js +3 -3
  16. package/src/adapters/index.js +168 -168
  17. package/src/adapters/lit/ai.js +20 -20
  18. package/src/adapters/lit/index.d.ts +120 -120
  19. package/src/adapters/lit/index.js +721 -721
  20. package/src/adapters/lit.d.ts +3 -3
  21. package/src/adapters/lit.js +3 -3
  22. package/src/adapters/preact/ai.js +33 -33
  23. package/src/adapters/preact/index.d.ts +108 -108
  24. package/src/adapters/preact/index.js +661 -661
  25. package/src/adapters/preact.d.ts +3 -3
  26. package/src/adapters/preact.js +3 -3
  27. package/src/adapters/react/index.js +48 -54
  28. package/src/adapters/react.d.ts +3 -3
  29. package/src/adapters/react.js +3 -3
  30. package/src/adapters/shared.js +64 -64
  31. package/src/adapters/solid/ai.js +32 -32
  32. package/src/adapters/solid/index.d.ts +101 -101
  33. package/src/adapters/solid/index.js +586 -586
  34. package/src/adapters/solid.d.ts +3 -3
  35. package/src/adapters/solid.js +3 -3
  36. package/src/adapters/svelte/ai.js +31 -31
  37. package/src/adapters/svelte/index.d.ts +166 -166
  38. package/src/adapters/svelte/index.js +798 -798
  39. package/src/adapters/svelte.d.ts +3 -3
  40. package/src/adapters/svelte.js +3 -3
  41. package/src/adapters/vanilla/ai.js +30 -30
  42. package/src/adapters/vanilla/index.d.ts +179 -179
  43. package/src/adapters/vanilla/index.js +785 -785
  44. package/src/adapters/vanilla.d.ts +3 -3
  45. package/src/adapters/vanilla.js +3 -3
  46. package/src/adapters/vue/ai.js +52 -52
  47. package/src/adapters/vue/index.d.ts +299 -299
  48. package/src/adapters/vue/index.js +610 -610
  49. package/src/adapters/vue.d.ts +3 -3
  50. package/src/adapters/vue.js +3 -3
  51. package/src/ai/wu-ai-actions.js +261 -261
  52. package/src/ai/wu-ai-agent.js +546 -546
  53. package/src/ai/wu-ai-browser-primitives.js +354 -354
  54. package/src/ai/wu-ai-browser.js +380 -380
  55. package/src/ai/wu-ai-context.js +332 -332
  56. package/src/ai/wu-ai-conversation.js +613 -613
  57. package/src/ai/wu-ai-orchestrate.js +1021 -1021
  58. package/src/ai/wu-ai-permissions.js +381 -381
  59. package/src/ai/wu-ai-provider.js +700 -700
  60. package/src/ai/wu-ai-schema.js +225 -225
  61. package/src/ai/wu-ai-triggers.js +396 -396
  62. package/src/ai/wu-ai.js +804 -804
  63. package/src/core/wu-app.js +236 -236
  64. package/src/core/wu-cache.js +498 -477
  65. package/src/core/wu-core.js +1412 -1398
  66. package/src/core/wu-error-boundary.js +396 -382
  67. package/src/core/wu-event-bus.js +390 -348
  68. package/src/core/wu-hooks.js +350 -350
  69. package/src/core/wu-html-parser.js +199 -190
  70. package/src/core/wu-iframe-sandbox.js +328 -328
  71. package/src/core/wu-loader.js +385 -273
  72. package/src/core/wu-logger.js +142 -134
  73. package/src/core/wu-manifest.js +532 -509
  74. package/src/core/wu-mcp-bridge.js +432 -432
  75. package/src/core/wu-overrides.js +510 -510
  76. package/src/core/wu-performance.js +228 -228
  77. package/src/core/wu-plugin.js +401 -348
  78. package/src/core/wu-prefetch.js +414 -414
  79. package/src/core/wu-proxy-sandbox.js +477 -476
  80. package/src/core/wu-sandbox.js +779 -779
  81. package/src/core/wu-script-executor.js +161 -113
  82. package/src/core/wu-snapshot-sandbox.js +227 -227
  83. package/src/core/wu-store.js +13 -3
  84. package/src/core/wu-strategies.js +256 -256
  85. package/src/core/wu-style-bridge.js +477 -477
  86. package/src/index.d.ts +317 -0
  87. package/src/index.js +234 -224
  88. package/src/utils/dependency-resolver.js +327 -327
@@ -1,273 +1,385 @@
1
- /**
2
- * 🚀 WU-LOADER: SISTEMA DE CARGA DINÁMICA UNIVERSAL
3
- * Carga aplicaciones y componentes sin depender del framework
4
- */
5
-
6
- import { logger } from './wu-logger.js';
7
-
8
- export class WuLoader {
9
- constructor() {
10
- this.cache = new Map();
11
- this.loadingPromises = new Map();
12
-
13
- logger.debug('[WuLoader] 📦 Dynamic loader initialized');
14
- }
15
-
16
- /**
17
- * Cargar aplicación completa
18
- * @param {string} appUrl - URL base de la aplicación
19
- * @param {Object} manifest - Manifest de la aplicación
20
- * @returns {string} Código JavaScript de la aplicación
21
- */
22
- async loadApp(appUrl, manifest) {
23
- const entryFile = manifest?.entry || 'index.js';
24
- const fullUrl = `${appUrl}/${entryFile}`;
25
-
26
- logger.debug(`[WuLoader] 📥 Loading app from: ${fullUrl}`);
27
-
28
- try {
29
- // Verificar cache
30
- if (this.cache.has(fullUrl)) {
31
- logger.debug(`[WuLoader] Cache hit for: ${fullUrl}`);
32
- return this.cache.get(fullUrl);
33
- }
34
-
35
- // Verificar si ya está cargando
36
- if (this.loadingPromises.has(fullUrl)) {
37
- logger.debug(`[WuLoader] Loading in progress for: ${fullUrl}`);
38
- return await this.loadingPromises.get(fullUrl);
39
- }
40
-
41
- // Crear promesa de carga
42
- const loadingPromise = this.fetchCode(fullUrl);
43
- this.loadingPromises.set(fullUrl, loadingPromise);
44
-
45
- const code = await loadingPromise;
46
-
47
- // Limpiar promesa de carga y cachear resultado
48
- this.loadingPromises.delete(fullUrl);
49
- this.cache.set(fullUrl, code);
50
-
51
- logger.debug(`[WuLoader] ✅ App loaded successfully: ${fullUrl}`);
52
- return code;
53
-
54
- } catch (error) {
55
- this.loadingPromises.delete(fullUrl);
56
- console.error(`[WuLoader] ❌ Failed to load app: ${fullUrl}`, error);
57
- throw new Error(`Failed to load app from ${fullUrl}: ${error.message}`);
58
- }
59
- }
60
-
61
- /**
62
- * Cargar componente específico
63
- * @param {string} appUrl - URL base de la aplicación
64
- * @param {string} componentPath - Ruta del componente
65
- * @returns {Function} Función del componente
66
- */
67
- async loadComponent(appUrl, componentPath) {
68
- // Normalizar ruta del componente
69
- let normalizedPath = componentPath;
70
- if (normalizedPath.startsWith('./')) {
71
- normalizedPath = normalizedPath.substring(2);
72
- }
73
- if (!normalizedPath.endsWith('.js') && !normalizedPath.endsWith('.jsx')) {
74
- normalizedPath += '.js';
75
- }
76
-
77
- const fullUrl = `${appUrl}/${normalizedPath}`;
78
-
79
- logger.debug(`[WuLoader] 🧩 Loading component from: ${fullUrl}`);
80
-
81
- try {
82
- // Cargar código del componente
83
- const code = await this.loadCode(fullUrl);
84
-
85
- // Crear función que retorna el componente
86
- const componentFunction = new Function('require', 'module', 'exports', `
87
- ${code}
88
- return typeof module.exports === 'function' ? module.exports :
89
- typeof module.exports === 'object' && module.exports.default ? module.exports.default :
90
- exports.default || exports;
91
- `);
92
-
93
- // Ejecutar y obtener el componente
94
- const fakeModule = { exports: {} };
95
- const fakeRequire = (name) => {
96
- logger.warn(`[WuLoader] Component ${componentPath} requires ${name} - not supported yet`);
97
- return {};
98
- };
99
-
100
- const component = componentFunction(fakeRequire, fakeModule, fakeModule.exports);
101
-
102
- logger.debug(`[WuLoader] Component loaded: ${componentPath}`);
103
- return component;
104
-
105
- } catch (error) {
106
- console.error(`[WuLoader] ❌ Failed to load component: ${componentPath}`, error);
107
- throw new Error(`Failed to load component ${componentPath}: ${error.message}`);
108
- }
109
- }
110
-
111
- /**
112
- * Cargar código con cache
113
- * @param {string} url - URL del archivo
114
- * @returns {string} Código JavaScript
115
- */
116
- async loadCode(url) {
117
- // Verificar cache
118
- if (this.cache.has(url)) {
119
- return this.cache.get(url);
120
- }
121
-
122
- // Verificar si ya está cargando
123
- if (this.loadingPromises.has(url)) {
124
- return await this.loadingPromises.get(url);
125
- }
126
-
127
- // Crear promesa de carga
128
- const loadingPromise = this.fetchCode(url);
129
- this.loadingPromises.set(url, loadingPromise);
130
-
131
- try {
132
- const code = await loadingPromise;
133
- this.loadingPromises.delete(url);
134
- this.cache.set(url, code);
135
- return code;
136
- } catch (error) {
137
- this.loadingPromises.delete(url);
138
- throw error;
139
- }
140
- }
141
-
142
- /**
143
- * Realizar fetch del código
144
- * @param {string} url - URL del archivo
145
- * @returns {string} Código JavaScript
146
- */
147
- async fetchCode(url) {
148
- const response = await fetch(url, {
149
- cache: 'no-cache',
150
- headers: {
151
- 'Accept': 'application/javascript, text/javascript, */*'
152
- }
153
- });
154
-
155
- if (!response.ok) {
156
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
157
- }
158
-
159
- const code = await response.text();
160
-
161
- if (!code.trim()) {
162
- throw new Error('Empty response');
163
- }
164
-
165
- return code;
166
- }
167
-
168
- /**
169
- * Precargar aplicaciones
170
- * @param {Array} appConfigs - Configuraciones de aplicaciones
171
- */
172
- async preload(appConfigs) {
173
- logger.debug(`[WuLoader] 🚀 Preloading ${appConfigs.length} apps...`);
174
-
175
- const preloadPromises = appConfigs.map(async (config) => {
176
- try {
177
- await this.loadApp(config.url, config.manifest);
178
- logger.debug(`[WuLoader] Preloaded: ${config.name}`);
179
- } catch (error) {
180
- logger.warn(`[WuLoader] ⚠️ Failed to preload ${config.name}:`, error.message);
181
- }
182
- });
183
-
184
- await Promise.allSettled(preloadPromises);
185
- logger.debug(`[WuLoader] 🎉 Preload completed`);
186
- }
187
-
188
- /**
189
- * Verificar si una URL está disponible
190
- * @param {string} url - URL a verificar
191
- * @returns {boolean} True si está disponible
192
- */
193
- async isAvailable(url) {
194
- try {
195
- const response = await fetch(url, { method: 'HEAD' });
196
- return response.ok;
197
- } catch {
198
- return false;
199
- }
200
- }
201
-
202
- /**
203
- * Resolver dependencias de imports
204
- * @param {Array} imports - Lista de imports del manifest
205
- * @param {Map} availableApps - Apps disponibles
206
- */
207
- async resolveDependencies(imports, availableApps) {
208
- const resolved = new Map();
209
-
210
- for (const importPath of imports || []) {
211
- const [appName, componentName] = importPath.split('.');
212
-
213
- if (!appName || !componentName) {
214
- logger.warn(`[WuLoader] Invalid import format: ${importPath}`);
215
- continue;
216
- }
217
-
218
- const app = availableApps.get(appName);
219
- if (!app) {
220
- logger.warn(`[WuLoader] Import app not found: ${appName}`);
221
- continue;
222
- }
223
-
224
- const manifest = app.manifest;
225
- const exportPath = manifest?.wu?.exports?.[componentName];
226
-
227
- if (!exportPath) {
228
- logger.warn(`[WuLoader] Export not found: ${importPath}`);
229
- continue;
230
- }
231
-
232
- try {
233
- const component = await this.loadComponent(app.url, exportPath);
234
- resolved.set(importPath, component);
235
- logger.debug(`[WuLoader] ✅ Resolved dependency: ${importPath}`);
236
- } catch (error) {
237
- console.error(`[WuLoader] Failed to resolve: ${importPath}`, error);
238
- }
239
- }
240
-
241
- return resolved;
242
- }
243
-
244
- /**
245
- * Limpiar cache
246
- * @param {string} pattern - Patrón opcional para limpiar URLs específicas
247
- */
248
- clearCache(pattern) {
249
- if (pattern) {
250
- const regex = new RegExp(pattern);
251
- for (const [url] of this.cache) {
252
- if (regex.test(url)) {
253
- this.cache.delete(url);
254
- logger.debug(`[WuLoader] 🗑️ Cleared cache for: ${url}`);
255
- }
256
- }
257
- } else {
258
- this.cache.clear();
259
- logger.debug(`[WuLoader] 🗑️ Cache cleared completely`);
260
- }
261
- }
262
-
263
- /**
264
- * Obtener estadísticas del loader
265
- */
266
- getStats() {
267
- return {
268
- cached: this.cache.size,
269
- loading: this.loadingPromises.size,
270
- cacheKeys: Array.from(this.cache.keys())
271
- };
272
- }
273
- }
1
+ /**
2
+ * WU-LOADER: SISTEMA DE CARGA DINAMICA UNIVERSAL
3
+ * Carga aplicaciones y componentes sin depender del framework
4
+ *
5
+ * Cache strategy: LRU with TTL eviction.
6
+ * Entries track lastAccess time. When the cache reaches maxCacheSize,
7
+ * the least recently accessed entry is evicted. Entries older than
8
+ * cacheTTL are treated as stale and removed on access or eviction.
9
+ */
10
+
11
+ import { logger } from './wu-logger.js';
12
+
13
+ /**
14
+ * @typedef {Object} WuLoaderOptions
15
+ * @property {number} [maxCacheSize=50] - Maximum cache entries
16
+ * @property {number} [cacheTTL=1800000] - Cache TTL in ms (default 30min)
17
+ */
18
+
19
+ /**
20
+ * @typedef {Object} WuLoaderStats
21
+ * @property {number} cached - Number of cached entries
22
+ * @property {number} loading - Number of in-flight loads
23
+ * @property {number} maxCacheSize - Max cache size setting
24
+ * @property {number} cacheTTL - Cache TTL setting
25
+ * @property {string[]} cacheKeys - Cached URL keys
26
+ */
27
+
28
+ export class WuLoader {
29
+ /**
30
+ * @param {Object} options
31
+ * @param {number} [options.maxCacheSize=50] - Maximum number of entries in the cache
32
+ * @param {number} [options.cacheTTL=1800000] - Time-to-live for cache entries in ms (default 30 minutes)
33
+ */
34
+ constructor(options = {}) {
35
+ this.maxCacheSize = options.maxCacheSize ?? 50;
36
+ this.cacheTTL = options.cacheTTL ?? 1800000;
37
+ this.cache = new Map();
38
+ this.loadingPromises = new Map();
39
+
40
+ logger.debug('[WuLoader] Dynamic loader initialized');
41
+ }
42
+
43
+ /**
44
+ * Read from cache with TTL validation and LRU access tracking.
45
+ * Returns undefined if the entry does not exist or has expired.
46
+ * @param {string} key
47
+ * @returns {string|undefined}
48
+ */
49
+ _cacheGet(key) {
50
+ if (!this.cache.has(key)) {
51
+ return undefined;
52
+ }
53
+
54
+ const entry = this.cache.get(key);
55
+ const now = Date.now();
56
+
57
+ if (now - entry.timestamp > this.cacheTTL) {
58
+ this.cache.delete(key);
59
+ logger.debug(`[WuLoader] Cache expired for: ${key}`);
60
+ return undefined;
61
+ }
62
+
63
+ // Promote: delete and re-insert so iteration order reflects recency.
64
+ // Map iteration order in JS follows insertion order, so the oldest
65
+ // inserted entry is always first -- exactly what we need for LRU eviction.
66
+ this.cache.delete(key);
67
+ entry.lastAccess = now;
68
+ this.cache.set(key, entry);
69
+
70
+ return entry.code;
71
+ }
72
+
73
+ /**
74
+ * Write to cache. Evicts stale and LRU entries before inserting.
75
+ * @param {string} key
76
+ * @param {string} code
77
+ */
78
+ _cacheSet(key, code) {
79
+ // If the key already exists, remove it first so re-insertion
80
+ // moves it to the end (most-recently-used position).
81
+ if (this.cache.has(key)) {
82
+ this.cache.delete(key);
83
+ }
84
+
85
+ this._evictIfNeeded();
86
+
87
+ const now = Date.now();
88
+ this.cache.set(key, {
89
+ code,
90
+ timestamp: now,
91
+ lastAccess: now
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Evict entries until cache is below maxCacheSize.
97
+ *
98
+ * Two-pass strategy:
99
+ * 1. Remove all expired entries (TTL exceeded).
100
+ * 2. If still at capacity, remove the least recently accessed entry.
101
+ * Because Map preserves insertion order and _cacheGet promotes on
102
+ * access, the first key from the iterator is always the LRU entry.
103
+ */
104
+ _evictIfNeeded() {
105
+ const now = Date.now();
106
+
107
+ // Pass 1: purge expired entries
108
+ for (const [key, entry] of this.cache) {
109
+ if (now - entry.timestamp > this.cacheTTL) {
110
+ this.cache.delete(key);
111
+ logger.debug(`[WuLoader] Evicted expired entry: ${key}`);
112
+ }
113
+ }
114
+
115
+ // Pass 2: evict LRU entries until we are under the limit
116
+ while (this.cache.size >= this.maxCacheSize) {
117
+ // Map.keys().next() gives us the oldest-inserted key (LRU)
118
+ const oldestKey = this.cache.keys().next().value;
119
+ this.cache.delete(oldestKey);
120
+ logger.debug(`[WuLoader] Evicted LRU entry: ${oldestKey}`);
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Cargar aplicacion completa
126
+ * @param {string} appUrl - URL base de la aplicacion
127
+ * @param {Object} manifest - Manifest de la aplicacion
128
+ * @returns {string} Codigo JavaScript de la aplicacion
129
+ */
130
+ async loadApp(appUrl, manifest) {
131
+ const entryFile = manifest?.entry || 'index.js';
132
+ const fullUrl = `${appUrl}/${entryFile}`;
133
+
134
+ logger.debug(`[WuLoader] Loading app from: ${fullUrl}`);
135
+
136
+ try {
137
+ // Check cache with TTL and LRU tracking
138
+ const cached = this._cacheGet(fullUrl);
139
+ if (cached !== undefined) {
140
+ logger.debug(`[WuLoader] Cache hit for: ${fullUrl}`);
141
+ return cached;
142
+ }
143
+
144
+ // Check if already loading
145
+ if (this.loadingPromises.has(fullUrl)) {
146
+ logger.debug(`[WuLoader] Loading in progress for: ${fullUrl}`);
147
+ return await this.loadingPromises.get(fullUrl);
148
+ }
149
+
150
+ // Create loading promise
151
+ const loadingPromise = this.fetchCode(fullUrl);
152
+ this.loadingPromises.set(fullUrl, loadingPromise);
153
+
154
+ const code = await loadingPromise;
155
+
156
+ // Clean up loading promise and cache result
157
+ this.loadingPromises.delete(fullUrl);
158
+ this._cacheSet(fullUrl, code);
159
+
160
+ logger.debug(`[WuLoader] App loaded successfully: ${fullUrl}`);
161
+ return code;
162
+
163
+ } catch (error) {
164
+ this.loadingPromises.delete(fullUrl);
165
+ console.error(`[WuLoader] Failed to load app: ${fullUrl}`, error);
166
+ throw new Error(`Failed to load app from ${fullUrl}: ${error.message}`);
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Cargar componente especifico
172
+ * @param {string} appUrl - URL base de la aplicacion
173
+ * @param {string} componentPath - Ruta del componente
174
+ * @returns {Function} Funcion del componente
175
+ */
176
+ async loadComponent(appUrl, componentPath) {
177
+ // Normalizar ruta del componente
178
+ let normalizedPath = componentPath;
179
+ if (normalizedPath.startsWith('./')) {
180
+ normalizedPath = normalizedPath.substring(2);
181
+ }
182
+ if (!normalizedPath.endsWith('.js') && !normalizedPath.endsWith('.jsx')) {
183
+ normalizedPath += '.js';
184
+ }
185
+
186
+ const fullUrl = `${appUrl}/${normalizedPath}`;
187
+
188
+ logger.debug(`[WuLoader] Loading component from: ${fullUrl}`);
189
+
190
+ try {
191
+ // Cargar codigo del componente
192
+ const code = await this.loadCode(fullUrl);
193
+
194
+ // Crear funcion que retorna el componente
195
+ const componentFunction = new Function('require', 'module', 'exports', `
196
+ ${code}
197
+ return typeof module.exports === 'function' ? module.exports :
198
+ typeof module.exports === 'object' && module.exports.default ? module.exports.default :
199
+ exports.default || exports;
200
+ `);
201
+
202
+ // Ejecutar y obtener el componente
203
+ const fakeModule = { exports: {} };
204
+ const fakeRequire = (name) => {
205
+ logger.warn(`[WuLoader] Component ${componentPath} requires ${name} - not supported yet`);
206
+ return {};
207
+ };
208
+
209
+ const component = componentFunction(fakeRequire, fakeModule, fakeModule.exports);
210
+
211
+ logger.debug(`[WuLoader] Component loaded: ${componentPath}`);
212
+ return component;
213
+
214
+ } catch (error) {
215
+ console.error(`[WuLoader] Failed to load component: ${componentPath}`, error);
216
+ throw new Error(`Failed to load component ${componentPath}: ${error.message}`);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Cargar codigo con cache
222
+ * @param {string} url - URL del archivo
223
+ * @returns {string} Codigo JavaScript
224
+ */
225
+ async loadCode(url) {
226
+ // Check cache with TTL and LRU tracking
227
+ const cached = this._cacheGet(url);
228
+ if (cached !== undefined) {
229
+ return cached;
230
+ }
231
+
232
+ // Check if already loading
233
+ if (this.loadingPromises.has(url)) {
234
+ return await this.loadingPromises.get(url);
235
+ }
236
+
237
+ // Create loading promise
238
+ const loadingPromise = this.fetchCode(url);
239
+ this.loadingPromises.set(url, loadingPromise);
240
+
241
+ try {
242
+ const code = await loadingPromise;
243
+ this.loadingPromises.delete(url);
244
+ this._cacheSet(url, code);
245
+ return code;
246
+ } catch (error) {
247
+ this.loadingPromises.delete(url);
248
+ throw error;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Realizar fetch del codigo
254
+ * @param {string} url - URL del archivo
255
+ * @returns {string} Codigo JavaScript
256
+ */
257
+ async fetchCode(url) {
258
+ const response = await fetch(url, {
259
+ cache: 'no-cache',
260
+ headers: {
261
+ 'Accept': 'application/javascript, text/javascript, */*'
262
+ }
263
+ });
264
+
265
+ if (!response.ok) {
266
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
267
+ }
268
+
269
+ const code = await response.text();
270
+
271
+ if (!code.trim()) {
272
+ throw new Error('Empty response');
273
+ }
274
+
275
+ return code;
276
+ }
277
+
278
+ /**
279
+ * Precargar aplicaciones
280
+ * @param {Array} appConfigs - Configuraciones de aplicaciones
281
+ */
282
+ async preload(appConfigs) {
283
+ logger.debug(`[WuLoader] Preloading ${appConfigs.length} apps...`);
284
+
285
+ const preloadPromises = appConfigs.map(async (config) => {
286
+ try {
287
+ await this.loadApp(config.url, config.manifest);
288
+ logger.debug(`[WuLoader] Preloaded: ${config.name}`);
289
+ } catch (error) {
290
+ logger.warn(`[WuLoader] Failed to preload ${config.name}:`, error.message);
291
+ }
292
+ });
293
+
294
+ await Promise.allSettled(preloadPromises);
295
+ logger.debug(`[WuLoader] Preload completed`);
296
+ }
297
+
298
+ /**
299
+ * Verificar si una URL esta disponible
300
+ * @param {string} url - URL a verificar
301
+ * @returns {boolean} True si esta disponible
302
+ */
303
+ async isAvailable(url) {
304
+ try {
305
+ const response = await fetch(url, { method: 'HEAD' });
306
+ return response.ok;
307
+ } catch {
308
+ return false;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Resolver dependencias de imports
314
+ * @param {Array} imports - Lista de imports del manifest
315
+ * @param {Map} availableApps - Apps disponibles
316
+ */
317
+ async resolveDependencies(imports, availableApps) {
318
+ const resolved = new Map();
319
+
320
+ for (const importPath of imports || []) {
321
+ const [appName, componentName] = importPath.split('.');
322
+
323
+ if (!appName || !componentName) {
324
+ logger.warn(`[WuLoader] Invalid import format: ${importPath}`);
325
+ continue;
326
+ }
327
+
328
+ const app = availableApps.get(appName);
329
+ if (!app) {
330
+ logger.warn(`[WuLoader] Import app not found: ${appName}`);
331
+ continue;
332
+ }
333
+
334
+ const manifest = app.manifest;
335
+ const exportPath = manifest?.wu?.exports?.[componentName];
336
+
337
+ if (!exportPath) {
338
+ logger.warn(`[WuLoader] Export not found: ${importPath}`);
339
+ continue;
340
+ }
341
+
342
+ try {
343
+ const component = await this.loadComponent(app.url, exportPath);
344
+ resolved.set(importPath, component);
345
+ logger.debug(`[WuLoader] Resolved dependency: ${importPath}`);
346
+ } catch (error) {
347
+ console.error(`[WuLoader] Failed to resolve: ${importPath}`, error);
348
+ }
349
+ }
350
+
351
+ return resolved;
352
+ }
353
+
354
+ /**
355
+ * Limpiar cache
356
+ * @param {string} pattern - Patron opcional para limpiar URLs especificas
357
+ */
358
+ clearCache(pattern) {
359
+ if (pattern) {
360
+ const regex = new RegExp(pattern);
361
+ for (const [url] of this.cache) {
362
+ if (regex.test(url)) {
363
+ this.cache.delete(url);
364
+ logger.debug(`[WuLoader] Cleared cache for: ${url}`);
365
+ }
366
+ }
367
+ } else {
368
+ this.cache.clear();
369
+ logger.debug(`[WuLoader] Cache cleared completely`);
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Obtener estadisticas del loader
375
+ */
376
+ getStats() {
377
+ return {
378
+ cached: this.cache.size,
379
+ maxCacheSize: this.maxCacheSize,
380
+ cacheTTL: this.cacheTTL,
381
+ loading: this.loadingPromises.size,
382
+ cacheKeys: Array.from(this.cache.keys())
383
+ };
384
+ }
385
+ }