@systemfsoftware/stryker-js 10.0.1 → 10.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { $ as WorkerPluginKind, F as MutationTestingPlanReady, G as TRACEPARENT_HEADER, I as NonNegativeInt, J as TestRunnerFailed, K as TRACESTATE_HEADER, L as PropagatedTrace, M as MutantTested, N as MutationTestReportReady, O as Metrics, P as MutationTestResultSchema$1, Q as WorkerEntryUrl, S as CheckerMutantWire, T as DryRunCompleted, U as ReporterRpcs, V as ReporterFailed, W as StrykerOptionsSchema, X as TraceContextMiddleware, Y as TestRunnerRpcs, Z as TraceContextReference, _ as RunStarted, c as PhaseEntered, dt as errorToString, et as formatTraceparent, f as RunEvent, ft as instrument, it as INSTRUMENTER_CONSTANTS, k as MetricsResultSchema, l as PlanKnown, m as RunMutantTested, n as StageError, nt as parseTraceparent, ot as Mutant, pt as normalizeFileName$4, r as Heartbeat, rt as toMutantRunResult, t as PrepareError, tt as isCustomTestRunner, ut as disableTypeChecks, v as VerdictReached, w as CheckerRpcs } from "./Run.schema-CgVfoOfk.mjs";
2
- import { t as mergeConfig } from "./merge-config-BrrNQWje.mjs";
1
+ import { $ as WorkerEntryUrl, F as MutationTestingPlanReady, G as TRACEPARENT_HEADER, I as NonNegativeInt, J as TestRunnerFailed, K as TRACESTATE_HEADER, L as PropagatedTrace, M as MutantTested, N as MutationTestReportReady, O as Metrics, P as MutationTestResultSchema, Q as WALL_CLOCK_TIMEOUT_REASON, S as CheckerMutantWire, T as DryRunCompleted, U as ReporterRpcs, V as ReporterFailed, W as StrykerOptionsSchema, X as TraceContextMiddleware, Y as TestRunnerRpcs, Z as TraceContextReference, _ as RunStarted, at as wallClockTimeoutStopsRun, c as PhaseEntered, ct as Mutant, et as WorkerPluginKind, f as RunEvent, ft as disableTypeChecks, ht as normalizeFileName$4, it as toMutantRunResult, k as MetricsResultSchema, l as PlanKnown, m as RunMutantTested, mt as instrument, n as StageError, nt as isCustomTestRunner, ot as INSTRUMENTER_CONSTANTS, pt as errorToString, r as Heartbeat, rt as parseTraceparent, t as PrepareError, tt as formatTraceparent, v as VerdictReached, w as CheckerRpcs } from "./Run.schema-B_KpPeBA.mjs";
2
+ import { t as mergeConfig } from "./merge-config-CHZWyvVQ.mjs";
3
3
  import * as Effect$1 from "effect/Effect";
4
4
  import * as FileSystem from "effect/FileSystem";
5
5
  import * as Path from "effect/Path";
@@ -26,6 +26,7 @@ import * as Exit from "effect/Exit";
26
26
  import * as Scope from "effect/Scope";
27
27
  import * as Duration from "effect/Duration";
28
28
  import * as MutableHashMap from "effect/MutableHashMap";
29
+ import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
29
30
  import * as MutableHashSet from "effect/MutableHashSet";
30
31
  import * as HashMap from "effect/HashMap";
31
32
  import * as HashSet from "effect/HashSet";
@@ -41,7 +42,6 @@ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
41
42
  import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
42
43
  import * as Cause from "effect/Cause";
43
44
  import * as ChildProcess from "effect/unstable/process/ChildProcess";
44
- import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
45
45
  import * as NodeChildProcessSpawner from "@effect/platform-node-shared/NodeChildProcessSpawner";
46
46
  import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto";
47
47
  import * as Crypto from "effect/Crypto";
@@ -345,9 +345,18 @@ const calculateTotalTimeForIds = (testIds, testTimeById) => testIds.reduce((acc,
345
345
  if (t !== void 0) return acc + t;
346
346
  return acc;
347
347
  }, 0);
348
+ const hitLimitForCount = (hitCount) => hitCount * 100;
349
+ const coveredWhenKnown = (covered, coverageKnown) => {
350
+ if (!coverageKnown) return false;
351
+ return covered;
352
+ };
353
+ const isMissingHitCount = (hitCount, covered, coverageKnown) => {
354
+ if (!coveredWhenKnown(covered, coverageKnown)) return false;
355
+ return hitCount === void 0;
356
+ };
348
357
  const getHitLimit = (hitCount) => {
349
358
  if (hitCount === void 0) return;
350
- return hitCount * 100;
359
+ return hitLimitForCount(hitCount);
351
360
  };
352
361
  const getMutantActivation = (testFilter) => {
353
362
  if (testFilter !== void 0) return "runtime";
@@ -407,6 +416,16 @@ const planForStaticallyCovered = (mutant, command, isStatic) => {
407
416
  const useCovered = Match.value(isStatic).pipe(Match.when(false, () => true), Match.orElse(() => ignoreStatic && tests.length > ZERO));
408
417
  return Match.value(useCovered).pipe(Match.when(true, () => toRunPlan(mutant, command, calculateTotalTimeForIds(tests, command.testTimeById), coveredBy, isStatic, coveredBy)), Match.orElse(() => Match.value(ignoreStatic).pipe(Match.when(true, () => toEarlyResultPlan(mutant, isStatic, "Ignored", "Static mutant (and \"ignoreStatic\" was enabled)", coveredBy)), Match.orElse(() => toRunPlan(mutant, command, command.timeSpentAllTests, getTestFilter(command.globalTestFilter), isStatic, coveredBy)))));
409
418
  };
419
+ const hasCoveringTests = (tests) => {
420
+ if (tests === void 0) return false;
421
+ return tests.length > 0;
422
+ };
423
+ const mutantIsCovered = (command, mutantId) => {
424
+ if (hasCoveringTests(command.testsByMutantId[mutantId])) return true;
425
+ return hasStaticCoverageForPlan(command.staticCoverage, mutantId);
426
+ };
427
+ const coverageKnown = (command) => command.staticCoverage !== void 0;
428
+ const boundFor = (command, mutant) => isMissingHitCount(command.hitsByMutantId[mutant.id], mutantIsCovered(command, mutant.id), coverageKnown(command));
410
429
  const decidePlanForMutant = (mutant, command) => {
411
430
  const isStatic = hasStaticCoverageForPlan(command.staticCoverage, mutant.id);
412
431
  return Option.match(Option.fromUndefinedOr(mutant.status), {
@@ -414,6 +433,15 @@ const decidePlanForMutant = (mutant, command) => {
414
433
  onNone: () => Match.value(hasCoverageForPlan(command.staticCoverage)).pipe(Match.when(true, () => planForStaticallyCovered(mutant, command, isStatic)), Match.orElse(() => toRunPlan(mutant, command, command.timeSpentAllTests, getTestFilter(command.globalTestFilter), void 0, void 0)))
415
434
  });
416
435
  };
436
+ const isClosedMutant = (mutant) => mutant.status !== void 0;
437
+ const missingIdOf = (command, mutant) => {
438
+ if (!boundFor(command, mutant)) return [];
439
+ return [mutant.id];
440
+ };
441
+ const missingHitCountIds = (command) => command.mutants.flatMap((mutant) => {
442
+ if (isClosedMutant(mutant)) return [];
443
+ return missingIdOf(command, mutant);
444
+ });
417
445
  const planMutantTests = (command) => {
418
446
  const plans = command.mutants.map((mutant) => decidePlanForMutant(mutant, command));
419
447
  return {
@@ -510,15 +538,28 @@ const materializePlan = (plan, original) => {
510
538
  }
511
539
  };
512
540
  };
541
+ const missingHitFailure = (missing) => {
542
+ if (missing.length === 0) return;
543
+ return Effect$1.fail(StageError.make({
544
+ stage: "mutationTest",
545
+ reason: `covered mutant missing dry-run hit count: ${missing.join(", ")}`
546
+ }));
547
+ };
548
+ const mutantsById = (mutants) => {
549
+ const byId = /* @__PURE__ */ new Map();
550
+ for (const mutant of mutants) byId.set(mutant.id, mutant);
551
+ return byId;
552
+ };
553
+ const materializeKnown = (plan, byId) => {
554
+ const original = byId.get(plan.mutantId);
555
+ if (original === void 0) return Effect$1.die(/* @__PURE__ */ new Error(`planner returned an unknown mutant id: ${plan.mutantId}`));
556
+ return Effect$1.succeed(materializePlan(plan, original));
557
+ };
513
558
  const makeMutantTestPlanner = (command) => {
559
+ const failed = missingHitFailure(missingHitCountIds(command));
560
+ if (failed !== void 0) return failed;
514
561
  const { plans } = planMutantTests(command);
515
- const byId = /* @__PURE__ */ new Map();
516
- for (const mutant of command.mutants) byId.set(mutant.id, mutant);
517
- return Effect$1.forEach(plans, (plan) => {
518
- const original = byId.get(plan.mutantId);
519
- if (original === void 0) return Effect$1.die(/* @__PURE__ */ new Error(`planner returned an unknown mutant id: ${plan.mutantId}`));
520
- return Effect$1.succeed(materializePlan(plan, original));
521
- });
562
+ return Effect$1.forEach(plans, (plan) => materializeKnown(plan, mutantsById(command.mutants)));
522
563
  };
523
564
  const decidePlans = (mutants, testCoverage, options, timeOverheadMS, globalTestFilter, sandboxFileByName) => {
524
565
  const command = coverageToCommand(mutants, testCoverage, options, timeOverheadMS, globalTestFilter, sandboxFileByName);
@@ -673,8 +714,12 @@ const incrementalDiffOfChanges = (input) => {
673
714
  };
674
715
  };
675
716
  const computeIncrementalDiff = (input) => Match.value(input.force).pipe(Match.when(true, () => forcedDiff(input)), Match.orElse(() => incrementalDiffOfChanges(input)));
676
- const previousFilesOf = (rawReport) => Match.value(rawReport).pipe(Match.when(Predicate.isObject, (report) => Option.getOrElse(Option.filter(Option.fromUndefinedOr(report["files"]), S.is(PreviousFilesSchema)), () => ({}))), Match.orElse(() => ({})));
677
- const previousTestFilesOf = (rawReport) => Match.value(rawReport).pipe(Match.when(Predicate.isObject, (report) => Option.getOrElse(Option.filter(Option.fromUndefinedOr(report["testFiles"]), S.is(PreviousTestFilesSchema)), () => ({}))), Match.orElse(() => ({})));
717
+ const filesFieldOf = (report) => Option.getOrElse(S.decodeUnknownOption(PreviousFilesSchema)(report.files), () => ({}));
718
+ const previousFilesFrom = (report) => Predicate.hasProperty(report, "files") ? filesFieldOf(report) : {};
719
+ const previousFilesOf = (rawReport) => Predicate.isObject(rawReport) ? previousFilesFrom(rawReport) : {};
720
+ const testFilesFieldOf = (report) => Option.getOrElse(S.decodeUnknownOption(PreviousTestFilesSchema)(report.testFiles), () => ({}));
721
+ const previousTestFilesFrom = (report) => Predicate.hasProperty(report, "testFiles") ? testFilesFieldOf(report) : {};
722
+ const previousTestFilesOf = (rawReport) => Predicate.isObject(rawReport) ? previousTestFilesFrom(rawReport) : {};
678
723
  const hasTestFileName = (result) => result.fileName !== void 0;
679
724
  const testIdsByRelativeFile = (testCoverage, basePath) => {
680
725
  const byFile = {};
@@ -759,6 +804,104 @@ const compileIgnoreRule = (pattern) => {
759
804
  };
760
805
  };
761
806
  //#endregion
807
+ //#region src/config-defaults.ts
808
+ const isNonNullObject$1 = (value) => typeof value === "object" && value !== null;
809
+ const isRecordValue$1 = (value) => isNonNullObject$1(value) && Array.isArray(value) === false;
810
+ const isArrayValue$1 = (value) => Array.isArray(value);
811
+ const freezeArrayValue$1 = (value) => Object.freeze(value.map((element) => deepFreeze$1(element)));
812
+ const freezeMapEntry$1 = ([entryKey, entryValue]) => [deepFreeze$1(entryKey), deepFreeze$1(entryValue)];
813
+ const freezeMapValue$1 = (value) => Object.freeze(new Map([...value.entries()].map(freezeMapEntry$1)));
814
+ const freezeSetValue$1 = (value) => Object.freeze(new Set([...value.values()].map((element) => deepFreeze$1(element))));
815
+ const freezeRegExpValue$1 = (value) => Object.freeze(value);
816
+ const freezeRecordValue$1 = (value) => Object.freeze(Object.entries(value).reduce((frozen, [property, propertyValue]) => {
817
+ frozen[property] = deepFreeze$1(propertyValue);
818
+ return frozen;
819
+ }, {}));
820
+ function deepFreeze$1(target) {
821
+ return Match.value(target).pipe(Match.when(isArrayValue$1, freezeArrayValue$1), Match.when((value) => value instanceof Map, freezeMapValue$1), Match.when((value) => value instanceof RegExp, freezeRegExpValue$1), Match.when((value) => value instanceof Set, freezeSetValue$1), Match.when(isRecordValue$1, freezeRecordValue$1), Match.orElse(() => target));
822
+ }
823
+ const optionsPath$1 = (...path) => path.join(".");
824
+ const SUPPORTED_CONFIG_FILE_EXTENSIONS$1 = [
825
+ ".ts",
826
+ ".mts",
827
+ ".js",
828
+ ".mjs"
829
+ ];
830
+ const configFileNames$1 = (extensions) => extensions.map((extension) => `stryker.config${extension}`);
831
+ const SUPPORTED_CONFIG_FILE_NAMES$1 = Object.freeze(configFileNames$1(SUPPORTED_CONFIG_FILE_EXTENSIONS$1));
832
+ const RELATIVE_SPECIFIER_PREFIXES$1 = [
833
+ "/",
834
+ "./",
835
+ "../",
836
+ "file://"
837
+ ];
838
+ function isModuleSpecifier$1(value) {
839
+ return RELATIVE_SPECIFIER_PREFIXES$1.every((prefix) => value.startsWith(prefix) === false);
840
+ }
841
+ const CONFIG_SYNTAX_HELP$1 = `
842
+ Example of how a config file should look:
843
+ /**
844
+ * @type {import('@systemfsoftware/stryker-js-plugin-interface').StrykerOptions}
845
+ */
846
+ export default {
847
+ mutate: ['src/**/*.js'],
848
+ testRunner: 'vitest',
849
+ reporters: ['progress', 'clear-text', 'html'],
850
+ concurrency: 4
851
+ };
852
+ See https://stryker-mutator.io/docs/stryker-js/config-file for more information.`.trim();
853
+ const createDefaultOptions$1 = S.decodeEffect(StrykerOptionsSchema)({}).pipe(Effect$1.orDie);
854
+ const defaultOptions$1 = Effect$1.map(createDefaultOptions$1, (opts) => deepFreeze$1(opts));
855
+ const scopedUnserializable = (scope) => (description) => ({
856
+ ...description,
857
+ path: [scope, ...description.path]
858
+ });
859
+ const describeUnserializableChild = (child, scope) => Match.value(findUnserializables(child)).pipe(Match.when(void 0, () => []), Match.orElse((descriptions) => descriptions.map(scopedUnserializable(scope))));
860
+ const collectUnserializables = (groups) => {
861
+ const found = groups.flat();
862
+ if (found.length > 0) return found;
863
+ };
864
+ const describeUnserializableArray = (value) => collectUnserializables(value.map((child, index) => describeUnserializableChild(child, index.toString())));
865
+ const describeUnserializableRecord = (value) => collectUnserializables(Object.entries(value).map(([key, child]) => describeUnserializableChild(child, key)));
866
+ const isPlainObjectValue = (value) => !Array.isArray(value) && value.constructor === Object;
867
+ const classNameOf = (value) => Match.value(value.constructor).pipe(Match.when(Match.defined, (ctor) => ctor.name), Match.orElse(() => "Object"));
868
+ const describeUnserializableInstance = (value) => [{
869
+ path: [],
870
+ reason: `Value is an instance of "${classNameOf(value)}", this detail will get lost in translation during serialization`
871
+ }];
872
+ const describeUnserializableObject = (value) => Match.value(value).pipe(Match.when(isArrayValue$1, describeUnserializableArray), Match.when(isPlainObjectValue, describeUnserializableRecord), Match.orElse(describeUnserializableInstance));
873
+ const NON_JSON_PRIMITIVE_TYPES = [
874
+ "bigint",
875
+ "function",
876
+ "symbol"
877
+ ];
878
+ const isNonJsonPrimitive = (value) => NON_JSON_PRIMITIVE_TYPES.includes(typeof value);
879
+ const describeNonJsonPrimitive = (value) => [{
880
+ path: [],
881
+ reason: `Primitive type "${typeof value}" has no JSON representation`
882
+ }];
883
+ const describeNumber = (value) => {
884
+ if (isFinite(value)) return void 0;
885
+ return [{
886
+ reason: `Number value \`${value}\` has no JSON representation`,
887
+ path: []
888
+ }];
889
+ };
890
+ function findUnserializables(thing) {
891
+ if (typeof thing === "number") return describeNumber(thing);
892
+ return describeNonNumberValue(thing);
893
+ }
894
+ function describeNonNumberValue(thing) {
895
+ return isNonJsonPrimitive(thing) ? describeNonJsonPrimitive(thing) : describeObjectValue(thing);
896
+ }
897
+ function describeObjectValue(thing) {
898
+ return isNonNullObject$1(thing) ? describeUnserializableObject(thing) : void 0;
899
+ }
900
+ function isWarningEnabled$1(warningType, warningOptions) {
901
+ if (typeof warningOptions === "boolean") return warningOptions;
902
+ else return warningOptions[warningType] === true;
903
+ }
904
+ //#endregion
762
905
  //#region src/Config.schema.ts
763
906
  const ConfigDocumentSchema = S.Record(S.String, S.Unknown);
764
907
  const ImportedModuleSchema = S.Struct({ default: S.optional(S.Unknown) });
@@ -982,9 +1125,10 @@ const sandboxFor = (platform, fileName, activeMutantId) => {
982
1125
  exports: moduleExports,
983
1126
  __filename: fileName
984
1127
  };
985
- sandbox["global"] = sandbox;
986
- sandbox["globalThis"] = sandbox;
987
- return sandbox;
1128
+ return Object.assign(sandbox, {
1129
+ global: sandbox,
1130
+ globalThis: sandbox
1131
+ });
988
1132
  };
989
1133
  const resultFromRun = (failureMessage, timeSpentMs) => Match.value(failureMessage).pipe(Match.when(Match.string, (failure) => ({
990
1134
  status: "complete",
@@ -1008,10 +1152,7 @@ const isPlainObject = (value) => {
1008
1152
  if (!Predicate.isObject(value)) return false;
1009
1153
  return true;
1010
1154
  };
1011
- const descriptorValue = (descriptor) => {
1012
- if (descriptor === void 0) return;
1013
- return descriptor.value;
1014
- };
1155
+ const descriptorValue = (descriptor) => descriptor === void 0 ? void 0 : descriptor.value;
1015
1156
  const hostStrykerNamespace = () => {
1016
1157
  const current = descriptorValue(Object.getOwnPropertyDescriptor(globalThis, INSTRUMENTER_CONSTANTS.NAMESPACE));
1017
1158
  if (isPlainObject(current)) return current;
@@ -1338,18 +1479,25 @@ const withTimeout = (inner) => ({
1338
1479
  dryRun: (options) => {
1339
1480
  const policy = Effect$1.timeoutOrElse({
1340
1481
  duration: Duration.millis(options.timeout),
1341
- orElse: () => Effect$1.succeed({ status: "timeout" })
1482
+ orElse: () => Effect$1.succeed({
1483
+ status: "timeout",
1484
+ reason: WALL_CLOCK_TIMEOUT_REASON
1485
+ })
1342
1486
  });
1343
1487
  return inner.dryRun(options).pipe(policy);
1344
1488
  },
1345
1489
  mutantRun: (options) => {
1346
1490
  const policy = Effect$1.timeoutOrElse({
1347
1491
  duration: Duration.millis(options.timeout),
1348
- orElse: () => Effect$1.succeed({ status: "timeout" })
1492
+ orElse: () => Effect$1.succeed({
1493
+ status: "timeout",
1494
+ reason: WALL_CLOCK_TIMEOUT_REASON
1495
+ })
1349
1496
  });
1350
1497
  return inner.mutantRun(options).pipe(policy);
1351
1498
  }
1352
1499
  });
1500
+ const invalidatesRunnerPool = (status, reason) => status === "timeout" && reason === "wall-clock-timeout";
1353
1501
  /**
1354
1502
  * Retry a run whose runner crashed, and report the crash as a result once the
1355
1503
  * attempts are spent.
@@ -1546,78 +1694,43 @@ const buildTestRunner = (context, childProcessRunner) => Effect$1.gen(function*
1546
1694
  });
1547
1695
  //#endregion
1548
1696
  //#region src/run/load-config.cell.ts
1549
- const isNonNullObject$1 = (value) => typeof value === "object" && value !== null;
1550
- const optionsPath$1 = (...path) => path.join(".");
1697
+ const isNonNullObject = (value) => typeof value === "object" && value !== null;
1698
+ const isRecordValue = (value) => isNonNullObject(value) && Array.isArray(value) === false;
1699
+ const optionsPath = (...path) => path.join(".");
1551
1700
  const combine = (prefixes, suffixes, extensions) => prefixes.flatMap((prefix) => suffixes.flatMap((suffix) => extensions.map((extension) => `${prefix}stryker${suffix}.${extension}`)));
1552
1701
  const CONFIG_FILE_NAME_PREFIXES = ["", "."];
1553
1702
  const CONFIG_FILE_NAME_SUFFIXES = [".conf", ".config"];
1554
- const SUPPORTED_CONFIG_FILE_EXTENSIONS$1 = [
1703
+ const SUPPORTED_CONFIG_FILE_EXTENSIONS = [
1555
1704
  "ts",
1556
1705
  "mts",
1557
1706
  "js",
1558
1707
  "mjs"
1559
1708
  ];
1560
1709
  const LEGACY_CONFIG_FILE_EXTENSIONS = ["json", "cjs"];
1561
- const configFileNames$1 = (extensions) => combine([...CONFIG_FILE_NAME_PREFIXES], [...CONFIG_FILE_NAME_SUFFIXES], [...extensions]);
1562
- const SUPPORTED_CONFIG_FILE_NAMES$1 = Object.freeze(configFileNames$1(SUPPORTED_CONFIG_FILE_EXTENSIONS$1));
1563
- const LEGACY_CONFIG_FILE_NAMES = Object.freeze(configFileNames$1(LEGACY_CONFIG_FILE_EXTENSIONS));
1564
- const SUPPORTED_CONFIG_FILE_EXTENSION_GUIDE = `one of these extensions: ${SUPPORTED_CONFIG_FILE_EXTENSIONS$1.map((extension) => `.${extension}`).join(", ")}`;
1710
+ const configFileNames = (extensions) => combine([...CONFIG_FILE_NAME_PREFIXES], [...CONFIG_FILE_NAME_SUFFIXES], [...extensions]);
1711
+ const SUPPORTED_CONFIG_FILE_NAMES = Object.freeze(configFileNames(SUPPORTED_CONFIG_FILE_EXTENSIONS));
1712
+ const LEGACY_CONFIG_FILE_NAMES = Object.freeze(configFileNames(LEGACY_CONFIG_FILE_EXTENSIONS));
1713
+ const SUPPORTED_CONFIG_FILE_EXTENSION_GUIDE = `one of these extensions: ${SUPPORTED_CONFIG_FILE_EXTENSIONS.map((extension) => `.${extension}`).join(", ")}`;
1565
1714
  const legacyConfigHint = (file) => `Stryker no longer reads JSON or CommonJS config files. Convert "${file}" to a config module with an "export default { ... }" object — Stryker reads config modules with ${SUPPORTED_CONFIG_FILE_EXTENSION_GUIDE} — and delete the legacy file.`;
1566
1715
  const unsupportedConfigHint = (file) => `"${file}" is not a supported config file. Stryker reads config modules with ${SUPPORTED_CONFIG_FILE_EXTENSION_GUIDE}, for example "stryker.config.ts" with an "export default { ... }" object.`;
1567
1716
  const extendsChildHint = (file) => `The extended config "${file}" is a JSON or CommonJS config file, which Stryker no longer reads. Convert it to a config module — Stryker reads config modules with ${SUPPORTED_CONFIG_FILE_EXTENSION_GUIDE} — and point "extends" at the converted file.`;
1568
1717
  const shadowedLegacyWarning = (legacyFile, supportedFile) => `Ignoring the legacy config file "${legacyFile}": "${supportedFile}" is the config Stryker reads. Stryker no longer reads JSON or CommonJS config files; delete the legacy file.`;
1569
- const isArrayValue$1 = (value) => Array.isArray(value);
1570
- const freezeArrayValue$1 = (value) => Object.freeze(value.map(deepFreeze$1));
1571
- const freezeMapEntry$1 = ([entryKey, entryValue]) => [deepFreeze$1(entryKey), deepFreeze$1(entryValue)];
1572
- const freezeMapValue$1 = (value) => Object.freeze(new Map([...value.entries()].map(freezeMapEntry$1)));
1573
- const freezeSetValue$1 = (value) => Object.freeze(new Set([...value.values()].map(deepFreeze$1)));
1574
- const freezeRegExpValue$1 = (value) => Object.freeze(value);
1575
- const freezeRecordValue$1 = (value) => Object.freeze(Object.entries(value).reduce((frozen, [property, propertyValue]) => {
1576
- frozen[property] = deepFreeze$1(propertyValue);
1718
+ const isArrayValue = (value) => Array.isArray(value);
1719
+ const isMapValue = (value) => value instanceof Map;
1720
+ const isSetValue = (value) => value instanceof Set;
1721
+ const freezeArrayValue = (value) => Object.freeze(value.map((element) => deepFreeze(element)));
1722
+ const freezeMapEntry = ([entryKey, entryValue]) => [deepFreeze(entryKey), deepFreeze(entryValue)];
1723
+ const freezeMapValue = (value) => Object.freeze(new Map([...value.entries()].map(freezeMapEntry)));
1724
+ const freezeSetValue = (value) => Object.freeze(new Set([...value.values()].map((element) => deepFreeze(element))));
1725
+ const freezeRegExpValue = (value) => Object.freeze(value);
1726
+ const freezeRecordValue = (value) => Object.freeze(Object.entries(value).reduce((frozen, [property, propertyValue]) => {
1727
+ frozen[property] = deepFreeze(propertyValue);
1577
1728
  return frozen;
1578
1729
  }, {}));
1579
- function deepFreeze$1(target) {
1580
- return Match.value(target).pipe(Match.when(isArrayValue$1, freezeArrayValue$1), Match.when((value) => value instanceof Map, freezeMapValue$1), Match.when((value) => value instanceof RegExp, freezeRegExpValue$1), Match.when((value) => value instanceof Set, freezeSetValue$1), Match.when(isNonNullObject$1, freezeRecordValue$1), Match.orElse(() => target));
1581
- }
1582
- const scopedUnserializable$1 = (scope) => (description) => ({
1583
- ...description,
1584
- path: [scope, ...description.path]
1585
- });
1586
- const describeUnserializableChild$1 = (child, scope) => Match.value(findUnserializables$1(child)).pipe(Match.when(void 0, () => []), Match.orElse((descriptions) => descriptions.map(scopedUnserializable$1(scope))));
1587
- const collectUnserializables$1 = (groups) => {
1588
- const found = groups.flat();
1589
- if (found.length > 0) return found;
1590
- };
1591
- const describeUnserializableArray$1 = (value) => collectUnserializables$1(value.map((child, index) => describeUnserializableChild$1(child, index.toString())));
1592
- const describeUnserializableRecord$1 = (value) => collectUnserializables$1(Object.entries(value).map(([key, child]) => describeUnserializableChild$1(child, key)));
1593
- const isPlainObjectValue$1 = (value) => !Array.isArray(value) && value.constructor === Object;
1594
- const classNameOf$1 = (value) => Match.value(value.constructor).pipe(Match.when(Match.defined, (ctor) => ctor.name), Match.orElse(() => "Object"));
1595
- const describeUnserializableInstance$1 = (value) => [{
1596
- path: [],
1597
- reason: `Value is an instance of "${classNameOf$1(value)}", this detail will get lost in translation during serialization`
1598
- }];
1599
- const describeUnserializableObject$1 = (value) => Match.value(value).pipe(Match.when(isArrayValue$1, describeUnserializableArray$1), Match.when(isPlainObjectValue$1, describeUnserializableRecord$1), Match.orElse(describeUnserializableInstance$1));
1600
- const NON_JSON_PRIMITIVE_TYPES$1 = [
1601
- "bigint",
1602
- "function",
1603
- "symbol"
1604
- ];
1605
- const isNonJsonPrimitive$1 = (value) => NON_JSON_PRIMITIVE_TYPES$1.includes(typeof value);
1606
- const describeNonJsonPrimitive$1 = (value) => [{
1607
- path: [],
1608
- reason: `Primitive type "${typeof value}" has no JSON representation`
1609
- }];
1610
- const describeNumber$1 = (value) => {
1611
- if (isFinite(value)) return void 0;
1612
- return [{
1613
- reason: `Number value \`${value}\` has no JSON representation`,
1614
- path: []
1615
- }];
1616
- };
1617
- function findUnserializables$1(thing) {
1618
- return Match.value(thing).pipe(Match.when((value) => typeof value === "number", describeNumber$1), Match.when(isNonJsonPrimitive$1, describeNonJsonPrimitive$1), Match.when(isNonNullObject$1, describeUnserializableObject$1), Match.orElse(() => void 0));
1730
+ function deepFreeze(target) {
1731
+ return Match.value(target).pipe(Match.when(isArrayValue, freezeArrayValue), Match.when(isMapValue, freezeMapValue), Match.when((value) => value instanceof RegExp, freezeRegExpValue), Match.when(isSetValue, freezeSetValue), Match.when(isRecordValue, freezeRecordValue), Match.orElse(() => target));
1619
1732
  }
1620
- function isWarningEnabled$1(warningType, warningOptions) {
1733
+ function isWarningEnabled(warningType, warningOptions) {
1621
1734
  if (typeof warningOptions === "boolean") return warningOptions;
1622
1735
  else return warningOptions[warningType] === true;
1623
1736
  }
@@ -1676,10 +1789,18 @@ const mergePluginDescriptors = (parentPlugins, childPlugins) => {
1676
1789
  const merged = [...parentPlugins, ...childPlugins];
1677
1790
  return merged.filter(isFirstDescriptorOccurrence(merged));
1678
1791
  };
1679
- const inheritNested = (parentValue, childValue) => Match.value(parentValue).pipe(Match.when((value) => isNonNullObject$1(value) && Array.isArray(value) === false, (parentNested) => Match.value(childValue).pipe(Match.when((value) => isNonNullObject$1(value) && Array.isArray(value) === false, (childNested) => ({
1680
- ...parentNested,
1681
- ...childNested
1682
- })), Match.orElse(() => childValue))), Match.orElse(() => childValue));
1792
+ const isConfigOptionsRecord = (value) => isNonNullObject(value) && Array.isArray(value) === false;
1793
+ function mergeConfigRecords(parentNested, childNested) {
1794
+ return {
1795
+ ...parentNested,
1796
+ ...childNested
1797
+ };
1798
+ }
1799
+ const toConfigRecordOption = (value) => isConfigOptionsRecord(value) ? Option.some(value) : Option.none();
1800
+ const inheritNested = (parentValue, childValue) => Option.match(Option.all([toConfigRecordOption(parentValue), toConfigRecordOption(childValue)]), {
1801
+ onSome: ([parentNested, childNested]) => mergeConfigRecords(parentNested, childNested),
1802
+ onNone: () => childValue
1803
+ });
1683
1804
  const inheritEntry = (out, key, parentValue, childValue) => Match.value(childValue).pipe(Match.when(null, () => {
1684
1805
  const next = { ...out };
1685
1806
  delete next[key];
@@ -1694,14 +1815,14 @@ const inheritEntry = (out, key, parentValue, childValue) => Match.value(childVal
1694
1815
  function mergeConfigs(parent, child) {
1695
1816
  return Object.entries(child).reduce((out, entry) => inheritEntry(out, entry[0], parent[entry[0]], entry[1]), { ...parent });
1696
1817
  }
1697
- const RELATIVE_SPECIFIER_PREFIXES$1 = [
1818
+ const RELATIVE_SPECIFIER_PREFIXES = [
1698
1819
  "./",
1699
1820
  "../",
1700
1821
  "/",
1701
1822
  "\\"
1702
1823
  ];
1703
- function isModuleSpecifier$1(value) {
1704
- return RELATIVE_SPECIFIER_PREFIXES$1.every((prefix) => value.startsWith(prefix) === false);
1824
+ function isModuleSpecifier(value) {
1825
+ return RELATIVE_SPECIFIER_PREFIXES.every((prefix) => value.startsWith(prefix) === false);
1705
1826
  }
1706
1827
  const stripExtends = (document) => {
1707
1828
  const { extends: _ignored, ...rest } = document;
@@ -1720,7 +1841,7 @@ const decideExtendsStep = (state, document, file, pathService) => {
1720
1841
  options: document
1721
1842
  }]
1722
1843
  };
1723
- return Match.value(document["extends"]).pipe(Match.when(void 0, () => ExtendsStepDone.make({ options: mergeChainDocuments(nextState.documents) })), Match.when(null, () => ExtendsStepDone.make({ options: mergeChainDocuments(nextState.documents) })), Match.when(Match.string, (extendValue) => Match.value(isModuleSpecifier$1(extendValue)).pipe(Match.when(true, () => ExtendsStepResolve.make({
1844
+ return Match.value(document["extends"]).pipe(Match.when(void 0, () => ExtendsStepDone.make({ options: mergeChainDocuments(nextState.documents) })), Match.when(null, () => ExtendsStepDone.make({ options: mergeChainDocuments(nextState.documents) })), Match.when(Match.string, (extendValue) => Match.value(isModuleSpecifier(extendValue)).pipe(Match.when(true, () => ExtendsStepResolve.make({
1724
1845
  specifier: extendValue,
1725
1846
  state: nextState
1726
1847
  })), Match.when(false, () => ExtendsStepRead.make({
@@ -1735,15 +1856,20 @@ const decodeConfigDocument = (configFile, document) => S.decodeUnknownEffect(Con
1735
1856
  file: configFile,
1736
1857
  cause
1737
1858
  })));
1738
- const requireDefaultExport = (configFile, defaultExport) => Match.value(defaultExport).pipe(Match.when(void 0, () => Effect$1.fail(ConfigFileInvalidError.make({
1859
+ const failInvalidConfig = (configFile, cause) => Effect$1.fail(ConfigFileInvalidError.make({
1739
1860
  file: configFile,
1740
- cause: "Config file must have a default export!"
1741
- }))), Match.when(isNonNullObject$1, (value) => Effect$1.succeed(value)), Match.orElse(() => Effect$1.fail(ConfigFileInvalidError.make({
1742
- file: configFile,
1743
- cause: "Default export of config file must be an object!"
1744
- }))));
1861
+ cause
1862
+ }));
1863
+ const requireDefaultExport = (configFile, defaultExport) => defaultExport === void 0 ? failInvalidConfig(configFile, "Config file must have a default export!") : settleObjectExport(configFile, defaultExport);
1864
+ function settleObjectExport(configFile, value) {
1865
+ return isNonNullObject(value) ? Effect$1.succeed(value) : failInvalidConfig(configFile, "Default export of config file must be an object!");
1866
+ }
1745
1867
  const isConfigFactory = (value) => typeof value === "function";
1746
- const factoryFailureCause = (cause) => Match.value(cause).pipe(Match.when(Match.instanceOf(Error), (error) => new Error(`Evaluating the config module's exported factory failed: ${error.message}`, { cause: error })), Match.orElse(() => cause));
1868
+ function wrappedFactoryFailure(error) {
1869
+ return new Error(`Evaluating the config module's exported factory failed: ${error.message}`, { cause: error });
1870
+ }
1871
+ const factoryFailureCause = (cause) => cause instanceof Error ? wrappedFactoryFailure(cause) : cause;
1872
+ const applyConfigFactory = (factory, configEnv) => factory(configEnv);
1747
1873
  /**
1748
1874
  * Settle a config module's default export into the object the document schema decodes.
1749
1875
  *
@@ -1754,7 +1880,7 @@ const factoryFailureCause = (cause) => Match.value(cause).pipe(Match.when(Match.
1754
1880
  * non-null object and would otherwise reach the document decoder as one.
1755
1881
  */
1756
1882
  const settleConfigDefault = (configFile, defaultExport, configEnv) => Effect$1.tryPromise({
1757
- try: () => Promise.resolve(defaultExport).then((exported) => Match.value(exported).pipe(Match.when(isConfigFactory, (factory) => factory(configEnv)), Match.orElse((value) => value))),
1883
+ try: () => Promise.resolve(defaultExport).then((exported) => isConfigFactory(exported) ? applyConfigFactory(exported, configEnv) : exported),
1758
1884
  catch: (cause) => ConfigFileInvalidError.make({
1759
1885
  file: configFile,
1760
1886
  cause: factoryFailureCause(cause)
@@ -1762,14 +1888,14 @@ const settleConfigDefault = (configFile, defaultExport, configEnv) => Effect$1.t
1762
1888
  }).pipe(Effect$1.flatMap((settled) => requireDefaultExport(configFile, settled)));
1763
1889
  const ERASABLE_SYNTAX_HELP = "Config modules may use only erasable TypeScript syntax: no enums, no namespaces with runtime code, no parameter properties, and no decorators.";
1764
1890
  const carriesCode = (cause) => cause instanceof Error && "code" in cause;
1765
- const errorCodeOf = (cause) => {
1766
- const code = Match.value(cause).pipe(Match.when(carriesCode, (error) => error.code), Match.orElse(() => void 0));
1767
- if (typeof code === "string") return code;
1768
- };
1769
- const errorMessageOf = (cause) => {
1770
- const message = Match.value(cause).pipe(Match.when(Match.instanceOf(Error), (error) => error.message), Match.orElse(() => void 0));
1771
- if (typeof message === "string") return message;
1772
- };
1891
+ function codeOf(coded) {
1892
+ return typeof coded.code === "string" ? coded.code : void 0;
1893
+ }
1894
+ const errorCodeOf = (cause) => carriesCode(cause) ? codeOf(cause) : void 0;
1895
+ const errorMessageOf = (cause) => cause instanceof Error ? errorTextOf(cause) : void 0;
1896
+ function errorTextOf(error) {
1897
+ return typeof error.message === "string" ? error.message : void 0;
1898
+ }
1773
1899
  const importFailureDetail = (failure) => errorMessageOf(failure.cause) ?? failure.message;
1774
1900
  const configImportCause = (configFile, failure) => Match.value(errorCodeOf(failure.cause)).pipe(Match.when("ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX", () => new Error(`The config file "${configFile}" uses TypeScript syntax Node cannot execute. ${ERASABLE_SYNTAX_HELP}`, { cause: failure.cause })), Match.when("ERR_UNKNOWN_FILE_EXTENSION", () => new Error(`The config file "${configFile}" has an extension Node cannot load. Stryker reads config modules with ${SUPPORTED_CONFIG_FILE_EXTENSION_GUIDE}.`, { cause: failure.cause })), Match.when("ERR_MODULE_NOT_FOUND", () => new Error(`The config file "${configFile}" imports a module Node cannot find. The specifier is most likely not installed — install it as a dependency of the project, or remove the import. Node reported: ${importFailureDetail(failure)}`, { cause: failure.cause })), Match.when("ERR_PACKAGE_PATH_NOT_EXPORTED", () => new Error(`The config file "${configFile}" imports a path a package does not export. The package is installed but its "exports" map leaves this specifier out — import a path the package publishes. Node reported: ${importFailureDetail(failure)}`, { cause: failure.cause })), Match.orElse(() => failure));
1775
1901
  const configModuleUrl = (configFile, pathService) => Match.value(configFile.startsWith("file:")).pipe(Match.when(true, () => Effect$1.try({
@@ -1846,7 +1972,7 @@ function recordOf(value) {
1846
1972
  return { ...value };
1847
1973
  }
1848
1974
  const thresholdErrors = (options) => Match.value(options.thresholds.high < options.thresholds.low).pipe(Match.when(true, () => ["Config option \"thresholds.high\" should be higher than \"thresholds.low\"."]), Match.orElse(() => []));
1849
- const ignoreStaticErrors = (options) => Match.value(options.ignoreStatic && options.coverageAnalysis !== "perTest").pipe(Match.when(true, () => [`Config option "${optionsPath$1("ignoreStatic")}" is not supported with coverage analysis "${options.coverageAnalysis}". Either turn off "${optionsPath$1("ignoreStatic")}", or configure "${optionsPath$1("coverageAnalysis")}" to be "perTest".`]), Match.orElse(() => []));
1975
+ const ignoreStaticErrors = (options) => Match.value(options.ignoreStatic && options.coverageAnalysis !== "perTest").pipe(Match.when(true, () => [`Config option "${optionsPath("ignoreStatic")}" is not supported with coverage analysis "${options.coverageAnalysis}". Either turn off "${optionsPath("ignoreStatic")}", or configure "${optionsPath("coverageAnalysis")}" to be "perTest".`]), Match.orElse(() => []));
1850
1976
  const matchLineNumber = (match, group) => Number.parseInt(match[group] ?? "", 10);
1851
1977
  const startLineErrors = (index, mutationRange, start) => Match.value(start < 1).pipe(Match.when(true, () => [`Config option "mutate[${index}]" is invalid. Mutation range "${mutationRange}" is invalid, line ${start} does not exist (lines start at 1).`]), Match.orElse(() => []));
1852
1978
  const lineOrderErrors = (index, mutationRange, start, end) => Match.value(start > end).pipe(Match.when(true, () => [`Config option "mutate[${index}]" is invalid. Mutation range "${mutationRange}" is invalid. The "from" line number (${start}) should be less then the "to" line number (${end}).`]), Match.orElse(() => []));
@@ -1890,7 +2016,7 @@ const OPTIONS_ADDED_BY_STRYKER = [
1890
2016
  "configFile",
1891
2017
  "$schema"
1892
2018
  ];
1893
- const schemaPropertyNames = (schema) => Match.value(schema["properties"]).pipe(Match.when(isNonNullObject$1, (properties) => Object.keys(recordOf(properties))), Match.orElse(() => []));
2019
+ const schemaPropertyNames = (schema) => Match.value(schema["properties"]).pipe(Match.when(isNonNullObject, (properties) => Object.keys(recordOf(properties))), Match.orElse(() => []));
1894
2020
  const excessOptionNames = (options, schema) => {
1895
2021
  const schemaKeys = schemaPropertyNames(schema);
1896
2022
  return Object.keys(options).filter((key) => key.endsWith("_comment") === false).filter((key) => OPTIONS_ADDED_BY_STRYKER.includes(key) === false).filter((key) => schemaKeys.includes(key) === false);
@@ -1903,7 +2029,7 @@ const warnAboutUnknownOptions = (options, excessNames) => Effect$1.gen(function*
1903
2029
  * Did you only write this property as a comment? If so, please postfix it with "_comment".
1904
2030
  * You might be missing a plugin that is supposed to use it. Stryker loaded plugins from: ${pluginsJson}
1905
2031
  * The plugin that is using it did not contribute explicit validation.
1906
- (disable "${optionsPath$1("warnings", "unknownOptions")}" to ignore this warning)`);
2032
+ (disable "${optionsPath("warnings", "unknownOptions")}" to ignore this warning)`);
1907
2033
  });
1908
2034
  const unknownOptionWarning = (options, schema) => {
1909
2035
  const excessNames = excessOptionNames(options, schema);
@@ -1911,17 +2037,17 @@ const unknownOptionWarning = (options, schema) => {
1911
2037
  };
1912
2038
  function markExcessOptions(options, schema) {
1913
2039
  return Effect$1.gen(function* () {
1914
- if (isWarningEnabled$1("unknownOptions", options.warnings)) yield* unknownOptionWarning(options, schema);
2040
+ if (isWarningEnabled("unknownOptions", options.warnings)) yield* unknownOptionWarning(options, schema);
1915
2041
  });
1916
2042
  }
1917
2043
  const logUnserializableWarnings = (unserializables) => Effect$1.gen(function* () {
1918
2044
  for (const unserializable of unserializables) yield* Effect$1.logWarning(`Config option "${unserializable.path.join(".")}" is not (fully) serializable. ${unserializable.reason}. Any test runner or checker worker processes might not receive this value as intended.`);
1919
- yield* Effect$1.logWarning(`(disable ${optionsPath$1("warnings", "unserializableOptions")} to ignore this warning)`);
2045
+ yield* Effect$1.logWarning(`(disable ${optionsPath("warnings", "unserializableOptions")} to ignore this warning)`);
1920
2046
  });
1921
- const warnAboutUnserializableOptions = (options) => Match.value(findUnserializables$1(options)).pipe(Match.when(void 0, () => Effect$1.void), Match.orElse((unserializables) => logUnserializableWarnings(unserializables)));
2047
+ const warnAboutUnserializableOptions = (options) => Match.value(findUnserializables(options)).pipe(Match.when(void 0, () => Effect$1.void), Match.orElse((unserializables) => logUnserializableWarnings(unserializables)));
1922
2048
  function markUnserializableOptions(options) {
1923
2049
  return Effect$1.gen(function* () {
1924
- if (isWarningEnabled$1("unserializableOptions", options.warnings)) yield* warnAboutUnserializableOptions(options);
2050
+ if (isWarningEnabled("unserializableOptions", options.warnings)) yield* warnAboutUnserializableOptions(options);
1925
2051
  });
1926
2052
  }
1927
2053
  function markOptions(options, schema) {
@@ -1938,8 +2064,8 @@ function validateOptions(options, schema) {
1938
2064
  return typed;
1939
2065
  });
1940
2066
  }
1941
- const createDefaultOptions$1 = S.decodeEffect(StrykerOptionsSchema)({}).pipe(Effect$1.orDie);
1942
- Effect$1.map(createDefaultOptions$1, (opts) => deepFreeze$1(opts));
2067
+ const createDefaultOptions = S.decodeEffect(StrykerOptionsSchema)({}).pipe(Effect$1.orDie);
2068
+ Effect$1.map(createDefaultOptions, (opts) => deepFreeze(opts));
1943
2069
  `
1944
2070
  Example of how a config file should look:
1945
2071
  /**
@@ -1973,7 +2099,7 @@ const firstExistingConfigFile = (fileNames) => Option.match(Option.fromUndefined
1973
2099
  });
1974
2100
  const configFileFor = (configFileName) => Effect$1.gen(function* () {
1975
2101
  const pathService = yield* Path.Path;
1976
- const supported = SUPPORTED_CONFIG_FILE_EXTENSIONS$1.includes(configFileExtension(configFileName, pathService));
2102
+ const supported = SUPPORTED_CONFIG_FILE_EXTENSIONS.includes(configFileExtension(configFileName, pathService));
1977
2103
  const legacy = isLegacyConfigFile(configFileName, pathService);
1978
2104
  return yield* Match.value(supported).pipe(Match.when(true, () => requireExistingConfigFile(configFileName)), Match.orElse(() => Effect$1.fail(ConfigFileUnsupportedError.make({
1979
2105
  file: configFileName,
@@ -1994,14 +2120,14 @@ const warnShadowedLegacyConfig = (supportedFile) => firstLegacyConfigFile().pipe
1994
2120
  onNone: () => Effect$1.succeed(supportedFile),
1995
2121
  onSome: (file) => Effect$1.as(legacyConfigWarning(file, supportedFile), supportedFile)
1996
2122
  })));
1997
- const discoverConfigFile = () => firstExistingConfigFile([...SUPPORTED_CONFIG_FILE_NAMES$1]).pipe(Effect$1.flatMap((found) => Option.match(found, {
2123
+ const discoverConfigFile = () => firstExistingConfigFile([...SUPPORTED_CONFIG_FILE_NAMES]).pipe(Effect$1.flatMap((found) => Option.match(found, {
1998
2124
  onNone: () => refuseLegacyOnlyProject().pipe(Effect$1.map((legacy) => Option.getOrUndefined(legacy))),
1999
2125
  onSome: (supportedFile) => warnShadowedLegacyConfig(supportedFile)
2000
2126
  })));
2001
2127
  function findConfigFile(configFileName) {
2002
- return Match.value(configFileName).pipe(Match.when(Match.string, (fileName) => configFileFor(fileName)), Match.orElse(() => discoverConfigFile()));
2128
+ return typeof configFileName === "string" ? configFileFor(configFileName) : discoverConfigFile();
2003
2129
  }
2004
- const resolveChildExtends = (configFile, child, configEnv) => Match.value("extends" in child).pipe(Match.when(true, () => resolveExtends(configFile, child, configEnv)), Match.orElse(() => Effect$1.succeed(child)));
2130
+ const resolveChildExtends = (configFile, child, configEnv) => "extends" in child ? resolveExtends(configFile, child, configEnv) : Effect$1.succeed(child);
2005
2131
  const isCiEnvironment = Config.String("CI").pipe(Effect$1.map((value) => Match.value(value.trim().toLowerCase()).pipe(Match.when("", () => false), Match.when("0", () => false), Match.when("false", () => false), Match.orElse(() => true))), Effect$1.orElseSucceed(() => false));
2006
2132
  function loadOptionsFromConfigFile(cliOptions, configEnv) {
2007
2133
  return findConfigFile(cliOptions["configFile"]).pipe(Effect$1.flatMap((configFile) => Match.value(configFile).pipe(Match.when(void 0, () => Effect$1.succeed({})), Match.orElse((found) => readConfigModule(found, configEnv).pipe(Effect$1.flatMap((child) => resolveChildExtends(found, child, configEnv)))))));
@@ -2016,7 +2142,7 @@ function readConfig(cliOptions, invocation) {
2016
2142
  };
2017
2143
  const cliRecord = yield* S.decodeEffect(ConfigDocumentSchema)(cliOptions).pipe(Effect$1.orDie);
2018
2144
  const fileRecord = yield* loadOptionsFromConfigFile(cliRecord, configEnv);
2019
- const fileOptions = yield* Result.match(S.decodeUnknownResult(ConfigDocumentSchema)(fileRecord), {
2145
+ const fileOptions = yield* Result.match(S.decodeResult(ConfigDocumentSchema)(fileRecord), {
2020
2146
  onFailure: (cause) => Effect$1.fail(ConfigFileInvalidError.make({
2021
2147
  file: "config",
2022
2148
  cause
@@ -2134,7 +2260,9 @@ const failPluginLoad = (descriptor, error) => Effect$1.logWarning(`Error during
2134
2260
  }))));
2135
2261
  const modulePluginContributions = (module) => Match.value(Predicate.hasProperty(module, "strykerPlugins")).pipe(Match.when(true, () => Schema.decodeUnknownResult(PluginModuleSchema)(module).pipe(Result.map((pluginModule) => pluginModule.strykerPlugins))), Match.orElse(() => Result.succeed(void 0)));
2136
2262
  const moduleIgnorers = (module) => Match.value(Predicate.hasProperty(module, "strykerIgnorers")).pipe(Match.when(true, () => Schema.decodeUnknownResult(IgnorerModuleSchema)(module).pipe(Result.map((ignorerModule) => ignorerModule.strykerIgnorers))), Match.orElse(() => Result.succeed(void 0)));
2137
- const moduleSchemaContribution = (module) => Match.value(module).pipe(Match.when(hasValidationSchemaContribution, (contribution) => contribution.strykerValidationSchema), Match.orElse(() => void 0));
2263
+ const moduleSchemaContribution = (module) => {
2264
+ if (hasValidationSchemaContribution(module)) return module.strykerValidationSchema;
2265
+ };
2138
2266
  const pluginContributionsOf = (module) => Result.flatMap(moduleIgnorers(module), (ignorers) => Result.map(modulePluginContributions(module), (plugins) => ({
2139
2267
  plugins,
2140
2268
  ignorers,
@@ -2665,96 +2793,6 @@ const planInstrumentation = Workflow.make(InstrumentCommand, (command) => Match.
2665
2793
  fileCount: command.fileCount
2666
2794
  })))));
2667
2795
  //#endregion
2668
- //#region src/config-defaults.ts
2669
- const isNonNullObject = (value) => typeof value === "object" && value !== null;
2670
- const isArrayValue = (value) => Array.isArray(value);
2671
- const freezeArrayValue = (value) => Object.freeze(value.map(deepFreeze));
2672
- const freezeMapEntry = ([entryKey, entryValue]) => [deepFreeze(entryKey), deepFreeze(entryValue)];
2673
- const freezeMapValue = (value) => Object.freeze(new Map([...value.entries()].map(freezeMapEntry)));
2674
- const freezeSetValue = (value) => Object.freeze(new Set([...value.values()].map(deepFreeze)));
2675
- const freezeRegExpValue = (value) => Object.freeze(value);
2676
- const freezeRecordValue = (value) => Object.freeze(Object.entries(value).reduce((frozen, [property, propertyValue]) => {
2677
- frozen[property] = deepFreeze(propertyValue);
2678
- return frozen;
2679
- }, {}));
2680
- function deepFreeze(target) {
2681
- return Match.value(target).pipe(Match.when(isArrayValue, freezeArrayValue), Match.when((value) => value instanceof Map, freezeMapValue), Match.when((value) => value instanceof RegExp, freezeRegExpValue), Match.when((value) => value instanceof Set, freezeSetValue), Match.when(isNonNullObject, freezeRecordValue), Match.orElse(() => target));
2682
- }
2683
- const optionsPath = (...path) => path.join(".");
2684
- const SUPPORTED_CONFIG_FILE_EXTENSIONS = [
2685
- ".ts",
2686
- ".mts",
2687
- ".js",
2688
- ".mjs"
2689
- ];
2690
- const configFileNames = (extensions) => extensions.map((extension) => `stryker.config${extension}`);
2691
- const SUPPORTED_CONFIG_FILE_NAMES = Object.freeze(configFileNames(SUPPORTED_CONFIG_FILE_EXTENSIONS));
2692
- const RELATIVE_SPECIFIER_PREFIXES = [
2693
- "/",
2694
- "./",
2695
- "../",
2696
- "file://"
2697
- ];
2698
- function isModuleSpecifier(value) {
2699
- return RELATIVE_SPECIFIER_PREFIXES.every((prefix) => value.startsWith(prefix) === false);
2700
- }
2701
- const CONFIG_SYNTAX_HELP = `
2702
- Example of how a config file should look:
2703
- /**
2704
- * @type {import('@systemfsoftware/stryker-js-plugin-interface').StrykerOptions}
2705
- */
2706
- export default {
2707
- mutate: ['src/**/*.js'],
2708
- testRunner: 'vitest',
2709
- reporters: ['progress', 'clear-text', 'html'],
2710
- concurrency: 4
2711
- };
2712
- See https://stryker-mutator.io/docs/stryker-js/config-file for more information.`.trim();
2713
- const createDefaultOptions = S.decodeEffect(StrykerOptionsSchema)({}).pipe(Effect$1.orDie);
2714
- const defaultOptions = Effect$1.map(createDefaultOptions, (opts) => deepFreeze(opts));
2715
- const scopedUnserializable = (scope) => (description) => ({
2716
- ...description,
2717
- path: [scope, ...description.path]
2718
- });
2719
- const describeUnserializableChild = (child, scope) => Match.value(findUnserializables(child)).pipe(Match.when(void 0, () => []), Match.orElse((descriptions) => descriptions.map(scopedUnserializable(scope))));
2720
- const collectUnserializables = (groups) => {
2721
- const found = groups.flat();
2722
- if (found.length > 0) return found;
2723
- };
2724
- const describeUnserializableArray = (value) => collectUnserializables(value.map((child, index) => describeUnserializableChild(child, index.toString())));
2725
- const describeUnserializableRecord = (value) => collectUnserializables(Object.entries(value).map(([key, child]) => describeUnserializableChild(child, key)));
2726
- const isPlainObjectValue = (value) => !Array.isArray(value) && value.constructor === Object;
2727
- const classNameOf = (value) => Match.value(value.constructor).pipe(Match.when(Match.defined, (ctor) => ctor.name), Match.orElse(() => "Object"));
2728
- const describeUnserializableInstance = (value) => [{
2729
- path: [],
2730
- reason: `Value is an instance of "${classNameOf(value)}", this detail will get lost in translation during serialization`
2731
- }];
2732
- const describeUnserializableObject = (value) => Match.value(value).pipe(Match.when(isArrayValue, describeUnserializableArray), Match.when(isPlainObjectValue, describeUnserializableRecord), Match.orElse(describeUnserializableInstance));
2733
- const NON_JSON_PRIMITIVE_TYPES = [
2734
- "bigint",
2735
- "function",
2736
- "symbol"
2737
- ];
2738
- const isNonJsonPrimitive = (value) => NON_JSON_PRIMITIVE_TYPES.includes(typeof value);
2739
- const describeNonJsonPrimitive = (value) => [{
2740
- path: [],
2741
- reason: `Primitive type "${typeof value}" has no JSON representation`
2742
- }];
2743
- const describeNumber = (value) => {
2744
- if (isFinite(value)) return void 0;
2745
- return [{
2746
- reason: `Number value \`${value}\` has no JSON representation`,
2747
- path: []
2748
- }];
2749
- };
2750
- function findUnserializables(thing) {
2751
- return Match.value(thing).pipe(Match.when((value) => typeof value === "number", describeNumber), Match.when(isNonJsonPrimitive, describeNonJsonPrimitive), Match.when(isNonNullObject, describeUnserializableObject), Match.orElse(() => void 0));
2752
- }
2753
- function isWarningEnabled(warningType, warningOptions) {
2754
- if (typeof warningOptions === "boolean") return warningOptions;
2755
- else return warningOptions[warningType] === true;
2756
- }
2757
- //#endregion
2758
2796
  //#region src/IncrementalReport.schema.ts
2759
2797
  const PositionSchema = S.Struct({
2760
2798
  line: S.Finite,
@@ -2845,7 +2883,7 @@ const decide = (command) => Option.match(Option.fromUndefinedOr(command.report),
2845
2883
  const admitIncrementalReport = Workflow.total(AdmitIncrementalReportCommand, (command) => Result.succeed(decide(command)));
2846
2884
  //#endregion
2847
2885
  //#region src/stryker-package.ts
2848
- const strykerVersion = "10.0.1";
2886
+ const strykerVersion = "10.1.1";
2849
2887
  //#endregion
2850
2888
  //#region src/incremental-report.cell.ts
2851
2889
  const parseAndDecodeIncrementalReport = S.decodeUnknownResult(S.fromJsonString(IncrementalReportSchema));
@@ -3277,7 +3315,7 @@ function readProject(options, targetMutatePatterns, basePath) {
3277
3315
  ];
3278
3316
  return Effect$1.gen(function* () {
3279
3317
  const inputFileNames = yield* resolveInputFileNames(ignoreRules, basePath);
3280
- const defaults = yield* defaultOptions;
3318
+ const defaults = yield* defaultOptions$1;
3281
3319
  const decision = selectFiles((() => {
3282
3320
  if (targetMutatePatterns === void 0) return {
3283
3321
  inputFileNames,
@@ -3619,7 +3657,7 @@ const makeDisableTypeChecksPreprocessor = (options, impl) => (project) => Effect
3619
3657
  message: "disableTypeChecks failed",
3620
3658
  cause
3621
3659
  })), Effect$1.tapError(() => Effect$1.gen(function* () {
3622
- if (isWarningEnabled("preprocessorErrors", options.warnings)) yield* Effect$1.logWarning(`Unable to disable type checking for file "${name}". Shouldn't type checking be disabled for this file? Consider configuring a more restrictive "${optionsPath("disableTypeChecks")}" settings (or turn it completely off with \`false\`)`);
3660
+ if (isWarningEnabled$1("preprocessorErrors", options.warnings)) yield* Effect$1.logWarning(`Unable to disable type checking for file "${name}". Shouldn't type checking be disabled for this file? Consider configuring a more restrictive "${optionsPath$1("disableTypeChecks")}" settings (or turn it completely off with \`false\`)`);
3623
3661
  })), Effect$1.orElseSucceed(() => void 0));
3624
3662
  if (content !== void 0) return withContent(file, content);
3625
3663
  })), Match.orElse(() => Effect$1.succeedNone)), { concurrency: 24 })).forEach(mergeUpdatedInto(project));
@@ -3647,7 +3685,10 @@ const parseJsonText = (jsonText) => {
3647
3685
  return Result.fail(errorToString(error));
3648
3686
  }
3649
3687
  };
3650
- const parseTsConfigShape = (fileName, parsed) => Match.value(parsed).pipe(Match.when(Schema.is(TsConfigSchema), (config) => Result.succeed(config)), Match.orElse(() => Result.fail(tsConfigParseError(fileName, `parsed to ${JSON.stringify(parsed)}, which does not match the tsconfig shape this package consumes`))));
3688
+ const parseTsConfigShape = (fileName, parsed) => {
3689
+ if (Schema.is(TsConfigSchema)(parsed)) return Result.succeed(parsed);
3690
+ return Result.fail(tsConfigParseError(fileName, `parsed to ${JSON.stringify(parsed)}, which does not match the tsconfig shape this package consumes`));
3691
+ };
3651
3692
  function parseTsConfig(fileName, jsonText) {
3652
3693
  return Result.match(parseJsonText(jsonText), {
3653
3694
  onFailure: (reason) => Result.fail(tsConfigParseError(fileName, reason)),
@@ -5079,6 +5120,15 @@ const reportDroppedMutants = (dropped) => Match.value(dropped.length).pipe(Match
5079
5120
  yield* Metric.update(checkerMutantsSkipped, dropped.length);
5080
5121
  yield* Effect$1.logWarning(`${dropped.length} mutant(s) cannot be described to a checker and were left out of the run (${droppedIdsOf(dropped)})`);
5081
5122
  })));
5123
+ const stringReasonOrUndefined = (val) => typeof val === "string" ? val : void 0;
5124
+ const reasonOf = (result) => "reason" in result ? stringReasonOrUndefined(result.reason) : void 0;
5125
+ const stopWallClock = (result) => {
5126
+ if (!wallClockTimeoutStopsRun(result.status, reasonOf(result))) return Effect$1.void;
5127
+ return Effect$1.fail(StageError.make({
5128
+ stage: "mutationTest",
5129
+ reason: WALL_CLOCK_TIMEOUT_REASON
5130
+ }));
5131
+ };
5082
5132
  const mutationTestCell$1 = Sandwich.read((command) => Effect$1.gen(function* () {
5083
5133
  yield* Scope.Scope;
5084
5134
  return { prev: command };
@@ -5295,6 +5345,15 @@ const mutationTestCell$1 = Sandwich.read((command) => Effect$1.gen(function* ()
5295
5345
  OutOfMemoryError: (error) => invalidateSlot(pool, runner, error),
5296
5346
  ChildProcessCrashedError: (error) => invalidateSlot(pool, runner, error)
5297
5347
  }));
5348
+ if (invalidatesRunnerPool(result.status, reasonOf(result))) return yield* invalidateSlot(pool, runner, ChildProcessCrashedError.make({
5349
+ pid: 0,
5350
+ exit: {
5351
+ _tag: "Signal",
5352
+ signal: "SIGKILL"
5353
+ },
5354
+ cause: "wall-clock timeout"
5355
+ }));
5356
+ yield* stopWallClock(result);
5298
5357
  const reported = yield* reporting.reportMutantRunResult(toReportedMutant(plan.mutant), result);
5299
5358
  const prepared = preparedStreamableOf(reported);
5300
5359
  const finished = yield* offerFinished(reported, prepared);
@@ -5529,7 +5588,7 @@ const drawMutationScoreTable = (metricsResult, options) => {
5529
5588
  //#endregion
5530
5589
  //#region src/Reporter.schema.ts
5531
5590
  var ClearTextReportCommand = class extends S.TaggedClass()("ClearTextReportCommand", {
5532
- report: MutationTestResultSchema$1,
5591
+ report: MutationTestResultSchema,
5533
5592
  metrics: MetricsResultSchema
5534
5593
  }) {};
5535
5594
  //#endregion
@@ -6013,9 +6072,7 @@ var MachineReporters = class extends S.TaggedClass()("MachineReporters", { repor
6013
6072
  const planPrepare = Workflow.total(PrepareDecoded, (decoded) => Match.value(decoded.mode).pipe(Match.when("human", () => Result.succeed(HumanReporters.make({ reporters: decoded.reporters }))), Match.when("machine", () => Result.succeed(MachineReporters.make({ reporters: decoded.reporters }))), Match.exhaustive));
6014
6073
  //#endregion
6015
6074
  //#region src/run/prepare.cell.ts
6016
- const isRecord = Predicate.isObject;
6017
- const asRecord = (value) => Match.value(value).pipe(Match.when(isRecord, (record) => Object.fromEntries(Object.entries(record))), Match.orElse(() => ({})));
6018
- const schemaPropertiesOf = (document) => asRecord(asRecord(document)["properties"]);
6075
+ const schemaPropertiesOf = (document) => Option.getOrElse(Option.fromNullishOr(document.properties), () => ({}));
6019
6076
  const buildMergedSchema = (core, contributions) => Match.value(contributions.length === 0).pipe(Match.when(true, () => core), Match.orElse(() => ({
6020
6077
  ...core,
6021
6078
  properties: Object.assign({}, schemaPropertiesOf(core), ...contributions.map((contribution) => schemaPropertiesOf(contribution)))
@@ -6319,7 +6376,6 @@ const makeRunEventStream = (resolved) => Effect$1.gen(function* () {
6319
6376
  const RunEventStreamLive = Layer.succeed(RunEventStreamPort, RunEventStreamPort.of({ createRunEventStream: (resolved) => makeRunEventStream(resolved) }));
6320
6377
  //#endregion
6321
6378
  //#region ../stryker-js-html-reporter/dist/index.mjs
6322
- const MutationTestResultSchema = S.Unknown;
6323
6379
  var HtmlReportCommand = class extends S.TaggedClass()("HtmlReportCommand", {
6324
6380
  report: MutationTestResultSchema,
6325
6381
  scriptContent: S.String
@@ -6449,4 +6505,4 @@ const strykerCell = (options, targetMutatePatterns) => Effect$1.gen(function* ()
6449
6505
  }, prepareCommandOf(options, patterns));
6450
6506
  });
6451
6507
  //#endregion
6452
- export { validateOptions as $, strykerVersion as A, MergeResult as At, optionsPath as B, checkerMutantsSkipped as C, ExtendsStepDone as Ct, CheckerSkippedRequested as D, ExtendsStepStateSchema as Dt, CheckerAnsweredUnrequested as E, ExtendsStepResolve as Et, deepFreeze as F, toRelativeNormalizedFileName as Ft, decideExtendsStep as G, REPORTER_EVENT_BATCH_BOUND as H, defaultOptions as I, RunEvents as It, importModule as J, describeErrors as K, findUnserializables as L, RunIdentity as Lt, CONFIG_SYNTAX_HELP as M, extendsPropertySchema as Mt, SUPPORTED_CONFIG_FILE_NAMES as N, forkOptionsSchema as Nt, createFileMatcher as O, ImportedModuleSchema as Ot, createDefaultOptions as P, survivorsPriorReport as Pt, readConfig as Q, isModuleSpecifier as R, RunEventWireLine as Rt, checkerMutantsChecked as S, ExtendsStepDocumentSchema as St, checkerRpcFailures as T, ExtendsStepRefused as Tt, reporterWorkerFactory as U, RunEnvironment as V, spawnReporterWorker as W, loadConfigCell as X, initialExtendsStepState as Y, mergeConfigs as Z, verdictExitClass as _, ConfigError as _t, RUN_EVENTS_QUEUE_BOUND as a, IdGenerator as at, checkGroupedPlans as b, ConfigFileUnreadableError as bt, shouldKeepTempDir as c, OutOfMemoryError as ct, buildVerdictEnvelope as d, isVmRunner as dt, buildTestRunner as et, generateRunId as f, vmRunnerCapabilities as ft, resolveExitCode as g, ConfigDocumentSchema as gt, highestExitClass as h, StrykerError as ht, makeRunEventStream as i, makeWorkerClient as it, IncrementalReportSchema as j, ReadConfigCommand as jt, matchesFile as k, MergeCommand as kt, ACTIONABLE_STATUSES as l, WorkerBootTimeoutError as lt, EXIT_CODE as m, vmTestRunner as mt, RunEventDrain as n, WorkerLauncher as nt, makeRunLayer as o, classifyWorkerExit as ot, isActionableStatus as p, vmRunnerName as pt, forkCoreSchema as q, RunEventDrainLive as r, connectRetry as rt, mutationTestCell as s, ChildProcessCrashedError as st, strykerCell as t, isCommandRunner as tt, VERDICT_ENVELOPE_SCHEMA_VERSION as u, VmRunner as ut, calculateMetrics as v, ConfigFileInvalidError as vt, checkerProcessCrashes as w, ExtendsStepRead as wt, checkerDuration as x, ConfigFileUnsupportedError as xt, countMutants as y, ConfigFileNotFoundError as yt, isWarningEnabled as z, S as zt };
6508
+ export { OutOfMemoryError as $, strykerVersion as A, defaultOptions$1 as At, initialExtendsStepState as B, checkerMutantsSkipped as C, extendsPropertySchema as Ct, CheckerSkippedRequested as D, SUPPORTED_CONFIG_FILE_NAMES$1 as Dt, CheckerAnsweredUnrequested as E, CONFIG_SYNTAX_HELP$1 as Et, spawnReporterWorker as F, toRelativeNormalizedFileName as Ft, buildTestRunner as G, mergeConfigs as H, decideExtendsStep as I, RunEvents as It, connectRetry as J, isCommandRunner as K, describeErrors as L, RunIdentity as Lt, RunEnvironment as M, isModuleSpecifier$1 as Mt, REPORTER_EVENT_BATCH_BOUND as N, isWarningEnabled$1 as Nt, createFileMatcher as O, createDefaultOptions$1 as Ot, reporterWorkerFactory as P, optionsPath$1 as Pt, ChildProcessCrashedError as Q, forkCoreSchema as R, RunEventWireLine as Rt, checkerMutantsChecked as S, ReadConfigCommand as St, checkerRpcFailures as T, survivorsPriorReport as Tt, readConfig as U, loadConfigCell as V, validateOptions as W, IdGenerator as X, makeWorkerClient as Y, classifyWorkerExit as Z, verdictExitClass as _, ExtendsStepResolve as _t, RUN_EVENTS_QUEUE_BOUND as a, vmTestRunner as at, checkGroupedPlans as b, MergeCommand as bt, shouldKeepTempDir as c, ConfigError as ct, buildVerdictEnvelope as d, ConfigFileUnreadableError as dt, WorkerBootTimeoutError as et, generateRunId as f, ConfigFileUnsupportedError as ft, resolveExitCode as g, ExtendsStepRefused as gt, highestExitClass as h, ExtendsStepRead as ht, makeRunEventStream as i, vmRunnerName as it, IncrementalReportSchema as j, findUnserializables as jt, matchesFile as k, deepFreeze$1 as kt, ACTIONABLE_STATUSES as l, ConfigFileInvalidError as lt, EXIT_CODE as m, ExtendsStepDone as mt, RunEventDrain as n, isVmRunner as nt, makeRunLayer as o, StrykerError as ot, isActionableStatus as p, ExtendsStepDocumentSchema as pt, WorkerLauncher as q, RunEventDrainLive as r, vmRunnerCapabilities as rt, mutationTestCell as s, ConfigDocumentSchema as st, strykerCell as t, VmRunner as tt, VERDICT_ENVELOPE_SCHEMA_VERSION as u, ConfigFileNotFoundError as ut, calculateMetrics as v, ExtendsStepStateSchema as vt, checkerProcessCrashes as w, forkOptionsSchema as wt, checkerDuration as x, MergeResult as xt, countMutants as y, ImportedModuleSchema as yt, importModule as z, S as zt };