@systemfsoftware/stryker-js-mutation-run 2.0.0 → 4.0.0
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/CHANGELOG.md +92 -0
- package/README.md +1 -1
- package/dist/{checker-C4tGiXtZ.mjs → checker-C0Vh51o8.mjs} +3 -3
- package/dist/{checker-resource-DTYEKXdg.d.mts → checker-resource-LZCpsbpn.d.mts} +1 -1
- package/dist/checker-worker.d.mts +2 -2
- package/dist/checker-worker.mjs +1 -1
- package/dist/{child-process-proxy-worker-lx1M5Mz3.mjs → child-process-proxy-worker-CmAwSjYi.mjs} +2 -2
- package/dist/child-process-proxy-worker-main.mjs +1 -1
- package/dist/{child-process-test-runner-worker-BTWRYb8V.mjs → child-process-test-runner-worker-DPXWNPqZ.mjs} +2 -2
- package/dist/child-process-test-runner-worker.d.mts +1 -1
- package/dist/child-process-test-runner-worker.mjs +1 -1
- package/dist/config/base.mjs +2 -1
- package/dist/config/config-resolution.d.mts +2 -2
- package/dist/config/config-resolution.mjs +1 -1
- package/dist/config/fork-schema.mjs +1 -1
- package/dist/{errors-BkjxkY_S.d.mts → errors-B8Vx4QIQ.d.mts} +1 -1
- package/dist/errors.d.mts +1 -1
- package/dist/errors.mjs +1 -1
- package/dist/{fork-schema-BHuTbRBR.mjs → fork-schema-K8Ab5szC.mjs} +1 -14
- package/dist/{incremental-differ-DY8AA09Q.mjs → incremental-differ-DAz2XlBK.mjs} +2 -2
- package/dist/{incremental-differ-DDVwtXBn.d.mts → incremental-differ-DtBnr6fJ.d.mts} +1 -1
- package/dist/{index-CyOSz6LC.d.mts → index-C19nE7Ww.d.mts} +23 -4
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +21 -34
- package/dist/mutants/incremental-differ.d.mts +1 -1
- package/dist/mutants/incremental-differ.mjs +1 -1
- package/dist/{plugins-Nx86IJQW.mjs → plugins-Bdy2g7fR.mjs} +69 -24
- package/dist/plugins.d.mts +1 -1
- package/dist/plugins.mjs +1 -1
- package/dist/{run-event-CzRz9Gtk.d.mts → run-event-Dx_Q5jrc.d.mts} +1 -1
- package/dist/run-event.d.mts +1 -1
- package/dist/{stryker-package-CTcsIZ4S.mjs → stryker-package-JIsmRMux.mjs} +1 -1
- package/dist/stryker-package.d.mts +1 -1
- package/dist/stryker-package.mjs +1 -1
- package/dist/{verdict-envelope-CKLZPjxC.d.mts → verdict-envelope-BJz-h7K-.d.mts} +2 -10
- package/dist/verdict-envelope.d.mts +1 -1
- package/dist/verdict-envelope.mjs +135 -1
- package/package.json +13 -14
- package/schema/stryker-schema.json +6 -20
- package/dist/verdict-envelope-BWc61mHm.mjs +0 -229
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { ConfigError } from "./errors.mjs";
|
|
3
3
|
import { Timer } from "./timer.mjs";
|
|
4
|
-
import { n as REMOVED_OPTIONS } from "./fork-schema-
|
|
4
|
+
import { n as REMOVED_OPTIONS } from "./fork-schema-K8Ab5szC.mjs";
|
|
5
5
|
import { INSTRUMENTER_CONSTANTS, StrykerOptionsSchema } from "@systemfsoftware/stryker-js-plugin-api/core";
|
|
6
6
|
import { commonTokens, tokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
|
|
7
7
|
import "typed-inject";
|
|
8
|
-
import { ERROR_CODES, StrykerError, deepFreeze, deepMerge, errorToString, findUnserializables, isErrnoException, normalizeFileName, normalizeWhitespaces, notEmpty, propertyPath, testFilesProvided } from "@stryker-
|
|
8
|
+
import { ERROR_CODES, StrykerError, deepFreeze, deepMerge, errorToString, findUnserializables, isErrnoException, normalizeFileName, normalizeWhitespaces, notEmpty, propertyPath, testFilesProvided } from "@systemfsoftware/stryker-js-util";
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import { isDeepStrictEqual } from "util";
|
|
11
11
|
import path from "path";
|
|
12
|
-
import {
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
13
13
|
import { Match, Schema } from "effect";
|
|
14
14
|
import "@systemfsoftware/stryker-js-plugin-api/logging";
|
|
15
15
|
import { createRequire } from "module";
|
|
@@ -18,7 +18,7 @@ import { Minimatch, minimatch } from "minimatch";
|
|
|
18
18
|
import * as Result$1 from "effect/Result";
|
|
19
19
|
import * as S from "effect/Schema";
|
|
20
20
|
import { Subject, mergeMap } from "rxjs";
|
|
21
|
-
import "@stryker-
|
|
21
|
+
import "@systemfsoftware/stryker-js-instrumenter";
|
|
22
22
|
import "mutation-testing-report-schema";
|
|
23
23
|
import * as Match$1 from "effect/Match";
|
|
24
24
|
import { exec } from "child_process";
|
|
@@ -131,11 +131,16 @@ var PluginCreator = class {
|
|
|
131
131
|
this.injector = injector;
|
|
132
132
|
}
|
|
133
133
|
create(kind, name) {
|
|
134
|
-
|
|
134
|
+
return this.instantiate(this.findPlugin(kind, name));
|
|
135
|
+
}
|
|
136
|
+
createAll(kind) {
|
|
137
|
+
return (this.pluginsByKind.get(kind) ?? []).filter((plugin) => isPluginOfKind(plugin, kind)).map((plugin) => this.instantiate(plugin));
|
|
138
|
+
}
|
|
139
|
+
instantiate(plugin) {
|
|
135
140
|
if (isFactoryPlugin(plugin)) return this.injector.injectFunction(plugin.factory);
|
|
136
141
|
else if (isClassPlugin(plugin)) return this.injector.injectClass(plugin.injectableClass);
|
|
137
142
|
else if (isValuePlugin(plugin)) return plugin.value;
|
|
138
|
-
throw new Error(
|
|
143
|
+
throw new Error("Plugin could not be created, missing \"factory\", \"injectableClass\" or \"value\" property.");
|
|
139
144
|
}
|
|
140
145
|
findPlugin(kind, name) {
|
|
141
146
|
const plugins = this.pluginsByKind.get(kind);
|
|
@@ -329,6 +334,10 @@ const initialExtendsStepState = {
|
|
|
329
334
|
visited: [],
|
|
330
335
|
documents: []
|
|
331
336
|
};
|
|
337
|
+
const DoneTag = { _tag: "done" };
|
|
338
|
+
const ReadTag = { _tag: "read" };
|
|
339
|
+
const ResolveTag = { _tag: "resolve" };
|
|
340
|
+
const RefusedTag = { _tag: "refused" };
|
|
332
341
|
/**
|
|
333
342
|
* Merge a child config over a parent's resolved options.
|
|
334
343
|
* R2: scalars replace wholesale; objects merge one level deep.
|
|
@@ -402,7 +411,7 @@ const mergeChainDocuments = (documents) => documents.reduceRight((merged, entry)
|
|
|
402
411
|
*/
|
|
403
412
|
const decideExtendsStep = (state, document, file) => {
|
|
404
413
|
if (state.visited.includes(file)) return {
|
|
405
|
-
|
|
414
|
+
...RefusedTag,
|
|
406
415
|
reason: "cycle",
|
|
407
416
|
file
|
|
408
417
|
};
|
|
@@ -414,22 +423,22 @@ const decideExtendsStep = (state, document, file) => {
|
|
|
414
423
|
}]
|
|
415
424
|
};
|
|
416
425
|
return Match$1.value(document["extends"]).pipe(Match$1.when(void 0, () => ({
|
|
417
|
-
|
|
426
|
+
...DoneTag,
|
|
418
427
|
options: mergeChainDocuments(nextState.documents)
|
|
419
428
|
})), Match$1.when(null, () => ({
|
|
420
|
-
|
|
429
|
+
...DoneTag,
|
|
421
430
|
options: mergeChainDocuments(nextState.documents)
|
|
422
431
|
})), Match$1.when(Match$1.string, (extendValue) => Match$1.value(isModuleSpecifier(extendValue)).pipe(Match$1.when(true, () => ({
|
|
423
|
-
|
|
432
|
+
...ResolveTag,
|
|
424
433
|
specifier: extendValue,
|
|
425
434
|
directory: path.dirname(file),
|
|
426
435
|
state: nextState
|
|
427
436
|
})), Match$1.when(false, () => ({
|
|
428
|
-
|
|
437
|
+
...ReadTag,
|
|
429
438
|
path: path.resolve(path.dirname(file), extendValue),
|
|
430
439
|
state: nextState
|
|
431
440
|
})), Match$1.exhaustive)), Match$1.orElse(() => ({
|
|
432
|
-
|
|
441
|
+
...RefusedTag,
|
|
433
442
|
reason: "non-string-extends",
|
|
434
443
|
file
|
|
435
444
|
})));
|
|
@@ -1508,10 +1517,12 @@ const SchemaValidationContributionSchema = Schema.Struct({ strykerValidationSche
|
|
|
1508
1517
|
//#endregion
|
|
1509
1518
|
//#region src/plugins/plugin-loader.ts
|
|
1510
1519
|
const IGNORED_PACKAGES = [
|
|
1511
|
-
"
|
|
1512
|
-
"
|
|
1513
|
-
"
|
|
1514
|
-
"
|
|
1520
|
+
"stryker-js-plugin-api",
|
|
1521
|
+
"stryker-js-util",
|
|
1522
|
+
"stryker-js-instrumenter",
|
|
1523
|
+
"stryker-js-mutation-run",
|
|
1524
|
+
"stryker-js-mutation-report",
|
|
1525
|
+
"stryker-js-cli"
|
|
1515
1526
|
];
|
|
1516
1527
|
/**
|
|
1517
1528
|
* Can resolve modules and pull them into memory
|
|
@@ -1525,11 +1536,11 @@ var PluginLoader = class {
|
|
|
1525
1536
|
/**
|
|
1526
1537
|
* Loads plugins based on configured plugin descriptors.
|
|
1527
1538
|
* A plugin descriptor can be:
|
|
1528
|
-
* * A full url: "file:///home/
|
|
1529
|
-
* * An absolute file path: "/home/
|
|
1539
|
+
* * A full url: "file:///home/user/github/my-plugin.js"
|
|
1540
|
+
* * An absolute file path: "/home/user/github/my-plugin.js"
|
|
1530
1541
|
* * A relative path: "./my-plugin.js"
|
|
1531
|
-
* * A bare import expression: "@stryker-
|
|
1532
|
-
* * A simple glob expression (only wild cards are supported): "@stryker-
|
|
1542
|
+
* * A bare import expression: "@systemfsoftware/stryker-js-vitest-runner"
|
|
1543
|
+
* * A simple glob expression (only wild cards are supported): "@systemfsoftware/stryker-js-*"
|
|
1533
1544
|
*/
|
|
1534
1545
|
async load(pluginDescriptors) {
|
|
1535
1546
|
const pluginModules = await this.resolvePluginModules(pluginDescriptors);
|
|
@@ -1566,14 +1577,48 @@ var PluginLoader = class {
|
|
|
1566
1577
|
}
|
|
1567
1578
|
async globPluginModules(pluginExpression) {
|
|
1568
1579
|
const { org, pkg } = parsePluginExpression(pluginExpression);
|
|
1569
|
-
const pluginDirectory = path.resolve(fileURLToPath(new URL("../../../../../", import.meta.url)), org);
|
|
1570
1580
|
const regexp = new RegExp("^" + pkg.replace("*", ".*"));
|
|
1571
|
-
this.
|
|
1572
|
-
const plugins = (await fs.promises.readdir(pluginDirectory)).filter((pluginName) => !IGNORED_PACKAGES.includes(pluginName) && regexp.test(pluginName)).map((pluginName) => `${org.length ? `${org}/` : ""}${pluginName}`);
|
|
1581
|
+
const plugins = (await this.readOrgDirectory(org)).filter((pluginName) => !IGNORED_PACKAGES.includes(pluginName) && regexp.test(pluginName)).map((pluginName) => `${org.length ? `${org}/` : ""}${pluginName}`);
|
|
1573
1582
|
if (plugins.length === 0 && !defaultOptions.plugins.includes(pluginExpression)) this.log.warn("Expression \"%s\" not resulted in plugins to load.", pluginExpression);
|
|
1574
1583
|
plugins.forEach((plugin) => this.log.debug("Loading plugin \"%s\" (matched with expression %s)", plugin, pluginExpression));
|
|
1575
1584
|
return plugins;
|
|
1576
1585
|
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Reads the org's package names from every install directory above this
|
|
1588
|
+
* module, unioned.
|
|
1589
|
+
*
|
|
1590
|
+
* Never a fixed number of `..` segments: `dist/` and `src/plugins/` sit at
|
|
1591
|
+
* different depths, so a numeric walk resolves to a different directory
|
|
1592
|
+
* depending on which one is running — and a wrong directory yields zero
|
|
1593
|
+
* plugins rather than an error, so the mistake ships as a silent 100% score.
|
|
1594
|
+
*
|
|
1595
|
+
* Never the first directory that happens to be populated either: under a
|
|
1596
|
+
* pnpm-isolated install this package's own virtual `node_modules` holds only
|
|
1597
|
+
* its own dependencies, and stopping there hides every sibling plugin the
|
|
1598
|
+
* project installed one level up. The union spans both, so discovery reaches
|
|
1599
|
+
* as far as a hoisted layout does. A missing org directory contributes
|
|
1600
|
+
* nothing; it is not an error.
|
|
1601
|
+
*/
|
|
1602
|
+
async readOrgDirectory(org) {
|
|
1603
|
+
const names = /* @__PURE__ */ new Set();
|
|
1604
|
+
let directory = path.dirname(fileURLToPath(import.meta.url));
|
|
1605
|
+
for (;;) {
|
|
1606
|
+
const installRoot = path.basename(directory) === "node_modules" ? directory : path.join(directory, "node_modules");
|
|
1607
|
+
const orgDirectory = path.resolve(installRoot, org);
|
|
1608
|
+
try {
|
|
1609
|
+
const entries = await fs.promises.readdir(orgDirectory);
|
|
1610
|
+
if (entries.length > 0) {
|
|
1611
|
+
this.log.debug("Found %d %s packages in %s", entries.length, org, orgDirectory);
|
|
1612
|
+
entries.forEach((entry) => names.add(entry));
|
|
1613
|
+
}
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
if (!isErrnoException(error) || error.code !== "ENOENT") throw error;
|
|
1616
|
+
}
|
|
1617
|
+
const parent = path.dirname(directory);
|
|
1618
|
+
if (parent === directory) return [...names];
|
|
1619
|
+
directory = parent;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1577
1622
|
async loadPlugin(descriptor) {
|
|
1578
1623
|
this.log.debug("Loading plugin %s", descriptor);
|
|
1579
1624
|
try {
|
|
@@ -1594,7 +1639,7 @@ var PluginLoader = class {
|
|
|
1594
1639
|
/**
|
|
1595
1640
|
* Distills organization name from a package expression.
|
|
1596
1641
|
* @example
|
|
1597
|
-
* '@stryker-
|
|
1642
|
+
* '@systemfsoftware/stryker-js-mutation-run' => { org: '@systemfsoftware', 'stryker-js-mutation-run' }
|
|
1598
1643
|
* 'glob' => { org: '', 'glob' }
|
|
1599
1644
|
*/
|
|
1600
1645
|
function parsePluginExpression(pluginExpression) {
|
package/dist/plugins.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as injection_tokens_d_exports, n as PluginLoader, r as PluginCreator, t as LoadedPlugins } from "./index-
|
|
1
|
+
import { a as injection_tokens_d_exports, n as PluginLoader, r as PluginCreator, t as LoadedPlugins } from "./index-C19nE7Ww.mjs";
|
|
2
2
|
export { LoadedPlugins, PluginCreator, PluginLoader, injection_tokens_d_exports as injectionTokens };
|
package/dist/plugins.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as injection_tokens_exports, p as PluginCreator, t as PluginLoader } from "./plugins-
|
|
1
|
+
import { C as injection_tokens_exports, p as PluginCreator, t as PluginLoader } from "./plugins-Bdy2g7fR.mjs";
|
|
2
2
|
export { PluginCreator, PluginLoader, injection_tokens_exports as injectionTokens };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as OutputMode, t as ModeSignal } from "./output-mode-CfGHDpi5.mjs";
|
|
2
|
-
import { i as VerdictEnvelope } from "./verdict-envelope-
|
|
2
|
+
import { i as VerdictEnvelope } from "./verdict-envelope-BJz-h7K-.mjs";
|
|
3
3
|
import { MutantStatus, schema } from "@systemfsoftware/stryker-js-plugin-api/core";
|
|
4
4
|
//#region src/run-event.d.ts
|
|
5
5
|
/**
|
package/dist/run-event.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as PhaseEntered, c as RunEventSink, d as RunStarted, f as RunTerminalEvent, i as MutantTested, l as RunFailed, n as HelpRendered, o as PlanKnown, p as VerdictReached, r as ManifestRendered, s as RunEvent, t as Heartbeat, u as RunPhase } from "./run-event-
|
|
1
|
+
import { a as PhaseEntered, c as RunEventSink, d as RunStarted, f as RunTerminalEvent, i as MutantTested, l as RunFailed, n as HelpRendered, o as PlanKnown, p as VerdictReached, r as ManifestRendered, s as RunEvent, t as Heartbeat, u as RunPhase } from "./run-event-Dx_Q5jrc.mjs";
|
|
2
2
|
export { Heartbeat, HelpRendered, ManifestRendered, MutantTested, PhaseEntered, PlanKnown, RunEvent, RunEventSink, RunFailed, RunPhase, RunStarted, RunTerminalEvent, VerdictReached };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/stryker-package.d.ts
|
|
2
2
|
declare const strykerVersion: string;
|
|
3
|
-
declare const strykerEngines: import("@stryker-
|
|
3
|
+
declare const strykerEngines: import("@systemfsoftware/stryker-js-util").ImmutableObject<{
|
|
4
4
|
readonly node: string;
|
|
5
5
|
}>;
|
|
6
6
|
//#endregion
|
package/dist/stryker-package.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as strykerVersion, t as strykerEngines } from "./stryker-package-
|
|
1
|
+
import { n as strykerVersion, t as strykerEngines } from "./stryker-package-JIsmRMux.mjs";
|
|
2
2
|
export { strykerEngines, strykerVersion };
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { n as OutputMode, t as ModeSignal } from "./output-mode-CfGHDpi5.mjs";
|
|
2
2
|
import { MutantStatus, schema } from "@systemfsoftware/stryker-js-plugin-api/core";
|
|
3
|
-
//#region src/test-contribution.d.ts
|
|
4
|
-
interface TestContributionVerdict {
|
|
5
|
-
readonly failed: boolean;
|
|
6
|
-
readonly message: string;
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
3
|
//#region src/verdict-envelope.d.ts
|
|
10
4
|
/**
|
|
11
5
|
* U4 — the verdict envelope (R5, R11, R20): the single JSON document machine
|
|
@@ -66,9 +60,8 @@ interface VerdictCounts {
|
|
|
66
60
|
/**
|
|
67
61
|
* The full verdict document. `score` and `reportFile` are `null` for a run
|
|
68
62
|
* with zero mutants (AE3): there is no score to report and no report file was
|
|
69
|
-
* written. `
|
|
70
|
-
*
|
|
71
|
-
* for why the remaining statuses are counts only.
|
|
63
|
+
* written. `mutants` is bounded to `ACTIONABLE_STATUSES` (R20) — see that
|
|
64
|
+
* definition for why the remaining statuses are counts only.
|
|
72
65
|
*/
|
|
73
66
|
interface VerdictEnvelope {
|
|
74
67
|
readonly schemaVersion: string;
|
|
@@ -78,7 +71,6 @@ interface VerdictEnvelope {
|
|
|
78
71
|
readonly score: number | null;
|
|
79
72
|
readonly thresholds: VerdictThresholds;
|
|
80
73
|
readonly counts: VerdictCounts;
|
|
81
|
-
readonly testContribution: TestContributionVerdict | null;
|
|
82
74
|
readonly reportFile: string | null;
|
|
83
75
|
readonly mutants: readonly VerdictMutant[];
|
|
84
76
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as VerdictMutant, c as generateRunId, i as VerdictEnvelope, l as isActionableStatus, n as VERDICT_ENVELOPE_SCHEMA_VERSION, o as VerdictThresholds, r as VerdictCounts, s as buildVerdictEnvelope, t as ACTIONABLE_STATUSES } from "./verdict-envelope-
|
|
1
|
+
import { a as VerdictMutant, c as generateRunId, i as VerdictEnvelope, l as isActionableStatus, n as VERDICT_ENVELOPE_SCHEMA_VERSION, o as VerdictThresholds, r as VerdictCounts, s as buildVerdictEnvelope, t as ACTIONABLE_STATUSES } from "./verdict-envelope-BJz-h7K-.mjs";
|
|
2
2
|
export { ACTIONABLE_STATUSES, VERDICT_ENVELOPE_SCHEMA_VERSION, VerdictCounts, VerdictEnvelope, VerdictMutant, VerdictThresholds, buildVerdictEnvelope, generateRunId, isActionableStatus };
|
|
@@ -1,2 +1,136 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "@systemfsoftware/stryker-js-plugin-api/core";
|
|
2
|
+
import { normalizeFileName } from "@systemfsoftware/stryker-js-util";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { calculateMutationTestMetrics } from "mutation-testing-metrics";
|
|
5
|
+
import { randomFillSync } from "node:crypto";
|
|
6
|
+
//#region src/verdict-envelope.ts
|
|
7
|
+
/**
|
|
8
|
+
* U4 — the verdict envelope (R5, R11, R20): the single JSON document machine
|
|
9
|
+
* mode prints to stdout at the end of a run. Everything an agent needs to
|
|
10
|
+
* act without opening the report file, including the survivor re-run
|
|
11
|
+
* matching key per actionable mutant (R20 bounds the list). All functions
|
|
12
|
+
* here are pure over the report — no I/O side effects, no randomness except
|
|
13
|
+
* inside `generateRunId`.
|
|
14
|
+
*/
|
|
15
|
+
const VERDICT_ENVELOPE_SCHEMA_VERSION = "1.0";
|
|
16
|
+
/**
|
|
17
|
+
* The statuses a `verdict.mutants` entry (and a `mutant` stream line, U7) is
|
|
18
|
+
* recorded for (R20). `Killed`, `Ignored`, and `CompileError` are reported
|
|
19
|
+
* as counts only: the full per-mutant record stays in the report file, and
|
|
20
|
+
* enumerating killed mutants served no consumer while pushing the terminal
|
|
21
|
+
* line past the 64 KB limit of `bufio.Scanner`-class readers. Measured:
|
|
22
|
+
* `oxlint-plugins/effect-workflow` produced a 2164-entry, ~440 KB line with
|
|
23
|
+
* zero actionable entries. This is the single definition of the R20 filter,
|
|
24
|
+
* shared with the progress stream.
|
|
25
|
+
*/
|
|
26
|
+
const ACTIONABLE_STATUSES = [
|
|
27
|
+
"Survived",
|
|
28
|
+
"NoCoverage",
|
|
29
|
+
"Timeout",
|
|
30
|
+
"RuntimeError"
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Whether `status` is actionable (R20) — one of `ACTIONABLE_STATUSES`.
|
|
34
|
+
*/
|
|
35
|
+
function isActionableStatus(status) {
|
|
36
|
+
return ACTIONABLE_STATUSES.some((actionable) => actionable === status);
|
|
37
|
+
}
|
|
38
|
+
const CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
39
|
+
/**
|
|
40
|
+
* A ULID-shaped run identifier: 48 bits of millisecond time followed by 80
|
|
41
|
+
* random bits, Crockford base32-encoded into exactly 26 characters. The time
|
|
42
|
+
* prefix keeps ids roughly sortable; the randomness makes collisions
|
|
43
|
+
* negligible.
|
|
44
|
+
*/
|
|
45
|
+
function generateRunId() {
|
|
46
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
bytes[0] = now / 1099511627776 % 256;
|
|
49
|
+
bytes[1] = now / 4294967296 % 256;
|
|
50
|
+
bytes[2] = now / 16777216 % 256;
|
|
51
|
+
bytes[3] = now / 65536 % 256;
|
|
52
|
+
bytes[4] = now / 256 % 256;
|
|
53
|
+
bytes[5] = now % 256;
|
|
54
|
+
randomFillSync(bytes.subarray(6));
|
|
55
|
+
let chars = "";
|
|
56
|
+
let value = 0;
|
|
57
|
+
let bits = 0;
|
|
58
|
+
for (const byte of bytes) {
|
|
59
|
+
value = value << 8 | byte;
|
|
60
|
+
bits += 8;
|
|
61
|
+
while (bits >= 5) {
|
|
62
|
+
chars += CROCKFORD_BASE32[value >>> bits - 5 & 31];
|
|
63
|
+
bits -= 5;
|
|
64
|
+
value &= (1 << bits) - 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (bits > 0) chars += CROCKFORD_BASE32[value << 5 - bits & 31];
|
|
68
|
+
return chars;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The resolved options the report helper embeds as `report.config` (it writes
|
|
72
|
+
* `config: this.options`). Read through `in`-narrowing because the report
|
|
73
|
+
* schema types `config` as `{}`, while the embedded value is our own resolved
|
|
74
|
+
* options with their index signature.
|
|
75
|
+
*/
|
|
76
|
+
function embeddedConfig(report) {
|
|
77
|
+
const config = report.config;
|
|
78
|
+
let jsonReporterFileName;
|
|
79
|
+
if (typeof config === "object" && config !== null) {
|
|
80
|
+
if ("jsonReporter" in config && typeof config.jsonReporter === "object" && config.jsonReporter !== null && "fileName" in config.jsonReporter && typeof config.jsonReporter.fileName === "string") jsonReporterFileName = config.jsonReporter.fileName;
|
|
81
|
+
}
|
|
82
|
+
return { jsonReporterFileName };
|
|
83
|
+
}
|
|
84
|
+
function breakThreshold(thresholds) {
|
|
85
|
+
if ("break" in thresholds) {
|
|
86
|
+
const breakValue = thresholds.break;
|
|
87
|
+
if (typeof breakValue === "number") return breakValue;
|
|
88
|
+
if (breakValue === null) return null;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function buildVerdictEnvelope(report, mode, signal, runId) {
|
|
93
|
+
const { jsonReporterFileName } = embeddedConfig(report);
|
|
94
|
+
const metrics = calculateMutationTestMetrics(report).systemUnderTestMetrics.metrics;
|
|
95
|
+
const hasMutants = metrics.totalMutants > 0;
|
|
96
|
+
const score = hasMutants && Number.isFinite(metrics.mutationScore) ? metrics.mutationScore : null;
|
|
97
|
+
const reportFile = hasMutants && jsonReporterFileName !== void 0 ? normalizeFileName(path.relative(process.cwd(), jsonReporterFileName)) : null;
|
|
98
|
+
const mutants = [];
|
|
99
|
+
for (const [file, fileResult] of Object.entries(report.files)) for (const mutant of fileResult.mutants) {
|
|
100
|
+
if (!isActionableStatus(mutant.status)) continue;
|
|
101
|
+
mutants.push({
|
|
102
|
+
id: mutant.id,
|
|
103
|
+
file,
|
|
104
|
+
location: mutant.location,
|
|
105
|
+
mutator: mutant.mutatorName,
|
|
106
|
+
replacement: mutant.replacement ?? null,
|
|
107
|
+
status: mutant.status
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
schemaVersion: "1.0",
|
|
112
|
+
runId,
|
|
113
|
+
mode,
|
|
114
|
+
signal,
|
|
115
|
+
score,
|
|
116
|
+
thresholds: {
|
|
117
|
+
high: report.thresholds.high,
|
|
118
|
+
low: report.thresholds.low,
|
|
119
|
+
break: breakThreshold(report.thresholds)
|
|
120
|
+
},
|
|
121
|
+
counts: {
|
|
122
|
+
killed: metrics.killed,
|
|
123
|
+
timeout: metrics.timeout,
|
|
124
|
+
survived: metrics.survived,
|
|
125
|
+
noCoverage: metrics.noCoverage,
|
|
126
|
+
runtimeErrors: metrics.runtimeErrors,
|
|
127
|
+
compileErrors: metrics.compileErrors,
|
|
128
|
+
ignored: metrics.ignored,
|
|
129
|
+
pending: metrics.pending
|
|
130
|
+
},
|
|
131
|
+
reportFile,
|
|
132
|
+
mutants
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
2
136
|
export { ACTIONABLE_STATUSES, VERDICT_ENVELOPE_SCHEMA_VERSION, buildVerdictEnvelope, generateRunId, isActionableStatus };
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemfsoftware/stryker-js-mutation-run",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
|
|
7
|
-
"directory": "packages/stryker-js/mutation-run"
|
|
7
|
+
"directory": "packages/testing/mutation/stryker-js/mutation-run"
|
|
8
8
|
},
|
|
9
|
-
"homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/stryker-js/mutation-run#readme",
|
|
9
|
+
"homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/testing/mutation/stryker-js/mutation-run#readme",
|
|
10
10
|
"bugs": "https://github.com/systemfsoftware/systemfsoftware/issues",
|
|
11
|
-
"description": "
|
|
11
|
+
"description": "The mutation engine of the systemfsoftware Stryker fork",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
@@ -38,9 +38,6 @@
|
|
|
38
38
|
"./package.json": "./package.json"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@stryker-mutator/api": "^9.6.1",
|
|
42
|
-
"@stryker-mutator/instrumenter": "^9.6.1",
|
|
43
|
-
"@stryker-mutator/util": "^9.6.1",
|
|
44
41
|
"diff-match-patch": "1.0.5",
|
|
45
42
|
"execa": "~9.6.0",
|
|
46
43
|
"lodash.groupby": "~4.6.0",
|
|
@@ -54,13 +51,15 @@
|
|
|
54
51
|
"tree-kill": "~1.2.2",
|
|
55
52
|
"tslib": "~2.8.0",
|
|
56
53
|
"typed-inject": "~5.0.0",
|
|
57
|
-
"@systemfsoftware/stryker-js-
|
|
54
|
+
"@systemfsoftware/stryker-js-instrumenter": "^0.1.0",
|
|
55
|
+
"@systemfsoftware/stryker-js-plugin-api": "^2.1.0",
|
|
56
|
+
"@systemfsoftware/stryker-js-util": "^0.1.0"
|
|
58
57
|
},
|
|
59
58
|
"peerDependencies": {
|
|
60
|
-
"effect": "4.0.0-rc.
|
|
59
|
+
"effect": "^4.0.0-rc.111"
|
|
61
60
|
},
|
|
62
61
|
"devDependencies": {
|
|
63
|
-
"@effect/vitest": "4.0.0-rc.
|
|
62
|
+
"@effect/vitest": "4.0.0-rc.111",
|
|
64
63
|
"@microsoft/api-extractor": "^7.58.7",
|
|
65
64
|
"@std/jsonc": "npm:@jsr/std__jsonc@^1.0.2",
|
|
66
65
|
"@systemfsoftware/arethetypeswrong-cli": "^1.1.1",
|
|
@@ -69,17 +68,17 @@
|
|
|
69
68
|
"@types/lodash.groupby": "4.6.9",
|
|
70
69
|
"@types/node": "^24",
|
|
71
70
|
"@types/semver": "7.7.1",
|
|
72
|
-
"effect": "4.0.0-rc.
|
|
71
|
+
"effect": "4.0.0-rc.111",
|
|
73
72
|
"glob": "13.0.6",
|
|
74
73
|
"oxlint": "^1.77.0",
|
|
75
74
|
"tsdown": "^0.22.14",
|
|
76
75
|
"typescript": "^7",
|
|
77
76
|
"vitest": "^4.1.10",
|
|
78
|
-
"@systemfsoftware/effect-gherkin-spec": "^1.0.0",
|
|
79
77
|
"@systemfsoftware/oxlint-config": "^0.1.0",
|
|
80
|
-
"@systemfsoftware/stryker-js-vitest-runner": "^0.1.
|
|
78
|
+
"@systemfsoftware/stryker-js-vitest-runner": "^0.1.3",
|
|
81
79
|
"@systemfsoftware/vitest-config": "^0.1.0",
|
|
82
|
-
"@systemfsoftware/tsconfig": "^1.3.
|
|
80
|
+
"@systemfsoftware/tsconfig": "^1.3.3",
|
|
81
|
+
"@systemfsoftware/effect-gherkin-spec": "^2.0.1"
|
|
83
82
|
},
|
|
84
83
|
"engines": {
|
|
85
84
|
"node": ">=20.0.0"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"type": "string"
|
|
10
10
|
},
|
|
11
11
|
"vitest": {
|
|
12
|
-
"description": "Configuration for @stryker-
|
|
12
|
+
"description": "Configuration for @systemfsoftware/stryker-js-vitest-runner",
|
|
13
13
|
"title": "VitestRunnerOptions",
|
|
14
14
|
"additionalProperties": false,
|
|
15
15
|
"type": "object",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"typescriptChecker": {
|
|
34
|
-
"description": "Configuration for @stryker-
|
|
34
|
+
"description": "Configuration for @systemfsoftware/stryker-js-typescript-checker. Set to null to omit the typescript checker entirely.",
|
|
35
35
|
"title": "TypescriptCheckerOptions",
|
|
36
36
|
"additionalProperties": false,
|
|
37
37
|
"oneOf": [
|
|
@@ -437,13 +437,13 @@
|
|
|
437
437
|
"description": "The package manager Stryker can use to install missing dependencies."
|
|
438
438
|
},
|
|
439
439
|
"plugins": {
|
|
440
|
-
"description": "With 'plugins', you can add additional Node modules for Stryker to load (or require). By default, all node_modules starting with @stryker-
|
|
440
|
+
"description": "With 'plugins', you can add additional Node modules for Stryker to load (or require). By default, all node_modules starting with @systemfsoftware/stryker-js-* will be loaded, so you would normally not need to specify this option. These modules should be installed right next to stryker.",
|
|
441
441
|
"type": "array",
|
|
442
442
|
"items": {
|
|
443
443
|
"type": "string"
|
|
444
444
|
},
|
|
445
445
|
"default": [
|
|
446
|
-
"@stryker-
|
|
446
|
+
"@systemfsoftware/stryker-js-*"
|
|
447
447
|
]
|
|
448
448
|
},
|
|
449
449
|
"appendPlugins": {
|
|
@@ -552,7 +552,7 @@
|
|
|
552
552
|
"default": 5
|
|
553
553
|
},
|
|
554
554
|
"tsconfigFile": {
|
|
555
|
-
"description": "Configure the (root) tsconfig file for typescript projects. This will allow Stryker to rewrite the `extends` and `references` settings in this and related tsconfig files in your sandbox. Defaults to `tsconfig.json`. This setting is also used when you enable the `@stryker-
|
|
555
|
+
"description": "Configure the (root) tsconfig file for typescript projects. This will allow Stryker to rewrite the `extends` and `references` settings in this and related tsconfig files in your sandbox. Defaults to `tsconfig.json`. This setting is also used when you enable the `@systemfsoftware/stryker-js-typescript-checker` plugin",
|
|
556
556
|
"type": "string",
|
|
557
557
|
"default": "tsconfig.json"
|
|
558
558
|
},
|
|
@@ -573,21 +573,7 @@
|
|
|
573
573
|
"type": "boolean",
|
|
574
574
|
"default": false
|
|
575
575
|
},
|
|
576
|
-
|
|
577
|
-
"description": "Fail the run when a test file whose name ends with one of these suffixes kills no mutant that another test file does not also kill. Such a file could be deleted without leaving a single mutant alive, so a passing mutation score is no evidence it earns its place. Set to null to disable the check. The gate only applies to file classes the mutation operators can express — workflow, policy, and kernel property tests today; schema refusal tests are gated by refutation adequacy rather than by mutation.",
|
|
578
|
-
"type": [
|
|
579
|
-
"array",
|
|
580
|
-
"null"
|
|
581
|
-
],
|
|
582
|
-
"items": {
|
|
583
|
-
"type": "string"
|
|
584
|
-
},
|
|
585
|
-
"default": [
|
|
586
|
-
".workflow.property.test.ts",
|
|
587
|
-
".policy.property.test.ts",
|
|
588
|
-
".kernel.property.test.ts"
|
|
589
|
-
]
|
|
590
|
-
},
|
|
576
|
+
|
|
591
577
|
"allowEmpty": {
|
|
592
578
|
"description": "Allows stryker to exit without any errors in cases where no tests are found",
|
|
593
579
|
"type": "boolean",
|