@systemfsoftware/stryker-js 12.0.0 → 12.1.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 CHANGED
@@ -1,5 +1,35 @@
1
1
  # @systemfsoftware/stryker-js
2
2
 
3
+ ## 12.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - A piped `stdout` now gets human output, exactly like a terminal. Machine consumers ask for the NDJSON event stream explicitly, with `--json` or `STRYKER_MODE=machine`; `--format text` names the human format, and passing it together with `--json` is a usage error (exit 2). Under `--json`, `stdout` carries wire records and nothing else, while progress status lines and log output stay on `stderr`.
8
+
9
+ Every run also writes those wire records to `reports/mutation-stream.jsonl` in human mode too, so a job that shows a readable log still produces the artifact `stryker merge-reports` rebuilds a shard's report from.
10
+
11
+ If a script or CI step read NDJSON from a piped run without requesting it, pass `--json` (or set `STRYKER_MODE=machine`) there.
12
+
13
+ Custom `RunEventDrain` implementations must accept a required `toStdout` argument in `drainFramed`; pass-throughs can ignore it.
14
+
15
+ ### Patch Changes
16
+
17
+ - A run whose selected files produce no mutants, for example because no loaded framework claims them, now runs every test in the dry run and finishes with an empty report. With `testRunner: 'vm'` or `'vitest'` it used to fail with "No tests were executed", because the dry run only looked for tests related to those files. The dry run now relates tests only to the files that carry mutants.
18
+
19
+ - A run whose `mutate` patterns match no file now finishes successfully instead of stopping with an instrument error. It performs the dry run, writes a mutation report with no files, and passes regardless of `thresholds.break`, the same as upstream StrykerJS. A sharded run whose shard receives no file no longer fails.
20
+
21
+ `stryker merge-reports` shows `n/a` for a package whose report has no tested mutant, instead of a score of `0.00`.
22
+
23
+ Errors reported for the prepare, dry-run, and mutation-testing stages now carry their reason as the error message, so a printed cause no longer shows up as a bare error name.
24
+
25
+ - `stryker merge-reports` now rebuilds a package's partial report from the mutants recorded in its stream part when the run ended before writing its final report. Earlier versions recognized none of the recorded mutants and reported the package as having no report.
26
+
27
+ - Mutation runs with `testRunner: 'vm'` finish in about half the time, with the same verdicts. The engine checks mutant groups on every checker process at once, stops the checkers as soon as checking ends, hands their share of `concurrency` to the test runners, and shares a Node compile cache with every worker it starts. The Vitest runner starts the next isolated worker thread while the current test file runs, so each file still gets a fresh thread but no longer waits for one to boot. On a 319-mutant TypeScript project with the TypeScript checker, a run went from 24.9 s to 12.6 s.
28
+
29
+ - `testRunner: 'vm'` runs Vitest itself on Vitest's isolated `threads` pool instead of an in-process reimplementation, and stays the default `testRunner`. It reports the same test ids, outcomes and per-mutant verdicts as `testRunner: 'vitest'`, so a project whose Vitest config enables browser mode is refused at startup with a message naming `testRunner: 'vitest'`.
30
+
31
+ Stryker no longer discovers test files for `vm`: Vitest selects them from your config, as it does for `testRunner: 'vitest'`. A run that loads no test files fails the dry run naming `testFiles`.
32
+
3
33
  ## 12.0.0
4
34
 
5
35
  ### Major Changes
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  The modern mutation testing framework for JavaScript and TypeScript.
4
4
  A ground-up, breaking-change fork of `@stryker-mutator/core` built with Effect 4:
5
- provides the `stryker` executable, in-process `vm` and isolated Vitest runners,
6
- and the typed `./config` authoring surface.
5
+ provides the `stryker` executable, the `vm` and `vitest` test runners, and the
6
+ typed `./config` authoring surface.
7
7
 
8
8
  ## Install
9
9
 
@@ -54,10 +54,13 @@ pnpm exec stryker run
54
54
 
55
55
  ## Zero-Plugin Built-in Runners
56
56
 
57
- The `vm` runner is the default: with no `testRunner` and no `testFiles`
58
- configured, it discovers `*.test` / `*.spec` files itself and runs them. It loads
59
- your suites through your project's `vitest`, so `vitest` must be installed.
60
- Set `testFiles` explicitly, or pick one of the runners below, to take control.
57
+ The `vm` runner is the default and needs no plugin packages: `testRunner: 'vm'`
58
+ runs Vitest itself, on Vitest's isolated `threads` pool, through the project's
59
+ own `vitest` install (`vitest` must be installed). With no `testFiles`
60
+ configured, Vitest selects the test files from your config, exactly as
61
+ `vitest run` does. The runner reports the same test ids, outcomes and
62
+ per-mutant verdicts as `vitest run`. Set `testFiles` explicitly, or pick one of
63
+ the runners below, to take control.
61
64
 
62
65
  ### 1. Shell Command Runner (`testRunner: 'command'`)
63
66
 
@@ -75,9 +78,13 @@ export default defineConfig({
75
78
  })
76
79
  ```
77
80
 
78
- ### 2. In-Process `vm` Runner (`testRunner: 'vm'`)
81
+ ### 2. The `vm` Runner (`testRunner: 'vm'`)
79
82
 
80
- Runs Vitest suites in-process, in one worker thread per test runner, loading each test file as native ESM through Node's module hooks. No child process and no bundler step. Each file gets its own module state by default; your `vitest.config.*` (environment, setup files, globals, projects) is picked up through your project's `vitest` install. Vitest browser-mode suites are refused with an error naming `testRunner: 'vitest'`; pick the `vitest` runner for those.
83
+ Runs the Vitest runner on Vitest's isolated `threads` pool. Each test file keeps
84
+ Vitest's own per-file isolation, and module mocking, snapshots, environments,
85
+ setup files, projects and custom transforms behave as they do under
86
+ `vitest run`. A Vitest config that enables browser mode is refused with an
87
+ error naming `testRunner: 'vitest'`; pick the `vitest` runner for those.
81
88
 
82
89
  ```ts
83
90
  import { defineConfig } from '@systemfsoftware/stryker-js/config'
@@ -89,6 +96,10 @@ export default defineConfig({
89
96
  })
90
97
  ```
91
98
 
99
+ Suites that need Vitest's `forks` pool (for example `process.chdir`, or native
100
+ addons that are not thread-safe) use `testRunner: 'vitest'` with the
101
+ `@systemfsoftware/stryker-js-vitest-runner` plugin instead.
102
+
92
103
  ## Configuration API (`@systemfsoftware/stryker-js/config`)
93
104
 
94
105
  The `./config` subpath exports the typed configuration authoring surface, including the `StrykerConfig` type naming the partial options a config file writes: a config annotated `const config: StrykerConfig = defineConfig({ ... })` typechecks and loads.
@@ -143,9 +154,20 @@ pnpm exec stryker run --incremental
143
154
  pnpm exec stryker run --concurrency 4
144
155
 
145
156
  # Merge partial mutation reports from parallel CI shards
146
- pnpm exec stryker merge-reports --output reports/mutation/mutation.json "reports/shards/*.json"
157
+ pnpm exec stryker merge-reports --parts reports/shards --out reports/mutation
147
158
  ```
148
159
 
160
+ ## Output Modes
161
+
162
+ Human output is the default, whether or not `stdout` is a terminal. A machine consumer opts into the NDJSON event stream explicitly:
163
+
164
+ ```bash
165
+ pnpm exec stryker run --json # wire records on stdout, diagnostics on stderr
166
+ STRYKER_MODE=machine pnpm exec stryker run # the same, named by environment
167
+ ```
168
+
169
+ `--format text` names the human format explicitly; `--json` together with `--format text` is a usage error (exit 2). Under `--json`, `stdout` carries wire records and nothing else — progress lines and log output stay on `stderr`. Every run also writes the same records to `reports/mutation-stream.jsonl` (`--progressStreamFile`) in both modes, which is the artifact `stryker merge-reports` rebuilds a shard's partial report from.
170
+
149
171
  ## Programmatic API
150
172
 
151
173
  ### Effect 4 Native Interface
@@ -7233,6 +7233,10 @@ const Percentage = S.Finite.pipe(S.check(S.isBetween({
7233
7233
  minimum: 0,
7234
7234
  maximum: 100
7235
7235
  })));
7236
+ const MutationScore = S.TaggedUnion({
7237
+ Scored: { percentage: Percentage },
7238
+ Unscored: {}
7239
+ });
7236
7240
  var Metrics = class Metrics extends S.Class("Metrics")({
7237
7241
  pending: NonNegativeInt,
7238
7242
  killed: NonNegativeInt,
@@ -7262,10 +7266,10 @@ var Metrics = class Metrics extends S.Class("Metrics")({
7262
7266
  return this.totalValid + this.totalInvalid + this.ignored + this.pending;
7263
7267
  }
7264
7268
  get mutationScore() {
7265
- return Math.min(100, Math.max(0, this.totalDetected / this.totalValid * 100));
7269
+ return scoreOf(this.totalDetected, this.totalValid);
7266
7270
  }
7267
7271
  get mutationScoreBasedOnCoveredCode() {
7268
- return Math.min(100, Math.max(0, this.totalDetected / this.totalCovered * 100));
7272
+ return scoreOf(this.totalDetected, this.totalCovered);
7269
7273
  }
7270
7274
  static fromMutants(mutants) {
7271
7275
  return Metrics.make({
@@ -7281,6 +7285,10 @@ var Metrics = class Metrics extends S.Class("Metrics")({
7281
7285
  }
7282
7286
  };
7283
7287
  const metricCountOf = (mutants, status) => mutants.filter((mutant) => mutant.status === status).length;
7288
+ const scoreOf = (detected, counted) => Boolean$1$1.match(counted > 0, {
7289
+ onTrue: () => MutationScore.cases.Scored.make({ percentage: detected / counted * 100 }),
7290
+ onFalse: () => MutationScore.cases.Unscored.make({})
7291
+ });
7284
7292
  const MetricsSchema = Metrics;
7285
7293
  const MetricsResultSchema = S.Struct({
7286
7294
  name: S.String,
@@ -7890,6 +7898,7 @@ var Report_exports = /* @__PURE__ */ __exportAll$1({
7890
7898
  MetricsResultSchema: () => MetricsResultSchema,
7891
7899
  MetricsSchema: () => MetricsSchema,
7892
7900
  MutantResultSchema: () => MutantResultSchema,
7901
+ MutationScore: () => MutationScore,
7893
7902
  MutationTestResultSchema: () => MutationTestResultSchema,
7894
7903
  NonNegativeFinite: () => NonNegativeFinite,
7895
7904
  NonNegativeInt: () => NonNegativeInt,
@@ -8256,6 +8265,10 @@ const STAGE_PRESENTATION = {
8256
8265
  var PrepareError = class extends S.TaggedError()("PrepareError", {
8257
8266
  stage: S.Literal("prepare"),
8258
8267
  reason: S.String
8259
- }) {};
8268
+ }) {
8269
+ get message() {
8270
+ return this.reason;
8271
+ }
8272
+ };
8260
8273
  //#endregion
8261
8274
  export { Plugin_exports as A, RunReadError as C, VerdictReached as D, SkippedReported as E, ErrorText_exports as F, Format_exports as I, Instrument_exports as L, Reporter_exports as M, TestRunner_exports as N, Checker_exports as O, Trace_exports as P, Mutant_exports as R, RunPhase as S, SkippedFileRow as T, PluginsReported as _, FormatRegistryRow as a, RunFailed as b, Heartbeat as c, MutationRunPlan as d, OutputMode as f, PluginFailureReason as g, PlanMutationRunCommand as h, FormatRegistryResolved as i, Report_exports as j, Options_exports as k, HelpRendered as l, PlanKnown as m, StageError as n, FrameworkContributionRow as o, PhaseEntered as p, FormatClaimShadowingRow as r, FrameworkModuleRow as s, PrepareError as t, ModeSignal as u, RunDecodeError as v, RunStarted as w, RunMutantTested as x, RunEvent as y };
@@ -1,4 +1,4 @@
1
- import { i as mod_d_exports$4, r as mod_d_exports$3 } from "./index-f-U3oZaJ.mjs";
1
+ import { i as mod_d_exports$4, r as mod_d_exports$3 } from "./index-CKd1sAni.mjs";
2
2
  import * as S from "effect/Schema";
3
3
  //#region src/run-event.schema.d.ts
4
4
  declare const RunPhase: S.Literals<readonly ["prepare", "instrument", "dry-run", "mutation-test"]>;
@@ -240,6 +240,8 @@ declare const PrepareError_base: S.Class<PrepareError, S.TaggedStruct<"PrepareEr
240
240
  readonly stage: S.Literal<"prepare">;
241
241
  readonly reason: S.String;
242
242
  }>, import("effect/Cause").YieldableError>;
243
- declare class PrepareError extends PrepareError_base {}
243
+ declare class PrepareError extends PrepareError_base {
244
+ get message(): string;
245
+ }
244
246
  //#endregion
245
247
  export { VerdictReached as A, RunReadError as C, SkippedReported as D, SkippedFileRow as E, VerdictCounts as O, RunPhase as S, RunTerminalEvent as T, PluginsReported as _, FormatRegistryRow as a, RunFailed as b, Heartbeat as c, MutationRunPlan as d, OutputMode as f, PluginFailureReason as g, PlanMutationRunCommand as h, FormatRegistryResolved as i, VerdictThresholds as j, VerdictMutant as k, HelpRendered as l, PlanKnown as m, StageError as n, FrameworkContributionRow as o, PhaseEntered as p, FormatClaimShadowingRow as r, FrameworkModuleRow as s, PrepareError as t, ModeSignal as u, RunDecodeError as v, RunStarted as w, RunMutantTested as x, RunEvent as y };
@@ -1,5 +1,5 @@
1
- import { a as mod_d_exports$5, c as mod_d_exports$2, d as Ignorer, i as mod_d_exports$4, l as mod_d_exports$3$1, n as mod_d_exports$2$1, o as mod_d_exports$6, r as mod_d_exports$3, s as mod_d_exports$1, t as mod_d_exports, u as Framework } from "./index-f-U3oZaJ.mjs";
2
- import { A as VerdictReached, D as SkippedReported, _ as PluginsReported, b as RunFailed, c as Heartbeat, i as FormatRegistryResolved, l as HelpRendered, m as PlanKnown, p as PhaseEntered, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-D43Nx5kI.mjs";
1
+ import { a as mod_d_exports$5, c as mod_d_exports$2, d as Ignorer, i as mod_d_exports$4, l as mod_d_exports$3$1, n as mod_d_exports$2$1, o as mod_d_exports$6, r as mod_d_exports$3, s as mod_d_exports$1, t as mod_d_exports, u as Framework } from "./index-CKd1sAni.mjs";
2
+ import { A as VerdictReached, D as SkippedReported, _ as PluginsReported, b as RunFailed, c as Heartbeat, i as FormatRegistryResolved, l as HelpRendered, m as PlanKnown, p as PhaseEntered, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-BnUKGQjA.mjs";
3
3
  import { Cell, Handle, Sandwich, Workflow } from "@systemfsoftware/effect-cell-types";
4
4
  import "effect/Option";
5
5
  import "effect/Result";
@@ -125,7 +125,7 @@ interface ResolvedModeInput {
125
125
  }
126
126
  //#endregion
127
127
  //#region src/run-event-stream.service.d.ts
128
- type FramedDrain = (framed: Stream.Stream<string, never, never>) => Effect$1.Effect<void, never, never>;
128
+ type FramedDrain = (framed: Stream.Stream<string, never, never>, toStdout: boolean) => Effect$1.Effect<void, never, never>;
129
129
  interface RunEventDrainShape {
130
130
  readonly drainFramed: FramedDrain;
131
131
  readonly setProgressStreamFile: (fileName: string) => Effect$1.Effect<void, never, never>;
package/dist/config.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { n as mod_d_exports$2 } from "./index-f-U3oZaJ.mjs";
2
- import { a as StrykerConfig, i as Primitive, n as Immutable, o as StrykerConfigExport, r as ImmutablePrimitive, s as StrykerConfigFn, t as ConfigEnv } from "./stryker-config.schema-GS_4_5gX.mjs";
1
+ import { n as mod_d_exports$2 } from "./index-CKd1sAni.mjs";
2
+ import { a as StrykerConfig, i as Primitive, n as Immutable, o as StrykerConfigExport, r as ImmutablePrimitive, s as StrykerConfigFn, t as ConfigEnv } from "./stryker-config.schema-VVxHqsAv.mjs";
3
3
  //#region src/config/define-config.d.ts
4
4
  export declare function defineConfig(config: StrykerConfig): StrykerConfig;
5
5
  export declare function defineConfig(config: Promise<StrykerConfig>): Promise<StrykerConfig>;
package/dist/events.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as VerdictReached, C as RunReadError, D as SkippedReported, E as SkippedFileRow, O as VerdictCounts, S as RunPhase, _ as PluginsReported, a as FormatRegistryRow, b as RunFailed, c as Heartbeat, d as MutationRunPlan, f as OutputMode, g as PluginFailureReason, h as PlanMutationRunCommand, i as FormatRegistryResolved, j as VerdictThresholds, k as VerdictMutant, l as HelpRendered, m as PlanKnown, n as StageError, o as FrameworkContributionRow, p as PhaseEntered, r as FormatClaimShadowingRow, s as FrameworkModuleRow, t as PrepareError, u as ModeSignal, v as RunDecodeError, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-D43Nx5kI.mjs";
1
+ import { A as VerdictReached, C as RunReadError, D as SkippedReported, E as SkippedFileRow, O as VerdictCounts, S as RunPhase, _ as PluginsReported, a as FormatRegistryRow, b as RunFailed, c as Heartbeat, d as MutationRunPlan, f as OutputMode, g as PluginFailureReason, h as PlanMutationRunCommand, i as FormatRegistryResolved, j as VerdictThresholds, k as VerdictMutant, l as HelpRendered, m as PlanKnown, n as StageError, o as FrameworkContributionRow, p as PhaseEntered, r as FormatClaimShadowingRow, s as FrameworkModuleRow, t as PrepareError, u as ModeSignal, v as RunDecodeError, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-BnUKGQjA.mjs";
2
2
  export { FormatClaimShadowingRow, FormatRegistryResolved, FormatRegistryRow, FrameworkContributionRow, FrameworkModuleRow, Heartbeat, HelpRendered, ModeSignal, MutationRunPlan, OutputMode, PhaseEntered, PlanKnown, PlanMutationRunCommand, PluginFailureReason, PluginsReported, PrepareError, RunDecodeError, RunEvent, RunFailed, RunMutantTested, RunPhase, RunReadError, RunStarted, SkippedFileRow, SkippedReported, StageError, type VerdictCounts, type VerdictMutant, VerdictReached, type VerdictThresholds };
package/dist/events.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { C as RunReadError, D as VerdictReached, E as SkippedReported, S as RunPhase, T as SkippedFileRow, _ as PluginsReported, a as FormatRegistryRow, b as RunFailed, c as Heartbeat, d as MutationRunPlan, f as OutputMode, g as PluginFailureReason, h as PlanMutationRunCommand, i as FormatRegistryResolved, l as HelpRendered, m as PlanKnown, n as StageError, o as FrameworkContributionRow, p as PhaseEntered, r as FormatClaimShadowingRow, s as FrameworkModuleRow, t as PrepareError, u as ModeSignal, v as RunDecodeError, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-BxsyHwHr.mjs";
1
+ import { C as RunReadError, D as VerdictReached, E as SkippedReported, S as RunPhase, T as SkippedFileRow, _ as PluginsReported, a as FormatRegistryRow, b as RunFailed, c as Heartbeat, d as MutationRunPlan, f as OutputMode, g as PluginFailureReason, h as PlanMutationRunCommand, i as FormatRegistryResolved, l as HelpRendered, m as PlanKnown, n as StageError, o as FrameworkContributionRow, p as PhaseEntered, r as FormatClaimShadowingRow, s as FrameworkModuleRow, t as PrepareError, u as ModeSignal, v as RunDecodeError, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-BaK_pxuK.mjs";
2
2
  export { FormatClaimShadowingRow, FormatRegistryResolved, FormatRegistryRow, FrameworkContributionRow, FrameworkModuleRow, Heartbeat, HelpRendered, ModeSignal, MutationRunPlan, OutputMode, PhaseEntered, PlanKnown, PlanMutationRunCommand, PluginFailureReason, PluginsReported, PrepareError, RunDecodeError, RunEvent, RunFailed, RunMutantTested, RunPhase, RunReadError, RunStarted, SkippedFileRow, SkippedReported, StageError, VerdictReached };