@systemfsoftware/stryker-js 10.0.0 → 10.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,4 +1,4 @@
1
- import { $ as WorkerPluginKind, F as MutationTestingPlanReady, G as TRACEPARENT_HEADER, I as NonNegativeInt, J as TestRunnerFailed, K as TRACESTATE_HEADER, L as PropagatedTrace, M as MutantTested, N as MutationTestReportReady, O as Metrics, P as MutationTestResultSchema$1, Q as WorkerEntryUrl, S as CheckerMutantWire, T as DryRunCompleted, U as ReporterRpcs, V as ReporterFailed, W as StrykerOptionsSchema, X as TraceContextMiddleware, Y as TestRunnerRpcs, Z as TraceContextReference, _ as RunStarted, c as PhaseEntered, dt as errorToString, et as formatTraceparent, f as RunEvent, ft as instrument, it as INSTRUMENTER_CONSTANTS, k as MetricsResultSchema, l as PlanKnown, m as RunMutantTested, n as StageError, nt as parseTraceparent, ot as Mutant, pt as normalizeFileName$4, r as Heartbeat, rt as toMutantRunResult, t as PrepareError, tt as isCustomTestRunner, ut as disableTypeChecks, v as VerdictReached, w as CheckerRpcs } from "./Run.schema-C0Cxsfe9.mjs";
1
+ import { $ as WorkerEntryUrl, F as MutationTestingPlanReady, G as TRACEPARENT_HEADER, I as NonNegativeInt, J as TestRunnerFailed, K as TRACESTATE_HEADER, L as PropagatedTrace, M as MutantTested, N as MutationTestReportReady, O as Metrics, P as MutationTestResultSchema$1, Q as WALL_CLOCK_TIMEOUT_REASON, S as CheckerMutantWire, T as DryRunCompleted, U as ReporterRpcs, V as ReporterFailed, W as StrykerOptionsSchema, X as TraceContextMiddleware, Y as TestRunnerRpcs, Z as TraceContextReference, _ as RunStarted, at as wallClockTimeoutStopsRun, c as PhaseEntered, ct as Mutant, et as WorkerPluginKind, f as RunEvent, ft as disableTypeChecks, ht as normalizeFileName$4, it as toMutantRunResult, k as MetricsResultSchema, l as PlanKnown, m as RunMutantTested, mt as instrument, n as StageError, nt as isCustomTestRunner, ot as INSTRUMENTER_CONSTANTS, pt as errorToString, r as Heartbeat, rt as parseTraceparent, t as PrepareError, tt as formatTraceparent, v as VerdictReached, w as CheckerRpcs } from "./Run.schema-B0ERLxQj.mjs";
2
2
  import { t as mergeConfig } from "./merge-config-BrrNQWje.mjs";
3
3
  import * as Effect$1 from "effect/Effect";
4
4
  import * as FileSystem from "effect/FileSystem";
@@ -16,12 +16,12 @@ import * as Ref from "effect/Ref";
16
16
  import * as Result from "effect/Result";
17
17
  import * as S from "effect/Schema";
18
18
  import * as Stream from "effect/Stream";
19
- import { Cell, Workflow } from "@systemfsoftware/effect-cell-types";
19
+ import { Cell, Sandwich, Workflow } from "@systemfsoftware/effect-cell-types";
20
20
  import * as Predicate from "effect/Predicate";
21
21
  import * as Array$1 from "effect/Array";
22
- import { Schema } from "effect";
22
+ import { Schema, SchemaGetter } from "effect";
23
23
  import * as RpcMiddleware from "effect/unstable/rpc/RpcMiddleware";
24
- import * as SchemaGetter from "effect/SchemaGetter";
24
+ import * as SchemaGetter$1 from "effect/SchemaGetter";
25
25
  import * as Exit from "effect/Exit";
26
26
  import * as Scope from "effect/Scope";
27
27
  import * as Duration from "effect/Duration";
@@ -52,7 +52,7 @@ import * as Equivalence from "effect/Equivalence";
52
52
  import * as Metric from "effect/Metric";
53
53
  import * as Pool from "effect/Pool";
54
54
  import * as Semaphore from "effect/Semaphore";
55
- import * as SchemaTransformation from "effect/SchemaTransformation";
55
+ import * as SchemaTransformation$1 from "effect/SchemaTransformation";
56
56
  import { randomBytes } from "@noble/hashes/utils.js";
57
57
  import * as Console from "effect/Console";
58
58
  import "effect/Sink";
@@ -142,8 +142,8 @@ const frameRunEvent = Workflow.total(FrameRunEventCommand, decideFrame);
142
142
  //#endregion
143
143
  //#region src/run-event-wire.schema.ts
144
144
  const RunEventWireLine = S.String.pipe(S.decodeTo(S.fromJsonString(RunEvent), {
145
- decode: SchemaGetter.transform((line) => line.replace(/\n$/, "")),
146
- encode: SchemaGetter.transform((json) => `${json}\n`)
145
+ decode: SchemaGetter$1.transform((line) => line.replace(/\n$/, "")),
146
+ encode: SchemaGetter$1.transform((json) => `${json}\n`)
147
147
  }));
148
148
  //#endregion
149
149
  //#region src/RunEvents.ts
@@ -345,9 +345,18 @@ const calculateTotalTimeForIds = (testIds, testTimeById) => testIds.reduce((acc,
345
345
  if (t !== void 0) return acc + t;
346
346
  return acc;
347
347
  }, 0);
348
+ const hitLimitForCount = (hitCount) => hitCount * 100;
349
+ const coveredWhenKnown = (covered, coverageKnown) => {
350
+ if (!coverageKnown) return false;
351
+ return covered;
352
+ };
353
+ const isMissingHitCount = (hitCount, covered, coverageKnown) => {
354
+ if (!coveredWhenKnown(covered, coverageKnown)) return false;
355
+ return hitCount === void 0;
356
+ };
348
357
  const getHitLimit = (hitCount) => {
349
358
  if (hitCount === void 0) return;
350
- return hitCount * 100;
359
+ return hitLimitForCount(hitCount);
351
360
  };
352
361
  const getMutantActivation = (testFilter) => {
353
362
  if (testFilter !== void 0) return "runtime";
@@ -407,6 +416,16 @@ const planForStaticallyCovered = (mutant, command, isStatic) => {
407
416
  const useCovered = Match.value(isStatic).pipe(Match.when(false, () => true), Match.orElse(() => ignoreStatic && tests.length > ZERO));
408
417
  return Match.value(useCovered).pipe(Match.when(true, () => toRunPlan(mutant, command, calculateTotalTimeForIds(tests, command.testTimeById), coveredBy, isStatic, coveredBy)), Match.orElse(() => Match.value(ignoreStatic).pipe(Match.when(true, () => toEarlyResultPlan(mutant, isStatic, "Ignored", "Static mutant (and \"ignoreStatic\" was enabled)", coveredBy)), Match.orElse(() => toRunPlan(mutant, command, command.timeSpentAllTests, getTestFilter(command.globalTestFilter), isStatic, coveredBy)))));
409
418
  };
419
+ const hasCoveringTests = (tests) => {
420
+ if (tests === void 0) return false;
421
+ return tests.length > 0;
422
+ };
423
+ const mutantIsCovered = (command, mutantId) => {
424
+ if (hasCoveringTests(command.testsByMutantId[mutantId])) return true;
425
+ return hasStaticCoverageForPlan(command.staticCoverage, mutantId);
426
+ };
427
+ const coverageKnown = (command) => command.staticCoverage !== void 0;
428
+ const boundFor = (command, mutant) => isMissingHitCount(command.hitsByMutantId[mutant.id], mutantIsCovered(command, mutant.id), coverageKnown(command));
410
429
  const decidePlanForMutant = (mutant, command) => {
411
430
  const isStatic = hasStaticCoverageForPlan(command.staticCoverage, mutant.id);
412
431
  return Option.match(Option.fromUndefinedOr(mutant.status), {
@@ -414,6 +433,15 @@ const decidePlanForMutant = (mutant, command) => {
414
433
  onNone: () => Match.value(hasCoverageForPlan(command.staticCoverage)).pipe(Match.when(true, () => planForStaticallyCovered(mutant, command, isStatic)), Match.orElse(() => toRunPlan(mutant, command, command.timeSpentAllTests, getTestFilter(command.globalTestFilter), void 0, void 0)))
415
434
  });
416
435
  };
436
+ const isClosedMutant = (mutant) => mutant.status !== void 0;
437
+ const missingIdOf = (command, mutant) => {
438
+ if (!boundFor(command, mutant)) return [];
439
+ return [mutant.id];
440
+ };
441
+ const missingHitCountIds = (command) => command.mutants.flatMap((mutant) => {
442
+ if (isClosedMutant(mutant)) return [];
443
+ return missingIdOf(command, mutant);
444
+ });
417
445
  const planMutantTests = (command) => {
418
446
  const plans = command.mutants.map((mutant) => decidePlanForMutant(mutant, command));
419
447
  return {
@@ -510,15 +538,28 @@ const materializePlan = (plan, original) => {
510
538
  }
511
539
  };
512
540
  };
541
+ const missingHitFailure = (missing) => {
542
+ if (missing.length === 0) return;
543
+ return Effect$1.fail(StageError.make({
544
+ stage: "mutationTest",
545
+ reason: `covered mutant missing dry-run hit count: ${missing.join(", ")}`
546
+ }));
547
+ };
548
+ const mutantsById = (mutants) => {
549
+ const byId = /* @__PURE__ */ new Map();
550
+ for (const mutant of mutants) byId.set(mutant.id, mutant);
551
+ return byId;
552
+ };
553
+ const materializeKnown = (plan, byId) => {
554
+ const original = byId.get(plan.mutantId);
555
+ if (original === void 0) return Effect$1.die(/* @__PURE__ */ new Error(`planner returned an unknown mutant id: ${plan.mutantId}`));
556
+ return Effect$1.succeed(materializePlan(plan, original));
557
+ };
513
558
  const makeMutantTestPlanner = (command) => {
559
+ const failed = missingHitFailure(missingHitCountIds(command));
560
+ if (failed !== void 0) return failed;
514
561
  const { plans } = planMutantTests(command);
515
- const byId = /* @__PURE__ */ new Map();
516
- for (const mutant of command.mutants) byId.set(mutant.id, mutant);
517
- return Effect$1.forEach(plans, (plan) => {
518
- const original = byId.get(plan.mutantId);
519
- if (original === void 0) return Effect$1.die(/* @__PURE__ */ new Error(`planner returned an unknown mutant id: ${plan.mutantId}`));
520
- return Effect$1.succeed(materializePlan(plan, original));
521
- });
562
+ return Effect$1.forEach(plans, (plan) => materializeKnown(plan, mutantsById(command.mutants)));
522
563
  };
523
564
  const decidePlans = (mutants, testCoverage, options, timeOverheadMS, globalTestFilter, sandboxFileByName) => {
524
565
  const command = coverageToCommand(mutants, testCoverage, options, timeOverheadMS, globalTestFilter, sandboxFileByName);
@@ -915,9 +956,9 @@ Layer.succeed(TraceContextMiddleware, serverMiddleware);
915
956
  let wire;
916
957
  const workerOptionsWire = () => wire ??= S.fromJsonString(S.toCodecJson(StrykerOptionsSchema));
917
958
  const encodeWorkerOptions = (options) => S.encodeEffect(workerOptionsWire())(options).pipe(Effect$1.orDie);
918
- const decodeWorkerOptions = (raw) => S.decodeUnknownEffect(workerOptionsWire())(raw);
959
+ const decodeWorkerOptions = (raw) => S.decodeEffect(workerOptionsWire())(raw);
919
960
  Effect$1.gen(function* () {
920
- const workerDir = yield* Config.string("STRYKER_WORKER_DIR");
961
+ const workerDir = yield* Config.String("STRYKER_WORKER_DIR");
921
962
  const fs = yield* FileSystem.FileSystem;
922
963
  const path = yield* Path.Path;
923
964
  const raw = yield* fs.readFileString(path.join(workerDir, "options.json"));
@@ -936,9 +977,9 @@ const otlpLayer = (serviceName, endpoint) => NodeSdk.layer(() => ({
936
977
  spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter({ url: tracesUrl(endpoint) }))
937
978
  }));
938
979
  Layer.unwrap(Effect$1.gen(function* () {
939
- if (!(yield* Config.boolean("OTEL_ENABLED").pipe(Config.withDefault(false)))) return Layer.empty;
940
- const serviceName = yield* Config.string("OTEL_SERVICE_NAME").pipe(Config.withDefault(WORKER_SERVICE_NAME));
941
- const endpoint = yield* Config.string("OTEL_EXPORTER_OTLP_ENDPOINT").pipe(Config.withDefault(DEFAULT_ENDPOINT));
980
+ if (!(yield* Config.Boolean("OTEL_ENABLED").pipe(Config.withDefault(false)))) return Layer.empty;
981
+ const serviceName = yield* Config.String("OTEL_SERVICE_NAME").pipe(Config.withDefault(WORKER_SERVICE_NAME));
982
+ const endpoint = yield* Config.String("OTEL_EXPORTER_OTLP_ENDPOINT").pipe(Config.withDefault(DEFAULT_ENDPOINT));
942
983
  return otlpLayer(serviceName, endpoint);
943
984
  }));
944
985
  //#endregion
@@ -1202,7 +1243,7 @@ const makeWorkerClient = (params) => Effect$1.gen(function* () {
1202
1243
  });
1203
1244
  //#endregion
1204
1245
  //#region src/platform/node.ts
1205
- const restrictToOwnerOrWarn = (fs, file) => fs.chmod(file, 384).pipe(Effect$1.catchTag("PlatformError", (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))));
1246
+ 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));
1206
1247
  /**
1207
1248
  * The Node worker launcher: spawn a worker child with this runtime's
1208
1249
  * executable, host the RPC server's address as a `net` `path` endpoint (a
@@ -1217,12 +1258,12 @@ const nodeWorkerLauncherLayer = Layer.effect(WorkerLauncher, Effect$1.gen(functi
1217
1258
  return { spawn: (params) => Effect$1.gen(function* () {
1218
1259
  const workerDir = yield* fs.makeTempDirectoryScoped({ prefix: params.tempDirPrefix });
1219
1260
  const workerId = yield* crypto.randomUUIDv4;
1220
- const socketPath = Match.value(process.platform).pipe(Match.when("win32", () => `\\\\.\\pipe\\stryker-worker-${workerId}`), Match.orElse(() => path.join(workerDir, "worker.sock")));
1261
+ const socketPath = Match.value(globalThis.process.platform).pipe(Match.when("win32", () => `\\\\.\\pipe\\stryker-worker-${workerId}`), Match.orElse(() => path.join(workerDir, "worker.sock")));
1221
1262
  const optionsFile = path.join(workerDir, "options.json");
1222
1263
  yield* fs.writeFileString(optionsFile, params.optionsJson);
1223
1264
  yield* restrictToOwnerOrWarn(fs, optionsFile);
1224
1265
  const entrypointPath = yield* path.fromFileUrl(new URL(params.entrypoint));
1225
- const handle = yield* ChildProcess.make(process.execPath, [...params.execArgv, entrypointPath], {
1266
+ const handle = yield* ChildProcess.make(globalThis.process.execPath, [...params.execArgv, entrypointPath], {
1226
1267
  cwd: params.workingDirectory,
1227
1268
  extendEnv: true,
1228
1269
  env: {
@@ -1256,14 +1297,14 @@ const nodePlatformLayer = Layer.mergeAll(nodeWorkerLauncherLayer.pipe(Layer.prov
1256
1297
  * The in-memory runner's platform: the V8 sandbox module and the module
1257
1298
  * builtin that strips TypeScript and resolves a sandbox's `require`.
1258
1299
  *
1259
- * Reached through `process.getBuiltinModule` rather than an `import` because a
1300
+ * Reached through `globalThis.process.getBuiltinModule` rather than an `import` because a
1260
1301
  * Node builtin import is forbidden in product code; the call is wrapped in
1261
1302
  * `Effect.promise` so a platform without `node:vm` fails as a defect — the
1262
1303
  * runner has no fallback to fall back to.
1263
1304
  */
1264
1305
  const nodeVmPlatformLayer = Layer.effect(VmRunner, Effect$1.sync(() => ({
1265
- module: process.getBuiltinModule("node:module"),
1266
- vm: process.getBuiltinModule("node:vm")
1306
+ module: globalThis.process.getBuiltinModule("node:module"),
1307
+ vm: globalThis.process.getBuiltinModule("node:vm")
1267
1308
  })));
1268
1309
  S.TaggedError()("CommandRunnerUnsupportedOption", { option: S.String });
1269
1310
  S.Struct({
@@ -1338,18 +1379,25 @@ const withTimeout = (inner) => ({
1338
1379
  dryRun: (options) => {
1339
1380
  const policy = Effect$1.timeoutOrElse({
1340
1381
  duration: Duration.millis(options.timeout),
1341
- orElse: () => Effect$1.succeed({ status: "timeout" })
1382
+ orElse: () => Effect$1.succeed({
1383
+ status: "timeout",
1384
+ reason: WALL_CLOCK_TIMEOUT_REASON
1385
+ })
1342
1386
  });
1343
1387
  return inner.dryRun(options).pipe(policy);
1344
1388
  },
1345
1389
  mutantRun: (options) => {
1346
1390
  const policy = Effect$1.timeoutOrElse({
1347
1391
  duration: Duration.millis(options.timeout),
1348
- orElse: () => Effect$1.succeed({ status: "timeout" })
1392
+ orElse: () => Effect$1.succeed({
1393
+ status: "timeout",
1394
+ reason: WALL_CLOCK_TIMEOUT_REASON
1395
+ })
1349
1396
  });
1350
1397
  return inner.mutantRun(options).pipe(policy);
1351
1398
  }
1352
1399
  });
1400
+ const invalidatesRunnerPool = (status, reason) => status === "timeout" && reason === "wall-clock-timeout";
1353
1401
  /**
1354
1402
  * Retry a run whose runner crashed, and report the crash as a result once the
1355
1403
  * attempts are spent.
@@ -2002,7 +2050,7 @@ function findConfigFile(configFileName) {
2002
2050
  return Match.value(configFileName).pipe(Match.when(Match.string, (fileName) => configFileFor(fileName)), Match.orElse(() => discoverConfigFile()));
2003
2051
  }
2004
2052
  const resolveChildExtends = (configFile, child, configEnv) => Match.value("extends" in child).pipe(Match.when(true, () => resolveExtends(configFile, child, configEnv)), Match.orElse(() => Effect$1.succeed(child)));
2005
- const isCiEnvironment = Config.string("CI").pipe(Effect$1.map((value) => Match.value(value.trim().toLowerCase()).pipe(Match.when("", () => false), Match.when("0", () => false), Match.when("false", () => false), Match.orElse(() => true))), Effect$1.orElseSucceed(() => false));
2053
+ const isCiEnvironment = Config.String("CI").pipe(Effect$1.map((value) => Match.value(value.trim().toLowerCase()).pipe(Match.when("", () => false), Match.when("0", () => false), Match.when("false", () => false), Match.orElse(() => true))), Effect$1.orElseSucceed(() => false));
2006
2054
  function loadOptionsFromConfigFile(cliOptions, configEnv) {
2007
2055
  return findConfigFile(cliOptions["configFile"]).pipe(Effect$1.flatMap((configFile) => Match.value(configFile).pipe(Match.when(void 0, () => Effect$1.succeed({})), Match.orElse((found) => readConfigModule(found, configEnv).pipe(Effect$1.flatMap((child) => resolveChildExtends(found, child, configEnv)))))));
2008
2056
  }
@@ -2014,7 +2062,7 @@ function readConfig(cliOptions, invocation) {
2014
2062
  isDryRun: cliOptions["dryRunOnly"] === true,
2015
2063
  isCi: yield* isCiEnvironment
2016
2064
  };
2017
- const cliRecord = yield* S.decodeUnknownEffect(ConfigDocumentSchema)(cliOptions).pipe(Effect$1.orDie);
2065
+ const cliRecord = yield* S.decodeEffect(ConfigDocumentSchema)(cliOptions).pipe(Effect$1.orDie);
2018
2066
  const fileRecord = yield* loadOptionsFromConfigFile(cliRecord, configEnv);
2019
2067
  const fileOptions = yield* Result.match(S.decodeUnknownResult(ConfigDocumentSchema)(fileRecord), {
2020
2068
  onFailure: (cause) => Effect$1.fail(ConfigFileInvalidError.make({
@@ -2046,9 +2094,13 @@ const PluginDescriptorSchema = Schema.Union([Schema.Struct({
2046
2094
  const PluginModuleSchema = Schema.Struct({ strykerPlugins: Schema.Array(PluginDescriptorSchema) });
2047
2095
  const isShouldIgnore = (value) => typeof value === "function";
2048
2096
  const ignoreNothing = () => void 0;
2097
+ const shouldIgnoreArbitrary = Schema.link()(Schema.Null, {
2098
+ decode: SchemaGetter.transform(() => ignoreNothing),
2099
+ encode: SchemaGetter.transform(() => null)
2100
+ });
2049
2101
  const IgnorerEntrySchema = Schema.Struct({
2050
2102
  name: Schema.String,
2051
- shouldIgnore: Schema.declare(isShouldIgnore, { toArbitrary: () => (fc) => fc.constant(ignoreNothing) })
2103
+ shouldIgnore: Schema.declare(isShouldIgnore, { toCodecArbitrary: () => shouldIgnoreArbitrary })
2052
2104
  });
2053
2105
  const IgnorerModuleSchema = Schema.Struct({ strykerIgnorers: Schema.Array(IgnorerEntrySchema) });
2054
2106
  const SchemaValidationContributionSchema = Schema.Struct({ strykerValidationSchema: Schema.Record(Schema.String, Schema.Unknown) });
@@ -2436,8 +2488,8 @@ const initFromPhaseSpan = (span) => {
2436
2488
  };
2437
2489
  };
2438
2490
  const environmentTraceInit = () => Effect$1.gen(function* () {
2439
- const traceparent = yield* Config.string("TRACEPARENT").pipe(Effect$1.option);
2440
- const tracestate = yield* Config.string("TRACESTATE").pipe(Effect$1.option);
2491
+ const traceparent = yield* Config.String("TRACEPARENT").pipe(Effect$1.option);
2492
+ const tracestate = yield* Config.String("TRACESTATE").pipe(Effect$1.option);
2441
2493
  return {
2442
2494
  ...traceparentInit(Option.getOrUndefined(traceparent)),
2443
2495
  ...tracestateInit(Option.getOrUndefined(tracestate))
@@ -2532,104 +2584,98 @@ const completeDryRunPassed = (raw) => Effect$1.gen(function* () {
2532
2584
  timeOverhead: Duration.millis(overheadMillis)
2533
2585
  };
2534
2586
  });
2535
- const dryRunCell = Cell.layer({
2536
- read: (command) => Effect$1.gen(function* () {
2537
- yield* Scope.Scope;
2538
- const idGenerator = yield* IdGenerator;
2539
- const { files, testFiles } = buildDryRunFiles(command);
2540
- const dryRunTimeout = command.options.dryRunTimeoutMinutes * 60 * 1e3;
2541
- yield* Effect$1.logInfo("Starting dry run");
2542
- const { rawResult, capabilities, gross } = yield* Effect$1.scoped(Effect$1.gen(function* () {
2543
- const childRunnerEffect = Effect$1.suspend(() => {
2544
- const runnerConfigured = command.options.testRunner;
2545
- const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
2546
- return resolveConfiguredWorkerSpawn({
2547
- loaded: command.loadedPlugins,
2548
- kind: "TestRunner",
2549
- configured: runnerConfigured
2550
- }).pipe(Effect$1.mapError(missingWorkerEntry("dryRun", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
2551
- options: command.options,
2552
- fileDescriptions: command.project.fileDescriptions,
2553
- sandboxWorkingDirectory: command.sandbox.workingDirectory,
2554
- workerEntrypoint: spawn.entrypoint,
2555
- idGenerator
2556
- })));
2557
- });
2558
- const runner = yield* buildTestRunner({
2587
+ const dryRunCell = Sandwich.read((command) => Effect$1.gen(function* () {
2588
+ yield* Scope.Scope;
2589
+ const idGenerator = yield* IdGenerator;
2590
+ const { files, testFiles } = buildDryRunFiles(command);
2591
+ const dryRunTimeout = command.options.dryRunTimeoutMinutes * 60 * 1e3;
2592
+ yield* Effect$1.logInfo("Starting dry run");
2593
+ const { rawResult, capabilities, gross } = yield* Effect$1.scoped(Effect$1.gen(function* () {
2594
+ const childRunnerEffect = Effect$1.suspend(() => {
2595
+ const runnerConfigured = command.options.testRunner;
2596
+ const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
2597
+ return resolveConfiguredWorkerSpawn({
2598
+ loaded: command.loadedPlugins,
2599
+ kind: "TestRunner",
2600
+ configured: runnerConfigured
2601
+ }).pipe(Effect$1.mapError(missingWorkerEntry("dryRun", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
2559
2602
  options: command.options,
2560
2603
  fileDescriptions: command.project.fileDescriptions,
2561
2604
  sandboxWorkingDirectory: command.sandbox.workingDirectory,
2562
- idGenerator,
2563
- retire: Effect$1.void,
2564
- testFiles: testFiles ?? []
2565
- }, childRunnerEffect);
2566
- const extra = Option.match(Option.fromUndefinedOr(testFiles), {
2567
- onNone: () => ({}),
2568
- onSome: (files) => ({ testFiles: files })
2569
- });
2570
- const timed = yield* Effect$1.timed(runner.dryRun({
2571
- timeout: dryRunTimeout,
2572
- coverageAnalysis: command.options.coverageAnalysis,
2573
- disableBail: command.options.disableBail,
2574
- files,
2575
- ...extra
2576
- }).pipe(Effect$1.mapError((cause) => StageError.make({
2577
- stage: "dryRun",
2578
- reason: "Dry run failed",
2579
- cause
2580
- }))));
2581
- const gross = timed[0];
2582
- return {
2583
- rawResult: timed[1],
2584
- capabilities: yield* runner.capabilities.pipe(Effect$1.mapError((cause) => StageError.make({
2585
- stage: "dryRun",
2586
- reason: "Failed to get test runner capabilities",
2587
- cause
2588
- }))),
2589
- gross
2590
- };
2591
- })).pipe(Effect$1.mapError((cause) => {
2592
- if (S.is(StageError)(cause)) return cause;
2593
- return StageError.make({
2605
+ workerEntrypoint: spawn.entrypoint,
2606
+ idGenerator
2607
+ })));
2608
+ });
2609
+ const runner = yield* buildTestRunner({
2610
+ options: command.options,
2611
+ fileDescriptions: command.project.fileDescriptions,
2612
+ sandboxWorkingDirectory: command.sandbox.workingDirectory,
2613
+ idGenerator,
2614
+ retire: Effect$1.void,
2615
+ testFiles: testFiles ?? []
2616
+ }, childRunnerEffect);
2617
+ const extra = Option.match(Option.fromUndefinedOr(testFiles), {
2618
+ onNone: () => ({}),
2619
+ onSome: (files) => ({ testFiles: files })
2620
+ });
2621
+ const timed = yield* Effect$1.timed(runner.dryRun({
2622
+ timeout: dryRunTimeout,
2623
+ coverageAnalysis: command.options.coverageAnalysis,
2624
+ disableBail: command.options.disableBail,
2625
+ files,
2626
+ ...extra
2627
+ }).pipe(Effect$1.mapError((cause) => StageError.make({
2628
+ stage: "dryRun",
2629
+ reason: "Dry run failed",
2630
+ cause
2631
+ }))));
2632
+ const gross = timed[0];
2633
+ return {
2634
+ rawResult: timed[1],
2635
+ capabilities: yield* runner.capabilities.pipe(Effect$1.mapError((cause) => StageError.make({
2594
2636
  stage: "dryRun",
2595
- reason: "Dry run failed to start test runner",
2637
+ reason: "Failed to get test runner capabilities",
2596
2638
  cause
2597
- });
2598
- }));
2599
- return {
2600
- prev: command,
2601
- rawResult,
2602
- capabilities,
2639
+ }))),
2603
2640
  gross
2604
2641
  };
2605
- }),
2606
- decode: (raw) => Match.value(raw.rawResult).pipe(Match.when(isCompleteDryRun, (complete) => decodeCompleteDryRun(complete, raw.prev.options.allowEmpty)), Match.when(isFailedDryRun, (failed) => decodeFailedDryRun(failed, raw.prev.options.allowEmpty)), Match.orElse((timedOut) => decodeTimedOutDryRun(timedOut, raw.prev.options.allowEmpty))),
2607
- decide: dryRun,
2608
- encode: (outcome) => outcome,
2609
- write: (outcome, raw) => withPhaseSpan("dryRun", {}, () => Effect$1.gen(function* () {
2610
- const env = yield* RunEnvironment;
2611
- const now = yield* Clock.currentTimeMillis;
2612
- const queue = yield* RunEvents;
2613
- yield* Queue.offer(queue, PhaseEntered.make({
2614
- phase: "dry-run",
2615
- elapsedMs: now - env.runStartedAt
2616
- }));
2617
- const out = outcome;
2618
- if (Result.isFailure(out)) {
2619
- const err = out.failure;
2620
- return yield* StageError.make({
2621
- stage: err.stage,
2622
- reason: err.reason,
2623
- cause: err
2624
- });
2625
- }
2626
- return yield* Match.value(out.success).pipe(Match.tag("DryRunFailed", (decision) => Effect$1.fail(StageError.make({
2642
+ })).pipe(Effect$1.mapError((cause) => {
2643
+ if (S.is(StageError)(cause)) return cause;
2644
+ return StageError.make({
2627
2645
  stage: "dryRun",
2628
- reason: "There were failed tests in the initial test run.",
2629
- cause: decision
2630
- }))), Match.tag("DryRunPassed", () => completeDryRunPassed(raw)), Match.exhaustive);
2631
- }))
2632
- });
2646
+ reason: "Dry run failed to start test runner",
2647
+ cause
2648
+ });
2649
+ }));
2650
+ return {
2651
+ prev: command,
2652
+ rawResult,
2653
+ capabilities,
2654
+ gross
2655
+ };
2656
+ })).decode(Sandwich.pure((raw) => Match.value(raw.rawResult).pipe(Match.when(isCompleteDryRun, (complete) => decodeCompleteDryRun(complete, raw.prev.options.allowEmpty)), Match.when(isFailedDryRun, (failed) => decodeFailedDryRun(failed, raw.prev.options.allowEmpty)), Match.orElse((timedOut) => decodeTimedOutDryRun(timedOut, raw.prev.options.allowEmpty))))).decide(dryRun).encode(Sandwich.pure((outcome) => Result.succeed(outcome))).write((outcome, raw) => withPhaseSpan("dryRun", {}, () => Effect$1.gen(function* () {
2657
+ const env = yield* RunEnvironment;
2658
+ const now = yield* Clock.currentTimeMillis;
2659
+ const queue = yield* RunEvents;
2660
+ yield* Queue.offer(queue, PhaseEntered.make({
2661
+ phase: "dry-run",
2662
+ elapsedMs: now - env.runStartedAt
2663
+ }));
2664
+ const out = outcome;
2665
+ if (Result.isFailure(out)) {
2666
+ const err = out.failure;
2667
+ return yield* StageError.make({
2668
+ stage: err.stage,
2669
+ reason: err.reason,
2670
+ cause: err
2671
+ });
2672
+ }
2673
+ return yield* Match.value(out.success).pipe(Match.tag("DryRunFailed", (decision) => Effect$1.fail(StageError.make({
2674
+ stage: "dryRun",
2675
+ reason: "There were failed tests in the initial test run.",
2676
+ cause: decision
2677
+ }))), Match.tag("DryRunPassed", () => completeDryRunPassed(raw)), Match.exhaustive);
2678
+ })));
2633
2679
  //#endregion
2634
2680
  //#region src/plan-instrumentation.workflow.ts
2635
2681
  var InstrumentError = class extends S.TaggedError()("InstrumentError", {
@@ -2846,118 +2892,8 @@ const decide = (command) => Option.match(Option.fromUndefinedOr(command.report),
2846
2892
  });
2847
2893
  const admitIncrementalReport = Workflow.total(AdmitIncrementalReportCommand, (command) => Result.succeed(decide(command)));
2848
2894
  //#endregion
2849
- //#region package.json
2850
- var package_default = {
2851
- name: "@systemfsoftware/stryker-js",
2852
- version: "10.0.0",
2853
- repository: {
2854
- "type": "git",
2855
- "url": "git+https://github.com/systemfsoftware/stryker-js-effect.git",
2856
- "directory": "packages/stryker-js"
2857
- },
2858
- homepage: "https://github.com/systemfsoftware/stryker-js-effect",
2859
- bugs: { "url": "https://github.com/systemfsoftware/stryker-js-effect/issues" },
2860
- description: "The Stryker mutation testing tool — the stryker executable, the run engine, and the configuration surface.",
2861
- type: "module",
2862
- files: ["dist"],
2863
- bin: { "stryker": "./dist/main.mjs" },
2864
- scripts: {
2865
- "clean": "rimraf dist",
2866
- "build": "pnpm run typecheck && rimraf dist && tsdown --config tsdown.bin.config.ts && tsdown --config tsdown.config.ts",
2867
- "typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
2868
- "test": "vitest run",
2869
- "mutation": "stryker run",
2870
- "attw": "attw --pack .",
2871
- "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
2872
- "api:check": "api-extractor run",
2873
- "api:update": "api-extractor run --local",
2874
- "lint:tsgo": "effect-tsgo diagnostics --project tsconfig.json --format ${TSGO_FORMAT:-text}"
2875
- },
2876
- license: "Apache-2.0",
2877
- engines: { "node": ">=22.18.0" },
2878
- dependencies: {
2879
- "@effect/opentelemetry": "4.0.0-rc.112",
2880
- "@effect/platform-node": "catalog:",
2881
- "@effect/platform-node-shared": "catalog:",
2882
- "@noble/hashes": "catalog:",
2883
- "@opentelemetry/api": "^1.9.0",
2884
- "@opentelemetry/exporter-metrics-otlp-http": "^0.208.0",
2885
- "@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
2886
- "@opentelemetry/sdk-metrics": "^2.2.0",
2887
- "@opentelemetry/sdk-trace-base": "^2.2.0",
2888
- "@systemfsoftware/effect-cell-types": "catalog:"
2889
- },
2890
- peerDependencies: { "effect": "catalog:" },
2891
- devDependencies: {
2892
- "@effect/tsgo": "catalog:",
2893
- "@effect/vitest": "catalog:",
2894
- "@microsoft/api-extractor": "catalog:",
2895
- "@oxc-parser/binding-wasm32-wasi": "catalog:",
2896
- "@std/jsonc": "catalog:",
2897
- "@systemfsoftware/all": "catalog:",
2898
- "@systemfsoftware/arethetypeswrong-cli": "catalog:",
2899
- "@systemfsoftware/effect-gherkin-spec": "catalog:",
2900
- "@systemfsoftware/effect-schema-law": "catalog:",
2901
- "@systemfsoftware/effect-schema-vite": "catalog:",
2902
- "@systemfsoftware/stryker-config": "workspace:^",
2903
- "@systemfsoftware/stryker-ignorer-effect-schema-declarations": "catalog:stryker",
2904
- "@systemfsoftware/stryker-ignorer-in-source-vitest-block": "catalog:stryker",
2905
- "@systemfsoftware/stryker-ignorer-interface": "workspace:^",
2906
- "@systemfsoftware/stryker-js": "catalog:stryker",
2907
- "@systemfsoftware/stryker-js-html-reporter": "workspace:^",
2908
- "@systemfsoftware/stryker-js-instrumenter": "workspace:^",
2909
- "@systemfsoftware/stryker-js-plugin-interface": "workspace:^",
2910
- "@systemfsoftware/stryker-js-plugin-runtime": "workspace:^",
2911
- "@systemfsoftware/stryker-js-typescript-checker": "catalog:stryker",
2912
- "@systemfsoftware/stryker-js-vitest-runner": "catalog:stryker",
2913
- "@systemfsoftware/stryker-test-contribution": "catalog:stryker",
2914
- "@systemfsoftware/tsconfig": "catalog:",
2915
- "@systemfsoftware/tsdown-config": "workspace:^",
2916
- "@systemfsoftware/vitest-config": "workspace:^",
2917
- "@types/node": "catalog:",
2918
- "@vitest/coverage-v8": "catalog:",
2919
- "concurrently": "catalog:",
2920
- "mutation-testing-elements": "catalog:stryker",
2921
- "oxc-parser": "catalog:",
2922
- "oxlint": "catalog:",
2923
- "oxlint-tsgolint": "catalog:",
2924
- "rimraf": "catalog:",
2925
- "tsdown": "catalog:",
2926
- "typescript": "catalog:",
2927
- "vitest": "catalog:"
2928
- },
2929
- publishConfig: {
2930
- "access": "public",
2931
- "provenance": true,
2932
- "exports": {
2933
- ".": {
2934
- "types": "./dist/index.d.mts",
2935
- "default": "./dist/index.mjs"
2936
- },
2937
- "./config": {
2938
- "types": "./dist/config.d.mts",
2939
- "default": "./dist/config.mjs"
2940
- },
2941
- "./events": {
2942
- "types": "./dist/events.d.mts",
2943
- "default": "./dist/events.mjs"
2944
- },
2945
- "./promises": {
2946
- "types": "./dist/promises.d.mts",
2947
- "default": "./dist/promises.mjs"
2948
- },
2949
- "./package.json": "./package.json"
2950
- }
2951
- },
2952
- exports: { "./package.json": "./package.json" }
2953
- };
2954
- //#endregion
2955
- //#region src/stryker-package.schema.ts
2956
- const PackageJsonSchema = Schema.Struct({ version: Schema.String });
2957
- //#endregion
2958
2895
  //#region src/stryker-package.ts
2959
- const rawPackageJson = package_default;
2960
- const strykerVersion = Schema.decodeUnknownSync(PackageJsonSchema)(rawPackageJson).version;
2896
+ const strykerVersion = "10.1.0";
2961
2897
  //#endregion
2962
2898
  //#region src/incremental-report.cell.ts
2963
2899
  const parseAndDecodeIncrementalReport = S.decodeUnknownResult(S.fromJsonString(IncrementalReportSchema));
@@ -2968,7 +2904,7 @@ const readIncrementalReportRaw = (input) => {
2968
2904
  contents: void 0
2969
2905
  });
2970
2906
  return Effect$1.gen(function* () {
2971
- const contents = yield* (yield* FileSystem.FileSystem).readFileString(input.incrementalFile).pipe(Effect$1.catchTag("PlatformError", (error) => Match.value(error.reason).pipe(Match.tag("NotFound", () => Effect$1.logInfo(`No incremental result file found at ${input.incrementalFile}, a full mutation testing run will be performed.`).pipe(Effect$1.as(void 0))), Match.orElse(() => Effect$1.fail(error)))));
2907
+ const contents = yield* (yield* FileSystem.FileSystem).readFileString(input.incrementalFile).pipe(Effect$1.tapError((error) => Match.value(error.reason).pipe(Match.tag("NotFound", () => Effect$1.logInfo(`No incremental result file found at ${input.incrementalFile}, a full mutation testing run will be performed.`)), Match.orElse(() => Effect$1.void))), Effect$1.catchTag("PlatformError", (error) => Match.value(error.reason).pipe(Match.tag("NotFound", () => Effect$1.undefined), Match.orElse(() => Effect$1.fail(error)))));
2972
2908
  return {
2973
2909
  incremental: true,
2974
2910
  incrementalFile: input.incrementalFile,
@@ -2996,13 +2932,7 @@ const writeIncrementalReportOutcome = (outcome, raw) => Result.match(outcome, {
2996
2932
  });
2997
2933
  }), Match.exhaustive)
2998
2934
  });
2999
- const incrementalReportCell = Cell.layer({
3000
- read: readIncrementalReportRaw,
3001
- decode: decodeIncrementalReportRaw,
3002
- decide: admitIncrementalReport,
3003
- encode: (outcome) => outcome,
3004
- write: writeIncrementalReportOutcome
3005
- });
2935
+ const incrementalReportCell = Sandwich.read(readIncrementalReportRaw).decode(Sandwich.pure(decodeIncrementalReportRaw)).decide(admitIncrementalReport).encode(Sandwich.pure((outcome) => Result.succeed(outcome))).write(writeIncrementalReportOutcome);
3006
2936
  //#endregion
3007
2937
  //#region src/Project.ts
3008
2938
  const DEFAULT_GLOB$1 = "**/*.{js,ts,jsx,tsx,html,vue,mjs,mts,cts,cjs}";
@@ -3736,9 +3666,9 @@ const makeDisableTypeChecksPreprocessor = (options, impl) => (project) => Effect
3736
3666
  const content = yield* Effect$1.map(impl(instrumenterFile), (instrumented) => instrumented.content).pipe(Effect$1.mapError((cause) => StrykerError.make({
3737
3667
  message: "disableTypeChecks failed",
3738
3668
  cause
3739
- })), Effect$1.catch((_error) => Effect$1.gen(function* () {
3669
+ })), Effect$1.tapError(() => Effect$1.gen(function* () {
3740
3670
  if (isWarningEnabled("preprocessorErrors", options.warnings)) yield* Effect$1.logWarning(`Unable to disable type checking for file "${name}". Shouldn't type checking be disabled for this file? Consider configuring a more restrictive "${optionsPath("disableTypeChecks")}" settings (or turn it completely off with \`false\`)`);
3741
- })));
3671
+ })), Effect$1.orElseSucceed(() => void 0));
3742
3672
  if (content !== void 0) return withContent(file, content);
3743
3673
  })), Match.orElse(() => Effect$1.succeedNone)), { concurrency: 24 })).forEach(mergeUpdatedInto(project));
3744
3674
  });
@@ -3885,7 +3815,7 @@ const directoryChain = (from, pathService) => {
3885
3815
  return [from, ...directoryChain(parent, pathService)];
3886
3816
  };
3887
3817
  const binDirectoriesFrom = (from, pathService) => directoryChain(pathService.resolve(from), pathService).map((directory) => pathService.join(directory, "node_modules", ".bin"));
3888
- const inheritedPath = () => Config.string("PATH").pipe(Effect$1.option, Effect$1.map((value) => Option.getOrUndefined(value) ?? ""));
3818
+ const inheritedPath = () => Config.String("PATH").pipe(Effect$1.option, Effect$1.map((value) => Option.getOrUndefined(value) ?? ""));
3889
3819
  const failOnBuildFailure = (command, result) => Match.value(result.exitCode).pipe(Match.when((exitCode) => exitCode !== 0, (exitCode) => Effect$1.fail(StrykerError.make({ message: `Build command "${command}" failed with exit code ${String(exitCode)}.\n${result.stderr}` }))), Match.orElse(() => Effect$1.void));
3890
3820
  const runBuildCommandIn = (command, workingDirectory) => Effect$1.gen(function* () {
3891
3821
  const pathService = yield* Path.Path;
@@ -4012,7 +3942,7 @@ const linkNodeModules = (nodeModules, workingDirectory, basePath, pathService) =
4012
3942
  const resolvedTo = pathService.resolve(pathService.join(basePath, nodeModules));
4013
3943
  const resolvedFrom = pathService.join(workingDirectory, nodeModules);
4014
3944
  yield* Effect$1.logDebug(`Create symlink from ${resolvedTo} to ${resolvedFrom}`);
4015
- yield* symlinkJunction(resolvedTo, resolvedFrom).pipe(Effect$1.catch((_error) => Effect$1.logWarning(`Unexpected error while trying to symlink "${nodeModules}" in sandbox directory.`)));
3945
+ yield* symlinkJunction(resolvedTo, resolvedFrom).pipe(Effect$1.tapError(() => Effect$1.logWarning(`Unexpected error while trying to symlink "${nodeModules}" in sandbox directory.`)), Effect$1.catchTag("PlatformError", () => Effect$1.void));
4016
3946
  });
4017
3947
  const linkFoundNodeModules = (options, workingDirectory, basePath, pathService) => Effect$1.gen(function* () {
4018
3948
  const nodeModulesList = yield* findNodeModulesList(basePath, options.tempDirName);
@@ -4050,90 +3980,84 @@ const makeSandbox = (input) => Effect$1.gen(function* () {
4050
3980
  });
4051
3981
  //#endregion
4052
3982
  //#region src/run/instrument.cell.ts
4053
- const instrumentCell = Cell.layer({
4054
- read: (command) => Effect$1.gen(function* () {
4055
- yield* Scope.Scope;
4056
- const env = yield* RunEnvironment;
4057
- const filesToMutate = yield* Effect$1.forEach([...MutableHashMap.values(command.project.filesToMutate)], (file) => toInstrumenterFile(file), { concurrency: 24 }).pipe(Effect$1.mapError((cause) => StageError.make({
3983
+ const instrumentCell = Sandwich.read((command) => Effect$1.gen(function* () {
3984
+ yield* Scope.Scope;
3985
+ const env = yield* RunEnvironment;
3986
+ const filesToMutate = yield* Effect$1.forEach([...MutableHashMap.values(command.project.filesToMutate)], (file) => toInstrumenterFile(file), { concurrency: 24 }).pipe(Effect$1.mapError((cause) => StageError.make({
3987
+ stage: "instrument",
3988
+ reason: "Failed to read files to mutate",
3989
+ cause
3990
+ })));
3991
+ const instrumentResult = yield* instrument(filesToMutate, {
3992
+ ignorers: [...command.ignorers],
3993
+ excludedMutations: [...command.options.mutator.excludedMutations]
3994
+ }, env.basePath).pipe(Effect$1.mapError((cause) => StageError.make({
3995
+ stage: "instrument",
3996
+ reason: "Instrumenter failed",
3997
+ cause
3998
+ })));
3999
+ const instrumentedProject = withInstrumentedFiles(command.project, instrumentResult.files);
4000
+ const basePath = env.basePath;
4001
+ let workingDirectory = command.temporaryDirectoryPath;
4002
+ let backupDirectory = "";
4003
+ if (command.options.inPlace) {
4004
+ workingDirectory = basePath;
4005
+ backupDirectory = command.temporaryDirectoryPath;
4006
+ }
4007
+ return {
4008
+ prev: command,
4009
+ filesToMutate,
4010
+ instrumentResult,
4011
+ instrumentedProject,
4012
+ sandbox: yield* makeSandbox({
4013
+ options: command.options,
4014
+ project: instrumentedProject,
4015
+ workingDirectory,
4016
+ backupDirectory,
4017
+ basePath
4018
+ }).pipe(Effect$1.mapError((cause) => StageError.make({
4058
4019
  stage: "instrument",
4059
- reason: "Failed to read files to mutate",
4020
+ reason: "Sandbox initialization failed",
4060
4021
  cause
4061
- })));
4062
- const instrumentResult = yield* instrument(filesToMutate, {
4063
- ignorers: [...command.ignorers],
4064
- excludedMutations: [...command.options.mutator.excludedMutations]
4065
- }, env.basePath).pipe(Effect$1.mapError((cause) => StageError.make({
4022
+ }))),
4023
+ concurrency: yield* makeConcurrency(command.options).pipe(Effect$1.mapError((cause) => StageError.make({
4066
4024
  stage: "instrument",
4067
- reason: "Instrumenter failed",
4025
+ reason: "Failed to compute concurrency",
4068
4026
  cause
4069
- })));
4070
- const instrumentedProject = withInstrumentedFiles(command.project, instrumentResult.files);
4071
- const basePath = env.basePath;
4072
- let workingDirectory = command.temporaryDirectoryPath;
4073
- let backupDirectory = "";
4074
- if (command.options.inPlace) {
4075
- workingDirectory = basePath;
4076
- backupDirectory = command.temporaryDirectoryPath;
4077
- }
4078
- return {
4079
- prev: command,
4080
- filesToMutate,
4081
- instrumentResult,
4082
- instrumentedProject,
4083
- sandbox: yield* makeSandbox({
4084
- options: command.options,
4085
- project: instrumentedProject,
4086
- workingDirectory,
4087
- backupDirectory,
4088
- basePath
4089
- }).pipe(Effect$1.mapError((cause) => StageError.make({
4090
- stage: "instrument",
4091
- reason: "Sandbox initialization failed",
4092
- cause
4093
- }))),
4094
- concurrency: yield* makeConcurrency(command.options).pipe(Effect$1.mapError((cause) => StageError.make({
4095
- stage: "instrument",
4096
- reason: "Failed to compute concurrency",
4097
- cause
4098
- })))
4099
- };
4100
- }),
4101
- decode: (raw) => Result.succeed(InstrumentCommand.make({
4102
- fileCount: raw.filesToMutate.length,
4103
- inPlace: raw.prev.options.inPlace,
4104
- pluginCount: raw.prev.loadedPlugins.pluginModulePaths.length
4105
- })),
4106
- decide: planInstrumentation,
4107
- encode: (outcome) => outcome,
4108
- write: (output, raw) => withPhaseSpan("instrument", { fileCount: raw.filesToMutate.length }, () => Effect$1.gen(function* () {
4109
- const env = yield* RunEnvironment;
4110
- const now = yield* Clock.currentTimeMillis;
4111
- const queue = yield* RunEvents;
4112
- yield* Queue.offer(queue, PhaseEntered.make({
4113
- phase: "instrument",
4114
- elapsedMs: now - env.runStartedAt
4115
- }));
4116
- const out = output;
4117
- if (Result.isFailure(out)) {
4118
- const err = out.failure;
4119
- return yield* StageError.make({
4120
- stage: err.stage,
4121
- reason: err.reason,
4122
- cause: err
4123
- });
4027
+ })))
4028
+ };
4029
+ })).decode(Sandwich.pure((raw) => Result.succeed(InstrumentCommand.make({
4030
+ fileCount: raw.filesToMutate.length,
4031
+ inPlace: raw.prev.options.inPlace,
4032
+ pluginCount: raw.prev.loadedPlugins.pluginModulePaths.length
4033
+ })))).decide(planInstrumentation).encode(Sandwich.pure((outcome) => Result.succeed(outcome))).write((output, raw) => withPhaseSpan("instrument", { fileCount: raw.filesToMutate.length }, () => Effect$1.gen(function* () {
4034
+ const env = yield* RunEnvironment;
4035
+ const now = yield* Clock.currentTimeMillis;
4036
+ const queue = yield* RunEvents;
4037
+ yield* Queue.offer(queue, PhaseEntered.make({
4038
+ phase: "instrument",
4039
+ elapsedMs: now - env.runStartedAt
4040
+ }));
4041
+ const out = output;
4042
+ if (Result.isFailure(out)) {
4043
+ const err = out.failure;
4044
+ return yield* StageError.make({
4045
+ stage: err.stage,
4046
+ reason: err.reason,
4047
+ cause: err
4048
+ });
4049
+ }
4050
+ return {
4051
+ ...raw.prev,
4052
+ project: raw.instrumentedProject,
4053
+ mutants: raw.instrumentResult.mutants,
4054
+ sandbox: raw.sandbox,
4055
+ concurrency: {
4056
+ testRunners: raw.concurrency.testRunners,
4057
+ checkers: raw.concurrency.checkers
4124
4058
  }
4125
- return {
4126
- ...raw.prev,
4127
- project: raw.instrumentedProject,
4128
- mutants: raw.instrumentResult.mutants,
4129
- sandbox: raw.sandbox,
4130
- concurrency: {
4131
- testRunners: raw.concurrency.testRunners,
4132
- checkers: raw.concurrency.checkers
4133
- }
4134
- };
4135
- }))
4136
- });
4059
+ };
4060
+ })));
4137
4061
  //#endregion
4138
4062
  //#region src/MutationTest.schema.ts
4139
4063
  var MutationTestCommand = class extends S.TaggedClass()("MutationTestCommand", {
@@ -4168,7 +4092,7 @@ const admitMutationTest = Workflow.make(MutationTestCommand, (command) => Match.
4168
4092
  }, () => Result.succeed(MutationTestNoTests.make({}))), Match.orElse(() => Result.succeed(MutationTestProceed.make({})))));
4169
4093
  //#endregion
4170
4094
  //#region src/checker-mutant-wire.ts
4171
- const CheckerMutantFromMutant = Mutant.pipe(S.decodeTo(CheckerMutantWire, SchemaTransformation.transform({
4095
+ const CheckerMutantFromMutant = Mutant.pipe(S.decodeTo(CheckerMutantWire, SchemaTransformation$1.transform({
4172
4096
  decode: (m) => ({
4173
4097
  id: m.id,
4174
4098
  fileName: m.fileName,
@@ -4184,7 +4108,7 @@ const CheckerMutantFromMutant = Mutant.pipe(S.decodeTo(CheckerMutantWire, Schema
4184
4108
  location: w.location
4185
4109
  })
4186
4110
  })));
4187
- const wireRecordOf = (mutant) => Result.mapError(S.decodeUnknownResult(CheckerMutantFromMutant)(mutant), (error) => ({
4111
+ const wireRecordOf = (mutant) => Result.mapError(S.decodeResult(CheckerMutantFromMutant)(mutant), (error) => ({
4188
4112
  id: mutant.id,
4189
4113
  fileName: mutant.fileName,
4190
4114
  reason: error.message
@@ -4501,35 +4425,29 @@ const logSkippedMutants = (checkerName, undescribable) => Match.value(undescriba
4501
4425
  * that joins them back. The join is where the work is, and it is pure.
4502
4426
  */
4503
4427
  const checkPlans = (checker, checkerName, plans, lookup) => {
4504
- return Cell.layer({
4505
- read: (command) => Effect$1.gen(function* () {
4506
- const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
4507
- onSome: (known) => selectedFromLookup(command.plans, known),
4508
- onNone: () => partitionMutantsForWire(command.plans)
4509
- });
4510
- yield* logSkippedMutants(command.checkerName, partitioned.undescribable);
4511
- if (partitioned.undescribable.length > 0) yield* Metric.update(checkerMutantsSkipped, partitioned.undescribable.length);
4512
- yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
4513
- const answers = yield* command.checker.check(command.checkerName, partitioned.wire);
4514
- return {
4515
- checkerName: command.checkerName,
4516
- requestedIds: command.plans.map((plan) => plan.mutant.id),
4517
- answers: {
4518
- ...compileErrorAnswersOf(partitioned.undescribable),
4519
- ...answers
4520
- }
4521
- };
4522
- }),
4523
- decode: (raw) => Result.succeed(CheckerCommand.make({
4524
- checkerName: raw.checkerName,
4525
- requestedIds: [...raw.requestedIds],
4526
- phase: "check",
4527
- answers: { ...raw.answers }
4528
- })),
4529
- decide: admitCheckerAnswer,
4530
- encode: (outcome) => outcome,
4531
- write: (outcome, raw) => writeCheckerOutcome(plans, raw.checkerName, outcome)
4532
- }).run({
4428
+ return Sandwich.read((command) => Effect$1.gen(function* () {
4429
+ const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
4430
+ onSome: (known) => selectedFromLookup(command.plans, known),
4431
+ onNone: () => partitionMutantsForWire(command.plans)
4432
+ });
4433
+ yield* logSkippedMutants(command.checkerName, partitioned.undescribable);
4434
+ if (partitioned.undescribable.length > 0) yield* Metric.update(checkerMutantsSkipped, partitioned.undescribable.length);
4435
+ yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
4436
+ const answers = yield* command.checker.check(command.checkerName, partitioned.wire);
4437
+ return {
4438
+ checkerName: command.checkerName,
4439
+ requestedIds: command.plans.map((plan) => plan.mutant.id),
4440
+ answers: {
4441
+ ...compileErrorAnswersOf(partitioned.undescribable),
4442
+ ...answers
4443
+ }
4444
+ };
4445
+ })).decode(Sandwich.pure((raw) => Result.succeed(CheckerCommand.make({
4446
+ checkerName: raw.checkerName,
4447
+ requestedIds: [...raw.requestedIds],
4448
+ phase: "check",
4449
+ answers: { ...raw.answers }
4450
+ })))).decide(admitCheckerAnswer).encode(Sandwich.pure((outcome) => Result.succeed(outcome))).write((outcome, raw) => writeCheckerOutcome(plans, raw.checkerName, outcome)).run({
4533
4451
  checker,
4534
4452
  checkerName,
4535
4453
  plans,
@@ -4540,31 +4458,25 @@ const checkPlans = (checker, checkerName, plans, lookup) => {
4540
4458
  * Ask a checker how to group run plans, and get groups of run plans back.
4541
4459
  */
4542
4460
  const groupPlans = (checker, checkerName, plans, lookup) => {
4543
- return Cell.layer({
4544
- read: (command) => Effect$1.gen(function* () {
4545
- const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
4546
- onSome: (known) => selectedFromLookup(command.plans, known),
4547
- onNone: () => partitionMutantsForWire(command.plans)
4548
- });
4549
- yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
4550
- const undescribableIds = undescribableIdsOf(partitioned.undescribable);
4551
- const withoutSkipped = (yield* command.checker.group(command.checkerName, partitioned.wire)).map((group) => group.filter((id) => !undescribableIds.has(id))).filter((group) => group.length > 0);
4552
- return {
4553
- checkerName: command.checkerName,
4554
- requestedIds: command.plans.map((plan) => plan.mutant.id),
4555
- idGroups: [...singletonGroupsOf(partitioned.undescribable), ...withoutSkipped]
4556
- };
4557
- }),
4558
- decode: (raw) => Result.succeed(CheckerCommand.make({
4559
- checkerName: raw.checkerName,
4560
- requestedIds: [...raw.requestedIds],
4561
- phase: "group",
4562
- idGroups: raw.idGroups.map((group) => [...group])
4563
- })),
4564
- decide: admitCheckerAnswer,
4565
- encode: (outcome) => outcome,
4566
- write: (outcome, raw) => writeGroupOutcome(plans, raw.checkerName, outcome)
4567
- }).run({
4461
+ return Sandwich.read((command) => Effect$1.gen(function* () {
4462
+ const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
4463
+ onSome: (known) => selectedFromLookup(command.plans, known),
4464
+ onNone: () => partitionMutantsForWire(command.plans)
4465
+ });
4466
+ yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
4467
+ const undescribableIds = undescribableIdsOf(partitioned.undescribable);
4468
+ const withoutSkipped = (yield* command.checker.group(command.checkerName, partitioned.wire)).map((group) => group.filter((id) => !undescribableIds.has(id))).filter((group) => group.length > 0);
4469
+ return {
4470
+ checkerName: command.checkerName,
4471
+ requestedIds: command.plans.map((plan) => plan.mutant.id),
4472
+ idGroups: [...singletonGroupsOf(partitioned.undescribable), ...withoutSkipped]
4473
+ };
4474
+ })).decode(Sandwich.pure((raw) => Result.succeed(CheckerCommand.make({
4475
+ checkerName: raw.checkerName,
4476
+ requestedIds: [...raw.requestedIds],
4477
+ phase: "group",
4478
+ idGroups: raw.idGroups.map((group) => [...group])
4479
+ })))).decide(admitCheckerAnswer).encode(Sandwich.pure((outcome) => Result.succeed(outcome))).write((outcome, raw) => writeGroupOutcome(plans, raw.checkerName, outcome)).run({
4568
4480
  checker,
4569
4481
  checkerName,
4570
4482
  plans,
@@ -4800,7 +4712,7 @@ function embeddedConfig(report) {
4800
4712
  }
4801
4713
  function breakThreshold(thresholds) {
4802
4714
  const ThresholdsBreakSchema = S.StructWithRest(S.Struct({ break: S.optional(S.Union([S.Finite, S.Null])) }), [S.Record(S.String, S.Unknown)]);
4803
- const decoded = S.decodeUnknownOption(ThresholdsBreakSchema)(thresholds);
4715
+ const decoded = S.decodeOption(ThresholdsBreakSchema)(thresholds);
4804
4716
  return Option.getOrNull(Option.flatMap(decoded, (value) => Option.fromNullishOr(value.break)));
4805
4717
  }
4806
4718
  const actionableMutants = (files) => Object.entries(files).flatMap(([file, fileResult]) => fileResult.mutants.filter((mutant) => isActionableStatus(mutant.status)).map((mutant) => ({
@@ -4965,7 +4877,7 @@ const makeMutationReportingService = (input) => {
4965
4877
  const resolved = yield* Effect$1.try(() => new URL(import.meta.resolve(`${specifier}/package.json`)));
4966
4878
  const manifestPath = yield* pathService.fromFileUrl(resolved);
4967
4879
  const text = yield* fs.readFileString(manifestPath);
4968
- return Result.match(S.decodeUnknownResult(S.fromJsonString(ManifestSchema))(text), {
4880
+ return Result.match(S.decodeResult(S.fromJsonString(ManifestSchema))(text), {
4969
4881
  onFailure: () => Option.none(),
4970
4882
  onSuccess: (manifest) => Option.some(manifest.version ?? "")
4971
4883
  });
@@ -5215,160 +5127,177 @@ const reportDroppedMutants = (dropped) => Match.value(dropped.length).pipe(Match
5215
5127
  yield* Metric.update(checkerMutantsSkipped, dropped.length);
5216
5128
  yield* Effect$1.logWarning(`${dropped.length} mutant(s) cannot be described to a checker and were left out of the run (${droppedIdsOf(dropped)})`);
5217
5129
  })));
5218
- const mutationTestCell$1 = Cell.layer({
5219
- read: (command) => Effect$1.gen(function* () {
5220
- yield* Scope.Scope;
5221
- return { prev: command };
5222
- }),
5223
- decode: (raw) => Result.succeed(MutationTestCommand.make({
5224
- dryRunOnly: raw.prev.options.dryRunOnly,
5225
- allowEmpty: raw.prev.options.allowEmpty,
5226
- testCount: raw.prev.dryRunResult.tests.length,
5227
- isZero: raw.prev.dryRunResult.tests.length === 0
5228
- })),
5229
- decide: admitMutationTest,
5230
- encode: (outcome) => outcome,
5231
- write: (outcome, raw) => {
5232
- const { dropped, plannable: plannableMutants } = partitionPlannable(raw.prev.mutants);
5233
- return withPhaseSpan("mutationTest", {
5234
- mutantCount: raw.prev.mutants.length,
5235
- skippedMutantCount: dropped.length,
5236
- testCount: raw.prev.dryRunResult.tests.length
5237
- }, () => Effect$1.gen(function* () {
5238
- yield* reportDroppedMutants(dropped);
5239
- const decision = yield* Result.match(outcome, {
5240
- onFailure: (err) => Effect$1.fail(StageError.make({
5241
- stage: err.stage,
5242
- reason: err.reason,
5243
- cause: err
5244
- })),
5245
- onSuccess: (d) => Effect$1.succeed(d)
5246
- });
5247
- return yield* Match.value(decision).pipe(Match.tag("MutationTestDryRunOnly", () => Effect$1.gen(function* () {
5248
- const env = yield* RunEnvironment;
5249
- const queue = yield* RunEvents;
5130
+ const hasStringReason = (r) => "reason" in r && typeof r.reason === "string";
5131
+ const reasonOf = (result) => Match.value(result).pipe(Match.when((r) => typeof r === "object" && r !== null, (r) => Match.value(r).pipe(Match.when(hasStringReason, (obj) => obj.reason), Match.orElse(() => void 0))), Match.orElse(() => void 0));
5132
+ const stopWallClock = (result) => {
5133
+ if (!wallClockTimeoutStopsRun(result.status, reasonOf(result))) return Effect$1.void;
5134
+ return Effect$1.fail(StageError.make({
5135
+ stage: "mutationTest",
5136
+ reason: WALL_CLOCK_TIMEOUT_REASON
5137
+ }));
5138
+ };
5139
+ const mutationTestCell$1 = Sandwich.read((command) => Effect$1.gen(function* () {
5140
+ yield* Scope.Scope;
5141
+ return { prev: command };
5142
+ })).decode(Sandwich.pure((raw) => Result.succeed(MutationTestCommand.make({
5143
+ dryRunOnly: raw.prev.options.dryRunOnly,
5144
+ allowEmpty: raw.prev.options.allowEmpty,
5145
+ testCount: raw.prev.dryRunResult.tests.length,
5146
+ isZero: raw.prev.dryRunResult.tests.length === 0
5147
+ })))).decide(admitMutationTest).encode(Sandwich.pure((outcome) => Result.succeed(outcome))).write((outcome, raw) => {
5148
+ const { dropped, plannable: plannableMutants } = partitionPlannable(raw.prev.mutants);
5149
+ return withPhaseSpan("mutationTest", {
5150
+ mutantCount: raw.prev.mutants.length,
5151
+ skippedMutantCount: dropped.length,
5152
+ testCount: raw.prev.dryRunResult.tests.length
5153
+ }, () => Effect$1.gen(function* () {
5154
+ yield* reportDroppedMutants(dropped);
5155
+ const decision = yield* Result.match(outcome, {
5156
+ onFailure: (err) => Effect$1.fail(StageError.make({
5157
+ stage: err.stage,
5158
+ reason: err.reason,
5159
+ cause: err
5160
+ })),
5161
+ onSuccess: (d) => Effect$1.succeed(d)
5162
+ });
5163
+ return yield* Match.value(decision).pipe(Match.tag("MutationTestDryRunOnly", () => Effect$1.gen(function* () {
5164
+ const env = yield* RunEnvironment;
5165
+ const queue = yield* RunEvents;
5166
+ const nowEmit = yield* Clock.currentTimeMillis;
5167
+ yield* Queue.offer(queue, PhaseEntered.make({
5168
+ phase: "mutation-test",
5169
+ elapsedMs: nowEmit - env.runStartedAt
5170
+ }));
5171
+ yield* Effect$1.logInfo("The dry-run has been completed successfully. No mutations have been executed.");
5172
+ return {
5173
+ results: [],
5174
+ verdict: null
5175
+ };
5176
+ })), Match.tag("MutationTestNoTests", () => Effect$1.gen(function* () {
5177
+ const env = yield* RunEnvironment;
5178
+ const queue = yield* RunEvents;
5179
+ const now = yield* Clock.currentTimeMillis;
5180
+ const elapsed = Duration.millis(now - env.runStartedAt);
5181
+ yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
5182
+ const nowEmit = yield* Clock.currentTimeMillis;
5183
+ yield* Queue.offer(queue, PhaseEntered.make({
5184
+ phase: "mutation-test",
5185
+ elapsedMs: nowEmit - env.runStartedAt
5186
+ }));
5187
+ return {
5188
+ results: [],
5189
+ verdict: null
5190
+ };
5191
+ })), Match.tag("MutationTestProceed", () => Effect$1.gen(function* () {
5192
+ const prev = raw.prev;
5193
+ const env = yield* RunEnvironment;
5194
+ yield* Effect$1.gen(function* () {
5250
5195
  const nowEmit = yield* Clock.currentTimeMillis;
5251
- yield* Queue.offer(queue, PhaseEntered.make({
5252
- phase: "mutation-test",
5253
- elapsedMs: nowEmit - env.runStartedAt
5254
- }));
5255
- yield* Effect$1.logInfo("The dry-run has been completed successfully. No mutations have been executed.");
5256
- return {
5257
- results: [],
5258
- verdict: null
5259
- };
5260
- })), Match.tag("MutationTestNoTests", () => Effect$1.gen(function* () {
5261
- const env = yield* RunEnvironment;
5262
5196
  const queue = yield* RunEvents;
5263
- const now = yield* Clock.currentTimeMillis;
5264
- const elapsed = Duration.millis(now - env.runStartedAt);
5265
- yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
5266
- const nowEmit = yield* Clock.currentTimeMillis;
5267
5197
  yield* Queue.offer(queue, PhaseEntered.make({
5268
5198
  phase: "mutation-test",
5269
5199
  elapsedMs: nowEmit - env.runStartedAt
5270
5200
  }));
5201
+ });
5202
+ const idGenerator = yield* IdGenerator;
5203
+ const checkerPool = yield* makeCheckerPool(prev, idGenerator, env.basePath);
5204
+ const testRunnerContext = {
5205
+ options: prev.options,
5206
+ fileDescriptions: prev.project.fileDescriptions,
5207
+ sandboxWorkingDirectory: prev.sandbox.workingDirectory,
5208
+ idGenerator,
5209
+ retire: Effect$1.void,
5210
+ testFiles: prev.project.testFiles.map((file) => prev.sandbox.sandboxFileFor(file))
5211
+ };
5212
+ const testRunnerPool = yield* Pool.make({
5213
+ acquire: buildTestRunner(testRunnerContext, Effect$1.suspend(() => {
5214
+ const runnerConfigured = prev.options.testRunner;
5215
+ const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
5216
+ return resolveConfiguredWorkerSpawn({
5217
+ loaded: prev.loadedPlugins,
5218
+ kind: "TestRunner",
5219
+ configured: runnerConfigured
5220
+ }).pipe(Effect$1.mapError(missingWorkerEntry("mutationTest", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
5221
+ options: prev.options,
5222
+ fileDescriptions: prev.project.fileDescriptions,
5223
+ sandboxWorkingDirectory: prev.sandbox.workingDirectory,
5224
+ workerEntrypoint: spawn.entrypoint,
5225
+ idGenerator
5226
+ })));
5227
+ })),
5228
+ size: prev.concurrency.testRunners
5229
+ });
5230
+ const reporting = makeMutationReportingService({
5231
+ reporterStage: prev.reporterStage,
5232
+ options: prev.options,
5233
+ project: prev.project,
5234
+ testCoverage: prev.testCoverage,
5235
+ runId: env.runId,
5236
+ resolvedMode: env.resolvedMode,
5237
+ sandboxDirectory: prev.sandbox.workingDirectory,
5238
+ basePath: env.basePath
5239
+ });
5240
+ const sandboxFileByName = Object.fromEntries([...MutableHashMap.keys(prev.project.filesToMutate)].map((name) => [name, prev.sandbox.sandboxFileFor(name)]));
5241
+ const currentRelativeFiles = yield* readCurrentRelativeFiles(prev.project, env.basePath);
5242
+ const incremental = incrementalDiff({
5243
+ currentMutants: plannableMutants,
5244
+ testCoverage: prev.testCoverage,
5245
+ incrementalReport: prev.project.incrementalReport,
5246
+ currentRelativeFiles,
5247
+ basePath: env.basePath,
5248
+ force: prev.options.force
5249
+ });
5250
+ const rememberedResults = rememberedResultsOf(plannableMutants, incremental.remembered);
5251
+ yield* Effect$1.when(Effect$1.logInfo(`Incremental mode: reusing ${rememberedResults.length} mutant result(s), running ${incremental.mutants.length} mutant(s).`), Effect$1.succeed(rememberedResults.length > 0));
5252
+ const { runPlans, earlyResults: noCoverageResults } = partitionRunPlans(yield* decidePlans(incremental.mutants, prev.testCoverage, {
5253
+ disableBail: prev.options.disableBail,
5254
+ timeoutMS: prev.options.timeoutMS,
5255
+ timeoutFactor: prev.options.timeoutFactor,
5256
+ ignoreStatic: prev.options.ignoreStatic
5257
+ }, Duration.toMillis(prev.timeOverhead), void 0, sandboxFileByName));
5258
+ const sortedPlans = sortRunPlans(runPlans);
5259
+ const allPlansForReporter = [...sortedPlans];
5260
+ yield* offerReporterEvent(prev.reporterStage, MutationTestingPlanReady.make({
5261
+ total: allPlansForReporter.length + noCoverageResults.length + rememberedResults.length,
5262
+ plans: allPlansForReporter.map((plan) => ({
5263
+ mutantId: plan.mutant.id,
5264
+ plan: plan.plan,
5265
+ netTime: plan.netTime,
5266
+ reloadEnvironment: plan.runOptions.reloadEnvironment
5267
+ }))
5268
+ })).pipe(Effect$1.ignoreCause);
5269
+ {
5270
+ const queue2 = yield* RunEvents;
5271
+ yield* Queue.offer(queue2, PlanKnown.make({ total: allPlansForReporter.length + noCoverageResults.length }));
5272
+ }
5273
+ const { passedPlans, checkerResults } = yield* checkPlansWithConfiguredCheckers(checkerPool, sortedPlans, reporting);
5274
+ const testRunnerStream = Stream.fromIterable(passedPlans);
5275
+ const plannedTotal = allPlansForReporter.length + noCoverageResults.length + rememberedResults.length;
5276
+ const pathService = yield* Path.Path;
5277
+ const progressQueue = yield* RunEvents;
5278
+ const completedRef = yield* Ref.make(0);
5279
+ const preparedStreamableOf = (result) => {
5280
+ if (!isMutantStatus(result.status)) return;
5271
5281
  return {
5272
- results: [],
5273
- verdict: null
5274
- };
5275
- })), Match.tag("MutationTestProceed", () => Effect$1.gen(function* () {
5276
- const prev = raw.prev;
5277
- const env = yield* RunEnvironment;
5278
- yield* Effect$1.gen(function* () {
5279
- const nowEmit = yield* Clock.currentTimeMillis;
5280
- const queue = yield* RunEvents;
5281
- yield* Queue.offer(queue, PhaseEntered.make({
5282
- phase: "mutation-test",
5283
- elapsedMs: nowEmit - env.runStartedAt
5284
- }));
5285
- });
5286
- const idGenerator = yield* IdGenerator;
5287
- const checkerPool = yield* makeCheckerPool(prev, idGenerator, env.basePath);
5288
- const testRunnerContext = {
5289
- options: prev.options,
5290
- fileDescriptions: prev.project.fileDescriptions,
5291
- sandboxWorkingDirectory: prev.sandbox.workingDirectory,
5292
- idGenerator,
5293
- retire: Effect$1.void,
5294
- testFiles: prev.project.testFiles.map((file) => prev.sandbox.sandboxFileFor(file))
5295
- };
5296
- const testRunnerPool = yield* Pool.make({
5297
- acquire: buildTestRunner(testRunnerContext, Effect$1.suspend(() => {
5298
- const runnerConfigured = prev.options.testRunner;
5299
- const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
5300
- return resolveConfiguredWorkerSpawn({
5301
- loaded: prev.loadedPlugins,
5302
- kind: "TestRunner",
5303
- configured: runnerConfigured
5304
- }).pipe(Effect$1.mapError(missingWorkerEntry("mutationTest", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
5305
- options: prev.options,
5306
- fileDescriptions: prev.project.fileDescriptions,
5307
- sandboxWorkingDirectory: prev.sandbox.workingDirectory,
5308
- workerEntrypoint: spawn.entrypoint,
5309
- idGenerator
5310
- })));
5311
- })),
5312
- size: prev.concurrency.testRunners
5313
- });
5314
- const reporting = makeMutationReportingService({
5315
- reporterStage: prev.reporterStage,
5316
- options: prev.options,
5317
- project: prev.project,
5318
- testCoverage: prev.testCoverage,
5319
- runId: env.runId,
5320
- resolvedMode: env.resolvedMode,
5321
- sandboxDirectory: prev.sandbox.workingDirectory,
5322
- basePath: env.basePath
5323
- });
5324
- const sandboxFileByName = Object.fromEntries([...MutableHashMap.keys(prev.project.filesToMutate)].map((name) => [name, prev.sandbox.sandboxFileFor(name)]));
5325
- const currentRelativeFiles = yield* readCurrentRelativeFiles(prev.project, env.basePath);
5326
- const incremental = incrementalDiff({
5327
- currentMutants: plannableMutants,
5328
- testCoverage: prev.testCoverage,
5329
- incrementalReport: prev.project.incrementalReport,
5330
- currentRelativeFiles,
5331
- basePath: env.basePath,
5332
- force: prev.options.force
5333
- });
5334
- const rememberedResults = rememberedResultsOf(plannableMutants, incremental.remembered);
5335
- yield* Effect$1.when(Effect$1.logInfo(`Incremental mode: reusing ${rememberedResults.length} mutant result(s), running ${incremental.mutants.length} mutant(s).`), Effect$1.succeed(rememberedResults.length > 0));
5336
- const { runPlans, earlyResults: noCoverageResults } = partitionRunPlans(yield* decidePlans(incremental.mutants, prev.testCoverage, {
5337
- disableBail: prev.options.disableBail,
5338
- timeoutMS: prev.options.timeoutMS,
5339
- timeoutFactor: prev.options.timeoutFactor,
5340
- ignoreStatic: prev.options.ignoreStatic
5341
- }, Duration.toMillis(prev.timeOverhead), void 0, sandboxFileByName));
5342
- const sortedPlans = sortRunPlans(runPlans);
5343
- const allPlansForReporter = [...sortedPlans];
5344
- yield* offerReporterEvent(prev.reporterStage, MutationTestingPlanReady.make({
5345
- total: allPlansForReporter.length + noCoverageResults.length + rememberedResults.length,
5346
- plans: allPlansForReporter.map((plan) => ({
5347
- mutantId: plan.mutant.id,
5348
- plan: plan.plan,
5349
- netTime: plan.netTime,
5350
- reloadEnvironment: plan.runOptions.reloadEnvironment
5351
- }))
5352
- })).pipe(Effect$1.ignoreCause);
5353
- {
5354
- const queue2 = yield* RunEvents;
5355
- yield* Queue.offer(queue2, PlanKnown.make({ total: allPlansForReporter.length + noCoverageResults.length }));
5356
- }
5357
- const { passedPlans, checkerResults } = yield* checkPlansWithConfiguredCheckers(checkerPool, sortedPlans, reporting);
5358
- const testRunnerStream = Stream.fromIterable(passedPlans);
5359
- const plannedTotal = allPlansForReporter.length + noCoverageResults.length + rememberedResults.length;
5360
- const pathService = yield* Path.Path;
5361
- const progressQueue = yield* RunEvents;
5362
- const completedRef = yield* Ref.make(0);
5363
- const preparedStreamableOf = (result) => {
5364
- if (!isMutantStatus(result.status)) return;
5365
- return {
5366
- status: result.status,
5367
- file: reportFileName(pathService.relative(env.basePath, result.fileName)),
5368
- location: toSchemaLocation(result.location)
5369
- };
5282
+ status: result.status,
5283
+ file: reportFileName(pathService.relative(env.basePath, result.fileName)),
5284
+ location: toSchemaLocation(result.location)
5370
5285
  };
5371
- const toStreamEvent = (result, completed, prepared) => MutantTested.make({
5286
+ };
5287
+ const toStreamEvent = (result, completed, prepared) => MutantTested.make({
5288
+ id: result.id,
5289
+ status: prepared.status,
5290
+ file: prepared.file,
5291
+ location: prepared.location,
5292
+ mutator: result.mutatorName,
5293
+ replacement: result.replacement,
5294
+ completed,
5295
+ total: plannedTotal
5296
+ });
5297
+ const offerFinished = (result, prepared) => Effect$1.gen(function* () {
5298
+ if (prepared === void 0) return;
5299
+ const completed = yield* Ref.updateAndGet(completedRef, (n) => n + 1);
5300
+ yield* Queue.offer(progressQueue, RunMutantTested.make({
5372
5301
  id: result.id,
5373
5302
  status: prepared.status,
5374
5303
  file: prepared.file,
@@ -5377,90 +5306,85 @@ const mutationTestCell$1 = Cell.layer({
5377
5306
  replacement: result.replacement,
5378
5307
  completed,
5379
5308
  total: plannedTotal
5380
- });
5381
- const offerFinished = (result, prepared) => Effect$1.gen(function* () {
5382
- if (prepared === void 0) return;
5383
- const completed = yield* Ref.updateAndGet(completedRef, (n) => n + 1);
5384
- yield* Queue.offer(progressQueue, RunMutantTested.make({
5385
- id: result.id,
5386
- status: prepared.status,
5387
- file: prepared.file,
5388
- location: prepared.location,
5389
- mutator: result.mutatorName,
5390
- replacement: result.replacement,
5391
- completed,
5392
- total: plannedTotal
5393
- }));
5394
- return completed;
5395
- });
5396
- const reportStreamTested = (result, completed, prepared) => offerReporterEvent(prev.reporterStage, toStreamEvent(result, completed, prepared)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Reporter stream failed handling mutantTested", cause)));
5397
- const offerStreamTested = (result, completed, prepared) => Option.match(Option.all([Option.fromNullishOr(completed), Option.fromNullishOr(prepared)]), {
5398
- onNone: () => Effect$1.void,
5399
- onSome: ([done, streamable]) => reportStreamTested(result, done, streamable)
5400
- });
5401
- const announceSettledMutant = (result) => Effect$1.gen(function* () {
5402
- const prepared = preparedStreamableOf(result);
5403
- const completed = yield* offerFinished(result, prepared);
5404
- yield* offerStreamTested(result, completed, prepared);
5405
- });
5406
- yield* Effect$1.forEach([
5407
- ...rememberedResults,
5408
- ...noCoverageResults,
5409
- ...checkerResults
5410
- ], announceSettledMutant, {
5411
- concurrency: 1,
5412
- discard: true
5413
- });
5414
- const completedMutants = yield* Ref.make([
5415
- ...rememberedResults,
5416
- ...noCoverageResults,
5417
- ...checkerResults
5418
- ]);
5419
- const checkpointGate = yield* Semaphore.make(1);
5420
- yield* reporting.checkpoint(yield* Ref.get(completedMutants)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
5421
- const persist = (result) => checkpointGate.withPermits(1)(Effect$1.gen(function* () {
5422
- const next = yield* Ref.updateAndGet(completedMutants, (prev) => [...prev, result]);
5423
- yield* reporting.checkpoint(next).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
5424
5309
  }));
5425
- const runResults = yield* withPhaseSpan("mutationTest.batch", {
5426
- total: plannedTotal,
5427
- testRunners: prev.concurrency.testRunners
5428
- }, () => Stream.mapEffect(testRunnerStream, (plan) => Effect$1.scoped(Effect$1.gen(function* () {
5429
- const pool = testRunnerPool;
5430
- const runner = yield* Pool.get(pool);
5431
- const result = yield* runner.mutantRun(plan.runOptions).pipe(Effect$1.withSpan("stryker.testRunner.mutantRun", { attributes: {
5432
- "stryker.mutant.id": plan.mutant.id,
5433
- "stryker.mutant.mutator": plan.mutant.mutatorName,
5434
- "stryker.mutant.file": plan.mutant.fileName
5435
- } }), Effect$1.tap((runResult) => Effect$1.annotateCurrentSpan({ "stryker.mutant.status": runResult.status })), Effect$1.catchTags({
5436
- OutOfMemoryError: (error) => invalidateSlot(pool, runner, error),
5437
- ChildProcessCrashedError: (error) => invalidateSlot(pool, runner, error)
5438
- }));
5439
- const reported = yield* reporting.reportMutantRunResult(toReportedMutant(plan.mutant), result);
5440
- const prepared = preparedStreamableOf(reported);
5441
- const finished = yield* offerFinished(reported, prepared);
5442
- yield* offerStreamTested(reported, finished, prepared);
5443
- yield* persist(reported);
5444
- return reported;
5445
- })), { concurrency: Math.max(1, prev.concurrency.testRunners) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
5446
- const allResults = [
5447
- ...rememberedResults,
5448
- ...noCoverageResults,
5449
- ...checkerResults,
5450
- ...runResults
5451
- ];
5452
- const outcomeResult = yield* reporting.reportAll(allResults);
5453
- const doneNow = yield* Clock.currentTimeMillis;
5454
- const elapsed = Duration.millis(doneNow - env.runStartedAt);
5455
- yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
5456
- return outcomeResult;
5457
- })), Match.exhaustive);
5458
- })).pipe(Effect$1.mapError((cause) => Match.value(cause).pipe(Match.tag("StageError", (stage) => stage), Match.tag("ChildProcessCrashedError", "OutOfMemoryError", "PlatformError", "TestRunnerFailed", () => StageError.make({
5459
- stage: "mutationTest",
5460
- reason: "Mutation testing failed",
5461
- cause
5462
- })), Match.exhaustive)));
5463
- }
5310
+ return completed;
5311
+ });
5312
+ const reportStreamTested = (result, completed, prepared) => offerReporterEvent(prev.reporterStage, toStreamEvent(result, completed, prepared)).pipe(Effect$1.tapCause((cause) => Effect$1.logWarning("Reporter stream failed handling mutantTested", cause)), Effect$1.ignoreCause);
5313
+ const offerStreamTested = (result, completed, prepared) => Option.match(Option.all([Option.fromNullishOr(completed), Option.fromNullishOr(prepared)]), {
5314
+ onNone: () => Effect$1.void,
5315
+ onSome: ([done, streamable]) => reportStreamTested(result, done, streamable)
5316
+ });
5317
+ const announceSettledMutant = (result) => Effect$1.gen(function* () {
5318
+ const prepared = preparedStreamableOf(result);
5319
+ const completed = yield* offerFinished(result, prepared);
5320
+ yield* offerStreamTested(result, completed, prepared);
5321
+ });
5322
+ yield* Effect$1.forEach([
5323
+ ...rememberedResults,
5324
+ ...noCoverageResults,
5325
+ ...checkerResults
5326
+ ], announceSettledMutant, {
5327
+ concurrency: 1,
5328
+ discard: true
5329
+ });
5330
+ const completedMutants = yield* Ref.make([
5331
+ ...rememberedResults,
5332
+ ...noCoverageResults,
5333
+ ...checkerResults
5334
+ ]);
5335
+ const checkpointGate = yield* Semaphore.make(1);
5336
+ yield* reporting.checkpoint(yield* Ref.get(completedMutants)).pipe(Effect$1.tapCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)), Effect$1.ignoreCause);
5337
+ const persist = (result) => checkpointGate.withPermits(1)(Effect$1.gen(function* () {
5338
+ const next = yield* Ref.updateAndGet(completedMutants, (prev) => [...prev, result]);
5339
+ yield* reporting.checkpoint(next).pipe(Effect$1.tapCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)), Effect$1.ignoreCause);
5340
+ }));
5341
+ const runResults = yield* withPhaseSpan("mutationTest.batch", {
5342
+ total: plannedTotal,
5343
+ testRunners: prev.concurrency.testRunners
5344
+ }, () => Stream.mapEffect(testRunnerStream, (plan) => Effect$1.scoped(Effect$1.gen(function* () {
5345
+ const pool = testRunnerPool;
5346
+ const runner = yield* Pool.get(pool);
5347
+ const result = yield* runner.mutantRun(plan.runOptions).pipe(Effect$1.withSpan("stryker.testRunner.mutantRun", { attributes: {
5348
+ "stryker.mutant.id": plan.mutant.id,
5349
+ "stryker.mutant.mutator": plan.mutant.mutatorName,
5350
+ "stryker.mutant.file": plan.mutant.fileName
5351
+ } }), Effect$1.tap((runResult) => Effect$1.annotateCurrentSpan({ "stryker.mutant.status": runResult.status })), Effect$1.catchTags({
5352
+ OutOfMemoryError: (error) => invalidateSlot(pool, runner, error),
5353
+ ChildProcessCrashedError: (error) => invalidateSlot(pool, runner, error)
5354
+ }));
5355
+ if (invalidatesRunnerPool(result.status, reasonOf(result))) return yield* invalidateSlot(pool, runner, ChildProcessCrashedError.make({
5356
+ pid: 0,
5357
+ exit: {
5358
+ _tag: "Signal",
5359
+ signal: "SIGKILL"
5360
+ },
5361
+ cause: "wall-clock timeout"
5362
+ }));
5363
+ yield* stopWallClock(result);
5364
+ const reported = yield* reporting.reportMutantRunResult(toReportedMutant(plan.mutant), result);
5365
+ const prepared = preparedStreamableOf(reported);
5366
+ const finished = yield* offerFinished(reported, prepared);
5367
+ yield* offerStreamTested(reported, finished, prepared);
5368
+ yield* persist(reported);
5369
+ return reported;
5370
+ })), { concurrency: Math.max(1, prev.concurrency.testRunners) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
5371
+ const allResults = [
5372
+ ...rememberedResults,
5373
+ ...noCoverageResults,
5374
+ ...checkerResults,
5375
+ ...runResults
5376
+ ];
5377
+ const outcomeResult = yield* reporting.reportAll(allResults);
5378
+ const doneNow = yield* Clock.currentTimeMillis;
5379
+ const elapsed = Duration.millis(doneNow - env.runStartedAt);
5380
+ yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
5381
+ return outcomeResult;
5382
+ })), Match.exhaustive);
5383
+ })).pipe(Effect$1.mapError((cause) => Match.value(cause).pipe(Match.tag("StageError", (stage) => stage), Match.tag("ChildProcessCrashedError", "OutOfMemoryError", "PlatformError", "TestRunnerFailed", () => StageError.make({
5384
+ stage: "mutationTest",
5385
+ reason: "Mutation testing failed",
5386
+ cause
5387
+ })), Match.exhaustive)));
5464
5388
  });
5465
5389
  //#endregion
5466
5390
  //#region src/reporter-output.ts
@@ -5475,7 +5399,7 @@ const write = (stdio, channel, chunks) => Stream.run(Stream.fromIterable(chunks)
5475
5399
  * that cannot compose Effects — its writes fire inside a synchronous for-await).
5476
5400
  * Write errors are ignored like a broken pipe on a stream handle.
5477
5401
  *
5478
- * Note: effect 4.0.0-rc.112 exports the runner as `Effect.runPromise`; there is no
5402
+ * Note: effect 4.0.0-rc.116 exports the runner as `Effect.runPromise`; there is no
5479
5403
  * `Runtime.runPromise` in this version (verified against the installed source) —
5480
5404
  * do not invent that import.
5481
5405
  */
@@ -5887,7 +5811,7 @@ const rememberTerminalReport = (seen, event) => {
5887
5811
  };
5888
5812
  }), Match.orElse(() => void 0));
5889
5813
  };
5890
- const decodeClearTextReport = (terminal) => S.decodeUnknownEffect(ClearTextReportCommand)({
5814
+ const decodeClearTextReport = (terminal) => S.decodeEffect(ClearTextReportCommand)({
5891
5815
  _tag: "ClearTextReportCommand",
5892
5816
  report: terminal.report,
5893
5817
  metrics: terminal.metrics
@@ -6329,13 +6253,7 @@ const writePrepare = (outcome, raw) => withPhaseSpan("prepare", {}, (span) => Ef
6329
6253
  reporterStage
6330
6254
  };
6331
6255
  }));
6332
- const prepareCell = Cell.layer({
6333
- read: readPrepare,
6334
- decode: decodePrepare,
6335
- decide: planPrepare,
6336
- encode: encodePrepareDecision,
6337
- write: writePrepare
6338
- });
6256
+ const prepareCell = Sandwich.read(readPrepare).decode(Sandwich.pure(decodePrepare)).decide(planPrepare).encode(Sandwich.pure((outcome) => Result.succeed(encodePrepareDecision(outcome)))).write(writePrepare);
6339
6257
  //#endregion
6340
6258
  //#region src/Run.ts
6341
6259
  const RUN_EVENTS_QUEUE_BOUND = 256;
@@ -6385,7 +6303,7 @@ const framedEventOf = (decision) => Option.match(decision, {
6385
6303
  onSome: (d) => Match.value(d).pipe(Match.tag("EventFramed", (framed) => Result.succeed(framed.event)), Match.tag("EventSuppressed", () => Result.fail(void 0)), Match.exhaustive)
6386
6304
  });
6387
6305
  var RunEventDrain = class extends Context.Service()("@systemfsoftware/stryker-js/run-event-stream/RunEventDrain") {};
6388
- const drainOf = (stdio, framed) => Stream.run(framed, stdio.stdout({ endOnDone: true })).pipe(Effect$1.withSpan("stryker.output.drain"), Effect$1.catchCause((cause) => Effect$1.logError("stryker.output.drain_failed", cause)));
6306
+ 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);
6389
6307
  const RunEventDrainLive = Layer.effect(RunEventDrain, Effect$1.gen(function* () {
6390
6308
  const stdio = yield* Stdio.Stdio;
6391
6309
  return RunEventDrain.of({
@@ -6552,7 +6470,7 @@ const RunEventDrainFileLive = Layer.effect(RunEventDrain, Effect$1.gen(function*
6552
6470
  drainFramed: (framed) => Effect$1.gen(function* () {
6553
6471
  const fileName = yield* Ref.get(fileNameRef);
6554
6472
  yield* drainStdoutAndFile(fs, path, stdio, fileName, framed);
6555
- }).pipe(Effect$1.catchCause((cause) => Effect$1.logError("stryker.output.drain_file_failed", cause))),
6473
+ }).pipe(Effect$1.tapCause((cause) => Effect$1.logError("stryker.output.drain_file_failed", cause)), Effect$1.ignoreCause),
6556
6474
  setProgressStreamFile: (fileName) => Ref.set(fileNameRef, fileName)
6557
6475
  });
6558
6476
  }));