| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 |
- import { fork, type ChildProcess } from 'node:child_process';
- import { fileURLToPath } from 'node:url';
- import type { CachedResult } from '@wordle/shared';
- interface SolverTask {
- target: string;
- allWords: string[];
- mode: 'fast' | 'full';
- }
- interface WorkerMessage {
- ok: boolean;
- value?: CachedResult;
- error?: string;
- }
- const workerPath = fileURLToPath(new URL('./solver-worker.ts', import.meta.url));
- /**
- * Run solver computation in a forked child process so long-running entropy
- * calculations never block the main event loop.
- *
- * Uses child_process.fork() rather than worker_threads because the solver
- * modules are TypeScript and need tsx's --import hook — which fork() can
- * inject via execArgv while worker_threads cannot.
- *
- * Spawns a short-lived child per call; ~100 ms overhead is negligible
- * compared to the 14 s entropy run.
- */
- export function runSolverInWorker(task: SolverTask): Promise<CachedResult> {
- return new Promise((resolve, reject) => {
- let child: ChildProcess | null = null;
- const done = (err: Error | null, result?: CachedResult) => {
- if (child) { child.kill(); child = null; }
- if (err) reject(err);
- else resolve(result!);
- };
- child = fork(workerPath, [], {
- execArgv: ['--import', 'tsx'],
- stdio: 'pipe',
- });
- child.on('message', (msg: WorkerMessage) => {
- if (msg.ok) done(null, msg.value);
- else done(new Error(msg.error ?? 'unknown worker error'));
- });
- child.on('error', (err) => done(err));
- child.on('exit', (code, signal) => {
- if (child) done(new Error(`Worker exited code=${code} signal=${signal}`));
- });
- child.send(task);
- });
- }
|