@opensip-tools/fitness 1.0.4

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 (461) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-typecheck.log +4 -0
  3. package/LICENSE +21 -0
  4. package/dist/__tests__/gate.test.d.ts +13 -0
  5. package/dist/__tests__/gate.test.d.ts.map +1 -0
  6. package/dist/__tests__/gate.test.js +422 -0
  7. package/dist/__tests__/gate.test.js.map +1 -0
  8. package/dist/__tests__/sarif.test.d.ts +2 -0
  9. package/dist/__tests__/sarif.test.d.ts.map +1 -0
  10. package/dist/__tests__/sarif.test.js +169 -0
  11. package/dist/__tests__/sarif.test.js.map +1 -0
  12. package/dist/cli/dashboard.d.ts +6 -0
  13. package/dist/cli/dashboard.d.ts.map +1 -0
  14. package/dist/cli/dashboard.js +77 -0
  15. package/dist/cli/dashboard.js.map +1 -0
  16. package/dist/cli/fit.d.ts +37 -0
  17. package/dist/cli/fit.d.ts.map +1 -0
  18. package/dist/cli/fit.js +539 -0
  19. package/dist/cli/fit.js.map +1 -0
  20. package/dist/cli/list-checks.d.ts +6 -0
  21. package/dist/cli/list-checks.d.ts.map +1 -0
  22. package/dist/cli/list-checks.js +23 -0
  23. package/dist/cli/list-checks.js.map +1 -0
  24. package/dist/cli/list-recipes.d.ts +6 -0
  25. package/dist/cli/list-recipes.d.ts.map +1 -0
  26. package/dist/cli/list-recipes.js +31 -0
  27. package/dist/cli/list-recipes.js.map +1 -0
  28. package/dist/framework/__tests__/ast-utilities.test.d.ts +2 -0
  29. package/dist/framework/__tests__/ast-utilities.test.d.ts.map +1 -0
  30. package/dist/framework/__tests__/ast-utilities.test.js +153 -0
  31. package/dist/framework/__tests__/ast-utilities.test.js.map +1 -0
  32. package/dist/framework/__tests__/check-config.test.d.ts +2 -0
  33. package/dist/framework/__tests__/check-config.test.d.ts.map +1 -0
  34. package/dist/framework/__tests__/check-config.test.js +56 -0
  35. package/dist/framework/__tests__/check-config.test.js.map +1 -0
  36. package/dist/framework/__tests__/command-executor.test.d.ts +2 -0
  37. package/dist/framework/__tests__/command-executor.test.d.ts.map +1 -0
  38. package/dist/framework/__tests__/command-executor.test.js +71 -0
  39. package/dist/framework/__tests__/command-executor.test.js.map +1 -0
  40. package/dist/framework/__tests__/content-filter-dispatch.test.d.ts +2 -0
  41. package/dist/framework/__tests__/content-filter-dispatch.test.d.ts.map +1 -0
  42. package/dist/framework/__tests__/content-filter-dispatch.test.js +104 -0
  43. package/dist/framework/__tests__/content-filter-dispatch.test.js.map +1 -0
  44. package/dist/framework/__tests__/content-filter.test.d.ts +2 -0
  45. package/dist/framework/__tests__/content-filter.test.d.ts.map +1 -0
  46. package/dist/framework/__tests__/content-filter.test.js +126 -0
  47. package/dist/framework/__tests__/content-filter.test.js.map +1 -0
  48. package/dist/framework/__tests__/define-check.test.d.ts +2 -0
  49. package/dist/framework/__tests__/define-check.test.d.ts.map +1 -0
  50. package/dist/framework/__tests__/define-check.test.js +155 -0
  51. package/dist/framework/__tests__/define-check.test.js.map +1 -0
  52. package/dist/framework/__tests__/directive-inventory.test.d.ts +2 -0
  53. package/dist/framework/__tests__/directive-inventory.test.d.ts.map +1 -0
  54. package/dist/framework/__tests__/directive-inventory.test.js +44 -0
  55. package/dist/framework/__tests__/directive-inventory.test.js.map +1 -0
  56. package/dist/framework/__tests__/execution-context.test.d.ts +2 -0
  57. package/dist/framework/__tests__/execution-context.test.d.ts.map +1 -0
  58. package/dist/framework/__tests__/execution-context.test.js +62 -0
  59. package/dist/framework/__tests__/execution-context.test.js.map +1 -0
  60. package/dist/framework/__tests__/file-accessor.test.d.ts +2 -0
  61. package/dist/framework/__tests__/file-accessor.test.d.ts.map +1 -0
  62. package/dist/framework/__tests__/file-accessor.test.js +106 -0
  63. package/dist/framework/__tests__/file-accessor.test.js.map +1 -0
  64. package/dist/framework/__tests__/file-cache.test.d.ts +2 -0
  65. package/dist/framework/__tests__/file-cache.test.d.ts.map +1 -0
  66. package/dist/framework/__tests__/file-cache.test.js +122 -0
  67. package/dist/framework/__tests__/file-cache.test.js.map +1 -0
  68. package/dist/framework/__tests__/import-graph.test.d.ts +15 -0
  69. package/dist/framework/__tests__/import-graph.test.d.ts.map +1 -0
  70. package/dist/framework/__tests__/import-graph.test.js +164 -0
  71. package/dist/framework/__tests__/import-graph.test.js.map +1 -0
  72. package/dist/framework/__tests__/path-matcher.test.d.ts +2 -0
  73. package/dist/framework/__tests__/path-matcher.test.d.ts.map +1 -0
  74. package/dist/framework/__tests__/path-matcher.test.js +113 -0
  75. package/dist/framework/__tests__/path-matcher.test.js.map +1 -0
  76. package/dist/framework/__tests__/register-helpers.test.d.ts +2 -0
  77. package/dist/framework/__tests__/register-helpers.test.d.ts.map +1 -0
  78. package/dist/framework/__tests__/register-helpers.test.js +42 -0
  79. package/dist/framework/__tests__/register-helpers.test.js.map +1 -0
  80. package/dist/framework/__tests__/registry.test.d.ts +2 -0
  81. package/dist/framework/__tests__/registry.test.d.ts.map +1 -0
  82. package/dist/framework/__tests__/registry.test.js +208 -0
  83. package/dist/framework/__tests__/registry.test.js.map +1 -0
  84. package/dist/framework/__tests__/result-builder.test.d.ts +2 -0
  85. package/dist/framework/__tests__/result-builder.test.d.ts.map +1 -0
  86. package/dist/framework/__tests__/result-builder.test.js +153 -0
  87. package/dist/framework/__tests__/result-builder.test.js.map +1 -0
  88. package/dist/framework/__tests__/scope-resolver.test.d.ts +2 -0
  89. package/dist/framework/__tests__/scope-resolver.test.d.ts.map +1 -0
  90. package/dist/framework/__tests__/scope-resolver.test.js +140 -0
  91. package/dist/framework/__tests__/scope-resolver.test.js.map +1 -0
  92. package/dist/framework/__tests__/severity-mapping.test.d.ts +2 -0
  93. package/dist/framework/__tests__/severity-mapping.test.d.ts.map +1 -0
  94. package/dist/framework/__tests__/severity-mapping.test.js +42 -0
  95. package/dist/framework/__tests__/severity-mapping.test.js.map +1 -0
  96. package/dist/framework/__tests__/strip-literals.test.d.ts +2 -0
  97. package/dist/framework/__tests__/strip-literals.test.d.ts.map +1 -0
  98. package/dist/framework/__tests__/strip-literals.test.js +87 -0
  99. package/dist/framework/__tests__/strip-literals.test.js.map +1 -0
  100. package/dist/framework/abortable-exec.d.ts +34 -0
  101. package/dist/framework/abortable-exec.d.ts.map +1 -0
  102. package/dist/framework/abortable-exec.js +136 -0
  103. package/dist/framework/abortable-exec.js.map +1 -0
  104. package/dist/framework/ast-utilities.d.ts +41 -0
  105. package/dist/framework/ast-utilities.d.ts.map +1 -0
  106. package/dist/framework/ast-utilities.js +106 -0
  107. package/dist/framework/ast-utilities.js.map +1 -0
  108. package/dist/framework/check-config.d.ts +171 -0
  109. package/dist/framework/check-config.d.ts.map +1 -0
  110. package/dist/framework/check-config.js +114 -0
  111. package/dist/framework/check-config.js.map +1 -0
  112. package/dist/framework/check-types.d.ts +57 -0
  113. package/dist/framework/check-types.d.ts.map +1 -0
  114. package/dist/framework/check-types.js +35 -0
  115. package/dist/framework/check-types.js.map +1 -0
  116. package/dist/framework/command-executor.d.ts +25 -0
  117. package/dist/framework/command-executor.d.ts.map +1 -0
  118. package/dist/framework/command-executor.js +63 -0
  119. package/dist/framework/command-executor.js.map +1 -0
  120. package/dist/framework/constants.d.ts +9 -0
  121. package/dist/framework/constants.d.ts.map +1 -0
  122. package/dist/framework/constants.js +16 -0
  123. package/dist/framework/constants.js.map +1 -0
  124. package/dist/framework/content-filter.d.ts +33 -0
  125. package/dist/framework/content-filter.d.ts.map +1 -0
  126. package/dist/framework/content-filter.js +236 -0
  127. package/dist/framework/content-filter.js.map +1 -0
  128. package/dist/framework/define-check.d.ts +38 -0
  129. package/dist/framework/define-check.d.ts.map +1 -0
  130. package/dist/framework/define-check.js +252 -0
  131. package/dist/framework/define-check.js.map +1 -0
  132. package/dist/framework/directive-inventory.d.ts +34 -0
  133. package/dist/framework/directive-inventory.d.ts.map +1 -0
  134. package/dist/framework/directive-inventory.js +77 -0
  135. package/dist/framework/directive-inventory.js.map +1 -0
  136. package/dist/framework/directive-parsing.d.ts +20 -0
  137. package/dist/framework/directive-parsing.d.ts.map +1 -0
  138. package/dist/framework/directive-parsing.js +121 -0
  139. package/dist/framework/directive-parsing.js.map +1 -0
  140. package/dist/framework/execution-context.d.ts +95 -0
  141. package/dist/framework/execution-context.d.ts.map +1 -0
  142. package/dist/framework/execution-context.js +122 -0
  143. package/dist/framework/execution-context.js.map +1 -0
  144. package/dist/framework/file-accessor.d.ts +20 -0
  145. package/dist/framework/file-accessor.d.ts.map +1 -0
  146. package/dist/framework/file-accessor.js +122 -0
  147. package/dist/framework/file-accessor.js.map +1 -0
  148. package/dist/framework/file-cache.d.ts +70 -0
  149. package/dist/framework/file-cache.d.ts.map +1 -0
  150. package/dist/framework/file-cache.js +178 -0
  151. package/dist/framework/file-cache.js.map +1 -0
  152. package/dist/framework/file-type-filter.d.ts +11 -0
  153. package/dist/framework/file-type-filter.d.ts.map +1 -0
  154. package/dist/framework/file-type-filter.js +21 -0
  155. package/dist/framework/file-type-filter.js.map +1 -0
  156. package/dist/framework/ignore-processing.d.ts +22 -0
  157. package/dist/framework/ignore-processing.d.ts.map +1 -0
  158. package/dist/framework/ignore-processing.js +241 -0
  159. package/dist/framework/ignore-processing.js.map +1 -0
  160. package/dist/framework/import-graph.d.ts +51 -0
  161. package/dist/framework/import-graph.d.ts.map +1 -0
  162. package/dist/framework/import-graph.js +216 -0
  163. package/dist/framework/import-graph.js.map +1 -0
  164. package/dist/framework/memory-profiler.d.ts +53 -0
  165. package/dist/framework/memory-profiler.d.ts.map +1 -0
  166. package/dist/framework/memory-profiler.js +92 -0
  167. package/dist/framework/memory-profiler.js.map +1 -0
  168. package/dist/framework/parse-cache.d.ts +23 -0
  169. package/dist/framework/parse-cache.d.ts.map +1 -0
  170. package/dist/framework/parse-cache.js +37 -0
  171. package/dist/framework/parse-cache.js.map +1 -0
  172. package/dist/framework/path-matcher.d.ts +86 -0
  173. package/dist/framework/path-matcher.d.ts.map +1 -0
  174. package/dist/framework/path-matcher.js +138 -0
  175. package/dist/framework/path-matcher.js.map +1 -0
  176. package/dist/framework/register-helpers.d.ts +10 -0
  177. package/dist/framework/register-helpers.d.ts.map +1 -0
  178. package/dist/framework/register-helpers.js +17 -0
  179. package/dist/framework/register-helpers.js.map +1 -0
  180. package/dist/framework/registry.d.ts +41 -0
  181. package/dist/framework/registry.d.ts.map +1 -0
  182. package/dist/framework/registry.js +103 -0
  183. package/dist/framework/registry.js.map +1 -0
  184. package/dist/framework/result-builder.d.ts +74 -0
  185. package/dist/framework/result-builder.d.ts.map +1 -0
  186. package/dist/framework/result-builder.js +154 -0
  187. package/dist/framework/result-builder.js.map +1 -0
  188. package/dist/framework/scope-resolver.d.ts +23 -0
  189. package/dist/framework/scope-resolver.d.ts.map +1 -0
  190. package/dist/framework/scope-resolver.js +201 -0
  191. package/dist/framework/scope-resolver.js.map +1 -0
  192. package/dist/framework/severity-mapping.d.ts +13 -0
  193. package/dist/framework/severity-mapping.d.ts.map +1 -0
  194. package/dist/framework/severity-mapping.js +51 -0
  195. package/dist/framework/severity-mapping.js.map +1 -0
  196. package/dist/framework/strip-literals.d.ts +48 -0
  197. package/dist/framework/strip-literals.d.ts.map +1 -0
  198. package/dist/framework/strip-literals.js +188 -0
  199. package/dist/framework/strip-literals.js.map +1 -0
  200. package/dist/gate.d.ts +74 -0
  201. package/dist/gate.d.ts.map +1 -0
  202. package/dist/gate.js +257 -0
  203. package/dist/gate.js.map +1 -0
  204. package/dist/index.d.ts +47 -0
  205. package/dist/index.d.ts.map +1 -0
  206. package/dist/index.js +51 -0
  207. package/dist/index.js.map +1 -0
  208. package/dist/plugins/__tests__/check-package-discovery.test.d.ts +2 -0
  209. package/dist/plugins/__tests__/check-package-discovery.test.d.ts.map +1 -0
  210. package/dist/plugins/__tests__/check-package-discovery.test.js +170 -0
  211. package/dist/plugins/__tests__/check-package-discovery.test.js.map +1 -0
  212. package/dist/plugins/__tests__/lang-domain.test.d.ts +2 -0
  213. package/dist/plugins/__tests__/lang-domain.test.d.ts.map +1 -0
  214. package/dist/plugins/__tests__/lang-domain.test.js +171 -0
  215. package/dist/plugins/__tests__/lang-domain.test.js.map +1 -0
  216. package/dist/plugins/__tests__/loader.test.d.ts +2 -0
  217. package/dist/plugins/__tests__/loader.test.d.ts.map +1 -0
  218. package/dist/plugins/__tests__/loader.test.js +194 -0
  219. package/dist/plugins/__tests__/loader.test.js.map +1 -0
  220. package/dist/plugins/check-package-discovery.d.ts +73 -0
  221. package/dist/plugins/check-package-discovery.d.ts.map +1 -0
  222. package/dist/plugins/check-package-discovery.js +212 -0
  223. package/dist/plugins/check-package-discovery.js.map +1 -0
  224. package/dist/plugins/loader.d.ts +31 -0
  225. package/dist/plugins/loader.d.ts.map +1 -0
  226. package/dist/plugins/loader.js +290 -0
  227. package/dist/plugins/loader.js.map +1 -0
  228. package/dist/plugins/types.d.ts +23 -0
  229. package/dist/plugins/types.d.ts.map +1 -0
  230. package/dist/plugins/types.js +9 -0
  231. package/dist/plugins/types.js.map +1 -0
  232. package/dist/recipes/__tests__/built-in-recipes.test.d.ts +2 -0
  233. package/dist/recipes/__tests__/built-in-recipes.test.d.ts.map +1 -0
  234. package/dist/recipes/__tests__/built-in-recipes.test.js +93 -0
  235. package/dist/recipes/__tests__/built-in-recipes.test.js.map +1 -0
  236. package/dist/recipes/__tests__/check-config.test.d.ts +5 -0
  237. package/dist/recipes/__tests__/check-config.test.d.ts.map +1 -0
  238. package/dist/recipes/__tests__/check-config.test.js +37 -0
  239. package/dist/recipes/__tests__/check-config.test.js.map +1 -0
  240. package/dist/recipes/__tests__/check-resolution.test.d.ts +2 -0
  241. package/dist/recipes/__tests__/check-resolution.test.d.ts.map +1 -0
  242. package/dist/recipes/__tests__/check-resolution.test.js +135 -0
  243. package/dist/recipes/__tests__/check-resolution.test.js.map +1 -0
  244. package/dist/recipes/__tests__/registry.test.d.ts +2 -0
  245. package/dist/recipes/__tests__/registry.test.d.ts.map +1 -0
  246. package/dist/recipes/__tests__/registry.test.js +97 -0
  247. package/dist/recipes/__tests__/registry.test.js.map +1 -0
  248. package/dist/recipes/__tests__/retry.test.d.ts +2 -0
  249. package/dist/recipes/__tests__/retry.test.d.ts.map +1 -0
  250. package/dist/recipes/__tests__/retry.test.js +75 -0
  251. package/dist/recipes/__tests__/retry.test.js.map +1 -0
  252. package/dist/recipes/__tests__/service.test.d.ts +11 -0
  253. package/dist/recipes/__tests__/service.test.d.ts.map +1 -0
  254. package/dist/recipes/__tests__/service.test.js +482 -0
  255. package/dist/recipes/__tests__/service.test.js.map +1 -0
  256. package/dist/recipes/built-in-recipes.d.ts +14 -0
  257. package/dist/recipes/built-in-recipes.d.ts.map +1 -0
  258. package/dist/recipes/built-in-recipes.js +247 -0
  259. package/dist/recipes/built-in-recipes.js.map +1 -0
  260. package/dist/recipes/check-config.d.ts +40 -0
  261. package/dist/recipes/check-config.d.ts.map +1 -0
  262. package/dist/recipes/check-config.js +61 -0
  263. package/dist/recipes/check-config.js.map +1 -0
  264. package/dist/recipes/check-resolution.d.ts +21 -0
  265. package/dist/recipes/check-resolution.d.ts.map +1 -0
  266. package/dist/recipes/check-resolution.js +121 -0
  267. package/dist/recipes/check-resolution.js.map +1 -0
  268. package/dist/recipes/check-result-processor.d.ts +51 -0
  269. package/dist/recipes/check-result-processor.d.ts.map +1 -0
  270. package/dist/recipes/check-result-processor.js +158 -0
  271. package/dist/recipes/check-result-processor.js.map +1 -0
  272. package/dist/recipes/parallel-execution.d.ts +33 -0
  273. package/dist/recipes/parallel-execution.d.ts.map +1 -0
  274. package/dist/recipes/parallel-execution.js +142 -0
  275. package/dist/recipes/parallel-execution.js.map +1 -0
  276. package/dist/recipes/registry.d.ts +81 -0
  277. package/dist/recipes/registry.d.ts.map +1 -0
  278. package/dist/recipes/registry.js +131 -0
  279. package/dist/recipes/registry.js.map +1 -0
  280. package/dist/recipes/retry.d.ts +25 -0
  281. package/dist/recipes/retry.d.ts.map +1 -0
  282. package/dist/recipes/retry.js +44 -0
  283. package/dist/recipes/retry.js.map +1 -0
  284. package/dist/recipes/sequential-execution.d.ts +10 -0
  285. package/dist/recipes/sequential-execution.d.ts.map +1 -0
  286. package/dist/recipes/sequential-execution.js +122 -0
  287. package/dist/recipes/sequential-execution.js.map +1 -0
  288. package/dist/recipes/service-types.d.ts +84 -0
  289. package/dist/recipes/service-types.d.ts.map +1 -0
  290. package/dist/recipes/service-types.js +8 -0
  291. package/dist/recipes/service-types.js.map +1 -0
  292. package/dist/recipes/service.d.ts +71 -0
  293. package/dist/recipes/service.d.ts.map +1 -0
  294. package/dist/recipes/service.js +331 -0
  295. package/dist/recipes/service.js.map +1 -0
  296. package/dist/recipes/types.d.ts +154 -0
  297. package/dist/recipes/types.d.ts.map +1 -0
  298. package/dist/recipes/types.js +54 -0
  299. package/dist/recipes/types.js.map +1 -0
  300. package/dist/sarif.d.ts +34 -0
  301. package/dist/sarif.d.ts.map +1 -0
  302. package/dist/sarif.js +192 -0
  303. package/dist/sarif.js.map +1 -0
  304. package/dist/signalers/__tests__/loader.test.d.ts +2 -0
  305. package/dist/signalers/__tests__/loader.test.d.ts.map +1 -0
  306. package/dist/signalers/__tests__/loader.test.js +74 -0
  307. package/dist/signalers/__tests__/loader.test.js.map +1 -0
  308. package/dist/signalers/index.d.ts +8 -0
  309. package/dist/signalers/index.d.ts.map +1 -0
  310. package/dist/signalers/index.js +9 -0
  311. package/dist/signalers/index.js.map +1 -0
  312. package/dist/signalers/loader.d.ts +24 -0
  313. package/dist/signalers/loader.d.ts.map +1 -0
  314. package/dist/signalers/loader.js +108 -0
  315. package/dist/signalers/loader.js.map +1 -0
  316. package/dist/signalers/schema.d.ts +288 -0
  317. package/dist/signalers/schema.d.ts.map +1 -0
  318. package/dist/signalers/schema.js +99 -0
  319. package/dist/signalers/schema.js.map +1 -0
  320. package/dist/signalers/types.d.ts +13 -0
  321. package/dist/signalers/types.d.ts.map +1 -0
  322. package/dist/signalers/types.js +5 -0
  323. package/dist/signalers/types.js.map +1 -0
  324. package/dist/targets/__tests__/loader.test.d.ts +2 -0
  325. package/dist/targets/__tests__/loader.test.d.ts.map +1 -0
  326. package/dist/targets/__tests__/loader.test.js +127 -0
  327. package/dist/targets/__tests__/loader.test.js.map +1 -0
  328. package/dist/targets/__tests__/resolver.test.d.ts +2 -0
  329. package/dist/targets/__tests__/resolver.test.d.ts.map +1 -0
  330. package/dist/targets/__tests__/resolver.test.js +54 -0
  331. package/dist/targets/__tests__/resolver.test.js.map +1 -0
  332. package/dist/targets/__tests__/target-registry.test.d.ts +2 -0
  333. package/dist/targets/__tests__/target-registry.test.d.ts.map +1 -0
  334. package/dist/targets/__tests__/target-registry.test.js +89 -0
  335. package/dist/targets/__tests__/target-registry.test.js.map +1 -0
  336. package/dist/targets/index.d.ts +10 -0
  337. package/dist/targets/index.d.ts.map +1 -0
  338. package/dist/targets/index.js +12 -0
  339. package/dist/targets/index.js.map +1 -0
  340. package/dist/targets/loader.d.ts +19 -0
  341. package/dist/targets/loader.d.ts.map +1 -0
  342. package/dist/targets/loader.js +159 -0
  343. package/dist/targets/loader.js.map +1 -0
  344. package/dist/targets/resolver.d.ts +19 -0
  345. package/dist/targets/resolver.d.ts.map +1 -0
  346. package/dist/targets/resolver.js +37 -0
  347. package/dist/targets/resolver.js.map +1 -0
  348. package/dist/targets/target-registry.d.ts +61 -0
  349. package/dist/targets/target-registry.d.ts.map +1 -0
  350. package/dist/targets/target-registry.js +93 -0
  351. package/dist/targets/target-registry.js.map +1 -0
  352. package/dist/targets/types.d.ts +85 -0
  353. package/dist/targets/types.d.ts.map +1 -0
  354. package/dist/targets/types.js +5 -0
  355. package/dist/targets/types.js.map +1 -0
  356. package/dist/tool.d.ts +17 -0
  357. package/dist/tool.d.ts.map +1 -0
  358. package/dist/tool.js +282 -0
  359. package/dist/tool.js.map +1 -0
  360. package/dist/types/findings.d.ts +117 -0
  361. package/dist/types/findings.d.ts.map +1 -0
  362. package/dist/types/findings.js +93 -0
  363. package/dist/types/findings.js.map +1 -0
  364. package/dist/types/severity.d.ts +15 -0
  365. package/dist/types/severity.d.ts.map +1 -0
  366. package/dist/types/severity.js +36 -0
  367. package/dist/types/severity.js.map +1 -0
  368. package/package.json +45 -0
  369. package/src/__tests__/gate.test.ts +537 -0
  370. package/src/__tests__/sarif.test.ts +201 -0
  371. package/src/cli/dashboard.ts +93 -0
  372. package/src/cli/fit.ts +612 -0
  373. package/src/cli/list-checks.ts +32 -0
  374. package/src/cli/list-recipes.ts +38 -0
  375. package/src/framework/__tests__/ast-utilities.test.ts +157 -0
  376. package/src/framework/__tests__/check-config.test.ts +65 -0
  377. package/src/framework/__tests__/command-executor.test.ts +79 -0
  378. package/src/framework/__tests__/content-filter-dispatch.test.ts +132 -0
  379. package/src/framework/__tests__/content-filter.test.ts +136 -0
  380. package/src/framework/__tests__/define-check.test.ts +180 -0
  381. package/src/framework/__tests__/directive-inventory.test.ts +53 -0
  382. package/src/framework/__tests__/execution-context.test.ts +80 -0
  383. package/src/framework/__tests__/file-accessor.test.ts +121 -0
  384. package/src/framework/__tests__/file-cache.test.ts +142 -0
  385. package/src/framework/__tests__/import-graph.test.ts +282 -0
  386. package/src/framework/__tests__/path-matcher.test.ts +130 -0
  387. package/src/framework/__tests__/register-helpers.test.ts +51 -0
  388. package/src/framework/__tests__/registry.test.ts +243 -0
  389. package/src/framework/__tests__/result-builder.test.ts +178 -0
  390. package/src/framework/__tests__/scope-resolver.test.ts +208 -0
  391. package/src/framework/__tests__/severity-mapping.test.ts +50 -0
  392. package/src/framework/__tests__/strip-literals.test.ts +109 -0
  393. package/src/framework/abortable-exec.ts +177 -0
  394. package/src/framework/ast-utilities.ts +112 -0
  395. package/src/framework/check-config.ts +339 -0
  396. package/src/framework/check-types.ts +77 -0
  397. package/src/framework/command-executor.ts +100 -0
  398. package/src/framework/constants.ts +16 -0
  399. package/src/framework/content-filter.ts +288 -0
  400. package/src/framework/define-check.ts +336 -0
  401. package/src/framework/directive-inventory.ts +110 -0
  402. package/src/framework/directive-parsing.ts +152 -0
  403. package/src/framework/execution-context.ts +247 -0
  404. package/src/framework/file-accessor.ts +171 -0
  405. package/src/framework/file-cache.ts +220 -0
  406. package/src/framework/file-type-filter.ts +25 -0
  407. package/src/framework/ignore-processing.ts +350 -0
  408. package/src/framework/import-graph.ts +280 -0
  409. package/src/framework/memory-profiler.ts +145 -0
  410. package/src/framework/parse-cache.ts +38 -0
  411. package/src/framework/path-matcher.ts +191 -0
  412. package/src/framework/register-helpers.ts +20 -0
  413. package/src/framework/registry.ts +125 -0
  414. package/src/framework/result-builder.ts +225 -0
  415. package/src/framework/scope-resolver.ts +262 -0
  416. package/src/framework/severity-mapping.ts +56 -0
  417. package/src/framework/strip-literals.ts +200 -0
  418. package/src/gate.ts +337 -0
  419. package/src/index.ts +110 -0
  420. package/src/plugins/__tests__/check-package-discovery.test.ts +204 -0
  421. package/src/plugins/__tests__/lang-domain.test.ts +198 -0
  422. package/src/plugins/__tests__/loader.test.ts +226 -0
  423. package/src/plugins/check-package-discovery.ts +242 -0
  424. package/src/plugins/loader.ts +327 -0
  425. package/src/plugins/types.ts +25 -0
  426. package/src/recipes/__tests__/built-in-recipes.test.ts +107 -0
  427. package/src/recipes/__tests__/check-config.test.ts +51 -0
  428. package/src/recipes/__tests__/check-resolution.test.ts +185 -0
  429. package/src/recipes/__tests__/registry.test.ts +115 -0
  430. package/src/recipes/__tests__/retry.test.ts +83 -0
  431. package/src/recipes/__tests__/service.test.ts +572 -0
  432. package/src/recipes/built-in-recipes.ts +273 -0
  433. package/src/recipes/check-config.ts +64 -0
  434. package/src/recipes/check-resolution.ts +169 -0
  435. package/src/recipes/check-result-processor.ts +258 -0
  436. package/src/recipes/parallel-execution.ts +220 -0
  437. package/src/recipes/registry.ts +192 -0
  438. package/src/recipes/retry.ts +69 -0
  439. package/src/recipes/sequential-execution.ts +139 -0
  440. package/src/recipes/service-types.ts +105 -0
  441. package/src/recipes/service.ts +400 -0
  442. package/src/recipes/types.ts +247 -0
  443. package/src/sarif.ts +232 -0
  444. package/src/signalers/__tests__/loader.test.ts +99 -0
  445. package/src/signalers/index.ts +9 -0
  446. package/src/signalers/loader.ts +141 -0
  447. package/src/signalers/schema.ts +117 -0
  448. package/src/signalers/types.ts +15 -0
  449. package/src/targets/__tests__/loader.test.ts +170 -0
  450. package/src/targets/__tests__/resolver.test.ts +74 -0
  451. package/src/targets/__tests__/target-registry.test.ts +103 -0
  452. package/src/targets/index.ts +13 -0
  453. package/src/targets/loader.ts +214 -0
  454. package/src/targets/resolver.ts +44 -0
  455. package/src/targets/target-registry.ts +111 -0
  456. package/src/targets/types.ts +89 -0
  457. package/src/tool.ts +302 -0
  458. package/src/types/findings.ts +239 -0
  459. package/src/types/severity.ts +39 -0
  460. package/tsconfig.json +8 -0
  461. package/vitest.config.ts +33 -0
@@ -0,0 +1,400 @@
1
+ // @fitness-ignore-file file-length-limits -- Complex module with tightly coupled logic; splitting would fragment cohesive functionality
2
+ // @fitness-ignore-file eslint-backend -- Fitness framework orchestrator; ESLint rule variations between fitness runner and IDE are expected
3
+ /**
4
+ * @fileoverview Central orchestrator for fitness recipe execution
5
+ *
6
+ * FitnessRecipeService resolves checks, manages session lifecycle,
7
+ * coordinates parallel/sequential execution, and builds results.
8
+ */
9
+
10
+ import { logger , NotFoundError, SystemError , generateId , initParseCache, clearParseCache } from '@opensip-tools/core'
11
+
12
+ import { fileCache, DEFAULT_PREWARM_PATTERNS } from '../framework/file-cache.js'
13
+ import { defaultRegistry, type Check, type CheckRegistry } from '../framework/registry.js'
14
+
15
+ import { setCurrentRecipeCheckConfig, clearCurrentRecipeCheckConfig } from './check-config.js'
16
+ import { resolveChecks, validateCheckReferences } from './check-resolution.js'
17
+ import { executeParallel, type ExecutionOptions, type ExecutionServiceContext } from './parallel-execution.js'
18
+ import { defaultRecipeRegistry, type FitnessRecipeRegistry } from './registry.js'
19
+ import { executeSequential } from './sequential-execution.js'
20
+ import {
21
+ DEFAULT_MAX_PARALLEL,
22
+ type CheckSelector,
23
+ type FitnessRecipe,
24
+ type FitnessRecipeResult,
25
+ type RecipeRunSummary,
26
+ } from './types.js'
27
+
28
+ import type {
29
+ FitnessRecipeServiceCallbacks,
30
+ FitnessRecipeServiceConfig,
31
+ FitnessRecipeSession,
32
+ } from './service-types.js'
33
+ import type { DirectiveEntry } from '../framework/directive-inventory.js'
34
+
35
+ const MODULE_FITNESS_RECIPES = 'fitness:recipes'
36
+
37
+ /** Default success threshold percentage when none is configured. */
38
+ const DEFAULT_SUCCESS_THRESHOLD_PERCENT = 85
39
+
40
+ /**
41
+ * Compute prewarm glob patterns from the resolved checks' fileTypes.
42
+ * If any check is universal (no fileTypes), falls back to DEFAULT_PREWARM_PATTERNS.
43
+ */
44
+ function computePrewarmPatterns(checks: readonly Check[]): readonly string[] {
45
+ const extensions = new Set<string>()
46
+ for (const check of checks) {
47
+ const ft = check.config.fileTypes
48
+ if (!ft || ft.length === 0) {
49
+ // Universal check — need all file types
50
+ return DEFAULT_PREWARM_PATTERNS
51
+ }
52
+ for (const ext of ft) {
53
+ extensions.add(ext)
54
+ }
55
+ }
56
+ return [...extensions].sort().map((ext) => `**/*.${ext}`)
57
+ }
58
+
59
+ /**
60
+ * Central orchestrator for fitness check execution.
61
+ */
62
+ export class FitnessRecipeService {
63
+ private readonly config: FitnessRecipeServiceConfig
64
+ private readonly checkRegistry: CheckRegistry
65
+ private readonly recipeRegistry: FitnessRecipeRegistry
66
+ private activeSession: FitnessRecipeSession | null = null
67
+ private abortController?: AbortController
68
+
69
+ constructor(config?: FitnessRecipeServiceConfig) {
70
+ this.config = config ?? {}
71
+ this.checkRegistry = config?.checkRegistry ?? defaultRegistry
72
+ this.recipeRegistry = config?.recipeRegistry ?? defaultRecipeRegistry
73
+ }
74
+
75
+ private get session(): FitnessRecipeSession {
76
+ if (!this.activeSession) {
77
+ throw new SystemError('No active session', { code: 'SYSTEM.FITNESS.NO_SESSION' })
78
+ }
79
+ return this.activeSession
80
+ }
81
+
82
+ /**
83
+ * Execute a fitness recipe by name or recipe object.
84
+ *
85
+ * Resolves checks, prewarms the file cache, runs checks in parallel or sequential mode,
86
+ * then builds and returns a {@link FitnessRecipeResult}. Only one session can be active
87
+ * at a time — call {@link abort} to cancel a running session.
88
+ *
89
+ * @param recipeOrName - A recipe name (looked up in the recipe registry) or a FitnessRecipe object.
90
+ * @returns The result of the recipe execution including per-check results and summary.
91
+ * @throws {SystemError} If a session is already in progress.
92
+ * @throws {NotFoundError} If the recipe name is not found in the registry.
93
+ */
94
+ // @fitness-ignore-next-line result-pattern-consistency -- return type is FitnessRecipeResult (not Result<T,E>); throw is appropriate for precondition failures
95
+ async start(recipeOrName: FitnessRecipe | string): Promise<FitnessRecipeResult> {
96
+ if (this.activeSession) {
97
+ throw new SystemError('Recipe execution already in progress', { code: 'SYSTEM.FITNESS.SESSION_IN_PROGRESS' })
98
+ }
99
+
100
+ const recipe = typeof recipeOrName === 'string' ? this.getRecipe(recipeOrName) : recipeOrName
101
+
102
+ if (!recipe) {
103
+ const identifier = typeof recipeOrName === 'string' ? recipeOrName : recipeOrName.name
104
+ // @fitness-ignore-next-line result-pattern-consistency -- internal method, exceptions propagate to CLI boundary
105
+ throw new NotFoundError(`Recipe not found: ${identifier}`, { code: 'RESOURCE.NOT_FOUND.RECIPE', metadata: { entity: 'recipe', identifier } })
106
+ }
107
+
108
+ return this.executeRecipe(recipe)
109
+ }
110
+
111
+ private async executeRecipe(recipe: FitnessRecipe): Promise<FitnessRecipeResult> {
112
+ const sessionId = this.generateSessionId()
113
+ this.activeSession = this.createSession(sessionId, recipe)
114
+
115
+ this.abortController = new AbortController()
116
+
117
+ logger.info('Starting recipe session', { evt: 'fitness.recipe.session.start', module: MODULE_FITNESS_RECIPES, sessionId, recipeName: recipe.name })
118
+
119
+ // Project the recipe's per-check config into module-level state so that
120
+ // individual checks can read their slice via getCheckConfig<T>(slug).
121
+ // Cleared in the `finally` below.
122
+ setCurrentRecipeCheckConfig(recipe.checks.config)
123
+
124
+ try {
125
+ const cwd = this.config.cwd ?? process.cwd()
126
+ const checks = this.resolveAndFilterChecks(recipe)
127
+
128
+ this.activeSession.totalChecks = checks.length
129
+
130
+ if (checks.length === 0) {
131
+ return this.buildResult()
132
+ }
133
+
134
+ await this.prepareExecution(checks, cwd)
135
+
136
+ // Execute
137
+ const execOpts: ExecutionOptions = {
138
+ checks,
139
+ cwd,
140
+ recipe,
141
+ checkTargetFiles: this.config.checkTargetFiles,
142
+ ...(this.config.globalExcludes ? { globalExcludes: this.config.globalExcludes } : {}),
143
+ }
144
+ const execCtx: ExecutionServiceContext = {
145
+ session: this.activeSession,
146
+ callbacks: this.callbacks,
147
+ abortController: this.abortController,
148
+ includeViolations: this.config.includeViolations ?? false,
149
+ }
150
+
151
+ await (recipe.execution.mode === 'parallel' ? executeParallel(execCtx, execOpts) : executeSequential(execCtx, execOpts));
152
+
153
+ this.activeSession.directives = this.collectAppliedDirectives()
154
+
155
+ this.activeSession.status = 'completed'
156
+ const result = this.buildResult()
157
+
158
+ logger.info('Recipe session completed', { evt: 'fitness.recipe.session.complete', module: MODULE_FITNESS_RECIPES, sessionId, recipeName: recipe.name, passed: result.summary.passedChecks, failed: result.summary.failedChecks, durationMs: result.durationMs })
159
+ void this.callbacks.onComplete?.(result)
160
+ return result
161
+ } catch (error) {
162
+ logger.error('Recipe session failed', { evt: 'fitness.recipe.session.error', module: MODULE_FITNESS_RECIPES, sessionId, recipeName: recipe.name, err: error instanceof Error ? error : undefined })
163
+ if (this.activeSession) {
164
+ this.activeSession.status = 'failed'
165
+ }
166
+ throw error
167
+ } finally {
168
+ clearCurrentRecipeCheckConfig()
169
+ void clearParseCache()
170
+ fileCache.clear()
171
+ this.abortController?.abort()
172
+ delete this.abortController
173
+ this.activeSession = null
174
+ }
175
+ }
176
+
177
+ private collectAppliedDirectives(): DirectiveEntry[] {
178
+ const result: DirectiveEntry[] = []
179
+ const session = this.activeSession
180
+ if (!session) return result
181
+ for (const cr of session.checkResults) {
182
+ if (cr.appliedDirectives) {
183
+ for (const directive of cr.appliedDirectives) {
184
+ result.push(directive)
185
+ }
186
+ }
187
+ }
188
+ return result
189
+ }
190
+
191
+ private createSession(sessionId: string, recipe: FitnessRecipe): FitnessRecipeSession {
192
+ return {
193
+ sessionId,
194
+ recipe,
195
+ startedAt: new Date(),
196
+ status: 'running',
197
+ totalChecks: 0,
198
+ completedChecks: 0,
199
+ passedChecks: 0,
200
+ failedChecks: 0,
201
+ totalErrors: 0,
202
+ totalWarnings: 0,
203
+ totalIgnored: 0,
204
+ ignoresByTag: new Map(),
205
+ checkResults: [],
206
+ directives: [],
207
+ }
208
+ }
209
+
210
+ private resolveAndFilterChecks(recipe: FitnessRecipe): Check[] {
211
+ const checkSlugs = resolveChecks(recipe.checks, this.checkRegistry)
212
+
213
+ // Validate explicit references
214
+ if (recipe.checks.type === 'explicit') {
215
+ const allSlugs = this.checkRegistry.listSlugs()
216
+ const { missing } = validateCheckReferences(recipe.checks.checkIds, [...allSlugs])
217
+ if (missing.length > 0) {
218
+ logger.warn(`Recipe references ${missing.length} unknown check(s)`, { evt: 'fitness.recipe.checks.missing', module: MODULE_FITNESS_RECIPES, missing, recipeName: recipe.name })
219
+ }
220
+ }
221
+
222
+ const configDisabled = new Set(this.config.disabledChecks)
223
+ const includeDisabledSet = new Set(recipe.includeDisabled)
224
+ const checks: Check[] = []
225
+
226
+ // Warn about unknown slugs in disabledChecks config
227
+ if (configDisabled.size > 0) {
228
+ const allSlugs = new Set(this.checkRegistry.listSlugs())
229
+ const unknownDisabled = [...configDisabled].filter((s) => !allSlugs.has(s))
230
+ if (unknownDisabled.length > 0) {
231
+ logger.warn(`disabledChecks references ${unknownDisabled.length} unknown slug(s)`, { evt: 'fitness.recipe.disabled.unknown', module: MODULE_FITNESS_RECIPES, unknownDisabled })
232
+ }
233
+ }
234
+
235
+ for (const slug of checkSlugs) {
236
+ const check = this.checkRegistry.getBySlug(slug)
237
+ if (!check) continue
238
+ const bareSlug = slug.includes(':') ? slug.split(':').pop()! : slug
239
+ const isDisabled = (check.config.disabled ?? false) || configDisabled.has(slug) || configDisabled.has(bareSlug)
240
+ const isForceIncluded = includeDisabledSet.has(slug) || includeDisabledSet.has(bareSlug)
241
+ if (!isDisabled || isForceIncluded) {
242
+ checks.push(check)
243
+ }
244
+ }
245
+
246
+ return checks
247
+ }
248
+
249
+ private async prepareExecution(checks: Check[], cwd: string): Promise<void> {
250
+ // Sync check catalog for dashboard visibility
251
+ if (this.callbacks.onCatalogSync) {
252
+ const entries = this.checkRegistry.list().map((c) => ({
253
+ id: c.config.id,
254
+ slug: c.config.slug,
255
+ tags: c.config.tags,
256
+ description: c.config.description,
257
+ }))
258
+ void this.callbacks.onCatalogSync(entries)
259
+ }
260
+
261
+ // Prewarm file cache with only the extensions needed by resolved checks
262
+ if (this.config.prewarmCache !== false) {
263
+ const patterns = this.config.prewarmPatterns ?? computePrewarmPatterns(checks)
264
+ await fileCache.prewarm(cwd, patterns)
265
+ }
266
+
267
+ // Initialize shared AST parse cache for cross-check deduplication
268
+ void initParseCache()
269
+ }
270
+
271
+ private buildResult(): FitnessRecipeResult {
272
+ const session = this.session
273
+ const completedAt = new Date()
274
+
275
+ const summary: RecipeRunSummary = {
276
+ totalChecks: session.totalChecks,
277
+ passedChecks: session.passedChecks,
278
+ failedChecks: session.failedChecks,
279
+ skippedChecks: session.totalChecks - session.completedChecks,
280
+ erroredChecks: session.checkResults.filter((r) => r.error !== undefined).length,
281
+ totalViolations: session.checkResults.reduce((sum, r) => sum + r.violationCount, 0),
282
+ totalErrors: session.totalErrors,
283
+ totalWarnings: session.totalWarnings,
284
+ totalIgnored: session.totalIgnored,
285
+ }
286
+
287
+ const score = session.totalChecks > 0 ? Math.round((session.passedChecks / session.totalChecks) * 100) : 100
288
+
289
+ const result: FitnessRecipeResult = {
290
+ recipeId: session.recipe.id,
291
+ recipeName: session.recipe.name,
292
+ sessionId: session.sessionId,
293
+ success: score >= (session.recipe.execution.successThreshold ?? DEFAULT_SUCCESS_THRESHOLD_PERCENT) && session.status === 'completed',
294
+ startedAt: session.startedAt,
295
+ completedAt,
296
+ durationMs: completedAt.getTime() - session.startedAt.getTime(),
297
+ checkResults: session.checkResults,
298
+ summary,
299
+ }
300
+
301
+ return {
302
+ ...result,
303
+ ...(session.ignoreCounts ? { ignoreCounts: session.ignoreCounts } : {}),
304
+ ...(session.directives.length > 0 ? { directives: session.directives } : {}),
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Convert CLI arguments to an ad-hoc FitnessRecipe.
310
+ */
311
+ static createAdHocRecipe(args: {
312
+ check?: string
313
+ tagFilters?: string[]
314
+ file?: string
315
+ parallel?: boolean
316
+ json?: boolean
317
+ unified?: boolean
318
+ verbose?: boolean
319
+ retry?: boolean
320
+ maxRetries?: number
321
+ maxParallel?: number
322
+ timeout?: number
323
+ successThreshold?: number
324
+ }): FitnessRecipe {
325
+ let checks: CheckSelector
326
+ let includeDisabled: string[] | undefined
327
+
328
+ if (args.check) {
329
+ if (args.check.includes('*') || args.check.includes('?')) {
330
+ checks = { type: 'pattern', include: [args.check] }
331
+ } else {
332
+ checks = { type: 'explicit', checkIds: [args.check] }
333
+ includeDisabled = [args.check]
334
+ }
335
+ } else if (args.tagFilters?.length) {
336
+ checks = { type: 'tags', include: args.tagFilters }
337
+ } else {
338
+ checks = { type: 'all', exclude: [] }
339
+ }
340
+
341
+ return {
342
+ id: 'RCP_cli-adhoc',
343
+ name: 'cli-adhoc',
344
+ displayName: 'CLI Ad-Hoc',
345
+ description: 'Dynamically created recipe from CLI arguments',
346
+ checks,
347
+ execution: {
348
+ mode: args.parallel === false ? 'sequential' : 'parallel',
349
+ stopOnFirstFailure: false,
350
+ timeout: args.timeout ?? 30_000,
351
+ maxParallel: args.maxParallel ?? DEFAULT_MAX_PARALLEL,
352
+ retryOnFailure: args.retry,
353
+ maxRetries: args.maxRetries ?? 2,
354
+ successThreshold: args.successThreshold,
355
+ },
356
+ reporting: {
357
+ format: (() => {
358
+ if (!args.json) return 'table' as const;
359
+ return args.unified ? 'unified' as const : 'json' as const;
360
+ })(),
361
+ verbose: args.verbose ?? false,
362
+ },
363
+ ...(includeDisabled ? { includeDisabled } : {}),
364
+ ...(args.file ? { fileFilter: args.file } : {}),
365
+ }
366
+ }
367
+
368
+ /** Get the currently active session, or null if no recipe is running. */
369
+ getActiveSession(): FitnessRecipeSession | null {
370
+ return this.activeSession
371
+ }
372
+
373
+ /** Abort the currently running recipe execution. No-op if no session is active. */
374
+ abort(): void {
375
+ this.abortController?.abort()
376
+ }
377
+
378
+ /** List all available recipes from the recipe registry. */
379
+ listRecipes(): readonly FitnessRecipe[] {
380
+ return this.recipeRegistry.getAllRecipes()
381
+ }
382
+
383
+ /**
384
+ * Look up a recipe by name or ID.
385
+ * @param nameOrId - The recipe name or full recipe ID (e.g. "default" or "RCP_default").
386
+ * @returns The recipe if found, undefined otherwise.
387
+ */
388
+ getRecipe(nameOrId: string): FitnessRecipe | undefined {
389
+ return this.recipeRegistry.loadRecipe(nameOrId)
390
+ }
391
+
392
+ protected generateSessionId(): string {
393
+ return generateId('SES')
394
+ }
395
+
396
+ protected get callbacks(): FitnessRecipeServiceCallbacks {
397
+ return this.config.callbacks ?? {}
398
+ }
399
+ }
400
+
@@ -0,0 +1,247 @@
1
+ // @fitness-ignore-file module-coupling-metrics -- type definition module: exports are related interfaces/types for the recipe execution domain
2
+ /**
3
+ * @fileoverview Core types for fitness recipes
4
+ *
5
+ * Defines FitnessRecipe, CheckSelector, RecipeCheckResult, and related
6
+ * types used throughout the recipe execution system.
7
+ */
8
+
9
+ import os from 'node:os'
10
+
11
+ import type { DirectiveEntry } from '../framework/directive-inventory.js'
12
+
13
+ // =============================================================================
14
+ // CHECK SELECTOR TYPES
15
+ // =============================================================================
16
+
17
+ /**
18
+ * Per-check configuration map.
19
+ *
20
+ * Keys are check slugs; values are check-specific config objects whose shape
21
+ * is declared by the consuming check. The recipe service projects this map
22
+ * into module-level state before execution so each check can read its slice
23
+ * via `getCheckConfig<T>(slug)`. See `check-config.ts`.
24
+ */
25
+ export type RecipeCheckConfigMap = Readonly<Record<string, Readonly<Record<string, unknown>>>>
26
+
27
+ /** Selector that specifies checks by explicit slug list */
28
+ interface ExplicitCheckSelector {
29
+ readonly type: 'explicit'
30
+ readonly checkIds: readonly string[]
31
+ readonly config?: RecipeCheckConfigMap
32
+ }
33
+
34
+ /** Selector that matches checks via glob patterns */
35
+ interface PatternCheckSelector {
36
+ readonly type: 'pattern'
37
+ readonly include: readonly string[]
38
+ readonly exclude?: readonly string[]
39
+ readonly config?: RecipeCheckConfigMap
40
+ }
41
+
42
+ /** Selector that includes all checks with specified tags */
43
+ interface TagsCheckSelector {
44
+ readonly type: 'tags'
45
+ readonly include: readonly string[]
46
+ readonly exclude?: readonly string[]
47
+ readonly config?: RecipeCheckConfigMap
48
+ }
49
+
50
+ /** Selector that includes all checks with optional exclusions */
51
+ interface AllCheckSelector {
52
+ readonly type: 'all'
53
+ readonly exclude?: readonly string[]
54
+ readonly config?: RecipeCheckConfigMap
55
+ }
56
+
57
+ /** Union of all check selector types used by recipes */
58
+ export type CheckSelector =
59
+ | ExplicitCheckSelector
60
+ | PatternCheckSelector
61
+ | TagsCheckSelector
62
+ | AllCheckSelector
63
+
64
+ // =============================================================================
65
+ // EXECUTION OPTIONS
66
+ // =============================================================================
67
+
68
+ /** Execution configuration for a fitness recipe */
69
+ interface FitnessExecutionOptions {
70
+ readonly mode: 'parallel' | 'sequential'
71
+ readonly stopOnFirstFailure: boolean
72
+ readonly timeout?: number
73
+ readonly maxParallel?: number
74
+ readonly retryOnFailure?: boolean
75
+ readonly maxRetries?: number
76
+ readonly successThreshold?: number
77
+ }
78
+
79
+ // =============================================================================
80
+ // REPORTING OPTIONS
81
+ // =============================================================================
82
+
83
+ /** Reporting output configuration for a fitness recipe */
84
+ interface FitnessReportingOptions {
85
+ readonly format: 'table' | 'json' | 'unified'
86
+ readonly verbose: boolean
87
+ readonly outputPath?: string
88
+ }
89
+
90
+ // =============================================================================
91
+ // FITNESS RECIPE
92
+ // =============================================================================
93
+
94
+ /** Complete recipe definition: checks, execution, and reporting */
95
+ export interface FitnessRecipe {
96
+ readonly id: string
97
+ readonly name: string
98
+ readonly displayName: string
99
+ readonly description: string
100
+ readonly checks: CheckSelector
101
+ readonly execution: FitnessExecutionOptions
102
+ readonly reporting: FitnessReportingOptions
103
+ readonly tags?: readonly string[]
104
+ readonly includeDisabled?: readonly string[]
105
+ readonly fileFilter?: string
106
+ }
107
+
108
+ // =============================================================================
109
+ // RECIPE RUN RESULT
110
+ // =============================================================================
111
+
112
+ /** A single violation detail from a fitness check. */
113
+ interface RecipeViolation {
114
+ readonly file: string
115
+ readonly line: number
116
+ readonly column?: number
117
+ readonly message: string
118
+ readonly severity: 'error' | 'warning'
119
+ readonly suggestion?: string
120
+ }
121
+
122
+ /** Result of a single check within a recipe execution */
123
+ export interface RecipeCheckResult {
124
+ readonly checkId: string
125
+ readonly checkSlug: string
126
+ readonly passed: boolean
127
+ readonly violationCount: number
128
+ readonly errorCount: number
129
+ readonly warningCount: number
130
+ readonly ignoredCount: number
131
+ readonly durationMs: number
132
+ readonly totalItems?: number | undefined
133
+ readonly itemType?: string | undefined
134
+ readonly skipped: boolean
135
+ readonly skipReason?: string
136
+ readonly error?: string
137
+ readonly timedOut?: boolean
138
+ appliedDirectives?: readonly DirectiveEntry[] | undefined
139
+ /** Violation details. Populated when includeViolations is true. */
140
+ readonly violations?: readonly RecipeViolation[]
141
+ }
142
+
143
+ /** Aggregated summary of a complete recipe run */
144
+ export interface RecipeRunSummary {
145
+ readonly totalChecks: number
146
+ readonly passedChecks: number
147
+ readonly failedChecks: number
148
+ readonly skippedChecks: number
149
+ readonly erroredChecks: number
150
+ readonly totalViolations: number
151
+ readonly totalErrors: number
152
+ readonly totalWarnings: number
153
+ readonly totalIgnored: number
154
+ }
155
+
156
+ /** Ignore directive counts by type. */
157
+ export interface IgnoresByType {
158
+ file: number
159
+ line: number
160
+ block: number
161
+ total: number
162
+ }
163
+
164
+ /** Complete result of a recipe execution including all check results and summary */
165
+ export interface FitnessRecipeResult {
166
+ readonly recipeId: string
167
+ readonly recipeName: string
168
+ readonly sessionId: string
169
+ readonly success: boolean
170
+ readonly startedAt: Date
171
+ readonly completedAt: Date
172
+ readonly durationMs: number
173
+ readonly checkResults: readonly RecipeCheckResult[]
174
+ readonly summary: RecipeRunSummary
175
+ readonly ignoreCounts?: IgnoresByType | undefined
176
+ readonly directives?: readonly DirectiveEntry[] | undefined
177
+ }
178
+
179
+ // =============================================================================
180
+ // RECIPE BUILDER HELPERS
181
+ // =============================================================================
182
+
183
+ /** Input definition used by defineRecipe() to create a FitnessRecipe */
184
+ export interface FitnessRecipeDefinition {
185
+ readonly name: string
186
+ readonly displayName: string
187
+ readonly description: string
188
+ readonly checks: CheckSelector
189
+ readonly execution?: Partial<FitnessExecutionOptions>
190
+ readonly reporting?: Partial<FitnessReportingOptions>
191
+ readonly tags?: readonly string[]
192
+ readonly includeDisabled?: readonly string[]
193
+ }
194
+
195
+ /** Default maximum parallelism based on available CPU cores */
196
+ export const DEFAULT_MAX_PARALLEL = os.availableParallelism?.() ?? os.cpus().length
197
+
198
+ /** Default execution options applied when not overridden by a recipe */
199
+ const DEFAULT_EXECUTION_OPTIONS: FitnessExecutionOptions = {
200
+ mode: 'parallel',
201
+ stopOnFirstFailure: false,
202
+ timeout: 30_000,
203
+ maxParallel: DEFAULT_MAX_PARALLEL,
204
+ } as const
205
+
206
+ /** Return the effective max parallelism for a recipe, falling back to the system default */
207
+ export function getEffectiveMaxParallel(recipe: FitnessRecipe): number {
208
+ return recipe.execution.maxParallel ?? DEFAULT_MAX_PARALLEL
209
+ }
210
+
211
+ /** Default reporting options applied when not overridden by a recipe */
212
+ const DEFAULT_REPORTING_OPTIONS: FitnessReportingOptions = {
213
+ format: 'table',
214
+ verbose: false,
215
+ } as const
216
+
217
+ /** Create a frozen FitnessRecipe from a definition, applying defaults for missing options */
218
+ export function defineRecipe(definition: FitnessRecipeDefinition): FitnessRecipe {
219
+ const id = `RCP_${definition.name}`
220
+
221
+ const recipe: FitnessRecipe = {
222
+ id,
223
+ name: definition.name,
224
+ displayName: definition.displayName,
225
+ description: definition.description,
226
+ checks: definition.checks,
227
+ execution: {
228
+ ...DEFAULT_EXECUTION_OPTIONS,
229
+ ...definition.execution,
230
+ },
231
+ reporting: {
232
+ ...DEFAULT_REPORTING_OPTIONS,
233
+ ...definition.reporting,
234
+ },
235
+ }
236
+
237
+ if (definition.tags || definition.includeDisabled) {
238
+ return Object.freeze({
239
+ ...recipe,
240
+ ...(definition.tags ? { tags: definition.tags } : {}),
241
+ ...(definition.includeDisabled ? { includeDisabled: definition.includeDisabled } : {}),
242
+ })
243
+ }
244
+
245
+ return Object.freeze(recipe)
246
+ }
247
+