wu-framework 1.1.14 → 1.1.16

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 (90) hide show
  1. package/LICENSE +39 -39
  2. package/README.md +408 -408
  3. package/dist/wu-framework.cjs.js.map +1 -1
  4. package/dist/wu-framework.dev.js +15151 -15151
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js.map +1 -1
  7. package/dist/wu-framework.umd.js.map +1 -1
  8. package/integrations/astro/README.md +127 -127
  9. package/integrations/astro/WuApp.astro +63 -63
  10. package/integrations/astro/WuShell.astro +39 -39
  11. package/integrations/astro/index.js +68 -68
  12. package/integrations/astro/package.json +38 -38
  13. package/integrations/astro/types.d.ts +53 -53
  14. package/package.json +161 -161
  15. package/src/adapters/angular/ai.js +30 -30
  16. package/src/adapters/angular/index.d.ts +154 -154
  17. package/src/adapters/angular/index.js +932 -932
  18. package/src/adapters/angular.d.ts +3 -3
  19. package/src/adapters/angular.js +3 -3
  20. package/src/adapters/index.js +168 -168
  21. package/src/adapters/lit/ai.js +20 -20
  22. package/src/adapters/lit/index.d.ts +120 -120
  23. package/src/adapters/lit/index.js +721 -721
  24. package/src/adapters/lit.d.ts +3 -3
  25. package/src/adapters/lit.js +3 -3
  26. package/src/adapters/preact/ai.js +33 -33
  27. package/src/adapters/preact/index.d.ts +108 -108
  28. package/src/adapters/preact/index.js +661 -661
  29. package/src/adapters/preact.d.ts +3 -3
  30. package/src/adapters/preact.js +3 -3
  31. package/src/adapters/react/index.js +48 -54
  32. package/src/adapters/react.d.ts +3 -3
  33. package/src/adapters/react.js +3 -3
  34. package/src/adapters/shared.js +64 -64
  35. package/src/adapters/solid/ai.js +32 -32
  36. package/src/adapters/solid/index.d.ts +101 -101
  37. package/src/adapters/solid/index.js +586 -586
  38. package/src/adapters/solid.d.ts +3 -3
  39. package/src/adapters/solid.js +3 -3
  40. package/src/adapters/svelte/ai.js +31 -31
  41. package/src/adapters/svelte/index.d.ts +166 -166
  42. package/src/adapters/svelte/index.js +798 -798
  43. package/src/adapters/svelte.d.ts +3 -3
  44. package/src/adapters/svelte.js +3 -3
  45. package/src/adapters/vanilla/ai.js +30 -30
  46. package/src/adapters/vanilla/index.d.ts +179 -179
  47. package/src/adapters/vanilla/index.js +785 -785
  48. package/src/adapters/vanilla.d.ts +3 -3
  49. package/src/adapters/vanilla.js +3 -3
  50. package/src/adapters/vue/ai.js +52 -52
  51. package/src/adapters/vue/index.d.ts +299 -299
  52. package/src/adapters/vue/index.js +610 -610
  53. package/src/adapters/vue.d.ts +3 -3
  54. package/src/adapters/vue.js +3 -3
  55. package/src/ai/wu-ai-actions.js +261 -261
  56. package/src/ai/wu-ai-agent.js +546 -546
  57. package/src/ai/wu-ai-browser-primitives.js +354 -354
  58. package/src/ai/wu-ai-browser.js +380 -380
  59. package/src/ai/wu-ai-context.js +332 -332
  60. package/src/ai/wu-ai-conversation.js +613 -613
  61. package/src/ai/wu-ai-orchestrate.js +1021 -1021
  62. package/src/ai/wu-ai-permissions.js +381 -381
  63. package/src/ai/wu-ai-provider.js +700 -700
  64. package/src/ai/wu-ai-schema.js +225 -225
  65. package/src/ai/wu-ai-triggers.js +396 -396
  66. package/src/ai/wu-ai.js +804 -804
  67. package/src/core/wu-app.js +236 -236
  68. package/src/core/wu-cache.js +477 -477
  69. package/src/core/wu-core.js +1398 -1398
  70. package/src/core/wu-error-boundary.js +382 -382
  71. package/src/core/wu-event-bus.js +348 -348
  72. package/src/core/wu-hooks.js +350 -350
  73. package/src/core/wu-html-parser.js +190 -190
  74. package/src/core/wu-iframe-sandbox.js +328 -328
  75. package/src/core/wu-loader.js +272 -272
  76. package/src/core/wu-logger.js +134 -134
  77. package/src/core/wu-manifest.js +509 -509
  78. package/src/core/wu-mcp-bridge.js +432 -432
  79. package/src/core/wu-overrides.js +510 -510
  80. package/src/core/wu-performance.js +228 -228
  81. package/src/core/wu-plugin.js +348 -348
  82. package/src/core/wu-prefetch.js +414 -414
  83. package/src/core/wu-proxy-sandbox.js +476 -476
  84. package/src/core/wu-sandbox.js +779 -779
  85. package/src/core/wu-script-executor.js +113 -113
  86. package/src/core/wu-snapshot-sandbox.js +227 -227
  87. package/src/core/wu-strategies.js +256 -256
  88. package/src/core/wu-style-bridge.js +477 -477
  89. package/src/index.js +224 -224
  90. package/src/utils/dependency-resolver.js +327 -327
@@ -1,382 +1,382 @@
1
- /**
2
- * 🛡️ WU-ERROR-BOUNDARY: ADVANCED ERROR HANDLING
3
- *
4
- * Sistema de error boundaries con:
5
- * - Chain of Responsibility pattern
6
- * - Recovery strategies
7
- * - Error classification
8
- * - Fallback rendering
9
- */
10
-
11
- import { logger } from './wu-logger.js';
12
-
13
- export class WuErrorBoundary {
14
- constructor(core) {
15
- this.core = core;
16
- this.handlers = [];
17
- this.errorLog = [];
18
- this.maxErrorLog = 100;
19
-
20
- this.config = {
21
- maxRetries: 3,
22
- retryDelay: 1000,
23
- showErrorUI: true
24
- };
25
-
26
- this.registerDefaultHandlers();
27
-
28
- logger.debug('[WuErrorBoundary] 🛡️ Error boundary initialized');
29
- }
30
-
31
- /**
32
- * 📋 REGISTER DEFAULT HANDLERS: Chain of responsibility
33
- */
34
- registerDefaultHandlers() {
35
- // 1. Network Error Handler
36
- this.register({
37
- name: 'network',
38
- canHandle: (error) => {
39
- return error.name === 'TypeError' &&
40
- (error.message.includes('fetch') || error.message.includes('network'));
41
- },
42
- handle: async (error, context) => {
43
- logger.debug('[ErrorHandler:Network] Handling network error');
44
-
45
- // Retry con backoff
46
- if (context.retryCount < this.config.maxRetries) {
47
- const delay = this.config.retryDelay * Math.pow(2, context.retryCount);
48
- logger.debug(`[ErrorHandler:Network] Retrying in ${delay}ms...`);
49
-
50
- await new Promise(resolve => setTimeout(resolve, delay));
51
-
52
- return {
53
- recovered: true,
54
- action: 'retry',
55
- retryCount: context.retryCount + 1
56
- };
57
- }
58
-
59
- return {
60
- recovered: false,
61
- action: 'fallback',
62
- message: 'Network error: Please check your connection'
63
- };
64
- }
65
- });
66
-
67
- // 2. Script Load Error Handler
68
- this.register({
69
- name: 'script-load',
70
- canHandle: (error) => {
71
- return error.name === 'Error' &&
72
- (error.message.includes('Loading') ||
73
- error.message.includes('Failed to fetch'));
74
- },
75
- handle: async (error, context) => {
76
- logger.debug('[ErrorHandler:ScriptLoad] Handling script load error');
77
-
78
- // Intentar URL alternativa si existe
79
- if (context.fallbackUrl) {
80
- logger.debug('[ErrorHandler:ScriptLoad] Trying fallback URL');
81
- return {
82
- recovered: true,
83
- action: 'use-fallback-url',
84
- url: context.fallbackUrl
85
- };
86
- }
87
-
88
- return {
89
- recovered: false,
90
- action: 'fallback',
91
- message: 'Failed to load microfrontend'
92
- };
93
- }
94
- });
95
-
96
- // 3. Mount Error Handler
97
- this.register({
98
- name: 'mount',
99
- canHandle: (error) => {
100
- return error.message && error.message.includes('mount');
101
- },
102
- handle: async (error, context) => {
103
- logger.debug('[ErrorHandler:Mount] Handling mount error');
104
-
105
- // Limpiar y reintentar
106
- if (context.retryCount < 2) {
107
- logger.debug('[ErrorHandler:Mount] Cleaning up and retrying...');
108
-
109
- // Cleanup
110
- try {
111
- await this.core.unmount(context.appName);
112
- } catch (cleanupError) {
113
- logger.warn('[ErrorHandler:Mount] Cleanup failed:', cleanupError);
114
- }
115
-
116
- await new Promise(resolve => setTimeout(resolve, 500));
117
-
118
- return {
119
- recovered: true,
120
- action: 'retry',
121
- retryCount: context.retryCount + 1
122
- };
123
- }
124
-
125
- return {
126
- recovered: false,
127
- action: 'fallback',
128
- message: 'Failed to mount application'
129
- };
130
- }
131
- });
132
-
133
- // 4. Timeout Error Handler
134
- this.register({
135
- name: 'timeout',
136
- canHandle: (error) => {
137
- return error.name === 'TimeoutError' ||
138
- error.message.includes('timeout');
139
- },
140
- handle: async (error, context) => {
141
- logger.debug('[ErrorHandler:Timeout] Handling timeout error');
142
-
143
- // Aumentar timeout y reintentar
144
- if (context.retryCount < 2) {
145
- return {
146
- recovered: true,
147
- action: 'retry-with-longer-timeout',
148
- timeout: (context.timeout || 5000) * 2,
149
- retryCount: context.retryCount + 1
150
- };
151
- }
152
-
153
- return {
154
- recovered: false,
155
- action: 'fallback',
156
- message: 'Operation timed out'
157
- };
158
- }
159
- });
160
-
161
- // 5. Generic Error Handler (fallback)
162
- this.register({
163
- name: 'generic',
164
- canHandle: () => true, // Maneja todo
165
- handle: async (error, context) => {
166
- logger.debug('[ErrorHandler:Generic] Handling generic error');
167
-
168
- return {
169
- recovered: false,
170
- action: 'fallback',
171
- message: error.message || 'An unexpected error occurred'
172
- };
173
- }
174
- });
175
- }
176
-
177
- /**
178
- * 📦 REGISTER: Registrar error handler
179
- * @param {Object} handler - Error handler { name, canHandle, handle }
180
- */
181
- register(handler) {
182
- if (!handler.name || !handler.canHandle || !handler.handle) {
183
- throw new Error('[WuErrorBoundary] Handler must have name, canHandle, and handle');
184
- }
185
-
186
- this.handlers.push(handler);
187
- logger.debug(`[WuErrorBoundary] Handler "${handler.name}" registered`);
188
- }
189
-
190
- /**
191
- * 🎯 HANDLE: Manejar error con chain of responsibility
192
- * @param {Error} error - Error a manejar
193
- * @param {Object} context - Contexto del error
194
- * @returns {Promise<Object>} Recovery result
195
- */
196
- async handle(error, context = {}) {
197
- // Agregar valores por defecto
198
- context = {
199
- retryCount: 0,
200
- timestamp: Date.now(),
201
- ...context
202
- };
203
-
204
- // Log error
205
- this.logError(error, context);
206
-
207
- // Buscar handler que pueda manejar este error
208
- for (const handler of this.handlers) {
209
- try {
210
- if (handler.canHandle(error, context)) {
211
- logger.debug(`[WuErrorBoundary] Using handler: ${handler.name}`);
212
-
213
- const result = await handler.handle(error, context);
214
-
215
- if (result.recovered) {
216
- logger.debug(`[WuErrorBoundary] ✅ Error recovered by ${handler.name}`);
217
- return result;
218
- }
219
-
220
- // Si no se recuperó, renderizar fallback
221
- if (result.action === 'fallback' && this.config.showErrorUI) {
222
- this.renderFallback(context, result);
223
- }
224
-
225
- return result;
226
- }
227
- } catch (handlerError) {
228
- console.error(`[WuErrorBoundary] Handler "${handler.name}" failed:`, handlerError);
229
- }
230
- }
231
-
232
- // No handler pudo manejar el error
233
- console.error('[WuErrorBoundary] ❌ No handler could handle the error');
234
-
235
- return {
236
- recovered: false,
237
- action: 'unhandled',
238
- message: 'Unhandled error'
239
- };
240
- }
241
-
242
- /**
243
- * 🎨 RENDER FALLBACK: Renderizar UI de error
244
- * @param {Object} context - Contexto del error
245
- * @param {Object} result - Resultado del handler
246
- */
247
- renderFallback(context, result) {
248
- if (!context.container) {
249
- logger.warn('[WuErrorBoundary] No container to render fallback');
250
- return;
251
- }
252
-
253
- const container = typeof context.container === 'string'
254
- ? document.querySelector(context.container)
255
- : context.container;
256
-
257
- if (!container) return;
258
-
259
- // Limpiar container
260
- container.innerHTML = '';
261
-
262
- // Crear UI de error
263
- const errorUI = document.createElement('div');
264
- errorUI.className = 'wu-error-boundary';
265
-
266
- Object.assign(errorUI.style, {
267
- padding: '2rem',
268
- borderRadius: '8px',
269
- background: '#fff3cd',
270
- border: '1px solid #ffc107',
271
- color: '#856404',
272
- fontFamily: 'system-ui, -apple-system, sans-serif',
273
- textAlign: 'center'
274
- });
275
-
276
- const icon = document.createElement('div');
277
- icon.textContent = '⚠️';
278
- icon.style.fontSize = '3rem';
279
- icon.style.marginBottom = '1rem';
280
-
281
- const title = document.createElement('h3');
282
- title.textContent = 'Application Error';
283
- title.style.margin = '0 0 0.5rem 0';
284
-
285
- const message = document.createElement('p');
286
- message.textContent = result.message || 'An error occurred';
287
- message.style.margin = '0 0 1rem 0';
288
-
289
- const button = document.createElement('button');
290
- button.textContent = '🔄 Reload';
291
- Object.assign(button.style, {
292
- padding: '0.5rem 1rem',
293
- background: '#ffc107',
294
- border: 'none',
295
- borderRadius: '4px',
296
- cursor: 'pointer',
297
- fontWeight: 'bold',
298
- color: '#000'
299
- });
300
-
301
- button.addEventListener('click', () => window.location.reload());
302
-
303
- errorUI.appendChild(icon);
304
- errorUI.appendChild(title);
305
- errorUI.appendChild(message);
306
- errorUI.appendChild(button);
307
-
308
- container.appendChild(errorUI);
309
- }
310
-
311
- /**
312
- * 📝 LOG ERROR: Registrar error
313
- * @param {Error} error - Error
314
- * @param {Object} context - Contexto
315
- */
316
- logError(error, context) {
317
- const errorEntry = {
318
- error: {
319
- name: error.name,
320
- message: error.message,
321
- stack: error.stack
322
- },
323
- context,
324
- timestamp: Date.now()
325
- };
326
-
327
- this.errorLog.push(errorEntry);
328
-
329
- // Mantener límite de log
330
- if (this.errorLog.length > this.maxErrorLog) {
331
- this.errorLog.shift();
332
- }
333
- }
334
-
335
- /**
336
- * 📋 GET ERROR LOG: Obtener log de errores
337
- * @param {number} limit - Límite de errores a retornar
338
- * @returns {Array}
339
- */
340
- getErrorLog(limit = 10) {
341
- return this.errorLog.slice(-limit);
342
- }
343
-
344
- /**
345
- * 📊 GET STATS: Estadísticas de errores
346
- * @returns {Object}
347
- */
348
- getStats() {
349
- const errorsByType = {};
350
-
351
- this.errorLog.forEach(entry => {
352
- const type = entry.error.name || 'Unknown';
353
- errorsByType[type] = (errorsByType[type] || 0) + 1;
354
- });
355
-
356
- return {
357
- totalErrors: this.errorLog.length,
358
- handlers: this.handlers.length,
359
- errorsByType,
360
- recentErrors: this.getErrorLog(5)
361
- };
362
- }
363
-
364
- /**
365
- * ⚙️ CONFIGURE: Configurar error boundary
366
- * @param {Object} config - Nueva configuración
367
- */
368
- configure(config) {
369
- this.config = {
370
- ...this.config,
371
- ...config
372
- };
373
- }
374
-
375
- /**
376
- * 🧹 CLEANUP: Limpiar error boundary
377
- */
378
- cleanup() {
379
- this.errorLog = [];
380
- logger.debug('[WuErrorBoundary] 🧹 Error boundary cleaned up');
381
- }
382
- }
1
+ /**
2
+ * 🛡️ WU-ERROR-BOUNDARY: ADVANCED ERROR HANDLING
3
+ *
4
+ * Sistema de error boundaries con:
5
+ * - Chain of Responsibility pattern
6
+ * - Recovery strategies
7
+ * - Error classification
8
+ * - Fallback rendering
9
+ */
10
+
11
+ import { logger } from './wu-logger.js';
12
+
13
+ export class WuErrorBoundary {
14
+ constructor(core) {
15
+ this.core = core;
16
+ this.handlers = [];
17
+ this.errorLog = [];
18
+ this.maxErrorLog = 100;
19
+
20
+ this.config = {
21
+ maxRetries: 3,
22
+ retryDelay: 1000,
23
+ showErrorUI: true
24
+ };
25
+
26
+ this.registerDefaultHandlers();
27
+
28
+ logger.debug('[WuErrorBoundary] 🛡️ Error boundary initialized');
29
+ }
30
+
31
+ /**
32
+ * 📋 REGISTER DEFAULT HANDLERS: Chain of responsibility
33
+ */
34
+ registerDefaultHandlers() {
35
+ // 1. Network Error Handler
36
+ this.register({
37
+ name: 'network',
38
+ canHandle: (error) => {
39
+ return error.name === 'TypeError' &&
40
+ (error.message.includes('fetch') || error.message.includes('network'));
41
+ },
42
+ handle: async (error, context) => {
43
+ logger.debug('[ErrorHandler:Network] Handling network error');
44
+
45
+ // Retry con backoff
46
+ if (context.retryCount < this.config.maxRetries) {
47
+ const delay = this.config.retryDelay * Math.pow(2, context.retryCount);
48
+ logger.debug(`[ErrorHandler:Network] Retrying in ${delay}ms...`);
49
+
50
+ await new Promise(resolve => setTimeout(resolve, delay));
51
+
52
+ return {
53
+ recovered: true,
54
+ action: 'retry',
55
+ retryCount: context.retryCount + 1
56
+ };
57
+ }
58
+
59
+ return {
60
+ recovered: false,
61
+ action: 'fallback',
62
+ message: 'Network error: Please check your connection'
63
+ };
64
+ }
65
+ });
66
+
67
+ // 2. Script Load Error Handler
68
+ this.register({
69
+ name: 'script-load',
70
+ canHandle: (error) => {
71
+ return error.name === 'Error' &&
72
+ (error.message.includes('Loading') ||
73
+ error.message.includes('Failed to fetch'));
74
+ },
75
+ handle: async (error, context) => {
76
+ logger.debug('[ErrorHandler:ScriptLoad] Handling script load error');
77
+
78
+ // Intentar URL alternativa si existe
79
+ if (context.fallbackUrl) {
80
+ logger.debug('[ErrorHandler:ScriptLoad] Trying fallback URL');
81
+ return {
82
+ recovered: true,
83
+ action: 'use-fallback-url',
84
+ url: context.fallbackUrl
85
+ };
86
+ }
87
+
88
+ return {
89
+ recovered: false,
90
+ action: 'fallback',
91
+ message: 'Failed to load microfrontend'
92
+ };
93
+ }
94
+ });
95
+
96
+ // 3. Mount Error Handler
97
+ this.register({
98
+ name: 'mount',
99
+ canHandle: (error) => {
100
+ return error.message && error.message.includes('mount');
101
+ },
102
+ handle: async (error, context) => {
103
+ logger.debug('[ErrorHandler:Mount] Handling mount error');
104
+
105
+ // Limpiar y reintentar
106
+ if (context.retryCount < 2) {
107
+ logger.debug('[ErrorHandler:Mount] Cleaning up and retrying...');
108
+
109
+ // Cleanup
110
+ try {
111
+ await this.core.unmount(context.appName);
112
+ } catch (cleanupError) {
113
+ logger.warn('[ErrorHandler:Mount] Cleanup failed:', cleanupError);
114
+ }
115
+
116
+ await new Promise(resolve => setTimeout(resolve, 500));
117
+
118
+ return {
119
+ recovered: true,
120
+ action: 'retry',
121
+ retryCount: context.retryCount + 1
122
+ };
123
+ }
124
+
125
+ return {
126
+ recovered: false,
127
+ action: 'fallback',
128
+ message: 'Failed to mount application'
129
+ };
130
+ }
131
+ });
132
+
133
+ // 4. Timeout Error Handler
134
+ this.register({
135
+ name: 'timeout',
136
+ canHandle: (error) => {
137
+ return error.name === 'TimeoutError' ||
138
+ error.message.includes('timeout');
139
+ },
140
+ handle: async (error, context) => {
141
+ logger.debug('[ErrorHandler:Timeout] Handling timeout error');
142
+
143
+ // Aumentar timeout y reintentar
144
+ if (context.retryCount < 2) {
145
+ return {
146
+ recovered: true,
147
+ action: 'retry-with-longer-timeout',
148
+ timeout: (context.timeout || 5000) * 2,
149
+ retryCount: context.retryCount + 1
150
+ };
151
+ }
152
+
153
+ return {
154
+ recovered: false,
155
+ action: 'fallback',
156
+ message: 'Operation timed out'
157
+ };
158
+ }
159
+ });
160
+
161
+ // 5. Generic Error Handler (fallback)
162
+ this.register({
163
+ name: 'generic',
164
+ canHandle: () => true, // Maneja todo
165
+ handle: async (error, context) => {
166
+ logger.debug('[ErrorHandler:Generic] Handling generic error');
167
+
168
+ return {
169
+ recovered: false,
170
+ action: 'fallback',
171
+ message: error.message || 'An unexpected error occurred'
172
+ };
173
+ }
174
+ });
175
+ }
176
+
177
+ /**
178
+ * 📦 REGISTER: Registrar error handler
179
+ * @param {Object} handler - Error handler { name, canHandle, handle }
180
+ */
181
+ register(handler) {
182
+ if (!handler.name || !handler.canHandle || !handler.handle) {
183
+ throw new Error('[WuErrorBoundary] Handler must have name, canHandle, and handle');
184
+ }
185
+
186
+ this.handlers.push(handler);
187
+ logger.debug(`[WuErrorBoundary] Handler "${handler.name}" registered`);
188
+ }
189
+
190
+ /**
191
+ * 🎯 HANDLE: Manejar error con chain of responsibility
192
+ * @param {Error} error - Error a manejar
193
+ * @param {Object} context - Contexto del error
194
+ * @returns {Promise<Object>} Recovery result
195
+ */
196
+ async handle(error, context = {}) {
197
+ // Agregar valores por defecto
198
+ context = {
199
+ retryCount: 0,
200
+ timestamp: Date.now(),
201
+ ...context
202
+ };
203
+
204
+ // Log error
205
+ this.logError(error, context);
206
+
207
+ // Buscar handler que pueda manejar este error
208
+ for (const handler of this.handlers) {
209
+ try {
210
+ if (handler.canHandle(error, context)) {
211
+ logger.debug(`[WuErrorBoundary] Using handler: ${handler.name}`);
212
+
213
+ const result = await handler.handle(error, context);
214
+
215
+ if (result.recovered) {
216
+ logger.debug(`[WuErrorBoundary] ✅ Error recovered by ${handler.name}`);
217
+ return result;
218
+ }
219
+
220
+ // Si no se recuperó, renderizar fallback
221
+ if (result.action === 'fallback' && this.config.showErrorUI) {
222
+ this.renderFallback(context, result);
223
+ }
224
+
225
+ return result;
226
+ }
227
+ } catch (handlerError) {
228
+ console.error(`[WuErrorBoundary] Handler "${handler.name}" failed:`, handlerError);
229
+ }
230
+ }
231
+
232
+ // No handler pudo manejar el error
233
+ console.error('[WuErrorBoundary] ❌ No handler could handle the error');
234
+
235
+ return {
236
+ recovered: false,
237
+ action: 'unhandled',
238
+ message: 'Unhandled error'
239
+ };
240
+ }
241
+
242
+ /**
243
+ * 🎨 RENDER FALLBACK: Renderizar UI de error
244
+ * @param {Object} context - Contexto del error
245
+ * @param {Object} result - Resultado del handler
246
+ */
247
+ renderFallback(context, result) {
248
+ if (!context.container) {
249
+ logger.warn('[WuErrorBoundary] No container to render fallback');
250
+ return;
251
+ }
252
+
253
+ const container = typeof context.container === 'string'
254
+ ? document.querySelector(context.container)
255
+ : context.container;
256
+
257
+ if (!container) return;
258
+
259
+ // Limpiar container
260
+ container.innerHTML = '';
261
+
262
+ // Crear UI de error
263
+ const errorUI = document.createElement('div');
264
+ errorUI.className = 'wu-error-boundary';
265
+
266
+ Object.assign(errorUI.style, {
267
+ padding: '2rem',
268
+ borderRadius: '8px',
269
+ background: '#fff3cd',
270
+ border: '1px solid #ffc107',
271
+ color: '#856404',
272
+ fontFamily: 'system-ui, -apple-system, sans-serif',
273
+ textAlign: 'center'
274
+ });
275
+
276
+ const icon = document.createElement('div');
277
+ icon.textContent = '⚠️';
278
+ icon.style.fontSize = '3rem';
279
+ icon.style.marginBottom = '1rem';
280
+
281
+ const title = document.createElement('h3');
282
+ title.textContent = 'Application Error';
283
+ title.style.margin = '0 0 0.5rem 0';
284
+
285
+ const message = document.createElement('p');
286
+ message.textContent = result.message || 'An error occurred';
287
+ message.style.margin = '0 0 1rem 0';
288
+
289
+ const button = document.createElement('button');
290
+ button.textContent = '🔄 Reload';
291
+ Object.assign(button.style, {
292
+ padding: '0.5rem 1rem',
293
+ background: '#ffc107',
294
+ border: 'none',
295
+ borderRadius: '4px',
296
+ cursor: 'pointer',
297
+ fontWeight: 'bold',
298
+ color: '#000'
299
+ });
300
+
301
+ button.addEventListener('click', () => window.location.reload());
302
+
303
+ errorUI.appendChild(icon);
304
+ errorUI.appendChild(title);
305
+ errorUI.appendChild(message);
306
+ errorUI.appendChild(button);
307
+
308
+ container.appendChild(errorUI);
309
+ }
310
+
311
+ /**
312
+ * 📝 LOG ERROR: Registrar error
313
+ * @param {Error} error - Error
314
+ * @param {Object} context - Contexto
315
+ */
316
+ logError(error, context) {
317
+ const errorEntry = {
318
+ error: {
319
+ name: error.name,
320
+ message: error.message,
321
+ stack: error.stack
322
+ },
323
+ context,
324
+ timestamp: Date.now()
325
+ };
326
+
327
+ this.errorLog.push(errorEntry);
328
+
329
+ // Mantener límite de log
330
+ if (this.errorLog.length > this.maxErrorLog) {
331
+ this.errorLog.shift();
332
+ }
333
+ }
334
+
335
+ /**
336
+ * 📋 GET ERROR LOG: Obtener log de errores
337
+ * @param {number} limit - Límite de errores a retornar
338
+ * @returns {Array}
339
+ */
340
+ getErrorLog(limit = 10) {
341
+ return this.errorLog.slice(-limit);
342
+ }
343
+
344
+ /**
345
+ * 📊 GET STATS: Estadísticas de errores
346
+ * @returns {Object}
347
+ */
348
+ getStats() {
349
+ const errorsByType = {};
350
+
351
+ this.errorLog.forEach(entry => {
352
+ const type = entry.error.name || 'Unknown';
353
+ errorsByType[type] = (errorsByType[type] || 0) + 1;
354
+ });
355
+
356
+ return {
357
+ totalErrors: this.errorLog.length,
358
+ handlers: this.handlers.length,
359
+ errorsByType,
360
+ recentErrors: this.getErrorLog(5)
361
+ };
362
+ }
363
+
364
+ /**
365
+ * ⚙️ CONFIGURE: Configurar error boundary
366
+ * @param {Object} config - Nueva configuración
367
+ */
368
+ configure(config) {
369
+ this.config = {
370
+ ...this.config,
371
+ ...config
372
+ };
373
+ }
374
+
375
+ /**
376
+ * 🧹 CLEANUP: Limpiar error boundary
377
+ */
378
+ cleanup() {
379
+ this.errorLog = [];
380
+ logger.debug('[WuErrorBoundary] 🧹 Error boundary cleaned up');
381
+ }
382
+ }