@mutineerjs/mutineer 0.2.4 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +48 -42
  2. package/dist/bin/mutineer.js +0 -0
  3. package/dist/mutators/__tests__/operator.spec.js +169 -0
  4. package/dist/mutators/__tests__/registry.spec.js +6 -0
  5. package/dist/mutators/__tests__/return-value.spec.js +239 -0
  6. package/dist/mutators/__tests__/utils.spec.js +68 -1
  7. package/dist/mutators/operator.d.ts +25 -0
  8. package/dist/mutators/operator.js +50 -0
  9. package/dist/mutators/registry.d.ts +6 -28
  10. package/dist/mutators/registry.js +14 -66
  11. package/dist/mutators/return-value.d.ts +39 -0
  12. package/dist/mutators/return-value.js +104 -0
  13. package/dist/mutators/utils.d.ts +21 -0
  14. package/dist/mutators/utils.js +44 -27
  15. package/dist/runner/__tests__/pool-executor.spec.js +2 -4
  16. package/dist/runner/__tests__/variants.spec.js +3 -1
  17. package/dist/runner/discover.js +2 -1
  18. package/dist/runner/jest/__tests__/adapter.spec.js +1 -1
  19. package/dist/runner/jest/__tests__/pool.spec.js +5 -6
  20. package/dist/runner/jest/__tests__/worker-runtime.spec.js +2 -2
  21. package/dist/runner/jest/adapter.js +1 -1
  22. package/dist/runner/jest/pool.d.ts +1 -1
  23. package/dist/runner/jest/pool.js +6 -6
  24. package/dist/runner/jest/worker.mjs +1 -1
  25. package/dist/runner/pool-executor.js +1 -1
  26. package/dist/runner/shared/__tests__/redirect-state.spec.js +3 -3
  27. package/dist/runner/shared/index.d.ts +1 -1
  28. package/dist/runner/shared/index.js +1 -1
  29. package/dist/runner/shared/redirect-state.d.ts +2 -2
  30. package/dist/runner/shared/redirect-state.js +4 -4
  31. package/dist/runner/types.d.ts +1 -1
  32. package/dist/runner/vitest/__tests__/adapter.spec.js +1 -1
  33. package/dist/runner/vitest/__tests__/pool.spec.js +1 -1
  34. package/dist/runner/vitest/__tests__/redirect-loader.spec.js +83 -1
  35. package/dist/runner/vitest/__tests__/worker-runtime.spec.js +84 -0
  36. package/dist/runner/vitest/adapter.js +1 -1
  37. package/dist/runner/vitest/index.d.ts +0 -1
  38. package/dist/runner/vitest/index.js +0 -1
  39. package/dist/runner/vitest/pool.d.ts +1 -1
  40. package/dist/runner/vitest/pool.js +7 -7
  41. package/dist/runner/vitest/redirect-loader.d.ts +1 -1
  42. package/dist/runner/vitest/redirect-loader.js +1 -1
  43. package/dist/runner/vitest/worker-runtime.js +3 -3
  44. package/dist/runner/vitest/worker.mjs +1 -1
  45. package/dist/utils/__tests__/coverage.spec.js +167 -0
  46. package/dist/utils/__tests__/progress.spec.js +96 -0
  47. package/package.json +71 -22
  48. package/dist/admin/assets/index-B7nXq-e7.js +0 -32
  49. package/dist/admin/assets/index-B7nXq-e7.js.map +0 -1
  50. package/dist/admin/assets/index-BDQLkBUE.js +0 -32
  51. package/dist/admin/assets/index-BDQLkBUE.js.map +0 -1
  52. package/dist/admin/assets/index-DVkP-Tc7.css +0 -1
  53. package/dist/admin/index.html +0 -13
  54. package/dist/admin/server/admin.d.ts +0 -6
  55. package/dist/admin/server/admin.js +0 -234
  56. package/dist/bin/mutate-vitest.d.ts +0 -2
  57. package/dist/bin/mutate-vitest.js +0 -90
  58. package/dist/plugin/viteMutate.d.ts +0 -15
  59. package/dist/plugin/viteMutate.js +0 -52
  60. package/dist/plugin/vitest.setup.d.ts +0 -47
  61. package/dist/plugin/vitest.setup.js +0 -118
  62. package/dist/plugin/withVitest.d.ts +0 -13
  63. package/dist/plugin/withVitest.js +0 -30
  64. package/dist/runner/__tests__/orchestrator.spec.js +0 -55
  65. package/dist/runner/adapters/__tests__/jest.spec.js +0 -88
  66. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.d.ts +0 -1
  67. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.js +0 -59
  68. package/dist/runner/adapters/__tests__/vitest.spec.d.ts +0 -1
  69. package/dist/runner/adapters/__tests__/vitest.spec.js +0 -118
  70. package/dist/runner/adapters/index.d.ts +0 -10
  71. package/dist/runner/adapters/index.js +0 -9
  72. package/dist/runner/adapters/jest/__tests__/index.spec.d.ts +0 -1
  73. package/dist/runner/adapters/jest/__tests__/index.spec.js +0 -88
  74. package/dist/runner/adapters/jest/index.d.ts +0 -24
  75. package/dist/runner/adapters/jest/index.js +0 -216
  76. package/dist/runner/adapters/jest/worker-runtime.d.ts +0 -37
  77. package/dist/runner/adapters/jest/worker-runtime.js +0 -171
  78. package/dist/runner/adapters/jest-worker-runtime.d.ts +0 -37
  79. package/dist/runner/adapters/jest-worker-runtime.js +0 -171
  80. package/dist/runner/adapters/jest.d.ts +0 -24
  81. package/dist/runner/adapters/jest.js +0 -216
  82. package/dist/runner/adapters/types.d.ts +0 -89
  83. package/dist/runner/adapters/types.js +0 -8
  84. package/dist/runner/adapters/vitest/__tests__/index.spec.d.ts +0 -1
  85. package/dist/runner/adapters/vitest/__tests__/index.spec.js +0 -118
  86. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.d.ts +0 -1
  87. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.js +0 -59
  88. package/dist/runner/adapters/vitest/index.d.ts +0 -33
  89. package/dist/runner/adapters/vitest/index.js +0 -267
  90. package/dist/runner/adapters/vitest/worker-runtime.d.ts +0 -25
  91. package/dist/runner/adapters/vitest/worker-runtime.js +0 -118
  92. package/dist/runner/adapters/vitest-worker-runtime.d.ts +0 -25
  93. package/dist/runner/adapters/vitest-worker-runtime.js +0 -118
  94. package/dist/runner/adapters/vitest.d.ts +0 -33
  95. package/dist/runner/adapters/vitest.js +0 -267
  96. package/dist/runner/pool/__tests__/index.spec.d.ts +0 -1
  97. package/dist/runner/pool/__tests__/index.spec.js +0 -83
  98. package/dist/runner/pool/__tests__/pool-plugin.spec.d.ts +0 -1
  99. package/dist/runner/pool/__tests__/pool-plugin.spec.js +0 -59
  100. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.d.ts +0 -1
  101. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.js +0 -78
  102. package/dist/runner/pool/index.d.ts +0 -8
  103. package/dist/runner/pool/index.js +0 -9
  104. package/dist/runner/pool/jest/pool.d.ts +0 -52
  105. package/dist/runner/pool/jest/pool.js +0 -309
  106. package/dist/runner/pool/jest/worker.d.mts +0 -1
  107. package/dist/runner/pool/jest/worker.mjs +0 -60
  108. package/dist/runner/pool/jest-pool.d.ts +0 -52
  109. package/dist/runner/pool/jest-pool.js +0 -309
  110. package/dist/runner/pool/jest-worker.d.mts +0 -1
  111. package/dist/runner/pool/jest-worker.mjs +0 -60
  112. package/dist/runner/pool/plugin.d.ts +0 -18
  113. package/dist/runner/pool/plugin.js +0 -60
  114. package/dist/runner/pool/pool-plugin.d.ts +0 -18
  115. package/dist/runner/pool/pool-plugin.js +0 -60
  116. package/dist/runner/pool/pool-redirect-loader.d.ts +0 -19
  117. package/dist/runner/pool/pool-redirect-loader.js +0 -116
  118. package/dist/runner/pool/pool-redirect-loader.mjs +0 -146
  119. package/dist/runner/pool/redirect-loader.d.ts +0 -19
  120. package/dist/runner/pool/redirect-loader.js +0 -116
  121. package/dist/runner/pool/vitest/pool.d.ts +0 -70
  122. package/dist/runner/pool/vitest/pool.js +0 -376
  123. package/dist/runner/pool/vitest/worker.d.mts +0 -15
  124. package/dist/runner/pool/vitest/worker.mjs +0 -96
  125. package/dist/runner/pool/vitest-worker.d.mts +0 -15
  126. package/dist/runner/pool/vitest-worker.mjs +0 -96
  127. package/dist/runner/shared-module-redirect.d.ts +0 -56
  128. package/dist/runner/shared-module-redirect.js +0 -84
  129. package/dist/types/api.d.ts +0 -20
  130. package/dist/types/api.js +0 -1
  131. /package/dist/{runner/__tests__/orchestrator.spec.d.ts → mutators/__tests__/operator.spec.d.ts} +0 -0
  132. /package/dist/{runner/adapters/__tests__/jest.spec.d.ts → mutators/__tests__/return-value.spec.d.ts} +0 -0
@@ -1 +0,0 @@
1
- @import"https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap";*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{color-scheme:dark}*{box-sizing:border-box}body{min-height:100vh;--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity));font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;margin:0}#app{padding:2rem}a{color:inherit}button{font:inherit}.static{position:static}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-6{margin-bottom:1.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.w-12{width:3rem}.w-4{width:1rem}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-6xl{max-width:72rem}.flex-1{flex:1 1 0%}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity))}.bg-green-500\/10{background-color:#22c55e1a}.bg-red-500\/10{background-color:#ef44441a}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.bg-slate-900\/60{background-color:#0f172a99}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity))}.bg-slate-950\/60{background-color:#02061799}.bg-slate-950\/70{background-color:#020617b3}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-sky-500{--tw-gradient-from: #0ea5e9 var(--tw-gradient-from-position);--tw-gradient-to: rgb(14 165 233 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-indigo-500{--tw-gradient-to: #6366f1 var(--tw-gradient-to-position)}.p-1{padding:.25rem}.p-12{padding:3rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pl-0{padding-left:0}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-6{line-height:1.5rem}.tracking-\[0\.08em\]{letter-spacing:.08em}.tracking-\[0\.1em\]{letter-spacing:.1em}.tracking-\[0\.2em\]{letter-spacing:.2em}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-sky-400{--tw-text-opacity: 1;color:rgb(56 189 248 / var(--tw-text-opacity))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.text-slate-950{--tw-text-opacity: 1;color:rgb(2 6 23 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.placeholder\:text-slate-500::-moz-placeholder{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.placeholder\:text-slate-500::placeholder{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.even\:bg-slate-800\/30:nth-child(2n){background-color:#1e293b4d}.hover\:bg-slate-800\/40:hover{background-color:#1e293b66}.hover\:brightness-110:hover{--tw-brightness: brightness(1.1);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.focus\:border-sky-400:focus{--tw-border-opacity: 1;border-color:rgb(56 189 248 / var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-sky-400:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(56 189 248 / var(--tw-ring-opacity))}.focus\:ring-sky-400\/60:focus{--tw-ring-color: rgb(56 189 248 / .6)}.disabled\:cursor-progress:disabled{cursor:progress}.disabled\:opacity-70:disabled{opacity:.7}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:text-right{text-align:right}}@media (min-width: 768px){.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}
@@ -1,13 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Mutineer Admin</title>
7
- <script type="module" crossorigin src="/assets/index-B7nXq-e7.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-DVkP-Tc7.css">
9
- </head>
10
- <body>
11
- <div id="app"></div>
12
- </body>
13
- </html>
@@ -1,6 +0,0 @@
1
- interface AdminServerOptions {
2
- port?: number;
3
- configPath?: string;
4
- }
5
- export declare function runAdminServer(cwd: string, options?: AdminServerOptions): Promise<void>;
6
- export {};
@@ -1,234 +0,0 @@
1
- import http from 'node:http';
2
- import path from 'node:path';
3
- import fs from 'node:fs/promises';
4
- import { fileURLToPath } from 'node:url';
5
- import { diffLines } from 'diff';
6
- import { readMutantCache } from '../../runner/orchestrator.js';
7
- import { mutateModuleSource } from '../../core/module.js';
8
- import { mutateVueSfcScriptSetup } from '../../core/sfc.js';
9
- import { loadMutineerConfig } from '../../runner/config.js';
10
- const mimeTypes = {
11
- '.html': 'text/html; charset=utf-8',
12
- '.js': 'application/javascript; charset=utf-8',
13
- '.css': 'text/css; charset=utf-8',
14
- '.json': 'application/json; charset=utf-8',
15
- '.svg': 'image/svg+xml',
16
- '.ico': 'image/x-icon',
17
- '.png': 'image/png',
18
- '.jpg': 'image/jpeg',
19
- '.jpeg': 'image/jpeg',
20
- '.map': 'application/json; charset=utf-8'
21
- };
22
- function toResultList(cache, cwd) {
23
- return Object.entries(cache).map(([key, entry]) => ({
24
- id: key,
25
- relativePath: path.relative(cwd, entry.file) || entry.file,
26
- ...entry
27
- }));
28
- }
29
- async function serveStatic(filePath, res) {
30
- try {
31
- const data = await fs.readFile(filePath);
32
- const ext = path.extname(filePath);
33
- res.statusCode = 200;
34
- res.setHeader('Content-Type', mimeTypes[ext] ?? 'application/octet-stream');
35
- res.setHeader('Cache-Control', ext === '.html' ? 'no-cache' : 'public, max-age=31536000, immutable');
36
- res.end(data);
37
- }
38
- catch (err) {
39
- const code = err.code;
40
- if (code === 'ENOENT') {
41
- res.statusCode = 404;
42
- res.end('Not found');
43
- return;
44
- }
45
- res.statusCode = 500;
46
- res.end('Internal server error');
47
- }
48
- }
49
- async function handleApiRequest(cwd, res) {
50
- const cache = await readMutantCache(cwd);
51
- const payload = { mutants: toResultList(cache, cwd) };
52
- res.statusCode = 200;
53
- res.setHeader('Content-Type', 'application/json; charset=utf-8');
54
- res.setHeader('Cache-Control', 'no-store');
55
- res.end(JSON.stringify(payload));
56
- }
57
- async function handleConfigRequest(cwd, res, configPath) {
58
- res.statusCode = 200;
59
- res.setHeader('Content-Type', 'application/json; charset=utf-8');
60
- try {
61
- const config = await loadMutineerConfig(cwd, configPath);
62
- const payload = { config };
63
- res.setHeader('Cache-Control', 'no-store');
64
- res.end(JSON.stringify(payload));
65
- }
66
- catch (err) {
67
- const message = err instanceof Error ? err.message : 'Unable to load config';
68
- const payload = { error: message };
69
- res.statusCode = 404;
70
- res.end(JSON.stringify(payload));
71
- }
72
- }
73
- function ensureWithinCwd(cwd, target) {
74
- const normalisedCwd = path.resolve(cwd);
75
- const resolved = path.resolve(target);
76
- const relative = path.relative(normalisedCwd, resolved);
77
- if (relative.startsWith('..') || path.isAbsolute(relative)) {
78
- throw new Error('Requested file is outside the project directory');
79
- }
80
- return resolved;
81
- }
82
- async function readFileFromProject(cwd, requestedPath) {
83
- let resolved;
84
- try {
85
- resolved = ensureWithinCwd(cwd, requestedPath);
86
- }
87
- catch {
88
- throw Object.assign(new Error('Access denied'), { status: 403 });
89
- }
90
- try {
91
- const code = await fs.readFile(resolved, 'utf8');
92
- return { resolved, code };
93
- }
94
- catch (err) {
95
- const codeErr = err;
96
- if (codeErr.code === 'ENOENT') {
97
- throw Object.assign(new Error('File not found'), { status: 404 });
98
- }
99
- throw Object.assign(new Error('Unable to read file'), { status: 500 });
100
- }
101
- }
102
- function findVariantForMutator(filePath, code, mutator, line, col) {
103
- const isVue = filePath.toLowerCase().endsWith('.vue');
104
- const variants = isVue ? mutateVueSfcScriptSetup(filePath, code) : mutateModuleSource(code);
105
- return variants.find(v => v.name === mutator && v.line === line && v.col === col);
106
- }
107
- function buildDiff(original, mutated) {
108
- const parts = diffLines(original, mutated);
109
- const lines = [];
110
- let leftLine = 1;
111
- let rightLine = 1;
112
- for (const part of parts) {
113
- const chunkLines = part.value.split(/\r?\n/);
114
- if (chunkLines[chunkLines.length - 1] === '')
115
- chunkLines.pop();
116
- for (const chunk of chunkLines) {
117
- if (part.added) {
118
- lines.push({ type: 'add', rightNumber: rightLine++, text: chunk });
119
- }
120
- else if (part.removed) {
121
- lines.push({ type: 'remove', leftNumber: leftLine++, text: chunk });
122
- }
123
- else {
124
- lines.push({
125
- type: 'context',
126
- leftNumber: leftLine++,
127
- rightNumber: rightLine++,
128
- text: chunk
129
- });
130
- }
131
- }
132
- }
133
- return { lines };
134
- }
135
- async function handleDiffRequest(cwd, url, res) {
136
- res.setHeader('Content-Type', 'application/json; charset=utf-8');
137
- const fileParam = url.searchParams.get('file');
138
- const mutator = url.searchParams.get('mutator');
139
- const lineParam = url.searchParams.get('line');
140
- const colParam = url.searchParams.get('col');
141
- if (!fileParam || !mutator || !lineParam || !colParam) {
142
- res.statusCode = 400;
143
- res.end(JSON.stringify({ error: 'Missing query parameters' }));
144
- return;
145
- }
146
- const line = Number(lineParam);
147
- const col = Number(colParam);
148
- if (!Number.isFinite(line) || !Number.isFinite(col)) {
149
- res.statusCode = 400;
150
- res.end(JSON.stringify({ error: 'Invalid line or column' }));
151
- return;
152
- }
153
- let fileData;
154
- try {
155
- const joined = path.isAbsolute(fileParam) ? fileParam : path.join(cwd, fileParam);
156
- fileData = await readFileFromProject(cwd, joined);
157
- }
158
- catch (err) {
159
- const status = typeof err === 'object' && err && 'status' in err && typeof err.status === 'number'
160
- ? err.status
161
- : 500;
162
- const message = err instanceof Error ? err.message : 'Unable to rebuild mutation diff';
163
- res.statusCode = status;
164
- res.end(JSON.stringify({ error: message }));
165
- return;
166
- }
167
- const variant = findVariantForMutator(fileData.resolved, fileData.code, mutator, line, col);
168
- if (!variant) {
169
- res.statusCode = 404;
170
- res.end(JSON.stringify({ error: 'Unable to rebuild mutation diff' }));
171
- return;
172
- }
173
- const diff = buildDiff(fileData.code, variant.code);
174
- res.statusCode = 200;
175
- res.setHeader('Cache-Control', 'no-store');
176
- res.end(JSON.stringify(diff));
177
- }
178
- export async function runAdminServer(cwd, options) {
179
- const port = options?.port ?? 4177;
180
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
181
- const staticDir = path.resolve(__dirname, '..');
182
- const indexPath = path.join(staticDir, 'index.html');
183
- try {
184
- await fs.access(indexPath);
185
- }
186
- catch {
187
- throw new Error(`Admin UI bundle not found at ${indexPath}. Run "npm run build:admin" before starting the server.`);
188
- }
189
- const server = http.createServer(async (req, res) => {
190
- try {
191
- if (!req.url) {
192
- res.statusCode = 400;
193
- res.end('Invalid request');
194
- return;
195
- }
196
- const url = new URL(req.url, `http://localhost:${port}`);
197
- if (url.pathname === '/api/results') {
198
- await handleApiRequest(cwd, res);
199
- return;
200
- }
201
- if (url.pathname === '/api/config') {
202
- await handleConfigRequest(cwd, res, options?.configPath);
203
- return;
204
- }
205
- if (url.pathname === '/api/diff') {
206
- await handleDiffRequest(cwd, url, res);
207
- return;
208
- }
209
- const decodedPath = decodeURIComponent(url.pathname);
210
- let target = path.join(staticDir, decodedPath);
211
- if (decodedPath === '/' || !path.extname(decodedPath)) {
212
- target = path.join(staticDir, 'index.html');
213
- }
214
- await serveStatic(target, res);
215
- }
216
- catch (err) {
217
- console.error('Admin server error:', err);
218
- res.statusCode = 500;
219
- res.end('Internal server error');
220
- }
221
- });
222
- return new Promise((resolve, reject) => {
223
- const stop = () => {
224
- server.close(() => resolve());
225
- };
226
- server.once('error', reject);
227
- process.once('SIGINT', stop);
228
- process.once('SIGTERM', stop);
229
- server.listen(port, () => {
230
- console.log(`\nMutineer admin UI ready at http://localhost:${port}`);
231
- console.log('Press Ctrl+C to stop the server.\n');
232
- });
233
- });
234
- }
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -1,90 +0,0 @@
1
- #!/usr/bin/env node
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import { fileURLToPath, pathToFileURL } from 'node:url';
5
- import { runOrchestrator } from '../runner/orchestrator.js';
6
- // Constants
7
- const DEFAULT_ADMIN_PORT = 4177;
8
- const ADMIN_COMMAND = 'admin';
9
- const CONFIG_FLAGS = ['--config', '-c'];
10
- /**
11
- * Resolves the path to the admin server module, preferring compiled JS over source TS
12
- */
13
- function resolveAdminServerPath() {
14
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
- const compiledPath = path.resolve(__dirname, '../admin/server/admin.js');
16
- return fs.existsSync(compiledPath) ? compiledPath : path.resolve(__dirname, '../../admin/server/admin.ts');
17
- }
18
- /**
19
- * Dynamically imports and returns the admin server function
20
- */
21
- async function loadAdminServer() {
22
- const serverPath = resolveAdminServerPath();
23
- const module = (await import(pathToFileURL(serverPath).href));
24
- return module.runAdminServer;
25
- }
26
- /**
27
- * Parses a numeric argument from argv, supporting both space-separated and inline formats
28
- * @example '--port 3000' or '--port=3000'
29
- */
30
- function parseNumericArg(argv, flag) {
31
- // Space-separated format: --flag value
32
- const idx = argv.findIndex(arg => arg === flag);
33
- if (idx >= 0) {
34
- const value = argv[idx + 1];
35
- if (value) {
36
- const parsed = Number(value);
37
- if (Number.isFinite(parsed))
38
- return parsed;
39
- }
40
- }
41
- // Inline format: --flag=value
42
- const prefix = `${flag}=`;
43
- const inline = argv.find(arg => arg.startsWith(prefix));
44
- if (inline) {
45
- const parsed = Number(inline.slice(prefix.length));
46
- if (Number.isFinite(parsed))
47
- return parsed;
48
- }
49
- return undefined;
50
- }
51
- /**
52
- * Parses a string argument from argv, supporting both space-separated and inline formats
53
- * @example '--config path.ts' or '--config=path.ts'
54
- */
55
- function parseStringArg(argv, flags) {
56
- // Space-separated format: --flag value
57
- const idx = argv.findIndex(arg => flags.includes(arg));
58
- if (idx >= 0) {
59
- const value = argv[idx + 1];
60
- if (value && !value.startsWith('-'))
61
- return value;
62
- }
63
- // Inline format: --flag=value
64
- for (const flag of flags) {
65
- const prefix = `${flag}=`;
66
- const inline = argv.find(arg => arg.startsWith(prefix));
67
- if (inline) {
68
- const value = inline.slice(prefix.length);
69
- if (value)
70
- return value;
71
- }
72
- }
73
- return undefined;
74
- }
75
- /**
76
- * Main entry point - routes to admin server or orchestrator
77
- */
78
- async function main() {
79
- const args = process.argv.slice(2);
80
- if (args[0] === ADMIN_COMMAND) {
81
- const port = parseNumericArg(args, '--port') ?? DEFAULT_ADMIN_PORT;
82
- const configPath = parseStringArg(args, CONFIG_FLAGS);
83
- const runAdminServer = await loadAdminServer();
84
- await runAdminServer(process.cwd(), { port, configPath });
85
- }
86
- else {
87
- await runOrchestrator(args, process.cwd());
88
- }
89
- }
90
- await main();
@@ -1,15 +0,0 @@
1
- import type { PluginOption } from 'vite';
2
- /**
3
- * Vite plugin that intercepts module loading during mutation testing.
4
- *
5
- * When MUTATE_FILE and MUTATE_CODE environment variables are set, this plugin
6
- * will replace the target file's code with the mutated version. This allows
7
- * vitest to run tests against each mutation variant.
8
- *
9
- * Environment Variables:
10
- * - MUTATE_FILE: Absolute path to the file being mutated
11
- * - MUTATE_CODE: The mutated source code to inject
12
- *
13
- * @returns Vite plugin option
14
- */
15
- export declare function viteMutineerPlugin(): PluginOption;
@@ -1,52 +0,0 @@
1
- import path from 'node:path';
2
- /**
3
- * Vite plugin that intercepts module loading during mutation testing.
4
- *
5
- * When MUTATE_FILE and MUTATE_CODE environment variables are set, this plugin
6
- * will replace the target file's code with the mutated version. This allows
7
- * vitest to run tests against each mutation variant.
8
- *
9
- * Environment Variables:
10
- * - MUTATE_FILE: Absolute path to the file being mutated
11
- * - MUTATE_CODE: The mutated source code to inject
12
- *
13
- * @returns Vite plugin option
14
- */
15
- export function viteMutineerPlugin() {
16
- // Normalize and cache the target path during initialization
17
- const mutateFile = process.env.MUTATE_FILE;
18
- const mutateCode = process.env.MUTATE_CODE;
19
- let normalizedTarget = null;
20
- if (mutateFile) {
21
- try {
22
- normalizedTarget = path.resolve(mutateFile);
23
- }
24
- catch (err) {
25
- console.warn(`[mutineer] Failed to resolve MUTATE_FILE: ${mutateFile}`, err);
26
- }
27
- }
28
- return {
29
- name: 'mutineer:swap',
30
- enforce: 'pre',
31
- load(id) {
32
- // Early exit if no mutation is active
33
- if (!normalizedTarget || !mutateCode) {
34
- return null;
35
- }
36
- // Normalize the module ID, handling query strings
37
- const cleanId = id.split('?')[0];
38
- let normalizedId;
39
- try {
40
- normalizedId = path.resolve(cleanId);
41
- }
42
- catch {
43
- return null;
44
- }
45
- // Return mutated code only if this is the target file
46
- if (normalizedId === normalizedTarget) {
47
- return mutateCode;
48
- }
49
- return null;
50
- }
51
- };
52
- }
@@ -1,47 +0,0 @@
1
- /**
2
- * Vitest setup file that decorates test titles with mutation metadata.
3
- *
4
- * When a mutation is active (MUTATE_ACTIVE=1), this file wraps the global
5
- * test functions (it, test, describe) to append mutation labels to test titles.
6
- * This helps identify which mutation caused which test failures in the output.
7
- *
8
- * Environment Variables:
9
- * - MUTATE_ACTIVE: Set to '1' to enable mutation decoration
10
- * - MUTATE_ID: Unique identifier for the mutation (e.g., 'relaxLE#0')
11
- * - MUTATE_NAME: Human-readable name of the mutation (e.g., 'relaxLE')
12
- *
13
- * Example output: "should validate input @relaxLE#0 relaxLE"
14
- */
15
- declare const active: boolean;
16
- declare const id: string;
17
- declare const mutationName: string;
18
- declare const label: string;
19
- /**
20
- * Appends mutation label to test title if not already present.
21
- */
22
- declare function decorateTitle(title: unknown): unknown;
23
- type ItLike = typeof it;
24
- type DescribeLike = typeof describe;
25
- type AnyCallable = (...args: any[]) => unknown;
26
- type Chainers = 'skip' | 'only' | 'todo' | 'concurrent' | 'sequential' | 'fails' | 'retry' | 'runIf' | 'skipIf';
27
- type ChainableRecord = {
28
- each?: AnyCallable;
29
- } & Partial<Record<Chainers, AnyCallable>>;
30
- type ChainableCallable = AnyCallable & ChainableRecord;
31
- /** Chainers that need to be forwarded from original to wrapped callable. */
32
- declare const CHAINERS: readonly Chainers[];
33
- /**
34
- * Helper to attach .each(...) method to a callable.
35
- * Ensures title decoration applies to parameterized tests.
36
- */
37
- declare function attachEachMethod(callable: AnyCallable, each: AnyCallable): AnyCallable;
38
- /**
39
- * Wraps a test function (it/test/describe) to decorate titles with mutation metadata.
40
- * Preserves all chainers, static properties, and symbols from the original function.
41
- */
42
- declare function wrapCallable<T extends AnyCallable>(original: T): T;
43
- declare const g: typeof globalThis & {
44
- it?: ItLike;
45
- test?: ItLike;
46
- describe?: DescribeLike;
47
- };
@@ -1,118 +0,0 @@
1
- "use strict";
2
- /**
3
- * Vitest setup file that decorates test titles with mutation metadata.
4
- *
5
- * When a mutation is active (MUTATE_ACTIVE=1), this file wraps the global
6
- * test functions (it, test, describe) to append mutation labels to test titles.
7
- * This helps identify which mutation caused which test failures in the output.
8
- *
9
- * Environment Variables:
10
- * - MUTATE_ACTIVE: Set to '1' to enable mutation decoration
11
- * - MUTATE_ID: Unique identifier for the mutation (e.g., 'relaxLE#0')
12
- * - MUTATE_NAME: Human-readable name of the mutation (e.g., 'relaxLE')
13
- *
14
- * Example output: "should validate input @relaxLE#0 relaxLE"
15
- */
16
- const active = process.env.MUTATE_ACTIVE === '1';
17
- // Early exit if mutation is not active
18
- if (!active) {
19
- process.exit(0);
20
- }
21
- const id = process.env.MUTATE_ID || '';
22
- const mutationName = process.env.MUTATE_NAME || '';
23
- const label = `@${id} ${mutationName}`;
24
- /**
25
- * Appends mutation label to test title if not already present.
26
- */
27
- function decorateTitle(title) {
28
- return typeof title === 'string' && !title.includes(label)
29
- ? `${title} ${label}`
30
- : title;
31
- }
32
- /** Chainers that need to be forwarded from original to wrapped callable. */
33
- const CHAINERS = [
34
- 'skip',
35
- 'only',
36
- 'todo',
37
- 'concurrent',
38
- 'sequential',
39
- 'fails',
40
- 'retry',
41
- 'runIf',
42
- 'skipIf'
43
- ];
44
- /**
45
- * Helper to attach .each(...) method to a callable.
46
- * Ensures title decoration applies to parameterized tests.
47
- */
48
- function attachEachMethod(callable, each) {
49
- return ((...tableArgs) => {
50
- const chain = each(...tableArgs);
51
- return (title, fn, timeout) => chain(decorateTitle(title), fn, timeout);
52
- });
53
- }
54
- /**
55
- * Wraps a test function (it/test/describe) to decorate titles with mutation metadata.
56
- * Preserves all chainers, static properties, and symbols from the original function.
57
- */
58
- function wrapCallable(original) {
59
- // Base wrapper that decorates the title
60
- const wrapped = ((title, fn, timeout) => {
61
- return original(decorateTitle(title), fn, timeout);
62
- });
63
- const wrappedChainable = wrapped;
64
- const originalChainable = original;
65
- // Forward chainers (skip, only, todo, etc.)
66
- for (const key of CHAINERS) {
67
- const method = originalChainable[key];
68
- if (typeof method !== 'function')
69
- continue;
70
- const forwarded = ((...args) => method.apply(original, args));
71
- wrappedChainable[key] = forwarded;
72
- // Attach .each(...) to chainers that support it
73
- const each = method.each;
74
- if (typeof each === 'function') {
75
- forwarded.each = attachEachMethod(forwarded, each);
76
- }
77
- }
78
- // Attach top-level .each(...)
79
- const topEach = originalChainable.each;
80
- if (typeof topEach === 'function') {
81
- wrappedChainable.each = attachEachMethod(wrapped, topEach);
82
- }
83
- // Preserve static properties and symbols from original
84
- const wrappedRecord = wrapped;
85
- for (const key of Object.getOwnPropertyNames(original)) {
86
- if (key in wrappedRecord)
87
- continue;
88
- try {
89
- const desc = Object.getOwnPropertyDescriptor(original, key);
90
- if (desc)
91
- Object.defineProperty(wrapped, key, desc);
92
- }
93
- catch {
94
- // Silently skip properties that cannot be copied
95
- }
96
- }
97
- for (const sym of Object.getOwnPropertySymbols(original)) {
98
- if (sym in wrappedRecord)
99
- continue;
100
- try {
101
- const desc = Object.getOwnPropertyDescriptor(original, sym);
102
- if (desc)
103
- Object.defineProperty(wrapped, sym, desc);
104
- }
105
- catch {
106
- // Silently skip symbols that cannot be copied
107
- }
108
- }
109
- return wrapped;
110
- }
111
- // Wrap global test functions
112
- const g = globalThis;
113
- if (g.it)
114
- g.it = wrapCallable(g.it);
115
- if (g.test)
116
- g.test = wrapCallable(g.test);
117
- if (g.describe)
118
- g.describe = wrapCallable(g.describe);
@@ -1,13 +0,0 @@
1
- import type { ViteUserConfig } from 'vitest/config';
2
- /**
3
- * Wraps a Vite/Vitest configuration to integrate Mutineer mutation testing.
4
- *
5
- * When running under mutation testing (MUTATE_ACTIVE=1), this function:
6
- * - Injects the Mutineer Vite plugin to intercept and replace module code
7
- * - Enables global test functions (it, test, describe) for convenience
8
- *
9
- * @param user - User's Vitest configuration (optional)
10
- * @returns Enhanced Vitest configuration with Mutineer integration
11
- * @throws Error if plugins is not an array-like structure
12
- */
13
- export declare function withMutineerSetup(user?: ViteUserConfig): ViteUserConfig;
@@ -1,30 +0,0 @@
1
- import { viteMutineerPlugin } from './viteMutate.js';
2
- /** Flag that indicates whether a mutation is currently active. */
3
- const MUTATION_ACTIVE_FLAG = '1';
4
- /**
5
- * Wraps a Vite/Vitest configuration to integrate Mutineer mutation testing.
6
- *
7
- * When running under mutation testing (MUTATE_ACTIVE=1), this function:
8
- * - Injects the Mutineer Vite plugin to intercept and replace module code
9
- * - Enables global test functions (it, test, describe) for convenience
10
- *
11
- * @param user - User's Vitest configuration (optional)
12
- * @returns Enhanced Vitest configuration with Mutineer integration
13
- * @throws Error if plugins is not an array-like structure
14
- */
15
- export function withMutineerSetup(user = {}) {
16
- const isMutating = process.env.MUTATE_ACTIVE === MUTATION_ACTIVE_FLAG;
17
- // Build plugin list: user plugins + mutineer plugin (if mutating)
18
- const userPlugins = Array.isArray(user.plugins) ? user.plugins : (user.plugins ? [user.plugins] : []);
19
- const plugins = isMutating
20
- ? [...userPlugins, viteMutineerPlugin()]
21
- : userPlugins;
22
- return {
23
- ...user,
24
- plugins,
25
- test: {
26
- ...(user.test ?? {}),
27
- globals: user.test?.globals ?? true, // default to true for convenience
28
- }
29
- };
30
- }