@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
package/src/sarif.ts ADDED
@@ -0,0 +1,232 @@
1
+ import { withRetry, logger } from '@opensip-tools/core';
2
+
3
+ import type { CliOutput } from '@opensip-tools/contracts';
4
+
5
+ const SARIF_SCHEMA = 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json';
6
+ const MAX_FINDINGS_PER_CHUNK = 500;
7
+
8
+ /** Logger module tag used by every event emitted from SARIF upload. */
9
+ const MODULE_TAG = 'cli:report';
10
+
11
+ /** Result of a cloud report upload */
12
+ export interface ReportResult {
13
+ readonly url: string;
14
+ readonly findingCount: number;
15
+ readonly runCount: number;
16
+ readonly success: boolean;
17
+ readonly error?: string;
18
+ readonly chunksTotal?: number;
19
+ readonly chunksSucceeded?: number;
20
+ }
21
+
22
+ interface SarifRun {
23
+ tool: { driver: { name: string; version: string; rules: { id: string }[] } };
24
+ results: Record<string, unknown>[];
25
+ }
26
+
27
+ /** Build a SARIF 2.1.0 log from CLI output — one run per check slug */
28
+ export function buildSarifLog(output: CliOutput): Record<string, unknown> {
29
+ return wrapSarifLog(buildSarifRuns(output));
30
+ }
31
+
32
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- SARIF builder: assembles rules + results per check; flatter shape would scatter the per-check loop
33
+ function buildSarifRuns(output: CliOutput): SarifRun[] {
34
+ const runs: SarifRun[] = [];
35
+
36
+ for (const ch of output.checks) {
37
+ if (ch.findings.length === 0) continue;
38
+
39
+ const ruleIds = new Set<string>();
40
+ const results: Record<string, unknown>[] = [];
41
+
42
+ for (const f of ch.findings) {
43
+ ruleIds.add(f.ruleId);
44
+
45
+ const result: Record<string, unknown> = {
46
+ ruleId: f.ruleId,
47
+ message: { text: f.message },
48
+ level: f.severity === 'error' ? 'error' : 'warning',
49
+ };
50
+
51
+ if (f.filePath) {
52
+ // SARIF startLine/startColumn are 1-based (per spec § 3.30.6). Values
53
+ // of 0 are invalid, and fitness signals without a line often carry
54
+ // line=0 as a sentinel — skip those fields rather than emit invalid SARIF.
55
+ const region: Record<string, number> = {};
56
+ if (f.line != null && f.line > 0) region.startLine = f.line;
57
+ if (f.column != null && f.column > 0) region.startColumn = f.column;
58
+ result.locations = [{
59
+ physicalLocation: {
60
+ artifactLocation: { uri: f.filePath },
61
+ ...(Object.keys(region).length > 0 ? { region } : {}),
62
+ },
63
+ }];
64
+ }
65
+
66
+ if (f.suggestion) {
67
+ result.fixes = [{ description: { text: f.suggestion } }];
68
+ }
69
+
70
+ results.push(result);
71
+ }
72
+
73
+ runs.push({
74
+ tool: {
75
+ driver: {
76
+ name: ch.checkSlug,
77
+ version: '1.0.0',
78
+ rules: [...ruleIds].map((id) => ({ id })),
79
+ },
80
+ },
81
+ results,
82
+ });
83
+ }
84
+
85
+ return runs;
86
+ }
87
+
88
+ function wrapSarifLog(runs: SarifRun[]): Record<string, unknown> {
89
+ return { version: '2.1.0' as const, $schema: SARIF_SCHEMA, runs };
90
+ }
91
+
92
+ /**
93
+ * Split SARIF runs into chunks of at most `maxFindings` findings each.
94
+ * Keeps whole runs together when possible; splits a single run across
95
+ * chunks only when it exceeds the limit on its own.
96
+ */
97
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- chunk packer: flush-current vs split-large branches reflect the two shapes the loop must handle
98
+ export function chunkSarifRuns(runs: SarifRun[], maxFindings = MAX_FINDINGS_PER_CHUNK): SarifRun[][] {
99
+ if (runs.length === 0) return [];
100
+
101
+ const chunks: SarifRun[][] = [];
102
+ let currentChunk: SarifRun[] = [];
103
+ let currentCount = 0;
104
+
105
+ for (const run of runs) {
106
+ if (run.results.length <= maxFindings && currentCount + run.results.length <= maxFindings) {
107
+ // Whole run fits in current chunk
108
+ currentChunk.push(run);
109
+ currentCount += run.results.length;
110
+ } else if (run.results.length <= maxFindings) {
111
+ // Whole run fits, but not in current chunk — start a new one
112
+ if (currentChunk.length > 0) chunks.push(currentChunk);
113
+ currentChunk = [run];
114
+ currentCount = run.results.length;
115
+ } else {
116
+ // Run exceeds max — flush current chunk, then split this run
117
+ if (currentChunk.length > 0) chunks.push(currentChunk);
118
+ currentChunk = [];
119
+ currentCount = 0;
120
+
121
+ for (let i = 0; i < run.results.length; i += maxFindings) {
122
+ const slice = run.results.slice(i, i + maxFindings);
123
+ const ruleIds = new Set(slice.map((r) => r.ruleId as string));
124
+ chunks.push([{
125
+ tool: {
126
+ driver: {
127
+ ...run.tool.driver,
128
+ rules: [...ruleIds].map((id) => ({ id })),
129
+ },
130
+ },
131
+ results: slice,
132
+ }]);
133
+ }
134
+ }
135
+ }
136
+
137
+ if (currentChunk.length > 0) chunks.push(currentChunk);
138
+ return chunks;
139
+ }
140
+
141
+ /** True for errors where retrying later or with a different chunk may succeed */
142
+ function isTransientError(status: number): boolean {
143
+ return status >= 500 || status === 429;
144
+ }
145
+
146
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- network reporter: chunked uploads with per-chunk retries and aggregated error summary; phases read better inline
147
+ export async function reportToCloud(output: CliOutput, url: string, apiKey?: string): Promise<ReportResult> {
148
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
149
+ if (apiKey) headers['X-API-Key'] = apiKey;
150
+
151
+ const allRuns = buildSarifRuns(output);
152
+ if (allRuns.length === 0) {
153
+ return { url, findingCount: 0, runCount: 0, success: true };
154
+ }
155
+
156
+ const sarifUrl = url.endsWith('/sarif') ? url : `${url}/sarif`;
157
+ const cwd = process.cwd();
158
+ const target = cwd ? `${sarifUrl}?cwd=${encodeURIComponent(cwd)}` : sarifUrl;
159
+ const totalFindings = allRuns.reduce((n, r) => n + r.results.length, 0);
160
+
161
+ const chunks = chunkSarifRuns(allRuns);
162
+ const errors: string[] = [];
163
+ let succeeded = 0;
164
+
165
+ for (let ci = 0; ci < chunks.length; ci++) {
166
+ const chunk = chunks[ci];
167
+ const chunkFindings = chunk.reduce((n, r) => n + r.results.length, 0);
168
+ // 60s base + 100ms per finding — receiver does per-finding work (dedup, persistence, traces)
169
+ const timeoutMs = Math.min(300_000, 60_000 + chunkFindings * 100);
170
+ const sarifLog = wrapSarifLog(chunk);
171
+
172
+ logger.info({ evt: 'cli.report.chunk.start', module: MODULE_TAG, chunk: `${ci + 1}/${chunks.length}`, findings: chunkFindings, timeoutMs });
173
+
174
+ try {
175
+ const res = await withRetry(
176
+ () => fetch(target, {
177
+ method: 'POST',
178
+ headers,
179
+ body: JSON.stringify(sarifLog),
180
+ signal: AbortSignal.timeout(timeoutMs),
181
+ }),
182
+ {
183
+ maxAttempts: 3,
184
+ initialDelayMs: 500,
185
+ maxDelayMs: 5000,
186
+ onRetry: (attempt, error, delayMs) => {
187
+ logger.info({
188
+ evt: 'cli.report.retry',
189
+ module: MODULE_TAG,
190
+ attempt,
191
+ error: error.message,
192
+ delayMs,
193
+ url: sarifUrl,
194
+ chunk: `${ci + 1}/${chunks.length}`,
195
+ });
196
+ },
197
+ },
198
+ );
199
+
200
+ if (!res.ok) {
201
+ const body = await res.text().catch(() => '');
202
+ const msg = `${res.status} ${res.statusText} ${body}`.trim();
203
+ errors.push(msg);
204
+
205
+ if (!isTransientError(res.status)) {
206
+ // Non-transient (4xx) — no point sending remaining chunks
207
+ logger.info({ evt: 'cli.report.abort', module: MODULE_TAG, reason: msg, remaining: chunks.length - ci - 1 });
208
+ break;
209
+ }
210
+ continue;
211
+ }
212
+
213
+ succeeded++;
214
+ logger.info({ evt: 'cli.report.chunk.done', module: MODULE_TAG, chunk: `${ci + 1}/${chunks.length}`, findings: chunkFindings });
215
+ } catch (error) {
216
+ // Network errors and timeouts are transient — continue with next chunk
217
+ const errMsg = error instanceof Error ? error.message : String(error);
218
+ errors.push(errMsg);
219
+ logger.info({ evt: 'cli.report.chunk.error', module: MODULE_TAG, chunk: `${ci + 1}/${chunks.length}`, error: errMsg });
220
+ }
221
+ }
222
+
223
+ return {
224
+ url: sarifUrl,
225
+ findingCount: totalFindings,
226
+ runCount: allRuns.length,
227
+ success: errors.length === 0,
228
+ error: errors.length > 0 ? errors.join('; ') : undefined,
229
+ chunksTotal: chunks.length,
230
+ chunksSucceeded: succeeded,
231
+ };
232
+ }
@@ -0,0 +1,99 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ import { ValidationError, SystemError } from '@opensip-tools/core';
6
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
+
8
+ import { loadSignalersConfig } from '../loader.js';
9
+
10
+ let testDir: string;
11
+
12
+ beforeEach(() => {
13
+
14
+ testDir = mkdtempSync(join(tmpdir(), 'opensip-signalers-loader-'));
15
+ });
16
+
17
+ afterEach(() => {
18
+ rmSync(testDir, { recursive: true, force: true });
19
+ });
20
+
21
+ describe('loadSignalersConfig', () => {
22
+ it('loads a minimal valid config', () => {
23
+ writeFileSync(
24
+ join(testDir, 'opensip-tools.config.yml'),
25
+ 'targets: {}\n',
26
+ );
27
+ const cfg = loadSignalersConfig(testDir);
28
+ expect(cfg.targets).toEqual({});
29
+ expect(cfg.fitness.failOnErrors).toBe(1);
30
+ expect(cfg.fitness.failOnWarnings).toBe(0);
31
+ });
32
+
33
+ it('parses targets with description, include, exclude', () => {
34
+ writeFileSync(
35
+ join(testDir, 'opensip-tools.config.yml'),
36
+ `targets:
37
+ src:
38
+ description: TS source
39
+ include:
40
+ - "src/**/*.ts"
41
+ exclude:
42
+ - "**/*.test.ts"
43
+ languages: [typescript]
44
+ concerns: [backend]
45
+ `,
46
+ );
47
+ const cfg = loadSignalersConfig(testDir);
48
+ expect(cfg.targets.src?.description).toBe('TS source');
49
+ expect(cfg.targets.src?.include).toEqual(['src/**/*.ts']);
50
+ });
51
+
52
+ it('caches by resolved file path within the TTL', () => {
53
+ const path = join(testDir, 'opensip-tools.config.yml');
54
+ writeFileSync(path, 'targets: {}\n');
55
+ const a = loadSignalersConfig(testDir);
56
+ // Re-write the file content; cached load should not re-read.
57
+ writeFileSync(path, 'targets: {alt: {description: x, include: ["y"]}}\n');
58
+ const b = loadSignalersConfig(testDir);
59
+ expect(b).toBe(a); // same frozen reference
60
+ });
61
+
62
+ it('throws ValidationError when target name violates kebab-case', () => {
63
+ writeFileSync(
64
+ join(testDir, 'opensip-tools.config.yml'),
65
+ `targets:
66
+ Invalid_Name:
67
+ description: x
68
+ include: ["y"]
69
+ `,
70
+ );
71
+ expect(() => loadSignalersConfig(testDir)).toThrow(ValidationError);
72
+ });
73
+
74
+ it('throws ValidationError when YAML is malformed', () => {
75
+ writeFileSync(join(testDir, 'opensip-tools.config.yml'), 'targets: [unbalanced\n');
76
+ expect(() => loadSignalersConfig(testDir)).toThrow(ValidationError);
77
+ });
78
+
79
+ it('throws SystemError when the file is too large', () => {
80
+ const huge = 'x'.repeat(11 * 1024 * 1024); // 11 MB > 10 MB limit
81
+ writeFileSync(join(testDir, 'opensip-tools.config.yml'), huge);
82
+ expect(() => loadSignalersConfig(testDir)).toThrow(SystemError);
83
+ });
84
+
85
+ it('respects an explicit --config path', () => {
86
+ writeFileSync(
87
+ join(testDir, 'custom.yml'),
88
+ 'targets: {}\n',
89
+ );
90
+ const cfg = loadSignalersConfig(testDir, join(testDir, 'custom.yml'));
91
+ expect(cfg.targets).toEqual({});
92
+ });
93
+
94
+ it('treats null YAML as an empty object', () => {
95
+ writeFileSync(join(testDir, 'opensip-tools.config.yml'), '');
96
+ const cfg = loadSignalersConfig(testDir);
97
+ expect(cfg.targets).toEqual({});
98
+ });
99
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview Signalers config barrel export
3
+ *
4
+ * Public API for opensip-tools.config.yml:
5
+ * - loadSignalersConfig() — Load and validate signalers config
6
+ */
7
+
8
+ // Loader
9
+ export { loadSignalersConfig } from './loader.js'
@@ -0,0 +1,141 @@
1
+ // @fitness-ignore-file toctou-race-condition -- cache check + populate is synchronous (sync file I/O + Map.set); no async gap, safe in single-threaded Node.js
2
+ /**
3
+ * @fileoverview Load and cache opensip-tools.config.yml
4
+ *
5
+ * Reads the signalers config file (path resolved via config-resolution),
6
+ * validates with Zod, and returns a frozen SignalersConfig. Throws when
7
+ * the config file is missing — silent no-op would mask a broken scan.
8
+ */
9
+
10
+ import { readFileSync, statSync } from 'node:fs'
11
+
12
+ import { resolveProjectConfigPath , ValidationError, SystemError , logger } from '@opensip-tools/core'
13
+ import yaml from 'js-yaml'
14
+
15
+ const deepFreeze = <T>(obj: T): T => structuredClone(obj)
16
+
17
+ import { SignalersConfigSchema } from './schema.js'
18
+
19
+ import type { SignalersConfig } from './types.js'
20
+
21
+ const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10 MB
22
+
23
+ /** TTL for cached signalers config entries in milliseconds (30 seconds) */
24
+ const SIGNALERS_CACHE_TTL_MS = 30_000
25
+
26
+ interface CacheEntry {
27
+ config: SignalersConfig
28
+ cachedAt: number
29
+ filePath: string
30
+ }
31
+
32
+ /** Cache keyed by resolved absolute file path, not rootDir — avoids
33
+ * stale hits when the same rootDir resolves to different files over
34
+ * time (e.g. --config flag changes between invocations in tests). */
35
+ const cache = new Map<string, CacheEntry>()
36
+
37
+ /**
38
+ * Read the raw config file from disk.
39
+ * @throws {SystemError} When the config file exceeds the maximum allowed size
40
+ * @throws {ValidationError} When the config file cannot be read
41
+ */
42
+ function readConfigFile(filePath: string): string {
43
+ try {
44
+ const stats = statSync(filePath)
45
+ if (stats.size > MAX_FILE_SIZE) {
46
+ throw new SystemError(
47
+ `Config file too large (${stats.size} bytes, max ${MAX_FILE_SIZE}): ${filePath}`,
48
+ { code: 'SYSTEM.FILE.TOO_LARGE' },
49
+ )
50
+ }
51
+ return readFileSync(filePath, 'utf8')
52
+ } catch (error) {
53
+ if (error instanceof ValidationError || error instanceof SystemError) throw error
54
+ const message = error instanceof Error ? error.message : String(error)
55
+ throw new ValidationError(
56
+ `Failed to read config file ${filePath}: ${message}`,
57
+ {
58
+ operation: 'load',
59
+ loader: 'signalers',
60
+ filePath,
61
+ cause: error instanceof Error ? error : undefined,
62
+ },
63
+ )
64
+ }
65
+ }
66
+
67
+ /** @throws {ValidationError} When the YAML content is invalid */
68
+ function parseYaml(raw: string, filePath: string): unknown {
69
+ try {
70
+ return yaml.load(raw) ?? {}
71
+ } catch (error) {
72
+ const message = error instanceof Error ? error.message : String(error)
73
+ throw new ValidationError(`${filePath} contains invalid YAML: ${message}`, {
74
+ operation: 'load',
75
+ loader: 'signalers',
76
+ cause: error instanceof Error ? error : undefined,
77
+ })
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Load and validate opensip-tools.config.yml from the given root directory.
83
+ *
84
+ * Resolution: --config (explicit) → package.json#opensip-tools.configPath
85
+ * → <rootDir>/opensip-tools.config.yml. See resolveProjectConfigPath().
86
+ *
87
+ * Results are cached per resolved file path.
88
+ *
89
+ * @param rootDir - Absolute path to the project root directory
90
+ * @param explicitPath - Optional config path from --config CLI flag
91
+ * @throws {ValidationError} When no config is found, YAML is invalid, or
92
+ * schema validation fails. Intentionally loud: a missing config means
93
+ * the scan would otherwise silently produce zero findings.
94
+ */
95
+ export function loadSignalersConfig(
96
+ rootDir: string,
97
+ explicitPath?: string,
98
+ ): SignalersConfig {
99
+ const filePath = resolveProjectConfigPath(rootDir, explicitPath)
100
+
101
+ const cached = cache.get(filePath)
102
+ if (cached && (Date.now() - cached.cachedAt) < SIGNALERS_CACHE_TTL_MS) {
103
+ return cached.config
104
+ }
105
+
106
+ const raw = readConfigFile(filePath)
107
+ const parsed = parseYaml(raw, filePath)
108
+
109
+ const result = SignalersConfigSchema.safeParse(parsed)
110
+ if (!result.success) {
111
+ const issues = result.error.issues
112
+ .map((i) => ` ${i.path.join('.')}: ${i.message} (expected: ${i.code})`)
113
+ .join('\n')
114
+ throw new ValidationError(
115
+ `${filePath} validation failed:\n${issues}`,
116
+ {
117
+ operation: 'load',
118
+ loader: 'signalers',
119
+ filePath,
120
+ code: 'ERRORS.SIGNALERS.VALIDATION_FAILED',
121
+ },
122
+ )
123
+ }
124
+
125
+ const data = result.data
126
+ const targetCount = Object.keys(data.targets).length
127
+
128
+ logger.info({
129
+ evt: 'core.signalers.config.loaded',
130
+ module: 'core:signalers',
131
+ file: filePath,
132
+ hasFitness: data.fitness !== undefined,
133
+ hasSimulation: data.simulation !== undefined,
134
+ targetCount,
135
+ })
136
+
137
+ const frozen = deepFreeze(data as unknown as Record<string, unknown>) as unknown as SignalersConfig
138
+ cache.set(filePath, { config: frozen, cachedAt: Date.now(), filePath })
139
+ return frozen
140
+ }
141
+
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @fileoverview Zod validation schema for opensip-tools.config.yml
3
+ *
4
+ * Defines the schema for signal producer configuration (fitness, simulation,
5
+ * assess) and file targeting. These settings live alongside the target
6
+ * definitions in opensip-tools.config.yml.
7
+ */
8
+
9
+ import { z } from 'zod'
10
+
11
+ // Inline defaults
12
+ const DEFAULTS = {
13
+ signals: {
14
+ fitness: { failOnErrors: 1, failOnWarnings: 0 },
15
+ },
16
+ } as const;
17
+
18
+ // =============================================================================
19
+ // Target Definition Schema
20
+ // =============================================================================
21
+
22
+ const TargetDefinitionSchema = z.object({
23
+ description: z.string().min(1, 'description is required'),
24
+ include: z.array(z.string()).min(1, 'at least one include pattern is required'),
25
+ exclude: z.array(z.string()).optional(),
26
+ tags: z.array(z.string()).optional(),
27
+ languages: z.array(z.string()).optional(),
28
+ concerns: z.array(z.string()).optional(),
29
+ })
30
+
31
+ // =============================================================================
32
+ // Signaler Schedule Schema
33
+ // =============================================================================
34
+
35
+ const SignalerScheduleSchema = z.object({
36
+ name: z.string().min(1).max(128),
37
+ recipe: z.string().min(1).max(128).optional(),
38
+ scenario: z.string().min(1).max(128).optional(),
39
+ interval: z.enum(['hourly', 'daily', 'weekdays', 'weekly']),
40
+ time: z.string().regex(/^\d{2}:\d{2}$/).default('00:00'),
41
+ day: z.enum(['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']).optional(),
42
+ }).refine(s => s.interval !== 'weekly' || s.day != null, {
43
+ message: 'Weekly schedules must specify a day',
44
+ })
45
+
46
+ // =============================================================================
47
+ // Producer Schemas (copied from config/schema.ts — removed from there in Phase 2)
48
+ // =============================================================================
49
+
50
+ /** Schema for fitness check configuration */
51
+ const FitnessSchema = z.object({
52
+ defaultTarget: z.string().min(1).max(255).optional(),
53
+ maxParallel: z.number().int().min(1).optional(),
54
+ timeout: z.number().int().min(1000).optional(),
55
+ failOnErrors: z.number().int().min(0).default(DEFAULTS.signals.fitness.failOnErrors),
56
+ failOnWarnings: z.number().int().min(0).default(DEFAULTS.signals.fitness.failOnWarnings),
57
+ disabledChecks: z.array(z.string().min(1).max(255)).optional().default([]),
58
+ schedules: z.array(SignalerScheduleSchema).default([]),
59
+ })
60
+
61
+ /** Schema for simulation engine configuration */
62
+ const SimulationSchema = z.object({
63
+ schedules: z.array(SignalerScheduleSchema).default([]),
64
+ })
65
+
66
+ // =============================================================================
67
+ // Check Overrides
68
+ // =============================================================================
69
+
70
+ const CheckTargetValueSchema = z.union([
71
+ z.string(),
72
+ z.array(z.string()).min(1),
73
+ ])
74
+
75
+ // =============================================================================
76
+ // CLI Defaults
77
+ // =============================================================================
78
+
79
+ /**
80
+ * Defaults applied to every `opensip-tools` CLI invocation in the project.
81
+ * Equivalent to flags on the command line (`--recipe`, `--exclude`, …) but
82
+ * declared once in the project config so every contributor and CI run agrees.
83
+ *
84
+ * Lives alongside targets so a project can ship a single config file.
85
+ */
86
+ const CliDefaultsSchema = z.object({
87
+ recipe: z.string().min(1).max(128).optional(),
88
+ exclude: z.array(z.string()).optional(),
89
+ verbose: z.boolean().optional(),
90
+ json: z.boolean().optional(),
91
+ reportTo: z.string().url().optional(),
92
+ apiKey: z.string().min(1).optional(),
93
+ fileTypes: z.array(z.string()).optional(),
94
+ ignore: z.array(z.string()).optional(),
95
+ })
96
+
97
+ // =============================================================================
98
+ // Root Schema
99
+ // =============================================================================
100
+
101
+ /** Wrap a section schema so YAML `null` is treated as `{}` (all defaults). */
102
+ function section<T extends z.ZodTypeAny>(schema: T) {
103
+ return z.preprocess((v) => v ?? {}, schema).default({})
104
+ }
105
+
106
+ /** Root schema for opensip-tools.config.yml validation */
107
+ export const SignalersConfigSchema = z.object({
108
+ globalExcludes: z.array(z.string()).default([]),
109
+ targets: z.record(
110
+ z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, 'target name must be kebab-case'),
111
+ TargetDefinitionSchema,
112
+ ).default({}),
113
+ checkOverrides: z.record(z.string(), CheckTargetValueSchema).optional(),
114
+ fitness: section(FitnessSchema),
115
+ simulation: section(SimulationSchema),
116
+ cli: section(CliDefaultsSchema),
117
+ })
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @fileoverview Signalers configuration types — derived from Zod schemas
3
+ */
4
+
5
+ import type { SignalersConfigSchema } from './schema.js'
6
+ import type { z } from 'zod'
7
+
8
+
9
+ /** Recursively marks all properties as readonly. */
10
+ type DeepReadonly<T> = {
11
+ readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
12
+ }
13
+
14
+ /** Top-level signalers configuration. */
15
+ export type SignalersConfig = DeepReadonly<z.infer<typeof SignalersConfigSchema>>