@systemfsoftware/stryker-js 15.0.1 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: {}
@@ -10244,6 +10238,48 @@ const resolveConfig = Workflow.make({
10244
10238
  })
10245
10239
  });
10246
10240
  //#endregion
10241
+ //#region src/atomic-write.cell.ts
10242
+ const writeFileAtomic = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingWriteAtomic.name)(function* (ports, file, content) {
10243
+ const directory = ports.path.dirname(file);
10244
+ yield* ports.fs.makeDirectory(directory, { recursive: true });
10245
+ const temp = yield* ports.fs.makeTempFile({
10246
+ directory,
10247
+ prefix: `${ports.path.basename(file)}.`,
10248
+ suffix: ".tmp"
10249
+ });
10250
+ yield* Effect$1.gen(function* () {
10251
+ yield* ports.fs.writeFileString(temp, content);
10252
+ yield* ports.fs.rename(temp, file);
10253
+ }).pipe(Effect$1.onExit((exit) => Exit.isSuccess(exit) ? Effect$1.void : ports.fs.remove(temp).pipe(Effect$1.ignore)));
10254
+ });
10255
+ //#endregion
10256
+ //#region src/mutant-cost.ts
10257
+ const executedTestBodyMsOf = (executedTests) => executedTests.reduce((total, test) => total + test.timeSpentMs, 0);
10258
+ 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));
10259
+ const bodyTimeWithinWall = (elapsedMs, testBodyMs) => Math.min(testBodyMs, elapsedMs);
10260
+ const costTotalMsOf = (cost) => cost === void 0 ? void 0 : cost.fixedOverheadMs + cost.testBodyMs;
10261
+ const mutantCostOf = (input) => {
10262
+ const testBodyMs = bodyTimeWithinWall(input.elapsedMs, input.testBodyMs);
10263
+ return {
10264
+ fixedOverheadMs: input.elapsedMs - testBodyMs,
10265
+ testBodyMs,
10266
+ testsExecuted: input.testsExecuted,
10267
+ shared: input.shared
10268
+ };
10269
+ };
10270
+ //#endregion
10271
+ //#region src/budget.ts
10272
+ const MILLIS_PER_SECOND = 1e3;
10273
+ const costMsOf = (result) => costTotalMsOf(result.cost) ?? 0;
10274
+ const predictedSecondsOf = (results, concurrency) => {
10275
+ const lanes = concurrency > 0 ? concurrency : 1;
10276
+ return Arr.reduce(results, 0, (total, result) => total + costMsOf(result)) / lanes / MILLIS_PER_SECOND;
10277
+ };
10278
+ const budgetOf$1 = (input) => ({
10279
+ predictedSeconds: input.planPredictedSeconds ?? predictedSecondsOf(input.results, input.concurrency),
10280
+ actualSeconds: input.actualSeconds
10281
+ });
10282
+ //#endregion
10247
10283
  //#region src/build-reproducers.workflow.ts
10248
10284
  const CONTEXT_LINES = 3;
10249
10285
  const maxOf$1 = (left, right) => Boolean$1$1.match(left < right, {
@@ -10315,7 +10351,7 @@ const fileNameOrder = Order.mapInput(Order.String, (reproducer) => reproducer.fi
10315
10351
  const idOrder$1 = Order.mapInput(Order.String, (reproducer) => reproducer.id);
10316
10352
  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
10353
  const mutantsOf = (report) => Arr.flatMap(Object.values(report.files), (file) => file.mutants);
10318
- const decide$23 = (command) => {
10354
+ const decide$24 = (command) => {
10319
10355
  const mutants = mutantsOf(command.report);
10320
10356
  return Boolean$1$1.match(mutants.length === 0, {
10321
10357
  onTrue: () => NoMutantsToReproduce.make({}),
@@ -10326,7 +10362,7 @@ const buildReproducers = Workflow.make({
10326
10362
  command: BuildReproducersCommand,
10327
10363
  decision: BuildReproducersDecision,
10328
10364
  error: S.Never,
10329
- decide: (command) => Result.succeed(decide$23(command))
10365
+ decide: (command) => Result.succeed(decide$24(command))
10330
10366
  });
10331
10367
  //#endregion
10332
10368
  //#region src/checkpoint-mutants.workflow.ts
@@ -10350,7 +10386,7 @@ var CheckpointMutantsCommand = class extends S.TaggedClass()("CheckpointMutantsC
10350
10386
  };
10351
10387
  const settledByIdOf = (settled) => HashMap.fromIterable(settled.map((row) => [row.mutant.id, row]));
10352
10388
  const rowOf = (mutant, settledById) => Option$1.getOrElse(HashMap.get(settledById, mutant.id), () => CheckpointPendingMutant.make({ mutant }));
10353
- const decide$22 = (command) => {
10389
+ const decide$23 = (command) => {
10354
10390
  const settledById = settledByIdOf(command.settled);
10355
10391
  return Result.succeed(command.plannedMutants.map((mutant) => rowOf(mutant, settledById)));
10356
10392
  };
@@ -10358,7 +10394,7 @@ const checkpointMutants = Workflow.make({
10358
10394
  command: CheckpointMutantsCommand,
10359
10395
  decision: S.Array(CheckpointMutantRow),
10360
10396
  error: S.Never,
10361
- decide: decide$22
10397
+ decide: decide$23
10362
10398
  });
10363
10399
  //#endregion
10364
10400
  //#region src/classify-exit.workflow.ts
@@ -10407,7 +10443,7 @@ const verdictExitClass = (score, breakingThreshold) => Match$1.valueTags(score,
10407
10443
  onSome: (threshold) => Match$1.value(percentage < threshold).pipe(Match$1.when(true, () => "VerdictFail"), Match$1.when(false, () => null), Match$1.exhaustive)
10408
10444
  })
10409
10445
  });
10410
- const decide$21 = (command) => Option$1.match(Option$1.fromNullishOr(verdictExitClass(command.score, command.breakingThreshold)), {
10446
+ const decide$22 = (command) => Option$1.match(Option$1.fromNullishOr(verdictExitClass(command.score, command.breakingThreshold)), {
10411
10447
  onNone: () => Option$1.match(precedenceOf(command.pending), {
10412
10448
  onNone: () => Result.succeed(ExitPassed.make({})),
10413
10449
  onSome: (exitClass) => Result.succeed(decisionOf$3(exitClass))
@@ -10418,7 +10454,7 @@ const classifyExit = Workflow.make({
10418
10454
  command: ClassifyExitCommand,
10419
10455
  decision: ClassifyExitDecision,
10420
10456
  error: S.Never,
10421
- decide: decide$21
10457
+ decide: decide$22
10422
10458
  });
10423
10459
  //#endregion
10424
10460
  //#region src/IncrementalDiff.schema.ts
@@ -10480,6 +10516,61 @@ const ReuseReportSchema = S.Struct({
10480
10516
  testFiles: S.optional(ReuseTestFilesSchema)
10481
10517
  });
10482
10518
  //#endregion
10519
+ //#region src/MutantCost.schema.ts
10520
+ const MutantCost = S.Struct({
10521
+ predictedMs: S.Finite,
10522
+ actualMs: S.NullOr(S.Finite),
10523
+ coveringTests: S.Int
10524
+ });
10525
+ const MutantCosts = S.Record(Mutant_exports.MutantId, MutantCost);
10526
+ const MutantCostSubject = S.Struct({
10527
+ id: Mutant_exports.MutantId,
10528
+ static: S.optional(S.Boolean)
10529
+ });
10530
+ S.Struct({
10531
+ subject: MutantCostSubject,
10532
+ staticCoverage: S.optional(S.Record(S.String, Report_exports.NonNegativeFinite)),
10533
+ allTestTimesMs: S.Array(Report_exports.NonNegativeFinite),
10534
+ coveringTestTimesMs: S.Array(Report_exports.NonNegativeFinite),
10535
+ executedActualMs: S.optional(Report_exports.NonNegativeFinite),
10536
+ priorActualMs: S.optional(Report_exports.NonNegativeFinite),
10537
+ fixedOverheadMs: Report_exports.NonNegativeFinite
10538
+ });
10539
+ S.Struct({
10540
+ subjects: S.Array(MutantCostSubject),
10541
+ staticCoverage: S.optional(S.Record(S.String, Report_exports.NonNegativeFinite)),
10542
+ allTestTimesMs: S.Array(Report_exports.NonNegativeFinite),
10543
+ coveringTestTimesMsByMutantId: S.Record(S.String, S.Array(Report_exports.NonNegativeFinite)),
10544
+ executedActualMsByMutantId: S.Record(S.String, Report_exports.NonNegativeFinite),
10545
+ priorActualMsByMutantId: S.Record(S.String, Report_exports.NonNegativeFinite),
10546
+ fixedOverheadMs: Report_exports.NonNegativeFinite
10547
+ });
10548
+ //#endregion
10549
+ //#region src/mutant-cost-model.ts
10550
+ const staticHitCountOf = (staticCoverage, id) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromNullishOr(staticCoverage), (coverage) => Record$1.get(coverage, id)), () => 0);
10551
+ const hasStaticHit = (staticCoverage, id) => staticCoverage === void 0 || staticHitCountOf(staticCoverage, id) > 0;
10552
+ const runsWholeSuite$1 = (input) => input.subject.static === true || hasStaticHit(input.staticCoverage, input.subject.id);
10553
+ const coveringTimesOf = (input) => runsWholeSuite$1(input) ? input.allTestTimesMs : input.coveringTestTimesMs;
10554
+ const actualMsOf = (input) => Option$1.getOrElse(Option$1.firstSomeOf([Option$1.fromNullishOr(input.executedActualMs), Option$1.fromNullishOr(input.priorActualMs)]), () => null);
10555
+ const mutantCostOfCase = (input) => {
10556
+ const coveringTimes = coveringTimesOf(input);
10557
+ return {
10558
+ predictedMs: input.fixedOverheadMs + coveringTimes.reduce((total, time) => total + time, 0),
10559
+ actualMs: actualMsOf(input),
10560
+ coveringTests: coveringTimes.length
10561
+ };
10562
+ };
10563
+ const caseOfSubject = (input, subject) => ({
10564
+ subject,
10565
+ staticCoverage: input.staticCoverage,
10566
+ allTestTimesMs: input.allTestTimesMs,
10567
+ coveringTestTimesMs: Option$1.getOrElse(Record$1.get(input.coveringTestTimesMsByMutantId, subject.id), () => []),
10568
+ executedActualMs: Option$1.getOrUndefined(Record$1.get(input.executedActualMsByMutantId, subject.id)),
10569
+ priorActualMs: Option$1.getOrUndefined(Record$1.get(input.priorActualMsByMutantId, subject.id)),
10570
+ fixedOverheadMs: input.fixedOverheadMs
10571
+ });
10572
+ const mutantCostsOf = (input) => Object.fromEntries(input.subjects.map((subject) => [subject.id, mutantCostOfCase(caseOfSubject(input, subject))]));
10573
+ //#endregion
10483
10574
  //#region src/mutation-reporting.schema.ts
10484
10575
  var ManifestUnreadable = class extends Schema.TaggedError()("ManifestUnreadable", {
10485
10576
  specifier: Schema.String,
@@ -11036,21 +11127,6 @@ S.Literal("mutation-part.json");
11036
11127
  S.Literal("mutation-stream.jsonl");
11037
11128
  const ReportFileNames = S.Record(S.String, Mutant_exports.CanonicalFileName);
11038
11129
  //#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
11130
  //#region src/reporting/static-verdict.ts
11055
11131
  const costOrZero = (cost) => Option$1.getOrElse(Option$1.fromUndefinedOr(costTotalMsOf(cost)), () => 0);
11056
11132
  const staticVerdictOf = (results) => {
@@ -11077,8 +11153,10 @@ var VerdictEnvelope = class extends S.Class("VerdictEnvelope")({
11077
11153
  mutants: S.Array(RunEvent_exports.VerdictMutant),
11078
11154
  scope: RunEvent_exports.RunScope,
11079
11155
  mutantSetPolicy: RunEvent_exports.MutantSetPolicy,
11156
+ incrementalMode: RunEvent_exports.IncrementalMode,
11080
11157
  phaseDurations: S.NullOr(RunEvent_exports.PhaseDurations),
11081
- static: S.NullOr(RunEvent_exports.StaticVerdict)
11158
+ static: S.NullOr(RunEvent_exports.StaticVerdict),
11159
+ budget: RunEvent_exports.Budget
11082
11160
  }) {};
11083
11161
  //#endregion
11084
11162
  //#region src/reporting/verdict-envelope.ts
@@ -11117,6 +11195,11 @@ const runIdTextOf = (now) => {
11117
11195
  };
11118
11196
  const generateRunId = (now) => RunEvent_exports.RunId.make(runIdTextOf(now));
11119
11197
  const isActionableStatus = S.is(Mutant_exports.ActionableStatusSchema);
11198
+ const ZERO_BUDGET = RunEvent_exports.Budget.make({
11199
+ predictedSeconds: 0,
11200
+ actualSeconds: 0
11201
+ });
11202
+ const budgetOf = (report) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromUndefinedOr(report["budget"]), (raw) => S.decodeUnknownOption(RunEvent_exports.Budget)(raw)), () => ZERO_BUDGET);
11120
11203
  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
11204
  id: mutant.id,
11122
11205
  file,
@@ -11125,13 +11208,17 @@ const actionableMutants = (files) => Arr.flatMap(Object.entries(files), ([file,
11125
11208
  replacement: mutant.replacement ?? null,
11126
11209
  status: mutant.status
11127
11210
  })));
11211
+ const reusesVerdicts = (config) => config.incremental === true && config.force !== true;
11212
+ const incrementalModeOf = (decoded) => Option$1.exists(decoded, reusesVerdicts) ? "incremental" : "full";
11128
11213
  const embeddedConfig = (report) => {
11129
11214
  const JsonReporterSchema = S.Struct({ fileName: S.String });
11130
11215
  const MutatorSchema = S.StructWithRest(S.Struct({ mutantSetPolicy: S.optional(RunEvent_exports.MutantSetPolicy) }), [S.Record(S.String, S.Unknown)]);
11131
11216
  const EmbeddedConfigSchema = S.StructWithRest(S.Struct({
11132
11217
  jsonReporter: S.optional(JsonReporterSchema),
11133
11218
  mutator: S.optional(MutatorSchema),
11134
- since: S.optional(S.String)
11219
+ since: S.optional(S.String),
11220
+ incremental: S.optional(S.Boolean),
11221
+ force: S.optional(S.Boolean)
11135
11222
  }), [S.Record(S.String, S.Unknown)]);
11136
11223
  const decoded = S.decodeUnknownOption(EmbeddedConfigSchema)(report.config);
11137
11224
  const jsonReporter = Option$1.flatMap(decoded, (config) => Option$1.fromUndefinedOr(config.jsonReporter));
@@ -11140,12 +11227,13 @@ const embeddedConfig = (report) => {
11140
11227
  return {
11141
11228
  jsonReporterFileName: Option$1.getOrUndefined(Option$1.map(jsonReporter, (reporter) => reporter.fileName)),
11142
11229
  mutantSetPolicy: Option$1.getOrElse(mutantSetPolicy, () => "default"),
11143
- scope: Option$1.isSome(since) ? "diff" : "full"
11230
+ scope: Option$1.isSome(since) ? "diff" : "full",
11231
+ incrementalMode: incrementalModeOf(decoded)
11144
11232
  };
11145
11233
  };
11146
11234
  const buildVerdictEnvelope = dual((args) => args.length === 8, (report, mode, signal, runId, basePath, pathService, phaseDurations, staticVerdict) => {
11147
11235
  const metrics = Report_exports.metricsFromMutants(Arr.flatMap(Object.values(report.files), (file) => file.mutants));
11148
- const { jsonReporterFileName, mutantSetPolicy, scope } = embeddedConfig(report);
11236
+ const { jsonReporterFileName, mutantSetPolicy, scope, incrementalMode } = embeddedConfig(report);
11149
11237
  return VerdictEnvelope.make({
11150
11238
  schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
11151
11239
  runId,
@@ -11165,14 +11253,33 @@ const buildVerdictEnvelope = dual((args) => args.length === 8, (report, mode, si
11165
11253
  mutants: actionableMutants(report.files),
11166
11254
  scope,
11167
11255
  mutantSetPolicy,
11256
+ incrementalMode,
11168
11257
  phaseDurations: Option$1.getOrNull(phaseDurations),
11169
- static: staticVerdict
11258
+ static: staticVerdict,
11259
+ budget: budgetOf(report)
11170
11260
  });
11171
11261
  });
11172
11262
  //#endregion
11173
11263
  //#region src/run-events.service.ts
11174
11264
  var RunEvents = class extends Context.Service()("@systemfsoftware/stryker-js/run-events.service/RunEvents") {};
11175
11265
  var RunIdentity = class extends Context.Service()("@systemfsoftware/stryker-js/run-events.service/RunIdentity") {};
11266
+ var WorkerReports = class extends Context.Service()("@systemfsoftware/stryker-js/run-events.service/WorkerReports") {};
11267
+ const WorkerReportsLive = Layer.effect(WorkerReports, Effect$1.gen(function* () {
11268
+ const queue = yield* RunEvents;
11269
+ const counters = {
11270
+ testRunner: yield* Ref.make(0),
11271
+ checker: yield* Ref.make(0)
11272
+ };
11273
+ return WorkerReports.of({ report: (role, startupMs) => Effect$1.gen(function* () {
11274
+ const index = yield* Ref.getAndUpdate(counters[role], (current) => current + 1);
11275
+ yield* Queue.offer(queue, RunEvent_exports.WorkerReported.make({
11276
+ schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
11277
+ role,
11278
+ index,
11279
+ startupMs
11280
+ }));
11281
+ }) });
11282
+ }));
11176
11283
  //#endregion
11177
11284
  //#region src/phase-durations.ts
11178
11285
  const elapsedOf = (marks, phase) => Option$1.map(Arr.findFirst(marks, (mark) => mark.phase === phase), (mark) => mark.elapsedMs);
@@ -11229,7 +11336,7 @@ const strykerOutputFilesOf = (options) => [
11229
11336
  ];
11230
11337
  //#endregion
11231
11338
  //#region package.json
11232
- var version = "15.0.1";
11339
+ var version = "16.0.0";
11233
11340
  //#endregion
11234
11341
  //#region src/stryker-package.schema.ts
11235
11342
  var StrykerPackage = class extends S.Class("StrykerPackage")({
@@ -11377,6 +11484,8 @@ var MutationReporting = class MutationReporting extends Context.Service()("@syst
11377
11484
  };
11378
11485
  return MutationReporting.of({
11379
11486
  reportCheckFailure: (mutant, result) => reportCheckFailure(mutant, result),
11487
+ reportIgnored: (mutant, result) => reportIgnored(mutant, result),
11488
+ reportNoCoverage: (mutant) => reportNoCoverage(mutant),
11380
11489
  reportMutantRunResult: (mutant, result) => mapRunResult(mutant, result),
11381
11490
  reportAll: (input) => reportAll(deps, input),
11382
11491
  checkpoint: (input, plannedMutants) => checkpoint(deps, input, plannedMutants)
@@ -11399,6 +11508,8 @@ const reportMutant = (mutant, status, outcome = {}) => Effect$1.succeed({
11399
11508
  });
11400
11509
  const reportMutantStatus = (mutant, status, statusReason) => reportMutant(mutant, status, { statusReason: statusReason ?? mutant.statusReason });
11401
11510
  const reportCheckFailure = (mutant, result) => reportMutantStatus(mutant, "CompileError", result.reason);
11511
+ const reportIgnored = (mutant, result) => reportMutantStatus(mutant, "Ignored", result.reason);
11512
+ const reportNoCoverage = (mutant) => reportMutantStatus(mutant, "NoCoverage");
11402
11513
  const reasonedOutcomeOf = (reason) => Option$1.match(Option$1.fromNullishOr(reason), {
11403
11514
  onNone: () => ({}),
11404
11515
  onSome: (present) => ({ statusReason: present })
@@ -11622,6 +11733,11 @@ const mutationTestReport = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReport
11622
11733
  const { files, testFiles, identities } = yield* assembleReport(deps, input, results);
11623
11734
  const dependencies = yield* discoverDependencies(deps);
11624
11735
  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);
11736
+ const budget = budgetOf$1({
11737
+ results,
11738
+ concurrency: input.concurrency,
11739
+ actualSeconds: ((yield* Clock.currentTimeMillis) - input.runStartedAt) / 1e3
11740
+ });
11625
11741
  return {
11626
11742
  report: {
11627
11743
  files,
@@ -11633,7 +11749,8 @@ const mutationTestReport = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReport
11633
11749
  framework: {
11634
11750
  ...STRYKER_FRAMEWORK,
11635
11751
  dependencies
11636
- }
11752
+ },
11753
+ budget
11637
11754
  },
11638
11755
  identities
11639
11756
  };
@@ -11674,27 +11791,49 @@ const emitVerdict = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingEmit
11674
11791
  mutants: envelope.mutants,
11675
11792
  scope: envelope.scope,
11676
11793
  mutantSetPolicy: envelope.mutantSetPolicy,
11794
+ incrementalMode: envelope.incrementalMode,
11677
11795
  phaseDurations: envelope.phaseDurations,
11678
- static: envelope.static
11796
+ static: envelope.static,
11797
+ budget: envelope.budget
11679
11798
  }));
11680
11799
  });
11681
11800
  const dryRunCoverageFieldOf = (testCoverage) => Option$1.match(Option$1.fromUndefinedOr(testCoverage.dryRunCoverage), {
11682
11801
  onNone: () => ({}),
11683
11802
  onSome: (dryRunCoverage) => ({ dryRunCoverage })
11684
11803
  });
11804
+ const testTimesOf = (tests) => Arr.map([...tests], (test) => test.timeSpentMs);
11805
+ const executedEntryOf = (result) => Option$1.match(Option$1.fromUndefinedOr(result.cost), {
11806
+ onNone: () => [],
11807
+ onSome: (cost) => [[result.id, costTotalMsOf(cost) ?? 0]]
11808
+ });
11809
+ const priorEntryOf = (entry) => entry[1].actualMs === null ? [] : [[entry[0], entry[1].actualMs]];
11810
+ const priorCostsOf = (report) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromNullishOr(report), (present) => Option$1.fromNullishOr(present.costs)), () => ({}));
11811
+ const mutantCostModelOf = (input, results) => ({
11812
+ subjects: Arr.map(results, (result) => ({
11813
+ id: result.id,
11814
+ static: result.static
11815
+ })),
11816
+ staticCoverage: input.testCoverage.staticCoverage,
11817
+ allTestTimesMs: Arr.map([...input.testCoverage.testsById], ([, test]) => test.timeSpentMs),
11818
+ coveringTestTimesMsByMutantId: Object.fromEntries([...input.testCoverage.testsByMutantId].map(([mutantId, tests]) => [mutantId, testTimesOf(tests)])),
11819
+ executedActualMsByMutantId: Object.fromEntries(Arr.flatMap(results, executedEntryOf)),
11820
+ priorActualMsByMutantId: Object.fromEntries(Arr.flatMap(Object.entries(priorCostsOf(input.project.incrementalReport)), priorEntryOf)),
11821
+ fixedOverheadMs: input.timeOverheadMs
11822
+ });
11823
+ const costsOf = (input, results) => mutantCostsOf(mutantCostModelOf(input, results));
11685
11824
  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
11825
  const runInputsDigest = yield* runInputsDigestOf(deps.fs, deps.path, input.basePath, input.options);
11688
11826
  const json = yield* S.encodeEffect(S.fromJsonString(S.Unknown, { space: 2 }))({
11689
- incrementalVersion: "2",
11690
- verdictSemanticsVersion: 1,
11827
+ incrementalVersion: "3",
11828
+ verdictSemanticsVersion: 3,
11691
11829
  mutantSetPolicy: input.options.mutator.mutantSetPolicy,
11692
11830
  runInputsDigest,
11693
11831
  ...report,
11694
11832
  files: stampFileIdentities(stampClosureDigests(report.files, input.closureDigestsByMutantId), identities),
11833
+ costs: costsOf(input, input.results),
11695
11834
  ...dryRunCoverageFieldOf(input.testCoverage)
11696
11835
  }).pipe(Effect$1.orDie);
11697
- yield* deps.fs.writeFileString(input.options.incrementalFile, json);
11836
+ yield* writeFileAtomic(deps, input.options.incrementalFile, json);
11698
11837
  });
11699
11838
  const writeReproducers = (deps, input, report) => {
11700
11839
  const decision = Result.getOrThrow(buildReproducers(BuildReproducersCommand.make({ report })));
@@ -11731,24 +11870,24 @@ const reportAll = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingReport
11731
11870
  verdict: finalVerdict
11732
11871
  };
11733
11872
  });
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
11873
  const slimIncrementalReport = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingSlimIncrementalReport.name)(function* (deps, input, results) {
11741
11874
  const { files, testFiles, identities } = yield* assembleReport(deps, input, results);
11742
11875
  const runInputsDigest = yield* runInputsDigestOf(deps.fs, deps.path, input.basePath, input.options);
11743
11876
  return {
11744
- incrementalVersion: "2",
11745
- verdictSemanticsVersion: 1,
11877
+ incrementalVersion: "3",
11878
+ verdictSemanticsVersion: 3,
11746
11879
  mutantSetPolicy: input.options.mutator.mutantSetPolicy,
11747
11880
  runInputsDigest,
11748
11881
  schemaVersion: Report_exports.WrittenSchemaVersion.literal,
11749
11882
  thresholds: input.options.thresholds,
11750
11883
  files: stampFileIdentities(stampClosureDigests(files, input.closureDigestsByMutantId), identities),
11884
+ costs: costsOf(input, results),
11751
11885
  testFiles,
11886
+ budget: budgetOf$1({
11887
+ results,
11888
+ concurrency: input.concurrency,
11889
+ actualSeconds: ((yield* Clock.currentTimeMillis) - input.runStartedAt) / 1e3
11890
+ }),
11752
11891
  ...dryRunCoverageFieldOf(input.testCoverage)
11753
11892
  };
11754
11893
  });
@@ -11782,7 +11921,7 @@ const checkpointResultsOf = (input, plannedMutants) => Result.match(checkpointMu
11782
11921
  const checkpointIncremental = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationReportingCheckpoint.name)(function* (deps, input, plannedMutants) {
11783
11922
  const report = yield* slimIncrementalReport(deps, input, checkpointResultsOf(input, plannedMutants));
11784
11923
  const json = yield* S.encodeEffect(S.fromJsonString(S.Unknown))(report).pipe(Effect$1.orDie);
11785
- yield* writeAtomic(deps, input.options.incrementalFile, json);
11924
+ yield* writeFileAtomic(deps, input.options.incrementalFile, json);
11786
11925
  });
11787
11926
  const checkpoint = (deps, input, plannedMutants) => Boolean$1$1.match(input.options.incremental, {
11788
11927
  onTrue: () => checkpointIncremental(deps, input, plannedMutants),
@@ -12399,8 +12538,8 @@ const renderJsonReport = Workflow.make({
12399
12538
  });
12400
12539
  //#endregion
12401
12540
  //#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) => ({
12541
+ 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"))));
12542
+ const readJsonReport = (input) => Effect$1.map(Stream.fromAsyncIterable(input.events, failAsJsonReporter).pipe(Stream.filterMap(reportOf$1), Stream.run(Sink.last())), (last) => ({
12404
12543
  _tag: "JsonReportCommand",
12405
12544
  reported: Option$1.getOrUndefined(last),
12406
12545
  rendered: true,
@@ -12834,7 +12973,7 @@ const logOf = (command, results) => {
12834
12973
  }]
12835
12974
  };
12836
12975
  };
12837
- const decide$20 = (command) => {
12976
+ const decide$21 = (command) => {
12838
12977
  const surfaced = surfacedOf(command);
12839
12978
  const results = Arr.take(surfaced, command.maxResults);
12840
12979
  const log = logOf(command, results);
@@ -12850,7 +12989,7 @@ const sarifReport = Workflow.make({
12850
12989
  command: SarifReportCommand,
12851
12990
  decision: SarifReportDecision,
12852
12991
  error: S.Never,
12853
- decide: (command) => Result.succeed(decide$20(command))
12992
+ decide: (command) => Result.succeed(decide$21(command))
12854
12993
  });
12855
12994
  //#endregion
12856
12995
  //#region src/cap-survivors.workflow.ts
@@ -12967,7 +13106,7 @@ const linesOf = (command) => {
12967
13106
  onFalse: () => Arr.flatMap(Option$1.toArray(HashMap.get(byId, ref.id)), (entry) => [lineOf(entry)])
12968
13107
  }));
12969
13108
  };
12970
- const decide$19 = (command) => {
13109
+ const decide$20 = (command) => {
12971
13110
  const lines = linesOf(command);
12972
13111
  return Boolean$1$1.match(Arr.length(lines) === 0, {
12973
13112
  onTrue: () => NothingToAnnotate.make({ survivorCount: Arr.length(command.survivors) }),
@@ -12978,7 +13117,7 @@ Workflow.make({
12978
13117
  command: RenderAnnotationsCommand,
12979
13118
  decision: RenderAnnotationsDecision,
12980
13119
  error: S.Never,
12981
- decide: (command) => Result.succeed(decide$19(command))
13120
+ decide: (command) => Result.succeed(decide$20(command))
12982
13121
  });
12983
13122
  //#endregion
12984
13123
  //#region src/surfacing.ts
@@ -13054,8 +13193,8 @@ const failAsSarif = (cause) => Reporter_exports.ReporterFailed.make({
13054
13193
  event: "mutationTestReportReady",
13055
13194
  cause: Option$1.getOrElse(Option$1.map(ErrorText_exports.errorTextOf(cause), (rendered) => rendered.text), () => "")
13056
13195
  });
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));
13196
+ 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"))));
13197
+ const readSarifReport = (events) => Effect$1.map(Stream.fromAsyncIterable(events, failAsSarif).pipe(Stream.filterMap(reportOf), Stream.run(Sink.last())), (last) => Option$1.getOrUndefined(last));
13059
13198
  const writeSarifReport = (options, report) => Effect$1.gen(function* () {
13060
13199
  const fs = yield* FileSystem.FileSystem;
13061
13200
  const path = yield* Path.Path;
@@ -13144,7 +13283,7 @@ var RunEnvironment = class extends Context.Service()("@systemfsoftware/stryker-j
13144
13283
  };
13145
13284
  const stageLayerOf = (env, events) => {
13146
13285
  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* () {
13286
+ 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
13287
  const stageScope = yield* Scope.make();
13149
13288
  yield* Effect$1.addFinalizer((exit) => Scope.close(stageScope, exit));
13150
13289
  return stageScope;
@@ -13512,7 +13651,7 @@ var WorkerCrashed = class extends S.TaggedClass()("WorkerCrashed", {
13512
13651
  };
13513
13652
  const ClassifyWorkerExitDecision = S.Union([WorkerOutOfMemory, WorkerCrashed]);
13514
13653
  const OutOfMemoryExitCode = S.Literals([134, 137]);
13515
- const decide$18 = (command) => Boolean$1$1.match(S.is(OutOfMemoryExitCode)(command.exitCode), {
13654
+ const decide$19 = (command) => Boolean$1$1.match(S.is(OutOfMemoryExitCode)(command.exitCode), {
13516
13655
  onTrue: () => Result.succeed(WorkerOutOfMemory.make({
13517
13656
  pid: command.pid,
13518
13657
  exitCode: command.exitCode
@@ -13526,7 +13665,7 @@ const classifyWorkerExit = Workflow.make({
13526
13665
  command: ClassifyWorkerExitCommand,
13527
13666
  decision: ClassifyWorkerExitDecision,
13528
13667
  error: S.Never,
13529
- decide: decide$18
13668
+ decide: decide$19
13530
13669
  });
13531
13670
  //#endregion
13532
13671
  //#region src/git-diff.schema.ts
@@ -13858,7 +13997,9 @@ const IncrementalReportSchema = S.StructWithRest(S.Struct({
13858
13997
  runInputsDigest: S.String,
13859
13998
  thresholds: ThresholdsLikeSchema,
13860
13999
  files: S.Record(S.String, FileResultLikeSchema),
14000
+ costs: MutantCosts,
13861
14001
  testFiles: S.optional(S.Record(S.String, TestFileLikeSchema)),
14002
+ budget: S.optional(RunEvent_exports.Budget),
13862
14003
  dryRunCoverage: S.optionalKey(S.Unknown)
13863
14004
  }), [S.Record(S.String, S.Unknown)]);
13864
14005
  //#endregion
@@ -13936,12 +14077,12 @@ const splitOf = (details, checkerCount) => Boolean$1$1.match(checkerCount > 0, {
13936
14077
  announcement: details.announcement
13937
14078
  })
13938
14079
  });
13939
- const decide$17 = (command) => Result.succeed(splitOf(detailsOf(command.concurrency, command.availableParallelism), command.checkerCount));
14080
+ const decide$18 = (command) => Result.succeed(splitOf(detailsOf(command.concurrency, command.availableParallelism), command.checkerCount));
13940
14081
  const resolveConcurrency = Workflow.make({
13941
14082
  command: ResolveConcurrency,
13942
14083
  decision: ConcurrencySplit,
13943
14084
  error: S.Never,
13944
- decide: decide$17
14085
+ decide: decide$18
13945
14086
  });
13946
14087
  //#endregion
13947
14088
  //#region src/concurrency.cell.ts
@@ -14173,7 +14314,7 @@ const discardOf = (reason, actual, expected) => IncrementalReportDiscard.make({
14173
14314
  actual,
14174
14315
  expected
14175
14316
  });
14176
- const decide$16 = (command) => Option$1.match(Option$1.fromUndefinedOr(command.report), {
14317
+ const decide$17 = (command) => Option$1.match(Option$1.fromUndefinedOr(command.report), {
14177
14318
  onNone: () => discardOf("noPriorRecord", void 0, command.expectedIncrementalVersion),
14178
14319
  onSome: (report) => Option$1.match(Option$1.firstSomeOf(identityMismatchesOf(command, report).map(mismatchOptionOf)), {
14179
14320
  onNone: () => IncrementalReportKeep.make({ report }),
@@ -14184,7 +14325,7 @@ const admitIncrementalReport = Workflow.make({
14184
14325
  command: AdmitIncrementalReportCommand,
14185
14326
  decision: S.Union([IncrementalReportKeep, IncrementalReportDiscard]),
14186
14327
  error: S.Never,
14187
- decide: (command) => Result.succeed(decide$16(command))
14328
+ decide: (command) => Result.succeed(decide$17(command))
14188
14329
  });
14189
14330
  //#endregion
14190
14331
  //#region src/git-diff.workflow.ts
@@ -14539,11 +14680,15 @@ const ALWAYS_IGNORE = Object.freeze([
14539
14680
  "/stryker.log",
14540
14681
  ".next",
14541
14682
  ".nuxt",
14542
- ".svelte-kit"
14683
+ ".svelte-kit",
14684
+ ".turbo"
14543
14685
  ]);
14544
14686
  const CRAWL_CONCURRENCY = 256;
14545
14687
  const DISCARD_TEXTS = {
14546
- noPriorRecord: (command) => `Unable to parse incremental result file at ${command.incrementalFile}; a full mutation testing run will be performed.`,
14688
+ noPriorRecord: (command) => `Unable to parse incremental result file at ${command.incrementalFile}${Option$1.match(Option$1.fromUndefinedOr(command.decodeError), {
14689
+ onNone: () => "",
14690
+ onSome: (error) => `: ${error}`
14691
+ })}; a full mutation testing run will be performed.`,
14547
14692
  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
14693
  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
14694
  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 +14746,14 @@ const isEntryIncluded = (ignorePatterns, entry) => {
14601
14746
  };
14602
14747
  const resolveInputFileNames = (ignorePatterns, basePath) => crawlDirectory(ignorePatterns, basePath, basePath).pipe(Stream.runCollect, Effect$1.map((files) => [...files]));
14603
14748
  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))));
14749
+ const decodeFailureText = (failure) => failure.message.replace(/\s+/gu, " ").trim();
14750
+ const reportReadOf = (contents) => Option$1.match(Option$1.fromUndefinedOr(contents), {
14751
+ onNone: () => ({}),
14752
+ onSome: (text) => Result.match(parseAndDecodeIncrementalReport(text), {
14753
+ onFailure: (failure) => ({ decodeError: decodeFailureText(failure) }),
14754
+ onSuccess: (report) => ({ report })
14755
+ })
14756
+ });
14605
14757
  const incrementalContentsOf = (fs, options) => Boolean$1.match(options.incremental, {
14606
14758
  onFalse: () => Effect$1.succeedNone,
14607
14759
  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 +14858,12 @@ const readProject = Effect$1.fn(SpanTaxonomy_exports.Spans.projectReadFromDisk.n
14706
14858
  yield* Effect$1.forEach(testFilePatterns, (pattern) => warnUnmatchedTestPattern(inputFileNames, input.basePath, pattern), { discard: true });
14707
14859
  const runInputsDigest = yield* runInputsDigestOf(fs, pathService, input.basePath, options);
14708
14860
  const contents = Option$1.getOrUndefined(yield* incrementalContentsOf(fs, options));
14861
+ const reportRead = reportReadOf(contents);
14709
14862
  return {
14710
14863
  _tag: "AdmitIncrementalReportCommand",
14711
- report: reportOf(contents),
14712
- expectedIncrementalVersion: "2",
14713
- verdictSemanticsVersion: 1,
14864
+ report: reportRead.report,
14865
+ expectedIncrementalVersion: "3",
14866
+ verdictSemanticsVersion: 3,
14714
14867
  mutantSetPolicy: options.mutator.mutantSetPolicy,
14715
14868
  runInputsDigest,
14716
14869
  options,
@@ -14719,6 +14872,7 @@ const readProject = Effect$1.fn(SpanTaxonomy_exports.Spans.projectReadFromDisk.n
14719
14872
  incremental: options.incremental,
14720
14873
  incrementalFile: options.incrementalFile,
14721
14874
  contents,
14875
+ decodeError: reportRead.decodeError,
14722
14876
  fileDescriptions: decision.fileDescriptions,
14723
14877
  testFiles: [...decision.testFiles]
14724
14878
  };
@@ -14771,6 +14925,58 @@ const readProjectCell = Sandwich.named(SpanTaxonomy_exports.Spans.projectRead.na
14771
14925
  }))
14772
14926
  });
14773
14927
  //#endregion
14928
+ //#region src/refuse-local-mutation.workflow.ts
14929
+ const LocalMutationDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js/LocalMutationDecision");
14930
+ var RefuseLocalMutationCommand = class extends S.TaggedClass()("RefuseLocalMutationCommand", {
14931
+ dryRunOnly: S.Boolean,
14932
+ githubActions: S.UndefinedOr(S.String),
14933
+ allowLocalMutation: S.UndefinedOr(S.String)
14934
+ }) {
14935
+ static [Workflow.InstrumentationBrand] = {};
14936
+ };
14937
+ var LocalMutationAllowed = class extends S.TaggedClass()("LocalMutationAllowed", {}) {
14938
+ [LocalMutationDecisionTypeId] = LocalMutationDecisionTypeId;
14939
+ };
14940
+ var LocalMutationRefused = class extends S.TaggedError()("LocalMutationRefused", {
14941
+ rule: RunEvent_exports.RefusalRule,
14942
+ message: S.String
14943
+ }) {};
14944
+ const decide$16 = (command) => Match$1.value({
14945
+ dryRunOnly: command.dryRunOnly,
14946
+ githubActions: command.githubActions,
14947
+ allowLocalMutation: command.allowLocalMutation
14948
+ }).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({
14949
+ rule: "mutation-runs-on-main-ci",
14950
+ message: "mutation runs only on main CI"
14951
+ }))));
14952
+ const refuseLocalMutation = Workflow.make({
14953
+ command: RefuseLocalMutationCommand,
14954
+ decision: LocalMutationAllowed,
14955
+ error: LocalMutationRefused,
14956
+ decide: decide$16
14957
+ });
14958
+ //#endregion
14959
+ //#region src/refuse-local-mutation.cell.ts
14960
+ const envOf = (key) => Config.option(Config.String(key)).pipe(Effect$1.map(Option$1.getOrUndefined), Effect$1.orElseSucceed(() => void 0));
14961
+ const guardOf = (input) => Effect$1.gen(function* () {
14962
+ const githubActions = yield* envOf("GITHUB_ACTIONS");
14963
+ const allowLocalMutation = yield* envOf("ALLOW_LOCAL_MUTATION");
14964
+ const command = RefuseLocalMutationCommand.make({
14965
+ dryRunOnly: input.options.dryRunOnly,
14966
+ githubActions,
14967
+ allowLocalMutation
14968
+ });
14969
+ return yield* Result.match(refuseLocalMutation(command), {
14970
+ onFailure: (refused) => Effect$1.fail(StageError.make({
14971
+ stage: "prepare",
14972
+ reason: refused.message,
14973
+ cause: refused
14974
+ })),
14975
+ onSuccess: () => Effect$1.succeed(input)
14976
+ });
14977
+ });
14978
+ const refuseLocalMutationCell = (input) => Cell.fromEffect(guardOf(input));
14979
+ //#endregion
14774
14980
  //#region src/dry-run-coverage.schema.ts
14775
14981
  const DryRunCoverageSchema = S.Struct({
14776
14982
  tests: S.Array(TestRunner_exports.TestResultSchema),
@@ -15954,6 +16160,35 @@ const buildTestRunner = dual(2, (context, childProcessRunner) => Option$1.match(
15954
16160
  onNone: () => decorateChildRunner(context, childProcessRunner)
15955
16161
  }));
15956
16162
  //#endregion
16163
+ //#region src/run/dry-run-coverage.ts
16164
+ const testsByIdOf = (result) => MutableHashMap.fromIterable(result.tests.map((test) => [test.id, test]));
16165
+ const coveredMutantIdsOf = (coverage) => Object.entries(coverage).filter(([, count]) => count > 0).map(([mutantId]) => mutantId);
16166
+ const testsByMutantIdOf = (mutantCoverage, testsById) => Object.entries(mutantCoverage.perTest).reduce((testsByMutantId, [testId, coverage]) => Option$1.match(MutableHashMap.get(testsById, testId), {
16167
+ onNone: () => testsByMutantId,
16168
+ onSome: (test) => coveredMutantIdsOf(coverage).reduce((acc, mutantId) => MutableHashMap.set(acc, mutantId, MutableHashSet.add(Option$1.getOrElse(MutableHashMap.get(acc, mutantId), () => MutableHashSet.empty()), test)), testsByMutantId)
16169
+ }), MutableHashMap.empty());
16170
+ 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());
16171
+ const testCoverageOf = ({ result, dryRunCoverage }) => {
16172
+ const testsById = testsByIdOf(result);
16173
+ const mutantCoverage = Option$1.fromNullishOr(result.mutantCoverage);
16174
+ return {
16175
+ testsByMutantId: Option$1.match(mutantCoverage, {
16176
+ onNone: () => MutableHashMap.empty(),
16177
+ onSome: (coverage) => testsByMutantIdOf(coverage, testsById)
16178
+ }),
16179
+ testsById,
16180
+ staticCoverage: Option$1.match(mutantCoverage, {
16181
+ onNone: () => void 0,
16182
+ onSome: (coverage) => coverage.static
16183
+ }),
16184
+ hitsByMutantId: Option$1.match(mutantCoverage, {
16185
+ onNone: () => MutableHashMap.empty(),
16186
+ onSome: (coverage) => hitsByMutantIdOf(coverage)
16187
+ }),
16188
+ dryRunCoverage
16189
+ };
16190
+ };
16191
+ //#endregion
15957
16192
  //#region src/run/incremental-reuse.ts
15958
16193
  const absoluteSourceOf = (path, basePath, file) => path.isAbsolute(file) ? file : path.join(basePath, file);
15959
16194
  const matchedSourcesOf = Effect$1.fnUntraced(function* (input) {
@@ -16283,33 +16518,6 @@ const withOriginalFileName = (test, prev) => Match$1.value(test.fileName).pipe(M
16283
16518
  fileName: originalFileFor(prev.sandbox, fileName)
16284
16519
  })), Match$1.orElse(() => test));
16285
16520
  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
16521
  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
16522
  const reportDryRunCompleted = (raw, tests, overheadMillis) => offerReporterEvent(raw.prev.reporterStage, Reporter_exports.DryRunCompleted.make({
16315
16523
  timing: {
@@ -16333,7 +16541,10 @@ const freshCoverageOf = (raw, dryRunResult, overheadMillis) => ({
16333
16541
  const dryRunDoneOf = (raw, dryRunResult, coverage) => ({
16334
16542
  ...raw.prev,
16335
16543
  dryRunResult,
16336
- testCoverage: testCoverageFrom(dryRunResult, coverage),
16544
+ testCoverage: testCoverageOf({
16545
+ result: dryRunResult,
16546
+ dryRunCoverage: coverage
16547
+ }),
16337
16548
  timeOverhead: Duration.millis(coverage.timeOverheadMs)
16338
16549
  });
16339
16550
  const completeFreshDryRun = Effect$1.fnUntraced(function* (raw, dryRunResult) {
@@ -16363,6 +16574,7 @@ const completeDryRunPassed = (raw) => Match$1.value(raw.rawResult).pipe(Match$1.
16363
16574
  }))), Match$1.exhaustive);
16364
16575
  const runFreshDryRun = Effect$1.fnUntraced(function* (command, idGenerator, currentTestClosureDigest, runInputsDigest) {
16365
16576
  const env = yield* RunEnvironment;
16577
+ const reports = yield* WorkerReports;
16366
16578
  const { files, testFiles } = yield* resolveDryRunFiles(command);
16367
16579
  const dryRunTimeout = command.options.dryRunTimeoutMinutes * 60 * 1e3;
16368
16580
  const options = dryRunOptionsOf(command, files, testFiles, dryRunTimeout);
@@ -16378,6 +16590,7 @@ const runFreshDryRun = Effect$1.fnUntraced(function* (command, idGenerator, curr
16378
16590
  idGenerator
16379
16591
  })));
16380
16592
  });
16593
+ const startedAt = yield* Clock.currentTimeMillis;
16381
16594
  const runner = yield* buildTestRunner({
16382
16595
  options: command.options,
16383
16596
  fileDescriptions: command.project.fileDescriptions,
@@ -16386,6 +16599,7 @@ const runFreshDryRun = Effect$1.fnUntraced(function* (command, idGenerator, curr
16386
16599
  retire: Effect$1.void,
16387
16600
  testFiles: testFiles ?? []
16388
16601
  }, childRunnerEffect);
16602
+ yield* reports.report("testRunner", (yield* Clock.currentTimeMillis) - startedAt);
16389
16603
  const capabilities = yield* runner.capabilities.pipe(Effect$1.mapError((cause) => StageError.make({
16390
16604
  stage: "dryRun",
16391
16605
  reason: "Failed to get test runner capabilities",
@@ -17337,6 +17551,10 @@ const FailedCheckResultSchema = S.Struct({
17337
17551
  status: S.Literal("compileError"),
17338
17552
  reason: S.String
17339
17553
  });
17554
+ const IgnoredCheckResultSchema = S.Struct({
17555
+ status: S.Literal("ignored"),
17556
+ reason: S.String
17557
+ });
17340
17558
  var CheckedPlanPassed = class extends S.TaggedClass()("CheckedPlanPassed", {
17341
17559
  mutantId: Mutant_exports.MutantId,
17342
17560
  entryIndex: S.Int
@@ -17351,27 +17569,48 @@ var CheckedPlanFailed = class extends S.TaggedClass()("CheckedPlanFailed", {
17351
17569
  }) {
17352
17570
  [CheckedPlanTypeId] = CheckedPlanTypeId;
17353
17571
  };
17572
+ var CheckedPlanIgnored = class extends S.TaggedClass()("CheckedPlanIgnored", {
17573
+ mutantId: Mutant_exports.MutantId,
17574
+ entryIndex: S.Int,
17575
+ reason: S.String,
17576
+ result: IgnoredCheckResultSchema
17577
+ }) {
17578
+ [CheckedPlanTypeId] = CheckedPlanTypeId;
17579
+ };
17354
17580
  var PartitionCheckedPlansCommand = class extends S.TaggedClass()("PartitionCheckedPlansCommand", { checked: S.Array(S.Tuple([Mutant_exports.MutantId, Checker_exports.CheckResultSchema])) }) {
17355
17581
  static [Workflow.InstrumentationBrand] = {};
17356
17582
  };
17357
- const isFailed = (result) => result.status !== "passed";
17583
+ const isFailed = (result) => result.status === "compileError";
17584
+ const isIgnored = (result) => result.status === "ignored";
17358
17585
  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({
17586
+ const decideCheckedPlan = (mutantId, result, entryIndex) => Option$1.match(Option$1.liftPredicate(result, isIgnored), {
17587
+ onSome: (ignored) => CheckedPlanIgnored.make({
17365
17588
  mutantId,
17366
17589
  entryIndex,
17367
- reason: failedReasonOf(failed),
17368
- result: failed
17590
+ reason: ignored.reason,
17591
+ result: ignored
17592
+ }),
17593
+ onNone: () => Option$1.match(Option$1.liftPredicate(result, isFailed), {
17594
+ onNone: () => CheckedPlanPassed.make({
17595
+ mutantId,
17596
+ entryIndex
17597
+ }),
17598
+ onSome: (failed) => CheckedPlanFailed.make({
17599
+ mutantId,
17600
+ entryIndex,
17601
+ reason: failedReasonOf(failed),
17602
+ result: failed
17603
+ })
17369
17604
  })
17370
17605
  });
17371
17606
  const decide$8 = (command) => Result.succeed(command.checked.map(([mutantId, result], entryIndex) => decideCheckedPlan(mutantId, result, entryIndex)));
17372
17607
  const partitionCheckedPlans = Workflow.make({
17373
17608
  command: PartitionCheckedPlansCommand,
17374
- decision: S.Array(S.Union([CheckedPlanPassed, CheckedPlanFailed])),
17609
+ decision: S.Array(S.Union([
17610
+ CheckedPlanPassed,
17611
+ CheckedPlanFailed,
17612
+ CheckedPlanIgnored
17613
+ ])),
17375
17614
  error: S.Never,
17376
17615
  decide: decide$8
17377
17616
  });
@@ -17408,6 +17647,7 @@ const onCheckerSlot = (pool, checkerIndex, run) => Pool.use(pool, (slot) => Opti
17408
17647
  }));
17409
17648
  const noPassedPlans = [];
17410
17649
  const noFailedChecks = [];
17650
+ const noIgnoredChecks = [];
17411
17651
  const splitCheckedPlans = Effect$1.fn(SpanTaxonomy_exports.Spans.checkerPoolSplitChecked.name)(function* (checked) {
17412
17652
  const decisions = yield* Effect$1.fromResult(partitionCheckedPlans(PartitionCheckedPlansCommand.make({ checked: checked.map(([plan, result]) => [plan.mutant.id, result]) })));
17413
17653
  return {
@@ -17418,6 +17658,10 @@ const splitCheckedPlans = Effect$1.fn(SpanTaxonomy_exports.Spans.checkerPoolSpli
17418
17658
  failedChecks: decisions.flatMap((decision) => Option$1.match(Option$1.liftPredicate(decision, S.is(CheckedPlanFailed)), {
17419
17659
  onNone: () => [],
17420
17660
  onSome: (failed) => Option$1.toArray(Option$1.map(Arr.get(checked, failed.entryIndex), ([plan]) => [plan, failed.result]))
17661
+ })),
17662
+ ignoredChecks: decisions.flatMap((decision) => Option$1.match(Option$1.liftPredicate(decision, S.is(CheckedPlanIgnored)), {
17663
+ onNone: () => [],
17664
+ onSome: (ignored) => Option$1.toArray(Option$1.map(Arr.get(checked, ignored.entryIndex), ([plan]) => [plan, ignored.result]))
17421
17665
  }))
17422
17666
  };
17423
17667
  });
@@ -17426,23 +17670,34 @@ const failedElement = (failedChecks) => Option$1.match(Arr.head(failedChecks), {
17426
17670
  onNone: () => Stream.empty,
17427
17671
  onSome: () => Stream.succeed({
17428
17672
  passedPlans: noPassedPlans,
17429
- failedChecks
17673
+ failedChecks,
17674
+ ignoredChecks: noIgnoredChecks
17675
+ })
17676
+ });
17677
+ const ignoredElement = (ignoredChecks) => Option$1.match(Arr.head(ignoredChecks), {
17678
+ onNone: () => Stream.empty,
17679
+ onSome: () => Stream.succeed({
17680
+ passedPlans: noPassedPlans,
17681
+ failedChecks: noFailedChecks,
17682
+ ignoredChecks
17430
17683
  })
17431
17684
  });
17432
17685
  const checkedGroupsFor = (pool, checkerNames, checkerIndex, plans) => Option$1.match(Arr.get(checkerNames, checkerIndex), {
17433
17686
  onNone: () => Stream.succeed({
17434
17687
  passedPlans: plans,
17435
- failedChecks: noFailedChecks
17688
+ failedChecks: noFailedChecks,
17689
+ ignoredChecks: noIgnoredChecks
17436
17690
  }),
17437
17691
  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
17692
  concurrency: "unbounded",
17439
17693
  unordered: true
17440
- }), Stream.flatMap((split) => Stream.concat(failedElement(split.failedChecks), checkedGroupsFor(pool, checkerNames, checkerIndex + 1, split.passedPlans)))))))
17694
+ }), Stream.flatMap((split) => Stream.concat(ignoredElement(split.ignoredChecks), Stream.concat(failedElement(split.failedChecks), checkedGroupsFor(pool, checkerNames, checkerIndex + 1, split.passedPlans))))))))
17441
17695
  });
17442
17696
  const checkPlansStream = dual(2, (checkerPool, plans) => Option$1.fromNullishOr(checkerPool).pipe(Option$1.match({
17443
17697
  onNone: () => Stream.succeed({
17444
17698
  passedPlans: plans,
17445
- failedChecks: noFailedChecks
17699
+ failedChecks: noFailedChecks,
17700
+ ignoredChecks: noIgnoredChecks
17446
17701
  }),
17447
17702
  onSome: (handle) => checkerNamesOf(CheckerPoolHandle.slot(handle)).pipe(Effect$1.map((checkerNames) => checkedGroupsFor(CheckerPoolHandle.slot(handle), checkerNames, 0, plans)), Stream.unwrap)
17448
17703
  }), Stream.withSpan(SpanTaxonomy_exports.Spans.checkerPoolCheckPlans.name)));
@@ -17450,16 +17705,23 @@ Effect$1.fnUntraced(function* (checkerPool, plans) {
17450
17705
  const checkedGroups = yield* Stream.runCollect(checkPlansStream(checkerPool, plans));
17451
17706
  const passedPlans = [];
17452
17707
  const failedChecks = [];
17708
+ const ignoredChecks = [];
17453
17709
  for (const group of checkedGroups) {
17454
17710
  passedPlans.push(...group.passedPlans);
17455
17711
  failedChecks.push(...group.failedChecks);
17712
+ ignoredChecks.push(...group.ignoredChecks);
17456
17713
  }
17457
17714
  return {
17458
17715
  passedPlans,
17459
- failedChecks
17716
+ failedChecks,
17717
+ ignoredChecks
17460
17718
  };
17461
17719
  });
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(), {
17720
+ const runCheckedPlans = dual(2, (self, execution) => self.pipe(Stream.flatMap(({ passedPlans, failedChecks, ignoredChecks }) => Stream.fromIterable([
17721
+ ...failedChecks.map(([plan, result]) => () => execution.settleFailure(plan, result)),
17722
+ ...ignoredChecks.map(([plan, result]) => () => execution.settleIgnored(plan, result)),
17723
+ ...passedPlans.map((plan) => () => execution.runPlan(plan))
17724
+ ])), Stream.mapEffect((work) => work(), {
17463
17725
  concurrency: Math.max(1, execution.concurrency),
17464
17726
  unordered: true
17465
17727
  })));
@@ -17514,11 +17776,13 @@ const checkerWorkerSpawnOf = (loaded, configured) => Effect$1.mapError(Effect$1.
17514
17776
  cause: PluginNotFoundError.make({ descriptor: missing.descriptor })
17515
17777
  }));
17516
17778
  const acquire$1 = Effect$1.fnUntraced(function* (spec) {
17779
+ const reports = yield* WorkerReports;
17517
17780
  return yield* Boolean$1$1.match(spec.options.checkers.length === 0, {
17518
17781
  onTrue: () => Effect$1.as(Effect$1.void, void 0),
17519
17782
  onFalse: () => Pool.make({
17520
17783
  acquire: Effect$1.forEach(spec.options.checkers, (checker) => Effect$1.gen(function* () {
17521
17784
  const resolved = yield* checkerWorkerSpawnOf(spec.loadedPlugins, ConfiguredPluginModulePath.make({ modulePath: checker.plugin }));
17785
+ const startedAt = yield* Clock.currentTimeMillis;
17522
17786
  const service = yield* scoped$2({
17523
17787
  options: {
17524
17788
  ...spec.options,
@@ -17530,6 +17794,7 @@ const acquire$1 = Effect$1.fnUntraced(function* (spec) {
17530
17794
  times: CHECKER_ACQUIRE_RETRIES,
17531
17795
  while: isCheckerCrash
17532
17796
  }));
17797
+ yield* reports.report("checker", (yield* Clock.currentTimeMillis) - startedAt);
17533
17798
  return {
17534
17799
  checkerName: resolved.name,
17535
17800
  checker: service
@@ -17887,7 +18152,7 @@ const readIncrementalReuseCommand = Effect$1.fn(SpanTaxonomy_exports.Spans.incre
17887
18152
  previousRecords: [...previousRecords],
17888
18153
  closureDigestsByMutantId: closures.digests,
17889
18154
  closureAnalysisFailed: closures.failed,
17890
- verdictSemanticsVersion: 1,
18155
+ verdictSemanticsVersion: 3,
17891
18156
  mutantSetPolicy: input.options.mutator.mutantSetPolicy,
17892
18157
  runInputsDigest,
17893
18158
  force: input.force,
@@ -18070,6 +18335,7 @@ const toReportedMutant = (mutant) => Object.assign(mutant, {
18070
18335
  coveredBy: mutant.coveredBy,
18071
18336
  static: mutant.static
18072
18337
  });
18338
+ const isNoCoveragePlan = (plan) => plan.runOptions.testFilter !== void 0 && plan.runOptions.testFilter.length === 0;
18073
18339
  const inPlannedOrder = ({ planned, results }) => {
18074
18340
  const rankById = Object.fromEntries(planned.map((mutant, index) => [mutant.id, index]));
18075
18341
  return Arr.sortWith(results, (result) => rankById[result.id] ?? planned.length, Order.Number);
@@ -18107,11 +18373,14 @@ const reportingInputOf = (input) => ({
18107
18373
  options: input.prev.options,
18108
18374
  project: input.prev.project,
18109
18375
  testCoverage: input.prev.testCoverage,
18376
+ timeOverheadMs: Duration.toMillis(input.prev.timeOverhead),
18110
18377
  runId: input.env.runId,
18111
18378
  resolvedMode: input.env.resolvedMode,
18112
18379
  basePath: input.env.basePath,
18113
18380
  reporterStage: input.prev.reporterStage,
18114
- formatRegistry: input.prev.formatRegistry
18381
+ formatRegistry: input.prev.formatRegistry,
18382
+ concurrency: input.prev.concurrency.testRunners + input.prev.concurrency.checkers,
18383
+ runStartedAt: input.env.runStartedAt
18115
18384
  });
18116
18385
  const preparedStreamableOf = Effect$1.fnUntraced(function* (context, result) {
18117
18386
  return yield* Option$1.match(Option$1.filter(Option$1.some(result.status), isMutantStatus), {
@@ -18365,10 +18634,11 @@ const coveredByField = (coveredBy) => Option$1.match(Option$1.fromUndefinedOr(co
18365
18634
  onNone: () => ({}),
18366
18635
  onSome: (present) => ({ coveredBy: [...present] })
18367
18636
  });
18368
- const testFilterField = (testFilter) => Option$1.match(Option$1.filter(Option$1.fromUndefinedOr(testFilter), (filter) => filter.length > 0), {
18637
+ const testFilterField = (testFilter) => Option$1.match(Option$1.fromUndefinedOr(testFilter), {
18369
18638
  onNone: () => ({}),
18370
18639
  onSome: (present) => ({ testFilter: [...present] })
18371
18640
  });
18641
+ const nonEmptyTestFilterOf = (tests) => Option$1.getOrUndefined(Option$1.filter(Option$1.fromUndefinedOr(tests), (present) => present.length > 0));
18372
18642
  const staticCoverageCountOf = (staticCoverage, mutantId) => Option$1.getOrElse(Option$1.flatMap(Option$1.fromNullishOr(staticCoverage), (countsByMutantId) => Record$1.get(countsByMutantId, mutantId)), () => 0);
18373
18643
  const hasCoverageForPlan = (staticCoverage) => Option$1.isSome(Option$1.fromUndefinedOr(staticCoverage));
18374
18644
  const mutantIsStatic = (command, mutantId) => staticCoverageCountOf(command.staticCoverage, mutantId) > 0;
@@ -18454,10 +18724,13 @@ const orderedTestsOf = (command, mutantId, tests) => {
18454
18724
  onSome: (present) => [present, ...rest]
18455
18725
  });
18456
18726
  };
18457
- const runWithCoveredTests = (mutant, command, isStatic, tests, coveredBy) => toRunPlan(mutant, command, calculateTotalTimeForIds(tests, command.testTimeById), orderedTestsOf(command, mutant.id, tests), isStatic, coveredBy);
18727
+ const runWithCoveredTests = (mutant, command, isStatic, tests, coveredBy) => toRunPlan(mutant, command, calculateTotalTimeForIds(tests, command.testTimeById), nonEmptyTestFilterOf(orderedTestsOf(command, mutant.id, tests)), isStatic, coveredBy);
18728
+ const NO_COVERAGE_NET_TIME_MS = 0;
18729
+ const NO_COVERAGE_TEST_FILTER = [];
18730
+ const toNoCoverageRunPlan = (mutant, command, isStatic, coveredBy) => toRunPlan(mutant, command, NO_COVERAGE_NET_TIME_MS, NO_COVERAGE_TEST_FILTER, isStatic, coveredBy);
18458
18731
  const planForUncoveredStatic = (mutant, command, isStatic, coveredBy) => Boolean$1$1.match(command.options.ignoreStatic, {
18459
18732
  onTrue: () => Result.succeed(toEarlyResultPlan(mutant, isStatic, "Ignored", IGNORED_STATIC_MUTANT_REASON, coveredBy)),
18460
- onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, command.globalTestFilter, isStatic, coveredBy)
18733
+ onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, nonEmptyTestFilterOf(command.globalTestFilter), isStatic, coveredBy)
18461
18734
  });
18462
18735
  const planForStaticallyCovered = (mutant, command, isStatic) => {
18463
18736
  const tests = Option$1.getOrElse(Record$1.get(command.testsByMutantId, mutant.id), () => []);
@@ -18486,10 +18759,10 @@ const decidePlanForMutant = (mutant, command) => {
18486
18759
  return Option$1.match(Option$1.fromUndefinedOr(mutant.status), {
18487
18760
  onSome: (status) => Result.succeed(toEarlyResultPlan(mutant, isStatic, status, mutant.statusReason, coveredByOfMutant(mutant))),
18488
18761
  onNone: () => Boolean$1$1.match(isPerTestUncoveredNonStatic(command, mutant, isStatic), {
18489
- onTrue: () => Result.succeed(toEarlyResultPlan(mutant, isStatic, "NoCoverage", void 0, coveringTestIdsOf(command, mutant.id))),
18762
+ onTrue: () => toNoCoverageRunPlan(mutant, command, isStatic, coveringTestIdsOf(command, mutant.id)),
18490
18763
  onFalse: () => Boolean$1$1.match(hasCoverageForPlan(command.staticCoverage), {
18491
18764
  onTrue: () => planForStaticallyCovered(mutant, command, isStatic),
18492
- onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, command.globalTestFilter, void 0, void 0)
18765
+ onFalse: () => toRunPlan(mutant, command, command.timeSpentAllTests, nonEmptyTestFilterOf(command.globalTestFilter), void 0, void 0)
18493
18766
  })
18494
18767
  })
18495
18768
  });
@@ -18781,7 +19054,10 @@ const planMutationTest = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationTestPlan
18781
19054
  }))
18782
19055
  })).pipe(Effect$1.ignoreCause);
18783
19056
  const progressQueue = yield* RunEvents;
18784
- yield* Queue.offer(progressQueue, RunEvent_exports.PlanKnown.make({ total: plansForReporter.length + earlyResults.length }));
19057
+ yield* Queue.offer(progressQueue, RunEvent_exports.PlanKnown.make({
19058
+ total: plansForReporter.length + earlyResults.length,
19059
+ shardPlan: null
19060
+ }));
18785
19061
  return {
18786
19062
  runPlans: sortedPlans,
18787
19063
  earlyResults,
@@ -18804,8 +19080,10 @@ const testRunnerWorkerSpawnOf = (loaded, configured) => Effect$1.mapError(Effect
18804
19080
  cause: PluginNotFoundError.make({ descriptor: missing.descriptor })
18805
19081
  }));
18806
19082
  const acquire = Effect$1.fnUntraced(function* (spec) {
18807
- return yield* Pool.makeWithTTL({
18808
- acquire: buildTestRunner({
19083
+ const reports = yield* WorkerReports;
19084
+ const acquireOne = Effect$1.gen(function* () {
19085
+ const startedAt = yield* Clock.currentTimeMillis;
19086
+ const runner = yield* buildTestRunner({
18809
19087
  options: spec.options,
18810
19088
  fileDescriptions: spec.fileDescriptions,
18811
19089
  sandboxWorkingDirectory: spec.sandboxWorkingDirectory,
@@ -18818,7 +19096,12 @@ const acquire = Effect$1.fnUntraced(function* (spec) {
18818
19096
  sandboxWorkingDirectory: spec.sandboxWorkingDirectory,
18819
19097
  workerEntrypoint: resolved.entrypoint,
18820
19098
  idGenerator: spec.idGenerator
18821
- }))))),
19099
+ })))));
19100
+ yield* reports.report("testRunner", (yield* Clock.currentTimeMillis) - startedAt);
19101
+ return runner;
19102
+ });
19103
+ return yield* Pool.makeWithTTL({
19104
+ acquire: acquireOne,
18822
19105
  min: spec.min,
18823
19106
  max: spec.max,
18824
19107
  timeToLive: IDLE_TIME_TO_LIVE
@@ -18831,6 +19114,9 @@ const TestRunnerPools = Blueprint.make()(TypeId).steps({
18831
19114
  const scoped = (spec) => TestRunnerPools.of(spec).scoped;
18832
19115
  //#endregion
18833
19116
  //#region src/run/mutation-test.cell.ts
19117
+ const TCE_EQUIVALENT_TO_ORIGINAL_REASON = "equivalent-to-original: tce";
19118
+ const TCE_DUPLICATE_AT_SITE_REASON = "duplicate-at-site: tce";
19119
+ const countIgnoredByReason = (results, reason) => results.filter((result) => result.status === "Ignored" && result.statusReason === reason).length;
18834
19120
  const writeMutationTestNoTests = Effect$1.fn(SpanTaxonomy_exports.Spans.mutationTestNoTests.name)(function* () {
18835
19121
  const env = yield* RunEnvironment;
18836
19122
  const now = yield* Clock.currentTimeMillis;
@@ -18934,22 +19220,26 @@ const proceedPipeline = Effect$1.fnUntraced(function* (raw) {
18934
19220
  });
18935
19221
  const runResults = yield* Effect$1.scoped(Effect$1.gen(function* () {
18936
19222
  const checkpoint = yield* makeCheckpointWriter(context, settledResults);
19223
+ const settleReported = (reported) => Effect$1.gen(function* () {
19224
+ yield* announceSettledMutant(context, reported);
19225
+ yield* checkpoint.record(reported);
19226
+ return reported;
19227
+ });
18937
19228
  return yield* withPhaseSpan(SpanTaxonomy_exports.Spans.mutationTestBatch, {
18938
19229
  total: plan.plannedTotal,
18939
19230
  testRunners: prev.concurrency.testRunners
18940
19231
  }, () => 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;
19232
+ settleFailure: (mutantPlan, result) => reporting.reportCheckFailure(toReportedMutant(mutantPlan.mutant), result).pipe(Effect$1.flatMap(settleReported)),
19233
+ settleIgnored: (mutantPlan, result) => reporting.reportIgnored(toReportedMutant(mutantPlan.mutant), result).pipe(Effect$1.flatMap(settleReported)),
19234
+ runPlan: (runPlan) => Option$1.match(Option$1.liftPredicate(runPlan, isNoCoveragePlan), {
19235
+ onNone: () => Effect$1.scoped(mutantRunCell.run({
19236
+ context,
19237
+ testRunnerPool,
19238
+ checkpoint,
19239
+ plan: runPlan
19240
+ })),
19241
+ onSome: (noCoverageRunPlan) => reporting.reportNoCoverage(toReportedMutant(noCoverageRunPlan.mutant)).pipe(Effect$1.flatMap(settleReported))
18946
19242
  }),
18947
- runPlan: (runPlan) => Effect$1.scoped(mutantRunCell.run({
18948
- context,
18949
- testRunnerPool,
18950
- checkpoint,
18951
- plan: runPlan
18952
- })),
18953
19243
  concurrency: testRunnerCapacity
18954
19244
  }).pipe(Stream.runFold(() => [], (acc, result) => {
18955
19245
  acc.push(result);
@@ -18965,6 +19255,10 @@ const proceedPipeline = Effect$1.fnUntraced(function* (raw) {
18965
19255
  requested: requestedIdsOf(prev.options),
18966
19256
  results: allResults
18967
19257
  }), (detail) => Queue.offer(progressQueue, detail), { discard: true });
19258
+ yield* Queue.offer(progressQueue, RunEvent_exports.TceReported.make({
19259
+ equivalentToOriginal: countIgnoredByReason(allResults, TCE_EQUIVALENT_TO_ORIGINAL_REASON),
19260
+ duplicateAtSite: countIgnoredByReason(allResults, TCE_DUPLICATE_AT_SITE_REASON)
19261
+ }));
18968
19262
  const outcomeResult = yield* reporting.reportAll({
18969
19263
  ...reportingInputOf({
18970
19264
  prev,
@@ -20056,11 +20350,11 @@ const prepareCell = Sandwich.named(SpanTaxonomy_exports.Spans.prepare.name)(read
20056
20350
  //#endregion
20057
20351
  //#region src/run/run-stages.cell.ts
20058
20352
  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({
20353
+ const mutationTestCell = Cell.andThen(Cell.andThen(Cell.andThen(Cell.andThen(Cell.mapError(loadConfigCell, (cause) => StageError.make({
20060
20354
  stage: "prepare",
20061
20355
  reason: configReadReasonOf(cause),
20062
20356
  cause
20063
- })), Cell.mapError(readProjectCell, (cause) => StageError.make({
20357
+ })), (loaded) => refuseLocalMutationCell(loaded)), Cell.mapError(readProjectCell, (cause) => StageError.make({
20064
20358
  stage: "prepare",
20065
20359
  reason: "Failed to read project",
20066
20360
  cause
@@ -20203,7 +20497,7 @@ S.Struct({
20203
20497
  mode: OutputMode_exports.OutputMode,
20204
20498
  signal: OutputMode_exports.ModeSignal
20205
20499
  });
20206
- const nextFramingState = (state, event) => Match$1.value(event).pipe(Match$1.tag("verdict", "error", "help", () => FramingState.make({
20500
+ const nextFramingState = (state, event) => Match$1.value(event).pipe(Match$1.tag("verdict", "error", "help", "refused", () => FramingState.make({
20207
20501
  mode: state.mode,
20208
20502
  signal: state.signal,
20209
20503
  headerWritten: state.headerWritten,
@@ -20224,7 +20518,7 @@ const nextFramingState = (state, event) => Match$1.value(event).pipe(Match$1.tag
20224
20518
  terminalSeen: state.terminalSeen,
20225
20519
  completed: e.completed,
20226
20520
  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);
20521
+ })), 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
20522
  const noteState = (state, event) => nextFramingState(state, event);
20229
20523
  const formatScore = (score) => Option$1.match(Option$1.fromNullishOr(score), {
20230
20524
  onNone: () => "n/a",
@@ -20234,7 +20528,7 @@ const formatTotal = (total) => Option$1.match(Option$1.fromNullishOr(total), {
20234
20528
  onNone: () => "?",
20235
20529
  onSome: (val) => String(val)
20236
20530
  });
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);
20531
+ 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
20532
  const stderrLineFor = (state, event) => Boolean$1$1.match(Boolean$1$1.and(state.mode === "human", !state.terminalSeen), {
20239
20533
  onTrue: () => formatStderrEvent(event),
20240
20534
  onFalse: () => null
@@ -20398,16 +20692,17 @@ const CONFIG_CODE = 2;
20398
20692
  const UNKNOWN_FAILURE = "Unknown failure";
20399
20693
  const SIGNAL_REMEDIATION = "the run was interrupted by a signal; re-run it to continue";
20400
20694
  const PARSE_REMEDIATION = "re-run with --help to see the full usage";
20695
+ const REFUSAL_REMEDIATION = "run mutation testing on main CI (GitHub Actions)";
20401
20696
  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);
20697
+ 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
20698
  const nonEmptyText = Option$1.liftPredicate(S.is(S.NonEmptyString));
20404
20699
  const capturedOrUnknown = (captured) => Option$1.getOrElse(nonEmptyText(captured), () => UNKNOWN_FAILURE);
20405
20700
  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);
20701
+ 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
20702
  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
20703
  onSome: (detail) => `check the config file: ${detail}`,
20409
20704
  onNone: () => "check the config file"
20410
- })), Match$1.tag("RunFailed", () => DEFAULT_REMEDIATION), Match$1.exhaustive);
20705
+ })), Match$1.tag("RunRefused", () => REFUSAL_REMEDIATION), Match$1.tag("RunFailed", () => DEFAULT_REMEDIATION), Match$1.exhaustive);
20411
20706
  const errorEnvelopeFromOutcome = (input) => ErrorEnvelope.make({
20412
20707
  schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
20413
20708
  code: exitCodeOf(input.error),
@@ -20504,22 +20799,33 @@ const emitNullScoreVerdict = (params) => {
20504
20799
  version: StrykerPackage.version
20505
20800
  }
20506
20801
  };
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
- }));
20802
+ return Effect$1.gen(function* () {
20803
+ const now = yield* Clock.currentTimeMillis;
20804
+ const envelope = buildVerdictEnvelope({
20805
+ ...report,
20806
+ budget: {
20807
+ predictedSeconds: 0,
20808
+ actualSeconds: (now - stream.startedAt) / 1e3
20809
+ }
20810
+ }, mode.mode, mode.signal, stream.runId, basePath, pathService, Option$1.none(), null);
20811
+ yield* Queue.offer(stream.queue, RunEvent_exports.VerdictReached.make({
20812
+ schemaVersion: envelope.schemaVersion,
20813
+ runId: envelope.runId,
20814
+ mode: envelope.mode,
20815
+ signal: envelope.signal,
20816
+ score: envelope.score,
20817
+ thresholds: envelope.thresholds,
20818
+ reportFile: envelope.reportFile,
20819
+ counts: envelope.counts,
20820
+ mutants: envelope.mutants,
20821
+ scope: envelope.scope,
20822
+ mutantSetPolicy: envelope.mutantSetPolicy,
20823
+ incrementalMode: envelope.incrementalMode,
20824
+ phaseDurations: envelope.phaseDurations,
20825
+ static: envelope.static,
20826
+ budget: envelope.budget
20827
+ }));
20828
+ });
20523
20829
  };
20524
20830
  const offerFailureEnvelope = (stream, failed, captured) => {
20525
20831
  const envelope = errorEnvelopeFromOutcome({
@@ -20534,6 +20840,11 @@ const offerFailureEnvelope = (stream, failed, captured) => {
20534
20840
  reason: null
20535
20841
  }));
20536
20842
  };
20843
+ const offerRefusedEnvelope = (stream, refused) => Queue.offer(stream.queue, RunEvent_exports.Refused.make({
20844
+ schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
20845
+ rule: refused.rule,
20846
+ message: refused.message
20847
+ }));
20537
20848
  const emitHelpEnvelope = (stream, help) => Queue.offer(stream.queue, RunEvent_exports.HelpRendered.make({
20538
20849
  schemaVersion: RunEvent_exports.StreamSchemaVersion.literal,
20539
20850
  code: 0,
@@ -20565,7 +20876,7 @@ const emitMachineModeOutput = Effect$1.fn(SpanTaxonomy_exports.Spans.runEventStr
20565
20876
  onSuccess: (decision) => Match$1.value(decision).pipe(Match$1.tag("RunOk", (ok) => Option$1.match(helpPayload(ok, captured), {
20566
20877
  onSome: (help) => emitHelpEnvelope(stream, help),
20567
20878
  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),
20879
+ })), 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
20880
  onFailure: (failure) => offerFailureEnvelope(stream, failure, captured)
20570
20881
  });
20571
20882
  });