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,936 @@
1
- import*as e from"node:os";import*as t from"node:path";import*as n from"node:process";import{Worker as r}from"node:worker_threads";import{validateTypesWithLanguageService as i}from"./typescript-sync-validator.js";export class TypeScriptWorkerPool{static instance;workers=[];poolSize;workerPath;initPromise=null;isInitialized=!1;TASK_TIMEOUT=8e3;WORKER_INIT_TIMEOUT=3e3;MAX_TASKS_PER_WORKER=200;WORKER_MEMORY_CHECK_INTERVAL=500;memoryCheckInterval=null;cleanupInterval=null;totalTasks=0;completedTasks=0;failedTasks=0;constructor(){let r=e.cpus().length;this.poolSize=Math.min(Math.max(r,4),16),this.workerPath=t.join(n.env.PATH_PROY||t.join(n.cwd(),`src`),`compiler`,`typescript-worker-thread.cjs`)}startMemoryMonitoring(){this.stopMemoryMonitoring(),this.memoryCheckInterval=setInterval(()=>{this.checkWorkersMemory()},15e3),this.cleanupInterval=setInterval(()=>{this.cleanupInactiveWorkers()},12e4)}stopMemoryMonitoring(){this.memoryCheckInterval&&(clearInterval(this.memoryCheckInterval),this.memoryCheckInterval=null),this.cleanupInterval&&(clearInterval(this.cleanupInterval),this.cleanupInterval=null)}async checkWorkersMemory(){let e=Date.now();for(let t of this.workers){t.lastMemoryCheck=e;try{if(t.memoryUsage=(await this.getWorkerMemoryUsage(t)).heapUsed,this.shouldRecycleWorker(t)){let e=this.getRecycleReason(t);console.warn(`[WorkerPool] Worker ${t.id} requiere reciclaje: ${e}`),await this.recycleWorker(t)}}catch(e){console.warn(`[WorkerPool] Error verificando memoria del worker ${t.id}:`,e)}}}async getWorkerMemoryUsage(e){return new Promise(t=>{let n=setTimeout(()=>{t({heapUsed:e.tasksProcessed*2048,heapTotal:e.tasksProcessed*3072,rss:e.tasksProcessed*4096})},1e3),r=`memory-${e.id}-${Date.now()}`,i=a=>{a.id===r&&a.type===`memory-usage`&&(clearTimeout(n),e.worker.off(`message`,i),t({heapUsed:a.memoryUsage?.heapUsed||e.tasksProcessed*2048,heapTotal:a.memoryUsage?.heapTotal||e.tasksProcessed*3072,rss:a.memoryUsage?.rss||e.tasksProcessed*4096}))};e.worker.on(`message`,i),e.worker.postMessage({type:`get-memory-usage`,id:r})})}getRecycleReason(e){let t=Date.now(),n=100*1024*1024,r=1800*1e3,i=this.MAX_TASKS_PER_WORKER,a=[];return e.memoryUsage>n&&a.push(`memoria excede ${Math.round(n/1024/1024)}MB (actual: ${Math.round(e.memoryUsage/1024/1024)}MB)`),t-e.creationTime>r&&a.push(`tiempo de vida excede ${Math.round(r/6e4)} minutos`),e.tasksProcessed>=i&&a.push(`tareas procesadas exceden ${i} (actual: ${e.tasksProcessed})`),a.join(`, `)}async cleanupInactiveWorkers(){let e=Date.now();for(let t of this.workers){let n=e-t.lastActivityTime;n>3e5&&!t.busy&&t.pendingTasks.size===0&&(console.info(`[WorkerPool] Worker ${t.id} inactivo por ${Math.round(n/6e4)} minutos, reciclando...`),await this.recycleWorker(t))}}shouldRecycleWorker(e){let t=Date.now(),n=this.MAX_TASKS_PER_WORKER;return e.memoryUsage>104857600||t-e.creationTime>18e5||e.tasksProcessed>=n}static getInstance(){return TypeScriptWorkerPool.instance||(TypeScriptWorkerPool.instance=new TypeScriptWorkerPool),TypeScriptWorkerPool.instance}setMode(t){switch(t){case`batch`:this.poolSize=Math.min(e.cpus().length,20);break;case`watch`:this.poolSize=Math.min(e.cpus().length,12);break;case`individual`:this.poolSize=Math.min(8,e.cpus().length);break}}async initializePool(){return this.initPromise||(this.initPromise=this._performPoolInitialization()),this.initPromise}async _performPoolInitialization(){try{if(!(await import(`node:fs`)).existsSync(this.workerPath))throw Error(`Worker thread file not found: ${this.workerPath}`);let e=Array.from({length:this.poolSize},(e,t)=>this.createWorker(t));this.workers=await Promise.all(e),this.isInitialized=!0}catch(e){throw this.isInitialized=!1,e}}async createWorker(e){return new Promise((t,i)=>{try{let a=new r(this.workerPath,{env:{...n.env,NODE_OPTIONS:``,WORKER_ID:e.toString()}}),o={worker:a,id:e,busy:!1,pendingTasks:new Map,taskCounter:0,memoryUsage:0,lastMemoryCheck:Date.now(),tasksProcessed:0,creationTime:Date.now(),lastActivityTime:Date.now()};this.setupWorkerListeners(o);let s=setTimeout(()=>{i(Error(`Worker ${e} initialization timeout`))},this.WORKER_INIT_TIMEOUT);a.on(`message`,function e(n){(n.id===`worker-ready`||n.message===`pong`)&&(clearTimeout(s),a.off(`message`,e),t(o))}),a.on(`error`,e=>{clearTimeout(s),i(e)}),a.postMessage({type:`ping`})}catch(e){i(e)}})}setupWorkerListeners(e){let{worker:t}=e;t.on(`message`,t=>{try{if(t.id===`worker-ready`||t.message===`pong`||t.type===`memory-usage`)return;let n=e.pendingTasks.get(t.id);if(!n)return;if(clearTimeout(n.timeout),e.pendingTasks.delete(t.id),e.pendingTasks.size===0&&(e.busy=!1),e.lastActivityTime=Date.now(),t.success&&t.diagnostics!==void 0&&t.hasErrors!==void 0){this.completedTasks++;let e=this.categorizeTypeScriptErrors({diagnostics:t.diagnostics,hasErrors:t.hasErrors},n.fileName);n.resolve(e)}else{this.failedTasks++;let e=t.error||`Error desconocido del worker`,r=this.createCategorizedError(e,n.fileName,t);n.reject(r)}}catch{}}),t.on(`error`,async t=>{await this.handleWorkerError(e,t)}),t.on(`exit`,async t=>{await this.handleWorkerExit(e,t)})}async handleWorkerError(e,t){console.warn(`[WorkerPool] Manejando error del worker ${e.id}:`,t.message),e.pendingTasks.forEach(n=>{clearTimeout(n.timeout),this.failedTasks++,n.reject(Error(`Worker ${e.id} failed: ${t.message}`))}),e.pendingTasks.clear();try{e.worker.removeAllListeners(),await e.worker.terminate()}catch(t){console.error(`[WorkerPool] Error terminando worker ${e.id}:`,t)}if(e.busy=!1,this.isInitialized&&this.workers.length>0)try{let t=await this.createWorker(e.id),n=this.workers.findIndex(t=>t.id===e.id);n!==-1&&(this.workers[n]=t)}catch(t){console.error(`[WorkerPool] No se pudo recrear worker ${e.id}:`,t)}}async handleWorkerExit(e,t){console.warn(`[WorkerPool] Worker ${e.id} salió con código ${t}`),e.pendingTasks.forEach(n=>{clearTimeout(n.timeout),this.failedTasks++,n.reject(Error(`Worker ${e.id} exited with code ${t}`))}),e.pendingTasks.clear(),e.busy=!1;try{e.worker.removeAllListeners()}catch{}if(this.isInitialized&&this.workers.length>0)try{await this.recreateWorker(e)}catch(t){console.error(`[WorkerPool] Error recreando worker ${e.id}:`,t)}}async recreateWorker(e){try{let t=await this.createWorker(e.id),n=this.workers.findIndex(t=>t.id===e.id);n!==-1&&(this.workers[n]=t)}catch{}}async recycleWorker(e){try{console.log(`[WorkerPool] Reciclando worker ${e.id} después de ${e.taskCounter} tareas`);let t=Date.now();await new Promise(n=>{let r=()=>{e.pendingTasks.size===0||Date.now()-t>=2e3?n():setTimeout(r,100)};r()});let n=Array.from(e.pendingTasks.entries());for(let[e,t]of n)clearTimeout(t.timeout),t.reject(Error(`Worker being recycled`));e.pendingTasks.clear();let r=e.worker;r.removeAllListeners(`message`),r.removeAllListeners(`error`),r.removeAllListeners(`exit`),r.removeAllListeners(`online`),r.removeAllListeners(`messageerror`);let i=e.worker;e.worker=null;let a=i.terminate(),o=new Promise((e,t)=>setTimeout(()=>t(Error(`Terminate timeout`)),3e3));await Promise.race([a,o]).catch(t=>{console.warn(`[WorkerPool] Warning al terminar worker ${e.id}:`,t)});let s=await this.createWorker(e.id),c=this.workers.findIndex(t=>t.id===e.id);c!==-1&&(this.workers[c]=s)}catch(t){console.error(`[WorkerPool] Error reciclando worker ${e.id}:`,t)}}findAvailableWorker(){let e=this.workers.find(e=>!e.busy&&e.pendingTasks.size===0);if(e)return e;let t=this.workers.reduce((e,t)=>t.pendingTasks.size<e.pendingTasks.size?t:e);return t.pendingTasks.size<5?t:null}async typeCheck(e,t,n){if(await this.initializePool(),!this.isInitialized)return this.typeCheckWithSyncFallback(e,t,n);let r=this.findAvailableWorker();if(!r)return this.typeCheckWithSyncFallback(e,t,n);try{return this.totalTasks++,await this.typeCheckWithWorker(r,e,t,n)}catch{return this.typeCheckWithSyncFallback(e,t,n)}}async typeCheckWithWorker(e,t,n,r){return e.taskCounter>=this.MAX_TASKS_PER_WORKER&&await this.recycleWorker(e),new Promise((i,a)=>{let o=`task-${e.id}-${++e.taskCounter}-${Date.now()}`;e.busy=!0,e.lastActivityTime=Date.now(),e.tasksProcessed++;let s=this.calculateDynamicTimeout(t,n,r),c=setTimeout(()=>{e.pendingTasks.delete(o),e.pendingTasks.size===0&&(e.busy=!1),a(Error(`Timeout (${s}ms) en type checking para ${t} - archivo complejo detectado`))},s);e.pendingTasks.set(o,{resolve:i,reject:a,timeout:c,fileName:t,startTime:Date.now()});try{let i={id:o,fileName:t,content:n,compilerOptions:{...r,_workerTimeout:s-1e3}};e.worker.postMessage(i)}catch(t){clearTimeout(c),e.pendingTasks.delete(o),e.pendingTasks.size===0&&(e.busy=!1),a(t)}})}calculateDynamicTimeout(e,t,n){let r=this.TASK_TIMEOUT,i=1,a=t.length;a>1e5?i+=1.5:a>5e4?i+=1:a>2e4&&(i+=.5);let o=(t.match(/^import\s+/gm)||[]).length,s=(t.match(/\btype\s+\w+/g)||[]).length,c=(t.match(/\binterface\s+\w+/g)||[]).length,l=(t.match(/<[^>]*>/g)||[]).length,u=o+s+c+l*.5;u>100?i+=2:u>50?i+=1:u>20&&(i+=.5),(n?.strict||n?.noImplicitAny)&&(i+=.3),e.includes(`.d.ts`)?i+=1:e.includes(`.vue`)&&(i+=.5),i=Math.min(i,5),i=Math.max(i,.5);let d=Math.round(r*i);return Math.min(d,6e4)}typeCheckWithSyncFallback(e,t,n){try{return i(e,t,n)}catch{return{diagnostics:[],hasErrors:!1}}}async terminate(){console.log(`[WorkerPool] Cerrando pool de workers...`),this.stopMemoryMonitoring();let e=0;for(let t of this.workers){e+=t.pendingTasks.size,t.pendingTasks.forEach(e=>{clearTimeout(e.timeout),e.reject(Error(`Worker pool cerrado`))}),t.pendingTasks.clear();try{t.worker.removeAllListeners()}catch{}}e>0&&console.log(`[WorkerPool] Se cancelaron ${e} tareas pendientes`);let t=this.workers.map(async e=>{try{await e.worker.terminate()}catch(t){console.warn(`[WorkerPool] Error terminando worker ${e.id}:`,t)}});await Promise.allSettled(t),this.workers=[],this.isInitialized=!1,this.initPromise=null,console.log(`[WorkerPool] Pool cerrado. Estadísticas finales: ${this.completedTasks} completadas, ${this.failedTasks} fallidas`)}getStats(){let e=this.workers.filter(e=>e.busy).length,t=this.workers.reduce((e,t)=>e+t.pendingTasks.size,0),n=this.totalTasks>0?Math.round(this.completedTasks/this.totalTasks*100):0;return{poolSize:this.workers.length,busyWorkers:e,totalPendingTasks:t,totalTasks:this.totalTasks,completedTasks:this.completedTasks,failedTasks:this.failedTasks,successRate:n}}categorizeTypeScriptErrors(e,t){if(!e.hasErrors||!e.diagnostics?.length)return e;let n=e.diagnostics.map(e=>({...e,_category:this.getErrorCategory(e),_severity:this.getErrorSeverity(e),_fileName:t,_timestamp:Date.now()}));return{...e,diagnostics:n,_errorStats:this.calculateErrorStats(n)}}getErrorCategory(e){let t=e.code;return[2304,2339,2346].includes(t)?`MISSING_DECLARATION`:[2322,2322,2345].includes(t)?`TYPE_MISMATCH`:[2307,2317].includes(t)?`MODULE_RESOLUTION`:[2552,2551].includes(t)?`PROPERTY_ACCESS`:t>=1e3&&t<2e3?`SYNTAX_ERROR`:t>=2e3&&t<3e3?`SEMANTIC_ERROR`:t>=4e3?`CONFIG_ERROR`:`OTHER`}getErrorSeverity(e){switch(e.category){case 1:return`ERROR`;case 2:return`WARNING`;case 3:return`INFO`;default:return`ERROR`}}calculateErrorStats(e){let t={totalErrors:e.length,errorsByCategory:{},errorsBySeverity:{},mostCommonError:null};e.forEach(e=>{let n=e._category||`OTHER`,r=e._severity||`ERROR`;t.errorsByCategory[n]=(t.errorsByCategory[n]||0)+1,t.errorsBySeverity[r]=(t.errorsBySeverity[r]||0)+1});let n={};e.forEach(e=>{let t=String(e.code);n[t]=(n[t]||0)+1});let r=Object.keys(n);if(r.length>0){let i=r.reduce((e,t)=>(n[e]||0)>(n[t]||0)?e:t);t.mostCommonError={code:i,count:n[i],message:e.find(e=>String(e.code)===i)?.messageText}}return t}createCategorizedError(e,t,n){let r=Error(e);return r.fileName=t,r.timestamp=Date.now(),r.workerResponse=n,r.category=this.categorizeGenericError(e),r.isRecoverable=this.isRecoverableError(e),r}categorizeGenericError(e){return e.includes(`timeout`)||e.includes(`Timeout`)?`TIMEOUT`:e.includes(`memory`)||e.includes(`Memory`)?`MEMORY`:e.includes(`Worker`)&&e.includes(`exited`)?`WORKER_CRASH`:e.includes(`initialization`)||e.includes(`init`)?`INITIALIZATION`:`UNKNOWN`}isRecoverableError(e){return[`timeout`,`Worker being recycled`,`Worker pool cerrado`,`temporary`].some(t=>e.toLowerCase().includes(t.toLowerCase()))}}
1
+ /**
2
+ * TypeScript Worker Pool - Pool de workers para compilación paralela
3
+ * Reemplaza el worker único con múltiples workers para aprovecha la concurrencia real
4
+ */
5
+ import * as os from 'node:os';
6
+ import * as path from 'node:path';
7
+ import * as process from 'node:process';
8
+ import { setImmediate } from 'node:timers';
9
+ import { Worker } from 'node:worker_threads';
10
+ import { validateTypesWithLanguageService } from './typescript-sync-validator.js';
11
+ /**
12
+ * Pool de workers para compilación TypeScript paralela
13
+ * Distribuye las tareas entre múltiples workers para mayor rendimiento
14
+ */
15
+ export class TypeScriptWorkerPool {
16
+ static instance;
17
+ workers = [];
18
+ poolSize;
19
+ workerPath;
20
+ initPromise = null;
21
+ isInitialized = false; // Configuración optimizada con reciclaje de workers
22
+ TASK_TIMEOUT = 8000; // 8 segundos por tarea (reducido para mayor velocidad)
23
+ WORKER_INIT_TIMEOUT = 3000; // 3 segundos para inicializar (reducido)
24
+ MAX_TASKS_PER_WORKER = 200; // ✨ OPTIMIZADO: Aumentado de 50 a 200 para reducir overhead de reciclaje
25
+ WORKER_MEMORY_CHECK_INTERVAL = 500; // ✨ OPTIMIZADO: Verificar cada 500 tareas (reducir overhead)
26
+ // ✨ FIX #1: Referencias a timers para limpieza adecuada
27
+ memoryCheckInterval = null;
28
+ cleanupInterval = null;
29
+ // Métricas de rendimiento
30
+ totalTasks = 0;
31
+ completedTasks = 0;
32
+ failedTasks = 0;
33
+ constructor() {
34
+ // Determinar tamaño óptimo del pool - MÁS AGRESIVO para mejor rendimiento
35
+ const cpuCount = os.cpus().length;
36
+ // Usar más workers para aprovechar mejor el CPU
37
+ this.poolSize = Math.min(Math.max(cpuCount, 4), 16); // Entre 4 y 16 workers
38
+ this.workerPath = path.join(process.env.PATH_PROY || path.join(process.cwd(), 'src'), 'compiler', 'typescript-worker-thread.cjs');
39
+ // ✨ ISSUE #4: Configurar monitoreo de memoria automático this.startMemoryMonitoring();
40
+ }
41
+ // ✨ ISSUE #4: Métodos de control de memoria y timeouts
42
+ /**
43
+ * Inicia el monitoreo automático de memoria de workers
44
+ * ✨ FIX #1: Ahora almacena referencias a los intervalos para limpieza posterior
45
+ */
46
+ startMemoryMonitoring() {
47
+ // Limpiar intervalos previos si existen
48
+ this.stopMemoryMonitoring();
49
+ // Monitoreo cada 15 segundos (más frecuente)
50
+ this.memoryCheckInterval = setInterval(() => {
51
+ this.checkWorkersMemory();
52
+ }, 15000);
53
+ // Limpieza de workers inactivos cada 2 minutos (más frecuente)
54
+ this.cleanupInterval = setInterval(() => {
55
+ this.cleanupInactiveWorkers();
56
+ }, 120000);
57
+ }
58
+ /**
59
+ * ✨ FIX #1: Detiene el monitoreo automático de memoria
60
+ */
61
+ stopMemoryMonitoring() {
62
+ if (this.memoryCheckInterval) {
63
+ clearInterval(this.memoryCheckInterval);
64
+ this.memoryCheckInterval = null;
65
+ }
66
+ if (this.cleanupInterval) {
67
+ clearInterval(this.cleanupInterval);
68
+ this.cleanupInterval = null;
69
+ }
70
+ }
71
+ /**
72
+ * Verifica el uso de memoria de todos los workers con medición real
73
+ */
74
+ async checkWorkersMemory() {
75
+ const now = Date.now();
76
+ for (const poolWorker of this.workers) {
77
+ // Actualizar tiempo de última verificación
78
+ poolWorker.lastMemoryCheck = now;
79
+ try {
80
+ // ✨ ISSUE #4: Obtener memoria real del worker
81
+ const memoryInfo = await this.getWorkerMemoryUsage(poolWorker);
82
+ poolWorker.memoryUsage = memoryInfo.heapUsed;
83
+ // Verificar límites de memoria y reciclaje automático
84
+ if (this.shouldRecycleWorker(poolWorker)) {
85
+ const reason = this.getRecycleReason(poolWorker);
86
+ console.warn(`[WorkerPool] Worker ${poolWorker.id} requiere reciclaje: ${reason}`);
87
+ await this.recycleWorker(poolWorker);
88
+ }
89
+ }
90
+ catch (error) {
91
+ console.warn(`[WorkerPool] Error verificando memoria del worker ${poolWorker.id}:`, error);
92
+ }
93
+ }
94
+ }
95
+ /**
96
+ * Obtiene el uso real de memoria de un worker
97
+ */
98
+ async getWorkerMemoryUsage(poolWorker) {
99
+ return new Promise(resolve => {
100
+ const timeout = setTimeout(() => {
101
+ // Fallback con estimación si no hay respuesta
102
+ resolve({
103
+ heapUsed: poolWorker.tasksProcessed * 2048, // 2KB por tarea
104
+ heapTotal: poolWorker.tasksProcessed * 3072, // 3KB total estimado
105
+ rss: poolWorker.tasksProcessed * 4096, // 4KB RSS estimado
106
+ });
107
+ }, 1000);
108
+ // Solicitar memoria real del worker
109
+ const memoryRequestId = `memory-${poolWorker.id}-${Date.now()}`;
110
+ const handler = (response) => {
111
+ if (response.id === memoryRequestId &&
112
+ response.type === 'memory-usage') {
113
+ clearTimeout(timeout);
114
+ poolWorker.worker.off('message', handler);
115
+ resolve({
116
+ heapUsed: response.memoryUsage?.heapUsed ||
117
+ poolWorker.tasksProcessed * 2048,
118
+ heapTotal: response.memoryUsage?.heapTotal ||
119
+ poolWorker.tasksProcessed * 3072,
120
+ rss: response.memoryUsage?.rss ||
121
+ poolWorker.tasksProcessed * 4096,
122
+ });
123
+ }
124
+ };
125
+ poolWorker.worker.on('message', handler);
126
+ poolWorker.worker.postMessage({
127
+ type: 'get-memory-usage',
128
+ id: memoryRequestId,
129
+ });
130
+ });
131
+ }
132
+ /**
133
+ * Obtiene la razón por la cual un worker debe ser reciclado
134
+ * ✨ OPTIMIZACIÓN #11: Límites aumentados para reducir overhead de reciclaje
135
+ */
136
+ getRecycleReason(poolWorker) {
137
+ const now = Date.now();
138
+ const MEMORY_LIMIT = 100 * 1024 * 1024; // ✨ 100MB (aumentado de 50MB)
139
+ const TIME_LIMIT = 30 * 60 * 1000; // 30 minutos
140
+ const TASK_LIMIT = this.MAX_TASKS_PER_WORKER;
141
+ const reasons = [];
142
+ if (poolWorker.memoryUsage > MEMORY_LIMIT) {
143
+ reasons.push(`memoria excede ${Math.round(MEMORY_LIMIT / 1024 / 1024)}MB (actual: ${Math.round(poolWorker.memoryUsage / 1024 / 1024)}MB)`);
144
+ }
145
+ if (now - poolWorker.creationTime > TIME_LIMIT) {
146
+ reasons.push(`tiempo de vida excede ${Math.round(TIME_LIMIT / 60000)} minutos`);
147
+ }
148
+ if (poolWorker.tasksProcessed >= TASK_LIMIT) {
149
+ reasons.push(`tareas procesadas exceden ${TASK_LIMIT} (actual: ${poolWorker.tasksProcessed})`);
150
+ }
151
+ return reasons.join(', ');
152
+ } /**
153
+ * Limpia workers que han estado inactivos por mucho tiempo
154
+ */
155
+ async cleanupInactiveWorkers() {
156
+ const now = Date.now();
157
+ const INACTIVE_TIMEOUT = 5 * 60 * 1000; // 5 minutos (reducido)
158
+ for (const poolWorker of this.workers) {
159
+ const timeSinceLastActivity = now - poolWorker.lastActivityTime;
160
+ if (timeSinceLastActivity > INACTIVE_TIMEOUT &&
161
+ !poolWorker.busy &&
162
+ poolWorker.pendingTasks.size === 0) {
163
+ console.info(`[WorkerPool] Worker ${poolWorker.id} inactivo por ${Math.round(timeSinceLastActivity / 60000)} minutos, reciclando...`);
164
+ await this.recycleWorker(poolWorker);
165
+ }
166
+ }
167
+ } /**
168
+ * Verifica si un worker debe ser reciclado por límites de memoria/tiempo
169
+ * ✨ OPTIMIZACIÓN #11: Límites aumentados para reducir overhead de reciclaje
170
+ */
171
+ shouldRecycleWorker(poolWorker) {
172
+ const now = Date.now();
173
+ const MEMORY_LIMIT = 100 * 1024 * 1024; // ✨ 100MB (aumentado de 30MB)
174
+ const TIME_LIMIT = 30 * 60 * 1000; // ✨ 30 minutos (aumentado de 15 min)
175
+ const TASK_LIMIT = this.MAX_TASKS_PER_WORKER;
176
+ return (poolWorker.memoryUsage > MEMORY_LIMIT ||
177
+ now - poolWorker.creationTime > TIME_LIMIT ||
178
+ poolWorker.tasksProcessed >= TASK_LIMIT);
179
+ }
180
+ /**
181
+ * Obtiene la instancia singleton del Worker Pool
182
+ */
183
+ static getInstance() {
184
+ if (!TypeScriptWorkerPool.instance) {
185
+ TypeScriptWorkerPool.instance = new TypeScriptWorkerPool();
186
+ }
187
+ return TypeScriptWorkerPool.instance;
188
+ }
189
+ /**
190
+ * Configura el modo de operación del pool - OPTIMIZADO para máxima velocidad
191
+ */
192
+ setMode(mode) {
193
+ // Ajustar configuración según el modo - MÁS AGRESIVO
194
+ switch (mode) {
195
+ case 'batch':
196
+ // Para modo batch, máxima concurrencia para throughput
197
+ this.poolSize = Math.min(os.cpus().length, 20);
198
+ break;
199
+ case 'watch':
200
+ // Para modo watch, más workers para mejor responsividad
201
+ this.poolSize = Math.min(os.cpus().length, 12);
202
+ break;
203
+ case 'individual':
204
+ // Para individual, pool moderado
205
+ this.poolSize = Math.min(8, os.cpus().length);
206
+ break;
207
+ }
208
+ }
209
+ /**
210
+ * Inicializa el pool de workers
211
+ */
212
+ async initializePool() {
213
+ if (this.initPromise) {
214
+ return this.initPromise;
215
+ }
216
+ this.initPromise = this._performPoolInitialization();
217
+ return this.initPromise;
218
+ }
219
+ /**
220
+ * Realiza la inicialización del pool de workers
221
+ */
222
+ async _performPoolInitialization() {
223
+ try {
224
+ // Verificar que el archivo del worker existe
225
+ const fs = await import('node:fs');
226
+ const exists = fs.existsSync(this.workerPath);
227
+ if (!exists) {
228
+ throw new Error(`Worker thread file not found: ${this.workerPath}`);
229
+ }
230
+ // Crear workers en paralelo
231
+ const workerPromises = Array.from({ length: this.poolSize }, (_, index) => this.createWorker(index));
232
+ this.workers = await Promise.all(workerPromises);
233
+ this.isInitialized = true;
234
+ }
235
+ catch (error) {
236
+ this.isInitialized = false;
237
+ throw error;
238
+ }
239
+ }
240
+ /**
241
+ * Crea un worker individual
242
+ */
243
+ async createWorker(workerId) {
244
+ return new Promise((resolve, reject) => {
245
+ try {
246
+ const worker = new Worker(this.workerPath, {
247
+ env: {
248
+ ...process.env,
249
+ NODE_OPTIONS: '',
250
+ WORKER_ID: workerId.toString(),
251
+ },
252
+ });
253
+ const poolWorker = {
254
+ worker,
255
+ id: workerId,
256
+ busy: false,
257
+ pendingTasks: new Map(),
258
+ taskCounter: 0,
259
+ // ✨ ISSUE #4: Inicializar controles de memoria
260
+ memoryUsage: 0,
261
+ lastMemoryCheck: Date.now(),
262
+ tasksProcessed: 0,
263
+ creationTime: Date.now(),
264
+ lastActivityTime: Date.now(),
265
+ };
266
+ // Configurar listeners
267
+ this.setupWorkerListeners(poolWorker);
268
+ // Timeout para inicialización
269
+ const initTimeout = setTimeout(() => {
270
+ reject(new Error(`Worker ${workerId} initialization timeout`));
271
+ }, this.WORKER_INIT_TIMEOUT);
272
+ // Esperar que el worker esté listo
273
+ const checkReady = () => {
274
+ worker.postMessage({ type: 'ping' });
275
+ };
276
+ worker.on('message', function readyHandler(response) {
277
+ if (response.id === 'worker-ready' ||
278
+ response.message === 'pong') {
279
+ clearTimeout(initTimeout);
280
+ worker.off('message', readyHandler);
281
+ resolve(poolWorker);
282
+ }
283
+ });
284
+ worker.on('error', (error) => {
285
+ clearTimeout(initTimeout);
286
+ reject(error);
287
+ });
288
+ // Intentar conectar
289
+ checkReady();
290
+ }
291
+ catch (error) {
292
+ reject(error);
293
+ }
294
+ });
295
+ }
296
+ /**
297
+ * Configura los listeners para un worker individual
298
+ */
299
+ setupWorkerListeners(poolWorker) {
300
+ const { worker } = poolWorker;
301
+ worker.on('message', (response) => {
302
+ try {
303
+ // Ignorar mensajes de control
304
+ if (response.id === 'worker-ready' ||
305
+ response.message === 'pong') {
306
+ return;
307
+ }
308
+ // ✨ ISSUE #4: Manejar reportes de memoria del worker
309
+ if (response.type === 'memory-usage') {
310
+ return; // Ya manejado en getWorkerMemoryUsage
311
+ }
312
+ // Buscar la tarea pendiente
313
+ const pendingTask = poolWorker.pendingTasks.get(response.id);
314
+ if (!pendingTask) {
315
+ return;
316
+ }
317
+ // ✨ FIX MEMORIA: Limpiar timeout INMEDIATAMENTE para liberar referencias
318
+ clearTimeout(pendingTask.timeout);
319
+ // ✨ FIX MEMORIA: Guardar referencias necesarias antes de eliminar la tarea
320
+ const { resolve, reject } = pendingTask;
321
+ // ✨ FIX MEMORIA: Eliminar la tarea del mapa ANTES de procesarla
322
+ poolWorker.pendingTasks.delete(response.id);
323
+ // Marcar worker como disponible si no tiene más tareas
324
+ if (poolWorker.pendingTasks.size === 0) {
325
+ poolWorker.busy = false;
326
+ }
327
+ // Actualizar actividad del worker
328
+ poolWorker.lastActivityTime = Date.now();
329
+ // ✨ ISSUE #4: Manejo mejorado de errores de TypeScript
330
+ if (response.success &&
331
+ response.diagnostics !== undefined &&
332
+ response.hasErrors !== undefined) {
333
+ this.completedTasks++;
334
+ // ✨ FIX MEMORIA: Crear resultado sin referencias circulares
335
+ const result = {
336
+ diagnostics: response.diagnostics || [],
337
+ hasErrors: response.hasErrors,
338
+ };
339
+ // ✨ FIX MEMORIA: No mantener response completo en memoria
340
+ resolve(result);
341
+ // ✨ FIX MEMORIA: Limpiar diagnostics después de resolver
342
+ setImmediate(() => {
343
+ if (response.diagnostics) {
344
+ response.diagnostics.length = 0;
345
+ }
346
+ });
347
+ }
348
+ else {
349
+ this.failedTasks++;
350
+ const errorMessage = response.error || 'Error desconocido del worker';
351
+ // ✨ FIX MEMORIA: Error simple sin referencias pesadas
352
+ const error = new Error(errorMessage);
353
+ reject(error);
354
+ }
355
+ }
356
+ catch {
357
+ // Error silencioso - no imprimir cada error
358
+ }
359
+ });
360
+ worker.on('error', async (error) => {
361
+ await this.handleWorkerError(poolWorker, error);
362
+ });
363
+ worker.on('exit', async (code) => {
364
+ await this.handleWorkerExit(poolWorker, code);
365
+ });
366
+ }
367
+ /**
368
+ * Maneja errores de un worker específico con cleanup completo
369
+ * ✨ FIX MEMORIA: Limpieza agresiva y sin esperas
370
+ */
371
+ async handleWorkerError(poolWorker, error) {
372
+ console.warn(`[WorkerPool] Manejando error del worker ${poolWorker.id}:`, error.message);
373
+ // 1. ✨ FIX MEMORIA: Limpiar INMEDIATAMENTE todas las tareas pendientes
374
+ const pendingTasksArray = Array.from(poolWorker.pendingTasks.entries());
375
+ for (const [taskId, task] of pendingTasksArray) {
376
+ clearTimeout(task.timeout);
377
+ this.failedTasks++;
378
+ task.reject(new Error(`Worker ${poolWorker.id} failed`));
379
+ poolWorker.pendingTasks.delete(taskId);
380
+ }
381
+ poolWorker.pendingTasks.clear();
382
+ // 2. ✨ FIX MEMORIA: Remover listeners antes de terminar
383
+ try {
384
+ poolWorker.worker.removeAllListeners('message');
385
+ poolWorker.worker.removeAllListeners('error');
386
+ poolWorker.worker.removeAllListeners('exit');
387
+ poolWorker.worker.removeAllListeners('online');
388
+ poolWorker.worker.removeAllListeners('messageerror');
389
+ }
390
+ catch {
391
+ // Silencioso
392
+ }
393
+ // 3. ✨ FIX MEMORIA: Terminar sin esperar
394
+ const workerToTerminate = poolWorker.worker;
395
+ poolWorker.worker = null;
396
+ poolWorker.busy = false;
397
+ workerToTerminate.terminate().catch(() => {
398
+ // Silencioso
399
+ });
400
+ // 4. ✨ FIX MEMORIA: Recrear worker asíncronamente sin bloquear
401
+ if (this.isInitialized && this.workers.length > 0) {
402
+ setImmediate(async () => {
403
+ try {
404
+ const newWorker = await this.createWorker(poolWorker.id);
405
+ const index = this.workers.findIndex(w => w.id === poolWorker.id);
406
+ if (index !== -1) {
407
+ this.workers[index] = newWorker;
408
+ }
409
+ }
410
+ catch {
411
+ // Silencioso
412
+ }
413
+ });
414
+ }
415
+ }
416
+ /**
417
+ * Maneja la salida inesperada de un worker con cleanup completo
418
+ */
419
+ async handleWorkerExit(poolWorker, code) {
420
+ console.warn(`[WorkerPool] Worker ${poolWorker.id} salió con código ${code}`);
421
+ // 1. Rechazar tareas pendientes con cleanup
422
+ poolWorker.pendingTasks.forEach(task => {
423
+ clearTimeout(task.timeout);
424
+ this.failedTasks++;
425
+ task.reject(new Error(`Worker ${poolWorker.id} exited with code ${code}`));
426
+ });
427
+ poolWorker.pendingTasks.clear();
428
+ poolWorker.busy = false;
429
+ // 2. Limpiar listeners para evitar memory leaks
430
+ try {
431
+ poolWorker.worker.removeAllListeners();
432
+ }
433
+ catch {
434
+ // Error silencioso en cleanup
435
+ }
436
+ // 3. Recrear worker si es necesario y el pool está activo
437
+ if (this.isInitialized && this.workers.length > 0) {
438
+ try {
439
+ await this.recreateWorker(poolWorker);
440
+ }
441
+ catch (recreateError) {
442
+ console.error(`[WorkerPool] Error recreando worker ${poolWorker.id}:`, recreateError);
443
+ }
444
+ }
445
+ }
446
+ /**
447
+ * Recrea un worker que falló
448
+ */
449
+ async recreateWorker(failedWorker) {
450
+ try {
451
+ const newWorker = await this.createWorker(failedWorker.id);
452
+ // Reemplazar en el array
453
+ const index = this.workers.findIndex(w => w.id === failedWorker.id);
454
+ if (index !== -1) {
455
+ this.workers[index] = newWorker;
456
+ }
457
+ }
458
+ catch {
459
+ // Error silencioso en recreación
460
+ }
461
+ }
462
+ /**
463
+ * Recicla un worker para prevenir memory leaks
464
+ * ✨ FIX MEMORIA: Limpieza agresiva de todas las referencias
465
+ */
466
+ async recycleWorker(poolWorker) {
467
+ try {
468
+ console.log(`[WorkerPool] Reciclando worker ${poolWorker.id} después de ${poolWorker.taskCounter} tareas`);
469
+ // 1. ✨ FIX MEMORIA: No esperar, rechazar inmediatamente todas las tareas pendientes
470
+ const pendingTasksArray = Array.from(poolWorker.pendingTasks.entries());
471
+ for (const [taskId, task] of pendingTasksArray) {
472
+ clearTimeout(task.timeout);
473
+ task.reject(new Error('Worker being recycled'));
474
+ poolWorker.pendingTasks.delete(taskId);
475
+ }
476
+ // ✨ FIX MEMORIA: Forzar limpieza del Map
477
+ poolWorker.pendingTasks.clear();
478
+ // 2. ✨ FIX MEMORIA: Remover listeners explícitamente por tipo
479
+ const worker = poolWorker.worker;
480
+ worker.removeAllListeners('message');
481
+ worker.removeAllListeners('error');
482
+ worker.removeAllListeners('exit');
483
+ worker.removeAllListeners('online');
484
+ worker.removeAllListeners('messageerror');
485
+ // 3. ✨ FIX MEMORIA: Guardar referencia temporal y limpiar poolWorker
486
+ const workerToTerminate = poolWorker.worker;
487
+ const workerId = poolWorker.id;
488
+ // Limpiar todas las propiedades del poolWorker
489
+ poolWorker.worker = null;
490
+ poolWorker.busy = false;
491
+ poolWorker.taskCounter = 0;
492
+ poolWorker.tasksProcessed = 0;
493
+ poolWorker.memoryUsage = 0;
494
+ // 4. ✨ FIX MEMORIA: Terminar con timeout agresivo
495
+ const terminatePromise = workerToTerminate.terminate();
496
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Terminate timeout')), 2000));
497
+ await Promise.race([terminatePromise, timeoutPromise]).catch(() => {
498
+ // Forzar terminación si falla
499
+ try {
500
+ workerToTerminate.terminate();
501
+ }
502
+ catch {
503
+ // Silencioso
504
+ }
505
+ });
506
+ // ✨ FIX MEMORIA: Forzar garbage collection del worker terminado
507
+ setImmediate(() => {
508
+ // Dar tiempo al GC para limpiar
509
+ });
510
+ // 5. ✨ FIX MEMORIA: Crear y reemplazar con nuevo worker
511
+ const newWorker = await this.createWorker(workerId);
512
+ const index = this.workers.findIndex(w => w.id === workerId);
513
+ if (index !== -1) {
514
+ this.workers[index] = newWorker;
515
+ }
516
+ }
517
+ catch (error) {
518
+ console.error(`[WorkerPool] Error reciclando worker ${poolWorker.id}:`, error);
519
+ // ✨ FIX MEMORIA: Asegurar limpieza incluso en error
520
+ poolWorker.pendingTasks.clear();
521
+ poolWorker.busy = false;
522
+ }
523
+ }
524
+ /**
525
+ * Encuentra el worker menos ocupado
526
+ */
527
+ findAvailableWorker() {
528
+ // Buscar worker completamente libre
529
+ const freeWorker = this.workers.find(w => !w.busy && w.pendingTasks.size === 0);
530
+ if (freeWorker) {
531
+ return freeWorker;
532
+ }
533
+ // Si no hay workers libres, buscar el menos ocupado
534
+ const leastBusyWorker = this.workers.reduce((least, current) => {
535
+ if (current.pendingTasks.size < least.pendingTasks.size) {
536
+ return current;
537
+ }
538
+ return least;
539
+ });
540
+ // Solo devolver si no está demasiado ocupado
541
+ if (leastBusyWorker.pendingTasks.size < 5) {
542
+ return leastBusyWorker;
543
+ }
544
+ return null; // Todos los workers están muy ocupados
545
+ }
546
+ /**
547
+ * Realiza type checking usando el pool de workers
548
+ */
549
+ async typeCheck(fileName, content, compilerOptions) {
550
+ // Asegurar que el pool esté inicializado
551
+ await this.initializePool();
552
+ if (!this.isInitialized) {
553
+ return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
554
+ }
555
+ // Buscar worker disponible
556
+ const availableWorker = this.findAvailableWorker();
557
+ // ✨ FIX: Incrementar totalTasks ANTES del try/catch para conteo correcto
558
+ this.totalTasks++;
559
+ if (!availableWorker) {
560
+ return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
561
+ }
562
+ try {
563
+ return await this.typeCheckWithWorker(availableWorker, fileName, content, compilerOptions);
564
+ }
565
+ catch {
566
+ return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
567
+ }
568
+ }
569
+ /**
570
+ * Realiza type checking usando un worker específico con reciclaje automático
571
+ * ✨ FIX MEMORIA: Optimizado para prevenir fugas de memoria
572
+ */
573
+ async typeCheckWithWorker(poolWorker, fileName, content, compilerOptions) {
574
+ // Verificar si el worker necesita reciclaje por número de tareas
575
+ if (poolWorker.taskCounter >= this.MAX_TASKS_PER_WORKER) {
576
+ // ✨ FIX: No esperar el reciclaje, usar fallback
577
+ return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
578
+ }
579
+ return new Promise((resolve, reject) => {
580
+ const taskId = `task-${poolWorker.id}-${++poolWorker.taskCounter}-${Date.now()}`;
581
+ // Marcar worker como ocupado y actualizar actividad
582
+ poolWorker.busy = true;
583
+ poolWorker.lastActivityTime = Date.now();
584
+ poolWorker.tasksProcessed++;
585
+ // ✨ ISSUE #4: Timeout dinámico basado en complejidad del archivo
586
+ const dynamicTimeout = this.calculateDynamicTimeout(fileName, content, compilerOptions);
587
+ // ✨ FIX MEMORIA: Configurar timeout con limpieza explícita
588
+ const timeout = setTimeout(() => {
589
+ const task = poolWorker.pendingTasks.get(taskId);
590
+ if (task) {
591
+ poolWorker.pendingTasks.delete(taskId);
592
+ if (poolWorker.pendingTasks.size === 0) {
593
+ poolWorker.busy = false;
594
+ }
595
+ reject(new Error(`Timeout (${dynamicTimeout}ms) en type checking para ${fileName}`));
596
+ }
597
+ }, dynamicTimeout);
598
+ // ✨ FIX MEMORIA: Wrapper para resolver/rechazar que limpia el timeout
599
+ const wrappedResolve = (result) => {
600
+ clearTimeout(timeout);
601
+ resolve(result);
602
+ };
603
+ const wrappedReject = (error) => {
604
+ clearTimeout(timeout);
605
+ reject(error);
606
+ };
607
+ // ✨ FIX MEMORIA: Agregar tarea con wrappers que auto-limpian
608
+ poolWorker.pendingTasks.set(taskId, {
609
+ resolve: wrappedResolve,
610
+ reject: wrappedReject,
611
+ timeout,
612
+ fileName,
613
+ startTime: Date.now(),
614
+ });
615
+ try {
616
+ // ✨ FIX: Pasar todas las opciones relevantes del tsconfig.json de forma serializable
617
+ // Crear copia limpia con solo valores primitivos (sin funciones, símbolos, etc.)
618
+ const serializableOptions = {};
619
+ for (const key in compilerOptions) {
620
+ const value = compilerOptions[key];
621
+ // Solo pasar valores serializables (primitivos, arrays, objetos simples)
622
+ if (value !== undefined &&
623
+ value !== null &&
624
+ typeof value !== 'function' &&
625
+ typeof value !== 'symbol') {
626
+ serializableOptions[key] = value;
627
+ }
628
+ }
629
+ const message = {
630
+ id: taskId,
631
+ fileName,
632
+ content,
633
+ compilerOptions: serializableOptions,
634
+ };
635
+ // Enviar mensaje al worker
636
+ poolWorker.worker.postMessage(message);
637
+ }
638
+ catch (error) {
639
+ // Limpiar en caso de error
640
+ clearTimeout(timeout);
641
+ poolWorker.pendingTasks.delete(taskId);
642
+ if (poolWorker.pendingTasks.size === 0) {
643
+ poolWorker.busy = false;
644
+ }
645
+ reject(error);
646
+ }
647
+ });
648
+ }
649
+ /**
650
+ * Calcula un timeout dinámico basado en la complejidad del archivo TypeScript
651
+ */
652
+ calculateDynamicTimeout(fileName, content, compilerOptions) {
653
+ const baseTimeout = this.TASK_TIMEOUT; // 10 segundos base
654
+ let complexityMultiplier = 1;
655
+ // Factor 1: Tamaño del archivo
656
+ const contentLength = content.length;
657
+ if (contentLength > 100000) {
658
+ // Archivos > 100KB
659
+ complexityMultiplier += 1.5;
660
+ }
661
+ else if (contentLength > 50000) {
662
+ // Archivos > 50KB
663
+ complexityMultiplier += 1;
664
+ }
665
+ else if (contentLength > 20000) {
666
+ // Archivos > 20KB
667
+ complexityMultiplier += 0.5;
668
+ }
669
+ // Factor 2: Complejidad sintáctica
670
+ const importCount = (content.match(/^import\s+/gm) || []).length;
671
+ const typeCount = (content.match(/\btype\s+\w+/g) || []).length;
672
+ const interfaceCount = (content.match(/\binterface\s+\w+/g) || [])
673
+ .length;
674
+ const genericCount = (content.match(/<[^>]*>/g) || []).length;
675
+ const complexConstructs = importCount + typeCount + interfaceCount + genericCount * 0.5;
676
+ if (complexConstructs > 100) {
677
+ complexityMultiplier += 2;
678
+ }
679
+ else if (complexConstructs > 50) {
680
+ complexityMultiplier += 1;
681
+ }
682
+ else if (complexConstructs > 20) {
683
+ complexityMultiplier += 0.5;
684
+ }
685
+ // Factor 3: Configuración de TypeScript estricta
686
+ if (compilerOptions?.strict || compilerOptions?.noImplicitAny) {
687
+ complexityMultiplier += 0.3;
688
+ }
689
+ // Factor 4: Extensión de archivo compleja (.d.ts, .vue.ts, etc.)
690
+ if (fileName.includes('.d.ts')) {
691
+ complexityMultiplier += 1; // Los archivos de definición son más complejos
692
+ }
693
+ else if (fileName.includes('.vue')) {
694
+ complexityMultiplier += 0.5; // Los archivos Vue requieren procesamiento adicional
695
+ }
696
+ // Aplicar límites razonables
697
+ complexityMultiplier = Math.min(complexityMultiplier, 5); // Máximo 5x el timeout base
698
+ complexityMultiplier = Math.max(complexityMultiplier, 0.5); // Mínimo 0.5x el timeout base
699
+ const finalTimeout = Math.round(baseTimeout * complexityMultiplier);
700
+ return Math.min(finalTimeout, 60000); // Máximo absoluto de 60 segundos
701
+ }
702
+ /**
703
+ * Fallback síncrono para type checking
704
+ * ✨ FIX: Ahora trackea las tareas correctamente
705
+ */
706
+ typeCheckWithSyncFallback(fileName, content, compilerOptions) {
707
+ try {
708
+ const result = validateTypesWithLanguageService(fileName, content, compilerOptions);
709
+ this.completedTasks++; // ✨ FIX: Contabilizar tareas completadas en fallback
710
+ return result;
711
+ }
712
+ catch {
713
+ this.failedTasks++; // ✨ FIX: Contabilizar tareas fallidas en fallback
714
+ return {
715
+ diagnostics: [],
716
+ hasErrors: false,
717
+ };
718
+ }
719
+ }
720
+ /**
721
+ * Cierra todos los workers del pool con cleanup completo
722
+ * ✨ FIX #1: Ahora limpia timers de monitoreo para evitar fugas de memoria
723
+ */
724
+ async terminate() {
725
+ console.log('[WorkerPool] Cerrando pool de workers...');
726
+ // 0. ✨ FIX #1: Detener monitoreo de memoria primero
727
+ this.stopMemoryMonitoring();
728
+ // 1. Rechazar todas las tareas pendientes con cleanup
729
+ let totalPendingTasks = 0;
730
+ for (const poolWorker of this.workers) {
731
+ totalPendingTasks += poolWorker.pendingTasks.size;
732
+ poolWorker.pendingTasks.forEach(task => {
733
+ clearTimeout(task.timeout);
734
+ task.reject(new Error('Worker pool cerrado'));
735
+ });
736
+ poolWorker.pendingTasks.clear();
737
+ // Limpiar listeners para evitar memory leaks
738
+ try {
739
+ poolWorker.worker.removeAllListeners();
740
+ }
741
+ catch {
742
+ // Error silencioso en cleanup
743
+ }
744
+ }
745
+ if (totalPendingTasks > 0) {
746
+ console.log(`[WorkerPool] Se cancelaron ${totalPendingTasks} tareas pendientes`);
747
+ }
748
+ // 2. Cerrar todos los workers con manejo de errores
749
+ const terminatePromises = this.workers.map(async (poolWorker) => {
750
+ try {
751
+ await poolWorker.worker.terminate();
752
+ }
753
+ catch (error) {
754
+ console.warn(`[WorkerPool] Error terminando worker ${poolWorker.id}:`, error);
755
+ }
756
+ });
757
+ await Promise.allSettled(terminatePromises);
758
+ // 3. Limpiar estado
759
+ this.workers = [];
760
+ this.isInitialized = false;
761
+ this.initPromise = null;
762
+ console.log(`[WorkerPool] Pool cerrado. Estadísticas finales: ${this.completedTasks} completadas, ${this.failedTasks} fallidas`);
763
+ }
764
+ /**
765
+ * Obtiene estadísticas del pool
766
+ */
767
+ getStats() {
768
+ const busyWorkers = this.workers.filter(w => w.busy).length;
769
+ const totalPendingTasks = this.workers.reduce((sum, w) => sum + w.pendingTasks.size, 0);
770
+ const successRate = this.totalTasks > 0
771
+ ? Math.round((this.completedTasks / this.totalTasks) * 100)
772
+ : 0;
773
+ return {
774
+ poolSize: this.workers.length,
775
+ busyWorkers,
776
+ totalPendingTasks,
777
+ totalTasks: this.totalTasks,
778
+ completedTasks: this.completedTasks,
779
+ failedTasks: this.failedTasks,
780
+ successRate,
781
+ };
782
+ }
783
+ // ✨ ISSUE #4: Métodos de categorización de errores de TypeScript
784
+ /**
785
+ * Categoriza y mejora los errores de TypeScript para mejor debugging
786
+ */
787
+ categorizeTypeScriptErrors(result, fileName) {
788
+ if (!result.hasErrors || !result.diagnostics?.length) {
789
+ return result;
790
+ }
791
+ const categorizedDiagnostics = result.diagnostics.map(diagnostic => {
792
+ // Añadir metadatos útiles para debugging
793
+ const enhanced = {
794
+ ...diagnostic,
795
+ _category: this.getErrorCategory(diagnostic),
796
+ _severity: this.getErrorSeverity(diagnostic),
797
+ _fileName: fileName,
798
+ _timestamp: Date.now(),
799
+ };
800
+ return enhanced;
801
+ });
802
+ return {
803
+ ...result,
804
+ diagnostics: categorizedDiagnostics,
805
+ // Añadir estadísticas de errores
806
+ _errorStats: this.calculateErrorStats(categorizedDiagnostics),
807
+ };
808
+ } /**
809
+ * Determina la categoría de un error de TypeScript
810
+ */
811
+ getErrorCategory(diagnostic) {
812
+ const code = diagnostic.code;
813
+ // Categorización basada en códigos de error comunes
814
+ if ([2304, 2339, 2346].includes(code)) {
815
+ return 'MISSING_DECLARATION'; // No puede encontrar nombre/propiedad
816
+ }
817
+ else if ([2322, 2322, 2345].includes(code)) {
818
+ return 'TYPE_MISMATCH'; // Error de tipos
819
+ }
820
+ else if ([2307, 2317].includes(code)) {
821
+ return 'MODULE_RESOLUTION'; // Error de resolución de módulos
822
+ }
823
+ else if ([2552, 2551].includes(code)) {
824
+ return 'PROPERTY_ACCESS'; // Error de acceso a propiedades
825
+ }
826
+ else if (code >= 1000 && code < 2000) {
827
+ return 'SYNTAX_ERROR'; // Errores de sintaxis
828
+ }
829
+ else if (code >= 2000 && code < 3000) {
830
+ return 'SEMANTIC_ERROR'; // Errores semánticos
831
+ }
832
+ else if (code >= 4000) {
833
+ return 'CONFIG_ERROR'; // Errores de configuración
834
+ }
835
+ return 'OTHER';
836
+ }
837
+ /**
838
+ * Determina la severidad de un error de TypeScript
839
+ */
840
+ getErrorSeverity(diagnostic) {
841
+ const category = diagnostic.category;
842
+ switch (category) {
843
+ case 1:
844
+ return 'ERROR'; // typescript.DiagnosticCategory.Error
845
+ case 2:
846
+ return 'WARNING'; // typescript.DiagnosticCategory.Warning
847
+ case 3:
848
+ return 'INFO'; // typescript.DiagnosticCategory.Message
849
+ default:
850
+ return 'ERROR';
851
+ }
852
+ }
853
+ /**
854
+ * Calcula estadísticas de errores para análisis
855
+ */
856
+ calculateErrorStats(diagnostics) {
857
+ const stats = {
858
+ totalErrors: diagnostics.length,
859
+ errorsByCategory: {},
860
+ errorsBySeverity: {},
861
+ mostCommonError: null,
862
+ };
863
+ // Contar por categoría y severidad
864
+ diagnostics.forEach(diag => {
865
+ const category = diag._category || 'OTHER';
866
+ const severity = diag._severity || 'ERROR';
867
+ stats.errorsByCategory[category] =
868
+ (stats.errorsByCategory[category] || 0) + 1;
869
+ stats.errorsBySeverity[severity] =
870
+ (stats.errorsBySeverity[severity] || 0) + 1;
871
+ }); // Encontrar el error más común
872
+ const errorCounts = {};
873
+ diagnostics.forEach(diag => {
874
+ const code = String(diag.code);
875
+ errorCounts[code] = (errorCounts[code] || 0) + 1;
876
+ });
877
+ const errorCountKeys = Object.keys(errorCounts);
878
+ if (errorCountKeys.length > 0) {
879
+ const mostCommonCode = errorCountKeys.reduce((a, b) => (errorCounts[a] || 0) > (errorCounts[b] || 0) ? a : b);
880
+ stats.mostCommonError = {
881
+ code: mostCommonCode,
882
+ count: errorCounts[mostCommonCode],
883
+ message: diagnostics.find(d => String(d.code) === mostCommonCode)?.messageText,
884
+ };
885
+ }
886
+ return stats;
887
+ }
888
+ /**
889
+ * Crea un error categorizado con información de contexto
890
+ */
891
+ createCategorizedError(errorMessage, fileName, response) {
892
+ const error = new Error(errorMessage);
893
+ // Añadir metadatos del error
894
+ error.fileName = fileName;
895
+ error.timestamp = Date.now();
896
+ error.workerResponse = response;
897
+ error.category = this.categorizeGenericError(errorMessage);
898
+ error.isRecoverable = this.isRecoverableError(errorMessage);
899
+ return error;
900
+ }
901
+ /**
902
+ * Categoriza errores genéricos del worker
903
+ */
904
+ categorizeGenericError(errorMessage) {
905
+ if (errorMessage.includes('timeout') ||
906
+ errorMessage.includes('Timeout')) {
907
+ return 'TIMEOUT';
908
+ }
909
+ else if (errorMessage.includes('memory') ||
910
+ errorMessage.includes('Memory')) {
911
+ return 'MEMORY';
912
+ }
913
+ else if (errorMessage.includes('Worker') &&
914
+ errorMessage.includes('exited')) {
915
+ return 'WORKER_CRASH';
916
+ }
917
+ else if (errorMessage.includes('initialization') ||
918
+ errorMessage.includes('init')) {
919
+ return 'INITIALIZATION';
920
+ }
921
+ return 'UNKNOWN';
922
+ }
923
+ /**
924
+ * Determina si un error es recuperable
925
+ */
926
+ isRecoverableError(errorMessage) {
927
+ const recoverablePatterns = [
928
+ 'timeout',
929
+ 'Worker being recycled',
930
+ 'Worker pool cerrado',
931
+ 'temporary',
932
+ ];
933
+ return recoverablePatterns.some(pattern => errorMessage.toLowerCase().includes(pattern.toLowerCase()));
934
+ }
935
+ }
936
+ //# sourceMappingURL=typescript-worker-pool.js.map