versacompiler 2.1.0 → 2.2.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.
Files changed (41) hide show
  1. package/README.md +1 -1
  2. package/dist/compiler/compile.js +2520 -25
  3. package/dist/compiler/error-reporter.js +467 -38
  4. package/dist/compiler/linter.js +72 -1
  5. package/dist/compiler/minify.js +272 -1
  6. package/dist/compiler/minifyTemplate.js +230 -1
  7. package/dist/compiler/module-resolution-optimizer.js +844 -1
  8. package/dist/compiler/parser.js +336 -1
  9. package/dist/compiler/performance-monitor.js +204 -56
  10. package/dist/compiler/tailwindcss.js +39 -1
  11. package/dist/compiler/transform-optimizer.js +392 -1
  12. package/dist/compiler/transformTStoJS.js +16 -1
  13. package/dist/compiler/transforms.js +554 -1
  14. package/dist/compiler/typescript-compiler.js +172 -2
  15. package/dist/compiler/typescript-error-parser.js +281 -10
  16. package/dist/compiler/typescript-manager.js +304 -2
  17. package/dist/compiler/typescript-sync-validator.js +295 -31
  18. package/dist/compiler/typescript-worker-pool.js +936 -1
  19. package/dist/compiler/typescript-worker-thread.cjs +466 -22
  20. package/dist/compiler/typescript-worker.js +339 -1
  21. package/dist/compiler/vuejs.js +396 -37
  22. package/dist/hrm/VueHRM.js +359 -1
  23. package/dist/hrm/errorScreen.js +83 -1
  24. package/dist/hrm/getInstanciaVue.js +313 -1
  25. package/dist/hrm/initHRM.js +586 -1
  26. package/dist/main.js +353 -7
  27. package/dist/servicios/browserSync.js +589 -2
  28. package/dist/servicios/file-watcher.js +425 -4
  29. package/dist/servicios/logger.js +63 -3
  30. package/dist/servicios/readConfig.js +399 -53
  31. package/dist/utils/excluded-modules.js +37 -1
  32. package/dist/utils/module-resolver.js +466 -1
  33. package/dist/utils/promptUser.js +48 -2
  34. package/dist/utils/proxyValidator.js +68 -1
  35. package/dist/utils/resolve-bin.js +58 -1
  36. package/dist/utils/utils.js +21 -1
  37. package/dist/utils/vue-types-setup.js +435 -241
  38. package/dist/wrappers/eslint-node.js +1 -1
  39. package/dist/wrappers/oxlint-node.js +122 -1
  40. package/dist/wrappers/tailwind-node.js +94 -1
  41. package/package.json +106 -103
@@ -1 +1,586 @@
1
- import{hideErrorOverlay as e,showErrorOverlay as t}from"./errorScreen.js";import{obtenerInstanciaVue as n}from"./getInstanciaVue.js";import{reloadComponent as r}from"./VueHRM.js";async function i(a=0){let o=Math.min(2e3*(a+1),1e4);if(window.___browserSync___&&window.___browserSync___.socket){let s=window.___browserSync___.socket,c=s.connected;s.off(`connect`),s.off(`disconnect`),s.off(`reloadFull`),s.off(`HRMVue`),s.off(`HRMHelper`),s.off(`error`),s.on(`connect`,async()=>{c=!0,e(),console.log(`✔️ Versa HMR: Socket conectado`)}),s.on(`disconnect`,()=>{c=!1,console.log(`❌ Versa HMR: Socket desconectado, reintentando...`),setTimeout(()=>{!s.connected&&a<10?i(a+1):s.connected||(console.error(`❌ Versa HMR: Socket no conectado después de 10 reintentos tras desconexión.`),t(`HMR Desconectado`,`No se pudo reconectar a BrowserSync después de múltiples reintentos.`))},o)}),s.on(`reloadFull`,()=>window.location.reload());let l=await n();s.on(`HRMVue`,async t=>{e(),l=window.__VUE_APP__||l,l?(console.log(`🔥 Preparando HMR para Vue...`),await r(l,t)):console.log(`🔄 Usando método fallback:`,l)}),s.on(`HRMHelper`,e=>{window.location.reload(),console.log(`🔄 Recargando página por HMRHelper:`,e)}),s.on(`error`,e=>{console.error(`❌ Versa HMR: Error en el socket:`,e),t(`Error de Socket`,`Se produjo un error en la conexión de BrowserSync.`)}),c||(console.log(`Versa HMR: Objeto socket encontrado, intentando conexión (Intento ${a+1}/10)`),setTimeout(()=>{!s.connected&&a<=10?(console.warn(`Versa HMR: Sin conexión de socket después del tiempo de espera inicial, reintentando initSocket...`),i(a+1)):s.connected||(console.error(`❌ Versa HMR: Socket aún no conectado después de 10 intentos iniciales.`),t(`Falló HMR de BrowserSync`,`No se pudo conectar al socket de BrowserSync después de intentos iniciales.`))},5e3))}else console.warn(`[HMR] Socket de BrowserSync no encontrado o BrowserSync no completamente inicializado. Reintentando initSocket... (${a+1}/10)`),a<10?setTimeout(()=>i(a+1),o):(console.error(`❌ Versa HMR: Socket de BrowserSync no encontrado después de 10 reintentos.`),t(`Falló HMR de BrowserSync`,`Socket o cliente de BrowserSync no encontrado después de múltiples reintentos.`))}i();
1
+ /**
2
+ * @fileoverview Inicialización del sistema Hot Module Replacement (HMR) para VersaCompiler
3
+ * Este archivo maneja la conexión con BrowserSync y configura los listeners para HMR de Vue
4
+ */
5
+
6
+ /**
7
+ * @typedef {Object} ComponentInfo
8
+ * @property {string} normalizedPath - Ruta normalizada del componente
9
+ * @property {string} nameFile - Nombre del archivo del componente
10
+ */
11
+
12
+ import { hideErrorOverlay, showErrorOverlay } from './errorScreen.js';
13
+ import { obtenerInstanciaVue } from './getInstanciaVue.js';
14
+ import { reloadComponent } from './VueHRM.js';
15
+
16
+ /**
17
+ * Analiza el contenido de un módulo para extraer sus imports/exports
18
+ * @param {string} content - Contenido del archivo JavaScript/TypeScript
19
+ * @returns {{imports: string[], exports: string[], hasHMRAccept: boolean}}
20
+ */
21
+ function analyzeModuleContent(content) {
22
+ const imports = [];
23
+ const exports = [];
24
+ let hasHMRAccept = false;
25
+
26
+ // Detectar imports (ESM y CommonJS)
27
+ const importMatches = content.matchAll(
28
+ /import\s+(?:{[^}]*}|[^\s]+|\*\s+as\s+\w+)\s+from\s+['"]([^'"]+)['"]/g,
29
+ );
30
+ const requireMatches = content.matchAll(/require\(['"]([^'"]+)['"]\)/g);
31
+
32
+ for (const match of importMatches) {
33
+ if (match[1]) imports.push(match[1]);
34
+ }
35
+ for (const match of requireMatches) {
36
+ if (match[1]) imports.push(match[1]);
37
+ }
38
+
39
+ // Detectar exports
40
+ const exportMatches = content.matchAll(
41
+ /export\s+(?:default|const|let|var|function|class|{[^}]*})\s+(\w+)/g,
42
+ );
43
+ for (const match of exportMatches) {
44
+ if (match[1]) exports.push(match[1]);
45
+ }
46
+
47
+ // Detectar si el módulo acepta HMR (import.meta.hot.accept)
48
+ hasHMRAccept = /import\.meta\.hot\.accept/.test(content);
49
+
50
+ return { imports, exports, hasHMRAccept };
51
+ }
52
+
53
+ /**
54
+ * Detecta automáticamente la estrategia de HMR para un archivo modificado
55
+ * @param {string} filePath - Ruta del archivo modificado
56
+ * @param {string|null} content - Contenido del archivo (si está disponible)
57
+ * @returns {Promise<{strategy: 'self-accept'|'propagate'|'full-reload', boundary?: string}>}
58
+ */
59
+ async function detectHMRStrategy(filePath, content = null) {
60
+ try {
61
+ // Si no tenemos el contenido, intentar leerlo
62
+ if (!content && typeof fetch !== 'undefined') {
63
+ try {
64
+ const response = await fetch(filePath);
65
+ content = await response.text();
66
+ } catch (error) {
67
+ console.warn(
68
+ '⚠️ No se pudo obtener contenido del módulo:',
69
+ error,
70
+ );
71
+ return { strategy: 'full-reload' };
72
+ }
73
+ }
74
+
75
+ if (!content) {
76
+ return { strategy: 'full-reload' };
77
+ }
78
+
79
+ const analysis = analyzeModuleContent(content);
80
+
81
+ // Estrategia 1: Self-accept (el módulo declara que puede reemplazarse a sí mismo)
82
+ if (analysis.hasHMRAccept) {
83
+ console.log('✨ Módulo con self-accept HMR:', filePath);
84
+ return { strategy: 'self-accept', boundary: filePath };
85
+ }
86
+
87
+ // Estrategia 2: Propagate (buscar el primer importador que acepte HMR)
88
+ // En el browser no tenemos acceso al módulo graph del servidor,
89
+ // así que usamos heurísticas basadas en el contenido
90
+
91
+ // Si solo exporta funciones/constantes simples, probablemente sea seguro recargar
92
+ const hasOnlySimpleExports =
93
+ analysis.exports.length > 0 &&
94
+ !content.includes('class ') &&
95
+ !content.includes('new ');
96
+
97
+ if (hasOnlySimpleExports) {
98
+ console.log(
99
+ '✨ Módulo con exports simples, propagando HMR:',
100
+ filePath,
101
+ );
102
+ return { strategy: 'propagate', boundary: filePath };
103
+ }
104
+
105
+ // Estrategia 3: Full reload (no es seguro hacer HMR)
106
+ console.log('⚠️ Módulo requiere recarga completa:', filePath);
107
+ return { strategy: 'full-reload' };
108
+ } catch (error) {
109
+ console.error('❌ Error detectando estrategia HMR:', error);
110
+ return { strategy: 'full-reload' };
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Envía un error del cliente al servidor para debugging
116
+ * @param {string} type - Tipo de error (hmr, reload, vue, etc)
117
+ * @param {Error|string} error - El error a reportar
118
+ * @param {Object} [context] - Contexto adicional del error
119
+ */
120
+ function reportErrorToServer(type, error, context = {}) {
121
+ try {
122
+ // Verificar que estamos en un navegador
123
+ if (typeof window === 'undefined') {
124
+ return;
125
+ }
126
+
127
+ if (!window.___browserSync___ || !window.___browserSync___.socket) {
128
+ console.warn(
129
+ '⚠️ BrowserSync socket no disponible para reportar error',
130
+ );
131
+ return;
132
+ }
133
+
134
+ const errorData = {
135
+ type,
136
+ timestamp: new Date().toISOString(),
137
+ userAgent: navigator.userAgent,
138
+ url: window.location.href,
139
+ context,
140
+ error: {
141
+ message: error?.message || String(error),
142
+ stack: error?.stack || '',
143
+ name: error?.name || 'Error',
144
+ },
145
+ };
146
+
147
+ // Enviar error al servidor
148
+ window.___browserSync___.socket.emit('client:error', errorData);
149
+
150
+ console.error(`📤 Error reportado al servidor [${type}]:`, errorData);
151
+ } catch (err) {
152
+ console.error('❌ Error al reportar error al servidor:', err);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Maneja el hot reload de librerías sin recarga completa de página
158
+ * @param {Object} data - Datos del evento HRMHelper
159
+ * @param {string} data.libraryName - Nombre de la librería a actualizar
160
+ * @param {string} data.libraryPath - Ruta de la nueva librería
161
+ * @param {string} [data.globalName] - Nombre global de la librería (ej: 'Vue', 'React')
162
+ * @param {Function} [importFn] - Función import para cargar módulos (inyectable para tests)
163
+ * @returns {Promise<boolean>} True si el hot reload fue exitoso
164
+ */
165
+ export async function handleLibraryHotReload(
166
+ data,
167
+ importFn = url => import(url),
168
+ ) {
169
+ const { libraryName, libraryPath, globalName } = data;
170
+
171
+ if (!libraryName || !libraryPath) {
172
+ console.error(
173
+ '❌ HRMHelper: Datos incompletos para hot reload de librería',
174
+ );
175
+ return false;
176
+ }
177
+
178
+ // 2. Determinar el nombre global de la librería
179
+ const targetGlobalName = globalName || libraryName;
180
+
181
+ // 3. Backup de la versión anterior (antes de cargar la nueva)
182
+ let oldLibraryVersion;
183
+
184
+ try {
185
+ console.log(`🔄 Iniciando hot reload de librería: ${libraryName}`);
186
+
187
+ oldLibraryVersion = window[targetGlobalName];
188
+
189
+ // 4. Cargar la nueva versión de la librería
190
+ const timestamp = Date.now();
191
+ const moduleUrl = `${libraryPath}?t=${timestamp}`;
192
+
193
+ console.log(`📦 Cargando nueva versión desde: ${moduleUrl}`);
194
+ const module = await importFn(moduleUrl);
195
+
196
+ if (!module.default && !module[libraryName]) {
197
+ console.error(
198
+ '❌ HRMHelper: La nueva versión no tiene export válido',
199
+ );
200
+ return false;
201
+ }
202
+
203
+ const newLibraryVersion = module.default || module[libraryName];
204
+
205
+ // 3. Reemplazar la librería en el contexto global
206
+ console.log(`🔄 Reemplazando ${targetGlobalName} en contexto global`);
207
+ window[targetGlobalName] = newLibraryVersion;
208
+
209
+ // 4. Limpiar caches si existen
210
+ if (
211
+ typeof newLibraryVersion === 'object' &&
212
+ newLibraryVersion.clearCache
213
+ ) {
214
+ try {
215
+ newLibraryVersion.clearCache();
216
+ } catch (_e) {
217
+ // Ignorar errores de clearCache, no es crítico
218
+ }
219
+ }
220
+
221
+ // 5. Re-inicializar aplicación si es necesario
222
+ if (targetGlobalName === 'Vue' || libraryName.includes('vue')) {
223
+ console.log(
224
+ '🔄 Librería Vue actualizada, se recomienda recarga completa',
225
+ );
226
+ // Para Vue, es más seguro hacer recarga completa
227
+ window.location.reload();
228
+ return true;
229
+ }
230
+
231
+ // 6. Intentar limpiar caches si existen
232
+ try {
233
+ // Limpiar cualquier cache que pueda existir
234
+ if (
235
+ typeof window !== 'undefined' &&
236
+ window.performance &&
237
+ window.performance.clearResourceTimings
238
+ ) {
239
+ window.performance.clearResourceTimings();
240
+ }
241
+ } catch (_e) {
242
+ // Ignorar errores de limpieza de cache
243
+ }
244
+
245
+ console.log(`✅ Hot reload exitoso de librería: ${libraryName}`);
246
+ return true;
247
+ } catch (error) {
248
+ console.error(
249
+ `❌ Error en hot reload de librería ${libraryName}:`,
250
+ error,
251
+ );
252
+
253
+ // Reportar error al servidor
254
+ reportErrorToServer('library-hotreload', error, {
255
+ libraryName,
256
+ libraryPath,
257
+ globalName,
258
+ hadOldVersion: oldLibraryVersion !== undefined,
259
+ });
260
+
261
+ // Intentar rollback si es posible
262
+ if (targetGlobalName && oldLibraryVersion !== undefined) {
263
+ console.log('🔄 Intentando rollback de librería...');
264
+ window[targetGlobalName] = oldLibraryVersion;
265
+ }
266
+
267
+ return false;
268
+ }
269
+ }
270
+
271
+ // Variable para controlar si ya se está inicializando
272
+ let isInitializing = false;
273
+ let initializationTimeout = null;
274
+
275
+ /**
276
+ * Inicializa la conexión socket con BrowserSync y configura los listeners para HMR
277
+ * @param {number} [retries=0] - Número de reintentos realizados
278
+ * @returns {Promise<void>} Promise que se resuelve cuando la conexión está configurada
279
+ */
280
+ async function initSocket(retries = 0) {
281
+ // Evitar inicializaciones concurrentes
282
+ if (isInitializing && retries > 0) {
283
+ console.log(
284
+ '⏳ Versa HMR: Ya hay una inicialización en curso, saltando...',
285
+ );
286
+ return;
287
+ }
288
+
289
+ isInitializing = true;
290
+
291
+ const maxRetries = 10;
292
+ const retryDelay = Math.min(2000 * (retries + 1), 10000); // Backoff exponencial hasta 10s
293
+
294
+ // Verificar si BrowserSync está disponible y tiene socket
295
+ if (window.___browserSync___ && window.___browserSync___.socket) {
296
+ const socket = window.___browserSync___.socket;
297
+ let connected = socket.connected; // Verificar estado inicial de conexión
298
+
299
+ // Limpiar listeners previos para evitar duplicados
300
+ socket.off('connect');
301
+ socket.off('disconnect');
302
+ socket.off('reloadFull');
303
+ socket.off('HRMVue');
304
+ socket.off('HRMHelper');
305
+ socket.off('error');
306
+
307
+ // Limpiar timeout previo si existe
308
+ if (initializationTimeout) {
309
+ clearTimeout(initializationTimeout);
310
+ initializationTimeout = null;
311
+ }
312
+
313
+ // Configurar listener para eventos de conexión
314
+ socket.on('connect', async () => {
315
+ connected = true;
316
+ isInitializing = false;
317
+ hideErrorOverlay();
318
+ console.log('✔️ Versa HMR: Socket conectado');
319
+ });
320
+
321
+ // Configurar listener para eventos de desconexión
322
+ socket.on('disconnect', () => {
323
+ connected = false;
324
+ isInitializing = false;
325
+ console.log('❌ Versa HMR: Socket desconectado, reintentando...');
326
+ // Lógica de reintentos para desconexión
327
+ initializationTimeout = setTimeout(() => {
328
+ if (!socket.connected && retries < maxRetries) {
329
+ initSocket(retries + 1);
330
+ } else if (!socket.connected) {
331
+ console.error(
332
+ `❌ Versa HMR: Socket no conectado después de ${maxRetries} reintentos tras desconexión.`,
333
+ );
334
+ showErrorOverlay(
335
+ 'HMR Desconectado',
336
+ 'No se pudo reconectar a BrowserSync después de múltiples reintentos.',
337
+ );
338
+ }
339
+ }, retryDelay);
340
+ });
341
+
342
+ // Configurar listener para recarga completa
343
+ socket.on('reloadFull', () => window.location.reload()); // Obtener la instancia de Vue con toda la lógica integrada
344
+ let vueInstance = await obtenerInstanciaVue();
345
+
346
+ // Configurar listener para HMR de componentes Vue
347
+ socket.on('HRMVue', async (/** @type {ComponentInfo} */ data) => {
348
+ try {
349
+ hideErrorOverlay();
350
+ vueInstance = window.__VUE_APP__ || vueInstance;
351
+ if (vueInstance) {
352
+ console.log('🔥 Preparando HMR para Vue...');
353
+ await reloadComponent(vueInstance, data);
354
+ } else {
355
+ console.log('🔄 Usando método fallback:', vueInstance);
356
+ }
357
+ } catch (error) {
358
+ console.error('❌ Error en HMR de Vue:', error);
359
+ reportErrorToServer('vue-hmr', error, {
360
+ component: data?.nameFile,
361
+ path: data?.normalizedPath,
362
+ });
363
+ showErrorOverlay(
364
+ 'Error en HMR de Vue',
365
+ error.message || String(error),
366
+ );
367
+ }
368
+ });
369
+
370
+ // Configurar listener para datos auxiliares de HMR
371
+ socket.on('HRMHelper', async data => {
372
+ console.log('🔄 HRMHelper recibido:', data);
373
+ console.log('📋 Archivo modificado:', data.filePath);
374
+
375
+ // Sistema inteligente de detección automática (como Vite/esbuild)
376
+ if (data.filePath && !data.libraryName && !data.libraryPath) {
377
+ console.log('🔍 Analizando estrategia HMR automática...');
378
+
379
+ // Detectar la estrategia de HMR apropiada
380
+ const hmrStrategy = await detectHMRStrategy(
381
+ data.filePath,
382
+ data.content,
383
+ );
384
+
385
+ console.log('📊 Estrategia detectada:', hmrStrategy.strategy);
386
+
387
+ // Ejecutar la estrategia apropiada
388
+ switch (hmrStrategy.strategy) {
389
+ case 'self-accept':
390
+ // El módulo puede reemplazarse a sí mismo
391
+ console.log('✨ Aplicando self-accept HMR');
392
+ try {
393
+ // Reimportar el módulo con cache busting
394
+ const timestamp = Date.now();
395
+ await import(`${data.filePath}?t=${timestamp}`);
396
+ console.log('✅ Módulo recargado exitosamente');
397
+ return;
398
+ } catch (error) {
399
+ console.error(
400
+ '❌ Error en self-accept HMR:',
401
+ error,
402
+ );
403
+ reportErrorToServer(
404
+ 'hmr-self-accept-failed',
405
+ error instanceof Error
406
+ ? error
407
+ : new Error(String(error)),
408
+ data,
409
+ );
410
+ }
411
+ break;
412
+
413
+ case 'propagate':
414
+ // Propagar la actualización a los importadores
415
+ console.log(
416
+ '🔄 Propagando actualización a importadores',
417
+ );
418
+ try {
419
+ // Invalidar el módulo en el cache del navegador
420
+ // y dejar que los importadores se actualicen
421
+ const timestamp = Date.now();
422
+ await import(`${data.filePath}?t=${timestamp}`);
423
+ console.log(
424
+ '✅ Actualización propagada exitosamente',
425
+ );
426
+ return;
427
+ } catch (error) {
428
+ console.error(
429
+ '❌ Error propagando actualización:',
430
+ error,
431
+ );
432
+ reportErrorToServer(
433
+ 'hmr-propagate-failed',
434
+ error instanceof Error
435
+ ? error
436
+ : new Error(String(error)),
437
+ data,
438
+ );
439
+ }
440
+ break;
441
+
442
+ case 'full-reload':
443
+ default:
444
+ // Recarga completa necesaria
445
+ console.log(
446
+ '🔄 Recarga completa requerida - módulo no soporta HMR',
447
+ );
448
+ reportErrorToServer(
449
+ 'hmr-full-reload',
450
+ new Error(
451
+ 'Módulo requiere recarga completa (no self-accept)',
452
+ ),
453
+ {
454
+ ...data,
455
+ strategy: hmrStrategy.strategy,
456
+ hint: 'Considera agregar import.meta.hot.accept() al módulo para HMR sin recarga',
457
+ },
458
+ );
459
+ window.location.reload();
460
+ return;
461
+ }
462
+ }
463
+
464
+ try {
465
+ // Intentar hacer hot reload de librería sin recarga completa
466
+ const success = await handleLibraryHotReload(data);
467
+ if (!success) {
468
+ console.warn(
469
+ '⚠️ Hot reload de librería falló, haciendo recarga completa',
470
+ );
471
+ reportErrorToServer(
472
+ 'hmr-helper-failed',
473
+ new Error('Hot reload returned false'),
474
+ data,
475
+ );
476
+ window.location.reload();
477
+ }
478
+ } catch (error) {
479
+ console.error('❌ Error en HRMHelper:', error);
480
+ reportErrorToServer(
481
+ 'hmr-helper-exception',
482
+ error instanceof Error ? error : new Error(String(error)),
483
+ data,
484
+ );
485
+ window.location.reload();
486
+ }
487
+ });
488
+
489
+ // Configurar listener para errores de socket
490
+ socket.on('error', err => {
491
+ console.error('❌ Versa HMR: Error en el socket:', err);
492
+ showErrorOverlay(
493
+ 'Error de Socket',
494
+ 'Se produjo un error en la conexión de BrowserSync.',
495
+ );
496
+ }); // Watchdog para verificar conexión inicial si el socket existe pero no está conectado
497
+ if (!connected) {
498
+ console.log(
499
+ `Versa HMR: Objeto socket encontrado, intentando conexión (Intento ${
500
+ retries + 1
501
+ }/${maxRetries})`,
502
+ );
503
+ initializationTimeout = setTimeout(() => {
504
+ if (!socket.connected && retries <= maxRetries) {
505
+ console.warn(
506
+ 'Versa HMR: Sin conexión de socket después del tiempo de espera inicial, reintentando initSocket...',
507
+ );
508
+ initSocket(retries + 1);
509
+ } else if (!socket.connected) {
510
+ isInitializing = false;
511
+ console.error(
512
+ `❌ Versa HMR: Socket aún no conectado después de ${maxRetries} intentos iniciales.`,
513
+ );
514
+ showErrorOverlay(
515
+ 'Falló HMR de BrowserSync',
516
+ 'No se pudo conectar al socket de BrowserSync después de intentos iniciales.',
517
+ );
518
+ }
519
+ }, 5000); // Timeout de 5s para el watchdog inicial
520
+ } else {
521
+ isInitializing = false;
522
+ }
523
+ } else {
524
+ // BrowserSync no está disponible, intentar reinicializar
525
+ console.warn(
526
+ `[HMR] Socket de BrowserSync no encontrado o BrowserSync no completamente inicializado. Reintentando initSocket... (${
527
+ retries + 1
528
+ }/${maxRetries})`,
529
+ );
530
+ if (retries < maxRetries) {
531
+ initializationTimeout = setTimeout(
532
+ () => initSocket(retries + 1),
533
+ retryDelay,
534
+ );
535
+ } else {
536
+ isInitializing = false;
537
+ console.error(
538
+ `❌ Versa HMR: Socket de BrowserSync no encontrado después de ${maxRetries} reintentos.`,
539
+ );
540
+ showErrorOverlay(
541
+ 'Falló HMR de BrowserSync',
542
+ 'Socket o cliente de BrowserSync no encontrado después de múltiples reintentos.',
543
+ );
544
+ }
545
+ }
546
+ }
547
+
548
+ // Solo ejecutar en ambiente de navegador (no en tests de Node.js)
549
+ if (
550
+ typeof window !== 'undefined' &&
551
+ typeof window.addEventListener === 'function'
552
+ ) {
553
+ // Capturar errores globales no manejados
554
+ window.addEventListener('error', event => {
555
+ if (
556
+ event.filename &&
557
+ (event.filename.includes('/hrm/') || event.filename.includes('HRM'))
558
+ ) {
559
+ reportErrorToServer(
560
+ 'uncaught-error',
561
+ event.error || new Error(event.message),
562
+ {
563
+ filename: event.filename,
564
+ lineno: event.lineno,
565
+ colno: event.colno,
566
+ },
567
+ );
568
+ }
569
+ });
570
+
571
+ // Capturar promesas rechazadas no manejadas
572
+ window.addEventListener('unhandledrejection', event => {
573
+ const error = event.reason;
574
+ if (
575
+ error &&
576
+ (error.stack?.includes('/hrm/') || error.message?.includes('HRM'))
577
+ ) {
578
+ reportErrorToServer('unhandled-rejection', error, {
579
+ promise: String(event.promise),
580
+ });
581
+ }
582
+ });
583
+
584
+ // Inicializar el sistema HMR al cargar el script
585
+ initSocket();
586
+ }