@systemfsoftware/stryker-js 11.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.
@@ -1,7 +1,6 @@
1
- import { a as Reporter_exports, c as ErrorText_exports, d as Mutant_exports, i as Report_exports, l as Format_exports, n as Options_exports, o as TestRunner_exports, r as Plugin_exports, s as Trace_exports$1, t as Checker_exports, u as Instrument_exports } from "./dist-ClUhPgb6.mjs";
2
- import { t as StrykerConfig } from "./stryker-config.schema-CCnDtVuW.mjs";
3
- import { D as VerdictReached, E as SkippedReported, _ as PluginsReported, b as RunFailed$1, c as Heartbeat, f as OutputMode, i as FormatRegistryResolved, l as HelpRendered, m as PlanKnown, n as StageError, p as PhaseEntered, t as PrepareError, u as ModeSignal, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-B_qdjbe9.mjs";
4
- import { Cell, Sandwich, Workflow } from "@systemfsoftware/effect-cell-types";
1
+ import { A as Plugin_exports, D as VerdictReached, E as SkippedReported, F as ErrorText_exports, I as Format_exports, L as Instrument_exports, M as Reporter_exports, N as TestRunner_exports, O as Checker_exports, P as Trace_exports$1, R as Mutant_exports, _ as PluginsReported, b as RunFailed$1, c as Heartbeat, f as OutputMode, i as FormatRegistryResolved, j as Report_exports, k as Options_exports, l as HelpRendered, m as PlanKnown, n as StageError, p as PhaseEntered, t as PrepareError, u as ModeSignal, w as RunStarted, x as RunMutantTested, y as RunEvent } from "./Run.schema-BaK_pxuK.mjs";
2
+ import { t as mergeConfig } from "./merge-config-ByZJPeQZ.mjs";
3
+ import { Blueprint, Cell, Handle, Sandwich, Workflow } from "@systemfsoftware/effect-cell-types";
5
4
  import * as Boolean$1 from "effect/Boolean";
6
5
  import * as Match from "effect/Match";
7
6
  import * as Option$1 from "effect/Option";
@@ -9,22 +8,21 @@ import * as Predicate$1 from "effect/Predicate";
9
8
  import * as Result from "effect/Result";
10
9
  import * as S from "effect/Schema";
11
10
  import * as Effect$1 from "effect/Effect";
12
- import { absurd, dual } from "effect/Function";
11
+ import { absurd, dual, pipe } from "effect/Function";
13
12
  import * as Arr from "effect/Array";
14
13
  import * as SchemaGetter$1 from "effect/SchemaGetter";
15
- import { Prototype } from "effect/Pipeable";
16
14
  import * as Context from "effect/Context";
17
- import { Boolean, Option, Predicate, Schema, SchemaGetter } from "effect";
15
+ import { Boolean, Option, Predicate, Schema, SchemaGetter, SchemaTransformation } from "effect";
16
+ import * as Exit from "effect/Exit";
18
17
  import * as RpcMiddleware from "effect/unstable/rpc/RpcMiddleware";
19
- import * as Metric from "effect/Metric";
20
18
  import * as Cause from "effect/Cause";
19
+ import * as Metric from "effect/Metric";
21
20
  import * as SchemaTransformation$1 from "effect/SchemaTransformation";
22
21
  import * as Record from "effect/Record";
23
22
  import * as Path from "effect/Path";
24
23
  import * as Config from "effect/Config";
25
24
  import * as FileSystem from "effect/FileSystem";
26
25
  import * as Clock from "effect/Clock";
27
- import * as Exit from "effect/Exit";
28
26
  import * as Stream from "effect/Stream";
29
27
  import * as ChildProcess from "effect/unstable/process/ChildProcess";
30
28
  import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
@@ -61,15 +59,15 @@ import { RpcClientError } from "effect/unstable/rpc/RpcClientError";
61
59
  import * as Socket from "effect/unstable/socket/Socket";
62
60
  import * as NodeFileSystem$1 from "@effect/platform-node-shared/NodeFileSystem";
63
61
  import * as NodePath$1 from "@effect/platform-node-shared/NodePath";
64
- import * as Equivalence from "effect/Equivalence";
65
62
  import { badArgument } from "effect/PlatformError";
66
- import { Session } from "@systemfsoftware/stryker-vm-harness";
63
+ import * as Equivalence from "effect/Equivalence";
67
64
  import * as Console from "effect/Console";
68
65
  import * as Formatter from "effect/Formatter";
69
66
  import * as Runtime from "effect/Runtime";
70
67
  import * as Order from "effect/Order";
71
68
  import * as CliError from "effect/unstable/cli/CliError";
72
69
  import { sha256 } from "@noble/hashes/sha2.js";
70
+ import "effect/Deferred";
73
71
  import * as Pool from "effect/Pool";
74
72
  import * as Semaphore from "effect/Semaphore";
75
73
  //#region src/admit-checker-answer.workflow.ts
@@ -180,14 +178,15 @@ var WorkerBootTimeoutError = class extends Schema.TaggedError()("WorkerBootTimeo
180
178
  };
181
179
  //#endregion
182
180
  //#region src/Checker/Checker.handle.ts
183
- const TypeId$4 = Symbol.for("@systemfsoftware/stryker-js/CheckerHandle");
181
+ const TypeId$10 = Symbol.for("~systemfsoftware/stryker-js/Checker");
184
182
  const checkerDuration = Metric.timer("stryker.checker.duration", { description: "Checker worker RPC duration in milliseconds" });
185
183
  const checkerMutantsChecked = Metric.counter("stryker.checker.mutants.checked", { description: "Total number of mutants a checker worker answered for" });
186
184
  const checkerRpcFailures = Metric.counter("stryker.checker.rpc_failures", {
187
185
  description: "Checker worker RPC calls that did not complete, excluding interruptions",
188
186
  incremental: true
189
187
  });
190
- const ClientTypeId = Symbol.for("@systemfsoftware/stryker-js/CheckerHandle/client");
188
+ const CheckerHandle = Handle.make()(TypeId$10);
189
+ const isCheckerHandle = CheckerHandle.is;
191
190
  const connectionCrashed = (cause) => ChildProcessCrashedError.make({
192
191
  pid: 0,
193
192
  exit: {
@@ -200,24 +199,17 @@ const recordCheckerCall = (spanName, checkerName, mutants, call) => call.pipe(Ef
200
199
  "stryker.checker.name": checkerName,
201
200
  "stryker.mutants.count": mutants.length
202
201
  } }), Effect$1.timed, Effect$1.onExit((exit) => Match.value(exit).pipe(Match.tag("Success", () => Metric.update(checkerMutantsChecked, mutants.length)), Match.tag("Failure", (failure) => failure.cause.pipe(Cause.hasInterruptsOnly, Match.value, 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) => Match.value(error).pipe(Match.tag("CheckerFailed", (failed) => failed), Match.orElse((e) => connectionCrashed(e.message)))));
203
- const checkOf = (self, checkerName, mutants) => recordCheckerCall("stryker.checker.check", checkerName, mutants, self[ClientTypeId].check({
202
+ const checkOf = (self, checkerName, mutants) => recordCheckerCall("stryker.checker.check", checkerName, mutants, CheckerHandle.slot(self).check({
204
203
  checkerName,
205
204
  mutants: [...mutants]
206
205
  }));
207
- const groupOf$1 = (self, checkerName, mutants) => recordCheckerCall("stryker.checker.group", checkerName, mutants, self[ClientTypeId].group({
206
+ const groupOf$1 = (self, checkerName, mutants) => recordCheckerCall("stryker.checker.group", checkerName, mutants, CheckerHandle.slot(self).group({
208
207
  checkerName,
209
208
  mutants: [...mutants]
210
209
  }));
211
- const makeCheckerHandle = (client) => {
212
- const self = {
213
- [TypeId$4]: TypeId$4,
214
- [ClientTypeId]: client,
215
- ...Prototype,
216
- check: (checkerName, mutants) => checkOf(self, checkerName, mutants),
217
- group: (checkerName, mutants) => groupOf$1(self, checkerName, mutants)
218
- };
219
- return self;
220
- };
210
+ const check = dual((args) => isCheckerHandle(args[0]), (self, checkerName, mutants) => checkOf(self, checkerName, mutants));
211
+ const group = dual((args) => isCheckerHandle(args[0]), (self, checkerName, mutants) => groupOf$1(self, checkerName, mutants));
212
+ const makeCheckerHandle = (client) => CheckerHandle.make({}, client);
221
213
  //#endregion
222
214
  //#region src/Checker/Checker.schema.ts
223
215
  var UndescribableMutant = class extends S.TaggedError()("UndescribableMutant", {
@@ -245,7 +237,7 @@ const CheckerMutantFromMutant = S.decodeTo(CheckerMutant, SchemaTransformation$1
245
237
  })
246
238
  }))(Mutant_exports.Mutant);
247
239
  //#endregion
248
- //#region src/Checker/Checker.cell.ts
240
+ //#region src/Checker/Checker.parts.ts
249
241
  const plansById = (plans) => new Map(plans.map((plan) => [plan.mutant.id, plan]));
250
242
  const missingPlanIds = (plans, present) => plans.map((plan) => plan.mutant.id).filter((id) => !present.has(id));
251
243
  const answeredPlanIds = (paired) => new Set(paired.map(([plan]) => plan.mutant.id));
@@ -313,8 +305,8 @@ const pairGroups = (checkerName, plans, idGroups) => Match.value(partitionGroups
313
305
  unrequestedIds: [...partition.unrequested],
314
306
  requestedIds: plans.map((plan) => plan.mutant.id)
315
307
  }))), Match.orElse((partition) => admitGroupedPlans(checkerName, plans, partition)));
316
- const writeDecidedAnswers = (plans, checkerName, answers) => Effect$1.fromResult(pairCheckResults(checkerName, plans, Object.fromEntries(answers.map((answer) => [answer.id, answer.result]))));
317
- const writeDecidedGroups = (plans, checkerName, idGroups) => Effect$1.fromResult(pairGroups(checkerName, plans, idGroups));
308
+ const writeDecidedAnswers = (input) => Effect$1.fromResult(pairCheckResults(input.checkerName, input.plans, Object.fromEntries(input.answers.map((answer) => [answer.id, answer.result]))));
309
+ const writeDecidedGroups = (input) => Effect$1.fromResult(pairGroups(input.checkerName, input.plans, input.idGroups));
318
310
  const wireRecordOf = (mutant) => Result.mapError(S.decodeResult(CheckerMutantFromMutant)(mutant), (error) => UndescribableMutant.make({
319
311
  id: mutant.id,
320
312
  fileName: mutant.fileName,
@@ -356,7 +348,7 @@ const skippedIdsOf = (undescribable) => `${undescribable.slice(0, SKIPPED_IDS_IN
356
348
  onSome: (count) => `, +${count - SKIPPED_IDS_IN_WARNING} more`
357
349
  })}`;
358
350
  const refusalReasonsOf = (undescribable) => [...new Set(undescribable.map((mutant) => mutant.reason))].join("; ");
359
- 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)})`)));
351
+ const logSkippedMutants = (input) => Match.value(input.undescribable.length).pipe(Match.when(0, () => Effect$1.void), Match.orElse(() => Effect$1.logWarning(`Checker "${input.checkerName}" skipped ${input.undescribable.length} mutant(s) it cannot be told about: ${refusalReasonsOf(input.undescribable)} (${skippedIdsOf(input.undescribable)})`)));
360
352
  const recordSkipped = (skipped) => Boolean$1.match(skipped > 0, {
361
353
  onTrue: () => Metric.update(UndescribableMutant.skipped, skipped),
362
354
  onFalse: () => Effect$1.void
@@ -365,9 +357,14 @@ const partitionedFor = (input) => Option$1.match(Option$1.fromUndefinedOr(input.
365
357
  onSome: (known) => selectedFromLookup(input.plans, known),
366
358
  onNone: () => partitionMutantsForWire(input.plans)
367
359
  });
360
+ //#endregion
361
+ //#region src/Checker/Checker.cell.ts
368
362
  const readCheckCommand = (input) => Effect$1.gen(function* () {
369
363
  const partitioned = partitionedFor(input);
370
- yield* logSkippedMutants(input.checkerName, partitioned.undescribable);
364
+ yield* logSkippedMutants({
365
+ checkerName: input.checkerName,
366
+ undescribable: partitioned.undescribable
367
+ });
371
368
  yield* recordSkipped(partitioned.undescribable.length);
372
369
  yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
373
370
  const answers = yield* input.checker.check(input.checkerName, partitioned.wire);
@@ -407,7 +404,11 @@ const commandFailed = (issue, input) => Checker_exports.CheckerFailed.make({
407
404
  mutantIds: input.plans.map((plan) => plan.mutant.id)
408
405
  });
409
406
  const checkCell = Sandwich.named("stryker.checker.check_plans")(readCheckCommand).decide(admitCheckerAnswer).write({
410
- CheckResultDecision: ({ pairs }, raw) => writeDecidedAnswers(raw.plans, raw.checkerName, pairs),
407
+ CheckResultDecision: ({ pairs }, raw) => writeDecidedAnswers({
408
+ plans: raw.plans,
409
+ checkerName: raw.checkerName,
410
+ answers: pairs
411
+ }),
411
412
  CheckGroupDecision: (_decision, raw) => Effect$1.fail(CheckerSkippedRequested.make({
412
413
  checkerName: raw.checkerName,
413
414
  phase: "check",
@@ -417,8 +418,12 @@ const checkCell = Sandwich.named("stryker.checker.check_plans")(readCheckCommand
417
418
  CheckerSkippedRequested: (breach) => Effect$1.fail(CheckerSkippedRequested.make(breach)),
418
419
  CommandRejected: ({ issue }, raw) => Effect$1.fail(commandFailed(issue, raw))
419
420
  });
420
- const checkGroupedCell = Sandwich.named("stryker.checker.group_plans")(readGroupCommand).decide(admitCheckerAnswer).write({
421
- CheckGroupDecision: ({ groups }, raw) => writeDecidedGroups(raw.plans, raw.checkerName, groups),
421
+ const groupCell = Sandwich.named("stryker.checker.group_plans")(readGroupCommand).decide(admitCheckerAnswer).write({
422
+ CheckGroupDecision: ({ groups }, raw) => writeDecidedGroups({
423
+ plans: raw.plans,
424
+ checkerName: raw.checkerName,
425
+ idGroups: groups
426
+ }),
422
427
  CheckResultDecision: (_decision, raw) => Effect$1.fail(CheckerSkippedRequested.make({
423
428
  checkerName: raw.checkerName,
424
429
  phase: "group",
@@ -427,16 +432,20 @@ const checkGroupedCell = Sandwich.named("stryker.checker.group_plans")(readGroup
427
432
  CheckerAnsweredUnrequested: (breach) => Effect$1.fail(CheckerAnsweredUnrequested.make(breach)),
428
433
  CheckerSkippedRequested: (breach) => Effect$1.fail(CheckerSkippedRequested.make(breach)),
429
434
  CommandRejected: ({ issue }, raw) => Effect$1.fail(commandFailed(issue, raw))
430
- }).pipe(Cell.flatMap((groups) => Cell.mapInput(Cell.collect(checkCell, (perGroup) => perGroup.flat()), (input) => groups.map((group) => ({
435
+ });
436
+ const checkGroupedCell = groupCell.pipe(Cell.flatMap((groups) => Cell.mapInput(Cell.collect(checkCell, (perGroup) => perGroup.flat()), (input) => groups.map((group) => ({
431
437
  ...input,
432
438
  plans: group
433
439
  })))));
434
- const checkGroupedPlans = dual(3, (checker, checkerName, plans) => checkGroupedCell.run({
440
+ const checkRequestOf = (checker, checkerName, plans) => ({
435
441
  checker,
436
442
  checkerName,
437
443
  plans,
438
444
  lookup: lookupOf(partitionMutantsForWire(plans))
439
- }));
445
+ });
446
+ const groupPlans = dual(3, (checker, checkerName, plans) => groupCell.run(checkRequestOf(checker, checkerName, plans)));
447
+ const checkPlans = dual(3, (checker, checkerName, plans) => checkCell.run(checkRequestOf(checker, checkerName, plans)));
448
+ const checkGroupedPlans = dual(3, (checker, checkerName, plans) => checkGroupedCell.run(checkRequestOf(checker, checkerName, plans)));
440
449
  //#endregion
441
450
  //#region src/Config.schema.ts
442
451
  const ConfigDocumentSchema = S.Record(S.String, S.Unknown);
@@ -479,6 +488,22 @@ const forkOptionsSchema = S.StructWithRest(S.Struct({
479
488
  extends: extendsPropertySchema
480
489
  }), [S.Record(S.String, S.Unknown)]);
481
490
  //#endregion
491
+ //#region src/config/default-options.ts
492
+ const isNonNullObject$1 = (value) => typeof value === "object" && value !== null;
493
+ const isRecordValue = (value) => isNonNullObject$1(value) && Array.isArray(value) === false;
494
+ const isArrayValue$1 = (value) => Array.isArray(value);
495
+ const freezeArrayValue = (value) => Object.freeze(value.map((element) => deepFreeze(element)));
496
+ const freezeMapEntry = ([entryKey, entryValue]) => [deepFreeze(entryKey), deepFreeze(entryValue)];
497
+ const freezeMapValue = (value) => Object.freeze(new Map([...value.entries()].map(freezeMapEntry)));
498
+ const freezeSetValue = (value) => Object.freeze(new Set([...value.values()].map((element) => deepFreeze(element))));
499
+ const freezeRegExpValue = (value) => Object.freeze(value);
500
+ const freezeRecordValue = (value) => Object.freeze(Record.map(value, (propertyValue) => deepFreeze(propertyValue)));
501
+ function deepFreeze(target) {
502
+ return Match.value(target).pipe(Match.when(isArrayValue$1, freezeArrayValue), Match.when((value) => value instanceof Map, freezeMapValue), Match.when((value) => value instanceof RegExp, freezeRegExpValue), Match.when((value) => value instanceof Set, freezeSetValue), Match.when(isRecordValue, freezeRecordValue), Match.orElse(() => target));
503
+ }
504
+ const createDefaultOptions = S.decodeEffect(Options_exports.StrykerOptionsSchema)({}).pipe(Effect$1.orDie);
505
+ const defaultOptions = Effect$1.map(createDefaultOptions, (options) => deepFreeze(options));
506
+ //#endregion
482
507
  //#region src/ConfigError.schema.ts
483
508
  var ConfigFileNotFoundError = class extends S.TaggedError()("ConfigFileNotFoundError", { file: S.String }) {
484
509
  exitClass = "ConfigError";
@@ -697,13 +722,16 @@ const interpretDryRunResult = Workflow.make({
697
722
  });
698
723
  //#endregion
699
724
  //#region src/pooled-test-runner.handle.ts
700
- const TypeId$3 = Symbol.for("@systemfsoftware/stryker-js/PooledTestRunner");
701
- const make$3 = (runner) => ({
702
- [TypeId$3]: TypeId$3,
703
- ...Prototype,
704
- ...runner
705
- });
706
- const withTimeout = (inner) => ({
725
+ const TypeId$9 = Symbol.for("~systemfsoftware/stryker-js/PooledTestRunner");
726
+ const PooledTestRunner = Handle.make()(TypeId$9);
727
+ PooledTestRunner.is;
728
+ const make$3 = (runner) => PooledTestRunner.make({
729
+ capabilities: runner.capabilities,
730
+ init: runner.init,
731
+ dryRun: runner.dryRun,
732
+ mutantRun: runner.mutantRun
733
+ });
734
+ const withTimeout = (inner) => make$3({
707
735
  ...inner,
708
736
  dryRun: (options) => inner.dryRun(options).pipe(Effect$1.timeoutOrElse({
709
737
  duration: Duration.millis(options.timeout),
@@ -725,7 +753,7 @@ const maxRetries = 2;
725
753
  const exhaustedMessage = (cause) => `Test runner crashed. Tried ${maxRetries} times to restart it without any luck. ${Cause.pretty(cause)}`;
726
754
  const withRetry = (inner) => {
727
755
  const attempt = (run, onExhausted) => run.pipe(Effect$1.tapError((error) => Match.value(error).pipe(Match.when(S.is(OutOfMemoryError), (outOfMemory) => Effect$1.logInfo(`Test runner process [${outOfMemory.pid}] ran out of memory. That usually means the tests leak memory. Stryker restarts the process and carries on, but the run is slower for it.`)), Match.orElse(() => Effect$1.void))), Effect$1.retry({ times: maxRetries }), Effect$1.catchCause((cause) => Effect$1.succeed(onExhausted(exhaustedMessage(cause)))));
728
- return {
756
+ return make$3({
729
757
  ...inner,
730
758
  dryRun: (options) => attempt(inner.dryRun(options), (errorMessage) => ({
731
759
  status: "error",
@@ -735,11 +763,11 @@ const withRetry = (inner) => {
735
763
  status: "error",
736
764
  errorMessage
737
765
  }))
738
- };
766
+ });
739
767
  };
740
768
  const withMaxReuse = dual(2, (options, retire) => (inner) => Match.value(options.maxTestRunnerReuse).pipe(Match.when((restartAfter) => restartAfter <= 0, () => Effect$1.succeed(inner)), Match.orElse((restartAfter) => Effect$1.gen(function* () {
741
769
  const runs = yield* Ref.make(0);
742
- return {
770
+ return make$3({
743
771
  ...inner,
744
772
  mutantRun: (runOptions) => {
745
773
  const policy = (self) => Effect$1.gen(function* () {
@@ -752,7 +780,7 @@ const withMaxReuse = dual(2, (options, retire) => (inner) => Match.value(options
752
780
  });
753
781
  return policy(inner.mutantRun(runOptions));
754
782
  }
755
- };
783
+ });
756
784
  }))));
757
785
  const nextEnvironmentState = (requested) => Boolean$1.match(requested, {
758
786
  onTrue: () => "loaded-static-mutant",
@@ -768,7 +796,7 @@ const reloadPlanOf = (requested, current, canReload) => ({
768
796
  });
769
797
  const withEnvironmentReload = dual(2, (inner, retire) => Effect$1.gen(function* () {
770
798
  const state = yield* Ref.make("pristine");
771
- return {
799
+ return make$3({
772
800
  ...inner,
773
801
  dryRun: (options) => {
774
802
  const policy = (self) => Ref.set(state, "loaded").pipe(Effect$1.andThen(self));
@@ -792,8 +820,11 @@ const withEnvironmentReload = dual(2, (inner, retire) => Effect$1.gen(function*
792
820
  reloadEnvironment: plan.reloadEnvironment
793
821
  }));
794
822
  })
795
- };
823
+ });
796
824
  }));
825
+ //#endregion
826
+ //#region src/command-runner.blueprint.ts
827
+ const TypeId$8 = Symbol.for("~systemfsoftware/stryker-js/CommandRunner");
797
828
  const ALL_TESTS_NAME = "All tests";
798
829
  const isCommandRunner = (name) => typeof name === "string" && name.toLowerCase() === "command";
799
830
  const commandRunnerCapabilities = { reloadEnvironment: true };
@@ -845,11 +876,7 @@ const commandRunnerMutantRun = (config, mutantPick) => runCommand(config, mutant
845
876
  onFailure: (failure) => Effect$1.fail(failure),
846
877
  onSuccess: (decision) => Effect$1.succeed(decision.asResult)
847
878
  })));
848
- const commandRunner = dual(2, (context, spawner) => {
849
- const config = {
850
- workingDir: context.sandboxWorkingDirectory,
851
- options: context.options
852
- };
879
+ const pooledRunnerOf = (config, spawner) => {
853
880
  const provided = Effect$1.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner);
854
881
  return make$3({
855
882
  capabilities: Effect$1.succeed(commandRunnerCapabilities),
@@ -857,6 +884,23 @@ const commandRunner = dual(2, (context, spawner) => {
857
884
  dryRun: () => commandRunnerDryRun(config).pipe(provided),
858
885
  mutantRun: (options) => commandRunnerMutantRun(config, options).pipe(provided)
859
886
  });
887
+ };
888
+ const CommandRunners = Blueprint.make()(TypeId$8).steps({
889
+ steps: {},
890
+ targets: { runner: (spec) => (spawner) => pooledRunnerOf({
891
+ workingDir: spec.workingDir,
892
+ options: spec.options
893
+ }, spawner) }
894
+ });
895
+ const commandRunner = dual(2, (context, spawner) => CommandRunners.of({
896
+ workingDir: context.sandboxWorkingDirectory,
897
+ options: context.options
898
+ }).runner(spawner));
899
+ S.Struct({
900
+ command: S.Literals(["run", "merge-reports"]),
901
+ isDryRun: S.Boolean,
902
+ mode: S.Literals(["human", "machine"]),
903
+ isCi: S.Boolean
860
904
  });
861
905
  //#endregion
862
906
  //#region src/config/warning-enabled.workflow.ts
@@ -996,7 +1040,7 @@ const resolveConfig = Workflow.make({
996
1040
  const ExitDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js/ExitDecision");
997
1041
  var ClassifyExitCommand = class extends S.TaggedClass()("ClassifyExitCommand", {
998
1042
  pending: S.Array(Plugin_exports.ExitClass),
999
- score: S.NullOr(S.Finite),
1043
+ score: Report_exports.MutationScore,
1000
1044
  breakingThreshold: S.NullOr(S.Finite)
1001
1045
  }) {
1002
1046
  static [Workflow.InstrumentationBrand] = {};
@@ -1031,9 +1075,12 @@ const PRECEDENCE = [
1031
1075
  ];
1032
1076
  const precedenceOf = (pending) => Arr.findFirst(PRECEDENCE, (candidate) => pending.includes(candidate));
1033
1077
  const decisionOf$1 = (exitClass) => Match.value(exitClass).pipe(Match.when("VerdictFail", () => ExitVerdictFailed.make({})), Match.when("ConfigError", () => ExitConfigErrored.make({})), Match.when("RuntimeError", () => ExitRuntimeErrored.make({})), Match.when("InternalError", () => ExitInternalErrored.make({})), Match.exhaustive);
1034
- const verdictExitClass = (score, breakingThreshold) => Option$1.match(Option$1.all([Option$1.fromNullishOr(score), Option$1.fromNullishOr(breakingThreshold)]), {
1035
- onNone: () => null,
1036
- onSome: ([actual, threshold]) => Match.value(actual < threshold).pipe(Match.when(true, () => "VerdictFail"), Match.when(false, () => null), Match.exhaustive)
1078
+ const verdictExitClass = (score, breakingThreshold) => Match.valueTags(score, {
1079
+ Unscored: () => null,
1080
+ Scored: ({ percentage }) => Option$1.match(Option$1.fromNullishOr(breakingThreshold), {
1081
+ onNone: () => null,
1082
+ onSome: (threshold) => Match.value(percentage < threshold).pipe(Match.when(true, () => "VerdictFail"), Match.when(false, () => null), Match.exhaustive)
1083
+ })
1037
1084
  });
1038
1085
  const decide$9 = (command) => Option$1.match(Option$1.fromNullishOr(verdictExitClass(command.score, command.breakingThreshold)), {
1039
1086
  onNone: () => Option$1.match(precedenceOf(command.pending), {
@@ -1203,7 +1250,14 @@ var Trace_exports = /* @__PURE__ */ __exportAll$1({
1203
1250
  layerTraceContextServer: () => layerTraceContextServer,
1204
1251
  withLinkedSpan: () => withLinkedSpan
1205
1252
  });
1206
- const WorkerOptionsWire = S.fromJsonString(Options_exports.StrykerOptionsSchema);
1253
+ const isEscapedKey = (key) => JSON.stringify(key) !== `"${key}"`;
1254
+ const omitEscapedKeys = (key, value) => isEscapedKey(key) ? void 0 : value;
1255
+ const withoutEscapedKeys = (options) => Option$1.getOrThrow(S.decodeUnknownOption(Options_exports.StrykerOptionsSchema)(JSON.parse(JSON.stringify(options, omitEscapedKeys))));
1256
+ const OptionsLawDomain = S.declare((value) => S.is(Options_exports.StrykerOptionsSchema)(value), { toCodecArbitrary: () => S.link()(Options_exports.StrykerOptionsSchema, {
1257
+ decode: SchemaGetter.transform(withoutEscapedKeys),
1258
+ encode: SchemaGetter.passthrough()
1259
+ }) });
1260
+ const WorkerOptionsWire = S.fromJsonString(Options_exports.StrykerOptionsSchema.pipe(S.decodeTo(OptionsLawDomain, SchemaTransformation.passthrough())));
1207
1261
  var WorkerOptions = class WorkerOptions extends Context.Service()("@systemfsoftware/stryker-js-plugin-runtime/worker-options.service/WorkerOptions") {
1208
1262
  static layer = Layer.effect(WorkerOptions, Effect$1.gen(function* () {
1209
1263
  const workerDir = yield* Config.String("STRYKER_WORKER_DIR");
@@ -1219,17 +1273,29 @@ const restrictSocket = (address) => Match.value(address).pipe(Match.tag("UnixPat
1219
1273
  onTrue: () => Effect$1.void,
1220
1274
  onFalse: () => Effect$1.flatMap(FileSystem.FileSystem, (fs) => fs.chmod(path, 384))
1221
1275
  })), Match.orElse(() => Effect$1.void));
1222
- const workerServerLayer = (params) => Layer.unwrap(Effect$1.gen(function* () {
1276
+ const TypeId$7 = Symbol.for("~systemfsoftware/stryker-js-plugin-runtime/WorkerServer");
1277
+ const workerServerLayerOf = (params) => Layer.unwrap(Effect$1.gen(function* () {
1223
1278
  const socket = yield* SocketServer.SocketServer;
1224
1279
  yield* restrictSocket(socket.address);
1225
1280
  return RpcServer.layer(params.rpcs).pipe(Layer.provide(params.handlers), Layer.provide(params.schemaServices), Layer.provide(RpcServer.layerProtocolSocketServer), Layer.provide(RpcSerialization.layerNdjson), Layer.provide(traceContextServer));
1226
1281
  }));
1282
+ Blueprint.make()(TypeId$7).steps({
1283
+ steps: {},
1284
+ targets: { layer: workerServerLayerOf }
1285
+ }).is;
1286
+ const workerServerLayer = workerServerLayerOf;
1227
1287
  const TRACES_SUFFIX = "/v1/traces";
1228
1288
  const tracesUrlOf = (endpoint) => Option$1.match(Option$1.liftPredicate(endpoint.replace(/\/+$/u, ""), (trimmed) => trimmed.endsWith(TRACES_SUFFIX)), {
1229
1289
  onSome: (trimmed) => trimmed,
1230
1290
  onNone: () => `${endpoint.replace(/\/+$/u, "")}${TRACES_SUFFIX}`
1231
1291
  });
1232
- const canonicalTracesUrl = S.String.pipe(S.check(S.isPattern(/\/v1\/traces$/u)), S.check(S.isPattern(/[^/]$/u)));
1292
+ const canonicalTracesUrl = S.String.pipe(S.check(S.isPattern(/\/v1\/traces$/u, { arbitraryConstraint: {
1293
+ patterns: [{
1294
+ source: "\\/v1\\/traces$",
1295
+ flags: "u"
1296
+ }],
1297
+ minLength: 12
1298
+ } })));
1233
1299
  const TracesUrl = S.String.pipe(S.decodeTo(canonicalTracesUrl, {
1234
1300
  decode: SchemaGetter$1.transform(tracesUrlOf),
1235
1301
  encode: SchemaGetter$1.passthrough()
@@ -1261,23 +1327,22 @@ var Worker_exports = /* @__PURE__ */ __exportAll$1({
1261
1327
  });
1262
1328
  //#endregion
1263
1329
  //#region src/spawned-socket-worker.handle.ts
1264
- const TypeId$2 = Symbol.for("@systemfsoftware/stryker-js/SpawnedSocketWorker");
1265
- const ClientLayerTypeId = Symbol.for("@systemfsoftware/stryker-js/SpawnedSocketWorker/clientLayer");
1266
- const make$2 = (worker) => ({
1267
- [TypeId$2]: TypeId$2,
1268
- [ClientLayerTypeId]: worker.clientLayer,
1330
+ const TypeId$6 = Symbol.for("~systemfsoftware/stryker-js/SpawnedSocketWorker");
1331
+ const SpawnedSocketWorker = Handle.make()(TypeId$6);
1332
+ SpawnedSocketWorker.is;
1333
+ const make$2 = (worker) => SpawnedSocketWorker.make({
1269
1334
  pid: worker.pid,
1270
- exited: worker.exited,
1271
- ...Prototype
1272
- });
1273
- const clientLayer = (self) => self[ClientLayerTypeId];
1335
+ exited: worker.exited
1336
+ }, { clientLayer: worker.clientLayer });
1337
+ const clientLayer = (self) => SpawnedSocketWorker.slot(self).clientLayer;
1274
1338
  //#endregion
1275
1339
  //#region src/WorkerLauncher.service.ts
1276
1340
  var WorkerLauncher = class extends Context.Service()("@systemfsoftware/stryker-js/WorkerLauncher.service/WorkerLauncher") {};
1277
1341
  //#endregion
1278
- //#region src/worker-client.resource.ts
1342
+ //#region src/worker-client.blueprint.ts
1343
+ const TypeId$5 = Symbol.for("~systemfsoftware/stryker-js/WorkerClient");
1279
1344
  const connectRetry = Schedule.max([Schedule.spaced(50), Schedule.recurs(100)]);
1280
- const makeWorkerClient = (params) => Effect$1.gen(function* () {
1345
+ const scopedOf$1 = (params) => Effect$1.gen(function* () {
1281
1346
  const launcher = yield* WorkerLauncher;
1282
1347
  const optionsJson = yield* S.encodeEffect(Worker_exports.WorkerOptionsWire)(params.options).pipe(Effect$1.orDie);
1283
1348
  const worker = yield* launcher.spawn({
@@ -1292,6 +1357,11 @@ const makeWorkerClient = (params) => Effect$1.gen(function* () {
1292
1357
  const traceContext = yield* Layer.build(Trace_exports.layerTraceContextClient);
1293
1358
  return yield* RpcClient.make(params.rpcs).pipe(Effect$1.provideContext(Context.merge(protocol, traceContext)));
1294
1359
  });
1360
+ const WorkerClients = () => Blueprint.make()(TypeId$5).steps({
1361
+ steps: {},
1362
+ targets: { scoped: scopedOf$1 }
1363
+ });
1364
+ const makeWorkerClient = (params) => WorkerClients().of(params).scoped;
1295
1365
  const ReporterStageTypeId = Symbol.for("@systemfsoftware/stryker-js/ReporterStage");
1296
1366
  const carriesAttachments = (stage) => "attachments" in stage;
1297
1367
  const makeReporterStage = (attachments) => {
@@ -1639,7 +1709,10 @@ var VerdictEnvelope = class VerdictEnvelope extends S.Class("VerdictEnvelope")({
1639
1709
  runId,
1640
1710
  mode,
1641
1711
  signal,
1642
- score: Option$1.getOrNull(Option$1.filter(Option$1.some(metrics.mutationScore), (score) => metrics.totalMutants > 0 && Number.isFinite(score))),
1712
+ score: Report_exports.MutationScore.match(metrics.mutationScore, {
1713
+ Scored: ({ percentage }) => percentage,
1714
+ Unscored: () => null
1715
+ }),
1643
1716
  thresholds: {
1644
1717
  high: report.thresholds.high,
1645
1718
  low: report.thresholds.low,
@@ -1677,7 +1750,7 @@ var RunEvents = class extends Context.Service()("@systemfsoftware/stryker-js/run
1677
1750
  var RunIdentity = class extends Context.Service()("@systemfsoftware/stryker-js/run-events.service/RunIdentity") {};
1678
1751
  //#endregion
1679
1752
  //#region package.json
1680
- var version = "11.0.0";
1753
+ var version = "12.1.0";
1681
1754
  //#endregion
1682
1755
  //#region src/stryker-package.schema.ts
1683
1756
  var StrykerPackage = class extends S.Class("StrykerPackage")({
@@ -1969,22 +2042,28 @@ const mutationTestReport = (deps, input) => (results) => Effect$1.gen(function*
1969
2042
  identities
1970
2043
  };
1971
2044
  });
1972
- const determineExitCode = (input) => (metrics) => Effect$1.gen(function* () {
1973
- const { mutationScore } = metrics.metrics;
2045
+ const determineExitCode = (input) => (metrics) => {
1974
2046
  const breaking = input.options.thresholds.break;
1975
- const formattedScore = mutationScore.toFixed(2);
1976
- return yield* Option$1.match(Option$1.fromNullishOr(Result.match(classifyExit(ClassifyExitCommand.make({
2047
+ const score = metrics.metrics.mutationScore;
2048
+ const failure = Option$1.fromNullishOr(Result.match(classifyExit(ClassifyExitCommand.make({
1977
2049
  pending: [],
1978
- score: mutationScore,
2050
+ score,
1979
2051
  breakingThreshold: breaking
1980
2052
  })), {
1981
2053
  onFailure: (refused) => refused,
1982
2054
  onSuccess: (decision) => Match.value(decision).pipe(Match.tag("ExitVerdictFailed", () => "VerdictFail"), Match.orElse(() => null))
1983
- })), {
1984
- onNone: () => Effect$1.map(Match.value(breaking).pipe(Match.when(null, () => Effect$1.logDebug("No breaking threshold configured. Won't fail the build no matter how low your mutation score is. Set `thresholds.break` to change this behavior.")), Match.orElse((threshold) => Effect$1.logInfo(`Final mutation score of ${formattedScore} is greater than or equal to break threshold ${String(threshold)}`))), () => null),
1985
- onSome: (failure) => Effect$1.map(Effect$1.andThen(Effect$1.logError(`Final mutation score ${formattedScore} under breaking threshold ${String(breaking)}, setting exit code to 1 (failure).`), Effect$1.logInfo("(improve mutation score or set `thresholds.break = null` to prevent this error in the future)")), () => failure)
2055
+ }));
2056
+ return Report_exports.MutationScore.match(score, {
2057
+ Scored: ({ percentage }) => Option$1.match(failure, {
2058
+ onNone: () => Effect$1.as(logScoredPass(breaking, percentage), null),
2059
+ onSome: (exitClass) => Effect$1.as(logBroken(breaking, percentage), exitClass)
2060
+ }),
2061
+ Unscored: () => Effect$1.as(logUnscored(breaking), Option$1.getOrNull(failure))
1986
2062
  });
1987
- });
2063
+ };
2064
+ const logScoredPass = (breaking, percentage) => Match.value(breaking).pipe(Match.when(null, () => Effect$1.logDebug("No breaking threshold configured. Won't fail the build no matter how low your mutation score is. Set `thresholds.break` to change this behavior.")), Match.orElse((threshold) => Effect$1.logInfo(`Final mutation score of ${percentage.toFixed(2)} is greater than or equal to break threshold ${String(threshold)}`)));
2065
+ const logUnscored = (breaking) => Match.value(breaking).pipe(Match.when(null, () => Effect$1.void), Match.orElse((threshold) => Effect$1.logInfo(`No valid mutant was tested, so there is no mutation score to hold against break threshold ${String(threshold)}`)));
2066
+ const logBroken = (breaking, percentage) => Effect$1.andThen(Effect$1.logError(`Final mutation score ${percentage.toFixed(2)} under breaking threshold ${String(breaking)}, setting exit code to 1 (failure).`), Effect$1.logInfo("(improve mutation score or set `thresholds.break = null` to prevent this error in the future)"));
1988
2067
  const emitVerdict = (deps, input) => (report) => Effect$1.gen(function* () {
1989
2068
  const envelope = VerdictEnvelope.build(report, input.resolvedMode.mode, input.resolvedMode.signal, input.runId, input.basePath, deps.path);
1990
2069
  yield* Queue.offer(deps.events, VerdictReached.make({
@@ -2016,7 +2095,7 @@ const reportAll = (deps, input) => Effect$1.gen(function* () {
2016
2095
  const verdict = yield* determineExitCode(input)(metrics);
2017
2096
  const finalVerdict = Result.match(classifyExit(ClassifyExitCommand.make({
2018
2097
  pending: [verdict, terminalDrain].filter((candidate) => candidate !== null),
2019
- score: null,
2098
+ score: Report_exports.MutationScore.cases.Unscored.make({}),
2020
2099
  breakingThreshold: null
2021
2100
  })), {
2022
2101
  onFailure: (refused) => refused,
@@ -2108,7 +2187,8 @@ var ClearTextReportCommand = class extends S.TaggedClass()("ClearTextReportComma
2108
2187
  };
2109
2188
  var ClearTextReportRendered = class extends S.TaggedClass()("ClearTextReportRendered", {
2110
2189
  stdout: S.Array(ReportChunkSchema),
2111
- diagnostics: S.Array(ReportChunkSchema)
2190
+ diagnostics: S.Array(ReportChunkSchema),
2191
+ stderr: S.Array(ReportChunkSchema)
2112
2192
  }) {
2113
2193
  [ClearTextReportTypeId] = ClearTextReportTypeId;
2114
2194
  };
@@ -2367,8 +2447,12 @@ const mutantReportSection = (report, metrics, render) => Boolean$1.match(render.
2367
2447
  },
2368
2448
  onFalse: () => EMPTY_SECTIONS
2369
2449
  });
2450
+ const hasFullScore = (metrics) => Match.valueTags(metrics.mutationScore, {
2451
+ Scored: ({ percentage }) => percentage === 100,
2452
+ Unscored: () => false
2453
+ });
2370
2454
  const partialScoresVisible = (metrics, render) => Boolean$1.match(render.skipFull, {
2371
- onTrue: () => metrics.childResults.some((child) => child.metrics.mutationScore !== 100),
2455
+ onTrue: () => metrics.childResults.some((child) => !hasFullScore(child.metrics)),
2372
2456
  onFalse: () => true
2373
2457
  });
2374
2458
  const drawsScoreTable = (metrics, render) => Boolean$1.match(render.reportScoreTable, {
@@ -2376,14 +2460,14 @@ const drawsScoreTable = (metrics, render) => Boolean$1.match(render.reportScoreT
2376
2460
  onFalse: () => false
2377
2461
  });
2378
2462
  const mutationScoreOf = (scoreType, metrics) => Match.value(scoreType).pipe(Match.when("total", () => metrics.mutationScore), Match.when("covered", () => metrics.mutationScoreBasedOnCoveredCode), Match.exhaustive);
2379
- const scoreText = (scoreType, row) => {
2380
- const score = mutationScoreOf(scoreType, row.metrics);
2381
- return Boolean$1.match(Number.isNaN(score), {
2382
- onTrue: () => "n/a",
2383
- onFalse: () => score.toFixed(2)
2384
- });
2385
- };
2386
- const thresholdTone = (thresholds, score) => Match.value(score).pipe(Match.when((present) => Number.isNaN(present), () => "muted"), Match.when((present) => present >= thresholds.high, () => "positive"), Match.when((present) => present >= thresholds.low, () => "warning"), Match.orElse(() => "negative"));
2463
+ const scoreText = (scoreType, row) => Match.valueTags(mutationScoreOf(scoreType, row.metrics), {
2464
+ Scored: ({ percentage }) => percentage.toFixed(2),
2465
+ Unscored: () => "n/a"
2466
+ });
2467
+ const thresholdTone = (thresholds, score) => Match.valueTags(score, {
2468
+ Scored: ({ percentage }) => Match.value(percentage).pipe(Match.when((value) => value >= thresholds.high, () => "positive"), Match.when((value) => value >= thresholds.low, () => "warning"), Match.orElse(() => "negative")),
2469
+ Unscored: () => "muted"
2470
+ });
2387
2471
  const scoreTone = (thresholds, scoreType, allowColor) => Boolean$1.match(allowColor, {
2388
2472
  onTrue: () => (row) => thresholdTone(thresholds, mutationScoreOf(scoreType, row.metrics)),
2389
2473
  onFalse: () => () => "plain"
@@ -2480,7 +2564,7 @@ const createColumns = (metricsResult, render) => [
2480
2564
  const leafRules = (columns) => columns.flatMap((column) => column.leaves.map((leaf) => [rule("-", slotWidthOf(leaf))]));
2481
2565
  const leafHeaders = (columns) => columns.flatMap((column) => column.leaves.map((leaf) => placedLine(leaf.header, leaf.style, leaf.netWidth)));
2482
2566
  const fullRowVisible = (render, row) => Boolean$1.match(render.skipFull, {
2483
- onTrue: () => row.metrics.mutationScore !== 100,
2567
+ onTrue: () => !hasFullScore(row.metrics),
2484
2568
  onFalse: () => true
2485
2569
  });
2486
2570
  const bodyRow = (columns, row, ancestorCount) => rowOfLines(columns.flatMap((column) => column.leaves.map((leaf) => [placedCell(leaf.cell(row, ancestorCount), leaf.style, leaf.netWidth, leaf.tone(row))])));
@@ -2525,7 +2609,11 @@ const renderClearTextReport = Workflow.make({
2525
2609
  const sections = renderClearText(report, metrics, command.render);
2526
2610
  return Result.succeed(ClearTextReportRendered.make({
2527
2611
  stdout: [...sections.stdout],
2528
- diagnostics: [...sections.diagnostics]
2612
+ diagnostics: [...sections.diagnostics],
2613
+ stderr: Boolean$1.match(command.render.debug, {
2614
+ onTrue: () => [...sections.diagnostics],
2615
+ onFalse: () => []
2616
+ })
2529
2617
  }));
2530
2618
  }
2531
2619
  })
@@ -2575,14 +2663,8 @@ const renderOptionsOf = (options) => ({
2575
2663
  });
2576
2664
  const readClearTextReport = (input) => Effect$1.map(Stream.fromAsyncIterable(input.events, failAsClearText).pipe(Stream.filterMap(terminalReportOf), Stream.run(Sink.last())), (terminal) => ({
2577
2665
  _tag: "ClearTextReportCommand",
2578
- reported: Option$1.match(terminal, {
2579
- onNone: () => void 0,
2580
- onSome: (ready) => ready.report
2581
- }),
2582
- computed: Option$1.match(terminal, {
2583
- onNone: () => void 0,
2584
- onSome: (ready) => ready.metrics
2585
- }),
2666
+ reported: Option$1.getOrUndefined(Option$1.map(terminal, (ready) => ready.report)),
2667
+ computed: Option$1.getOrUndefined(Option$1.map(terminal, (ready) => ready.metrics)),
2586
2668
  render: renderOptionsOf(input.options),
2587
2669
  rendered: true
2588
2670
  }));
@@ -2619,13 +2701,10 @@ const renderLine = (line) => runsOf(line).map(renderSpan).join("");
2619
2701
  const renderChunk = (chunk) => chunk.map(renderLine).join("\n");
2620
2702
  const writeChunks = (output, channel, chunks) => output.write(channel, chunks.map((chunk) => `${renderChunk(chunk)}\n`)).pipe(Effect$1.mapError(failAsClearText), Effect$1.asVoid);
2621
2703
  const clearTextReportCell = Sandwich.named("stryker.report.clearText")(readClearTextReport).decide(renderClearTextReport).write({
2622
- ClearTextReportRendered: (rendered, raw) => Effect$1.gen(function* () {
2704
+ ClearTextReportRendered: (rendered, _raw) => Effect$1.gen(function* () {
2623
2705
  const output = yield* ReporterOutput;
2624
2706
  yield* writeChunks(output, "stdout", rendered.stdout);
2625
- yield* Boolean$1.match(raw.render.debug, {
2626
- onTrue: () => writeChunks(output, "stderr", rendered.diagnostics),
2627
- onFalse: () => Effect$1.void
2628
- });
2707
+ yield* Effect$1.forEach(rendered.stderr, (chunk) => writeChunks(output, "stderr", [chunk]), { discard: true });
2629
2708
  }),
2630
2709
  ClearTextReportSuppressed: () => Effect$1.void,
2631
2710
  CommandRejected: ({ issue }) => Effect$1.fail(failAsClearText(issue))
@@ -2642,11 +2721,16 @@ const clearTextReporterFactory = (context) => {
2642
2721
  const JsonReportTypeId = Symbol.for("@systemfsoftware/stryker-js/JsonReport");
2643
2722
  var JsonReportCommand = class extends S.TaggedClass()("JsonReportCommand", {
2644
2723
  reported: S.optional(Report_exports.MutationTestResultSchema),
2645
- rendered: S.Boolean
2724
+ rendered: S.Boolean,
2725
+ debug: S.Boolean,
2726
+ fileName: S.String
2646
2727
  }) {
2647
2728
  static [Workflow.InstrumentationBrand] = { rendered: "stryker.report.render" };
2648
2729
  };
2649
- var JsonReportRendered = class extends S.TaggedClass()("JsonReportRendered", { report: Report_exports.MutationTestResultSchema }) {
2730
+ var JsonReportRendered = class extends S.TaggedClass()("JsonReportRendered", {
2731
+ report: Report_exports.MutationTestResultSchema,
2732
+ announceFileName: S.Option(S.String)
2733
+ }) {
2650
2734
  [JsonReportTypeId] = JsonReportTypeId;
2651
2735
  };
2652
2736
  var JsonReportSuppressed = class extends S.TaggedClass()("JsonReportSuppressed", {}) {
@@ -2659,7 +2743,13 @@ const renderJsonReport = Workflow.make({
2659
2743
  error: S.Never,
2660
2744
  decide: (command) => Option$1.match(Option$1.fromUndefinedOr(command.reported), {
2661
2745
  onNone: () => Result.succeed(JsonReportSuppressed.make({})),
2662
- onSome: (report) => Result.succeed(JsonReportRendered.make({ report }))
2746
+ onSome: (report) => Result.succeed(JsonReportRendered.make({
2747
+ report,
2748
+ announceFileName: Boolean$1.match(command.debug, {
2749
+ onTrue: () => Option$1.some(command.fileName),
2750
+ onFalse: () => Option$1.none()
2751
+ })
2752
+ }))
2663
2753
  })
2664
2754
  });
2665
2755
  //#endregion
@@ -2674,13 +2764,11 @@ const readJsonReport = (input) => Effect$1.map(Stream.fromAsyncIterable(input.ev
2674
2764
  _tag: "JsonReportCommand",
2675
2765
  reported: Option$1.getOrUndefined(last),
2676
2766
  rendered: true,
2767
+ debug: input.options.logLevel === "debug",
2768
+ fileName: input.options.jsonReporter.fileName,
2677
2769
  options: input.options
2678
2770
  }));
2679
2771
  const jsonBytesOf = (report) => S.encodeEffect(S.fromJsonString(S.Unknown, { space: 0 }))(report).pipe(Effect$1.mapError(failAsJsonReporter));
2680
- const announceRelativePath = (output, options, path) => Boolean$1.match(options.logLevel === "debug", {
2681
- onTrue: () => Effect$1.ignore(output.write("stderr", [`Using relative path ${path.normalize(options.jsonReporter.fileName)}\n`])),
2682
- onFalse: () => Effect$1.void
2683
- });
2684
2772
  const jsonReportCell = Sandwich.named("stryker.report.json")(readJsonReport).decide(renderJsonReport).write({
2685
2773
  JsonReportRendered: (rendered, raw) => Effect$1.gen(function* () {
2686
2774
  const output = yield* ReporterOutput;
@@ -2688,7 +2776,7 @@ const jsonReportCell = Sandwich.named("stryker.report.json")(readJsonReport).dec
2688
2776
  const path = yield* Path.Path;
2689
2777
  const json = yield* jsonBytesOf(rendered.report);
2690
2778
  const fileName = path.resolve(raw.options.jsonReporter.fileName);
2691
- yield* announceRelativePath(output, raw.options, path);
2779
+ yield* Effect$1.forEach(Option$1.toArray(rendered.announceFileName), (name) => Effect$1.ignore(output.write("stderr", [`Using relative path ${path.normalize(name)}\n`])), { discard: true });
2692
2780
  yield* fs.makeDirectory(path.dirname(fileName), { recursive: true }).pipe(Effect$1.mapError(failAsJsonReporter));
2693
2781
  yield* fs.writeFileString(fileName, json).pipe(Effect$1.mapError(failAsJsonReporter));
2694
2782
  const url = yield* path.toFileUrl(fileName).pipe(Effect$1.mapError(failAsJsonReporter));
@@ -2705,116 +2793,11 @@ const jsonReporterFactory = (context) => {
2705
2793
  }));
2706
2794
  };
2707
2795
  //#endregion
2708
- //#region src/render-progress-report.workflow.ts
2709
- const ProgressReportTypeId = Symbol.for("@systemfsoftware/stryker-js/ProgressReport");
2710
- const ProgressTimingSchema = S.Struct({
2711
- net: S.Finite,
2712
- overhead: S.Finite
2713
- });
2714
- const ProgressCapabilitiesSchema = S.Struct({ reloadEnvironment: S.Boolean });
2715
- const ProgressTallySchema = S.Struct({
2716
- survived: S.Finite,
2717
- timedOut: S.Finite,
2718
- tested: S.Finite,
2719
- mutants: S.Finite,
2720
- total: S.Finite,
2721
- ticks: S.Finite,
2722
- ticksByMutantId: S.Record(S.String, S.Finite),
2723
- timing: ProgressTimingSchema,
2724
- capabilities: ProgressCapabilitiesSchema,
2725
- startedAt: S.Finite
2726
- });
2727
- const ProgressBarStateSchema = S.Struct({
2728
- format: S.String,
2729
- total: S.Finite,
2730
- curr: S.Finite,
2731
- width: S.Finite,
2732
- complete: S.String,
2733
- incomplete: S.String
2734
- });
2735
- const ProgressStateSchema = S.Struct({
2736
- tally: ProgressTallySchema,
2737
- bar: S.NullOr(ProgressBarStateSchema)
2738
- });
2739
- var ProgressReportCommand = class extends S.TaggedClass()("ProgressReportCommand", {
2740
- kind: S.Literals([
2741
- "tick",
2742
- "skip",
2743
- "finalize"
2744
- ]),
2745
- state: ProgressStateSchema,
2746
- now: S.Finite
2747
- }) {
2748
- static [Workflow.InstrumentationBrand] = { now: "stryker.report.rendered" };
2749
- };
2750
- var ProgressBarTick = class extends S.TaggedClass()("ProgressBarTick", {
2751
- bar: ProgressBarStateSchema,
2752
- tally: ProgressTallySchema,
2753
- now: S.Finite
2754
- }) {
2755
- [ProgressReportTypeId] = ProgressReportTypeId;
2756
- };
2757
- var ProgressLineBreak = class extends S.TaggedClass()("ProgressLineBreak", {}) {
2758
- [ProgressReportTypeId] = ProgressReportTypeId;
2759
- };
2760
- var ProgressChunkSuppressed = class extends S.TaggedClass()("ProgressChunkSuppressed", {}) {
2761
- [ProgressReportTypeId] = ProgressReportTypeId;
2762
- };
2763
- const ProgressReportDecision = S.Union([
2764
- ProgressBarTick,
2765
- ProgressLineBreak,
2766
- ProgressChunkSuppressed
2767
- ]);
2768
- const isComplete$1 = (bar) => bar.curr >= bar.total;
2769
- const renderProgressReport = Workflow.make({
2770
- command: ProgressReportCommand,
2771
- decision: ProgressReportDecision,
2772
- error: S.Never,
2773
- decide: (command) => Result.succeed(Match.value(command.kind).pipe(Match.when("finalize", () => Option$1.match(Option$1.fromNullishOr(command.state.bar), {
2774
- onNone: () => ProgressChunkSuppressed.make({}),
2775
- onSome: (bar) => Boolean$1.match(isComplete$1(bar), {
2776
- onTrue: () => ProgressChunkSuppressed.make({}),
2777
- onFalse: () => ProgressLineBreak.make({})
2778
- })
2779
- })), Match.when("skip", () => ProgressChunkSuppressed.make({})), Match.when("tick", () => Option$1.match(Option$1.fromNullishOr(command.state.bar), {
2780
- onNone: () => ProgressChunkSuppressed.make({}),
2781
- onSome: (bar) => ProgressBarTick.make({
2782
- bar,
2783
- tally: command.state.tally,
2784
- now: command.now
2785
- })
2786
- })), Match.exhaustive))
2787
- });
2788
- //#endregion
2789
- //#region src/progress-report.cell.ts
2790
- const failAsProgress = (cause) => Reporter_exports.ReporterFailed.make({
2791
- reporterName: "progress",
2792
- event: "mutationTestReportReady",
2793
- cause: Option$1.getOrElse(Option$1.map(ErrorText_exports.ErrorText.fromCause(cause), (rendered) => rendered.text), () => "")
2794
- });
2796
+ //#region src/progress-report.steps.ts
2795
2797
  const PROGRESS_BAR_FORMAT = "Mutation testing [:bar] :percent (elapsed: :et, remaining: :etc) :tested/:mutants Mutants tested (:survived survived, :timedOut timed out)";
2796
2798
  const PROGRESS_BAR_WIDTH = 50;
2797
2799
  const PROGRESS_BAR_COMPLETE = "=";
2798
2800
  const PROGRESS_BAR_INCOMPLETE = " ";
2799
- const emptyTally = (startedAt) => ({
2800
- survived: 0,
2801
- timedOut: 0,
2802
- tested: 0,
2803
- mutants: 0,
2804
- total: 0,
2805
- ticks: 0,
2806
- ticksByMutantId: {},
2807
- timing: {
2808
- net: 0,
2809
- overhead: 0
2810
- },
2811
- capabilities: { reloadEnvironment: false },
2812
- startedAt
2813
- });
2814
- const INITIAL_PROGRESS = {
2815
- tally: emptyTally(0),
2816
- bar: null
2817
- };
2818
2801
  const TALLY_COUNTED = {
2819
2802
  Killed: {
2820
2803
  survived: 0,
@@ -2917,24 +2900,14 @@ const advance = (state, event, now) => Match.value(event).pipe(Match.tag("dryRun
2917
2900
  kind: "skip",
2918
2901
  state
2919
2902
  })));
2920
- const readProgressStep = (input) => Effect$1.map(Effect$1.sync(() => performance.now()), (now) => Option$1.match(Option$1.fromNullishOr(input.event), {
2903
+ const progressStep = (input) => Option$1.match(Option$1.fromNullishOr(input.event), {
2921
2904
  onNone: () => ({
2922
- _tag: "ProgressReportCommand",
2923
2905
  kind: "finalize",
2924
- state: input.state,
2925
- now
2906
+ state: input.state
2926
2907
  }),
2927
- onSome: (event) => {
2928
- const step = advance(input.state, event, now);
2929
- return {
2930
- _tag: "ProgressReportCommand",
2931
- kind: step.kind,
2932
- state: step.state,
2933
- now
2934
- };
2935
- }
2936
- }));
2937
- const isComplete = (bar) => bar.curr >= bar.total;
2908
+ onSome: (present) => advance(input.state, present, input.now)
2909
+ });
2910
+ const isComplete$1 = (bar) => bar.curr >= bar.total;
2938
2911
  const secondsBetween = (now, startedAt) => Math.floor((now - startedAt) / 1e3);
2939
2912
  const formatTime = (timeInSeconds) => {
2940
2913
  const hours = Math.floor(timeInSeconds / 3600);
@@ -2972,7 +2945,128 @@ const lineBreakOf = (complete) => Boolean$1.match(complete, {
2972
2945
  onTrue: () => "\n",
2973
2946
  onFalse: () => ""
2974
2947
  });
2975
- const renderTick = (tick) => `\r${formatBar(tick.bar, progressData(tick.tally, tick.now))}${lineBreakOf(isComplete(tick.bar))}`;
2948
+ const renderTick = (tick) => `\r${formatBar(tick.bar, progressData(tick.tally, tick.now))}${lineBreakOf(isComplete$1(tick.bar))}`;
2949
+ //#endregion
2950
+ //#region src/render-progress-report.workflow.ts
2951
+ const ProgressReportTypeId = Symbol.for("@systemfsoftware/stryker-js/ProgressReport");
2952
+ const ProgressTimingSchema = S.Struct({
2953
+ net: S.Finite,
2954
+ overhead: S.Finite
2955
+ });
2956
+ const ProgressCapabilitiesSchema = S.Struct({ reloadEnvironment: S.Boolean });
2957
+ const ProgressTallySchema = S.Struct({
2958
+ survived: S.Finite,
2959
+ timedOut: S.Finite,
2960
+ tested: S.Finite,
2961
+ mutants: S.Finite,
2962
+ total: S.Finite,
2963
+ ticks: S.Finite,
2964
+ ticksByMutantId: S.Record(S.String, S.Finite),
2965
+ timing: ProgressTimingSchema,
2966
+ capabilities: ProgressCapabilitiesSchema,
2967
+ startedAt: S.Finite
2968
+ });
2969
+ const ProgressBarStateSchema = S.Struct({
2970
+ format: S.String,
2971
+ total: S.Finite,
2972
+ curr: S.Finite,
2973
+ width: S.Finite,
2974
+ complete: S.String,
2975
+ incomplete: S.String
2976
+ });
2977
+ const ProgressStateSchema = S.Struct({
2978
+ tally: ProgressTallySchema,
2979
+ bar: S.NullOr(ProgressBarStateSchema)
2980
+ });
2981
+ var ProgressReportCommand = class extends S.TaggedClass()("ProgressReportCommand", {
2982
+ kind: S.Literals([
2983
+ "tick",
2984
+ "skip",
2985
+ "finalize"
2986
+ ]),
2987
+ state: ProgressStateSchema,
2988
+ now: S.Finite
2989
+ }) {
2990
+ static [Workflow.InstrumentationBrand] = { now: "stryker.report.rendered" };
2991
+ };
2992
+ var ProgressBarTick = class extends S.TaggedClass()("ProgressBarTick", {
2993
+ bar: ProgressBarStateSchema,
2994
+ tally: ProgressTallySchema,
2995
+ now: S.Finite
2996
+ }) {
2997
+ [ProgressReportTypeId] = ProgressReportTypeId;
2998
+ };
2999
+ var ProgressLineBreak = class extends S.TaggedClass()("ProgressLineBreak", {}) {
3000
+ [ProgressReportTypeId] = ProgressReportTypeId;
3001
+ };
3002
+ var ProgressChunkSuppressed = class extends S.TaggedClass()("ProgressChunkSuppressed", {}) {
3003
+ [ProgressReportTypeId] = ProgressReportTypeId;
3004
+ };
3005
+ const ProgressReportDecision = S.Union([
3006
+ ProgressBarTick,
3007
+ ProgressLineBreak,
3008
+ ProgressChunkSuppressed
3009
+ ]);
3010
+ const isComplete = (bar) => bar.curr >= bar.total;
3011
+ const renderProgressReport = Workflow.make({
3012
+ command: ProgressReportCommand,
3013
+ decision: ProgressReportDecision,
3014
+ error: S.Never,
3015
+ decide: (command) => Result.succeed(Match.value(command.kind).pipe(Match.when("finalize", () => Option$1.match(Option$1.fromNullishOr(command.state.bar), {
3016
+ onNone: () => ProgressChunkSuppressed.make({}),
3017
+ onSome: (bar) => Boolean$1.match(isComplete(bar), {
3018
+ onTrue: () => ProgressChunkSuppressed.make({}),
3019
+ onFalse: () => ProgressLineBreak.make({})
3020
+ })
3021
+ })), Match.when("skip", () => ProgressChunkSuppressed.make({})), Match.when("tick", () => Option$1.match(Option$1.fromNullishOr(command.state.bar), {
3022
+ onNone: () => ProgressChunkSuppressed.make({}),
3023
+ onSome: (bar) => ProgressBarTick.make({
3024
+ bar,
3025
+ tally: command.state.tally,
3026
+ now: command.now
3027
+ })
3028
+ })), Match.exhaustive))
3029
+ });
3030
+ //#endregion
3031
+ //#region src/progress-report.cell.ts
3032
+ const failAsProgress = (cause) => Reporter_exports.ReporterFailed.make({
3033
+ reporterName: "progress",
3034
+ event: "mutationTestReportReady",
3035
+ cause: Option$1.getOrElse(Option$1.map(ErrorText_exports.ErrorText.fromCause(cause), (rendered) => rendered.text), () => "")
3036
+ });
3037
+ const emptyTally = (startedAt) => ({
3038
+ survived: 0,
3039
+ timedOut: 0,
3040
+ tested: 0,
3041
+ mutants: 0,
3042
+ total: 0,
3043
+ ticks: 0,
3044
+ ticksByMutantId: {},
3045
+ timing: {
3046
+ net: 0,
3047
+ overhead: 0
3048
+ },
3049
+ capabilities: { reloadEnvironment: false },
3050
+ startedAt
3051
+ });
3052
+ const INITIAL_PROGRESS = {
3053
+ tally: emptyTally(0),
3054
+ bar: null
3055
+ };
3056
+ const readProgressStep = (input) => Effect$1.gen(function* () {
3057
+ const now = yield* Clock.currentTimeMillis;
3058
+ const step = progressStep({
3059
+ state: input.state,
3060
+ event: input.event,
3061
+ now
3062
+ });
3063
+ return {
3064
+ _tag: "ProgressReportCommand",
3065
+ kind: step.kind,
3066
+ state: step.state,
3067
+ now
3068
+ };
3069
+ });
2976
3070
  const writeChunk = (chunk) => Effect$1.flatMap(ReporterOutput, (output) => Effect$1.ignore(output.write("stdout", [chunk])));
2977
3071
  const progressReportCell = Sandwich.named("stryker.report.progress")(readProgressStep).decide(renderProgressReport).write({
2978
3072
  ProgressBarTick: (tick, raw) => Effect$1.as(writeChunk(renderTick(tick)), raw.state),
@@ -3061,7 +3155,7 @@ const phaseEntered = (phase) => Effect$1.gen(function* () {
3061
3155
  }));
3062
3156
  });
3063
3157
  //#endregion
3064
- //#region src/run/load-config.cell.ts
3158
+ //#region src/run/load-config.parts.ts
3065
3159
  const isNonNullObject = (value) => typeof value === "object" && value !== null;
3066
3160
  const combine = (prefixes, suffixes, extensions) => prefixes.flatMap((prefix) => suffixes.flatMap((suffix) => extensions.map((extension) => `${prefix}stryker${suffix}.${extension}`)));
3067
3161
  const CONFIG_FILE_NAME_PREFIXES = ["", "."];
@@ -3228,7 +3322,7 @@ const applyConfigFactory = (factory, configEnv) => factory(configEnv);
3228
3322
  /**
3229
3323
  * Settle a config module's default export into the object the document schema decodes.
3230
3324
  *
3231
- * `./config` types `StrykerConfig.define` as accepting the config object, a promise of it,
3325
+ * `./config` types `defineConfig` as accepting the config object, a promise of it,
3232
3326
  * or a factory receiving `ConfigEnv`, so the loader accepts all three. The factory
3233
3327
  * is invoked here because this is the only place that knows the invocation the
3234
3328
  * config is being read for; the promise is awaited here because a promise is a
@@ -3536,20 +3630,10 @@ const readLoadConfig = (input) => Effect$1.gen(function* () {
3536
3630
  };
3537
3631
  const cliRecord = yield* S.decodeEffect(ConfigDocumentSchema)(input.cliOptions).pipe(Effect$1.orDie);
3538
3632
  return yield* loadOptionsFromConfigFile(cliRecord, configEnv).pipe(Effect$1.map((fileOptions) => LoadConfigCommand.make({
3539
- document: StrykerConfig.merge(Option$1.getOrElse(fileOptions, () => ({})), cliRecord),
3633
+ document: mergeConfig(Option$1.getOrElse(fileOptions, () => ({})), cliRecord),
3540
3634
  fileFound: Option$1.isSome(fileOptions)
3541
3635
  })));
3542
3636
  });
3543
- const loadConfig = Sandwich.named("stryker.config_read")(readLoadConfig).decide(resolveConfig).write({
3544
- ConfigFromFile: ({ options }) => Effect$1.succeed(options),
3545
- ConfigFromDefaults: ({ options }) => Effect$1.succeed(options),
3546
- ConfigOptionsRefused: ({ message }) => Effect$1.fail(ConfigError.make({ message: configErrorMessage(describeMessageOf(message)) })),
3547
- CommandRejected: ({ issue }) => Effect$1.fail(ConfigError.make({ message: issue }))
3548
- });
3549
- const readConfig = dual(2, (cliOptions, invocation) => loadConfig.run({
3550
- cliOptions,
3551
- invocation
3552
- }));
3553
3637
  const emitPreparePhaseEntered = phaseEntered("prepare");
3554
3638
  const failConfigWith = (message) => Effect$1.fail(ConfigError.make({ message })).pipe(Effect$1.tapCause(() => emitPreparePhaseEntered));
3555
3639
  const readRunConfig = (input) => Effect$1.gen(function* () {
@@ -3566,6 +3650,18 @@ const readRunConfig = (input) => Effect$1.gen(function* () {
3566
3650
  basePath: env.basePath
3567
3651
  });
3568
3652
  });
3653
+ //#endregion
3654
+ //#region src/run/load-config.cell.ts
3655
+ const loadConfig = Sandwich.named("stryker.config_read")(readLoadConfig).decide(resolveConfig).write({
3656
+ ConfigFromFile: ({ options }) => Effect$1.succeed(options),
3657
+ ConfigFromDefaults: ({ options }) => Effect$1.succeed(options),
3658
+ ConfigOptionsRefused: ({ message }) => Effect$1.fail(ConfigError.make({ message: configErrorMessage(describeMessageOf(message)) })),
3659
+ CommandRejected: ({ issue }) => Effect$1.fail(ConfigError.make({ message: issue }))
3660
+ });
3661
+ const readConfig = dual(2, (cliOptions, invocation) => loadConfig.run({
3662
+ cliOptions,
3663
+ invocation
3664
+ }));
3569
3665
  const loadConfigCell = Sandwich.named("stryker.load_config")(readRunConfig).decide(resolveConfig).write({
3570
3666
  ConfigFromFile: ({ options }, raw) => Effect$1.succeed({
3571
3667
  options,
@@ -3620,9 +3716,10 @@ const classifyWorkerExit = Workflow.make({
3620
3716
  decide: decide$8
3621
3717
  });
3622
3718
  //#endregion
3623
- //#region src/worker-protocol.resource.ts
3719
+ //#region src/worker-protocol.blueprint.ts
3720
+ const TypeId$4 = Symbol.for("~systemfsoftware/stryker-js/WorkerProtocol");
3624
3721
  const droppedConnection = RpcClientError.make({ reason: Socket.SocketReadError.make({ cause: /* @__PURE__ */ new Error("worker connection dropped with the request in flight") }) });
3625
- const layerWorkerProtocol = (socket) => Layer.effect(RpcClient.Protocol, Effect$1.gen(function* () {
3722
+ const layerOf = (socket) => Layer.effect(RpcClient.Protocol, Effect$1.gen(function* () {
3626
3723
  const handlers = MutableHashMap.empty();
3627
3724
  const inFlight = MutableHashMap.empty();
3628
3725
  const connected = yield* Ref.make(false);
@@ -3661,10 +3758,15 @@ const layerWorkerProtocol = (socket) => Layer.effect(RpcClient.Protocol, Effect$
3661
3758
  send: (clientId, request, transferables) => track(clientId, request).pipe(Effect$1.andThen(inner.send(clientId, request, transferables)))
3662
3759
  });
3663
3760
  }));
3761
+ const WorkerProtocol = Blueprint.make()(TypeId$4).steps({
3762
+ steps: {},
3763
+ targets: { layer: (spec) => layerOf(spec.socket) }
3764
+ });
3765
+ const layerWorkerProtocol = (socket) => WorkerProtocol.of({ socket }).layer;
3664
3766
  //#endregion
3665
3767
  //#region src/drivers/node.ts
3666
3768
  const restrictToOwnerOrWarn = (fs, file) => fs.chmod(file, 384).pipe(Effect$1.tapError((cause) => Effect$1.logWarning(`Could not restrict "${file}" to its owner; the worker directory's own mode still protects it.`).pipe(Effect$1.annotateLogs("cause", cause))), Effect$1.catchTag("PlatformError", () => Effect$1.void));
3667
- const nodeWorkerLauncherLayer = Layer.effect(WorkerLauncher, Effect$1.gen(function* () {
3769
+ const nodeWorkerLauncherLayer = (childEnv) => Layer.effect(WorkerLauncher, Effect$1.gen(function* () {
3668
3770
  const crypto = yield* Crypto.Crypto;
3669
3771
  const fs = yield* FileSystem.FileSystem;
3670
3772
  const path = yield* Path.Path;
@@ -3683,6 +3785,7 @@ const nodeWorkerLauncherLayer = Layer.effect(WorkerLauncher, Effect$1.gen(functi
3683
3785
  env: {
3684
3786
  STRYKER_WORKER_DIR: workerDir,
3685
3787
  STRYKER_SOCKET: socketPath,
3788
+ ...childEnv,
3686
3789
  ...params.env
3687
3790
  },
3688
3791
  stderr: "inherit"
@@ -3722,7 +3825,8 @@ const nodeWorkerLauncherLayer = Layer.effect(WorkerLauncher, Effect$1.gen(functi
3722
3825
  const nodeFsPathLayer$1 = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);
3723
3826
  const nodeSpawnerLayer = NodeChildProcessSpawner.layer.pipe(Layer.provide(nodeFsPathLayer$1));
3724
3827
  const nodeBase = Layer.mergeAll(nodeFsPathLayer$1, nodeSpawnerLayer, NodeStdio.layer);
3725
- const nodePlatformLayer = Layer.mergeAll(nodeWorkerLauncherLayer.pipe(Layer.provide(Layer.merge(nodeBase, NodeCrypto.layer))), nodeBase);
3828
+ const makeNodePlatformLayer = (options) => Layer.mergeAll(nodeWorkerLauncherLayer(options.childEnv).pipe(Layer.provide(Layer.merge(nodeBase, NodeCrypto.layer))), nodeBase);
3829
+ const nodePlatformLayer = makeNodePlatformLayer({ childEnv: {} });
3726
3830
  //#endregion
3727
3831
  //#region src/IncrementalDiff.schema.ts
3728
3832
  const PositionSchema$1 = S.Struct({
@@ -3897,7 +4001,7 @@ const writeHtmlFile = (fileName, html) => Effect$1.gen(function* () {
3897
4001
  const readRenderCommand = (input) => Effect$1.succeed({
3898
4002
  _tag: "RenderHtmlReport",
3899
4003
  fileName: input.fileName,
3900
- inlinedBundle: inlinedBundle(),
4004
+ inlinedBundle: input.inlinedBundle,
3901
4005
  report: input.report
3902
4006
  });
3903
4007
  const writeHtmlReport = Sandwich.named("html_report.write")(readRenderCommand).decide(renderHtmlReport).write({
@@ -3916,7 +4020,8 @@ const failAsHtmlReporter = (cause) => Reporter_exports.ReporterFailed.make({
3916
4020
  });
3917
4021
  const drainEvents = (fileName, events) => Stream.runForEach(Stream.fromAsyncIterable(events, failAsHtmlReporter).pipe(Stream.filter(S.is(Reporter_exports.MutationTestReportReady))), (ready) => writeHtmlReport.run({
3918
4022
  fileName,
3919
- report: ready.report
4023
+ report: ready.report,
4024
+ inlinedBundle: inlinedBundle()
3920
4025
  }).pipe(Effect$1.mapError(failAsHtmlReporter)));
3921
4026
  const makeHtmlReporter = dual(2, (options, _init) => (events) => Layer.build(nodeFsPathLayer).pipe(Effect$1.flatMap((platform) => Effect$1.provideContext(drainEvents(options.htmlReporter.fileName, events), platform)), Effect$1.scoped));
3922
4027
  var HtmlReporter_exports = /* @__PURE__ */ __exportAll({ makeHtmlReporter: () => makeHtmlReporter });
@@ -3927,7 +4032,8 @@ const PERCENTAGE_OPTION = /^(100|[1-9]?[0-9])%$/;
3927
4032
  var TestRunnersOnly = class extends S.TaggedClass()("TestRunnersOnly", {
3928
4033
  testRunners: S.Finite,
3929
4034
  total: S.Finite,
3930
- isPercentage: S.Boolean
4035
+ isPercentage: S.Boolean,
4036
+ announcement: S.Option(S.String)
3931
4037
  }) {
3932
4038
  [ConcurrencyDecisionTypeId] = ConcurrencyDecisionTypeId;
3933
4039
  };
@@ -3935,7 +4041,8 @@ var TestRunnersAndCheckers = class extends S.TaggedClass()("TestRunnersAndChecke
3935
4041
  testRunners: S.Finite,
3936
4042
  checkers: S.Finite,
3937
4043
  total: S.Finite,
3938
- isPercentage: S.Boolean
4044
+ isPercentage: S.Boolean,
4045
+ announcement: S.Option(S.String)
3939
4046
  }) {
3940
4047
  [ConcurrencyDecisionTypeId] = ConcurrencyDecisionTypeId;
3941
4048
  };
@@ -3957,31 +4064,37 @@ const defaultedTotal = (availableParallelism) => Boolean$1.match(availableParall
3957
4064
  const percentageDetails = (text, availableParallelism) => Option$1.match(percentageOf(text), {
3958
4065
  onSome: (percentage) => ({
3959
4066
  total: percentageTotal(percentage, availableParallelism),
3960
- isPercentage: true
4067
+ isPercentage: true,
4068
+ announcement: Option$1.some(`Computed concurrency ${percentageTotal(percentage, availableParallelism)} from "${text}" based on ${availableParallelism} available parallelism.`)
3961
4069
  }),
3962
4070
  onNone: () => ({
3963
4071
  total: defaultedTotal(availableParallelism),
3964
- isPercentage: false
4072
+ isPercentage: false,
4073
+ announcement: Option$1.none()
3965
4074
  })
3966
4075
  });
3967
4076
  const detailsOf = (concurrency, availableParallelism) => Match.value(concurrency).pipe(Match.when(Predicate$1.isString, (text) => percentageDetails(text, availableParallelism)), Match.when(Predicate$1.isNumber, (total) => ({
3968
4077
  total,
3969
- isPercentage: false
4078
+ isPercentage: false,
4079
+ announcement: Option$1.none()
3970
4080
  })), Match.orElse(() => ({
3971
4081
  total: defaultedTotal(availableParallelism),
3972
- isPercentage: false
4082
+ isPercentage: false,
4083
+ announcement: Option$1.none()
3973
4084
  })));
3974
4085
  const splitOf = (details, checkerCount) => Boolean$1.match(checkerCount > 0, {
3975
4086
  onTrue: () => TestRunnersAndCheckers.make({
3976
4087
  testRunners: max$1(floor(details.total / 2), 1),
3977
4088
  checkers: max$1(ceil(details.total / 2), 1),
3978
4089
  total: details.total,
3979
- isPercentage: details.isPercentage
4090
+ isPercentage: details.isPercentage,
4091
+ announcement: details.announcement
3980
4092
  }),
3981
4093
  onFalse: () => TestRunnersOnly.make({
3982
4094
  testRunners: details.total,
3983
4095
  total: details.total,
3984
- isPercentage: details.isPercentage
4096
+ isPercentage: details.isPercentage,
4097
+ announcement: details.announcement
3985
4098
  })
3986
4099
  });
3987
4100
  const decide$7 = (command) => Result.succeed(splitOf(detailsOf(command.concurrency, command.availableParallelism), command.checkerCount));
@@ -4003,19 +4116,15 @@ const readConcurrency = (record) => Effect$1.sync(() => ({
4003
4116
  availableParallelism: availableParallelism(),
4004
4117
  record
4005
4118
  }));
4006
- const announcePercentage = (command, total, isPercentage) => Boolean$1.match(isPercentage, {
4007
- onTrue: () => Effect$1.logDebug(`Computed concurrency ${total} from "${command.concurrency}" based on ${command.availableParallelism} available parallelism.`),
4008
- onFalse: () => Effect$1.void
4009
- });
4010
4119
  const concurrencyCell = Sandwich.named("stryker.concurrency")(readConcurrency).decide(resolveConcurrency).write({
4011
- TestRunnersAndCheckers: (split, command) => Effect$1.as(Effect$1.andThen(announcePercentage(command, split.total, split.isPercentage), Effect$1.logInfo(`Creating ${split.checkers} checker process(es) and ${split.testRunners} test runner process(es).`)), {
4120
+ TestRunnersAndCheckers: (split, command) => Effect$1.as(Effect$1.andThen(Effect$1.forEach(Option$1.toArray(split.announcement), (line) => Effect$1.logDebug(line), { discard: true }), Effect$1.logInfo(`Creating ${split.checkers} checker process(es) and ${split.testRunners} test runner process(es).`)), {
4012
4121
  ...command.record,
4013
4122
  concurrency: {
4014
4123
  testRunners: split.testRunners,
4015
4124
  checkers: split.checkers
4016
4125
  }
4017
4126
  }),
4018
- TestRunnersOnly: (split, command) => Effect$1.as(Effect$1.andThen(announcePercentage(command, split.total, split.isPercentage), Effect$1.logInfo(`Creating ${split.testRunners} test runner process(es).`)), {
4127
+ TestRunnersOnly: (split, command) => Effect$1.as(Effect$1.andThen(Effect$1.forEach(Option$1.toArray(split.announcement), (line) => Effect$1.logDebug(line), { discard: true }), Effect$1.logInfo(`Creating ${split.testRunners} test runner process(es).`)), {
4019
4128
  ...command.record,
4020
4129
  concurrency: {
4021
4130
  testRunners: split.testRunners,
@@ -4058,132 +4167,8 @@ const admitIncrementalReport = Workflow.make({
4058
4167
  error: S.Never,
4059
4168
  decide: (command) => Result.succeed(decide$6(command))
4060
4169
  });
4061
- const isVmRunner = (name) => typeof name === "string" && name.toLowerCase() === "vm";
4062
- const vmRunnerCapabilities = { reloadEnvironment: true };
4063
- const HOST_TIMEOUT_SLACK_MS = 1e3;
4064
- const messageOf$1 = (cause) => cause instanceof Error ? cause.message : new Error("the vm test runner failed", { cause }).message;
4065
- const testResultOf = (test) => {
4066
- const base = {
4067
- id: test.id,
4068
- name: test.name,
4069
- timeSpentMs: test.timeSpentMs
4070
- };
4071
- return Match.value(test.status).pipe(Match.when("failed", () => ({
4072
- ...base,
4073
- status: "failed",
4074
- failureMessage: test.failureMessage ?? ""
4075
- })), Match.when("skipped", () => ({
4076
- ...base,
4077
- status: "skipped"
4078
- })), Match.orElse(() => ({
4079
- ...base,
4080
- status: "success"
4081
- })));
4082
- };
4083
- const dryRunResultOf = (response) => {
4084
- const tests = response.tests.map(testResultOf);
4085
- return Option$1.match(Option$1.fromUndefinedOr(response.mutantCoverage), {
4086
- onNone: () => ({
4087
- status: "complete",
4088
- tests
4089
- }),
4090
- onSome: (mutantCoverage) => ({
4091
- status: "complete",
4092
- tests,
4093
- mutantCoverage
4094
- })
4095
- });
4096
- };
4097
- const respond = (response) => Match.value(response).pipe(Match.discriminator("status")("init-failed", (failed) => Effect$1.fail(TestRunner_exports.TestRunnerFailed.make({
4098
- runnerName: "vm",
4099
- phase: "init",
4100
- cause: failed.message
4101
- }))), Match.discriminator("status")("timeout", () => Effect$1.succeed({ status: "timeout" })), Match.discriminator("status")("error", (errored) => Effect$1.succeed({
4102
- status: "error",
4103
- errorMessage: errored.errorMessage
4104
- })), Match.discriminator("status")("complete", (complete) => Effect$1.succeed(dryRunResultOf(complete))), Match.exhaustive);
4105
- const pooledVmRunner = (config, scope) => {
4106
- let client;
4107
- let sessionTestFiles = config.testFiles;
4108
- const sessionOptions = () => ({
4109
- sandboxWorkingDirectory: config.sandboxWorkingDirectory,
4110
- testFiles: sessionTestFiles
4111
- });
4112
- const spawnClient = Effect$1.try({
4113
- try: () => {
4114
- const spawned = Session.createVmWorkerClient(sessionOptions(), { onExit: () => {
4115
- if (client === spawned) client = void 0;
4116
- } });
4117
- return spawned;
4118
- },
4119
- catch: (cause) => TestRunner_exports.TestRunnerFailed.make({
4120
- runnerName: "vm",
4121
- phase: "init",
4122
- cause: `the vm test runner could not start its worker: ${messageOf$1(cause)}`
4123
- })
4124
- });
4125
- const dropClient = Effect$1.suspend(() => {
4126
- const current = client;
4127
- client = void 0;
4128
- return current === void 0 ? Effect$1.void : Effect$1.promise(() => current.terminate());
4129
- });
4130
- const run = (request) => Effect$1.gen(function* () {
4131
- if (client === void 0) {
4132
- const spawned = yield* spawnClient;
4133
- yield* Scope.addFinalizer(scope, Effect$1.promise(() => spawned.terminate()));
4134
- client = spawned;
4135
- }
4136
- const current = client;
4137
- const answer = yield* Effect$1.tryPromise({
4138
- try: () => current.run(request),
4139
- catch: (cause) => ({
4140
- status: "error",
4141
- errorMessage: messageOf$1(cause)
4142
- })
4143
- }).pipe(Effect$1.catch((failed) => Effect$1.succeed(failed)), Effect$1.timeoutOrElse({
4144
- duration: request.timeoutMs + HOST_TIMEOUT_SLACK_MS,
4145
- orElse: () => dropClient.pipe(Effect$1.as({ status: "timeout" }))
4146
- }));
4147
- return yield* respond(answer);
4148
- }).pipe(Effect$1.onInterrupt(() => dropClient));
4149
- const shouldAdoptTestFiles = () => config.testFiles.length === 0 && client === void 0;
4150
- const adoptedTestFiles = (testFiles) => testFiles ?? [];
4151
- const testFilterFieldOf = (testFilter) => testFilter === void 0 ? {} : { testFilter: [...testFilter] };
4152
- const hitLimitFieldOf = (hitLimit) => hitLimit === void 0 ? {} : { hitLimit };
4153
- const mutantRequestOf = (options) => ({
4154
- kind: "mutant",
4155
- timeoutMs: options.timeout,
4156
- activeMutantId: String(options.activeMutant.id),
4157
- ...testFilterFieldOf(options.testFilter),
4158
- ...hitLimitFieldOf(options.hitLimit),
4159
- reloadEnvironment: options.reloadEnvironment
4160
- });
4161
- return make$3({
4162
- capabilities: Effect$1.succeed(vmRunnerCapabilities),
4163
- init: Effect$1.void,
4164
- dryRun: (options) => {
4165
- if (shouldAdoptTestFiles()) sessionTestFiles = adoptedTestFiles(options.testFiles);
4166
- return run({
4167
- kind: "dry",
4168
- timeoutMs: options.timeout,
4169
- reloadEnvironment: true
4170
- });
4171
- },
4172
- mutantRun: (options) => run(mutantRequestOf(options)).pipe(Effect$1.map((dryRunResult) => interpretDryRunResult(InterpretDryRunResultCommand.make({ dryRunResult }))), Effect$1.flatMap((decided) => Result.match(decided, {
4173
- onFailure: (failure) => Effect$1.fail(failure),
4174
- onSuccess: (decision) => Effect$1.succeed(decision.asResult)
4175
- })), Effect$1.catchTag("TestRunnerFailed", (failure) => Effect$1.succeed({
4176
- status: "error",
4177
- errorMessage: failure.cause
4178
- })))
4179
- });
4180
- };
4181
- const vmTestRunner = (config) => Effect$1.gen(function* () {
4182
- const scope = yield* Effect$1.scope;
4183
- return pooledVmRunner(config, scope);
4184
- });
4185
4170
  //#endregion
4186
- //#region src/read-project.cell.ts
4171
+ //#region src/read-project.parts.ts
4187
4172
  const ALWAYS_IGNORE = Object.freeze([
4188
4173
  "node_modules",
4189
4174
  ".git",
@@ -4195,23 +4180,9 @@ const ALWAYS_IGNORE = Object.freeze([
4195
4180
  ]);
4196
4181
  const IGNORE_PATTERN_CHARACTER = "!";
4197
4182
  const DEFAULT_GLOB = "**/*.{js,ts,jsx,tsx,html,vue,mjs,mts,cts,cjs}";
4198
- const VITEST_DEFAULT_TEST_FILE_PATTERNS = ["**/*.{test,spec}.{js,jsx,ts,tsx,cjs,cjsx,cts,ctsx,mjs,mjsx,mts,mtsx}"];
4199
- const defaultTestFileIgnores = (tempDirName) => [
4200
- "**/node_modules/**",
4201
- "**/dist/**",
4202
- "**/.{idea,git,cache,output,temp}/**",
4203
- `**/${tempDirName}/**`
4204
- ];
4205
- const shouldDiscoverTestFiles = (options) => options.testFiles.length === 0 && isVmRunner(options.testRunner);
4206
- const testFileSelectionOf = (options) => Boolean.match(shouldDiscoverTestFiles(options), {
4207
- onTrue: () => ({
4208
- testFilePatterns: VITEST_DEFAULT_TEST_FILE_PATTERNS,
4209
- testFileIgnores: defaultTestFileIgnores(options.tempDirName)
4210
- }),
4211
- onFalse: () => ({
4212
- testFilePatterns: options.testFiles,
4213
- testFileIgnores: []
4214
- })
4183
+ const testFileSelectionOf = (options) => ({
4184
+ testFilePatterns: options.testFiles,
4185
+ testFileIgnores: []
4215
4186
  });
4216
4187
  const normalizeFileName = (value) => value.replace(/\\/g, "/");
4217
4188
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -4596,7 +4567,7 @@ const readProjectCommand = (input) => Effect$1.gen(function* () {
4596
4567
  const mutatePatterns = input.options.mutate;
4597
4568
  const { testFileIgnores, testFilePatterns } = testFileSelectionOf(input.options);
4598
4569
  const inputFileNames = yield* resolveInputFileNames(ignoreRulesOf(input.options), input.basePath);
4599
- const defaults = yield* StrykerConfig.defaultOptions;
4570
+ const defaults = yield* defaultOptions;
4600
4571
  const decision = selectFiles(selectionOf$1(inputFileNames, mutatePatterns, testFilePatterns, testFileIgnores, input.basePath, input.targetMutatePatterns));
4601
4572
  yield* Boolean.match(stringArrayEquivalence(mutatePatterns, defaults.mutate), {
4602
4573
  onTrue: () => Effect$1.void,
@@ -4619,7 +4590,7 @@ const readProjectCommand = (input) => Effect$1.gen(function* () {
4619
4590
  testFiles: [...decision.testFiles]
4620
4591
  });
4621
4592
  });
4622
- const discardLogOf = (command, discard) => Boolean.match(command.incremental, {
4593
+ const discardLogOf = ({ command, discard }) => Boolean.match(command.incremental, {
4623
4594
  onFalse: () => Effect$1.void,
4624
4595
  onTrue: () => Option$1.match(Option$1.fromUndefinedOr(command.contents), {
4625
4596
  onNone: () => Effect$1.void,
@@ -4629,15 +4600,26 @@ const discardLogOf = (command, discard) => Boolean.match(command.incremental, {
4629
4600
  })
4630
4601
  })
4631
4602
  });
4632
- const projectOf = (command, report) => ({
4603
+ const projectOf = ({ command, report }) => ({
4633
4604
  options: command.options,
4634
4605
  targetMutatePatterns: command.targetMutatePatterns,
4635
4606
  basePath: command.basePath,
4636
4607
  project: makeProject(command.fileDescriptions, report, command.testFiles)
4637
4608
  });
4609
+ //#endregion
4610
+ //#region src/read-project.cell.ts
4638
4611
  const readProjectCell = Sandwich.named("stryker.project_read")(readProjectCommand).decide(admitIncrementalReport).write({
4639
- IncrementalReportKeep: (keep, command) => Effect$1.succeed(projectOf(command, keep.report)),
4640
- IncrementalReportDiscard: (discard, command) => Effect$1.as(discardLogOf(command, discard), projectOf(command, void 0)),
4612
+ IncrementalReportKeep: (keep, command) => Effect$1.succeed(projectOf({
4613
+ command,
4614
+ report: keep.report
4615
+ })),
4616
+ IncrementalReportDiscard: (discard, command) => Effect$1.as(discardLogOf({
4617
+ command,
4618
+ discard
4619
+ }), projectOf({
4620
+ command,
4621
+ report: void 0
4622
+ })),
4641
4623
  CommandRejected: ({ issue }) => Effect$1.fail(badArgument({
4642
4624
  module: "stryker-js",
4643
4625
  method: "incremental-report.cell",
@@ -4712,8 +4694,11 @@ const formatTotal = (total) => Option$1.match(Option$1.fromNullishOr(total), {
4712
4694
  onSome: (val) => String(val)
4713
4695
  });
4714
4696
  const formatStderrEvent = (event) => Match.value(event).pipe(Match.tag("plan", (e) => `plan ${e.total} mutants`), Match.tag("phase", (e) => `phase ${e.phase}`), Match.tag("tick", (e) => `${e.completed}/${formatTotal(e.total)} elapsed ${e.elapsedMs}ms`), Match.tag("verdict", (e) => `score ${formatScore(e.score)} killed ${e.counts.killed} survived ${e.counts.survived}`), Match.tag("error", (e) => `error ${e.error}`), Match.tag("stream", () => null), Match.tag("mutant", () => null), Match.tag("help", () => null), Match.tag("plugins", () => null), Match.tag("formats", () => null), Match.tag("skipped", () => null), Match.exhaustive);
4715
- const stderrLineFor = (state, event) => Match.value(state.terminalSeen).pipe(Match.when(true, () => null), Match.when(false, () => formatStderrEvent(event)), Match.exhaustive);
4716
- const shouldFrame = (state) => Match.value(state.mode).pipe(Match.when("machine", () => !state.terminalSeen), Match.when("human", () => false), Match.exhaustive);
4697
+ const stderrLineFor = (state, event) => Boolean$1.match(Boolean$1.and(state.mode === "human", !state.terminalSeen), {
4698
+ onTrue: () => formatStderrEvent(event),
4699
+ onFalse: () => null
4700
+ });
4701
+ const shouldFrame = (state) => !state.terminalSeen;
4717
4702
  const decideFrame = (command) => {
4718
4703
  const nextState = noteState(command.state, command.event);
4719
4704
  const stderrLine = stderrLineFor(command.state, command.event);
@@ -4772,7 +4757,10 @@ const formatTemplate = (template, args) => {
4772
4757
  };
4773
4758
  const headOf = (args) => Option$1.fromNullishOr(args[0]);
4774
4759
  const formatArgs = (args) => Option$1.match(Option$1.filter(headOf(args), Predicate$1.isString), {
4775
- onSome: (first) => formatTemplate(first, args.slice(1)),
4760
+ onSome: (first) => Boolean$1.match(args.length === 1, {
4761
+ onTrue: () => first,
4762
+ onFalse: () => formatTemplate(first, args.slice(1))
4763
+ }),
4776
4764
  onNone: () => args.map(inspectValue).join(" ")
4777
4765
  });
4778
4766
  const DEFAULT_CONSOLE_LABEL = "default";
@@ -5006,7 +4994,7 @@ const PriorReportDocument = S.Struct({
5006
4994
  }))
5007
4995
  });
5008
4996
  //#endregion
5009
- //#region src/Survivors/Survivors.cell.ts
4997
+ //#region src/Survivors/Survivors.parts.ts
5010
4998
  const DEFAULT_SURVIVORS_PRIOR_REPORT = "reports/mutation-report.json";
5011
4999
  const EMPTY_CONFIG = {};
5012
5000
  const hashContent = (content) => bytesToHex(sha256(utf8ToBytes(content)));
@@ -5035,9 +5023,9 @@ const reportMutantToMutant = (file, mutant, resolveAbsolutePath, relativize) =>
5035
5023
  };
5036
5024
  const extractSurvivors = (priorReport, resolveAbsolutePath, relativize) => Object.entries(priorReport.files).flatMap(([file, fileResult]) => fileResult.mutants.filter((mutant) => mutant.status === "Survived").map((mutant) => reportMutantToMutant(file, mutant, resolveAbsolutePath, relativize)));
5037
5025
  const resolveAbsolutePathOf = (basePath) => (file) => `${basePath}/${file}`;
5038
- const resolveSurvivorsRunOptions = (cliOptions, mode) => readConfig(cliOptions, {
5026
+ const resolveSurvivorsRunOptions = (input) => readConfig(input.cliOptions, {
5039
5027
  command: "run",
5040
- mode
5028
+ mode: input.mode
5041
5029
  });
5042
5030
  const priorReportPathOf = (resolved) => Option$1.getOrElse(Option$1.filter(Option$1.fromUndefinedOr(resolved["survivorsPriorReport"]), Predicate$1.isString), () => DEFAULT_SURVIVORS_PRIOR_REPORT);
5043
5031
  const readPriorReport = (priorReportPath) => Effect$1.flatMap(FileSystem.FileSystem, (fs) => fs.readFileString(priorReportPath).pipe(Effect$1.map((text) => ({
@@ -5060,14 +5048,11 @@ const readSourceFile = (file) => Effect$1.flatMap(FileSystem.FileSystem, (fs) =>
5060
5048
  }))));
5061
5049
  const SOURCE_HASH_CONCURRENCY = 24;
5062
5050
  const currentSourceHashesFor = (files) => Effect$1.map(Effect$1.forEach(files, (file) => Effect$1.map(readSourceFile(file), (content) => [file, hashContent(content)]), { concurrency: SOURCE_HASH_CONCURRENCY }), (pairs) => Object.fromEntries(pairs));
5063
- Sandwich.named("stryker.survivors_admission")((input) => Effect$1.gen(function* () {
5064
- const resolvedOptions = yield* resolveSurvivorsRunOptions(input.cliOptions, input.mode);
5065
- const priorReportPath = priorReportPathOf(resolvedOptions);
5066
- const relativize = (fileName) => RelativeNormalizedFileName.fromAbsolute(fileName, input.basePath).fileName;
5067
- const resolveAbsolutePath = resolveAbsolutePathOf(input.basePath);
5068
- const read = yield* readPriorReport(priorReportPath);
5069
- const sourceContentHashes = yield* currentSourceHashesFor(priorReportFileKeys(read.raw));
5070
- return yield* Boolean$1.match(read.found, {
5051
+ const survivorsRawOf = (input) => {
5052
+ const { read, resolvedOptions, priorReportPath, basePath, sourceContentHashes } = input;
5053
+ const relativize = (fileName) => RelativeNormalizedFileName.fromAbsolute(fileName, basePath).fileName;
5054
+ const resolveAbsolutePath = resolveAbsolutePathOf(basePath);
5055
+ return Boolean$1.match(read.found, {
5071
5056
  onFalse: () => Effect$1.succeed({
5072
5057
  currentConfig: resolvedOptions,
5073
5058
  frameworkVersion: StrykerPackage.version,
@@ -5095,6 +5080,22 @@ Sandwich.named("stryker.survivors_admission")((input) => Effect$1.gen(function*
5095
5080
  })
5096
5081
  }))
5097
5082
  });
5083
+ };
5084
+ Sandwich.named("stryker.survivors_admission")((input) => Effect$1.gen(function* () {
5085
+ const resolvedOptions = yield* resolveSurvivorsRunOptions({
5086
+ cliOptions: input.cliOptions,
5087
+ mode: input.mode
5088
+ });
5089
+ const priorReportPath = priorReportPathOf(resolvedOptions);
5090
+ const read = yield* readPriorReport(priorReportPath);
5091
+ const sourceContentHashes = yield* currentSourceHashesFor(priorReportFileKeys(read.raw));
5092
+ return yield* survivorsRawOf({
5093
+ read,
5094
+ resolvedOptions,
5095
+ priorReportPath,
5096
+ basePath: input.basePath,
5097
+ sourceContentHashes
5098
+ });
5098
5099
  })).decide(admitSurvivorsRun).write({
5099
5100
  Admitted: (admitted, raw) => Effect$1.map(S.decodeEffect(Admitted)(admitted), (admission) => ({
5100
5101
  admission,
@@ -5411,7 +5412,7 @@ var RunEventDrain = class RunEventDrain extends Context.Service()("@systemfsoftw
5411
5412
  static layer = Layer.effect(RunEventDrain, Effect$1.gen(function* () {
5412
5413
  const stdio = yield* Stdio.Stdio;
5413
5414
  return RunEventDrain.of({
5414
- drainFramed: (framed) => drainOf(stdio, framed),
5415
+ drainFramed: (framed, toStdout) => drainOf(stdio, framed, toStdout),
5415
5416
  setProgressStreamFile: () => Effect$1.void
5416
5417
  });
5417
5418
  }));
@@ -5424,25 +5425,29 @@ var RunEventDrain = class RunEventDrain extends Context.Service()("@systemfsoftw
5424
5425
  };
5425
5426
  const RunEventDrainLive = RunEventDrain.layer;
5426
5427
  const encodeUtf8 = (line) => new TextEncoder().encode(line);
5427
- const drainStdoutAndFile = (fs, path, stdio, fileName, framed) => Effect$1.gen(function* () {
5428
+ const runToSink = (stdio, lines, toStdout) => Boolean$1.match(toStdout, {
5429
+ onTrue: () => Stream.run(lines, stdio.stdout({ endOnDone: true })),
5430
+ onFalse: () => Stream.runDrain(lines)
5431
+ });
5432
+ const drainToSinks = (fs, path, stdio, fileName, toStdout, framed) => Effect$1.gen(function* () {
5428
5433
  yield* fs.makeDirectory(path.dirname(fileName), { recursive: true });
5429
5434
  yield* Effect$1.scoped(Effect$1.gen(function* () {
5430
5435
  const handle = yield* fs.open(fileName, { flag: "w" });
5431
5436
  const withFile = framed.pipe(Stream.tap((line) => handle.writeAll(encodeUtf8(line)).pipe(Effect$1.flatMap(() => handle.sync))));
5432
- yield* Stream.run(withFile, stdio.stdout({ endOnDone: true })).pipe(Effect$1.ignore);
5437
+ yield* runToSink(stdio, withFile, toStdout).pipe(Effect$1.ignore);
5433
5438
  }));
5434
5439
  }).pipe(Effect$1.orDie);
5435
5440
  const drainFileOf = (stdio, fs, path) => Effect$1.gen(function* () {
5436
5441
  const fileNameRef = yield* Ref.make(DEFAULT_PROGRESS_STREAM_FILE);
5437
5442
  return RunEventDrain.of({
5438
- drainFramed: (framed) => Effect$1.gen(function* () {
5443
+ drainFramed: (framed, toStdout) => Effect$1.gen(function* () {
5439
5444
  const fileName = yield* Ref.get(fileNameRef);
5440
- yield* drainStdoutAndFile(fs, path, stdio, fileName, framed);
5445
+ yield* drainToSinks(fs, path, stdio, fileName, toStdout, framed);
5441
5446
  }).pipe(Effect$1.tapCause((cause) => Effect$1.logError("stryker.output.drain_file_failed", cause)), Effect$1.ignoreCause),
5442
5447
  setProgressStreamFile: (fileName) => Ref.set(fileNameRef, fileName)
5443
5448
  });
5444
5449
  });
5445
- const drainOf = (stdio, framed) => Stream.run(framed, stdio.stdout({ endOnDone: true })).pipe(Effect$1.withSpan("stryker.output.drain"), Effect$1.tapCause((cause) => Effect$1.logError("stryker.output.drain_failed", cause)), Effect$1.ignoreCause);
5450
+ const drainOf = (stdio, framed, toStdout) => runToSink(stdio, framed, toStdout).pipe(Effect$1.withSpan("stryker.output.drain"), Effect$1.tapCause((cause) => Effect$1.logError("stryker.output.drain_failed", cause)), Effect$1.ignoreCause);
5446
5451
  const writeStderr = (stdio, line) => Stream.run(Stream.succeed(`${line}\n`), stdio.stderr({ endOnDone: false })).pipe(Effect$1.ignore);
5447
5452
  const emitNullScoreVerdict = (params) => {
5448
5453
  const { stream, mode, thresholds, basePath, pathService } = params;
@@ -5490,7 +5495,7 @@ const emitHelpEnvelope = (stream, help) => Queue.offer(stream.queue, HelpRendere
5490
5495
  const helpPayload = (ok, captured) => Option$1.filter(Option$1.some(captured), () => ok.help || captured.length > 0);
5491
5496
  const emitNullScoreVerdictWhenOpen = (stream, mode, basePath, pathService) => Effect$1.andThen(stream.isOpen, (open) => Boolean$1.match(open, {
5492
5497
  onTrue: () => Effect$1.gen(function* () {
5493
- const defaults = yield* StrykerConfig.defaultOptions;
5498
+ const defaults = yield* defaultOptions;
5494
5499
  yield* emitNullScoreVerdict({
5495
5500
  stream,
5496
5501
  mode,
@@ -5589,7 +5594,8 @@ const makeRunEventStream = (resolved) => Effect$1.gen(function* () {
5589
5594
  open: Effect$1.gen(function* () {
5590
5595
  yield* Option$1.match(yield* Ref.get(drainFiberRef), {
5591
5596
  onNone: () => Effect$1.gen(function* () {
5592
- const drainFiber = yield* drain.drainFramed(framed).pipe(Effect$1.forkDetach);
5597
+ const mode = (yield* Ref.get(stateRef)).mode;
5598
+ const drainFiber = yield* drain.drainFramed(framed, mode === "machine").pipe(Effect$1.forkDetach);
5593
5599
  yield* Ref.set(drainFiberRef, Option$1.some(drainFiber));
5594
5600
  }),
5595
5601
  onSome: () => Effect$1.void
@@ -5611,6 +5617,14 @@ const makeRunEventStream = (resolved) => Effect$1.gen(function* () {
5611
5617
  var DryRunError = class extends S.TaggedError()("DryRunError", {
5612
5618
  stage: S.Literals(["dryRun", "dryRunNoTests"]),
5613
5619
  reason: S.String
5620
+ }) {
5621
+ get message() {
5622
+ return this.reason;
5623
+ }
5624
+ };
5625
+ var FailedTestSummary = class extends S.Class("FailedTestSummary")({
5626
+ name: S.String,
5627
+ failureMessage: S.String
5614
5628
  }) {};
5615
5629
  var DryRunCommand = class extends S.TaggedClass()("DryRunCommand", {
5616
5630
  status: S.Literals([
@@ -5620,6 +5634,7 @@ var DryRunCommand = class extends S.TaggedClass()("DryRunCommand", {
5620
5634
  ]),
5621
5635
  testCount: S.Finite,
5622
5636
  failedTestCount: S.Finite,
5637
+ failedTests: S.Array(FailedTestSummary),
5623
5638
  allowEmpty: S.Boolean,
5624
5639
  errorMessage: S.optional(S.String),
5625
5640
  reason: S.optional(S.String)
@@ -5637,7 +5652,8 @@ var DryRunPassed = class extends S.TaggedClass()("DryRunPassed", { testCount: S.
5637
5652
  };
5638
5653
  var DryRunFailed = class extends S.TaggedClass()("DryRunFailed", {
5639
5654
  testCount: S.Finite,
5640
- failedTestCount: S.Finite
5655
+ failedTestCount: S.Finite,
5656
+ failedTests: S.Array(FailedTestSummary)
5641
5657
  }) {
5642
5658
  [DryRunDecisionTypeId] = DryRunDecisionTypeId;
5643
5659
  };
@@ -5648,13 +5664,15 @@ const completeOutcomeOf = (command) => Match.value(command).pipe(Match.when({
5648
5664
  const decideComplete = (command) => Match.value({
5649
5665
  outcome: completeOutcomeOf(command),
5650
5666
  testCount: command.testCount,
5651
- failedTestCount: command.failedTestCount
5667
+ failedTestCount: command.failedTestCount,
5668
+ failedTests: command.failedTests
5652
5669
  }).pipe(Match.when({ outcome: "noTests" }, () => Result.fail(DryRunError.make({
5653
5670
  stage: "dryRunNoTests",
5654
5671
  reason: "No tests were executed. Stryker will exit prematurely. Please check your configuration."
5655
- }))), Match.when({ outcome: "failed" }, ({ testCount, failedTestCount }) => Result.succeed(DryRunFailed.make({
5672
+ }))), Match.when({ outcome: "failed" }, ({ testCount, failedTestCount, failedTests }) => Result.succeed(DryRunFailed.make({
5656
5673
  testCount,
5657
- failedTestCount
5674
+ failedTestCount,
5675
+ failedTests
5658
5676
  }))), Match.when({ outcome: "passed" }, ({ testCount }) => Result.succeed(DryRunPassed.make({ testCount }))), Match.exhaustive);
5659
5677
  const reasonOrDefault = (reason, fallback) => Match.value(reason).pipe(Match.when(void 0, () => fallback), Match.orElse((present) => present));
5660
5678
  const decide$5 = (command) => Match.value(command.status).pipe(Match.when("Error", () => Result.fail(DryRunError.make({
@@ -5704,7 +5722,52 @@ var PluginLoadRefusedError = class extends Schema.TaggedError()("PluginLoadRefus
5704
5722
  }
5705
5723
  };
5706
5724
  //#endregion
5707
- //#region src/TestRunner.resource.ts
5725
+ //#region src/Sandbox.handle.ts
5726
+ const TypeId$3 = Symbol.for("~systemfsoftware/stryker-js/Sandbox");
5727
+ const SandboxHandle = Handle.make()(TypeId$3);
5728
+ SandboxHandle.is;
5729
+ const sandboxFileNameOf = (fileMap, fileName) => Match.value(fileMap.get(fileName)).pipe(Match.when(Predicate$1.isString, (sandboxFileName) => Result.succeed(sandboxFileName)), Match.orElse(() => Result.fail(StrykerError.make({ message: `Cannot find sandbox file for ${fileName}` }))));
5730
+ const withoutLeadingSeparator = (suffix) => Boolean$1.match(suffix.startsWith("/"), {
5731
+ onTrue: () => suffix.slice(1),
5732
+ onFalse: () => suffix
5733
+ });
5734
+ const relativeToBase = (resolvedSandbox, resolvedWorking, base, pathService) => {
5735
+ const trimmed = withoutLeadingSeparator(resolvedSandbox.slice(resolvedWorking.length));
5736
+ return Boolean$1.match(trimmed.length === 0, {
5737
+ onTrue: () => base,
5738
+ onFalse: () => pathService.join(base, trimmed)
5739
+ });
5740
+ };
5741
+ const originalFileNameOf = (sandboxFileName, workingDirectory, base, pathService) => {
5742
+ const resolvedSandbox = pathService.resolve(sandboxFileName);
5743
+ const resolvedWorking = pathService.resolve(workingDirectory);
5744
+ return Boolean$1.match(resolvedSandbox.startsWith(resolvedWorking), {
5745
+ onTrue: () => relativeToBase(resolvedSandbox, resolvedWorking, base, pathService),
5746
+ onFalse: () => resolvedSandbox.replace(resolvedWorking, base)
5747
+ });
5748
+ };
5749
+ const make$1 = (options) => SandboxHandle.make({ workingDirectory: options.workingDirectory }, {
5750
+ fileMap: options.fileMap,
5751
+ basePath: options.basePath,
5752
+ pathService: options.pathService
5753
+ });
5754
+ const sandboxFileFor = dual(2, (self, fileName) => sandboxFileNameOf(SandboxHandle.slot(self).fileMap, fileName));
5755
+ const originalFileFor = dual(2, (self, sandboxFileName) => {
5756
+ const slot = SandboxHandle.slot(self);
5757
+ return originalFileNameOf(sandboxFileName, self.workingDirectory, slot.basePath, slot.pathService);
5758
+ });
5759
+ const VM_RUNNER_SPECIFIER = "@systemfsoftware/stryker-js-vm-runner";
5760
+ const vmRunnerOptions = { pool: "threads" };
5761
+ const vmRunnerPluginUrl = () => import.meta.resolve(VM_RUNNER_SPECIFIER);
5762
+ const isVmRunner = (name) => typeof name === "string" && name.toLowerCase() === "vm";
5763
+ const vmTestRunnerConfig = () => ({
5764
+ plugin: vmRunnerPluginUrl(),
5765
+ options: { ...vmRunnerOptions }
5766
+ });
5767
+ const testRunnerConfigOf = (configured) => Match.value(configured).pipe(Match.when(isVmRunner, vmTestRunnerConfig), Match.orElse((unchanged) => unchanged));
5768
+ //#endregion
5769
+ //#region src/TestRunner.blueprint.ts
5770
+ const TypeId$2 = Symbol.for("~systemfsoftware/stryker-js/TestRunner");
5708
5771
  const toRunnerBootFailure = (runnerName) => (error) => Match.value(error).pipe(Match.tag("WorkerBootTimeoutError", (timeout) => TestRunner_exports.TestRunnerFailed.make({
5709
5772
  runnerName,
5710
5773
  phase: "init",
@@ -5727,12 +5790,16 @@ const toRunnerFailure = (runnerName, phase) => (error) => Match.value(error).pip
5727
5790
  * `Pool.invalidate` and the crash-retry combinator can only act on a failure they
5728
5791
  * can see.
5729
5792
  */
5730
- const makeChildProcessTestRunner = (params) => Effect$1.gen(function* () {
5731
- const runnerName = Match.value(params.options.testRunner).pipe(Match.when(Options_exports.isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
5732
- const execArgv = Match.value(params.options.testRunner).pipe(Match.when(Options_exports.isCustomTestRunner, (runner) => Match.value(runner.nodeArgs).pipe(Match.when(Match.undefined, () => params.options.testRunnerNodeArgs), Match.orElse((args) => args))), Match.orElse(() => params.options.testRunnerNodeArgs));
5793
+ const scopedOf = (params) => Effect$1.gen(function* () {
5794
+ const options = {
5795
+ ...params.options,
5796
+ testRunner: testRunnerConfigOf(params.options.testRunner)
5797
+ };
5798
+ const runnerName = Match.value(options.testRunner).pipe(Match.when(Options_exports.isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
5799
+ const execArgv = Match.value(options.testRunner).pipe(Match.when(Options_exports.isCustomTestRunner, (runner) => Match.value(runner.nodeArgs).pipe(Match.when(Match.undefined, () => options.testRunnerNodeArgs), Match.orElse((args) => args))), Match.orElse(() => options.testRunnerNodeArgs));
5733
5800
  const client = yield* makeWorkerClient({
5734
5801
  rpcs: Plugin_exports.TestRunnerRpcs,
5735
- options: params.options,
5802
+ options,
5736
5803
  entrypoint: params.workerEntrypoint,
5737
5804
  workingDirectory: params.sandboxWorkingDirectory,
5738
5805
  execArgv: [...execArgv],
@@ -5750,11 +5817,13 @@ const makeChildProcessTestRunner = (params) => Effect$1.gen(function* () {
5750
5817
  mutantRun: (options) => client.mutantRun({ options }).pipe(Effect$1.mapError(toRunnerFailure(runnerName, "mutantRun")))
5751
5818
  });
5752
5819
  });
5820
+ const TestRunners = Blueprint.make()(TypeId$2).steps({
5821
+ steps: {},
5822
+ targets: { scoped: scopedOf }
5823
+ });
5824
+ const makeChildProcessTestRunner = (params) => TestRunners.of(params).scoped;
5753
5825
  const commandRunnerEffect = (context) => ChildProcessSpawner.ChildProcessSpawner.pipe(Effect$1.map((spawner) => commandRunner(context, spawner).pipe(withTimeout, withRetry)));
5754
- const inProcessRunner = (context) => Match.value(context.options.testRunner).pipe(Match.when(isCommandRunner, () => Option$1.some(commandRunnerEffect(context))), Match.when(isVmRunner, () => Option$1.some(vmTestRunner({
5755
- testFiles: context.testFiles,
5756
- sandboxWorkingDirectory: context.sandboxWorkingDirectory
5757
- }))), Match.orElse(() => Option$1.none()));
5826
+ const inProcessRunner = (context) => Match.value(context.options.testRunner).pipe(Match.when(isCommandRunner, () => Option$1.some(commandRunnerEffect(context))), Match.orElse(() => Option$1.none()));
5758
5827
  const buildTestRunner = dual(2, (context, childProcessRunner) => Option$1.match(inProcessRunner(context), {
5759
5828
  onSome: (inProcess) => inProcess,
5760
5829
  onNone: () => Effect$1.gen(function* () {
@@ -5911,9 +5980,9 @@ const resolveConfiguredPlugin = Workflow.make({
5911
5980
  decide: decide$4
5912
5981
  });
5913
5982
  //#endregion
5914
- //#region src/run/dry-run.cell.ts
5915
- const sandboxPathsOf = (sandbox, fileNames) => Result.all(fileNames.map((fileName) => sandbox.sandboxFileFor(fileName)));
5916
- const configuredPluginOf$1 = (configured) => Match.value(configured).pipe(Match.when(Options_exports.isCustomTestRunner, (custom) => ConfiguredPluginModulePath.make({ modulePath: custom.plugin })), Match.orElse((name) => ConfiguredPluginName.make({ name })));
5983
+ //#region src/run/dry-run.parts.ts
5984
+ const sandboxPathsOf = (sandbox, fileNames) => Result.all(fileNames.map((fileName) => sandboxFileFor(sandbox, fileName)));
5985
+ const configuredPluginOf$1 = (configured) => Match.value(testRunnerConfigOf(configured)).pipe(Match.when(Options_exports.isCustomTestRunner, (custom) => ConfiguredPluginModulePath.make({ modulePath: custom.plugin })), Match.orElse((name) => ConfiguredPluginName.make({ name })));
5917
5986
  const workerSpawnOf$1 = (stage, loaded, kind, configured) => Effect$1.mapError(Effect$1.fromResult(resolveConfiguredPlugin(WorkerSpawnCommand.make({
5918
5987
  sources: loaded.pluginSources,
5919
5988
  kind,
@@ -5927,7 +5996,11 @@ const optionalSandboxPathsOf = (command) => Boolean$1.match(command.options.test
5927
5996
  onTrue: () => Result.succeed(void 0),
5928
5997
  onFalse: () => sandboxPathsOf(command.sandbox, command.project.testFiles)
5929
5998
  });
5930
- const buildDryRunFiles = (command) => Result.flatMap(sandboxPathsOf(command.sandbox, [...MutableHashMap.keys(command.project.filesToMutate)]), (files) => Result.map(optionalSandboxPathsOf(command), (testFiles) => ({
5999
+ const mutatedFileNamesOf = (command) => {
6000
+ const mutated = MutableHashSet.fromIterable(command.mutants.map((mutant) => mutant.fileName));
6001
+ return [...MutableHashMap.keys(command.project.filesToMutate)].filter((name) => MutableHashSet.has(mutated, name));
6002
+ };
6003
+ const buildDryRunFiles = (command) => Result.flatMap(sandboxPathsOf(command.sandbox, mutatedFileNamesOf(command)), (files) => Result.map(optionalSandboxPathsOf(command), (testFiles) => ({
5931
6004
  files,
5932
6005
  testFiles
5933
6006
  })));
@@ -5936,11 +6009,16 @@ const resolveDryRunFiles = (command) => Effect$1.fromResult(buildDryRunFiles(com
5936
6009
  reason: "Failed to resolve sandbox file",
5937
6010
  cause
5938
6011
  })));
6012
+ const failedTestSummariesOf = (tests) => tests.filter((test) => test.status === "failed").map((test) => FailedTestSummary.make({
6013
+ name: test.name,
6014
+ failureMessage: test.failureMessage
6015
+ }));
5939
6016
  const commandEncodedComplete = (complete, allowEmpty) => ({
5940
6017
  _tag: "DryRunCommand",
5941
6018
  status: "Complete",
5942
6019
  testCount: complete.tests.length,
5943
6020
  failedTestCount: complete.tests.filter((test) => test.status === "failed").length,
6021
+ failedTests: failedTestSummariesOf(complete.tests),
5944
6022
  allowEmpty
5945
6023
  });
5946
6024
  const commandEncodedFailed = (failed, allowEmpty) => ({
@@ -5948,6 +6026,7 @@ const commandEncodedFailed = (failed, allowEmpty) => ({
5948
6026
  status: "Error",
5949
6027
  testCount: 0,
5950
6028
  failedTestCount: 0,
6029
+ failedTests: [],
5951
6030
  allowEmpty,
5952
6031
  errorMessage: failed.errorMessage
5953
6032
  });
@@ -5956,6 +6035,7 @@ const commandEncodedTimedOut = (timedOut, allowEmpty) => ({
5956
6035
  status: "Timeout",
5957
6036
  testCount: 0,
5958
6037
  failedTestCount: 0,
6038
+ failedTests: [],
5959
6039
  allowEmpty,
5960
6040
  ...Option$1.match(Option$1.fromNullishOr(timedOut.reason), {
5961
6041
  onNone: () => ({}),
@@ -5987,7 +6067,7 @@ const totalTestTime = (tests) => tests.reduce((total, test) => total + test.time
5987
6067
  const overheadMillisOf = (grossMillis, tests) => Math.max(0, grossMillis - totalTestTime(tests));
5988
6068
  const withOriginalFileName = (test, prev) => Match.value(test.fileName).pipe(Match.when(Predicate$1.isString, (fileName) => ({
5989
6069
  ...test,
5990
- fileName: prev.sandbox.originalFileFor(fileName)
6070
+ fileName: originalFileFor(prev.sandbox, fileName)
5991
6071
  })), Match.orElse(() => test));
5992
6072
  const withOriginalFileNames = (tests, prev) => tests.map((test) => withOriginalFileName(test, prev));
5993
6073
  const ZERO = 0;
@@ -6110,20 +6190,32 @@ const writeDryRunPassed = (raw) => withPhaseSpan("dryRun", {}, () => Effect$1.ge
6110
6190
  yield* phaseEntered("dry-run");
6111
6191
  return yield* completeDryRunPassed(raw);
6112
6192
  }));
6113
- const writeDryRunFailed = (raw, testCount, failedTestCount) => withPhaseSpan("dryRun", {}, () => Effect$1.gen(function* () {
6193
+ const FAILED_TESTS_REASON = "There were failed tests in the initial test run.";
6194
+ const failedTestsDetail = (failedTests) => failedTests.map((test) => ` ${test.name}${test.failureMessage.length > 0 ? `: ${test.failureMessage}` : ""}`).join("\n");
6195
+ const reasonWithFailedTests = (detail) => detail.length > 0 ? `${FAILED_TESTS_REASON}\n${detail}` : FAILED_TESTS_REASON;
6196
+ const writeDryRunFailed = ({ testCount, failedTestCount, failedTests }) => withPhaseSpan("dryRun", {}, () => Effect$1.gen(function* () {
6114
6197
  yield* phaseEntered("dry-run");
6198
+ const detail = failedTestsDetail(failedTests);
6199
+ yield* Effect$1.logError(`Initial test run failed. ${failedTestCount} of ${testCount} test(s) failed:\n${detail}`);
6115
6200
  return yield* StageError.make({
6116
6201
  stage: "dryRun",
6117
- reason: "There were failed tests in the initial test run.",
6202
+ reason: reasonWithFailedTests(detail),
6118
6203
  cause: DryRunFailed.make({
6119
6204
  testCount,
6120
- failedTestCount
6205
+ failedTestCount,
6206
+ failedTests
6121
6207
  })
6122
6208
  });
6123
6209
  }));
6210
+ //#endregion
6211
+ //#region src/run/dry-run.cell.ts
6124
6212
  const dryRunCell = Sandwich.named("stryker.dry_run")(readDryRun).decide(dryRun).write({
6125
6213
  DryRunPassed: (_decision, raw) => writeDryRunPassed(raw),
6126
- DryRunFailed: ({ testCount, failedTestCount }, raw) => writeDryRunFailed(raw, testCount, failedTestCount),
6214
+ DryRunFailed: ({ testCount, failedTestCount, failedTests }, _raw) => writeDryRunFailed({
6215
+ testCount,
6216
+ failedTestCount,
6217
+ failedTests
6218
+ }),
6127
6219
  DryRunError: ({ stage, reason }) => Effect$1.fail(StageError.make({
6128
6220
  stage,
6129
6221
  reason,
@@ -6139,14 +6231,11 @@ const dryRunCell = Sandwich.named("stryker.dry_run")(readDryRun).decide(dryRun).
6139
6231
  });
6140
6232
  //#endregion
6141
6233
  //#region src/plan-instrumentation.workflow.ts
6142
- var InstrumentError = class extends S.TaggedError()("InstrumentError", {
6143
- stage: S.Literal("instrument"),
6144
- reason: S.String
6145
- }) {};
6234
+ const Count = S.Int.check(S.isGreaterThanOrEqualTo(0));
6146
6235
  var InstrumentCommand = class extends S.TaggedClass()("InstrumentCommand", {
6147
- fileCount: S.Finite,
6236
+ fileCount: Count,
6148
6237
  inPlace: S.Boolean,
6149
- pluginCount: S.Finite
6238
+ pluginCount: Count
6150
6239
  }) {
6151
6240
  static [Workflow.InstrumentationBrand] = {
6152
6241
  fileCount: "stryker.instrument.file_count",
@@ -6157,32 +6246,28 @@ const InstrumentDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js/Instrum
6157
6246
  var InPlaceInstrument = class extends S.TaggedClass()("InPlaceInstrument", {
6158
6247
  workingDirectoryHint: S.String,
6159
6248
  backupDirectoryHint: S.String,
6160
- fileCount: S.Finite
6249
+ fileCount: Count
6161
6250
  }) {
6162
6251
  [InstrumentDecisionTypeId] = InstrumentDecisionTypeId;
6163
6252
  };
6164
6253
  var EphemeralInstrument = class extends S.TaggedClass()("EphemeralInstrument", {
6165
6254
  workingDirectoryHint: S.String,
6166
- fileCount: S.Finite
6255
+ fileCount: Count
6167
6256
  }) {
6168
6257
  [InstrumentDecisionTypeId] = InstrumentDecisionTypeId;
6169
6258
  };
6170
- const commandShape = (command) => Match.value(command.fileCount === 0).pipe(Match.when(true, () => "empty"), Match.when(false, () => Match.value(command.inPlace).pipe(Match.when(true, () => "inPlace"), Match.when(false, () => "ephemeral"), Match.exhaustive)), Match.exhaustive);
6171
- const decide$3 = (command) => Match.value(commandShape(command)).pipe(Match.when("empty", () => Result.fail(InstrumentError.make({
6172
- stage: "instrument",
6173
- reason: "No files to instrument."
6174
- }))), Match.when("inPlace", () => Result.succeed(InPlaceInstrument.make({
6259
+ const decide$3 = (command) => Match.value(command.inPlace).pipe(Match.when(true, () => Result.succeed(InPlaceInstrument.make({
6175
6260
  workingDirectoryHint: "inPlace",
6176
6261
  backupDirectoryHint: "backup",
6177
6262
  fileCount: command.fileCount
6178
- }))), Match.when("ephemeral", () => Result.succeed(EphemeralInstrument.make({
6263
+ }))), Match.when(false, () => Result.succeed(EphemeralInstrument.make({
6179
6264
  workingDirectoryHint: "temp",
6180
6265
  fileCount: command.fileCount
6181
6266
  }))), Match.exhaustive);
6182
6267
  const planInstrumentation = Workflow.make({
6183
6268
  command: InstrumentCommand,
6184
6269
  decision: S.Union([InPlaceInstrument, EphemeralInstrument]),
6185
- error: InstrumentError,
6270
+ error: S.Never,
6186
6271
  decide: decide$3
6187
6272
  });
6188
6273
  //#endregion
@@ -6406,37 +6491,6 @@ function buildErrorMessage({ type, sourceText, position }) {
6406
6491
  return `Cannot parse JSONC: unexpected token "${token}" in JSONC at position ${position}`;
6407
6492
  }
6408
6493
  //#endregion
6409
- //#region src/Sandbox.handle.ts
6410
- const TypeId$1 = Symbol.for("@systemfsoftware/stryker-js/SandboxHandle");
6411
- const sandboxFileNameOf = (fileMap, fileName) => Match.value(fileMap.get(fileName)).pipe(Match.when(Predicate.isString, (sandboxFileName) => Result.succeed(sandboxFileName)), Match.orElse(() => Result.fail(StrykerError.make({ message: `Cannot find sandbox file for ${fileName}` }))));
6412
- const withoutLeadingSeparator = (suffix) => Boolean.match(suffix.startsWith("/"), {
6413
- onTrue: () => suffix.slice(1),
6414
- onFalse: () => suffix
6415
- });
6416
- const relativeToBase = (resolvedSandbox, resolvedWorking, base, pathService) => {
6417
- const trimmed = withoutLeadingSeparator(resolvedSandbox.slice(resolvedWorking.length));
6418
- return Boolean.match(trimmed.length === 0, {
6419
- onTrue: () => base,
6420
- onFalse: () => pathService.join(base, trimmed)
6421
- });
6422
- };
6423
- const originalFileNameOf = (sandboxFileName, workingDirectory, base, pathService) => {
6424
- const resolvedSandbox = pathService.resolve(sandboxFileName);
6425
- const resolvedWorking = pathService.resolve(workingDirectory);
6426
- return Boolean.match(resolvedSandbox.startsWith(resolvedWorking), {
6427
- onTrue: () => relativeToBase(resolvedSandbox, resolvedWorking, base, pathService),
6428
- onFalse: () => resolvedSandbox.replace(resolvedWorking, base)
6429
- });
6430
- };
6431
- const make$1 = (options) => {
6432
- const handle = {
6433
- workingDirectory: options.workingDirectory,
6434
- sandboxFileFor: (fileName) => sandboxFileNameOf(options.fileMap, fileName),
6435
- originalFileFor: (sandboxFileName) => originalFileNameOf(sandboxFileName, options.workingDirectory, options.basePath, options.pathService)
6436
- };
6437
- return Object.assign(handle, { [TypeId$1]: TypeId$1 });
6438
- };
6439
- //#endregion
6440
6494
  //#region src/Sandbox.schema.ts
6441
6495
  /**
6442
6496
  * Sandbox.schema — declarations for the Sandbox capability.
@@ -6463,7 +6517,7 @@ const TsConfigSchema = Schema.StructWithRest(Schema.Struct({
6463
6517
  /** A tsconfig `extends` entry list: the array form of `extends`. */
6464
6518
  const ExtendsArraySchema = Schema.Array(Schema.String);
6465
6519
  //#endregion
6466
- //#region src/Sandbox.resource.ts
6520
+ //#region src/Sandbox.blueprint.ts
6467
6521
  const combinePreprocessors = (preprocessors) => (project) => Effect$1.forEach(preprocessors, (pre) => pre(project), { discard: true });
6468
6522
  const mergeUpdatedFile = (project, updated) => {
6469
6523
  MutableHashMap.set(project.files, updated.name, updated);
@@ -6723,13 +6777,13 @@ const symlinkNodeModules = (options, workingDirectory, basePath, pathService) =>
6723
6777
  onFalse: () => Effect$1.void
6724
6778
  });
6725
6779
  });
6726
- const acquireSandbox = (state) => Effect$1.gen(function* () {
6727
- const { options, project, workingDirectory, backupDirectory, basePath } = state.spec;
6780
+ const acquireSandbox = (spec) => Effect$1.gen(function* () {
6781
+ const { options, project, workingDirectory, backupDirectory, basePath } = spec;
6728
6782
  yield* Scope.Scope;
6729
6783
  const pathService = yield* Path.Path;
6730
6784
  yield* announceSandbox(options, workingDirectory, backupDirectory, basePath, pathService);
6731
6785
  yield* Effect$1.when(restoreOriginalFiles(workingDirectory, backupDirectory, basePath), Effect$1.succeed(hasBackupToRestore(options, backupDirectory)));
6732
- yield* combinePreprocessors([createPreprocessor(options, basePath, state.spec.formatRegistry), ...state.preprocessors])(project).pipe(Effect$1.mapError((cause) => StrykerError.make({
6786
+ yield* combinePreprocessors([createPreprocessor(options, basePath, spec.formatRegistry), ...spec.preprocessors])(project).pipe(Effect$1.mapError((cause) => StrykerError.make({
6733
6787
  message: "Sandbox preprocessor failed",
6734
6788
  cause
6735
6789
  })));
@@ -6754,31 +6808,22 @@ const acquireSandbox = (state) => Effect$1.gen(function* () {
6754
6808
  pathService
6755
6809
  });
6756
6810
  });
6757
- const TypeId = Symbol.for("@systemfsoftware/stryker-js/Sandbox");
6758
- const makeProto = (state) => {
6759
- return {
6760
- [TypeId]: TypeId,
6761
- spec: state.spec,
6762
- ...Prototype,
6763
- withPreprocessor(preprocessor) {
6764
- return makeProto({
6765
- spec: state.spec,
6766
- preprocessors: [...state.preprocessors, preprocessor]
6767
- });
6768
- },
6769
- get scoped() {
6770
- return acquireSandbox(state);
6771
- },
6772
- layer(service) {
6773
- return Layer.effect(service)(acquireSandbox(state));
6774
- }
6775
- };
6776
- };
6777
- const make = (spec) => makeProto({
6778
- spec,
6811
+ const TypeId$1 = Symbol.for("~systemfsoftware/stryker-js/Sandbox");
6812
+ const Sandboxes = Blueprint.make()(TypeId$1).steps({
6813
+ steps: { withPreprocessor: (spec, preprocessor) => ({
6814
+ ...spec,
6815
+ preprocessors: [...spec.preprocessors, preprocessor]
6816
+ }) },
6817
+ targets: {
6818
+ scoped: (spec) => acquireSandbox(spec),
6819
+ layer: (spec) => (service) => Layer.effect(service)(acquireSandbox(spec))
6820
+ }
6821
+ });
6822
+ const make = (spec) => Sandboxes.of({
6823
+ ...spec,
6779
6824
  preprocessors: []
6780
6825
  });
6781
- dual(2, (self, preprocessor) => self.withPreprocessor(preprocessor));
6826
+ Sandboxes.operations.withPreprocessor;
6782
6827
  const makeSandbox = (input) => make(input).scoped;
6783
6828
  //#endregion
6784
6829
  //#region src/run/explain-file-skip.workflow.ts
@@ -6847,13 +6892,13 @@ const explainFileSkip = Workflow.make({
6847
6892
  decide: (command) => Match.value(skipKnownOrUnknownOf(explainedOf(command))).pipe(Match.tag("SkipKnownExplained", (known) => Result.succeed(known)), Match.tag("SkipUnknownExplained", (unknown) => Result.succeed(unknown)), Match.exhaustive)
6848
6893
  });
6849
6894
  //#endregion
6850
- //#region src/run/instrument.cell.ts
6851
- const offerSkipsIfAny = (skipped, claimants) => Boolean$1.match(skipped.length === 0, {
6895
+ //#region src/run/instrument.parts.ts
6896
+ const offerSkipsIfAny = (input) => Boolean$1.match(input.skipped.length === 0, {
6852
6897
  onTrue: () => Effect$1.void,
6853
6898
  onFalse: () => Effect$1.gen(function* () {
6854
- const files = skipped.map((skip) => Result.match(explainFileSkip(ExplainFileSkipCommand.make({
6899
+ const files = input.skipped.map((skip) => Result.match(explainFileSkip(ExplainFileSkipCommand.make({
6855
6900
  extension: skip.extension,
6856
- claimants: [...claimants]
6901
+ claimants: [...input.claimants]
6857
6902
  })), {
6858
6903
  onFailure: absurd,
6859
6904
  onSuccess: (explained) => ({
@@ -6866,11 +6911,40 @@ const offerSkipsIfAny = (skipped, claimants) => Boolean$1.match(skipped.length =
6866
6911
  yield* Queue.offer(queue, SkippedReported.make({ files }));
6867
6912
  })
6868
6913
  });
6914
+ const sandboxDirectoriesOf = (input) => Boolean$1.match(input.command.options.inPlace, {
6915
+ onTrue: () => ({
6916
+ workingDirectory: input.basePath,
6917
+ backupDirectory: input.command.temporaryDirectoryPath
6918
+ }),
6919
+ onFalse: () => ({
6920
+ workingDirectory: input.command.temporaryDirectoryPath,
6921
+ backupDirectory: ""
6922
+ })
6923
+ });
6924
+ const mergeInstrumentedFile = (input) => {
6925
+ const files = MutableHashMap.fromIterable(input.project.files);
6926
+ MutableHashMap.set(files, input.file.name, input.file);
6927
+ const filesToMutate = MutableHashMap.fromIterable(input.project.filesToMutate);
6928
+ Boolean$1.match(input.file.mutate !== false, {
6929
+ onTrue: () => MutableHashMap.set(filesToMutate, input.file.name, input.file),
6930
+ onFalse: () => MutableHashMap.remove(filesToMutate, input.file.name)
6931
+ });
6932
+ return {
6933
+ ...input.project,
6934
+ files,
6935
+ filesToMutate
6936
+ };
6937
+ };
6938
+ //#endregion
6939
+ //#region src/run/instrument.cell.ts
6869
6940
  const enteringInstrumentPhase = (raw, body) => withPhaseSpan("instrument", { fileCount: raw.filesToMutate.length }, () => Effect$1.gen(function* () {
6870
6941
  yield* phaseEntered("instrument");
6871
6942
  return yield* body;
6872
6943
  }));
6873
- const writeInstrument = (raw) => enteringInstrumentPhase(raw, Effect$1.as(offerSkipsIfAny(raw.instrumentResult.skipped, raw.prev.frameworkClaimants), {
6944
+ const writeInstrument = (raw) => enteringInstrumentPhase(raw, Effect$1.as(offerSkipsIfAny({
6945
+ skipped: raw.instrumentResult.skipped,
6946
+ claimants: raw.prev.frameworkClaimants
6947
+ }), {
6874
6948
  ...raw.prev,
6875
6949
  project: raw.instrumentedProject,
6876
6950
  mutants: raw.instrumentResult.mutants,
@@ -6880,37 +6954,13 @@ const writeInstrument = (raw) => enteringInstrumentPhase(raw, Effect$1.as(offerS
6880
6954
  checkers: raw.concurrency.checkers
6881
6955
  }
6882
6956
  }));
6883
- const sandboxDirectoriesOf = (command, basePath) => Boolean$1.match(command.options.inPlace, {
6884
- onTrue: () => ({
6885
- workingDirectory: basePath,
6886
- backupDirectory: command.temporaryDirectoryPath
6887
- }),
6888
- onFalse: () => ({
6889
- workingDirectory: command.temporaryDirectoryPath,
6890
- backupDirectory: ""
6891
- })
6892
- });
6893
- const mergeInstrumentedFile = (project, file) => {
6894
- const files = MutableHashMap.fromIterable(project.files);
6895
- MutableHashMap.set(files, file.name, file);
6896
- const filesToMutate = MutableHashMap.fromIterable(project.filesToMutate);
6897
- Boolean$1.match(file.mutate !== false, {
6898
- onTrue: () => MutableHashMap.set(filesToMutate, file.name, file),
6899
- onFalse: () => MutableHashMap.remove(filesToMutate, file.name)
6900
- });
6901
- return {
6902
- ...project,
6903
- files,
6904
- filesToMutate
6905
- };
6906
- };
6907
- const withInstrumentedFiles = (project, instrumented) => Arr.reduce([...instrumented], project, (current, { name, content }) => Option$1.match(MutableHashMap.get(current.files, name), {
6908
- onNone: () => current,
6909
- onSome: (existing) => mergeInstrumentedFile(current, {
6957
+ const withInstrumentedFiles = (project, instrumented) => Arr.reduce([...instrumented], project, (current, { name, content }) => Option$1.getOrElse(Option$1.map(MutableHashMap.get(current.files, name), (existing) => mergeInstrumentedFile({
6958
+ project: current,
6959
+ file: {
6910
6960
  ...existing,
6911
6961
  content
6912
- })
6913
- }));
6962
+ }
6963
+ })), () => current));
6914
6964
  const instrumentCell = Sandwich.named("stryker.instrument")((command) => Effect$1.gen(function* () {
6915
6965
  yield* Scope.Scope;
6916
6966
  const env = yield* RunEnvironment;
@@ -6934,7 +6984,10 @@ const instrumentCell = Sandwich.named("stryker.instrument")((command) => Effect$
6934
6984
  cause
6935
6985
  })));
6936
6986
  const instrumentedProject = withInstrumentedFiles(command.project, instrumentResult.files);
6937
- const directories = sandboxDirectoriesOf(command, env.basePath);
6987
+ const directories = sandboxDirectoriesOf({
6988
+ command,
6989
+ basePath: env.basePath
6990
+ });
6938
6991
  const sandbox = yield* makeSandbox({
6939
6992
  options: command.options,
6940
6993
  project: instrumentedProject,
@@ -6962,14 +7015,6 @@ const instrumentCell = Sandwich.named("stryker.instrument")((command) => Effect$
6962
7015
  })).decide(planInstrumentation).write({
6963
7016
  InPlaceInstrument: (_decision, raw) => writeInstrument(raw),
6964
7017
  EphemeralInstrument: (_decision, raw) => writeInstrument(raw),
6965
- InstrumentError: ({ stage, reason }, raw) => enteringInstrumentPhase(raw, Effect$1.fail(StageError.make({
6966
- stage,
6967
- reason,
6968
- cause: InstrumentError.make({
6969
- stage,
6970
- reason
6971
- })
6972
- }))),
6973
7018
  CommandRejected: ({ issue }) => Effect$1.fail(StageError.make({
6974
7019
  stage: "instrument",
6975
7020
  reason: issue
@@ -6994,7 +7039,11 @@ var MutationTestCommand = class extends S.TaggedClass()("MutationTestCommand", {
6994
7039
  var MutationTestError = class extends S.TaggedError()("MutationTestError", {
6995
7040
  stage: S.Literal("mutationTest"),
6996
7041
  reason: S.String
6997
- }) {};
7042
+ }) {
7043
+ get message() {
7044
+ return this.reason;
7045
+ }
7046
+ };
6998
7047
  const MutationTestDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js/MutationTestDecision");
6999
7048
  var MutationTestProceed = class extends S.TaggedClass()("MutationTestProceed", {}) {
7000
7049
  [MutationTestDecisionTypeId] = MutationTestDecisionTypeId;
@@ -7028,13 +7077,18 @@ const admitMutationTest = Workflow.make({
7028
7077
  decide: decide$2
7029
7078
  });
7030
7079
  //#endregion
7031
- //#region src/Checker/Checker.resource.ts
7080
+ //#region src/Checker/Checker.blueprint.ts
7081
+ const TypeId = Symbol.for("~systemfsoftware/stryker-js/CheckerBlueprint");
7032
7082
  const checkerProcessCrashes = Metric.counter("stryker.checker.process_crashes", {
7033
7083
  description: "Checker worker processes that never became usable",
7034
7084
  incremental: true
7035
7085
  });
7036
7086
  const TEMP_DIR_PREFIX = "stryker-checker-";
7037
7087
  const nodeArgsOf = (options) => Match.value(options.checkers[0]?.nodeArgs).pipe(Match.when(Match.undefined, () => options.checkerNodeArgs), Match.orElse((args) => args));
7088
+ const serviceOf = (handle) => ({
7089
+ check: (checkerName, mutants) => check(handle, checkerName, mutants),
7090
+ group: (checkerName, mutants) => group(handle, checkerName, mutants)
7091
+ });
7038
7092
  const acquire = (spec) => Effect$1.gen(function* () {
7039
7093
  const client = yield* makeWorkerClient({
7040
7094
  rpcs: Plugin_exports.CheckerRpcs,
@@ -7044,9 +7098,13 @@ const acquire = (spec) => Effect$1.gen(function* () {
7044
7098
  execArgv: [...nodeArgsOf(spec.options)],
7045
7099
  tempDirPrefix: TEMP_DIR_PREFIX
7046
7100
  }).pipe(Effect$1.mapError((error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", (crash) => crash), Match.tag("WorkerBootTimeoutError", () => connectionCrashed(`Checker worker failed to start: its boot window closed before it accepted the RPC connection`)), Match.exhaustive)), Effect$1.tapError(() => Metric.update(checkerProcessCrashes, 1)));
7047
- return makeCheckerHandle(client);
7101
+ return serviceOf(makeCheckerHandle(client));
7048
7102
  });
7049
- const scoped = (spec) => acquire(spec);
7103
+ const Checkers = Blueprint.make()(TypeId).steps({
7104
+ steps: {},
7105
+ targets: { scoped: acquire }
7106
+ });
7107
+ const scoped = (spec) => Checkers.of(spec).scoped;
7050
7108
  //#endregion
7051
7109
  //#region src/incremental-diff.workflow.ts
7052
7110
  const REMEMBERED_STATUS = /* @__PURE__ */ new Set([
@@ -7351,7 +7409,7 @@ const planMutantTests = Workflow.make({
7351
7409
  decide
7352
7410
  });
7353
7411
  //#endregion
7354
- //#region src/run/mutation-test.cell.ts
7412
+ //#region src/run/mutation-test.parts.ts
7355
7413
  const relativeFileNameOf = (fileName, basePath) => RelativeNormalizedFileName.fromAbsolute(fileName, basePath).fileName;
7356
7414
  const readCurrentRelativeFiles = (project, basePath) => Effect$1.gen(function* () {
7357
7415
  const entries = yield* (yield* ProjectFiles).readAllOriginal(MutableHashMap.values(project.files));
@@ -7368,7 +7426,7 @@ const reportingInputOf = (prev, env, results) => ({
7368
7426
  reporterStage: prev.reporterStage,
7369
7427
  formatRegistry: prev.formatRegistry
7370
7428
  });
7371
- const sandboxFilePairsOf = (sandbox, fileNames) => Result.all(fileNames.map((fileName) => Result.map(sandbox.sandboxFileFor(fileName), (sandboxFileName) => [fileName, sandboxFileName])));
7429
+ const sandboxFilePairsOf = (sandbox, fileNames) => Result.all(fileNames.map((fileName) => Result.map(sandboxFileFor(sandbox, fileName), (sandboxFileName) => [fileName, sandboxFileName])));
7372
7430
  const sandboxFilesOf = (sandbox, fileNames) => Effect$1.fromResult(sandboxFilePairsOf(sandbox, fileNames)).pipe(Effect$1.mapError((cause) => StageError.make({
7373
7431
  stage: "mutationTest",
7374
7432
  reason: "Failed to resolve sandbox file",
@@ -7422,8 +7480,9 @@ const toReportedMutant = (mutant) => Object.assign(mutant, {
7422
7480
  static: mutant.static
7423
7481
  });
7424
7482
  const CHECKER_ACQUIRE_RETRIES = 2;
7483
+ const TEST_RUNNER_POOL_IDLE_TIME_TO_LIVE = Duration.minutes(1);
7425
7484
  const isCheckerCrash = (error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", () => true), Match.orElse(() => false));
7426
- const configuredPluginOf = (configured) => Match.value(configured).pipe(Match.when(Options_exports.isCustomTestRunner, (custom) => ConfiguredPluginModulePath.make({ modulePath: custom.plugin })), Match.orElse((name) => ConfiguredPluginName.make({ name })));
7485
+ const configuredPluginOf = (configured) => Match.value(testRunnerConfigOf(configured)).pipe(Match.when(Options_exports.isCustomTestRunner, (custom) => ConfiguredPluginModulePath.make({ modulePath: custom.plugin })), Match.orElse((name) => ConfiguredPluginName.make({ name })));
7427
7486
  const workerSpawnOf = (stage, loaded, kind, configured) => Effect$1.mapError(Effect$1.fromResult(resolveConfiguredPlugin(WorkerSpawnCommand.make({
7428
7487
  sources: loaded.pluginSources,
7429
7488
  kind,
@@ -7617,13 +7676,17 @@ const checkerBreachToStageError = (error) => Match.value(error).pipe(Match.tag("
7617
7676
  cause: breach
7618
7677
  })), Match.exhaustive);
7619
7678
  const invalidateSlot = (pool, slot, error) => Effect$1.flatMap(Pool.invalidate(pool, slot), () => Effect$1.fail(error));
7620
- const checkSlotPlans = (pool, slot, checker, checkerName, currentPlans) => checkGroupedPlans(checker, checkerName, currentPlans).pipe(Effect$1.catchTags({
7621
- OutOfMemoryError: (error) => invalidateSlot(pool, slot, error),
7622
- ChildProcessCrashedError: (error) => invalidateSlot(pool, slot, error),
7623
- CheckerFailed: (error) => error.pipe(checkerBreachToStageError, Effect$1.fail),
7624
- CheckerAnsweredUnrequested: (error) => error.pipe(checkerBreachToStageError, Effect$1.fail),
7625
- CheckerSkippedRequested: (error) => error.pipe(checkerBreachToStageError, Effect$1.fail)
7679
+ const onCheckerSlot = (pool, checkerIndex, run) => Pool.use(pool, (slot) => Option$1.match(Option$1.fromUndefinedOr(slot[checkerIndex]), {
7680
+ onNone: () => Effect$1.die(/* @__PURE__ */ new Error(`checker slot has no entry at index ${checkerIndex}`)),
7681
+ onSome: ({ checker }) => run(checker).pipe(Effect$1.catchTags({
7682
+ OutOfMemoryError: (error) => invalidateSlot(pool, slot, error),
7683
+ ChildProcessCrashedError: (error) => invalidateSlot(pool, slot, error),
7684
+ CheckerFailed: (error) => error.pipe(checkerBreachToStageError, Effect$1.fail),
7685
+ CheckerAnsweredUnrequested: (error) => error.pipe(checkerBreachToStageError, Effect$1.fail),
7686
+ CheckerSkippedRequested: (error) => error.pipe(checkerBreachToStageError, Effect$1.fail)
7687
+ }))
7626
7688
  }));
7689
+ const checkGroupsConcurrently = (pool, checkerIndex, checkerName, currentPlans) => Effect$1.flatMap(onCheckerSlot(pool, checkerIndex, (checker) => groupPlans(checker, checkerName, currentPlans)), (groups) => Effect$1.map(Effect$1.forEach(groups, (group) => onCheckerSlot(pool, checkerIndex, (checker) => checkPlans(checker, checkerName, group)), { concurrency: "unbounded" }), (perGroup) => perGroup.flat()));
7627
7690
  const isFailedCheck = (check) => check[1].status !== "passed";
7628
7691
  const splitCheckedPlans = (checked, reporting) => {
7629
7692
  const failures = checked.filter(isFailedCheck);
@@ -7633,18 +7696,15 @@ const splitCheckedPlans = (checked, reporting) => {
7633
7696
  results
7634
7697
  }));
7635
7698
  };
7636
- const stepOneChecker = (pool, slot, checker, checkerName, currentPlans, reporting) => checkSlotPlans(pool, slot, checker, checkerName, currentPlans).pipe(Effect$1.flatMap((checked) => splitCheckedPlans(checked, reporting)));
7699
+ const stepOneChecker = (pool, checkerIndex, checkerName, currentPlans, reporting) => checkGroupsConcurrently(pool, checkerIndex, checkerName, currentPlans).pipe(Effect$1.flatMap((checked) => splitCheckedPlans(checked, reporting)));
7637
7700
  const emptyRunResults = [];
7638
- const runConfiguredCheckers = (pool, plans, reporting) => Effect$1.scoped(Effect$1.gen(function* () {
7639
- const slot = yield* Pool.get(pool);
7640
- return yield* Effect$1.reduce(slot, () => ({
7641
- passedPlans: plans,
7642
- checkerResults: emptyRunResults
7643
- }), (acc, { checkerName, checker }) => Effect$1.map(stepOneChecker(pool, slot, checker, checkerName, acc.passedPlans, reporting), (split) => ({
7644
- passedPlans: split.passed,
7645
- checkerResults: [...acc.checkerResults, ...split.results]
7646
- })));
7647
- }));
7701
+ const runConfiguredCheckers = (pool, plans, reporting) => Effect$1.flatMap(Pool.use(pool, (slot) => Effect$1.succeed(slot.map(({ checkerName }) => checkerName))), (checkerNames) => Effect$1.reduce(checkerNames, () => ({
7702
+ passedPlans: plans,
7703
+ checkerResults: emptyRunResults
7704
+ }), (acc, checkerName, checkerIndex) => Effect$1.map(stepOneChecker(pool, checkerIndex, checkerName, acc.passedPlans, reporting), (split) => ({
7705
+ passedPlans: split.passed,
7706
+ checkerResults: [...acc.checkerResults, ...split.results]
7707
+ }))));
7648
7708
  const checkPlansWithConfiguredCheckers = (checkerPool, plans, reporting) => Option$1.match(Option$1.fromNullishOr(checkerPool), {
7649
7709
  onNone: () => Effect$1.succeed({
7650
7710
  passedPlans: plans,
@@ -7652,6 +7712,14 @@ const checkPlansWithConfiguredCheckers = (checkerPool, plans, reporting) => Opti
7652
7712
  }),
7653
7713
  onSome: (pool) => runConfiguredCheckers(pool, plans, reporting)
7654
7714
  });
7715
+ const checkerResourcesInOwnScope = (acquire) => Effect$1.gen(function* () {
7716
+ const checkerScope = yield* Scope.make();
7717
+ yield* Effect$1.addFinalizer(() => Scope.close(checkerScope, Exit.void));
7718
+ return {
7719
+ resources: yield* acquire.pipe(Scope.provide(checkerScope)),
7720
+ releaseInBackground: Scope.close(checkerScope, Exit.void).pipe(Effect$1.forkScoped({ uninterruptible: true }))
7721
+ };
7722
+ });
7655
7723
  const isPlannable = (mutant) => Result.isSuccess(S.decodeResult(CheckerMutantFromMutant)(mutant));
7656
7724
  const DROPPED_IDS_IN_WARNING = 5;
7657
7725
  const configuredTestFilesOf = (run) => run.options.testFiles.length === 0 ? [] : run.project.testFiles;
@@ -7676,7 +7744,7 @@ const stopWallClock = (result) => Boolean$1.match(wallClockTimeoutStopsRun(resul
7676
7744
  })),
7677
7745
  onFalse: () => Effect$1.void
7678
7746
  });
7679
- const writeMutationTestNoTests = () => Effect$1.gen(function* () {
7747
+ const writeMutationTestNoTests = Effect$1.gen(function* () {
7680
7748
  const env = yield* RunEnvironment;
7681
7749
  const now = yield* Clock.currentTimeMillis;
7682
7750
  const elapsed = Duration.millis(now - env.runStartedAt);
@@ -7687,7 +7755,7 @@ const writeMutationTestNoTests = () => Effect$1.gen(function* () {
7687
7755
  verdict: null
7688
7756
  };
7689
7757
  });
7690
- const writeMutationTestDryRunOnly = () => Effect$1.gen(function* () {
7758
+ const writeMutationTestDryRunOnly = Effect$1.gen(function* () {
7691
7759
  yield* phaseEntered("mutation-test");
7692
7760
  yield* Effect$1.logInfo("The dry-run has been completed successfully. No mutations have been executed.");
7693
7761
  return {
@@ -7697,14 +7765,15 @@ const writeMutationTestDryRunOnly = () => Effect$1.gen(function* () {
7697
7765
  });
7698
7766
  const writeMutationTestProceed = (raw) => Effect$1.gen(function* () {
7699
7767
  const prev = raw.prev;
7768
+ const testRunnerCapacity = prev.concurrency.testRunners + prev.concurrency.checkers;
7700
7769
  const { dropped, plannable: plannableMutants } = partitionPlannable(prev.mutants);
7701
7770
  yield* reportDroppedMutants(dropped);
7702
7771
  yield* phaseEntered("mutation-test");
7703
7772
  const idGenerator = yield* IdGenerator;
7704
7773
  const env = yield* RunEnvironment;
7705
- const checkerPool = yield* makeCheckerPool(prev, env.basePath);
7774
+ const scopedCheckers = yield* checkerResourcesInOwnScope(makeCheckerPool(prev, env.basePath));
7706
7775
  const testFiles = yield* Effect$1.map(sandboxFilesOf(prev.sandbox, configuredTestFilesOf(prev)), (pairs) => pairs.map(([, sandboxFileName]) => sandboxFileName));
7707
- const testRunnerPool = yield* Pool.make({
7776
+ const testRunnerPool = yield* Pool.makeWithTTL({
7708
7777
  acquire: buildTestRunner({
7709
7778
  options: prev.options,
7710
7779
  fileDescriptions: prev.project.fileDescriptions,
@@ -7722,7 +7791,9 @@ const writeMutationTestProceed = (raw) => Effect$1.gen(function* () {
7722
7791
  idGenerator
7723
7792
  })));
7724
7793
  })),
7725
- size: prev.concurrency.testRunners
7794
+ min: prev.concurrency.testRunners,
7795
+ max: testRunnerCapacity,
7796
+ timeToLive: TEST_RUNNER_POOL_IDLE_TIME_TO_LIVE
7726
7797
  });
7727
7798
  const reporting = yield* MutationReporting;
7728
7799
  const sandboxFileByName = Object.fromEntries(yield* sandboxFilesOf(prev.sandbox, [...MutableHashMap.keys(prev.project.filesToMutate)]));
@@ -7765,7 +7836,8 @@ const writeMutationTestProceed = (raw) => Effect$1.gen(function* () {
7765
7836
  })).pipe(Effect$1.ignoreCause);
7766
7837
  const progressQueue = yield* RunEvents;
7767
7838
  yield* Queue.offer(progressQueue, PlanKnown.make({ total: allPlansForReporter.length + noCoverageResults.length }));
7768
- const { passedPlans, checkerResults } = yield* checkPlansWithConfiguredCheckers(checkerPool, sortedPlans, reporting);
7839
+ const { passedPlans, checkerResults } = yield* checkPlansWithConfiguredCheckers(scopedCheckers.resources, sortedPlans, reporting);
7840
+ const checkerRelease = yield* scopedCheckers.releaseInBackground;
7769
7841
  const testRunnerStream = Stream.fromIterable(passedPlans);
7770
7842
  const plannedTotal = sortedPlans.length + noCoverageResults.length + rememberedResults.length;
7771
7843
  const completedRef = yield* Ref.make(0);
@@ -7866,7 +7938,7 @@ const writeMutationTestProceed = (raw) => Effect$1.gen(function* () {
7866
7938
  yield* offerStreamTested(reported, finished, prepared);
7867
7939
  yield* persist(reported);
7868
7940
  return reported;
7869
- })), { concurrency: Math.max(1, prev.concurrency.testRunners) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
7941
+ })), { concurrency: Math.max(1, testRunnerCapacity) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
7870
7942
  const allResults = [
7871
7943
  ...rememberedResults,
7872
7944
  ...noCoverageResults,
@@ -7874,16 +7946,24 @@ const writeMutationTestProceed = (raw) => Effect$1.gen(function* () {
7874
7946
  ...runResults
7875
7947
  ];
7876
7948
  const outcomeResult = yield* reporting.reportAll(reportingInputOf(prev, env, allResults));
7949
+ yield* Fiber.await(checkerRelease);
7877
7950
  const doneNow = yield* Clock.currentTimeMillis;
7878
7951
  const elapsed = Duration.millis(doneNow - env.runStartedAt);
7879
7952
  yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
7880
7953
  return outcomeResult;
7881
7954
  }).pipe(Effect$1.mapError((cause) => mapMutationTestCause(cause)));
7882
- const writeMutationTestOutcome = (raw, outcome) => withPhaseSpan("mutationTest", {
7955
+ const writeMutationTestOutcome = ({ raw, outcome }) => withPhaseSpan("mutationTest", {
7883
7956
  mutantCount: raw.prev.mutants.length,
7884
7957
  skippedMutantCount: partitionPlannable(raw.prev.mutants).dropped.length,
7885
7958
  testCount: raw.prev.dryRunResult.tests.length
7886
7959
  }, () => outcome);
7960
+ const mapMutationTestCause = (cause) => Match.value({ cause }).pipe(Match.when({ cause: isStageError }, ({ cause }) => cause), Match.orElse(({ cause }) => StageError.make({
7961
+ stage: "mutationTest",
7962
+ reason: "Mutation testing failed",
7963
+ cause
7964
+ })));
7965
+ //#endregion
7966
+ //#region src/run/mutation-test.cell.ts
7887
7967
  const mutationTestCell$1 = Sandwich.named("stryker.mutation_test")((command) => Effect$1.gen(function* () {
7888
7968
  yield* Scope.Scope;
7889
7969
  const prev = command;
@@ -7896,9 +7976,18 @@ const mutationTestCell$1 = Sandwich.named("stryker.mutation_test")((command) =>
7896
7976
  prev
7897
7977
  };
7898
7978
  })).decide(admitMutationTest).write({
7899
- MutationTestDryRunOnly: (_decision, raw) => writeMutationTestOutcome(raw, writeMutationTestDryRunOnly()),
7900
- MutationTestNoTests: (_decision, raw) => writeMutationTestOutcome(raw, writeMutationTestNoTests()),
7901
- MutationTestProceed: (_decision, raw) => writeMutationTestOutcome(raw, writeMutationTestProceed(raw)),
7979
+ MutationTestDryRunOnly: (_decision, raw) => writeMutationTestOutcome({
7980
+ raw,
7981
+ outcome: writeMutationTestDryRunOnly
7982
+ }),
7983
+ MutationTestNoTests: (_decision, raw) => writeMutationTestOutcome({
7984
+ raw,
7985
+ outcome: writeMutationTestNoTests
7986
+ }),
7987
+ MutationTestProceed: (_decision, raw) => writeMutationTestOutcome({
7988
+ raw,
7989
+ outcome: writeMutationTestProceed(raw)
7990
+ }),
7902
7991
  MutationTestError: ({ stage, reason }) => Effect$1.fail(StageError.make({
7903
7992
  stage,
7904
7993
  reason,
@@ -7912,11 +8001,6 @@ const mutationTestCell$1 = Sandwich.named("stryker.mutation_test")((command) =>
7912
8001
  reason: issue
7913
8002
  }))
7914
8003
  });
7915
- const mapMutationTestCause = (cause) => Match.value({ cause }).pipe(Match.when({ cause: isStageError }, ({ cause }) => cause), Match.orElse(({ cause }) => StageError.make({
7916
- stage: "mutationTest",
7917
- reason: "Mutation testing failed",
7918
- cause
7919
- })));
7920
8004
  //#endregion
7921
8005
  //#region src/framework-claimant.service.ts
7922
8006
  const dependencyNamesOf = (dependencies) => Object.keys(dependencies ?? {});
@@ -8342,7 +8426,7 @@ const pluginUrlsFromOptions = (options) => [
8342
8426
  ...options.plugins,
8343
8427
  ...options.appendPlugins,
8344
8428
  ...options.ignorers,
8345
- ...Match.value(options.testRunner).pipe(Match.when(Options_exports.isCustomTestRunner, (runner) => [runner.plugin]), Match.orElse(() => [])),
8429
+ ...Match.value(options.testRunner).pipe(Match.when(Options_exports.isCustomTestRunner, (runner) => [runner.plugin]), Match.when(isVmRunner, () => [vmRunnerPluginUrl()]), Match.orElse(() => [])),
8346
8430
  ...options.checkers.map((checker) => checker.plugin)
8347
8431
  ];
8348
8432
  //#endregion
@@ -8463,24 +8547,48 @@ const planPrepare = Workflow.make({
8463
8547
  decide: (decoded) => Match.value(decoded.mode).pipe(Match.when("human", () => Result.succeed(HumanReporters.make({ reporters: decoded.reporters }))), Match.when("machine", () => Result.succeed(MachineReporters.make({ reporters: decoded.reporters }))), Match.exhaustive)
8464
8548
  });
8465
8549
  //#endregion
8550
+ //#region src/run/prepare.parts.ts
8551
+ const announceSummary = (input) => Match.value(input.env.resolvedMode.mode).pipe(Match.when("human", () => announceHumanSummary({
8552
+ allowConsoleColors: input.env.allowConsoleColors,
8553
+ summary: input.summary
8554
+ })), Match.orElse(() => Effect$1.logInfo(input.summary)));
8555
+ const announceHumanSummary = (input) => Boolean$1.match(input.allowConsoleColors, {
8556
+ onTrue: () => Console.log(`${AnsiCode.fields.green.literal}${input.summary}${AnsiCode.fields.reset.literal}`),
8557
+ onFalse: () => Console.log(input.summary)
8558
+ });
8559
+ const STREAM_REPORTER = "progress-stream";
8560
+ const HUMAN_REPORTER = "clear-text";
8561
+ const STDOUT_REPORTERS = {
8562
+ "clear-text": true,
8563
+ "progress": true
8564
+ };
8565
+ const asHumanReporter = (name) => {
8566
+ if (name === STREAM_REPORTER) return HUMAN_REPORTER;
8567
+ return name;
8568
+ };
8569
+ const selectReporters = dual(2, (configured, mode) => Match.value(mode).pipe(Match.when("human", () => [...new Set(configured.map(asHumanReporter))]), Match.when("machine", () => {
8570
+ const permitted = configured.filter((name) => STDOUT_REPORTERS[name] !== true);
8571
+ return Match.value(permitted.includes(STREAM_REPORTER)).pipe(Match.when(true, () => permitted), Match.when(false, () => [...permitted, STREAM_REPORTER]), Match.exhaustive);
8572
+ }), Match.exhaustive));
8573
+ const failOnEmptyProject = (fileCount) => Match.value(fileCount).pipe(Match.when(0, () => Effect$1.fail(StageError.make({
8574
+ stage: "prepare",
8575
+ reason: "No input files found.",
8576
+ cause: PrepareError.make({
8577
+ stage: "prepare",
8578
+ reason: "No input files found."
8579
+ })
8580
+ }))), Match.orElse(() => Effect$1.void));
8581
+ //#endregion
8466
8582
  //#region src/run/prepare.cell.ts
8467
8583
  const NO_PLUGIN_DESCRIPTORS = [];
8468
8584
  const schemaPropertiesOf = (document) => Option$1.getOrElse(Option$1.fromNullishOr(document.properties), () => ({}));
8469
- const buildMergedSchema = (core, contributions) => Boolean$1.match(contributions.length === 0, {
8470
- onTrue: () => core,
8471
- onFalse: () => ({
8472
- ...core,
8473
- properties: contributions.reduce((merged, contribution) => ({
8474
- ...merged,
8475
- ...schemaPropertiesOf(contribution)
8476
- }), schemaPropertiesOf(core))
8477
- })
8478
- });
8479
- const announceSummary = (env, summary) => Match.value(env.resolvedMode.mode).pipe(Match.when("human", () => announceHumanSummary(env.allowConsoleColors, summary)), Match.orElse(() => Effect$1.logInfo(summary)));
8480
- const announceHumanSummary = (allowConsoleColors, summary) => Boolean$1.match(allowConsoleColors, {
8481
- onTrue: () => Console.log(`${AnsiCode.fields.green.literal}${summary}${AnsiCode.fields.reset.literal}`),
8482
- onFalse: () => Console.log(summary)
8483
- });
8585
+ const buildMergedSchema = (core, contributions) => contributions.reduce((merged, contribution) => ({
8586
+ ...merged,
8587
+ properties: {
8588
+ ...schemaPropertiesOf(merged),
8589
+ ...schemaPropertiesOf(contribution)
8590
+ }
8591
+ }), core);
8484
8592
  const readPrepare = (command) => Effect$1.gen(function* () {
8485
8593
  yield* Scope.Scope;
8486
8594
  const env = yield* RunEnvironment;
@@ -8531,9 +8639,10 @@ const readPrepare = (command) => Effect$1.gen(function* () {
8531
8639
  }])]);
8532
8640
  const frameworkClaimants = yield* installedFrameworkClaimants(env.basePath);
8533
8641
  return {
8642
+ now: yield* Clock.currentTimeMillis,
8534
8643
  mode: env.resolvedMode.mode,
8535
8644
  reporters: [...options.reporters],
8536
- fileCount: MutableHashMap.size(command.project.files),
8645
+ fileCount: pipe(command.project.files, MutableHashMap.size),
8537
8646
  availableReporters: [...HashMap.values(reporterChoicesByName)].map((choice) => choice.name),
8538
8647
  env,
8539
8648
  queue,
@@ -8548,22 +8657,17 @@ const readPrepare = (command) => Effect$1.gen(function* () {
8548
8657
  };
8549
8658
  });
8550
8659
  const writePrepare = (reporters, raw) => withPhaseSpan("prepare", {}, (span) => Effect$1.gen(function* () {
8551
- const mutateCount = MutableHashMap.size(raw.project.filesToMutate);
8552
- yield* announceSummary(raw.env, `Found ${mutateCount} of ${MutableHashMap.size(raw.project.files)} file(s) to be mutated.`);
8660
+ const mutateCount = pipe(raw.project.filesToMutate, MutableHashMap.size);
8661
+ yield* announceSummary({
8662
+ env: raw.env,
8663
+ summary: `Found ${mutateCount} of ${pipe(raw.project.files, MutableHashMap.size)} file(s) to be mutated.`
8664
+ });
8553
8665
  yield* validateReporterNames(raw.options.reporters, [...HashMap.values(raw.reporterChoicesByName)].map((choice) => choice.name)).pipe(Effect$1.mapError((cause) => StageError.make({
8554
8666
  stage: "prepare",
8555
8667
  reason: cause.message,
8556
8668
  cause
8557
8669
  })));
8558
- const failOnEmptyProject = (fileCount) => Match.value(fileCount).pipe(Match.when(0, () => Effect$1.fail(StageError.make({
8559
- stage: "prepare",
8560
- reason: "No input files found.",
8561
- cause: PrepareError.make({
8562
- stage: "prepare",
8563
- reason: "No input files found."
8564
- })
8565
- }))), Match.orElse(() => Effect$1.void));
8566
- yield* failOnEmptyProject(raw.project.files.pipe(MutableHashMap.size));
8670
+ yield* failOnEmptyProject(pipe(raw.project.files, MutableHashMap.size));
8567
8671
  const temporaryDirectoryPath = yield* Effect$1.map(Layer.build(TemporaryDirectory.layer(raw.options)), (temporaryDirectory) => Context.get(temporaryDirectory, TemporaryDirectory).path).pipe(Effect$1.mapError((cause) => StageError.make({
8568
8672
  stage: "prepare",
8569
8673
  reason: "Failed to create temporary directory",
@@ -8572,7 +8676,7 @@ const writePrepare = (reporters, raw) => withPhaseSpan("prepare", {}, (span) =>
8572
8676
  const reporterInputs = yield* reporterInputsOf(reporters, raw.reporterChoicesByName, raw.loaded, raw.env.basePath, raw.options);
8573
8677
  const reporterInit = yield* currentReporterInit(span);
8574
8678
  const reporterStage = yield* attachReporterFactories(reporterInputs, raw.options, reporterInit);
8575
- const now = yield* Clock.currentTimeMillis;
8679
+ const { now } = raw;
8576
8680
  yield* Queue.offer(raw.queue, PhaseEntered.make({
8577
8681
  phase: "prepare",
8578
8682
  elapsedMs: now - raw.env.runStartedAt
@@ -8588,20 +8692,6 @@ const writePrepare = (reporters, raw) => withPhaseSpan("prepare", {}, (span) =>
8588
8692
  frameworkClaimants: raw.frameworkClaimants
8589
8693
  };
8590
8694
  }));
8591
- const STREAM_REPORTER = "progress-stream";
8592
- const HUMAN_REPORTER = "clear-text";
8593
- const STDOUT_REPORTERS = {
8594
- "clear-text": true,
8595
- "progress": true
8596
- };
8597
- const asHumanReporter = (name) => {
8598
- if (name === STREAM_REPORTER) return HUMAN_REPORTER;
8599
- return name;
8600
- };
8601
- const selectReporters = dual(2, (configured, mode) => Match.value(mode).pipe(Match.when("human", () => [...new Set(configured.map(asHumanReporter))]), Match.when("machine", () => {
8602
- const permitted = configured.filter((name) => STDOUT_REPORTERS[name] !== true);
8603
- return Match.value(permitted.includes(STREAM_REPORTER)).pipe(Match.when(true, () => permitted), Match.when(false, () => [...permitted, STREAM_REPORTER]), Match.exhaustive);
8604
- }), Match.exhaustive));
8605
8695
  const prepareCell = Sandwich.named("stryker.prepare")(readPrepare).decide(planPrepare).write({
8606
8696
  HumanReporters: ({ reporters }, raw) => writePrepare(reporters, raw),
8607
8697
  MachineReporters: ({ reporters }, raw) => writePrepare(reporters, raw),
@@ -8636,4 +8726,4 @@ const strykerCell = dual((args) => Predicate$1.isObject(args[0]), (options, targ
8636
8726
  })
8637
8727
  })), Effect$1.scoped));
8638
8728
  //#endregion
8639
- export { ImportedModuleSchema as $, VerdictMutant as A, FileMatcher as B, readConfig as C, RunEvents as D, IdGenerator as E, makeWorkerClient as F, ConfigFileUnsupportedError as G, ConfigFileInvalidError as H, WorkerLauncher as I, ExtendsStepDone as J, ConfigDocumentSchema as K, make$2 as L, MetricsResultFromReport as M, reporterWorkerFactory as N, RunIdentity as O, spawnReporterWorker as P, ExtendsStepStateSchema as Q, StrykerError as R, mergeConfigs as S, RunEnvironment as T, ConfigFileNotFoundError as U, ConfigError as V, ConfigFileUnreadableError as W, ExtendsStepRefused as X, ExtendsStepRead as Y, ExtendsStepResolve as Z, describeErrors as _, RunEventDrainLive as a, survivorsPriorReport as at, initialExtendsStepState as b, isVmRunner as c, OutOfMemoryError as ct, nodePlatformLayer as d, CheckerSkippedRequested as dt, MergeCommand as et, layerWorkerProtocol as f, decideExtendsStep as g, classifyWorkerExit as h, RunEventDrain as i, forkOptionsSchema as it, VerdictThresholds as j, VerdictEnvelope as k, vmTestRunner as l, WorkerBootTimeoutError as lt, ClassifyWorkerExitDecision as m, strykerCell as n, ReadConfigCommand as nt, makeRunEventStream as o, checkGroupedPlans as ot, ClassifyWorkerExitCommand as p, ExtendsStepDocumentSchema as q, buildTestRunner as r, extendsPropertySchema as rt, RunEventWireLine as s, ChildProcessCrashedError as st, mutationTestCell as t, MergeResult as tt, IncrementalReportSchema as u, CheckerAnsweredUnrequested as ut, forkCoreSchema as v, validateOptions as w, loadConfigCell as x, importModule as y, isCommandRunner as z };
8729
+ export { ExtendsStepDone as $, RunEvents as A, make$2 as B, forkCoreSchema as C, validateOptions as D, mergeConfigs as E, MetricsResultFromReport as F, ConfigFileInvalidError as G, isCommandRunner as H, reporterWorkerFactory as I, ConfigFileUnsupportedError as J, ConfigFileNotFoundError as K, spawnReporterWorker as L, VerdictEnvelope as M, VerdictMutant as N, RunEnvironment as O, VerdictThresholds as P, ExtendsStepDocumentSchema as Q, makeWorkerClient as R, describeErrors as S, initialExtendsStepState as T, FileMatcher as U, StrykerError as V, ConfigError as W, defaultOptions as X, createDefaultOptions as Y, ConfigDocumentSchema as Z, ClassifyWorkerExitDecision as _, isVmRunner as a, MergeCommand as at, readConfig as b, vmTestRunnerConfig as c, extendsPropertySchema as ct, makeRunEventStream as d, checkGroupedPlans as dt, ExtendsStepRead as et, RunEventWireLine as f, ChildProcessCrashedError as ft, ClassifyWorkerExitCommand as g, CheckerSkippedRequested as gt, layerWorkerProtocol as h, CheckerAnsweredUnrequested as ht, makeChildProcessTestRunner as i, ImportedModuleSchema as it, RunIdentity as j, IdGenerator as k, RunEventDrain as l, forkOptionsSchema as lt, nodePlatformLayer as m, WorkerBootTimeoutError as mt, strykerCell as n, ExtendsStepResolve as nt, testRunnerConfigOf as o, MergeResult as ot, IncrementalReportSchema as p, OutOfMemoryError as pt, ConfigFileUnreadableError as q, buildTestRunner as r, ExtendsStepStateSchema as rt, vmRunnerPluginUrl as s, ReadConfigCommand as st, mutationTestCell as t, ExtendsStepRefused as tt, RunEventDrainLive as u, survivorsPriorReport as ut, classifyWorkerExit as v, importModule as w, decideExtendsStep as x, loadConfigCell as y, WorkerLauncher as z };