@systemfsoftware/stryker-js 15.0.1 → 16.0.1

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,5 +1,5 @@
1
1
  import { t as __exportAll$4 } from "./rolldown-runtime-8H4AJuhK.mjs";
2
- import { a as MutatorCatalog_exports, c as Plugin_exports, d as TestRunner_exports, f as Trace_exports$1, i as Mutant_exports, l as Report_exports, n as mergeConfigs, o as MutatorProvider_exports, r as Checker_exports, s as Options_exports, t as mergeConfig, u as Reporter_exports } from "./merge-config-zsGl1aWc.mjs";
2
+ import { a as MutatorCatalog_exports, c as Plugin_exports, d as TestRunner_exports, f as Trace_exports$1, i as Mutant_exports, l as Report_exports, n as mergeConfigs, o as MutatorProvider_exports, r as Checker_exports, s as Options_exports, t as mergeConfig, u as Reporter_exports } from "./merge-config-BYhhPA7r.mjs";
3
3
  import { n as StageError, t as PrepareError } from "./Run.schema-DuaneDGd.mjs";
4
4
  import { Blueprint, Cell, Handle, Sandwich, Workflow } from "@systemfsoftware/effect-cell-types";
5
5
  import * as S from "effect/Schema";
@@ -33,6 +33,7 @@ import * as Queue from "effect/Queue";
33
33
  import * as Scope from "effect/Scope";
34
34
  import * as HashMap from "effect/HashMap";
35
35
  import * as MutableHashMap from "effect/MutableHashMap";
36
+ import * as Exit from "effect/Exit";
36
37
  import * as api from "@opentelemetry/api";
37
38
  import * as Headers from "effect/http/Headers";
38
39
  import * as Tracer from "effect/Tracer";
@@ -40,7 +41,6 @@ import * as RpcSerialization from "effect/rpc/RpcSerialization";
40
41
  import * as RpcServer from "effect/rpc/RpcServer";
41
42
  import * as SocketServer from "effect/socket/SocketServer";
42
43
  import * as Duration from "effect/Duration";
43
- import * as Exit from "effect/Exit";
44
44
  import * as Fiber from "effect/Fiber";
45
45
  import * as HashSet from "effect/HashSet";
46
46
  import * as PubSub from "effect/PubSub";
@@ -185,7 +185,32 @@ const PluginLoadFailureReason = Schema.Union([
185
185
  Schema.TaggedStruct("InvalidContribution", { detail: Schema.String }),
186
186
  Schema.TaggedStruct("ImportFailed", { cause: Schema.Unknown })
187
187
  ]);
188
- const StreamSchemaVersion = S.Literal("2.0");
188
+ const ShardPlanVersion = S.Literal(1);
189
+ const ShardProject = S.Struct({
190
+ project: S.String,
191
+ mutants: S.Array(S.String)
192
+ });
193
+ const Shard = S.Struct({
194
+ index: S.Int,
195
+ count: S.Int,
196
+ predictedSeconds: S.Finite,
197
+ projects: S.Array(ShardProject)
198
+ });
199
+ const labelOf = (shard) => `${shard.index}/${shard.count}`;
200
+ const shardLabelsAreUnique = S.makeFilter((shards) => {
201
+ const duplicated = Mutant_exports.duplicatedValue(shards.map(labelOf));
202
+ return duplicated === void 0 ? void 0 : `shard plan shards share the label "${duplicated}"`;
203
+ }, { arbitraryConstraint: { uniqueBy: labelOf } });
204
+ const ShardPlan = S.Struct({
205
+ version: ShardPlanVersion,
206
+ targetSeconds: S.Finite,
207
+ shards: S.Array(Shard).check(shardLabelsAreUnique),
208
+ matrix: S.Struct({ include: S.Array(S.Struct({
209
+ shard: S.String,
210
+ predictedSeconds: S.Finite
211
+ })) })
212
+ });
213
+ const StreamSchemaVersion = S.Literal("6.0");
189
214
  const RunPhase = S.Literals([
190
215
  "prepare",
191
216
  "instrument",
@@ -203,8 +228,18 @@ var PhaseEntered = class extends S.TaggedClass()("phase", {
203
228
  phase: RunPhase,
204
229
  elapsedMs: Report_exports.NonNegativeFinite
205
230
  }) {};
206
- var PlanKnown = class extends S.TaggedClass()("plan", { total: Report_exports.NonNegativeInt }) {};
207
- const MutantCost = S.Struct({
231
+ var PlanKnown = class extends S.TaggedClass()("plan", {
232
+ total: Report_exports.NonNegativeInt,
233
+ shardPlan: S.NullOr(ShardPlan)
234
+ }) {};
235
+ const WorkerRole = S.Literals(["testRunner", "checker"]);
236
+ var WorkerReported = class extends S.TaggedClass()("worker", {
237
+ schemaVersion: StreamSchemaVersion,
238
+ role: WorkerRole,
239
+ index: Report_exports.NonNegativeInt,
240
+ startupMs: Report_exports.NonNegativeFinite
241
+ }) {};
242
+ const MutantCost$1 = S.Struct({
208
243
  fixedOverheadMs: Report_exports.NonNegativeFinite,
209
244
  testBodyMs: Report_exports.NonNegativeFinite,
210
245
  testsExecuted: Report_exports.NonNegativeInt,
@@ -226,7 +261,7 @@ var RunMutantTestedEvent = class extends S.TaggedClass()("mutantTested", {
226
261
  completed: Report_exports.NonNegativeInt,
227
262
  total: Report_exports.NonNegativeInt,
228
263
  static: S.Boolean,
229
- cost: S.NullOr(MutantCost)
264
+ cost: S.NullOr(MutantCost$1)
230
265
  }) {};
231
266
  const RunMutantTested = S.TaggedStruct("mutant", {
232
267
  id: Mutant_exports.MutantId,
@@ -238,7 +273,7 @@ const RunMutantTested = S.TaggedStruct("mutant", {
238
273
  completed: Report_exports.NonNegativeInt,
239
274
  total: Report_exports.NonNegativeInt,
240
275
  static: S.Boolean,
241
- cost: S.NullOr(MutantCost)
276
+ cost: S.NullOr(MutantCost$1)
242
277
  }).pipe(S.decodeTo(S.toType(RunMutantTestedEvent), {
243
278
  decode: SchemaGetter.transform((line) => RunMutantTestedEvent.make({
244
279
  id: line.id,
@@ -290,6 +325,7 @@ const VerdictMutant = S.Struct({
290
325
  });
291
326
  const RunScope = S.Literals(["full", "diff"]);
292
327
  const MutantSetPolicy = S.Literals(["default", "full"]);
328
+ const IncrementalMode = S.Literals(["incremental", "full"]);
293
329
  const PhaseDurations = S.Struct({
294
330
  prepare: Report_exports.NonNegativeFinite,
295
331
  instrument: Report_exports.NonNegativeFinite,
@@ -300,6 +336,10 @@ const StaticVerdict = S.Struct({
300
336
  count: Report_exports.NonNegativeInt,
301
337
  costMs: Report_exports.NonNegativeFinite
302
338
  });
339
+ const Budget = S.Struct({
340
+ predictedSeconds: Report_exports.NonNegativeFinite,
341
+ actualSeconds: Report_exports.NonNegativeFinite
342
+ });
303
343
  var VerdictReached = class extends S.TaggedClass()("verdict", {
304
344
  schemaVersion: StreamSchemaVersion,
305
345
  runId: RunId,
@@ -312,8 +352,10 @@ var VerdictReached = class extends S.TaggedClass()("verdict", {
312
352
  mutants: S.Array(VerdictMutant),
313
353
  scope: RunScope,
314
354
  mutantSetPolicy: MutantSetPolicy,
355
+ incrementalMode: S.optionalKey(IncrementalMode),
315
356
  phaseDurations: S.NullOr(PhaseDurations),
316
- static: S.NullOr(StaticVerdict)
357
+ static: S.NullOr(StaticVerdict),
358
+ budget: Budget
317
359
  }) {};
318
360
  const FrameworkContributionRow = S.Struct({
319
361
  name: S.String,
@@ -360,6 +402,10 @@ var ReuseReported = class extends S.TaggedClass()("reuse", {
360
402
  ran: Report_exports.NonNegativeInt,
361
403
  refused: ReuseRefusals
362
404
  }) {};
405
+ var TceReported = class extends S.TaggedClass()("tce", {
406
+ equivalentToOriginal: Report_exports.NonNegativeInt,
407
+ duplicateAtSite: Report_exports.NonNegativeInt
408
+ }) {};
363
409
  var MutantDetailReported = class extends S.TaggedClass()("mutant-detail", {
364
410
  id: Mutant_exports.MutantId,
365
411
  status: Mutant_exports.MutantStatusSchema,
@@ -385,27 +431,37 @@ var HelpRendered = class extends S.TaggedClass()("help", {
385
431
  code: S.Literals([0]),
386
432
  help: S.String
387
433
  }) {};
434
+ const RefusalRule = S.Literal("mutation-runs-on-main-ci");
435
+ var Refused = class extends S.TaggedClass()("refused", {
436
+ schemaVersion: StreamSchemaVersion,
437
+ rule: RefusalRule,
438
+ message: S.String
439
+ }) {};
388
440
  const RunEvent = Object.assign(S.Union([
389
441
  RunStarted,
390
442
  PhaseEntered,
391
443
  PlanKnown,
444
+ WorkerReported,
392
445
  RunMutantTested,
393
446
  Heartbeat,
394
447
  PluginsReported,
395
448
  FormatRegistryResolved,
396
449
  SkippedReported,
397
450
  ReuseReported,
451
+ TceReported,
398
452
  MutantDetailReported,
399
453
  FeedbackReported,
400
454
  VerdictReached,
401
455
  RunFailed,
402
- HelpRendered
456
+ HelpRendered,
457
+ Refused
403
458
  ]), { QUEUE_BOUND: 256 });
404
459
  const RunEventWireLine = S.String.pipe(S.decodeTo(S.fromJsonString(RunEvent), {
405
460
  decode: SchemaGetter$1.transform((line) => line.replace(/\n$/, "")),
406
461
  encode: SchemaGetter$1.transform((json) => `${json}\n`)
407
462
  }));
408
463
  var RunEvent_exports = /* @__PURE__ */ __exportAll$3({
464
+ Budget: () => Budget,
409
465
  FeedbackJudgment: () => FeedbackJudgment,
410
466
  FeedbackReported: () => FeedbackReported,
411
467
  FormatClaimShadowingRow: () => FormatClaimShadowingRow,
@@ -415,7 +471,8 @@ var RunEvent_exports = /* @__PURE__ */ __exportAll$3({
415
471
  FrameworkModuleRow: () => FrameworkModuleRow,
416
472
  Heartbeat: () => Heartbeat,
417
473
  HelpRendered: () => HelpRendered,
418
- MutantCost: () => MutantCost,
474
+ IncrementalMode: () => IncrementalMode,
475
+ MutantCost: () => MutantCost$1,
419
476
  MutantDetailReported: () => MutantDetailReported,
420
477
  MutantSetPolicy: () => MutantSetPolicy,
421
478
  PhaseDurations: () => PhaseDurations,
@@ -423,6 +480,8 @@ var RunEvent_exports = /* @__PURE__ */ __exportAll$3({
423
480
  PlanKnown: () => PlanKnown,
424
481
  PluginLoadFailureReason: () => PluginLoadFailureReason,
425
482
  PluginsReported: () => PluginsReported,
483
+ RefusalRule: () => RefusalRule,
484
+ Refused: () => Refused,
426
485
  ReuseRefusals: () => ReuseRefusals,
427
486
  ReuseReported: () => ReuseReported,
428
487
  RunEvent: () => RunEvent,
@@ -438,10 +497,13 @@ var RunEvent_exports = /* @__PURE__ */ __exportAll$3({
438
497
  SkippedReported: () => SkippedReported,
439
498
  StaticVerdict: () => StaticVerdict,
440
499
  StreamSchemaVersion: () => StreamSchemaVersion,
500
+ TceReported: () => TceReported,
441
501
  VerdictLocation: () => VerdictLocation,
442
502
  VerdictMutant: () => VerdictMutant,
443
503
  VerdictReached: () => VerdictReached,
444
- VerdictThresholds: () => VerdictThresholds
504
+ VerdictThresholds: () => VerdictThresholds,
505
+ WorkerReported: () => WorkerReported,
506
+ WorkerRole: () => WorkerRole
445
507
  });
446
508
  const SpanName = S.String.check(S.isPattern(/^[A-Za-z][A-Za-z0-9_-]*(?:\.[A-Za-z][A-Za-z0-9_-]*)*$/u, { expected: "a dot-separated span name whose segments start with a letter, such as \"stryker.checker.check\"" })).pipe(S.brand("SpanName"));
447
509
  const SpanAttributeKey = S.String.check(S.isPattern(/^[A-Za-z][A-Za-z0-9_.-]*$/u, { expected: "an attribute key starting with a letter, such as \"stryker.checker.name\"" })).pipe(S.brand("SpanAttributeKey"));
@@ -688,74 +750,6 @@ const Spans = {
688
750
  name: "stryker.load_config",
689
751
  attributes: {}
690
752
  },
691
- mergeReports: {
692
- name: "stryker.merge_reports",
693
- attributes: {}
694
- },
695
- mergeReportsCollectPartDirs: {
696
- name: "stryker.merge_reports.collect_part_dirs",
697
- attributes: {}
698
- },
699
- mergeReportsDirectoryExists: {
700
- name: "stryker.merge_reports.directory_exists",
701
- attributes: {}
702
- },
703
- mergeReportsEncodeReport: {
704
- name: "stryker.merge_reports.encode_report",
705
- attributes: {}
706
- },
707
- mergeReportsFail: {
708
- name: "stryker.merge_reports.fail",
709
- attributes: {}
710
- },
711
- mergeReportsGather: {
712
- name: "stryker.merge_reports.gather",
713
- attributes: {}
714
- },
715
- mergeReportsListNames: {
716
- name: "stryker.merge_reports.list_names",
717
- attributes: {}
718
- },
719
- mergeReportsPutFile: {
720
- name: "stryker.merge_reports.put_file",
721
- attributes: {}
722
- },
723
- mergeReportsReadPart: {
724
- name: "stryker.merge_reports.read_part",
725
- attributes: {}
726
- },
727
- mergeReportsReadText: {
728
- name: "stryker.merge_reports.read_text",
729
- attributes: {}
730
- },
731
- mergeReportsRefuseCommand: {
732
- name: "stryker.merge_reports.refuse_command",
733
- attributes: {}
734
- },
735
- mergeReportsRefuseParts: {
736
- name: "stryker.merge_reports.refuse_parts",
737
- attributes: {}
738
- },
739
- mergeReportsRenderHtml: {
740
- name: "stryker.merge_reports.render_html",
741
- attributes: {}
742
- },
743
- mergeReportsWriteAbsent: {
744
- name: "stryker.merge_reports.write_absent",
745
- attributes: {}
746
- },
747
- mergeReportsWriteFiles: {
748
- name: "stryker.merge_reports.write_files",
749
- attributes: {}
750
- },
751
- mergeReportsWriteHtml: {
752
- name: "stryker.merge_reports.write_html",
753
- attributes: {}
754
- },
755
- mergeReportsWriteMerged: {
756
- name: "stryker.merge_reports.write_merged",
757
- attributes: {}
758
- },
759
753
  mutantRun: {
760
754
  name: "stryker.mutant_run",
761
755
  attributes: {}
@@ -2160,6 +2154,13 @@ const createDefaultOptions = S.decodeEffect(Options_exports.StrykerOptionsSchema
2160
2154
  const defaultOptions = Effect$1.map(createDefaultOptions, (options) => deepFreeze(options));
2161
2155
  //#endregion
2162
2156
  //#region src/ConfigError.schema.ts
2157
+ const CAUSE_DEPTH = 4;
2158
+ const ownMessageOf = (value) => Option$1.filter(Option$1.map(Option$1.liftPredicate(value, Predicate$1.hasProperty("message")), (carrier) => String(carrier.message)), S.is(S.NonEmptyString));
2159
+ const causeMessagesOf = (value, depth) => depth === 0 ? [] : [...Option$1.toArray(ownMessageOf(value)), ...Option$1.match(Option$1.liftPredicate(value, Predicate$1.hasProperty("cause")), {
2160
+ onNone: () => [],
2161
+ onSome: (carrier) => causeMessagesOf(carrier.cause, depth - 1)
2162
+ })];
2163
+ const causeTextOf$1 = (cause) => Option$1.liftPredicate(causeMessagesOf(cause, CAUSE_DEPTH).join(": "), S.is(S.NonEmptyString));
2163
2164
  var ConfigFileNotFoundError = class extends S.TaggedError()("ConfigFileNotFoundError", { file: S.String }) {
2164
2165
  exitClass = "ConfigError";
2165
2166
  get message() {
@@ -2181,7 +2182,10 @@ var ConfigFileUnreadableError = class extends S.TaggedError()("ConfigFileUnreada
2181
2182
  }) {
2182
2183
  exitClass = "ConfigError";
2183
2184
  get message() {
2184
- return `Config file is unreadable: ${this.file}`;
2185
+ return Option$1.match(causeTextOf$1(this.cause), {
2186
+ onNone: () => `Config file is unreadable: ${this.file}`,
2187
+ onSome: (cause) => `Config file is unreadable: ${this.file}: ${cause}`
2188
+ });
2185
2189
  }
2186
2190
  };
2187
2191
  var ConfigFileInvalidError = class extends S.TaggedError()("ConfigFileInvalidError", {
@@ -10244,6 +10248,48 @@ const resolveConfig = Workflow.make({
10244
10248
  })
10245
10249
  });
10246
10250
  //#endregion
10251
+ //#region src/atomic-write.cell.ts
10252
+ const writeFileAtomic = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingWriteAtomic.name)(function* (ports, file, content) {
10253
+ const directory = ports.path.dirname(file);
10254
+ yield* ports.fs.makeDirectory(directory, { recursive: true });
10255
+ const temp = yield* ports.fs.makeTempFile({
10256
+ directory,
10257
+ prefix: `${ports.path.basename(file)}.`,
10258
+ suffix: ".tmp"
10259
+ });
10260
+ yield* Effect$1.gen(function* () {
10261
+ yield* ports.fs.writeFileString(temp, content);
10262
+ yield* ports.fs.rename(temp, file);
10263
+ }).pipe(Effect$1.onExit((exit) => Exit.isSuccess(exit) ? Effect$1.void : ports.fs.remove(temp).pipe(Effect$1.ignore)));
10264
+ });
10265
+ //#endregion
10266
+ //#region src/mutant-cost.ts
10267
+ const executedTestBodyMsOf = (executedTests) => executedTests.reduce((total, test) => total + test.timeSpentMs, 0);
10268
+ const testBodyMsOf = (result) => Match$1.value(result).pipe(Match$1.when({ status: "killed" }, (killed) => executedTestBodyMsOf(killed.executedTests)), Match$1.when({ status: "survived" }, (survived) => executedTestBodyMsOf(survived.executedTests)), Match$1.orElse(() => 0));
10269
+ const bodyTimeWithinWall = (elapsedMs, testBodyMs) => Math.min(testBodyMs, elapsedMs);
10270
+ const costTotalMsOf = (cost) => cost === void 0 ? void 0 : cost.fixedOverheadMs + cost.testBodyMs;
10271
+ const mutantCostOf = (input) => {
10272
+ const testBodyMs = bodyTimeWithinWall(input.elapsedMs, input.testBodyMs);
10273
+ return {
10274
+ fixedOverheadMs: input.elapsedMs - testBodyMs,
10275
+ testBodyMs,
10276
+ testsExecuted: input.testsExecuted,
10277
+ shared: input.shared
10278
+ };
10279
+ };
10280
+ //#endregion
10281
+ //#region src/budget.ts
10282
+ const MILLIS_PER_SECOND = 1e3;
10283
+ const costMsOf = (result) => costTotalMsOf(result.cost) ?? 0;
10284
+ const predictedSecondsOf = (results, concurrency) => {
10285
+ const lanes = concurrency > 0 ? concurrency : 1;
10286
+ return Arr.reduce(results, 0, (total, result) => total + costMsOf(result)) / lanes / MILLIS_PER_SECOND;
10287
+ };
10288
+ const budgetOf$1 = (input) => ({
10289
+ predictedSeconds: input.planPredictedSeconds ?? predictedSecondsOf(input.results, input.concurrency),
10290
+ actualSeconds: input.actualSeconds
10291
+ });
10292
+ //#endregion
10247
10293
  //#region src/build-reproducers.workflow.ts
10248
10294
  const CONTEXT_LINES = 3;
10249
10295
  const maxOf$1 = (left, right) => Boolean$1$1.match(left < right, {
@@ -10315,7 +10361,7 @@ const fileNameOrder = Order.mapInput(Order.String, (reproducer) => reproducer.fi
10315
10361
  const idOrder$1 = Order.mapInput(Order.String, (reproducer) => reproducer.id);
10316
10362
  const reproducersOf = (report) => Arr.sort(Arr.flatMap(Object.entries(report.files), ([fileName, file]) => Arr.map(file.mutants, (mutant) => reproducerOf(fileName, file.source, mutant))), Order.combine(fileNameOrder, idOrder$1));
10317
10363
  const mutantsOf = (report) => Arr.flatMap(Object.values(report.files), (file) => file.mutants);
10318
- const decide$23 = (command) => {
10364
+ const decide$24 = (command) => {
10319
10365
  const mutants = mutantsOf(command.report);
10320
10366
  return Boolean$1$1.match(mutants.length === 0, {
10321
10367
  onTrue: () => NoMutantsToReproduce.make({}),
@@ -10326,7 +10372,7 @@ const buildReproducers = Workflow.make({
10326
10372
  command: BuildReproducersCommand,
10327
10373
  decision: BuildReproducersDecision,
10328
10374
  error: S.Never,
10329
- decide: (command) => Result.succeed(decide$23(command))
10375
+ decide: (command) => Result.succeed(decide$24(command))
10330
10376
  });
10331
10377
  //#endregion
10332
10378
  //#region src/checkpoint-mutants.workflow.ts
@@ -10350,7 +10396,7 @@ var CheckpointMutantsCommand = class extends S.TaggedClass()("CheckpointMutantsC
10350
10396
  };
10351
10397
  const settledByIdOf = (settled) => HashMap.fromIterable(settled.map((row) => [row.mutant.id, row]));
10352
10398
  const rowOf = (mutant, settledById) => Option$1.getOrElse(HashMap.get(settledById, mutant.id), () => CheckpointPendingMutant.make({ mutant }));
10353
- const decide$22 = (command) => {
10399
+ const decide$23 = (command) => {
10354
10400
  const settledById = settledByIdOf(command.settled);
10355
10401
  return Result.succeed(command.plannedMutants.map((mutant) => rowOf(mutant, settledById)));
10356
10402
  };
@@ -10358,7 +10404,7 @@ const checkpointMutants = Workflow.make({
10358
10404
  command: CheckpointMutantsCommand,
10359
10405
  decision: S.Array(CheckpointMutantRow),
10360
10406
  error: S.Never,
10361
- decide: decide$22
10407
+ decide: decide$23
10362
10408
  });
10363
10409
  //#endregion
10364
10410
  //#region src/classify-exit.workflow.ts
@@ -10407,7 +10453,7 @@ const verdictExitClass = (score, breakingThreshold) => Match$1.valueTags(score,
10407
10453
  onSome: (threshold) => Match$1.value(percentage < threshold).pipe(Match$1.when(true, () => "VerdictFail"), Match$1.when(false, () => null), Match$1.exhaustive)
10408
10454
  })
10409
10455
  });
10410
- const decide$21 = (command) => Option$1.match(Option$1.fromNullishOr(verdictExitClass(command.score, command.breakingThreshold)), {
10456
+ const decide$22 = (command) => Option$1.match(Option$1.fromNullishOr(verdictExitClass(command.score, command.breakingThreshold)), {
10411
10457
  onNone: () => Option$1.match(precedenceOf(command.pending), {
10412
10458
  onNone: () => Result.succeed(ExitPassed.make({})),
10413
10459
  onSome: (exitClass) => Result.succeed(decisionOf$3(exitClass))
@@ -10418,7 +10464,7 @@ const classifyExit = Workflow.make({
10418
10464
  command: ClassifyExitCommand,
10419
10465
  decision: ClassifyExitDecision,
10420
10466
  error: S.Never,
10421
- decide: decide$21
10467
+ decide: decide$22
10422
10468
  });
10423
10469
  //#endregion
10424
10470
  //#region src/IncrementalDiff.schema.ts
@@ -10480,6 +10526,61 @@ const ReuseReportSchema = S.Struct({
10480
10526
  testFiles: S.optional(ReuseTestFilesSchema)
10481
10527
  });
10482
10528
  //#endregion
10529
+ //#region src/MutantCost.schema.ts
10530
+ const MutantCost = S.Struct({
10531
+ predictedMs: S.Finite,
10532
+ actualMs: S.NullOr(S.Finite),
10533
+ coveringTests: S.Int
10534
+ });
10535
+ const MutantCosts = S.Record(Mutant_exports.MutantId, MutantCost);
10536
+ const MutantCostSubject = S.Struct({
10537
+ id: Mutant_exports.MutantId,
10538
+ static: S.optional(S.Boolean)
10539
+ });
10540
+ S.Struct({
10541
+ subject: MutantCostSubject,
10542
+ staticCoverage: S.optional(S.Record(S.String, Report_exports.NonNegativeFinite)),
10543
+ allTestTimesMs: S.Array(Report_exports.NonNegativeFinite),
10544
+ coveringTestTimesMs: S.Array(Report_exports.NonNegativeFinite),
10545
+ executedActualMs: S.optional(Report_exports.NonNegativeFinite),
10546
+ priorActualMs: S.optional(Report_exports.NonNegativeFinite),
10547
+ fixedOverheadMs: Report_exports.NonNegativeFinite
10548
+ });
10549
+ S.Struct({
10550
+ subjects: S.Array(MutantCostSubject),
10551
+ staticCoverage: S.optional(S.Record(S.String, Report_exports.NonNegativeFinite)),
10552
+ allTestTimesMs: S.Array(Report_exports.NonNegativeFinite),
10553
+ coveringTestTimesMsByMutantId: S.Record(S.String, S.Array(Report_exports.NonNegativeFinite)),
10554
+ executedActualMsByMutantId: S.Record(S.String, Report_exports.NonNegativeFinite),
10555
+ priorActualMsByMutantId: S.Record(S.String, Report_exports.NonNegativeFinite),
10556
+ fixedOverheadMs: Report_exports.NonNegativeFinite
10557
+ });
10558
+ //#endregion
10559
+ //#region src/mutant-cost-model.ts
10560
+ const staticHitCountOf = (staticCoverage, id) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromNullishOr(staticCoverage), (coverage) => Record$1.get(coverage, id)), () => 0);
10561
+ const hasStaticHit = (staticCoverage, id) => staticCoverage === void 0 || staticHitCountOf(staticCoverage, id) > 0;
10562
+ const runsWholeSuite$1 = (input) => input.subject.static === true || hasStaticHit(input.staticCoverage, input.subject.id);
10563
+ const coveringTimesOf = (input) => runsWholeSuite$1(input) ? input.allTestTimesMs : input.coveringTestTimesMs;
10564
+ const actualMsOf = (input) => Option$1.getOrElse(Option$1.firstSomeOf([Option$1.fromNullishOr(input.executedActualMs), Option$1.fromNullishOr(input.priorActualMs)]), () => null);
10565
+ const mutantCostOfCase = (input) => {
10566
+ const coveringTimes = coveringTimesOf(input);
10567
+ return {
10568
+ predictedMs: input.fixedOverheadMs + coveringTimes.reduce((total, time) => total + time, 0),
10569
+ actualMs: actualMsOf(input),
10570
+ coveringTests: coveringTimes.length
10571
+ };
10572
+ };
10573
+ const caseOfSubject = (input, subject) => ({
10574
+ subject,
10575
+ staticCoverage: input.staticCoverage,
10576
+ allTestTimesMs: input.allTestTimesMs,
10577
+ coveringTestTimesMs: Option$1.getOrElse(Record$1.get(input.coveringTestTimesMsByMutantId, subject.id), () => []),
10578
+ executedActualMs: Option$1.getOrUndefined(Record$1.get(input.executedActualMsByMutantId, subject.id)),
10579
+ priorActualMs: Option$1.getOrUndefined(Record$1.get(input.priorActualMsByMutantId, subject.id)),
10580
+ fixedOverheadMs: input.fixedOverheadMs
10581
+ });
10582
+ const mutantCostsOf = (input) => Object.fromEntries(input.subjects.map((subject) => [subject.id, mutantCostOfCase(caseOfSubject(input, subject))]));
10583
+ //#endregion
10483
10584
  //#region src/mutation-reporting.schema.ts
10484
10585
  var ManifestUnreadable = class extends Schema.TaggedError()("ManifestUnreadable", {
10485
10586
  specifier: Schema.String,
@@ -11036,21 +11137,6 @@ S.Literal("mutation-part.json");
11036
11137
  S.Literal("mutation-stream.jsonl");
11037
11138
  const ReportFileNames = S.Record(S.String, Mutant_exports.CanonicalFileName);
11038
11139
  //#endregion
11039
- //#region src/mutant-cost.ts
11040
- const executedTestBodyMsOf = (executedTests) => executedTests.reduce((total, test) => total + test.timeSpentMs, 0);
11041
- const testBodyMsOf = (result) => Match$1.value(result).pipe(Match$1.when({ status: "killed" }, (killed) => executedTestBodyMsOf(killed.executedTests)), Match$1.when({ status: "survived" }, (survived) => executedTestBodyMsOf(survived.executedTests)), Match$1.orElse(() => 0));
11042
- const bodyTimeWithinWall = (elapsedMs, testBodyMs) => Math.min(testBodyMs, elapsedMs);
11043
- const costTotalMsOf = (cost) => cost === void 0 ? void 0 : cost.fixedOverheadMs + cost.testBodyMs;
11044
- const mutantCostOf = (input) => {
11045
- const testBodyMs = bodyTimeWithinWall(input.elapsedMs, input.testBodyMs);
11046
- return {
11047
- fixedOverheadMs: input.elapsedMs - testBodyMs,
11048
- testBodyMs,
11049
- testsExecuted: input.testsExecuted,
11050
- shared: input.shared
11051
- };
11052
- };
11053
- //#endregion
11054
11140
  //#region src/reporting/static-verdict.ts
11055
11141
  const costOrZero = (cost) => Option$1.getOrElse(Option$1.fromUndefinedOr(costTotalMsOf(cost)), () => 0);
11056
11142
  const staticVerdictOf = (results) => {
@@ -11077,8 +11163,10 @@ var VerdictEnvelope = class extends S.Class("VerdictEnvelope")({
11077
11163
  mutants: S.Array(RunEvent_exports.VerdictMutant),
11078
11164
  scope: RunEvent_exports.RunScope,
11079
11165
  mutantSetPolicy: RunEvent_exports.MutantSetPolicy,
11166
+ incrementalMode: RunEvent_exports.IncrementalMode,
11080
11167
  phaseDurations: S.NullOr(RunEvent_exports.PhaseDurations),
11081
- static: S.NullOr(RunEvent_exports.StaticVerdict)
11168
+ static: S.NullOr(RunEvent_exports.StaticVerdict),
11169
+ budget: RunEvent_exports.Budget
11082
11170
  }) {};
11083
11171
  //#endregion
11084
11172
  //#region src/reporting/verdict-envelope.ts
@@ -11117,6 +11205,11 @@ const runIdTextOf = (now) => {
11117
11205
  };
11118
11206
  const generateRunId = (now) => RunEvent_exports.RunId.make(runIdTextOf(now));
11119
11207
  const isActionableStatus = S.is(Mutant_exports.ActionableStatusSchema);
11208
+ const ZERO_BUDGET = RunEvent_exports.Budget.make({
11209
+ predictedSeconds: 0,
11210
+ actualSeconds: 0
11211
+ });
11212
+ const budgetOf = (report) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromUndefinedOr(report["budget"]), (raw) => S.decodeUnknownOption(RunEvent_exports.Budget)(raw)), () => ZERO_BUDGET);
11120
11213
  const actionableMutants = (files) => Arr.flatMap(Object.entries(files), ([file, fileResult]) => Arr.map(Arr.filter(fileResult.mutants, (mutant) => isActionableStatus(mutant.status)), (mutant) => RunEvent_exports.VerdictMutant.make({
11121
11214
  id: mutant.id,
11122
11215
  file,
@@ -11125,13 +11218,17 @@ const actionableMutants = (files) => Arr.flatMap(Object.entries(files), ([file,
11125
11218
  replacement: mutant.replacement ?? null,
11126
11219
  status: mutant.status
11127
11220
  })));
11221
+ const reusesVerdicts = (config) => config.incremental === true && config.force !== true;
11222
+ const incrementalModeOf = (decoded) => Option$1.exists(decoded, reusesVerdicts) ? "incremental" : "full";
11128
11223
  const embeddedConfig = (report) => {
11129
11224
  const JsonReporterSchema = S.Struct({ fileName: S.String });
11130
11225
  const MutatorSchema = S.StructWithRest(S.Struct({ mutantSetPolicy: S.optional(RunEvent_exports.MutantSetPolicy) }), [S.Record(S.String, S.Unknown)]);
11131
11226
  const EmbeddedConfigSchema = S.StructWithRest(S.Struct({
11132
11227
  jsonReporter: S.optional(JsonReporterSchema),
11133
11228
  mutator: S.optional(MutatorSchema),
11134
- since: S.optional(S.String)
11229
+ since: S.optional(S.String),
11230
+ incremental: S.optional(S.Boolean),
11231
+ force: S.optional(S.Boolean)
11135
11232
  }), [S.Record(S.String, S.Unknown)]);
11136
11233
  const decoded = S.decodeUnknownOption(EmbeddedConfigSchema)(report.config);
11137
11234
  const jsonReporter = Option$1.flatMap(decoded, (config) => Option$1.fromUndefinedOr(config.jsonReporter));
@@ -11140,12 +11237,13 @@ const embeddedConfig = (report) => {
11140
11237
  return {
11141
11238
  jsonReporterFileName: Option$1.getOrUndefined(Option$1.map(jsonReporter, (reporter) => reporter.fileName)),
11142
11239
  mutantSetPolicy: Option$1.getOrElse(mutantSetPolicy, () => "default"),
11143
- scope: Option$1.isSome(since) ? "diff" : "full"
11240
+ scope: Option$1.isSome(since) ? "diff" : "full",
11241
+ incrementalMode: incrementalModeOf(decoded)
11144
11242
  };
11145
11243
  };
11146
11244
  const buildVerdictEnvelope = dual((args) => args.length === 8, (report, mode, signal, runId, basePath, pathService, phaseDurations, staticVerdict) => {
11147
11245
  const metrics = Report_exports.metricsFromMutants(Arr.flatMap(Object.values(report.files), (file) => file.mutants));
11148
- const { jsonReporterFileName, mutantSetPolicy, scope } = embeddedConfig(report);
11246
+ const { jsonReporterFileName, mutantSetPolicy, scope, incrementalMode } = embeddedConfig(report);
11149
11247
  return VerdictEnvelope.make({
11150
11248
  schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
11151
11249
  runId,
@@ -11165,14 +11263,33 @@ const buildVerdictEnvelope = dual((args) => args.length === 8, (report, mode, si
11165
11263
  mutants: actionableMutants(report.files),
11166
11264
  scope,
11167
11265
  mutantSetPolicy,
11266
+ incrementalMode,
11168
11267
  phaseDurations: Option$1.getOrNull(phaseDurations),
11169
- static: staticVerdict
11268
+ static: staticVerdict,
11269
+ budget: budgetOf(report)
11170
11270
  });
11171
11271
  });
11172
11272
  //#endregion
11173
11273
  //#region src/run-events.service.ts
11174
11274
  var RunEvents = class extends Context.Service()("@systemfsoftware/stryker-js/run-events.service/RunEvents") {};
11175
11275
  var RunIdentity = class extends Context.Service()("@systemfsoftware/stryker-js/run-events.service/RunIdentity") {};
11276
+ var WorkerReports = class extends Context.Service()("@systemfsoftware/stryker-js/run-events.service/WorkerReports") {};
11277
+ const WorkerReportsLive = Layer.effect(WorkerReports, Effect$1.gen(function* () {
11278
+ const queue = yield* RunEvents;
11279
+ const counters = {
11280
+ testRunner: yield* Ref.make(0),
11281
+ checker: yield* Ref.make(0)
11282
+ };
11283
+ return WorkerReports.of({ report: (role, startupMs) => Effect$1.gen(function* () {
11284
+ const index = yield* Ref.getAndUpdate(counters[role], (current) => current + 1);
11285
+ yield* Queue.offer(queue, RunEvent_exports.WorkerReported.make({
11286
+ schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
11287
+ role,
11288
+ index,
11289
+ startupMs
11290
+ }));
11291
+ }) });
11292
+ }));
11176
11293
  //#endregion
11177
11294
  //#region src/phase-durations.ts
11178
11295
  const elapsedOf = (marks, phase) => Option$1.map(Arr.findFirst(marks, (mark) => mark.phase === phase), (mark) => mark.elapsedMs);
@@ -11229,7 +11346,7 @@ const strykerOutputFilesOf = (options) => [
11229
11346
  ];
11230
11347
  //#endregion
11231
11348
  //#region package.json
11232
- var version = "15.0.1";
11349
+ var version = "16.0.1";
11233
11350
  //#endregion
11234
11351
  //#region src/stryker-package.schema.ts
11235
11352
  var StrykerPackage = class extends S.Class("StrykerPackage")({
@@ -11377,6 +11494,8 @@ var MutationReporting = class MutationReporting extends Context.Service()("@syst
11377
11494
  };
11378
11495
  return MutationReporting.of({
11379
11496
  reportCheckFailure: (mutant, result) => reportCheckFailure(mutant, result),
11497
+ reportIgnored: (mutant, result) => reportIgnored(mutant, result),
11498
+ reportNoCoverage: (mutant) => reportNoCoverage(mutant),
11380
11499
  reportMutantRunResult: (mutant, result) => mapRunResult(mutant, result),
11381
11500
  reportAll: (input) => reportAll(deps, input),
11382
11501
  checkpoint: (input, plannedMutants) => checkpoint(deps, input, plannedMutants)
@@ -11399,6 +11518,8 @@ const reportMutant = (mutant, status, outcome = {}) => Effect$1.succeed({
11399
11518
  });
11400
11519
  const reportMutantStatus = (mutant, status, statusReason) => reportMutant(mutant, status, { statusReason: statusReason ?? mutant.statusReason });
11401
11520
  const reportCheckFailure = (mutant, result) => reportMutantStatus(mutant, "CompileError", result.reason);
11521
+ const reportIgnored = (mutant, result) => reportMutantStatus(mutant, "Ignored", result.reason);
11522
+ const reportNoCoverage = (mutant) => reportMutantStatus(mutant, "NoCoverage");
11402
11523
  const reasonedOutcomeOf = (reason) => Option$1.match(Option$1.fromNullishOr(reason), {
11403
11524
  onNone: () => ({}),
11404
11525
  onSome: (present) => ({ statusReason: present })
@@ -11622,6 +11743,11 @@ const mutationTestReport = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReport
11622
11743
  const { files, testFiles, identities } = yield* assembleReport(deps, input, results);
11623
11744
  const dependencies = yield* discoverDependencies(deps);
11624
11745
  const config = yield* S.encodeEffect(S.fromJsonString(S.Unknown))(input.options).pipe(Effect$1.flatMap(S.decodeEffect(S.fromJsonString(S.Record(S.String, S.Json)))), Effect$1.orDie);
11746
+ const budget = budgetOf$1({
11747
+ results,
11748
+ concurrency: input.concurrency,
11749
+ actualSeconds: ((yield* Clock.currentTimeMillis) - input.runStartedAt) / 1e3
11750
+ });
11625
11751
  return {
11626
11752
  report: {
11627
11753
  files,
@@ -11633,7 +11759,8 @@ const mutationTestReport = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReport
11633
11759
  framework: {
11634
11760
  ...STRYKER_FRAMEWORK,
11635
11761
  dependencies
11636
- }
11762
+ },
11763
+ budget
11637
11764
  },
11638
11765
  identities
11639
11766
  };
@@ -11674,27 +11801,49 @@ const emitVerdict = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingEmit
11674
11801
  mutants: envelope.mutants,
11675
11802
  scope: envelope.scope,
11676
11803
  mutantSetPolicy: envelope.mutantSetPolicy,
11804
+ incrementalMode: envelope.incrementalMode,
11677
11805
  phaseDurations: envelope.phaseDurations,
11678
- static: envelope.static
11806
+ static: envelope.static,
11807
+ budget: envelope.budget
11679
11808
  }));
11680
11809
  });
11681
11810
  const dryRunCoverageFieldOf = (testCoverage) => Option$1.match(Option$1.fromUndefinedOr(testCoverage.dryRunCoverage), {
11682
11811
  onNone: () => ({}),
11683
11812
  onSome: (dryRunCoverage) => ({ dryRunCoverage })
11684
11813
  });
11814
+ const testTimesOf = (tests) => Arr.map([...tests], (test) => test.timeSpentMs);
11815
+ const executedEntryOf = (result) => Option$1.match(Option$1.fromUndefinedOr(result.cost), {
11816
+ onNone: () => [],
11817
+ onSome: (cost) => [[result.id, costTotalMsOf(cost) ?? 0]]
11818
+ });
11819
+ const priorEntryOf = (entry) => entry[1].actualMs === null ? [] : [[entry[0], entry[1].actualMs]];
11820
+ const priorCostsOf = (report) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromNullishOr(report), (present) => Option$1.fromNullishOr(present.costs)), () => ({}));
11821
+ const mutantCostModelOf = (input, results) => ({
11822
+ subjects: Arr.map(results, (result) => ({
11823
+ id: result.id,
11824
+ static: result.static
11825
+ })),
11826
+ staticCoverage: input.testCoverage.staticCoverage,
11827
+ allTestTimesMs: Arr.map([...input.testCoverage.testsById], ([, test]) => test.timeSpentMs),
11828
+ coveringTestTimesMsByMutantId: Object.fromEntries([...input.testCoverage.testsByMutantId].map(([mutantId, tests]) => [mutantId, testTimesOf(tests)])),
11829
+ executedActualMsByMutantId: Object.fromEntries(Arr.flatMap(results, executedEntryOf)),
11830
+ priorActualMsByMutantId: Object.fromEntries(Arr.flatMap(Object.entries(priorCostsOf(input.project.incrementalReport)), priorEntryOf)),
11831
+ fixedOverheadMs: input.timeOverheadMs
11832
+ });
11833
+ const costsOf = (input, results) => mutantCostsOf(mutantCostModelOf(input, results));
11685
11834
  const writeIncrementalReport = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingWriteIncrementalReport.name)(function* (deps, input, report, identities) {
11686
- yield* deps.fs.makeDirectory(deps.path.dirname(input.options.incrementalFile), { recursive: true });
11687
11835
  const runInputsDigest = yield* runInputsDigestOf(deps.fs, deps.path, input.basePath, input.options);
11688
11836
  const json = yield* S.encodeEffect(S.fromJsonString(S.Unknown, { space: 2 }))({
11689
- incrementalVersion: "2",
11690
- verdictSemanticsVersion: 1,
11837
+ incrementalVersion: "3",
11838
+ verdictSemanticsVersion: 3,
11691
11839
  mutantSetPolicy: input.options.mutator.mutantSetPolicy,
11692
11840
  runInputsDigest,
11693
11841
  ...report,
11694
11842
  files: stampFileIdentities(stampClosureDigests(report.files, input.closureDigestsByMutantId), identities),
11843
+ costs: costsOf(input, input.results),
11695
11844
  ...dryRunCoverageFieldOf(input.testCoverage)
11696
11845
  }).pipe(Effect$1.orDie);
11697
- yield* deps.fs.writeFileString(input.options.incrementalFile, json);
11846
+ yield* writeFileAtomic(deps, input.options.incrementalFile, json);
11698
11847
  });
11699
11848
  const writeReproducers = (deps, input, report) => {
11700
11849
  const decision = Result.getOrThrow(buildReproducers(BuildReproducersCommand.make({ report })));
@@ -11731,24 +11880,24 @@ const reportAll = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingReport
11731
11880
  verdict: finalVerdict
11732
11881
  };
11733
11882
  });
11734
- const writeAtomic = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingWriteAtomic.name)(function* (deps, file, content) {
11735
- yield* deps.fs.makeDirectory(deps.path.dirname(file), { recursive: true });
11736
- const tmp = `${file}.tmp`;
11737
- yield* deps.fs.writeFileString(tmp, content);
11738
- yield* deps.fs.rename(tmp, file).pipe(Effect$1.catch(() => deps.fs.copyFile(tmp, file).pipe(Effect$1.andThen(deps.fs.remove(tmp)))));
11739
- });
11740
11883
  const slimIncrementalReport = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingSlimIncrementalReport.name)(function* (deps, input, results) {
11741
11884
  const { files, testFiles, identities } = yield* assembleReport(deps, input, results);
11742
11885
  const runInputsDigest = yield* runInputsDigestOf(deps.fs, deps.path, input.basePath, input.options);
11743
11886
  return {
11744
- incrementalVersion: "2",
11745
- verdictSemanticsVersion: 1,
11887
+ incrementalVersion: "3",
11888
+ verdictSemanticsVersion: 3,
11746
11889
  mutantSetPolicy: input.options.mutator.mutantSetPolicy,
11747
11890
  runInputsDigest,
11748
11891
  schemaVersion: Report_exports.WrittenSchemaVersion.literal,
11749
11892
  thresholds: input.options.thresholds,
11750
11893
  files: stampFileIdentities(stampClosureDigests(files, input.closureDigestsByMutantId), identities),
11894
+ costs: costsOf(input, results),
11751
11895
  testFiles,
11896
+ budget: budgetOf$1({
11897
+ results,
11898
+ concurrency: input.concurrency,
11899
+ actualSeconds: ((yield* Clock.currentTimeMillis) - input.runStartedAt) / 1e3
11900
+ }),
11752
11901
  ...dryRunCoverageFieldOf(input.testCoverage)
11753
11902
  };
11754
11903
  });
@@ -11782,7 +11931,7 @@ const checkpointResultsOf = (input, plannedMutants) => Result.match(checkpointMu
11782
11931
  const checkpointIncremental = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingCheckpoint.name)(function* (deps, input, plannedMutants) {
11783
11932
  const report = yield* slimIncrementalReport(deps, input, checkpointResultsOf(input, plannedMutants));
11784
11933
  const json = yield* S.encodeEffect(S.fromJsonString(S.Unknown))(report).pipe(Effect$1.orDie);
11785
- yield* writeAtomic(deps, input.options.incrementalFile, json);
11934
+ yield* writeFileAtomic(deps, input.options.incrementalFile, json);
11786
11935
  });
11787
11936
  const checkpoint = (deps, input, plannedMutants) => Boolean$1$1.match(input.options.incremental, {
11788
11937
  onTrue: () => checkpointIncremental(deps, input, plannedMutants),
@@ -12399,8 +12548,8 @@ const renderJsonReport = Workflow.make({
12399
12548
  });
12400
12549
  //#endregion
12401
12550
  //#region src/json-report.cell.ts
12402
- const reportOf$2 = Filter.make((event) => Match$1.value(event).pipe(Match$1.tag("mutationTestReportReady", (ready) => Result.succeed(ready.report)), Match$1.orElse(() => Result.fail("not-ready"))));
12403
- const readJsonReport = (input) => Effect$1.map(Stream.fromAsyncIterable(input.events, failAsJsonReporter).pipe(Stream.filterMap(reportOf$2), Stream.run(Sink.last())), (last) => ({
12551
+ const reportOf$1 = Filter.make((event) => Match$1.value(event).pipe(Match$1.tag("mutationTestReportReady", (ready) => Result.succeed(ready.report)), Match$1.orElse(() => Result.fail("not-ready"))));
12552
+ const readJsonReport = (input) => Effect$1.map(Stream.fromAsyncIterable(input.events, failAsJsonReporter).pipe(Stream.filterMap(reportOf$1), Stream.run(Sink.last())), (last) => ({
12404
12553
  _tag: "JsonReportCommand",
12405
12554
  reported: Option$1.getOrUndefined(last),
12406
12555
  rendered: true,
@@ -12834,7 +12983,7 @@ const logOf = (command, results) => {
12834
12983
  }]
12835
12984
  };
12836
12985
  };
12837
- const decide$20 = (command) => {
12986
+ const decide$21 = (command) => {
12838
12987
  const surfaced = surfacedOf(command);
12839
12988
  const results = Arr.take(surfaced, command.maxResults);
12840
12989
  const log = logOf(command, results);
@@ -12850,7 +12999,7 @@ const sarifReport = Workflow.make({
12850
12999
  command: SarifReportCommand,
12851
13000
  decision: SarifReportDecision,
12852
13001
  error: S.Never,
12853
- decide: (command) => Result.succeed(decide$20(command))
13002
+ decide: (command) => Result.succeed(decide$21(command))
12854
13003
  });
12855
13004
  //#endregion
12856
13005
  //#region src/cap-survivors.workflow.ts
@@ -12967,7 +13116,7 @@ const linesOf = (command) => {
12967
13116
  onFalse: () => Arr.flatMap(Option$1.toArray(HashMap.get(byId, ref.id)), (entry) => [lineOf(entry)])
12968
13117
  }));
12969
13118
  };
12970
- const decide$19 = (command) => {
13119
+ const decide$20 = (command) => {
12971
13120
  const lines = linesOf(command);
12972
13121
  return Boolean$1$1.match(Arr.length(lines) === 0, {
12973
13122
  onTrue: () => NothingToAnnotate.make({ survivorCount: Arr.length(command.survivors) }),
@@ -12978,7 +13127,7 @@ Workflow.make({
12978
13127
  command: RenderAnnotationsCommand,
12979
13128
  decision: RenderAnnotationsDecision,
12980
13129
  error: S.Never,
12981
- decide: (command) => Result.succeed(decide$19(command))
13130
+ decide: (command) => Result.succeed(decide$20(command))
12982
13131
  });
12983
13132
  //#endregion
12984
13133
  //#region src/surfacing.ts
@@ -13054,8 +13203,8 @@ const failAsSarif = (cause) => Reporter_exports.ReporterFailed.make({
13054
13203
  event: "mutationTestReportReady",
13055
13204
  cause: Option$1.getOrElse(Option$1.map(ErrorText_exports.errorTextOf(cause), (rendered) => rendered.text), () => "")
13056
13205
  });
13057
- const reportOf$1 = Filter.make((event) => Match$1.value(event).pipe(Match$1.tag("mutationTestReportReady", (ready) => Result.succeed(ready.report)), Match$1.orElse(() => Result.fail("not-ready"))));
13058
- const readSarifReport = (events) => Effect$1.map(Stream.fromAsyncIterable(events, failAsSarif).pipe(Stream.filterMap(reportOf$1), Stream.run(Sink.last())), (last) => Option$1.getOrUndefined(last));
13206
+ const reportOf = Filter.make((event) => Match$1.value(event).pipe(Match$1.tag("mutationTestReportReady", (ready) => Result.succeed(ready.report)), Match$1.orElse(() => Result.fail("not-ready"))));
13207
+ const readSarifReport = (events) => Effect$1.map(Stream.fromAsyncIterable(events, failAsSarif).pipe(Stream.filterMap(reportOf), Stream.run(Sink.last())), (last) => Option$1.getOrUndefined(last));
13059
13208
  const writeSarifReport = (options, report) => Effect$1.gen(function* () {
13060
13209
  const fs = yield* FileSystem.FileSystem;
13061
13210
  const path = yield* Path.Path;
@@ -13144,7 +13293,7 @@ var RunEnvironment = class extends Context.Service()("@systemfsoftware/stryker-j
13144
13293
  };
13145
13294
  const stageLayerOf = (env, events) => {
13146
13295
  const eventsLayer = Match$1.value(events).pipe(Match$1.when(void 0, () => Layer.effect(RunEvents, Queue.bounded(RunEvent_exports.RunEvent.QUEUE_BOUND))), Match$1.orElse((queue) => Layer.succeed(RunEvents, queue)));
13147
- const stageLayer = Layer.mergeAll(Layer.succeed(RunEnvironment, env), eventsLayer, PhaseClock.layer(env.runStartedAt), IdGenerator.layer, ProjectFiles.layer, Layer.effect(Scope.Scope, Effect$1.gen(function* () {
13296
+ const stageLayer = Layer.mergeAll(Layer.succeed(RunEnvironment, env), eventsLayer, WorkerReportsLive.pipe(Layer.provide(eventsLayer)), PhaseClock.layer(env.runStartedAt), IdGenerator.layer, ProjectFiles.layer, Layer.effect(Scope.Scope, Effect$1.gen(function* () {
13148
13297
  const stageScope = yield* Scope.make();
13149
13298
  yield* Effect$1.addFinalizer((exit) => Scope.close(stageScope, exit));
13150
13299
  return stageScope;
@@ -13512,7 +13661,7 @@ var WorkerCrashed = class extends S.TaggedClass()("WorkerCrashed", {
13512
13661
  };
13513
13662
  const ClassifyWorkerExitDecision = S.Union([WorkerOutOfMemory, WorkerCrashed]);
13514
13663
  const OutOfMemoryExitCode = S.Literals([134, 137]);
13515
- const decide$18 = (command) => Boolean$1$1.match(S.is(OutOfMemoryExitCode)(command.exitCode), {
13664
+ const decide$19 = (command) => Boolean$1$1.match(S.is(OutOfMemoryExitCode)(command.exitCode), {
13516
13665
  onTrue: () => Result.succeed(WorkerOutOfMemory.make({
13517
13666
  pid: command.pid,
13518
13667
  exitCode: command.exitCode
@@ -13526,7 +13675,7 @@ const classifyWorkerExit = Workflow.make({
13526
13675
  command: ClassifyWorkerExitCommand,
13527
13676
  decision: ClassifyWorkerExitDecision,
13528
13677
  error: S.Never,
13529
- decide: decide$18
13678
+ decide: decide$19
13530
13679
  });
13531
13680
  //#endregion
13532
13681
  //#region src/git-diff.schema.ts
@@ -13858,7 +14007,9 @@ const IncrementalReportSchema = S.StructWithRest(S.Struct({
13858
14007
  runInputsDigest: S.String,
13859
14008
  thresholds: ThresholdsLikeSchema,
13860
14009
  files: S.Record(S.String, FileResultLikeSchema),
14010
+ costs: MutantCosts,
13861
14011
  testFiles: S.optional(S.Record(S.String, TestFileLikeSchema)),
14012
+ budget: S.optional(RunEvent_exports.Budget),
13862
14013
  dryRunCoverage: S.optionalKey(S.Unknown)
13863
14014
  }), [S.Record(S.String, S.Unknown)]);
13864
14015
  //#endregion
@@ -13936,12 +14087,12 @@ const splitOf = (details, checkerCount) => Boolean$1$1.match(checkerCount > 0, {
13936
14087
  announcement: details.announcement
13937
14088
  })
13938
14089
  });
13939
- const decide$17 = (command) => Result.succeed(splitOf(detailsOf(command.concurrency, command.availableParallelism), command.checkerCount));
14090
+ const decide$18 = (command) => Result.succeed(splitOf(detailsOf(command.concurrency, command.availableParallelism), command.checkerCount));
13940
14091
  const resolveConcurrency = Workflow.make({
13941
14092
  command: ResolveConcurrency,
13942
14093
  decision: ConcurrencySplit,
13943
14094
  error: S.Never,
13944
- decide: decide$17
14095
+ decide: decide$18
13945
14096
  });
13946
14097
  //#endregion
13947
14098
  //#region src/concurrency.cell.ts
@@ -14173,7 +14324,7 @@ const discardOf = (reason, actual, expected) => IncrementalReportDiscard.make({
14173
14324
  actual,
14174
14325
  expected
14175
14326
  });
14176
- const decide$16 = (command) => Option$1.match(Option$1.fromUndefinedOr(command.report), {
14327
+ const decide$17 = (command) => Option$1.match(Option$1.fromUndefinedOr(command.report), {
14177
14328
  onNone: () => discardOf("noPriorRecord", void 0, command.expectedIncrementalVersion),
14178
14329
  onSome: (report) => Option$1.match(Option$1.firstSomeOf(identityMismatchesOf(command, report).map(mismatchOptionOf)), {
14179
14330
  onNone: () => IncrementalReportKeep.make({ report }),
@@ -14184,7 +14335,7 @@ const admitIncrementalReport = Workflow.make({
14184
14335
  command: AdmitIncrementalReportCommand,
14185
14336
  decision: S.Union([IncrementalReportKeep, IncrementalReportDiscard]),
14186
14337
  error: S.Never,
14187
- decide: (command) => Result.succeed(decide$16(command))
14338
+ decide: (command) => Result.succeed(decide$17(command))
14188
14339
  });
14189
14340
  //#endregion
14190
14341
  //#region src/git-diff.workflow.ts
@@ -14539,11 +14690,15 @@ const ALWAYS_IGNORE = Object.freeze([
14539
14690
  "/stryker.log",
14540
14691
  ".next",
14541
14692
  ".nuxt",
14542
- ".svelte-kit"
14693
+ ".svelte-kit",
14694
+ ".turbo"
14543
14695
  ]);
14544
14696
  const CRAWL_CONCURRENCY = 256;
14545
14697
  const DISCARD_TEXTS = {
14546
- noPriorRecord: (command) => `Unable to parse incremental result file at ${command.incrementalFile}; a full mutation testing run will be performed.`,
14698
+ noPriorRecord: (command) => `Unable to parse incremental result file at ${command.incrementalFile}${Option$1.match(Option$1.fromUndefinedOr(command.decodeError), {
14699
+ onNone: () => "",
14700
+ onSome: (error) => `: ${error}`
14701
+ })}; a full mutation testing run will be performed.`,
14547
14702
  cacheLayoutChanged: (command, discard) => `Incremental result file at ${command.incrementalFile} has cache layout version ${discard.actual ?? ""}, expected ${discard.expected}; a full mutation testing run will be performed.`,
14548
14703
  semanticsChanged: (command, discard) => `Incremental result file at ${command.incrementalFile} has verdict semantics version ${discard.actual ?? ""}, expected ${discard.expected}; a full mutation testing run will be performed.`,
14549
14704
  policyChanged: (command, discard) => `Incremental result file at ${command.incrementalFile} has mutant-set policy ${discard.actual ?? ""}, expected ${discard.expected}; a full mutation testing run will be performed.`,
@@ -14601,7 +14756,14 @@ const isEntryIncluded = (ignorePatterns, entry) => {
14601
14756
  };
14602
14757
  const resolveInputFileNames = (ignorePatterns, basePath) => crawlDirectory(ignorePatterns, basePath, basePath).pipe(Stream.runCollect, Effect$1.map((files) => [...files]));
14603
14758
  const parseAndDecodeIncrementalReport = Schema.decodeUnknownResult(Schema.fromJsonString(IncrementalReportSchema));
14604
- const reportOf = (contents) => Option$1.getOrUndefined(Option$1.flatMap(Option$1.fromUndefinedOr(contents), (text) => Result.getSuccess(parseAndDecodeIncrementalReport(text))));
14759
+ const decodeFailureText = (failure) => failure.message.replace(/\s+/gu, " ").trim();
14760
+ const reportReadOf = (contents) => Option$1.match(Option$1.fromUndefinedOr(contents), {
14761
+ onNone: () => ({}),
14762
+ onSome: (text) => Result.match(parseAndDecodeIncrementalReport(text), {
14763
+ onFailure: (failure) => ({ decodeError: decodeFailureText(failure) }),
14764
+ onSuccess: (report) => ({ report })
14765
+ })
14766
+ });
14605
14767
  const incrementalContentsOf = (fs, options) => Boolean$1.match(options.incremental, {
14606
14768
  onFalse: () => Effect$1.succeedNone,
14607
14769
  onTrue: () => fs.readFileString(options.incrementalFile).pipe(Effect$1.asSome, Effect$1.tapError((error) => Match$1.value(error.reason).pipe(Match$1.tag("NotFound", () => Effect$1.logInfo(`No incremental result file found at ${options.incrementalFile}, a full mutation testing run will be performed.`)), Match$1.orElse(() => Effect$1.void))), Effect$1.catchTag("PlatformError", (error) => Match$1.value(error.reason).pipe(Match$1.tag("NotFound", () => Effect$1.succeedNone), Match$1.orElse(() => Effect$1.fail(error)))))
@@ -14706,11 +14868,12 @@ const readProject = Effect$1.fn(SpanTaxonomy_exports.Spans.projectReadFromDisk.n
14706
14868
  yield* Effect$1.forEach(testFilePatterns, (pattern) => warnUnmatchedTestPattern(inputFileNames, input.basePath, pattern), { discard: true });
14707
14869
  const runInputsDigest = yield* runInputsDigestOf(fs, pathService, input.basePath, options);
14708
14870
  const contents = Option$1.getOrUndefined(yield* incrementalContentsOf(fs, options));
14871
+ const reportRead = reportReadOf(contents);
14709
14872
  return {
14710
14873
  _tag: "AdmitIncrementalReportCommand",
14711
- report: reportOf(contents),
14712
- expectedIncrementalVersion: "2",
14713
- verdictSemanticsVersion: 1,
14874
+ report: reportRead.report,
14875
+ expectedIncrementalVersion: "3",
14876
+ verdictSemanticsVersion: 3,
14714
14877
  mutantSetPolicy: options.mutator.mutantSetPolicy,
14715
14878
  runInputsDigest,
14716
14879
  options,
@@ -14719,6 +14882,7 @@ const readProject = Effect$1.fn(SpanTaxonomy_exports.Spans.projectReadFromDisk.n
14719
14882
  incremental: options.incremental,
14720
14883
  incrementalFile: options.incrementalFile,
14721
14884
  contents,
14885
+ decodeError: reportRead.decodeError,
14722
14886
  fileDescriptions: decision.fileDescriptions,
14723
14887
  testFiles: [...decision.testFiles]
14724
14888
  };
@@ -14771,6 +14935,58 @@ const readProjectCell = Sandwich.named(SpanTaxonomy_exports.Spans.projectRead.na
14771
14935
  }))
14772
14936
  });
14773
14937
  //#endregion
14938
+ //#region src/refuse-local-mutation.workflow.ts
14939
+ const LocalMutationDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js/LocalMutationDecision");
14940
+ var RefuseLocalMutationCommand = class extends S.TaggedClass()("RefuseLocalMutationCommand", {
14941
+ dryRunOnly: S.Boolean,
14942
+ githubActions: S.UndefinedOr(S.String),
14943
+ allowLocalMutation: S.UndefinedOr(S.String)
14944
+ }) {
14945
+ static [Workflow.InstrumentationBrand] = {};
14946
+ };
14947
+ var LocalMutationAllowed = class extends S.TaggedClass()("LocalMutationAllowed", {}) {
14948
+ [LocalMutationDecisionTypeId] = LocalMutationDecisionTypeId;
14949
+ };
14950
+ var LocalMutationRefused = class extends S.TaggedError()("LocalMutationRefused", {
14951
+ rule: RunEvent_exports.RefusalRule,
14952
+ message: S.String
14953
+ }) {};
14954
+ const decide$16 = (command) => Match$1.value({
14955
+ dryRunOnly: command.dryRunOnly,
14956
+ githubActions: command.githubActions,
14957
+ allowLocalMutation: command.allowLocalMutation
14958
+ }).pipe(Match$1.when({ dryRunOnly: true }, () => Result.succeed(LocalMutationAllowed.make({}))), Match$1.when({ githubActions: "true" }, () => Result.succeed(LocalMutationAllowed.make({}))), Match$1.when({ allowLocalMutation: "1" }, () => Result.succeed(LocalMutationAllowed.make({}))), Match$1.orElse(() => Result.fail(LocalMutationRefused.make({
14959
+ rule: "mutation-runs-on-main-ci",
14960
+ message: "mutation runs only on main CI"
14961
+ }))));
14962
+ const refuseLocalMutation = Workflow.make({
14963
+ command: RefuseLocalMutationCommand,
14964
+ decision: LocalMutationAllowed,
14965
+ error: LocalMutationRefused,
14966
+ decide: decide$16
14967
+ });
14968
+ //#endregion
14969
+ //#region src/refuse-local-mutation.cell.ts
14970
+ const envOf = (key) => Config.option(Config.String(key)).pipe(Effect$1.map(Option$1.getOrUndefined), Effect$1.orElseSucceed(() => void 0));
14971
+ const guardOf = (input) => Effect$1.gen(function* () {
14972
+ const githubActions = yield* envOf("GITHUB_ACTIONS");
14973
+ const allowLocalMutation = yield* envOf("ALLOW_LOCAL_MUTATION");
14974
+ const command = RefuseLocalMutationCommand.make({
14975
+ dryRunOnly: input.options.dryRunOnly,
14976
+ githubActions,
14977
+ allowLocalMutation
14978
+ });
14979
+ return yield* Result.match(refuseLocalMutation(command), {
14980
+ onFailure: (refused) => Effect$1.fail(StageError.make({
14981
+ stage: "prepare",
14982
+ reason: refused.message,
14983
+ cause: refused
14984
+ })),
14985
+ onSuccess: () => Effect$1.succeed(input)
14986
+ });
14987
+ });
14988
+ const refuseLocalMutationCell = (input) => Cell.fromEffect(guardOf(input));
14989
+ //#endregion
14774
14990
  //#region src/dry-run-coverage.schema.ts
14775
14991
  const DryRunCoverageSchema = S.Struct({
14776
14992
  tests: S.Array(TestRunner_exports.TestResultSchema),
@@ -15954,6 +16170,35 @@ const buildTestRunner = dual(2, (context, childProcessRunner) => Option$1.match(
15954
16170
  onNone: () => decorateChildRunner(context, childProcessRunner)
15955
16171
  }));
15956
16172
  //#endregion
16173
+ //#region src/run/dry-run-coverage.ts
16174
+ const testsByIdOf = (result) => MutableHashMap.fromIterable(result.tests.map((test) => [test.id, test]));
16175
+ const coveredMutantIdsOf = (coverage) => Object.entries(coverage).filter(([, count]) => count > 0).map(([mutantId]) => mutantId);
16176
+ const testsByMutantIdOf = (mutantCoverage, testsById) => Object.entries(mutantCoverage.perTest).reduce((testsByMutantId, [testId, coverage]) => Option$1.match(MutableHashMap.get(testsById, testId), {
16177
+ onNone: () => testsByMutantId,
16178
+ onSome: (test) => coveredMutantIdsOf(coverage).reduce((acc, mutantId) => MutableHashMap.set(acc, mutantId, MutableHashSet.add(Option$1.getOrElse(MutableHashMap.get(acc, mutantId), () => MutableHashSet.empty()), test)), testsByMutantId)
16179
+ }), MutableHashMap.empty());
16180
+ const hitsByMutantIdOf = (mutantCoverage) => [mutantCoverage.static, ...Object.values(mutantCoverage.perTest)].reduce((hitsByMutantId, coverage) => Object.entries(coverage).reduce((acc, [mutantId, count]) => MutableHashMap.set(acc, mutantId, Option$1.getOrElse(MutableHashMap.get(acc, mutantId), () => 0) + count), hitsByMutantId), MutableHashMap.empty());
16181
+ const testCoverageOf = ({ result, dryRunCoverage }) => {
16182
+ const testsById = testsByIdOf(result);
16183
+ const mutantCoverage = Option$1.fromNullishOr(result.mutantCoverage);
16184
+ return {
16185
+ testsByMutantId: Option$1.match(mutantCoverage, {
16186
+ onNone: () => MutableHashMap.empty(),
16187
+ onSome: (coverage) => testsByMutantIdOf(coverage, testsById)
16188
+ }),
16189
+ testsById,
16190
+ staticCoverage: Option$1.match(mutantCoverage, {
16191
+ onNone: () => void 0,
16192
+ onSome: (coverage) => coverage.static
16193
+ }),
16194
+ hitsByMutantId: Option$1.match(mutantCoverage, {
16195
+ onNone: () => MutableHashMap.empty(),
16196
+ onSome: (coverage) => hitsByMutantIdOf(coverage)
16197
+ }),
16198
+ dryRunCoverage
16199
+ };
16200
+ };
16201
+ //#endregion
15957
16202
  //#region src/run/incremental-reuse.ts
15958
16203
  const absoluteSourceOf = (path, basePath, file) => path.isAbsolute(file) ? file : path.join(basePath, file);
15959
16204
  const matchedSourcesOf = Effect$1.fnUntraced(function* (input) {
@@ -16283,33 +16528,6 @@ const withOriginalFileName = (test, prev) => Match$1.value(test.fileName).pipe(M
16283
16528
  fileName: originalFileFor(prev.sandbox, fileName)
16284
16529
  })), Match$1.orElse(() => test));
16285
16530
  const withOriginalFileNames = (tests, prev) => tests.map((test) => withOriginalFileName(test, prev));
16286
- const testsByIdOf = (result) => MutableHashMap.fromIterable(result.tests.map((test) => [test.id, test]));
16287
- const coveredMutantIdsOf = (coverage) => Object.entries(coverage).filter(([, count]) => count > 0).map(([mutantId]) => mutantId);
16288
- const testsByMutantIdOf = (mutantCoverage, testsById) => Object.entries(mutantCoverage.perTest).reduce((testsByMutantId, [testId, coverage]) => Option$1.match(MutableHashMap.get(testsById, testId), {
16289
- onNone: () => testsByMutantId,
16290
- onSome: (test) => coveredMutantIdsOf(coverage).reduce((acc, mutantId) => MutableHashMap.set(acc, mutantId, MutableHashSet.add(Option$1.getOrElse(MutableHashMap.get(acc, mutantId), () => MutableHashSet.empty()), test)), testsByMutantId)
16291
- }), MutableHashMap.empty());
16292
- const hitsByMutantIdOf = (mutantCoverage) => [mutantCoverage.static, ...Object.values(mutantCoverage.perTest)].reduce((hitsByMutantId, coverage) => Object.entries(coverage).reduce((acc, [mutantId, count]) => MutableHashMap.set(acc, mutantId, Option$1.getOrElse(MutableHashMap.get(acc, mutantId), () => 0) + count), hitsByMutantId), MutableHashMap.empty());
16293
- const testCoverageFrom = (result, dryRunCoverage) => {
16294
- const testsById = testsByIdOf(result);
16295
- const mutantCoverage = Option$1.fromNullishOr(result.mutantCoverage);
16296
- return {
16297
- testsByMutantId: Option$1.match(mutantCoverage, {
16298
- onNone: () => MutableHashMap.empty(),
16299
- onSome: (coverage) => testsByMutantIdOf(coverage, testsById)
16300
- }),
16301
- testsById,
16302
- staticCoverage: Option$1.match(mutantCoverage, {
16303
- onNone: () => void 0,
16304
- onSome: (coverage) => coverage.static
16305
- }),
16306
- hitsByMutantId: Option$1.match(mutantCoverage, {
16307
- onNone: () => MutableHashMap.empty(),
16308
- onSome: (coverage) => hitsByMutantIdOf(coverage)
16309
- }),
16310
- dryRunCoverage
16311
- };
16312
- };
16313
16531
  const announceDryRunOutcome = (tests, prev, gross, overheadMillis) => Match$1.value(tests.length).pipe(Match$1.when(0, () => Effect$1.logInfo("No tests were found")), Match$1.orElse(() => Effect$1.logInfo(`Initial test run succeeded. Ran ${tests.length} tests in ${Duration.format(gross)} (net ${totalTestTime(tests)} ms, overhead ${overheadMillis} ms).`).pipe(Effect$1.andThen(Effect$1.when(Effect$1.logInfo("Note: running the dry-run only. No mutations will be tested."), Effect$1.succeed(prev.options.dryRunOnly))))));
16314
16532
  const reportDryRunCompleted = (raw, tests, overheadMillis) => offerReporterEvent(raw.prev.reporterStage, Reporter_exports.DryRunCompleted.make({
16315
16533
  timing: {
@@ -16333,7 +16551,10 @@ const freshCoverageOf = (raw, dryRunResult, overheadMillis) => ({
16333
16551
  const dryRunDoneOf = (raw, dryRunResult, coverage) => ({
16334
16552
  ...raw.prev,
16335
16553
  dryRunResult,
16336
- testCoverage: testCoverageFrom(dryRunResult, coverage),
16554
+ testCoverage: testCoverageOf({
16555
+ result: dryRunResult,
16556
+ dryRunCoverage: coverage
16557
+ }),
16337
16558
  timeOverhead: Duration.millis(coverage.timeOverheadMs)
16338
16559
  });
16339
16560
  const completeFreshDryRun = Effect$1.fnUntraced(function* (raw, dryRunResult) {
@@ -16363,6 +16584,7 @@ const completeDryRunPassed = (raw) => Match$1.value(raw.rawResult).pipe(Match$1.
16363
16584
  }))), Match$1.exhaustive);
16364
16585
  const runFreshDryRun = Effect$1.fnUntraced(function* (command, idGenerator, currentTestClosureDigest, runInputsDigest) {
16365
16586
  const env = yield* RunEnvironment;
16587
+ const reports = yield* WorkerReports;
16366
16588
  const { files, testFiles } = yield* resolveDryRunFiles(command);
16367
16589
  const dryRunTimeout = command.options.dryRunTimeoutMinutes * 60 * 1e3;
16368
16590
  const options = dryRunOptionsOf(command, files, testFiles, dryRunTimeout);
@@ -16378,6 +16600,7 @@ const runFreshDryRun = Effect$1.fnUntraced(function* (command, idGenerator, curr
16378
16600
  idGenerator
16379
16601
  })));
16380
16602
  });
16603
+ const startedAt = yield* Clock.currentTimeMillis;
16381
16604
  const runner = yield* buildTestRunner({
16382
16605
  options: command.options,
16383
16606
  fileDescriptions: command.project.fileDescriptions,
@@ -16386,6 +16609,7 @@ const runFreshDryRun = Effect$1.fnUntraced(function* (command, idGenerator, curr
16386
16609
  retire: Effect$1.void,
16387
16610
  testFiles: testFiles ?? []
16388
16611
  }, childRunnerEffect);
16612
+ yield* reports.report("testRunner", (yield* Clock.currentTimeMillis) - startedAt);
16389
16613
  const capabilities = yield* runner.capabilities.pipe(Effect$1.mapError((cause) => StageError.make({
16390
16614
  stage: "dryRun",
16391
16615
  reason: "Failed to get test runner capabilities",
@@ -17337,6 +17561,10 @@ const FailedCheckResultSchema = S.Struct({
17337
17561
  status: S.Literal("compileError"),
17338
17562
  reason: S.String
17339
17563
  });
17564
+ const IgnoredCheckResultSchema = S.Struct({
17565
+ status: S.Literal("ignored"),
17566
+ reason: S.String
17567
+ });
17340
17568
  var CheckedPlanPassed = class extends S.TaggedClass()("CheckedPlanPassed", {
17341
17569
  mutantId: Mutant_exports.MutantId,
17342
17570
  entryIndex: S.Int
@@ -17351,27 +17579,48 @@ var CheckedPlanFailed = class extends S.TaggedClass()("CheckedPlanFailed", {
17351
17579
  }) {
17352
17580
  [CheckedPlanTypeId] = CheckedPlanTypeId;
17353
17581
  };
17582
+ var CheckedPlanIgnored = class extends S.TaggedClass()("CheckedPlanIgnored", {
17583
+ mutantId: Mutant_exports.MutantId,
17584
+ entryIndex: S.Int,
17585
+ reason: S.String,
17586
+ result: IgnoredCheckResultSchema
17587
+ }) {
17588
+ [CheckedPlanTypeId] = CheckedPlanTypeId;
17589
+ };
17354
17590
  var PartitionCheckedPlansCommand = class extends S.TaggedClass()("PartitionCheckedPlansCommand", { checked: S.Array(S.Tuple([Mutant_exports.MutantId, Checker_exports.CheckResultSchema])) }) {
17355
17591
  static [Workflow.InstrumentationBrand] = {};
17356
17592
  };
17357
- const isFailed = (result) => result.status !== "passed";
17593
+ const isFailed = (result) => result.status === "compileError";
17594
+ const isIgnored = (result) => result.status === "ignored";
17358
17595
  const failedReasonOf = (failed) => failed.reason;
17359
- const decideCheckedPlan = (mutantId, result, entryIndex) => Option$1.match(Option$1.liftPredicate(result, isFailed), {
17360
- onNone: () => CheckedPlanPassed.make({
17361
- mutantId,
17362
- entryIndex
17363
- }),
17364
- onSome: (failed) => CheckedPlanFailed.make({
17596
+ const decideCheckedPlan = (mutantId, result, entryIndex) => Option$1.match(Option$1.liftPredicate(result, isIgnored), {
17597
+ onSome: (ignored) => CheckedPlanIgnored.make({
17365
17598
  mutantId,
17366
17599
  entryIndex,
17367
- reason: failedReasonOf(failed),
17368
- result: failed
17600
+ reason: ignored.reason,
17601
+ result: ignored
17602
+ }),
17603
+ onNone: () => Option$1.match(Option$1.liftPredicate(result, isFailed), {
17604
+ onNone: () => CheckedPlanPassed.make({
17605
+ mutantId,
17606
+ entryIndex
17607
+ }),
17608
+ onSome: (failed) => CheckedPlanFailed.make({
17609
+ mutantId,
17610
+ entryIndex,
17611
+ reason: failedReasonOf(failed),
17612
+ result: failed
17613
+ })
17369
17614
  })
17370
17615
  });
17371
17616
  const decide$8 = (command) => Result.succeed(command.checked.map(([mutantId, result], entryIndex) => decideCheckedPlan(mutantId, result, entryIndex)));
17372
17617
  const partitionCheckedPlans = Workflow.make({
17373
17618
  command: PartitionCheckedPlansCommand,
17374
- decision: S.Array(S.Union([CheckedPlanPassed, CheckedPlanFailed])),
17619
+ decision: S.Array(S.Union([
17620
+ CheckedPlanPassed,
17621
+ CheckedPlanFailed,
17622
+ CheckedPlanIgnored
17623
+ ])),
17375
17624
  error: S.Never,
17376
17625
  decide: decide$8
17377
17626
  });
@@ -17408,6 +17657,7 @@ const onCheckerSlot = (pool, checkerIndex, run) => Pool.use(pool, (slot) => Opti
17408
17657
  }));
17409
17658
  const noPassedPlans = [];
17410
17659
  const noFailedChecks = [];
17660
+ const noIgnoredChecks = [];
17411
17661
  const splitCheckedPlans = Effect$1.fn(SpanTaxonomy_exports.Spans.checkerPoolSplitChecked.name)(function* (checked) {
17412
17662
  const decisions = yield* Effect$1.fromResult(partitionCheckedPlans(PartitionCheckedPlansCommand.make({ checked: checked.map(([plan, result]) => [plan.mutant.id, result]) })));
17413
17663
  return {
@@ -17418,6 +17668,10 @@ const splitCheckedPlans = Effect$1.fn(SpanTaxonomy_exports.Spans.checkerPoolSpli
17418
17668
  failedChecks: decisions.flatMap((decision) => Option$1.match(Option$1.liftPredicate(decision, S.is(CheckedPlanFailed)), {
17419
17669
  onNone: () => [],
17420
17670
  onSome: (failed) => Option$1.toArray(Option$1.map(Arr.get(checked, failed.entryIndex), ([plan]) => [plan, failed.result]))
17671
+ })),
17672
+ ignoredChecks: decisions.flatMap((decision) => Option$1.match(Option$1.liftPredicate(decision, S.is(CheckedPlanIgnored)), {
17673
+ onNone: () => [],
17674
+ onSome: (ignored) => Option$1.toArray(Option$1.map(Arr.get(checked, ignored.entryIndex), ([plan]) => [plan, ignored.result]))
17421
17675
  }))
17422
17676
  };
17423
17677
  });
@@ -17426,23 +17680,34 @@ const failedElement = (failedChecks) => Option$1.match(Arr.head(failedChecks), {
17426
17680
  onNone: () => Stream.empty,
17427
17681
  onSome: () => Stream.succeed({
17428
17682
  passedPlans: noPassedPlans,
17429
- failedChecks
17683
+ failedChecks,
17684
+ ignoredChecks: noIgnoredChecks
17685
+ })
17686
+ });
17687
+ const ignoredElement = (ignoredChecks) => Option$1.match(Arr.head(ignoredChecks), {
17688
+ onNone: () => Stream.empty,
17689
+ onSome: () => Stream.succeed({
17690
+ passedPlans: noPassedPlans,
17691
+ failedChecks: noFailedChecks,
17692
+ ignoredChecks
17430
17693
  })
17431
17694
  });
17432
17695
  const checkedGroupsFor = (pool, checkerNames, checkerIndex, plans) => Option$1.match(Arr.get(checkerNames, checkerIndex), {
17433
17696
  onNone: () => Stream.succeed({
17434
17697
  passedPlans: plans,
17435
- failedChecks: noFailedChecks
17698
+ failedChecks: noFailedChecks,
17699
+ ignoredChecks: noIgnoredChecks
17436
17700
  }),
17437
17701
  onSome: (checkerName) => Stream.unwrap(onCheckerSlot(pool, checkerIndex, (checker) => groupPlans(checker, checkerName, plans)).pipe(Effect$1.map((groups) => Stream.fromIterable(groups).pipe(Stream.mapEffect((group) => onCheckerSlot(pool, checkerIndex, (checker) => checkPlans$1(checker, checkerName, group)).pipe(Effect$1.flatMap((checked) => splitCheckedPlans(checked))), {
17438
17702
  concurrency: "unbounded",
17439
17703
  unordered: true
17440
- }), Stream.flatMap((split) => Stream.concat(failedElement(split.failedChecks), checkedGroupsFor(pool, checkerNames, checkerIndex + 1, split.passedPlans)))))))
17704
+ }), Stream.flatMap((split) => Stream.concat(ignoredElement(split.ignoredChecks), Stream.concat(failedElement(split.failedChecks), checkedGroupsFor(pool, checkerNames, checkerIndex + 1, split.passedPlans))))))))
17441
17705
  });
17442
17706
  const checkPlansStream = dual(2, (checkerPool, plans) => Option$1.fromNullishOr(checkerPool).pipe(Option$1.match({
17443
17707
  onNone: () => Stream.succeed({
17444
17708
  passedPlans: plans,
17445
- failedChecks: noFailedChecks
17709
+ failedChecks: noFailedChecks,
17710
+ ignoredChecks: noIgnoredChecks
17446
17711
  }),
17447
17712
  onSome: (handle) => checkerNamesOf(CheckerPoolHandle.slot(handle)).pipe(Effect$1.map((checkerNames) => checkedGroupsFor(CheckerPoolHandle.slot(handle), checkerNames, 0, plans)), Stream.unwrap)
17448
17713
  }), Stream.withSpan(SpanTaxonomy_exports.Spans.checkerPoolCheckPlans.name)));
@@ -17450,16 +17715,23 @@ Effect$1.fnUntraced(function* (checkerPool, plans) {
17450
17715
  const checkedGroups = yield* Stream.runCollect(checkPlansStream(checkerPool, plans));
17451
17716
  const passedPlans = [];
17452
17717
  const failedChecks = [];
17718
+ const ignoredChecks = [];
17453
17719
  for (const group of checkedGroups) {
17454
17720
  passedPlans.push(...group.passedPlans);
17455
17721
  failedChecks.push(...group.failedChecks);
17722
+ ignoredChecks.push(...group.ignoredChecks);
17456
17723
  }
17457
17724
  return {
17458
17725
  passedPlans,
17459
- failedChecks
17726
+ failedChecks,
17727
+ ignoredChecks
17460
17728
  };
17461
17729
  });
17462
- const runCheckedPlans = dual(2, (self, execution) => self.pipe(Stream.flatMap(({ passedPlans, failedChecks }) => Stream.fromIterable([...failedChecks.map(([plan, result]) => () => execution.settleFailure(plan, result)), ...passedPlans.map((plan) => () => execution.runPlan(plan))])), Stream.mapEffect((work) => work(), {
17730
+ const runCheckedPlans = dual(2, (self, execution) => self.pipe(Stream.flatMap(({ passedPlans, failedChecks, ignoredChecks }) => Stream.fromIterable([
17731
+ ...failedChecks.map(([plan, result]) => () => execution.settleFailure(plan, result)),
17732
+ ...ignoredChecks.map(([plan, result]) => () => execution.settleIgnored(plan, result)),
17733
+ ...passedPlans.map((plan) => () => execution.runPlan(plan))
17734
+ ])), Stream.mapEffect((work) => work(), {
17463
17735
  concurrency: Math.max(1, execution.concurrency),
17464
17736
  unordered: true
17465
17737
  })));
@@ -17514,11 +17786,13 @@ const checkerWorkerSpawnOf = (loaded, configured) => Effect$1.mapError(Effect$1.
17514
17786
  cause: PluginNotFoundError.make({ descriptor: missing.descriptor })
17515
17787
  }));
17516
17788
  const acquire$1 = Effect$1.fnUntraced(function* (spec) {
17789
+ const reports = yield* WorkerReports;
17517
17790
  return yield* Boolean$1$1.match(spec.options.checkers.length === 0, {
17518
17791
  onTrue: () => Effect$1.as(Effect$1.void, void 0),
17519
17792
  onFalse: () => Pool.make({
17520
17793
  acquire: Effect$1.forEach(spec.options.checkers, (checker) => Effect$1.gen(function* () {
17521
17794
  const resolved = yield* checkerWorkerSpawnOf(spec.loadedPlugins, ConfiguredPluginModulePath.make({ modulePath: checker.plugin }));
17795
+ const startedAt = yield* Clock.currentTimeMillis;
17522
17796
  const service = yield* scoped$2({
17523
17797
  options: {
17524
17798
  ...spec.options,
@@ -17530,6 +17804,7 @@ const acquire$1 = Effect$1.fnUntraced(function* (spec) {
17530
17804
  times: CHECKER_ACQUIRE_RETRIES,
17531
17805
  while: isCheckerCrash
17532
17806
  }));
17807
+ yield* reports.report("checker", (yield* Clock.currentTimeMillis) - startedAt);
17533
17808
  return {
17534
17809
  checkerName: resolved.name,
17535
17810
  checker: service
@@ -17887,7 +18162,7 @@ const readIncrementalReuseCommand = Effect$1.fn(SpanTaxonomy_exports.Spans.incre
17887
18162
  previousRecords: [...previousRecords],
17888
18163
  closureDigestsByMutantId: closures.digests,
17889
18164
  closureAnalysisFailed: closures.failed,
17890
- verdictSemanticsVersion: 1,
18165
+ verdictSemanticsVersion: 3,
17891
18166
  mutantSetPolicy: input.options.mutator.mutantSetPolicy,
17892
18167
  runInputsDigest,
17893
18168
  force: input.force,
@@ -18070,6 +18345,7 @@ const toReportedMutant = (mutant) => Object.assign(mutant, {
18070
18345
  coveredBy: mutant.coveredBy,
18071
18346
  static: mutant.static
18072
18347
  });
18348
+ const isNoCoveragePlan = (plan) => plan.runOptions.testFilter !== void 0 && plan.runOptions.testFilter.length === 0;
18073
18349
  const inPlannedOrder = ({ planned, results }) => {
18074
18350
  const rankById = Object.fromEntries(planned.map((mutant, index) => [mutant.id, index]));
18075
18351
  return Arr.sortWith(results, (result) => rankById[result.id] ?? planned.length, Order.Number);
@@ -18107,11 +18383,14 @@ const reportingInputOf = (input) => ({
18107
18383
  options: input.prev.options,
18108
18384
  project: input.prev.project,
18109
18385
  testCoverage: input.prev.testCoverage,
18386
+ timeOverheadMs: Duration.toMillis(input.prev.timeOverhead),
18110
18387
  runId: input.env.runId,
18111
18388
  resolvedMode: input.env.resolvedMode,
18112
18389
  basePath: input.env.basePath,
18113
18390
  reporterStage: input.prev.reporterStage,
18114
- formatRegistry: input.prev.formatRegistry
18391
+ formatRegistry: input.prev.formatRegistry,
18392
+ concurrency: input.prev.concurrency.testRunners + input.prev.concurrency.checkers,
18393
+ runStartedAt: input.env.runStartedAt
18115
18394
  });
18116
18395
  const preparedStreamableOf = Effect$1.fnUntraced(function* (context, result) {
18117
18396
  return yield* Option$1.match(Option$1.filter(Option$1.some(result.status), isMutantStatus), {
@@ -18365,10 +18644,11 @@ const coveredByField = (coveredBy) => Option$1.match(Option$1.fromUndefinedOr(co
18365
18644
  onNone: () => ({}),
18366
18645
  onSome: (present) => ({ coveredBy: [...present] })
18367
18646
  });
18368
- const testFilterField = (testFilter) => Option$1.match(Option$1.filter(Option$1.fromUndefinedOr(testFilter), (filter) => filter.length > 0), {
18647
+ const testFilterField = (testFilter) => Option$1.match(Option$1.fromUndefinedOr(testFilter), {
18369
18648
  onNone: () => ({}),
18370
18649
  onSome: (present) => ({ testFilter: [...present] })
18371
18650
  });
18651
+ const nonEmptyTestFilterOf = (tests) => Option$1.getOrUndefined(Option$1.filter(Option$1.fromUndefinedOr(tests), (present) => present.length > 0));
18372
18652
  const staticCoverageCountOf = (staticCoverage, mutantId) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromNullishOr(staticCoverage), (countsByMutantId) => Record$1.get(countsByMutantId, mutantId)), () => 0);
18373
18653
  const hasCoverageForPlan = (staticCoverage) => Option$1.isSome(Option$1.fromUndefinedOr(staticCoverage));
18374
18654
  const mutantIsStatic = (command, mutantId) => staticCoverageCountOf(command.staticCoverage, mutantId) > 0;
@@ -18454,10 +18734,13 @@ const orderedTestsOf = (command, mutantId, tests) => {
18454
18734
  onSome: (present) => [present, ...rest]
18455
18735
  });
18456
18736
  };
18457
- const runWithCoveredTests = (mutant, command, isStatic, tests, coveredBy) => toRunPlan(mutant, command, calculateTotalTimeForIds(tests, command.testTimeById), orderedTestsOf(command, mutant.id, tests), isStatic, coveredBy);
18737
+ const runWithCoveredTests = (mutant, command, isStatic, tests, coveredBy) => toRunPlan(mutant, command, calculateTotalTimeForIds(tests, command.testTimeById), nonEmptyTestFilterOf(orderedTestsOf(command, mutant.id, tests)), isStatic, coveredBy);
18738
+ const NO_COVERAGE_NET_TIME_MS = 0;
18739
+ const NO_COVERAGE_TEST_FILTER = [];
18740
+ const toNoCoverageRunPlan = (mutant, command, isStatic, coveredBy) => toRunPlan(mutant, command, NO_COVERAGE_NET_TIME_MS, NO_COVERAGE_TEST_FILTER, isStatic, coveredBy);
18458
18741
  const planForUncoveredStatic = (mutant, command, isStatic, coveredBy) => Boolean$1$1.match(command.options.ignoreStatic, {
18459
18742
  onTrue: () => Result.succeed(toEarlyResultPlan(mutant, isStatic, "Ignored", IGNORED_STATIC_MUTANT_REASON, coveredBy)),
18460
- onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, command.globalTestFilter, isStatic, coveredBy)
18743
+ onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, nonEmptyTestFilterOf(command.globalTestFilter), isStatic, coveredBy)
18461
18744
  });
18462
18745
  const planForStaticallyCovered = (mutant, command, isStatic) => {
18463
18746
  const tests = Option$1.getOrElse(Record$1.get(command.testsByMutantId, mutant.id), () => []);
@@ -18486,10 +18769,10 @@ const decidePlanForMutant = (mutant, command) => {
18486
18769
  return Option$1.match(Option$1.fromUndefinedOr(mutant.status), {
18487
18770
  onSome: (status) => Result.succeed(toEarlyResultPlan(mutant, isStatic, status, mutant.statusReason, coveredByOfMutant(mutant))),
18488
18771
  onNone: () => Boolean$1$1.match(isPerTestUncoveredNonStatic(command, mutant, isStatic), {
18489
- onTrue: () => Result.succeed(toEarlyResultPlan(mutant, isStatic, "NoCoverage", void 0, coveringTestIdsOf(command, mutant.id))),
18772
+ onTrue: () => toNoCoverageRunPlan(mutant, command, isStatic, coveringTestIdsOf(command, mutant.id)),
18490
18773
  onFalse: () => Boolean$1$1.match(hasCoverageForPlan(command.staticCoverage), {
18491
18774
  onTrue: () => planForStaticallyCovered(mutant, command, isStatic),
18492
- onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, command.globalTestFilter, void 0, void 0)
18775
+ onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, nonEmptyTestFilterOf(command.globalTestFilter), void 0, void 0)
18493
18776
  })
18494
18777
  })
18495
18778
  });
@@ -18781,7 +19064,10 @@ const planMutationTest = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationTestPlan
18781
19064
  }))
18782
19065
  })).pipe(Effect$1.ignoreCause);
18783
19066
  const progressQueue = yield* RunEvents;
18784
- yield* Queue.offer(progressQueue, RunEvent_exports.PlanKnown.make({ total: plansForReporter.length + earlyResults.length }));
19067
+ yield* Queue.offer(progressQueue, RunEvent_exports.PlanKnown.make({
19068
+ total: plansForReporter.length + earlyResults.length,
19069
+ shardPlan: null
19070
+ }));
18785
19071
  return {
18786
19072
  runPlans: sortedPlans,
18787
19073
  earlyResults,
@@ -18804,8 +19090,10 @@ const testRunnerWorkerSpawnOf = (loaded, configured) => Effect$1.mapError(Effect
18804
19090
  cause: PluginNotFoundError.make({ descriptor: missing.descriptor })
18805
19091
  }));
18806
19092
  const acquire = Effect$1.fnUntraced(function* (spec) {
18807
- return yield* Pool.makeWithTTL({
18808
- acquire: buildTestRunner({
19093
+ const reports = yield* WorkerReports;
19094
+ const acquireOne = Effect$1.gen(function* () {
19095
+ const startedAt = yield* Clock.currentTimeMillis;
19096
+ const runner = yield* buildTestRunner({
18809
19097
  options: spec.options,
18810
19098
  fileDescriptions: spec.fileDescriptions,
18811
19099
  sandboxWorkingDirectory: spec.sandboxWorkingDirectory,
@@ -18818,7 +19106,12 @@ const acquire = Effect$1.fnUntraced(function* (spec) {
18818
19106
  sandboxWorkingDirectory: spec.sandboxWorkingDirectory,
18819
19107
  workerEntrypoint: resolved.entrypoint,
18820
19108
  idGenerator: spec.idGenerator
18821
- }))))),
19109
+ })))));
19110
+ yield* reports.report("testRunner", (yield* Clock.currentTimeMillis) - startedAt);
19111
+ return runner;
19112
+ });
19113
+ return yield* Pool.makeWithTTL({
19114
+ acquire: acquireOne,
18822
19115
  min: spec.min,
18823
19116
  max: spec.max,
18824
19117
  timeToLive: IDLE_TIME_TO_LIVE
@@ -18831,6 +19124,9 @@ const TestRunnerPools = Blueprint.make()(TypeId).steps({
18831
19124
  const scoped = (spec) => TestRunnerPools.of(spec).scoped;
18832
19125
  //#endregion
18833
19126
  //#region src/run/mutation-test.cell.ts
19127
+ const TCE_EQUIVALENT_TO_ORIGINAL_REASON = "equivalent-to-original: tce";
19128
+ const TCE_DUPLICATE_AT_SITE_REASON = "duplicate-at-site: tce";
19129
+ const countIgnoredByReason = (results, reason) => results.filter((result) => result.status === "Ignored" && result.statusReason === reason).length;
18834
19130
  const writeMutationTestNoTests = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationTestNoTests.name)(function* () {
18835
19131
  const env = yield* RunEnvironment;
18836
19132
  const now = yield* Clock.currentTimeMillis;
@@ -18934,22 +19230,26 @@ const proceedPipeline = Effect$1.fnUntraced(function* (raw) {
18934
19230
  });
18935
19231
  const runResults = yield* Effect$1.scoped(Effect$1.gen(function* () {
18936
19232
  const checkpoint = yield* makeCheckpointWriter(context, settledResults);
19233
+ const settleReported = (reported) => Effect$1.gen(function* () {
19234
+ yield* announceSettledMutant(context, reported);
19235
+ yield* checkpoint.record(reported);
19236
+ return reported;
19237
+ });
18937
19238
  return yield* withPhaseSpan(SpanTaxonomy_exports.Spans.mutationTestBatch, {
18938
19239
  total: plan.plannedTotal,
18939
19240
  testRunners: prev.concurrency.testRunners
18940
19241
  }, () => runCheckedPlans(checkedPlans, {
18941
- settleFailure: (mutantPlan, result) => Effect$1.gen(function* () {
18942
- const reported = yield* reporting.reportCheckFailure(toReportedMutant(mutantPlan.mutant), result);
18943
- yield* announceSettledMutant(context, reported);
18944
- yield* checkpoint.record(reported);
18945
- return reported;
19242
+ settleFailure: (mutantPlan, result) => reporting.reportCheckFailure(toReportedMutant(mutantPlan.mutant), result).pipe(Effect$1.flatMap(settleReported)),
19243
+ settleIgnored: (mutantPlan, result) => reporting.reportIgnored(toReportedMutant(mutantPlan.mutant), result).pipe(Effect$1.flatMap(settleReported)),
19244
+ runPlan: (runPlan) => Option$1.match(Option$1.liftPredicate(runPlan, isNoCoveragePlan), {
19245
+ onNone: () => Effect$1.scoped(mutantRunCell.run({
19246
+ context,
19247
+ testRunnerPool,
19248
+ checkpoint,
19249
+ plan: runPlan
19250
+ })),
19251
+ onSome: (noCoverageRunPlan) => reporting.reportNoCoverage(toReportedMutant(noCoverageRunPlan.mutant)).pipe(Effect$1.flatMap(settleReported))
18946
19252
  }),
18947
- runPlan: (runPlan) => Effect$1.scoped(mutantRunCell.run({
18948
- context,
18949
- testRunnerPool,
18950
- checkpoint,
18951
- plan: runPlan
18952
- })),
18953
19253
  concurrency: testRunnerCapacity
18954
19254
  }).pipe(Stream.runFold(() => [], (acc, result) => {
18955
19255
  acc.push(result);
@@ -18965,6 +19265,10 @@ const proceedPipeline = Effect$1.fnUntraced(function* (raw) {
18965
19265
  requested: requestedIdsOf(prev.options),
18966
19266
  results: allResults
18967
19267
  }), (detail) => Queue.offer(progressQueue, detail), { discard: true });
19268
+ yield* Queue.offer(progressQueue, RunEvent_exports.TceReported.make({
19269
+ equivalentToOriginal: countIgnoredByReason(allResults, TCE_EQUIVALENT_TO_ORIGINAL_REASON),
19270
+ duplicateAtSite: countIgnoredByReason(allResults, TCE_DUPLICATE_AT_SITE_REASON)
19271
+ }));
18968
19272
  const outcomeResult = yield* reporting.reportAll({
18969
19273
  ...reportingInputOf({
18970
19274
  prev,
@@ -20055,12 +20359,12 @@ const prepareCell = Sandwich.named(SpanTaxonomy_exports.Spans.prepare.name)(read
20055
20359
  });
20056
20360
  //#endregion
20057
20361
  //#region src/run/run-stages.cell.ts
20058
- const configReadReasonOf = (cause) => Match$1.value(cause).pipe(Match$1.tag("ConfigError", (refused) => refused.message), Match$1.orElse(() => "Failed to read config"));
20059
- const mutationTestCell = Cell.andThen(Cell.andThen(Cell.andThen(Cell.mapError(loadConfigCell, (cause) => StageError.make({
20362
+ const configReadReasonOf = (cause) => cause.message;
20363
+ const mutationTestCell = Cell.andThen(Cell.andThen(Cell.andThen(Cell.andThen(Cell.mapError(loadConfigCell, (cause) => StageError.make({
20060
20364
  stage: "prepare",
20061
20365
  reason: configReadReasonOf(cause),
20062
20366
  cause
20063
- })), Cell.mapError(readProjectCell, (cause) => StageError.make({
20367
+ })), (loaded) => refuseLocalMutationCell(loaded)), Cell.mapError(readProjectCell, (cause) => StageError.make({
20064
20368
  stage: "prepare",
20065
20369
  reason: "Failed to read project",
20066
20370
  cause
@@ -20203,7 +20507,7 @@ S.Struct({
20203
20507
  mode: OutputMode_exports.OutputMode,
20204
20508
  signal: OutputMode_exports.ModeSignal
20205
20509
  });
20206
- const nextFramingState = (state, event) => Match$1.value(event).pipe(Match$1.tag("verdict", "error", "help", () => FramingState.make({
20510
+ const nextFramingState = (state, event) => Match$1.value(event).pipe(Match$1.tag("verdict", "error", "help", "refused", () => FramingState.make({
20207
20511
  mode: state.mode,
20208
20512
  signal: state.signal,
20209
20513
  headerWritten: state.headerWritten,
@@ -20224,7 +20528,7 @@ const nextFramingState = (state, event) => Match$1.value(event).pipe(Match$1.tag
20224
20528
  terminalSeen: state.terminalSeen,
20225
20529
  completed: e.completed,
20226
20530
  total: e.total
20227
- })), Match$1.tag("stream", () => state), Match$1.tag("phase", () => state), Match$1.tag("tick", () => state), Match$1.tag("plugins", () => state), Match$1.tag("formats", () => state), Match$1.tag("skipped", () => state), Match$1.tag("reuse", "mutant-detail", "feedback", () => state), Match$1.exhaustive);
20531
+ })), Match$1.tag("stream", () => state), Match$1.tag("phase", () => state), Match$1.tag("tick", () => state), Match$1.tag("worker", () => state), Match$1.tag("plugins", () => state), Match$1.tag("formats", () => state), Match$1.tag("skipped", () => state), Match$1.tag("reuse", "tce", "mutant-detail", "feedback", () => state), Match$1.exhaustive);
20228
20532
  const noteState = (state, event) => nextFramingState(state, event);
20229
20533
  const formatScore = (score) => Option$1.match(Option$1.fromNullishOr(score), {
20230
20534
  onNone: () => "n/a",
@@ -20234,7 +20538,7 @@ const formatTotal = (total) => Option$1.match(Option$1.fromNullishOr(total), {
20234
20538
  onNone: () => "?",
20235
20539
  onSome: (val) => String(val)
20236
20540
  });
20237
- const formatStderrEvent = (event) => Match$1.value(event).pipe(Match$1.tag("plan", (e) => `plan ${e.total} mutants`), Match$1.tag("phase", (e) => `phase ${e.phase}`), Match$1.tag("tick", (e) => `${e.completed}/${formatTotal(e.total)} elapsed ${e.elapsedMs}ms`), Match$1.tag("verdict", (e) => `score ${formatScore(e.score)} killed ${e.counts.killed} survived ${e.counts.survived}`), Match$1.tag("error", (e) => `error ${e.error}`), Match$1.tag("stream", () => null), Match$1.tag("mutantTested", () => null), Match$1.tag("help", () => null), Match$1.tag("plugins", () => null), Match$1.tag("formats", () => null), Match$1.tag("skipped", () => null), Match$1.tag("reuse", "mutant-detail", "feedback", () => null), Match$1.exhaustive);
20541
+ const formatStderrEvent = (event) => Match$1.value(event).pipe(Match$1.tag("plan", (e) => `plan ${e.total} mutants`), Match$1.tag("phase", (e) => `phase ${e.phase}`), Match$1.tag("tick", (e) => `${e.completed}/${formatTotal(e.total)} elapsed ${e.elapsedMs}ms`), Match$1.tag("verdict", (e) => `score ${formatScore(e.score)} killed ${e.counts.killed} survived ${e.counts.survived}`), Match$1.tag("error", (e) => `error ${e.error}`), Match$1.tag("refused", (e) => `refused ${e.rule}`), Match$1.tag("stream", () => null), Match$1.tag("mutantTested", () => null), Match$1.tag("worker", () => null), Match$1.tag("help", () => null), Match$1.tag("plugins", () => null), Match$1.tag("formats", () => null), Match$1.tag("skipped", () => null), Match$1.tag("reuse", "tce", "mutant-detail", "feedback", () => null), Match$1.exhaustive);
20238
20542
  const stderrLineFor = (state, event) => Boolean$1$1.match(Boolean$1$1.and(state.mode === "human", !state.terminalSeen), {
20239
20543
  onTrue: () => formatStderrEvent(event),
20240
20544
  onFalse: () => null
@@ -20398,16 +20702,17 @@ const CONFIG_CODE = 2;
20398
20702
  const UNKNOWN_FAILURE = "Unknown failure";
20399
20703
  const SIGNAL_REMEDIATION = "the run was interrupted by a signal; re-run it to continue";
20400
20704
  const PARSE_REMEDIATION = "re-run with --help to see the full usage";
20705
+ const REFUSAL_REMEDIATION = "run mutation testing on main CI (GitHub Actions)";
20401
20706
  const DEFAULT_REMEDIATION = "see --reportFile or the verdict envelope on stdout";
20402
- const exitCodeOf = (outcome) => Match$1.value(outcome).pipe(Match$1.tag("RunOk", () => 0), Match$1.tag("RunInterrupted", (error) => error.code), Match$1.tag("RunParseFailed", () => CONFIG_CODE), Match$1.tag("RunSurvivorsRejected", () => CONFIG_CODE), Match$1.tag("RunConfigFailed", () => CONFIG_CODE), Match$1.tag("RunFailed", (failed) => failed.code), Match$1.exhaustive);
20707
+ const exitCodeOf = (outcome) => Match$1.value(outcome).pipe(Match$1.tag("RunOk", () => 0), Match$1.tag("RunInterrupted", (error) => error.code), Match$1.tag("RunParseFailed", () => CONFIG_CODE), Match$1.tag("RunSurvivorsRejected", () => CONFIG_CODE), Match$1.tag("RunConfigFailed", () => CONFIG_CODE), Match$1.tag("RunRefused", () => CONFIG_CODE), Match$1.tag("RunFailed", (failed) => failed.code), Match$1.exhaustive);
20403
20708
  const nonEmptyText = Option$1.liftPredicate(S.is(S.NonEmptyString));
20404
20709
  const capturedOrUnknown = (captured) => Option$1.getOrElse(nonEmptyText(captured), () => UNKNOWN_FAILURE);
20405
20710
  const capturedThenRecorded = (captured, recorded) => Option$1.getOrElse(nonEmptyText(captured), () => Option$1.getOrElse(Option$1.fromNullishOr(recorded), () => UNKNOWN_FAILURE));
20406
- const failureTextOf = (error, captured) => Match$1.value(error).pipe(Match$1.tag("RunParseFailed", (failed) => Option$1.getOrElse(Option$1.map(Option$1.fromNullishOr(failed.unrecognized), (value) => `Received unknown argument: '${value}'`), () => capturedOrUnknown(captured))), Match$1.tag("RunSurvivorsRejected", (failed) => Option$1.getOrElse(Option$1.fromNullishOr(failed.diagnostic), () => UNKNOWN_FAILURE)), Match$1.tag("RunInterrupted", () => capturedOrUnknown(captured)), Match$1.tag("RunConfigFailed", (failed) => capturedThenRecorded(captured, failed.detail)), Match$1.tag("RunFailed", (failed) => capturedThenRecorded(captured, failed.diagnostic)), Match$1.exhaustive);
20711
+ const failureTextOf = (error, captured) => Match$1.value(error).pipe(Match$1.tag("RunParseFailed", (failed) => Option$1.getOrElse(Option$1.map(Option$1.fromNullishOr(failed.unrecognized), (value) => `Received unknown argument: '${value}'`), () => capturedOrUnknown(captured))), Match$1.tag("RunSurvivorsRejected", (failed) => Option$1.getOrElse(Option$1.fromNullishOr(failed.diagnostic), () => UNKNOWN_FAILURE)), Match$1.tag("RunInterrupted", () => capturedOrUnknown(captured)), Match$1.tag("RunConfigFailed", (failed) => capturedThenRecorded(captured, failed.detail)), Match$1.tag("RunRefused", (failed) => failed.message), Match$1.tag("RunFailed", (failed) => capturedThenRecorded(captured, failed.diagnostic)), Match$1.exhaustive);
20407
20712
  const remediationTextOf = (error) => Match$1.value(error).pipe(Match$1.tag("RunInterrupted", () => SIGNAL_REMEDIATION), Match$1.tag("RunParseFailed", () => PARSE_REMEDIATION), Match$1.tag("RunSurvivorsRejected", (failed) => Option$1.getOrElse(Option$1.fromNullishOr(failed.diagnostic), () => DEFAULT_REMEDIATION)), Match$1.tag("RunConfigFailed", (failed) => Option$1.match(Option$1.fromNullishOr(failed.detail), {
20408
20713
  onSome: (detail) => `check the config file: ${detail}`,
20409
20714
  onNone: () => "check the config file"
20410
- })), Match$1.tag("RunFailed", () => DEFAULT_REMEDIATION), Match$1.exhaustive);
20715
+ })), Match$1.tag("RunRefused", () => REFUSAL_REMEDIATION), Match$1.tag("RunFailed", () => DEFAULT_REMEDIATION), Match$1.exhaustive);
20411
20716
  const errorEnvelopeFromOutcome = (input) => ErrorEnvelope.make({
20412
20717
  schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
20413
20718
  code: exitCodeOf(input.error),
@@ -20504,22 +20809,33 @@ const emitNullScoreVerdict = (params) => {
20504
20809
  version: StrykerPackage.version
20505
20810
  }
20506
20811
  };
20507
- const envelope = buildVerdictEnvelope(report, mode.mode, mode.signal, stream.runId, basePath, pathService, Option$1.none(), null);
20508
- return Queue.offer(stream.queue, RunEvent_exports.VerdictReached.make({
20509
- schemaVersion: envelope.schemaVersion,
20510
- runId: envelope.runId,
20511
- mode: envelope.mode,
20512
- signal: envelope.signal,
20513
- score: envelope.score,
20514
- thresholds: envelope.thresholds,
20515
- reportFile: envelope.reportFile,
20516
- counts: envelope.counts,
20517
- mutants: envelope.mutants,
20518
- scope: envelope.scope,
20519
- mutantSetPolicy: envelope.mutantSetPolicy,
20520
- phaseDurations: envelope.phaseDurations,
20521
- static: envelope.static
20522
- }));
20812
+ return Effect$1.gen(function* () {
20813
+ const now = yield* Clock.currentTimeMillis;
20814
+ const envelope = buildVerdictEnvelope({
20815
+ ...report,
20816
+ budget: {
20817
+ predictedSeconds: 0,
20818
+ actualSeconds: (now - stream.startedAt) / 1e3
20819
+ }
20820
+ }, mode.mode, mode.signal, stream.runId, basePath, pathService, Option$1.none(), null);
20821
+ yield* Queue.offer(stream.queue, RunEvent_exports.VerdictReached.make({
20822
+ schemaVersion: envelope.schemaVersion,
20823
+ runId: envelope.runId,
20824
+ mode: envelope.mode,
20825
+ signal: envelope.signal,
20826
+ score: envelope.score,
20827
+ thresholds: envelope.thresholds,
20828
+ reportFile: envelope.reportFile,
20829
+ counts: envelope.counts,
20830
+ mutants: envelope.mutants,
20831
+ scope: envelope.scope,
20832
+ mutantSetPolicy: envelope.mutantSetPolicy,
20833
+ incrementalMode: envelope.incrementalMode,
20834
+ phaseDurations: envelope.phaseDurations,
20835
+ static: envelope.static,
20836
+ budget: envelope.budget
20837
+ }));
20838
+ });
20523
20839
  };
20524
20840
  const offerFailureEnvelope = (stream, failed, captured) => {
20525
20841
  const envelope = errorEnvelopeFromOutcome({
@@ -20534,6 +20850,11 @@ const offerFailureEnvelope = (stream, failed, captured) => {
20534
20850
  reason: null
20535
20851
  }));
20536
20852
  };
20853
+ const offerRefusedEnvelope = (stream, refused) => Queue.offer(stream.queue, RunEvent_exports.Refused.make({
20854
+ schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
20855
+ rule: refused.rule,
20856
+ message: refused.message
20857
+ }));
20537
20858
  const emitHelpEnvelope = (stream, help) => Queue.offer(stream.queue, RunEvent_exports.HelpRendered.make({
20538
20859
  schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
20539
20860
  code: 0,
@@ -20565,7 +20886,7 @@ const emitMachineModeOutput = Effect$1.fn(SpanTaxonomy_exports.Spans.runEventStr
20565
20886
  onSuccess: (decision) => Match$1.value(decision).pipe(Match$1.tag("RunOk", (ok) => Option$1.match(helpPayload(ok, captured), {
20566
20887
  onSome: (help) => emitHelpEnvelope(stream, help),
20567
20888
  onNone: () => emitNullScoreVerdictWhenOpen(stream, mode, basePath, pathService)
20568
- })), Match$1.tag("RunParseFailed", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.tag("RunSurvivorsRejected", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.tag("RunConfigFailed", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.tag("RunFailed", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.exhaustive),
20889
+ })), Match$1.tag("RunParseFailed", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.tag("RunSurvivorsRejected", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.tag("RunConfigFailed", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.tag("RunRefused", (refused) => offerRefusedEnvelope(stream, refused)), Match$1.tag("RunFailed", (failed) => offerFailureEnvelope(stream, failed, captured)), Match$1.exhaustive),
20569
20890
  onFailure: (failure) => offerFailureEnvelope(stream, failure, captured)
20570
20891
  });
20571
20892
  });