@systemfsoftware/stryker-js-vitest-runner 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @systemfsoftware/stryker-js-vitest-runner
2
2
 
3
+ ## 4.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Re-released against @systemfsoftware/stryker-js without the removed --llms manifest. The Run stream no longer carries a manifest terminal event, and the RunEvent / RunTerminalEvent unions no longer include the manifest arm, so any exhaustive consumer of those types must drop that case.
8
+
9
+ - Rebuilds against updated workspace dependencies, including the new
10
+ `@systemfsoftware/stryker-js` reporter protocol major.
11
+
12
+ - Updated dependencies:
13
+ - @systemfsoftware/stryker-js@3.0.0
14
+
3
15
  ## 4.0.0
4
16
 
5
17
  ### Major Changes
package/dist/index.d.mts CHANGED
@@ -6,7 +6,7 @@
6
6
  * in, so the requirement is visible in the type and an engine that does not
7
7
  * provide it fails to compile.
8
8
  */
9
- declare const strykerPlugins: import("@systemfsoftware/stryker-js/Plugin").PluginContribution<"TestRunner">[];
9
+ declare const strykerPlugins: import("@systemfsoftware/stryker-js/Plugin").PluginLayerContribution<"TestRunner">[];
10
10
  /**
11
11
  * The `vitest` option section as a JSON Schema document, for Stryker's option
12
12
  * validation — derived from the declaration, never read from a file. It is built
package/dist/index.mjs CHANGED
@@ -12,6 +12,7 @@ import * as FileSystem from "effect/FileSystem";
12
12
  import * as Match from "effect/Match";
13
13
  import * as Option from "effect/Option";
14
14
  import * as Path from "effect/Path";
15
+ import * as Predicate from "effect/Predicate";
15
16
  import * as Ref from "effect/Ref";
16
17
  import * as Result from "effect/Result";
17
18
  import { Effect } from "effect";
@@ -110,17 +111,15 @@ const toRawTestIdRaw$1 = (test) => {
110
111
  onSome: (file) => Option.getOrElse(Option.fromNullishOr(getFilepath$1(file)), () => "unknown.js")
111
112
  })}#${collectTestNameRaw$1(test)}`;
112
113
  };
114
+ const stripProjectRoot = (file, projectRoot) => Match.value(file.startsWith(projectRoot)).pipe(Match.when(true, () => file.slice(projectRoot.length)), Match.when(false, () => file), Match.exhaustive);
115
+ const toProjectRelativePath = (file) => file.replace(/^[/\\]+/, "").replaceAll("\\", "/");
113
116
  const normalizeTestIdRaw$1 = (id, projectRoot) => {
114
117
  const hash = id.indexOf("#");
115
- if (hash === -1) return id;
116
- const file = id.slice(0, hash);
117
- const rest = id.slice(hash + 1);
118
- return `${(() => {
119
- if (file.startsWith(projectRoot)) return file.slice(projectRoot.length);
120
- return file;
121
- })().replace(/^[/\\]+/, "").replaceAll("\\", "/")}#${rest}`;
118
+ return Match.value(hash === -1).pipe(Match.when(true, () => id), Match.when(false, () => {
119
+ return `${toProjectRelativePath(stripProjectRoot(id.slice(0, hash), projectRoot))}#${id.slice(hash + 1)}`;
120
+ }), Match.exhaustive);
122
121
  };
123
- const toTestStatus$1 = (taskState, mode) => Match.value(mode === "skip").pipe(Match.when(true, () => "skipped"), Match.when(false, () => Match.value(taskState).pipe(Match.when("pass", () => "success"), Match.when("fail", () => "failed"), Match.when("skip", () => "skipped"), Match.when("todo", () => "skipped"), Match.when(void 0, () => "failed"), Match.when("queued", () => "failed"), Match.when("run", () => "failed"), Match.when("only", () => "failed"), Match.orElse(() => "failed"))), Match.exhaustive);
122
+ const toTestStatus$1 = (taskState, mode) => Match.value(mode === "skip").pipe(Match.when(true, () => "skipped"), Match.when(false, () => Match.value(taskState).pipe(Match.when("pass", () => "success"), Match.when("skip", () => "skipped"), Match.when("todo", () => "skipped"), Match.orElse(() => "failed"))), Match.exhaustive);
124
123
  const findSuiteErrorRaw$1 = (suite) => Option.match(Option.fromNullishOr(suite), {
125
124
  onNone: () => void 0,
126
125
  onSome: (current) => Option.match(recordOption$1(current), {
@@ -194,7 +193,12 @@ const convertTestRaw$1 = (test, projectRoot) => {
194
193
  status
195
194
  })));
196
195
  };
197
- const decideVitestDryRun$1 = (command) => Match.value(command.rawTests.map((t) => convertTestRaw$1(t, command.projectRoot))).pipe(Match.when((tests) => tests.some((t) => t.status === "failed") === false && command.hasExternalError === true, (tests) => Result.succeed(VitestDryRunOutput.make({
196
+ /**
197
+ * A run whose tests all passed yet reported an error outside the test files: the shape a dry
198
+ * run reports as `Error` rather than `Complete`.
199
+ */
200
+ const isSilentExternalError = (tests, command) => Match.value(tests.some((test) => test.status === "failed")).pipe(Match.when(true, () => false), Match.when(false, () => command.hasExternalError), Match.exhaustive);
201
+ const decideVitestDryRun$1 = (command) => Match.value(command.rawTests.map((t) => convertTestRaw$1(t, command.projectRoot))).pipe(Match.when((tests) => isSilentExternalError(tests, command), (tests) => Result.succeed(VitestDryRunOutput.make({
198
202
  status: "Error",
199
203
  testsJson: JSON.stringify(tests),
200
204
  errorMessage: `An error occurred outside of a test run: ${command.externalErrorText}`
@@ -203,11 +207,7 @@ const decideVitestDryRun$1 = (command) => Match.value(command.rawTests.map((t) =
203
207
  testsJson: JSON.stringify(tests),
204
208
  errorMessage: void 0
205
209
  }))));
206
- const hitLimitReason = (hitCount, hitLimit) => {
207
- if (hitCount === void 0 || hitLimit === void 0) return Option.none();
208
- if (hitCount > hitLimit) return Option.some(`Hit limit reached (${hitCount}/${hitLimit})`);
209
- return Option.none();
210
- };
210
+ const hitLimitReason = (hitCount, hitLimit) => Option.flatMap(Option.fromNullishOr(hitCount), (count) => Option.flatMap(Option.fromNullishOr(hitLimit), (limit) => Match.value(count > limit).pipe(Match.when(true, () => Option.some(`Hit limit reached (${count}/${limit})`)), Match.when(false, () => Option.none()), Match.exhaustive)));
211
211
  const decideVitestMutantRun = (command) => Match.value(hitLimitReason(command.hitCount, command.hitLimit)).pipe(Match.when(Option.isSome, (hit) => Result.succeed(MutantTimeout.make({
212
212
  testsJson: "[]",
213
213
  reason: hit.value
@@ -290,7 +290,7 @@ const PackageManifest = S.StructWithRest(S.Struct({
290
290
  name: S.optional(S.String),
291
291
  exports: S.optional(S.Record(S.String, ExportEntry))
292
292
  }), [S.Record(S.String, S.Unknown)]);
293
- const VitestNodeModuleSchema = S.declare((input) => input !== null && typeof input === "object" && !Array.isArray(input), { description: "The project-local vitest/node module" });
293
+ const VitestNodeModuleSchema = S.declare((input) => Predicate.isObject(input), { description: "The project-local vitest/node module" });
294
294
  const VitestPackageSchema = S.Struct({ version: S.String });
295
295
  var VitestDryRunCommand = class extends S.TaggedClass()("VitestDryRunCommand", {
296
296
  rawTests: S.Array(S.Unknown),
@@ -330,8 +330,7 @@ function collectTestsFromSuite(suite) {
330
330
  });
331
331
  }
332
332
  function isErrorCodeError(error) {
333
- if (error instanceof Error && "code" in error) return typeof Reflect.get(error, "code") === "string";
334
- return false;
333
+ return error instanceof Error && typeof Reflect.get(error, "code") === "string";
335
334
  }
336
335
  const VITEST_ERROR_CODES = Object.freeze({ FILES_NOT_FOUND: "VITEST_FILES_NOT_FOUND" });
337
336
  const recordOption = (value) => S.decodeUnknownOption(S.Record(S.String, S.Unknown))(value);
@@ -392,7 +391,7 @@ const normalizeTestIdRaw = (id, projectRoot) => {
392
391
  return file;
393
392
  })().replace(/^[/\\]+/, "").replaceAll("\\", "/")}#${rest}`;
394
393
  };
395
- const toTestStatus = (taskState, mode) => Match.value(mode === "skip").pipe(Match.when(true, () => "skipped"), Match.when(false, () => Match.value(taskState).pipe(Match.when("pass", () => "success"), Match.when("fail", () => "failed"), Match.when("skip", () => "skipped"), Match.when("todo", () => "skipped"), Match.when(void 0, () => "failed"), Match.when("queued", () => "failed"), Match.when("run", () => "failed"), Match.when("only", () => "failed"), Match.orElse(() => "failed"))), Match.exhaustive);
394
+ const toTestStatus = (taskState, mode) => Match.value(mode === "skip").pipe(Match.when(true, () => "skipped"), Match.when(false, () => Match.value(taskState).pipe(Match.when("pass", () => "success"), Match.when("skip", () => "skipped"), Match.when("todo", () => "skipped"), Match.orElse(() => "failed"))), Match.exhaustive);
396
395
  const findSuiteErrorRaw = (suite) => Option.match(Option.fromNullishOr(suite), {
397
396
  onNone: () => void 0,
398
397
  onSome: (current) => Option.match(recordOption(current), {
@@ -468,55 +467,48 @@ const convertTestRaw = (test, projectRoot) => {
468
467
  };
469
468
  const decideVitestDryRun = (command) => {
470
469
  const tests = command.rawTests.map((t) => convertTestRaw(t, command.projectRoot));
471
- if (tests.some((t) => t.status === "failed") === false && command.hasExternalError) return DryRunExternalError.make({
472
- testsJson: JSON.stringify(tests),
470
+ const testsJson = JSON.stringify(tests);
471
+ const hasFailure = tests.some((t) => t.status === "failed");
472
+ return Match.value(hasFailure).pipe(Match.when(true, () => DryRunComplete.make({ testsJson })), Match.orElse(() => Match.value(command.hasExternalError).pipe(Match.when(true, () => DryRunExternalError.make({
473
+ testsJson,
473
474
  errorMessage: `An error occurred outside of a test run: ${command.externalErrorText}`
474
- });
475
- return DryRunComplete.make({ testsJson: JSON.stringify(tests) });
475
+ })), Match.orElse(() => DryRunComplete.make({ testsJson })))));
476
476
  };
477
477
  const SOURCE_CONDITION = "@systemfsoftware/source";
478
- const sourceTargetOf = (entry) => {
479
- if (typeof entry === "string") return (() => {
480
- if (entry.endsWith(".ts") || entry.endsWith(".tsx") || entry.endsWith(".mts")) return entry;
481
- })();
482
- const source = entry[SOURCE_CONDITION];
483
- return (() => {
484
- if (typeof source === "string") return source;
485
- })();
486
- };
487
- const specifierForExport = (packageName, exportKey) => {
488
- if (exportKey === ".") return packageName;
489
- if (exportKey === "./package.json" || !exportKey.startsWith("./")) return void 0;
490
- return `${packageName}/${exportKey.slice(2)}`;
491
- };
492
- const sandboxSelfAliases = (manifest, projectRoot, pathService) => {
493
- const name = manifest.name;
494
- const exports = manifest.exports;
495
- if (name === void 0 || name.length === 0 || exports === void 0) return [];
496
- const aliases = [];
497
- for (const [key, value] of Object.entries(exports)) {
498
- const spec = specifierForExport(name, key);
499
- const target = sourceTargetOf(value);
500
- if (spec === void 0 || target === void 0) continue;
501
- aliases.push({
502
- find: new RegExp(`^${RegExp.escape(spec)}$`),
503
- replacement: pathService.resolve(projectRoot, target)
504
- });
478
+ const TYPESCRIPT_SOURCE_EXTENSIONS = [
479
+ ".ts",
480
+ ".tsx",
481
+ ".mts"
482
+ ];
483
+ const isTypescriptSourcePath = (filePath) => TYPESCRIPT_SOURCE_EXTENSIONS.some((extension) => filePath.endsWith(extension));
484
+ const typescriptSourcePath = (filePath) => Option.getOrUndefined(Option.filter(Option.some(filePath), isTypescriptSourcePath));
485
+ const sourceTargetOf = (entry) => Match.value(entry).pipe(Match.when(Match.string, (filePath) => typescriptSourcePath(filePath)), Match.orElse((conditions) => Option.getOrUndefined(Option.flatMap(recordOption(conditions), (record) => getStringField(record, SOURCE_CONDITION)))));
486
+ const subpathSpecifier = (packageName, exportKey) => Match.value(exportKey.startsWith("./")).pipe(Match.when(true, () => `${packageName}/${exportKey.slice(2)}`), Match.orElse(() => void 0));
487
+ const specifierForExport = (packageName, exportKey) => Match.value(exportKey).pipe(Match.when(".", () => packageName), Match.when("./package.json", () => void 0), Match.orElse((key) => subpathSpecifier(packageName, key)));
488
+ const namedExports = (manifest) => Option.flatMap(Option.filter(Option.fromNullishOr(manifest.name), (name) => name.length > 0), (name) => Option.map(Option.fromNullishOr(manifest.exports), (exportMap) => ({
489
+ name,
490
+ exports: exportMap
491
+ })));
492
+ const exportAlias = (packageName, projectRoot, pathService, [exportKey, entry]) => Option.flatMap(Option.fromNullishOr(specifierForExport(packageName, exportKey)), (spec) => Option.map(Option.fromNullishOr(sourceTargetOf(entry)), (target) => ({
493
+ find: new RegExp(`^${RegExp.escape(spec)}$`),
494
+ replacement: pathService.resolve(projectRoot, target)
495
+ })));
496
+ const sandboxSelfAliases = (manifest, projectRoot, pathService) => Option.match(namedExports(manifest), {
497
+ onNone: () => [],
498
+ onSome: ({ name, exports: exportMap }) => Object.entries(exportMap).flatMap((entry) => Option.toArray(exportAlias(name, projectRoot, pathService, entry)))
499
+ });
500
+ const parseJson = (text) => {
501
+ try {
502
+ return JSON.parse(text);
503
+ } catch {
504
+ return;
505
505
  }
506
- return aliases;
507
506
  };
508
507
  const readSandboxSelfAliases = (projectRoot) => Effect$1.gen(function* () {
509
508
  const fs = yield* FileSystem.FileSystem;
510
509
  const pathService = yield* Path.Path;
511
510
  const raw = yield* fs.readFileString(pathService.join(projectRoot, "package.json")).pipe(Effect$1.orElseSucceed(() => null));
512
- if (raw === null) return [];
513
- let parsed;
514
- try {
515
- parsed = JSON.parse(raw);
516
- } catch {
517
- return [];
518
- }
519
- return Option.match(S.decodeUnknownOption(PackageManifest)(parsed), {
511
+ return Option.match(Option.flatMap(Option.fromNullishOr(raw), (content) => S.decodeUnknownOption(PackageManifest)(parseJson(content))), {
520
512
  onNone: () => [],
521
513
  onSome: (manifest) => sandboxSelfAliases(manifest, projectRoot, pathService)
522
514
  });
@@ -525,10 +517,10 @@ const sandboxSelfPlugin = (aliases) => ({
525
517
  name: "stryker-sandbox-self-exports",
526
518
  enforce: "pre",
527
519
  resolveId(source) {
528
- for (const alias of aliases) if (alias.find.test(source)) return alias.replacement;
520
+ return Option.getOrUndefined(Option.map(Option.fromNullishOr(aliases.find((alias) => alias.find.test(source))), (alias) => alias.replacement));
529
521
  }
530
522
  });
531
- const isRunnerTestSuite = (value) => typeof value === "object" && value !== null && "tasks" in value && Array.isArray(Reflect.get(value, "tasks"));
523
+ const isRunnerTestSuite = (value) => Predicate.isObject(value) && Array.isArray(value["tasks"]);
532
524
  const STRYKER_SETUP_URL = new URL("./stryker-setup.mjs", import.meta.url);
533
525
  const resolveVitest = (_dir) => Effect$1.gen(function* () {
534
526
  const fallback = Effect$1.gen(function* () {
@@ -561,72 +553,81 @@ const resolveVitest = (_dir) => Effect$1.gen(function* () {
561
553
  };
562
554
  }).pipe(Effect$1.catchCause(() => fallback), Effect$1.catchDefect(() => fallback));
563
555
  }).pipe(Effect$1.orDie);
556
+ const versionPart = (parts, index) => Option.getOrElse(Option.map(Option.fromNullishOr(parts[index]), (part) => Number(part)), () => 0);
557
+ const minimumMinorForMajor = (major) => Match.value(major).pipe(Match.when((value) => value > 4, () => Option.some(0)), Match.when(4, () => Option.some(1)), Match.orElse(() => Option.none()));
564
558
  const shouldUseSuiteMetaSecondArg = (version) => {
565
559
  const parts = version.split(".");
566
- const major = Number(parts[0] ?? "0");
567
- const minor = Number(parts[1] ?? "0");
568
- if (Number.isNaN(major) || Number.isNaN(minor)) return false;
569
- return major > 4 || major === 4 && minor >= 1;
560
+ const major = versionPart(parts, 0);
561
+ const minor = versionPart(parts, 1);
562
+ return Match.value(Number.isNaN(major) || Number.isNaN(minor)).pipe(Match.when(true, () => false), Match.orElse(() => Option.exists(minimumMinorForMajor(major), (minimumMinor) => minor >= minimumMinor)));
570
563
  };
571
- const experimentalStateGetFiles = (vitest) => vitest.state.getFiles();
572
- const experimentalStateClearFiles = (vitest) => {
573
- if (typeof vitest === "object" && vitest !== null && "state" in vitest) {
574
- const state = Reflect.get(vitest, "state");
575
- if (typeof state === "object" && state !== null && "filesMap" in state) {
576
- const filesMap = Reflect.get(state, "filesMap");
577
- if (filesMap instanceof Map) filesMap.clear();
578
- else if (typeof filesMap === "object" && filesMap !== null && "clear" in filesMap) {
579
- const clear = Reflect.get(filesMap, "clear");
580
- if (typeof clear === "function") Reflect.apply(clear, filesMap, []);
581
- }
582
- }
583
- }
584
- };
585
- const experimentalStateHasExternalErrors = (vitest) => {
586
- if (typeof vitest === "object" && vitest !== null && "state" in vitest) {
587
- const state = Reflect.get(vitest, "state");
588
- if (typeof state === "object" && state !== null && "errorsSet" in state) {
589
- const errorsSet = Reflect.get(state, "errorsSet");
590
- if (errorsSet instanceof Set) return errorsSet.size > 0;
591
- if (typeof errorsSet === "object" && errorsSet !== null && "size" in errorsSet) {
592
- const size = Reflect.get(errorsSet, "size");
593
- return (() => {
594
- if (typeof size === "number") return size > 0;
595
- return false;
596
- })();
597
- }
598
- }
599
- }
600
- return false;
564
+ const relatedFilesOf = (relatedValue, relatedFiles) => Match.value(relatedValue !== false).pipe(Match.when(true, () => Option.getOrUndefined(Option.map(Option.fromNullishOr(relatedFiles), (files) => files.map(normalizeFileName)))), Match.orElse(() => void 0));
565
+ const testIdPlan = (testIds, projectRoot, pathService) => Option.map(Option.filter(Option.fromNullishOr(testIds), (ids) => ids.length > 0), (ids) => ({
566
+ testNamePattern: new RegExp(ids.map((id) => RegExp.escape(fromTestId(id).test)).join("|")),
567
+ testFiles: ids.map((id) => pathService.resolve(projectRoot, fromTestId(id).file))
568
+ }));
569
+ const runFilterPlan = (filter, projectRoot, pathService) => {
570
+ const plan = testIdPlan(filter.testIds, projectRoot, pathService);
571
+ return {
572
+ testNamePattern: Option.getOrUndefined(Option.map(plan, (value) => value.testNamePattern)),
573
+ testFiles: Option.match(plan, {
574
+ onNone: () => Option.getOrUndefined(Option.map(Option.fromNullishOr(filter.testFiles), (files) => [...files])),
575
+ onSome: (value) => value.testFiles
576
+ })
577
+ };
601
578
  };
602
- const experimentalStateGetExternalErrorText = (vitest) => {
603
- if (typeof vitest === "object" && vitest !== null && "state" in vitest) {
604
- const state = Reflect.get(vitest, "state");
605
- if (typeof state === "object" && state !== null && "errorsSet" in state) {
606
- const errorsSet = Reflect.get(state, "errorsSet");
607
- if (errorsSet instanceof Set) return [...errorsSet].map(errorToString).join("\n");
608
- const isIterable = (value) => typeof value === "object" && value !== null && Symbol.iterator in value && typeof Reflect.get(value, Symbol.iterator) === "function";
609
- if (isIterable(errorsSet)) return [...errorsSet].map(errorToString).join("\n");
610
- }
579
+ const isMissingTestFilesCause = (cause) => Match.value(isErrorCodeError(cause)).pipe(Match.when(true, () => typeof cause === "string" && cause.includes(VITEST_ERROR_CODES.FILES_NOT_FOUND)), Match.orElse(() => false));
580
+ const experimentalStateGetFiles = (vitest) => vitest.state.getFiles();
581
+ const propertyOf = (value, key) => Option.flatMap(Option.filter(Option.fromNullishOr(value), Predicate.isObject), (record) => Option.fromNullishOr(record[key]));
582
+ const vitestStateOf = (vitest) => propertyOf(vitest, "state");
583
+ const errorsSetOf = (vitest) => Option.flatMap(vitestStateOf(vitest), (state) => propertyOf(state, "errorsSet"));
584
+ const invokeMethod = (holder, name) => Option.match(Option.filter(propertyOf(holder, name), Predicate.isFunction), {
585
+ onNone: () => void 0,
586
+ onSome: (method) => {
587
+ Reflect.apply(method, holder, []);
611
588
  }
612
- return "";
613
- };
589
+ });
590
+ const clearFilesMap = (filesMap) => Match.value(filesMap).pipe(Match.when(Match.instanceOf(Map), (map) => {
591
+ map.clear();
592
+ }), Match.orElse((value) => invokeMethod(value, "clear")));
593
+ const experimentalStateClearFiles = (vitest) => Option.match(Option.flatMap(vitestStateOf(vitest), (state) => propertyOf(state, "filesMap")), {
594
+ onNone: () => void 0,
595
+ onSome: (filesMap) => clearFilesMap(filesMap)
596
+ });
597
+ const entryCountOf = (collection) => Match.value(collection).pipe(Match.when(Match.instanceOf(Set), (set) => Option.some(set.size)), Match.orElse((value) => Option.filter(propertyOf(value, "size"), Predicate.isNumber)));
598
+ const experimentalStateHasExternalErrors = (vitest) => Option.exists(Option.flatMap(errorsSetOf(vitest), entryCountOf), (count) => count > 0);
599
+ const experimentalStateGetExternalErrorText = (vitest) => Option.match(errorsSetOf(vitest), {
600
+ onNone: () => "",
601
+ onSome: (errorsSet) => Match.value(errorsSet).pipe(Match.when(Predicate.isIterable, (errors) => [...errors].map(errorToString).join("\n")), Match.orElse(() => ""))
602
+ });
603
+ const applyHarnessValue = (ctx, key, value) => Match.value(key).pipe(Match.when("hitLimit", () => {
604
+ ctx.provide("hitLimit", Option.getOrUndefined(Option.filter(Option.fromNullishOr(value), Predicate.isNumber)));
605
+ }), Match.when("mutantActivation", () => {
606
+ Match.value(value).pipe(Match.when(Match.is("runtime", "static"), (activation) => {
607
+ ctx.provide("mutantActivation", activation);
608
+ }), Match.orElse(() => void 0));
609
+ }), Match.orElse(() => {
610
+ Match.value(value).pipe(Match.when(Predicate.isString, (activeMutant) => {
611
+ ctx.provide("activeMutant", activeMutant);
612
+ }), Match.orElse(() => void 0));
613
+ }));
614
614
  const applyRunFilterToConfig = (vitest, options) => {
615
615
  Reflect.set(vitest.config, "related", options.related);
616
616
  for (const project of vitest.projects) Reflect.set(project.config, "testNamePattern", options.testNamePattern);
617
617
  };
618
+ const disableScreenshotFailures = (value) => Option.match(Option.filter(Option.fromNullishOr(value), Predicate.isObject), {
619
+ onNone: () => void 0,
620
+ onSome: (browser) => {
621
+ Reflect.set(browser, "screenshotFailures", false);
622
+ }
623
+ });
624
+ const setupFilePathsOf = (value) => Match.value(value).pipe(Match.when(Array.isArray, (setupFiles) => setupFiles.filter(Predicate.isString)), Match.orElse(() => []));
618
625
  const applySetupFilesToProjects = (vitest, localSetupFile) => {
619
- const browser = Reflect.get(vitest.config, "browser");
620
- if (typeof browser === "object" && browser !== null) Reflect.set(browser, "screenshotFailures", false);
626
+ disableScreenshotFailures(Reflect.get(vitest.config, "browser"));
621
627
  for (const project of vitest.projects) {
622
- const setupFilesRaw = Reflect.get(project.config, "setupFiles");
623
- const files = (() => {
624
- if (Array.isArray(setupFilesRaw)) return setupFilesRaw.filter((x) => typeof x === "string");
625
- return [];
626
- })();
627
- Reflect.set(project.config, "setupFiles", [localSetupFile, ...files]);
628
- const pBrowser = Reflect.get(project.config, "browser");
629
- if (typeof pBrowser === "object" && pBrowser !== null) Reflect.set(pBrowser, "screenshotFailures", false);
628
+ const setupFiles = setupFilePathsOf(Reflect.get(project.config, "setupFiles"));
629
+ Reflect.set(project.config, "setupFiles", [localSetupFile, ...setupFiles]);
630
+ disableScreenshotFailures(Reflect.get(project.config, "browser"));
630
631
  }
631
632
  };
632
633
  const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(function* () {
@@ -677,17 +678,18 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
677
678
  phase: "init",
678
679
  cause: errorToString(cause)
679
680
  })));
680
- yield* fsService.copyFile(input.setupFilePath ?? defaultSetupPath, localSetupFile).pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
681
+ const setupFilePath = Option.getOrElse(Option.fromNullishOr(input.setupFilePath), () => defaultSetupPath);
682
+ yield* fsService.copyFile(setupFilePath, localSetupFile).pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
681
683
  runnerName: "vitest",
682
684
  phase: "init",
683
685
  cause: errorToString(cause)
684
686
  })));
685
- const { createVitest, version } = yield* (input.resolveVitestFor ?? resolveVitest)(projectRoot).pipe(Effect$1.provideService(Module, moduleService), Effect$1.provideService(FileSystem.FileSystem, fsService), Effect$1.provideService(Path.Path, pathService), Effect$1.catchDefect((cause) => Effect$1.fail(new TestRunnerFailed({
687
+ const { createVitest, version } = yield* Option.getOrElse(Option.fromNullishOr(input.resolveVitestFor), () => resolveVitest)(projectRoot).pipe(Effect$1.provideService(Module, moduleService), Effect$1.provideService(FileSystem.FileSystem, fsService), Effect$1.provideService(Path.Path, pathService), Effect$1.catchDefect((cause) => Effect$1.fail(new TestRunnerFailed({
686
688
  runnerName: "vitest",
687
689
  phase: "init",
688
690
  cause: errorToString(cause)
689
691
  }))));
690
- const namespace = input.globalNamespace ?? INSTRUMENTER_CONSTANTS.NAMESPACE;
692
+ const namespace = Option.getOrElse(Option.fromNullishOr(input.globalNamespace), () => INSTRUMENTER_CONSTANTS.NAMESPACE);
691
693
  const scanDir = (() => {
692
694
  if (typeof options.vitest.dir === "string") return pathService.resolve(projectRoot, options.vitest.dir);
693
695
  })();
@@ -744,60 +746,42 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
744
746
  const ctx = yield* requireCtx;
745
747
  experimentalStateClearFiles(ctx);
746
748
  });
747
- const getFileMeta = (file) => (() => {
748
- if (file !== null && typeof file === "object" && "meta" in file) return Reflect.get(file, "meta");
749
- })();
749
+ const getFileMeta = (file) => Option.getOrUndefined(propertyOf(file, "meta"));
750
750
  const readHitCount = Effect$1.gen(function* () {
751
751
  const ctx = yield* requireCtx.pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })));
752
- const files = experimentalStateGetFiles(ctx);
753
- let total = 0;
754
- for (const file of files) {
755
- const meta = getFileMeta(file);
756
- const decoded = yield* S.decodeUnknownEffect(HitCountMetaSchema)(meta).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.orElseSucceed(() => ({ hitCount: void 0 })));
757
- if (decoded.hitCount !== void 0) total += decoded.hitCount;
752
+ return (yield* Effect$1.forEach(experimentalStateGetFiles(ctx), (file) => Effect$1.map(S.decodeUnknownEffect(HitCountMetaSchema)(getFileMeta(file)).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.orElseSucceed(() => ({ hitCount: void 0 }))), (decoded) => Option.getOrElse(Option.fromNullishOr(decoded.hitCount), () => 0)))).reduce((total, count) => total + count, 0);
753
+ });
754
+ const stringProperty = (value, key) => Option.getOrElse(Option.filter(propertyOf(value, key), Predicate.isString), () => "");
755
+ const dedupeFilesByName = (files) => Object.fromEntries(files.map((file) => [`${stringProperty(file, "projectName")}-${stringProperty(file, "name")}`, file]));
756
+ const validateCoverage = (mutantCoverage) => {
757
+ const normalized = normalizeCoverage(mutantCoverage, input.sandboxDirectory, pathService);
758
+ return S.decodeEffect(MutantCoverageShapeSchema)(normalized).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.map(() => normalized));
759
+ };
760
+ const coverageOfFile = (file) => Effect$1.gen(function* () {
761
+ const decoded = yield* S.decodeUnknownEffect(MutantCoverageMetaSchema)(getFileMeta(file)).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.orElseSucceed(() => ({ mutantCoverage: void 0 })));
762
+ return yield* Option.match(Option.fromNullishOr(decoded.mutantCoverage), {
763
+ onNone: () => Effect$1.succeed(void 0),
764
+ onSome: (mutantCoverage) => validateCoverage(mutantCoverage)
765
+ });
766
+ });
767
+ const mergeTestCoverage = (perTest, testId, coverage) => Option.match(Option.fromNullishOr(perTest[testId]), {
768
+ onNone: () => {
769
+ perTest[testId] = coverage;
770
+ },
771
+ onSome: (existing) => {
772
+ mergeCoverage(existing, coverage);
758
773
  }
759
- return total;
760
774
  });
775
+ const mergeProjectCoverage = (acc, projectCoverage) => {
776
+ for (const [testId, testCoverage] of Object.entries(projectCoverage.perTest)) mergeTestCoverage(acc.perTest, testId, testCoverage);
777
+ mergeCoverage(acc.static, projectCoverage.static);
778
+ return acc;
779
+ };
761
780
  const readMutantCoverage = Effect$1.gen(function* () {
762
781
  const ctx = yield* requireCtx.pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })));
763
- const files = experimentalStateGetFiles(ctx);
764
- const deduped = {};
765
- for (const file of files) {
766
- const projectNameValue = (() => {
767
- if (typeof file === "object" && file !== null && "projectName" in file) return Reflect.get(file, "projectName");
768
- })();
769
- const projectName = (() => {
770
- if (typeof projectNameValue === "string") return projectNameValue;
771
- return "";
772
- })();
773
- const nameValue = (() => {
774
- if (typeof file === "object" && file !== null && "name" in file) return Reflect.get(file, "name");
775
- })();
776
- const name = (() => {
777
- if (typeof nameValue === "string") return nameValue;
778
- return "";
779
- })();
780
- deduped[`${projectName}-${name}`] = file;
781
- }
782
- const coverages = [];
783
- for (const file of Object.values(deduped)) {
784
- const rawMeta = getFileMeta(file);
785
- const decoded = yield* S.decodeUnknownEffect(MutantCoverageMetaSchema)(rawMeta).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.orElseSucceed(() => ({ mutantCoverage: void 0 })));
786
- if (decoded.mutantCoverage !== void 0) {
787
- const normalized = normalizeCoverage(decoded.mutantCoverage, input.sandboxDirectory, pathService);
788
- const validated = yield* S.decodeEffect(MutantCoverageShapeSchema)(normalized).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.map(() => normalized));
789
- coverages.push(validated);
790
- }
791
- }
792
- if (coverages.length === 0) return void 0;
793
- if (coverages.length === 1) return coverages[0];
794
- const first = coverages[0];
795
- return coverages.slice(1).reduce((acc, projectCoverage) => {
796
- for (const [testId, testCoverage] of Object.entries(projectCoverage.perTest)) if (testId in acc.perTest) mergeCoverage(acc.perTest[testId], testCoverage);
797
- else acc.perTest[testId] = testCoverage;
798
- mergeCoverage(acc.static, projectCoverage.static);
799
- return acc;
800
- }, first);
782
+ const files = Object.values(dedupeFilesByName(experimentalStateGetFiles(ctx)));
783
+ const coverages = (yield* Effect$1.forEach(files, coverageOfFile)).filter(Predicate.isNotNullish);
784
+ return Option.getOrUndefined(Option.map(Option.fromNullishOr(coverages[0]), (first) => coverages.slice(1).reduce(mergeProjectCoverage, first)));
801
785
  });
802
786
  const collectRaw = (filter) => Effect$1.gen(function* () {
803
787
  const ctx = yield* requireCtx;
@@ -808,31 +792,20 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
808
792
  cause: errorToString(cause)
809
793
  })));
810
794
  const vitestInRun = Reflect.get(options, "vitest");
811
- const relatedValue = Reflect.get(vitestInRun, "related");
812
- const related = (() => {
813
- if (relatedValue !== false && filter.relatedFiles !== void 0) return filter.relatedFiles.map(normalizeFileName);
814
- })();
815
- let testFilesToRun = (() => {
816
- if (filter.testFiles !== void 0) return [...filter.testFiles];
817
- })();
818
- let pattern;
819
- if ((filter.testIds ?? []).length > 0) {
820
- const parsedTests = (filter.testIds ?? []).map(fromTestId);
821
- pattern = new RegExp(parsedTests.map(({ test: name }) => RegExp.escape(name)).join("|"));
822
- testFilesToRun = parsedTests.map(({ file }) => pathService.resolve(input.sandboxDirectory, file));
823
- }
795
+ const related = relatedFilesOf(Reflect.get(vitestInRun, "related"), filter.relatedFiles);
796
+ const plan = runFilterPlan(filter, input.sandboxDirectory, pathService);
824
797
  applyRunFilterToConfig(ctx, {
825
798
  related,
826
- testNamePattern: pattern
799
+ testNamePattern: plan.testNamePattern
827
800
  });
828
801
  yield* Effect$1.tryPromise({
829
- try: () => ctx.start(testFilesToRun),
802
+ try: () => ctx.start(plan.testFiles),
830
803
  catch: (cause) => new TestRunnerFailed({
831
804
  runnerName: "vitest",
832
805
  phase: "dryRun",
833
806
  cause: errorToString(cause)
834
807
  })
835
- }).pipe(Effect$1.catchIf((error) => isErrorCodeError(error.cause) && typeof error.cause === "string" && error.cause.includes(VITEST_ERROR_CODES.FILES_NOT_FOUND), () => Effect$1.void));
808
+ }).pipe(Effect$1.catchIf((error) => isMissingTestFilesCause(error.cause), () => Effect$1.void));
836
809
  const rawTests = experimentalStateGetFiles(ctx).flatMap((file) => (() => {
837
810
  if (isRunnerTestSuite(file)) return collectTestsFromSuite(file);
838
811
  return [];
@@ -841,10 +814,7 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
841
814
  return {
842
815
  rawTests,
843
816
  hasExternalError,
844
- externalErrorText: (() => {
845
- if (hasExternalError) return experimentalStateGetExternalErrorText(ctx);
846
- return "";
847
- })()
817
+ externalErrorText: Match.value(hasExternalError).pipe(Match.when(true, () => experimentalStateGetExternalErrorText(ctx)), Match.orElse(() => ""))
848
818
  };
849
819
  });
850
820
  const harnessImpl = {
@@ -853,12 +823,7 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
853
823
  }),
854
824
  provide: (key, value) => Effect$1.gen(function* () {
855
825
  const ctx = yield* requireCtx;
856
- if (key === "hitLimit") {
857
- if (typeof value === "number" || value === void 0) ctx.provide("hitLimit", value);
858
- else ctx.provide("hitLimit", void 0);
859
- } else if (key === "mutantActivation") {
860
- if (value === "runtime" || value === "static") ctx.provide("mutantActivation", value);
861
- } else if (typeof value === "string") ctx.provide("activeMutant", value);
826
+ applyHarnessValue(ctx, key, value);
862
827
  })
863
828
  };
864
829
  const mutantRunCell = Cell.layer({
@@ -901,31 +866,15 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
901
866
  reportAllKillers
902
867
  };
903
868
  }),
904
- decode: (raw) => {
905
- const base = {
906
- rawTests: raw.rawTests,
907
- projectRoot: raw.projectRoot,
908
- hasExternalError: raw.hasExternalError,
909
- externalErrorText: raw.externalErrorText,
910
- reportAllKillers: raw.reportAllKillers
911
- };
912
- if (raw.hitCount !== void 0) {
913
- if (raw.hitLimit !== void 0) return Result.succeed(new VitestMutantRunCommand({
914
- ...base,
915
- hitCount: raw.hitCount,
916
- hitLimit: raw.hitLimit
917
- }));
918
- return Result.succeed(new VitestMutantRunCommand({
919
- ...base,
920
- hitCount: raw.hitCount
921
- }));
922
- }
923
- if (raw.hitLimit !== void 0) return Result.succeed(new VitestMutantRunCommand({
924
- ...base,
925
- hitLimit: raw.hitLimit
926
- }));
927
- return Result.succeed(new VitestMutantRunCommand(base));
928
- },
869
+ decode: (raw) => Result.succeed(new VitestMutantRunCommand({
870
+ rawTests: raw.rawTests,
871
+ projectRoot: raw.projectRoot,
872
+ hasExternalError: raw.hasExternalError,
873
+ externalErrorText: raw.externalErrorText,
874
+ hitCount: raw.hitCount,
875
+ hitLimit: raw.hitLimit,
876
+ reportAllKillers: raw.reportAllKillers
877
+ })),
929
878
  decide: interpretVitestRun,
930
879
  encode: (outcome) => Result.match(outcome, {
931
880
  onFailure: (e) => ({
@@ -933,15 +882,7 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
933
882
  errorMessage: e.message
934
883
  }),
935
884
  onSuccess: (out) => {
936
- const nrOfTests = () => {
937
- try {
938
- const raw = JSON.parse(out.testsJson);
939
- if (Array.isArray(raw)) return raw.filter(isIdRecord).length;
940
- return 0;
941
- } catch {
942
- return 0;
943
- }
944
- };
885
+ const nrOfTests = () => countIdRecords(parseJson(out.testsJson));
945
886
  return Match.value(out).pipe(Match.tag("Error", (error) => ({
946
887
  status: "error",
947
888
  errorMessage: error.errorMessage ?? "unknown"
@@ -967,20 +908,32 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
967
908
  }),
968
909
  write: (output, _raw) => Effect$1.succeed(output)
969
910
  });
911
+ const dryRunFilter = (options) => {
912
+ const relatedFiles = Option.getOrUndefined(Option.map(Option.fromNullishOr(options.files), (files) => [...files]));
913
+ return Match.value(testFilesProvided(options)).pipe(Match.when(true, () => ({
914
+ testFiles: Option.getOrElse(Option.map(Option.fromNullishOr(options.testFiles), (files) => [...files]), () => []),
915
+ relatedFiles
916
+ })), Match.orElse(() => ({ relatedFiles })));
917
+ };
918
+ const completeDryRun = (testsJson) => Effect$1.gen(function* () {
919
+ const tests = Match.value(parseJson(testsJson)).pipe(Match.when(Array.isArray, (entries) => entries.filter(isTestResultLike)), Match.orElse(() => []));
920
+ const mutantCoverage = yield* readMutantCoverage.pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
921
+ runnerName: "vitest",
922
+ phase: "dryRun",
923
+ cause: errorToString(cause)
924
+ })));
925
+ return Match.value(mutantCoverage).pipe(Match.when(Match.defined, (coverage) => ({
926
+ status: "complete",
927
+ tests,
928
+ mutantCoverage: coverage
929
+ })), Match.orElse(() => ({
930
+ status: "complete",
931
+ tests
932
+ })));
933
+ });
970
934
  const dryRun = (options) => Effect$1.gen(function* () {
971
935
  yield* (yield* VitestHarness).setMode("dry-run");
972
- const hasTestFiles = testFilesProvided(options);
973
- const filter = (() => {
974
- if (hasTestFiles) return {
975
- testFiles: [...options.testFiles ?? []],
976
- relatedFiles: (() => {
977
- if (options.files !== void 0) return [...options.files];
978
- })()
979
- };
980
- return { relatedFiles: (() => {
981
- if (options.files !== void 0) return [...options.files];
982
- })() };
983
- })();
936
+ const filter = dryRunFilter(options);
984
937
  const { rawTests, hasExternalError, externalErrorText } = yield* collectRaw(filter);
985
938
  const decision = decideVitestDryRun(new VitestDryRunCommand({
986
939
  rawTests,
@@ -988,31 +941,10 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
988
941
  hasExternalError,
989
942
  externalErrorText
990
943
  }));
991
- const result = Match.value(decision).pipe(Match.tag("Error", (error) => ({
944
+ return yield* Match.value(decision).pipe(Match.tag("Error", (error) => Effect$1.succeed({
992
945
  status: "error",
993
946
  errorMessage: error.errorMessage
994
- })), Match.tag("Complete", (complete) => {
995
- const raw = JSON.parse(complete.testsJson);
996
- let tests;
997
- if (Array.isArray(raw)) tests = raw.filter(isTestResultLike);
998
- else tests = [];
999
- return {
1000
- status: "complete",
1001
- tests
1002
- };
1003
- }), Match.exhaustive);
1004
- if (result.status === "complete") {
1005
- const mutantCoverage = yield* readMutantCoverage.pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
1006
- runnerName: "vitest",
1007
- phase: "dryRun",
1008
- cause: errorToString(cause)
1009
- })));
1010
- if (mutantCoverage !== void 0) return {
1011
- ...result,
1012
- mutantCoverage
1013
- };
1014
- }
1015
- return result;
947
+ })), Match.tag("Complete", (complete) => completeDryRun(complete.testsJson)), Match.exhaustive);
1016
948
  }).pipe(Effect$1.provideService(VitestHarness, harnessImpl), Effect$1.mapError((cause) => (() => {
1017
949
  if (cause instanceof TestRunnerFailed) return cause;
1018
950
  return new TestRunnerFailed({
@@ -1029,24 +961,31 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
1029
961
  cause: errorToString(cause)
1030
962
  });
1031
963
  })()));
964
+ const disposeContext = (ctx, localSetupFile) => Effect$1.gen(function* () {
965
+ Option.match(Option.fromNullishOr(localSetupFile), {
966
+ onNone: () => void 0,
967
+ onSome: (file) => {
968
+ ctx.onClose(() => Effect$1.runPromise(fsService.remove(file, {
969
+ recursive: true,
970
+ force: true
971
+ }).pipe(Effect$1.orElseSucceed(() => void 0))));
972
+ }
973
+ });
974
+ yield* Effect$1.tryPromise({
975
+ try: () => ctx.close(),
976
+ catch: (cause) => new TestRunnerFailed({
977
+ runnerName: "vitest",
978
+ phase: "dispose",
979
+ cause: errorToString(cause)
980
+ })
981
+ });
982
+ });
1032
983
  const dispose = Effect$1.gen(function* () {
1033
984
  const state = yield* getState;
1034
- if (state.ctx !== void 0) {
1035
- const localSetupFile = state.localSetupFile;
1036
- if (localSetupFile !== void 0) state.ctx.onClose(() => Effect$1.runPromise(fsService.remove(localSetupFile, {
1037
- recursive: true,
1038
- force: true
1039
- }).pipe(Effect$1.orElseSucceed(() => void 0))));
1040
- const currentCtx = state.ctx;
1041
- yield* Effect$1.tryPromise({
1042
- try: () => currentCtx.close(),
1043
- catch: (cause) => new TestRunnerFailed({
1044
- runnerName: "vitest",
1045
- phase: "dispose",
1046
- cause: errorToString(cause)
1047
- })
1048
- });
1049
- }
985
+ return yield* Option.match(Option.fromNullishOr(state.ctx), {
986
+ onNone: () => Effect$1.void,
987
+ onSome: (ctx) => disposeContext(ctx, state.localSetupFile)
988
+ });
1050
989
  });
1051
990
  return TestRunner.of({
1052
991
  capabilities,
@@ -1057,14 +996,21 @@ const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(f
1057
996
  });
1058
997
  }));
1059
998
  function isTestResultLike(value) {
1060
- return typeof value === "object" && value !== null && "id" in value && typeof Reflect.get(value, "id") === "string";
999
+ return Predicate.isObject(value) && typeof value["id"] === "string";
1061
1000
  }
1062
- function isIdRecord(value) {
1063
- return typeof value === "object" && value !== null && "id" in value && typeof Reflect.get(value, "id") === "string";
1001
+ function countIdRecords(raw) {
1002
+ return Match.value(raw).pipe(Match.when(Array.isArray, (entries) => entries.filter(isTestResultLike).length), Match.orElse(() => 0));
1064
1003
  }
1004
+ const mergeHitCount = (to, mutantId, hitCount) => Option.match(Option.fromNullishOr(to[mutantId]), {
1005
+ onNone: () => {
1006
+ to[mutantId] = hitCount;
1007
+ },
1008
+ onSome: (existing) => {
1009
+ to[mutantId] = existing + hitCount;
1010
+ }
1011
+ });
1065
1012
  function mergeCoverage(to, from) {
1066
- for (const [mutantId, hitCount] of Object.entries(from)) if (mutantId in to) to[mutantId] = to[mutantId] + hitCount;
1067
- else to[mutantId] = hitCount;
1013
+ for (const [mutantId, hitCount] of Object.entries(from)) mergeHitCount(to, mutantId, hitCount);
1068
1014
  }
1069
1015
  //#endregion
1070
1016
  //#region src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@systemfsoftware/stryker-js-vitest-runner",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
@@ -32,29 +32,29 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@systemfsoftware/effect-cell-types": "^6.0.1",
35
- "@systemfsoftware/stryker-js": "^2.0.0"
35
+ "@systemfsoftware/stryker-js": "^3.0.0"
36
36
  },
37
37
  "peerDependencies": {
38
- "effect": "^4.0.0-rc.112",
38
+ "effect": "4.0.0-rc.112",
39
39
  "vitest": ">=2.0.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@effect/platform-node": "^4.0.0-rc.112",
42
+ "@effect/platform-node": "4.0.0-rc.112",
43
43
  "@effect/vitest": "4.0.0-rc.112",
44
44
  "@systemfsoftware/arethetypeswrong-cli": "^1.1.1",
45
45
  "@types/node": "^24",
46
46
  "@vitest/browser-playwright": "^4",
47
- "effect": "^4.0.0-rc.112",
47
+ "effect": "4.0.0-rc.112",
48
48
  "oxlint": "^1.77.0",
49
49
  "rimraf": "^6.1.3",
50
50
  "tsdown": "^0.22.14",
51
51
  "typescript": "^7",
52
52
  "vitest": "^4",
53
- "@systemfsoftware/effect-gherkin-spec": "^4.0.1",
54
- "@systemfsoftware/oxlint-config": "^0.1.0",
53
+ "@systemfsoftware/all": "^2.0.0",
55
54
  "@systemfsoftware/tsconfig": "^1.3.3",
56
- "@systemfsoftware/vitest-config": "^0.1.0",
57
- "@systemfsoftware/all": "^1.1.3"
55
+ "@systemfsoftware/oxlint-config": "^0.1.0",
56
+ "@systemfsoftware/effect-gherkin-spec": "^4.0.1",
57
+ "@systemfsoftware/vitest-config": "^0.1.0"
58
58
  },
59
59
  "publishConfig": {
60
60
  "provenance": true