@mutineerjs/mutineer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -0
  3. package/dist/admin/assets/index-B7nXq-e7.js +32 -0
  4. package/dist/admin/assets/index-B7nXq-e7.js.map +1 -0
  5. package/dist/admin/assets/index-BDQLkBUE.js +32 -0
  6. package/dist/admin/assets/index-BDQLkBUE.js.map +1 -0
  7. package/dist/admin/assets/index-DVkP-Tc7.css +1 -0
  8. package/dist/admin/index.html +13 -0
  9. package/dist/admin/server/admin.d.ts +6 -0
  10. package/dist/admin/server/admin.js +234 -0
  11. package/dist/bin/mutate-vitest.d.ts +2 -0
  12. package/dist/bin/mutate-vitest.js +90 -0
  13. package/dist/bin/mutineer.d.ts +2 -0
  14. package/dist/bin/mutineer.js +46 -0
  15. package/dist/core/__tests__/module.spec.d.ts +1 -0
  16. package/dist/core/__tests__/module.spec.js +6 -0
  17. package/dist/core/module.d.ts +11 -0
  18. package/dist/core/module.js +14 -0
  19. package/dist/core/sfc.d.ts +12 -0
  20. package/dist/core/sfc.js +54 -0
  21. package/dist/core/types.d.ts +6 -0
  22. package/dist/core/types.js +1 -0
  23. package/dist/core/variant-utils.d.ts +30 -0
  24. package/dist/core/variant-utils.js +54 -0
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.js +3 -0
  27. package/dist/mutators/__tests__/registry.spec.d.ts +1 -0
  28. package/dist/mutators/__tests__/registry.spec.js +43 -0
  29. package/dist/mutators/__tests__/utils.spec.d.ts +1 -0
  30. package/dist/mutators/__tests__/utils.spec.js +15 -0
  31. package/dist/mutators/registry.d.ts +37 -0
  32. package/dist/mutators/registry.js +101 -0
  33. package/dist/mutators/types.d.ts +39 -0
  34. package/dist/mutators/types.js +7 -0
  35. package/dist/mutators/utils.d.ts +37 -0
  36. package/dist/mutators/utils.js +151 -0
  37. package/dist/plugin/viteMutate.d.ts +15 -0
  38. package/dist/plugin/viteMutate.js +52 -0
  39. package/dist/plugin/vitest.setup.d.ts +47 -0
  40. package/dist/plugin/vitest.setup.js +118 -0
  41. package/dist/plugin/withVitest.d.ts +13 -0
  42. package/dist/plugin/withVitest.js +30 -0
  43. package/dist/runner/__tests__/discover.spec.d.ts +1 -0
  44. package/dist/runner/__tests__/discover.spec.js +59 -0
  45. package/dist/runner/__tests__/orchestrator.spec.d.ts +1 -0
  46. package/dist/runner/__tests__/orchestrator.spec.js +55 -0
  47. package/dist/runner/adapters/__tests__/jest.spec.d.ts +1 -0
  48. package/dist/runner/adapters/__tests__/jest.spec.js +88 -0
  49. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.d.ts +1 -0
  50. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.js +59 -0
  51. package/dist/runner/adapters/__tests__/vitest.spec.d.ts +1 -0
  52. package/dist/runner/adapters/__tests__/vitest.spec.js +118 -0
  53. package/dist/runner/adapters/index.d.ts +10 -0
  54. package/dist/runner/adapters/index.js +9 -0
  55. package/dist/runner/adapters/jest/__tests__/index.spec.d.ts +1 -0
  56. package/dist/runner/adapters/jest/__tests__/index.spec.js +88 -0
  57. package/dist/runner/adapters/jest/index.d.ts +24 -0
  58. package/dist/runner/adapters/jest/index.js +216 -0
  59. package/dist/runner/adapters/jest/worker-runtime.d.ts +37 -0
  60. package/dist/runner/adapters/jest/worker-runtime.js +171 -0
  61. package/dist/runner/adapters/jest-worker-runtime.d.ts +37 -0
  62. package/dist/runner/adapters/jest-worker-runtime.js +171 -0
  63. package/dist/runner/adapters/jest.d.ts +24 -0
  64. package/dist/runner/adapters/jest.js +216 -0
  65. package/dist/runner/adapters/types.d.ts +89 -0
  66. package/dist/runner/adapters/types.js +8 -0
  67. package/dist/runner/adapters/vitest/__tests__/index.spec.d.ts +1 -0
  68. package/dist/runner/adapters/vitest/__tests__/index.spec.js +118 -0
  69. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.d.ts +1 -0
  70. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.js +59 -0
  71. package/dist/runner/adapters/vitest/index.d.ts +33 -0
  72. package/dist/runner/adapters/vitest/index.js +267 -0
  73. package/dist/runner/adapters/vitest/worker-runtime.d.ts +25 -0
  74. package/dist/runner/adapters/vitest/worker-runtime.js +118 -0
  75. package/dist/runner/adapters/vitest-worker-runtime.d.ts +25 -0
  76. package/dist/runner/adapters/vitest-worker-runtime.js +118 -0
  77. package/dist/runner/adapters/vitest.d.ts +33 -0
  78. package/dist/runner/adapters/vitest.js +267 -0
  79. package/dist/runner/args.d.ts +50 -0
  80. package/dist/runner/args.js +123 -0
  81. package/dist/runner/cache.d.ts +38 -0
  82. package/dist/runner/cache.js +118 -0
  83. package/dist/runner/changed.d.ts +22 -0
  84. package/dist/runner/changed.js +210 -0
  85. package/dist/runner/cleanup.d.ts +4 -0
  86. package/dist/runner/cleanup.js +21 -0
  87. package/dist/runner/config.d.ts +13 -0
  88. package/dist/runner/config.js +94 -0
  89. package/dist/runner/discover.d.ts +7 -0
  90. package/dist/runner/discover.js +258 -0
  91. package/dist/runner/jest/__tests__/adapter.spec.d.ts +1 -0
  92. package/dist/runner/jest/__tests__/adapter.spec.js +110 -0
  93. package/dist/runner/jest/adapter.d.ts +24 -0
  94. package/dist/runner/jest/adapter.js +191 -0
  95. package/dist/runner/jest/index.d.ts +8 -0
  96. package/dist/runner/jest/index.js +7 -0
  97. package/dist/runner/jest/pool.d.ts +47 -0
  98. package/dist/runner/jest/pool.js +307 -0
  99. package/dist/runner/jest/resolver.cjs +61 -0
  100. package/dist/runner/jest/resolver.d.cts +11 -0
  101. package/dist/runner/jest/worker-runtime.d.ts +30 -0
  102. package/dist/runner/jest/worker-runtime.js +98 -0
  103. package/dist/runner/jest/worker.d.mts +1 -0
  104. package/dist/runner/jest/worker.mjs +55 -0
  105. package/dist/runner/orchestrator.d.ts +13 -0
  106. package/dist/runner/orchestrator.js +387 -0
  107. package/dist/runner/pool/__tests__/index.spec.d.ts +1 -0
  108. package/dist/runner/pool/__tests__/index.spec.js +83 -0
  109. package/dist/runner/pool/__tests__/pool-plugin.spec.d.ts +1 -0
  110. package/dist/runner/pool/__tests__/pool-plugin.spec.js +59 -0
  111. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.d.ts +1 -0
  112. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.js +78 -0
  113. package/dist/runner/pool/index.d.ts +8 -0
  114. package/dist/runner/pool/index.js +9 -0
  115. package/dist/runner/pool/jest/pool.d.ts +52 -0
  116. package/dist/runner/pool/jest/pool.js +309 -0
  117. package/dist/runner/pool/jest/worker.d.mts +1 -0
  118. package/dist/runner/pool/jest/worker.mjs +60 -0
  119. package/dist/runner/pool/jest-pool.d.ts +52 -0
  120. package/dist/runner/pool/jest-pool.js +309 -0
  121. package/dist/runner/pool/jest-worker.d.mts +1 -0
  122. package/dist/runner/pool/jest-worker.mjs +60 -0
  123. package/dist/runner/pool/plugin.d.ts +18 -0
  124. package/dist/runner/pool/plugin.js +60 -0
  125. package/dist/runner/pool/pool-plugin.d.ts +18 -0
  126. package/dist/runner/pool/pool-plugin.js +60 -0
  127. package/dist/runner/pool/pool-redirect-loader.d.ts +19 -0
  128. package/dist/runner/pool/pool-redirect-loader.js +116 -0
  129. package/dist/runner/pool/pool-redirect-loader.mjs +146 -0
  130. package/dist/runner/pool/redirect-loader.d.ts +19 -0
  131. package/dist/runner/pool/redirect-loader.js +116 -0
  132. package/dist/runner/pool/vitest/pool.d.ts +70 -0
  133. package/dist/runner/pool/vitest/pool.js +376 -0
  134. package/dist/runner/pool/vitest/worker.d.mts +15 -0
  135. package/dist/runner/pool/vitest/worker.mjs +96 -0
  136. package/dist/runner/pool/vitest-worker.d.mts +15 -0
  137. package/dist/runner/pool/vitest-worker.mjs +96 -0
  138. package/dist/runner/shared/index.d.ts +9 -0
  139. package/dist/runner/shared/index.js +8 -0
  140. package/dist/runner/shared/mutant-paths.d.ts +15 -0
  141. package/dist/runner/shared/mutant-paths.js +30 -0
  142. package/dist/runner/shared/redirect-state.d.ts +45 -0
  143. package/dist/runner/shared/redirect-state.js +50 -0
  144. package/dist/runner/shared-module-redirect.d.ts +56 -0
  145. package/dist/runner/shared-module-redirect.js +84 -0
  146. package/dist/runner/types.d.ts +88 -0
  147. package/dist/runner/types.js +8 -0
  148. package/dist/runner/variants.d.ts +21 -0
  149. package/dist/runner/variants.js +66 -0
  150. package/dist/runner/vitest/__tests__/adapter.spec.d.ts +1 -0
  151. package/dist/runner/vitest/__tests__/adapter.spec.js +131 -0
  152. package/dist/runner/vitest/__tests__/plugin.spec.d.ts +1 -0
  153. package/dist/runner/vitest/__tests__/plugin.spec.js +65 -0
  154. package/dist/runner/vitest/__tests__/pool.spec.d.ts +1 -0
  155. package/dist/runner/vitest/__tests__/pool.spec.js +106 -0
  156. package/dist/runner/vitest/__tests__/redirect-loader.spec.d.ts +1 -0
  157. package/dist/runner/vitest/__tests__/redirect-loader.spec.js +87 -0
  158. package/dist/runner/vitest/__tests__/worker-runtime.spec.d.ts +1 -0
  159. package/dist/runner/vitest/__tests__/worker-runtime.spec.js +75 -0
  160. package/dist/runner/vitest/adapter.d.ts +33 -0
  161. package/dist/runner/vitest/adapter.js +277 -0
  162. package/dist/runner/vitest/index.d.ts +11 -0
  163. package/dist/runner/vitest/index.js +10 -0
  164. package/dist/runner/vitest/plugin.d.ts +12 -0
  165. package/dist/runner/vitest/plugin.js +49 -0
  166. package/dist/runner/vitest/pool.d.ts +65 -0
  167. package/dist/runner/vitest/pool.js +376 -0
  168. package/dist/runner/vitest/redirect-loader.d.ts +30 -0
  169. package/dist/runner/vitest/redirect-loader.js +123 -0
  170. package/dist/runner/vitest/worker-runtime.d.ts +16 -0
  171. package/dist/runner/vitest/worker-runtime.js +105 -0
  172. package/dist/runner/vitest/worker.d.mts +15 -0
  173. package/dist/runner/vitest/worker.mjs +92 -0
  174. package/dist/types/api.d.ts +20 -0
  175. package/dist/types/api.js +1 -0
  176. package/dist/types/config.d.ts +48 -0
  177. package/dist/types/config.js +1 -0
  178. package/dist/types/index.d.ts +13 -0
  179. package/dist/types/index.js +11 -0
  180. package/dist/types/mutant.d.ts +44 -0
  181. package/dist/types/mutant.js +7 -0
  182. package/dist/utils/PoolSpinner.d.ts +5 -0
  183. package/dist/utils/PoolSpinner.js +6 -0
  184. package/dist/utils/ProgressBar.d.ts +11 -0
  185. package/dist/utils/ProgressBar.js +9 -0
  186. package/dist/utils/__tests__/coverage.spec.d.ts +1 -0
  187. package/dist/utils/__tests__/coverage.spec.js +91 -0
  188. package/dist/utils/__tests__/progress.spec.d.ts +1 -0
  189. package/dist/utils/__tests__/progress.spec.js +50 -0
  190. package/dist/utils/__tests__/summary.spec.d.ts +1 -0
  191. package/dist/utils/__tests__/summary.spec.js +54 -0
  192. package/dist/utils/coverage.d.ts +57 -0
  193. package/dist/utils/coverage.js +204 -0
  194. package/dist/utils/logger.d.ts +8 -0
  195. package/dist/utils/logger.js +18 -0
  196. package/dist/utils/progress.d.ts +25 -0
  197. package/dist/utils/progress.js +90 -0
  198. package/dist/utils/summary.d.ts +12 -0
  199. package/dist/utils/summary.js +107 -0
  200. package/package.json +59 -0
@@ -0,0 +1 @@
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}
@@ -0,0 +1,13 @@
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>
@@ -0,0 +1,6 @@
1
+ interface AdminServerOptions {
2
+ port?: number;
3
+ configPath?: string;
4
+ }
5
+ export declare function runAdminServer(cwd: string, options?: AdminServerOptions): Promise<void>;
6
+ export {};
@@ -0,0 +1,234 @@
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
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,90 @@
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();
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { runOrchestrator } from '../runner/orchestrator.js';
5
+ import { cleanupMutineerDirs } from '../runner/cleanup.js';
6
+ // Constants
7
+ const RUN_COMMAND = 'run';
8
+ const CLEAN_COMMAND = 'clean';
9
+ const INIT_COMMAND = 'init';
10
+ const CONFIG_TEMPLATE = `\
11
+ import { defineMutineerConfig } from 'mutineer'
12
+
13
+ export default defineMutineerConfig({
14
+ source: 'src',
15
+ })
16
+ `;
17
+ /**
18
+ * Main entry point - routes to orchestrator or clean
19
+ */
20
+ async function main() {
21
+ const args = process.argv.slice(2);
22
+ if (args[0] === RUN_COMMAND) {
23
+ await runOrchestrator(args.slice(1), process.cwd());
24
+ }
25
+ else if (args[0] === INIT_COMMAND) {
26
+ const configFile = path.join(process.cwd(), 'mutineer.config.ts');
27
+ if (fs.existsSync(configFile)) {
28
+ console.error('mutineer.config.ts already exists.');
29
+ process.exitCode = 1;
30
+ }
31
+ else {
32
+ fs.writeFileSync(configFile, CONFIG_TEMPLATE);
33
+ console.log('Created mutineer.config.ts');
34
+ }
35
+ }
36
+ else if (args[0] === CLEAN_COMMAND) {
37
+ console.log('Cleaning up __mutineer__ directories...');
38
+ await cleanupMutineerDirs(process.cwd());
39
+ console.log('Done.');
40
+ }
41
+ else {
42
+ console.error(`Unknown command: ${args[0] ?? '(none)'}\nUsage: mutineer <init|run|clean>`);
43
+ process.exitCode = 1;
44
+ }
45
+ }
46
+ await main();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ describe('Module', () => {
3
+ it('should pass a basic test', () => {
4
+ expect(true).toBe(true);
5
+ });
6
+ });
@@ -0,0 +1,11 @@
1
+ import type { MutationVariant } from './types.js';
2
+ /**
3
+ * Generate all possible mutations for a given source code.
4
+ * @param code - The source code to mutate
5
+ * @param include - Optional list of mutator names to include (if provided, only these are used)
6
+ * @param exclude - Optional list of mutator names to exclude
7
+ * @param max - Optional maximum number of mutations to generate. Must be > 0 if provided.
8
+ * @returns Array of unique mutations, up to `max` if specified
9
+ * @throws Error if max is provided and <= 0
10
+ */
11
+ export declare function mutateModuleSource(code: string, include?: readonly string[], exclude?: readonly string[], max?: number): readonly MutationVariant[];
@@ -0,0 +1,14 @@
1
+ import { generateMutationVariants, getFilteredRegistry, } from './variant-utils.js';
2
+ /**
3
+ * Generate all possible mutations for a given source code.
4
+ * @param code - The source code to mutate
5
+ * @param include - Optional list of mutator names to include (if provided, only these are used)
6
+ * @param exclude - Optional list of mutator names to exclude
7
+ * @param max - Optional maximum number of mutations to generate. Must be > 0 if provided.
8
+ * @returns Array of unique mutations, up to `max` if specified
9
+ * @throws Error if max is provided and <= 0
10
+ */
11
+ export function mutateModuleSource(code, include, exclude, max) {
12
+ const registry = getFilteredRegistry(include, exclude);
13
+ return generateMutationVariants(registry, code, { max });
14
+ }
@@ -0,0 +1,12 @@
1
+ import type { MutationVariant } from './types.js';
2
+ /**
3
+ * Generate all possible mutations for a Vue SFC `<script setup>` block.
4
+ * @param filename - The path to the Vue file (used by the parser for error reporting)
5
+ * @param code - The full SFC source code
6
+ * @param include - Optional list of mutator names to include (if provided, only these are used)
7
+ * @param exclude - Optional list of mutator names to exclude
8
+ * @param max - Optional maximum number of mutations to generate. Must be > 0 if provided.
9
+ * @returns Array of unique mutations (with mutated full source), up to `max` if specified
10
+ * @throws Error if max is provided and <= 0
11
+ */
12
+ export declare function mutateVueSfcScriptSetup(filename: string, code: string, include?: readonly string[], exclude?: readonly string[], max?: number): readonly MutationVariant[];
@@ -0,0 +1,54 @@
1
+ import { parse } from '@vue/compiler-sfc';
2
+ import MagicString from 'magic-string';
3
+ import { getFilteredRegistry } from './variant-utils.js';
4
+ /**
5
+ * Generate all possible mutations for a Vue SFC `<script setup>` block.
6
+ * @param filename - The path to the Vue file (used by the parser for error reporting)
7
+ * @param code - The full SFC source code
8
+ * @param include - Optional list of mutator names to include (if provided, only these are used)
9
+ * @param exclude - Optional list of mutator names to exclude
10
+ * @param max - Optional maximum number of mutations to generate. Must be > 0 if provided.
11
+ * @returns Array of unique mutations (with mutated full source), up to `max` if specified
12
+ * @throws Error if max is provided and <= 0
13
+ */
14
+ export function mutateVueSfcScriptSetup(filename, code, include, exclude, max) {
15
+ // Input validation
16
+ if (max !== undefined && max <= 0) {
17
+ throw new Error(`max must be a positive number, got: ${max}`);
18
+ }
19
+ const sfc = parse(code, { filename });
20
+ const scriptSetup = sfc.descriptor.scriptSetup;
21
+ if (!scriptSetup)
22
+ return [];
23
+ const startOffset = scriptSetup.loc.start.offset;
24
+ const endOffset = scriptSetup.loc.end.offset;
25
+ const originalBlock = code.slice(startOffset, endOffset);
26
+ const registry = getFilteredRegistry(include, exclude);
27
+ const variants = [];
28
+ const seenOutputs = new Set();
29
+ for (const mutator of registry) {
30
+ // Early termination if limit reached
31
+ if (max !== undefined && variants.length >= max) {
32
+ break;
33
+ }
34
+ for (const mutation of mutator.apply(originalBlock)) {
35
+ const ms = new MagicString(code);
36
+ ms.overwrite(startOffset, endOffset, mutation.code);
37
+ const mutatedOutput = ms.toString();
38
+ if (!seenOutputs.has(mutatedOutput)) {
39
+ seenOutputs.add(mutatedOutput);
40
+ variants.push({
41
+ name: mutator.name,
42
+ code: mutatedOutput,
43
+ line: mutation.line,
44
+ col: mutation.col,
45
+ });
46
+ // Check if we've reached the limit and exit early
47
+ if (max !== undefined && variants.length >= max) {
48
+ return variants;
49
+ }
50
+ }
51
+ }
52
+ }
53
+ return variants;
54
+ }
@@ -0,0 +1,6 @@
1
+ export interface MutationVariant {
2
+ readonly name: string;
3
+ readonly code: string;
4
+ readonly line: number;
5
+ readonly col: number;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import type { MutationVariant } from './types.js';
2
+ import type { ASTMutator } from '../mutators/registry.js';
3
+ /**
4
+ * Shared logic for generating and deduplicating mutations.
5
+ * Extracted to avoid duplication between module.ts and sfc.ts.
6
+ */
7
+ interface VariantGenOptions {
8
+ readonly max?: number;
9
+ }
10
+ /**
11
+ * Generate mutations from a registry of mutators, deduplicating the output.
12
+ * Supports early termination when max limit is reached.
13
+ *
14
+ * @param registry - Array of mutators to apply
15
+ * @param code - Source code to mutate
16
+ * @param max - Optional maximum number of variants to generate (must be > 0 if provided)
17
+ * @returns Array of unique mutations
18
+ * @throws Error if max is provided and <= 0
19
+ */
20
+ export declare function generateMutationVariants(registry: readonly ASTMutator[], code: string, opts?: VariantGenOptions): readonly MutationVariant[];
21
+ /**
22
+ * Get a filtered registry based on include/exclude options.
23
+ * This is a convenience wrapper around getRegistry for use in variant generation.
24
+ *
25
+ * @param include - Optional list of mutator names to include
26
+ * @param exclude - Optional list of mutator names to exclude
27
+ * @returns Filtered array of mutators
28
+ */
29
+ export declare function getFilteredRegistry(include?: readonly string[], exclude?: readonly string[]): readonly ASTMutator[];
30
+ export {};
@@ -0,0 +1,54 @@
1
+ import { getRegistry } from '../mutators/registry.js';
2
+ /**
3
+ * Generate mutations from a registry of mutators, deduplicating the output.
4
+ * Supports early termination when max limit is reached.
5
+ *
6
+ * @param registry - Array of mutators to apply
7
+ * @param code - Source code to mutate
8
+ * @param max - Optional maximum number of variants to generate (must be > 0 if provided)
9
+ * @returns Array of unique mutations
10
+ * @throws Error if max is provided and <= 0
11
+ */
12
+ export function generateMutationVariants(registry, code, opts = {}) {
13
+ const { max } = opts;
14
+ // Input validation
15
+ if (max !== undefined && max <= 0) {
16
+ throw new Error(`max must be a positive number, got: ${max}`);
17
+ }
18
+ const variants = [];
19
+ const seen = new Set();
20
+ for (const mutator of registry) {
21
+ // Early termination if limit reached
22
+ if (max !== undefined && variants.length >= max) {
23
+ break;
24
+ }
25
+ for (const mutation of mutator.apply(code)) {
26
+ // Skip unchanged code and duplicates in a single check
27
+ if (!seen.has(mutation.code)) {
28
+ seen.add(mutation.code);
29
+ variants.push({
30
+ name: mutator.name,
31
+ code: mutation.code,
32
+ line: mutation.line,
33
+ col: mutation.col,
34
+ });
35
+ // Check if we've reached the limit and exit early
36
+ if (max !== undefined && variants.length >= max) {
37
+ return variants;
38
+ }
39
+ }
40
+ }
41
+ }
42
+ return variants;
43
+ }
44
+ /**
45
+ * Get a filtered registry based on include/exclude options.
46
+ * This is a convenience wrapper around getRegistry for use in variant generation.
47
+ *
48
+ * @param include - Optional list of mutator names to include
49
+ * @param exclude - Optional list of mutator names to exclude
50
+ * @returns Filtered array of mutators
51
+ */
52
+ export function getFilteredRegistry(include, exclude) {
53
+ return getRegistry(include, exclude);
54
+ }
@@ -0,0 +1,4 @@
1
+ import type { MutineerConfig } from './types/config.js';
2
+ export type { MutineerConfig, MutateTarget } from './types/config.js';
3
+ export type { MutantStatus, MutantRunStatus, MutantCacheEntry, MutantResult, MutantRunResult, MutantRunSummary, MutantPayload, MutantDescriptor, MutantLocation, Variant, } from './types/mutant.js';
4
+ export declare function defineMutineerConfig(cfg: MutineerConfig): MutineerConfig;
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export function defineMutineerConfig(cfg) {
2
+ return cfg;
3
+ }
@@ -0,0 +1 @@
1
+ export {};