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