@systemfsoftware/stryker-js-mutation-run 1.2.5
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/LICENSE +212 -0
- package/README.md +38 -0
- package/dist/checker-C4tGiXtZ.mjs +588 -0
- package/dist/checker-resource-DTYEKXdg.d.mts +108 -0
- package/dist/checker-worker.d.mts +16 -0
- package/dist/checker-worker.mjs +2 -0
- package/dist/child-process-proxy-worker-lx1M5Mz3.mjs +567 -0
- package/dist/child-process-proxy-worker-main.d.mts +1 -0
- package/dist/child-process-proxy-worker-main.mjs +6 -0
- package/dist/child-process-test-runner-worker-BTWRYb8V.mjs +50 -0
- package/dist/child-process-test-runner-worker.d.mts +16 -0
- package/dist/child-process-test-runner-worker.mjs +2 -0
- package/dist/config/base.d.mts +21 -0
- package/dist/config/base.mjs +47 -0
- package/dist/config/config-resolution.d.mts +80 -0
- package/dist/config/config-resolution.mjs +2 -0
- package/dist/config/fork-schema.d.mts +5 -0
- package/dist/config/fork-schema.mjs +2 -0
- package/dist/errors-BkjxkY_S.d.mts +6 -0
- package/dist/errors.d.mts +2 -0
- package/dist/errors.mjs +10 -0
- package/dist/exit-classification.d.mts +28 -0
- package/dist/exit-classification.mjs +39 -0
- package/dist/fork-schema-BHuTbRBR.mjs +62 -0
- package/dist/incremental-differ-DDVwtXBn.d.mts +72 -0
- package/dist/incremental-differ-DY8AA09Q.mjs +579 -0
- package/dist/index-CyOSz6LC.d.mts +104 -0
- package/dist/index.d.mts +87 -0
- package/dist/index.mjs +1885 -0
- package/dist/mutants/incremental-differ.d.mts +2 -0
- package/dist/mutants/incremental-differ.mjs +2 -0
- package/dist/output-mode-CfGHDpi5.d.mts +17 -0
- package/dist/output-mode.d.mts +2 -0
- package/dist/output-mode.mjs +1 -0
- package/dist/plugins-Nx86IJQW.mjs +1618 -0
- package/dist/plugins.d.mts +2 -0
- package/dist/plugins.mjs +2 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/run-event-CzRz9Gtk.d.mts +121 -0
- package/dist/run-event.d.mts +2 -0
- package/dist/run-event.mjs +1 -0
- package/dist/stryker-package-CTcsIZ4S.mjs +23 -0
- package/dist/stryker-package.d.mts +7 -0
- package/dist/stryker-package.mjs +2 -0
- package/dist/timer-BMrE1SQ3.d.mts +20 -0
- package/dist/timer.d.mts +2 -0
- package/dist/timer.mjs +48 -0
- package/dist/verdict-envelope-BWc61mHm.mjs +229 -0
- package/dist/verdict-envelope-CKLZPjxC.d.mts +94 -0
- package/dist/verdict-envelope.d.mts +2 -0
- package/dist/verdict-envelope.mjs +2 -0
- package/package.json +102 -0
- package/schema/stryker-schema.json +903 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { r as PluginCreator } from "./index-CyOSz6LC.mjs";
|
|
2
|
+
import { t as CheckerResource } from "./checker-resource-DTYEKXdg.mjs";
|
|
3
|
+
import { Mutant, StrykerOptions } from "@systemfsoftware/stryker-js-plugin-api/core";
|
|
4
|
+
import { CheckResult } from "@systemfsoftware/stryker-js-plugin-api/check";
|
|
5
|
+
//#region src/checker/checker-worker.d.ts
|
|
6
|
+
declare class CheckerWorker implements CheckerResource {
|
|
7
|
+
private readonly innerCheckers;
|
|
8
|
+
static inject: ["options", "pluginCreator"];
|
|
9
|
+
constructor(options: StrykerOptions, pluginCreator: PluginCreator);
|
|
10
|
+
init(): Promise<void>;
|
|
11
|
+
check(checkerName: string, mutants: Mutant[]): Promise<Record<string, CheckResult>>;
|
|
12
|
+
group(checkerName: string, mutants: Mutant[]): Promise<string[][]>;
|
|
13
|
+
private perform;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { CheckerWorker };
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { D as loggingServer, E as loggerShowColors, I as pluginsByKind, O as loggingServerAddress, P as pluginCreator, T as loggerConsoleOut, k as loggingSink, p as PluginCreator, t as PluginLoader, w as loggerActiveLevel } from "./plugins-Nx86IJQW.mjs";
|
|
2
|
+
import { StrykerOptionsSchema } from "@systemfsoftware/stryker-js-plugin-api/core";
|
|
3
|
+
import { Scope, commonTokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
|
|
4
|
+
import "typed-inject";
|
|
5
|
+
import { errorToString } from "@stryker-mutator/util";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import util, { promisify } from "util";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { Exit, Schema } from "effect";
|
|
10
|
+
import "@systemfsoftware/stryker-js-plugin-api/logging";
|
|
11
|
+
import net from "net";
|
|
12
|
+
import net$1 from "node:net";
|
|
13
|
+
import { promisify as promisify$1 } from "node:util";
|
|
14
|
+
//#region src/logging/log-level.ts
|
|
15
|
+
/**
|
|
16
|
+
* Re-export of @systemfsoftware/stryker-js-plugin-api/core's LogLevel const enum as a local runtime object.
|
|
17
|
+
* The upstream uses `const enum` which rolldown/oxc doesn't inline at bundle time,
|
|
18
|
+
* and which has no runtime emit.
|
|
19
|
+
*
|
|
20
|
+
* Using a const object + type pattern instead of an enum keeps the type structurally
|
|
21
|
+
* compatible with the upstream's LogLevel (both are the same string literal union),
|
|
22
|
+
* so values from @systemfsoftware/stryker-js-plugin-api/core types are assignable without casting.
|
|
23
|
+
*/
|
|
24
|
+
const LogLevel = {
|
|
25
|
+
Trace: "trace",
|
|
26
|
+
Debug: "debug",
|
|
27
|
+
Information: "info",
|
|
28
|
+
Warning: "warn",
|
|
29
|
+
Error: "error",
|
|
30
|
+
Fatal: "fatal",
|
|
31
|
+
Off: "off"
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/logging/logging-event.schema.ts
|
|
35
|
+
/**
|
|
36
|
+
* The wire format of a {@link LoggingEvent}; worker processes serialize
|
|
37
|
+
* events over the logging socket, the server deserializes them.
|
|
38
|
+
*/
|
|
39
|
+
const SerializedLoggingEventSchema = Schema.Struct({
|
|
40
|
+
startTime: Schema.String,
|
|
41
|
+
categoryName: Schema.String,
|
|
42
|
+
message: Schema.String,
|
|
43
|
+
level: Schema.Literals([
|
|
44
|
+
LogLevel.Trace,
|
|
45
|
+
LogLevel.Debug,
|
|
46
|
+
LogLevel.Information,
|
|
47
|
+
LogLevel.Warning,
|
|
48
|
+
LogLevel.Error,
|
|
49
|
+
LogLevel.Fatal,
|
|
50
|
+
LogLevel.Off
|
|
51
|
+
]),
|
|
52
|
+
pid: Schema.Finite
|
|
53
|
+
});
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/logging/logging-event.ts
|
|
56
|
+
const pattern = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
|
|
57
|
+
const ansiRegex = new RegExp(pattern, "g");
|
|
58
|
+
var LoggingEvent = class LoggingEvent {
|
|
59
|
+
startTime;
|
|
60
|
+
categoryName;
|
|
61
|
+
data;
|
|
62
|
+
level;
|
|
63
|
+
pid;
|
|
64
|
+
constructor(categoryName, level, data, startTime, pid) {
|
|
65
|
+
this.startTime = startTime;
|
|
66
|
+
this.categoryName = categoryName;
|
|
67
|
+
this.data = data;
|
|
68
|
+
this.level = level;
|
|
69
|
+
this.pid = pid;
|
|
70
|
+
}
|
|
71
|
+
static create(categoryName, level, data) {
|
|
72
|
+
return new LoggingEvent(categoryName, level, data, /* @__PURE__ */ new Date(), process.pid);
|
|
73
|
+
}
|
|
74
|
+
format() {
|
|
75
|
+
return `${this.#formatPrefix()} ${this.#formatMessage().replace(ansiRegex, "")}`;
|
|
76
|
+
}
|
|
77
|
+
formatColorized() {
|
|
78
|
+
return `${this.#colorizedStart()}${this.#formatPrefix()}${this.#colorizedEnd()} ${this.#formatMessage()}`;
|
|
79
|
+
}
|
|
80
|
+
#formatPrefix() {
|
|
81
|
+
return `${this.startTime.toTimeString().slice(0, 8)} (${this.pid}) ${this.level.toUpperCase()} ${this.categoryName}`;
|
|
82
|
+
}
|
|
83
|
+
#formatMessage() {
|
|
84
|
+
return util.format(...this.data);
|
|
85
|
+
}
|
|
86
|
+
#colorizedStart() {
|
|
87
|
+
return `\x1B[${styles[this.level]}m`;
|
|
88
|
+
}
|
|
89
|
+
#colorizedEnd() {
|
|
90
|
+
return "\x1B[39m";
|
|
91
|
+
}
|
|
92
|
+
static deserialize(ser) {
|
|
93
|
+
return new LoggingEvent(ser.categoryName, ser.level, [ser.message], new Date(ser.startTime), ser.pid);
|
|
94
|
+
}
|
|
95
|
+
serialize() {
|
|
96
|
+
return {
|
|
97
|
+
startTime: this.startTime.toJSON(),
|
|
98
|
+
categoryName: this.categoryName,
|
|
99
|
+
message: this.#formatMessage(),
|
|
100
|
+
level: this.level,
|
|
101
|
+
pid: this.pid
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const styles = Object.freeze({
|
|
106
|
+
trace: 34,
|
|
107
|
+
debug: 36,
|
|
108
|
+
info: 32,
|
|
109
|
+
warn: 33,
|
|
110
|
+
error: 91,
|
|
111
|
+
fatal: 35,
|
|
112
|
+
off: 90
|
|
113
|
+
});
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/logging/priority.ts
|
|
116
|
+
const logLevelPriority = Object.freeze({
|
|
117
|
+
[LogLevel.Trace]: 0,
|
|
118
|
+
[LogLevel.Debug]: 1,
|
|
119
|
+
[LogLevel.Information]: 2,
|
|
120
|
+
[LogLevel.Warning]: 3,
|
|
121
|
+
[LogLevel.Error]: 4,
|
|
122
|
+
[LogLevel.Fatal]: 5,
|
|
123
|
+
[LogLevel.Off]: 6
|
|
124
|
+
});
|
|
125
|
+
function minPriority(a, b) {
|
|
126
|
+
return logLevelPriority[a] < logLevelPriority[b] ? a : b;
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/logging/logging-backend.ts
|
|
130
|
+
const LOG_FILE_NAME = "stryker.log";
|
|
131
|
+
/**
|
|
132
|
+
* The logging backend that handles the actual logging. So to both a file and the stdout, stderr.
|
|
133
|
+
*/
|
|
134
|
+
var LoggingBackend = class {
|
|
135
|
+
activeStdoutLevel = LogLevel.Information;
|
|
136
|
+
activeFileLevel = LogLevel.Off;
|
|
137
|
+
showColors;
|
|
138
|
+
#consoleOut;
|
|
139
|
+
static inject = [loggerConsoleOut, loggerShowColors];
|
|
140
|
+
constructor(consoleOut, showColors) {
|
|
141
|
+
this.#consoleOut = consoleOut;
|
|
142
|
+
this.showColors = showColors;
|
|
143
|
+
}
|
|
144
|
+
log(event) {
|
|
145
|
+
const eventPriority = logLevelPriority[event.level];
|
|
146
|
+
if (eventPriority >= logLevelPriority[this.activeStdoutLevel]) this.#consoleOut.write(`${this.showColors ? event.formatColorized() : event.format()}\n`);
|
|
147
|
+
if (eventPriority >= logLevelPriority[this.activeFileLevel] && !this.#fileStream.errored) this.#fileStream.write(`${event.format()}\n`);
|
|
148
|
+
}
|
|
149
|
+
isEnabled(level) {
|
|
150
|
+
return logLevelPriority[level] >= this.priority;
|
|
151
|
+
}
|
|
152
|
+
get activeLogLevel() {
|
|
153
|
+
return minPriority(this.activeStdoutLevel, this.activeFileLevel);
|
|
154
|
+
}
|
|
155
|
+
get priority() {
|
|
156
|
+
return logLevelPriority[this.activeLogLevel];
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* `allowConsoleColors` is deliberately not read here (R8). It defaults to
|
|
160
|
+
* `true` in the schema, so honouring it would re-enable colour on every run
|
|
161
|
+
* and defeat the machine-mode contract; colour is decided once, from the
|
|
162
|
+
* resolved mode and `NO_COLOR`, and injected at construction.
|
|
163
|
+
*/
|
|
164
|
+
configure({ logLevel, fileLogLevel }) {
|
|
165
|
+
if (logLevel) this.activeStdoutLevel = logLevel;
|
|
166
|
+
if (fileLogLevel) this.activeFileLevel = fileLogLevel;
|
|
167
|
+
}
|
|
168
|
+
#_fileStream;
|
|
169
|
+
get #fileStream() {
|
|
170
|
+
if (!this.#_fileStream) {
|
|
171
|
+
this.#_fileStream = fs.createWriteStream(LOG_FILE_NAME, { flags: "a" });
|
|
172
|
+
this.#_fileStream.on("error", (error) => {
|
|
173
|
+
console.error(`An error occurred while writing to "${LOG_FILE_NAME}"`, error);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return this.#_fileStream;
|
|
177
|
+
}
|
|
178
|
+
async dispose() {
|
|
179
|
+
if (this.#_fileStream) await promisify(this.#_fileStream.end.bind(this.#_fileStream))();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/logging/logger-impl.ts
|
|
184
|
+
var LoggerImpl = class {
|
|
185
|
+
#categoryName;
|
|
186
|
+
#loggingBackend;
|
|
187
|
+
constructor(categoryName, loggingBackend) {
|
|
188
|
+
this.#categoryName = categoryName;
|
|
189
|
+
this.#loggingBackend = loggingBackend;
|
|
190
|
+
}
|
|
191
|
+
isTraceEnabled() {
|
|
192
|
+
return this.#loggingBackend.isEnabled(LogLevel.Trace);
|
|
193
|
+
}
|
|
194
|
+
isDebugEnabled() {
|
|
195
|
+
return this.#loggingBackend.isEnabled(LogLevel.Debug);
|
|
196
|
+
}
|
|
197
|
+
isInfoEnabled() {
|
|
198
|
+
return this.#loggingBackend.isEnabled(LogLevel.Information);
|
|
199
|
+
}
|
|
200
|
+
isWarnEnabled() {
|
|
201
|
+
return this.#loggingBackend.isEnabled(LogLevel.Warning);
|
|
202
|
+
}
|
|
203
|
+
isErrorEnabled() {
|
|
204
|
+
return this.#loggingBackend.isEnabled(LogLevel.Error);
|
|
205
|
+
}
|
|
206
|
+
isFatalEnabled() {
|
|
207
|
+
return this.#loggingBackend.isEnabled(LogLevel.Fatal);
|
|
208
|
+
}
|
|
209
|
+
trace(message, ...args) {
|
|
210
|
+
this.#loggingBackend.log(LoggingEvent.create(this.#categoryName, LogLevel.Trace, [message, ...args]));
|
|
211
|
+
}
|
|
212
|
+
debug(message, ...args) {
|
|
213
|
+
this.#loggingBackend.log(LoggingEvent.create(this.#categoryName, LogLevel.Debug, [message, ...args]));
|
|
214
|
+
}
|
|
215
|
+
info(message, ...args) {
|
|
216
|
+
this.#loggingBackend.log(LoggingEvent.create(this.#categoryName, LogLevel.Information, [message, ...args]));
|
|
217
|
+
}
|
|
218
|
+
warn(message, ...args) {
|
|
219
|
+
this.#loggingBackend.log(LoggingEvent.create(this.#categoryName, LogLevel.Warning, [message, ...args]));
|
|
220
|
+
}
|
|
221
|
+
error(message, ...args) {
|
|
222
|
+
this.#loggingBackend.log(LoggingEvent.create(this.#categoryName, LogLevel.Error, [message, ...args]));
|
|
223
|
+
}
|
|
224
|
+
fatal(message, ...args) {
|
|
225
|
+
this.#loggingBackend.log(LoggingEvent.create(this.#categoryName, LogLevel.Fatal, [message, ...args]));
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/logging/logging-server.ts
|
|
230
|
+
const DELIMITER = "__STRYKER_CORE__";
|
|
231
|
+
var LoggingServer = class LoggingServer {
|
|
232
|
+
loggingSink;
|
|
233
|
+
static inject = [loggingSink];
|
|
234
|
+
#server;
|
|
235
|
+
constructor(loggingSink) {
|
|
236
|
+
this.loggingSink = loggingSink;
|
|
237
|
+
this.#server = net$1.createServer((socket) => {
|
|
238
|
+
socket.setEncoding("utf-8");
|
|
239
|
+
let dataSoFar = "";
|
|
240
|
+
socket.on("data", (data) => {
|
|
241
|
+
dataSoFar += data;
|
|
242
|
+
let index;
|
|
243
|
+
while ((index = dataSoFar.indexOf(DELIMITER)) !== -1) {
|
|
244
|
+
const serialized = JSON.parse(dataSoFar.substring(0, index));
|
|
245
|
+
const logEvent = Schema.decodeUnknownSync(SerializedLoggingEventSchema)(serialized);
|
|
246
|
+
dataSoFar = dataSoFar.substring(index + 16);
|
|
247
|
+
this.loggingSink.log(LoggingEvent.deserialize(logEvent));
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
socket.on("error", (error) => {
|
|
251
|
+
this.loggingSink.log(LoggingEvent.create(LoggingServer.name, LogLevel.Debug, ["An worker log process hung up unexpectedly", error]));
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
listen() {
|
|
256
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
257
|
+
this.#server.listen(() => {
|
|
258
|
+
const address = this.#server.address();
|
|
259
|
+
if (address === null || typeof address === "string") throw new Error(`Address of the logging server is not an AddressInfo: ${typeof address}`);
|
|
260
|
+
resolve({ port: address.port });
|
|
261
|
+
});
|
|
262
|
+
return promise;
|
|
263
|
+
}
|
|
264
|
+
async dispose() {
|
|
265
|
+
await promisify$1(this.#server.close.bind(this.#server))();
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/logging/logging-client.ts
|
|
270
|
+
var LoggingClient = class LoggingClient {
|
|
271
|
+
logLevel;
|
|
272
|
+
loggingServerAddress;
|
|
273
|
+
#socket;
|
|
274
|
+
static inject = [loggerActiveLevel, loggingServerAddress];
|
|
275
|
+
constructor(logLevel, loggingServerAddress) {
|
|
276
|
+
this.logLevel = logLevel;
|
|
277
|
+
this.loggingServerAddress = loggingServerAddress;
|
|
278
|
+
}
|
|
279
|
+
openConnection() {
|
|
280
|
+
return new Promise((res, rej) => {
|
|
281
|
+
this.#socket = net.createConnection(this.loggingServerAddress.port, "localhost", res);
|
|
282
|
+
this.#socket.on("error", (error) => {
|
|
283
|
+
console.error("Error occurred in logging client", error);
|
|
284
|
+
rej(error);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
log(event) {
|
|
289
|
+
if (!this.#socket) throw new Error(`Cannot use the logging client before it is connected, please call '${LoggingClient.name}.prototype.${LoggingClient.prototype.openConnection.name}' first`);
|
|
290
|
+
if (this.isEnabled(event.level) && this.#socket.writable) this.#socket.write(JSON.stringify(event.serialize()) + DELIMITER);
|
|
291
|
+
}
|
|
292
|
+
isEnabled(level) {
|
|
293
|
+
return logLevelPriority[level] >= logLevelPriority[this.logLevel];
|
|
294
|
+
}
|
|
295
|
+
async dispose() {
|
|
296
|
+
if (this.#socket) await promisify(this.#socket.end.bind(this.#socket))();
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/logging/provide-logging.ts
|
|
301
|
+
function getLoggerFactory(loggingSink) {
|
|
302
|
+
return (categoryName) => new LoggerImpl(categoryName ?? "UNKNOWN", loggingSink);
|
|
303
|
+
}
|
|
304
|
+
getLoggerFactory.inject = [loggingSink];
|
|
305
|
+
function loggerFactory(getLogger, target) {
|
|
306
|
+
return getLogger(target?.name);
|
|
307
|
+
}
|
|
308
|
+
loggerFactory.inject = [commonTokens.getLogger, commonTokens.target];
|
|
309
|
+
function provideLogging(injector) {
|
|
310
|
+
return injector.provideFactory(commonTokens.getLogger, getLoggerFactory).provideFactory(commonTokens.logger, loggerFactory, Scope.Transient).provideClass("loggingServer", LoggingServer);
|
|
311
|
+
}
|
|
312
|
+
provideLogging.inject = [loggingSink, commonTokens.injector];
|
|
313
|
+
async function provideLoggingBackend(injector, loggerConsoleOut$1, showColors) {
|
|
314
|
+
const out = injector.provideValue(loggerConsoleOut, loggerConsoleOut$1).provideValue(loggerShowColors, showColors).provideClass(loggingSink, LoggingBackend).provideClass(loggingServer, LoggingServer);
|
|
315
|
+
const loggingServerAddress$2 = await out.resolve(loggingServer).listen();
|
|
316
|
+
return out.provideValue(loggingServerAddress, loggingServerAddress$2);
|
|
317
|
+
}
|
|
318
|
+
provideLoggingBackend.inject = [commonTokens.injector];
|
|
319
|
+
async function provideLoggingClient(injector, loggingServerAddress$1, activeLogLevel) {
|
|
320
|
+
const out = injector.provideValue(loggingServerAddress, loggingServerAddress$1).provideValue(loggerActiveLevel, activeLogLevel).provideClass(loggingSink, LoggingClient);
|
|
321
|
+
await out.resolve(loggingSink).openConnection();
|
|
322
|
+
return out;
|
|
323
|
+
}
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/worker-pool/message-protocol.schema.ts
|
|
326
|
+
const LoggingServerAddressSchema = Schema.Struct({ port: Schema.Finite });
|
|
327
|
+
const PositionSchema = Schema.Struct({
|
|
328
|
+
line: Schema.Finite,
|
|
329
|
+
column: Schema.Finite
|
|
330
|
+
});
|
|
331
|
+
const MutationRangeSchema = Schema.Struct({
|
|
332
|
+
start: PositionSchema,
|
|
333
|
+
end: PositionSchema
|
|
334
|
+
});
|
|
335
|
+
const FileDescriptionSchema = Schema.Struct({ mutate: Schema.Union([Schema.mutable(Schema.Array(MutationRangeSchema)), Schema.Boolean]) });
|
|
336
|
+
const FileDescriptionsSchema = Schema.Record(Schema.String, FileDescriptionSchema);
|
|
337
|
+
const InitMessageSchema = Schema.Struct({
|
|
338
|
+
kind: Schema.Literal(0),
|
|
339
|
+
loggingServerAddress: LoggingServerAddressSchema,
|
|
340
|
+
options: StrykerOptionsSchema,
|
|
341
|
+
fileDescriptions: FileDescriptionsSchema,
|
|
342
|
+
pluginModulePaths: Schema.Array(Schema.String),
|
|
343
|
+
workingDirectory: Schema.String,
|
|
344
|
+
namedExport: Schema.String,
|
|
345
|
+
modulePath: Schema.String
|
|
346
|
+
});
|
|
347
|
+
const CallMessageSchema = Schema.Struct({
|
|
348
|
+
correlationId: Schema.Finite,
|
|
349
|
+
kind: Schema.Literal(1),
|
|
350
|
+
args: Schema.mutable(Schema.Array(Schema.Any)),
|
|
351
|
+
methodName: Schema.String
|
|
352
|
+
});
|
|
353
|
+
const DisposeMessageSchema = Schema.Struct({ kind: Schema.Literal(2) });
|
|
354
|
+
/** Messages sent from the parent process to the child process. */
|
|
355
|
+
const WorkerMessageSchema = Schema.Union([
|
|
356
|
+
CallMessageSchema,
|
|
357
|
+
DisposeMessageSchema,
|
|
358
|
+
InitMessageSchema
|
|
359
|
+
]);
|
|
360
|
+
const ReadyMessageSchema = Schema.Struct({ kind: Schema.Literals([
|
|
361
|
+
5,
|
|
362
|
+
1,
|
|
363
|
+
0
|
|
364
|
+
]) });
|
|
365
|
+
const WorkResultSchema = Schema.Struct({
|
|
366
|
+
correlationId: Schema.Finite,
|
|
367
|
+
kind: Schema.Literal(3),
|
|
368
|
+
result: Schema.Any
|
|
369
|
+
});
|
|
370
|
+
const RejectionResultSchema = Schema.Struct({
|
|
371
|
+
correlationId: Schema.Finite,
|
|
372
|
+
kind: Schema.Literal(4),
|
|
373
|
+
error: Schema.String
|
|
374
|
+
});
|
|
375
|
+
const InitRejectionResultSchema = Schema.Struct({
|
|
376
|
+
kind: Schema.Literal(2),
|
|
377
|
+
error: Schema.String
|
|
378
|
+
});
|
|
379
|
+
/** Messages sent from the child process back to the parent process. */
|
|
380
|
+
const ParentMessageSchema = Schema.Union([
|
|
381
|
+
ReadyMessageSchema,
|
|
382
|
+
WorkResultSchema,
|
|
383
|
+
RejectionResultSchema,
|
|
384
|
+
InitRejectionResultSchema
|
|
385
|
+
]);
|
|
386
|
+
/**
|
|
387
|
+
* Both wire schemas are declared with `S.fromJsonString` applied at the point of
|
|
388
|
+
* use, never here: a const initialized to `S.encodeSync(...)` is a *use* of a
|
|
389
|
+
* schema, not a declaration of one, and this file declares. Keeping the uses out
|
|
390
|
+
* also keeps them out of the generated law suite, which reads every exported
|
|
391
|
+
* schema in the package and cannot build an arbitrary from a function.
|
|
392
|
+
*/
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/worker-pool/subject-module.schema.ts
|
|
395
|
+
/**
|
|
396
|
+
* The child worker was pointed at a module that carries no constructable value
|
|
397
|
+
* under the requested export name. It is a tagged error rather than a bare
|
|
398
|
+
* `throw` so the failure has a name in the type and survives the process
|
|
399
|
+
* boundary as data: the parent reads it back off the `InitError` reply.
|
|
400
|
+
*/
|
|
401
|
+
var SubjectModuleError = class extends Schema.TaggedError()("SubjectModuleError", {
|
|
402
|
+
modulePath: Schema.String,
|
|
403
|
+
namedExport: Schema.String
|
|
404
|
+
}) {};
|
|
405
|
+
/**
|
|
406
|
+
* This half produced a message its own schema refuses to encode. Unlike a decode
|
|
407
|
+
* failure there is no honest way to answer it on the wire - the reply itself is
|
|
408
|
+
* what is broken - so it stays a defect this process raises rather than a value
|
|
409
|
+
* it sends.
|
|
410
|
+
*/
|
|
411
|
+
var ProtocolEncodeError = class extends Schema.TaggedError()("ProtocolEncodeError", { reason: Schema.String }) {};
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/worker-pool/child-process-proxy-worker.ts
|
|
414
|
+
/**
|
|
415
|
+
* This half's two directions, built once from the declared schemas. Three
|
|
416
|
+
* properties matter and each is load-bearing:
|
|
417
|
+
*
|
|
418
|
+
* - The JSON boundary belongs to the schema. A hand-written `JSON.stringify`
|
|
419
|
+
* step is not schema-aware, so it drops a member the decoder then demands and
|
|
420
|
+
* the two halves of one format disagree.
|
|
421
|
+
* - They return an `Exit`, never throwing. A decode failure on a process
|
|
422
|
+
* boundary is an expected outcome, so it is a value this code must answer for
|
|
423
|
+
* rather than an exception that leaves the type unmarked.
|
|
424
|
+
* - They take `unknown`, so the envelope codec performs the only narrowing this
|
|
425
|
+
* half needs. A separate `S.Json` check on the payload would guard the same
|
|
426
|
+
* bytes twice: `args` and `result` are declared `S.Json`, so these already
|
|
427
|
+
* reject anything the wire cannot carry.
|
|
428
|
+
*/
|
|
429
|
+
const decodeWorkerMessage = Schema.decodeUnknownExit(Schema.fromJsonString(WorkerMessageSchema));
|
|
430
|
+
const encodeParentMessage = Schema.encodeUnknownExit(Schema.fromJsonString(ParentMessageSchema));
|
|
431
|
+
/**
|
|
432
|
+
* The two runtime narrowings this worker cannot derive statically, because both
|
|
433
|
+
* subjects arrive from a path decided at runtime. They are type guards rather
|
|
434
|
+
* than schemas on purpose: a `Schema.declare` whose predicate only asks
|
|
435
|
+
* `typeof x === 'function'` states far more than it decides, and it cannot be
|
|
436
|
+
* generated, round-tripped or held to any law - the schema law suite refuses it.
|
|
437
|
+
*/
|
|
438
|
+
const isCallableMember = (input) => typeof input === "function";
|
|
439
|
+
const isSubjectClass = (input) => typeof input === "function";
|
|
440
|
+
var ChildProcessProxyWorker = class ChildProcessProxyWorker {
|
|
441
|
+
injectorFactory;
|
|
442
|
+
log;
|
|
443
|
+
injector;
|
|
444
|
+
realSubject;
|
|
445
|
+
constructor(injectorFactory) {
|
|
446
|
+
this.injectorFactory = injectorFactory;
|
|
447
|
+
process.on("message", this.handleMessage);
|
|
448
|
+
this.send({ kind: 0 });
|
|
449
|
+
this.injector = this.injectorFactory();
|
|
450
|
+
}
|
|
451
|
+
send(value) {
|
|
452
|
+
const encoded = encodeParentMessage(value);
|
|
453
|
+
if (Exit.isFailure(encoded)) throw new ProtocolEncodeError({ reason: String(encoded.cause) });
|
|
454
|
+
if (process.send) process.send(encoded.value);
|
|
455
|
+
}
|
|
456
|
+
handleMessage = (serializedMessage) => {
|
|
457
|
+
const decoded = decodeWorkerMessage(serializedMessage);
|
|
458
|
+
if (Exit.isFailure(decoded)) {
|
|
459
|
+
this.send({
|
|
460
|
+
error: String(decoded.cause),
|
|
461
|
+
kind: 2
|
|
462
|
+
});
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const message = decoded.value;
|
|
466
|
+
switch (message.kind) {
|
|
467
|
+
case 0:
|
|
468
|
+
this.handleInit(message);
|
|
469
|
+
this.removeAnyAdditionalMessageListeners(this.handleMessage);
|
|
470
|
+
break;
|
|
471
|
+
case 1:
|
|
472
|
+
this.handleCall(message);
|
|
473
|
+
this.removeAnyAdditionalMessageListeners(this.handleMessage);
|
|
474
|
+
break;
|
|
475
|
+
case 2: {
|
|
476
|
+
const sendCompleted = () => {
|
|
477
|
+
this.send({ kind: 5 });
|
|
478
|
+
};
|
|
479
|
+
this.injector.dispose().then(sendCompleted).catch(sendCompleted);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
async handleInit(message) {
|
|
485
|
+
try {
|
|
486
|
+
this.handlePromiseRejections();
|
|
487
|
+
const pluginInjector = provideLogging(await provideLoggingClient(this.injector, message.loggingServerAddress, minPriority(message.options.logLevel, message.options.fileLogLevel))).provideValue(commonTokens.options, message.options).provideValue(commonTokens.fileDescriptions, message.fileDescriptions);
|
|
488
|
+
this.log = pluginInjector.resolve(commonTokens.getLogger)(ChildProcessProxyWorker.name);
|
|
489
|
+
const { pluginsByKind: pluginsByKind$1 } = await pluginInjector.injectClass(PluginLoader).load(message.pluginModulePaths);
|
|
490
|
+
const workingDir = path.resolve(message.workingDirectory);
|
|
491
|
+
const injector = pluginInjector.provideValue(pluginsByKind, pluginsByKind$1).provideValue(commonTokens.sandboxDirectory, workingDir).provideClass(pluginCreator, PluginCreator);
|
|
492
|
+
const childModule = await import(message.modulePath);
|
|
493
|
+
const namedExport = Schema.decodeUnknownSync(Schema.Record(Schema.String, Schema.Unknown))(childModule)[message.namedExport];
|
|
494
|
+
if (!isSubjectClass(namedExport)) throw new SubjectModuleError({
|
|
495
|
+
modulePath: message.modulePath,
|
|
496
|
+
namedExport: message.namedExport
|
|
497
|
+
});
|
|
498
|
+
const RealSubjectClass = namedExport;
|
|
499
|
+
if (process.cwd() !== workingDir) {
|
|
500
|
+
this.log.debug(`Changing current working directory for this process to ${workingDir}`);
|
|
501
|
+
process.chdir(workingDir);
|
|
502
|
+
}
|
|
503
|
+
this.realSubject = injector.injectClass(RealSubjectClass);
|
|
504
|
+
this.send({ kind: 1 });
|
|
505
|
+
} catch (err) {
|
|
506
|
+
this.send({
|
|
507
|
+
error: errorToString(err),
|
|
508
|
+
kind: 2
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async handleCall(message) {
|
|
513
|
+
try {
|
|
514
|
+
const result = await this.doCall(message);
|
|
515
|
+
this.send({
|
|
516
|
+
correlationId: message.correlationId,
|
|
517
|
+
kind: 3,
|
|
518
|
+
result: result ?? null
|
|
519
|
+
});
|
|
520
|
+
} catch (err) {
|
|
521
|
+
this.send({
|
|
522
|
+
correlationId: message.correlationId,
|
|
523
|
+
error: errorToString(err),
|
|
524
|
+
kind: 4
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
doCall(message) {
|
|
529
|
+
const realSubject = this.realSubject;
|
|
530
|
+
if (realSubject === void 0) throw new Error("Cannot call methods on the real subject before the child process is initialized.");
|
|
531
|
+
const subjectMember = realSubject[message.methodName];
|
|
532
|
+
if (isCallableMember(subjectMember)) return Reflect.apply(subjectMember, realSubject, message.args);
|
|
533
|
+
else return subjectMember;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Remove any addition message listeners that might me eavesdropping.
|
|
537
|
+
* the @ngtools/webpack plugin listens to messages and throws an error whenever it could not handle a message
|
|
538
|
+
* @see https://github.com/angular/angular-cli/blob/f776d3cf7982b64734c57fe4407434e9f4ec09f7/packages/%40ngtools/webpack/src/type_checker.ts#L79
|
|
539
|
+
* @param exceptListener The listener that should remain
|
|
540
|
+
*/
|
|
541
|
+
removeAnyAdditionalMessageListeners(exceptListener) {
|
|
542
|
+
process.listeners("message").forEach((listener) => {
|
|
543
|
+
if (listener !== exceptListener) {
|
|
544
|
+
this.log?.debug("Removing an additional message listener, we don't want eavesdropping on our inter-process communication: %s", listener.toString());
|
|
545
|
+
process.removeListener("message", listener);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* During mutation testing, it's to be expected that promise rejections are not handled synchronously anymore (or not at all)
|
|
551
|
+
* Let's handle those events so future versions of node don't crash
|
|
552
|
+
* See issue 350: https://github.com/stryker-mutator/stryker-js/issues/350
|
|
553
|
+
*/
|
|
554
|
+
handlePromiseRejections() {
|
|
555
|
+
const unhandledRejections = [];
|
|
556
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
557
|
+
const unhandledPromiseId = unhandledRejections.push(promise);
|
|
558
|
+
this.log?.debug(`UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: ${unhandledPromiseId}): ${errorToString(reason)}`);
|
|
559
|
+
});
|
|
560
|
+
process.on("rejectionHandled", (promise) => {
|
|
561
|
+
const unhandledPromiseId = unhandledRejections.indexOf(promise) + 1;
|
|
562
|
+
this.log?.debug(`PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: ${unhandledPromiseId})`);
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
//#endregion
|
|
567
|
+
export { provideLogging as a, WorkerMessageSchema as i, ProtocolEncodeError as n, provideLoggingBackend as o, ParentMessageSchema as r, ChildProcessProxyWorker as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { t as ChildProcessProxyWorker } from "./child-process-proxy-worker-lx1M5Mz3.mjs";
|
|
2
|
+
import { createInjector } from "typed-inject";
|
|
3
|
+
//#region src/worker-pool/child-process-proxy-worker-main.ts
|
|
4
|
+
new ChildProcessProxyWorker(createInjector);
|
|
5
|
+
//#endregion
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { P as pluginCreator } from "./plugins-Nx86IJQW.mjs";
|
|
2
|
+
import "@systemfsoftware/stryker-js-plugin-api/core";
|
|
3
|
+
import { PluginKind, commonTokens, tokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
|
|
4
|
+
import { errorToString } from "@stryker-mutator/util";
|
|
5
|
+
import * as S from "effect/Schema";
|
|
6
|
+
import { DryRunStatus, MutantRunStatus } from "@systemfsoftware/stryker-js-plugin-api/test-runner";
|
|
7
|
+
//#region src/test-runner/mutant-coverage.schema.ts
|
|
8
|
+
/**
|
|
9
|
+
* The coverage document the instrumented test bundle accumulates:
|
|
10
|
+
* hit counts per mutant id, both statically (once per test file) and per
|
|
11
|
+
* test id.
|
|
12
|
+
*/
|
|
13
|
+
const MutantCoverageSchema = S.Struct({
|
|
14
|
+
static: S.Record(S.String, S.Finite),
|
|
15
|
+
perTest: S.Record(S.String, S.Record(S.String, S.Finite))
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/test-runner/child-process-test-runner-worker.ts
|
|
19
|
+
var ChildProcessTestRunnerWorker = class {
|
|
20
|
+
underlyingTestRunner;
|
|
21
|
+
static inject = tokens(commonTokens.options, pluginCreator);
|
|
22
|
+
constructor({ testRunner }, pluginCreator) {
|
|
23
|
+
this.underlyingTestRunner = pluginCreator.create(PluginKind.TestRunner, testRunner);
|
|
24
|
+
}
|
|
25
|
+
async capabilities() {
|
|
26
|
+
return this.underlyingTestRunner.capabilities();
|
|
27
|
+
}
|
|
28
|
+
async init() {
|
|
29
|
+
if (this.underlyingTestRunner.init) await this.underlyingTestRunner.init();
|
|
30
|
+
}
|
|
31
|
+
async dispose() {
|
|
32
|
+
if (this.underlyingTestRunner.dispose) await this.underlyingTestRunner.dispose();
|
|
33
|
+
}
|
|
34
|
+
async dryRun(options) {
|
|
35
|
+
const dryRunResult = await this.underlyingTestRunner.dryRun(options);
|
|
36
|
+
if (dryRunResult.status === DryRunStatus.Complete && !dryRunResult.mutantCoverage && options.coverageAnalysis !== "off") {
|
|
37
|
+
const mutantCoverage = S.decodeUnknownSync(S.optional(MutantCoverageSchema))(globalThis.__mutantCoverage__);
|
|
38
|
+
if (mutantCoverage !== void 0) dryRunResult.mutantCoverage = mutantCoverage;
|
|
39
|
+
}
|
|
40
|
+
if (dryRunResult.status === DryRunStatus.Error) dryRunResult.errorMessage = errorToString(dryRunResult.errorMessage);
|
|
41
|
+
return dryRunResult;
|
|
42
|
+
}
|
|
43
|
+
async mutantRun(options) {
|
|
44
|
+
const result = await this.underlyingTestRunner.mutantRun(options);
|
|
45
|
+
if (result.status === MutantRunStatus.Error) result.errorMessage = errorToString(result.errorMessage);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { ChildProcessTestRunnerWorker as t };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { r as PluginCreator } from "./index-CyOSz6LC.mjs";
|
|
2
|
+
import { StrykerOptions } from "@systemfsoftware/stryker-js-plugin-api/core";
|
|
3
|
+
import { DryRunOptions, DryRunResult, MutantRunOptions, MutantRunResult, TestRunner, TestRunnerCapabilities } from "@systemfsoftware/stryker-js-plugin-api/test-runner";
|
|
4
|
+
//#region src/test-runner/child-process-test-runner-worker.d.ts
|
|
5
|
+
declare class ChildProcessTestRunnerWorker implements TestRunner {
|
|
6
|
+
private readonly underlyingTestRunner;
|
|
7
|
+
static inject: ["options", "pluginCreator"];
|
|
8
|
+
constructor({ testRunner }: StrykerOptions, pluginCreator: PluginCreator);
|
|
9
|
+
capabilities(): Promise<TestRunnerCapabilities>;
|
|
10
|
+
init(): Promise<void>;
|
|
11
|
+
dispose(): Promise<void>;
|
|
12
|
+
dryRun(options: DryRunOptions): Promise<DryRunResult>;
|
|
13
|
+
mutantRun(options: MutantRunOptions): Promise<MutantRunResult>;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { ChildProcessTestRunnerWorker };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { StrykerOptionsSchema } from "@systemfsoftware/stryker-js-plugin-api/core";
|
|
2
|
+
import * as S from "effect/Schema";
|
|
3
|
+
//#region src/config/base-preset.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The document a config file holds: the schema's INPUT side, before
|
|
6
|
+
* defaults are applied. Typed against the schema instead of the decoded
|
|
7
|
+
* `StrykerOptions` because this file is a template for user config files,
|
|
8
|
+
* not a resolved option set — and the deep-partial of the decoded type
|
|
9
|
+
* collapses array fields (`never[]`) that this document legitimately sets.
|
|
10
|
+
*/
|
|
11
|
+
type BasePreset = S.Codec.Encoded<typeof StrykerOptionsSchema>;
|
|
12
|
+
/**
|
|
13
|
+
* Modal Stryker options, inherited via
|
|
14
|
+
* `"extends": "@systemfsoftware/stryker-js-mutation-run/config/base"`.
|
|
15
|
+
*
|
|
16
|
+
* Relative paths below resolve against the working directory of the run that
|
|
17
|
+
* reads them — the consuming package — not against this file.
|
|
18
|
+
*/
|
|
19
|
+
declare const basePreset: BasePreset;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { basePreset as default };
|