@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,242 @@
1
+ /**
2
+ * @fileoverview Auto-discovery of @opensip-tools/checks-* packages
3
+ * installed in node_modules.
4
+ *
5
+ * Resolution rules (apply in order):
6
+ *
7
+ * 1. If `plugins.checkPackages` is declared in the project config,
8
+ * that explicit list wins. Auto-discovery is skipped entirely.
9
+ * Lets users pin their check set deterministically.
10
+ *
11
+ * 2. Else if `plugins.autoDiscoverChecks: false` is declared,
12
+ * no additional check packages are loaded. Lets users opt out
13
+ * of dependency-based discovery (e.g. when running in an
14
+ * environment with unrelated @opensip-tools packages installed).
15
+ *
16
+ * 3. Otherwise (default), scan node_modules for any package whose
17
+ * name matches `@opensip-tools/checks-*` and return the list.
18
+ *
19
+ * No package is privileged — what used to be `checks-builtin` is now
20
+ * just one of many `@opensip-tools/checks-*` packages declared as
21
+ * ordinary CLI dependencies and discovered uniformly. The CLI no
22
+ * longer hardcodes any check package import.
23
+ *
24
+ * The walker handles pnpm's nested node_modules layout: it looks at
25
+ * the project's direct node_modules, then walks up to ancestor
26
+ * node_modules (matching Node's resolution algorithm). We don't need
27
+ * to recurse into transitive deps because check packages are expected
28
+ * to be direct dependencies.
29
+ */
30
+
31
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
32
+ import { createRequire } from 'node:module'
33
+ import { dirname, join } from 'node:path'
34
+
35
+ import { logger } from '@opensip-tools/core'
36
+
37
+ const CONFIG_FILENAME = 'opensip-tools.config.yml'
38
+
39
+ // Bridge ESM ↔ CJS: createRequire bound to this module's URL gives us a
40
+ // `require` that resolves js-yaml from this package's own deps. The bare
41
+ // `require('js-yaml')` form fails in ESM because no global require is
42
+ // present; using createRequire(import.meta.url) is the supported escape
43
+ // hatch.
44
+ const requireFromHere = createRequire(import.meta.url)
45
+
46
+ const SCOPE = '@opensip-tools'
47
+ const CHECKS_PREFIX = 'checks-'
48
+
49
+ export interface CheckPackageDiscoveryOptions {
50
+ /** Absolute path to the project root (where opensip-tools.config.yml lives). */
51
+ readonly projectDir: string
52
+ /** Explicit list from `plugins.checkPackages` in the config. */
53
+ readonly explicitPackages?: readonly string[]
54
+ /** When false, auto-discovery is disabled. Default: true. */
55
+ readonly autoDiscover?: boolean
56
+ }
57
+
58
+ export interface DiscoveredCheckPackage {
59
+ /** npm package name, e.g. '@opensip-tools/checks-python'. */
60
+ readonly name: string
61
+ /** Absolute path to the package's directory inside node_modules. */
62
+ readonly packageDir: string
63
+ }
64
+
65
+ /**
66
+ * Resolve the list of check packages to load, applying the ordered
67
+ * resolution rules in the file header. Returns every discovered
68
+ * @opensip-tools/checks-* package; the CLI loads them all uniformly,
69
+ * with no package privileged over another.
70
+ */
71
+ export function discoverCheckPackages(
72
+ options: CheckPackageDiscoveryOptions,
73
+ ): DiscoveredCheckPackage[] {
74
+ const { projectDir, explicitPackages, autoDiscover = true } = options
75
+
76
+ // Rule 1: explicit list wins
77
+ if (explicitPackages !== undefined) {
78
+ if (explicitPackages.length === 0) {
79
+ return []
80
+ }
81
+ const out: DiscoveredCheckPackage[] = []
82
+ for (const name of explicitPackages) {
83
+ const dir = resolvePackageDir(projectDir, name)
84
+ if (dir) {
85
+ out.push({ name, packageDir: dir })
86
+ } else {
87
+ logger.warn({
88
+ evt: 'plugin.check_package.not_resolved',
89
+ module: 'core:plugins',
90
+ name,
91
+ msg: `Configured check package "${name}" is not installed in node_modules — skipping`,
92
+ })
93
+ }
94
+ }
95
+ return out
96
+ }
97
+
98
+ // Rule 2: opt-out
99
+ if (!autoDiscover) {
100
+ return []
101
+ }
102
+
103
+ // Rule 3: auto-discover
104
+ return autoDiscoverChecks(projectDir)
105
+ }
106
+
107
+ /**
108
+ * Walk up the directory tree from `projectDir` looking for the first
109
+ * `node_modules/@opensip-tools/` directory and return all `checks-*`
110
+ * package directories found there. Mirrors Node's module resolution
111
+ * (any ancestor node_modules counts), which handles pnpm hoisting and
112
+ * monorepo layouts where the scope may live in the workspace root.
113
+ */
114
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- ancestor-walk discovery: walks node_modules trees up the directory tree until the filesystem root
115
+ function autoDiscoverChecks(projectDir: string): DiscoveredCheckPackage[] {
116
+ const seen = new Set<string>()
117
+ const out: DiscoveredCheckPackage[] = []
118
+ let dir = projectDir
119
+ let prev = ''
120
+ while (dir !== prev) {
121
+ const scopeDir = join(dir, 'node_modules', SCOPE)
122
+ if (existsSync(scopeDir)) {
123
+ for (const entry of safeReaddir(scopeDir)) {
124
+ if (!entry.startsWith(CHECKS_PREFIX)) continue
125
+ const name = `${SCOPE}/${entry}`
126
+ if (seen.has(name)) continue
127
+ const packageDir = join(scopeDir, entry)
128
+ if (!hasPackageJson(packageDir)) continue
129
+ seen.add(name)
130
+ out.push({ name, packageDir })
131
+ }
132
+ }
133
+ prev = dir
134
+ dir = dirname(dir)
135
+ }
136
+ return out
137
+ }
138
+
139
+ function resolvePackageDir(projectDir: string, name: string): string | undefined {
140
+ let dir = projectDir
141
+ let prev = ''
142
+ while (dir !== prev) {
143
+ const candidate = join(dir, 'node_modules', name)
144
+ if (hasPackageJson(candidate)) return candidate
145
+ prev = dir
146
+ dir = dirname(dir)
147
+ }
148
+ return undefined
149
+ }
150
+
151
+ function hasPackageJson(packageDir: string): boolean {
152
+ if (!existsSync(packageDir)) return false
153
+ return existsSync(join(packageDir, 'package.json'))
154
+ }
155
+
156
+ function safeReaddir(dir: string): string[] {
157
+ try {
158
+ return readdirSync(dir)
159
+ } catch {
160
+ return []
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Read `name` and `main`/`exports` from a package.json. Used by the CLI
166
+ * to resolve the entry point of a discovered check package.
167
+ */
168
+ export interface CheckPackageMetadata {
169
+ readonly name: string
170
+ readonly mainEntry: string
171
+ }
172
+
173
+ /**
174
+ * Read the `plugins.checkPackages` and `plugins.autoDiscoverChecks`
175
+ * fields from the project's opensip-tools.config.yml without doing a
176
+ * full schema parse. Returns the raw values so callers can apply the
177
+ * resolution rules in `discoverCheckPackages()`.
178
+ *
179
+ * Mirrors the inline-yaml-read pattern used by readProjectPluginsList()
180
+ * — avoids a circular dep between plugins/ and targets/.
181
+ */
182
+ export function readCheckPackagePreferences(projectDir: string): {
183
+ readonly checkPackages?: readonly string[]
184
+ readonly autoDiscoverChecks?: boolean
185
+ } {
186
+ const configPath = join(projectDir, CONFIG_FILENAME)
187
+ if (!existsSync(configPath)) return {}
188
+ try {
189
+ const raw = readFileSync(configPath, 'utf8')
190
+ const yaml = requireFromHere('js-yaml') as { load: (s: string) => unknown }
191
+ const doc = yaml.load(raw) as Record<string, unknown> | null
192
+ if (!doc || typeof doc !== 'object') return {}
193
+ const plugins = doc.plugins
194
+ if (!plugins || typeof plugins !== 'object') return {}
195
+ const p = plugins as Record<string, unknown>
196
+ const result: { checkPackages?: readonly string[]; autoDiscoverChecks?: boolean } = {}
197
+ if (Array.isArray(p.checkPackages)) {
198
+ result.checkPackages = p.checkPackages.filter((v): v is string => typeof v === 'string')
199
+ }
200
+ if (typeof p.autoDiscoverChecks === 'boolean') {
201
+ result.autoDiscoverChecks = p.autoDiscoverChecks
202
+ }
203
+ return result
204
+ } catch {
205
+ return {}
206
+ }
207
+ }
208
+
209
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- exports-map resolution: each branch corresponds to a documented npm exports shape (string, object with import/default/require)
210
+ export function readCheckPackageMetadata(packageDir: string): CheckPackageMetadata | undefined {
211
+ const pkgPath = join(packageDir, 'package.json')
212
+ if (!existsSync(pkgPath)) return undefined
213
+ try {
214
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as {
215
+ name?: string
216
+ main?: string
217
+ exports?: Record<string, unknown> | string
218
+ }
219
+ if (!pkg.name) return undefined
220
+ let mainEntry: string | undefined
221
+ const exports = pkg.exports
222
+ if (typeof exports === 'string') {
223
+ mainEntry = exports
224
+ } else if (exports && typeof exports === 'object' && '.' in exports) {
225
+ const dot = exports['.']
226
+ if (typeof dot === 'string') {
227
+ mainEntry = dot
228
+ } else if (dot && typeof dot === 'object') {
229
+ const obj = dot as Record<string, unknown>
230
+ if (typeof obj.import === 'string') mainEntry = obj.import
231
+ else if (typeof obj.default === 'string') mainEntry = obj.default
232
+ }
233
+ }
234
+ if (!mainEntry && typeof pkg.main === 'string') {
235
+ mainEntry = pkg.main
236
+ }
237
+ mainEntry ??= './index.js'
238
+ return { name: pkg.name, mainEntry: join(packageDir, mainEntry) }
239
+ } catch {
240
+ return undefined
241
+ }
242
+ }
@@ -0,0 +1,327 @@
1
+ /**
2
+ * @fileoverview Plugin loader — dynamic import and registration
3
+ *
4
+ * Takes discovered plugins, imports them, validates their exports,
5
+ * and registers checks/recipes with namespaces.
6
+ */
7
+
8
+ import { pathToFileURL } from 'node:url'
9
+
10
+ import { logger, defaultLanguageRegistry, discoverPlugins } from '@opensip-tools/core'
11
+
12
+
13
+ import { isCheck } from '../framework/check-types.js'
14
+ import { defaultRegistry } from '../framework/registry.js'
15
+ import { defaultRecipeRegistry } from '../recipes/registry.js'
16
+
17
+ import type { FitPluginExports } from './types.js'
18
+ import type { FitnessRecipe } from '../recipes/types.js'
19
+ import type {
20
+ DiscoveredPlugin,
21
+ LangPluginExports,
22
+ LoadedPlugin,
23
+ PluginDomain,
24
+ PluginLoadResult,
25
+ } from '@opensip-tools/core'
26
+
27
+ /** Logger module tag used by every event emitted from this loader. */
28
+ const MODULE_TAG = 'core:plugins'
29
+
30
+ /**
31
+ * Load a single discovered plugin.
32
+ *
33
+ * For `domain === 'lang'`, the module is expected to export an `adapters`
34
+ * array of LanguageAdapter; each is registered with defaultLanguageRegistry.
35
+ * For other domains, registers checks and recipes with defaultRegistry and
36
+ * defaultRecipeRegistry respectively.
37
+ */
38
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- plugin loader dispatcher: handles three domains and lifecycle (validate, register, error-wrap) inline; splitting fragments error context
39
+ export async function loadPlugin(
40
+ plugin: DiscoveredPlugin,
41
+ domain: PluginDomain = 'fit',
42
+ ): Promise<LoadedPlugin> {
43
+ try {
44
+ const moduleUrl = pathToFileURL(plugin.entryPoint).href
45
+ const mod = await import(moduleUrl) as FitPluginExports & LangPluginExports
46
+
47
+ let checksRegistered = 0
48
+ let recipesRegistered = 0
49
+ let adaptersRegistered = 0
50
+
51
+ // Lang domain: register language adapters from `adapters` array,
52
+ // named exports, and default export. Adapters are deduplicated by id.
53
+ if (domain === 'lang') {
54
+ const registeredAdapterIds = new Set<string>()
55
+
56
+ const tryRegisterAdapter = (value: unknown, sourceLabel: string): void => {
57
+ if (!looksLikeLanguageAdapter(value)) return
58
+ const id = (value as { id: string }).id
59
+ if (registeredAdapterIds.has(id)) return
60
+ defaultLanguageRegistry.register(value as Parameters<typeof defaultLanguageRegistry.register>[0])
61
+ registeredAdapterIds.add(id)
62
+ adaptersRegistered++
63
+ logger.debug({
64
+ evt: 'plugin.loader.adapter.registered',
65
+ module: MODULE_TAG,
66
+ namespace: plugin.namespace,
67
+ source: sourceLabel,
68
+ id,
69
+ })
70
+ }
71
+
72
+ if (mod.adapters !== undefined) {
73
+ if (Array.isArray(mod.adapters)) {
74
+ for (const [index, adapter] of mod.adapters.entries()) {
75
+ if (looksLikeLanguageAdapter(adapter)) {
76
+ tryRegisterAdapter(adapter, `adapters[${index}]`)
77
+ } else {
78
+ logger.warn({
79
+ evt: 'plugin.loader.invalid_adapter_item',
80
+ module: MODULE_TAG,
81
+ namespace: plugin.namespace,
82
+ source: plugin.source,
83
+ index,
84
+ msg: `Plugin "${plugin.namespace}" adapters[${index}] is not a valid LanguageAdapter — skipping.`,
85
+ })
86
+ }
87
+ }
88
+ } else {
89
+ logger.warn({
90
+ evt: 'plugin.loader.invalid_adapters_export',
91
+ module: MODULE_TAG,
92
+ namespace: plugin.namespace,
93
+ source: plugin.source,
94
+ msg: `Plugin "${plugin.namespace}" exports "adapters" but it is not an array — skipping adapter registration.`,
95
+ })
96
+ }
97
+ }
98
+
99
+ // Named exports that look like LanguageAdapter
100
+ for (const [exportName, value] of Object.entries(mod)) {
101
+ if (
102
+ exportName === 'default' ||
103
+ exportName === 'adapters' ||
104
+ exportName === 'checks' ||
105
+ exportName === 'recipes' ||
106
+ exportName === 'metadata'
107
+ ) continue
108
+ tryRegisterAdapter(value, `named:${exportName}`)
109
+ }
110
+
111
+ // Default export: a single LanguageAdapter
112
+ const defaultExport = (mod as { default?: unknown }).default
113
+ tryRegisterAdapter(defaultExport, 'default')
114
+ }
115
+
116
+ // Register checks with namespace (skipped for lang domain).
117
+ //
118
+ // Two authorship styles are supported:
119
+ // 1. `export const checks = [...]` — array of Check instances
120
+ // 2. `export const myCheck = defineCheck({...})` — Check as a named export
121
+ //
122
+ // Style 2 enables single-file plugins that drop into ~/.opensip-tools/fit/
123
+ // to author one check per file without a redundant array wrapper.
124
+ // Both styles can coexist in the same module; checks are deduplicated by
125
+ // their stable id so a check appearing in both an array and a named
126
+ // export is registered exactly once.
127
+ if (domain !== 'lang') {
128
+ const registeredIds = new Set<string>()
129
+
130
+ // Style 1: explicit `checks` array
131
+ if (mod.checks !== undefined) {
132
+ if (Array.isArray(mod.checks)) {
133
+ for (const [index, check] of mod.checks.entries()) {
134
+ if (isCheck(check)) {
135
+ if (!registeredIds.has(check.config.id)) {
136
+ defaultRegistry.register(check, plugin.namespace)
137
+ registeredIds.add(check.config.id)
138
+ checksRegistered++
139
+ }
140
+ } else {
141
+ logger.warn({
142
+ evt: 'plugin.loader.invalid_check_item',
143
+ module: MODULE_TAG,
144
+ namespace: plugin.namespace,
145
+ source: plugin.source,
146
+ index,
147
+ msg: `Plugin "${plugin.namespace}" checks[${index}] is not a valid Check object — skipping.`,
148
+ })
149
+ }
150
+ }
151
+ } else {
152
+ logger.warn({
153
+ evt: 'plugin.loader.invalid_checks_export',
154
+ module: MODULE_TAG,
155
+ namespace: plugin.namespace,
156
+ source: plugin.source,
157
+ msg: `Plugin "${plugin.namespace}" exports "checks" but it is not an array — skipping checks registration.`,
158
+ })
159
+ }
160
+ }
161
+
162
+ // Style 2: any named export that is a Check instance
163
+ for (const [exportName, value] of Object.entries(mod)) {
164
+ if (exportName === 'default' || exportName === 'checks' || exportName === 'recipes' || exportName === 'metadata') continue
165
+ if (isCheck(value) && !registeredIds.has(value.config.id)) {
166
+ defaultRegistry.register(value, plugin.namespace)
167
+ registeredIds.add(value.config.id)
168
+ checksRegistered++
169
+ }
170
+ }
171
+
172
+ // Default export: a single Check instance
173
+ const defaultExport = (mod as { default?: unknown }).default
174
+ if (isCheck(defaultExport) && !registeredIds.has(defaultExport.config.id)) {
175
+ defaultRegistry.register(defaultExport, plugin.namespace)
176
+ registeredIds.add(defaultExport.config.id)
177
+ checksRegistered++
178
+ }
179
+ }
180
+
181
+ // Register recipes (skipped for lang domain)
182
+ if (domain !== 'lang' && mod.recipes !== undefined) {
183
+ const recipes: readonly FitnessRecipe[] = mod.recipes
184
+ for (const [index, recipe] of recipes.entries()) {
185
+ if (recipe && typeof recipe === 'object' && 'id' in recipe && 'name' in recipe) {
186
+ try {
187
+ defaultRecipeRegistry.register(recipe, { allowOverwrite: false })
188
+ recipesRegistered++
189
+ } catch {
190
+ // Duplicate recipe — skip silently
191
+ }
192
+ } else {
193
+ logger.warn({
194
+ evt: 'plugin.loader.invalid_recipe_item',
195
+ module: MODULE_TAG,
196
+ namespace: plugin.namespace,
197
+ source: plugin.source,
198
+ index,
199
+ msg: `Plugin "${plugin.namespace}" recipes[${index}] is not a valid Recipe object (missing id or name) — skipping.`,
200
+ })
201
+ }
202
+ }
203
+ }
204
+
205
+ // Warn only when nothing was registered. With named-export discovery,
206
+ // counting actual registrations is more accurate than checking which
207
+ // declared exports were present.
208
+ const nothingRegistered =
209
+ domain === 'lang'
210
+ ? adaptersRegistered === 0
211
+ : checksRegistered === 0 && recipesRegistered === 0
212
+
213
+ if (nothingRegistered) {
214
+ logger.warn({
215
+ evt: 'plugin.loader.no_exports',
216
+ module: MODULE_TAG,
217
+ namespace: plugin.namespace,
218
+ source: plugin.source,
219
+ domain,
220
+ msg:
221
+ domain === 'lang'
222
+ ? `Plugin "${plugin.namespace}" registered no language adapters — nothing to use.`
223
+ : `Plugin "${plugin.namespace}" registered no checks or recipes — nothing to run.`,
224
+ })
225
+ }
226
+
227
+ logger.info({
228
+ evt: 'plugin.loader.load.success',
229
+ module: MODULE_TAG,
230
+ namespace: plugin.namespace,
231
+ source: plugin.source,
232
+ domain,
233
+ checksRegistered,
234
+ recipesRegistered,
235
+ adaptersRegistered,
236
+ })
237
+
238
+ return {
239
+ namespace: plugin.namespace,
240
+ source: plugin.source,
241
+ type: plugin.type,
242
+ checksRegistered,
243
+ recipesRegistered,
244
+ adaptersRegistered,
245
+ }
246
+ } catch (error) {
247
+ const errorMsg = error instanceof Error ? error.message : String(error)
248
+
249
+ logger.warn({
250
+ evt: 'plugin.loader.load.error',
251
+ module: MODULE_TAG,
252
+ namespace: plugin.namespace,
253
+ source: plugin.source,
254
+ error: errorMsg,
255
+ err: error instanceof Error ? error : undefined,
256
+ msg: `Plugin "${plugin.namespace}" failed to load: ${errorMsg}. Continuing without this plugin.`,
257
+ })
258
+
259
+ return {
260
+ namespace: plugin.namespace,
261
+ source: plugin.source,
262
+ type: plugin.type,
263
+ checksRegistered: 0,
264
+ recipesRegistered: 0,
265
+ adaptersRegistered: 0,
266
+ error: errorMsg,
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Structural check for LanguageAdapter — used by the lang plugin domain
273
+ * to identify adapter values among arbitrary named exports. We don't
274
+ * have a runtime type guard equivalent to isCheck() for adapters, so
275
+ * we duck-type the required surface (`id`, `fileExtensions`, `parse`,
276
+ * `stripStrings`, `stripComments`).
277
+ */
278
+ function looksLikeLanguageAdapter(value: unknown): boolean {
279
+ if (!value || typeof value !== 'object') return false
280
+ const v = value as Record<string, unknown>
281
+ return (
282
+ typeof v.id === 'string' &&
283
+ Array.isArray(v.fileExtensions) &&
284
+ typeof v.parse === 'function' &&
285
+ typeof v.stripStrings === 'function' &&
286
+ typeof v.stripComments === 'function'
287
+ )
288
+ }
289
+
290
+ /**
291
+ * Discover and load all plugins for a domain. Loads sequentially to
292
+ * ensure deterministic registration order.
293
+ *
294
+ * Discovers loose `.mjs` files under
295
+ * `<projectDir>/opensip-tools/<tool>/{checks,recipes,scenarios}/` plus
296
+ * any npm-installed packages in
297
+ * `<projectDir>/opensip-tools/.runtime/plugins/<domain>/node_modules/`
298
+ * that are listed in `plugins.<domain>` in the project config.
299
+ *
300
+ * Without a `projectDir`, no plugins are loaded — there's no
301
+ * user-global fallback.
302
+ */
303
+ export async function loadAllPlugins(
304
+ domain: PluginDomain,
305
+ projectDir?: string,
306
+ ): Promise<PluginLoadResult> {
307
+ const discovered = discoverPlugins(domain, projectDir)
308
+
309
+ const plugins: LoadedPlugin[] = []
310
+ const errors: string[] = []
311
+
312
+ for (const plugin of discovered) {
313
+ const result = await loadPlugin(plugin, domain)
314
+ plugins.push(result)
315
+ if (result.error) {
316
+ errors.push(`${result.source}: ${result.error}`)
317
+ }
318
+ }
319
+
320
+ return {
321
+ plugins,
322
+ totalChecks: plugins.reduce((sum, p) => sum + p.checksRegistered, 0),
323
+ totalRecipes: plugins.reduce((sum, p) => sum + p.recipesRegistered, 0),
324
+ totalAdapters: plugins.reduce((sum, p) => sum + (p.adaptersRegistered ?? 0), 0),
325
+ errors,
326
+ }
327
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview Fitness plugin export contract
3
+ *
4
+ * What an @opensip-tools/checks-* (or any fit-domain plugin) exports.
5
+ * Held in fitness because it references Check / FitnessRecipe — types
6
+ * the kernel doesn't know about.
7
+ */
8
+
9
+
10
+ import type { Check } from '../framework/check-types.js'
11
+ import type { FitnessRecipe } from '../recipes/types.js'
12
+ import type { CheckDisplayEntry, PluginMetadata } from '@opensip-tools/core'
13
+
14
+ /** What a fitness plugin package/file exports */
15
+ export interface FitPluginExports {
16
+ readonly checks?: readonly Check[]
17
+ readonly recipes?: readonly FitnessRecipe[]
18
+ readonly metadata?: PluginMetadata
19
+ /**
20
+ * Optional display map: check slug → [icon, displayName].
21
+ * The CLI merges these from every loaded check package and uses
22
+ * the merged map when rendering tables and dashboard catalog entries.
23
+ */
24
+ readonly checkDisplay?: Readonly<Record<string, CheckDisplayEntry>>
25
+ }
@@ -0,0 +1,107 @@
1
+ import { describe, it, expect } from 'vitest';
2
+
3
+ import { builtInRecipes, builtInRecipesByName, isBuiltInRecipe } from '../built-in-recipes.js';
4
+
5
+ describe('built-in recipes', () => {
6
+ it('has expected recipes', () => {
7
+ expect(builtInRecipesByName.has('default')).toBe(true);
8
+ expect(builtInRecipesByName.has('quick-smoke')).toBe(true);
9
+ expect(builtInRecipesByName.has('security')).toBe(true);
10
+ expect(builtInRecipesByName.has('backend')).toBe(true);
11
+ expect(builtInRecipesByName.has('frontend')).toBe(true);
12
+ expect(builtInRecipesByName.has('ci')).toBe(true);
13
+ expect(builtInRecipesByName.has('pre-commit')).toBe(true);
14
+ expect(builtInRecipesByName.has('pre-release')).toBe(true);
15
+ expect(builtInRecipesByName.has('nightly-full')).toBe(true);
16
+ expect(builtInRecipesByName.has('architecture')).toBe(true);
17
+ });
18
+
19
+ it('builtInRecipes array matches builtInRecipesByName map', () => {
20
+ expect(builtInRecipes.length).toBe(builtInRecipesByName.size);
21
+ for (const recipe of builtInRecipes) {
22
+ expect(builtInRecipesByName.get(recipe.name)).toBe(recipe);
23
+ }
24
+ });
25
+
26
+ it('all recipes have required fields', () => {
27
+ for (const [name, recipe] of builtInRecipesByName) {
28
+ expect(recipe.name).toBe(name);
29
+ expect(recipe.id).toBeDefined();
30
+ expect(recipe.displayName).toBeDefined();
31
+ expect(recipe.description).toBeDefined();
32
+ expect(recipe.checks).toBeDefined();
33
+ expect(recipe.execution).toBeDefined();
34
+ expect(recipe.reporting).toBeDefined();
35
+ }
36
+ });
37
+
38
+ it('all recipes have valid check selector types', () => {
39
+ const validTypes = ['all', 'explicit', 'pattern', 'tags'];
40
+ for (const [, recipe] of builtInRecipesByName) {
41
+ expect(validTypes).toContain(recipe.checks.type);
42
+ }
43
+ });
44
+
45
+ it('default recipe includes all checks', () => {
46
+ const defaultRecipe = builtInRecipesByName.get('default');
47
+ expect(defaultRecipe?.checks.type).toBe('all');
48
+ });
49
+
50
+ it('pre-release recipe includes all checks', () => {
51
+ const preRelease = builtInRecipesByName.get('pre-release');
52
+ expect(preRelease?.checks.type).toBe('all');
53
+ });
54
+
55
+ it('quick-smoke recipe uses explicit check list', () => {
56
+ const quickSmoke = builtInRecipesByName.get('quick-smoke');
57
+ expect(quickSmoke?.checks.type).toBe('explicit');
58
+ if (quickSmoke?.checks.type === 'explicit') {
59
+ expect(quickSmoke.checks.checkIds.length).toBeGreaterThan(0);
60
+ }
61
+ });
62
+
63
+ it('backend recipe uses pattern selector', () => {
64
+ const backend = builtInRecipesByName.get('backend');
65
+ expect(backend?.checks.type).toBe('pattern');
66
+ if (backend?.checks.type === 'pattern') {
67
+ expect(backend.checks.include.length).toBeGreaterThan(0);
68
+ }
69
+ });
70
+
71
+ it('architecture recipe uses tags selector', () => {
72
+ const arch = builtInRecipesByName.get('architecture');
73
+ expect(arch?.checks.type).toBe('tags');
74
+ if (arch?.checks.type === 'tags') {
75
+ expect(arch.checks.include).toContain('architecture');
76
+ }
77
+ });
78
+
79
+ it('all recipes have valid execution modes', () => {
80
+ for (const [, recipe] of builtInRecipesByName) {
81
+ expect(['parallel', 'sequential']).toContain(recipe.execution.mode);
82
+ }
83
+ });
84
+
85
+ it('all recipes have valid reporting formats', () => {
86
+ for (const [, recipe] of builtInRecipesByName) {
87
+ expect(['table', 'json', 'unified']).toContain(recipe.reporting.format);
88
+ }
89
+ });
90
+
91
+ it('isBuiltInRecipe returns true for known recipes', () => {
92
+ expect(isBuiltInRecipe('default')).toBe(true);
93
+ expect(isBuiltInRecipe('security')).toBe(true);
94
+ expect(isBuiltInRecipe('ci')).toBe(true);
95
+ });
96
+
97
+ it('isBuiltInRecipe returns false for unknown recipes', () => {
98
+ expect(isBuiltInRecipe('nonexistent')).toBe(false);
99
+ expect(isBuiltInRecipe('')).toBe(false);
100
+ });
101
+
102
+ it('recipes are frozen (immutable)', () => {
103
+ for (const recipe of builtInRecipes) {
104
+ expect(Object.isFrozen(recipe)).toBe(true);
105
+ }
106
+ });
107
+ });