@mutineerjs/mutineer 0.2.4 → 0.3.2

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 (132) hide show
  1. package/README.md +48 -42
  2. package/dist/bin/mutineer.js +0 -0
  3. package/dist/mutators/__tests__/operator.spec.js +169 -0
  4. package/dist/mutators/__tests__/registry.spec.js +6 -0
  5. package/dist/mutators/__tests__/return-value.spec.js +239 -0
  6. package/dist/mutators/__tests__/utils.spec.js +68 -1
  7. package/dist/mutators/operator.d.ts +25 -0
  8. package/dist/mutators/operator.js +50 -0
  9. package/dist/mutators/registry.d.ts +6 -28
  10. package/dist/mutators/registry.js +14 -66
  11. package/dist/mutators/return-value.d.ts +39 -0
  12. package/dist/mutators/return-value.js +104 -0
  13. package/dist/mutators/utils.d.ts +21 -0
  14. package/dist/mutators/utils.js +44 -27
  15. package/dist/runner/__tests__/pool-executor.spec.js +2 -4
  16. package/dist/runner/__tests__/variants.spec.js +3 -1
  17. package/dist/runner/discover.js +2 -1
  18. package/dist/runner/jest/__tests__/adapter.spec.js +1 -1
  19. package/dist/runner/jest/__tests__/pool.spec.js +5 -6
  20. package/dist/runner/jest/__tests__/worker-runtime.spec.js +2 -2
  21. package/dist/runner/jest/adapter.js +1 -1
  22. package/dist/runner/jest/pool.d.ts +1 -1
  23. package/dist/runner/jest/pool.js +6 -6
  24. package/dist/runner/jest/worker.mjs +1 -1
  25. package/dist/runner/pool-executor.js +1 -1
  26. package/dist/runner/shared/__tests__/redirect-state.spec.js +3 -3
  27. package/dist/runner/shared/index.d.ts +1 -1
  28. package/dist/runner/shared/index.js +1 -1
  29. package/dist/runner/shared/redirect-state.d.ts +2 -2
  30. package/dist/runner/shared/redirect-state.js +4 -4
  31. package/dist/runner/types.d.ts +1 -1
  32. package/dist/runner/vitest/__tests__/adapter.spec.js +1 -1
  33. package/dist/runner/vitest/__tests__/pool.spec.js +1 -1
  34. package/dist/runner/vitest/__tests__/redirect-loader.spec.js +83 -1
  35. package/dist/runner/vitest/__tests__/worker-runtime.spec.js +84 -0
  36. package/dist/runner/vitest/adapter.js +1 -1
  37. package/dist/runner/vitest/index.d.ts +0 -1
  38. package/dist/runner/vitest/index.js +0 -1
  39. package/dist/runner/vitest/pool.d.ts +1 -1
  40. package/dist/runner/vitest/pool.js +7 -7
  41. package/dist/runner/vitest/redirect-loader.d.ts +1 -1
  42. package/dist/runner/vitest/redirect-loader.js +1 -1
  43. package/dist/runner/vitest/worker-runtime.js +3 -3
  44. package/dist/runner/vitest/worker.mjs +1 -1
  45. package/dist/utils/__tests__/coverage.spec.js +167 -0
  46. package/dist/utils/__tests__/progress.spec.js +96 -0
  47. package/package.json +71 -22
  48. package/dist/admin/assets/index-B7nXq-e7.js +0 -32
  49. package/dist/admin/assets/index-B7nXq-e7.js.map +0 -1
  50. package/dist/admin/assets/index-BDQLkBUE.js +0 -32
  51. package/dist/admin/assets/index-BDQLkBUE.js.map +0 -1
  52. package/dist/admin/assets/index-DVkP-Tc7.css +0 -1
  53. package/dist/admin/index.html +0 -13
  54. package/dist/admin/server/admin.d.ts +0 -6
  55. package/dist/admin/server/admin.js +0 -234
  56. package/dist/bin/mutate-vitest.d.ts +0 -2
  57. package/dist/bin/mutate-vitest.js +0 -90
  58. package/dist/plugin/viteMutate.d.ts +0 -15
  59. package/dist/plugin/viteMutate.js +0 -52
  60. package/dist/plugin/vitest.setup.d.ts +0 -47
  61. package/dist/plugin/vitest.setup.js +0 -118
  62. package/dist/plugin/withVitest.d.ts +0 -13
  63. package/dist/plugin/withVitest.js +0 -30
  64. package/dist/runner/__tests__/orchestrator.spec.js +0 -55
  65. package/dist/runner/adapters/__tests__/jest.spec.js +0 -88
  66. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.d.ts +0 -1
  67. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.js +0 -59
  68. package/dist/runner/adapters/__tests__/vitest.spec.d.ts +0 -1
  69. package/dist/runner/adapters/__tests__/vitest.spec.js +0 -118
  70. package/dist/runner/adapters/index.d.ts +0 -10
  71. package/dist/runner/adapters/index.js +0 -9
  72. package/dist/runner/adapters/jest/__tests__/index.spec.d.ts +0 -1
  73. package/dist/runner/adapters/jest/__tests__/index.spec.js +0 -88
  74. package/dist/runner/adapters/jest/index.d.ts +0 -24
  75. package/dist/runner/adapters/jest/index.js +0 -216
  76. package/dist/runner/adapters/jest/worker-runtime.d.ts +0 -37
  77. package/dist/runner/adapters/jest/worker-runtime.js +0 -171
  78. package/dist/runner/adapters/jest-worker-runtime.d.ts +0 -37
  79. package/dist/runner/adapters/jest-worker-runtime.js +0 -171
  80. package/dist/runner/adapters/jest.d.ts +0 -24
  81. package/dist/runner/adapters/jest.js +0 -216
  82. package/dist/runner/adapters/types.d.ts +0 -89
  83. package/dist/runner/adapters/types.js +0 -8
  84. package/dist/runner/adapters/vitest/__tests__/index.spec.d.ts +0 -1
  85. package/dist/runner/adapters/vitest/__tests__/index.spec.js +0 -118
  86. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.d.ts +0 -1
  87. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.js +0 -59
  88. package/dist/runner/adapters/vitest/index.d.ts +0 -33
  89. package/dist/runner/adapters/vitest/index.js +0 -267
  90. package/dist/runner/adapters/vitest/worker-runtime.d.ts +0 -25
  91. package/dist/runner/adapters/vitest/worker-runtime.js +0 -118
  92. package/dist/runner/adapters/vitest-worker-runtime.d.ts +0 -25
  93. package/dist/runner/adapters/vitest-worker-runtime.js +0 -118
  94. package/dist/runner/adapters/vitest.d.ts +0 -33
  95. package/dist/runner/adapters/vitest.js +0 -267
  96. package/dist/runner/pool/__tests__/index.spec.d.ts +0 -1
  97. package/dist/runner/pool/__tests__/index.spec.js +0 -83
  98. package/dist/runner/pool/__tests__/pool-plugin.spec.d.ts +0 -1
  99. package/dist/runner/pool/__tests__/pool-plugin.spec.js +0 -59
  100. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.d.ts +0 -1
  101. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.js +0 -78
  102. package/dist/runner/pool/index.d.ts +0 -8
  103. package/dist/runner/pool/index.js +0 -9
  104. package/dist/runner/pool/jest/pool.d.ts +0 -52
  105. package/dist/runner/pool/jest/pool.js +0 -309
  106. package/dist/runner/pool/jest/worker.d.mts +0 -1
  107. package/dist/runner/pool/jest/worker.mjs +0 -60
  108. package/dist/runner/pool/jest-pool.d.ts +0 -52
  109. package/dist/runner/pool/jest-pool.js +0 -309
  110. package/dist/runner/pool/jest-worker.d.mts +0 -1
  111. package/dist/runner/pool/jest-worker.mjs +0 -60
  112. package/dist/runner/pool/plugin.d.ts +0 -18
  113. package/dist/runner/pool/plugin.js +0 -60
  114. package/dist/runner/pool/pool-plugin.d.ts +0 -18
  115. package/dist/runner/pool/pool-plugin.js +0 -60
  116. package/dist/runner/pool/pool-redirect-loader.d.ts +0 -19
  117. package/dist/runner/pool/pool-redirect-loader.js +0 -116
  118. package/dist/runner/pool/pool-redirect-loader.mjs +0 -146
  119. package/dist/runner/pool/redirect-loader.d.ts +0 -19
  120. package/dist/runner/pool/redirect-loader.js +0 -116
  121. package/dist/runner/pool/vitest/pool.d.ts +0 -70
  122. package/dist/runner/pool/vitest/pool.js +0 -376
  123. package/dist/runner/pool/vitest/worker.d.mts +0 -15
  124. package/dist/runner/pool/vitest/worker.mjs +0 -96
  125. package/dist/runner/pool/vitest-worker.d.mts +0 -15
  126. package/dist/runner/pool/vitest-worker.mjs +0 -96
  127. package/dist/runner/shared-module-redirect.d.ts +0 -56
  128. package/dist/runner/shared-module-redirect.js +0 -84
  129. package/dist/types/api.d.ts +0 -20
  130. package/dist/types/api.js +0 -1
  131. /package/dist/{runner/__tests__/orchestrator.spec.d.ts → mutators/__tests__/operator.spec.d.ts} +0 -0
  132. /package/dist/{runner/adapters/__tests__/jest.spec.d.ts → mutators/__tests__/return-value.spec.d.ts} +0 -0
@@ -1,376 +0,0 @@
1
- /**
2
- * Vitest Worker Pool
3
- *
4
- * Manages a pool of persistent Vitest worker processes that can run
5
- * multiple mutations without restarting, providing significant speedup
6
- * over the per-spawn approach.
7
- *
8
- * Each worker:
9
- * - Starts Vitest in watch mode via programmatic API
10
- * - Receives mutations via stdin (JSON)
11
- * - Uses dynamic redirect loader to swap module at runtime
12
- * - Returns results via stdout (JSON)
13
- */
14
- import { spawn } from 'node:child_process';
15
- import * as path from 'node:path';
16
- import * as readline from 'node:readline';
17
- import * as fs from 'node:fs';
18
- import { fileURLToPath } from 'node:url';
19
- import { EventEmitter } from 'node:events';
20
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
- class VitestWorker extends EventEmitter {
22
- constructor(id, cwd, vitestConfig, debug = false) {
23
- super();
24
- this.cwd = cwd;
25
- this.vitestConfig = vitestConfig;
26
- this.debug = debug;
27
- this.process = null;
28
- this.rl = null;
29
- this.pendingTask = null;
30
- this.ready = false;
31
- this.shuttingDown = false;
32
- this.id = id;
33
- }
34
- async start() {
35
- const workerJs = path.join(__dirname, 'worker.js');
36
- const workerMts = path.join(__dirname, 'worker.mjs');
37
- const workerTs = path.join(__dirname, 'worker.mts');
38
- const workerScript = fs.existsSync(workerJs)
39
- ? workerJs
40
- : fs.existsSync(workerMts)
41
- ? workerMts
42
- : workerTs;
43
- const loaderJs = path.join(__dirname, '..', 'redirect-loader.js');
44
- const loaderMjs = path.join(__dirname, '..', 'redirect-loader.mjs');
45
- const loaderTs = path.join(__dirname, '..', 'redirect-loader.ts');
46
- const loaderScript = fs.existsSync(loaderJs)
47
- ? loaderJs
48
- : fs.existsSync(loaderMjs)
49
- ? loaderMjs
50
- : loaderTs;
51
- const env = {
52
- ...process.env,
53
- MUTINEER_WORKER_ID: this.id,
54
- MUTINEER_CWD: this.cwd,
55
- ...(this.vitestConfig ? { MUTINEER_VITEST_CONFIG: this.vitestConfig } : {}),
56
- ...(this.debug ? { MUTINEER_DEBUG: '1' } : {}),
57
- };
58
- this.log('Starting worker process');
59
- this.process = spawn(process.execPath, [
60
- '--experimental-strip-types',
61
- '--experimental-transform-types',
62
- '--no-warnings',
63
- '--import', loaderScript,
64
- workerScript,
65
- ], {
66
- cwd: this.cwd,
67
- env,
68
- stdio: ['pipe', 'pipe', 'pipe'],
69
- });
70
- // Handle stderr (debug/error output)
71
- this.process.stderr?.on('data', (data) => {
72
- if (this.debug) {
73
- process.stderr.write(`[worker-${this.id}] ${data}`);
74
- }
75
- });
76
- // Set up line reader for stdout (JSON messages)
77
- this.rl = readline.createInterface({
78
- input: this.process.stdout,
79
- terminal: false,
80
- });
81
- this.rl.on('line', (line) => this.handleMessage(line));
82
- this.process.on('error', (err) => {
83
- this.log(`Process error: ${err.message}`);
84
- this.handleExit(1);
85
- });
86
- this.process.on('exit', (code) => {
87
- this.log(`Process exited with code ${code}`);
88
- this.handleExit(code ?? 1);
89
- });
90
- // Wait for ready signal
91
- await new Promise((resolve, reject) => {
92
- const timeoutMs = 120_000; // allow more time for Vitest init (coverage, large projects)
93
- const timeout = setTimeout(() => {
94
- reject(new Error(`Worker ${this.id} did not become ready in time (${timeoutMs}ms)`));
95
- }, timeoutMs);
96
- this.once('ready', () => {
97
- clearTimeout(timeout);
98
- resolve();
99
- });
100
- this.once('error', (err) => {
101
- clearTimeout(timeout);
102
- reject(err);
103
- });
104
- });
105
- }
106
- handleMessage(line) {
107
- const trimmed = line.trim();
108
- if (!trimmed)
109
- return;
110
- // Only attempt to parse JSON lines; ignore any other stdout noise
111
- if (!trimmed.startsWith('{')) {
112
- if (this.debug)
113
- this.log(`Non-JSON stdout: ${trimmed}`);
114
- return;
115
- }
116
- let msg;
117
- try {
118
- msg = JSON.parse(trimmed);
119
- }
120
- catch {
121
- this.log(`Invalid JSON from worker: ${line}`);
122
- return;
123
- }
124
- if (msg.type === 'ready') {
125
- this.ready = true;
126
- this.emit('ready');
127
- return;
128
- }
129
- if (msg.type === 'result') {
130
- if (this.pendingTask) {
131
- const { resolve, timeoutHandle } = this.pendingTask;
132
- if (timeoutHandle)
133
- clearTimeout(timeoutHandle);
134
- this.pendingTask = null;
135
- resolve({
136
- killed: msg.killed ?? true,
137
- durationMs: msg.durationMs ?? 0,
138
- error: msg.error,
139
- });
140
- }
141
- return;
142
- }
143
- if (msg.type === 'shutdown') {
144
- this.emit('shutdown');
145
- return;
146
- }
147
- }
148
- handleExit(code) {
149
- this.ready = false;
150
- if (this.pendingTask && !this.shuttingDown) {
151
- const { reject, timeoutHandle } = this.pendingTask;
152
- if (timeoutHandle)
153
- clearTimeout(timeoutHandle);
154
- this.pendingTask = null;
155
- reject(new Error(`Worker exited unexpectedly with code ${code}`));
156
- }
157
- this.emit('exit', code);
158
- }
159
- isReady() {
160
- return this.ready && this.process !== null && !this.shuttingDown;
161
- }
162
- isBusy() {
163
- return this.pendingTask !== null;
164
- }
165
- async run(mutant, tests, timeoutMs = 10_000) {
166
- if (!this.isReady()) {
167
- throw new Error(`Worker ${this.id} is not ready`);
168
- }
169
- if (this.isBusy()) {
170
- throw new Error(`Worker ${this.id} is busy`);
171
- }
172
- return new Promise((resolve, reject) => {
173
- const timeoutHandle = setTimeout(() => {
174
- if (this.pendingTask) {
175
- this.pendingTask = null;
176
- // Kill and restart the worker on timeout
177
- this.kill();
178
- resolve({ killed: true, durationMs: timeoutMs, error: 'timeout' });
179
- }
180
- }, timeoutMs);
181
- this.pendingTask = { resolve, reject, timeoutHandle };
182
- const request = JSON.stringify({ type: 'run', mutant, tests });
183
- this.process.stdin.write(request + '\n');
184
- });
185
- }
186
- async shutdown() {
187
- if (!this.process || this.shuttingDown)
188
- return;
189
- this.shuttingDown = true;
190
- return new Promise((resolve) => {
191
- const timeout = setTimeout(() => {
192
- this.kill();
193
- resolve();
194
- }, 5000);
195
- this.once('shutdown', () => {
196
- clearTimeout(timeout);
197
- resolve();
198
- });
199
- this.process.stdin.write(JSON.stringify({ type: 'shutdown' }) + '\n');
200
- });
201
- }
202
- kill() {
203
- if (this.process) {
204
- try {
205
- this.process.kill('SIGKILL');
206
- }
207
- catch {
208
- // Ignore
209
- }
210
- this.process = null;
211
- }
212
- this.ready = false;
213
- }
214
- log(msg) {
215
- if (this.debug) {
216
- console.error(`[VitestWorker-${this.id}] ${msg}`);
217
- }
218
- }
219
- }
220
- export class VitestPool {
221
- constructor(options) {
222
- this.workers = [];
223
- this.availableWorkers = [];
224
- this.waitingTasks = [];
225
- this.initialized = false;
226
- this.shuttingDown = false;
227
- this.options = {
228
- cwd: options.cwd,
229
- concurrency: options.concurrency,
230
- vitestConfig: options.vitestConfig,
231
- timeoutMs: options.timeoutMs ?? 10_000,
232
- debug: options.debug ?? false,
233
- createWorker: options.createWorker,
234
- };
235
- }
236
- async init() {
237
- if (this.initialized)
238
- return;
239
- this.log(`Initializing pool with ${this.options.concurrency} workers`);
240
- const startPromises = [];
241
- for (let i = 0; i < this.options.concurrency; i++) {
242
- const worker = this.options.createWorker?.(`w${i}`, {
243
- cwd: this.options.cwd,
244
- vitestConfig: this.options.vitestConfig,
245
- debug: this.options.debug,
246
- }) ??
247
- new VitestWorker(`w${i}`, this.options.cwd, this.options.vitestConfig, this.options.debug);
248
- worker.on('exit', () => {
249
- if (!this.shuttingDown) {
250
- this.handleWorkerExit(worker);
251
- }
252
- });
253
- this.workers.push(worker);
254
- startPromises.push(worker.start().then(() => {
255
- this.availableWorkers.push(worker);
256
- this.log(`Worker ${worker.id} ready`);
257
- }));
258
- }
259
- await Promise.all(startPromises);
260
- this.initialized = true;
261
- this.log('Pool initialized');
262
- }
263
- handleWorkerExit(worker) {
264
- // Remove from available list
265
- const availIdx = this.availableWorkers.indexOf(worker);
266
- if (availIdx >= 0) {
267
- this.availableWorkers.splice(availIdx, 1);
268
- }
269
- // Try to restart the worker
270
- this.log(`Worker ${worker.id} exited, attempting restart`);
271
- const newWorker = this.options.createWorker?.(worker.id, {
272
- cwd: this.options.cwd,
273
- vitestConfig: this.options.vitestConfig,
274
- debug: this.options.debug,
275
- }) ??
276
- new VitestWorker(worker.id, this.options.cwd, this.options.vitestConfig, this.options.debug);
277
- const idx = this.workers.indexOf(worker);
278
- if (idx >= 0) {
279
- this.workers[idx] = newWorker;
280
- }
281
- newWorker.on('exit', () => {
282
- if (!this.shuttingDown) {
283
- this.handleWorkerExit(newWorker);
284
- }
285
- });
286
- newWorker.start()
287
- .then(() => {
288
- this.releaseWorker(newWorker);
289
- this.log(`Worker ${newWorker.id} restarted`);
290
- })
291
- .catch((err) => {
292
- this.log(`Failed to restart worker ${newWorker.id}: ${err}`);
293
- });
294
- }
295
- async acquireWorker() {
296
- // Try to get an available worker
297
- const worker = this.availableWorkers.shift();
298
- if (worker) {
299
- return worker;
300
- }
301
- // Wait for one to become available
302
- return new Promise((resolve) => {
303
- this.waitingTasks.push(resolve);
304
- });
305
- }
306
- releaseWorker(worker) {
307
- // If someone is waiting, give them the worker directly
308
- const waiting = this.waitingTasks.shift();
309
- if (waiting) {
310
- waiting(worker);
311
- return;
312
- }
313
- // Otherwise return to the pool
314
- if (worker.isReady()) {
315
- this.availableWorkers.push(worker);
316
- }
317
- }
318
- async run(mutant, tests) {
319
- if (!this.initialized) {
320
- throw new Error('Pool not initialized. Call init() first.');
321
- }
322
- if (this.shuttingDown) {
323
- throw new Error('Pool is shutting down');
324
- }
325
- const worker = await this.acquireWorker();
326
- try {
327
- const result = await worker.run(mutant, tests, this.options.timeoutMs);
328
- return result;
329
- }
330
- finally {
331
- this.releaseWorker(worker);
332
- }
333
- }
334
- async shutdown() {
335
- if (this.shuttingDown)
336
- return;
337
- this.shuttingDown = true;
338
- this.log('Shutting down pool');
339
- await Promise.all(this.workers.map((w) => w.shutdown()));
340
- this.workers = [];
341
- this.availableWorkers = [];
342
- this.initialized = false;
343
- this.log('Pool shut down');
344
- }
345
- log(msg) {
346
- if (this.options.debug) {
347
- console.error(`[VitestPool] ${msg}`);
348
- }
349
- }
350
- }
351
- /**
352
- * Run a single mutation using the pool.
353
- * Convenience function for integration with orchestrator.
354
- */
355
- export async function runWithPool(pool, mutant, tests) {
356
- try {
357
- const result = await pool.run(mutant, [...tests]);
358
- if (result.error === 'timeout') {
359
- return { status: 'timeout', durationMs: result.durationMs, error: result.error };
360
- }
361
- if (result.error && !result.killed) {
362
- return { status: 'error', durationMs: result.durationMs, error: result.error };
363
- }
364
- return {
365
- status: result.killed ? 'killed' : 'escaped',
366
- durationMs: result.durationMs,
367
- };
368
- }
369
- catch (err) {
370
- return {
371
- status: 'error',
372
- durationMs: 0,
373
- error: err instanceof Error ? err.message : String(err),
374
- };
375
- }
376
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Persistent Vitest worker process.
3
- *
4
- * This worker stays alive and receives mutation tasks via stdin,
5
- * using Vitest's programmatic API to rerun tests without process restart.
6
- *
7
- * Communication protocol (JSON-RPC over stdin/stdout):
8
- *
9
- * Request: { "type": "run", "mutant": { file, code, id, name }, "tests": string[] }
10
- * Response: { "type": "result", "killed": boolean, "durationMs": number }
11
- *
12
- * Request: { "type": "shutdown" }
13
- * Response: { "type": "shutdown", "ok": true }
14
- */
15
- export {};
@@ -1,96 +0,0 @@
1
- /**
2
- * Persistent Vitest worker process.
3
- *
4
- * This worker stays alive and receives mutation tasks via stdin,
5
- * using Vitest's programmatic API to rerun tests without process restart.
6
- *
7
- * Communication protocol (JSON-RPC over stdin/stdout):
8
- *
9
- * Request: { "type": "run", "mutant": { file, code, id, name }, "tests": string[] }
10
- * Response: { "type": "result", "killed": boolean, "durationMs": number }
11
- *
12
- * Request: { "type": "shutdown" }
13
- * Response: { "type": "shutdown", "ok": true }
14
- */
15
- import * as readline from 'node:readline';
16
- import { createVitestWorkerRuntime } from '../../adapters/vitest/worker-runtime.js';
17
- // Global state for redirect - shared with the plugin via globalThis
18
- // Type is declared in pool-plugin.ts
19
- globalThis.__mutineer_redirect__ = { from: null, to: null };
20
- function send(response) {
21
- console.log(JSON.stringify(response));
22
- }
23
- function log(msg) {
24
- if (process.env.MUTINEER_DEBUG !== '1')
25
- return;
26
- console.error(`[vitest-worker] ${msg}`);
27
- }
28
- async function main() {
29
- const workerId = process.env.MUTINEER_WORKER_ID ?? 'unknown';
30
- const cwd = process.env.MUTINEER_CWD ?? process.cwd();
31
- const vitestConfigPath = process.env.MUTINEER_VITEST_CONFIG;
32
- log(`Starting worker ${workerId} in ${cwd}`);
33
- const runtime = createVitestWorkerRuntime({
34
- workerId,
35
- cwd,
36
- vitestConfigPath,
37
- debug: process.env.MUTINEER_DEBUG === '1',
38
- });
39
- try {
40
- await runtime.init();
41
- }
42
- catch (err) {
43
- console.error('[vitest-worker] Failed to initialize Vitest:', err);
44
- process.exit(1);
45
- }
46
- // Signal ready
47
- send({ type: 'ready', workerId });
48
- // Process requests from stdin
49
- const rl = readline.createInterface({
50
- input: process.stdin,
51
- terminal: false,
52
- });
53
- for await (const line of rl) {
54
- if (!line.trim())
55
- continue;
56
- let request;
57
- try {
58
- request = JSON.parse(line);
59
- }
60
- catch (err) {
61
- log(`Invalid JSON: ${line}`);
62
- continue;
63
- }
64
- if (request.type === 'shutdown') {
65
- log('Shutting down');
66
- await runtime.shutdown();
67
- send({ type: 'shutdown', ok: true });
68
- process.exit(0);
69
- }
70
- if (request.type === 'run') {
71
- try {
72
- const { mutant, tests } = request;
73
- const result = await runtime.run(mutant, tests);
74
- send({
75
- type: 'result',
76
- killed: result.killed,
77
- durationMs: result.durationMs,
78
- error: result.error,
79
- });
80
- }
81
- catch (err) {
82
- // On error, treat as killed (conservative)
83
- send({
84
- type: 'result',
85
- killed: true,
86
- durationMs: 0,
87
- error: String(err),
88
- });
89
- }
90
- }
91
- }
92
- }
93
- main().catch((err) => {
94
- console.error('[vitest-worker] Fatal error:', err);
95
- process.exit(1);
96
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Persistent Vitest worker process.
3
- *
4
- * This worker stays alive and receives mutation tasks via stdin,
5
- * using Vitest's programmatic API to rerun tests without process restart.
6
- *
7
- * Communication protocol (JSON-RPC over stdin/stdout):
8
- *
9
- * Request: { "type": "run", "mutant": { file, code, id, name }, "tests": string[] }
10
- * Response: { "type": "result", "killed": boolean, "durationMs": number }
11
- *
12
- * Request: { "type": "shutdown" }
13
- * Response: { "type": "shutdown", "ok": true }
14
- */
15
- export {};
@@ -1,96 +0,0 @@
1
- /**
2
- * Persistent Vitest worker process.
3
- *
4
- * This worker stays alive and receives mutation tasks via stdin,
5
- * using Vitest's programmatic API to rerun tests without process restart.
6
- *
7
- * Communication protocol (JSON-RPC over stdin/stdout):
8
- *
9
- * Request: { "type": "run", "mutant": { file, code, id, name }, "tests": string[] }
10
- * Response: { "type": "result", "killed": boolean, "durationMs": number }
11
- *
12
- * Request: { "type": "shutdown" }
13
- * Response: { "type": "shutdown", "ok": true }
14
- */
15
- import * as readline from 'node:readline';
16
- import { createVitestWorkerRuntime } from '../adapters/vitest-worker-runtime.js';
17
- // Global state for redirect - shared with the plugin via globalThis
18
- // Type is declared in pool-plugin.ts
19
- globalThis.__mutineer_redirect__ = { from: null, to: null };
20
- function send(response) {
21
- console.log(JSON.stringify(response));
22
- }
23
- function log(msg) {
24
- if (process.env.MUTINEER_DEBUG !== '1')
25
- return;
26
- console.error(`[vitest-worker] ${msg}`);
27
- }
28
- async function main() {
29
- const workerId = process.env.MUTINEER_WORKER_ID ?? 'unknown';
30
- const cwd = process.env.MUTINEER_CWD ?? process.cwd();
31
- const vitestConfigPath = process.env.MUTINEER_VITEST_CONFIG;
32
- log(`Starting worker ${workerId} in ${cwd}`);
33
- const runtime = createVitestWorkerRuntime({
34
- workerId,
35
- cwd,
36
- vitestConfigPath,
37
- debug: process.env.MUTINEER_DEBUG === '1',
38
- });
39
- try {
40
- await runtime.init();
41
- }
42
- catch (err) {
43
- console.error('[vitest-worker] Failed to initialize Vitest:', err);
44
- process.exit(1);
45
- }
46
- // Signal ready
47
- send({ type: 'ready', workerId });
48
- // Process requests from stdin
49
- const rl = readline.createInterface({
50
- input: process.stdin,
51
- terminal: false,
52
- });
53
- for await (const line of rl) {
54
- if (!line.trim())
55
- continue;
56
- let request;
57
- try {
58
- request = JSON.parse(line);
59
- }
60
- catch (err) {
61
- log(`Invalid JSON: ${line}`);
62
- continue;
63
- }
64
- if (request.type === 'shutdown') {
65
- log('Shutting down');
66
- await runtime.shutdown();
67
- send({ type: 'shutdown', ok: true });
68
- process.exit(0);
69
- }
70
- if (request.type === 'run') {
71
- try {
72
- const { mutant, tests } = request;
73
- const result = await runtime.run(mutant, tests);
74
- send({
75
- type: 'result',
76
- killed: result.killed,
77
- durationMs: result.durationMs,
78
- error: result.error,
79
- });
80
- }
81
- catch (err) {
82
- // On error, treat as killed (conservative)
83
- send({
84
- type: 'result',
85
- killed: true,
86
- durationMs: 0,
87
- error: String(err),
88
- });
89
- }
90
- }
91
- }
92
- }
93
- main().catch((err) => {
94
- console.error('[vitest-worker] Fatal error:', err);
95
- process.exit(1);
96
- });
@@ -1,56 +0,0 @@
1
- /**
2
- * Shared module redirection utilities for both Jest and Vitest runners.
3
- *
4
- * This module provides common functionality for intercepting module resolution
5
- * and redirecting imports to mutated versions of source files.
6
- */
7
- export interface MutantRedirect {
8
- from: string | null;
9
- to: string | null;
10
- }
11
- declare global {
12
- var __mutineer_redirect__: MutantRedirect | undefined;
13
- }
14
- /**
15
- * Get the path where a mutant should be written.
16
- * Mutants are stored in __mutineer__/ subdirectories alongside source files.
17
- *
18
- * @param originalFile - The original source file path
19
- * @param mutantId - The mutation ID
20
- * @returns Path to the mutant file in __mutineer__/ directory
21
- */
22
- export declare function getMutantFilePath(originalFile: string, mutantId: string): string;
23
- /**
24
- * Get the current redirect configuration from globalThis.
25
- * Used by both Jest and Vitest to check if a module should be redirected.
26
- *
27
- * @returns Object with normalized from/to paths, or null if no redirect is active
28
- */
29
- export declare function getActiveRedirect(): {
30
- from: string;
31
- to: string;
32
- } | null;
33
- /**
34
- * Initialize the global redirect state.
35
- * Should be called once when the runtime starts.
36
- */
37
- export declare function initializeRedirect(): void;
38
- /**
39
- * Set a redirect for module resolution.
40
- *
41
- * @param originalFile - The original source file (absolute path)
42
- * @param mutantPath - The mutant file path
43
- */
44
- export declare function setRedirect(originalFile: string, mutantPath: string): void;
45
- /**
46
- * Clear the current redirect.
47
- */
48
- export declare function clearRedirect(): void;
49
- /**
50
- * Check if a given file path matches the current redirect source.
51
- * Handles path normalization and extension matching.
52
- *
53
- * @param filePath - The file path to check
54
- * @returns true if this file should be redirected
55
- */
56
- export declare function shouldRedirectPath(filePath: string): boolean;