@systemfsoftware/stryker-js-typescript-checker 3.0.3 → 4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @systemfsoftware/stryker-js-typescript-checker
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - CheckMutantsDecision is now a branded tagged union CheckFinished|RetestRequired instead of a plain record; consumer dispatch is exhaustive over the tags
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies:
12
+ - @systemfsoftware/effect-cell-types@6.0.0
13
+ - @systemfsoftware/stryker-js@1.0.0
14
+
3
15
  ## 3.0.3
4
16
 
5
17
  ### Patch Changes
package/dist/index.mjs CHANGED
@@ -8,8 +8,8 @@ import * as S from "effect/Schema";
8
8
  import { Cell, Wire, Workflow } from "@systemfsoftware/effect-cell-types";
9
9
  import { Mutant, errorToString } from "@systemfsoftware/stryker-js/Mutant";
10
10
  import { Predicate, Result, Schema } from "effect";
11
- import { pipe } from "effect/Function";
12
11
  import * as HashMap from "effect/HashMap";
12
+ import * as Match from "effect/Match";
13
13
  import * as MutableHashMap from "effect/MutableHashMap";
14
14
  import * as Option from "effect/Option";
15
15
  import { API, DiagnosticCategory } from "typescript/unstable/sync";
@@ -39,46 +39,7 @@ var typescript_checker_options_default = {
39
39
  } }
40
40
  };
41
41
  //#endregion
42
- //#region src/Checker.schema.ts
43
- /**
44
- * Checker — declarations for the TypeScript checker.
45
- *
46
- * Houses the wire types and error variants shared by the capability and its
47
- * workflow. Decoded at the checker boundary; no I/O.
48
- */
49
- var CheckMutantsCommand = class extends S.TaggedClass()("CheckMutantsCommand", { mutants: S.Array(Mutant) }) {};
50
- /**
51
- * Every way the TypeScript compiler can fail while serving a check.
52
- * One tagged error — callers branch only on failure itself; `reason` keeps
53
- * cases distinguishable in reports.
54
- */
55
- var CompilerFailed = class extends S.TaggedError()("CompilerFailed", {
56
- reason: S.Literals([
57
- "not-initialized",
58
- "no-projects",
59
- "unknown-file-node",
60
- "file-not-in-project"
61
- ]),
62
- subject: S.optional(S.String)
63
- }) {
64
- get message() {
65
- switch (this.reason) {
66
- case "not-initialized": return "The TypeScript compiler was used before it was initialized";
67
- case "no-projects": return `No projects were found for ${this.subject ?? "the tsconfig"}`;
68
- case "unknown-file-node": return `The file graph has no node for '${this.subject ?? "a file"}', which should not happen`;
69
- case "file-not-in-project": return `'${this.subject ?? "a file"}' is part of your TypeScript project but could not be found on disk`;
70
- }
71
- }
72
- };
73
- //#endregion
74
- //#region src/Checker.workflow.ts
75
- /**
76
- * Checker — pure check decision.
77
- *
78
- * `Workflow.make` lives here and only here. The workflow receives a fully
79
- * decoded input (mutants + diagnostics + file graph) and produces results plus
80
- * `needsRetest` without touching I/O.
81
- */
42
+ //#region src/check-mutants.workflow.ts
82
43
  var DiagnosticWithoutFileError = class extends S.TaggedError()("DiagnosticWithoutFileError", { text: Wire.mint(S.String) }) {};
83
44
  var DiagnosticInUnrelatedFileError = class extends S.TaggedError()("DiagnosticInUnrelatedFileError", {
84
45
  text: Wire.mint(S.String),
@@ -98,6 +59,20 @@ var CheckMutantsInput = class extends S.TaggedClass()("CheckMutantsInput", {
98
59
  diagnostics: S.Array(DiagnosticSchema),
99
60
  nodes: Wire.mint(S.Record(Wire.mint(S.String), TSFileNodeSchema))
100
61
  }) {};
62
+ const CheckMutantsTypeId = Symbol.for("@systemfsoftware/stryker-js-typescript-checker/CheckMutants");
63
+ const MutantCheckStatusSchema = S.Union([S.Struct({ status: S.Literal("passed") }), S.Struct({
64
+ status: S.Literal("compileError"),
65
+ reason: S.String
66
+ })]);
67
+ var CheckFinished = class extends S.TaggedClass()("CheckFinished", { results: S.Record(S.String, MutantCheckStatusSchema) }) {
68
+ [CheckMutantsTypeId] = CheckMutantsTypeId;
69
+ };
70
+ var RetestRequired = class extends S.TaggedClass()("RetestRequired", {
71
+ results: S.Record(S.String, MutantCheckStatusSchema),
72
+ needsRetest: S.Array(Mutant)
73
+ }) {
74
+ [CheckMutantsTypeId] = CheckMutantsTypeId;
75
+ };
101
76
  const normalizeFileName$3 = (fileName) => fileName.replace(/\\/g, "/");
102
77
  const getMutantsWithReferenceToChildrenOrSelf = (node, mutants, nodesChecked = []) => {
103
78
  if (nodesChecked.includes(node.fileName)) return [];
@@ -148,18 +123,12 @@ const buildResult = (input) => {
148
123
  const mutants = input.mutants;
149
124
  const diagnostics = input.diagnostics;
150
125
  const nodes = input.nodes;
151
- if (mutants.length === 0) return Result$1.succeed({
152
- results: {},
153
- needsRetest: []
154
- });
126
+ if (mutants.length === 0) return Result$1.succeed(CheckFinished.make({ results: {} }));
155
127
  const first = mutants[0];
156
128
  if (first === void 0 || nodes[normalizeFileName$3(first.fileName)] === void 0) {
157
129
  const results = {};
158
130
  for (const m of mutants) results[m.id] = { status: "passed" };
159
- return Result$1.succeed({
160
- results,
161
- needsRetest: []
162
- });
131
+ return Result$1.succeed(CheckFinished.make({ results }));
163
132
  }
164
133
  const classified = classifyDiagnosticsPure(diagnostics, mutants, nodes);
165
134
  if (Result$1.isFailure(classified)) return Result$1.fail(classified.failure);
@@ -175,13 +144,46 @@ const buildResult = (input) => {
175
144
  };
176
145
  else if (retestIds[m.id] !== true) results[m.id] = { status: "passed" };
177
146
  }
178
- return Result$1.succeed({
147
+ if (needsRetest.length === 0) return Result$1.succeed(CheckFinished.make({ results }));
148
+ return Result$1.succeed(RetestRequired.make({
179
149
  results,
180
- needsRetest
181
- });
150
+ needsRetest: [...needsRetest]
151
+ }));
182
152
  };
183
153
  const checkMutants = Workflow.make(CheckMutantsInput, (input) => buildResult(input));
184
154
  //#endregion
155
+ //#region src/Checker.schema.ts
156
+ /**
157
+ * Checker — declarations for the TypeScript checker.
158
+ *
159
+ * Houses the wire types and error variants shared by the capability and its
160
+ * workflow. Decoded at the checker boundary; no I/O.
161
+ */
162
+ var CheckMutantsCommand = class extends S.TaggedClass()("CheckMutantsCommand", { mutants: S.Array(Mutant) }) {};
163
+ /**
164
+ * Every way the TypeScript compiler can fail while serving a check.
165
+ * One tagged error — callers branch only on failure itself; `reason` keeps
166
+ * cases distinguishable in reports.
167
+ */
168
+ var CompilerFailed = class extends S.TaggedError()("CompilerFailed", {
169
+ reason: S.Literals([
170
+ "not-initialized",
171
+ "no-projects",
172
+ "unknown-file-node",
173
+ "file-not-in-project"
174
+ ]),
175
+ subject: S.optional(S.String)
176
+ }) {
177
+ get message() {
178
+ switch (this.reason) {
179
+ case "not-initialized": return "The TypeScript compiler was used before it was initialized";
180
+ case "no-projects": return `No projects were found for ${this.subject ?? "the tsconfig"}`;
181
+ case "unknown-file-node": return `The file graph has no node for '${this.subject ?? "a file"}', which should not happen`;
182
+ case "file-not-in-project": return `'${this.subject ?? "a file"}' is part of your TypeScript project but could not be found on disk`;
183
+ }
184
+ }
185
+ };
186
+ //#endregion
185
187
  //#region src/Compiler.schema.ts
186
188
  /**
187
189
  * Compiler — declarations for the TypeScript compiler and version guard.
@@ -1102,18 +1104,7 @@ function makeTypescriptCompiler(options, fs, fsService, pathService) {
1102
1104
  }
1103
1105
  //#endregion
1104
1106
  //#region src/Checker.ts
1105
- /**
1106
- * Checker — capability that validates mutants against the TypeScript compiler.
1107
- *
1108
- * Bridges the checker plugin protocol (`@systemfsoftware/stryker-js/Checker`)
1109
- * to the compiler service and the pure `checkMutants` workflow. Diagnostics
1110
- * are classified without I/O; the file graph is sourced from the compiler.
1111
- */
1112
1107
  const normalizeFileName = (fileName) => fileName.replace(/\\/g, "/");
1113
- /**
1114
- * Pure grouping decision: separates mutants inside the project graph from
1115
- * those outside it, honouring `prioritizePerformanceOverAccuracy`.
1116
- */
1117
1108
  function partitionMutantsForGrouping(mutants, nodes, prioritizePerformanceOverAccuracy) {
1118
1109
  if (!prioritizePerformanceOverAccuracy) return {
1119
1110
  inside: [],
@@ -1137,32 +1128,39 @@ function getPrioritize(options) {
1137
1128
  if (typeof val === "boolean") return val;
1138
1129
  return false;
1139
1130
  }
1140
- const makeCheckDescription = (compiler) => pipe(Cell.read((command) => Effect.gen(function* () {
1141
- const nodesHm = yield* compiler.nodes.pipe(Effect.mapError((cause) => new CheckerFailed({
1142
- checkerName: "typescript",
1143
- mutantIds: command.mutants.map((m) => m.id),
1144
- cause: errorToString(cause)
1145
- })));
1146
- const nodes = {};
1147
- for (const [k, v] of nodesHm) nodes[k] = v;
1148
- const diagnostics = yield* compiler.check([...command.mutants]).pipe(Effect.mapError((cause) => new CheckerFailed({
1149
- checkerName: "typescript",
1150
- mutantIds: command.mutants.map((m) => m.id),
1151
- cause: errorToString(cause)
1152
- })));
1153
- return new CheckMutantsInput({
1154
- mutants: [...command.mutants],
1155
- diagnostics: [...diagnostics],
1156
- nodes
1157
- });
1158
- })), Cell.decode((raw) => Result.succeed(raw)), Cell.decide(checkMutants), Cell.encode((outcome) => outcome), Cell.write((outcome) => Result.match(outcome, {
1159
- onFailure: (failure) => Effect.fail(new CheckerFailed({
1160
- checkerName: "typescript",
1161
- mutantIds: [],
1162
- cause: errorToString(failure)
1163
- })),
1164
- onSuccess: (decision) => Effect.succeed(decision)
1165
- })));
1131
+ const checkCell = Cell.layer({
1132
+ read: (command) => Effect.gen(function* () {
1133
+ const compiler = yield* TypeScriptCompiler;
1134
+ const nodesHm = yield* compiler.nodes.pipe(Effect.mapError((cause) => new CheckerFailed({
1135
+ checkerName: "typescript",
1136
+ mutantIds: command.mutants.map((m) => m.id),
1137
+ cause: errorToString(cause)
1138
+ })));
1139
+ const nodes = {};
1140
+ for (const [k, v] of nodesHm) nodes[k] = v;
1141
+ const diagnostics = yield* compiler.check([...command.mutants]).pipe(Effect.mapError((cause) => new CheckerFailed({
1142
+ checkerName: "typescript",
1143
+ mutantIds: command.mutants.map((m) => m.id),
1144
+ cause: errorToString(cause)
1145
+ })));
1146
+ return new CheckMutantsInput({
1147
+ mutants: [...command.mutants],
1148
+ diagnostics: [...diagnostics],
1149
+ nodes
1150
+ });
1151
+ }),
1152
+ decode: (raw) => Result.succeed(raw),
1153
+ decide: checkMutants,
1154
+ encode: (outcome) => outcome,
1155
+ write: (outcome) => Result.match(outcome, {
1156
+ onFailure: (failure) => Effect.fail(new CheckerFailed({
1157
+ checkerName: "typescript",
1158
+ mutantIds: [],
1159
+ cause: errorToString(failure)
1160
+ })),
1161
+ onSuccess: (decision) => Effect.succeed(decision)
1162
+ })
1163
+ });
1166
1164
  function makeCheckerService({ options, compiler }) {
1167
1165
  const formatDiagnostic = (error) => Effect.gen(function* () {
1168
1166
  let severity;
@@ -1204,8 +1202,7 @@ function makeCheckerService({ options, compiler }) {
1204
1202
  }
1205
1203
  }),
1206
1204
  check: (mutants) => Effect.gen(function* () {
1207
- const description = makeCheckDescription(compiler);
1208
- const applyOnce = (group) => Cell.apply(description, new CheckMutantsCommand({ mutants: [...group] }));
1205
+ const applyOnce = (group) => Cell.run(checkCell, new CheckMutantsCommand({ mutants: [...group] })).pipe(Effect.provideService(TypeScriptCompiler, compiler));
1209
1206
  const first = yield* applyOnce(mutants);
1210
1207
  let map = HashMap.empty();
1211
1208
  const mergeResults = (results) => {
@@ -1216,14 +1213,17 @@ function makeCheckerService({ options, compiler }) {
1216
1213
  });
1217
1214
  };
1218
1215
  mergeResults(first.results);
1219
- if (first.needsRetest.length > 0) yield* applyOnce([]);
1220
- const originals = {};
1221
- for (const m of mutants) originals[m.id] = m;
1222
- for (const pending of first.needsRetest) {
1223
- const original = originals[pending.id];
1224
- if (original === void 0) continue;
1225
- mergeResults((yield* applyOnce([original])).results);
1226
- }
1216
+ yield* Match.value(first).pipe(Match.tag("CheckFinished", () => Effect.void), Match.tag("RetestRequired", (retest) => Effect.gen(function* () {
1217
+ yield* applyOnce([]);
1218
+ const originals = {};
1219
+ for (const m of mutants) originals[m.id] = m;
1220
+ for (const pending of retest.needsRetest) {
1221
+ const original = originals[pending.id];
1222
+ if (original === void 0) continue;
1223
+ const one = yield* applyOnce([original]);
1224
+ mergeResults(one.results);
1225
+ }
1226
+ })), Match.exhaustive);
1227
1227
  return map;
1228
1228
  }),
1229
1229
  group: (mutants) => Effect.gen(function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@systemfsoftware/stryker-js-typescript-checker",
3
- "version": "3.0.3",
3
+ "version": "4.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
@@ -22,8 +22,8 @@
22
22
  "dependencies": {
23
23
  "effect": "^4.0.0-rc.112",
24
24
  "typescript": "^7",
25
- "@systemfsoftware/effect-cell-types": "^5.0.2",
26
- "@systemfsoftware/stryker-js": "^0.2.0"
25
+ "@systemfsoftware/stryker-js": "^1.0.0",
26
+ "@systemfsoftware/effect-cell-types": "^6.0.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@effect/platform-node": "^4.0.0-rc.112",
@@ -35,14 +35,13 @@
35
35
  "rimraf": "^6.1.3",
36
36
  "tsdown": "^0.22.14",
37
37
  "vitest": "^4",
38
+ "@systemfsoftware/all": "^1.1.2",
38
39
  "@systemfsoftware/effect-gherkin-spec": "^4.0.0",
39
- "@systemfsoftware/effect-schema-vite": "^2.0.2",
40
40
  "@systemfsoftware/effect-schema-law": "^2.0.1",
41
- "@systemfsoftware/all": "^1.1.0",
42
- "@systemfsoftware/oxlint-config": "^0.1.0",
43
- "@systemfsoftware/stryker-js-platform-node": "^0.2.0",
44
41
  "@systemfsoftware/tsconfig": "^1.3.3",
45
- "@systemfsoftware/vitest-config": "^0.1.0"
42
+ "@systemfsoftware/vitest-config": "^0.1.0",
43
+ "@systemfsoftware/oxlint-config": "^0.1.0",
44
+ "@systemfsoftware/effect-schema-vite": "^2.0.2"
46
45
  },
47
46
  "inlinedDependencies": {
48
47
  "@jsr/std__jsonc": "1.0.2"