@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,110 @@
1
+ /**
2
+ * @fileoverview Directive Inventory - shared parsing logic for
3
+ * fitness-ignore directives.
4
+ */
5
+
6
+ // =============================================================================
7
+ // Types
8
+ // =============================================================================
9
+
10
+ /** A single fitness-ignore directive found in a source file. */
11
+ export interface DirectiveEntry {
12
+ filePath: string
13
+ lineNumber: number
14
+ type: 'file' | 'next-line'
15
+ checkId: string
16
+ group: string
17
+ reason: string | null
18
+ weakReason: boolean
19
+ }
20
+
21
+ // =============================================================================
22
+ // Shared Constants
23
+ // =============================================================================
24
+
25
+ /** Patterns that indicate a weak or generic ignore reason. */
26
+ const WEAK_REASON_PATTERNS = Object.freeze<readonly RegExp[]>([
27
+ /^ignore$/i,
28
+ /^skip$/i,
29
+ /^todo$/i,
30
+ /^fixme$/i,
31
+ /^temporary$/i,
32
+ /^temp$/i,
33
+ /^wip$/i,
34
+ /^disable$/i,
35
+ /^suppress$/i,
36
+ /^\s*$/,
37
+ ])
38
+
39
+ // =============================================================================
40
+ // Shared Parsing
41
+ // =============================================================================
42
+
43
+ /**
44
+ * Parse a file-level or next-line directive from a comment line.
45
+ * Accepts both `//` and `/*`-style comments for consistency with the
46
+ * suppression parser in directive-parsing.ts — otherwise block-comment
47
+ * directives suppress findings but vanish from inventory counts.
48
+ */
49
+ export function parseDirectiveLine(line: string): {
50
+ type: 'file' | 'next-line'
51
+ checkId: string
52
+ reason: string | null
53
+ } | null {
54
+ const trimmed = line.trimStart()
55
+ if (!trimmed.startsWith('// ') && !trimmed.startsWith('/* ')) return null
56
+
57
+ const afterComment = trimmed.slice(3).trimStart()
58
+
59
+ if (afterComment.startsWith('@fitness-ignore-file ')) {
60
+ const rest = afterComment.slice('@fitness-ignore-file '.length)
61
+ return parseDirectiveRest(rest, 'file')
62
+ }
63
+
64
+ if (afterComment.startsWith('@fitness-ignore-next-line ')) {
65
+ const rest = afterComment.slice('@fitness-ignore-next-line '.length)
66
+ return parseDirectiveRest(rest, 'next-line')
67
+ }
68
+
69
+ return null
70
+ }
71
+
72
+ function parseDirectiveRest(
73
+ rest: string,
74
+ type: 'file' | 'next-line',
75
+ ): { type: 'file' | 'next-line'; checkId: string; reason: string | null } | null {
76
+ // Strip trailing `*/` from block-comment directives (e.g. `foo */` → `foo`).
77
+ // eslint-disable-next-line sonarjs/slow-regex -- anchored at end-of-string, bounded \s* runs; no ReDoS exposure
78
+ const normalized = rest.replace(/\s*\*\/\s*$/, '').trimEnd()
79
+
80
+ const separatorIndex = normalized.indexOf(' -- ')
81
+
82
+ if (separatorIndex === -1) {
83
+ const checkId = normalized.trim()
84
+ if (!checkId || checkId.includes(' ')) return null
85
+ return { type, checkId, reason: null }
86
+ }
87
+
88
+ const checkId = normalized.slice(0, separatorIndex).trim()
89
+ const reason = normalized.slice(separatorIndex + 4).trim()
90
+
91
+ if (!checkId || checkId.includes(' ')) return null
92
+ return { type, checkId, reason: reason || null }
93
+ }
94
+
95
+ /**
96
+ * Check if a reason is weak/generic. Missing reason (null) is considered weak.
97
+ */
98
+ export function isWeakReason(reason: string | null): boolean {
99
+ if (reason === null) return true
100
+ return WEAK_REASON_PATTERNS.some((pattern) => pattern.test(reason.trim()))
101
+ }
102
+
103
+ /**
104
+ * Extract the group prefix from a check ID (the directory name).
105
+ */
106
+ export function extractGroup(checkId: string): string {
107
+ const slashIndex = checkId.indexOf('/')
108
+ return slashIndex > 0 ? checkId.slice(0, slashIndex) : 'other'
109
+ }
110
+
@@ -0,0 +1,152 @@
1
+ // @fitness-ignore-file semgrep-justifications -- References nosemgrep patterns for directive parsing
2
+ /**
3
+ * @fileoverview Ignore directive parsing utilities for fitness checks
4
+ *
5
+ * Provides utilities for parsing suppression directives:
6
+ * - @fitness-ignore-file, @fitness-ignore-next-line
7
+ * - eslint-disable-next-line, eslint-disable-line
8
+ * - @ts-expect-error, @ts-ignore
9
+ * - nosemgrep
10
+ */
11
+
12
+ // =============================================================================
13
+ // CONSTANTS
14
+ // =============================================================================
15
+
16
+ const KNOWN_DIRECTIVE_KEYWORDS = [
17
+ 'eslint-disable-next-line',
18
+ 'eslint-disable-line',
19
+ '@ts-expect-error',
20
+ '@ts-ignore',
21
+ '@ts-nocheck',
22
+ 'prettier-ignore',
23
+ 'biome-ignore',
24
+ '@fitness-ignore-next-line',
25
+ '@fitness-ignore-file',
26
+ ] as const
27
+
28
+ const MAX_DIRECTIVE_SKIP = 3
29
+
30
+ // =============================================================================
31
+ // INTERNAL HELPERS
32
+ // =============================================================================
33
+
34
+ function isKnownDirectiveLine(line: string): boolean {
35
+ const trimmed = line.trimStart()
36
+
37
+ if (!trimmed.startsWith('//') && !trimmed.startsWith('/*')) {
38
+ return false
39
+ }
40
+
41
+ const commentContent = trimmed.slice(2).trimStart()
42
+
43
+ return KNOWN_DIRECTIVE_KEYWORDS.some((keyword) => {
44
+ if (!commentContent.startsWith(keyword)) return false
45
+ const nextChar = commentContent[keyword.length]
46
+ return nextChar === undefined || nextChar === ' ' || nextChar === '\t' || nextChar === ':'
47
+ })
48
+ }
49
+
50
+ function isCheckIdChar(char: string): boolean {
51
+ const code = char.codePointAt(0) ?? 0
52
+ const isLowerCase = code >= 97 && code <= 122
53
+ const isUpperCase = code >= 65 && code <= 90
54
+ const isDigit = code >= 48 && code <= 57
55
+ const isSpecialChar = code === 95 || code === 45 || code === 47
56
+ return isLowerCase || isUpperCase || isDigit || isSpecialChar
57
+ }
58
+
59
+ function extractCheckIdFromDirective(line: string, directiveKeyword: string): string | null {
60
+ // Both `//` and `/*` are 2-char prefixes; sliceLen is fixed.
61
+ const sliceLen = 2
62
+ let commentIndex = line.indexOf('//')
63
+ if (commentIndex === -1) {
64
+ commentIndex = line.indexOf('/*')
65
+ if (commentIndex === -1) return null
66
+ }
67
+
68
+ const afterComment = line.slice(commentIndex + sliceLen).trimStart()
69
+ if (!afterComment.startsWith(directiveKeyword)) return null
70
+
71
+ const afterDirective = afterComment.slice(directiveKeyword.length)
72
+ if (
73
+ afterDirective.length === 0 ||
74
+ (!afterDirective.startsWith(' ') && !afterDirective.startsWith('\t'))
75
+ ) {
76
+ return null
77
+ }
78
+
79
+ const checkIdStart = afterDirective.trimStart()
80
+ let checkId = ''
81
+ for (const char of checkIdStart) {
82
+ if (isCheckIdChar(char)) {
83
+ checkId += char
84
+ } else {
85
+ break
86
+ }
87
+ }
88
+
89
+ return checkId.length > 0 ? checkId : null
90
+ }
91
+
92
+ // =============================================================================
93
+ // PUBLIC API
94
+ // =============================================================================
95
+
96
+ /**
97
+ * Parse file-level ignore directive from file content.
98
+ * Returns true if the file should be entirely ignored for that check.
99
+ */
100
+ export function parseFileIgnoreDirective(
101
+ content: string,
102
+ checkId: string | readonly string[],
103
+ ): boolean {
104
+ const lines = content.split('\n').slice(0, 50)
105
+ const checkIds = Array.isArray(checkId) ? checkId : [checkId]
106
+
107
+ for (const line of lines) {
108
+ const extractedId = extractCheckIdFromDirective(line, '@fitness-ignore-file')
109
+ if (extractedId !== null && checkIds.includes(extractedId)) {
110
+ return true
111
+ }
112
+ }
113
+
114
+ return false
115
+ }
116
+
117
+ /**
118
+ * Parse next-line ignore directives from file content.
119
+ * Returns a set of line numbers that should be ignored.
120
+ */
121
+ export function parseIgnoreDirectives(
122
+ content: string,
123
+ checkId: string | readonly string[],
124
+ ): Set<number> {
125
+ const ignoredLines = new Set<number>()
126
+ const lines = content.split('\n')
127
+ const checkIds = Array.isArray(checkId) ? checkId : [checkId]
128
+
129
+ for (let i = 0; i < lines.length; i++) {
130
+ const extractedId = extractCheckIdFromDirective(lines[i] ?? '', '@fitness-ignore-next-line')
131
+ if (extractedId !== null && checkIds.includes(extractedId)) {
132
+ let targetLine = i + 1
133
+ let skipped = 0
134
+
135
+ while (
136
+ targetLine < lines.length &&
137
+ skipped < MAX_DIRECTIVE_SKIP &&
138
+ isKnownDirectiveLine(lines[targetLine] ?? '')
139
+ ) {
140
+ targetLine++
141
+ skipped++
142
+ }
143
+
144
+ ignoredLines.add(targetLine + 1)
145
+ }
146
+ }
147
+
148
+ return ignoredLines
149
+ }
150
+
151
+
152
+
@@ -0,0 +1,247 @@
1
+ // @fitness-ignore-file concurrency-safety -- single-threaded execution context
2
+ // @fitness-ignore-file error-handling-suite -- catch blocks delegate errors through established patterns
3
+ /**
4
+ * @fileoverview Execution context creation for fitness checks
5
+ *
6
+ * Provides the runtime context available to check execute functions,
7
+ * including file access, pattern matching, and abort support.
8
+ */
9
+
10
+ import * as fs from 'node:fs/promises'
11
+ import { relative } from 'node:path'
12
+
13
+
14
+ import { SystemError } from '@opensip-tools/core'
15
+ import { Minimatch } from 'minimatch'
16
+
17
+ import { DEFAULT_EXCLUSION_PATTERNS } from './constants.js'
18
+ import { fileCache } from './file-cache.js'
19
+ import { PathMatcher } from './path-matcher.js'
20
+ import { extractSnippet } from './result-builder.js'
21
+
22
+ import type { ResolvedScope } from './check-config.js'
23
+
24
+ /**
25
+ * Check identifier (UUID format).
26
+ */
27
+ // eslint-disable-next-line sonarjs/redundant-type-aliases -- semantic alias for UUID-shaped check identifier
28
+ type CheckId = string
29
+
30
+ /**
31
+ * Error thrown when a check is aborted via AbortSignal.
32
+ */
33
+ export class CheckAbortedError extends SystemError {
34
+ readonly name = 'CheckAbortedError' as const
35
+ readonly checkId: string
36
+
37
+ constructor(checkId: string, message?: string) {
38
+ super(message ?? `Check ${checkId} was aborted`, { code: 'SYSTEM.FITNESS.CHECK_ABORTED' })
39
+ this.checkId = checkId
40
+ Object.setPrototypeOf(this, CheckAbortedError.prototype)
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Result of extracting a code snippet.
46
+ */
47
+ interface ExtractSnippetResult {
48
+ readonly snippet: string
49
+ readonly contextLines: number
50
+ }
51
+
52
+ /**
53
+ * Execution context provided to check execute function.
54
+ */
55
+ export interface ExecutionContext {
56
+ /** Repository root directory */
57
+ readonly cwd: string
58
+ /** Read a file's contents */
59
+ readonly readFile: (path: string) => Promise<string>
60
+ /** Check if file exists */
61
+ readonly fileExists: (path: string) => Promise<boolean>
62
+ /** The check's stable ID (UUID) */
63
+ readonly checkId: CheckId
64
+ /** The check's human-readable slug (kebab-case) */
65
+ readonly checkSlug: string
66
+ /** Match files using the check's scope or custom patterns */
67
+ readonly matchFiles: (
68
+ patterns?: readonly string[],
69
+ options?: { ignore?: readonly string[] },
70
+ ) => Promise<readonly string[]>
71
+ /** Get a PathMatcher for the check's scope */
72
+ readonly getMatcher: () => PathMatcher
73
+ /** Verbose logging enabled */
74
+ readonly verbose: boolean
75
+ /** Log a message (only in verbose mode) */
76
+ readonly log: (message: string) => void
77
+ /** Extract a code snippet with context lines */
78
+ readonly extractSnippet: (
79
+ content: string,
80
+ line: number,
81
+ contextLines?: number,
82
+ ) => ExtractSnippetResult
83
+ /** AbortSignal for cancellation support */
84
+ readonly signal?: AbortSignal
85
+ /** Throws if the check has been aborted */
86
+ readonly checkAborted: () => void
87
+ }
88
+
89
+ /**
90
+ * Options for running a check.
91
+ */
92
+ export interface RunOptions {
93
+ readonly verbose?: boolean
94
+ readonly scopeOverride?: string | ResolvedScope
95
+ readonly additionalExcludes?: readonly string[]
96
+ readonly signal?: AbortSignal
97
+ /** Pre-resolved file paths from per-check target overrides. When set, matchFiles() returns these instead of cache paths. */
98
+ readonly targetFiles?: readonly string[]
99
+ /**
100
+ * Run-wide file exclusion patterns from the project config's
101
+ * `globalExcludes`. Applied to the fileCache fallback path used by
102
+ * scope-empty checks (e.g. `file-length-limit`). Without this filter,
103
+ * a check that declares `scope: { languages: [], concerns: [] }`
104
+ * would scan every prewarmed file regardless of whether the project
105
+ * told us to exclude it — surfacing findings inside `docs/`,
106
+ * `tests/fixtures/`, etc., contrary to user intent.
107
+ */
108
+ readonly globalExcludes?: readonly string[]
109
+ }
110
+
111
+ /**
112
+ * Configuration needed to create execution context.
113
+ */
114
+ export interface ExecutionContextConfig {
115
+ readonly id: CheckId
116
+ readonly slug: string
117
+ readonly itemType: string
118
+ readonly unit?: string | undefined
119
+ }
120
+
121
+ /**
122
+ * Creates the matchFiles function for the execution context.
123
+ *
124
+ * `globalExcludes` come from the project config's top-level
125
+ * `globalExcludes` array. They are applied ONLY to the fileCache
126
+ * fallback path — the path taken by scope-empty checks. Custom
127
+ * `patterns` arguments are honored as-is (the caller knows what they
128
+ * want), and `targetFiles` from per-check overrides are pre-filtered
129
+ * by `preResolveAllTargets`. Pre-compiled to Minimatch matchers so
130
+ * we don't pay regex compilation per filter call.
131
+ */
132
+ function createMatchFilesFunction(
133
+ cwd: string,
134
+ matcher: PathMatcher,
135
+ targetFiles?: readonly string[],
136
+ globalExcludes?: readonly string[],
137
+ ): (
138
+ patterns?: readonly string[],
139
+ options?: { ignore?: readonly string[] },
140
+ ) => Promise<readonly string[]> {
141
+ const compiledGlobalExcludes = globalExcludes && globalExcludes.length > 0
142
+ ? globalExcludes.map((pattern) => new Minimatch(pattern, { dot: true }))
143
+ : undefined
144
+
145
+ const applyGlobalExcludes = (files: readonly string[]): readonly string[] => {
146
+ if (!compiledGlobalExcludes) return files
147
+ return files.filter((filePath) => {
148
+ const rel = relative(cwd, filePath)
149
+ return !compiledGlobalExcludes.some((m) => m.match(rel))
150
+ })
151
+ }
152
+
153
+ return async (
154
+ patterns?: readonly string[],
155
+ options?: { ignore?: readonly string[] },
156
+ ): Promise<readonly string[]> => {
157
+ if (patterns && patterns.length > 0) {
158
+ const customMatcher = PathMatcher.create({
159
+ cwd,
160
+ include: [...patterns],
161
+ exclude: [...(options?.ignore ?? []), ...DEFAULT_EXCLUSION_PATTERNS],
162
+ })
163
+ return customMatcher.files()
164
+ }
165
+
166
+ // Per-check target files take priority over cache.
167
+ // These are already filtered by globalExcludes during target
168
+ // pre-resolution (scope-resolver.ts), so don't re-filter.
169
+ if (targetFiles) {
170
+ return targetFiles
171
+ }
172
+
173
+ // When the matcher has no include patterns (checks without targets),
174
+ // fall back to the prewarmed file cache paths. The cache itself
175
+ // honors no exclusion config — that's the layer where globalExcludes
176
+ // must be applied, otherwise scope-empty checks scan every prewarmed
177
+ // file regardless of project intent.
178
+ if (matcher.includePatterns.length === 0) {
179
+ return applyGlobalExcludes(fileCache.paths())
180
+ }
181
+
182
+ return matcher.files()
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Creates the execution context for a check.
188
+ */
189
+ export function createExecutionContext(
190
+ config: ExecutionContextConfig,
191
+ cwd: string,
192
+ matcher: PathMatcher,
193
+ options?: RunOptions,
194
+ ): ExecutionContext {
195
+ return {
196
+ cwd,
197
+ checkId: config.id,
198
+ checkSlug: config.slug,
199
+ verbose: options?.verbose ?? false,
200
+
201
+ // @fitness-ignore-next-line unbounded-memory -- size validation via fs.stat() is on the next line; false positive on method name
202
+ /** @throws {SystemError} When the file exceeds 10MB */
203
+ async readFile(filePath: string): Promise<string> {
204
+ const fileStats = await fs.stat(filePath)
205
+ if (fileStats.size > 10_000_000) {
206
+ throw new SystemError(`File too large (${fileStats.size} bytes, max 10MB): ${filePath}`, { code: 'SYSTEM.FITNESS.FILE_TOO_LARGE' })
207
+ }
208
+ return fileCache.get(filePath)
209
+ },
210
+
211
+ fileExists(filePath: string): Promise<boolean> {
212
+ return fileCache.exists(filePath)
213
+ },
214
+
215
+ matchFiles: createMatchFilesFunction(cwd, matcher, options?.targetFiles, options?.globalExcludes),
216
+
217
+ getMatcher(): PathMatcher {
218
+ return matcher
219
+ },
220
+
221
+ log(message: string): void {
222
+ if (options?.verbose) {
223
+ // @fitness-ignore-next-line no-console-log -- Verbose check-level debug output bypasses structured logger for immediate CLI feedback
224
+ // @fitness-ignore-next-line logging-standards -- Verbose check-level debug output bypasses structured logger for immediate CLI feedback
225
+
226
+ console.log(`[${config.slug}] ${message}`)
227
+ }
228
+ },
229
+
230
+ extractSnippet(
231
+ content: string,
232
+ line: number,
233
+ contextLines = 2,
234
+ ): ExtractSnippetResult {
235
+ return extractSnippet(content, line, contextLines)
236
+ },
237
+
238
+ ...(options?.signal ? { signal: options.signal } : {}),
239
+
240
+ /** @throws {CheckAbortedError} When the check has been aborted */
241
+ checkAborted(): void {
242
+ if (options?.signal?.aborted) {
243
+ throw new CheckAbortedError(config.slug)
244
+ }
245
+ },
246
+ }
247
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @fileoverview FileAccessor implementation for lazy file loading
3
+ *
4
+ * Provides lazy-loading file access with LRU caching for
5
+ * analyzeAll mode checks that need to correlate across files.
6
+ */
7
+
8
+ import * as fs from 'node:fs/promises'
9
+
10
+ import { ValidationError , applyContentFilter } from '@opensip-tools/core'
11
+
12
+
13
+ import { fileCache } from './file-cache.js'
14
+
15
+ import type { FileAccessor } from './check-config.js'
16
+
17
+ // =============================================================================
18
+ // LRU CACHE
19
+ // =============================================================================
20
+
21
+ class LRUCache<K, V> {
22
+ private readonly cache: Map<K, V>
23
+ private readonly capacity: number
24
+
25
+ constructor(capacity: number) {
26
+ this.cache = new Map()
27
+ this.capacity = capacity
28
+ }
29
+
30
+ get(key: K): V | undefined {
31
+ // in-memory: single-threaded Node.js access pattern
32
+ const value = this.cache.get(key)
33
+ if (value !== undefined) {
34
+ this.cache.delete(key)
35
+ this.cache.set(key, value)
36
+ }
37
+ return value
38
+ }
39
+
40
+ set(key: K, value: V): void {
41
+ if (this.cache.has(key)) {
42
+ this.cache.delete(key)
43
+ } else if (this.cache.size >= this.capacity) {
44
+ const firstKey = this.cache.keys().next().value
45
+ if (firstKey !== undefined) {
46
+ this.cache.delete(firstKey)
47
+ }
48
+ }
49
+ this.cache.set(key, value)
50
+ }
51
+
52
+ get size(): number {
53
+ return this.cache.size
54
+ }
55
+
56
+ clear(): void {
57
+ this.cache.clear()
58
+ }
59
+ }
60
+
61
+ // =============================================================================
62
+ // FILE ACCESSOR IMPLEMENTATION
63
+ // =============================================================================
64
+
65
+ /** Options for creating a FileAccessor instance. */
66
+ export interface FileAccessorOptions {
67
+ readonly cacheCapacity?: number
68
+ readonly signal?: AbortSignal
69
+ /**
70
+ * Content filtering applied before returning file content. See
71
+ * BaseCheckConfig.contentFilter for the canonical doc.
72
+ */
73
+ readonly contentFilter?: 'raw' | 'strip-strings' | 'strip-strings-and-comments'
74
+ }
75
+
76
+ const DEFAULT_CACHE_CAPACITY = 100
77
+
78
+ /** FileAccessor implementation with LRU caching and abort signal support. */
79
+ class FileAccessorImpl implements FileAccessor {
80
+ readonly paths: readonly string[]
81
+ private readonly cache: LRUCache<string, string>
82
+ private readonly pathSet: Set<string>
83
+ private readonly signal?: AbortSignal
84
+ private readonly contentFilterMode?: FileAccessorOptions['contentFilter']
85
+
86
+ constructor(filePaths: readonly string[], options: FileAccessorOptions = {}) {
87
+ this.paths = filePaths
88
+ this.pathSet = new Set(filePaths)
89
+ this.cache = new LRUCache(options.cacheCapacity ?? DEFAULT_CACHE_CAPACITY)
90
+ this.signal = options.signal
91
+ this.contentFilterMode = options.contentFilter
92
+ }
93
+
94
+ async read(filePath: string): Promise<string> {
95
+ // in-memory: single-threaded Node.js access pattern
96
+ // @fitness-ignore-next-line detached-promises -- throwIfAborted() is synchronous, optional chaining is not a detached promise
97
+ this.signal?.throwIfAborted()
98
+
99
+ if (!this.pathSet.has(filePath)) {
100
+ // @fitness-ignore-next-line result-pattern-consistency -- internal method, exceptions propagate to public Result boundary
101
+ throw new ValidationError(
102
+ `File path not in matched set: ${filePath}. ` +
103
+ `Only paths from the 'paths' property can be read.`,
104
+ { code: 'VALIDATION.FITNESS.PATH_NOT_IN_SET' },
105
+ )
106
+ }
107
+
108
+ const cached = this.cache.get(filePath)
109
+ if (cached !== undefined) {
110
+ return cached
111
+ }
112
+
113
+ // Try global file cache first (populated by prewarm or previous checks)
114
+ let content = fileCache.getCached(filePath)
115
+ if (content === undefined) {
116
+ const fileStats = await fs.stat(filePath)
117
+ if (fileStats.size > 10_000_000) {
118
+ // @fitness-ignore-next-line result-pattern-consistency -- infrastructure boundary guard, not domain logic
119
+ throw new ValidationError(
120
+ `File too large (${fileStats.size} bytes, max 10MB): ${filePath}`,
121
+ { code: 'VALIDATION.FITNESS.FILE_TOO_LARGE' },
122
+ )
123
+ }
124
+ content = await fs.readFile(filePath, 'utf8')
125
+ }
126
+ // Dispatch through the LanguageAdapter for the file's extension.
127
+ // See languages/content-filter-dispatch.ts.
128
+ content = applyContentFilter(filePath, content, this.contentFilterMode ?? 'none')
129
+ this.cache.set(filePath, content)
130
+ return content
131
+ }
132
+
133
+ async readMany(filePaths: readonly string[]): Promise<Map<string, string>> {
134
+ // in-memory: single-threaded Node.js access pattern
135
+ const results = new Map<string, string>()
136
+ // @fitness-ignore-next-line no-unbounded-concurrency -- bounded by FileAccessor path set; LRU cache limits memory
137
+ const entries = await Promise.all(
138
+ filePaths.map(async (filePath) => {
139
+ const content = await this.read(filePath)
140
+ return [filePath, content] as const
141
+ }),
142
+ )
143
+ for (const [filePath, content] of entries) {
144
+ results.set(filePath, content)
145
+ }
146
+ return results
147
+ }
148
+
149
+ async readAll(): Promise<Map<string, string>> {
150
+ return this.readMany(this.paths)
151
+ }
152
+
153
+ /** Number of files currently held in the LRU cache. */
154
+ get cachedCount(): number {
155
+ return this.cache.size
156
+ }
157
+
158
+ /** Evict all entries from the file cache. */
159
+ clearCache(): void {
160
+ this.cache.clear()
161
+ }
162
+ }
163
+
164
+ /** Create a FileAccessor for lazy-loading files with LRU caching. */
165
+ // @fitness-ignore-next-line result-pattern-consistency -- factory function, cannot fail in domain-meaningful ways
166
+ export function createFileAccessor(
167
+ filePaths: readonly string[],
168
+ options: FileAccessorOptions = {},
169
+ ): FileAccessor {
170
+ return new FileAccessorImpl(filePaths, options)
171
+ }