@systemfsoftware/stryker-js-instrumenter 10.0.1 → 12.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/dist/index.mjs CHANGED
@@ -8,10 +8,14 @@ import * as S from "effect/Schema";
8
8
  import * as Effect from "effect/Effect";
9
9
  import { dual } from "effect/Function";
10
10
  import { Handle, Sandwich, Workflow } from "@systemfsoftware/effect-cell-types";
11
- import * as Order from "effect/Order";
12
- import * as SchemaGetter from "effect/SchemaGetter";
11
+ import { Mutant, Options } from "@systemfsoftware/stryker-js-plugin-interface";
13
12
  import { RegExpParser, visitRegExpAST } from "@eslint-community/regexpp";
14
13
  import * as Arr from "effect/Array";
14
+ import * as Order from "effect/Order";
15
+ import * as SchemaGetter from "effect/SchemaGetter";
16
+ import { SpanTaxonomy } from "@systemfsoftware/stryker-js-cli-contract";
17
+ import { sha256 } from "@noble/hashes/sha2.js";
18
+ import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
15
19
  //#region src/ErrorText.schema.ts
16
20
  var ErrorText = class extends S.Class("ErrorText")({ text: S.NonEmptyString }) {};
17
21
  var CauseText = class extends S.Class("CauseText")({ text: S.NonEmptyString }) {};
@@ -106,71 +110,9 @@ var ErrorText_exports = /* @__PURE__ */ __exportAll({
106
110
  errorTextOf: () => errorTextOf$2
107
111
  });
108
112
  //#endregion
109
- //#region src/Location.schema.ts
110
- const Offset = S.Int.check(S.isGreaterThanOrEqualTo(0));
111
- /** A 1-based line: the first line of a file is line 1. */
112
- const Line = S.Int.check(S.isGreaterThanOrEqualTo(1));
113
- /** A 1-based column: the first character of a line is column 1. */
114
- const Column = S.Int.check(S.isGreaterThanOrEqualTo(1));
115
- const Position = S.Struct({
116
- line: Line,
117
- column: Column
118
- });
119
- const positionOrder = Order.combine(Order.mapInput(Order.Number, (position) => position.line), Order.mapInput(Order.Number, (position) => position.column));
120
- const inOrder = (order) => (ends) => ({
121
- start: Order.min(order)(ends.start, ends.end),
122
- end: Order.max(order)(ends.start, ends.end)
123
- });
124
- const notReversed = (order) => (ends) => Order.isLessThanOrEqualTo(order)(ends.start, ends.end);
125
- const PositionEnds = S.Struct({
126
- start: Position,
127
- end: Position
128
- });
129
- const Location = S.declare((value) => S.is(PositionEnds)(value) && notReversed(positionOrder)(value), {
130
- expected: "a location whose end is not before its start",
131
- toCodecArbitrary: () => S.link()(PositionEnds, {
132
- decode: SchemaGetter.transform(inOrder(positionOrder)),
133
- encode: SchemaGetter.transform((ends) => ends)
134
- })
135
- });
136
- const OpenEnds = S.Struct({
137
- start: Position,
138
- end: S.optional(Position)
139
- });
140
- const closedEnds = (ends) => Option.map(Option.fromUndefinedOr(ends.end), (end) => ({
141
- start: ends.start,
142
- end
143
- }));
144
- const OpenEndLocation = S.declare((value) => S.is(OpenEnds)(value) && Option.match(closedEnds(value), {
145
- onNone: () => true,
146
- onSome: notReversed(positionOrder)
147
- }), {
148
- expected: "a location whose end, when present, is not before its start",
149
- toCodecArbitrary: () => S.link()(OpenEnds, {
150
- decode: SchemaGetter.transform((ends) => Option.getOrElse(Option.map(closedEnds(ends), inOrder(positionOrder)), () => ends)),
151
- encode: SchemaGetter.transform((ends) => ends)
152
- })
153
- });
154
- const OffsetEnds = S.Struct({
155
- start: Offset,
156
- end: Offset
157
- });
158
- const Span = S.declare((value) => S.is(OffsetEnds)(value) && notReversed(Order.Number)(value), {
159
- expected: "a span whose end is not before its start",
160
- toCodecArbitrary: () => S.link()(OffsetEnds, {
161
- decode: SchemaGetter.transform(inOrder(Order.Number)),
162
- encode: SchemaGetter.transform((ends) => ends)
163
- })
164
- });
165
- const ScriptOrigin = S.Struct({
166
- line: Line,
167
- columnShift: Offset
168
- });
169
- S.is(Offset), S.is(Line), S.is(Column), S.is(Location), S.is(Span);
170
- //#endregion
171
113
  //#region src/directives/directive.schema.ts
172
- const MutatorNameSchema = S.String.pipe(S.check(S.isPattern(/^[a-zA-Z]+(?: [a-zA-Z]+)*$/)));
173
- const DirectiveReasonSchema = S.String.pipe(S.check(S.isPattern(/^\S(?:[^\r\n\u2028\u2029]*\S)?$/)));
114
+ const MutatorNameSchema = S.String.pipe(S.check(S.isPattern(/^(?:[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/)?[a-zA-Z][a-zA-Z0-9]*(?: [a-zA-Z][a-zA-Z0-9]*)*$/u)));
115
+ const DirectiveReasonSchema = S.String.pipe(S.check(S.isPattern(/^\S(?:[^\r\n\u2028\u2029]*\S)?$/u)));
174
116
  const DirectiveSchema = S.Struct({
175
117
  action: S.Literals(["disable", "restore"]),
176
118
  scope: S.Literals(["next-line", "block"]),
@@ -179,106 +121,15 @@ const DirectiveSchema = S.Struct({
179
121
  });
180
122
  const LocatedDirectiveSchema = S.Struct({
181
123
  directive: DirectiveSchema,
182
- at: Position,
183
- governedLine: Line
124
+ at: Mutant.Position,
125
+ governedLine: Mutant.Line
184
126
  });
185
127
  S.Struct({
186
128
  directive: DirectiveSchema,
187
- at: Position,
129
+ at: Mutant.Position,
188
130
  mutatorName: MutatorNameSchema
189
131
  });
190
132
  //#endregion
191
- //#region src/Mutant.schema.ts
192
- const MutantStatusSchema = S.Literals([
193
- "Killed",
194
- "Survived",
195
- "NoCoverage",
196
- "CompileError",
197
- "RuntimeError",
198
- "Timeout",
199
- "Ignored",
200
- "Pending"
201
- ]);
202
- const SurvivorStatusSchema = S.Literals(["Survived", "NoCoverage"]);
203
- const RememberedStatusSchema = S.Literals([
204
- "Survived",
205
- "Killed",
206
- "Timeout",
207
- "NoCoverage",
208
- "Ignored"
209
- ]);
210
- const EphemeralStatusSchema = S.Literals([
211
- "CompileError",
212
- "RuntimeError",
213
- "Pending"
214
- ]);
215
- const ActionableStatusSchema = S.Literals([
216
- "Survived",
217
- "NoCoverage",
218
- "Timeout",
219
- "RuntimeError"
220
- ]);
221
- const MutantId = S.String.check(S.isPattern(/^(0|[1-9][0-9]*)$/)).pipe(S.brand("MutantId"));
222
- const MutatorName = S.NonEmptyString.pipe(S.brand("MutatorName"));
223
- const CanonicalFileName = S.String.pipe(S.decodeTo(S.String.pipe(S.check(S.isPattern(/^[^\\]+$/)), S.brand("CanonicalFileName")), {
224
- decode: SchemaGetter.transform((fileName) => fileName.replace(/\\/g, "/")),
225
- encode: SchemaGetter.transform((canonical) => canonical)
226
- }));
227
- /**
228
- * A mutant's file location in the mutation-testing-report-schema contract:
229
- * 1-based line and 1-based column, the same base the JSON report and the
230
- * machine stream emit. Every producer on the instrument path (node spans,
231
- * embedded-region shifts) targets this base; no downstream layer converts.
232
- */
233
- const Mutant = S.TaggedStruct("Mutant", {
234
- id: MutantId,
235
- fileName: CanonicalFileName,
236
- mutatorName: MutatorName,
237
- replacement: S.String,
238
- location: Location,
239
- status: S.optional(MutantStatusSchema),
240
- statusReason: S.optional(S.String),
241
- coveredBy: S.String.pipe(S.Array, S.optional),
242
- static: S.optional(S.Boolean),
243
- testsCompleted: S.optional(S.Finite),
244
- description: S.optional(S.String)
245
- }).check(S.makeFilter((mutant) => mutant.statusReason === void 0 || mutant.status !== void 0, { message: "a mutant carries a status reason only together with a status" }));
246
- const MutantFromUnknown = S.Unknown.pipe(S.decodeTo(Mutant));
247
- const RunOptionsFields = {
248
- timeout: S.Finite.pipe(S.check(S.isGreaterThanOrEqualTo(0))),
249
- disableBail: S.Boolean
250
- };
251
- const MutantActivationSchema = S.Literals(["runtime", "static"]);
252
- const HitCount = S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(0)));
253
- const MutantRunOptionsSchema = S.Struct({
254
- ...RunOptionsFields,
255
- activeMutant: Mutant,
256
- sandboxFileName: S.String,
257
- mutantActivation: MutantActivationSchema,
258
- reloadEnvironment: S.Boolean,
259
- testFilter: S.String.pipe(S.Array, S.optionalKey),
260
- hitLimit: S.optionalKey(HitCount)
261
- });
262
- const MutantCoverageSchema = S.Struct({
263
- perTest: S.Record(S.String, S.Record(MutantId, HitCount)),
264
- static: S.Record(MutantId, HitCount)
265
- });
266
- var InstrumenterContext = class extends S.Class("InstrumenterContext")({
267
- activeMutant: S.optional(S.String),
268
- currentTestId: S.optional(S.String),
269
- mutantCoverage: S.optional(MutantCoverageSchema),
270
- hitCount: S.optional(HitCount),
271
- hitLimit: S.optional(HitCount)
272
- }) {
273
- static NAMESPACE = "__stryker__";
274
- static MUTATION_COVERAGE_OBJECT = "mutantCoverage";
275
- static ACTIVE_MUTANT = "activeMutant";
276
- static CURRENT_TEST_ID = "currentTestId";
277
- static HIT_COUNT = "hitCount";
278
- static HIT_LIMIT = "hitLimit";
279
- static ACTIVE_MUTANT_ENV_VARIABLE = "__STRYKER_ACTIVE_MUTANT__";
280
- };
281
- //#endregion
282
133
  //#region src/Instrument.schema.ts
283
134
  var InstrumentError = class extends S.TaggedError("@systemfsoftware/stryker-js-instrumenter/Instrument.schema/InstrumentError")("InstrumentError", {
284
135
  message: S.String,
@@ -291,9 +142,9 @@ var InstrumentError = class extends S.TaggedError("@systemfsoftware/stryker-js-i
291
142
  });
292
143
  }
293
144
  };
294
- const MutateDescriptionSchema = S.Union([S.Boolean, S.Array(Location)]);
145
+ const MutateDescriptionSchema = S.Union([S.Boolean, S.Array(Mutant.Location)]);
295
146
  const FileSchema = S.Struct({
296
- name: CanonicalFileName,
147
+ name: Mutant.CanonicalFileName,
297
148
  content: S.String,
298
149
  mutate: MutateDescriptionSchema
299
150
  });
@@ -301,8 +152,9 @@ const IgnorerSchema = S.Unknown;
301
152
  S.Struct({
302
153
  excludedMutations: S.Array(MutatorNameSchema),
303
154
  ignorers: S.Array(IgnorerSchema),
304
- noHeader: S.optional(S.Boolean),
305
- optInMutations: S.String.pipe(S.Array, S.optional)
155
+ mutators: S.Unknown,
156
+ mutantSetPolicy: Options.MutantSetPolicy,
157
+ noHeader: S.optional(S.Boolean)
306
158
  });
307
159
  var InstrumentFileSkip = class extends S.TaggedClass()("InstrumentFileSkip", {
308
160
  file: S.String,
@@ -318,7 +170,7 @@ var InstrumentFilesCommand = class extends S.TaggedClass()("InstrumentFilesComma
318
170
  };
319
171
  var InstrumentResult = class extends S.TaggedClass()("InstrumentResult", {
320
172
  files: S.Array(FileSchema),
321
- mutants: S.Array(Mutant),
173
+ mutants: S.Array(Mutant.Mutant),
322
174
  skipped: S.Array(InstrumentFileSkip)
323
175
  }) {};
324
176
  const PlacerNameSchema = S.Literals([
@@ -359,6 +211,68 @@ var MutantsUnplaced = class extends S.TaggedError()("MutantsUnplaced", {
359
211
  };
360
212
  var PlacementRefused = class extends S.TaggedError()("PlacementRefused", { message: S.String }) {};
361
213
  //#endregion
214
+ //#region src/relational-sufficient-sets.ts
215
+ /**
216
+ * The sufficient sets of non-redundant relational-operator mutations.
217
+ *
218
+ * Source: René Just and Franz Schweiggert, "Higher Accuracy and Lower Run Time:
219
+ * Efficient Mutation Analysis Using Non-Redundant Mutation Operators", Journal
220
+ * of Software Testing, Verification and Reliability 24(5), 2014, Table III
221
+ * (https://homes.cs.washington.edu/~rjust/publ/non_redundant_mutants_jstvr_2014.pdf).
222
+ *
223
+ * The paper groups every input tuple into the three intervals `a < b`,
224
+ * `a == b`, and `a > b`. For each relational operator exactly one
225
+ * minimal-distance mutation covers each interval, and those three mutations
226
+ * subsume every other relational replacement. Each row below lists the two
227
+ * replacement operators and the boolean the whole comparison collapses to. The
228
+ * sets assume numeric operands without `NaN`; `mutantSetPolicy: 'full'`
229
+ * restores every variant.
230
+ */
231
+ const RELATIONAL_OPERATORS = [
232
+ "<",
233
+ "<=",
234
+ ">",
235
+ ">="
236
+ ];
237
+ const isRelationalOperator = (operator) => RELATIONAL_OPERATORS.some((relational) => relational === operator);
238
+ const SUFFICIENT_RELATIONAL_SETS = {
239
+ "<": {
240
+ replacements: ["<=", "!="],
241
+ literal: false
242
+ },
243
+ "<=": {
244
+ replacements: ["<", "=="],
245
+ literal: true
246
+ },
247
+ ">": {
248
+ replacements: [">=", "!="],
249
+ literal: false
250
+ },
251
+ ">=": {
252
+ replacements: [">", "=="],
253
+ literal: true
254
+ }
255
+ };
256
+ //#endregion
257
+ //#region src/Location.schema.ts
258
+ const Offset = S.Int.check(S.isGreaterThanOrEqualTo(0));
259
+ const OffsetEnds = S.Struct({
260
+ start: Offset,
261
+ end: Offset
262
+ });
263
+ const Span = S.declare((value) => S.is(OffsetEnds)(value) && Mutant.notReversed(Order.Number)(value), {
264
+ expected: "a span whose end is not before its start",
265
+ toCodecArbitrary: () => S.link()(OffsetEnds, {
266
+ decode: SchemaGetter.transform(Mutant.inOrder(Order.Number)),
267
+ encode: SchemaGetter.transform((ends) => ends)
268
+ })
269
+ });
270
+ const ScriptOrigin = S.Struct({
271
+ line: Mutant.Line,
272
+ columnShift: Offset
273
+ });
274
+ S.is(Offset), S.is(Span);
275
+ //#endregion
362
276
  //#region src/Location.ts
363
277
  const LINE_TERMINATOR = /\r\n|[\n\r\u2028\u2029]/g;
364
278
  const lineStartsOf = (text) => [0, ...[...text.matchAll(LINE_TERMINATOR)].map((match) => match.index + match[0].length)];
@@ -386,7 +300,7 @@ const positionAtOffset = (lineStarts, offset, low, high, start) => Boolean$1.mat
386
300
  })
387
301
  });
388
302
  const positionAt = dual(2, (lineStarts, offset) => positionAtOffset(lineStarts, offset, 0, lineStarts.length - 1, 0));
389
- const locationOf = dual(2, (lineStarts, span) => Location.make({
303
+ const locationOf = dual(2, (lineStarts, span) => Mutant.Location.make({
390
304
  start: positionAt(lineStarts, span.start),
391
305
  end: positionAt(lineStarts, span.end)
392
306
  }));
@@ -1423,7 +1337,7 @@ function toApiMutant(mutant) {
1423
1337
  mutatorName: mutant.mutatorName,
1424
1338
  replacement: mutant.replacementCode
1425
1339
  };
1426
- return S.decodeResult(Mutant)(mutant.ignoreReason === void 0 ? baseFields : {
1340
+ return S.decodeResult(Mutant.Mutant)(mutant.ignoreReason === void 0 ? baseFields : {
1427
1341
  ...baseFields,
1428
1342
  statusReason: mutant.ignoreReason,
1429
1343
  status: "Ignored"
@@ -1844,9 +1758,19 @@ const booleanOperators = Object.freeze([
1844
1758
  ">=",
1845
1759
  "||"
1846
1760
  ]);
1847
- const conditionalExpressionMutator = (node, context) => Match.value(isTestOfLoop(node, context)).pipe(Match.when(true, () => [booleanLiteral(false)]), Match.orElse(() => conditionTestMutants(node, context)));
1761
+ const conditionalExpressionMutator = (node, context) => Match.value(isTestOfLoop(node, context)).pipe(Match.when(true, () => withSufficientConditional(node, context, [booleanLiteral(false)])), Match.orElse(() => conditionTestMutants(node, context)));
1848
1762
  function conditionTestMutants(node, context) {
1849
- return Match.value(isTestOfCondition(node, context)).pipe(Match.when(true, () => [booleanLiteral(true), booleanLiteral(false)]), Match.orElse(() => booleanExpressionMutants(node, context)));
1763
+ return Match.value(isTestOfCondition(node, context)).pipe(Match.when(true, () => withSufficientConditional(node, context, [booleanLiteral(true), booleanLiteral(false)])), Match.orElse(() => booleanExpressionMutants(node, context)));
1764
+ }
1765
+ function withSufficientConditional(node, context, replacements) {
1766
+ const literal = sufficientRelationalLiteral(node, context);
1767
+ return literal === void 0 ? replacements : appendBooleanLiteral(replacements, literal);
1768
+ }
1769
+ function sufficientRelationalLiteral(node, context) {
1770
+ return Option.getOrUndefined(Option.map(Option.filter(Option.fromNullishOr(relationalSiteFacts(node, context)), (facts) => [facts.inConditionPosition, context.mutantSetPolicy === "default"].every(Boolean)), (facts) => SUFFICIENT_RELATIONAL_SETS[facts.operator].literal));
1771
+ }
1772
+ function appendBooleanLiteral(replacements, literal) {
1773
+ return replacements.some((replacement) => isBooleanLiteral(replacement) && replacement.value === literal) ? replacements : [...replacements, booleanLiteral(literal)];
1850
1774
  }
1851
1775
  function booleanExpressionMutants(node, context) {
1852
1776
  return Match.value(isBooleanExpression(node)).pipe(Match.when(true, () => booleanExpressionReplacements(context)), Match.orElse(() => statementMutants(node)));
@@ -1909,6 +1833,40 @@ function isTestBearingStatement(node) {
1909
1833
  function testOfStatement(node) {
1910
1834
  return Match.value(node).pipe(Match.when(isTestBearingStatement, (statement) => statement.test ?? void 0), Match.orElse(() => void 0));
1911
1835
  }
1836
+ const isConditionalExpression = (node) => node.type === "ConditionalExpression";
1837
+ function isTestOfConditionalExpression(node, context) {
1838
+ return Option.exists(Option.filter(Option.fromNullishOr(context.parent), isConditionalExpression), (parent) => parent.test === node);
1839
+ }
1840
+ function isConditionPosition(node, context) {
1841
+ return [
1842
+ isTestOfLoop(node, context),
1843
+ isTestOfCondition(node, context),
1844
+ isTestOfConditionalExpression(node, context)
1845
+ ].some(Boolean);
1846
+ }
1847
+ function binaryOperatorOf(node) {
1848
+ return node.type === "BinaryExpression" ? node.operator : void 0;
1849
+ }
1850
+ const isRelationalComparison = (node) => Option.exists(Option.fromNullishOr(binaryOperatorOf(node)), isRelationalOperator);
1851
+ const relationalSiteFactsDataFirst = (node, context) => Option.getOrUndefined(Option.map(Option.filter(Option.fromNullishOr(binaryOperatorOf(node)), isRelationalOperator), (operator) => ({
1852
+ operator,
1853
+ inConditionPosition: isConditionPosition(node, context)
1854
+ })));
1855
+ const relationalSiteFacts = dual((args) => args.length >= 2, relationalSiteFactsDataFirst);
1856
+ const relationalSufficientDataFirst = (facts, replacement) => facts === void 0 ? true : sufficientInCondition(facts, replacement);
1857
+ function sufficientInCondition(facts, replacement) {
1858
+ return facts.inConditionPosition ? sufficientReplacementOf(facts, replacement) : true;
1859
+ }
1860
+ function sufficientReplacementOf(facts, replacement) {
1861
+ return Match.value(replacement).pipe(Match.when(isBooleanLiteral, (literal) => literal.value === SUFFICIENT_RELATIONAL_SETS[facts.operator].literal), Match.when(isBinaryExpressionNode, (binary) => isSufficientOperator(facts, binary.operator)), Match.orElse(() => true));
1862
+ }
1863
+ const relationalSufficientReplacement = dual((args) => args.length >= 2, relationalSufficientDataFirst);
1864
+ function isSufficientOperator(facts, operator) {
1865
+ return SUFFICIENT_RELATIONAL_SETS[facts.operator].replacements.some((sufficient) => sufficient === operator);
1866
+ }
1867
+ function isBinaryExpressionNode(node) {
1868
+ return node.type === "BinaryExpression";
1869
+ }
1912
1870
  function isBooleanExpression(node) {
1913
1871
  return isOperatorExpression(node) && booleanOperators.includes(node.operator);
1914
1872
  }
@@ -1926,12 +1884,23 @@ const operators = {
1926
1884
  "!==": ["==="]
1927
1885
  };
1928
1886
  const EQUALITY_OPERATOR_KEYS = Object.keys(operators);
1929
- const equalityOperatorMutator = (node) => Match.value(node).pipe(Match.when(isEqualityBinary, (binary) => mutatedEqualityOperators(binary)), Match.orElse(() => NO_MUTANTS));
1887
+ const equalityOperatorMutator = (node, context) => Match.value(node).pipe(Match.when(isEqualityBinary, (binary) => mutatedEqualityOperators(binary, context)), Match.orElse(() => NO_MUTANTS));
1930
1888
  function isEqualityBinary(node) {
1931
1889
  return node.type === "BinaryExpression" && EQUALITY_OPERATOR_KEYS.includes(node.operator);
1932
1890
  }
1933
- function mutatedEqualityOperators(binary) {
1934
- return operators[binary.operator].map((operator) => withOperator(binary, operator));
1891
+ function mutatedEqualityOperators(binary, context) {
1892
+ const base = operators[binary.operator].map((operator) => withOperator(binary, operator));
1893
+ return isRelationalConditionSite(binary, context) ? [...base, ...sufficientAdditions(binary)] : base;
1894
+ }
1895
+ function isRelationalConditionSite(node, context) {
1896
+ return [
1897
+ context.mutantSetPolicy === "default",
1898
+ isRelationalComparison(node),
1899
+ isConditionPosition(node, context)
1900
+ ].every(Boolean);
1901
+ }
1902
+ function sufficientAdditions(binary) {
1903
+ return isRelationalOperator(binary.operator) ? SUFFICIENT_RELATIONAL_SETS[binary.operator].replacements.filter((operator) => !operators[binary.operator].some((replacement) => replacement === operator)).map((operator) => withOperator(binary, operator)) : NO_MUTANTS;
1935
1904
  }
1936
1905
  const logicalOperatorReplacements = Object.freeze({
1937
1906
  "&&": "||",
@@ -2214,6 +2183,27 @@ const optInMutators = Object.freeze({
2214
2183
  SynchronizationRemoval: synchronizationRemovalMutator,
2215
2184
  FinalizerEscape: finalizerEscapeMutator
2216
2185
  });
2186
+ const stockRegistry = {
2187
+ defaults: defaultMutators,
2188
+ optIn: optInMutators
2189
+ };
2190
+ const registryOfDataFirst = (catalogs, contributions) => {
2191
+ const implementations = new Map([
2192
+ ...Object.entries(defaultMutators),
2193
+ ...Object.entries(optInMutators),
2194
+ ...contributions.flatMap(({ entries }) => entries.map((entry) => [entry.name, entry.implementation]))
2195
+ ]);
2196
+ const entries = catalogs.flatMap((catalog) => catalog.entries);
2197
+ const entriesOfTier = (tier) => entries.filter((entry) => entry.tier === tier).flatMap((entry) => Option.match(Option.fromNullishOr(implementations.get(entry.name)), {
2198
+ onNone: () => [],
2199
+ onSome: (implementation) => [[entry.name, implementation]]
2200
+ }));
2201
+ return {
2202
+ defaults: Object.fromEntries(entriesOfTier("default")),
2203
+ optIn: Object.fromEntries(entriesOfTier("optIn"))
2204
+ };
2205
+ };
2206
+ const registryOf = dual((args) => args.length >= 2, registryOfDataFirst);
2217
2207
  /**
2218
2208
  * The entries a run applies, and the names its directives may reference.
2219
2209
  *
@@ -2237,7 +2227,7 @@ const selectMutators = dual((args) => args.length >= 2, selectMutatorsDataFirst)
2237
2227
  var ParseFailed = class extends S.TaggedError("@systemfsoftware/stryker-js-instrumenter/Parser.schema/ParseFailed")("ParseFailed", {
2238
2228
  fileName: S.String,
2239
2229
  message: S.String,
2240
- location: Position,
2230
+ location: Mutant.Position,
2241
2231
  cause: S.Defect()
2242
2232
  }) {
2243
2233
  get message() {
@@ -2248,7 +2238,7 @@ var ParseFailed = class extends S.TaggedError("@systemfsoftware/stryker-js-instr
2248
2238
  //#region src/Parser.service.ts
2249
2239
  const oxcModule = Effect.cached(Effect.promise(() => import("oxc-parser")));
2250
2240
  const loadOxc = Effect.flatMap(oxcModule, (load) => load);
2251
- const parseWithOxcDataFirst = Effect.fn("stryker.instrument.parser.parseWithOxc")(function* (text, fileName, lang) {
2241
+ const parseWithOxcDataFirst = Effect.fn(SpanTaxonomy.Spans.instrumentParserParseWithOxc.name)(function* (text, fileName, lang) {
2252
2242
  const result = (yield* loadOxc).parseSync(fileName, text, {
2253
2243
  lang,
2254
2244
  range: true
@@ -2275,7 +2265,7 @@ const oxcParseFailure = (errors, fileName, lineStarts) => Option.match(Option.fr
2275
2265
  });
2276
2266
  const oxcErrorLabelStart = (error) => Option.getOrElse(Option.map(Option.fromNullishOr(error.labels.at(0)), (label) => label.start), () => 0);
2277
2267
  const createParser = () => ({ parse: (code, fileName, scriptFormat) => Match.value(scriptFormat).pipe(Match.when("js", () => parseJS(code, fileName)), Match.when("ts", () => parseTS(code, fileName)), Match.when("tsx", () => parseTsx(code, fileName)), Match.exhaustive) });
2278
- const parseJSDataFirst = Effect.fn("stryker.instrument.parser.parseJS")(function* (text, fileName) {
2268
+ const parseJSDataFirst = Effect.fn(SpanTaxonomy.Spans.instrumentParserParseJS.name)(function* (text, fileName) {
2279
2269
  const parsed = yield* parseWithOxc(text, fileName, "js");
2280
2270
  return {
2281
2271
  originFileName: fileName,
@@ -2286,7 +2276,7 @@ const parseJSDataFirst = Effect.fn("stryker.instrument.parser.parseJS")(function
2286
2276
  };
2287
2277
  });
2288
2278
  const parseJS = dual((args) => args.length >= 2, parseJSDataFirst);
2289
- const parseTSDataFirst = Effect.fn("stryker.instrument.parser.parseTS")(function* (text, fileName) {
2279
+ const parseTSDataFirst = Effect.fn(SpanTaxonomy.Spans.instrumentParserParseTS.name)(function* (text, fileName) {
2290
2280
  const parsed = yield* parseWithOxc(text, fileName, "ts");
2291
2281
  return {
2292
2282
  originFileName: fileName,
@@ -2297,7 +2287,7 @@ const parseTSDataFirst = Effect.fn("stryker.instrument.parser.parseTS")(function
2297
2287
  };
2298
2288
  });
2299
2289
  const parseTS = dual((args) => args.length >= 2, parseTSDataFirst);
2300
- const parseTsxDataFirst = Effect.fn("stryker.instrument.parser.parseTsx")(function* (text, fileName) {
2290
+ const parseTsxDataFirst = Effect.fn(SpanTaxonomy.Spans.instrumentParserParseTsx.name)(function* (text, fileName) {
2301
2291
  const parsed = yield* parseWithOxc(text, fileName, "tsx");
2302
2292
  return {
2303
2293
  root: parsed.root,
@@ -2782,7 +2772,7 @@ const logicalExpressionText = (ctx, node, prec) => {
2782
2772
  };
2783
2773
  const conditionalExpressionText = (ctx, node, prec) => {
2784
2774
  const myPrec = PREC.Conditional;
2785
- const testStr = wrapIfNeeded(ctx, node.test, precOf(node.test), myPrec, false);
2775
+ const testStr = wrapIfNeeded(ctx, node.test, precOf(node.test), myPrec, CONDITIONAL_TEST_WRAPPED_KINDS[node.test.type] === true);
2786
2776
  const consStr = dispatchNode(ctx, node.consequent, PREC.Assignment);
2787
2777
  const altStr = dispatchNode(ctx, node.alternate, PREC.Assignment);
2788
2778
  return parenthesizedIf(myPrec < prec, `${testStr} ? ${consStr} : ${altStr}`);
@@ -3180,6 +3170,7 @@ const MEMBER_OBJECT_WRAPPED_KINDS = {
3180
3170
  AwaitExpression: true,
3181
3171
  YieldExpression: true
3182
3172
  };
3173
+ const CONDITIONAL_TEST_WRAPPED_KINDS = { ConditionalExpression: true };
3183
3174
  const CALLEE_WRAPPED_KINDS = {
3184
3175
  SequenceExpression: true,
3185
3176
  ConditionalExpression: true
@@ -3400,9 +3391,117 @@ const resolveFormat = Workflow.make({
3400
3391
  error: FormatOverrideUnclaimed,
3401
3392
  decide: (command) => Match.value(selectionFor(command)).pipe(Match.tag("ClaimSelected", ({ claim }) => Result.succeed(assignedFrom(claim, command))), Match.tag("PinUnclaimed", ({ formatId }) => Result.fail(overrideUnclaimedFrom(command, formatId))), Match.tag("ExtensionUnclaimed", () => Result.succeed(skippedFrom(command))), Match.exhaustive)
3402
3393
  });
3394
+ const AridRuleId = S.Literals([
3395
+ "arid-logging",
3396
+ "arid-telemetry",
3397
+ "arid-time",
3398
+ "arid-config-default",
3399
+ "arid-memoization"
3400
+ ]);
3401
+ const AridCalleeSchema = S.Struct({
3402
+ object: S.String,
3403
+ member: S.String
3404
+ });
3405
+ const AridFrameSchema = S.Struct({
3406
+ callee: S.Option(AridCalleeSchema),
3407
+ childIsArgument: S.Boolean
3408
+ });
3409
+ const NAMED_LOGGERS = ["Logger", "console"];
3410
+ const readsNamespace = (callee, object) => callee.object === object;
3411
+ const readsName = (callee, name) => callee.member === name;
3412
+ const readsMember = (callee, object, member) => [readsNamespace(callee, object), readsName(callee, member)].every(Boolean);
3413
+ const readsMemberPrefixed = (callee, object, prefix) => [readsNamespace(callee, object), callee.member.startsWith(prefix)].every(Boolean);
3414
+ const ARID_RULES_IN_PRECEDENCE_ORDER = [
3415
+ {
3416
+ ruleId: "arid-logging",
3417
+ matches: (callee) => readsMemberPrefixed(callee, "Effect", "log")
3418
+ },
3419
+ {
3420
+ ruleId: "arid-logging",
3421
+ matches: (callee) => NAMED_LOGGERS.includes(callee.object)
3422
+ },
3423
+ {
3424
+ ruleId: "arid-telemetry",
3425
+ matches: (callee) => readsMember(callee, "Effect", "withSpan")
3426
+ },
3427
+ {
3428
+ ruleId: "arid-telemetry",
3429
+ matches: (callee) => readsMemberPrefixed(callee, "Effect", "annotate")
3430
+ },
3431
+ {
3432
+ ruleId: "arid-telemetry",
3433
+ matches: (callee) => readsMember(callee, "Effect", "withLogSpan")
3434
+ },
3435
+ {
3436
+ ruleId: "arid-telemetry",
3437
+ matches: (callee) => readsNamespace(callee, "Metric")
3438
+ },
3439
+ {
3440
+ ruleId: "arid-time",
3441
+ matches: (callee) => readsMember(callee, "Effect", "sleep")
3442
+ },
3443
+ {
3444
+ ruleId: "arid-time",
3445
+ matches: (callee) => readsNamespace(callee, "Schedule")
3446
+ },
3447
+ {
3448
+ ruleId: "arid-time",
3449
+ matches: (callee) => readsNamespace(callee, "Duration")
3450
+ },
3451
+ {
3452
+ ruleId: "arid-time",
3453
+ matches: (callee) => readsMember(callee, "Date", "now")
3454
+ },
3455
+ {
3456
+ ruleId: "arid-config-default",
3457
+ matches: (callee) => readsMember(callee, "Config", "withDefault")
3458
+ },
3459
+ {
3460
+ ruleId: "arid-memoization",
3461
+ matches: (callee) => readsMember(callee, "Effect", "cached")
3462
+ },
3463
+ {
3464
+ ruleId: "arid-memoization",
3465
+ matches: (callee) => readsMember(callee, "Effect", "cachedWithTTL")
3466
+ }
3467
+ ];
3468
+ const ruleFor = (callee) => Option.map(Option.fromNullishOr(ARID_RULES_IN_PRECEDENCE_ORDER.find((rule) => rule.matches(callee))), (rule) => rule.ruleId);
3469
+ const calleeDetail = (callee) => `${callee.object}.${callee.member}`;
3470
+ var AridCodeCommand = class extends S.TaggedClass()("AridCodeCommand", {
3471
+ policy: Options.MutantSetPolicy,
3472
+ frames: S.Array(AridFrameSchema)
3473
+ }) {
3474
+ static [Workflow.InstrumentationBrand] = {};
3475
+ };
3476
+ const AridCodeDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js-instrumenter/AridCodeDecision");
3477
+ var AridSuppressed = class extends S.TaggedClass()("AridSuppressed", {
3478
+ ruleId: AridRuleId,
3479
+ detail: S.String
3480
+ }) {
3481
+ [AridCodeDecisionTypeId] = AridCodeDecisionTypeId;
3482
+ };
3483
+ var AridKept = class extends S.TaggedClass()("AridKept", {}) {
3484
+ [AridCodeDecisionTypeId] = AridCodeDecisionTypeId;
3485
+ };
3486
+ const matchOfFrame = (frame) => Option.flatMap(Option.filter(frame.callee, () => frame.childIsArgument), (callee) => Option.map(ruleFor(callee), (ruleId) => ({
3487
+ ruleId,
3488
+ callee
3489
+ })));
3490
+ const firstMatch = (frames) => Option.firstSomeOf(frames.map(matchOfFrame));
3491
+ const defaultPolicyDecision = (frames) => Match.value(firstMatch(frames)).pipe(Match.when(Option.isSome, (found) => AridSuppressed.make({
3492
+ ruleId: found.value.ruleId,
3493
+ detail: calleeDetail(found.value.callee)
3494
+ })), Match.when(Option.isNone, () => AridKept.make({})), Match.exhaustive);
3495
+ const decisionFor = (command) => Match.value(command.policy).pipe(Match.when("default", () => defaultPolicyDecision(command.frames)), Match.when("full", () => AridKept.make({})), Match.exhaustive);
3496
+ const aridCode = Workflow.make({
3497
+ command: AridCodeCommand,
3498
+ decision: S.Union([AridSuppressed, AridKept]),
3499
+ error: S.Never,
3500
+ decide: (command) => Result.succeed(decisionFor(command))
3501
+ });
3403
3502
  //#endregion
3404
3503
  //#region src/directives/decode-directive.workflow.ts
3405
- const DIRECTIVE_PATTERN = /^\s?Stryker (disable|restore)(?: (next-line))? ([a-zA-Z, ]+)(?::(.+)?)?/;
3504
+ const DIRECTIVE_PATTERN = /^\s?Stryker (disable|restore)(?: (next-line))? ([a-zA-Z0-9,/ -]+)(?::(.+)?)?/;
3406
3505
  const DEFAULT_REASON = "Ignored using a comment";
3407
3506
  const NEXT_LINE$1 = "next-line";
3408
3507
  var DecodeDirectiveCommand = class extends S.TaggedClass()("DecodeDirectiveCommand", { commentText: S.String }) {
@@ -3456,6 +3555,23 @@ const foldRule = Workflow.make({
3456
3555
  decide: (command) => Match.value(command.directive.directive.action).pipe(Match.when("restore", () => Result.succeed(RestoreFolded.make({ rule: [...command.rule, command.directive] }))), Match.when("disable", () => Result.succeed(DisableFolded.make({ rule: [...command.rule, command.directive] }))), Match.exhaustive)
3457
3556
  });
3458
3557
  //#endregion
3558
+ //#region src/InstrumentContext.schema.ts
3559
+ var InstrumenterContext = class extends S.Class("InstrumenterContext")({
3560
+ activeMutant: S.optional(S.String),
3561
+ currentTestId: S.optional(S.String),
3562
+ mutantCoverage: S.optional(Mutant.MutantCoverageSchema),
3563
+ hitCount: S.optional(Mutant.HitCount),
3564
+ hitLimit: S.optional(Mutant.HitCount)
3565
+ }) {
3566
+ static NAMESPACE = "__stryker__";
3567
+ static MUTATION_COVERAGE_OBJECT = "mutantCoverage";
3568
+ static ACTIVE_MUTANT = "activeMutant";
3569
+ static CURRENT_TEST_ID = "currentTestId";
3570
+ static HIT_COUNT = "hitCount";
3571
+ static HIT_LIMIT = "hitLimit";
3572
+ static ACTIVE_MUTANT_ENV_VARIABLE = "__STRYKER_ACTIVE_MUTANT__";
3573
+ };
3574
+ //#endregion
3459
3575
  //#region src/InstrumentHeader.ts
3460
3576
  const hasLeadingComments$1 = (value) => Predicate.hasProperty(value, "leadingComments") && Array.isArray(value.leadingComments);
3461
3577
  const STRYKER_NAMESPACE_HELPER = "stryNS_9fa48";
@@ -3542,6 +3658,18 @@ function commentedHeader(firstHeader, leadingComments, rest) {
3542
3658
  return [cloned, ...rest];
3543
3659
  }
3544
3660
  //#endregion
3661
+ //#region src/MutantIdentity.ts
3662
+ const MUTANT_ID_HEX_LENGTH = 16;
3663
+ const lengthPrefixed = (value) => `${value.length}:${value}`;
3664
+ const tupleFields = (tuple) => [
3665
+ tuple.fileName,
3666
+ tuple.mutatorName,
3667
+ tuple.originalCode,
3668
+ tuple.replacementCode
3669
+ ];
3670
+ const mutantTupleKey = (tuple) => tupleFields(tuple).map(lengthPrefixed).join("");
3671
+ const mutantIdOf = (identity) => Mutant.MutantId.make(bytesToHex(sha256(utf8ToBytes([...tupleFields(identity), `${identity.ordinal}`].map(lengthPrefixed).join("")))).slice(0, MUTANT_ID_HEX_LENGTH));
3672
+ //#endregion
3545
3673
  //#region src/place-mutants.workflow.ts
3546
3674
  const PlacementFactsSchema = S.Struct({
3547
3675
  isExpression: S.Boolean,
@@ -3555,7 +3683,7 @@ const ReplacementFactsSchema = S.Struct({
3555
3683
  isSwitchCase: S.Boolean
3556
3684
  });
3557
3685
  const PlacedMutantSchema = S.Struct({
3558
- id: MutantId,
3686
+ id: Mutant.MutantId,
3559
3687
  mutatorName: MutatorNameSchema,
3560
3688
  replacement: ReplacementFactsSchema
3561
3689
  });
@@ -3569,19 +3697,19 @@ var PlaceMutantsCommand = class extends S.TaggedClass()("PlaceMutantsCommand", {
3569
3697
  const PlacementDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js-instrumenter/PlacementDecision");
3570
3698
  var ExpressionSite = class extends S.TaggedClass()("ExpressionSite", {
3571
3699
  fileName: S.String,
3572
- mutantIds: S.Array(MutantId)
3700
+ mutantIds: S.Array(Mutant.MutantId)
3573
3701
  }) {
3574
3702
  [PlacementDecisionTypeId] = PlacementDecisionTypeId;
3575
3703
  };
3576
3704
  var StatementSite = class extends S.TaggedClass()("StatementSite", {
3577
3705
  fileName: S.String,
3578
- mutantIds: S.Array(MutantId)
3706
+ mutantIds: S.Array(Mutant.MutantId)
3579
3707
  }) {
3580
3708
  [PlacementDecisionTypeId] = PlacementDecisionTypeId;
3581
3709
  };
3582
3710
  var SwitchCaseSite = class extends S.TaggedClass()("SwitchCaseSite", {
3583
3711
  fileName: S.String,
3584
- mutantIds: S.Array(MutantId)
3712
+ mutantIds: S.Array(Mutant.MutantId)
3585
3713
  }) {
3586
3714
  [PlacementDecisionTypeId] = PlacementDecisionTypeId;
3587
3715
  };
@@ -3593,7 +3721,7 @@ const ExpectedKindSchema = S.Literals([
3593
3721
  var MutantKindMismatch = class extends S.TaggedError()("MutantKindMismatch", {
3594
3722
  fileName: S.String,
3595
3723
  placer: PlacerNameSchema,
3596
- mutantId: MutantId,
3724
+ mutantId: Mutant.MutantId,
3597
3725
  mutatorName: MutatorNameSchema,
3598
3726
  expected: ExpectedKindSchema
3599
3727
  }) {
@@ -3654,53 +3782,129 @@ const placeMutants = Workflow.make({
3654
3782
  })
3655
3783
  });
3656
3784
  //#endregion
3785
+ //#region src/mutant-set-policy.workflow.ts
3786
+ const MutantSetFactsSchema = S.Struct({
3787
+ originalCode: S.String,
3788
+ replacementCode: S.String,
3789
+ relationalSufficient: S.Boolean
3790
+ });
3791
+ const MutantSetRuleId = S.Literals([
3792
+ "redundant-relational",
3793
+ "equivalent-to-original",
3794
+ "duplicate-at-site"
3795
+ ]);
3796
+ var MutantSetPolicyCommand = class extends S.TaggedClass()("MutantSetPolicyCommand", {
3797
+ policy: Options.MutantSetPolicy,
3798
+ candidates: S.Array(MutantSetFactsSchema)
3799
+ }) {
3800
+ static [Workflow.InstrumentationBrand] = {};
3801
+ };
3802
+ const MutantSetOutcomeTypeId = Symbol.for("@systemfsoftware/stryker-js-instrumenter/MutantSetOutcome");
3803
+ var MutantSuppressed = class extends S.TaggedClass()("MutantSuppressed", {
3804
+ ruleId: MutantSetRuleId,
3805
+ detail: S.String
3806
+ }) {
3807
+ [MutantSetOutcomeTypeId] = MutantSetOutcomeTypeId;
3808
+ };
3809
+ var MutantKept = class extends S.TaggedClass()("MutantKept", {}) {
3810
+ [MutantSetOutcomeTypeId] = MutantSetOutcomeTypeId;
3811
+ };
3812
+ const MutantSetOutcome = S.Union([MutantSuppressed, MutantKept]);
3813
+ const MutantSetDecision = S.Array(MutantSetOutcome);
3814
+ const canonicalForm = (text) => stripOuterParentheses(text.trim());
3815
+ const stripOuterParentheses = (text) => Match.value(wrapsWhole(text)).pipe(Match.when(true, () => stripOuterParentheses(text.slice(1, -1).trim())), Match.orElse(() => text));
3816
+ const parenDepthDelta = (character) => Match.value(character).pipe(Match.when("(", () => 1), Match.when(")", () => -1), Match.orElse(() => 0));
3817
+ const depthsAfter = (text) => text.split("").reduce((depths, character) => [...depths, Option.getOrElse(Arr.last(depths), () => 0) + parenDepthDelta(character)], []);
3818
+ const wrapsWhole = (text) => {
3819
+ const depths = depthsAfter(text);
3820
+ return [
3821
+ text.startsWith("("),
3822
+ text.endsWith(")"),
3823
+ depths.length > 0,
3824
+ depths.slice(0, -1).every((depth) => depth >= 1),
3825
+ Option.getOrElse(Arr.last(depths), () => 0) === 0
3826
+ ].every(Boolean);
3827
+ };
3828
+ const defaultOnly = (policy, value) => Match.value(policy).pipe(Match.when("full", () => Option.none()), Match.when("default", value), Match.exhaustive);
3829
+ const printedAs = (code) => canonicalForm(code);
3830
+ const printsSomething = (code) => printedAs(code).length > 0;
3831
+ const samePrintedCode = (left, right) => [printsSomething(left), printedAs(left) === printedAs(right)].every(Boolean);
3832
+ const alreadyPrinted = (code, earlier) => [printsSomething(code), earlier.includes(printedAs(code))].every(Boolean);
3833
+ const relationalSuppression = (facts, policy) => defaultOnly(policy, () => Option.map(Option.filter(Option.some(facts), (candidate) => candidate.relationalSufficient === false), () => MutantSuppressed.make({
3834
+ ruleId: "redundant-relational",
3835
+ detail: `${printedAs(facts.replacementCode)} is not in the sufficient set for ${printedAs(facts.originalCode)}`
3836
+ })));
3837
+ const equivalentSuppression = (facts, policy) => defaultOnly(policy, () => Option.map(Option.filter(Option.some(facts), (candidate) => samePrintedCode(candidate.replacementCode, candidate.originalCode)), () => MutantSuppressed.make({
3838
+ ruleId: "equivalent-to-original",
3839
+ detail: `${printedAs(facts.replacementCode)} is the original code`
3840
+ })));
3841
+ const duplicateSuppression = (facts, earlier, policy) => defaultOnly(policy, () => Option.map(Option.filter(Option.some(facts), (candidate) => alreadyPrinted(candidate.replacementCode, earlier)), () => MutantSuppressed.make({
3842
+ ruleId: "duplicate-at-site",
3843
+ detail: `${printedAs(facts.replacementCode)} is already planted at this site`
3844
+ })));
3845
+ const outcomeOf = (facts, earlier, policy) => Option.getOrElse(Option.orElse(relationalSuppression(facts, policy), () => Option.orElse(equivalentSuppression(facts, policy), () => duplicateSuppression(facts, earlier, policy))), () => MutantKept.make({}));
3846
+ const foldCandidate = (state, facts, policy) => ({
3847
+ outcomes: [...state.outcomes, outcomeOf(facts, state.earlier, policy)],
3848
+ earlier: [...state.earlier, canonicalForm(facts.replacementCode)]
3849
+ });
3850
+ const outcomesFor = (command) => command.candidates.reduce((state, facts) => foldCandidate(state, facts, command.policy), {
3851
+ outcomes: [],
3852
+ earlier: []
3853
+ }).outcomes;
3854
+ const mutantSetPolicy = Workflow.make({
3855
+ command: MutantSetPolicyCommand,
3856
+ decision: MutantSetDecision,
3857
+ error: S.Never,
3858
+ decide: (command) => Result.succeed([...outcomesFor(command)])
3859
+ });
3860
+ //#endregion
3657
3861
  //#region src/plan-mutants.workflow.ts
3658
3862
  const WILDCARD = "all";
3659
3863
  const NEXT_LINE = "next-line";
3864
+ const RULE_SEPARATOR = ": ";
3865
+ const policyWorkflow = mutantSetPolicy;
3866
+ const policyCommand = MutantSetPolicyCommand;
3867
+ const keptOutcome = MutantKept;
3868
+ const ignoreReasonFor = (ruleId, detail) => `${ruleId}${RULE_SEPARATOR}${detail}`;
3660
3869
  const MutantCandidateSchema = S.Struct({
3870
+ id: Mutant.MutantId,
3661
3871
  mutatorName: MutatorNameSchema,
3662
3872
  replacementCode: S.String,
3663
- location: S.optional(Location),
3664
- ignorerReason: S.optional(S.String)
3873
+ location: S.optional(Mutant.Location),
3874
+ ignorerReason: S.optional(S.String),
3875
+ mutantSet: MutantSetFactsSchema
3665
3876
  });
3666
3877
  const PlannedMutantSchema = S.Struct({
3667
- id: MutantId,
3878
+ id: Mutant.MutantId,
3668
3879
  mutatorName: MutatorNameSchema,
3669
3880
  replacementCode: S.String,
3670
- location: Location,
3881
+ location: Mutant.Location,
3671
3882
  ignoreReason: S.optional(S.String)
3672
3883
  });
3673
- const MutantCounterSchema = S.Int.pipe(S.check(S.isBetween({
3674
- minimum: 0,
3675
- maximum: 1e6
3676
- })));
3677
- const MutantCountSchema = S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(0)));
3678
3884
  var PlanMutantsCommand = class extends S.TaggedClass()("PlanMutantsCommand", {
3679
3885
  fileName: S.String,
3680
- firstIndex: MutantCounterSchema,
3681
3886
  offset: ScriptOrigin,
3682
- line: Line,
3887
+ line: Mutant.Line,
3683
3888
  mutatorNames: S.Array(MutatorNameSchema),
3684
3889
  excludedMutations: S.Array(MutatorNameSchema),
3685
3890
  rule: S.Array(LocatedDirectiveSchema),
3686
3891
  directives: S.Array(LocatedDirectiveSchema),
3687
- candidates: S.Array(MutantCandidateSchema)
3892
+ candidates: S.Array(MutantCandidateSchema),
3893
+ mutantSetPolicy: Options.MutantSetPolicy
3688
3894
  }) {
3689
3895
  static [Workflow.InstrumentationBrand] = {};
3690
3896
  };
3691
3897
  const MutantPlanTypeId = Symbol.for("@systemfsoftware/stryker-js-instrumenter/MutantPlan");
3692
3898
  var MutantsPlanned = class extends S.TaggedClass()("MutantsPlanned", {
3693
3899
  mutants: S.Array(PlannedMutantSchema),
3694
- placeable: S.Array(PlannedMutantSchema),
3695
- warnings: S.Array(S.String),
3696
- nextIndex: MutantCountSchema
3900
+ placeableIds: S.Array(Mutant.MutantId),
3901
+ warnings: S.Array(S.String)
3697
3902
  }) {
3698
3903
  [MutantPlanTypeId] = MutantPlanTypeId;
3699
3904
  };
3700
3905
  var MutantsFullyIgnored = class extends S.TaggedClass()("MutantsFullyIgnored", {
3701
3906
  mutants: S.Array(PlannedMutantSchema),
3702
- warnings: S.Array(S.String),
3703
- nextIndex: MutantCountSchema
3907
+ warnings: S.Array(S.String)
3704
3908
  }) {
3705
3909
  [MutantPlanTypeId] = MutantPlanTypeId;
3706
3910
  };
@@ -3715,9 +3919,19 @@ var MutantWithoutLocation = class extends S.TaggedError()("MutantWithoutLocation
3715
3919
  const reachedLine = (located, line) => Match.value(located.directive.scope).pipe(Match.when(NEXT_LINE, () => located.governedLine === line), Match.when("block", () => true), Match.exhaustive);
3716
3920
  const namesMutator = (located, lowerMutatorName) => located.directive.mutatorNames.some((name) => [name === WILDCARD, name.toLowerCase() === lowerMutatorName].some(Boolean));
3717
3921
  const lastReachingDirective = (rule, lowerMutatorName, line) => Option.fromNullishOr(rule.findLast((located) => [reachedLine(located, line), namesMutator(located, lowerMutatorName)].every(Boolean)));
3718
- const directiveReason = (rule, mutatorName, line) => Option.flatMap(lastReachingDirective(rule, mutatorName.toLowerCase(), line), (located) => Match.value(located.directive.action).pipe(Match.when("disable", () => Option.some(located.directive.reason)), Match.when("restore", () => Option.none()), Match.exhaustive));
3719
- const exclusionReason = (excludedMutations, mutatorName) => Match.value(excludedMutations.includes(mutatorName)).pipe(Match.when(true, () => Option.some(`Ignored because of excluded mutation "${mutatorName}"`)), Match.when(false, () => Option.none()), Match.exhaustive);
3720
- const ignoreReasonOf = (command, candidate) => Option.getOrUndefined(Option.orElse(Option.orElse(directiveReason(command.rule, candidate.mutatorName, command.line), () => exclusionReason(command.excludedMutations, candidate.mutatorName)), () => Option.fromNullishOr(candidate.ignorerReason)));
3922
+ const directiveReason = (rule, mutatorName, line) => Option.flatMap(lastReachingDirective(rule, mutatorName.toLowerCase(), line), (located) => Match.value(located.directive.action).pipe(Match.when("disable", () => Option.some(ignoreReasonFor("directive", located.directive.reason))), Match.when("restore", () => Option.none()), Match.exhaustive));
3923
+ const exclusionReason = (excludedMutations, mutatorName) => Match.value(excludedMutations.includes(mutatorName)).pipe(Match.when(true, () => Option.some(ignoreReasonFor("excluded-mutator", `Ignored because of excluded mutation "${mutatorName}"`))), Match.when(false, () => Option.none()), Match.exhaustive);
3924
+ const ignorerReason = (candidate) => Option.map(Option.fromNullishOr(candidate.ignorerReason), (reason) => ignoreReasonFor("ignorer", reason));
3925
+ const ignoreReasonOf = (command, candidate, policyReason) => Option.getOrUndefined(Option.orElse(Option.orElse(Option.orElse(directiveReason(command.rule, candidate.mutatorName, command.line), () => exclusionReason(command.excludedMutations, candidate.mutatorName)), () => ignorerReason(candidate)), () => Option.fromNullishOr(policyReason)));
3926
+ const policyOutcomes = (command) => Result.match(policyWorkflow(policyCommand.make({
3927
+ policy: command.mutantSetPolicy,
3928
+ candidates: command.candidates.map((candidate) => candidate.mutantSet)
3929
+ })), {
3930
+ onFailure: () => command.candidates.map(() => keptOutcome.make({})),
3931
+ onSuccess: (outcomes) => outcomes
3932
+ });
3933
+ const policyReasonOf = (outcome) => Match.value(outcome).pipe(Match.withReturnType(), Match.tag("MutantSuppressed", (suppressed) => ignoreReasonFor(suppressed.ruleId, suppressed.detail)), Match.tag("MutantKept", () => void 0), Match.exhaustive);
3934
+ const policyReasonsAt = (command) => policyOutcomes(command).map(policyReasonOf);
3721
3935
  const unusedDirectives = (directives, mutatorNames) => directives.flatMap((located) => located.directive.mutatorNames.filter((mutatorName) => mutatorName !== WILDCARD).filter((mutatorName) => !mutatorNames.includes(mutatorName.toLowerCase())).map((mutatorName) => ({
3722
3936
  directive: located.directive,
3723
3937
  at: located.at,
@@ -3743,30 +3957,31 @@ const shiftedLocation = (location, offset) => ({
3743
3957
  start: shiftedPosition(location.start, offset),
3744
3958
  end: shiftedPosition(location.end, offset)
3745
3959
  });
3746
- const plannedMutant = (command, candidate, index) => Option.match(Option.fromNullishOr(candidate.location), {
3960
+ const plannedMutant = (command, candidate, policyReason) => Option.match(Option.fromNullishOr(candidate.location), {
3747
3961
  onNone: () => Result.fail(MutantWithoutLocation.make({
3748
3962
  fileName: command.fileName,
3749
3963
  mutatorName: candidate.mutatorName
3750
3964
  })),
3751
3965
  onSome: (location) => Result.succeed({
3752
- id: PlannedMutantSchema.fields.id.make(`${command.firstIndex + index}`),
3966
+ id: candidate.id,
3753
3967
  mutatorName: candidate.mutatorName,
3754
3968
  replacementCode: candidate.replacementCode,
3755
3969
  location: shiftedLocation(location, command.offset),
3756
- ignoreReason: ignoreReasonOf(command, candidate)
3970
+ ignoreReason: ignoreReasonOf(command, candidate, policyReason)
3757
3971
  })
3758
3972
  });
3759
- const plannedMutants = (command) => command.candidates.reduce((accumulated, candidate, index) => Result.flatMap(accumulated, (mutants) => Result.map(plannedMutant(command, candidate, index), (mutant) => [...mutants, mutant])), Result.succeed([]));
3973
+ const plannedMutants = (command) => {
3974
+ const policyReasons = policyReasonsAt(command);
3975
+ return command.candidates.reduce((accumulated, candidate, index) => Result.flatMap(accumulated, (mutants) => Result.map(plannedMutant(command, candidate, policyReasons[index]), (mutant) => [...mutants, mutant])), Result.succeed([]));
3976
+ };
3760
3977
  const withoutReason = (mutant) => mutant.ignoreReason === void 0;
3761
3978
  const planOf = (command, mutants) => Match.value(mutants.some(withoutReason)).pipe(Match.when(true, () => MutantsPlanned.make({
3762
3979
  mutants: [...mutants],
3763
- placeable: mutants.filter(withoutReason),
3764
- warnings: warningsOf(command),
3765
- nextIndex: command.firstIndex + mutants.length
3980
+ placeableIds: mutants.filter(withoutReason).map((mutant) => mutant.id),
3981
+ warnings: warningsOf(command)
3766
3982
  })), Match.when(false, () => MutantsFullyIgnored.make({
3767
3983
  mutants: [...mutants],
3768
- warnings: warningsOf(command),
3769
- nextIndex: command.firstIndex + mutants.length
3984
+ warnings: warningsOf(command)
3770
3985
  })), Match.exhaustive);
3771
3986
  const planMutants = Workflow.make({
3772
3987
  command: PlanMutantsCommand,
@@ -3790,15 +4005,15 @@ const traversalFailure = (cause) => InstrumentError.make({
3790
4005
  message: cause instanceof Error ? cause.message : errorTextOf$1(cause),
3791
4006
  cause
3792
4007
  });
3793
- const DEFAULT_MUTATOR_REGISTRY = {
3794
- defaults: defaultMutators,
3795
- optIn: optInMutators
3796
- };
3797
4008
  const createMutantCollector = () => {
3798
4009
  const mutants = [];
4010
+ const occurrences = /* @__PURE__ */ new Map();
3799
4011
  return {
3800
- get nextIndex() {
3801
- return mutants.length;
4012
+ ordinalOf: (tuple) => {
4013
+ const key = mutantTupleKey(tuple);
4014
+ const ordinal = occurrences.get(key) ?? 0;
4015
+ occurrences.set(key, ordinal + 1);
4016
+ return ordinal;
3802
4017
  },
3803
4018
  append: (added) => {
3804
4019
  mutants.push(...added);
@@ -4101,9 +4316,8 @@ const MUTATION_OFFSET = {
4101
4316
  line: 1,
4102
4317
  columnShift: 0
4103
4318
  };
4104
- const initialFoldState = (firstIndex) => ({
4319
+ const initialFoldState = () => ({
4105
4320
  directiveRule: [],
4106
- nextIndex: firstIndex,
4107
4321
  mutants: [],
4108
4322
  warnings: [],
4109
4323
  claims: [],
@@ -4139,29 +4353,77 @@ const replacementRecord = (mutant, applied) => {
4139
4353
  }
4140
4354
  };
4141
4355
  };
4142
- const plannedWithNodes = (candidates, planned, fileName) => candidates.flatMap((candidate, index) => Option.match(Option.fromNullishOr(planned[index]), {
4356
+ const plannedWithNodes = (candidates, planned, fileName, includes) => planned.flatMap((mutant, index) => Option.match(Option.fromNullishOr(candidates[index]), {
4143
4357
  onNone: () => [],
4144
- onSome: (mutant) => [createMutant(mutant, fileName, candidate.node, candidate.replacement)]
4358
+ onSome: (candidate) => includes(mutant) ? [createMutant(mutant, fileName, candidate.node, candidate.replacement)] : []
4145
4359
  }));
4146
4360
  const ignorersReasonFor = (node, ancestors, ignorers) => ignorers.reduce((reason, ignorer) => Option.orElse(reason, () => Option.fromNullishOr(ignorer.shouldIgnore(node, ancestors))), Option.none());
4361
+ const hasKey = (node, key) => key in node;
4362
+ const readKey = (node, key) => hasKey(node, key) ? node[key] : void 0;
4363
+ const isMemberExpressionValue = (node) => nodeType(node) === "MemberExpression";
4364
+ const isIdentifierValue = (value) => value !== void 0 && readKey(value, "type") === "Identifier";
4365
+ const hasIdentifierParts = (member) => [isIdentifierValue(readKey(member, "property")), isIdentifierValue(readKey(member, "object"))].every(Boolean);
4366
+ const isMemberOfIdentifierParts = (node) => isMemberExpressionValue(node) && hasIdentifierParts(node);
4367
+ function isNamedCallee(node) {
4368
+ return isMemberOfIdentifierParts(node) && readKey(node, "computed") === false;
4369
+ }
4370
+ const aridCalleeOf = (callee) => Match.value(callee).pipe(Match.when(isNamedCallee, (named) => Option.some({
4371
+ object: named.object.name,
4372
+ member: named.property.name
4373
+ })), Match.orElse(() => Option.none()));
4374
+ const aridFrameFor = (child, ancestor) => ancestor.type === "CallExpression" ? Option.some({
4375
+ callee: aridCalleeOf(ancestor.callee),
4376
+ childIsArgument: ancestor.arguments.some((argument) => argument === child)
4377
+ }) : Option.none();
4378
+ const aridFramesOf = (frame) => framesUpward(frame).flatMap((current) => Option.match(Option.fromNullishOr(current.parent), {
4379
+ onNone: () => [],
4380
+ onSome: (parent) => Option.toArray(aridFrameFor(current.node, parent.node))
4381
+ }));
4382
+ const aridStatusReason = (decision) => Match.value(decision).pipe(Match.tag("AridSuppressed", (suppressed) => Option.some(`${suppressed.ruleId}: ${suppressed.detail}`)), Match.tag("AridKept", () => Option.none()), Match.exhaustive);
4383
+ const aridReasonOf = (frame, policy) => Match.value(aridCode(AridCodeCommand.make({
4384
+ policy,
4385
+ frames: [...aridFramesOf(frame)]
4386
+ }))).pipe(Match.when(Result.isSuccess, (decided) => aridStatusReason(decided.success)), Match.orElse(() => Option.none()));
4147
4387
  const mutablesFor = (frame, location, context) => {
4148
4388
  const ancestors = ancestorsOfFrame(frame);
4149
- const mutatorContext = toMutatorContext(ancestors);
4389
+ const mutatorContext = toMutatorContext(ancestors, context.mutantSetPolicy);
4150
4390
  const replacements = context.mutatorEntries.flatMap(([mutatorName, mutate]) => [...mutate(frame.node, mutatorContext)].map((replacement) => ({
4151
4391
  mutatorName,
4152
4392
  replacement
4153
4393
  })));
4154
4394
  const ignorerReason = replacements.length === 0 ? void 0 : Option.getOrUndefined(ignorersReasonFor(frame.node, ancestors, context.ignorers));
4155
- return replacements.map(({ mutatorName, replacement }) => ({
4156
- node: frame.node,
4157
- replacement,
4158
- data: {
4395
+ const aridReason = aridReasonOf(frame, context.mutantSetPolicy);
4396
+ const originalCode = printNode(frame.node);
4397
+ const relationalSite = relationalSiteFacts(frame.node, mutatorContext);
4398
+ return replacements.map(({ mutatorName, replacement }) => {
4399
+ const replacementCode = printNode(replacement);
4400
+ const tuple = {
4401
+ fileName: context.fileName,
4159
4402
  mutatorName,
4160
- replacementCode: printNode(replacement),
4161
- location,
4162
- ignorerReason
4163
- }
4164
- }));
4403
+ originalCode,
4404
+ replacementCode
4405
+ };
4406
+ return {
4407
+ node: frame.node,
4408
+ replacement,
4409
+ aridReason,
4410
+ data: {
4411
+ id: mutantIdOf({
4412
+ ...tuple,
4413
+ ordinal: context.ordinalOf(tuple)
4414
+ }),
4415
+ mutatorName,
4416
+ replacementCode,
4417
+ location,
4418
+ ignorerReason,
4419
+ mutantSet: {
4420
+ originalCode,
4421
+ replacementCode,
4422
+ relationalSufficient: relationalSufficientReplacement(relationalSite, replacement)
4423
+ }
4424
+ }
4425
+ };
4426
+ });
4165
4427
  };
4166
4428
  const mutateRangesOf = (mutateDescription) => Option.filter(Option.some(mutateDescription), isMutateRangeList);
4167
4429
  const isOutsideMutateRanges = (location, mutateDescription) => Option.exists(mutateRangesOf(mutateDescription), (ranges) => ranges.every((range) => !locationOverlaps(range, location)));
@@ -4199,30 +4461,43 @@ const attachPlaceable = (mutants, frame, state, context) => Match.value(mutants.
4199
4461
  })),
4200
4462
  onSome: (target) => applyToClaim(mutants, target, state, context)
4201
4463
  })), Match.exhaustive);
4464
+ const aridReasonAt = (candidates, index) => Option.flatMap(Option.fromNullishOr(candidates[index]), (candidate) => candidate.aridReason);
4465
+ const withAridReason = (mutant, reason) => Option.match(reason, {
4466
+ onNone: () => mutant,
4467
+ onSome: (text) => mutant.ignoreReason === void 0 ? {
4468
+ ...mutant,
4469
+ ignoreReason: text
4470
+ } : mutant
4471
+ });
4472
+ const withAridReasons = (candidates, planned) => planned.map((mutant, index) => withAridReason(mutant, aridReasonAt(candidates, index)));
4202
4473
  const collectPlan = (frame, candidates, plan, state, context) => {
4203
- const collected = plannedWithNodes(candidates, plan.mutants, context.fileName);
4474
+ const planned = withAridReasons(candidates, plan.mutants);
4475
+ const collected = plannedWithNodes(candidates, planned, context.fileName, () => true);
4204
4476
  const nextState = {
4205
4477
  ...state,
4206
4478
  mutants: [...state.mutants, ...collected],
4207
- warnings: [...state.warnings, ...plan.warnings],
4208
- nextIndex: plan.nextIndex
4479
+ warnings: [...state.warnings, ...plan.warnings]
4209
4480
  };
4210
- return Match.value(plan).pipe(Match.tag("MutantsPlanned", (planned) => attachPlaceable(plannedWithNodes(candidates, planned.placeable, context.fileName), frame, {
4211
- ...nextState,
4212
- hasLiveMutants: true
4213
- }, context)), Match.orElse(() => Result.succeed(nextState)));
4481
+ return Match.value(plan).pipe(Match.tag("MutantsPlanned", (plannedPlan) => {
4482
+ const placeable = new Set(plannedPlan.placeableIds);
4483
+ const live = plannedWithNodes(candidates, planned, context.fileName, (mutant) => placeable.has(mutant.id) && mutant.ignoreReason === void 0);
4484
+ return attachPlaceable(live, frame, {
4485
+ ...nextState,
4486
+ hasLiveMutants: nextState.hasLiveMutants || live.length > 0
4487
+ }, context);
4488
+ }), Match.tag("MutantsFullyIgnored", () => Result.succeed(nextState)), Match.exhaustive);
4214
4489
  };
4215
4490
  const planMutantsAt = (frame, candidates, directives, location, state, context) => {
4216
4491
  const plan = planMutants(PlanMutantsCommand.make({
4217
4492
  fileName: context.fileName,
4218
- firstIndex: state.nextIndex,
4219
4493
  offset: context.offset,
4220
4494
  line: location.start.line,
4221
4495
  mutatorNames: [...context.allMutatorNames],
4222
4496
  excludedMutations: [...context.excludedMutations],
4223
4497
  rule: [...state.directiveRule],
4224
4498
  directives: [...directives],
4225
- candidates: candidates.map((candidate) => candidate.data)
4499
+ candidates: candidates.map((candidate) => candidate.data),
4500
+ mutantSetPolicy: context.mutantSetPolicy
4226
4501
  }));
4227
4502
  return Match.value(plan).pipe(Match.when(Result.isFailure, (failed) => Result.fail(failed.failure)), Match.orElse((succeeded) => collectPlan(frame, candidates, succeeded.success, state, context)));
4228
4503
  };
@@ -4285,10 +4560,10 @@ const foldPlacements = (frame, state, context) => Result.flatMap(locationOfNode(
4285
4560
  };
4286
4561
  return Match.value(shouldSkipNode(frame, location, context.mutateDescription)).pipe(Match.when(true, () => Result.succeed(ruled)), Match.when(false, () => visitFrame(frame, directives, location, ruled, context)), Match.exhaustive);
4287
4562
  });
4288
- const planInstrumentation = (root, firstIndex, context) => Result.map(foldPlacements({
4563
+ const planInstrumentation = (root, context) => Result.map(foldPlacements({
4289
4564
  node: root,
4290
4565
  parent: null
4291
- }, initialFoldState(firstIndex), context), (state) => ({
4566
+ }, initialFoldState(), context), (state) => ({
4292
4567
  mutants: state.mutants,
4293
4568
  placements: state.placements,
4294
4569
  warnings: state.warnings,
@@ -4341,23 +4616,24 @@ const refusalError = (refusal) => Match.value(refusal).pipe(Match.tag("NodeWitho
4341
4616
  message: `Mutant without a source location: ${failed.mutatorName} in ${failed.fileName}`,
4342
4617
  cause: void 0
4343
4618
  })), Match.exhaustive);
4344
- const transformScriptDataFirst = Effect.fn("stryker.instrument.transform.script")(function* ({ root, originFileName, rawContent, offset, comments }, mutantCollector, { options, mutateDescription, basePath }) {
4619
+ const transformScriptDataFirst = Effect.fn(SpanTaxonomy.Spans.instrumentTransformScript.name)(function* ({ root, originFileName, rawContent, offset, comments }, mutantCollector, { options, mutateDescription, basePath }) {
4345
4620
  const lineStarts = lineStartsOf(rawContent);
4346
4621
  attachComments(make(root), comments, lineStarts);
4347
- const selection = selectMutators(DEFAULT_MUTATOR_REGISTRY, options.optInMutations);
4348
4622
  const context = {
4349
4623
  fileName: originFileName,
4350
4624
  lineStarts,
4351
4625
  mutateDescription,
4352
4626
  offset: offset ?? MUTATION_OFFSET,
4353
4627
  basePath,
4354
- mutatorEntries: selection.active,
4355
- allMutatorNames: selection.known.map((name) => name.toLowerCase()),
4628
+ mutatorEntries: options.mutators.active,
4629
+ allMutatorNames: options.mutators.known.map((name) => name.toLowerCase()),
4356
4630
  excludedMutations: options.excludedMutations,
4357
- ignorers: options.ignorers
4631
+ mutantSetPolicy: options.mutantSetPolicy,
4632
+ ignorers: options.ignorers,
4633
+ ordinalOf: mutantCollector.ordinalOf
4358
4634
  };
4359
4635
  const planned = yield* Effect.try({
4360
- try: () => planInstrumentation(root, mutantCollector.nextIndex, context),
4636
+ try: () => planInstrumentation(root, context),
4361
4637
  catch: traversalFailure
4362
4638
  });
4363
4639
  const plan = yield* Match.value(planned).pipe(Match.when(Result.isFailure, (refused) => {
@@ -4370,11 +4646,12 @@ const transformScriptDataFirst = Effect.fn("stryker.instrument.transform.script"
4370
4646
  return plan.warnings;
4371
4647
  });
4372
4648
  const transformScript = dual((args) => args.length >= 3, transformScriptDataFirst);
4373
- function toMutatorContext(ancestors) {
4649
+ function toMutatorContext(ancestors, mutantSetPolicy) {
4374
4650
  return {
4375
4651
  parent: ancestors[0],
4376
4652
  grandParent: ancestors[1],
4377
- ancestors: [...ancestors]
4653
+ ancestors: [...ancestors],
4654
+ mutantSetPolicy
4378
4655
  };
4379
4656
  }
4380
4657
  const toError = (value) => value instanceof Error ? value : new Error("Unexpected error", { cause: value });
@@ -4530,12 +4807,11 @@ const coreFormatRegistry = formatRegistry([
4530
4807
  ]);
4531
4808
  const resolveFileDataFirst = (registry, fileName, formatIdOverride) => resolveFormat(resolutionCommandOf(registry, fileName, formatIdOverride));
4532
4809
  const resolveFile = dual((args) => typeof args[0] !== "string", resolveFileDataFirst);
4533
- const NO_OPT_IN_MUTATIONS = [];
4534
- const optInMutationsOf = (options) => options.optInMutations ?? NO_OPT_IN_MUTATIONS;
4535
4810
  const isIgnorer = (value) => Predicate.isObject(value) && typeof value["shouldIgnore"] === "function";
4536
4811
  const toTransformerOptions = (options) => ({
4537
4812
  excludedMutations: [...options.excludedMutations],
4538
- optInMutations: [...optInMutationsOf(options)],
4813
+ mutators: options.mutators,
4814
+ mutantSetPolicy: options.mutantSetPolicy,
4539
4815
  ignorers: options.ignorers.filter(isIgnorer),
4540
4816
  ...options.noHeader !== void 0 ? { noHeader: options.noHeader } : {}
4541
4817
  });
@@ -4675,7 +4951,7 @@ const frameworkEntryOfDataFirst = (moduleName, framework) => ({
4675
4951
  },
4676
4952
  owner: moduleName,
4677
4953
  ownerVersion: framework.claim.ownerVersion,
4678
- parse: Effect.fn("stryker.instrument.framework_entry.parse")(function* (text, fileName) {
4954
+ parse: Effect.fn(SpanTaxonomy.Spans.instrumentFrameworkEntryParse.name)(function* (text, fileName) {
4679
4955
  const context = frameworkContextOf(yield* loadFrameworkToolkit);
4680
4956
  const lineStarts = lineStartsOf(text);
4681
4957
  const result = yield* runHook(moduleName, "parse", fileName, () => framework.parse(text, context));
@@ -4692,7 +4968,7 @@ const frameworkEntryOfDataFirst = (moduleName, framework) => ({
4692
4968
  }),
4693
4969
  transform: (ast, mutantCollector, context) => Option.match(embeddedOf(ast), {
4694
4970
  onNone: () => Effect.fail(embeddedAstError(ast)),
4695
- onSome: Effect.fn("stryker.instrument.framework_entry.transform")(function* (embedded) {
4971
+ onSome: Effect.fn(SpanTaxonomy.Spans.instrumentFrameworkEntryTransform.name)(function* (embedded) {
4696
4972
  const warnings = yield* Effect.forEach(embedded.scripts, (script) => context.transform(script.ast, mutantCollector, context));
4697
4973
  embedded.document = yield* runHook(moduleName, "transform", embedded.originFileName, () => framework.transform(embedded.document, embedded.context));
4698
4974
  return warnings.flat();
@@ -4731,7 +5007,7 @@ const readDisable = (input) => Effect.sync(() => {
4731
5007
  registry: input.registry
4732
5008
  };
4733
5009
  });
4734
- const disableTypeChecksCell = Sandwich.named("stryker.instrument.disableTypeChecks")((input) => readDisable(input)).decide(resolveFormat).write({
5010
+ const disableTypeChecksCell = Sandwich.named(SpanTaxonomy.Spans.instrumentDisableTypeChecks.name)((input) => readDisable(input)).decide(resolveFormat).write({
4735
5011
  FormatAssigned: (assigned, raw) => spliceForAssigned({
4736
5012
  file: raw.file,
4737
5013
  registry: raw.registry,
@@ -4787,7 +5063,7 @@ const skippedOnlyResult = (raw) => InstrumentResult.make({
4787
5063
  mutants: [],
4788
5064
  skipped: [...raw.skipped]
4789
5065
  });
4790
- const readInstrumentFiles = Effect.fn("stryker.instrument.read_files")(function* (input) {
5066
+ const readInstrumentFiles = Effect.fn(SpanTaxonomy.Spans.instrumentReadFiles.name)(function* (input) {
4791
5067
  const outcomes = yield* Effect.forEach(input.files, (file) => fileOutcome({
4792
5068
  registry: input.registry,
4793
5069
  file
@@ -4826,29 +5102,15 @@ const instrumentFilesCell = Sandwich.named("stryker.instrument.files")((input) =
4826
5102
  //#endregion
4827
5103
  //#region src/Instrument.service.ts
4828
5104
  const toSchemaFile = (file) => ({
4829
- name: CanonicalFileName.make(file.name),
5105
+ name: Mutant.CanonicalFileName.make(file.name),
4830
5106
  content: file.content,
4831
5107
  mutate: file.mutate
4832
5108
  });
4833
- const KNOWN_OPT_IN_MUTATIONS = Object.keys(optInMutators);
4834
- const unknownOptInMutations = (requested) => requested.filter((name) => !KNOWN_OPT_IN_MUTATIONS.includes(name));
4835
- const listNames = (names) => Option.match(Option.fromNullishOr(names.at(0)), {
4836
- onNone: () => "none",
4837
- onSome: () => names.map((name) => `'${name}'`).join(", ")
4838
- });
4839
- const unknownOptInMutationsError = (requested) => Option.map(Option.fromNullishOr(unknownOptInMutations(requested).at(0)), () => InstrumentError.make({
4840
- message: `Unknown opt-in mutations: ${listNames(unknownOptInMutations(requested))}. Known opt-in mutations: ${listNames(KNOWN_OPT_IN_MUTATIONS)}.`,
4841
- cause: void 0
4842
- }));
4843
- const refuseUnknownOptInMutations = (options) => Option.match(unknownOptInMutationsError(optInMutationsOf(options)), {
4844
- onNone: () => Effect.void,
4845
- onSome: (error) => Effect.fail(error)
4846
- });
4847
- const instrumentDataFirst = (files, options, registry = coreFormatRegistry) => refuseUnknownOptInMutations(options).pipe(Effect.andThen(instrumentFilesCell.run({
5109
+ const instrumentDataFirst = (files, options, registry = coreFormatRegistry) => instrumentFilesCell.run({
4848
5110
  files: files.map(toSchemaFile),
4849
5111
  options,
4850
5112
  registry
4851
- })));
5113
+ });
4852
5114
  const instrument = dual((args) => Array.isArray(args[0]), instrumentDataFirst);
4853
5115
  const disableTypeChecksDataFirst = (file, registry = coreFormatRegistry) => disableTypeChecksCell.run({
4854
5116
  file: toSchemaFile(file),
@@ -4865,36 +5127,25 @@ const disableTypeChecks = dual((args) => Predicate.hasProperty(args[0], "content
4865
5127
  //#region src/Instrument/mod.ts
4866
5128
  var Instrument_exports = /* @__PURE__ */ __exportAll({
4867
5129
  InstrumentFileSkip: () => InstrumentFileSkip,
5130
+ InstrumenterContext: () => InstrumenterContext,
4868
5131
  disableTypeChecks: () => disableTypeChecks,
4869
5132
  instrument: () => instrument
4870
5133
  });
4871
5134
  //#endregion
4872
- //#region src/Mutant/mod.ts
4873
- var Mutant_exports = /* @__PURE__ */ __exportAll({
4874
- ActionableStatusSchema: () => ActionableStatusSchema,
4875
- CanonicalFileName: () => CanonicalFileName,
4876
- Column: () => Column,
4877
- EphemeralStatusSchema: () => EphemeralStatusSchema,
4878
- HitCount: () => HitCount,
4879
- InstrumenterContext: () => InstrumenterContext,
4880
- Line: () => Line,
4881
- Location: () => Location,
4882
- Mutant: () => Mutant,
4883
- MutantActivationSchema: () => MutantActivationSchema,
4884
- MutantCoverageSchema: () => MutantCoverageSchema,
4885
- MutantFromUnknown: () => MutantFromUnknown,
4886
- MutantId: () => MutantId,
4887
- MutantRunOptionsSchema: () => MutantRunOptionsSchema,
4888
- MutantStatusSchema: () => MutantStatusSchema,
4889
- MutatorName: () => MutatorName,
5135
+ //#region src/Mutator/mod.ts
5136
+ var Mutator_exports = /* @__PURE__ */ __exportAll({
5137
+ defaultMutators: () => defaultMutators,
5138
+ optInMutators: () => optInMutators,
5139
+ registryOf: () => registryOf,
5140
+ selectMutators: () => selectMutators,
5141
+ stockRegistry: () => stockRegistry
5142
+ });
5143
+ //#endregion
5144
+ //#region src/Source/mod.ts
5145
+ var Source_exports = /* @__PURE__ */ __exportAll({
4890
5146
  Offset: () => Offset,
4891
- OpenEndLocation: () => OpenEndLocation,
4892
- Position: () => Position,
4893
- RememberedStatusSchema: () => RememberedStatusSchema,
4894
- RunOptionsFields: () => RunOptionsFields,
4895
5147
  ScriptOrigin: () => ScriptOrigin,
4896
- Span: () => Span,
4897
- SurvivorStatusSchema: () => SurvivorStatusSchema
5148
+ Span: () => Span
4898
5149
  });
4899
5150
  //#endregion
4900
- export { ErrorText_exports as ErrorText, Format_exports as Format, Instrument_exports as Instrument, Mutant_exports as Mutant, lineStartsOf, locationOf, offsetAt, originAt, positionAt };
5151
+ export { ErrorText_exports as ErrorText, Format_exports as Format, Instrument_exports as Instrument, Mutator_exports as Mutator, Source_exports as Source, lineStartsOf, locationOf, offsetAt, originAt, positionAt };