tywrap 0.4.0 → 0.5.1

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 (89) hide show
  1. package/README.md +1 -0
  2. package/dist/index.d.ts +3 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -6
  5. package/dist/index.js.map +1 -1
  6. package/dist/runtime/base.d.ts +7 -7
  7. package/dist/runtime/base.js +8 -8
  8. package/dist/runtime/bounded-context.d.ts +15 -31
  9. package/dist/runtime/bounded-context.d.ts.map +1 -1
  10. package/dist/runtime/bounded-context.js +16 -15
  11. package/dist/runtime/bounded-context.js.map +1 -1
  12. package/dist/runtime/http.d.ts +24 -5
  13. package/dist/runtime/http.d.ts.map +1 -1
  14. package/dist/runtime/http.js +57 -12
  15. package/dist/runtime/http.js.map +1 -1
  16. package/dist/runtime/node.d.ts +33 -7
  17. package/dist/runtime/node.d.ts.map +1 -1
  18. package/dist/runtime/node.js +139 -151
  19. package/dist/runtime/node.js.map +1 -1
  20. package/dist/runtime/pooled-transport.d.ts +2 -18
  21. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  22. package/dist/runtime/pooled-transport.js +2 -29
  23. package/dist/runtime/pooled-transport.js.map +1 -1
  24. package/dist/runtime/process-io.d.ts +2 -18
  25. package/dist/runtime/process-io.d.ts.map +1 -1
  26. package/dist/runtime/process-io.js +3 -30
  27. package/dist/runtime/process-io.js.map +1 -1
  28. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts +12 -0
  29. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -0
  30. package/dist/runtime/pyodide-bootstrap-core.generated.js +12 -0
  31. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -0
  32. package/dist/runtime/pyodide-io.d.ts +7 -37
  33. package/dist/runtime/pyodide-io.d.ts.map +1 -1
  34. package/dist/runtime/pyodide-io.js +90 -214
  35. package/dist/runtime/pyodide-io.js.map +1 -1
  36. package/dist/runtime/pyodide.d.ts +25 -5
  37. package/dist/runtime/pyodide.d.ts.map +1 -1
  38. package/dist/runtime/pyodide.js +59 -10
  39. package/dist/runtime/pyodide.js.map +1 -1
  40. package/dist/runtime/{bridge-protocol.d.ts → rpc-client.d.ts} +59 -46
  41. package/dist/runtime/rpc-client.d.ts.map +1 -0
  42. package/dist/runtime/{bridge-protocol.js → rpc-client.js} +60 -42
  43. package/dist/runtime/rpc-client.js.map +1 -0
  44. package/dist/runtime/worker-pool.d.ts +2 -19
  45. package/dist/runtime/worker-pool.d.ts.map +1 -1
  46. package/dist/runtime/worker-pool.js +2 -30
  47. package/dist/runtime/worker-pool.js.map +1 -1
  48. package/dist/types/index.d.ts +8 -3
  49. package/dist/types/index.d.ts.map +1 -1
  50. package/dist/tywrap.d.ts.map +1 -1
  51. package/dist/tywrap.js +1 -3
  52. package/dist/tywrap.js.map +1 -1
  53. package/package.json +9 -12
  54. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  55. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  56. package/runtime/python_bridge.py +85 -702
  57. package/runtime/safe_codec.py +10 -2
  58. package/runtime/tywrap_bridge_core.py +875 -0
  59. package/src/index.ts +6 -6
  60. package/src/runtime/base.ts +8 -8
  61. package/src/runtime/bounded-context.ts +17 -56
  62. package/src/runtime/http.ts +85 -13
  63. package/src/runtime/node.ts +165 -217
  64. package/src/runtime/pooled-transport.ts +2 -57
  65. package/src/runtime/process-io.ts +3 -55
  66. package/src/runtime/pyodide-bootstrap-core.generated.ts +12 -0
  67. package/src/runtime/pyodide-io.ts +92 -243
  68. package/src/runtime/pyodide.ts +87 -10
  69. package/src/runtime/{bridge-protocol.ts → rpc-client.ts} +76 -49
  70. package/src/runtime/worker-pool.ts +2 -58
  71. package/src/types/index.ts +20 -4
  72. package/src/tywrap.ts +1 -3
  73. package/dist/core/analyzer.d.ts +0 -64
  74. package/dist/core/analyzer.d.ts.map +0 -1
  75. package/dist/core/analyzer.js +0 -710
  76. package/dist/core/analyzer.js.map +0 -1
  77. package/dist/runtime/bridge-core.d.ts +0 -65
  78. package/dist/runtime/bridge-core.d.ts.map +0 -1
  79. package/dist/runtime/bridge-core.js +0 -379
  80. package/dist/runtime/bridge-core.js.map +0 -1
  81. package/dist/runtime/bridge-protocol.d.ts.map +0 -1
  82. package/dist/runtime/bridge-protocol.js.map +0 -1
  83. package/dist/utils/parallel-processor.d.ts +0 -146
  84. package/dist/utils/parallel-processor.d.ts.map +0 -1
  85. package/dist/utils/parallel-processor.js +0 -707
  86. package/dist/utils/parallel-processor.js.map +0 -1
  87. package/src/core/analyzer.ts +0 -824
  88. package/src/runtime/bridge-core.ts +0 -494
  89. package/src/utils/parallel-processor.ts +0 -955
@@ -1,707 +0,0 @@
1
- /**
2
- * Parallel Processing System for tywrap
3
- * High-performance parallel processing for large Python codebases
4
- */
5
- import { EventEmitter } from 'events';
6
- import { cpus } from 'os';
7
- import { performance } from 'perf_hooks';
8
- import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';
9
- import { fileURLToPath } from 'node:url';
10
- import { CodeGenerator } from '../core/generator.js';
11
- import { PyAnalyzer } from '../core/analyzer.js';
12
- import { globalCache } from './cache.js';
13
- import { getComponentLogger } from './logger.js';
14
- const log = getComponentLogger('ParallelProcessor');
15
- export class ParallelProcessor extends EventEmitter {
16
- workers = new Map();
17
- workerStats = new Map();
18
- taskQueue = [];
19
- queueInterval;
20
- activeTasks = new Map();
21
- pendingResults = new Map();
22
- roundRobinIndex = 0;
23
- options;
24
- disposed = false;
25
- debug = false;
26
- constructor(options = {}) {
27
- super();
28
- const defaultWorkerScript = fileURLToPath(import.meta.url);
29
- this.options = {
30
- maxWorkers: options.maxWorkers ?? Math.min(cpus().length, 8),
31
- taskTimeout: options.taskTimeout ?? 30000,
32
- retryAttempts: options.retryAttempts ?? 2,
33
- enableMemoryMonitoring: options.enableMemoryMonitoring ?? true,
34
- enableCaching: options.enableCaching ?? true,
35
- workerScript: options.workerScript ?? defaultWorkerScript,
36
- batchSize: options.batchSize ?? 1,
37
- loadBalancing: options.loadBalancing ?? 'least-loaded',
38
- debug: options.debug ?? false,
39
- };
40
- this.debug = this.options.debug;
41
- if (this.debug) {
42
- this.debugLog(`🚀 Parallel processor initialized with ${this.options.maxWorkers} workers`);
43
- }
44
- }
45
- debugLog(message) {
46
- if (!this.debug) {
47
- return;
48
- }
49
- process.stdout.write(`${message}\n`);
50
- }
51
- /**
52
- * Initialize worker pool
53
- */
54
- async init() {
55
- if (this.disposed) {
56
- throw new Error('Processor has been disposed');
57
- }
58
- // Spawn initial worker pool
59
- for (let i = 0; i < this.options.maxWorkers; i++) {
60
- await this.spawnWorker(`worker_${i}`);
61
- }
62
- // Start task processing
63
- this.processQueue();
64
- this.debugLog(`✅ Worker pool initialized with ${this.workers.size} workers`);
65
- }
66
- /**
67
- * Process Python module analysis in parallel
68
- */
69
- async analyzeModulesParallel(sources, options = {}) {
70
- const chunkSize = options.chunkSize ?? Math.ceil(sources.length / this.options.maxWorkers);
71
- const chunks = this.chunkArray(sources, chunkSize);
72
- this.debugLog(`📊 Analyzing ${sources.length} modules in ${chunks.length} chunks`);
73
- const tasks = chunks.map((chunk, index) => ({
74
- id: `analyze_chunk_${index}`,
75
- type: 'analyze',
76
- data: { sources: chunk },
77
- priority: 1,
78
- }));
79
- const results = await this.executeTasks(tasks);
80
- // Flatten chunked results
81
- const flatResults = [];
82
- for (const result of results) {
83
- if (!result.success || !result.result) {
84
- continue;
85
- }
86
- if (result.success && result.result) {
87
- const chunkResults = result.result;
88
- if (Array.isArray(chunkResults)) {
89
- flatResults.push(...chunkResults);
90
- }
91
- }
92
- }
93
- return flatResults;
94
- }
95
- /**
96
- * Generate TypeScript wrappers in parallel
97
- */
98
- async generateWrappersParallel(modules, options = {}) {
99
- const chunkSize = options.chunkSize ?? Math.ceil(modules.length / this.options.maxWorkers);
100
- const chunks = this.chunkArray(modules, chunkSize);
101
- this.debugLog(`🏗️ Generating ${modules.length} wrappers in ${chunks.length} chunks`);
102
- const tasks = chunks.map((chunk, index) => ({
103
- id: `generate_chunk_${index}`,
104
- type: 'generate',
105
- data: { modules: chunk },
106
- priority: 1,
107
- }));
108
- const results = await this.executeTasks(tasks);
109
- // Flatten chunked results
110
- const flatResults = [];
111
- for (const result of results) {
112
- if (result.success && result.result) {
113
- const chunkResults = result.result;
114
- if (Array.isArray(chunkResults)) {
115
- flatResults.push(...chunkResults);
116
- }
117
- }
118
- }
119
- return flatResults;
120
- }
121
- /**
122
- * Execute tasks in parallel with load balancing
123
- */
124
- async executeTasks(tasks) {
125
- if (this.workers.size === 0) {
126
- await this.init();
127
- }
128
- // Sort tasks by priority
129
- const sortedTasks = [...tasks].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
130
- // Execute tasks with batching
131
- const results = [];
132
- const inFlight = new Set();
133
- const maxInFlight = this.workers.size * 2;
134
- for (const task of sortedTasks) {
135
- const promise = this.executeTask(task);
136
- inFlight.add(promise);
137
- promise
138
- .finally(() => {
139
- inFlight.delete(promise);
140
- })
141
- .catch(() => {
142
- // no-op: individual task failures are encoded in results
143
- });
144
- // Limit concurrent tasks to prevent overwhelming workers
145
- if (inFlight.size >= maxInFlight) {
146
- const completed = await Promise.race(inFlight);
147
- results.push(completed);
148
- }
149
- }
150
- // Wait for remaining tasks
151
- const remaining = await Promise.all(inFlight);
152
- results.push(...remaining);
153
- return results;
154
- }
155
- /**
156
- * Execute single task
157
- */
158
- async executeTask(task) {
159
- // Check cache first if enabled
160
- if (this.options.enableCaching) {
161
- const cacheKey = globalCache.generateKey('parallel_task', task.type, task.data);
162
- const cached = await globalCache.get(cacheKey);
163
- if (cached) {
164
- this.debugLog(`🎯 Cache HIT for task ${task.id}`);
165
- return cached;
166
- }
167
- }
168
- let attempts = 0;
169
- let lastError;
170
- while (attempts < this.options.retryAttempts) {
171
- attempts++;
172
- try {
173
- const selected = this.selectOptimalWorker();
174
- if (!selected) {
175
- throw new Error('No available workers');
176
- }
177
- const result = await this.sendTaskToWorker(selected.workerId, selected.worker, task);
178
- // Cache successful results
179
- if (this.options.enableCaching && result.success) {
180
- const cacheKey = globalCache.generateKey('parallel_task', task.type, task.data);
181
- await globalCache.set(cacheKey, result, { computeTime: result.duration });
182
- }
183
- return result;
184
- }
185
- catch (error) {
186
- lastError = error;
187
- log.warn('Task failed', { taskId: task.id, attempt: attempts, error: String(error) });
188
- if (attempts < this.options.retryAttempts) {
189
- // Exponential backoff
190
- await this.sleep(Math.pow(2, attempts) * 1000);
191
- }
192
- }
193
- }
194
- // Return failure result
195
- return {
196
- taskId: task.id,
197
- success: false,
198
- error: lastError?.message ?? 'Task failed after retries',
199
- duration: 0,
200
- };
201
- }
202
- /**
203
- * Select optimal worker using load balancing strategy
204
- */
205
- selectOptimalWorker() {
206
- const availableWorkers = Array.from(this.workers.entries())
207
- .map(([workerId, worker]) => ({ workerId, worker }))
208
- .filter(({ workerId, worker }) => {
209
- if (worker.threadId <= 0) {
210
- return false;
211
- }
212
- const stats = this.workerStats.get(workerId);
213
- return stats?.isActive !== false;
214
- });
215
- if (availableWorkers.length === 0) {
216
- return null;
217
- }
218
- switch (this.options.loadBalancing) {
219
- case 'round-robin': {
220
- const selected = availableWorkers[this.roundRobinIndex % availableWorkers.length];
221
- this.roundRobinIndex = (this.roundRobinIndex + 1) % availableWorkers.length;
222
- return selected ?? null;
223
- }
224
- case 'least-loaded': {
225
- // Find worker with least active tasks
226
- const workerLoads = availableWorkers.map(({ workerId, worker }) => {
227
- const stats = this.workerStats.get(workerId);
228
- return { workerId, worker, load: stats?.tasksCompleted ?? 0 };
229
- });
230
- workerLoads.sort((a, b) => a.load - b.load);
231
- const first = workerLoads[0];
232
- return first ? { workerId: first.workerId, worker: first.worker } : null;
233
- }
234
- case 'weighted': {
235
- // Weight by average task completion time
236
- const workerWeights = availableWorkers.map(({ workerId, worker }) => {
237
- const stats = this.workerStats.get(workerId);
238
- const avgTime = stats?.averageTime ?? 1000;
239
- return { workerId, worker, weight: 1 / avgTime };
240
- });
241
- // Weighted random selection
242
- const totalWeight = workerWeights.reduce((sum, w) => sum + w.weight, 0);
243
- let random = Math.random() * totalWeight;
244
- for (const { workerId, worker: candidate, weight } of workerWeights) {
245
- random -= weight;
246
- if (random <= 0) {
247
- return { workerId, worker: candidate };
248
- }
249
- }
250
- const fallback = workerWeights[0];
251
- return fallback ? { workerId: fallback.workerId, worker: fallback.worker } : null;
252
- }
253
- default:
254
- return availableWorkers[0] ?? null;
255
- }
256
- }
257
- /**
258
- * Send task to specific worker
259
- */
260
- async sendTaskToWorker(workerId, worker, task) {
261
- return new Promise((resolve, reject) => {
262
- if (this.disposed) {
263
- reject(new Error('Processor has been disposed'));
264
- return;
265
- }
266
- const timeoutMs = task.timeout ?? this.options.taskTimeout;
267
- const timeout = setTimeout(() => {
268
- const pending = this.pendingResults.get(task.id);
269
- if (!pending) {
270
- return;
271
- }
272
- this.pendingResults.delete(task.id);
273
- this.activeTasks.delete(task.id);
274
- pending.reject(new Error(`Task ${task.id} timed out after ${timeoutMs}ms`));
275
- }, timeoutMs);
276
- this.pendingResults.set(task.id, {
277
- workerId,
278
- resolve: resolve,
279
- reject,
280
- timeout,
281
- });
282
- this.activeTasks.set(task.id, task);
283
- try {
284
- worker.postMessage({
285
- type: 'task',
286
- task,
287
- });
288
- }
289
- catch (error) {
290
- this.pendingResults.delete(task.id);
291
- this.activeTasks.delete(task.id);
292
- clearTimeout(timeout);
293
- reject(error instanceof Error ? error : new Error(String(error)));
294
- }
295
- });
296
- }
297
- /**
298
- * Spawn new worker
299
- */
300
- async spawnWorker(workerId) {
301
- const worker = new Worker(this.options.workerScript, {
302
- workerData: {
303
- workerId,
304
- options: {
305
- enableMemoryMonitoring: this.options.enableMemoryMonitoring,
306
- enableCaching: this.options.enableCaching,
307
- },
308
- },
309
- });
310
- // Setup worker message handling
311
- worker.on('message', message => {
312
- this.handleWorkerMessage(workerId, message);
313
- });
314
- // Setup worker error handling
315
- worker.on('error', error => {
316
- const workerError = error instanceof Error ? error : new Error(String(error));
317
- log.error('Worker error', { workerId, error: String(workerError) });
318
- this.handleWorkerError(workerId, workerError);
319
- });
320
- // Setup worker exit handling
321
- worker.on('exit', code => {
322
- log.warn('Worker exited', { workerId, code });
323
- this.handleWorkerExit(workerId, code);
324
- });
325
- this.workers.set(workerId, worker);
326
- this.workerStats.set(workerId, {
327
- workerId,
328
- tasksCompleted: 0,
329
- totalTime: 0,
330
- averageTime: 0,
331
- errorCount: 0,
332
- memoryPeak: 0,
333
- isActive: true,
334
- });
335
- this.debugLog(`👷 Spawned worker ${workerId}`);
336
- return worker;
337
- }
338
- /**
339
- * Handle message from worker
340
- */
341
- handleWorkerMessage(workerId, message) {
342
- if (message.type === 'task_complete') {
343
- const { taskId, result, error, duration, memoryUsage } = message;
344
- const pending = this.pendingResults.get(taskId);
345
- if (pending) {
346
- this.pendingResults.delete(taskId);
347
- this.activeTasks.delete(taskId);
348
- if (pending.timeout) {
349
- clearTimeout(pending.timeout);
350
- }
351
- // Update worker stats
352
- const stats = this.workerStats.get(workerId);
353
- if (stats) {
354
- stats.tasksCompleted++;
355
- stats.totalTime += duration;
356
- stats.averageTime = stats.totalTime / stats.tasksCompleted;
357
- if (error) {
358
- stats.errorCount++;
359
- }
360
- if (memoryUsage && memoryUsage > stats.memoryPeak) {
361
- stats.memoryPeak = memoryUsage;
362
- }
363
- }
364
- const parallelResult = {
365
- taskId,
366
- success: !error,
367
- result,
368
- error,
369
- duration,
370
- memoryUsage,
371
- };
372
- pending.resolve(parallelResult);
373
- }
374
- }
375
- else if (message.type === 'worker_ready') {
376
- this.emit('worker_ready', workerId);
377
- }
378
- }
379
- /**
380
- * Handle worker error
381
- */
382
- handleWorkerError(workerId, error) {
383
- const stats = this.workerStats.get(workerId);
384
- if (stats) {
385
- stats.errorCount++;
386
- stats.isActive = false;
387
- }
388
- // Reject only tasks owned by this worker.
389
- const ownedTasks = [];
390
- for (const [taskId, pending] of this.pendingResults) {
391
- if (pending.workerId === workerId) {
392
- ownedTasks.push(taskId);
393
- }
394
- }
395
- for (const taskId of ownedTasks) {
396
- const pending = this.pendingResults.get(taskId);
397
- if (!pending) {
398
- continue;
399
- }
400
- if (pending.timeout) {
401
- clearTimeout(pending.timeout);
402
- }
403
- pending.reject(new Error(`Worker ${workerId} error: ${error.message}`));
404
- this.pendingResults.delete(taskId);
405
- this.activeTasks.delete(taskId);
406
- }
407
- this.emit('worker_error', workerId, error);
408
- }
409
- /**
410
- * Handle worker exit
411
- */
412
- handleWorkerExit(workerId, code) {
413
- this.workers.delete(workerId);
414
- const stats = this.workerStats.get(workerId);
415
- if (stats) {
416
- stats.isActive = false;
417
- }
418
- // Reject any tasks that were in-flight on this worker.
419
- const ownedTasks = [];
420
- for (const [taskId, pending] of this.pendingResults) {
421
- if (pending.workerId === workerId) {
422
- ownedTasks.push(taskId);
423
- }
424
- }
425
- for (const taskId of ownedTasks) {
426
- const pending = this.pendingResults.get(taskId);
427
- if (!pending) {
428
- continue;
429
- }
430
- if (pending.timeout) {
431
- clearTimeout(pending.timeout);
432
- }
433
- pending.reject(new Error(`Worker ${workerId} exited with code ${code}`));
434
- this.pendingResults.delete(taskId);
435
- this.activeTasks.delete(taskId);
436
- }
437
- // Respawn worker if not disposing
438
- if (!this.disposed && this.workers.size < this.options.maxWorkers) {
439
- this.spawnWorker(`${workerId}_respawn_${Date.now()}`).catch(error => {
440
- log.error('Failed to respawn worker', { workerId, error: String(error) });
441
- });
442
- }
443
- this.emit('worker_exit', workerId, code);
444
- }
445
- /**
446
- * Process task queue
447
- */
448
- processQueue() {
449
- // Simple queue processing - could be enhanced with priority scheduling
450
- if (this.queueInterval) {
451
- return;
452
- }
453
- this.queueInterval = setInterval(() => {
454
- if (this.taskQueue.length > 0 && this.workers.size > 0) {
455
- const task = this.taskQueue.shift();
456
- if (task) {
457
- this.executeTask(task).catch(error => {
458
- log.error('Queue task execution failed', { error: String(error) });
459
- });
460
- }
461
- }
462
- }, 100);
463
- // Don't keep the event loop alive just for an idle queue pump.
464
- this.queueInterval.unref?.();
465
- }
466
- /**
467
- * Get worker statistics
468
- */
469
- getWorkerStats() {
470
- return Array.from(this.workerStats.values());
471
- }
472
- /**
473
- * Get overall processing statistics
474
- */
475
- getStats() {
476
- const workers = Array.from(this.workerStats.values());
477
- const totalTasks = workers.reduce((sum, w) => sum + w.tasksCompleted, 0);
478
- const totalErrors = workers.reduce((sum, w) => sum + w.errorCount, 0);
479
- const totalTime = workers.reduce((sum, w) => sum + w.totalTime, 0);
480
- const avgTime = totalTasks > 0 ? totalTime / totalTasks : 0;
481
- return {
482
- activeWorkers: workers.filter(w => w.isActive).length,
483
- totalWorkers: workers.length,
484
- tasksCompleted: totalTasks,
485
- totalErrors,
486
- averageTaskTime: avgTime,
487
- queueLength: this.taskQueue.length,
488
- activeTasks: this.activeTasks.size,
489
- workerStats: workers,
490
- };
491
- }
492
- /**
493
- * Utility methods
494
- */
495
- chunkArray(array, chunkSize) {
496
- const chunks = [];
497
- for (let i = 0; i < array.length; i += chunkSize) {
498
- chunks.push(array.slice(i, i + chunkSize));
499
- }
500
- return chunks;
501
- }
502
- sleep(ms) {
503
- return new Promise(resolve => setTimeout(resolve, ms));
504
- }
505
- /**
506
- * Gracefully dispose all workers
507
- */
508
- async dispose() {
509
- if (this.disposed) {
510
- return;
511
- }
512
- this.disposed = true;
513
- this.debugLog('🛑 Disposing parallel processor...');
514
- if (this.queueInterval) {
515
- clearInterval(this.queueInterval);
516
- this.queueInterval = undefined;
517
- }
518
- // Reject any pending results immediately so callers don't hang.
519
- const disposedError = new Error('Processor has been disposed');
520
- for (const pending of this.pendingResults.values()) {
521
- if (pending.timeout) {
522
- clearTimeout(pending.timeout);
523
- }
524
- pending.reject(disposedError);
525
- }
526
- this.pendingResults.clear();
527
- this.activeTasks.clear();
528
- // Terminate all workers
529
- const terminationPromises = Array.from(this.workers.values()).map(worker => {
530
- return new Promise(resolve => {
531
- const timeout = setTimeout(() => {
532
- worker.terminate();
533
- resolve();
534
- }, 5000);
535
- worker.once('exit', () => {
536
- clearTimeout(timeout);
537
- resolve();
538
- });
539
- try {
540
- worker.postMessage({ type: 'shutdown' });
541
- }
542
- catch {
543
- // Ignore: worker may already be dead.
544
- }
545
- });
546
- });
547
- await Promise.all(terminationPromises);
548
- // Clear data structures
549
- this.workers.clear();
550
- this.workerStats.clear();
551
- this.taskQueue.length = 0;
552
- this.removeAllListeners();
553
- this.debugLog('✅ Parallel processor disposed');
554
- }
555
- setDebug(debug) {
556
- this.debug = debug;
557
- }
558
- }
559
- // Worker script implementation (this runs in worker threads)
560
- if (!isMainThread && parentPort) {
561
- const port = parentPort;
562
- const { workerId } = workerData;
563
- let analyzer = null;
564
- let analyzerInitialized = false;
565
- let generator = null;
566
- const getAnalyzer = async () => {
567
- analyzer ??= new PyAnalyzer();
568
- if (!analyzerInitialized) {
569
- await analyzer.initialize();
570
- analyzerInitialized = true;
571
- }
572
- return analyzer;
573
- };
574
- const getGenerator = () => {
575
- generator ??= new CodeGenerator();
576
- return generator;
577
- };
578
- const createResultId = (prefix, name, index) => `${prefix}:${name}:${index}`;
579
- // Import required modules in worker context
580
- const workerImplementation = {
581
- async processTask(task) {
582
- const startTime = performance.now();
583
- let result;
584
- let error;
585
- try {
586
- switch (task.type) {
587
- case 'analyze':
588
- result = await this.processAnalysisTask(task.data);
589
- break;
590
- case 'generate':
591
- result = await this.processGenerationTask(task.data);
592
- break;
593
- case 'validate':
594
- result = await this.processValidationTask(task.data);
595
- break;
596
- default:
597
- throw new Error(`Unknown task type: ${task.type}`);
598
- }
599
- }
600
- catch (err) {
601
- error = String(err);
602
- }
603
- const duration = performance.now() - startTime;
604
- const memoryUsage = process.memoryUsage().heapUsed;
605
- return { result, error, duration, memoryUsage };
606
- },
607
- async processAnalysisTask(data) {
608
- const results = [];
609
- const activeAnalyzer = await getAnalyzer();
610
- for (const [index, source] of data.sources.entries()) {
611
- const start = performance.now();
612
- try {
613
- const modulePath = source.path ?? `${source.name}.py`;
614
- const analysis = await activeAnalyzer.analyzePythonModule(source.content, modulePath);
615
- results.push({
616
- taskId: createResultId('analyze', source.name, index),
617
- success: true,
618
- result: analysis,
619
- duration: performance.now() - start,
620
- memoryUsage: process.memoryUsage().heapUsed,
621
- });
622
- }
623
- catch (error) {
624
- results.push({
625
- taskId: createResultId('analyze', source.name, index),
626
- success: false,
627
- error: String(error),
628
- duration: performance.now() - start,
629
- memoryUsage: process.memoryUsage().heapUsed,
630
- });
631
- }
632
- }
633
- return results;
634
- },
635
- async processGenerationTask(data) {
636
- const results = [];
637
- const activeGenerator = getGenerator();
638
- for (const [index, moduleData] of data.modules.entries()) {
639
- const start = performance.now();
640
- try {
641
- const generationOptions = {
642
- moduleName: moduleData.name,
643
- ...moduleData.options,
644
- };
645
- const generated = await activeGenerator.generateModule(moduleData.module, generationOptions);
646
- results.push({
647
- taskId: createResultId('generate', moduleData.name, index),
648
- success: true,
649
- result: generated,
650
- duration: performance.now() - start,
651
- memoryUsage: process.memoryUsage().heapUsed,
652
- });
653
- }
654
- catch (error) {
655
- results.push({
656
- taskId: createResultId('generate', moduleData.name, index),
657
- success: false,
658
- error: String(error),
659
- duration: performance.now() - start,
660
- memoryUsage: process.memoryUsage().heapUsed,
661
- });
662
- }
663
- }
664
- return results;
665
- },
666
- async processValidationTask(_data) {
667
- // Validation logic would go here
668
- return { validated: true };
669
- },
670
- };
671
- port.on('message', async (raw) => {
672
- const message = raw;
673
- if (!message || typeof message !== 'object') {
674
- return;
675
- }
676
- if (message.type === 'task') {
677
- try {
678
- const taskResult = await workerImplementation.processTask(message.task);
679
- port.postMessage({
680
- type: 'task_complete',
681
- taskId: message.task.id,
682
- ...(typeof taskResult === 'object' && taskResult !== null
683
- ? taskResult
684
- : { result: taskResult }),
685
- });
686
- }
687
- catch (error) {
688
- port.postMessage({
689
- type: 'task_complete',
690
- taskId: message.task.id,
691
- error: String(error),
692
- duration: 0,
693
- });
694
- }
695
- }
696
- else if (message.type === 'shutdown') {
697
- process.exit(0);
698
- }
699
- });
700
- // Signal that worker is ready
701
- port.postMessage({
702
- type: 'worker_ready',
703
- workerId,
704
- });
705
- }
706
- export const globalParallelProcessor = new ParallelProcessor();
707
- //# sourceMappingURL=parallel-processor.js.map