@systemfsoftware/stryker-js 7.0.0 → 9.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.
@@ -54,8 +54,10 @@ import * as RpcClient from "effect/unstable/rpc/RpcClient";
54
54
  import * as Schedule from "effect/Schedule";
55
55
  import * as Data from "effect/Data";
56
56
  import * as Equivalence from "effect/Equivalence";
57
+ import * as Metric from "effect/Metric";
57
58
  import * as Pool from "effect/Pool";
58
59
  import * as Semaphore from "effect/Semaphore";
60
+ import * as SchemaTransformation from "effect/SchemaTransformation";
59
61
  import { randomBytes } from "@noble/hashes/utils.js";
60
62
  import "effect/Sink";
61
63
  import * as NodeFileSystem$1 from "@effect/platform-node-shared/NodeFileSystem";
@@ -6202,11 +6204,11 @@ function replaceFirstMatch(path, original, replacement) {
6202
6204
  */
6203
6205
  function toApiLocation(startOffset, endOffset, lineTable, offset) {
6204
6206
  return {
6205
- start: toPosition$1(positionFromLineTable(startOffset, lineTable), offset),
6206
- end: toPosition$1(positionFromLineTable(endOffset, lineTable), offset)
6207
+ start: toPosition(positionFromLineTable(startOffset, lineTable), offset),
6208
+ end: toPosition(positionFromLineTable(endOffset, lineTable), offset)
6207
6209
  };
6208
6210
  }
6209
- function toPosition$1(source, offset) {
6211
+ function toPosition(source, offset) {
6210
6212
  let columnOffset = 0;
6211
6213
  if (source.line === 1) columnOffset = offset.column;
6212
6214
  return {
@@ -7917,6 +7919,24 @@ const instrument = (files, options, basePath) => Effect$1.gen(function* () {
7917
7919
  mutants
7918
7920
  });
7919
7921
  });
7922
+ //#endregion
7923
+ //#region ../stryker-js-plugin-interface/dist/index.mjs
7924
+ /** A position inside a file, in the coordinates the wire carries — 0-based line and column. */
7925
+ const CheckerPositionWire = S.Struct({
7926
+ line: S.Finite,
7927
+ column: S.Finite
7928
+ });
7929
+ const CheckerLocationWire = S.Struct({
7930
+ start: CheckerPositionWire,
7931
+ end: CheckerPositionWire
7932
+ });
7933
+ const CheckerMutantWire = S.Struct({
7934
+ id: S.NonEmptyString,
7935
+ fileName: S.NonEmptyString,
7936
+ mutatorName: S.NonEmptyString,
7937
+ replacement: S.String,
7938
+ location: CheckerLocationWire
7939
+ });
7920
7940
  S.Literals(["passed", "compileError"]);
7921
7941
  const CheckResultSchema$1 = S.Union([S.Struct({ status: S.Literal("passed") }), S.Struct({
7922
7942
  status: S.Literal("compileError"),
@@ -8185,7 +8205,7 @@ const TestRunnerDryRunRequest = S.Struct({ options: DryRunOptionsSchema });
8185
8205
  const TestRunnerMutantRunRequest = S.Struct({ options: MutantRunOptionsSchema });
8186
8206
  const CheckerRequest = S.Struct({
8187
8207
  checkerName: S.String,
8188
- mutants: S.Array(Mutant)
8208
+ mutants: S.Array(CheckerMutantWire)
8189
8209
  });
8190
8210
  const CheckerCheckResult = S.Record(S.String, CheckResultSchema$1);
8191
8211
  const CheckerGroupResult = S.Array(S.Array(S.String));
@@ -9323,6 +9343,39 @@ const PreviousFileSchema = S.Struct({
9323
9343
  const PreviousTestFileSchema = S.Struct({ source: S.optional(S.String) });
9324
9344
  const PreviousFilesSchema = S.Record(S.String, PreviousFileSchema);
9325
9345
  const PreviousTestFilesSchema = S.Record(S.String, PreviousTestFileSchema);
9346
+ //#endregion
9347
+ //#region src/mutant-result-mapping.ts
9348
+ const toSchemaPosition = (pos) => ({
9349
+ column: pos.column + 1,
9350
+ line: pos.line + 1
9351
+ });
9352
+ const toSchemaLocation = (location) => ({
9353
+ start: toSchemaPosition(location.start),
9354
+ end: toSchemaPosition(location.end)
9355
+ });
9356
+ const checkStatusToMutantStatus = (_status) => "CompileError";
9357
+ const mutantResult = (mutant, status, outcome = {}) => ({
9358
+ _tag: "Mutant",
9359
+ id: mutant.id,
9360
+ fileName: mutant.fileName,
9361
+ mutatorName: mutant.mutatorName,
9362
+ replacement: mutant.replacement,
9363
+ location: toSchemaLocation(mutant.location),
9364
+ status,
9365
+ coveredBy: mutant.coveredBy,
9366
+ static: mutant.static,
9367
+ testsCompleted: mutant.testsCompleted,
9368
+ description: mutant.description,
9369
+ ...outcome
9370
+ });
9371
+ const mapRunResult = (mutant, result) => Match.value(result).pipe(Match.discriminator("status")("error", (errored) => mutantResult(mutant, "RuntimeError", { statusReason: errored.errorMessage })), Match.discriminator("status")("killed", (killed) => mutantResult(mutant, "Killed", {
9372
+ testsCompleted: killed.nrOfTests,
9373
+ killedBy: [...killed.killedBy],
9374
+ statusReason: killed.failureMessage
9375
+ })), Match.discriminator("status")("timeout", (timedOut) => {
9376
+ if (timedOut.reason === void 0) return mutantResult(mutant, "Timeout");
9377
+ return mutantResult(mutant, "Timeout", { statusReason: timedOut.reason });
9378
+ }), Match.discriminator("status")("survived", (survived) => mutantResult(mutant, "Survived", { testsCompleted: survived.nrOfTests })), Match.exhaustive);
9326
9379
  const ZERO = 0;
9327
9380
  const firstDefined = (first, second) => Option.getOrElse(Option.fromNullishOr(first), () => second);
9328
9381
  const staticField = (isStatic) => Option.match(Option.fromUndefinedOr(isStatic), {
@@ -9587,6 +9640,18 @@ const decidePlans = (mutants, testCoverage, options, timeOverheadMS, globalTestF
9587
9640
  const command = coverageToCommand(mutants, testCoverage, options, timeOverheadMS, globalTestFilter, sandboxFileByName);
9588
9641
  return makeMutantTestPlanner(command);
9589
9642
  };
9643
+ const isRunPlan = (plan) => plan.plan === "Run";
9644
+ const isEarlyPlan = (plan) => !isRunPlan(plan);
9645
+ const earlyResultOf = (plan) => Object.assign({}, plan.mutant, {
9646
+ location: toSchemaLocation(plan.mutant.location),
9647
+ status: plan.mutant.status ?? "Ignored"
9648
+ });
9649
+ const partitionRunPlans = (plans) => ({
9650
+ runPlans: plans.filter(isRunPlan),
9651
+ earlyResults: plans.filter(isEarlyPlan).map(earlyResultOf)
9652
+ });
9653
+ const byReloadEnvironment = (left, right) => Number(left.runOptions.reloadEnvironment) - Number(right.runOptions.reloadEnvironment);
9654
+ const sortRunPlans = (plans) => [...plans].sort(byReloadEnvironment);
9590
9655
  const REMEMBERED_STATUS = /* @__PURE__ */ new Set([
9591
9656
  "Killed",
9592
9657
  "Survived",
@@ -11740,12 +11805,193 @@ const ThresholdsLikeSchema = S.Struct({
11740
11805
  low: S.Finite
11741
11806
  });
11742
11807
  const IncrementalReportSchema = S.StructWithRest(S.Struct({
11808
+ incrementalVersion: S.String,
11743
11809
  schemaVersion: S.String,
11744
11810
  thresholds: ThresholdsLikeSchema,
11745
11811
  files: S.Record(S.String, FileResultLikeSchema),
11746
11812
  testFiles: S.optional(S.Record(S.String, TestFileLikeSchema))
11747
11813
  }), [S.Record(S.String, S.Unknown)]);
11748
- var IncrementalReportError = class extends S.TaggedError()("IncrementalReportError", { message: S.String }) {};
11814
+ //#endregion
11815
+ //#region src/admit-incremental-report.workflow.ts
11816
+ var AdmitIncrementalReportCommand = class extends S.TaggedClass()("AdmitIncrementalReportCommand", {
11817
+ report: S.optional(IncrementalReportSchema),
11818
+ expectedVersion: S.String
11819
+ }) {};
11820
+ const IncrementalReportDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js/IncrementalReportDecision");
11821
+ var IncrementalReportKeep = class extends S.TaggedClass()("IncrementalReportKeep", { report: IncrementalReportSchema }) {
11822
+ [IncrementalReportDecisionTypeId] = IncrementalReportDecisionTypeId;
11823
+ };
11824
+ var IncrementalReportDiscard = class extends S.TaggedClass()("IncrementalReportDiscard", {
11825
+ actual: S.optional(S.String),
11826
+ expected: S.String
11827
+ }) {
11828
+ [IncrementalReportDecisionTypeId] = IncrementalReportDecisionTypeId;
11829
+ };
11830
+ const decide = (command) => Option.match(Option.fromUndefinedOr(command.report), {
11831
+ onNone: () => IncrementalReportDiscard.make({
11832
+ actual: void 0,
11833
+ expected: command.expectedVersion
11834
+ }),
11835
+ onSome: (report) => Match.value(report.incrementalVersion === command.expectedVersion).pipe(Match.when(true, () => IncrementalReportKeep.make({ report })), Match.orElse(() => IncrementalReportDiscard.make({
11836
+ actual: report.incrementalVersion,
11837
+ expected: command.expectedVersion
11838
+ })))
11839
+ });
11840
+ const admitIncrementalReport = Workflow.total(AdmitIncrementalReportCommand, (command) => Result.succeed(decide(command)));
11841
+ //#endregion
11842
+ //#region package.json
11843
+ var package_default = {
11844
+ name: "@systemfsoftware/stryker-js",
11845
+ version: "9.0.0",
11846
+ repository: {
11847
+ "type": "git",
11848
+ "url": "git+https://github.com/systemfsoftware/stryker-js-effect.git",
11849
+ "directory": "packages/stryker-js"
11850
+ },
11851
+ homepage: "https://github.com/systemfsoftware/stryker-js-effect",
11852
+ bugs: { "url": "https://github.com/systemfsoftware/stryker-js-effect/issues" },
11853
+ description: "The Stryker mutation testing tool — the stryker executable, the run engine, and the configuration surface.",
11854
+ type: "module",
11855
+ files: ["dist"],
11856
+ bin: { "stryker": "./dist/main.mjs" },
11857
+ scripts: {
11858
+ "clean": "rimraf dist",
11859
+ "build": "rimraf dist && tsdown --config tsdown.bin.config.ts && tsdown --config tsdown.config.ts",
11860
+ "typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
11861
+ "test": "vitest run",
11862
+ "mutation": "stryker run",
11863
+ "attw": "attw --pack .",
11864
+ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
11865
+ "api:check": "api-extractor run",
11866
+ "api:update": "api-extractor run --local",
11867
+ "lint:tsgo": "effect-tsgo diagnostics --project tsconfig.json --format ${TSGO_FORMAT:-text}"
11868
+ },
11869
+ license: "Apache-2.0",
11870
+ engines: { "node": ">=22.18.0" },
11871
+ dependencies: {
11872
+ "@effect/opentelemetry": "4.0.0-rc.112",
11873
+ "@effect/platform-node": "catalog:",
11874
+ "@effect/platform-node-shared": "catalog:",
11875
+ "@noble/hashes": "catalog:",
11876
+ "@opentelemetry/api": "^1.9.0",
11877
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.208.0",
11878
+ "@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
11879
+ "@opentelemetry/sdk-metrics": "^2.2.0",
11880
+ "@opentelemetry/sdk-trace-base": "^2.2.0",
11881
+ "@systemfsoftware/effect-cell-types": "catalog:"
11882
+ },
11883
+ peerDependencies: { "effect": "catalog:" },
11884
+ devDependencies: {
11885
+ "@effect/tsgo": "catalog:",
11886
+ "@effect/vitest": "catalog:",
11887
+ "@microsoft/api-extractor": "catalog:",
11888
+ "@oxc-parser/binding-wasm32-wasi": "catalog:",
11889
+ "@std/jsonc": "catalog:",
11890
+ "@systemfsoftware/all": "catalog:",
11891
+ "@systemfsoftware/arethetypeswrong-cli": "catalog:",
11892
+ "@systemfsoftware/effect-gherkin-spec": "catalog:",
11893
+ "@systemfsoftware/effect-schema-law": "catalog:",
11894
+ "@systemfsoftware/effect-schema-vite": "catalog:",
11895
+ "@systemfsoftware/stryker-config": "workspace:^",
11896
+ "@systemfsoftware/stryker-ignorer-effect-schema-declarations": "catalog:stryker",
11897
+ "@systemfsoftware/stryker-ignorer-in-source-vitest-block": "catalog:stryker",
11898
+ "@systemfsoftware/stryker-ignorer-interface": "workspace:^",
11899
+ "@systemfsoftware/stryker-js": "catalog:stryker",
11900
+ "@systemfsoftware/stryker-js-html-reporter": "workspace:^",
11901
+ "@systemfsoftware/stryker-js-instrumenter": "workspace:^",
11902
+ "@systemfsoftware/stryker-js-plugin-interface": "workspace:^",
11903
+ "@systemfsoftware/stryker-js-plugin-runtime": "workspace:^",
11904
+ "@systemfsoftware/stryker-js-typescript-checker": "catalog:stryker",
11905
+ "@systemfsoftware/stryker-js-vitest-runner": "catalog:stryker",
11906
+ "@systemfsoftware/stryker-test-contribution": "catalog:stryker",
11907
+ "@systemfsoftware/tsconfig": "catalog:",
11908
+ "@systemfsoftware/tsdown-config": "workspace:^",
11909
+ "@systemfsoftware/vitest-config": "workspace:^",
11910
+ "@types/node": "catalog:",
11911
+ "@vitest/coverage-v8": "catalog:",
11912
+ "concurrently": "catalog:",
11913
+ "mutation-testing-elements": "catalog:stryker",
11914
+ "oxc-parser": "catalog:",
11915
+ "oxlint": "catalog:",
11916
+ "oxlint-tsgolint": "catalog:",
11917
+ "rimraf": "catalog:",
11918
+ "tsdown": "catalog:",
11919
+ "typescript": "catalog:",
11920
+ "vitest": "catalog:"
11921
+ },
11922
+ publishConfig: {
11923
+ "access": "public",
11924
+ "provenance": true,
11925
+ "exports": {
11926
+ ".": {
11927
+ "types": "./dist/index.d.mts",
11928
+ "default": "./dist/index.mjs"
11929
+ },
11930
+ "./config": {
11931
+ "types": "./dist/config.d.mts",
11932
+ "default": "./dist/config.mjs"
11933
+ },
11934
+ "./promises": {
11935
+ "types": "./dist/promises.d.mts",
11936
+ "default": "./dist/promises.mjs"
11937
+ },
11938
+ "./package.json": "./package.json"
11939
+ }
11940
+ },
11941
+ exports: { "./package.json": "./package.json" }
11942
+ };
11943
+ //#endregion
11944
+ //#region src/stryker-package.schema.ts
11945
+ const PackageJsonSchema = Schema.Struct({ version: Schema.String });
11946
+ //#endregion
11947
+ //#region src/stryker-package.ts
11948
+ const rawPackageJson = package_default;
11949
+ const strykerVersion = Schema.decodeUnknownSync(PackageJsonSchema)(rawPackageJson).version;
11950
+ //#endregion
11951
+ //#region src/incremental-report.cell.ts
11952
+ const parseAndDecodeIncrementalReport = S.decodeUnknownResult(S.fromJsonString(IncrementalReportSchema));
11953
+ const readIncrementalReportRaw = (input) => {
11954
+ if (!input.incremental) return Effect$1.succeed({
11955
+ incremental: false,
11956
+ incrementalFile: input.incrementalFile,
11957
+ contents: void 0
11958
+ });
11959
+ return Effect$1.gen(function* () {
11960
+ const contents = yield* (yield* FileSystem.FileSystem).readFileString(input.incrementalFile).pipe(Effect$1.catchTag("PlatformError", (error) => Match.value(error.reason).pipe(Match.tag("NotFound", () => Effect$1.logInfo(`No incremental result file found at ${input.incrementalFile}, a full mutation testing run will be performed.`).pipe(Effect$1.as(void 0))), Match.orElse(() => Effect$1.fail(error)))));
11961
+ return {
11962
+ incremental: true,
11963
+ incrementalFile: input.incrementalFile,
11964
+ contents
11965
+ };
11966
+ });
11967
+ };
11968
+ const decodeIncrementalReportRaw = (raw) => Result.succeed(AdmitIncrementalReportCommand.make({
11969
+ report: Option.getOrUndefined(Option.flatMap(Option.fromUndefinedOr(raw.contents), (text) => Result.getSuccess(parseAndDecodeIncrementalReport(text)))),
11970
+ expectedVersion: strykerVersion
11971
+ }));
11972
+ const writeIncrementalReportOutcome = (outcome, raw) => Result.match(outcome, {
11973
+ onFailure: (error) => Effect$1.fail(error),
11974
+ onSuccess: (decision) => Match.value(decision).pipe(Match.tag("IncrementalReportKeep", (keep) => {
11975
+ const report = keep.report;
11976
+ return Effect$1.succeedSome(report);
11977
+ }), Match.tag("IncrementalReportDiscard", (discard) => {
11978
+ if (!raw.incremental) return Effect$1.succeedNone;
11979
+ return Option.match(Option.fromUndefinedOr(raw.contents), {
11980
+ onNone: () => Effect$1.succeedNone,
11981
+ onSome: () => Option.match(Option.fromUndefinedOr(discard.actual), {
11982
+ onNone: () => Effect$1.logInfo(`Unable to parse incremental result file at ${raw.incrementalFile}; a full mutation testing run will be performed.`).pipe(Effect$1.as(Option.none())),
11983
+ onSome: (actual) => Effect$1.logInfo(`Incremental result file at ${raw.incrementalFile} version ${actual} does not match expected version ${discard.expected}; a full mutation testing run will be performed.`).pipe(Effect$1.as(Option.none()))
11984
+ })
11985
+ });
11986
+ }), Match.exhaustive)
11987
+ });
11988
+ const incrementalReportCell = Cell.layer({
11989
+ read: readIncrementalReportRaw,
11990
+ decode: decodeIncrementalReportRaw,
11991
+ decide: admitIncrementalReport,
11992
+ encode: (outcome) => outcome,
11993
+ write: writeIncrementalReportOutcome
11994
+ });
11749
11995
  //#endregion
11750
11996
  //#region src/Project.ts
11751
11997
  const DEFAULT_GLOB$1 = "**/*.{js,ts,jsx,tsx,html,vue,mjs,mts,cts,cjs}";
@@ -11967,54 +12213,6 @@ const selectFiles = (input) => ({
11967
12213
  fileDescriptions: resolveFileDescriptionsPure(input.inputFileNames, input.mutatePatterns, input.targetMutatePatterns, input.basePath),
11968
12214
  testFiles: resolveTestFilesPure(input.inputFileNames, input.testFilePatterns, input.basePath)
11969
12215
  });
11970
- const toPosition = (position) => ({
11971
- line: position.line,
11972
- column: position.column
11973
- });
11974
- const toLocation = (location) => ({
11975
- start: toPosition(location.start),
11976
- end: toPosition(location.end)
11977
- });
11978
- const toOpenEndLocation = (location) => Option.match(Option.fromUndefinedOr(location.end), {
11979
- onNone: () => ({ start: toPosition(location.start) }),
11980
- onSome: (end) => ({
11981
- start: toPosition(location.start),
11982
- end: toPosition(end)
11983
- })
11984
- });
11985
- const withMappedMutantLocations = (report) => Object.fromEntries(Object.entries(report.files).map(([fileName, file]) => [fileName, {
11986
- ...file,
11987
- mutants: file.mutants.map((mutant) => ({
11988
- ...mutant,
11989
- location: toLocation(mutant.location)
11990
- }))
11991
- }]));
11992
- const withMappedTestLocations = (testFiles) => Object.fromEntries(Object.entries(testFiles).map(([fileName, file]) => [fileName, {
11993
- ...file,
11994
- tests: file.tests.map((test) => Option.match(Option.fromUndefinedOr(test.location), {
11995
- onNone: () => ({ ...test }),
11996
- onSome: (location) => ({
11997
- ...test,
11998
- location: toOpenEndLocation(location)
11999
- })
12000
- }))
12001
- }]));
12002
- const reshape = (decoded) => Option.match(Option.fromUndefinedOr(decoded.testFiles), {
12003
- onNone: () => ({
12004
- ...decoded,
12005
- files: withMappedMutantLocations(decoded)
12006
- }),
12007
- onSome: (testFiles) => ({
12008
- ...decoded,
12009
- files: withMappedMutantLocations(decoded),
12010
- testFiles: withMappedTestLocations(testFiles)
12011
- })
12012
- });
12013
- const decodeIncrementalReport = (raw) => Result.match(S.decodeUnknownResult(IncrementalReportSchema)(raw), {
12014
- onFailure: () => Result.fail(IncrementalReportError.make({ message: "The incremental report is not a mutation testing report; delete it or re-run without --incremental." })),
12015
- onSuccess: (decoded) => Result.succeed(reshape(decoded))
12016
- });
12017
- const parseJson = (text) => JSON.parse(text);
12018
12216
  const stringArrayEquivalence = Equivalence.Array(Equivalence.String);
12019
12217
  function makeProjectFile(name, mutate, content, originalContent) {
12020
12218
  return {
@@ -12171,23 +12369,6 @@ function resolveInputFileNames(ignoreRules, basePath) {
12171
12369
  return yield* crawlDir(basePath, basePath);
12172
12370
  });
12173
12371
  }
12174
- function parseIncrementalReport(contents) {
12175
- return Option.match(Option.fromUndefinedOr(contents), {
12176
- onNone: () => Effect$1.succeedNone,
12177
- onSome: (text) => Effect$1.gen(function* () {
12178
- const parsed = yield* Effect$1.try(() => parseJson(text));
12179
- const rawReport = yield* Effect$1.fromResult(decodeIncrementalReport(parsed));
12180
- const isMutationTestResult = (_value) => true;
12181
- if (!isMutationTestResult(rawReport)) throw new Error("Invalid incremental report shape");
12182
- return Option.fromUndefinedOr(rawReport);
12183
- })
12184
- });
12185
- }
12186
- function readIncrementalReport(incremental, incrementalFile) {
12187
- return Match.value(incremental).pipe(Match.when(false, () => Effect$1.succeedNone), Match.orElse(() => Effect$1.gen(function* () {
12188
- return yield* parseIncrementalReport(yield* (yield* FileSystem.FileSystem).readFileString(incrementalFile).pipe(Effect$1.catchTag("PlatformError", (error) => Match.value(error.reason).pipe(Match.tag("NotFound", () => Effect$1.logInfo(`No incremental result file found at ${incrementalFile}, a full mutation testing run will be performed.`).pipe(Effect$1.as(void 0))), Match.orElse(() => Effect$1.fail(error))))));
12189
- })));
12190
- }
12191
12372
  function readProject(options, targetMutatePatterns, basePath) {
12192
12373
  const { mutate, tempDirName, ignorePatterns, incremental, incrementalFile, progressStreamFile, htmlReporter, jsonReporter, testFiles } = options;
12193
12374
  const mutatePatterns = mutate;
@@ -12239,7 +12420,10 @@ function readProject(options, targetMutatePatterns, basePath) {
12239
12420
  basePath
12240
12421
  }).testFiles.length === 0) yield* Effect$1.logWarning(`Glob pattern "${pattern}" did not match any test files.`);
12241
12422
  }));
12242
- const incrementalReport = yield* readIncrementalReport(incremental, incrementalFile);
12423
+ const incrementalReport = yield* incrementalReportCell.run({
12424
+ incremental,
12425
+ incrementalFile
12426
+ });
12243
12427
  return makeProject(decision.fileDescriptions, Option.getOrUndefined(incrementalReport), [...decision.testFiles]);
12244
12428
  });
12245
12429
  }
@@ -12972,6 +13156,29 @@ const admitMutationTest = Workflow.make(MutationTestCommand, (command) => Match.
12972
13156
  allowEmpty: true
12973
13157
  }, () => Result.succeed(MutationTestNoTests.make({}))), Match.orElse(() => Result.succeed(MutationTestProceed.make({})))));
12974
13158
  //#endregion
13159
+ //#region src/checker-mutant-wire.ts
13160
+ const CheckerMutantFromMutant = Mutant.pipe(S.decodeTo(CheckerMutantWire, SchemaTransformation.transform({
13161
+ decode: (m) => ({
13162
+ id: m.id,
13163
+ fileName: m.fileName,
13164
+ mutatorName: m.mutatorName,
13165
+ replacement: m.replacement,
13166
+ location: m.location
13167
+ }),
13168
+ encode: (w) => Mutant.make({
13169
+ id: w.id,
13170
+ fileName: w.fileName,
13171
+ mutatorName: w.mutatorName,
13172
+ replacement: w.replacement,
13173
+ location: w.location
13174
+ })
13175
+ })));
13176
+ const wireRecordOf = (mutant) => Result.mapError(S.decodeUnknownResult(CheckerMutantFromMutant)(mutant), (error) => ({
13177
+ id: mutant.id,
13178
+ fileName: mutant.fileName,
13179
+ reason: error.message
13180
+ }));
13181
+ //#endregion
12975
13182
  //#region src/admit-checker-answer.workflow.ts
12976
13183
  var CheckerAnsweredUnrequested = class extends S.TaggedError()("CheckerAnsweredUnrequested", {
12977
13184
  checkerName: S.String,
@@ -13039,6 +13246,19 @@ const evaluateCheckResult = (command) => {
13039
13246
  };
13040
13247
  const admitCheckerAnswer = Workflow.make(CheckerCommand, (command) => Match.value(command.phase).pipe(Match.when("group", () => evaluateGroup(command)), Match.when("check", () => evaluateCheckResult(command)), Match.exhaustive));
13041
13248
  //#endregion
13249
+ //#region src/metrics.ts
13250
+ const checkerDuration = Metric.timer("stryker.checker.duration", { description: "Checker worker RPC duration in milliseconds" });
13251
+ const checkerMutantsChecked = Metric.counter("stryker.checker.mutants.checked", { description: "Total number of mutants a checker worker answered for" });
13252
+ const checkerMutantsSkipped = Metric.counter("stryker.checker.mutants.skipped", { description: "Total number of mutants dropped because they cannot be described to a checker" });
13253
+ const checkerRpcFailures = Metric.counter("stryker.checker.rpc_failures", {
13254
+ description: "Checker worker RPC calls that did not complete, excluding interruptions",
13255
+ incremental: true
13256
+ });
13257
+ const checkerProcessCrashes = Metric.counter("stryker.checker.process_crashes", {
13258
+ description: "Checker worker processes that never became usable",
13259
+ incremental: true
13260
+ });
13261
+ //#endregion
13042
13262
  //#region src/Checker.ts
13043
13263
  /**
13044
13264
  * Checker — the Checker capability.
@@ -13154,16 +13374,20 @@ const makeCheckerChildProcess = (params) => Effect$1.gen(function* () {
13154
13374
  execArgv: [...params.execArgv],
13155
13375
  optionsJson,
13156
13376
  tempDirPrefix: "stryker-checker-"
13157
- }).pipe(Effect$1.mapError((error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", (crash) => crash), Match.tag("WorkerBootTimeoutError", () => crashed(`Checker worker failed to start: its boot window closed before it accepted the RPC connection`)), Match.exhaustive)));
13377
+ }).pipe(Effect$1.mapError((error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", (crash) => crash), Match.tag("WorkerBootTimeoutError", () => crashed(`Checker worker failed to start: its boot window closed before it accepted the RPC connection`)), Match.exhaustive)), Effect$1.tapError(() => Metric.update(checkerProcessCrashes, 1)));
13378
+ const recordCheckerCall = (spanName, checkerName, mutants, call) => call.pipe(Effect$1.withSpan(spanName, { attributes: {
13379
+ "stryker.checker.name": checkerName,
13380
+ "stryker.mutants.count": mutants.length
13381
+ } }), Effect$1.timed, Effect$1.onExit((exit) => Match.value(exit).pipe(Match.tag("Success", () => Metric.update(checkerMutantsChecked, mutants.length)), Match.tag("Failure", (failure) => Match.value(Cause.hasInterruptsOnly(failure.cause)).pipe(Match.when(true, () => Effect$1.void), Match.orElse(() => Metric.update(checkerRpcFailures, 1)))), Match.exhaustive)), Effect$1.tap(([duration]) => Metric.update(checkerDuration, duration)), Effect$1.map(([, result]) => result), Effect$1.mapError((error) => crashed(error.message)));
13158
13382
  return {
13159
- check: (checkerName, mutants) => client.check({
13383
+ check: (checkerName, mutants) => recordCheckerCall("stryker.checker.check", checkerName, mutants, client.check({
13160
13384
  checkerName,
13161
13385
  mutants: [...mutants]
13162
- }).pipe(Effect$1.mapError((error) => crashed(error.message))),
13163
- group: (checkerName, mutants) => client.group({
13386
+ })),
13387
+ group: (checkerName, mutants) => recordCheckerCall("stryker.checker.group", checkerName, mutants, client.group({
13164
13388
  checkerName,
13165
13389
  mutants: [...mutants]
13166
- }).pipe(Effect$1.mapError((error) => crashed(error.message)))
13390
+ }))
13167
13391
  };
13168
13392
  });
13169
13393
  const createCheckerFactory = (options, fileDescriptions, workerEntrypoint, idGenerator, workingDirectory) => makeCheckerChildProcess({
@@ -13194,6 +13418,69 @@ const writeGroupOutcome = (plans, checkerName, outcome) => Result.match(outcome,
13194
13418
  onFailure: (error) => Effect$1.fail(error),
13195
13419
  onSuccess: (decision) => writeGroupDecision(plans, checkerName, decision)
13196
13420
  });
13421
+ const partitionMutantsForWire = (plans) => {
13422
+ const wire = [];
13423
+ const undescribable = [];
13424
+ plans.forEach((plan) => Result.match(wireRecordOf(plan.mutant), {
13425
+ onSuccess: (wireMutant) => {
13426
+ wire.push(wireMutant);
13427
+ },
13428
+ onFailure: (refused) => {
13429
+ undescribable.push(refused);
13430
+ }
13431
+ }));
13432
+ return {
13433
+ wire,
13434
+ undescribable
13435
+ };
13436
+ };
13437
+ const compileErrorAnswersOf = (undescribable) => Object.fromEntries(undescribable.map((mutant) => [mutant.id, {
13438
+ status: "compileError",
13439
+ reason: mutant.reason
13440
+ }]));
13441
+ const singletonGroupsOf = (undescribable) => undescribable.map((mutant) => [mutant.id]);
13442
+ const undescribableIdsOf = (undescribable) => new Set(undescribable.map((mutant) => mutant.id));
13443
+ const lookupOf = (partitioned) => ({
13444
+ wireById: new Map(partitioned.wire.map((mutant) => [mutant.id, mutant])),
13445
+ undescribableById: new Map(partitioned.undescribable.map((mutant) => [mutant.id, mutant]))
13446
+ });
13447
+ const selectedFromLookup = (plans, lookup) => {
13448
+ const wire = [];
13449
+ const undescribable = [];
13450
+ plans.forEach((plan) => {
13451
+ Option.match(Option.fromUndefinedOr(lookup.wireById.get(plan.mutant.id)), {
13452
+ onSome: (wired) => {
13453
+ wire.push(wired);
13454
+ },
13455
+ onNone: () => Option.match(Option.fromUndefinedOr(lookup.undescribableById.get(plan.mutant.id)), {
13456
+ onSome: (refused) => {
13457
+ undescribable.push(refused);
13458
+ },
13459
+ onNone: () => {
13460
+ Result.match(wireRecordOf(plan.mutant), {
13461
+ onSuccess: (wired) => {
13462
+ wire.push(wired);
13463
+ },
13464
+ onFailure: (refused) => {
13465
+ undescribable.push(refused);
13466
+ }
13467
+ });
13468
+ }
13469
+ })
13470
+ });
13471
+ });
13472
+ return {
13473
+ wire,
13474
+ undescribable
13475
+ };
13476
+ };
13477
+ const SKIPPED_IDS_IN_WARNING = 5;
13478
+ const skippedIdsOf = (undescribable) => `${undescribable.slice(0, SKIPPED_IDS_IN_WARNING).map((item) => item.id).join(", ")}${Option.match(Option.liftPredicate(undescribable.length, (count) => count > SKIPPED_IDS_IN_WARNING), {
13479
+ onNone: () => "",
13480
+ onSome: (count) => `, +${count - SKIPPED_IDS_IN_WARNING} more`
13481
+ })}`;
13482
+ const refusalReasonsOf = (undescribable) => [...new Set(undescribable.map((mutant) => mutant.reason))].join("; ");
13483
+ const logSkippedMutants = (checkerName, undescribable) => Match.value(undescribable.length).pipe(Match.when(0, () => Effect$1.void), Match.orElse(() => Effect$1.logWarning(`Checker "${checkerName}" skipped ${undescribable.length} mutant(s) it cannot be told about: ${refusalReasonsOf(undescribable)} (${skippedIdsOf(undescribable)})`)));
13197
13484
  /**
13198
13485
  * Ask a checker about run plans and get run plans back.
13199
13486
  *
@@ -13202,13 +13489,26 @@ const writeGroupOutcome = (plans, checkerName, outcome) => Result.match(outcome,
13202
13489
  * project the plans down, call the checker, and hand the answers to the pure decision
13203
13490
  * that joins them back. The join is where the work is, and it is pure.
13204
13491
  */
13205
- const checkPlans = (checker, checkerName, plans) => {
13492
+ const checkPlans = (checker, checkerName, plans, lookup) => {
13206
13493
  return Cell.layer({
13207
- read: (command) => command.checker.check(command.checkerName, command.plans.map((plan) => plan.mutant)).pipe(Effect$1.map((answers) => ({
13208
- checkerName: command.checkerName,
13209
- requestedIds: command.plans.map((plan) => plan.mutant.id),
13210
- answers
13211
- }))),
13494
+ read: (command) => Effect$1.gen(function* () {
13495
+ const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
13496
+ onSome: (known) => selectedFromLookup(command.plans, known),
13497
+ onNone: () => partitionMutantsForWire(command.plans)
13498
+ });
13499
+ yield* logSkippedMutants(command.checkerName, partitioned.undescribable);
13500
+ if (partitioned.undescribable.length > 0) yield* Metric.update(checkerMutantsSkipped, partitioned.undescribable.length);
13501
+ yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
13502
+ const answers = yield* command.checker.check(command.checkerName, partitioned.wire);
13503
+ return {
13504
+ checkerName: command.checkerName,
13505
+ requestedIds: command.plans.map((plan) => plan.mutant.id),
13506
+ answers: {
13507
+ ...compileErrorAnswersOf(partitioned.undescribable),
13508
+ ...answers
13509
+ }
13510
+ };
13511
+ }),
13212
13512
  decode: (raw) => Result.succeed(CheckerCommand.make({
13213
13513
  checkerName: raw.checkerName,
13214
13514
  requestedIds: [...raw.requestedIds],
@@ -13221,19 +13521,29 @@ const checkPlans = (checker, checkerName, plans) => {
13221
13521
  }).run({
13222
13522
  checker,
13223
13523
  checkerName,
13224
- plans
13524
+ plans,
13525
+ lookup
13225
13526
  });
13226
13527
  };
13227
13528
  /**
13228
13529
  * Ask a checker how to group run plans, and get groups of run plans back.
13229
13530
  */
13230
- const groupPlans = (checker, checkerName, plans) => {
13531
+ const groupPlans = (checker, checkerName, plans, lookup) => {
13231
13532
  return Cell.layer({
13232
- read: (command) => command.checker.group(command.checkerName, command.plans.map((plan) => plan.mutant)).pipe(Effect$1.map((idGroups) => ({
13233
- checkerName: command.checkerName,
13234
- requestedIds: command.plans.map((plan) => plan.mutant.id),
13235
- idGroups
13236
- }))),
13533
+ read: (command) => Effect$1.gen(function* () {
13534
+ const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
13535
+ onSome: (known) => selectedFromLookup(command.plans, known),
13536
+ onNone: () => partitionMutantsForWire(command.plans)
13537
+ });
13538
+ yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
13539
+ const undescribableIds = undescribableIdsOf(partitioned.undescribable);
13540
+ const withoutSkipped = (yield* command.checker.group(command.checkerName, partitioned.wire)).map((group) => group.filter((id) => !undescribableIds.has(id))).filter((group) => group.length > 0);
13541
+ return {
13542
+ checkerName: command.checkerName,
13543
+ requestedIds: command.plans.map((plan) => plan.mutant.id),
13544
+ idGroups: [...singletonGroupsOf(partitioned.undescribable), ...withoutSkipped]
13545
+ };
13546
+ }),
13237
13547
  decode: (raw) => Result.succeed(CheckerCommand.make({
13238
13548
  checkerName: raw.checkerName,
13239
13549
  requestedIds: [...raw.requestedIds],
@@ -13246,46 +13556,15 @@ const groupPlans = (checker, checkerName, plans) => {
13246
13556
  }).run({
13247
13557
  checker,
13248
13558
  checkerName,
13249
- plans
13559
+ plans,
13560
+ lookup
13250
13561
  });
13251
13562
  };
13252
13563
  const checkGroupedPlans = (checker, checkerName, plans) => Effect$1.gen(function* () {
13253
- const groups = yield* groupPlans(checker, checkerName, plans);
13254
- return (yield* Effect$1.forEach(groups, (group) => checkPlans(checker, checkerName, group), { concurrency: 1 })).flat();
13255
- });
13256
- //#endregion
13257
- //#region src/mutant-result-mapping.ts
13258
- const toSchemaPosition = (pos) => ({
13259
- column: pos.column + 1,
13260
- line: pos.line + 1
13261
- });
13262
- const toSchemaLocation = (location) => ({
13263
- start: toSchemaPosition(location.start),
13264
- end: toSchemaPosition(location.end)
13265
- });
13266
- const checkStatusToMutantStatus = (_status) => "CompileError";
13267
- const mutantResult = (mutant, status, outcome = {}) => ({
13268
- _tag: "Mutant",
13269
- id: mutant.id,
13270
- fileName: mutant.fileName,
13271
- mutatorName: mutant.mutatorName,
13272
- replacement: mutant.replacement,
13273
- location: toSchemaLocation(mutant.location),
13274
- status,
13275
- coveredBy: mutant.coveredBy,
13276
- static: mutant.static,
13277
- testsCompleted: mutant.testsCompleted,
13278
- description: mutant.description,
13279
- ...outcome
13564
+ const lookup = lookupOf(partitionMutantsForWire(plans));
13565
+ const groups = yield* groupPlans(checker, checkerName, plans, lookup);
13566
+ return (yield* Effect$1.forEach(groups, (group) => checkPlans(checker, checkerName, group, lookup), { concurrency: 1 })).flat();
13280
13567
  });
13281
- const mapRunResult = (mutant, result) => Match.value(result).pipe(Match.discriminator("status")("error", (errored) => mutantResult(mutant, "RuntimeError", { statusReason: errored.errorMessage })), Match.discriminator("status")("killed", (killed) => mutantResult(mutant, "Killed", {
13282
- testsCompleted: killed.nrOfTests,
13283
- killedBy: [...killed.killedBy],
13284
- statusReason: killed.failureMessage
13285
- })), Match.discriminator("status")("timeout", (timedOut) => {
13286
- if (timedOut.reason === void 0) return mutantResult(mutant, "Timeout");
13287
- return mutantResult(mutant, "Timeout", { statusReason: timedOut.reason });
13288
- }), Match.discriminator("status")("survived", (survived) => mutantResult(mutant, "Survived", { testsCompleted: survived.nrOfTests })), Match.exhaustive);
13289
13568
  //#endregion
13290
13569
  //#region src/calculate-metrics.ts
13291
13570
  const countStatus = (mutants, status) => mutants.filter((mutant) => mutant.status === status).length;
@@ -13468,113 +13747,6 @@ const assembleTestFiles = (input) => {
13468
13747
  return Object.fromEntries(entries);
13469
13748
  };
13470
13749
  //#endregion
13471
- //#region package.json
13472
- var package_default = {
13473
- name: "@systemfsoftware/stryker-js",
13474
- version: "7.0.0",
13475
- repository: {
13476
- "type": "git",
13477
- "url": "git+https://github.com/systemfsoftware/stryker-js-effect.git",
13478
- "directory": "packages/stryker-js"
13479
- },
13480
- homepage: "https://github.com/systemfsoftware/stryker-js-effect",
13481
- bugs: { "url": "https://github.com/systemfsoftware/stryker-js-effect/issues" },
13482
- description: "The Stryker mutation testing tool — the stryker executable, the run engine, and the configuration surface.",
13483
- type: "module",
13484
- files: ["dist"],
13485
- bin: { "stryker": "./dist/main.mjs" },
13486
- scripts: {
13487
- "clean": "rimraf dist",
13488
- "build": "rimraf dist && tsdown --config tsdown.bin.config.ts && tsdown --config tsdown.config.ts",
13489
- "typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
13490
- "test": "vitest run",
13491
- "mutation": "stryker run",
13492
- "attw": "attw --pack .",
13493
- "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
13494
- "api:check": "api-extractor run",
13495
- "api:update": "api-extractor run --local",
13496
- "lint:tsgo": "effect-tsgo diagnostics --project tsconfig.json --format ${TSGO_FORMAT:-text}"
13497
- },
13498
- license: "Apache-2.0",
13499
- engines: { "node": ">=22.18.0" },
13500
- dependencies: {
13501
- "@effect/opentelemetry": "4.0.0-rc.112",
13502
- "@effect/platform-node": "catalog:",
13503
- "@effect/platform-node-shared": "catalog:",
13504
- "@noble/hashes": "catalog:",
13505
- "@opentelemetry/api": "^1.9.0",
13506
- "@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
13507
- "@opentelemetry/sdk-trace-base": "^2.2.0",
13508
- "@systemfsoftware/effect-cell-types": "catalog:"
13509
- },
13510
- peerDependencies: { "effect": "catalog:" },
13511
- devDependencies: {
13512
- "@effect/tsgo": "catalog:",
13513
- "@effect/vitest": "catalog:",
13514
- "@microsoft/api-extractor": "catalog:",
13515
- "@oxc-parser/binding-wasm32-wasi": "catalog:",
13516
- "@std/jsonc": "catalog:",
13517
- "@systemfsoftware/all": "catalog:",
13518
- "@systemfsoftware/arethetypeswrong-cli": "catalog:",
13519
- "@systemfsoftware/effect-gherkin-spec": "catalog:",
13520
- "@systemfsoftware/effect-schema-law": "catalog:",
13521
- "@systemfsoftware/effect-schema-vite": "catalog:",
13522
- "@systemfsoftware/stryker-config": "workspace:^",
13523
- "@systemfsoftware/stryker-ignorer-effect-schema-declarations": "catalog:stryker",
13524
- "@systemfsoftware/stryker-ignorer-in-source-vitest-block": "catalog:stryker",
13525
- "@systemfsoftware/stryker-ignorer-interface": "workspace:^",
13526
- "@systemfsoftware/stryker-js": "catalog:stryker",
13527
- "@systemfsoftware/stryker-js-html-reporter": "workspace:^",
13528
- "@systemfsoftware/stryker-js-instrumenter": "workspace:^",
13529
- "@systemfsoftware/stryker-js-plugin-interface": "workspace:^",
13530
- "@systemfsoftware/stryker-js-plugin-runtime": "workspace:^",
13531
- "@systemfsoftware/stryker-js-typescript-checker": "catalog:stryker",
13532
- "@systemfsoftware/stryker-js-vitest-runner": "catalog:stryker",
13533
- "@systemfsoftware/stryker-test-contribution": "catalog:stryker",
13534
- "@systemfsoftware/tsconfig": "catalog:",
13535
- "@systemfsoftware/tsdown-config": "workspace:^",
13536
- "@systemfsoftware/vitest-config": "workspace:^",
13537
- "@types/node": "catalog:",
13538
- "@vitest/coverage-v8": "catalog:",
13539
- "concurrently": "catalog:",
13540
- "mutation-testing-elements": "catalog:stryker",
13541
- "oxc-parser": "catalog:",
13542
- "oxlint": "catalog:",
13543
- "oxlint-tsgolint": "catalog:",
13544
- "rimraf": "catalog:",
13545
- "tsdown": "catalog:",
13546
- "typescript": "catalog:",
13547
- "vitest": "catalog:"
13548
- },
13549
- publishConfig: {
13550
- "access": "public",
13551
- "provenance": true,
13552
- "exports": {
13553
- ".": {
13554
- "types": "./dist/index.d.mts",
13555
- "default": "./dist/index.mjs"
13556
- },
13557
- "./config": {
13558
- "types": "./dist/config.d.mts",
13559
- "default": "./dist/config.mjs"
13560
- },
13561
- "./promises": {
13562
- "types": "./dist/promises.d.mts",
13563
- "default": "./dist/promises.mjs"
13564
- },
13565
- "./package.json": "./package.json"
13566
- }
13567
- },
13568
- exports: { "./package.json": "./package.json" }
13569
- };
13570
- //#endregion
13571
- //#region src/stryker-package.schema.ts
13572
- const PackageJsonSchema = Schema.Struct({ version: Schema.String });
13573
- //#endregion
13574
- //#region src/stryker-package.ts
13575
- const rawPackageJson = package_default;
13576
- const strykerVersion = Schema.decodeUnknownSync(PackageJsonSchema)(rawPackageJson).version;
13577
- //#endregion
13578
13750
  //#region src/verdict-envelope.ts
13579
13751
  const normalizeFileName = (fileName) => fileName.replaceAll("\\", "/");
13580
13752
  const VERDICT_ENVELOPE_SCHEMA_VERSION = "1.1";
@@ -13856,7 +14028,10 @@ const makeMutationReportingService = (input) => {
13856
14028
  const fs = yield* FileSystem.FileSystem;
13857
14029
  const dir = pathService.dirname(input.options.incrementalFile);
13858
14030
  yield* fs.makeDirectory(dir, { recursive: true });
13859
- const json = yield* S.encodeEffect(S.fromJsonString(S.Unknown, { space: 2 }))(report).pipe(Effect$1.orDie);
14031
+ const json = yield* S.encodeEffect(S.fromJsonString(S.Unknown, { space: 2 }))({
14032
+ incrementalVersion: strykerVersion,
14033
+ ...report
14034
+ }).pipe(Effect$1.orDie);
13860
14035
  yield* fs.writeFileString(input.options.incrementalFile, json);
13861
14036
  }
13862
14037
  return {
@@ -13875,6 +14050,7 @@ const makeMutationReportingService = (input) => {
13875
14050
  const slimIncrementalReport = (results) => Effect$1.gen(function* () {
13876
14051
  const { files, testFiles } = yield* assembleReport(results);
13877
14052
  return {
14053
+ incrementalVersion: strykerVersion,
13878
14054
  schemaVersion: "1.0",
13879
14055
  thresholds: input.options.thresholds,
13880
14056
  files,
@@ -13925,25 +14101,6 @@ const rememberedResultsOf = (mutants, remembered) => {
13925
14101
  onSome: (mutant) => [rememberedResultOf(mutant, entry)]
13926
14102
  }));
13927
14103
  };
13928
- const isRunPlan = (plan) => plan.plan === "Run";
13929
- const earlyResultOf = (plan) => Object.assign({}, plan.mutant, {
13930
- location: toSchemaLocation(plan.mutant.location),
13931
- status: plan.mutant.status ?? "Ignored"
13932
- });
13933
- const collectPlan = (plan, coveredPlans, earlyResults) => Match.value(plan).pipe(Match.when(isRunPlan, (runPlan) => {
13934
- coveredPlans.push(runPlan);
13935
- }), Match.orElse((earlyPlan) => {
13936
- earlyResults.push(earlyResultOf(earlyPlan));
13937
- }));
13938
- const partitionPlans = (plans) => {
13939
- const coveredPlans = [];
13940
- const earlyResults = [];
13941
- plans.forEach((plan) => collectPlan(plan, coveredPlans, earlyResults));
13942
- return {
13943
- coveredPlans,
13944
- earlyResults
13945
- };
13946
- };
13947
14104
  const VALID_MUTANT_STATUS_SET = /* @__PURE__ */ new Set([
13948
14105
  "Killed",
13949
14106
  "Survived",
@@ -13961,6 +14118,8 @@ const toReportedMutant = (mutant) => Object.assign(mutant, {
13961
14118
  coveredBy: mutant.coveredBy,
13962
14119
  static: mutant.static
13963
14120
  });
14121
+ const CHECKER_ACQUIRE_RETRIES = 2;
14122
+ const isCheckerCrash = (error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", () => true), Match.orElse(() => false));
13964
14123
  const makeCheckerPool = (prev, idGenerator) => Match.value(prev.options.checkers.length === 0).pipe(Match.when(true, () => Effect$1.as(Effect$1.void, void 0)), Match.orElse(() => Pool.make({
13965
14124
  acquire: Effect$1.forEach(prev.options.checkers, (checker) => Effect$1.gen(function* () {
13966
14125
  const resolved = yield* resolveConfiguredWorkerSpawn({
@@ -13971,7 +14130,10 @@ const makeCheckerPool = (prev, idGenerator) => Match.value(prev.options.checkers
13971
14130
  const service = yield* createCheckerFactory({
13972
14131
  ...prev.options,
13973
14132
  checkers: [checker]
13974
- }, prev.project.fileDescriptions, resolved.spawn.entrypoint, idGenerator, prev.sandbox.workingDirectory);
14133
+ }, prev.project.fileDescriptions, resolved.spawn.entrypoint, idGenerator, prev.sandbox.workingDirectory).pipe(Effect$1.retry({
14134
+ times: CHECKER_ACQUIRE_RETRIES,
14135
+ while: isCheckerCrash
14136
+ }));
13975
14137
  return {
13976
14138
  checkerName: resolved.name,
13977
14139
  checker: service
@@ -13994,6 +14156,28 @@ const checkPlansWithConfiguredCheckers = (checkerPool, plans, reporting) => Opti
13994
14156
  }).pipe(Effect$1.as(checked.filter(([, result]) => result.status === "passed").map(([plan]) => plan))))));
13995
14157
  }))
13996
14158
  });
14159
+ const isPlannable = (mutant) => Result.isSuccess(wireRecordOf(mutant));
14160
+ const DROPPED_IDS_IN_WARNING = 5;
14161
+ const partitionPlannable = (mutants) => {
14162
+ const plannable = [];
14163
+ const dropped = [];
14164
+ mutants.forEach((mutant) => {
14165
+ if (isPlannable(mutant)) plannable.push(mutant);
14166
+ else dropped.push(mutant);
14167
+ });
14168
+ return {
14169
+ plannable,
14170
+ dropped
14171
+ };
14172
+ };
14173
+ const droppedIdsOf = (dropped) => `${dropped.slice(0, DROPPED_IDS_IN_WARNING).map((mutant) => mutant.id).join(", ")}${Option.match(Option.liftPredicate(dropped.length, (count) => count > DROPPED_IDS_IN_WARNING), {
14174
+ onNone: () => "",
14175
+ onSome: (count) => `, +${count - DROPPED_IDS_IN_WARNING} more`
14176
+ })}`;
14177
+ const reportDroppedMutants = (dropped) => Match.value(dropped.length).pipe(Match.when(0, () => Effect$1.void), Match.orElse(() => Effect$1.gen(function* () {
14178
+ yield* Metric.update(checkerMutantsSkipped, dropped.length);
14179
+ yield* Effect$1.logWarning(`${dropped.length} mutant(s) cannot be described to a checker and were left out of the run (${droppedIdsOf(dropped)})`);
14180
+ })));
13997
14181
  const mutationTestCell$1 = Cell.layer({
13998
14182
  read: (command) => Effect$1.gen(function* () {
13999
14183
  yield* Scope.Scope;
@@ -14007,156 +14191,147 @@ const mutationTestCell$1 = Cell.layer({
14007
14191
  })),
14008
14192
  decide: admitMutationTest,
14009
14193
  encode: (outcome) => outcome,
14010
- write: (outcome, raw) => withPhaseSpan("mutationTest", {
14011
- mutantCount: raw.prev.mutants.length,
14012
- testCount: raw.prev.dryRunResult.tests.length
14013
- }, () => Effect$1.gen(function* () {
14014
- const decision = yield* Result.match(outcome, {
14015
- onFailure: (err) => Effect$1.fail(StageError.make({
14016
- stage: err.stage,
14017
- reason: err.reason,
14018
- cause: err
14019
- })),
14020
- onSuccess: (d) => Effect$1.succeed(d)
14021
- });
14022
- return yield* Match.value(decision).pipe(Match.tag("MutationTestDryRunOnly", () => Effect$1.gen(function* () {
14023
- const env = yield* RunEnvironment;
14024
- const queue = yield* RunEvents;
14025
- const nowEmit = yield* Clock.currentTimeMillis;
14026
- yield* Queue.offer(queue, PhaseEntered.make({
14027
- phase: "mutation-test",
14028
- elapsedMs: nowEmit - env.runStartedAt
14029
- }));
14030
- yield* Effect$1.logInfo("The dry-run has been completed successfully. No mutations have been executed.");
14031
- return {
14032
- results: [],
14033
- verdict: null
14034
- };
14035
- })), Match.tag("MutationTestNoTests", () => Effect$1.gen(function* () {
14036
- const env = yield* RunEnvironment;
14037
- const queue = yield* RunEvents;
14038
- const now = yield* Clock.currentTimeMillis;
14039
- const elapsed = Duration.millis(now - env.runStartedAt);
14040
- yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
14041
- const nowEmit = yield* Clock.currentTimeMillis;
14042
- yield* Queue.offer(queue, PhaseEntered.make({
14043
- phase: "mutation-test",
14044
- elapsedMs: nowEmit - env.runStartedAt
14045
- }));
14046
- return {
14047
- results: [],
14048
- verdict: null
14049
- };
14050
- })), Match.tag("MutationTestProceed", () => Effect$1.gen(function* () {
14051
- const prev = raw.prev;
14052
- const env = yield* RunEnvironment;
14053
- yield* Effect$1.gen(function* () {
14194
+ write: (outcome, raw) => {
14195
+ const { dropped, plannable: plannableMutants } = partitionPlannable(raw.prev.mutants);
14196
+ return withPhaseSpan("mutationTest", {
14197
+ mutantCount: raw.prev.mutants.length,
14198
+ skippedMutantCount: dropped.length,
14199
+ testCount: raw.prev.dryRunResult.tests.length
14200
+ }, () => Effect$1.gen(function* () {
14201
+ yield* reportDroppedMutants(dropped);
14202
+ const decision = yield* Result.match(outcome, {
14203
+ onFailure: (err) => Effect$1.fail(StageError.make({
14204
+ stage: err.stage,
14205
+ reason: err.reason,
14206
+ cause: err
14207
+ })),
14208
+ onSuccess: (d) => Effect$1.succeed(d)
14209
+ });
14210
+ return yield* Match.value(decision).pipe(Match.tag("MutationTestDryRunOnly", () => Effect$1.gen(function* () {
14211
+ const env = yield* RunEnvironment;
14212
+ const queue = yield* RunEvents;
14054
14213
  const nowEmit = yield* Clock.currentTimeMillis;
14214
+ yield* Queue.offer(queue, PhaseEntered.make({
14215
+ phase: "mutation-test",
14216
+ elapsedMs: nowEmit - env.runStartedAt
14217
+ }));
14218
+ yield* Effect$1.logInfo("The dry-run has been completed successfully. No mutations have been executed.");
14219
+ return {
14220
+ results: [],
14221
+ verdict: null
14222
+ };
14223
+ })), Match.tag("MutationTestNoTests", () => Effect$1.gen(function* () {
14224
+ const env = yield* RunEnvironment;
14055
14225
  const queue = yield* RunEvents;
14226
+ const now = yield* Clock.currentTimeMillis;
14227
+ const elapsed = Duration.millis(now - env.runStartedAt);
14228
+ yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
14229
+ const nowEmit = yield* Clock.currentTimeMillis;
14056
14230
  yield* Queue.offer(queue, PhaseEntered.make({
14057
14231
  phase: "mutation-test",
14058
14232
  elapsedMs: nowEmit - env.runStartedAt
14059
14233
  }));
14060
- });
14061
- const idGenerator = yield* IdGenerator;
14062
- const checkerPool = yield* makeCheckerPool(prev, idGenerator);
14063
- const testRunnerContext = {
14064
- options: prev.options,
14065
- fileDescriptions: prev.project.fileDescriptions,
14066
- sandboxWorkingDirectory: prev.sandbox.workingDirectory,
14067
- idGenerator,
14068
- retire: Effect$1.void,
14069
- testFiles: prev.project.testFiles.map((file) => prev.sandbox.sandboxFileFor(file))
14070
- };
14071
- const testRunnerPool = yield* Pool.make({
14072
- acquire: buildTestRunner(testRunnerContext, Effect$1.suspend(() => {
14073
- const runnerConfigured = prev.options.testRunner;
14074
- const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
14075
- return resolveConfiguredWorkerSpawn({
14076
- loaded: prev.loadedPlugins,
14077
- kind: "TestRunner",
14078
- configured: runnerConfigured
14079
- }).pipe(Effect$1.mapError(missingWorkerEntry("mutationTest", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
14080
- options: prev.options,
14081
- fileDescriptions: prev.project.fileDescriptions,
14082
- sandboxWorkingDirectory: prev.sandbox.workingDirectory,
14083
- workerEntrypoint: spawn.entrypoint,
14084
- idGenerator
14085
- })));
14086
- })),
14087
- size: prev.concurrency.testRunners
14088
- });
14089
- const reporting = makeMutationReportingService({
14090
- reporterStage: prev.reporterStage,
14091
- options: prev.options,
14092
- project: prev.project,
14093
- testCoverage: prev.testCoverage,
14094
- runId: env.runId,
14095
- resolvedMode: env.resolvedMode,
14096
- sandboxDirectory: prev.sandbox.workingDirectory,
14097
- basePath: env.basePath
14098
- });
14099
- const sandboxFileByName = Object.fromEntries([...MutableHashMap.keys(prev.project.filesToMutate)].map((name) => [name, prev.sandbox.sandboxFileFor(name)]));
14100
- const currentRelativeFiles = yield* readCurrentRelativeFiles(prev.project, env.basePath);
14101
- const incremental = incrementalDiff({
14102
- currentMutants: prev.mutants,
14103
- testCoverage: prev.testCoverage,
14104
- incrementalReport: prev.project.incrementalReport,
14105
- currentRelativeFiles,
14106
- basePath: env.basePath,
14107
- force: prev.options.force
14108
- });
14109
- const rememberedResults = rememberedResultsOf(prev.mutants, incremental.remembered);
14110
- yield* Effect$1.when(Effect$1.logInfo(`Incremental mode: reusing ${rememberedResults.length} mutant result(s), running ${incremental.mutants.length} mutant(s).`), Effect$1.succeed(rememberedResults.length > 0));
14111
- const { coveredPlans, earlyResults: noCoverageResults } = partitionPlans(yield* decidePlans(incremental.mutants, prev.testCoverage, {
14112
- disableBail: prev.options.disableBail,
14113
- timeoutMS: prev.options.timeoutMS,
14114
- timeoutFactor: prev.options.timeoutFactor,
14115
- ignoreStatic: prev.options.ignoreStatic
14116
- }, Duration.toMillis(prev.timeOverhead), void 0, sandboxFileByName));
14117
- const sortedPlans = [...coveredPlans].sort((a, b) => Number(a.runOptions.reloadEnvironment) - Number(b.runOptions.reloadEnvironment));
14118
- const allPlansForReporter = [...sortedPlans];
14119
- yield* offerReporterEvent(prev.reporterStage, MutationTestingPlanReady.make({
14120
- total: allPlansForReporter.length + noCoverageResults.length + rememberedResults.length,
14121
- plans: allPlansForReporter.map((plan) => ({
14122
- mutantId: plan.mutant.id,
14123
- plan: plan.plan,
14124
- netTime: plan.netTime,
14125
- reloadEnvironment: plan.runOptions.reloadEnvironment
14126
- }))
14127
- })).pipe(Effect$1.ignoreCause);
14128
- {
14129
- const queue2 = yield* RunEvents;
14130
- yield* Queue.offer(queue2, PlanKnown.make({ total: allPlansForReporter.length + noCoverageResults.length }));
14131
- }
14132
- const passedPlans = yield* checkPlansWithConfiguredCheckers(checkerPool, sortedPlans, reporting);
14133
- const testRunnerStream = Stream.fromIterable(passedPlans);
14134
- const plannedTotal = allPlansForReporter.length + noCoverageResults.length + rememberedResults.length;
14135
- const pathService = yield* Path.Path;
14136
- const progressQueue = yield* RunEvents;
14137
- const completedRef = yield* Ref.make(0);
14138
- const preparedStreamableOf = (result) => {
14139
- if (!isMutantStatus(result.status)) return;
14140
14234
  return {
14141
- status: result.status,
14142
- file: reportFileName(pathService.relative(env.basePath, result.fileName)),
14143
- location: toSchemaLocation(result.location)
14235
+ results: [],
14236
+ verdict: null
14144
14237
  };
14145
- };
14146
- const toStreamEvent = (result, completed, prepared) => MutantTested.make({
14147
- id: result.id,
14148
- status: prepared.status,
14149
- file: prepared.file,
14150
- location: prepared.location,
14151
- mutator: result.mutatorName,
14152
- replacement: result.replacement,
14153
- completed,
14154
- total: plannedTotal
14155
- });
14156
- const offerFinished = (result, prepared) => Effect$1.gen(function* () {
14157
- if (prepared === void 0) return;
14158
- const completed = yield* Ref.updateAndGet(completedRef, (n) => n + 1);
14159
- yield* Queue.offer(progressQueue, RunMutantTested.make({
14238
+ })), Match.tag("MutationTestProceed", () => Effect$1.gen(function* () {
14239
+ const prev = raw.prev;
14240
+ const env = yield* RunEnvironment;
14241
+ yield* Effect$1.gen(function* () {
14242
+ const nowEmit = yield* Clock.currentTimeMillis;
14243
+ const queue = yield* RunEvents;
14244
+ yield* Queue.offer(queue, PhaseEntered.make({
14245
+ phase: "mutation-test",
14246
+ elapsedMs: nowEmit - env.runStartedAt
14247
+ }));
14248
+ });
14249
+ const idGenerator = yield* IdGenerator;
14250
+ const checkerPool = yield* makeCheckerPool(prev, idGenerator);
14251
+ const testRunnerContext = {
14252
+ options: prev.options,
14253
+ fileDescriptions: prev.project.fileDescriptions,
14254
+ sandboxWorkingDirectory: prev.sandbox.workingDirectory,
14255
+ idGenerator,
14256
+ retire: Effect$1.void,
14257
+ testFiles: prev.project.testFiles.map((file) => prev.sandbox.sandboxFileFor(file))
14258
+ };
14259
+ const testRunnerPool = yield* Pool.make({
14260
+ acquire: buildTestRunner(testRunnerContext, Effect$1.suspend(() => {
14261
+ const runnerConfigured = prev.options.testRunner;
14262
+ const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
14263
+ return resolveConfiguredWorkerSpawn({
14264
+ loaded: prev.loadedPlugins,
14265
+ kind: "TestRunner",
14266
+ configured: runnerConfigured
14267
+ }).pipe(Effect$1.mapError(missingWorkerEntry("mutationTest", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
14268
+ options: prev.options,
14269
+ fileDescriptions: prev.project.fileDescriptions,
14270
+ sandboxWorkingDirectory: prev.sandbox.workingDirectory,
14271
+ workerEntrypoint: spawn.entrypoint,
14272
+ idGenerator
14273
+ })));
14274
+ })),
14275
+ size: prev.concurrency.testRunners
14276
+ });
14277
+ const reporting = makeMutationReportingService({
14278
+ reporterStage: prev.reporterStage,
14279
+ options: prev.options,
14280
+ project: prev.project,
14281
+ testCoverage: prev.testCoverage,
14282
+ runId: env.runId,
14283
+ resolvedMode: env.resolvedMode,
14284
+ sandboxDirectory: prev.sandbox.workingDirectory,
14285
+ basePath: env.basePath
14286
+ });
14287
+ const sandboxFileByName = Object.fromEntries([...MutableHashMap.keys(prev.project.filesToMutate)].map((name) => [name, prev.sandbox.sandboxFileFor(name)]));
14288
+ const currentRelativeFiles = yield* readCurrentRelativeFiles(prev.project, env.basePath);
14289
+ const incremental = incrementalDiff({
14290
+ currentMutants: plannableMutants,
14291
+ testCoverage: prev.testCoverage,
14292
+ incrementalReport: prev.project.incrementalReport,
14293
+ currentRelativeFiles,
14294
+ basePath: env.basePath,
14295
+ force: prev.options.force
14296
+ });
14297
+ const rememberedResults = rememberedResultsOf(plannableMutants, incremental.remembered);
14298
+ yield* Effect$1.when(Effect$1.logInfo(`Incremental mode: reusing ${rememberedResults.length} mutant result(s), running ${incremental.mutants.length} mutant(s).`), Effect$1.succeed(rememberedResults.length > 0));
14299
+ const { runPlans, earlyResults: noCoverageResults } = partitionRunPlans(yield* decidePlans(incremental.mutants, prev.testCoverage, {
14300
+ disableBail: prev.options.disableBail,
14301
+ timeoutMS: prev.options.timeoutMS,
14302
+ timeoutFactor: prev.options.timeoutFactor,
14303
+ ignoreStatic: prev.options.ignoreStatic
14304
+ }, Duration.toMillis(prev.timeOverhead), void 0, sandboxFileByName));
14305
+ const sortedPlans = sortRunPlans(runPlans);
14306
+ const allPlansForReporter = [...sortedPlans];
14307
+ yield* offerReporterEvent(prev.reporterStage, MutationTestingPlanReady.make({
14308
+ total: allPlansForReporter.length + noCoverageResults.length + rememberedResults.length,
14309
+ plans: allPlansForReporter.map((plan) => ({
14310
+ mutantId: plan.mutant.id,
14311
+ plan: plan.plan,
14312
+ netTime: plan.netTime,
14313
+ reloadEnvironment: plan.runOptions.reloadEnvironment
14314
+ }))
14315
+ })).pipe(Effect$1.ignoreCause);
14316
+ {
14317
+ const queue2 = yield* RunEvents;
14318
+ yield* Queue.offer(queue2, PlanKnown.make({ total: allPlansForReporter.length + noCoverageResults.length }));
14319
+ }
14320
+ const passedPlans = yield* checkPlansWithConfiguredCheckers(checkerPool, sortedPlans, reporting);
14321
+ const testRunnerStream = Stream.fromIterable(passedPlans);
14322
+ const plannedTotal = allPlansForReporter.length + noCoverageResults.length + rememberedResults.length;
14323
+ const pathService = yield* Path.Path;
14324
+ const progressQueue = yield* RunEvents;
14325
+ const completedRef = yield* Ref.make(0);
14326
+ const preparedStreamableOf = (result) => {
14327
+ if (!isMutantStatus(result.status)) return;
14328
+ return {
14329
+ status: result.status,
14330
+ file: reportFileName(pathService.relative(env.basePath, result.fileName)),
14331
+ location: toSchemaLocation(result.location)
14332
+ };
14333
+ };
14334
+ const toStreamEvent = (result, completed, prepared) => MutantTested.make({
14160
14335
  id: result.id,
14161
14336
  status: prepared.status,
14162
14337
  file: prepared.file,
@@ -14165,71 +14340,85 @@ const mutationTestCell$1 = Cell.layer({
14165
14340
  replacement: result.replacement,
14166
14341
  completed,
14167
14342
  total: plannedTotal
14343
+ });
14344
+ const offerFinished = (result, prepared) => Effect$1.gen(function* () {
14345
+ if (prepared === void 0) return;
14346
+ const completed = yield* Ref.updateAndGet(completedRef, (n) => n + 1);
14347
+ yield* Queue.offer(progressQueue, RunMutantTested.make({
14348
+ id: result.id,
14349
+ status: prepared.status,
14350
+ file: prepared.file,
14351
+ location: prepared.location,
14352
+ mutator: result.mutatorName,
14353
+ replacement: result.replacement,
14354
+ completed,
14355
+ total: plannedTotal
14356
+ }));
14357
+ return completed;
14358
+ });
14359
+ const reportStreamTested = (result, completed, prepared) => offerReporterEvent(prev.reporterStage, toStreamEvent(result, completed, prepared)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Reporter stream failed handling mutantTested", cause)));
14360
+ const offerStreamTested = (result, completed, prepared) => Option.match(Option.all([Option.fromNullishOr(completed), Option.fromNullishOr(prepared)]), {
14361
+ onNone: () => Effect$1.void,
14362
+ onSome: ([done, streamable]) => reportStreamTested(result, done, streamable)
14363
+ });
14364
+ const announceSettledMutant = (result) => Effect$1.gen(function* () {
14365
+ const prepared = preparedStreamableOf(result);
14366
+ const completed = yield* offerFinished(result, prepared);
14367
+ yield* offerStreamTested(result, completed, prepared);
14368
+ });
14369
+ yield* Effect$1.forEach([...rememberedResults, ...noCoverageResults], announceSettledMutant, {
14370
+ concurrency: 1,
14371
+ discard: true
14372
+ });
14373
+ const completedMutants = yield* Ref.make([...rememberedResults, ...noCoverageResults]);
14374
+ const checkpointGate = yield* Semaphore.make(1);
14375
+ yield* reporting.checkpoint(yield* Ref.get(completedMutants)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
14376
+ const persist = (result) => checkpointGate.withPermits(1)(Effect$1.gen(function* () {
14377
+ const next = yield* Ref.updateAndGet(completedMutants, (prev) => [...prev, result]);
14378
+ yield* reporting.checkpoint(next).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
14168
14379
  }));
14169
- return completed;
14170
- });
14171
- const reportStreamTested = (result, completed, prepared) => offerReporterEvent(prev.reporterStage, toStreamEvent(result, completed, prepared)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Reporter stream failed handling mutantTested", cause)));
14172
- const offerStreamTested = (result, completed, prepared) => Option.match(Option.all([Option.fromNullishOr(completed), Option.fromNullishOr(prepared)]), {
14173
- onNone: () => Effect$1.void,
14174
- onSome: ([done, streamable]) => reportStreamTested(result, done, streamable)
14175
- });
14176
- const announceSettledMutant = (result) => Effect$1.gen(function* () {
14177
- const prepared = preparedStreamableOf(result);
14178
- const completed = yield* offerFinished(result, prepared);
14179
- yield* offerStreamTested(result, completed, prepared);
14180
- });
14181
- yield* Effect$1.forEach([...rememberedResults, ...noCoverageResults], announceSettledMutant, {
14182
- concurrency: 1,
14183
- discard: true
14184
- });
14185
- const completedMutants = yield* Ref.make([...rememberedResults, ...noCoverageResults]);
14186
- const checkpointGate = yield* Semaphore.make(1);
14187
- yield* reporting.checkpoint(yield* Ref.get(completedMutants)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
14188
- const persist = (result) => checkpointGate.withPermits(1)(Effect$1.gen(function* () {
14189
- const next = yield* Ref.updateAndGet(completedMutants, (prev) => [...prev, result]);
14190
- yield* reporting.checkpoint(next).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
14191
- }));
14192
- const runResults = yield* withPhaseSpan("mutationTest.batch", {
14193
- total: plannedTotal,
14194
- testRunners: prev.concurrency.testRunners
14195
- }, () => Stream.mapEffect(testRunnerStream, (plan) => Effect$1.scoped(Effect$1.gen(function* () {
14196
- const pool = testRunnerPool;
14197
- const runner = yield* Pool.get(pool);
14198
- const result = yield* runner.mutantRun(plan.runOptions).pipe(Effect$1.catchTags({
14199
- OutOfMemoryError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error)),
14200
- ChildProcessCrashedError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error))
14201
- }));
14202
- const reported = yield* reporting.reportMutantRunResult(toReportedMutant(plan.mutant), result);
14203
- const prepared = preparedStreamableOf(reported);
14204
- const finished = yield* offerFinished(reported, prepared);
14205
- yield* offerStreamTested(reported, finished, prepared);
14206
- yield* persist(reported);
14207
- return reported;
14208
- })), { concurrency: Math.max(1, prev.concurrency.testRunners) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
14209
- const allResults = [
14210
- ...rememberedResults,
14211
- ...noCoverageResults,
14212
- ...runResults
14213
- ];
14214
- const outcomeResult = yield* reporting.reportAll(allResults);
14215
- const doneNow = yield* Clock.currentTimeMillis;
14216
- const elapsed = Duration.millis(doneNow - env.runStartedAt);
14217
- yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
14218
- return outcomeResult;
14219
- })), Match.exhaustive);
14220
- })).pipe(Effect$1.mapError((cause) => Match.value(cause).pipe(Match.tag("StageError", (stage) => stage), Match.tag("CheckerAnsweredUnrequested", (breach) => StageError.make({
14221
- stage: "mutationTest",
14222
- reason: `Checker "${breach.checkerName}" answered about mutants it was not asked about (${breach.phase} phase): ${breach.unrequestedIds.join(", ")}`,
14223
- cause: breach
14224
- })), Match.tag("CheckerSkippedRequested", (breach) => StageError.make({
14225
- stage: "mutationTest",
14226
- reason: `Checker "${breach.checkerName}" skipped requested mutants (${breach.phase} phase): ${breach.missingIds.join(", ")}`,
14227
- cause: breach
14228
- })), Match.tag("ChildProcessCrashedError", "OutOfMemoryError", "PlatformError", "TestRunnerFailed", () => StageError.make({
14229
- stage: "mutationTest",
14230
- reason: "Mutation testing failed",
14231
- cause
14232
- })), Match.exhaustive)))
14380
+ const runResults = yield* withPhaseSpan("mutationTest.batch", {
14381
+ total: plannedTotal,
14382
+ testRunners: prev.concurrency.testRunners
14383
+ }, () => Stream.mapEffect(testRunnerStream, (plan) => Effect$1.scoped(Effect$1.gen(function* () {
14384
+ const pool = testRunnerPool;
14385
+ const runner = yield* Pool.get(pool);
14386
+ const result = yield* runner.mutantRun(plan.runOptions).pipe(Effect$1.catchTags({
14387
+ OutOfMemoryError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error)),
14388
+ ChildProcessCrashedError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error))
14389
+ }));
14390
+ const reported = yield* reporting.reportMutantRunResult(toReportedMutant(plan.mutant), result);
14391
+ const prepared = preparedStreamableOf(reported);
14392
+ const finished = yield* offerFinished(reported, prepared);
14393
+ yield* offerStreamTested(reported, finished, prepared);
14394
+ yield* persist(reported);
14395
+ return reported;
14396
+ })), { concurrency: Math.max(1, prev.concurrency.testRunners) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
14397
+ const allResults = [
14398
+ ...rememberedResults,
14399
+ ...noCoverageResults,
14400
+ ...runResults
14401
+ ];
14402
+ const outcomeResult = yield* reporting.reportAll(allResults);
14403
+ const doneNow = yield* Clock.currentTimeMillis;
14404
+ const elapsed = Duration.millis(doneNow - env.runStartedAt);
14405
+ yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
14406
+ return outcomeResult;
14407
+ })), Match.exhaustive);
14408
+ })).pipe(Effect$1.mapError((cause) => Match.value(cause).pipe(Match.tag("StageError", (stage) => stage), Match.tag("CheckerAnsweredUnrequested", (breach) => StageError.make({
14409
+ stage: "mutationTest",
14410
+ reason: `Checker "${breach.checkerName}" answered about mutants it was not asked about (${breach.phase} phase): ${breach.unrequestedIds.join(", ")}`,
14411
+ cause: breach
14412
+ })), Match.tag("CheckerSkippedRequested", (breach) => StageError.make({
14413
+ stage: "mutationTest",
14414
+ reason: `Checker "${breach.checkerName}" skipped requested mutants (${breach.phase} phase): ${breach.missingIds.join(", ")}`,
14415
+ cause: breach
14416
+ })), Match.tag("ChildProcessCrashedError", "OutOfMemoryError", "PlatformError", "TestRunnerFailed", () => StageError.make({
14417
+ stage: "mutationTest",
14418
+ reason: "Mutation testing failed",
14419
+ cause
14420
+ })), Match.exhaustive)));
14421
+ }
14233
14422
  });
14234
14423
  //#endregion
14235
14424
  //#region src/reporter-output.ts
@@ -15437,4 +15626,4 @@ const strykerCell = (options, targetMutatePatterns) => Effect$1.gen(function* ()
15437
15626
  }, prepareCommandOf(options, patterns));
15438
15627
  });
15439
15628
  //#endregion
15440
- export { ConfigFileNotFoundError as $, ReporterInitOptions as $t, mergeConfigs as A, resolveExitCode as At, classifyWorkerExit as B, optionsPath$1 as Bt, spawnReporterWorker as C, RunFailed as Ct, importModule as D, VerdictReached as Dt, forkCoreSchema as E, RunStarted as Et, isCommandRunner as F, deepFreeze$1 as Ft, isVmRunner as G, MutantResultSchema as Gt, OutOfMemoryError as H, ExitClass as Ht, WorkerLauncher as I, defaultOptions$1 as It, vmTestRunner as J, MutationTestResultSchema$1 as Jt, vmRunnerCapabilities as K, MutantTested as Kt, connectRetry as L, findUnserializables$1 as Lt, resolveExtends as M, CONFIG_SYNTAX_HELP$1 as Mt, validateOptions as N, SUPPORTED_CONFIG_FILE_NAMES$1 as Nt, initialExtendsStepState as O, EXIT_CODE as Ot, buildTestRunner as P, createDefaultOptions$1 as Pt, ConfigFileInvalidError as Q, ReporterEventBatch as Qt, makeWorkerClient as R, isModuleSpecifier$1 as Rt, reporterWorkerFactory as S, PlanKnown as St, describeErrors as T, RunPhase as Tt, WorkerBootTimeoutError as U, FileResultSchema as Ut, ChildProcessCrashedError as V, DryRunCompleted as Vt, VmRunner as W, MetricsSchema as Wt, ConfigDocumentSchema as X, ReporterAck as Xt, StrykerError as Y, MutationTestingPlanReady as Yt, ConfigError as Z, ReporterDrained as Zt, createFileMatcher as _, RunEvents as _t, shouldKeepTempDir as a, LocationSchema$1 as an, ExtendsStepRefused as at, RunEnvironment as b, HelpRendered as bt, buildVerdictEnvelope as c, PositionSchema$2 as cn, ImportedModuleSchema as ct, strykerVersion as d, ReadConfigCommand as dt, ReporterRpcs as en, ConfigFileUnreadableError as et, calculateMetrics as f, extendsPropertySchema as ft, CheckerSkippedRequested as g, StageError as gt, CheckerAnsweredUnrequested as h, toRelativeNormalizedFileName as ht, mutationTestCell as i, WorkerPluginKind as in, ExtendsStepRead as it, readConfig as j, verdictExitClass as jt, loadConfigCell as k, highestExitClass as kt, generateRunId as l, MergeCommand as lt, checkGroupedPlans as m, survivorsPriorReport as mt, RUN_EVENTS_QUEUE_BOUND as n, TestRunnerConfigSchema as nn, ExtendsStepDocumentSchema as nt, ACTIONABLE_STATUSES as o, MutantActivationSchema as on, ExtendsStepResolve as ot, countMutants as p, forkOptionsSchema as pt, vmRunnerName as q, MutationTestReportReady as qt, makeRunLayer as r, TraceContextMiddleware as rn, ExtendsStepDone as rt, VERDICT_ENVELOPE_SCHEMA_VERSION as s, MutantStatusSchema as sn, ExtendsStepStateSchema as st, strykerCell as t, StrykerOptionsSchema as tn, ConfigFileUnsupportedError as tt, isActionableStatus as u, MergeResult as ut, matchesFile as v, RunIdentity as vt, decideExtendsStep as w, RunMutantTested as wt, REPORTER_EVENT_BATCH_BOUND as x, PhaseEntered as xt, IncrementalReportSchema as y, Heartbeat as yt, IdGenerator as z, isWarningEnabled$1 as zt };
15629
+ export { StrykerError as $, FileResultSchema as $t, describeErrors as A, RunStarted as At, WorkerLauncher as B, defaultOptions$1 as Bt, strykerVersion as C, Heartbeat as Ct, reporterWorkerFactory as D, RunFailed as Dt, REPORTER_EVENT_BATCH_BOUND as E, PlanKnown as Et, mergeConfigs as F, verdictExitClass as Ft, ChildProcessCrashedError as G, CheckerCheckResult as Gt, makeWorkerClient as H, isModuleSpecifier$1 as Ht, readConfig as I, CONFIG_SYNTAX_HELP$1 as It, VmRunner as J, CheckerMutantWire as Jt, OutOfMemoryError as K, CheckerFailed as Kt, validateOptions as L, SUPPORTED_CONFIG_FILE_NAMES$1 as Lt, importModule as M, EXIT_CODE as Mt, initialExtendsStepState as N, highestExitClass as Nt, spawnReporterWorker as O, RunMutantTested as Ot, loadConfigCell as P, resolveExitCode as Pt, vmTestRunner as Q, ExitClass as Qt, buildTestRunner as R, createDefaultOptions$1 as Rt, matchesFile as S, RunIdentity as St, RunEnvironment as T, PhaseEntered as Tt, IdGenerator as U, isWarningEnabled$1 as Ut, connectRetry as V, findUnserializables$1 as Vt, classifyWorkerExit as W, optionsPath$1 as Wt, vmRunnerCapabilities as X, CheckerRpcs as Xt, isVmRunner as Y, CheckerRequest as Yt, vmRunnerName as Z, DryRunCompleted as Zt, checkerProcessCrashes as _, MutantStatusSchema as _n, forkOptionsSchema as _t, shouldKeepTempDir as a, MutationTestingPlanReady as an, ConfigFileUnsupportedError as at, CheckerSkippedRequested as b, StageError as bt, buildVerdictEnvelope as c, ReporterEventBatch as cn, ExtendsStepRead as ct, calculateMetrics as d, StrykerOptionsSchema as dn, ExtendsStepStateSchema as dt, MetricsSchema as en, ConfigDocumentSchema as et, countMutants as f, TestRunnerConfigSchema as fn, ImportedModuleSchema as ft, checkerMutantsSkipped as g, MutantActivationSchema as gn, extendsPropertySchema as gt, checkerMutantsChecked as h, LocationSchema$1 as hn, ReadConfigCommand as ht, mutationTestCell as i, MutationTestResultSchema$1 as in, ConfigFileUnreadableError as it, forkCoreSchema as j, VerdictReached as jt, decideExtendsStep as k, RunPhase as kt, generateRunId as l, ReporterInitOptions as ln, ExtendsStepRefused as lt, checkerDuration as m, WorkerPluginKind as mn, MergeResult as mt, RUN_EVENTS_QUEUE_BOUND as n, MutantTested as nn, ConfigFileInvalidError as nt, ACTIONABLE_STATUSES as o, ReporterAck as on, ExtendsStepDocumentSchema as ot, checkGroupedPlans as p, TraceContextMiddleware as pn, MergeCommand as pt, WorkerBootTimeoutError as q, CheckerGroupResult as qt, makeRunLayer as r, MutationTestReportReady as rn, ConfigFileNotFoundError as rt, VERDICT_ENVELOPE_SCHEMA_VERSION as s, ReporterDrained as sn, ExtendsStepDone as st, strykerCell as t, MutantResultSchema as tn, ConfigError as tt, isActionableStatus as u, ReporterRpcs as un, ExtendsStepResolve as ut, checkerRpcFailures as v, PositionSchema$2 as vn, survivorsPriorReport as vt, IncrementalReportSchema as w, HelpRendered as wt, createFileMatcher as x, RunEvents as xt, CheckerAnsweredUnrequested as y, toRelativeNormalizedFileName as yt, isCommandRunner as z, deepFreeze$1 as zt };