wu-framework 1.0.0

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.
@@ -0,0 +1,326 @@
1
+ /**
2
+ * 🔗 WU-DEPENDENCY-RESOLVER: SISTEMA DE RESOLUCIÓN DE DEPENDENCIAS
3
+ * Maneja imports/exports entre micro-apps
4
+ */
5
+
6
+ export class WuDependencyResolver {
7
+ constructor() {
8
+ this.resolvedComponents = new Map();
9
+ this.pendingResolutions = new Map();
10
+
11
+ console.log('[WuDependencyResolver] 🔗 Dependency resolver initialized');
12
+ }
13
+
14
+ /**
15
+ * Resolver todas las dependencias de una app
16
+ * @param {Array} imports - Lista de imports del manifest
17
+ * @param {Map} availableApps - Apps disponibles
18
+ * @returns {Map} Componentes resueltos
19
+ */
20
+ async resolveAll(imports, availableApps) {
21
+ console.log(`[WuDependencyResolver] 🔍 Resolving ${imports.length} dependencies...`);
22
+
23
+ const resolved = new Map();
24
+ const errors = [];
25
+
26
+ for (const importPath of imports) {
27
+ try {
28
+ const component = await this.resolve(importPath, availableApps);
29
+ resolved.set(importPath, component);
30
+ console.log(`[WuDependencyResolver] ✅ Resolved: ${importPath}`);
31
+ } catch (error) {
32
+ errors.push({ import: importPath, error: error.message });
33
+ console.warn(`[WuDependencyResolver] ❌ Failed to resolve: ${importPath}`, error);
34
+ }
35
+ }
36
+
37
+ if (errors.length > 0) {
38
+ console.warn(`[WuDependencyResolver] ⚠️ ${errors.length} dependencies failed to resolve:`, errors);
39
+ }
40
+
41
+ console.log(`[WuDependencyResolver] 🎉 Resolved ${resolved.size}/${imports.length} dependencies`);
42
+ return resolved;
43
+ }
44
+
45
+ /**
46
+ * Resolver una dependencia específica
47
+ * @param {string} importPath - Ruta de import (ej: "shared.Button")
48
+ * @param {Map} availableApps - Apps disponibles
49
+ * @returns {Function} Componente resuelto
50
+ */
51
+ async resolve(importPath, availableApps) {
52
+ // Verificar cache
53
+ if (this.resolvedComponents.has(importPath)) {
54
+ console.log(`[WuDependencyResolver] ⚡ Cache hit: ${importPath}`);
55
+ return this.resolvedComponents.get(importPath);
56
+ }
57
+
58
+ // Verificar si ya está resolviendo
59
+ if (this.pendingResolutions.has(importPath)) {
60
+ console.log(`[WuDependencyResolver] ⏳ Waiting for pending resolution: ${importPath}`);
61
+ return await this.pendingResolutions.get(importPath);
62
+ }
63
+
64
+ // Crear promesa de resolución
65
+ const resolutionPromise = this.performResolution(importPath, availableApps);
66
+ this.pendingResolutions.set(importPath, resolutionPromise);
67
+
68
+ try {
69
+ const component = await resolutionPromise;
70
+ this.pendingResolutions.delete(importPath);
71
+ this.resolvedComponents.set(importPath, component);
72
+ return component;
73
+ } catch (error) {
74
+ this.pendingResolutions.delete(importPath);
75
+ throw error;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Realizar la resolución efectiva
81
+ * @param {string} importPath - Ruta de import
82
+ * @param {Map} availableApps - Apps disponibles
83
+ * @returns {Function} Componente resuelto
84
+ */
85
+ async performResolution(importPath, availableApps) {
86
+ const [appName, componentName] = importPath.split('.');
87
+
88
+ if (!appName || !componentName) {
89
+ throw new Error(`Invalid import format: ${importPath}. Use "app.component"`);
90
+ }
91
+
92
+ // Buscar la app
93
+ const app = availableApps.get(appName);
94
+ if (!app) {
95
+ throw new Error(`App not found: ${appName}`);
96
+ }
97
+
98
+ // Buscar el export en el manifest
99
+ const manifest = app.manifest;
100
+ const exportPath = manifest?.wu?.exports?.[componentName];
101
+
102
+ if (!exportPath) {
103
+ throw new Error(`Component ${componentName} not exported by ${appName}`);
104
+ }
105
+
106
+ // Cargar el componente usando el loader de la app
107
+ const loader = app.loader || this.getDefaultLoader();
108
+ const component = await loader.loadComponent(app.url, exportPath);
109
+
110
+ if (!component) {
111
+ throw new Error(`Failed to load component: ${importPath}`);
112
+ }
113
+
114
+ return component;
115
+ }
116
+
117
+ /**
118
+ * Obtener loader por defecto si la app no tiene uno
119
+ */
120
+ getDefaultLoader() {
121
+ if (!this._defaultLoader) {
122
+ // Crear un loader básico si no hay uno disponible
123
+ this._defaultLoader = {
124
+ loadComponent: async (appUrl, componentPath) => {
125
+ const fullUrl = `${appUrl}/${componentPath}`;
126
+ const response = await fetch(fullUrl);
127
+
128
+ if (!response.ok) {
129
+ throw new Error(`Failed to fetch: ${fullUrl}`);
130
+ }
131
+
132
+ const code = await response.text();
133
+
134
+ // Evaluar código del componente
135
+ const componentFunction = new Function('require', 'module', 'exports', `
136
+ ${code}
137
+ return typeof module.exports === 'function' ? module.exports :
138
+ typeof module.exports === 'object' && module.exports.default ? module.exports.default :
139
+ exports.default || exports;
140
+ `);
141
+
142
+ const fakeModule = { exports: {} };
143
+ const fakeRequire = () => ({});
144
+
145
+ return componentFunction(fakeRequire, fakeModule, fakeModule.exports);
146
+ }
147
+ };
148
+ }
149
+ return this._defaultLoader;
150
+ }
151
+
152
+ /**
153
+ * Validar dependencias de una app
154
+ * @param {Array} imports - Lista de imports
155
+ * @param {Map} availableApps - Apps disponibles
156
+ * @returns {Object} Resultado de validación
157
+ */
158
+ validate(imports, availableApps) {
159
+ const result = {
160
+ valid: [],
161
+ invalid: [],
162
+ missing: [],
163
+ circular: []
164
+ };
165
+
166
+ for (const importPath of imports) {
167
+ const [appName, componentName] = importPath.split('.');
168
+
169
+ // Validar formato
170
+ if (!appName || !componentName) {
171
+ result.invalid.push({
172
+ import: importPath,
173
+ reason: 'Invalid format. Use "app.component"'
174
+ });
175
+ continue;
176
+ }
177
+
178
+ // Validar que la app existe
179
+ const app = availableApps.get(appName);
180
+ if (!app) {
181
+ result.missing.push({
182
+ import: importPath,
183
+ app: appName,
184
+ reason: 'App not registered'
185
+ });
186
+ continue;
187
+ }
188
+
189
+ // Validar que el export existe
190
+ const manifest = app.manifest;
191
+ const exportExists = manifest?.wu?.exports?.[componentName];
192
+
193
+ if (!exportExists) {
194
+ result.invalid.push({
195
+ import: importPath,
196
+ reason: `Component ${componentName} not exported by ${appName}`
197
+ });
198
+ continue;
199
+ }
200
+
201
+ result.valid.push({
202
+ import: importPath,
203
+ app: appName,
204
+ component: componentName,
205
+ path: exportExists
206
+ });
207
+ }
208
+
209
+ // Detectar dependencias circulares
210
+ result.circular = this.detectCircularDependencies(imports, availableApps);
211
+
212
+ return result;
213
+ }
214
+
215
+ /**
216
+ * Detectar dependencias circulares
217
+ * @param {Array} imports - Lista de imports
218
+ * @param {Map} availableApps - Apps disponibles
219
+ * @returns {Array} Dependencias circulares encontradas
220
+ */
221
+ detectCircularDependencies(imports, availableApps) {
222
+ const circular = [];
223
+ const visited = new Set();
224
+ const visiting = new Set();
225
+
226
+ const visit = (appName, path = []) => {
227
+ if (visiting.has(appName)) {
228
+ // Encontrada dependencia circular
229
+ const circularPath = [...path, appName];
230
+ circular.push(circularPath);
231
+ return;
232
+ }
233
+
234
+ if (visited.has(appName)) {
235
+ return;
236
+ }
237
+
238
+ visiting.add(appName);
239
+ const currentPath = [...path, appName];
240
+
241
+ // Buscar las dependencias de esta app
242
+ const app = availableApps.get(appName);
243
+ if (app && app.manifest?.wu?.imports) {
244
+ for (const importPath of app.manifest.wu.imports) {
245
+ const [depAppName] = importPath.split('.');
246
+ if (depAppName) {
247
+ visit(depAppName, currentPath);
248
+ }
249
+ }
250
+ }
251
+
252
+ visiting.delete(appName);
253
+ visited.add(appName);
254
+ };
255
+
256
+ // Visitar todas las apps
257
+ for (const app of availableApps.keys()) {
258
+ visit(app);
259
+ }
260
+
261
+ return circular;
262
+ }
263
+
264
+ /**
265
+ * Crear wrapper para componente compartido
266
+ * @param {Function} component - Componente original
267
+ * @param {string} importPath - Ruta de import
268
+ * @returns {Function} Componente wrapper
269
+ */
270
+ createComponentWrapper(component, importPath) {
271
+ return function WuSharedComponent(props) {
272
+ console.log(`[WuDependencyResolver] 🧩 Rendering shared component: ${importPath}`);
273
+
274
+ try {
275
+ return component(props);
276
+ } catch (error) {
277
+ console.error(`[WuDependencyResolver] ❌ Error in shared component ${importPath}:`, error);
278
+
279
+ // Componente de error fallback
280
+ return {
281
+ type: 'div',
282
+ props: {
283
+ style: {
284
+ padding: '10px',
285
+ border: '1px solid #ff6b6b',
286
+ borderRadius: '4px',
287
+ background: '#ffe0e0',
288
+ color: '#d63031'
289
+ },
290
+ children: `Error in shared component: ${importPath}`
291
+ }
292
+ };
293
+ }
294
+ };
295
+ }
296
+
297
+ /**
298
+ * Limpiar cache de dependencias
299
+ * @param {string} pattern - Patrón opcional
300
+ */
301
+ clearCache(pattern) {
302
+ if (pattern) {
303
+ const regex = new RegExp(pattern);
304
+ for (const [importPath] of this.resolvedComponents) {
305
+ if (regex.test(importPath)) {
306
+ this.resolvedComponents.delete(importPath);
307
+ console.log(`[WuDependencyResolver] 🗑️ Cleared cache for: ${importPath}`);
308
+ }
309
+ }
310
+ } else {
311
+ this.resolvedComponents.clear();
312
+ console.log(`[WuDependencyResolver] 🗑️ Dependency cache cleared completely`);
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Obtener estadísticas del resolver
318
+ */
319
+ getStats() {
320
+ return {
321
+ resolved: this.resolvedComponents.size,
322
+ pending: this.pendingResolutions.size,
323
+ components: Array.from(this.resolvedComponents.keys())
324
+ };
325
+ }
326
+ }