@systemfsoftware/stryker-js 1.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/README.md +1 -1
  3. package/dist/{Checker-DYgIIEai.d.mts → Checker-A8o5B3Ub.d.mts} +1 -1
  4. package/dist/Checker.d.mts +1 -1
  5. package/dist/{Evaluator-RZBS4oeh.d.mts → Evaluator-CnAN8-pZ.d.mts} +2 -2
  6. package/dist/Evaluator.d.mts +1 -1
  7. package/dist/ExitClass.d.mts +1 -1
  8. package/dist/ExitClass.mjs +20 -31
  9. package/dist/Metrics.d.mts +7 -0
  10. package/dist/Metrics.mjs +79 -0
  11. package/dist/Metrics.schema-BaVd6rXs.d.mts +50 -0
  12. package/dist/Metrics.schema-DZdoc46X.mjs +27 -0
  13. package/dist/{Mutant-BPRJTF8f.d.mts → Mutant-Ci25PsWA.d.mts} +6 -6
  14. package/dist/Mutant.d.mts +3 -2
  15. package/dist/Mutant.mjs +54 -78
  16. package/dist/{Mutant.schema-BTWhFTk4.mjs → Mutant.schema-H3r0ho_R.mjs} +3 -3
  17. package/dist/{Plugin-DcDAIBQi.d.mts → Plugin-CunIOPtw.d.mts} +31 -13
  18. package/dist/Plugin-DXFdJP1l.mjs +105 -0
  19. package/dist/Plugin.d.mts +2 -2
  20. package/dist/Plugin.mjs +2 -2
  21. package/dist/Report.d.mts +2 -0
  22. package/dist/Report.mjs +98 -0
  23. package/dist/Report.schema-_buHXdnZ.d.mts +251 -0
  24. package/dist/Reporter-BU7qo6MR.d.mts +16 -0
  25. package/dist/Reporter-BWTwlSJf.mjs +25 -0
  26. package/dist/Reporter.d.mts +4 -2
  27. package/dist/Reporter.mjs +3 -2
  28. package/dist/ReporterEvent.d.mts +2 -0
  29. package/dist/ReporterEvent.mjs +58 -0
  30. package/dist/ReporterEvent.schema-Db6r_euv.d.mts +190 -0
  31. package/dist/{Run-BnmGGgWj.d.mts → Run-CnhaYlAC.d.mts} +6 -12
  32. package/dist/Run.d.mts +2 -2
  33. package/dist/Run.mjs +56 -2
  34. package/dist/{Run-BcDo-hIC.mjs → Run.schema-C4m-b9Ki.mjs} +2 -62
  35. package/dist/TestRunner.d.mts +1 -1
  36. package/dist/TestRunner.mjs +57 -2
  37. package/dist/{TestRunner-DC9U8Unb.mjs → TestRunner.schema-Dy0Wdro2.mjs} +2 -64
  38. package/dist/index.d.mts +5 -5
  39. package/dist/index.mjs +4 -4
  40. package/dist/provided-options.d.mts +1 -1
  41. package/package.json +13 -10
  42. package/dist/Plugin-Ceh1G8Dz.mjs +0 -117
  43. package/dist/Reporter-COVWW1my.mjs +0 -33
  44. package/dist/Reporter-eZ3M7lO6.d.mts +0 -83
package/CHANGELOG.md CHANGED
@@ -1,5 +1,169 @@
1
1
  # @systemfsoftware/stryker-js
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - Reporter plugins are now pull-stream consumers: a reporter exports a factory
8
+ that returns an async consumer of the four run events, instead of registering
9
+ an Effect layer that provides the `Reporter` capability service.
10
+
11
+ - Removed: `ReporterService`, `broadcastReporter`, `NamedReporter`, the
12
+ `Reporter` service class, and the legacy event payload types.
13
+ - The event protocol ships as a Standard Schema object; validate and infer it
14
+ with `import { ReporterEventSchema, type ReporterFactory, type ReporterInit }
15
+ from '@systemfsoftware/stryker-js/ReporterEvent'`.
16
+ - Event payloads are narrowed: the dry-run event no longer carries the coverage
17
+ map; the plan event carries reduced mutant descriptors instead of full run
18
+ options; `mutantTested` drops `killedBy`, `coveredBy`, `static`,
19
+ `testsCompleted` and `statusReason`, and renames `mutatorName` to `mutator`.
20
+ - `ReporterFailed.event` now names the event kind being processed instead of a
21
+ lifecycle method name; `'wrapUp'` no longer occurs.
22
+ - An unknown reporter name now fails the run at startup instead of being
23
+ silently ignored. A reporter that fails on the terminal report now fails the
24
+ run; a reporter that fails earlier is logged and detached with the exit code
25
+ unchanged.
26
+ - `@systemfsoftware/stryker-js-engine/builtin-reporters` no longer exports
27
+ `strykerPlugins`; call `makeBuiltinReporterFactories({ fileSystem, path })`.
28
+ - The built-in json and clear-text reporters write their notices directly to
29
+ stdout and stderr instead of the Effect logger, and the html reporter no
30
+ longer depends on `@effect/platform-node`.
31
+
32
+ - Remove the --llms command manifest: the CLI no longer accepts --llms and the Run stream no longer carries a manifest terminal event.
33
+
34
+ ## 2.0.0
35
+
36
+ ### Major Changes
37
+
38
+ - The packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the
39
+ language every plugin is written against. `mutation-run` is split: the run
40
+ itself is `@systemfsoftware/stryker-js-engine`
41
+ (host-neutral, no Node on its manifest) and the Node process entries are
42
+ `@systemfsoftware/stryker-js-cli`, which owns the worker files and the runtime
43
+ gate. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`.
44
+ `@systemfsoftware/stryker-js-platform-node` is never published — do not install
45
+ it. Install the new names and change your imports.
46
+
47
+ Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are
48
+ imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and
49
+ `Location` from the `Mutant` export. Point a config's `extends` at the language
50
+ package's `Schema` export.
51
+
52
+ `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`,
53
+ `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`.
54
+ The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the
55
+ rest — are gone. A comparison against a removed spelling never matched the value
56
+ the reporter actually produced, so check any status comparison you wrote.
57
+
58
+ Statuses, plugin kinds, exit classes and AST formats are string literal unions
59
+ rather than enums, so read them as their string values. Member access such as
60
+ `ExitClass.VerdictFail` no longer resolves.
61
+
62
+ A plugin no longer receives a logger, and the logger port is gone. Plugins log
63
+ through Effect, and the host decides where that output goes.
64
+
65
+ The bundled base preset is gone. A config inherits from the language package's
66
+ `Schema` export and states the thresholds, reporters and plugins it wants; you no
67
+ longer silently inherit a package manager, a plugin list or a break threshold.
68
+
69
+ - An evaluator now answers with the verdict it reached instead of failing to
70
+ report one.
71
+
72
+ `Evaluator.evaluate` returns `ExitClass | null` on the success channel: the
73
+ class the run should end in, or `null` for nothing to report. `ExitClass` is
74
+ exported from `@systemfsoftware/stryker-js/evaluate`. The error
75
+ channel is for the evaluator itself breaking — a report it cannot read, a
76
+ decision it cannot reach.
77
+
78
+ Previously the only way to report a failed gate was to fail, which a caller
79
+ could not tell apart from the evaluator crashing, so neither the exit code nor
80
+ the message could distinguish them. If you wrote an evaluator that failed to
81
+ signal a verdict, return the class instead.
82
+
83
+ A run's verdict is now the most severe class anyone reported — the score against
84
+ your `break` threshold, plus every evaluator's answer.
85
+
86
+ - Plugins are written against Effect instead of Promise.
87
+
88
+ `Checker`, `TestRunner`, `Reporter`, `Ignorer` and `Evaluator` are capability services whose operations return an `Effect`. Provide your plugin as a `Layer` through `declarePlugin`; the class, factory and value declarations are gone, along with the `typed-inject` dependency. Implement the operations that were optional — return `Effect.void` where there is nothing to do, and one group per mutant from `group` if you have no grouping opinion.
89
+
90
+ Each capability fails with a tagged error. Outcomes are not failures: a killed mutant, a survivor and a compile error in the code under test are successful results.
91
+
92
+ Read the sandbox path from `SandboxDirectory`. `commonTokens`, `tokens` and the plugin context types are removed. `determineHitLimitReached` returns an `Option`.
93
+
94
+ ### Minor Changes
95
+
96
+ - `RENDERED_OPTION_DEFAULTS` is exported from the `core` entry point. It carries
97
+ the four option defaults that appear in human-readable help text, so a tool
98
+ printing "the default is X" reads the same value the option schema applies
99
+ instead of restating it.
100
+
101
+ - The shared helpers package is gone. Nothing installs it any more, and the
102
+ handful of helpers worth sharing now live in the plugin contract next to the
103
+ types they serve:
104
+
105
+ - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString`
106
+ and `isErrnoException` from `@systemfsoftware/stryker-js/core`
107
+ - `noopLogger` from `@systemfsoftware/stryker-js/logging`
108
+ - `testFilesProvided` from `@systemfsoftware/stryker-js/test-runner`
109
+
110
+ If you imported any of those, change the specifier. Everything else it exported
111
+ had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place
112
+ of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`.
113
+
114
+ - Checker and test-runner workers now talk over Effect's own worker RPC, and every
115
+ call they exchange is a declared operation with a declared result.
116
+
117
+ Before, the parent and its workers spoke a protocol written by hand: messages were
118
+ newline-delimited JSON, arguments were typed as "any JSON value", and each method
119
+ was reached by name through a proxy. Nothing checked that a payload was one the far
120
+ end could serve, so a value it could not read was refused after it arrived, and a
121
+ call whose message never landed was waited on anyway.
122
+
123
+ The six operations that cross that boundary — a checker's `check` and `group`, a
124
+ runner's `capabilities`, `dryRun` and `mutantRun` — now each name what they take
125
+ and what they return, and the options a worker starts from are sent once when it
126
+ starts rather than as a first method call. A payload that does not fit is refused
127
+ where it is built, and a worker that cannot answer fails the call that was waiting.
128
+
129
+ The two worker entry points are no longer importable subpaths of this package.
130
+ They were only ever spawned as processes, and the paths resolved to TypeScript
131
+ sources that could not be executed.
132
+
133
+ ### Patch Changes
134
+
135
+ - A mutation run that is killed mid-way keeps every completed mutant: the JSONL progress stream is flushed after each result, and incremental mode writes remembered verdicts as they finish so the next run continues instead of starting over.
136
+
137
+ Remembered killed mutants still name the tests that killed them, so a resumed run's report matches a complete run.
138
+
139
+ The progress stream path is progressStreamFile (default reports/mutation-stream.jsonl) and can be set in config or with --progressStreamFile.
140
+
141
+ - New version is published through npm trusted publishing, so it carries a provenance attestation you can verify.
142
+
143
+ - Peer Effect requirement advances to 4.0.0-rc.112. No API changes.
144
+
145
+ - An oversized message between the runner and a worker now fails the run with a
146
+ reason instead of exhausting memory. Each side of the connection reads frames
147
+ up to 16 MiB, which leaves headroom over the largest legitimate payload — a dry
148
+ run carrying per-test coverage — and a frame past the limit fails the calls
149
+ waiting on it rather than growing until the process dies.
150
+
151
+ - Each of these packages now has a README, so its registry page says what the package is, how
152
+ to install it, and what to import or register — previously the page was blank. The lint
153
+ plugins show the configuration line that enables what they recommend.
154
+
155
+ `@systemfsoftware/stryker-js-html-reporter` also carries its licence text
156
+
157
+ - These packages no longer install dependencies they never imported, so installing them pulls less into your tree.
158
+
159
+ `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes.
160
+
161
+ - Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way.
162
+
163
+ - The closing verdict line carries its findings again. It had shrunk to the score alone, so a consumer reading the stream could no longer see the score limits the run was held to, where the report was written, or which mutants survived — the survivors were reported while the run was in flight and then absent from the summary that closes it.
164
+
165
+ The verdict now states the thresholds, the report path, and every surviving mutant with its file, position, mutator and replacement.
166
+
3
167
  ## 1.0.0
4
168
 
5
169
  ### Major Changes
package/README.md CHANGED
@@ -29,4 +29,4 @@ Each concept module is an enumerated specifier:
29
29
 
30
30
  ## License
31
31
 
32
- Apache-2.0. Part of [systemfsoftware](https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/testing/mutation/stryker-js/stryker-js#readme).
32
+ Apache-2.0. Part of [systemfsoftware](https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/stryker-js/stryker-js#readme).
@@ -1,4 +1,4 @@
1
- import { _t as Mutant } from "./Mutant-BPRJTF8f.mjs";
1
+ import { gt as Mutant } from "./Mutant-Ci25PsWA.mjs";
2
2
  import * as Context from "effect/Context";
3
3
  import * as S from "effect/Schema";
4
4
  import * as Effect from "effect/Effect";
@@ -1,2 +1,2 @@
1
- import { a as FailedCheckResult, c as CheckStatus, l as CheckerFailed, n as Checker, o as PassedCheckResult, r as CheckerService, s as CheckResultSchema, t as CheckResult } from "./Checker-DYgIIEai.mjs";
1
+ import { a as FailedCheckResult, c as CheckStatus, l as CheckerFailed, n as Checker, o as PassedCheckResult, r as CheckerService, s as CheckResultSchema, t as CheckResult } from "./Checker-A8o5B3Ub.mjs";
2
2
  export { CheckResult, CheckResultSchema, CheckStatus, Checker, CheckerFailed, CheckerService, FailedCheckResult, PassedCheckResult };
@@ -1,7 +1,7 @@
1
+ import { _ as MutationTestResult } from "./Report.schema-_buHXdnZ.mjs";
1
2
  import { r as ExitClass } from "./ExitClass.schema-BmOIK5zT.mjs";
2
3
  import * as Context from "effect/Context";
3
4
  import * as S from "effect/Schema";
4
- import * as schema from "mutation-testing-report-schema/api";
5
5
  import * as Effect from "effect/Effect";
6
6
  //#region src/Evaluator.schema.d.ts
7
7
  declare const EvaluatorFailed_base: S.Class<EvaluatorFailed, S.TaggedStruct<"EvaluatorFailed", {
@@ -11,7 +11,7 @@ declare class EvaluatorFailed extends EvaluatorFailed_base {}
11
11
  //#endregion
12
12
  //#region src/Evaluator.d.ts
13
13
  interface EvaluatorService {
14
- readonly evaluate: (report: schema.MutationTestResult) => Effect.Effect<ExitClass | null, EvaluatorFailed>;
14
+ readonly evaluate: (report: MutationTestResult) => Effect.Effect<ExitClass | null, EvaluatorFailed>;
15
15
  }
16
16
  declare const Evaluator_base: Context.ServiceClass<Evaluator, "~@systemfsoftware/stryker-js/Evaluator", EvaluatorService>;
17
17
  declare class Evaluator extends Evaluator_base {}
@@ -1,3 +1,3 @@
1
- import { n as EvaluatorService, r as EvaluatorFailed, t as Evaluator } from "./Evaluator-RZBS4oeh.mjs";
1
+ import { n as EvaluatorService, r as EvaluatorFailed, t as Evaluator } from "./Evaluator-CnAN8-pZ.mjs";
2
2
  import { r as ExitClass } from "./ExitClass.schema-BmOIK5zT.mjs";
3
3
  export { Evaluator, EvaluatorFailed, EvaluatorService, type ExitClass };
@@ -1,9 +1,9 @@
1
1
  import { n as ClassifyExitDecision, r as ExitClass, t as ClassifyExitCommand } from "./ExitClass.schema-BmOIK5zT.mjs";
2
2
  //#region src/ExitClass.d.ts
3
3
  declare const EXIT_CODE: Record<ExitClass, number>;
4
- declare function verdictExitClass(score: number | null, breakingThreshold: number | null): ExitClass | null;
5
4
  declare function highestExitClass(pending: Iterable<ExitClass>): ExitClass | null;
6
5
  declare function resolveExitCode(pending: Iterable<ExitClass>, signal: number | null): number;
6
+ declare function verdictExitClass(score: number | null, breakingThreshold: number | null): ExitClass | null;
7
7
  declare const classifyExit: (command: ClassifyExitCommand) => ClassifyExitDecision;
8
8
  //#endregion
9
9
  export { EXIT_CODE, ExitClass, classifyExit, highestExitClass, resolveExitCode, verdictExitClass };
@@ -1,5 +1,6 @@
1
1
  import * as S from "effect/Schema";
2
2
  import * as Match from "effect/Match";
3
+ import * as Option from "effect/Option";
3
4
  //#region src/ExitClass.schema.ts
4
5
  const ExitClass = S.Literals([
5
6
  "VerdictFail",
@@ -25,42 +26,30 @@ const EXIT_CODE = {
25
26
  RuntimeError: 3,
26
27
  InternalError: 4
27
28
  };
28
- function verdictExitClass(score, breakingThreshold) {
29
- if (breakingThreshold === null || score === null) return null;
30
- if (score < breakingThreshold) return "VerdictFail";
31
- return null;
32
- }
33
29
  function highestExitClass(pending) {
34
- let highest = null;
35
- for (const exitClass of pending) if (highest === null || EXIT_CODE[exitClass] > EXIT_CODE[highest]) highest = exitClass;
36
- return highest;
30
+ return Array.from(pending).reduce((incumbent, challenger) => Option.match(Option.fromNullishOr(incumbent), {
31
+ onNone: () => challenger,
32
+ onSome: (value) => Match.value(EXIT_CODE[value] < EXIT_CODE[challenger]).pipe(Match.when(true, () => challenger), Match.when(false, () => value), Match.exhaustive)
33
+ }), null);
37
34
  }
38
35
  function resolveExitCode(pending, signal) {
39
- if (signal !== null) return 128 + signal;
40
- const highest = highestExitClass(pending);
41
- if (highest === null) return 0;
42
- return EXIT_CODE[highest];
36
+ return Option.match(Option.fromNullishOr(signal), {
37
+ onNone: () => Option.match(Option.fromNullishOr(highestExitClass(pending)), {
38
+ onNone: () => 0,
39
+ onSome: (exitClass) => EXIT_CODE[exitClass]
40
+ }),
41
+ onSome: (value) => 128 + value
42
+ });
43
+ }
44
+ function verdictExitClass(score, breakingThreshold) {
45
+ return Option.getOrNull(Option.zipWith(Option.fromNullishOr(score), Option.fromNullishOr(breakingThreshold), (value, threshold) => Match.value(value < threshold).pipe(Match.when(true, () => "VerdictFail"), Match.when(false, () => null), Match.exhaustive)));
43
46
  }
44
- const deriveVerdict = (score, breakingThreshold) => {
45
- if (score === null || breakingThreshold === null) return null;
46
- if (score < breakingThreshold) return "VerdictFail";
47
- return null;
48
- };
49
- const rank = (value) => Match.value(value).pipe(Match.when("InternalError", () => 4), Match.when("RuntimeError", () => 3), Match.when("ConfigError", () => 2), Match.when("VerdictFail", () => 1), Match.exhaustive);
50
- const withVerdict = (pending, verdict) => {
51
- if (verdict === null) return pending;
52
- if (pending.includes(verdict)) return pending;
53
- return [...pending, verdict];
54
- };
55
- const highestOf = (values) => values.reduce((acc, cur) => {
56
- if (acc === null) return cur;
57
- if (rank(cur) > rank(acc)) return cur;
58
- return acc;
59
- }, null);
60
47
  const classifyExit = (command) => {
61
- const verdictClass = deriveVerdict(command.score, command.breakingThreshold);
62
- const pendingWithVerdict = withVerdict(command.pending, verdictClass);
63
- const highestClass = highestOf(pendingWithVerdict);
48
+ const verdictClass = verdictExitClass(command.score, command.breakingThreshold);
49
+ const highestClass = highestExitClass(Option.match(Option.fromNullishOr(verdictClass), {
50
+ onNone: () => command.pending,
51
+ onSome: (verdict) => Array.from(/* @__PURE__ */ new Set([...command.pending, verdict]))
52
+ }));
64
53
  return ClassifyExitDecision.make({
65
54
  highestClass,
66
55
  verdictClass
@@ -0,0 +1,7 @@
1
+ import { a as FileResult, p as MutantResult } from "./Report.schema-_buHXdnZ.mjs";
2
+ import { i as MetricsSchema, n as MetricsResult, r as MetricsResultSchema, t as Metrics } from "./Metrics.schema-BaVd6rXs.mjs";
3
+ //#region src/Metrics.d.ts
4
+ declare const countMutants: (mutants: readonly MutantResult[]) => Metrics;
5
+ declare const calculateMetrics: (files: Readonly<Record<string, FileResult>>) => MetricsResult;
6
+ //#endregion
7
+ export { type Metrics, type MetricsResult, MetricsResultSchema, MetricsSchema, calculateMetrics, countMutants };
@@ -0,0 +1,79 @@
1
+ import { n as MetricsSchema, t as MetricsResultSchema } from "./Metrics.schema-DZdoc46X.mjs";
2
+ import * as Match from "effect/Match";
3
+ import * as Option from "effect/Option";
4
+ import * as Arr from "effect/Array";
5
+ //#region src/Metrics.ts
6
+ const countStatus = (mutants, status) => mutants.filter((mutant) => mutant.status === status).length;
7
+ const percentage = (numerator, denominator) => numerator / denominator * 100;
8
+ /** A percentage whose denominator is zero is not 0%; `emptyDenominator` says what it is instead. */
9
+ const percentOr = (emptyDenominator, numerator, denominator) => Match.value(denominator).pipe(Match.when(0, () => emptyDenominator), Match.orElse(() => percentage(numerator, denominator)));
10
+ const coveredFraction = (totalDetected, totalCovered, totalValid) => Match.value(totalValid).pipe(Match.when(0, () => NaN), Match.orElse(() => percentOr(0, totalDetected, totalCovered)));
11
+ const countMutants = (mutants) => {
12
+ const pending = countStatus(mutants, "Pending");
13
+ const killed = countStatus(mutants, "Killed");
14
+ const timeout = countStatus(mutants, "Timeout");
15
+ const survived = countStatus(mutants, "Survived");
16
+ const noCoverage = countStatus(mutants, "NoCoverage");
17
+ const runtimeErrors = countStatus(mutants, "RuntimeError");
18
+ const compileErrors = countStatus(mutants, "CompileError");
19
+ const ignored = countStatus(mutants, "Ignored");
20
+ const totalDetected = timeout + killed;
21
+ const totalUndetected = survived + noCoverage;
22
+ const totalCovered = totalDetected + survived;
23
+ const totalValid = totalUndetected + totalDetected;
24
+ const totalInvalid = runtimeErrors + compileErrors;
25
+ return {
26
+ pending,
27
+ killed,
28
+ timeout,
29
+ survived,
30
+ noCoverage,
31
+ runtimeErrors,
32
+ compileErrors,
33
+ ignored,
34
+ totalDetected,
35
+ totalUndetected,
36
+ totalCovered,
37
+ totalValid,
38
+ totalInvalid,
39
+ totalMutants: totalValid + totalInvalid + ignored + pending,
40
+ mutationScore: percentOr(NaN, totalDetected, totalValid),
41
+ mutationScoreBasedOnCoveredCode: coveredFraction(totalDetected, totalCovered, totalValid)
42
+ };
43
+ };
44
+ const segmentOf = (fileName) => {
45
+ const separator = fileName.indexOf("/");
46
+ if (separator === -1) return fileName;
47
+ return fileName.slice(0, separator);
48
+ };
49
+ const groupBySegment = (files) => Object.entries(files).reduce((groups, [fileName, file]) => {
50
+ const segment = segmentOf(fileName);
51
+ const group = groups[segment] ?? {};
52
+ group[fileName] = file;
53
+ groups[segment] = group;
54
+ return groups;
55
+ }, {});
56
+ const metricsOf = (files) => countMutants(Object.values(files).flatMap((file) => file.mutants));
57
+ const fileResultOf = (fileName, file) => ({
58
+ name: fileName,
59
+ metrics: countMutants(file.mutants),
60
+ childResults: []
61
+ });
62
+ const nestedGroupResult = (segment, entries) => {
63
+ const nested = Object.fromEntries(entries.map(([fileName, file]) => [fileName.slice(segment.length + 1), file]));
64
+ return {
65
+ name: segment,
66
+ metrics: metricsOf(nested),
67
+ childResults: childResultsOf(nested)
68
+ };
69
+ };
70
+ const soleSegmentFileResult = (segment, entries) => Option.flatMap(Arr.head(entries), ([fileName, file]) => Match.value(fileName === segment && entries.length === 1).pipe(Match.when(true, () => Option.some(fileResultOf(fileName, file))), Match.orElse(() => Option.none())));
71
+ const childResultOf = (segment, entries) => Option.getOrElse(soleSegmentFileResult(segment, entries), () => nestedGroupResult(segment, entries));
72
+ const childResultsOf = (files) => Object.entries(groupBySegment(files)).map(([segment, grouped]) => childResultOf(segment, Object.entries(grouped))).sort((left, right) => left.name.localeCompare(right.name));
73
+ const calculateMetrics = (files) => ({
74
+ name: "All files",
75
+ metrics: metricsOf(files),
76
+ childResults: childResultsOf(files)
77
+ });
78
+ //#endregion
79
+ export { MetricsResultSchema, MetricsSchema, calculateMetrics, countMutants };
@@ -0,0 +1,50 @@
1
+ import * as S from "effect/Schema";
2
+ //#region src/Metrics.schema.d.ts
3
+ declare const MetricsSchema: S.Struct<{
4
+ readonly pending: S.Finite;
5
+ readonly killed: S.Finite;
6
+ readonly timeout: S.Finite;
7
+ readonly survived: S.Finite;
8
+ readonly noCoverage: S.Finite;
9
+ readonly runtimeErrors: S.Finite;
10
+ readonly compileErrors: S.Finite;
11
+ readonly ignored: S.Finite;
12
+ readonly totalDetected: S.Finite;
13
+ readonly totalUndetected: S.Finite;
14
+ readonly totalInvalid: S.Finite;
15
+ readonly totalValid: S.Finite;
16
+ readonly totalMutants: S.Finite;
17
+ readonly totalCovered: S.Finite;
18
+ readonly mutationScore: S.Finite;
19
+ readonly mutationScoreBasedOnCoveredCode: S.Finite;
20
+ }>;
21
+ type Metrics = typeof MetricsSchema.Type;
22
+ interface MetricsResult {
23
+ readonly name: string;
24
+ readonly metrics: Metrics;
25
+ readonly childResults: readonly MetricsResult[];
26
+ }
27
+ declare const MetricsResultSchema: S.Struct<{
28
+ readonly name: S.String;
29
+ readonly metrics: S.Struct<{
30
+ readonly pending: S.Finite;
31
+ readonly killed: S.Finite;
32
+ readonly timeout: S.Finite;
33
+ readonly survived: S.Finite;
34
+ readonly noCoverage: S.Finite;
35
+ readonly runtimeErrors: S.Finite;
36
+ readonly compileErrors: S.Finite;
37
+ readonly ignored: S.Finite;
38
+ readonly totalDetected: S.Finite;
39
+ readonly totalUndetected: S.Finite;
40
+ readonly totalInvalid: S.Finite;
41
+ readonly totalValid: S.Finite;
42
+ readonly totalMutants: S.Finite;
43
+ readonly totalCovered: S.Finite;
44
+ readonly mutationScore: S.Finite;
45
+ readonly mutationScoreBasedOnCoveredCode: S.Finite;
46
+ }>;
47
+ readonly childResults: S.$Array<S.suspend<S.Codec<MetricsResult, MetricsResult, never, never>>>;
48
+ }>;
49
+ //#endregion
50
+ export { MetricsSchema as i, MetricsResult as n, MetricsResultSchema as r, Metrics as t };
@@ -0,0 +1,27 @@
1
+ import * as S from "effect/Schema";
2
+ //#region src/Metrics.schema.ts
3
+ const MetricsSchema = S.Struct({
4
+ pending: S.Finite,
5
+ killed: S.Finite,
6
+ timeout: S.Finite,
7
+ survived: S.Finite,
8
+ noCoverage: S.Finite,
9
+ runtimeErrors: S.Finite,
10
+ compileErrors: S.Finite,
11
+ ignored: S.Finite,
12
+ totalDetected: S.Finite,
13
+ totalUndetected: S.Finite,
14
+ totalInvalid: S.Finite,
15
+ totalValid: S.Finite,
16
+ totalMutants: S.Finite,
17
+ totalCovered: S.Finite,
18
+ mutationScore: S.Finite,
19
+ mutationScoreBasedOnCoveredCode: S.Finite
20
+ });
21
+ const MetricsResultSchema = S.Struct({
22
+ name: S.String,
23
+ metrics: MetricsSchema,
24
+ childResults: S.Array(S.suspend(() => MetricsResultSchema))
25
+ }).annotate({ identifier: "MetricsResult" });
26
+ //#endregion
27
+ export { MetricsSchema as n, MetricsResultSchema as t };
@@ -1,6 +1,6 @@
1
+ import { C as Report_schema_d_exports } from "./Report.schema-_buHXdnZ.mjs";
1
2
  import * as Context from "effect/Context";
2
3
  import * as S from "effect/Schema";
3
- import * as schema from "mutation-testing-report-schema/api";
4
4
  import * as Effect from "effect/Effect";
5
5
  //#region src/Mutant.schema.d.ts
6
6
  declare const PositionSchema: S.Struct<{
@@ -18,9 +18,9 @@ declare const LocationSchema: S.Struct<{
18
18
  }>;
19
19
  }>;
20
20
  declare const Mutant_base: S.Class<Mutant, S.TaggedStruct<"Mutant", {
21
- readonly id: S.String;
22
- readonly fileName: S.String;
23
- readonly mutatorName: S.String;
21
+ readonly id: S.NonEmptyString;
22
+ readonly fileName: S.NonEmptyString;
23
+ readonly mutatorName: S.NonEmptyString;
24
24
  readonly replacement: S.String;
25
25
  readonly location: S.Struct<{
26
26
  readonly start: S.Struct<{
@@ -272,7 +272,7 @@ declare function testFilesProvided(options: {
272
272
  readonly testFiles?: readonly string[];
273
273
  }): boolean;
274
274
  declare namespace Mutant_d_exports {
275
- export { Coverage, CoverageData, CoveragePerTestId, ERROR_CODES, EarlyResultPlan, ErrnoException, FileDescription, FileDescriptions, INSTRUMENTER_CONSTANTS, InstrumenterContext, Location, LocationSchema, Mutant, MutantCoverage, MutantEarlyResultPlan, MutantResult, MutantRunPlan, MutantStatus, MutantTestCoverage, MutantTestPlan, MutateDescription, MutationRange, Position$1 as Position, PositionSchema, RunPlan, TestPlan, causeText, errorToString, isErrnoException, isMutant, normalizeFileName, schema };
275
+ export { Coverage, CoverageData, CoveragePerTestId, ERROR_CODES, EarlyResultPlan, ErrnoException, FileDescription, FileDescriptions, INSTRUMENTER_CONSTANTS, InstrumenterContext, Location, LocationSchema, Mutant, MutantCoverage, MutantEarlyResultPlan, MutantResult, MutantRunPlan, MutantStatus, MutantTestCoverage, MutantTestPlan, MutateDescription, MutationRange, Position$1 as Position, PositionSchema, RunPlan, TestPlan, causeText, errorToString, isErrnoException, isMutant, normalizeFileName, Report_schema_d_exports as schema };
276
276
  }
277
277
  type CoverageData = Record<string, number>;
278
278
  type CoveragePerTestId = Record<string, CoverageData>;
@@ -348,4 +348,4 @@ type MutantEarlyResultPlan = EarlyResultPlan;
348
348
  type MutantTestPlan = TestPlan;
349
349
  declare const causeText: (cause: unknown, depth: number) => string | undefined;
350
350
  //#endregion
351
- export { TimeoutMutantRunResult as $, CompleteDryRunResult as A, MutantRunOptions as B, causeText as C, normalizeFileName as D, isMutant as E, ErrorMutantRunResult as F, SuccessTestResult as G, Position as H, FailedTestResult as I, TestRunner as J, SurvivedMutantRunResult as K, KilledMutantRunResult as L, DryRunOptions as M, DryRunResult as N, schema as O, ErrorDryRunResult as P, TimeoutDryRunResult as Q, MutantActivation as R, TestPlan as S, isErrnoException as T, RunOptions as U, MutantRunResult as V, SkippedTestResult as W, TestRunnerService as X, TestRunnerCapabilities as Y, TestRunner_d_exports as Z, MutantTestPlan as _, Mutant as _t, EarlyResultPlan as a, DryRunStatus as at, MutationRange as b, FileDescriptions as c, MutantRunOptionsSchema as ct, MutantCoverage as d, TestResultSchema as dt, testFilesProvided as et, MutantEarlyResultPlan as f, TestRunnerCapabilitiesSchema as ft, MutantTestCoverage as g, LocationSchema as gt, MutantStatus as h, Location as ht, ERROR_CODES as i, DryRunResultSchema as it, CoverageAnalysis as j, BaseTestResult as k, INSTRUMENTER_CONSTANTS as l, MutantRunResultSchema as lt, MutantRunPlan as m, TestStatus as mt, CoverageData as n, CoverageAnalysisSchema as nt, ErrnoException as o, MutantActivationSchema as ot, MutantResult as p, TestRunnerFailed as pt, TestResult as q, CoveragePerTestId as r, DryRunOptionsSchema as rt, FileDescription as s, MutantCoverageSchema as st, Coverage as t, toMutantRunResult as tt, InstrumenterContext as u, MutantRunStatus as ut, Mutant_d_exports as v, Position$1 as vt, errorToString as w, RunPlan as x, MutateDescription as y, PositionSchema as yt, MutantCoverage$1 as z };
351
+ export { testFilesProvided as $, CoverageAnalysis as A, MutantRunResult as B, causeText as C, normalizeFileName as D, isMutant as E, FailedTestResult as F, SurvivedMutantRunResult as G, RunOptions as H, KilledMutantRunResult as I, TestRunnerCapabilities as J, TestResult as K, MutantActivation as L, DryRunResult as M, ErrorDryRunResult as N, BaseTestResult as O, ErrorMutantRunResult as P, TimeoutMutantRunResult as Q, MutantCoverage$1 as R, TestPlan as S, isErrnoException as T, SkippedTestResult as U, Position as V, SuccessTestResult as W, TestRunner_d_exports as X, TestRunnerService as Y, TimeoutDryRunResult as Z, MutantTestPlan as _, Position$1 as _t, EarlyResultPlan as a, MutantActivationSchema as at, MutationRange as b, FileDescriptions as c, MutantRunResultSchema as ct, MutantCoverage as d, TestRunnerCapabilitiesSchema as dt, toMutantRunResult as et, MutantEarlyResultPlan as f, TestRunnerFailed as ft, MutantTestCoverage as g, Mutant as gt, MutantStatus as h, LocationSchema as ht, ERROR_CODES as i, DryRunStatus as it, DryRunOptions as j, CompleteDryRunResult as k, INSTRUMENTER_CONSTANTS as l, MutantRunStatus as lt, MutantRunPlan as m, Location as mt, CoverageData as n, DryRunOptionsSchema as nt, ErrnoException as o, MutantCoverageSchema as ot, MutantResult as p, TestStatus as pt, TestRunner as q, CoveragePerTestId as r, DryRunResultSchema as rt, FileDescription as s, MutantRunOptionsSchema as st, Coverage as t, CoverageAnalysisSchema as tt, InstrumenterContext as u, TestResultSchema as ut, Mutant_d_exports as v, PositionSchema as vt, errorToString as w, RunPlan as x, MutateDescription as y, MutantRunOptions as z };
package/dist/Mutant.d.mts CHANGED
@@ -1,2 +1,3 @@
1
- import { C as causeText, D as normalizeFileName, E as isMutant, O as schema, S as TestPlan, T as isErrnoException, _ as MutantTestPlan, _t as Mutant, a as EarlyResultPlan, b as MutationRange, c as FileDescriptions, d as MutantCoverage, f as MutantEarlyResultPlan, g as MutantTestCoverage, gt as LocationSchema, h as MutantStatus, ht as Location, i as ERROR_CODES, l as INSTRUMENTER_CONSTANTS, m as MutantRunPlan, n as CoverageData, o as ErrnoException, p as MutantResult, r as CoveragePerTestId, s as FileDescription, t as Coverage, u as InstrumenterContext, vt as Position, w as errorToString, x as RunPlan, y as MutateDescription, yt as PositionSchema } from "./Mutant-BPRJTF8f.mjs";
2
- export { Coverage, CoverageData, CoveragePerTestId, ERROR_CODES, EarlyResultPlan, ErrnoException, FileDescription, FileDescriptions, INSTRUMENTER_CONSTANTS, InstrumenterContext, type Location, LocationSchema, Mutant, MutantCoverage, MutantEarlyResultPlan, MutantResult, MutantRunPlan, MutantStatus, MutantTestCoverage, MutantTestPlan, MutateDescription, MutationRange, type Position, PositionSchema, RunPlan, TestPlan, causeText, errorToString, isErrnoException, isMutant, normalizeFileName, schema };
1
+ import { C as causeText, D as normalizeFileName, E as isMutant, S as TestPlan, T as isErrnoException, _ as MutantTestPlan, _t as Position, a as EarlyResultPlan, b as MutationRange, c as FileDescriptions, d as MutantCoverage, f as MutantEarlyResultPlan, g as MutantTestCoverage, gt as Mutant, h as MutantStatus, ht as LocationSchema, i as ERROR_CODES, l as INSTRUMENTER_CONSTANTS, m as MutantRunPlan, mt as Location, n as CoverageData, o as ErrnoException, p as MutantResult, r as CoveragePerTestId, s as FileDescription, t as Coverage, u as InstrumenterContext, vt as PositionSchema, w as errorToString, x as RunPlan, y as MutateDescription } from "./Mutant-Ci25PsWA.mjs";
2
+ import { C as Report_schema_d_exports } from "./Report.schema-_buHXdnZ.mjs";
3
+ export { Coverage, CoverageData, CoveragePerTestId, ERROR_CODES, EarlyResultPlan, ErrnoException, FileDescription, FileDescriptions, INSTRUMENTER_CONSTANTS, InstrumenterContext, type Location, LocationSchema, Mutant, MutantCoverage, MutantEarlyResultPlan, MutantResult, MutantRunPlan, MutantStatus, MutantTestCoverage, MutantTestPlan, MutateDescription, MutationRange, type Position, PositionSchema, RunPlan, TestPlan, causeText, errorToString, isErrnoException, isMutant, normalizeFileName, Report_schema_d_exports as schema };