@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,309 +0,0 @@
1
- import { fork } from 'node:child_process';
2
- import * as path from 'node:path';
3
- import * as fs from 'node:fs';
4
- import { fileURLToPath } from 'node:url';
5
- import { EventEmitter } from 'node:events';
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- class JestWorker extends EventEmitter {
8
- constructor(id, cwd, jestConfig, debug = false) {
9
- super();
10
- this.cwd = cwd;
11
- this.jestConfig = jestConfig;
12
- this.debug = debug;
13
- this.process = null;
14
- this.pendingTask = null;
15
- this.ready = false;
16
- this.shuttingDown = false;
17
- this.id = id;
18
- }
19
- async start() {
20
- const workerJs = path.join(__dirname, 'worker.js');
21
- const workerMts = path.join(__dirname, 'worker.mjs');
22
- const workerTs = path.join(__dirname, 'worker.mts');
23
- const workerScript = fs.existsSync(workerJs)
24
- ? workerJs
25
- : fs.existsSync(workerMts)
26
- ? workerMts
27
- : workerTs;
28
- const env = {
29
- ...process.env,
30
- MUTINEER_WORKER_ID: this.id,
31
- MUTINEER_CWD: this.cwd,
32
- ...(this.jestConfig ? { MUTINEER_JEST_CONFIG: this.jestConfig } : {}),
33
- ...(this.debug ? { MUTINEER_DEBUG: '1' } : {}),
34
- };
35
- this.log('Starting Jest worker process');
36
- this.process = fork(workerScript, [], {
37
- cwd: this.cwd,
38
- env,
39
- stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
40
- execArgv: ['--experimental-strip-types', '--experimental-transform-types', '--no-warnings'],
41
- });
42
- this.process.stderr?.on('data', (data) => {
43
- if (this.debug) {
44
- process.stderr.write(`[jest-worker-${this.id}] ${data}`);
45
- }
46
- });
47
- this.process.on('message', (msg) => this.handleMessage(msg));
48
- this.process.on('error', (err) => {
49
- this.log(`Process error: ${err.message}`);
50
- this.handleExit(1);
51
- });
52
- this.process.on('exit', (code) => {
53
- this.log(`Process exited with code ${code}`);
54
- this.handleExit(code ?? 1);
55
- });
56
- await new Promise((resolve, reject) => {
57
- const timeoutMs = 60_000;
58
- const timeout = setTimeout(() => {
59
- reject(new Error(`Worker ${this.id} did not become ready in time (${timeoutMs}ms)`));
60
- }, timeoutMs);
61
- this.once('ready', () => {
62
- clearTimeout(timeout);
63
- resolve();
64
- });
65
- this.once('error', (err) => {
66
- clearTimeout(timeout);
67
- reject(err);
68
- });
69
- });
70
- }
71
- handleMessage(raw) {
72
- const msg = raw;
73
- if (msg.type === 'ready') {
74
- this.ready = true;
75
- this.emit('ready');
76
- return;
77
- }
78
- if (msg.type === 'result') {
79
- if (this.pendingTask) {
80
- const { resolve, timeoutHandle } = this.pendingTask;
81
- if (timeoutHandle)
82
- clearTimeout(timeoutHandle);
83
- this.pendingTask = null;
84
- resolve({
85
- killed: msg.killed ?? true,
86
- durationMs: msg.durationMs ?? 0,
87
- error: msg.error,
88
- });
89
- }
90
- return;
91
- }
92
- if (msg.type === 'shutdown') {
93
- this.emit('shutdown');
94
- return;
95
- }
96
- }
97
- handleExit(code) {
98
- this.ready = false;
99
- if (this.pendingTask && !this.shuttingDown) {
100
- const { reject, timeoutHandle } = this.pendingTask;
101
- if (timeoutHandle)
102
- clearTimeout(timeoutHandle);
103
- this.pendingTask = null;
104
- reject(new Error(`Worker exited unexpectedly with code ${code}`));
105
- }
106
- this.emit('exit', code);
107
- }
108
- isReady() {
109
- return this.ready && this.process !== null && !this.shuttingDown;
110
- }
111
- isBusy() {
112
- return this.pendingTask !== null;
113
- }
114
- async run(mutant, tests, timeoutMs = 10_000) {
115
- if (!this.isReady()) {
116
- throw new Error(`Worker ${this.id} is not ready`);
117
- }
118
- if (this.isBusy()) {
119
- throw new Error(`Worker ${this.id} is busy`);
120
- }
121
- return new Promise((resolve, reject) => {
122
- const timeoutHandle = setTimeout(() => {
123
- if (this.pendingTask) {
124
- this.pendingTask = null;
125
- this.kill();
126
- resolve({ killed: true, durationMs: timeoutMs, error: 'timeout' });
127
- }
128
- }, timeoutMs);
129
- this.pendingTask = { resolve, reject, timeoutHandle };
130
- this.process.send?.({ type: 'run', mutant, tests });
131
- });
132
- }
133
- async shutdown() {
134
- if (!this.process || this.shuttingDown)
135
- return;
136
- this.shuttingDown = true;
137
- return new Promise((resolve) => {
138
- const timeout = setTimeout(() => {
139
- this.kill();
140
- resolve();
141
- }, 5000);
142
- this.once('shutdown', () => {
143
- clearTimeout(timeout);
144
- resolve();
145
- });
146
- this.process.send?.({ type: 'shutdown' });
147
- });
148
- }
149
- kill() {
150
- if (this.process) {
151
- try {
152
- this.process.kill('SIGKILL');
153
- }
154
- catch {
155
- // ignore
156
- }
157
- this.process = null;
158
- }
159
- this.ready = false;
160
- }
161
- log(msg) {
162
- if (this.debug) {
163
- console.error(`[JestWorker-${this.id}] ${msg}`);
164
- }
165
- }
166
- }
167
- export class JestPool {
168
- constructor(options) {
169
- this.workers = [];
170
- this.availableWorkers = [];
171
- this.waitingTasks = [];
172
- this.initialized = false;
173
- this.shuttingDown = false;
174
- this.options = {
175
- cwd: options.cwd,
176
- concurrency: options.concurrency,
177
- jestConfig: options.jestConfig,
178
- timeoutMs: options.timeoutMs ?? 10_000,
179
- debug: options.debug ?? false,
180
- createWorker: options.createWorker,
181
- };
182
- }
183
- async init() {
184
- if (this.initialized)
185
- return;
186
- const startPromises = [];
187
- for (let i = 0; i < this.options.concurrency; i++) {
188
- const worker = this.options.createWorker?.(`w${i}`, {
189
- cwd: this.options.cwd,
190
- jestConfig: this.options.jestConfig,
191
- debug: this.options.debug,
192
- }) ??
193
- new JestWorker(`w${i}`, this.options.cwd, this.options.jestConfig, this.options.debug);
194
- worker.on('exit', () => {
195
- if (!this.shuttingDown) {
196
- this.handleWorkerExit(worker);
197
- }
198
- });
199
- this.workers.push(worker);
200
- startPromises.push(worker.start().then(() => {
201
- this.availableWorkers.push(worker);
202
- }));
203
- }
204
- await Promise.all(startPromises);
205
- this.initialized = true;
206
- }
207
- handleWorkerExit(worker) {
208
- const availIdx = this.availableWorkers.indexOf(worker);
209
- if (availIdx >= 0) {
210
- this.availableWorkers.splice(availIdx, 1);
211
- }
212
- const newWorker = this.options.createWorker?.(worker.id, {
213
- cwd: this.options.cwd,
214
- jestConfig: this.options.jestConfig,
215
- debug: this.options.debug,
216
- }) ??
217
- new JestWorker(worker.id, this.options.cwd, this.options.jestConfig, this.options.debug);
218
- const idx = this.workers.indexOf(worker);
219
- if (idx >= 0) {
220
- this.workers[idx] = newWorker;
221
- }
222
- newWorker.on('exit', () => {
223
- if (!this.shuttingDown) {
224
- this.handleWorkerExit(newWorker);
225
- }
226
- });
227
- newWorker.start()
228
- .then(() => {
229
- this.releaseWorker(newWorker);
230
- })
231
- .catch((err) => {
232
- this.log(`Failed to restart worker ${newWorker.id}: ${err}`);
233
- });
234
- }
235
- async acquireWorker() {
236
- const worker = this.availableWorkers.shift();
237
- if (worker) {
238
- return worker;
239
- }
240
- return new Promise((resolve) => {
241
- this.waitingTasks.push(resolve);
242
- });
243
- }
244
- releaseWorker(worker) {
245
- const waiting = this.waitingTasks.shift();
246
- if (waiting) {
247
- waiting(worker);
248
- return;
249
- }
250
- if (worker.isReady()) {
251
- this.availableWorkers.push(worker);
252
- }
253
- }
254
- async run(mutant, tests) {
255
- if (!this.initialized) {
256
- throw new Error('Pool not initialized. Call init() first.');
257
- }
258
- if (this.shuttingDown) {
259
- throw new Error('Pool is shutting down');
260
- }
261
- const worker = await this.acquireWorker();
262
- try {
263
- const result = await worker.run(mutant, tests, this.options.timeoutMs);
264
- if (this.options.debug) {
265
- console.error(`[JestPool] worker ${worker.id} returned killed=${result.killed} error=${result.error ?? 'none'} duration=${result.durationMs}`);
266
- }
267
- return result;
268
- }
269
- finally {
270
- this.releaseWorker(worker);
271
- }
272
- }
273
- async shutdown() {
274
- if (this.shuttingDown)
275
- return;
276
- this.shuttingDown = true;
277
- await Promise.all(this.workers.map((w) => w.shutdown()));
278
- this.workers = [];
279
- this.availableWorkers = [];
280
- this.initialized = false;
281
- }
282
- log(msg) {
283
- if (this.options.debug) {
284
- console.error(`[JestPool] ${msg}`);
285
- }
286
- }
287
- }
288
- export async function runWithJestPool(pool, mutant, tests) {
289
- try {
290
- const result = await pool.run(mutant, [...tests]);
291
- if (result.error === 'timeout') {
292
- return { status: 'timeout', durationMs: result.durationMs, error: result.error };
293
- }
294
- if (result.error && !result.killed) {
295
- return { status: 'error', durationMs: result.durationMs, error: result.error };
296
- }
297
- return {
298
- status: result.killed ? 'killed' : 'escaped',
299
- durationMs: result.durationMs,
300
- };
301
- }
302
- catch (err) {
303
- return {
304
- status: 'error',
305
- durationMs: 0,
306
- error: err instanceof Error ? err.message : String(err),
307
- };
308
- }
309
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,60 +0,0 @@
1
- import { createJestWorkerRuntime } from '../../adapters/jest/worker-runtime.js';
2
- globalThis.__mutineer_redirect__ = { from: null, to: null };
3
- function log(msg) {
4
- if (process.env.MUTINEER_DEBUG !== '1')
5
- return;
6
- console.error(`[jest-worker] ${msg}`);
7
- }
8
- async function main() {
9
- const workerId = process.env.MUTINEER_WORKER_ID ?? 'unknown';
10
- const cwd = process.env.MUTINEER_CWD ?? process.cwd();
11
- const jestConfigPath = process.env.MUTINEER_JEST_CONFIG;
12
- const debug = process.env.MUTINEER_DEBUG === '1';
13
- log(`Starting worker ${workerId} in ${cwd}`);
14
- const runtime = createJestWorkerRuntime({
15
- workerId,
16
- cwd,
17
- jestConfigPath,
18
- debug,
19
- });
20
- try {
21
- await runtime.init();
22
- }
23
- catch (err) {
24
- console.error('[jest-worker] Failed to initialize:', err);
25
- process.exit(1);
26
- }
27
- process.send?.({ type: 'ready', workerId });
28
- process.on('message', async (raw) => {
29
- if (raw.type === 'shutdown') {
30
- log('Shutting down');
31
- await runtime.shutdown();
32
- process.send?.({ type: 'shutdown', ok: true });
33
- process.exit(0);
34
- }
35
- if (raw.type === 'run') {
36
- try {
37
- const { mutant, tests } = raw;
38
- const result = await runtime.run(mutant, tests);
39
- process.send?.({
40
- type: 'result',
41
- killed: result.killed,
42
- durationMs: result.durationMs,
43
- error: result.error,
44
- });
45
- }
46
- catch (err) {
47
- process.send?.({
48
- type: 'result',
49
- killed: true,
50
- durationMs: 0,
51
- error: String(err),
52
- });
53
- }
54
- }
55
- });
56
- }
57
- main().catch((err) => {
58
- console.error('[jest-worker] Fatal error:', err);
59
- process.exit(1);
60
- });
@@ -1,52 +0,0 @@
1
- import { EventEmitter } from 'node:events';
2
- import type { MutantPayload, MutantRunResult, MutantRunSummary } from '../../types/mutant.js';
3
- declare class JestWorker extends EventEmitter {
4
- private readonly cwd;
5
- private readonly jestConfig?;
6
- private readonly debug;
7
- readonly id: string;
8
- private process;
9
- private pendingTask;
10
- private ready;
11
- private shuttingDown;
12
- constructor(id: string, cwd: string, jestConfig?: string | undefined, debug?: boolean);
13
- start(): Promise<void>;
14
- private handleMessage;
15
- private handleExit;
16
- isReady(): boolean;
17
- isBusy(): boolean;
18
- run(mutant: MutantPayload, tests: string[], timeoutMs?: number): Promise<MutantRunSummary>;
19
- shutdown(): Promise<void>;
20
- kill(): void;
21
- private log;
22
- }
23
- export interface JestPoolOptions {
24
- cwd: string;
25
- concurrency: number;
26
- jestConfig?: string;
27
- timeoutMs?: number;
28
- debug?: boolean;
29
- createWorker?: (id: string, opts: {
30
- cwd: string;
31
- jestConfig?: string;
32
- debug: boolean;
33
- }) => JestWorker;
34
- }
35
- export declare class JestPool {
36
- private workers;
37
- private availableWorkers;
38
- private waitingTasks;
39
- private readonly options;
40
- private initialized;
41
- private shuttingDown;
42
- constructor(options: JestPoolOptions);
43
- init(): Promise<void>;
44
- private handleWorkerExit;
45
- private acquireWorker;
46
- private releaseWorker;
47
- run(mutant: MutantPayload, tests: string[]): Promise<MutantRunSummary>;
48
- shutdown(): Promise<void>;
49
- private log;
50
- }
51
- export declare function runWithJestPool(pool: JestPool, mutant: MutantPayload, tests: readonly string[]): Promise<MutantRunResult>;
52
- export type { MutantPayload, MutantRunResult, MutantRunSummary } from '../../types/mutant.js';