@systemfsoftware/stryker-js 10.1.1 → 10.2.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.
@@ -7,6 +7,7 @@ import * as S from "effect/Schema";
7
7
  import * as Predicate from "effect/Predicate";
8
8
  import * as Arr from "effect/Array";
9
9
  import { Effect, Schema, SchemaGetter, SchemaTransformation } from "effect";
10
+ import * as Bool from "effect/Boolean";
10
11
  import * as Rpc from "effect/unstable/rpc/Rpc";
11
12
  import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
12
13
  import * as RpcMiddleware from "effect/unstable/rpc/RpcMiddleware";
@@ -2659,7 +2660,8 @@ const IgnorerSchema = S.Unknown;
2659
2660
  S.Struct({
2660
2661
  excludedMutations: S.Array(S.String),
2661
2662
  ignorers: S.Array(IgnorerSchema),
2662
- noHeader: S.optional(S.Boolean)
2663
+ noHeader: S.optional(S.Boolean),
2664
+ optInMutations: S.optional(S.Array(S.String))
2663
2665
  });
2664
2666
  var InstrumentResult = class extends S.TaggedClass()("InstrumentResult", {
2665
2667
  files: S.Array(FileSchema),
@@ -2968,6 +2970,22 @@ function unaryExpression(operator, argument, loc) {
2968
2970
  prefix: true
2969
2971
  }, loc);
2970
2972
  }
2973
+ function binaryExpression(operator, left, right, loc) {
2974
+ return mark({
2975
+ type: "BinaryExpression",
2976
+ operator,
2977
+ left,
2978
+ right
2979
+ }, loc);
2980
+ }
2981
+ function logicalExpression(operator, left, right, loc) {
2982
+ return mark({
2983
+ type: "LogicalExpression",
2984
+ operator,
2985
+ left,
2986
+ right
2987
+ }, loc);
2988
+ }
2971
2989
  function updateExpression(operator, argument, prefix, loc) {
2972
2990
  return mark({
2973
2991
  type: "UpdateExpression",
@@ -3606,7 +3624,7 @@ const loadWalkerModule = (specifier, fileName, cause) => Effect$1.gen(function*
3606
3624
  return walk;
3607
3625
  });
3608
3626
  const parseSvelte = (text, fileName, context) => Effect$1.gen(function* () {
3609
- const { parse: svelteParse, preprocess, VERSION } = yield* Effect$1.promise(() => import("./compiler-Faeg70AC.mjs"));
3627
+ const { parse: svelteParse, preprocess, VERSION } = yield* Effect$1.promise(() => import("./compiler-BTpMWGqK.mjs"));
3610
3628
  if (!isAtLeast(VERSION, MINIMUM_SVELTE_VERSION)) return yield* SvelteVersionNotSupported.make({
3611
3629
  version: VERSION,
3612
3630
  fileName,
@@ -5585,14 +5603,14 @@ function isTSType(node) {
5585
5603
  function isNodeOfKind(node, kind) {
5586
5604
  return node.type === kind;
5587
5605
  }
5588
- const FUNCTION_KINDS = {
5606
+ const FUNCTION_KINDS$1 = {
5589
5607
  FunctionDeclaration: true,
5590
5608
  FunctionExpression: true,
5591
5609
  TSDeclareFunction: true,
5592
5610
  TSEmptyBodyFunctionExpression: true
5593
5611
  };
5594
5612
  function isFunctionNode(node) {
5595
- return FUNCTION_KINDS[node.type] === true;
5613
+ return FUNCTION_KINDS$1[node.type] === true;
5596
5614
  }
5597
5615
  function isAssignmentPattern(node) {
5598
5616
  return node.type === "AssignmentPattern";
@@ -6088,6 +6106,583 @@ function errorToString(error) {
6088
6106
  }
6089
6107
  Object.freeze({ NoSuchFileOrDirectory: "ENOENT" });
6090
6108
  /**
6109
+ * EffectCall — the resolution front half the opt-in concurrency mutators share.
6110
+ *
6111
+ * Given a visited node it answers: is this a covered Effect call (R1); through
6112
+ * which module and export did its callee resolve; which call form does its
6113
+ * argument count select (KTD10); does a data-last form sit where the replacement
6114
+ * function is contextually typed (R4); and how can each `effect` module the
6115
+ * replacement needs be reached from the file's own imports (KTD3). It decides
6116
+ * nothing about a replacement: a mutator asks for module access through the
6117
+ * description's `moduleAccess`, and gets none when the file has no unshadowed
6118
+ * binding for that module (R2).
6119
+ *
6120
+ * Pure. One expression per decision, no I/O, no throwing. The import table is
6121
+ * built once per Program and memoised in a WeakMap, because every visited node
6122
+ * asks for it and a file's imports never change mid-parse (KTD4).
6123
+ */
6124
+ const MODULE_NAMES = [
6125
+ "Effect",
6126
+ "Ref",
6127
+ "Semaphore",
6128
+ "SynchronizedRef"
6129
+ ];
6130
+ const EFFECT_SOURCE = "effect";
6131
+ const FUNCTION_SOURCE = "effect/Function";
6132
+ const DATA_FIRST = "data-first";
6133
+ const DATA_LAST_PIPE_ARG = "data-last-pipe-arg";
6134
+ const DATA_LAST_PIPE_METHOD = "data-last-pipe-method";
6135
+ const DATA_LAST_IMMEDIATE = "data-last-immediate";
6136
+ const PIPED_REFERENCE = "piped-reference";
6137
+ const NO_ARGUMENTS = Object.freeze([]);
6138
+ const ARITIES = Object.freeze({
6139
+ "Ref.modify": {
6140
+ dataFirst: [2],
6141
+ dataLast: 1
6142
+ },
6143
+ "Ref.modifySome": {
6144
+ dataFirst: [2],
6145
+ dataLast: 1
6146
+ },
6147
+ "Ref.update": {
6148
+ dataFirst: [2],
6149
+ dataLast: 1
6150
+ },
6151
+ "Ref.updateSome": {
6152
+ dataFirst: [2],
6153
+ dataLast: 1
6154
+ },
6155
+ "Ref.updateAndGet": {
6156
+ dataFirst: [2],
6157
+ dataLast: 1
6158
+ },
6159
+ "Ref.getAndUpdate": {
6160
+ dataFirst: [2],
6161
+ dataLast: 1
6162
+ },
6163
+ "SynchronizedRef.modify": {
6164
+ dataFirst: [2],
6165
+ dataLast: 1
6166
+ },
6167
+ "SynchronizedRef.modifySome": {
6168
+ dataFirst: [2],
6169
+ dataLast: 1
6170
+ },
6171
+ "SynchronizedRef.update": {
6172
+ dataFirst: [2],
6173
+ dataLast: 1
6174
+ },
6175
+ "SynchronizedRef.updateSome": {
6176
+ dataFirst: [2],
6177
+ dataLast: 1
6178
+ },
6179
+ "SynchronizedRef.updateAndGet": {
6180
+ dataFirst: [2],
6181
+ dataLast: 1
6182
+ },
6183
+ "SynchronizedRef.getAndUpdate": {
6184
+ dataFirst: [2],
6185
+ dataLast: 1
6186
+ },
6187
+ "Semaphore.withPermits": {
6188
+ dataFirst: [3],
6189
+ dataLast: 2
6190
+ },
6191
+ "Semaphore.withPermit": {
6192
+ dataFirst: [2],
6193
+ dataLast: 1
6194
+ },
6195
+ "Effect.ensuring": {
6196
+ dataFirst: [2],
6197
+ dataLast: 1
6198
+ },
6199
+ "Effect.onExit": {
6200
+ dataFirst: [2],
6201
+ dataLast: 1
6202
+ },
6203
+ "Effect.onError": {
6204
+ dataFirst: [2],
6205
+ dataLast: 1
6206
+ },
6207
+ "Effect.onInterrupt": {
6208
+ dataFirst: [2],
6209
+ dataLast: 1
6210
+ },
6211
+ "Effect.uninterruptible": { dataFirst: [1] },
6212
+ "Effect.uninterruptibleMask": { dataFirst: [1] },
6213
+ "Effect.acquireRelease": { dataFirst: [2, 3] },
6214
+ "Effect.acquireUseRelease": { dataFirst: [3] }
6215
+ });
6216
+ const EMPTY_IMPORT_TABLE = Object.freeze({
6217
+ moduleBindings: /* @__PURE__ */ new Map(),
6218
+ namespaces: /* @__PURE__ */ new Map(),
6219
+ bareFunctions: /* @__PURE__ */ new Map(),
6220
+ pipeBindings: /* @__PURE__ */ new Set()
6221
+ });
6222
+ const importTables = /* @__PURE__ */ new WeakMap();
6223
+ const importTableFor = (program) => Option.getOrElse(Option.fromNullishOr(importTables.get(program)), () => rememberImportTable(program));
6224
+ function rememberImportTable(program) {
6225
+ const table = buildImportTable(program);
6226
+ importTables.set(program, table);
6227
+ return table;
6228
+ }
6229
+ const buildImportTable = (program) => Arr.reduce(Arr.flatMap(Arr.filter(program.body, isImportDeclaration$1), importContributions), EMPTY_IMPORT_TABLE, applyContribution);
6230
+ const withEntry = (entries, key, value) => new Map([...entries, [key, value]]);
6231
+ const withMember = (members, value) => /* @__PURE__ */ new Set([...members, value]);
6232
+ const applyContribution = (table, contribution) => Match.value(contribution).pipe(Match.when((candidate) => candidate.kind === "module", (moduleBinding) => ({
6233
+ ...table,
6234
+ moduleBindings: withEntry(table.moduleBindings, moduleBinding.local, moduleBinding.module)
6235
+ })), Match.when((candidate) => candidate.kind === "namespace", (namespaceBinding) => ({
6236
+ ...table,
6237
+ namespaces: withEntry(table.namespaces, namespaceBinding.local, namespaceBinding.binding)
6238
+ })), Match.when((candidate) => candidate.kind === "bareFunction", (bareFunction) => ({
6239
+ ...table,
6240
+ bareFunctions: withEntry(table.bareFunctions, bareFunction.local, {
6241
+ module: bareFunction.module,
6242
+ exportName: bareFunction.exportName
6243
+ })
6244
+ })), Match.orElse((pipeBinding) => ({
6245
+ ...table,
6246
+ pipeBindings: withMember(table.pipeBindings, pipeBinding.local)
6247
+ })));
6248
+ const importContributions = (declaration) => Option.toArray(onlyWhen(declaration.importKind !== "type", declaration)).flatMap((imported) => Arr.flatMap(imported.specifiers, (specifier) => Option.toArray(specifierContribution(imported.source.value, specifier))));
6249
+ const specifierContribution = (source, specifier) => Match.value(specifier).pipe(Match.when(isNamedImportSpecifier, (named) => namedSpecifierContribution(source, named)), Match.when(isNamespaceImportSpecifier, (namespace) => namespaceSpecifierContribution(source, namespace)), Match.orElse(() => Option.none()));
6250
+ const namedSpecifierContribution = (source, specifier) => onlyWhen(specifier.importKind !== "type", specifier).pipe(Option.flatMap((imported) => namedContribution(source, moduleExportName(imported.imported), imported.local.name)));
6251
+ const namedContribution = (source, importedName, localName) => Match.value(source).pipe(Match.when((candidate) => candidate === EFFECT_SOURCE, () => effectRootContribution(importedName, localName)), Match.when((candidate) => candidate === FUNCTION_SOURCE, () => pipeContribution(importedName, localName)), Match.orElse(() => Option.map(moduleNameFromSource(source), (module) => bareFunctionContribution(localName, module, importedName))));
6252
+ const effectRootContribution = (importedName, localName) => Option.orElse(Option.map(moduleNameFromName(importedName), (module) => moduleContribution(localName, module)), () => pipeContribution(importedName, localName));
6253
+ const pipeContribution = (importedName, localName) => onlyWhen(importedName === "pipe", pipeContributionOf(localName));
6254
+ const namespaceSpecifierContribution = (source, specifier) => Option.map(namespaceBindingFor(source), (binding) => ({
6255
+ kind: "namespace",
6256
+ local: specifier.local.name,
6257
+ binding
6258
+ }));
6259
+ const moduleContribution = (local, module) => ({
6260
+ kind: "module",
6261
+ local,
6262
+ module
6263
+ });
6264
+ const bareFunctionContribution = (local, module, exportName) => ({
6265
+ kind: "bareFunction",
6266
+ local,
6267
+ module,
6268
+ exportName
6269
+ });
6270
+ const pipeContributionOf = (local) => ({
6271
+ kind: "pipe",
6272
+ local
6273
+ });
6274
+ const namespaceBindingFor = (source) => Match.value(source).pipe(Match.when((candidate) => candidate === EFFECT_SOURCE, () => Option.some({ kind: "root" })), Match.when((candidate) => candidate === FUNCTION_SOURCE, () => Option.some({ kind: "functionModule" })), Match.orElse(() => Option.map(moduleNameFromSource(source), (module) => ({
6275
+ kind: "module",
6276
+ module
6277
+ }))));
6278
+ const moduleNameFromName = (name) => Arr.findFirst(MODULE_NAMES, (module) => module === name);
6279
+ const moduleNameFromSource = (source) => Arr.findFirst(MODULE_NAMES, (module) => source === `${EFFECT_SOURCE}/${module}`);
6280
+ const resolveEffectCall = (node, context) => Option.flatMap(programOf(context), (program) => resolveIn(node, context, importTableFor(program)));
6281
+ const programOf = (context) => Arr.findLast(context.ancestors, isProgram);
6282
+ const resolveIn = (node, context, table) => Match.value(node).pipe(Match.when(isCallExpression$1, (call) => resolveCall(call, context, table)), Match.when(isMemberExpression, (member) => resolveReference(member, context, table)), Match.when(isIdentifier$1, (reference) => resolveReference(reference, context, table)), Match.orElse(() => Option.none()));
6283
+ const resolveCall = (call, context, table) => Option.flatMap(resolveCallee(call.callee, context, table), (callee) => Option.flatMap(spreadFreeArguments$1(call.arguments), (args) => Option.flatMap(callForm(callee, args, call, context, table), (form) => Option.some({
6284
+ module: callee.module,
6285
+ exportName: callee.exportName,
6286
+ form,
6287
+ args,
6288
+ moduleAccess: moduleAccessFunction(callee, context, table)
6289
+ }))));
6290
+ const resolveReference = (reference, context, table) => Option.flatMap(resolveCallee(reference, context, table), (callee) => Option.flatMap(referenceForm(reference, context, table), (form) => Option.some({
6291
+ module: callee.module,
6292
+ exportName: callee.exportName,
6293
+ form,
6294
+ args: NO_ARGUMENTS,
6295
+ moduleAccess: moduleAccessFunction(callee, context, table)
6296
+ })));
6297
+ const referenceForm = (reference, context, table) => Option.flatMap(parentCall(context), (parent) => Option.map(pipePosition(parent, reference, context, table), () => PIPED_REFERENCE));
6298
+ const spreadFreeArguments$1 = (args) => Match.value(args.some(isSpreadElement)).pipe(Match.when(true, () => Option.none()), Match.orElse(() => Option.some(args.filter(isSpreadFree))));
6299
+ const resolveCallee = (callee, context, table) => Match.value(callee).pipe(Match.when(isIdentifier$1, (reference) => resolveBareCallee(reference, context, table)), Match.when(isMemberExpression, (member) => resolveMemberCallee(member, context, table)), Match.orElse(() => Option.none()));
6300
+ const resolveBareCallee = (reference, context, table) => Option.flatMap(Option.fromNullishOr(table.bareFunctions.get(reference.name)), (binding) => Option.map(unshadowedName(reference.name, context), () => ({
6301
+ module: binding.module,
6302
+ exportName: binding.exportName,
6303
+ owner: Option.none()
6304
+ })));
6305
+ const resolveMemberCallee = (member, context, table) => Option.flatMap(staticMemberName(member), (exportName) => Option.map(resolveModuleObject(member.object, context, table), (owner) => ({
6306
+ module: owner.module,
6307
+ exportName,
6308
+ owner: Option.some(owner)
6309
+ })));
6310
+ const resolveModuleObject = (expression, context, table) => Match.value(expression).pipe(Match.when(isIdentifier$1, (reference) => identifierModuleObject(reference, context, table)), Match.when(isMemberExpression, (member) => namespacedModuleObject(member, context, table)), Match.orElse(() => Option.none()));
6311
+ const identifierModuleObject = (reference, context, table) => Option.orElse(Option.flatMap(Option.fromNullishOr(table.moduleBindings.get(reference.name)), (module) => Option.map(unshadowedName(reference.name, context), () => ({
6312
+ module,
6313
+ access: reference
6314
+ }))), () => Option.flatMap(namespaceOf(reference, context, table), (binding) => Option.map(moduleOf(binding), (module) => ({
6315
+ module,
6316
+ access: reference
6317
+ }))));
6318
+ const namespacedModuleObject = (member, context, table) => Option.flatMap(staticMemberName(member), (moduleName) => Option.flatMap(namespaceOf(member.object, context, table), (binding) => Match.value(binding).pipe(Match.when(isRootNamespace, () => Option.map(moduleNameFromName(moduleName), (module) => ({
6319
+ module,
6320
+ access: member
6321
+ }))), Match.when(isModuleNamespace, (moduleNamespace) => Option.map(onlyWhen(moduleNamespace.module === moduleName, member), (access) => ({
6322
+ module: moduleNamespace.module,
6323
+ access
6324
+ }))), Match.orElse(() => Option.none()))));
6325
+ const isRootNamespace = (binding) => binding.kind === "root";
6326
+ const moduleOf = (binding) => Match.value(binding).pipe(Match.when(isModuleNamespace, (moduleNamespace) => Option.some(moduleNamespace.module)), Match.orElse(() => Option.none()));
6327
+ const isModuleNamespace = (binding) => binding.kind === "module";
6328
+ const namespaceOf = (expression, context, table) => Match.value(expression).pipe(Match.when(isIdentifier$1, (reference) => Option.flatMap(Option.fromNullishOr(table.namespaces.get(reference.name)), (binding) => Option.map(unshadowedName(reference.name, context), () => binding))), Match.orElse(() => Option.none()));
6329
+ const callForm = (callee, args, call, context, table) => Option.flatMap(arityOf(callee), (arity) => Match.value(args.length).pipe(Match.when((count) => arity.dataFirst.includes(count), () => Option.some(DATA_FIRST)), Match.when((count) => arity.dataLast === count, () => dataLastForm(call, context, table)), Match.orElse(() => Option.none())));
6330
+ const arityOf = (callee) => Option.fromNullishOr(ARITIES[`${callee.module}.${callee.exportName}`]);
6331
+ const dataLastForm = (call, context, table) => Option.flatMap(parentCall(context), (parent) => Match.value(parent.callee === call).pipe(Match.when(true, () => Option.some(DATA_LAST_IMMEDIATE)), Match.orElse(() => Option.flatMap(pipePosition(parent, call, context, table), formForPosition))));
6332
+ const pipePosition = (parent, argument, context, table) => Option.flatMap(argumentIndex(parent.arguments, argument), (index) => Option.orElse(onlyWhen(isPipeMethodCall(parent), {
6333
+ index,
6334
+ callee: "method"
6335
+ }), () => onlyWhen(isEffectPipeCall(parent, context, table), {
6336
+ index,
6337
+ callee: "effect"
6338
+ })));
6339
+ const formForPosition = (position) => Match.value(position.callee).pipe(Match.when((callee) => callee === "method", () => Option.some(DATA_LAST_PIPE_METHOD)), Match.orElse(() => onlyWhen(position.index >= 1, DATA_LAST_PIPE_ARG)));
6340
+ const argumentIndex = (args, node) => Option.flatMap(Option.fromNullishOr(args.findIndex((argument) => sameNode(argument, node))), (index) => onlyWhen(index >= 0, index));
6341
+ const parentCall = (context) => Option.flatMap(Option.fromNullishOr(context.parent), (parent) => Match.value(parent).pipe(Match.when(isCallExpression$1, (call) => Option.some(call)), Match.orElse(() => Option.none())));
6342
+ const isPipeMethodCall = (parent) => Match.value(parent.callee).pipe(Match.when(isMemberExpression, (member) => Option.exists(staticMemberName(member), (name) => name === "pipe")), Match.orElse(() => false));
6343
+ const isEffectPipeCall = (parent, context, table) => Match.value(parent.callee).pipe(Match.when(isIdentifier$1, (reference) => Bool.and(isVisible(reference.name, context), table.pipeBindings.has(reference.name))), Match.when(isMemberExpression, (member) => Option.exists(staticMemberName(member), (name) => Bool.and(name === "pipe", isPipeNamespace(member.object, context, table)))), Match.orElse(() => false));
6344
+ const isPipeNamespace = (expression, context, table) => Match.value(expression).pipe(Match.when(isIdentifier$1, (reference) => Option.match(namespaceOf(expression, context, table), {
6345
+ onNone: () => false,
6346
+ onSome: (binding) => Bool.and(isPipeNamespaceBinding(binding), isVisible(reference.name, context))
6347
+ })), Match.orElse(() => false));
6348
+ const isPipeNamespaceBinding = (binding) => Match.value(binding.kind).pipe(Match.when((kind) => kind === "root", () => true), Match.when((kind) => kind === "functionModule", () => true), Match.orElse(() => false));
6349
+ const moduleAccessFunction = (callee, context, table) => (module) => Arr.findFirst([
6350
+ ownerAccessFor(callee, module),
6351
+ namedModuleAccess(module, context, table),
6352
+ rootNamespaceAccess(module, context, table),
6353
+ moduleNamespaceAccess(module, context, table)
6354
+ ], Option.isSome).pipe(Option.flatten);
6355
+ const ownerAccessFor = (callee, module) => Option.flatMap(callee.owner, (owner) => onlyWhen(owner.module === module, owner.access));
6356
+ const namedModuleAccess = (module, context, table) => Option.map(Option.fromNullishOr([...table.moduleBindings].find(([local, bound]) => Bool.and(bound === module, isVisible(local, context)))), ([local]) => identifier(local));
6357
+ const rootNamespaceAccess = (module, context, table) => Option.map(Option.fromNullishOr([...table.namespaces].find(([local, binding]) => Bool.and(binding.kind === "root", isVisible(local, context)))), ([local]) => memberExpression(identifier(local), identifier(module), false));
6358
+ const moduleNamespaceAccess = (module, context, table) => Option.map(Option.fromNullishOr([...table.namespaces].find(([local, binding]) => Bool.and(Option.exists(moduleOf(binding), (bound) => bound === module), isVisible(local, context)))), ([local]) => identifier(local));
6359
+ const isMovableArgument = (node) => nodesOutsideFunctions(node).every((candidate) => YIELD_OR_AWAIT_KINDS[nodeType(candidate) ?? ""] !== true);
6360
+ const YIELD_OR_AWAIT_KINDS = {
6361
+ AwaitExpression: true,
6362
+ YieldExpression: true
6363
+ };
6364
+ const isDroppableArgument = (node) => Match.value(node).pipe(Match.when(isIdentifier$1, () => true), Match.when(isLiteral, () => true), Match.when(isThisExpression, () => true), Match.when(isFunctionLike, () => true), Match.when(isMemberExpression, (member) => isCallFreeMemberChain(member)), Match.orElse(() => false));
6365
+ const isCallFreeMemberChain = (member) => Match.value(member.computed).pipe(Match.when(true, () => false), Match.orElse(() => Match.value(member.object).pipe(Match.when(isIdentifier$1, () => true), Match.when(isThisExpression, () => true), Match.when(isMemberExpression, (inner) => isCallFreeMemberChain(inner)), Match.orElse(() => false))));
6366
+ const identifiersIn = (node) => allNodesUnder(node).flatMap((candidate) => Option.toArray(identifierText(candidate)));
6367
+ const freshIdentifier = (base, taken) => freeIdentifier(base, taken, 0);
6368
+ const freeIdentifier = (base, taken, suffix) => Match.value(taken.has(suffixedName(base, suffix))).pipe(Match.when(false, () => suffixedName(base, suffix)), Match.orElse(() => freeIdentifier(base, taken, suffix + 1)));
6369
+ const suffixedName = (base, suffix) => Match.value(suffix).pipe(Match.when(0, () => base), Match.orElse((count) => `${base}${count}`));
6370
+ const isShadowed = (name, context) => context.ancestors.some((ancestor) => scopeDeclares(ancestor, name));
6371
+ const isVisible = (name, context) => isShadowed(name, context) === false;
6372
+ const unshadowedName = (name, context) => onlyWhen(isVisible(name, context), name);
6373
+ const scopeDeclares = (ancestor, name) => Match.value(ancestor).pipe(Match.when(isProgram, (program) => statementsDeclare(program.body, name)), Match.when(isBlockStatement, (block) => statementsDeclare(block.body, name)), Match.when(isStaticBlock, (block) => statementsDeclare(block.body, name)), Match.when(isTSModuleBlock, (block) => statementsDeclare(block.body, name)), Match.when(isSwitchStatement, (statement) => statement.cases.some((caseClause) => statementsDeclare(caseClause.consequent, name))), Match.when(isFunctionLike, (fn) => functionDeclares(fn, name)), Match.when(isCatchClause, (clause) => patternDeclaresOr(clause.param, name)), Match.when(isForStatement, (statement) => variableDeclarationDeclaresOr(statement.init, name)), Match.when(isForInStatement, (statement) => variableDeclarationDeclaresOr(statement.left, name)), Match.when(isForOfStatement, (statement) => variableDeclarationDeclaresOr(statement.left, name)), Match.when(isClassLike, (cls) => declaresIdentifier(cls.id, name)), Match.orElse(() => false));
6374
+ const functionDeclares = (fn, name) => holdsAny([
6375
+ fn.params.some((param) => patternDeclaresOr(param, name)),
6376
+ declaresIdentifier(fn.id, name),
6377
+ blockBodyDeclaresHoistedVar(fn.body, name)
6378
+ ]);
6379
+ const blockBodyDeclaresHoistedVar = (body, name) => Option.match(Option.fromNullishOr(body), {
6380
+ onNone: () => false,
6381
+ onSome: (present) => Match.value(present).pipe(Match.when(isBlockStatement, (block) => hoistedVarsDeclare(block, name)), Match.orElse(() => false))
6382
+ });
6383
+ const hoistedVarsDeclare = (block, name) => nodesOutsideFunctions(block).some((candidate) => hoistedCandidateDeclares(candidate, name));
6384
+ const hoistedCandidateDeclares = (candidate, name) => Match.value(candidate).pipe(Match.when(isVariableDeclaration, (declaration) => Bool.and(declaration.kind === "var", statementBindingsDeclare(declaration, name))), Match.orElse(() => false));
6385
+ const statementsDeclare = (statements, name) => statements.some((statement) => statementDeclares(statement, name));
6386
+ const statementDeclares = (statement, name) => Match.value(statement).pipe(Match.when(isVariableDeclaration, (declaration) => statementBindingsDeclare(declaration, name)), Match.when(isFunctionDeclaration, (declaration) => declaresIdentifier(declaration.id, name)), Match.when(isClassLike, (declaration) => declaresIdentifier(declaration.id, name)), Match.when(isTSEnumDeclaration, (declaration) => declaration.id.name === name), Match.when(isTSModuleDeclaration, (declaration) => moduleDeclarationDeclares(declaration, name)), Match.when(isTSImportEqualsDeclaration, (declaration) => importEqualsDeclares(declaration, name)), Match.when(isExportNamedDeclaration, (declaration) => Option.exists(Option.fromNullishOr(declaration.declaration), (inner) => statementDeclares(inner, name))), Match.orElse(() => false));
6387
+ const statementBindingsDeclare = (declaration, name) => declaration.declarations.some((declarator) => patternDeclaresOr(declarator.id, name));
6388
+ const variableDeclarationDeclaresOr = (node, name) => Option.match(Option.fromNullishOr(node), {
6389
+ onNone: () => false,
6390
+ onSome: (present) => Match.value(present).pipe(Match.when(isVariableDeclaration, (declaration) => statementBindingsDeclare(declaration, name)), Match.orElse(() => false))
6391
+ });
6392
+ const moduleDeclarationDeclares = (declaration, name) => Bool.and(declaration.body !== null, Option.exists(identifierText(declaration.id), (text) => text === name));
6393
+ const importEqualsDeclares = (declaration, name) => Bool.and(declaration.importKind !== "type", declaration.id.name === name);
6394
+ const declaresIdentifier = (id, name) => Option.exists(identifierText(id), (text) => text === name);
6395
+ const patternDeclaresOr = (pattern, name) => Option.match(Option.fromNullishOr(pattern), {
6396
+ onNone: () => false,
6397
+ onSome: (present) => Match.value(present).pipe(Match.when(isBindingIdentifierPattern, (binding) => binding.name === name), Match.when(isObjectBindingPattern, (object) => object.properties.some((property) => propertyDeclares(property, name))), Match.when(isArrayBindingPattern, (array) => array.elements.some((element) => patternDeclaresOr(element, name))), Match.when(isAssignmentBindingPattern, (assignment) => patternDeclaresOr(assignment.left, name)), Match.when(isBindingRestElement, (rest) => patternDeclaresOr(rest.argument, name)), Match.orElse(() => false))
6398
+ });
6399
+ const propertyDeclares = (property, name) => Match.value(property).pipe(Match.when(isBindingProperty, (binding) => patternDeclaresOr(binding.value, name)), Match.when(isBindingRestElement, (rest) => patternDeclaresOr(rest.argument, name)), Match.orElse(() => false));
6400
+ const isBindingIdentifierPattern = (candidate) => nodeType(candidate) === "Identifier";
6401
+ const isObjectBindingPattern = (candidate) => nodeType(candidate) === "ObjectPattern";
6402
+ const isArrayBindingPattern = (candidate) => nodeType(candidate) === "ArrayPattern";
6403
+ const isAssignmentBindingPattern = (candidate) => nodeType(candidate) === "AssignmentPattern";
6404
+ const isBindingRestElement = (candidate) => nodeType(candidate) === "RestElement";
6405
+ const isBindingProperty = (candidate) => nodeType(candidate) === "Property";
6406
+ const identifierText = (node) => Option.match(Option.fromNullishOr(node), {
6407
+ onNone: () => Option.none(),
6408
+ onSome: (present) => Match.value(present).pipe(Match.when(isNamedIdentifier, (name) => Option.some(name.name)), Match.when(isStringLiteral$1, (literal) => literalValueText(literal.value)), Match.orElse(() => Option.none()))
6409
+ });
6410
+ const literalValueText = (value) => Match.value(value).pipe(Match.when(Predicate.isString, (text) => Option.some(text)), Match.orElse(() => Option.none()));
6411
+ const moduleExportName = (name) => Option.getOrElse(identifierText(name), () => "");
6412
+ const staticMemberName = (member) => Match.value(member.computed).pipe(Match.when(false, () => identifierText(member.property)), Match.orElse(() => Option.none()));
6413
+ const nodesOutsideFunctions = (root) => {
6414
+ const collected = [];
6415
+ traverse(root, { enter(path) {
6416
+ collected.push(path.node);
6417
+ pruneAtFunction(path);
6418
+ } });
6419
+ return collected;
6420
+ };
6421
+ const allNodesUnder = (root) => {
6422
+ const collected = [];
6423
+ traverse(root, { enter(path) {
6424
+ collected.push(path.node);
6425
+ } });
6426
+ return collected;
6427
+ };
6428
+ const pruneAtFunction = (path) => Option.match(onlyWhen(isFunctionLike(path.node), path), {
6429
+ onNone: () => void 0,
6430
+ onSome: (atFunction) => atFunction.skip()
6431
+ });
6432
+ const moduleCall = (module, property, args) => callExpression(memberExpression(module, identifier(property), false), args);
6433
+ const onlyWhen = (holds, value) => Match.value(holds).pipe(Match.when(true, () => Option.some(value)), Match.orElse(() => Option.none()));
6434
+ const holdsAny = (conditions) => conditions.some((condition) => condition);
6435
+ const sameNode = (first, second) => first === second;
6436
+ const FUNCTION_KINDS = {
6437
+ ArrowFunctionExpression: true,
6438
+ FunctionDeclaration: true,
6439
+ FunctionExpression: true
6440
+ };
6441
+ const CLASS_KINDS = {
6442
+ ClassDeclaration: true,
6443
+ ClassExpression: true
6444
+ };
6445
+ const isFunctionLike = (candidate) => FUNCTION_KINDS[nodeType(candidate) ?? ""] === true;
6446
+ const isClassLike = (candidate) => CLASS_KINDS[nodeType(candidate) ?? ""] === true;
6447
+ const isProgram = (candidate) => nodeType(candidate) === "Program";
6448
+ const isBlockStatement = (candidate) => nodeType(candidate) === "BlockStatement";
6449
+ const isStaticBlock = (candidate) => nodeType(candidate) === "StaticBlock";
6450
+ const isTSModuleBlock = (candidate) => nodeType(candidate) === "TSModuleBlock";
6451
+ const isSwitchStatement = (candidate) => nodeType(candidate) === "SwitchStatement";
6452
+ const isCatchClause = (candidate) => nodeType(candidate) === "CatchClause";
6453
+ const isForStatement = (candidate) => nodeType(candidate) === "ForStatement";
6454
+ const isForInStatement = (candidate) => nodeType(candidate) === "ForInStatement";
6455
+ const isForOfStatement = (candidate) => nodeType(candidate) === "ForOfStatement";
6456
+ const isVariableDeclaration = (candidate) => nodeType(candidate) === "VariableDeclaration";
6457
+ const isFunctionDeclaration = (candidate) => nodeType(candidate) === "FunctionDeclaration";
6458
+ const isTSEnumDeclaration = (candidate) => nodeType(candidate) === "TSEnumDeclaration";
6459
+ const isTSModuleDeclaration = (candidate) => nodeType(candidate) === "TSModuleDeclaration";
6460
+ const isTSImportEqualsDeclaration = (candidate) => nodeType(candidate) === "TSImportEqualsDeclaration";
6461
+ const isExportNamedDeclaration = (candidate) => nodeType(candidate) === "ExportNamedDeclaration";
6462
+ const isImportDeclaration$1 = (candidate) => nodeType(candidate) === "ImportDeclaration";
6463
+ const isNamedImportSpecifier = (candidate) => nodeType(candidate) === "ImportSpecifier";
6464
+ const isNamespaceImportSpecifier = (candidate) => nodeType(candidate) === "ImportNamespaceSpecifier";
6465
+ const isCallExpression$1 = (candidate) => nodeType(candidate) === "CallExpression";
6466
+ const isMemberExpression = (candidate) => nodeType(candidate) === "MemberExpression";
6467
+ const isIdentifier$1 = (candidate) => nodeType(candidate) === "Identifier";
6468
+ const isNamedIdentifier = (candidate) => nodeType(candidate) === "Identifier";
6469
+ const isThisExpression = (candidate) => nodeType(candidate) === "ThisExpression";
6470
+ const isLiteral = (candidate) => nodeType(candidate) === "Literal";
6471
+ const isStringLiteral$1 = (candidate) => nodeType(candidate) === "Literal";
6472
+ const isSpreadElement = (argument) => nodeType(argument) === "SpreadElement";
6473
+ const isSpreadFree = (argument) => nodeType(argument) !== "SpreadElement";
6474
+ const NO_MUTANTS$3 = [];
6475
+ const ATOMIC_UPDATE_SPLIT_MODULES = ["Ref", "SynchronizedRef"];
6476
+ const ATOMIC_UPDATE_SPLIT_OPERATIONS = [
6477
+ "modify",
6478
+ "modifySome",
6479
+ "update",
6480
+ "updateSome",
6481
+ "updateAndGet",
6482
+ "getAndUpdate"
6483
+ ];
6484
+ const atomicUpdateSplitMutator = (node, context) => Option.match(resolveEffectCall(node, context), {
6485
+ onNone: () => NO_MUTANTS$3,
6486
+ onSome: (call) => Option.toArray(atomicUpdateSplitReplacement(call))
6487
+ });
6488
+ const atomicUpdateSplitReplacement = (call) => Option.flatMap(splitOperation(call), (operation) => Option.some(replacementExpression$1(operation)));
6489
+ const splitOperation = (call) => Match.value(call).pipe(Match.when(isAtomicUpdateSplitOperation, (covered) => splitOperationOf(covered)), Match.orElse(() => Option.none()));
6490
+ const isAtomicUpdateSplitOperation = (call) => Bool.and(included(ATOMIC_UPDATE_SPLIT_MODULES, call.module), included(ATOMIC_UPDATE_SPLIT_OPERATIONS, call.exportName));
6491
+ const splitOperationOf = (call) => Option.flatMap(moduleBindings(call), (bindings) => Option.flatMap(splitArguments$1(call), (args) => Option.some({
6492
+ effectModule: bindings.effectModule,
6493
+ operationModule: bindings.operationModule,
6494
+ exportName: call.exportName,
6495
+ ref: args.ref,
6496
+ f: args.f,
6497
+ binders: freshBinders$1(takenNames$2(call, bindings, args.f))
6498
+ })));
6499
+ const splitArguments$1 = (call) => Match.value(call.form).pipe(Match.when((form) => form === "data-first", () => dataFirstArguments$1(call)), Match.when((form) => form === "piped-reference", () => Option.none()), Match.orElse(() => dataLastArguments(call)));
6500
+ const dataFirstArguments$1 = (call) => Option.flatMap(Arr.get(call.args, 0), (ref) => Option.flatMap(Arr.get(call.args, 1), (f) => onlyWhen(Bool.and(isMovableArgument(ref), isMovableArgument(f)), {
6501
+ ref: Option.some(cloneNode(ref)),
6502
+ f: cloneNode(f)
6503
+ })));
6504
+ const dataLastArguments = (call) => Option.flatMap(Arr.get(call.args, 0), (f) => onlyWhen(isMovableArgument(f), {
6505
+ ref: Option.none(),
6506
+ f: cloneNode(f)
6507
+ }));
6508
+ const moduleBindings = (call) => Option.flatMap(call.moduleAccess("Effect"), (effectModule) => Option.map(call.moduleAccess(call.module), (operationModule) => ({
6509
+ effectModule,
6510
+ operationModule
6511
+ })));
6512
+ const takenNames$2 = (call, bindings, f) => /* @__PURE__ */ new Set([
6513
+ ...call.args.flatMap((argument) => identifiersIn(argument)),
6514
+ ...identifiersIn(f),
6515
+ ...identifiersIn(bindings.effectModule),
6516
+ ...identifiersIn(bindings.operationModule)
6517
+ ]);
6518
+ const freshBinders$1 = (taken) => ({
6519
+ self: freshIdentifier("self", taken),
6520
+ s: freshIdentifier("s", taken),
6521
+ snap: freshIdentifier("snap", taken),
6522
+ b: freshIdentifier("b", taken),
6523
+ a: freshIdentifier("a", taken)
6524
+ });
6525
+ const replacementExpression$1 = (operation) => Option.match(operation.ref, {
6526
+ onNone: () => arrowFunctionExpression([identifier(operation.binders.self)], splitBody(operation)),
6527
+ onSome: (ref) => moduleCall(operation.effectModule, "flatMap", [moduleCall(operation.effectModule, "succeed", [ref]), arrowFunctionExpression([identifier(operation.binders.self)], splitBody(operation))])
6528
+ });
6529
+ const splitBody = (operation) => moduleCall(operation.effectModule, "flatMap", [moduleCall(operation.operationModule, "get", [identifier(operation.binders.self)]), arrowFunctionExpression([identifier(operation.binders.s)], moduleCall(operation.effectModule, "flatMap", [moduleCall(operation.operationModule, "make", [identifier(operation.binders.s)]), arrowFunctionExpression([identifier(operation.binders.snap)], moduleCall(operation.effectModule, "flatMap", [moduleCall(operation.operationModule, operation.exportName, [identifier(operation.binders.snap), operation.f]), arrowFunctionExpression([identifier(operation.binders.b)], moduleCall(operation.effectModule, "flatMap", [memberExpression(operation.effectModule, identifier("yieldNow"), false), arrowFunctionExpression([], moduleCall(operation.effectModule, "flatMap", [moduleCall(operation.operationModule, "get", [identifier(operation.binders.snap)]), arrowFunctionExpression([identifier(operation.binders.a)], moduleCall(operation.effectModule, "as", [moduleCall(operation.operationModule, "set", [identifier(operation.binders.self), identifier(operation.binders.a)]), identifier(operation.binders.b)]))]))]))]))]))]);
6530
+ const included = (values, value) => values.includes(value);
6531
+ const NO_MUTANTS$2 = [];
6532
+ const COVERED_MODULE = "Effect";
6533
+ const TAG = "_tag";
6534
+ const SUCCESS_TAG = "Success";
6535
+ const INTERRUPT_TAG = "Interrupt";
6536
+ const finalizerEscapeMutator = (node, context) => Option.match(resolveEffectCall(node, context), {
6537
+ onNone: () => NO_MUTANTS$2,
6538
+ onSome: (call) => Option.toArray(replacementOf(call))
6539
+ });
6540
+ const replacementOf = (call) => Option.flatMap(onlyWhen(call.module === COVERED_MODULE, call), (covered) => Option.flatMap(covered.moduleAccess(COVERED_MODULE), (effect) => Option.flatMap(coveredForm(covered), (form) => Option.flatMap(splitArguments(covered, form), (split) => Option.flatMap(shapeOf(covered.exportName, split.rest), (shape) => Option.some(replacementExpression(call, effect, form, split, shape)))))));
6541
+ const replacementExpression = (call, effect, form, split, shape) => {
6542
+ const binders = freshBinders(takenNames$1(call, effect));
6543
+ const plan = {
6544
+ effect,
6545
+ self: selfExpression(split, binders),
6546
+ shape,
6547
+ binders
6548
+ };
6549
+ return placedBody(form, bodyExpression(plan), binders);
6550
+ };
6551
+ const coveredForm = (call) => Match.value(call.form).pipe(Match.when("data-first", () => Option.some("data-first")), Match.when("data-last-pipe-arg", () => Option.some("data-last")), Match.when("data-last-pipe-method", () => Option.some("data-last")), Match.when("data-last-immediate", () => Option.some("data-last")), Match.orElse(() => Option.none()));
6552
+ const splitArguments = (call, form) => Match.value(form).pipe(Match.when("data-first", () => dataFirstArguments(call)), Match.orElse(() => Option.some({
6553
+ self: Option.none(),
6554
+ rest: [...call.args]
6555
+ })));
6556
+ const dataFirstArguments = (call) => Option.map(Arr.get(call.args, 0), (self) => ({
6557
+ self: Option.some(cloneNode(self)),
6558
+ rest: call.args.slice(1)
6559
+ }));
6560
+ const selfExpression = (split, binders) => Option.getOrElse(split.self, () => identifier(binders.self));
6561
+ const shapeOf = (exportName, rest) => Match.value(exportName).pipe(Match.when("ensuring", () => finalizerShape(rest)), Match.when("onExit", () => exitShape(rest)), Match.when("onError", () => handlerShape(rest)), Match.when("onInterrupt", () => droppedHandlerShape(rest)), Match.when("acquireRelease", () => releaseShape(rest)), Match.when("acquireUseRelease", () => useReleaseShape(rest)), Match.orElse(() => Option.none()));
6562
+ const finalizerShape = (rest) => Option.flatMap(Arr.get(rest, 0), (fin) => onlyWhen(isMovableArgument(fin), {
6563
+ kind: "ensuring",
6564
+ fin
6565
+ }));
6566
+ const exitShape = (rest) => Option.map(Arr.get(rest, 0), (f) => ({
6567
+ kind: "onExit",
6568
+ f
6569
+ }));
6570
+ const handlerShape = (rest) => Option.map(Arr.get(rest, 0), (h) => ({
6571
+ kind: "onError",
6572
+ h
6573
+ }));
6574
+ const droppedHandlerShape = (rest) => Option.flatMap(Arr.get(rest, 0), (h) => onlyWhen(isDroppableArgument(h), {
6575
+ kind: "onInterrupt",
6576
+ h
6577
+ }));
6578
+ const releaseShape = (rest) => Option.flatMap(Arr.get(rest, 0), (rel) => Option.flatMap(droppableOptionAt(rest, 1), (optionsDroppable) => onlyWhen(Bool.and(isMovableArgument(rel), optionsDroppable), {
6579
+ kind: "acquireRelease",
6580
+ rel
6581
+ })));
6582
+ const useReleaseShape = (rest) => Option.flatMap(Arr.get(rest, 1), (rel) => Option.flatMap(Arr.get(rest, 0), (use) => Option.flatMap(releaseArityOf(rel), (releaseArity) => onlyWhen(isMovableArgument(rel), {
6583
+ kind: "acquireUseRelease",
6584
+ use,
6585
+ rel,
6586
+ releaseArity
6587
+ }))));
6588
+ const droppableOptionAt = (rest, index) => Option.match(Arr.get(rest, index), {
6589
+ onNone: () => Option.some(true),
6590
+ onSome: (argument) => onlyWhen(isDroppableArgument(argument), true)
6591
+ });
6592
+ const bodyExpression = (plan) => Match.value(plan.shape).pipe(Match.when({ kind: "ensuring" }, (shape) => moduleCall(plan.effect, "onExitIf", [
6593
+ plan.self,
6594
+ notInterruptedPredicate(plan.binders),
6595
+ arrowFunctionExpression([], shape.fin)
6596
+ ])), Match.when({ kind: "onExit" }, (shape) => moduleCall(plan.effect, "onExitIf", [
6597
+ plan.self,
6598
+ notInterruptedPredicate(plan.binders),
6599
+ shape.f
6600
+ ])), Match.when({ kind: "onError" }, (shape) => moduleCall(plan.effect, "onErrorIf", [
6601
+ plan.self,
6602
+ noInterruptPredicate(plan.binders),
6603
+ shape.h
6604
+ ])), Match.when({ kind: "onInterrupt" }, () => plan.self), Match.when({ kind: "acquireRelease" }, (shape) => releaseBody(plan, shape)), Match.when({ kind: "acquireUseRelease" }, (shape) => useReleaseBody(plan, shape)), Match.exhaustive);
6605
+ const releaseBody = (plan, shape) => moduleCall(plan.effect, "flatMap", [moduleCall(plan.effect, "interruptible", [plan.self]), arrowFunctionExpression([identifier(plan.binders.a)], moduleCall(plan.effect, "acquireRelease", [moduleCall(plan.effect, "succeed", [identifier(plan.binders.a)]), shape.rel]))]);
6606
+ const useReleaseBody = (plan, shape) => moduleCall(plan.effect, "acquireUseRelease", [
6607
+ plan.self,
6608
+ shape.use,
6609
+ arrowFunctionExpression([identifier(plan.binders.a), identifier(plan.binders.exit)], conditionalExpression(notInterruptedTest(plan.binders), appliedRelease(shape.rel, plan.binders, shape.releaseArity), memberExpression(plan.effect, identifier("void"), false)))
6610
+ ]);
6611
+ const appliedRelease = (rel, binders, releaseArity) => callExpression(rel, Arr.take([identifier(binders.a), identifier(binders.exit)], releaseArity));
6612
+ const releaseArityOf = (rel) => Match.value(rel).pipe(Match.when(isInlineFunction, plainParameterArity), Match.orElse(() => Option.none()));
6613
+ const isInlineFunction = (candidate) => nodeType(candidate) === "ArrowFunctionExpression" || nodeType(candidate) === "FunctionExpression";
6614
+ const plainParameterArity = (fn) => onlyWhen(Bool.and(fn.params.every(isPlainParameter), fn.params.length <= 2), fn.params.length);
6615
+ const isPlainParameter = (param) => nodeType(param) === "Identifier";
6616
+ const placedBody = (form, body, binders) => Match.value(form).pipe(Match.when("data-first", () => body), Match.orElse(() => arrowFunctionExpression([identifier(binders.self)], body)));
6617
+ const notInterruptedPredicate = (binders) => arrowFunctionExpression([identifier(binders.exit)], notInterruptedTest(binders));
6618
+ const notInterruptedTest = (binders) => logicalExpression("||", tagCheck(binders.exit, SUCCESS_TAG), unaryExpression("!", interruptionSome(failureReasons(binders.exit), binders.reason)));
6619
+ const noInterruptPredicate = (binders) => arrowFunctionExpression([identifier(binders.cause)], unaryExpression("!", interruptionSome(causeReasons(binders.cause), binders.reason)));
6620
+ const tagCheck = (name, tag) => binaryExpression("===", memberExpression(identifier(name), identifier(TAG), false), stringLiteral(tag));
6621
+ const failureReasons = (exitName) => memberExpression(memberExpression(identifier(exitName), identifier("cause"), false), identifier("reasons"), false);
6622
+ const causeReasons = (causeName) => memberExpression(identifier(causeName), identifier("reasons"), false);
6623
+ const interruptionSome = (reasons, reasonName) => callExpression(memberExpression(reasons, identifier("some"), false), [arrowFunctionExpression([identifier(reasonName)], tagCheck(reasonName, INTERRUPT_TAG))]);
6624
+ const takenNames$1 = (call, effect) => /* @__PURE__ */ new Set([...call.args.flatMap((argument) => identifiersIn(argument)), ...identifiersIn(effect)]);
6625
+ const freshBinders = (taken) => ({
6626
+ self: freshIdentifier("self", taken),
6627
+ exit: freshIdentifier("exit", taken),
6628
+ cause: freshIdentifier("cause", taken),
6629
+ reason: freshIdentifier("reason", taken),
6630
+ a: freshIdentifier("a", taken)
6631
+ });
6632
+ /**
6633
+ * SynchronizationRemoval — the R7 mutants: a removed lock and a removed mask.
6634
+ *
6635
+ * Four call shapes, each producing the wrapped effect alone:
6636
+ *
6637
+ * - `Semaphore.withPermits(sem, n, e)` and `Semaphore.withPermit(sem, e)` become
6638
+ * the guarded effect. The guard arguments are dropped, so they must be
6639
+ * droppable (KTD5); the effect itself is cloned so the mutant's tree shares
6640
+ * nothing with the original's.
6641
+ * - Their data-last forms become `(self) => self`, which the resolver only
6642
+ * reports where that function is contextually typed (R4, KTD9) — a piped
6643
+ * argument, a piped method argument, or the callee of an immediate call.
6644
+ * - `Effect.uninterruptible(e)` becomes `e`, and a bare `Effect.uninterruptible`
6645
+ * used as a pipe argument becomes `(self) => self` (KTD9).
6646
+ * - `Effect.uninterruptibleMask(f)` becomes `E.suspend(() => f(E.interruptible))`
6647
+ * (KTD7): `f` is called at run time, as the original calls it, and the restore
6648
+ * it receives is the identity wherever the surrounding region is interruptible.
6649
+ * This needs the Effect module object in scope (R2, KTD3).
6650
+ *
6651
+ * Resolution lives in `EffectCall.ts`; this module never inspects imports. It is
6652
+ * a pure function from a resolved call to the one mutant it implies, and yields
6653
+ * no mutant whenever the replacement would evaluate an argument twice or lean on
6654
+ * a moved argument that can yield.
6655
+ */
6656
+ const NO_MUTANTS$1 = [];
6657
+ const synchronizationRemovalMutator = (node, context) => Option.match(resolveEffectCall(node, context), {
6658
+ onNone: () => NO_MUTANTS$1,
6659
+ onSome: (call) => Option.toArray(synchronizationRemovalReplacement(call))
6660
+ });
6661
+ const synchronizationRemovalReplacement = (call) => Match.value(call).pipe(Match.when(isWithPermits, withPermitsReplacement), Match.when(isWithPermit, withPermitReplacement), Match.when(isUninterruptible, uninterruptibleReplacement), Match.when(isUninterruptibleMask, uninterruptibleMaskReplacement), Match.orElse(() => Option.none()));
6662
+ const isWithPermits = (call) => Bool.and(call.module === "Semaphore", call.exportName === "withPermits");
6663
+ const isWithPermit = (call) => Bool.and(call.module === "Semaphore", call.exportName === "withPermit");
6664
+ const isUninterruptible = (call) => Bool.and(call.module === "Effect", call.exportName === "uninterruptible");
6665
+ const isUninterruptibleMask = (call) => Bool.and(call.module === "Effect", call.exportName === "uninterruptibleMask");
6666
+ const isDataFirst = (form) => form === "data-first";
6667
+ const isPipedReference = (form) => form === "piped-reference";
6668
+ const withPermitsReplacement = (call) => Match.value(call.form).pipe(Match.when(isDataFirst, () => whenDroppable(call, [0, 1], (guarded) => argumentReplacement(guarded, 2))), Match.orElse(() => whenDroppable(call, [0, 1], identityFunctionOf)));
6669
+ const withPermitReplacement = (call) => Match.value(call.form).pipe(Match.when(isDataFirst, () => whenDroppable(call, [0], (guarded) => argumentReplacement(guarded, 1))), Match.orElse(() => whenDroppable(call, [0], identityFunctionOf)));
6670
+ const uninterruptibleReplacement = (call) => Match.value(call.form).pipe(Match.when(isPipedReference, () => Option.some(identityFunction(call))), Match.orElse(() => argumentReplacement(call, 0)));
6671
+ const uninterruptibleMaskReplacement = (call) => whenMovable(call, [0], (masked) => Option.flatMap(masked.moduleAccess("Effect"), (effectModule) => Option.map(Arr.get(masked.args, 0), (f) => maskedReplacement(effectModule, f))));
6672
+ const maskedReplacement = (effectModule, f) => moduleCall(effectModule, "suspend", [arrowFunctionExpression([], callExpression(cloneNode(f), [memberExpression(effectModule, identifier("interruptible"), false)]))]);
6673
+ const argumentReplacement = (call, index) => Option.map(Arr.get(call.args, index), (argument) => cloneNode(argument));
6674
+ const identityFunctionOf = (call) => Option.some(identityFunction(call));
6675
+ const identityFunction = (call) => {
6676
+ const name = freshIdentifier("self", takenNames(call));
6677
+ return arrowFunctionExpression([identifier(name)], identifier(name));
6678
+ };
6679
+ const takenNames = (call) => new Set(call.args.flatMap((argument) => identifiersIn(argument)));
6680
+ const whenDroppable = (call, indices, build) => Option.flatMap(onlyWhen(argumentsDroppable(call, indices), call), build);
6681
+ const whenMovable = (call, indices, build) => Option.flatMap(onlyWhen(argumentsMovable(call, indices), call), build);
6682
+ const argumentsDroppable = (call, indices) => indices.every((index) => argumentSatisfies(call, index, isDroppableArgument));
6683
+ const argumentsMovable = (call, indices) => indices.every((index) => argumentSatisfies(call, index, isMovableArgument));
6684
+ const argumentSatisfies = (call, index, predicate) => Option.exists(Arr.get(call.args, index), predicate);
6685
+ /**
6091
6686
  * Mutator — every mutation operator and its registry.
6092
6687
  */
6093
6688
  /**
@@ -6920,8 +7515,11 @@ function isUpdateExpression(node) {
6920
7515
  *
6921
7516
  * Naming each mutator here costs one line when a mutator is added and makes
6922
7517
  * that line a compile-checked import instead of a runtime effect.
7518
+ *
7519
+ * `optInMutators` below follows the same hand-written rule, for the same
7520
+ * reason: a mutator exists for a run only when a human named it here.
6923
7521
  */
6924
- const allMutators = Object.freeze({
7522
+ const defaultMutators = Object.freeze({
6925
7523
  ArithmeticOperator: arithmeticOperatorMutator,
6926
7524
  ArrayDeclaration: arrayDeclarationMutator,
6927
7525
  ArrowFunction: arrowFunctionMutator,
@@ -6939,6 +7537,28 @@ const allMutators = Object.freeze({
6939
7537
  UnaryOperator: unaryOperatorMutator,
6940
7538
  UpdateOperator: updateOperatorMutator
6941
7539
  });
7540
+ const optInMutators = Object.freeze({
7541
+ AtomicUpdateSplit: atomicUpdateSplitMutator,
7542
+ SynchronizationRemoval: synchronizationRemovalMutator,
7543
+ FinalizerEscape: finalizerEscapeMutator
7544
+ });
7545
+ /**
7546
+ * The entries a run applies, and the names its directives may reference.
7547
+ *
7548
+ * Naming is additive, never a whitelist: `optInMutations` adds entries on top
7549
+ * of the defaults and removes none. Selection walks the registry's declared
7550
+ * order, not the order the run listed its names in, so a mutant's identity
7551
+ * never depends on how a config happened to spell the list; a name listed
7552
+ * twice selects its entry once. An unknown name selects nothing here —
7553
+ * `instrument` refuses it before any file is parsed, because a typo that
7554
+ * silently enables nothing removes mutants and raises the score.
7555
+ *
7556
+ * Pure: a registry and a run's names in, entries and names out.
7557
+ */
7558
+ const selectMutators = (registry, optInMutations) => ({
7559
+ active: [...Object.entries(registry.defaults), ...Object.entries(registry.optIn).filter(([name]) => optInMutations.includes(name))],
7560
+ known: [...Object.keys(registry.defaults), ...Object.keys(registry.optIn)]
7561
+ });
6942
7562
  S.TaggedError("@systemfsoftware/stryker-js-instrumenter/Transformer.schema/PlacementFailed")("PlacementFailed", {
6943
7563
  fileName: S.String,
6944
7564
  placerName: S.String,
@@ -6952,6 +7572,10 @@ S.TaggedError("@systemfsoftware/stryker-js-instrumenter/Transformer.schema/Trans
6952
7572
  const STRYKER_NAMESPACE_HELPER = "stryNS_9fa48";
6953
7573
  const COVER_MUTANT_HELPER = "stryCov_9fa48";
6954
7574
  const IS_MUTANT_ACTIVE_HELPER = "stryMutAct_9fa48";
7575
+ const DEFAULT_MUTATOR_REGISTRY = {
7576
+ defaults: defaultMutators,
7577
+ optIn: optInMutators
7578
+ };
6955
7579
  function createMutantCollector() {
6956
7580
  return [];
6957
7581
  }
@@ -6987,11 +7611,11 @@ function directiveApplies(directive, lowerMutatorName, line) {
6987
7611
  const NO_COMMENTS = [];
6988
7612
  const PARSE_FAILURE = "Stryker directive without directive type or mutators";
6989
7613
  const MISSING_LOCATION = "Comment without location";
6990
- function processStrykerDirectives(rule, node, allMutatorNames, originFileName) {
7614
+ function processStrykerDirectives(rule, node, knownMutatorNames, originFileName, nodeLine) {
6991
7615
  const directives = attachedComments(node).map(parseStrykerDirective).flatMap(Option.toArray);
6992
- const warnings = directives.flatMap((directive) => mutatorWarnings(directive, allMutatorNames, originFileName));
7616
+ const warnings = directives.flatMap((directive) => mutatorWarnings(directive, knownMutatorNames, originFileName));
6993
7617
  return {
6994
- rule: directives.reduce(applyStrykerDirective, rule),
7618
+ rule: directives.map((directive) => nextLineOn(directive, nodeLine)).reduce(applyStrykerDirective, rule),
6995
7619
  warnings
6996
7620
  };
6997
7621
  }
@@ -7020,36 +7644,39 @@ function strykerDirective(match, loc) {
7020
7644
  function matchGroup(match, group) {
7021
7645
  return Option.getOrThrowWith(Option.fromNullishOr(match[group]), () => /* @__PURE__ */ new Error(PARSE_FAILURE));
7022
7646
  }
7023
- function applyStrykerDirective(rule, directive) {
7024
- return Match.value(directive.type).pipe(Match.when("disable", () => ignoreRuleFor(rule, directive)), Match.when("restore", () => restoreRuleFor(rule, directive)), Match.orElse(() => rule));
7647
+ const nextLineOn = (directive, nodeLine) => ({
7648
+ directive,
7649
+ nodeLine
7650
+ });
7651
+ function applyStrykerDirective(rule, directed) {
7652
+ return Match.value(directed.directive.type).pipe(Match.when("disable", () => ignoreRuleFor(rule, directed)), Match.when("restore", () => restoreRuleFor(rule, directed)), Match.orElse(() => rule));
7025
7653
  }
7026
- function ignoreRuleFor(rule, directive) {
7654
+ function ignoreRuleFor(rule, directed) {
7027
7655
  return {
7028
7656
  kind: "Ignore",
7029
- mutatorNames: directive.mutatorNames.map((mutatorName) => mutatorName.toLowerCase()),
7030
- line: directiveLine(directive),
7031
- ignoreReason: directive.reason,
7657
+ mutatorNames: directed.directive.mutatorNames.map((mutatorName) => mutatorName.toLowerCase()),
7658
+ line: directiveLine(directed),
7659
+ ignoreReason: directed.directive.reason,
7032
7660
  previous: rule
7033
7661
  };
7034
7662
  }
7035
- function restoreRuleFor(rule, directive) {
7663
+ function restoreRuleFor(rule, directed) {
7036
7664
  return {
7037
7665
  kind: "Restore",
7038
- mutatorNames: directive.mutatorNames.map((mutatorName) => mutatorName.toLowerCase()),
7039
- line: directiveLine(directive),
7666
+ mutatorNames: directed.directive.mutatorNames.map((mutatorName) => mutatorName.toLowerCase()),
7667
+ line: directiveLine(directed),
7040
7668
  previous: rule
7041
7669
  };
7042
7670
  }
7043
- /** `next-line` directives carry the line they were written on; a block directive carries none. */
7044
- function directiveLine(directive) {
7045
- return Match.value(directive.scope).pipe(Match.when("next-line", () => commentLocation(directive.loc).start.line), Match.orElse(() => void 0));
7671
+ function directiveLine(directed) {
7672
+ return Match.value(directed.directive.scope).pipe(Match.when("next-line", () => directed.nodeLine), Match.orElse(() => void 0));
7046
7673
  }
7047
7674
  function commentLocation(loc) {
7048
7675
  return Option.getOrThrowWith(Option.fromNullishOr(loc), () => /* @__PURE__ */ new Error(MISSING_LOCATION));
7049
7676
  }
7050
- /** Warnings for the directive's mutator names that the configured mutators do not know. */
7051
- function mutatorWarnings(directive, allMutatorNames, originFileName) {
7052
- return directive.mutatorNames.filter((mutatorName) => mutatorName !== WILDCARD).filter((mutatorName) => !allMutatorNames.includes(mutatorName.toLowerCase())).map((mutatorName) => mutatorWarning(directive, mutatorName, originFileName));
7677
+ /** Warnings for the directive's mutator names that no registered mutator answers to, selected or not. */
7678
+ function mutatorWarnings(directive, knownMutatorNames, originFileName) {
7679
+ return directive.mutatorNames.filter((mutatorName) => mutatorName !== WILDCARD).filter((mutatorName) => !knownMutatorNames.includes(mutatorName.toLowerCase())).map((mutatorName) => mutatorWarning(directive, mutatorName, originFileName));
7053
7680
  }
7054
7681
  function mutatorWarning(directive, mutatorName, originFileName) {
7055
7682
  const loc = commentLocation(directive.loc);
@@ -7535,19 +8162,21 @@ const emptyAppliedMutants = () => /* @__PURE__ */ new Map();
7535
8162
  function isMutateRangeList(value) {
7536
8163
  return Array.isArray(value);
7537
8164
  }
7538
- const transformScript = ({ root, originFileName, rawContent, offset, comments }, mutantCollector, { options, mutateDescription, basePath }, mutators, mutantPlacers) => {
8165
+ const transformScript = ({ root, originFileName, rawContent, offset, comments }, mutantCollector, { options, mutateDescription, basePath }, registry, mutantPlacers) => {
7539
8166
  const placementMap = /* @__PURE__ */ new Map();
7540
8167
  return Effect$1.gen(function* () {
7541
8168
  const lineTable = buildLineTable(rawContent);
7542
8169
  attachComments(root, comments, lineTable);
7543
8170
  let directiveRule = rootRule;
7544
- const mutatorEntries = Object.entries(Option.getOrElse(Option.fromNullishOr(mutators), () => allMutators));
8171
+ const mutatorRegistry = Option.getOrElse(Option.fromNullishOr(registry), () => DEFAULT_MUTATOR_REGISTRY);
8172
+ const selection = selectMutators(mutatorRegistry, options.optInMutations);
8173
+ const mutatorEntries = selection.active;
7545
8174
  const placers = Option.getOrElse(Option.fromNullishOr(mutantPlacers), () => allMutantPlacers);
7546
- const allMutatorNames = mutatorEntries.map(([name]) => name.toLowerCase());
8175
+ const knownMutatorNames = selection.known.map((name) => name.toLowerCase());
7547
8176
  const warnings = [];
7548
8177
  traverse(root, {
7549
8178
  enter(path) {
7550
- const result = processStrykerDirectives(directiveRule, path.node, allMutatorNames, originFileName);
8179
+ const result = processStrykerDirectives(directiveRule, path.node, knownMutatorNames, originFileName, nodeStartLine(path.node));
7551
8180
  directiveRule = result.rule;
7552
8181
  warnings.push(...result.warnings);
7553
8182
  visitNode(path);
@@ -7557,6 +8186,12 @@ const transformScript = ({ root, originFileName, rawContent, offset, comments },
7557
8186
  if (hasAppliedMutants(placement)) applyPlacement(path, placement);
7558
8187
  }
7559
8188
  });
8189
+ function nodeStartLine(node) {
8190
+ return Option.match(Option.fromNullishOr(spanOf(node)), {
8191
+ onNone: () => 0,
8192
+ onSome: (span) => positionFromLineTable(span.start, lineTable).line
8193
+ });
8194
+ }
7560
8195
  yield* placeHeaderIfNeeded(mutantCollector, originFileName, options, root);
7561
8196
  return warnings;
7562
8197
  function visitNode(path) {
@@ -7828,9 +8463,26 @@ function toOneBasedLineNumber(range) {
7828
8463
  function isIgnorer(value) {
7829
8464
  return Predicate.isObject(value) && typeof value["shouldIgnore"] === "function";
7830
8465
  }
8466
+ const EMPTY_MUTATION_NAMES = [];
8467
+ const KNOWN_OPT_IN_MUTATIONS = Object.keys(optInMutators);
8468
+ const requestedOptInMutations = (options) => Option.getOrElse(Option.fromNullishOr(options.optInMutations), () => EMPTY_MUTATION_NAMES);
8469
+ const unknownOptInMutations = (requested) => requested.filter((name) => !KNOWN_OPT_IN_MUTATIONS.includes(name));
8470
+ const listNames = (names) => Option.match(Option.fromNullishOr(names.at(0)), {
8471
+ onNone: () => "none",
8472
+ onSome: () => names.map((name) => `'${name}'`).join(", ")
8473
+ });
8474
+ const unknownOptInMutationsError = (requested) => Option.map(Option.fromNullishOr(unknownOptInMutations(requested).at(0)), () => InstrumentError.make({
8475
+ message: `Unknown opt-in mutations: ${listNames(unknownOptInMutations(requested))}. Known opt-in mutations: ${listNames(KNOWN_OPT_IN_MUTATIONS)}.`,
8476
+ cause: void 0
8477
+ }));
8478
+ const refuseUnknownOptInMutations = (requested) => Option.match(unknownOptInMutationsError(requested), {
8479
+ onNone: () => Effect$1.void,
8480
+ onSome: (error) => Effect$1.fail(error)
8481
+ });
7831
8482
  function toTransformerOptions(options) {
7832
8483
  const base = {
7833
8484
  excludedMutations: [...options.excludedMutations],
8485
+ optInMutations: [...requestedOptInMutations(options)],
7834
8486
  ignorers: options.ignorers.filter(isIgnorer)
7835
8487
  };
7836
8488
  if (options.noHeader !== void 0) return {
@@ -7852,6 +8504,7 @@ function printedFile(file, ast) {
7852
8504
  }];
7853
8505
  }
7854
8506
  const instrument = (files, options, basePath) => Effect$1.gen(function* () {
8507
+ yield* refuseUnknownOptInMutations(requestedOptInMutations(options));
7855
8508
  const schemaFiles = files.map((file) => ({
7856
8509
  name: file.name,
7857
8510
  content: file.content,
@@ -8438,7 +9091,10 @@ const OrderedMutationScoreThresholds = S.declare(isMutationScoreThresholds, {
8438
9091
  })
8439
9092
  });
8440
9093
  const MutationScoreThresholdsSchema = MutationScoreThresholdsValuesSchema.pipe(S.decodeTo(OrderedMutationScoreThresholds, SchemaTransformation.passthrough()));
8441
- const MutatorDescriptor = S.Struct({ excludedMutations: defaulted(S.Array(S.String), []) });
9094
+ const MutatorDescriptor = S.Struct({
9095
+ excludedMutations: defaulted(S.Array(S.String), []),
9096
+ optInMutations: defaulted(S.Array(S.String), [])
9097
+ });
8442
9098
  const WarningOptions = openStruct({
8443
9099
  unknownOptions: defaulted(S.Boolean, true),
8444
9100
  preprocessorErrors: defaulted(S.Boolean, true),
@@ -8490,7 +9146,10 @@ const StrykerOptionsSchema = S.StructWithRest(S.Struct({
8490
9146
  maxConcurrentTestRunners: defaulted(S.Finite, 9007199254740991),
8491
9147
  maxTestRunnerReuse: defaulted(S.Finite, 0),
8492
9148
  mutate: defaulted(S.Array(S.String), ["{src,lib}/**/!(*.+(s|S)pec|*.+(t|T)est).+(cjs|mjs|js|ts|mts|cts|jsx|tsx|html|vue|svelte)", "!{src,lib}/**/__tests__/**/*.+(cjs|mjs|js|ts|mts|cts|jsx|tsx|html|vue|svelte)"]),
8493
- mutator: defaulted(MutatorDescriptor, { excludedMutations: [] }),
9149
+ mutator: defaulted(MutatorDescriptor, {
9150
+ excludedMutations: [],
9151
+ optInMutations: []
9152
+ }),
8494
9153
  packageManager: S.optional(PackageManager),
8495
9154
  plugins: defaulted(S.Array(PluginFileUrl), []),
8496
9155
  appendPlugins: defaulted(S.Array(PluginFileUrl), []),