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,382 +1,396 @@
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
+ // Truncate stack to first 5 lines to prevent retaining large object references
318
+ const stack = error.stack ? error.stack.split('\n').slice(0, 5).join('\n') : '';
319
+
320
+ // Shallow-copy context to avoid retaining references to live objects
321
+ const safeContext = {};
322
+ for (const key of Object.keys(context)) {
323
+ const val = context[key];
324
+ if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean' || val === null) {
325
+ safeContext[key] = val;
326
+ } else {
327
+ safeContext[key] = String(val);
328
+ }
329
+ }
330
+
331
+ const errorEntry = {
332
+ error: {
333
+ name: error.name,
334
+ message: error.message,
335
+ stack
336
+ },
337
+ context: safeContext,
338
+ timestamp: Date.now()
339
+ };
340
+
341
+ this.errorLog.push(errorEntry);
342
+
343
+ // Maintain log limit
344
+ if (this.errorLog.length > this.maxErrorLog) {
345
+ this.errorLog.shift();
346
+ }
347
+ }
348
+
349
+ /**
350
+ * 📋 GET ERROR LOG: Obtener log de errores
351
+ * @param {number} limit - Límite de errores a retornar
352
+ * @returns {Array}
353
+ */
354
+ getErrorLog(limit = 10) {
355
+ return this.errorLog.slice(-limit);
356
+ }
357
+
358
+ /**
359
+ * 📊 GET STATS: Estadísticas de errores
360
+ * @returns {Object}
361
+ */
362
+ getStats() {
363
+ const errorsByType = {};
364
+
365
+ this.errorLog.forEach(entry => {
366
+ const type = entry.error.name || 'Unknown';
367
+ errorsByType[type] = (errorsByType[type] || 0) + 1;
368
+ });
369
+
370
+ return {
371
+ totalErrors: this.errorLog.length,
372
+ handlers: this.handlers.length,
373
+ errorsByType,
374
+ recentErrors: this.getErrorLog(5)
375
+ };
376
+ }
377
+
378
+ /**
379
+ * ⚙️ CONFIGURE: Configurar error boundary
380
+ * @param {Object} config - Nueva configuración
381
+ */
382
+ configure(config) {
383
+ this.config = {
384
+ ...this.config,
385
+ ...config
386
+ };
387
+ }
388
+
389
+ /**
390
+ * 🧹 CLEANUP: Limpiar error boundary
391
+ */
392
+ cleanup() {
393
+ this.errorLog = [];
394
+ logger.debug('[WuErrorBoundary] 🧹 Error boundary cleaned up');
395
+ }
396
+ }