@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,258 @@
1
+ /**
2
+ * @fileoverview Check result processor for fitness recipe execution
3
+ *
4
+ * Processes individual check results (success or error), updates session state,
5
+ * builds check summaries, and determines whether execution should stop.
6
+ */
7
+
8
+ import { logger , SystemError } from '@opensip-tools/core'
9
+
10
+ import { memoryProfiler, type CheckMemoryProfile } from '../framework/memory-profiler.js'
11
+ import { countErrors, countWarnings } from '../types/severity.js'
12
+
13
+ import type { CheckSummary, FitnessRecipeServiceCallbacks, FitnessRecipeSession } from './service-types.js'
14
+ import type { FitnessRecipe, RecipeCheckResult } from './types.js'
15
+ import type { CheckResult } from '../types/findings.js'
16
+
17
+ // =============================================================================
18
+ // TYPES
19
+ // =============================================================================
20
+
21
+ /** Input data for processing a successful check result */
22
+ export interface ProcessSuccessInput {
23
+ checkId: string
24
+ checkSlug: string
25
+ tags: readonly string[]
26
+ checkIndex: number
27
+ totalChecks: number
28
+ result: CheckResult
29
+ durationMs: number
30
+ memoryBeforeMB: number
31
+ }
32
+
33
+ /** Input data for processing a failed or timed-out check result */
34
+ export interface ProcessErrorInput {
35
+ checkId: string
36
+ checkSlug: string
37
+ checkIndex: number
38
+ totalChecks: number
39
+ error: unknown
40
+ durationMs: number
41
+ memoryBeforeMB: number
42
+ timedOut: boolean
43
+ timeoutMs?: number
44
+ }
45
+
46
+ /** Context required by the result processor (session, callbacks, recipe) */
47
+ export interface ProcessorContext {
48
+ session: FitnessRecipeSession
49
+ callbacks: FitnessRecipeServiceCallbacks
50
+ recipe: FitnessRecipe
51
+ includeViolations: boolean
52
+ }
53
+
54
+ /** Output from processing a check result, including whether execution should stop */
55
+ export interface ProcessResultOutput {
56
+ checkResult: RecipeCheckResult
57
+ memoryProfile: CheckMemoryProfile
58
+ shouldStop: boolean
59
+ }
60
+
61
+ // =============================================================================
62
+ // CHECK SUMMARY BUILDERS
63
+ // =============================================================================
64
+
65
+ /** Build a CheckSummary from a successful check execution */
66
+ function createCheckSummary(
67
+ checkId: string,
68
+ checkSlug: string,
69
+ passed: boolean,
70
+ errorCount: number,
71
+ warningCount: number,
72
+ durationMs: number,
73
+ memoryProfile: CheckMemoryProfile,
74
+ ignoredCount = 0,
75
+ filesScanned = 0,
76
+ ): CheckSummary {
77
+ return {
78
+ checkId,
79
+ checkSlug,
80
+ passed,
81
+ errors: errorCount,
82
+ warnings: warningCount,
83
+ durationMs,
84
+ filesScanned,
85
+ ignoredCount,
86
+ memoryProfile,
87
+ }
88
+ }
89
+
90
+ /** Build a CheckSummary from a failed or errored check execution */
91
+ function createErrorSummary(
92
+ checkId: string,
93
+ checkSlug: string,
94
+ durationMs: number,
95
+ memoryProfile: CheckMemoryProfile,
96
+ timedOut: boolean,
97
+ errorMessage: string,
98
+ ): CheckSummary {
99
+ return {
100
+ checkId,
101
+ checkSlug,
102
+ passed: false,
103
+ errors: 1,
104
+ warnings: 0,
105
+ durationMs,
106
+ filesScanned: 0,
107
+ ignoredCount: 0,
108
+ memoryProfile,
109
+ timedOut,
110
+ errorMessage,
111
+ }
112
+ }
113
+
114
+ // =============================================================================
115
+ // SESSION UPDATES
116
+ // =============================================================================
117
+
118
+ function updateSessionForSuccess(
119
+ session: FitnessRecipeSession,
120
+ checkResult: RecipeCheckResult,
121
+ tags: readonly string[],
122
+ ): void {
123
+ session.checkResults.push(checkResult)
124
+ session.completedChecks++
125
+
126
+ if (checkResult.passed) {
127
+ session.passedChecks++
128
+ } else {
129
+ session.failedChecks++
130
+ }
131
+
132
+ session.totalErrors += checkResult.errorCount
133
+ session.totalWarnings += checkResult.warningCount
134
+ session.totalIgnored += checkResult.ignoredCount
135
+ for (const tag of tags) {
136
+ session.ignoresByTag.set(tag, (session.ignoresByTag.get(tag) ?? 0) + checkResult.ignoredCount)
137
+ }
138
+ }
139
+
140
+ function updateSessionForError(session: FitnessRecipeSession, checkResult: RecipeCheckResult): void {
141
+ session.checkResults.push(checkResult)
142
+ session.completedChecks++
143
+ session.failedChecks++
144
+ session.totalErrors++
145
+ }
146
+
147
+ // =============================================================================
148
+ // MAIN PROCESSORS
149
+ // =============================================================================
150
+
151
+ /** Process a successful check result: update session, invoke callbacks, and determine stop condition */
152
+ export function processSuccessResult(
153
+ ctx: ProcessorContext,
154
+ input: ProcessSuccessInput,
155
+ ): ProcessResultOutput {
156
+ const { checkId, checkSlug, tags, checkIndex, totalChecks, result, durationMs, memoryBeforeMB } =
157
+ input
158
+ const { session, callbacks, recipe } = ctx
159
+
160
+ // Apply file filter if set
161
+ let effectiveSignals = result.signals
162
+ if (recipe.fileFilter) {
163
+ effectiveSignals = result.signals.filter(
164
+ (s) => s.code?.file === recipe.fileFilter,
165
+ )
166
+ }
167
+ const signalCount = effectiveSignals.length
168
+ const errorCount = recipe.fileFilter
169
+ ? countErrors(effectiveSignals)
170
+ : result.errors
171
+ const warningCount = recipe.fileFilter
172
+ ? countWarnings(effectiveSignals)
173
+ : result.warnings
174
+ const ignoredCount = result.ignoredCount ?? 0
175
+ const passed = recipe.fileFilter ? errorCount === 0 : result.passed
176
+
177
+ const memoryProfile = memoryProfiler.recordCheckComplete(checkId, memoryBeforeMB, signalCount, durationMs)
178
+
179
+ if (memoryProfiler.exceedsThreshold(memoryProfile.memoryDeltaMB)) {
180
+ logger.warn('Check exceeded memory threshold', { evt: 'fitness.check.memory.exceeded', module: 'fitness:recipes', checkId, checkSlug, memoryDeltaMB: memoryProfile.memoryDeltaMB })
181
+ callbacks.onMemoryWarning?.(checkId, memoryProfile)
182
+ }
183
+
184
+ const checkResult: RecipeCheckResult = {
185
+ checkId,
186
+ checkSlug,
187
+ passed,
188
+ violationCount: signalCount,
189
+ errorCount,
190
+ warningCount,
191
+ ignoredCount,
192
+ durationMs,
193
+ totalItems: result.metadata.totalItems,
194
+ itemType: result.metadata.itemType,
195
+ skipped: false,
196
+ ...(result.appliedDirectives && result.appliedDirectives.length > 0
197
+ ? { appliedDirectives: result.appliedDirectives }
198
+ : {}),
199
+ ...(ctx.includeViolations ? {
200
+ violations: effectiveSignals.map((s) => ({
201
+ file: s.code?.file ?? 'unknown',
202
+ line: s.code?.line ?? 0,
203
+ column: s.code?.column,
204
+ message: s.message,
205
+ severity: s.severity === 'high' ? 'error' as const : 'warning' as const,
206
+ suggestion: s.suggestion,
207
+ })),
208
+ } : {}),
209
+ }
210
+
211
+ updateSessionForSuccess(session, checkResult, tags)
212
+
213
+ const summary = createCheckSummary(checkId, checkSlug, passed, errorCount, warningCount, durationMs, memoryProfile, ignoredCount, result.metadata.filesScanned ?? 0)
214
+ callbacks.onCheckComplete?.(checkSlug, summary, checkIndex, totalChecks)
215
+
216
+ const shouldStop = recipe.execution.stopOnFirstFailure && !passed
217
+
218
+ return { checkResult, memoryProfile, shouldStop }
219
+ }
220
+
221
+ /** Process a failed check result: update session, invoke callbacks, and determine stop condition */
222
+ export function processErrorResult(ctx: ProcessorContext, input: ProcessErrorInput): ProcessResultOutput {
223
+ const { checkId, checkSlug, checkIndex, totalChecks, error, durationMs, memoryBeforeMB, timedOut, timeoutMs } = input
224
+ const { session, callbacks, recipe } = ctx
225
+
226
+ let errMsg: string
227
+ if (timedOut && timeoutMs) {
228
+ errMsg = `Check ${checkSlug} timed out after ${timeoutMs}ms`
229
+ } else {
230
+ errMsg = error instanceof Error ? error.message : String(error)
231
+ }
232
+
233
+ const memoryProfile = memoryProfiler.recordCheckComplete(checkId, memoryBeforeMB, 0, durationMs)
234
+
235
+ const checkResult: RecipeCheckResult = {
236
+ checkId,
237
+ checkSlug,
238
+ passed: false,
239
+ violationCount: 0,
240
+ errorCount: 1,
241
+ warningCount: 0,
242
+ ignoredCount: 0,
243
+ durationMs,
244
+ skipped: false,
245
+ error: errMsg,
246
+ timedOut,
247
+ }
248
+
249
+ updateSessionForError(session, checkResult)
250
+
251
+ const summary = createErrorSummary(checkId, checkSlug, durationMs, memoryProfile, timedOut, errMsg)
252
+ callbacks.onError?.(checkSlug, error instanceof Error ? error : new SystemError(errMsg, { code: 'SYSTEM.FITNESS.CHECK_ERROR' }))
253
+ callbacks.onCheckComplete?.(checkSlug, summary, checkIndex, totalChecks)
254
+
255
+ const shouldStop = recipe.execution.stopOnFirstFailure
256
+
257
+ return { checkResult, memoryProfile, shouldStop }
258
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * @fileoverview Parallel execution engine for fitness recipe checks
3
+ *
4
+ * Runs fitness checks concurrently with configurable parallelism,
5
+ * per-check timeouts, abort support, and retry logic.
6
+ */
7
+
8
+ import { TimeoutError , logger } from '@opensip-tools/core'
9
+
10
+ import { memoryProfiler } from '../framework/memory-profiler.js'
11
+
12
+
13
+ import { processSuccessResult, processErrorResult, type ProcessorContext } from './check-result-processor.js'
14
+ import { executeWithRetry } from './retry.js'
15
+ import { getEffectiveMaxParallel } from './types.js'
16
+
17
+ import type { FitnessRecipeServiceCallbacks, FitnessRecipeSession } from './service-types.js'
18
+ import type { FitnessRecipe } from './types.js'
19
+ import type { Check } from '../framework/check-types.js'
20
+ import type { CheckResult } from '../types/findings.js'
21
+
22
+ /** Logger module tag used by every event emitted from the parallel execution engine. */
23
+ const MODULE_TAG = 'fitness:execution'
24
+
25
+ // =============================================================================
26
+ // TYPES
27
+ // =============================================================================
28
+
29
+ /** Options for executing a set of fitness checks */
30
+ export interface ExecutionOptions {
31
+ checks: Check[]
32
+ cwd: string
33
+ recipe: FitnessRecipe
34
+ /** Per-check pre-resolved file paths from target overrides */
35
+ checkTargetFiles?: ReadonlyMap<string, readonly string[]>
36
+ /**
37
+ * Run-wide globalExcludes from project config. Forwarded into each
38
+ * check's RunOptions so scope-empty checks honor exclusions instead
39
+ * of scanning every file in the prewarmed cache.
40
+ */
41
+ globalExcludes?: readonly string[]
42
+ }
43
+
44
+ /** Service context providing session state, callbacks, and abort control */
45
+ export interface ExecutionServiceContext {
46
+ session: FitnessRecipeSession
47
+ callbacks: FitnessRecipeServiceCallbacks
48
+ abortController?: AbortController
49
+ includeViolations?: boolean
50
+ }
51
+
52
+ // =============================================================================
53
+ // PARALLEL EXECUTION
54
+ // =============================================================================
55
+
56
+ /** Execute fitness checks concurrently with configurable parallelism and per-check timeouts */
57
+ export async function executeParallel(ctx: ExecutionServiceContext, opts: ExecutionOptions): Promise<void> {
58
+ // in-memory: single-threaded Node.js access pattern
59
+ const { checks, cwd, recipe, checkTargetFiles, globalExcludes } = opts
60
+ const { session, callbacks, abortController } = ctx
61
+ const recipeTimeout = recipe.execution.timeout ?? 30_000
62
+ const maxParallel = getEffectiveMaxParallel(recipe)
63
+ const totalChecks = checks.length
64
+
65
+ const memoryBeforeMap = new Map<string, number>()
66
+ let nextCheckIndex = 0
67
+ let activeCount = 0
68
+ let shouldStopExecution = false
69
+
70
+ const processorCtx: ProcessorContext = { session, callbacks, recipe, includeViolations: ctx.includeViolations ?? false }
71
+
72
+ // @fitness-ignore-next-line concurrency-safety -- async arrow provides Promise<void> return type for consistency; single-threaded Node.js access pattern
73
+ /* eslint-disable @typescript-eslint/require-await -- callers `await` this; preserving async signature in case future processors become async */
74
+ const processCheckResult = async (
75
+ checkIndex: number,
76
+ checkId: string,
77
+ checkSlug: string,
78
+ checkTags: readonly string[],
79
+ result: CheckResult,
80
+ durationMs: number,
81
+ ): Promise<void> => {
82
+ // in-memory: single-threaded Node.js access pattern
83
+ const memoryBeforeMB = memoryBeforeMap.get(checkId) ?? 0
84
+ const output = processSuccessResult(processorCtx, {
85
+ checkId,
86
+ checkSlug,
87
+ tags: checkTags,
88
+ checkIndex,
89
+ totalChecks,
90
+ result,
91
+ durationMs,
92
+ memoryBeforeMB,
93
+ })
94
+ if (output.shouldStop) shouldStopExecution = true
95
+ }
96
+ /* eslint-enable @typescript-eslint/require-await */
97
+
98
+ const processCheckError = (
99
+ checkIndex: number,
100
+ checkId: string,
101
+ checkSlug: string,
102
+ error: unknown,
103
+ durationMs: number,
104
+ timedOut = false,
105
+ timeoutMs?: number,
106
+ ): void => {
107
+ const memoryBeforeMB = memoryBeforeMap.get(checkId) ?? 0
108
+ const errorInput = {
109
+ checkId,
110
+ checkSlug,
111
+ checkIndex,
112
+ totalChecks,
113
+ error,
114
+ durationMs,
115
+ memoryBeforeMB,
116
+ timedOut,
117
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
118
+ }
119
+ const output = processErrorResult(processorCtx, errorInput)
120
+ if (output.shouldStop) shouldStopExecution = true
121
+ }
122
+
123
+ await new Promise<void>((resolve) => {
124
+ const advanceWindow = () => {
125
+ activeCount--
126
+
127
+ if (!shouldStopExecution && nextCheckIndex < checks.length && !abortController?.signal.aborted) {
128
+ const nextCheck = checks[nextCheckIndex]
129
+ if (nextCheck) {
130
+ nextCheckIndex++
131
+ startCheck(nextCheck, nextCheckIndex)
132
+ }
133
+ }
134
+
135
+ if (activeCount === 0 && (nextCheckIndex >= checks.length || shouldStopExecution)) {
136
+ resolve()
137
+ }
138
+ }
139
+
140
+ // @fitness-ignore-next-line file-length-limits -- Parallel check launcher: orchestrates timeout, retry, result processing, and window advancement in a single cohesive unit
141
+ const startCheck = (check: Check, displayIndex: number) => {
142
+ // in-memory: single-threaded Node.js access pattern
143
+ const checkId = check.config.id
144
+ activeCount++
145
+
146
+ const checkTimeout = check.config.timeout ?? recipeTimeout
147
+ memoryBeforeMap.set(checkId, memoryProfiler.recordCheckStart())
148
+ callbacks.onCheckStart?.(check.config.slug, displayIndex, totalChecks)
149
+ logger.info({ evt: 'fitness.check.start', module: MODULE_TAG, checkSlug: check.config.slug, index: displayIndex, total: totalChecks, timeoutMs: checkTimeout })
150
+
151
+ const startTime = Date.now()
152
+ const checkAbortController = new AbortController()
153
+ const timeoutId = setTimeout(() => checkAbortController.abort(), checkTimeout)
154
+
155
+ const targetFiles = checkTargetFiles?.get(check.config.slug)
156
+ void executeWithRetry(() => check.run(cwd, {
157
+ signal: checkAbortController.signal,
158
+ ...(targetFiles ? { targetFiles } : {}),
159
+ ...(globalExcludes ? { globalExcludes } : {}),
160
+ }), {
161
+ enabled: recipe.execution.retryOnFailure ?? false,
162
+ maxRetries: recipe.execution.maxRetries ?? 2,
163
+ checkId,
164
+ checkSlug: check.config.slug,
165
+ })
166
+ .then(async (retryResult) => {
167
+ const durationMs = Date.now() - startTime
168
+
169
+ if (retryResult.result === undefined) {
170
+ clearTimeout(timeoutId)
171
+ const isTimeout = checkAbortController.signal.aborted
172
+ logger.info({ evt: 'fitness.check.error', module: MODULE_TAG, checkSlug: check.config.slug, durationMs, timedOut: isTimeout, error: retryResult.lastError instanceof Error ? retryResult.lastError.message : String(retryResult.lastError) })
173
+ void processCheckError(
174
+ displayIndex,
175
+ checkId,
176
+ check.config.slug,
177
+ retryResult.lastError,
178
+ durationMs,
179
+ isTimeout,
180
+ isTimeout ? checkTimeout : undefined,
181
+ )
182
+ } else if (checkAbortController.signal.aborted) {
183
+ clearTimeout(timeoutId)
184
+ logger.info({ evt: 'fitness.check.timeout', module: MODULE_TAG, checkSlug: check.config.slug, durationMs, timeoutMs: checkTimeout })
185
+ void processCheckError(
186
+ displayIndex,
187
+ checkId,
188
+ check.config.slug,
189
+ new TimeoutError(`Check ${checkId} timed out after ${checkTimeout}ms`, checkTimeout),
190
+ durationMs,
191
+ true,
192
+ checkTimeout,
193
+ )
194
+ } else {
195
+ clearTimeout(timeoutId)
196
+ logger.info({ evt: 'fitness.check.done', module: MODULE_TAG, checkSlug: check.config.slug, durationMs, signals: retryResult.result.signals.length })
197
+ await processCheckResult(displayIndex, checkId, check.config.slug, check.config.tags ?? [], retryResult.result, durationMs)
198
+ }
199
+ })
200
+ .catch((error: unknown) => {
201
+ clearTimeout(timeoutId)
202
+ const durationMs = Date.now() - startTime
203
+ logger.info({ evt: 'fitness.check.error', module: MODULE_TAG, checkSlug: check.config.slug, durationMs, error: error instanceof Error ? error.message : String(error) })
204
+ void processCheckError(displayIndex, checkId, check.config.slug, error, durationMs)
205
+ })
206
+ .finally(() => advanceWindow())
207
+ }
208
+
209
+ const initialBatchSize = Math.min(maxParallel, checks.length)
210
+ for (let i = 0; i < initialBatchSize; i++) {
211
+ const check = checks[i]
212
+ if (check) {
213
+ nextCheckIndex = i + 1
214
+ startCheck(check, i + 1)
215
+ }
216
+ }
217
+
218
+ if (checks.length === 0) resolve()
219
+ })
220
+ }
@@ -0,0 +1,192 @@
1
+ // @fitness-ignore-file batch-operation-limits -- iterates bounded collections (config entries, registry items, or small analysis results)
2
+ // @fitness-ignore-file no-console-log -- User recipe warnings/summary output before CLI framework is initialized
3
+ // @fitness-ignore-file logging-standards -- User recipe warnings/summary output before structured logger is available
4
+ /**
5
+ * @fileoverview Fitness recipe registry
6
+ *
7
+ * Manages registration and lookup of fitness recipes (built-in and user-defined).
8
+ */
9
+
10
+ import { ValidationError } from '@opensip-tools/core'
11
+
12
+ import { builtInRecipes, isBuiltInRecipe } from './built-in-recipes.js'
13
+
14
+ import type { FitnessRecipe } from './types.js'
15
+
16
+ /** Stub for user recipe loading (not ported to opensip-tools) */
17
+ interface UserFitnessRecipesResult {
18
+ recipes: FitnessRecipe[]
19
+ warnings: string[]
20
+ loadedFrom?: string
21
+ }
22
+
23
+ /** Options for constructing a FitnessRecipeRegistry */
24
+ export interface FitnessRecipeRegistryOptions {
25
+ readonly basePath?: string
26
+ readonly loadUserRecipes?: boolean
27
+ readonly logWarnings?: boolean
28
+ readonly logSummary?: boolean
29
+ }
30
+
31
+ /** Display-friendly info about a registered recipe */
32
+ export interface RecipeDisplayInfo {
33
+ readonly name: string
34
+ readonly displayName: string
35
+ readonly description: string
36
+ readonly tags: readonly string[]
37
+ readonly isBuiltIn: boolean
38
+ readonly isUserDefined: boolean
39
+ readonly overridesBuiltIn: boolean
40
+ }
41
+
42
+ /** Registry for fitness recipes, loading built-in and user-defined recipes */
43
+ export class FitnessRecipeRegistry {
44
+ private readonly byId = new Map<string, FitnessRecipe>()
45
+ private readonly byName = new Map<string, FitnessRecipe>()
46
+ private _userRecipesLoadResult: UserFitnessRecipesResult | undefined
47
+ private readonly _overriddenBuiltIns = new Set<string>()
48
+
49
+ constructor(options: FitnessRecipeRegistryOptions = {}) {
50
+ const {
51
+ basePath,
52
+ loadUserRecipes: shouldLoadUserRecipes = true,
53
+ logWarnings = true,
54
+ logSummary = false,
55
+ } = options
56
+
57
+ this.registerBuiltInRecipes()
58
+
59
+ if (shouldLoadUserRecipes) {
60
+ this.loadAndRegisterUserRecipes(basePath, logWarnings, logSummary)
61
+ }
62
+ }
63
+
64
+ private registerBuiltInRecipes(): void {
65
+ for (const recipe of builtInRecipes) {
66
+ this.byId.set(recipe.id, recipe)
67
+ this.byName.set(recipe.name, recipe)
68
+ }
69
+ }
70
+
71
+ private loadAndRegisterUserRecipes(
72
+ _basePath: string | undefined,
73
+ _logWarnings: boolean,
74
+ _logSummary: boolean,
75
+ ): void {
76
+ // User recipe loading not ported to opensip-tools — stub
77
+ this._userRecipesLoadResult = { recipes: [], warnings: [] }
78
+ }
79
+
80
+ /** Return the result of loading user-defined recipes, if attempted */
81
+ getUserRecipesLoadResult(): UserFitnessRecipesResult | undefined {
82
+ return this._userRecipesLoadResult
83
+ }
84
+
85
+ /** Check whether a built-in recipe has been overridden by a user recipe */
86
+ isOverridden(name: string): boolean {
87
+ return this._overriddenBuiltIns.has(name)
88
+ }
89
+
90
+ /** Return the names of all built-in recipes overridden by user recipes */
91
+ getOverriddenBuiltIns(): readonly string[] {
92
+ return [...this._overriddenBuiltIns]
93
+ }
94
+
95
+ /** Look up a recipe by name or ID */
96
+ loadRecipe(nameOrId: string): FitnessRecipe | undefined {
97
+ return this.byName.get(nameOrId) ?? this.byId.get(nameOrId)
98
+ }
99
+
100
+ /** Retrieve a recipe by its name */
101
+ getByName(name: string): FitnessRecipe | undefined {
102
+ return this.byName.get(name)
103
+ }
104
+
105
+ /** Retrieve a recipe by its ID */
106
+ getById(id: string): FitnessRecipe | undefined {
107
+ return this.byId.get(id)
108
+ }
109
+
110
+ /** Check whether a recipe exists by name or ID */
111
+ has(nameOrId: string): boolean {
112
+ return this.byName.has(nameOrId) || this.byId.has(nameOrId)
113
+ }
114
+
115
+ /** Return all registered recipes */
116
+ getAllRecipes(): readonly FitnessRecipe[] {
117
+ return [...this.byId.values()]
118
+ }
119
+
120
+ /** Return all registered recipe names */
121
+ getNames(): readonly string[] {
122
+ return [...this.byName.keys()]
123
+ }
124
+
125
+ /** Return all recipes matching a given tag */
126
+ getByTag(tag: string): readonly FitnessRecipe[] {
127
+ return [...this.byId.values()].filter((r) => r.tags?.includes(tag))
128
+ }
129
+
130
+ /** Number of registered recipes */
131
+ get size(): number {
132
+ return this.byId.size
133
+ }
134
+
135
+ /** Register a recipe. Throws if already registered unless allowOverwrite is true. */
136
+ register(recipe: FitnessRecipe, options?: { allowOverwrite?: boolean }): void {
137
+ const allowOverwrite = options?.allowOverwrite ?? false
138
+ if (!allowOverwrite && (this.byId.has(recipe.id) || this.byName.has(recipe.name))) {
139
+ // @fitness-ignore-next-line result-pattern-consistency -- internal registration guard, throw is appropriate
140
+ throw new ValidationError(`Recipe '${recipe.name}' (${recipe.id}) already registered`, { code: 'VALIDATION.FITNESS.DUPLICATE_RECIPE' })
141
+ }
142
+ this.byId.set(recipe.id, recipe)
143
+ this.byName.set(recipe.name, recipe)
144
+ }
145
+
146
+ /** Register multiple recipes at once */
147
+ registerAll(recipes: readonly FitnessRecipe[], options?: { allowOverwrite?: boolean }): void {
148
+ for (const recipe of recipes) {
149
+ this.register(recipe, options)
150
+ }
151
+ }
152
+
153
+ /** Remove a recipe by ID. Returns true if the recipe was found and removed. */
154
+ remove(id: string): boolean {
155
+ const recipe = this.byId.get(id)
156
+ if (!recipe) return false
157
+ this.byId.delete(id)
158
+ this.byName.delete(recipe.name)
159
+ return true
160
+ }
161
+
162
+ /** Remove all registered recipes */
163
+ clear(): void {
164
+ this.byId.clear()
165
+ this.byName.clear()
166
+ }
167
+
168
+ /** Clear all recipes and re-register built-in recipes */
169
+ reset(): void {
170
+ this.clear()
171
+ this.registerBuiltInRecipes()
172
+ }
173
+
174
+ /** Return display-friendly info for all registered recipes */
175
+ listForDisplay(): readonly RecipeDisplayInfo[] {
176
+ return [...this.byId.values()].map((recipe) => {
177
+ const isUserRecipe = recipe.id.startsWith('URCP_')
178
+ return {
179
+ name: recipe.name,
180
+ displayName: recipe.displayName,
181
+ description: recipe.description,
182
+ tags: recipe.tags ?? [],
183
+ isBuiltIn: !isUserRecipe && isBuiltInRecipe(recipe.name),
184
+ isUserDefined: isUserRecipe,
185
+ overridesBuiltIn: this._overriddenBuiltIns.has(recipe.name),
186
+ }
187
+ })
188
+ }
189
+ }
190
+
191
+ /** Shared singleton recipe registry with built-in recipes pre-loaded */
192
+ export const defaultRecipeRegistry = new FitnessRecipeRegistry({ logWarnings: false })