@opensip-cli/checks-typescript 0.1.0

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 (404) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +31 -0
  4. package/dist/__tests__/all-checks-execute.test.d.ts +12 -0
  5. package/dist/__tests__/all-checks-execute.test.d.ts.map +1 -0
  6. package/dist/__tests__/all-checks-execute.test.js +846 -0
  7. package/dist/__tests__/all-checks-execute.test.js.map +1 -0
  8. package/dist/__tests__/behavior-fixtures-2.test.d.ts +9 -0
  9. package/dist/__tests__/behavior-fixtures-2.test.d.ts.map +1 -0
  10. package/dist/__tests__/behavior-fixtures-2.test.js +625 -0
  11. package/dist/__tests__/behavior-fixtures-2.test.js.map +1 -0
  12. package/dist/__tests__/behavior-fixtures-3.test.d.ts +7 -0
  13. package/dist/__tests__/behavior-fixtures-3.test.d.ts.map +1 -0
  14. package/dist/__tests__/behavior-fixtures-3.test.js +658 -0
  15. package/dist/__tests__/behavior-fixtures-3.test.js.map +1 -0
  16. package/dist/__tests__/behavior-fixtures-4.test.d.ts +8 -0
  17. package/dist/__tests__/behavior-fixtures-4.test.d.ts.map +1 -0
  18. package/dist/__tests__/behavior-fixtures-4.test.js +590 -0
  19. package/dist/__tests__/behavior-fixtures-4.test.js.map +1 -0
  20. package/dist/__tests__/behavior-fixtures-5.test.d.ts +7 -0
  21. package/dist/__tests__/behavior-fixtures-5.test.d.ts.map +1 -0
  22. package/dist/__tests__/behavior-fixtures-5.test.js +548 -0
  23. package/dist/__tests__/behavior-fixtures-5.test.js.map +1 -0
  24. package/dist/__tests__/behavior-fixtures-6.test.d.ts +18 -0
  25. package/dist/__tests__/behavior-fixtures-6.test.d.ts.map +1 -0
  26. package/dist/__tests__/behavior-fixtures-6.test.js +1700 -0
  27. package/dist/__tests__/behavior-fixtures-6.test.js.map +1 -0
  28. package/dist/__tests__/behavior-fixtures.test.d.ts +10 -0
  29. package/dist/__tests__/behavior-fixtures.test.d.ts.map +1 -0
  30. package/dist/__tests__/behavior-fixtures.test.js +812 -0
  31. package/dist/__tests__/behavior-fixtures.test.js.map +1 -0
  32. package/dist/__tests__/branch-fixtures-2.test.d.ts +6 -0
  33. package/dist/__tests__/branch-fixtures-2.test.d.ts.map +1 -0
  34. package/dist/__tests__/branch-fixtures-2.test.js +1369 -0
  35. package/dist/__tests__/branch-fixtures-2.test.js.map +1 -0
  36. package/dist/__tests__/branch-fixtures-3.test.d.ts +7 -0
  37. package/dist/__tests__/branch-fixtures-3.test.d.ts.map +1 -0
  38. package/dist/__tests__/branch-fixtures-3.test.js +877 -0
  39. package/dist/__tests__/branch-fixtures-3.test.js.map +1 -0
  40. package/dist/__tests__/branch-fixtures.test.d.ts +6 -0
  41. package/dist/__tests__/branch-fixtures.test.d.ts.map +1 -0
  42. package/dist/__tests__/branch-fixtures.test.js +1072 -0
  43. package/dist/__tests__/branch-fixtures.test.js.map +1 -0
  44. package/dist/__tests__/checks.test.d.ts +2 -0
  45. package/dist/__tests__/checks.test.d.ts.map +1 -0
  46. package/dist/__tests__/checks.test.js +39 -0
  47. package/dist/__tests__/checks.test.js.map +1 -0
  48. package/dist/__tests__/fixture-coverage.allowlist.d.ts +19 -0
  49. package/dist/__tests__/fixture-coverage.allowlist.d.ts.map +1 -0
  50. package/dist/__tests__/fixture-coverage.allowlist.js +27 -0
  51. package/dist/__tests__/fixture-coverage.allowlist.js.map +1 -0
  52. package/dist/__tests__/fixture-coverage.test.d.ts +13 -0
  53. package/dist/__tests__/fixture-coverage.test.d.ts.map +1 -0
  54. package/dist/__tests__/fixture-coverage.test.js +57 -0
  55. package/dist/__tests__/fixture-coverage.test.js.map +1 -0
  56. package/dist/__tests__/no-bootstrap-tool-import.test.d.ts +2 -0
  57. package/dist/__tests__/no-bootstrap-tool-import.test.d.ts.map +1 -0
  58. package/dist/__tests__/no-bootstrap-tool-import.test.js +75 -0
  59. package/dist/__tests__/no-bootstrap-tool-import.test.js.map +1 -0
  60. package/dist/__tests__/phantom-dependency-detection.test.d.ts +12 -0
  61. package/dist/__tests__/phantom-dependency-detection.test.d.ts.map +1 -0
  62. package/dist/__tests__/phantom-dependency-detection.test.js +112 -0
  63. package/dist/__tests__/phantom-dependency-detection.test.js.map +1 -0
  64. package/dist/__tests__/typescript-frontend.test.d.ts +8 -0
  65. package/dist/__tests__/typescript-frontend.test.d.ts.map +1 -0
  66. package/dist/__tests__/typescript-frontend.test.js +57 -0
  67. package/dist/__tests__/typescript-frontend.test.js.map +1 -0
  68. package/dist/checks/architecture/circular-import-detection.d.ts +14 -0
  69. package/dist/checks/architecture/circular-import-detection.d.ts.map +1 -0
  70. package/dist/checks/architecture/circular-import-detection.js +55 -0
  71. package/dist/checks/architecture/circular-import-detection.js.map +1 -0
  72. package/dist/checks/architecture/contracts-schema-consistency.d.ts +11 -0
  73. package/dist/checks/architecture/contracts-schema-consistency.d.ts.map +1 -0
  74. package/dist/checks/architecture/contracts-schema-consistency.js +75 -0
  75. package/dist/checks/architecture/contracts-schema-consistency.js.map +1 -0
  76. package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts +12 -0
  77. package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts.map +1 -0
  78. package/dist/checks/architecture/drizzle-orm-migration-guardrails.js +92 -0
  79. package/dist/checks/architecture/drizzle-orm-migration-guardrails.js.map +1 -0
  80. package/dist/checks/architecture/index.d.ts +10 -0
  81. package/dist/checks/architecture/index.d.ts.map +1 -0
  82. package/dist/checks/architecture/index.js +10 -0
  83. package/dist/checks/architecture/index.js.map +1 -0
  84. package/dist/checks/architecture/missing-type-exports.d.ts +13 -0
  85. package/dist/checks/architecture/missing-type-exports.d.ts.map +1 -0
  86. package/dist/checks/architecture/missing-type-exports.js +245 -0
  87. package/dist/checks/architecture/missing-type-exports.js.map +1 -0
  88. package/dist/checks/architecture/module-coupling-fan-out.d.ts +20 -0
  89. package/dist/checks/architecture/module-coupling-fan-out.d.ts.map +1 -0
  90. package/dist/checks/architecture/module-coupling-fan-out.js +120 -0
  91. package/dist/checks/architecture/module-coupling-fan-out.js.map +1 -0
  92. package/dist/checks/architecture/no-bootstrap-tool-import.d.ts +38 -0
  93. package/dist/checks/architecture/no-bootstrap-tool-import.d.ts.map +1 -0
  94. package/dist/checks/architecture/no-bootstrap-tool-import.js +95 -0
  95. package/dist/checks/architecture/no-bootstrap-tool-import.js.map +1 -0
  96. package/dist/checks/architecture/package-json-exports-field.d.ts +10 -0
  97. package/dist/checks/architecture/package-json-exports-field.d.ts.map +1 -0
  98. package/dist/checks/architecture/package-json-exports-field.js +56 -0
  99. package/dist/checks/architecture/package-json-exports-field.js.map +1 -0
  100. package/dist/checks/architecture/phantom-dependency-detection.d.ts +22 -0
  101. package/dist/checks/architecture/phantom-dependency-detection.d.ts.map +1 -0
  102. package/dist/checks/architecture/phantom-dependency-detection.js +330 -0
  103. package/dist/checks/architecture/phantom-dependency-detection.js.map +1 -0
  104. package/dist/checks/architecture/tsconfig-extends-validation.d.ts +10 -0
  105. package/dist/checks/architecture/tsconfig-extends-validation.d.ts.map +1 -0
  106. package/dist/checks/architecture/tsconfig-extends-validation.js +78 -0
  107. package/dist/checks/architecture/tsconfig-extends-validation.js.map +1 -0
  108. package/dist/checks/index.d.ts +6 -0
  109. package/dist/checks/index.d.ts.map +1 -0
  110. package/dist/checks/index.js +6 -0
  111. package/dist/checks/index.js.map +1 -0
  112. package/dist/checks/quality/api/api-contract-validation.d.ts +15 -0
  113. package/dist/checks/quality/api/api-contract-validation.d.ts.map +1 -0
  114. package/dist/checks/quality/api/api-contract-validation.js +316 -0
  115. package/dist/checks/quality/api/api-contract-validation.js.map +1 -0
  116. package/dist/checks/quality/api/api-response-validation.d.ts +14 -0
  117. package/dist/checks/quality/api/api-response-validation.d.ts.map +1 -0
  118. package/dist/checks/quality/api/api-response-validation.js +209 -0
  119. package/dist/checks/quality/api/api-response-validation.js.map +1 -0
  120. package/dist/checks/quality/api/fastify-route-validation.d.ts +14 -0
  121. package/dist/checks/quality/api/fastify-route-validation.d.ts.map +1 -0
  122. package/dist/checks/quality/api/fastify-route-validation.js +298 -0
  123. package/dist/checks/quality/api/fastify-route-validation.js.map +1 -0
  124. package/dist/checks/quality/api/fastify-schema-coverage.d.ts +11 -0
  125. package/dist/checks/quality/api/fastify-schema-coverage.d.ts.map +1 -0
  126. package/dist/checks/quality/api/fastify-schema-coverage.js +261 -0
  127. package/dist/checks/quality/api/fastify-schema-coverage.js.map +1 -0
  128. package/dist/checks/quality/api/index.d.ts +5 -0
  129. package/dist/checks/quality/api/index.d.ts.map +1 -0
  130. package/dist/checks/quality/api/index.js +5 -0
  131. package/dist/checks/quality/api/index.js.map +1 -0
  132. package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts +32 -0
  133. package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts.map +1 -0
  134. package/dist/checks/quality/code-structure/duplicate-utility-functions.js +451 -0
  135. package/dist/checks/quality/code-structure/duplicate-utility-functions.js.map +1 -0
  136. package/dist/checks/quality/code-structure/index.d.ts +3 -0
  137. package/dist/checks/quality/code-structure/index.d.ts.map +1 -0
  138. package/dist/checks/quality/code-structure/index.js +3 -0
  139. package/dist/checks/quality/code-structure/index.js.map +1 -0
  140. package/dist/checks/quality/code-structure/no-any-types.d.ts +13 -0
  141. package/dist/checks/quality/code-structure/no-any-types.d.ts.map +1 -0
  142. package/dist/checks/quality/code-structure/no-any-types.js +116 -0
  143. package/dist/checks/quality/code-structure/no-any-types.js.map +1 -0
  144. package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.d.ts +15 -0
  145. package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.d.ts.map +1 -0
  146. package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js +51 -0
  147. package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js.map +1 -0
  148. package/dist/checks/quality/data-integrity/array-validation.d.ts +16 -0
  149. package/dist/checks/quality/data-integrity/array-validation.d.ts.map +1 -0
  150. package/dist/checks/quality/data-integrity/array-validation.js +508 -0
  151. package/dist/checks/quality/data-integrity/array-validation.js.map +1 -0
  152. package/dist/checks/quality/data-integrity/database-index-coverage.d.ts +14 -0
  153. package/dist/checks/quality/data-integrity/database-index-coverage.d.ts.map +1 -0
  154. package/dist/checks/quality/data-integrity/database-index-coverage.js +235 -0
  155. package/dist/checks/quality/data-integrity/database-index-coverage.js.map +1 -0
  156. package/dist/checks/quality/data-integrity/database-schema-validation.d.ts +16 -0
  157. package/dist/checks/quality/data-integrity/database-schema-validation.d.ts.map +1 -0
  158. package/dist/checks/quality/data-integrity/database-schema-validation.js +328 -0
  159. package/dist/checks/quality/data-integrity/database-schema-validation.js.map +1 -0
  160. package/dist/checks/quality/data-integrity/in-memory-repository-detection.d.ts +14 -0
  161. package/dist/checks/quality/data-integrity/in-memory-repository-detection.d.ts.map +1 -0
  162. package/dist/checks/quality/data-integrity/in-memory-repository-detection.js +157 -0
  163. package/dist/checks/quality/data-integrity/in-memory-repository-detection.js.map +1 -0
  164. package/dist/checks/quality/data-integrity/index.d.ts +8 -0
  165. package/dist/checks/quality/data-integrity/index.d.ts.map +1 -0
  166. package/dist/checks/quality/data-integrity/index.js +8 -0
  167. package/dist/checks/quality/data-integrity/index.js.map +1 -0
  168. package/dist/checks/quality/data-integrity/missing-input-validation.d.ts +12 -0
  169. package/dist/checks/quality/data-integrity/missing-input-validation.d.ts.map +1 -0
  170. package/dist/checks/quality/data-integrity/missing-input-validation.js +180 -0
  171. package/dist/checks/quality/data-integrity/missing-input-validation.js.map +1 -0
  172. package/dist/checks/quality/data-integrity/null-safety.d.ts +33 -0
  173. package/dist/checks/quality/data-integrity/null-safety.d.ts.map +1 -0
  174. package/dist/checks/quality/data-integrity/null-safety.js +766 -0
  175. package/dist/checks/quality/data-integrity/null-safety.js.map +1 -0
  176. package/dist/checks/quality/data-integrity/numeric-validation.d.ts +12 -0
  177. package/dist/checks/quality/data-integrity/numeric-validation.d.ts.map +1 -0
  178. package/dist/checks/quality/data-integrity/numeric-validation.js +409 -0
  179. package/dist/checks/quality/data-integrity/numeric-validation.js.map +1 -0
  180. package/dist/checks/quality/frontend/a11y-form-labels.d.ts +14 -0
  181. package/dist/checks/quality/frontend/a11y-form-labels.d.ts.map +1 -0
  182. package/dist/checks/quality/frontend/a11y-form-labels.js +93 -0
  183. package/dist/checks/quality/frontend/a11y-form-labels.js.map +1 -0
  184. package/dist/checks/quality/frontend/a11y-semantic-html.d.ts +14 -0
  185. package/dist/checks/quality/frontend/a11y-semantic-html.d.ts.map +1 -0
  186. package/dist/checks/quality/frontend/a11y-semantic-html.js +88 -0
  187. package/dist/checks/quality/frontend/a11y-semantic-html.js.map +1 -0
  188. package/dist/checks/quality/frontend/index.d.ts +4 -0
  189. package/dist/checks/quality/frontend/index.d.ts.map +1 -0
  190. package/dist/checks/quality/frontend/index.js +4 -0
  191. package/dist/checks/quality/frontend/index.js.map +1 -0
  192. package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts +13 -0
  193. package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts.map +1 -0
  194. package/dist/checks/quality/frontend/test-only-frontend-modules.js +159 -0
  195. package/dist/checks/quality/frontend/test-only-frontend-modules.js.map +1 -0
  196. package/dist/checks/quality/incomplete-regex-escaping.d.ts +13 -0
  197. package/dist/checks/quality/incomplete-regex-escaping.d.ts.map +1 -0
  198. package/dist/checks/quality/incomplete-regex-escaping.js +207 -0
  199. package/dist/checks/quality/incomplete-regex-escaping.js.map +1 -0
  200. package/dist/checks/quality/index.d.ts +11 -0
  201. package/dist/checks/quality/index.d.ts.map +1 -0
  202. package/dist/checks/quality/index.js +11 -0
  203. package/dist/checks/quality/index.js.map +1 -0
  204. package/dist/checks/quality/linting/index.d.ts +2 -0
  205. package/dist/checks/quality/linting/index.d.ts.map +1 -0
  206. package/dist/checks/quality/linting/index.js +2 -0
  207. package/dist/checks/quality/linting/index.js.map +1 -0
  208. package/dist/checks/quality/linting/typescript-frontend.d.ts +25 -0
  209. package/dist/checks/quality/linting/typescript-frontend.d.ts.map +1 -0
  210. package/dist/checks/quality/linting/typescript-frontend.js +159 -0
  211. package/dist/checks/quality/linting/typescript-frontend.js.map +1 -0
  212. package/dist/checks/quality/observability/index.d.ts +5 -0
  213. package/dist/checks/quality/observability/index.d.ts.map +1 -0
  214. package/dist/checks/quality/observability/index.js +5 -0
  215. package/dist/checks/quality/observability/index.js.map +1 -0
  216. package/dist/checks/quality/observability/logger-event-name-format.d.ts +12 -0
  217. package/dist/checks/quality/observability/logger-event-name-format.d.ts.map +1 -0
  218. package/dist/checks/quality/observability/logger-event-name-format.js +124 -0
  219. package/dist/checks/quality/observability/logger-event-name-format.js.map +1 -0
  220. package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts +5 -0
  221. package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts.map +1 -0
  222. package/dist/checks/quality/observability/no-hardcoded-correlation-id.js +77 -0
  223. package/dist/checks/quality/observability/no-hardcoded-correlation-id.js.map +1 -0
  224. package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.d.ts +11 -0
  225. package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.d.ts.map +1 -0
  226. package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.js +107 -0
  227. package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.js.map +1 -0
  228. package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.d.ts +12 -0
  229. package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.d.ts.map +1 -0
  230. package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.js +94 -0
  231. package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.js.map +1 -0
  232. package/dist/checks/quality/observability/observability-coverage/analyzer.d.ts +13 -0
  233. package/dist/checks/quality/observability/observability-coverage/analyzer.d.ts.map +1 -0
  234. package/dist/checks/quality/observability/observability-coverage/analyzer.js +117 -0
  235. package/dist/checks/quality/observability/observability-coverage/analyzer.js.map +1 -0
  236. package/dist/checks/quality/observability/observability-coverage/index.d.ts +4 -0
  237. package/dist/checks/quality/observability/observability-coverage/index.d.ts.map +1 -0
  238. package/dist/checks/quality/observability/observability-coverage/index.js +4 -0
  239. package/dist/checks/quality/observability/observability-coverage/index.js.map +1 -0
  240. package/dist/checks/quality/observability/observability-coverage/logger-detector.d.ts +29 -0
  241. package/dist/checks/quality/observability/observability-coverage/logger-detector.d.ts.map +1 -0
  242. package/dist/checks/quality/observability/observability-coverage/logger-detector.js +111 -0
  243. package/dist/checks/quality/observability/observability-coverage/logger-detector.js.map +1 -0
  244. package/dist/checks/quality/observability/observability-coverage/types.d.ts +64 -0
  245. package/dist/checks/quality/observability/observability-coverage/types.d.ts.map +1 -0
  246. package/dist/checks/quality/observability/observability-coverage/types.js +6 -0
  247. package/dist/checks/quality/observability/observability-coverage/types.js.map +1 -0
  248. package/dist/checks/quality/observability/pii-exposure-in-logs.d.ts +22 -0
  249. package/dist/checks/quality/observability/pii-exposure-in-logs.d.ts.map +1 -0
  250. package/dist/checks/quality/observability/pii-exposure-in-logs.js +212 -0
  251. package/dist/checks/quality/observability/pii-exposure-in-logs.js.map +1 -0
  252. package/dist/checks/quality/observability/pii-exposure-in-logs.test.d.ts +11 -0
  253. package/dist/checks/quality/observability/pii-exposure-in-logs.test.d.ts.map +1 -0
  254. package/dist/checks/quality/observability/pii-exposure-in-logs.test.js +46 -0
  255. package/dist/checks/quality/observability/pii-exposure-in-logs.test.js.map +1 -0
  256. package/dist/checks/quality/patterns/__tests__/toctou-fp.test.d.ts +14 -0
  257. package/dist/checks/quality/patterns/__tests__/toctou-fp.test.d.ts.map +1 -0
  258. package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js +61 -0
  259. package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js.map +1 -0
  260. package/dist/checks/quality/patterns/async-waterfall-detection.d.ts +26 -0
  261. package/dist/checks/quality/patterns/async-waterfall-detection.d.ts.map +1 -0
  262. package/dist/checks/quality/patterns/async-waterfall-detection.js +410 -0
  263. package/dist/checks/quality/patterns/async-waterfall-detection.js.map +1 -0
  264. package/dist/checks/quality/patterns/dispose-pattern-completeness.d.ts +13 -0
  265. package/dist/checks/quality/patterns/dispose-pattern-completeness.d.ts.map +1 -0
  266. package/dist/checks/quality/patterns/dispose-pattern-completeness.js +220 -0
  267. package/dist/checks/quality/patterns/dispose-pattern-completeness.js.map +1 -0
  268. package/dist/checks/quality/patterns/error-handling-quality.d.ts +17 -0
  269. package/dist/checks/quality/patterns/error-handling-quality.d.ts.map +1 -0
  270. package/dist/checks/quality/patterns/error-handling-quality.js +335 -0
  271. package/dist/checks/quality/patterns/error-handling-quality.js.map +1 -0
  272. package/dist/checks/quality/patterns/index.d.ts +10 -0
  273. package/dist/checks/quality/patterns/index.d.ts.map +1 -0
  274. package/dist/checks/quality/patterns/index.js +10 -0
  275. package/dist/checks/quality/patterns/index.js.map +1 -0
  276. package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.d.ts +16 -0
  277. package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.d.ts.map +1 -0
  278. package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.js +205 -0
  279. package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.js.map +1 -0
  280. package/dist/checks/quality/patterns/result-pattern-consistency.d.ts +16 -0
  281. package/dist/checks/quality/patterns/result-pattern-consistency.d.ts.map +1 -0
  282. package/dist/checks/quality/patterns/result-pattern-consistency.js +328 -0
  283. package/dist/checks/quality/patterns/result-pattern-consistency.js.map +1 -0
  284. package/dist/checks/quality/patterns/silent-early-returns.d.ts +23 -0
  285. package/dist/checks/quality/patterns/silent-early-returns.d.ts.map +1 -0
  286. package/dist/checks/quality/patterns/silent-early-returns.js +266 -0
  287. package/dist/checks/quality/patterns/silent-early-returns.js.map +1 -0
  288. package/dist/checks/quality/patterns/stream-buffer-size-limits.d.ts +13 -0
  289. package/dist/checks/quality/patterns/stream-buffer-size-limits.d.ts.map +1 -0
  290. package/dist/checks/quality/patterns/stream-buffer-size-limits.js +163 -0
  291. package/dist/checks/quality/patterns/stream-buffer-size-limits.js.map +1 -0
  292. package/dist/checks/quality/patterns/throws-documentation.d.ts +23 -0
  293. package/dist/checks/quality/patterns/throws-documentation.d.ts.map +1 -0
  294. package/dist/checks/quality/patterns/throws-documentation.js +519 -0
  295. package/dist/checks/quality/patterns/throws-documentation.js.map +1 -0
  296. package/dist/checks/quality/patterns/toctou-race-condition.d.ts +48 -0
  297. package/dist/checks/quality/patterns/toctou-race-condition.d.ts.map +1 -0
  298. package/dist/checks/quality/patterns/toctou-race-condition.js +639 -0
  299. package/dist/checks/quality/patterns/toctou-race-condition.js.map +1 -0
  300. package/dist/checks/quality/stubbed-implementation-detection.d.ts +24 -0
  301. package/dist/checks/quality/stubbed-implementation-detection.d.ts.map +1 -0
  302. package/dist/checks/quality/stubbed-implementation-detection.js +355 -0
  303. package/dist/checks/quality/stubbed-implementation-detection.js.map +1 -0
  304. package/dist/checks/quality/unused-config-options.d.ts +12 -0
  305. package/dist/checks/quality/unused-config-options.d.ts.map +1 -0
  306. package/dist/checks/quality/unused-config-options.js +245 -0
  307. package/dist/checks/quality/unused-config-options.js.map +1 -0
  308. package/dist/checks/resilience/__tests__/callback-invocation-safe.test.d.ts +2 -0
  309. package/dist/checks/resilience/__tests__/callback-invocation-safe.test.d.ts.map +1 -0
  310. package/dist/checks/resilience/__tests__/callback-invocation-safe.test.js +79 -0
  311. package/dist/checks/resilience/__tests__/callback-invocation-safe.test.js.map +1 -0
  312. package/dist/checks/resilience/__tests__/context-leakage-fp.test.d.ts +12 -0
  313. package/dist/checks/resilience/__tests__/context-leakage-fp.test.d.ts.map +1 -0
  314. package/dist/checks/resilience/__tests__/context-leakage-fp.test.js +34 -0
  315. package/dist/checks/resilience/__tests__/context-leakage-fp.test.js.map +1 -0
  316. package/dist/checks/resilience/__tests__/context-mutation.test.d.ts +11 -0
  317. package/dist/checks/resilience/__tests__/context-mutation.test.d.ts.map +1 -0
  318. package/dist/checks/resilience/__tests__/context-mutation.test.js +54 -0
  319. package/dist/checks/resilience/__tests__/context-mutation.test.js.map +1 -0
  320. package/dist/checks/resilience/callback-invocation-safe.d.ts +34 -0
  321. package/dist/checks/resilience/callback-invocation-safe.d.ts.map +1 -0
  322. package/dist/checks/resilience/callback-invocation-safe.js +247 -0
  323. package/dist/checks/resilience/callback-invocation-safe.js.map +1 -0
  324. package/dist/checks/resilience/context-leakage.d.ts +25 -0
  325. package/dist/checks/resilience/context-leakage.d.ts.map +1 -0
  326. package/dist/checks/resilience/context-leakage.js +435 -0
  327. package/dist/checks/resilience/context-leakage.js.map +1 -0
  328. package/dist/checks/resilience/context-mutation.d.ts +21 -0
  329. package/dist/checks/resilience/context-mutation.d.ts.map +1 -0
  330. package/dist/checks/resilience/context-mutation.js +368 -0
  331. package/dist/checks/resilience/context-mutation.js.map +1 -0
  332. package/dist/checks/resilience/detached-promises.d.ts +40 -0
  333. package/dist/checks/resilience/detached-promises.d.ts.map +1 -0
  334. package/dist/checks/resilience/detached-promises.js +646 -0
  335. package/dist/checks/resilience/detached-promises.js.map +1 -0
  336. package/dist/checks/resilience/index.d.ts +7 -0
  337. package/dist/checks/resilience/index.d.ts.map +1 -0
  338. package/dist/checks/resilience/index.js +7 -0
  339. package/dist/checks/resilience/index.js.map +1 -0
  340. package/dist/checks/resilience/no-raw-fetch.d.ts +11 -0
  341. package/dist/checks/resilience/no-raw-fetch.d.ts.map +1 -0
  342. package/dist/checks/resilience/no-raw-fetch.js +110 -0
  343. package/dist/checks/resilience/no-raw-fetch.js.map +1 -0
  344. package/dist/checks/resilience/no-unbounded-concurrency.d.ts +11 -0
  345. package/dist/checks/resilience/no-unbounded-concurrency.d.ts.map +1 -0
  346. package/dist/checks/resilience/no-unbounded-concurrency.js +117 -0
  347. package/dist/checks/resilience/no-unbounded-concurrency.js.map +1 -0
  348. package/dist/checks/security/__tests__/sql-injection.test.d.ts +17 -0
  349. package/dist/checks/security/__tests__/sql-injection.test.d.ts.map +1 -0
  350. package/dist/checks/security/__tests__/sql-injection.test.js +97 -0
  351. package/dist/checks/security/__tests__/sql-injection.test.js.map +1 -0
  352. package/dist/checks/security/index.d.ts +4 -0
  353. package/dist/checks/security/index.d.ts.map +1 -0
  354. package/dist/checks/security/index.js +4 -0
  355. package/dist/checks/security/index.js.map +1 -0
  356. package/dist/checks/security/input-sanitization.d.ts +20 -0
  357. package/dist/checks/security/input-sanitization.d.ts.map +1 -0
  358. package/dist/checks/security/input-sanitization.js +255 -0
  359. package/dist/checks/security/input-sanitization.js.map +1 -0
  360. package/dist/checks/security/sql-injection.d.ts +24 -0
  361. package/dist/checks/security/sql-injection.d.ts.map +1 -0
  362. package/dist/checks/security/sql-injection.js +330 -0
  363. package/dist/checks/security/sql-injection.js.map +1 -0
  364. package/dist/checks/security/unsafe-secret-comparison.d.ts +17 -0
  365. package/dist/checks/security/unsafe-secret-comparison.d.ts.map +1 -0
  366. package/dist/checks/security/unsafe-secret-comparison.js +227 -0
  367. package/dist/checks/security/unsafe-secret-comparison.js.map +1 -0
  368. package/dist/checks/testing/index.d.ts +2 -0
  369. package/dist/checks/testing/index.d.ts.map +1 -0
  370. package/dist/checks/testing/index.js +2 -0
  371. package/dist/checks/testing/index.js.map +1 -0
  372. package/dist/checks/testing/mock-implementations-in-production.d.ts +12 -0
  373. package/dist/checks/testing/mock-implementations-in-production.d.ts.map +1 -0
  374. package/dist/checks/testing/mock-implementations-in-production.js +211 -0
  375. package/dist/checks/testing/mock-implementations-in-production.js.map +1 -0
  376. package/dist/display/architecture.d.ts +9 -0
  377. package/dist/display/architecture.d.ts.map +1 -0
  378. package/dist/display/architecture.js +18 -0
  379. package/dist/display/architecture.js.map +1 -0
  380. package/dist/display/index.d.ts +20 -0
  381. package/dist/display/index.d.ts.map +1 -0
  382. package/dist/display/index.js +30 -0
  383. package/dist/display/index.js.map +1 -0
  384. package/dist/display/quality.d.ts +7 -0
  385. package/dist/display/quality.d.ts.map +1 -0
  386. package/dist/display/quality.js +39 -0
  387. package/dist/display/quality.js.map +1 -0
  388. package/dist/display/resilience.d.ts +7 -0
  389. package/dist/display/resilience.d.ts.map +1 -0
  390. package/dist/display/resilience.js +13 -0
  391. package/dist/display/resilience.js.map +1 -0
  392. package/dist/display/security-testing.d.ts +9 -0
  393. package/dist/display/security-testing.d.ts.map +1 -0
  394. package/dist/display/security-testing.js +14 -0
  395. package/dist/display/security-testing.js.map +1 -0
  396. package/dist/display/types.d.ts +6 -0
  397. package/dist/display/types.d.ts.map +1 -0
  398. package/dist/display/types.js +6 -0
  399. package/dist/display/types.js.map +1 -0
  400. package/dist/index.d.ts +19 -0
  401. package/dist/index.d.ts.map +1 -0
  402. package/dist/index.js +21 -0
  403. package/dist/index.js.map +1 -0
  404. package/package.json +55 -0
@@ -0,0 +1,451 @@
1
+ // @fitness-ignore-file throws-documentation -- Functions throw self-documenting typed errors
2
+ // @fitness-ignore-file toctou-race-condition -- TOCTOU acceptable in this non-concurrent context
3
+ // @fitness-ignore-file file-length-limit -- complex check with tightly coupled hash/normalization/scoring logic; splitting would risk losing the duplicate-detection contract
4
+ /**
5
+ * @fileoverview Duplicate Utility Functions check
6
+ *
7
+ * Detects duplicate utility functions that should be consolidated.
8
+ * Flags TWO types of issues:
9
+ * 1. Identical implementations - true duplicates that must be deduplicated
10
+ * 2. Same-named functions with different implementations - consolidation opportunities
11
+ */
12
+ import { createHash } from 'node:crypto';
13
+ import { basename, dirname } from 'node:path';
14
+ import { defineCheck, getCheckConfig, } from '@opensip-cli/fitness';
15
+ import { getSharedSourceFile } from '@opensip-cli/lang-typescript';
16
+ import * as ts from 'typescript';
17
+ /**
18
+ * Common utility function name patterns
19
+ */
20
+ const UTILITY_PATTERNS = [
21
+ /^format[A-Z]/, // formatDate, formatCurrency
22
+ /^parse[A-Z]/, // parseJson, parseDate
23
+ /^is[A-Z]/, // isValid, isEmpty
24
+ /^has[A-Z]/, // hasValue, hasKey
25
+ /^to[A-Z]/, // toString, toNumber
26
+ /^get[A-Z]/, // getValue, getDefault
27
+ /^validate[A-Z]/, // validateEmail, validatePhone
28
+ /^sanitize[A-Z]/, // sanitizeInput, sanitizeHtml
29
+ /^normalize[A-Z]/, // normalizeUrl, normalizeText
30
+ /^debounce/,
31
+ /^throttle/,
32
+ /^sleep/,
33
+ /^delay/,
34
+ /^retry/,
35
+ /^clamp/,
36
+ /^range/,
37
+ /^chunk/,
38
+ /^unique/,
39
+ /^flatten/,
40
+ ];
41
+ /**
42
+ * Minimum function body length (characters) to consider for duplicate detection.
43
+ * Very short functions (1-2 lines) are often trivial and not worth flagging.
44
+ */
45
+ const MIN_FUNCTION_BODY_LENGTH = 50;
46
+ /**
47
+ * Functions that are intentionally domain-specific and should NOT be flagged
48
+ * as duplicates. Limited to genuinely generic identifiers — config / factory
49
+ * / logger / common type-guard names — that almost any TS codebase will hit.
50
+ *
51
+ * Project-specific names (e.g. opensip's `getCurrentCorrelationId`,
52
+ * `formatDuration`, `getRemoteUrl`, `sanitizeForPrompt`) belong in a recipe's
53
+ * `checks.config['duplicate-utility-functions'].additionalDomainSpecificFunctions`
54
+ * block. The check reads that list via {@link getCheckConfig} and merges it
55
+ * with these defaults.
56
+ */
57
+ const DOMAIN_SPECIFIC_FUNCTIONS = new Set([
58
+ // Config / factory pattern - each domain has its own configuration
59
+ 'getConfig',
60
+ 'getDefaultConfig',
61
+ 'getContainer',
62
+ 'getFactory',
63
+ // Logger / shared singletons
64
+ 'getLogger',
65
+ // Common predicates that have multiple legitimate definitions
66
+ 'isPlainObject',
67
+ 'isStringArray',
68
+ 'isCommentLine',
69
+ 'isTestFile',
70
+ 'isValidEmail',
71
+ // Common formatter / parser names that vary by input shape
72
+ 'formatDate',
73
+ 'formatTimestamp',
74
+ 'parseArgs',
75
+ // Common validation entry points
76
+ 'validateConfig',
77
+ 'validateSchema',
78
+ // Common error-message helper
79
+ 'getErrorMessage',
80
+ // AST predicates / shared kernel helpers — each layer (engine, lang adapter,
81
+ // graph adapter) legitimately defines its own implementation tuned to its
82
+ // node shape; the architecture rules prevent cross-layer imports.
83
+ 'isPropertyAccess',
84
+ 'isFunctionLike',
85
+ 'getSharedSourceFile',
86
+ 'getLineNumber',
87
+ 'isReturnValueDiscarded',
88
+ // Plugin / package discovery (mirrored in fitness + simulation discovery walkers)
89
+ 'hasPackageJson',
90
+ // Language-adapter parsers and dir normalizers — one per graph-* pack
91
+ // (graph-go, graph-java, graph-python, graph-rust, graph-typescript) by
92
+ // design; cross-pack imports are forbidden by .config/dependency-cruiser.cjs.
93
+ 'normalizeProjectDir',
94
+ 'parseProject',
95
+ // Tokenizer helper present in several language-adapter strip-comment passes
96
+ 'isIdentChar',
97
+ // Assertion validation — fitness engine + simulation engine each own one
98
+ 'validateAssertions',
99
+ ]);
100
+ /**
101
+ * Build the effective domain-specific set by merging built-in defaults with
102
+ * the recipe-provided augmentation for `duplicate-utility-functions`.
103
+ */
104
+ function buildEffectiveDomainSpecificSet() {
105
+ const cfg = getCheckConfig('duplicate-utility-functions');
106
+ if (!cfg.additionalDomainSpecificFunctions ||
107
+ cfg.additionalDomainSpecificFunctions.length === 0) {
108
+ return DOMAIN_SPECIFIC_FUNCTIONS;
109
+ }
110
+ const merged = new Set(DOMAIN_SPECIFIC_FUNCTIONS);
111
+ for (const name of cfg.additionalDomainSpecificFunctions)
112
+ merged.add(name);
113
+ return merged;
114
+ }
115
+ /**
116
+ * Get unique directories from a list of function locations
117
+ */
118
+ function getUniqueDirectories(locations) {
119
+ /* v8 ignore next -- defensive guard */
120
+ if (!Array.isArray(locations)) {
121
+ return new Set();
122
+ }
123
+ return new Set(locations.map((l) => dirname(l.file)));
124
+ }
125
+ /**
126
+ * Flatten all locations from hash groups into a single array
127
+ */
128
+ function flattenHashGroups(hashGroups) {
129
+ const allLocations = [];
130
+ if (hashGroups.size === 0) {
131
+ return allLocations;
132
+ }
133
+ for (const locations of hashGroups.values()) {
134
+ if (Array.isArray(locations) && locations.length > 0) {
135
+ allLocations.push(...locations);
136
+ }
137
+ }
138
+ return allLocations;
139
+ }
140
+ /**
141
+ * Get first location from each hash group (unique implementations)
142
+ */
143
+ function getFirstFromEachHashGroup(hashGroups) {
144
+ const uniqueImpls = [];
145
+ if (hashGroups.size === 0) {
146
+ return uniqueImpls;
147
+ }
148
+ for (const locations of hashGroups.values()) {
149
+ /* v8 ignore next -- defensive guard */
150
+ if (!Array.isArray(locations) || locations.length === 0) {
151
+ continue;
152
+ }
153
+ const first = locations[0];
154
+ if (first) {
155
+ uniqueImpls.push(first);
156
+ }
157
+ }
158
+ return uniqueImpls;
159
+ }
160
+ /**
161
+ * Format other files for display in violation message
162
+ */
163
+ function formatOtherFiles(locations) {
164
+ const otherFiles = locations
165
+ .slice(1)
166
+ .map((l) => basename(l.file))
167
+ .slice(0, 3);
168
+ const moreCount = locations.length > 4 ? ` (+${locations.length - 4} more)` : '';
169
+ return `${otherFiles.join(', ')}${moreCount}`;
170
+ }
171
+ /**
172
+ * Add a function to the functions-by-name collection
173
+ */
174
+ function addFunctionToCollection(functionsByName, fn) {
175
+ let nameGroup = functionsByName.get(fn.name);
176
+ if (!nameGroup) {
177
+ nameGroup = new Map();
178
+ functionsByName.set(fn.name, nameGroup);
179
+ }
180
+ let hashGroup = nameGroup.get(fn.bodyHash);
181
+ if (!hashGroup) {
182
+ hashGroup = [];
183
+ nameGroup.set(fn.bodyHash, hashGroup);
184
+ }
185
+ hashGroup.push(fn);
186
+ }
187
+ /**
188
+ * Check if locations represent a valid duplicate across multiple directories.
189
+ */
190
+ function isValidCrossDirectoryDuplicate(locations) {
191
+ /* v8 ignore next -- defensive guard */
192
+ if (!Array.isArray(locations) || locations.length <= 1) {
193
+ return false;
194
+ }
195
+ const locationDirs = getUniqueDirectories(locations);
196
+ return locationDirs.size > 1;
197
+ }
198
+ /**
199
+ * Remove single-line comments from code
200
+ */
201
+ function removeSingleLineComments(code) {
202
+ return code
203
+ .split('\n')
204
+ .map((line) => {
205
+ const commentIndex = line.indexOf('//');
206
+ return commentIndex === -1 ? line : line.slice(0, commentIndex);
207
+ })
208
+ .join('\n');
209
+ }
210
+ /**
211
+ * Remove multi-line comments from code
212
+ * Uses iterative approach to avoid regex backtracking issues.
213
+ */
214
+ function removeMultiLineComments(code) {
215
+ let result = '';
216
+ let i = 0;
217
+ while (i < code.length) {
218
+ if (code[i] === '/' && code[i + 1] === '*') {
219
+ const endIndex = code.indexOf('*/', i + 2);
220
+ if (endIndex === -1) {
221
+ break;
222
+ }
223
+ i = endIndex + 2;
224
+ }
225
+ else {
226
+ result += code[i];
227
+ i++;
228
+ }
229
+ }
230
+ return result;
231
+ }
232
+ /**
233
+ * Normalize function body for comparison.
234
+ * Removes whitespace, comments, and normalizes identifiers.
235
+ */
236
+ function normalizeBody(body) {
237
+ let normalized = body;
238
+ normalized = removeSingleLineComments(normalized);
239
+ normalized = removeMultiLineComments(normalized);
240
+ normalized = normalized.replaceAll(/\s+/g, ' ');
241
+ normalized = normalized.trim();
242
+ return normalized;
243
+ }
244
+ /**
245
+ * Generate a hash of the normalized function body.
246
+ * Uses SHA-256 for content-addressable deduplication (not for security purposes).
247
+ */
248
+ function hashBody(body) {
249
+ const normalized = normalizeBody(body);
250
+ return createHash('sha256').update(normalized).digest('hex');
251
+ }
252
+ /**
253
+ * Check if function name matches utility patterns. The `domainSpecific`
254
+ * argument is the effective allowlist (built-in defaults plus any
255
+ * recipe-supplied augmentation).
256
+ */
257
+ function isUtilityFunction(name, domainSpecific) {
258
+ if (domainSpecific.has(name)) {
259
+ return false;
260
+ }
261
+ return UTILITY_PATTERNS.some((pattern) => pattern.test(name));
262
+ }
263
+ /**
264
+ * Extract utility functions from a file with their body hashes
265
+ */
266
+ function extractUtilityFunctionsWithBody(filePath, content, domainSpecific) {
267
+ const functions = [];
268
+ try {
269
+ const sourceFile = getSharedSourceFile(filePath, content);
270
+ /* v8 ignore next -- defensive guard */
271
+ if (!sourceFile)
272
+ return [];
273
+ const visit = (node) => {
274
+ // Check function declarations
275
+ if (ts.isFunctionDeclaration(node) && node.name && node.body) {
276
+ const name = node.name.text;
277
+ if (isUtilityFunction(name, domainSpecific)) {
278
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
279
+ const body = node.body.getText(sourceFile);
280
+ functions.push({
281
+ name,
282
+ line: line + 1,
283
+ file: filePath,
284
+ bodyHash: hashBody(body),
285
+ bodyLength: body.length,
286
+ });
287
+ }
288
+ }
289
+ // Check arrow functions assigned to variables
290
+ if (ts.isVariableDeclaration(node) &&
291
+ ts.isIdentifier(node.name) &&
292
+ node.initializer &&
293
+ ts.isArrowFunction(node.initializer)) {
294
+ const name = node.name.text;
295
+ if (isUtilityFunction(name, domainSpecific)) {
296
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
297
+ const body = node.initializer.body.getText(sourceFile);
298
+ functions.push({
299
+ name,
300
+ line: line + 1,
301
+ file: filePath,
302
+ bodyHash: hashBody(body),
303
+ bodyLength: body.length,
304
+ });
305
+ }
306
+ }
307
+ ts.forEachChild(node, visit);
308
+ };
309
+ visit(sourceFile);
310
+ /* v8 ignore next 1 -- defensive catch: parse failures already handled */
311
+ }
312
+ catch {
313
+ // @swallow-ok Ignore parse errors
314
+ }
315
+ return functions;
316
+ }
317
+ /**
318
+ * Create a violation for identical implementations
319
+ */
320
+ function createIdenticalViolation(name, locations) {
321
+ const first = locations[0];
322
+ if (!first) {
323
+ throw new Error(`createIdenticalViolation called with empty locations array for '${name}'`);
324
+ }
325
+ const otherFilesStr = formatOtherFiles(locations);
326
+ return {
327
+ line: first.line,
328
+ message: `Utility function '${name}' has identical implementation in ${locations.length} locations`,
329
+ severity: 'warning',
330
+ suggestion: `Move '${name}' to packages/shared/backend/foundation/utils/ or a relevant domain utils module. Also in: ${otherFilesStr}`,
331
+ type: 'duplicate-utility-identical',
332
+ match: name,
333
+ filePath: first.file,
334
+ };
335
+ }
336
+ /**
337
+ * Create a violation for similar implementations (same name, different body)
338
+ */
339
+ function createSimilarViolation(name, uniqueImpls) {
340
+ const first = uniqueImpls[0];
341
+ if (!first) {
342
+ throw new Error(`createSimilarViolation called with empty uniqueImpls array for '${name}'`);
343
+ }
344
+ const otherFilesStr = formatOtherFiles(uniqueImpls);
345
+ const numImplementations = uniqueImpls.length;
346
+ return {
347
+ line: first.line,
348
+ message: `Utility function '${name}' has ${numImplementations} different implementations - consider consolidation with options`,
349
+ severity: 'warning',
350
+ suggestion: `Create a unified '${name}' function with configurable options in packages/shared/backend/foundation/utils/. Different implementations found in: ${otherFilesStr}`,
351
+ type: 'duplicate-utility-similar',
352
+ match: name,
353
+ filePath: first.file,
354
+ };
355
+ }
356
+ async function collectFunctionsFromFiles(files, domainSpecific) {
357
+ const functionsByName = new Map();
358
+ for (const filePath of files.paths) {
359
+ try {
360
+ // @fitness-ignore-next-line performance-anti-patterns -- sequential file reading to control memory; FileAccessor is lazy
361
+ const content = await files.read(filePath);
362
+ const functions = extractUtilityFunctionsWithBody(filePath, content, domainSpecific);
363
+ const validFunctions = functions.filter((fn) => fn.bodyLength >= MIN_FUNCTION_BODY_LENGTH);
364
+ for (const fn of validFunctions) {
365
+ void addFunctionToCollection(functionsByName, fn);
366
+ }
367
+ /* v8 ignore next 1 -- defensive catch: parse failures already handled */
368
+ }
369
+ catch {
370
+ // @swallow-ok Skip unreadable files
371
+ }
372
+ }
373
+ return functionsByName;
374
+ }
375
+ function findIdenticalViolations(name, hashGroups) {
376
+ const violations = [];
377
+ for (const locations of hashGroups.values()) {
378
+ if (isValidCrossDirectoryDuplicate(locations)) {
379
+ violations.push(createIdenticalViolation(name, locations));
380
+ }
381
+ }
382
+ return violations;
383
+ }
384
+ function findSimilarViolation(name, hashGroups) {
385
+ if (hashGroups.size <= 1) {
386
+ return null;
387
+ }
388
+ const uniqueImpls = getFirstFromEachHashGroup(hashGroups);
389
+ /* v8 ignore next -- defensive guard */
390
+ if (!Array.isArray(uniqueImpls) || uniqueImpls.length <= 1) {
391
+ return null;
392
+ }
393
+ const implDirs = getUniqueDirectories(uniqueImpls);
394
+ if (implDirs.size <= 1) {
395
+ return null;
396
+ }
397
+ return createSimilarViolation(name, uniqueImpls);
398
+ }
399
+ function processFunctionGroup(name, hashGroups) {
400
+ const allLocations = flattenHashGroups(hashGroups);
401
+ const dirs = getUniqueDirectories(allLocations);
402
+ if (dirs.size <= 1 || hashGroups.size === 0) {
403
+ return [];
404
+ }
405
+ const violations = [...findIdenticalViolations(name, hashGroups)];
406
+ const similarViolation = findSimilarViolation(name, hashGroups);
407
+ if (similarViolation) {
408
+ violations.push(similarViolation);
409
+ }
410
+ return violations;
411
+ }
412
+ /**
413
+ * Check: quality/duplicate-utility-functions
414
+ *
415
+ * Detects utility functions that should be consolidated across the codebase.
416
+ * Reports two types of issues:
417
+ * - IDENTICAL: Same name, same implementation (true duplicates)
418
+ * - SIMILAR: Same name, different implementation (consolidation opportunities)
419
+ */
420
+ export const duplicateUtilityFunctions = defineCheck({
421
+ id: 'aa303a1e-f3f8-4a11-ade2-9e29af89c299',
422
+ slug: 'duplicate-utility-functions',
423
+ scope: { languages: ['typescript'], concerns: ['backend', 'frontend', 'cli'] },
424
+ contentFilter: 'strip-strings',
425
+ confidence: 'high',
426
+ description: 'Detect duplicate and similar utility functions',
427
+ longDescription: `**Purpose:** Detects utility functions that are duplicated or similarly named across the codebase, flagging consolidation opportunities into shared packages.
428
+
429
+ **Detects:** Cross-file analysis using TypeScript AST extraction and SHA-256 body hashing.
430
+ - **Identical duplicates:** Same-named utility functions with identical normalized bodies in different directories
431
+ - **Similar implementations:** Same-named utility functions with different bodies across directories (consolidation with options pattern)
432
+ - Targets functions matching utility name patterns: \`format*\`, \`parse*\`, \`is*\`, \`has*\`, \`to*\`, \`get*\`, \`validate*\`, \`sanitize*\`, \`normalize*\`, \`debounce\`, \`throttle\`, \`sleep\`, \`retry\`, etc.
433
+ - Skips domain-specific functions listed in \`DOMAIN_SPECIFIC_FUNCTIONS\` and bodies under 50 characters
434
+
435
+ **Why it matters:** Duplicated utilities create maintenance risk and inconsistent behavior. A single shared implementation in \`foundation/utils\` ensures consistent behavior and reduces code volume.
436
+
437
+ **Scope:** General best practice`,
438
+ tags: ['quality', 'dry', 'utilities', 'duplication'],
439
+ fileTypes: ['ts'],
440
+ async analyzeAll(files) {
441
+ const domainSpecific = buildEffectiveDomainSpecificSet();
442
+ const functionsByName = await collectFunctionsFromFiles(files, domainSpecific);
443
+ const violations = [];
444
+ for (const [name, hashGroups] of functionsByName) {
445
+ // @fitness-ignore-next-line performance-anti-patterns -- spread aggregates small violation arrays from pure function
446
+ violations.push(...processFunctionGroup(name, hashGroups));
447
+ }
448
+ return violations;
449
+ },
450
+ });
451
+ //# sourceMappingURL=duplicate-utility-functions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-utility-functions.js","sourceRoot":"","sources":["../../../../src/checks/quality/code-structure/duplicate-utility-functions.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,iGAAiG;AACjG,8KAA8K;AAC9K;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EACL,WAAW,EACX,cAAc,GAGf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAiBjC;;GAEG;AACH,MAAM,gBAAgB,GAAG;IACvB,cAAc,EAAE,6BAA6B;IAC7C,aAAa,EAAE,uBAAuB;IACtC,UAAU,EAAE,mBAAmB;IAC/B,WAAW,EAAE,mBAAmB;IAChC,UAAU,EAAE,qBAAqB;IACjC,WAAW,EAAE,uBAAuB;IACpC,gBAAgB,EAAE,+BAA+B;IACjD,gBAAgB,EAAE,8BAA8B;IAChD,iBAAiB,EAAE,8BAA8B;IACjD,WAAW;IACX,WAAW;IACX,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,UAAU;CACX,CAAC;AAEF;;;GAGG;AACH,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAEpC;;;;;;;;;;GAUG;AACH,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC;IACxC,mEAAmE;IACnE,WAAW;IACX,kBAAkB;IAClB,cAAc;IACd,YAAY;IACZ,6BAA6B;IAC7B,WAAW;IACX,8DAA8D;IAC9D,eAAe;IACf,eAAe;IACf,eAAe;IACf,YAAY;IACZ,cAAc;IACd,2DAA2D;IAC3D,YAAY;IACZ,iBAAiB;IACjB,WAAW;IACX,iCAAiC;IACjC,gBAAgB;IAChB,gBAAgB;IAChB,8BAA8B;IAC9B,iBAAiB;IACjB,6EAA6E;IAC7E,0EAA0E;IAC1E,kEAAkE;IAClE,kBAAkB;IAClB,gBAAgB;IAChB,qBAAqB;IACrB,eAAe;IACf,wBAAwB;IACxB,kFAAkF;IAClF,gBAAgB;IAChB,sEAAsE;IACtE,wEAAwE;IACxE,8EAA8E;IAC9E,qBAAqB;IACrB,cAAc;IACd,4EAA4E;IAC5E,aAAa;IACb,yEAAyE;IACzE,oBAAoB;CACrB,CAAC,CAAC;AAEH;;;GAGG;AACH,SAAS,+BAA+B;IACtC,MAAM,GAAG,GAAG,cAAc,CAAkC,6BAA6B,CAAC,CAAC;IAC3F,IACE,CAAC,GAAG,CAAC,iCAAiC;QACtC,GAAG,CAAC,iCAAiC,CAAC,MAAM,KAAK,CAAC,EAClD,CAAC;QACD,OAAO,yBAAyB,CAAC;IACnC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,yBAAyB,CAAC,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,iCAAiC;QAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,MAAM,CAAC;AAChB,CAAC;AAaD;;GAEG;AACH,SAAS,oBAAoB,CAAC,SAAyB;IACrD,uCAAuC;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,GAAG,EAAE,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,UAAuC;IAChE,MAAM,YAAY,GAAmB,EAAE,CAAC;IACxC,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CAAC,UAAuC;IACxE,MAAM,WAAW,GAAmB,EAAE,CAAC;IACvC,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5C,uCAAuC;QACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxD,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,KAAK,EAAE,CAAC;YACV,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,SAAyB;IACjD,MAAM,UAAU,GAAG,SAAS;SACzB,KAAK,CAAC,CAAC,CAAC;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SAC5B,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACf,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,SAAS,EAAE,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,eAAgC,EAAE,EAAgB;IACjF,IAAI,SAAS,GAAG,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,EAAE,CAAC;QACf,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IAED,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAS,8BAA8B,CAAC,SAAyB;IAC/D,uCAAuC;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACvD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,YAAY,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACrD,OAAO,YAAY,CAAC,IAAI,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,IAAY;IAC5C,OAAO,IAAI;SACR,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IAClE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,uBAAuB,CAAC,IAAY;IAC3C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3C,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;gBACpB,MAAM;YACR,CAAC;YACD,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,EAAE,CAAC;QACN,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,UAAU,GAAG,wBAAwB,CAAC,UAAU,CAAC,CAAC;IAClD,UAAU,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;IACjD,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChD,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAC/B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,IAAY,EAAE,cAAmC;IAC1E,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,SAAS,+BAA+B,CACtC,QAAgB,EAChB,OAAe,EACf,cAAmC;IAEnC,MAAM,SAAS,GAAmB,EAAE,CAAC;IAErC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC1D,uCAAuC;QACvC,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,CAAC;QAE3B,MAAM,KAAK,GAAG,CAAC,IAAa,EAAE,EAAE;YAC9B,8BAA8B;YAC9B,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC5B,IAAI,iBAAiB,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,CAAC;oBAC5C,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBAC3C,SAAS,CAAC,IAAI,CAAC;wBACb,IAAI;wBACJ,IAAI,EAAE,IAAI,GAAG,CAAC;wBACd,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC;wBACxB,UAAU,EAAE,IAAI,CAAC,MAAM;qBACxB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,8CAA8C;YAC9C,IACE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC;gBAC9B,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC1B,IAAI,CAAC,WAAW;gBAChB,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,EACpC,CAAC;gBACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC5B,IAAI,iBAAiB,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,CAAC;oBAC5C,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBACvD,SAAS,CAAC,IAAI,CAAC;wBACb,IAAI;wBACJ,IAAI,EAAE,IAAI,GAAG,CAAC;wBACd,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC;wBACxB,UAAU,EAAE,IAAI,CAAC,MAAM;qBACxB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC,CAAC;QAEF,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,yEAAyE;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,IAAY,EAAE,SAAyB;IACvE,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mEAAmE,IAAI,GAAG,CAAC,CAAC;IAC9F,CAAC;IACD,MAAM,aAAa,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAElD,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,qBAAqB,IAAI,qCAAqC,SAAS,CAAC,MAAM,YAAY;QACnG,QAAQ,EAAE,SAAS;QACnB,UAAU,EAAE,SAAS,IAAI,8FAA8F,aAAa,EAAE;QACtI,IAAI,EAAE,6BAA6B;QACnC,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,KAAK,CAAC,IAAI;KACrB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,IAAY,EAAE,WAA2B;IACvE,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mEAAmE,IAAI,GAAG,CAAC,CAAC;IAC9F,CAAC;IACD,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,kBAAkB,GAAG,WAAW,CAAC,MAAM,CAAC;IAE9C,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,qBAAqB,IAAI,SAAS,kBAAkB,kEAAkE;QAC/H,QAAQ,EAAE,SAAS;QACnB,UAAU,EAAE,qBAAqB,IAAI,0HAA0H,aAAa,EAAE;QAC9K,IAAI,EAAE,2BAA2B;QACjC,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,KAAK,CAAC,IAAI;KACrB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,yBAAyB,CACtC,KAAmB,EACnB,cAAmC;IAEnC,MAAM,eAAe,GAAoB,IAAI,GAAG,EAAE,CAAC;IAEnD,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,yHAAyH;YACzH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,SAAS,GAAG,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;YACrF,MAAM,cAAc,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,IAAI,wBAAwB,CAAC,CAAC;YAE3F,KAAK,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC;gBAChC,KAAK,uBAAuB,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,yEAAyE;QAC3E,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,SAAS,uBAAuB,CAC9B,IAAY,EACZ,UAAuC;IAEvC,MAAM,UAAU,GAAqB,EAAE,CAAC;IAExC,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5C,IAAI,8BAA8B,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9C,UAAU,CAAC,IAAI,CAAC,wBAAwB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,oBAAoB,CAC3B,IAAY,EACZ,UAAuC;IAEvC,IAAI,UAAU,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;IAC1D,uCAAuC;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,QAAQ,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,sBAAsB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,oBAAoB,CAC3B,IAAY,EACZ,UAAuC;IAEvC,MAAM,YAAY,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAEhD,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAqB,CAAC,GAAG,uBAAuB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAEpF,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAChE,IAAI,gBAAgB,EAAE,CAAC;QACrB,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,WAAW,CAAC;IACnD,EAAE,EAAE,sCAAsC;IAC1C,IAAI,EAAE,6BAA6B;IACnC,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE;IAC9E,aAAa,EAAE,eAAe;IAE9B,UAAU,EAAE,MAAM;IAClB,WAAW,EAAE,gDAAgD;IAC7D,eAAe,EAAE;;;;;;;;;;iCAUc;IAC/B,IAAI,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,CAAC;IACpD,SAAS,EAAE,CAAC,IAAI,CAAC;IAEjB,KAAK,CAAC,UAAU,CAAC,KAAmB;QAClC,MAAM,cAAc,GAAG,+BAA+B,EAAE,CAAC;QACzD,MAAM,eAAe,GAAG,MAAM,yBAAyB,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;QAC/E,MAAM,UAAU,GAAqB,EAAE,CAAC;QAExC,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,eAAe,EAAE,CAAC;YACjD,qHAAqH;YACrH,UAAU,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './duplicate-utility-functions.js';
2
+ export * from './no-any-types.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/code-structure/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAC;AACjD,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './duplicate-utility-functions.js';
2
+ export * from './no-any-types.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/checks/quality/code-structure/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAC;AACjD,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview No Any Types Check
3
+ *
4
+ * Detects usage of 'any' type in TypeScript code. The 'any' type bypasses
5
+ * type checking and should be replaced with 'unknown' with proper type narrowing.
6
+ */
7
+ /**
8
+ * Check: quality/no-any-types
9
+ *
10
+ * Detects usage of any type - use unknown with type narrowing instead.
11
+ */
12
+ export declare const noAnyTypes: import("@opensip-cli/fitness").Check;
13
+ //# sourceMappingURL=no-any-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-any-types.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/code-structure/no-any-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwFH;;;;GAIG;AACH,eAAO,MAAM,UAAU,sCA2BrB,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @fileoverview No Any Types Check
3
+ *
4
+ * Detects usage of 'any' type in TypeScript code. The 'any' type bypasses
5
+ * type checking and should be replaced with 'unknown' with proper type narrowing.
6
+ */
7
+ import { defineCheck } from '@opensip-cli/fitness';
8
+ import { getSharedSourceFile } from '@opensip-cli/lang-typescript';
9
+ import * as ts from 'typescript';
10
+ /**
11
+ * Quick filter keywords for 'any' type patterns
12
+ */
13
+ const QUICK_FILTER_KEYWORDS = [': any', 'any)', 'any,', 'any;', '<any', 'any>'];
14
+ /**
15
+ * Find lines with eslint-disable-next-line comments
16
+ */
17
+ function findDisabledLines(content) {
18
+ const disabledLines = new Set();
19
+ const lines = content.split('\n');
20
+ for (const [i, line] of lines.entries()) {
21
+ if (line?.includes('eslint-disable-next-line')) {
22
+ disabledLines.add(i + 1);
23
+ }
24
+ }
25
+ return disabledLines;
26
+ }
27
+ /**
28
+ * Analyze a file for 'any' type usage
29
+ */
30
+ function analyzeFile(content, filePath) {
31
+ const violations = [];
32
+ const disabledLines = findDisabledLines(content);
33
+ try {
34
+ const sourceFile = getSharedSourceFile(filePath, content);
35
+ /* v8 ignore next -- defensive guard */
36
+ if (!sourceFile)
37
+ return violations;
38
+ const visit = (node) => {
39
+ if (node.kind === ts.SyntaxKind.AnyKeyword) {
40
+ const parent = node.parent;
41
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
42
+ // Skip if this line has an eslint-disable-next-line comment
43
+ if (disabledLines.has(line)) {
44
+ ts.forEachChild(node, visit);
45
+ return;
46
+ }
47
+ // Get context from parent
48
+ let context = 'type annotation';
49
+ if (ts.isParameter(parent)) {
50
+ context = 'function parameter';
51
+ }
52
+ else if (ts.isVariableDeclaration(parent)) {
53
+ context = 'variable declaration';
54
+ }
55
+ else if (ts.isPropertySignature(parent) || ts.isPropertyDeclaration(parent)) {
56
+ context = 'property';
57
+ }
58
+ else if (ts.isFunctionDeclaration(parent) || ts.isMethodDeclaration(parent)) {
59
+ context = 'return type';
60
+ }
61
+ else if (ts.isTypeAliasDeclaration(parent)) {
62
+ context = 'type alias';
63
+ }
64
+ violations.push({
65
+ line: line + 1,
66
+ column: character + 1,
67
+ message: `'any' type used in ${context}`,
68
+ severity: 'error',
69
+ type: 'any-type',
70
+ suggestion: "Replace 'any' with 'unknown' and add type narrowing with type guards or assertion functions.",
71
+ match: 'any',
72
+ filePath,
73
+ });
74
+ }
75
+ ts.forEachChild(node, visit);
76
+ };
77
+ visit(sourceFile);
78
+ /* v8 ignore next 1 -- defensive catch: parse failures already handled */
79
+ }
80
+ catch {
81
+ // @swallow-ok Skip files that fail to parse
82
+ }
83
+ return violations;
84
+ }
85
+ /**
86
+ * Check: quality/no-any-types
87
+ *
88
+ * Detects usage of any type - use unknown with type narrowing instead.
89
+ */
90
+ export const noAnyTypes = defineCheck({
91
+ id: '3d456769-bbcb-461f-8efd-e7b340dcb1b8',
92
+ slug: 'no-any-types',
93
+ scope: { languages: ['typescript'], concerns: ['backend', 'frontend', 'cli'] },
94
+ description: 'Detect usage of any type - use unknown with type narrowing instead',
95
+ longDescription: `**Purpose:** Detects usage of the \`any\` type in TypeScript code, which bypasses type checking and should be replaced with \`unknown\` plus proper type narrowing.
96
+
97
+ **Detects:** Analyzes each file individually using TypeScript AST traversal for \`AnyKeyword\` nodes.
98
+ - \`any\` in function parameters, variable declarations, property signatures, return types, and type aliases
99
+ - Respects \`eslint-disable-next-line\` comments on the preceding line
100
+ - Uses a quick-filter optimization: skips files not containing \`: any\`, \`any)\`, \`any,\`, \`any;\`, \`<any\`, or \`any>\`
101
+
102
+ **Why it matters:** The \`any\` type disables TypeScript's type safety, hiding bugs that would otherwise be caught at compile time. Using \`unknown\` with type guards preserves safety while handling dynamic data.
103
+
104
+ **Scope:** General best practice`,
105
+ tags: ['quality', 'type-safety', 'code-quality'],
106
+ fileTypes: ['ts', 'tsx'],
107
+ confidence: 'high',
108
+ analyze(content, filePath) {
109
+ // Quick filter: skip files without 'any' type patterns
110
+ if (!QUICK_FILTER_KEYWORDS.some((kw) => content.includes(kw))) {
111
+ return [];
112
+ }
113
+ return analyzeFile(content, filePath);
114
+ },
115
+ });
116
+ //# sourceMappingURL=no-any-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-any-types.js","sourceRoot":"","sources":["../../../../src/checks/quality/code-structure/no-any-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @fileoverview Regression tests for two `null-safety` FP fixes.
3
+ *
4
+ * 1. Cross-line guard: a property access guarded by an enclosing `if` / `&&`
5
+ * on a PREVIOUS line (e.g. `if (candidates.length === 1 && candidates[0])
6
+ * { … candidates[0].bodyHash … }`) was flagged because the safety scan was
7
+ * line-local. The fix walks enclosing conditions.
8
+ * 2. Immutable combinators: `.merge(...)` chained on a factory result
9
+ * (e.g. OTel `detectResources(...).merge(...)`) returns a non-null value
10
+ * and is now a known-safe fluent method.
11
+ *
12
+ * Genuine unguarded access on a call/element result must still fire.
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=null-safety-fp.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"null-safety-fp.test.d.ts","sourceRoot":"","sources":["../../../../../src/checks/quality/data-integrity/__tests__/null-safety-fp.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}