@systemfsoftware/stryker-js-mutation-run 3.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -1
  3. package/dist/{checker-C4tGiXtZ.mjs → checker-C0Vh51o8.mjs} +3 -3
  4. package/dist/{checker-resource-DTYEKXdg.d.mts → checker-resource-LZCpsbpn.d.mts} +1 -1
  5. package/dist/checker-worker.d.mts +2 -2
  6. package/dist/checker-worker.mjs +1 -1
  7. package/dist/{child-process-proxy-worker-lx1M5Mz3.mjs → child-process-proxy-worker-CmAwSjYi.mjs} +2 -2
  8. package/dist/child-process-proxy-worker-main.mjs +1 -1
  9. package/dist/{child-process-test-runner-worker-BTWRYb8V.mjs → child-process-test-runner-worker-DPXWNPqZ.mjs} +2 -2
  10. package/dist/child-process-test-runner-worker.d.mts +1 -1
  11. package/dist/child-process-test-runner-worker.mjs +1 -1
  12. package/dist/config/base.mjs +2 -1
  13. package/dist/config/config-resolution.d.mts +2 -2
  14. package/dist/config/config-resolution.mjs +1 -1
  15. package/dist/config/fork-schema.mjs +1 -1
  16. package/dist/{errors-BkjxkY_S.d.mts → errors-B8Vx4QIQ.d.mts} +1 -1
  17. package/dist/errors.d.mts +1 -1
  18. package/dist/errors.mjs +1 -1
  19. package/dist/{fork-schema-BHuTbRBR.mjs → fork-schema-K8Ab5szC.mjs} +1 -14
  20. package/dist/{incremental-differ-DY8AA09Q.mjs → incremental-differ-DAz2XlBK.mjs} +2 -2
  21. package/dist/{incremental-differ-DDVwtXBn.d.mts → incremental-differ-DtBnr6fJ.d.mts} +1 -1
  22. package/dist/{index-CyOSz6LC.d.mts → index-C19nE7Ww.d.mts} +23 -4
  23. package/dist/index.d.mts +6 -6
  24. package/dist/index.mjs +20 -33
  25. package/dist/mutants/incremental-differ.d.mts +1 -1
  26. package/dist/mutants/incremental-differ.mjs +1 -1
  27. package/dist/{plugins-Nx86IJQW.mjs → plugins-Bdy2g7fR.mjs} +69 -24
  28. package/dist/plugins.d.mts +1 -1
  29. package/dist/plugins.mjs +1 -1
  30. package/dist/{run-event-CzRz9Gtk.d.mts → run-event-Dx_Q5jrc.d.mts} +1 -1
  31. package/dist/run-event.d.mts +1 -1
  32. package/dist/{stryker-package-CTcsIZ4S.mjs → stryker-package-JIsmRMux.mjs} +1 -1
  33. package/dist/stryker-package.d.mts +1 -1
  34. package/dist/stryker-package.mjs +1 -1
  35. package/dist/{verdict-envelope-CKLZPjxC.d.mts → verdict-envelope-BJz-h7K-.d.mts} +2 -10
  36. package/dist/verdict-envelope.d.mts +1 -1
  37. package/dist/verdict-envelope.mjs +135 -1
  38. package/package.json +11 -12
  39. package/schema/stryker-schema.json +6 -20
  40. package/dist/verdict-envelope-BWc61mHm.mjs +0 -229
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @systemfsoftware/stryker-js-mutation-run
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - The engine no longer judges test contribution, and the verdict envelope no longer includes a `testContribution` field. Install the companion plugin and list it in `plugins` if you want that check. Configs that extend this package's base preset now run the gate for workflow, policy, and kernel property tests; remove the plugin from `plugins` to turn it off.
8
+
9
+ ### Minor Changes
10
+
11
+ - Declarations that were never meant to be hidden are public again.
12
+
13
+ - The package now publishes a `test-contribution` entry. Import it to call the functions that decide whether a test file earned its keep on a mutation run.
14
+
15
+ ### Patch Changes
16
+
17
+ - The peer requirements for `effect` and for the Effect test-runner integration now accept any compatible `4.0.0-rc` release, instead of demanding one exact release candidate.
18
+
19
+ Installing alongside a newer release candidate no longer reports an unmet peer dependency or resolves a second copy of `effect` into the dependency tree.
20
+
3
21
  ## 3.0.0
4
22
 
5
23
  ### Major Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @systemfsoftware/stryker-js-mutation-run
2
2
 
3
- A source-code fork of @stryker-mutator/core
3
+ A source-code fork of the upstream StrykerJS core package
4
4
 
5
5
  ## Install
6
6
 
@@ -1,9 +1,9 @@
1
- import { F as pluginModulePaths, O as loggingServerAddress, P as pluginCreator, Q as testRunnerFactory, Z as testRunnerConcurrencyTokens, h as checkerFactory, it as workerIdGenerator, m as checkerConcurrencyTokens, s as kill } from "./plugins-Nx86IJQW.mjs";
2
- import { i as WorkerMessageSchema, n as ProtocolEncodeError, r as ParentMessageSchema } from "./child-process-proxy-worker-lx1M5Mz3.mjs";
1
+ import { F as pluginModulePaths, O as loggingServerAddress, P as pluginCreator, Q as testRunnerFactory, Z as testRunnerConcurrencyTokens, h as checkerFactory, it as workerIdGenerator, m as checkerConcurrencyTokens, s as kill } from "./plugins-Bdy2g7fR.mjs";
2
+ import { i as WorkerMessageSchema, n as ProtocolEncodeError, r as ParentMessageSchema } from "./child-process-proxy-worker-CmAwSjYi.mjs";
3
3
  import "@systemfsoftware/stryker-js-plugin-api/core";
4
4
  import { PluginKind, commonTokens, tokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
5
5
  import { tokens as tokens$1 } from "typed-inject";
6
- import { ExpirableTask, StrykerError, Task, isErrnoException, notEmpty } from "@stryker-mutator/util";
6
+ import { ExpirableTask, StrykerError, Task, isErrnoException, notEmpty } from "@systemfsoftware/stryker-js-util";
7
7
  import { URL, fileURLToPath } from "url";
8
8
  import "@systemfsoftware/stryker-js-plugin-api/logging";
9
9
  import os, { EOL } from "os";
@@ -1,4 +1,4 @@
1
- import "./index-CyOSz6LC.mjs";
1
+ import "./index-C19nE7Ww.mjs";
2
2
  import { FileDescriptions, Mutant, PartialStrykerOptions, StrykerOptions } from "@systemfsoftware/stryker-js-plugin-api/core";
3
3
  import "@systemfsoftware/stryker-js-plugin-api/plugin";
4
4
  import { Disposable, Injector as Injector$1 } from "typed-inject";
@@ -1,5 +1,5 @@
1
- import { r as PluginCreator } from "./index-CyOSz6LC.mjs";
2
- import { t as CheckerResource } from "./checker-resource-DTYEKXdg.mjs";
1
+ import { r as PluginCreator } from "./index-C19nE7Ww.mjs";
2
+ import { t as CheckerResource } from "./checker-resource-LZCpsbpn.mjs";
3
3
  import { Mutant, StrykerOptions } from "@systemfsoftware/stryker-js-plugin-api/core";
4
4
  import { CheckResult } from "@systemfsoftware/stryker-js-plugin-api/check";
5
5
  //#region src/checker/checker-worker.d.ts
@@ -1,2 +1,2 @@
1
- import { n as CheckerWorker } from "./checker-C4tGiXtZ.mjs";
1
+ import { n as CheckerWorker } from "./checker-C0Vh51o8.mjs";
2
2
  export { CheckerWorker };
@@ -1,8 +1,8 @@
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";
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-Bdy2g7fR.mjs";
2
2
  import { StrykerOptionsSchema } from "@systemfsoftware/stryker-js-plugin-api/core";
3
3
  import { Scope, commonTokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
4
4
  import "typed-inject";
5
- import { errorToString } from "@stryker-mutator/util";
5
+ import { errorToString } from "@systemfsoftware/stryker-js-util";
6
6
  import fs from "fs";
7
7
  import util, { promisify } from "util";
8
8
  import path from "path";
@@ -1,4 +1,4 @@
1
- import { t as ChildProcessProxyWorker } from "./child-process-proxy-worker-lx1M5Mz3.mjs";
1
+ import { t as ChildProcessProxyWorker } from "./child-process-proxy-worker-CmAwSjYi.mjs";
2
2
  import { createInjector } from "typed-inject";
3
3
  //#region src/worker-pool/child-process-proxy-worker-main.ts
4
4
  new ChildProcessProxyWorker(createInjector);
@@ -1,7 +1,7 @@
1
- import { P as pluginCreator } from "./plugins-Nx86IJQW.mjs";
1
+ import { P as pluginCreator } from "./plugins-Bdy2g7fR.mjs";
2
2
  import "@systemfsoftware/stryker-js-plugin-api/core";
3
3
  import { PluginKind, commonTokens, tokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
4
- import { errorToString } from "@stryker-mutator/util";
4
+ import { errorToString } from "@systemfsoftware/stryker-js-util";
5
5
  import * as S from "effect/Schema";
6
6
  import { DryRunStatus, MutantRunStatus } from "@systemfsoftware/stryker-js-plugin-api/test-runner";
7
7
  //#region src/test-runner/mutant-coverage.schema.ts
@@ -1,4 +1,4 @@
1
- import { r as PluginCreator } from "./index-CyOSz6LC.mjs";
1
+ import { r as PluginCreator } from "./index-C19nE7Ww.mjs";
2
2
  import { StrykerOptions } from "@systemfsoftware/stryker-js-plugin-api/core";
3
3
  import { DryRunOptions, DryRunResult, MutantRunOptions, MutantRunResult, TestRunner, TestRunnerCapabilities } from "@systemfsoftware/stryker-js-plugin-api/test-runner";
4
4
  //#region src/test-runner/child-process-test-runner-worker.d.ts
@@ -1,2 +1,2 @@
1
- import { t as ChildProcessTestRunnerWorker } from "./child-process-test-runner-worker-BTWRYb8V.mjs";
1
+ import { t as ChildProcessTestRunnerWorker } from "./child-process-test-runner-worker-DPXWNPqZ.mjs";
2
2
  export { ChildProcessTestRunnerWorker };
@@ -15,7 +15,8 @@ const basePreset = {
15
15
  "@systemfsoftware/stryker-js-vitest-runner",
16
16
  "@systemfsoftware/stryker-js-typescript-checker",
17
17
  "@systemfsoftware/stryker-plugins/effect-schema-ignorer",
18
- "@systemfsoftware/stryker-plugins/workflow-make-ignorer"
18
+ "@systemfsoftware/stryker-plugins/workflow-make-ignorer",
19
+ "@systemfsoftware/stryker-test-contribution"
19
20
  ],
20
21
  reporters: [
21
22
  "progress",
@@ -1,6 +1,6 @@
1
- import "../errors-BkjxkY_S.mjs";
1
+ import "../errors-B8Vx4QIQ.mjs";
2
2
  import { PartialStrykerOptions, StrykerOptions } from "@systemfsoftware/stryker-js-plugin-api/core";
3
- import { I, Immutable } from "@stryker-mutator/util";
3
+ import { I, Immutable } from "@systemfsoftware/stryker-js-util";
4
4
  import { Logger } from "@systemfsoftware/stryker-js-plugin-api/logging";
5
5
  //#region src/config/options-validator.d.ts
6
6
  /**
@@ -1,2 +1,2 @@
1
- import { d as ConfigReader, n as OptionsValidator, r as defaultOptions } from "../plugins-Nx86IJQW.mjs";
1
+ import { d as ConfigReader, n as OptionsValidator, r as defaultOptions } from "../plugins-Bdy2g7fR.mjs";
2
2
  export { ConfigReader, OptionsValidator, defaultOptions };
@@ -1,2 +1,2 @@
1
- import { t as forkCoreSchema } from "../fork-schema-BHuTbRBR.mjs";
1
+ import { t as forkCoreSchema } from "../fork-schema-K8Ab5szC.mjs";
2
2
  export { forkCoreSchema };
@@ -1,4 +1,4 @@
1
- import { StrykerError } from "@stryker-mutator/util";
1
+ import { StrykerError } from "@systemfsoftware/stryker-js-util";
2
2
  //#region src/errors.d.ts
3
3
  declare class ConfigError extends StrykerError {}
4
4
  declare function retrieveCause(error: unknown): unknown;
package/dist/errors.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as retrieveCause, t as ConfigError } from "./errors-BkjxkY_S.mjs";
1
+ import { n as retrieveCause, t as ConfigError } from "./errors-B8Vx4QIQ.mjs";
2
2
  export { ConfigError, retrieveCause };
package/dist/errors.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { InjectionError } from "typed-inject";
2
- import { StrykerError } from "@stryker-mutator/util";
2
+ import { StrykerError } from "@systemfsoftware/stryker-js-util";
3
3
  //#region src/errors.ts
4
4
  var ConfigError = class extends StrykerError {};
5
5
  function retrieveCause(error) {
@@ -1,5 +1,5 @@
1
1
  import { StrykerOptionsSchema } from "@systemfsoftware/stryker-js-plugin-api/core";
2
- import { Effect } from "effect";
2
+ import "effect";
3
3
  import * as S from "effect/Schema";
4
4
  //#region src/config/removed-surface.ts
5
5
  /**
@@ -26,18 +26,6 @@ const REMOVED_OPTIONS = {
26
26
  };
27
27
  //#endregion
28
28
  //#region src/config/fork-schema.schema.ts
29
- const requireTestContribution = S.NullOr(S.Array(S.String)).pipe(S.annotate({
30
- default: [
31
- ".workflow.property.test.ts",
32
- ".policy.property.test.ts",
33
- ".kernel.property.test.ts"
34
- ],
35
- 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."
36
- }), S.withDecodingDefaultKey(Effect.succeed([
37
- ".workflow.property.test.ts",
38
- ".policy.property.test.ts",
39
- ".kernel.property.test.ts"
40
- ])));
41
29
  const survivorsPriorReport = S.optionalKey(S.String.pipe(S.annotate({ description: "The path of the prior mutation report a --survivors run admits against and re-tests the survivors of. Defaults to \"reports/mutation-report.json\" when unset. Deliberately has no default: a default would be injected into every resolved options object and written into every report, poisoning the KTD7 marker that identifies a report produced by a survivors run." })));
42
30
  const extendsPropertySchema = S.optionalKey(S.String.pipe(S.annotate({ description: "Path to another stryker config file whose options merge underneath this one. Resolved relative to this file. A child scalar or array replaces the inherited value; a child object merges one level deep; a child key set to null deletes the inherited key. Inheritance chains are not rewritten, so an inherited relative path value still resolves against the working directory of the run that reads it." })));
43
31
  /**
@@ -50,7 +38,6 @@ const extendsPropertySchema = S.optionalKey(S.String.pipe(S.annotate({ descripti
50
38
  */
51
39
  const forkOptionsSchema = S.StructWithRest(S.Struct({
52
40
  ...Object.fromEntries(Object.entries(StrykerOptionsSchema.schema.fields).filter(([name]) => !Object.hasOwn(REMOVED_OPTIONS, name))),
53
- requireTestContribution,
54
41
  survivorsPriorReport,
55
42
  extends: extendsPropertySchema
56
43
  }), [S.Record(S.String, S.Unknown)]);
@@ -1,7 +1,7 @@
1
- import { b as dryRunResult } from "./plugins-Nx86IJQW.mjs";
1
+ import { b as dryRunResult } from "./plugins-Bdy2g7fR.mjs";
2
2
  import "@systemfsoftware/stryker-js-plugin-api/core";
3
3
  import { commonTokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
4
- import { normalizeFileName, normalizeLineEndings, notEmpty } from "@stryker-mutator/util";
4
+ import { normalizeFileName, normalizeLineEndings, notEmpty } from "@systemfsoftware/stryker-js-util";
5
5
  import path from "path";
6
6
  import "@systemfsoftware/stryker-js-plugin-api/logging";
7
7
  import "mutation-testing-report-schema";
@@ -1,5 +1,5 @@
1
1
  import { CoverageData, FileDescriptions, Mutant, StrykerOptions, schema } from "@systemfsoftware/stryker-js-plugin-api/core";
2
- import { I } from "@stryker-mutator/util";
2
+ import { I } from "@systemfsoftware/stryker-js-util";
3
3
  import { Logger } from "@systemfsoftware/stryker-js-plugin-api/logging";
4
4
  import { CompleteDryRunResult, TestResult } from "@systemfsoftware/stryker-js-plugin-api/test-runner";
5
5
  //#region src/mutants/diff-statistics-collector.d.ts
@@ -58,6 +58,8 @@ declare class PluginCreator {
58
58
  static readonly inject: ["pluginsByKind", "$injector"];
59
59
  constructor(pluginsByKind: Map<PluginKind, Plugin<PluginKind>[]>, injector: Injector<PluginContext>);
60
60
  create<TPlugin extends keyof Plugins>(kind: TPlugin, name: string): PluginInterfaces[TPlugin];
61
+ createAll<TPlugin extends keyof Plugins>(kind: TPlugin): PluginInterfaces[TPlugin][];
62
+ private instantiate;
61
63
  private findPlugin;
62
64
  }
63
65
  //#endregion
@@ -89,15 +91,32 @@ declare class PluginLoader {
89
91
  /**
90
92
  * Loads plugins based on configured plugin descriptors.
91
93
  * A plugin descriptor can be:
92
- * * A full url: "file:///home/nicojs/github/my-plugin.js"
93
- * * An absolute file path: "/home/nicojs/github/my-plugin.js"
94
+ * * A full url: "file:///home/user/github/my-plugin.js"
95
+ * * An absolute file path: "/home/user/github/my-plugin.js"
94
96
  * * A relative path: "./my-plugin.js"
95
- * * A bare import expression: "@stryker-mutator/karma-runner"
96
- * * A simple glob expression (only wild cards are supported): "@stryker-mutator/*"
97
+ * * A bare import expression: "@systemfsoftware/stryker-js-vitest-runner"
98
+ * * A simple glob expression (only wild cards are supported): "@systemfsoftware/stryker-js-*"
97
99
  */
98
100
  load(pluginDescriptors: readonly string[]): Promise<LoadedPlugins>;
99
101
  private resolvePluginModules;
100
102
  private globPluginModules;
103
+ /**
104
+ * Reads the org's package names from every install directory above this
105
+ * module, unioned.
106
+ *
107
+ * Never a fixed number of `..` segments: `dist/` and `src/plugins/` sit at
108
+ * different depths, so a numeric walk resolves to a different directory
109
+ * depending on which one is running — and a wrong directory yields zero
110
+ * plugins rather than an error, so the mistake ships as a silent 100% score.
111
+ *
112
+ * Never the first directory that happens to be populated either: under a
113
+ * pnpm-isolated install this package's own virtual `node_modules` holds only
114
+ * its own dependencies, and stopping there hides every sibling plugin the
115
+ * project installed one level up. The union spans both, so discovery reaches
116
+ * as far as a hoisted layout does. A missing org directory contributes
117
+ * nothing; it is not an error.
118
+ */
119
+ private readOrgDirectory;
101
120
  private loadPlugin;
102
121
  }
103
122
  //#endregion
package/dist/index.d.mts CHANGED
@@ -1,15 +1,15 @@
1
- import { c as progressEnabled, d as resolvedMode, f as runEventSink, i as clearTextEnabled, l as reporterOverride, m as runStartedAt, o as loggingServerAddress, p as runId, s as loggingSink, u as reporterPluginModules } from "./index-CyOSz6LC.mjs";
2
- import { n as LoggingServerAddress, r as LoggingBackend } from "./checker-resource-DTYEKXdg.mjs";
1
+ import { c as progressEnabled, d as resolvedMode, f as runEventSink, i as clearTextEnabled, l as reporterOverride, m as runStartedAt, o as loggingServerAddress, p as runId, s as loggingSink, u as reporterPluginModules } from "./index-C19nE7Ww.mjs";
2
+ import { n as LoggingServerAddress, r as LoggingBackend } from "./checker-resource-LZCpsbpn.mjs";
3
3
  import { r as ResolvedMode } from "./output-mode-CfGHDpi5.mjs";
4
- import { c as RunEventSink } from "./run-event-CzRz9Gtk.mjs";
5
- import "./incremental-differ-DDVwtXBn.mjs";
4
+ import { c as RunEventSink } from "./run-event-Dx_Q5jrc.mjs";
5
+ import "./incremental-differ-DtBnr6fJ.mjs";
6
6
  import { FileDescriptions, Mutant, MutantResult, MutantStatus, PartialStrykerOptions, StrykerOptions } from "@systemfsoftware/stryker-js-plugin-api/core";
7
7
  import { BaseContext, Injector, PluginContext, commonTokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
8
8
  import { Disposable, Injector as Injector$1, createInjector } from "typed-inject";
9
- import { I, requireResolve } from "@stryker-mutator/util";
9
+ import { I, requireResolve } from "@systemfsoftware/stryker-js-util";
10
10
  import { Logger } from "@systemfsoftware/stryker-js-plugin-api/logging";
11
11
  import "rxjs";
12
- import "@stryker-mutator/instrumenter";
12
+ import "@systemfsoftware/stryker-js-instrumenter";
13
13
  import "mutation-testing-report-schema";
14
14
  import { CompleteDryRunResult, MutantRunResult, TestRunner } from "@systemfsoftware/stryker-js-plugin-api/test-runner";
15
15
  import { execaCommand } from "execa";
package/dist/index.mjs CHANGED
@@ -1,18 +1,18 @@
1
1
  import { ConfigError, retrieveCause } from "./errors.mjs";
2
- import { $ as testRunnerPool, A as mutantTestPlanner, B as reporter, F as pluginModulePaths, G as runEventSink, H as reporterPluginModules, I as pluginsByKind, J as sandbox, K as runId, L as process$1, M as mutationTestReportHelper, N as optionsValidator, O as loggingServerAddress, P as pluginCreator, Q as testRunnerFactory, R as progressEnabled, S as incrementalDiffer, U as requireFromCwd, V as reporterOverride, W as resolvedMode, X as testCoverage, Y as temporaryDirectory, Z as testRunnerConcurrencyTokens, _ as clearTextEnabled, a as isWarningEnabled, b as dryRunResult, c as ProjectReader, d as ConfigReader, et as timeOverheadMS, f as FileSystem, g as checkerPool, h as checkerFactory, i as optionsPath, it as workerIdGenerator, j as mutants, k as loggingSink, l as MetaSchemaBuilder, m as checkerConcurrencyTokens, n as OptionsValidator, nt as unexpectedExitRegistry, o as CommandTestRunner, p as PluginCreator, q as runStartedAt, rt as validationSchema, t as PluginLoader, tt as timer, u as FileMatcher, v as concurrencyTokenProvider, x as execa, y as disableTypeChecksHelper, z as project } from "./plugins-Nx86IJQW.mjs";
2
+ import { $ as testRunnerPool, A as mutantTestPlanner, B as reporter, F as pluginModulePaths, G as runEventSink, H as reporterPluginModules, I as pluginsByKind, J as sandbox, K as runId, L as process$1, M as mutationTestReportHelper, N as optionsValidator, O as loggingServerAddress, P as pluginCreator, Q as testRunnerFactory, R as progressEnabled, S as incrementalDiffer, U as requireFromCwd, V as reporterOverride, W as resolvedMode, X as testCoverage, Y as temporaryDirectory, Z as testRunnerConcurrencyTokens, _ as clearTextEnabled, a as isWarningEnabled, b as dryRunResult, c as ProjectReader, d as ConfigReader, et as timeOverheadMS, f as FileSystem, g as checkerPool, h as checkerFactory, i as optionsPath, it as workerIdGenerator, j as mutants, k as loggingSink, l as MetaSchemaBuilder, m as checkerConcurrencyTokens, n as OptionsValidator, nt as unexpectedExitRegistry, o as CommandTestRunner, p as PluginCreator, q as runStartedAt, rt as validationSchema, t as PluginLoader, tt as timer, u as FileMatcher, v as concurrencyTokenProvider, x as execa, y as disableTypeChecksHelper, z as project } from "./plugins-Bdy2g7fR.mjs";
3
3
  import { Timer } from "./timer.mjs";
4
- import { t as forkCoreSchema } from "./fork-schema-BHuTbRBR.mjs";
5
- import { a as provideLogging, o as provideLoggingBackend } from "./child-process-proxy-worker-lx1M5Mz3.mjs";
6
- import { a as ChildProcessCrashedError, c as createTestRunnerPool, i as OutOfMemoryError, l as ConcurrencyTokenProvider, o as ResourceDecorator, r as ChildProcessProxy, s as createCheckerPool, t as createCheckerFactory } from "./checker-C4tGiXtZ.mjs";
7
- import { n as toRelativeNormalizedFileName, r as TestCoverage, t as IncrementalDiffer } from "./incremental-differ-DY8AA09Q.mjs";
4
+ import { t as forkCoreSchema } from "./fork-schema-K8Ab5szC.mjs";
5
+ import { a as provideLogging, o as provideLoggingBackend } from "./child-process-proxy-worker-CmAwSjYi.mjs";
6
+ import { a as ChildProcessCrashedError, c as createTestRunnerPool, i as OutOfMemoryError, l as ConcurrencyTokenProvider, o as ResourceDecorator, r as ChildProcessProxy, s as createCheckerPool, t as createCheckerFactory } from "./checker-C0Vh51o8.mjs";
7
+ import { n as toRelativeNormalizedFileName, r as TestCoverage, t as IncrementalDiffer } from "./incremental-differ-DAz2XlBK.mjs";
8
8
  import { setPendingExitClass } from "./exit-classification.mjs";
9
- import { n as strykerVersion } from "./stryker-package-CTcsIZ4S.mjs";
10
- import { o as judgeTestContribution, r as buildVerdictEnvelope } from "./verdict-envelope-BWc61mHm.mjs";
11
- import { t as ChildProcessTestRunnerWorker } from "./child-process-test-runner-worker-BTWRYb8V.mjs";
9
+ import { n as strykerVersion } from "./stryker-package-JIsmRMux.mjs";
10
+ import { buildVerdictEnvelope } from "./verdict-envelope.mjs";
11
+ import { t as ChildProcessTestRunnerWorker } from "./child-process-test-runner-worker-DPXWNPqZ.mjs";
12
12
  import { PlanKind } from "@systemfsoftware/stryker-js-plugin-api/core";
13
13
  import { PluginKind, commonTokens, tokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
14
14
  import { createInjector, tokens as tokens$1 } from "typed-inject";
15
- import { ExpirableTask, deepFreeze, errorToString, isErrnoException, normalizeFileName, normalizeWhitespaces, notEmpty, requireResolve, split } from "@stryker-mutator/util";
15
+ import { ExpirableTask, deepFreeze, errorToString, isErrnoException, normalizeFileName, normalizeWhitespaces, notEmpty, requireResolve, split } from "@systemfsoftware/stryker-js-util";
16
16
  import fs from "fs";
17
17
  import path from "path";
18
18
  import { URL } from "url";
@@ -21,7 +21,7 @@ import "@systemfsoftware/stryker-js-plugin-api/logging";
21
21
  import { EOL } from "os";
22
22
  import * as S from "effect/Schema";
23
23
  import { EMPTY, bufferTime, concat, from, lastValueFrom, merge, mergeMap, of, partition } from "rxjs";
24
- import { createInstrumenter, disableTypeChecks, frameworkPluginsFileUrl } from "@stryker-mutator/instrumenter";
24
+ import { createInstrumenter, disableTypeChecks, frameworkPluginsFileUrl } from "@systemfsoftware/stryker-js-instrumenter";
25
25
  import { DryRunStatus, MutantRunStatus, TestStatus } from "@systemfsoftware/stryker-js-plugin-api/test-runner";
26
26
  import { execaCommand } from "execa";
27
27
  import "@systemfsoftware/stryker-js-plugin-api/report";
@@ -1004,7 +1004,7 @@ const STRYKER_FRAMEWORK = Object.freeze({
1004
1004
  name: "StrykerJS",
1005
1005
  version: strykerVersion,
1006
1006
  branding: {
1007
- homepageUrl: "https://stryker-mutator.io",
1007
+ homepageUrl: "https://github.com/systemfsoftware/systemfsoftware",
1008
1008
  imageUrl: "data:image/svg+xml;utf8,%3Csvg viewBox='0 0 1458 1458' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd' clip-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2'%3E%3Cpath fill='none' d='M0 0h1458v1458H0z'/%3E%3CclipPath id='a'%3E%3Cpath d='M0 0h1458v1458H0z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23a)'%3E%3Cpath d='M1458 729c0 402.655-326.345 729-729 729S0 1131.655 0 729C0 326.445 326.345 0 729 0s729 326.345 729 729' fill='%23e74c3c' fill-rule='nonzero'/%3E%3Cpath d='M778.349 1456.15L576.6 1254.401l233-105 85-78.668v-64.332l-257-257-44-187-50-208 251.806-82.793L1076.6 389.401l380.14 379.15c-19.681 367.728-311.914 663.049-678.391 687.599z' fill-opacity='.3'/%3E%3Cpath d='M753.4 329.503c41.79 0 74.579 7.83 97.925 25.444 23.571 18.015 41.69 43.956 55.167 77.097l11.662 28.679 165.733-58.183-14.137-32.13c-26.688-60.655-64.896-108.61-114.191-144.011-49.329-35.423-117.458-54.302-204.859-54.302-50.78 0-95.646 7.376-134.767 21.542-40.093 14.671-74.09 34.79-102.239 60.259-28.84 26.207-50.646 57.06-65.496 92.701-14.718 35.052-22.101 72.538-22.101 112.401 0 72.536 20.667 133.294 61.165 182.704 38.624 47.255 98.346 88.037 179.861 121.291 42.257 17.475 78.715 33.125 109.227 46.994 27.193 12.361 49.294 26.124 66.157 41.751 15.309 14.186 26.497 30.584 33.63 49.258 7.721 20.214 11.16 45.69 11.16 76.402 0 28.021-4.251 51.787-13.591 71.219-8.832 18.374-20.171 33.178-34.523 44.219-14.787 11.374-31.193 19.591-49.393 24.466-19.68 5.359-39.14 7.993-58.69 7.993-29.359 0-54.387-3.407-75.182-10.747-20.112-7.013-37.144-16.144-51.259-27.486-13.618-11.009-24.971-23.766-33.744-38.279-9.64-15.8-17.272-31.924-23.032-48.408l-10.965-31.376-161.669 60.585 10.734 30.124c10.191 28.601 24.197 56.228 42.059 82.748 18.208 27.144 41.322 51.369 69.525 72.745 27.695 21.075 60.904 38.218 99.481 51.041 37.777 12.664 82.004 19.159 132.552 19.159 49.998 0 95.818-8.321 137.611-24.622 42.228-16.471 78.436-38.992 108.835-67.291 30.719-28.597 54.631-62.103 71.834-100.642 17.263-38.56 25.923-79.392 25.923-122.248 0-54.339-8.368-100.37-24.208-138.32-16.29-38.759-38.252-71.661-65.948-98.797-26.965-26.418-58.269-48.835-93.858-67.175-33.655-17.241-69.196-33.11-106.593-47.533-35.934-13.429-65.822-26.601-89.948-39.525-22.153-11.868-40.009-24.21-53.547-37.309-11.429-11.13-19.83-23.678-24.718-37.664-5.413-15.49-7.98-33.423-7.98-53.577 0-40.883 11.293-71.522 37.086-90.539 28.443-20.825 64.985-30.658 109.311-30.658z' fill='%23f1c40f' fill-rule='nonzero'/%3E%3Cpath d='M720 0h18v113h-18zM1458 738v-18h-113v18h113zM720 1345h18v113h-18zM113 738v-18H0v18h113z'/%3E%3C/g%3E%3C/svg%3E"
1009
1009
  }
1010
1010
  });
@@ -1044,8 +1044,9 @@ var MutationTestReportHelper = class {
1044
1044
  runEventSink;
1045
1045
  runId;
1046
1046
  resolvedMode;
1047
- static inject = tokens(reporter, commonTokens.options, project, commonTokens.logger, testCoverage, "fs", requireFromCwd, runEventSink, runId, resolvedMode);
1048
- constructor(reporter, options, project, log, testCoverage, fs, requireFromCwd, runEventSink, runId, resolvedMode) {
1047
+ pluginCreator;
1048
+ static inject = tokens(reporter, commonTokens.options, project, commonTokens.logger, testCoverage, "fs", requireFromCwd, runEventSink, runId, resolvedMode, pluginCreator);
1049
+ constructor(reporter, options, project, log, testCoverage, fs, requireFromCwd, runEventSink, runId, resolvedMode, pluginCreator) {
1049
1050
  this.reporter = reporter;
1050
1051
  this.options = options;
1051
1052
  this.project = project;
@@ -1056,6 +1057,7 @@ var MutationTestReportHelper = class {
1056
1057
  this.runEventSink = runEventSink;
1057
1058
  this.runId = runId;
1058
1059
  this.resolvedMode = resolvedMode;
1060
+ this.pluginCreator = pluginCreator;
1059
1061
  }
1060
1062
  reportCheckFailed(mutant, checkResult) {
1061
1063
  const location = toSchemaLocation(mutant.location);
@@ -1123,7 +1125,7 @@ var MutationTestReportHelper = class {
1123
1125
  await this.fs.writeFile(this.options.incrementalFile, JSON.stringify(report, null, 2), "utf-8");
1124
1126
  }
1125
1127
  this.determineExitCode(metrics);
1126
- this.determineTestContribution(report);
1128
+ for (const evaluator of this.pluginCreator.createAll(PluginKind.Evaluator)) evaluator.evaluate(report);
1127
1129
  this.emitVerdict(report);
1128
1130
  }
1129
1131
  /**
@@ -1139,20 +1141,6 @@ var MutationTestReportHelper = class {
1139
1141
  ...buildVerdictEnvelope(report, this.resolvedMode.mode, this.resolvedMode.signal, this.runId)
1140
1142
  });
1141
1143
  }
1142
- determineTestContribution(report) {
1143
- const verdict = judgeTestContribution(report, this.options["requireTestContribution"], this.options.disableBail);
1144
- if (verdict === void 0) {
1145
- this.log.debug("No test contribution required. Won't fail the build for a test file that kills nothing another test file does not also kill. Set `requireTestContribution` to change this behavior.");
1146
- return;
1147
- }
1148
- if (!verdict.failed) {
1149
- this.log.info(verdict.message);
1150
- return;
1151
- }
1152
- this.log.error(`${verdict.message}\nSetting exit code to 1 (failure).`);
1153
- this.log.info("(sharpen or delete them, or set `requireTestContribution = null` to prevent this error in the future)");
1154
- setPendingExitClass(1);
1155
- }
1156
1144
  determineExitCode(metrics) {
1157
1145
  const { mutationScore } = metrics.systemUnderTestMetrics.metrics;
1158
1146
  const breaking = this.options.thresholds.break;
@@ -1272,11 +1260,10 @@ var MutationTestReportHelper = class {
1272
1260
  }
1273
1261
  };
1274
1262
  return [
1275
- "@stryker-mutator/mocha-runner",
1276
- "@stryker-mutator/karma-runner",
1277
- "@stryker-mutator/jasmine-runner",
1278
- "@stryker-mutator/jest-runner",
1279
- "@stryker-mutator/typescript-checker",
1263
+ "@systemfsoftware/stryker-js-vitest-runner",
1264
+ "@systemfsoftware/stryker-js-typescript-checker",
1265
+ "@systemfsoftware/stryker-plugins",
1266
+ "vitest",
1280
1267
  "karma",
1281
1268
  "karma-chai",
1282
1269
  "karma-chrome-launcher",
@@ -1,2 +1,2 @@
1
- import { n as toRelativeNormalizedFileName, t as IncrementalDiffer } from "../incremental-differ-DDVwtXBn.mjs";
1
+ import { n as toRelativeNormalizedFileName, t as IncrementalDiffer } from "../incremental-differ-DtBnr6fJ.mjs";
2
2
  export { IncrementalDiffer, toRelativeNormalizedFileName };
@@ -1,2 +1,2 @@
1
- import { n as toRelativeNormalizedFileName, t as IncrementalDiffer } from "../incremental-differ-DY8AA09Q.mjs";
1
+ import { n as toRelativeNormalizedFileName, t as IncrementalDiffer } from "../incremental-differ-DAz2XlBK.mjs";
2
2
  export { IncrementalDiffer, toRelativeNormalizedFileName };
@@ -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-BHuTbRBR.mjs";
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-mutator/util";
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 { URL, fileURLToPath, pathToFileURL } from "url";
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-mutator/instrumenter";
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
- const plugin = this.findPlugin(kind, name);
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(`Plugin "${kind}:${name}" could not be created, missing "factory", "injectableClass" or "value" property.`);
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
- _tag: "refused",
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
- _tag: "done",
426
+ ...DoneTag,
418
427
  options: mergeChainDocuments(nextState.documents)
419
428
  })), Match$1.when(null, () => ({
420
- _tag: "done",
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
- _tag: "resolve",
432
+ ...ResolveTag,
424
433
  specifier: extendValue,
425
434
  directory: path.dirname(file),
426
435
  state: nextState
427
436
  })), Match$1.when(false, () => ({
428
- _tag: "read",
437
+ ...ReadTag,
429
438
  path: path.resolve(path.dirname(file), extendValue),
430
439
  state: nextState
431
440
  })), Match$1.exhaustive)), Match$1.orElse(() => ({
432
- _tag: "refused",
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
- "core",
1512
- "api",
1513
- "util",
1514
- "instrumenter"
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/nicojs/github/my-plugin.js"
1529
- * * An absolute file path: "/home/nicojs/github/my-plugin.js"
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-mutator/karma-runner"
1532
- * * A simple glob expression (only wild cards are supported): "@stryker-mutator/*"
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.log.debug("Loading %s from %s", pluginExpression, pluginDirectory);
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-mutator/core' => { org: '@stryker-mutator', 'core' }
1642
+ * '@systemfsoftware/stryker-js-mutation-run' => { org: '@systemfsoftware', 'stryker-js-mutation-run' }
1598
1643
  * 'glob' => { org: '', 'glob' }
1599
1644
  */
1600
1645
  function parsePluginExpression(pluginExpression) {
@@ -1,2 +1,2 @@
1
- import { a as injection_tokens_d_exports, n as PluginLoader, r as PluginCreator, t as LoadedPlugins } from "./index-CyOSz6LC.mjs";
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-Nx86IJQW.mjs";
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-CKLZPjxC.mjs";
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
  /**
@@ -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-CzRz9Gtk.mjs";
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,4 +1,4 @@
1
- import { deepFreeze } from "@stryker-mutator/util";
1
+ import { deepFreeze } from "@systemfsoftware/stryker-js-util";
2
2
  import fs from "fs";
3
3
  import { URL, fileURLToPath } from "url";
4
4
  import * as S from "effect/Schema";
@@ -1,6 +1,6 @@
1
1
  //#region src/stryker-package.d.ts
2
2
  declare const strykerVersion: string;
3
- declare const strykerEngines: import("@stryker-mutator/util").ImmutableObject<{
3
+ declare const strykerEngines: import("@systemfsoftware/stryker-js-util").ImmutableObject<{
4
4
  readonly node: string;
5
5
  }>;
6
6
  //#endregion
@@ -1,2 +1,2 @@
1
- import { n as strykerVersion, t as strykerEngines } from "./stryker-package-CTcsIZ4S.mjs";
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. `testContribution` is `null` when the check is not configured.
70
- * `mutants` is bounded to `ACTIONABLE_STATUSES` (R20) see that definition
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-CKLZPjxC.mjs";
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 { a as isActionableStatus, i as generateRunId, n as VERDICT_ENVELOPE_SCHEMA_VERSION, r as buildVerdictEnvelope, t as ACTIONABLE_STATUSES } from "./verdict-envelope-BWc61mHm.mjs";
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@systemfsoftware/stryker-js-mutation-run",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
@@ -8,7 +8,7 @@
8
8
  },
9
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": "A source-code fork of @stryker-mutator/core",
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-plugin-api": "^2.0.0"
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.108"
59
+ "effect": "^4.0.0-rc.111"
61
60
  },
62
61
  "devDependencies": {
63
- "@effect/vitest": "4.0.0-rc.108",
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.108",
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/stryker-js-vitest-runner": "^0.1.2",
79
- "@systemfsoftware/effect-gherkin-spec": "^2.0.0",
77
+ "@systemfsoftware/oxlint-config": "^0.1.0",
78
+ "@systemfsoftware/stryker-js-vitest-runner": "^0.1.3",
80
79
  "@systemfsoftware/vitest-config": "^0.1.0",
81
80
  "@systemfsoftware/tsconfig": "^1.3.3",
82
- "@systemfsoftware/oxlint-config": "^0.1.0"
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-mutator/vitest-runner",
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-mutator/typescript-checker. Set to null to omit the typescript checker entirely.",
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-mutator/* will be loaded, so you would normally not need to specify this option. These modules should be installed right next to stryker. For a current list of plugins, you can consult 'npm' or 'stryker-mutator.io.'",
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-mutator/*"
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-mutator/typescript-checker plugin",
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
- "requireTestContribution": {
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",
@@ -1,229 +0,0 @@
1
- import "@systemfsoftware/stryker-js-plugin-api/core";
2
- import { normalizeFileName } from "@stryker-mutator/util";
3
- import path from "path";
4
- import { calculateMutationTestMetrics } from "mutation-testing-metrics";
5
- import { randomFillSync } from "node:crypto";
6
- //#region src/test-contribution.ts
7
- const KILLING_STATUSES = /* @__PURE__ */ new Set(["Killed", "Timeout"]);
8
- const testFileById = (testFiles) => {
9
- const byId = /* @__PURE__ */ new Map();
10
- for (const [fileName, testFile] of Object.entries(testFiles)) for (const test of testFile.tests) byId.set(test.id, fileName);
11
- return byId;
12
- };
13
- const killersOf = (killedBy, fileById) => new Set(killedBy.map((testId) => fileById.get(testId) ?? testId));
14
- const contributionByTestFile = (report) => {
15
- const testFiles = report.testFiles ?? {};
16
- const fileById = testFileById(testFiles);
17
- const soleKills = /* @__PURE__ */ new Map();
18
- const totalKills = /* @__PURE__ */ new Map();
19
- const unattributed = /* @__PURE__ */ new Set();
20
- for (const file of Object.values(report.files)) for (const mutant of file.mutants) {
21
- if (!KILLING_STATUSES.has(mutant.status)) continue;
22
- const killers = killersOf(mutant.killedBy ?? [], fileById);
23
- if (killers.size === 0) {
24
- const covered = mutant.coveredBy;
25
- if (covered !== void 0) for (const fileName of killersOf(covered, fileById)) unattributed.add(fileName);
26
- continue;
27
- }
28
- const soleKill = killers.size === 1;
29
- for (const fileName of killers) {
30
- totalKills.set(fileName, (totalKills.get(fileName) ?? 0) + 1);
31
- if (soleKill) soleKills.set(fileName, (soleKills.get(fileName) ?? 0) + 1);
32
- }
33
- }
34
- const byTestFile = /* @__PURE__ */ new Map();
35
- for (const fileName of Object.keys(testFiles)) byTestFile.set(fileName, {
36
- soleKills: soleKills.get(fileName) ?? 0,
37
- totalKills: totalKills.get(fileName) ?? 0,
38
- coversUnattributedKill: unattributed.has(fileName)
39
- });
40
- return byTestFile;
41
- };
42
- const toothlessTestFiles = (contribution, { suffixes, everyKillerRecorded }) => {
43
- const toothless = [];
44
- for (const [fileName, { soleKills, totalKills, coversUnattributedKill }] of contribution) {
45
- const defends = everyKillerRecorded ? soleKills > 0 : totalKills > 0;
46
- const inScope = suffixes.some((suffix) => fileName.endsWith(suffix));
47
- if (!defends && inScope && !coversUnattributedKill) toothless.push(fileName);
48
- }
49
- return toothless.sort();
50
- };
51
- const suffixesToRequire = (value) => {
52
- if (!Array.isArray(value)) return void 0;
53
- const suffixes = value.filter((entry) => typeof entry === "string");
54
- return suffixes.length === 0 ? void 0 : suffixes;
55
- };
56
- const PRECISION = "every killing test was recorded";
57
- const judgeTestContribution = (report, requireTestContribution, everyKillerRecorded) => {
58
- const suffixes = suffixesToRequire(requireTestContribution);
59
- if (suffixes === void 0) return void 0;
60
- const matches = suffixes.join(", ");
61
- const contribution = contributionByTestFile(report);
62
- const inScope = [...contribution.keys()].filter((fileName) => suffixes.some((suffix) => fileName.endsWith(suffix)));
63
- if (inScope.length === 0) return {
64
- failed: false,
65
- message: `No test file matching ${matches} ran, so none was judged.`
66
- };
67
- if (!everyKillerRecorded) return {
68
- failed: true,
69
- message: `This run used Stryker's bail mode, which stops each mutant at its first killing test. A test file's contribution therefore cannot be measured on this evidence. Set \`disableBail: true\` to record every killing test, or remove \`${matches}\` from \`requireTestContribution\` (set it to \`null\` to disable the check) to narrow the gate out of scope for this run.`
70
- };
71
- if (![...contribution.values()].some(({ totalKills }) => totalKills > 0)) return {
72
- failed: true,
73
- message: `This run credited no kill to any test file, so no test file's contribution to it can be measured. Until that is fixed the ${inScope.length} file(s) matching ${matches} are unjudged, not cleared.`
74
- };
75
- const toothless = toothlessTestFiles(contribution, {
76
- suffixes,
77
- everyKillerRecorded
78
- });
79
- if (toothless.length === 0) return {
80
- failed: false,
81
- message: `Every test file matching ${matches} kills a mutant nothing else kills (${PRECISION}).`
82
- };
83
- const listed = toothless.map((fileName) => ` - ${fileName}`).join("\n");
84
- return {
85
- failed: true,
86
- message: `Deleting these ${toothless.length} test file(s) would leave every mutant just as dead (${PRECISION}):\n${listed}`
87
- };
88
- };
89
- //#endregion
90
- //#region src/verdict-envelope.ts
91
- /**
92
- * U4 — the verdict envelope (R5, R11, R20): the single JSON document machine
93
- * mode prints to stdout at the end of a run. Everything an agent needs to
94
- * act without opening the report file, including the survivor re-run
95
- * matching key per actionable mutant (R20 bounds the list). All functions
96
- * here are pure over the report — no I/O side effects, no randomness except
97
- * inside `generateRunId`.
98
- */
99
- const VERDICT_ENVELOPE_SCHEMA_VERSION = "1.0";
100
- /**
101
- * The statuses a `verdict.mutants` entry (and a `mutant` stream line, U7) is
102
- * recorded for (R20). `Killed`, `Ignored`, and `CompileError` are reported
103
- * as counts only: the full per-mutant record stays in the report file, and
104
- * enumerating killed mutants served no consumer while pushing the terminal
105
- * line past the 64 KB limit of `bufio.Scanner`-class readers. Measured:
106
- * `oxlint-plugins/effect-workflow` produced a 2164-entry, ~440 KB line with
107
- * zero actionable entries. This is the single definition of the R20 filter,
108
- * shared with the progress stream.
109
- */
110
- const ACTIONABLE_STATUSES = [
111
- "Survived",
112
- "NoCoverage",
113
- "Timeout",
114
- "RuntimeError"
115
- ];
116
- /**
117
- * Whether `status` is actionable (R20) — one of `ACTIONABLE_STATUSES`.
118
- */
119
- function isActionableStatus(status) {
120
- return ACTIONABLE_STATUSES.some((actionable) => actionable === status);
121
- }
122
- const CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
123
- /**
124
- * A ULID-shaped run identifier: 48 bits of millisecond time followed by 80
125
- * random bits, Crockford base32-encoded into exactly 26 characters. The time
126
- * prefix keeps ids roughly sortable; the randomness makes collisions
127
- * negligible.
128
- */
129
- function generateRunId() {
130
- const bytes = /* @__PURE__ */ new Uint8Array(16);
131
- const now = Date.now();
132
- bytes[0] = now / 1099511627776 % 256;
133
- bytes[1] = now / 4294967296 % 256;
134
- bytes[2] = now / 16777216 % 256;
135
- bytes[3] = now / 65536 % 256;
136
- bytes[4] = now / 256 % 256;
137
- bytes[5] = now % 256;
138
- randomFillSync(bytes.subarray(6));
139
- let chars = "";
140
- let value = 0;
141
- let bits = 0;
142
- for (const byte of bytes) {
143
- value = value << 8 | byte;
144
- bits += 8;
145
- while (bits >= 5) {
146
- chars += CROCKFORD_BASE32[value >>> bits - 5 & 31];
147
- bits -= 5;
148
- value &= (1 << bits) - 1;
149
- }
150
- }
151
- if (bits > 0) chars += CROCKFORD_BASE32[value << 5 - bits & 31];
152
- return chars;
153
- }
154
- /**
155
- * The resolved options the report helper embeds as `report.config` (it writes
156
- * `config: this.options`). Read through `in`-narrowing because the report
157
- * schema types `config` as `{}`, while the embedded value is our own resolved
158
- * options with their index signature.
159
- */
160
- function embeddedConfig(report) {
161
- const config = report.config;
162
- let jsonReporterFileName;
163
- let requireTestContribution;
164
- let disableBail = false;
165
- if (typeof config === "object" && config !== null) {
166
- if ("jsonReporter" in config && typeof config.jsonReporter === "object" && config.jsonReporter !== null && "fileName" in config.jsonReporter && typeof config.jsonReporter.fileName === "string") jsonReporterFileName = config.jsonReporter.fileName;
167
- if ("requireTestContribution" in config) requireTestContribution = config.requireTestContribution;
168
- if ("disableBail" in config && typeof config.disableBail === "boolean") disableBail = config.disableBail;
169
- }
170
- return {
171
- jsonReporterFileName,
172
- requireTestContribution,
173
- disableBail
174
- };
175
- }
176
- function breakThreshold(thresholds) {
177
- if ("break" in thresholds) {
178
- const breakValue = thresholds.break;
179
- if (typeof breakValue === "number") return breakValue;
180
- if (breakValue === null) return null;
181
- }
182
- return null;
183
- }
184
- function buildVerdictEnvelope(report, mode, signal, runId) {
185
- const { jsonReporterFileName, requireTestContribution, disableBail } = embeddedConfig(report);
186
- const metrics = calculateMutationTestMetrics(report).systemUnderTestMetrics.metrics;
187
- const hasMutants = metrics.totalMutants > 0;
188
- const score = hasMutants && Number.isFinite(metrics.mutationScore) ? metrics.mutationScore : null;
189
- const reportFile = hasMutants && jsonReporterFileName !== void 0 ? normalizeFileName(path.relative(process.cwd(), jsonReporterFileName)) : null;
190
- const mutants = [];
191
- for (const [file, fileResult] of Object.entries(report.files)) for (const mutant of fileResult.mutants) {
192
- if (!isActionableStatus(mutant.status)) continue;
193
- mutants.push({
194
- id: mutant.id,
195
- file,
196
- location: mutant.location,
197
- mutator: mutant.mutatorName,
198
- replacement: mutant.replacement ?? null,
199
- status: mutant.status
200
- });
201
- }
202
- return {
203
- schemaVersion: "1.0",
204
- runId,
205
- mode,
206
- signal,
207
- score,
208
- thresholds: {
209
- high: report.thresholds.high,
210
- low: report.thresholds.low,
211
- break: breakThreshold(report.thresholds)
212
- },
213
- counts: {
214
- killed: metrics.killed,
215
- timeout: metrics.timeout,
216
- survived: metrics.survived,
217
- noCoverage: metrics.noCoverage,
218
- runtimeErrors: metrics.runtimeErrors,
219
- compileErrors: metrics.compileErrors,
220
- ignored: metrics.ignored,
221
- pending: metrics.pending
222
- },
223
- testContribution: judgeTestContribution(report, requireTestContribution, disableBail) ?? null,
224
- reportFile,
225
- mutants
226
- };
227
- }
228
- //#endregion
229
- export { isActionableStatus as a, generateRunId as i, VERDICT_ENVELOPE_SCHEMA_VERSION as n, judgeTestContribution as o, buildVerdictEnvelope as r, ACTIONABLE_STATUSES as t };