@nielspeter/eess-ts 0.2.1 → 0.5.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 (467) hide show
  1. package/README.md +61 -35
  2. package/dist/builders/call-rule-builder.d.ts +66 -26
  3. package/dist/builders/call-rule-builder.d.ts.map +1 -1
  4. package/dist/builders/call-rule-builder.js +75 -32
  5. package/dist/builders/call-rule-builder.js.map +1 -1
  6. package/dist/builders/class-rule-builder.d.ts +125 -26
  7. package/dist/builders/class-rule-builder.d.ts.map +1 -1
  8. package/dist/builders/class-rule-builder.js +141 -55
  9. package/dist/builders/class-rule-builder.js.map +1 -1
  10. package/dist/builders/correspondence-builder.d.ts +287 -0
  11. package/dist/builders/correspondence-builder.d.ts.map +1 -0
  12. package/dist/builders/correspondence-builder.js +385 -0
  13. package/dist/builders/correspondence-builder.js.map +1 -0
  14. package/dist/builders/correspondence-findings.d.ts +104 -0
  15. package/dist/builders/correspondence-findings.d.ts.map +1 -0
  16. package/dist/builders/correspondence-findings.js +301 -0
  17. package/dist/builders/correspondence-findings.js.map +1 -0
  18. package/dist/builders/cross-layer-builder.d.ts +99 -16
  19. package/dist/builders/cross-layer-builder.d.ts.map +1 -1
  20. package/dist/builders/cross-layer-builder.js +182 -35
  21. package/dist/builders/cross-layer-builder.js.map +1 -1
  22. package/dist/builders/function-rule-builder.d.ts +144 -25
  23. package/dist/builders/function-rule-builder.d.ts.map +1 -1
  24. package/dist/builders/function-rule-builder.js +177 -39
  25. package/dist/builders/function-rule-builder.js.map +1 -1
  26. package/dist/builders/jsx-rule-builder.d.ts +70 -14
  27. package/dist/builders/jsx-rule-builder.d.ts.map +1 -1
  28. package/dist/builders/jsx-rule-builder.js +73 -14
  29. package/dist/builders/jsx-rule-builder.js.map +1 -1
  30. package/dist/builders/module-rule-builder.d.ts +2 -2
  31. package/dist/builders/module-rule-builder.d.ts.map +1 -1
  32. package/dist/builders/module-rule-builder.js +16 -19
  33. package/dist/builders/module-rule-builder.js.map +1 -1
  34. package/dist/builders/scoped-function-rule-builder.d.ts +11 -0
  35. package/dist/builders/scoped-function-rule-builder.d.ts.map +1 -1
  36. package/dist/builders/scoped-function-rule-builder.js +12 -0
  37. package/dist/builders/scoped-function-rule-builder.js.map +1 -1
  38. package/dist/builders/slice-discovery-message.d.ts +36 -0
  39. package/dist/builders/slice-discovery-message.d.ts.map +1 -0
  40. package/dist/builders/slice-discovery-message.js +181 -0
  41. package/dist/builders/slice-discovery-message.js.map +1 -0
  42. package/dist/builders/slice-rule-builder.d.ts +151 -12
  43. package/dist/builders/slice-rule-builder.d.ts.map +1 -1
  44. package/dist/builders/slice-rule-builder.js +243 -35
  45. package/dist/builders/slice-rule-builder.js.map +1 -1
  46. package/dist/builders/type-rule-builder.d.ts +95 -24
  47. package/dist/builders/type-rule-builder.d.ts.map +1 -1
  48. package/dist/builders/type-rule-builder.js +109 -42
  49. package/dist/builders/type-rule-builder.js.map +1 -1
  50. package/dist/{helpers → builders}/within.d.ts +2 -2
  51. package/dist/builders/within.d.ts.map +1 -0
  52. package/dist/{helpers → builders}/within.js +1 -1
  53. package/dist/builders/within.js.map +1 -0
  54. package/dist/cli/commands/baseline.d.ts +8 -3
  55. package/dist/cli/commands/baseline.d.ts.map +1 -1
  56. package/dist/cli/commands/baseline.js +111 -7
  57. package/dist/cli/commands/baseline.js.map +1 -1
  58. package/dist/cli/commands/check.d.ts +7 -2
  59. package/dist/cli/commands/check.d.ts.map +1 -1
  60. package/dist/cli/commands/check.js +385 -47
  61. package/dist/cli/commands/check.js.map +1 -1
  62. package/dist/cli/commands/doctor.d.ts +29 -0
  63. package/dist/cli/commands/doctor.d.ts.map +1 -0
  64. package/dist/cli/commands/doctor.js +224 -0
  65. package/dist/cli/commands/doctor.js.map +1 -0
  66. package/dist/cli/commands/explain.d.ts +2 -1
  67. package/dist/cli/commands/explain.d.ts.map +1 -1
  68. package/dist/cli/commands/explain.js +19 -10
  69. package/dist/cli/commands/explain.js.map +1 -1
  70. package/dist/cli/commands/init.d.ts +9 -3
  71. package/dist/cli/commands/init.d.ts.map +1 -1
  72. package/dist/cli/commands/init.js +162 -137
  73. package/dist/cli/commands/init.js.map +1 -1
  74. package/dist/cli/import-rule-module.d.ts +29 -0
  75. package/dist/cli/import-rule-module.d.ts.map +1 -0
  76. package/dist/cli/import-rule-module.js +47 -0
  77. package/dist/cli/import-rule-module.js.map +1 -0
  78. package/dist/cli/index.d.ts +2 -2
  79. package/dist/cli/index.d.ts.map +1 -1
  80. package/dist/cli/index.js +101 -43
  81. package/dist/cli/index.js.map +1 -1
  82. package/dist/cli/load-rules.d.ts +6 -11
  83. package/dist/cli/load-rules.d.ts.map +1 -1
  84. package/dist/cli/load-rules.js +25 -32
  85. package/dist/cli/load-rules.js.map +1 -1
  86. package/dist/cli/resolve-config.d.ts +0 -8
  87. package/dist/cli/resolve-config.d.ts.map +1 -1
  88. package/dist/cli/resolve-config.js +25 -55
  89. package/dist/cli/resolve-config.js.map +1 -1
  90. package/dist/cli/rule-file-findings.d.ts +178 -0
  91. package/dist/cli/rule-file-findings.d.ts.map +1 -0
  92. package/dist/cli/rule-file-findings.js +351 -0
  93. package/dist/cli/rule-file-findings.js.map +1 -0
  94. package/dist/cli/watch.d.ts +17 -34
  95. package/dist/cli/watch.d.ts.map +1 -1
  96. package/dist/cli/watch.js +14 -129
  97. package/dist/cli/watch.js.map +1 -1
  98. package/dist/conditions/body-analysis-function.d.ts.map +1 -1
  99. package/dist/conditions/body-analysis-function.js +16 -7
  100. package/dist/conditions/body-analysis-function.js.map +1 -1
  101. package/dist/conditions/body-analysis-module.d.ts.map +1 -1
  102. package/dist/conditions/body-analysis-module.js +14 -9
  103. package/dist/conditions/body-analysis-module.js.map +1 -1
  104. package/dist/conditions/body-analysis.d.ts.map +1 -1
  105. package/dist/conditions/body-analysis.js +16 -7
  106. package/dist/conditions/body-analysis.js.map +1 -1
  107. package/dist/conditions/call.d.ts.map +1 -1
  108. package/dist/conditions/call.js +41 -19
  109. package/dist/conditions/call.js.map +1 -1
  110. package/dist/conditions/catch-analysis.d.ts +2 -1
  111. package/dist/conditions/catch-analysis.d.ts.map +1 -1
  112. package/dist/conditions/catch-analysis.js.map +1 -1
  113. package/dist/conditions/cross-layer.d.ts +2 -10
  114. package/dist/conditions/cross-layer.d.ts.map +1 -1
  115. package/dist/conditions/cross-layer.js +227 -23
  116. package/dist/conditions/cross-layer.js.map +1 -1
  117. package/dist/conditions/dependency.d.ts +35 -6
  118. package/dist/conditions/dependency.d.ts.map +1 -1
  119. package/dist/conditions/dependency.js +299 -45
  120. package/dist/conditions/dependency.js.map +1 -1
  121. package/dist/conditions/exports.d.ts.map +1 -1
  122. package/dist/conditions/exports.js +41 -37
  123. package/dist/conditions/exports.js.map +1 -1
  124. package/dist/conditions/function.d.ts.map +1 -1
  125. package/dist/conditions/function.js +19 -5
  126. package/dist/conditions/function.js.map +1 -1
  127. package/dist/conditions/helpers.d.ts +2 -1
  128. package/dist/conditions/helpers.d.ts.map +1 -1
  129. package/dist/conditions/helpers.js +4 -1
  130. package/dist/conditions/helpers.js.map +1 -1
  131. package/dist/conditions/jsx.d.ts.map +1 -1
  132. package/dist/conditions/jsx.js +7 -2
  133. package/dist/conditions/jsx.js.map +1 -1
  134. package/dist/conditions/match-identity.d.ts +43 -0
  135. package/dist/conditions/match-identity.d.ts.map +1 -0
  136. package/dist/conditions/match-identity.js +43 -0
  137. package/dist/conditions/match-identity.js.map +1 -0
  138. package/dist/conditions/members.d.ts.map +1 -1
  139. package/dist/conditions/members.js +20 -6
  140. package/dist/conditions/members.js.map +1 -1
  141. package/dist/conditions/reverse-dependency.d.ts +18 -5
  142. package/dist/conditions/reverse-dependency.d.ts.map +1 -1
  143. package/dist/conditions/reverse-dependency.js +155 -93
  144. package/dist/conditions/reverse-dependency.js.map +1 -1
  145. package/dist/conditions/slice.d.ts +86 -2
  146. package/dist/conditions/slice.d.ts.map +1 -1
  147. package/dist/conditions/slice.js +234 -53
  148. package/dist/conditions/slice.js.map +1 -1
  149. package/dist/conditions/structural.d.ts.map +1 -1
  150. package/dist/conditions/structural.js +16 -4
  151. package/dist/conditions/structural.js.map +1 -1
  152. package/dist/core/check-all.d.ts +19 -0
  153. package/dist/core/check-all.d.ts.map +1 -0
  154. package/dist/core/check-all.js +150 -0
  155. package/dist/core/check-all.js.map +1 -0
  156. package/dist/core/combinators.d.ts +48 -0
  157. package/dist/core/combinators.d.ts.map +1 -0
  158. package/dist/core/combinators.js +49 -0
  159. package/dist/core/combinators.js.map +1 -0
  160. package/dist/core/descendant-cache.d.ts +36 -0
  161. package/dist/core/descendant-cache.d.ts.map +1 -0
  162. package/dist/core/descendant-cache.js +117 -0
  163. package/dist/core/descendant-cache.js.map +1 -0
  164. package/dist/core/diagnose.d.ts +234 -0
  165. package/dist/core/diagnose.d.ts.map +1 -0
  166. package/dist/core/diagnose.js +390 -0
  167. package/dist/core/diagnose.js.map +1 -0
  168. package/dist/core/disk-set.d.ts +48 -0
  169. package/dist/core/disk-set.d.ts.map +1 -0
  170. package/dist/core/disk-set.js +218 -0
  171. package/dist/core/disk-set.js.map +1 -0
  172. package/dist/core/element-cache.d.ts +75 -0
  173. package/dist/core/element-cache.d.ts.map +1 -0
  174. package/dist/core/element-cache.js +32 -0
  175. package/dist/core/element-cache.js.map +1 -0
  176. package/dist/core/empty-project-advice.d.ts +75 -0
  177. package/dist/core/empty-project-advice.d.ts.map +1 -0
  178. package/dist/core/empty-project-advice.js +61 -0
  179. package/dist/core/empty-project-advice.js.map +1 -0
  180. package/dist/core/exclusion-comments.d.ts +19 -0
  181. package/dist/core/exclusion-comments.d.ts.map +1 -0
  182. package/dist/core/exclusion-comments.js +7 -0
  183. package/dist/core/exclusion-comments.js.map +1 -0
  184. package/dist/core/execute-rule.d.ts +158 -0
  185. package/dist/core/execute-rule.d.ts.map +1 -0
  186. package/dist/core/execute-rule.js +666 -0
  187. package/dist/core/execute-rule.js.map +1 -0
  188. package/dist/core/glob-diagnosis.d.ts +78 -0
  189. package/dist/core/glob-diagnosis.d.ts.map +1 -0
  190. package/dist/core/glob-diagnosis.js +134 -0
  191. package/dist/core/glob-diagnosis.js.map +1 -0
  192. package/dist/core/glob-evaluator.d.ts +48 -0
  193. package/dist/core/glob-evaluator.d.ts.map +1 -0
  194. package/dist/core/glob-evaluator.js +89 -0
  195. package/dist/core/glob-evaluator.js.map +1 -0
  196. package/dist/core/import-candidates.d.ts +60 -0
  197. package/dist/core/import-candidates.d.ts.map +1 -0
  198. package/dist/core/import-candidates.js +93 -0
  199. package/dist/core/import-candidates.js.map +1 -0
  200. package/dist/core/import-options.d.ts +55 -2
  201. package/dist/core/import-options.d.ts.map +1 -1
  202. package/dist/core/import-options.js +64 -0
  203. package/dist/core/import-options.js.map +1 -1
  204. package/dist/core/invalidate-on-modify.d.ts +20 -0
  205. package/dist/core/invalidate-on-modify.d.ts.map +1 -0
  206. package/dist/core/invalidate-on-modify.js +26 -0
  207. package/dist/core/invalidate-on-modify.js.map +1 -0
  208. package/dist/core/line-index.d.ts +3 -0
  209. package/dist/core/line-index.d.ts.map +1 -0
  210. package/dist/core/line-index.js +227 -0
  211. package/dist/core/line-index.js.map +1 -0
  212. package/dist/core/metric-violation.d.ts +86 -0
  213. package/dist/core/metric-violation.d.ts.map +1 -0
  214. package/dist/core/metric-violation.js +132 -0
  215. package/dist/core/metric-violation.js.map +1 -0
  216. package/dist/core/module-edges.d.ts +361 -0
  217. package/dist/core/module-edges.d.ts.map +1 -0
  218. package/dist/core/module-edges.js +593 -0
  219. package/dist/core/module-edges.js.map +1 -0
  220. package/dist/core/object-literal-functions.d.ts +42 -0
  221. package/dist/core/object-literal-functions.d.ts.map +1 -0
  222. package/dist/core/object-literal-functions.js +75 -0
  223. package/dist/core/object-literal-functions.js.map +1 -0
  224. package/dist/core/orphan-exclusions.d.ts +44 -0
  225. package/dist/core/orphan-exclusions.d.ts.map +1 -0
  226. package/dist/core/orphan-exclusions.js +224 -0
  227. package/dist/core/orphan-exclusions.js.map +1 -0
  228. package/dist/core/pair-condition.d.ts +32 -3
  229. package/dist/core/pair-condition.d.ts.map +1 -1
  230. package/dist/core/path-universe.d.ts +22 -0
  231. package/dist/core/path-universe.d.ts.map +1 -0
  232. package/dist/core/path-universe.js +45 -0
  233. package/dist/core/path-universe.js.map +1 -0
  234. package/dist/core/per-root-compiler-options.d.ts +20 -0
  235. package/dist/core/per-root-compiler-options.d.ts.map +1 -0
  236. package/dist/core/per-root-compiler-options.js +57 -0
  237. package/dist/core/per-root-compiler-options.js.map +1 -0
  238. package/dist/core/project-relative.d.ts +98 -0
  239. package/dist/core/project-relative.d.ts.map +1 -0
  240. package/dist/core/project-relative.js +185 -0
  241. package/dist/core/project-relative.js.map +1 -0
  242. package/dist/core/project.d.ts +7 -1
  243. package/dist/core/project.d.ts.map +1 -1
  244. package/dist/core/project.js +29 -4
  245. package/dist/core/project.js.map +1 -1
  246. package/dist/core/rule-builder.d.ts +303 -0
  247. package/dist/core/rule-builder.d.ts.map +1 -0
  248. package/dist/core/rule-builder.js +463 -0
  249. package/dist/core/rule-builder.js.map +1 -0
  250. package/dist/core/rule-declaration.d.ts +32 -0
  251. package/dist/core/rule-declaration.d.ts.map +1 -0
  252. package/dist/core/rule-declaration.js +88 -0
  253. package/dist/core/rule-declaration.js.map +1 -0
  254. package/dist/core/terminal-builder.d.ts +629 -0
  255. package/dist/core/terminal-builder.d.ts.map +1 -0
  256. package/dist/core/terminal-builder.js +807 -0
  257. package/dist/core/terminal-builder.js.map +1 -0
  258. package/dist/core/terminal-execution.d.ts +39 -0
  259. package/dist/core/terminal-execution.d.ts.map +1 -0
  260. package/dist/core/terminal-execution.js +103 -0
  261. package/dist/core/terminal-execution.js.map +1 -0
  262. package/dist/core/ts-comment-mask.d.ts +2 -0
  263. package/dist/core/ts-comment-mask.d.ts.map +1 -0
  264. package/dist/core/ts-comment-mask.js +102 -0
  265. package/dist/core/ts-comment-mask.js.map +1 -0
  266. package/dist/core/vacuity-diagnosis.d.ts +103 -0
  267. package/dist/core/vacuity-diagnosis.d.ts.map +1 -0
  268. package/dist/core/vacuity-diagnosis.js +415 -0
  269. package/dist/core/vacuity-diagnosis.js.map +1 -0
  270. package/dist/core/violation.d.ts +16 -11
  271. package/dist/core/violation.d.ts.map +1 -1
  272. package/dist/core/violation.js +33 -13
  273. package/dist/core/violation.js.map +1 -1
  274. package/dist/graphql/graphql-rule-builder.d.ts +95 -0
  275. package/dist/graphql/graphql-rule-builder.d.ts.map +1 -0
  276. package/dist/graphql/graphql-rule-builder.js +127 -0
  277. package/dist/graphql/graphql-rule-builder.js.map +1 -0
  278. package/dist/graphql/index.d.ts +1 -0
  279. package/dist/graphql/index.d.ts.map +1 -1
  280. package/dist/graphql/index.js +2 -1
  281. package/dist/graphql/index.js.map +1 -1
  282. package/dist/graphql/resolver-rule-builder.d.ts +44 -23
  283. package/dist/graphql/resolver-rule-builder.d.ts.map +1 -1
  284. package/dist/graphql/resolver-rule-builder.js +80 -72
  285. package/dist/graphql/resolver-rule-builder.js.map +1 -1
  286. package/dist/graphql/schema-loader.d.ts +21 -0
  287. package/dist/graphql/schema-loader.d.ts.map +1 -1
  288. package/dist/graphql/schema-loader.js +82 -12
  289. package/dist/graphql/schema-loader.js.map +1 -1
  290. package/dist/graphql/schema-predicates.d.ts.map +1 -1
  291. package/dist/graphql/schema-predicates.js +17 -12
  292. package/dist/graphql/schema-predicates.js.map +1 -1
  293. package/dist/graphql/schema-rule-builder.d.ts +15 -24
  294. package/dist/graphql/schema-rule-builder.d.ts.map +1 -1
  295. package/dist/graphql/schema-rule-builder.js +39 -77
  296. package/dist/graphql/schema-rule-builder.js.map +1 -1
  297. package/dist/helpers/baseline-diagnostics.d.ts +45 -0
  298. package/dist/helpers/baseline-diagnostics.d.ts.map +1 -0
  299. package/dist/helpers/baseline-diagnostics.js +168 -0
  300. package/dist/helpers/baseline-diagnostics.js.map +1 -0
  301. package/dist/helpers/baseline-generator.d.ts +28 -0
  302. package/dist/helpers/baseline-generator.d.ts.map +1 -0
  303. package/dist/helpers/baseline-generator.js +27 -0
  304. package/dist/helpers/baseline-generator.js.map +1 -0
  305. package/dist/helpers/baseline.d.ts +336 -0
  306. package/dist/helpers/baseline.d.ts.map +1 -0
  307. package/dist/helpers/baseline.js +664 -0
  308. package/dist/helpers/baseline.js.map +1 -0
  309. package/dist/helpers/body-traversal.d.ts +27 -2
  310. package/dist/helpers/body-traversal.d.ts.map +1 -1
  311. package/dist/helpers/body-traversal.js +135 -16
  312. package/dist/helpers/body-traversal.js.map +1 -1
  313. package/dist/helpers/callback-extractor.d.ts.map +1 -1
  314. package/dist/helpers/callback-extractor.js +53 -84
  315. package/dist/helpers/callback-extractor.js.map +1 -1
  316. package/dist/helpers/complexity.d.ts +13 -5
  317. package/dist/helpers/complexity.d.ts.map +1 -1
  318. package/dist/helpers/complexity.js +15 -6
  319. package/dist/helpers/complexity.js.map +1 -1
  320. package/dist/helpers/diff-aware.d.ts +16 -0
  321. package/dist/helpers/diff-aware.d.ts.map +1 -0
  322. package/dist/helpers/diff-aware.js +16 -0
  323. package/dist/helpers/diff-aware.js.map +1 -0
  324. package/dist/helpers/matchers.d.ts +68 -7
  325. package/dist/helpers/matchers.d.ts.map +1 -1
  326. package/dist/helpers/matchers.js +164 -86
  327. package/dist/helpers/matchers.js.map +1 -1
  328. package/dist/helpers/slice-graph.d.ts +100 -8
  329. package/dist/helpers/slice-graph.d.ts.map +1 -1
  330. package/dist/helpers/slice-graph.js +126 -34
  331. package/dist/helpers/slice-graph.js.map +1 -1
  332. package/dist/helpers/tarjan.d.ts +0 -14
  333. package/dist/helpers/tarjan.d.ts.map +1 -1
  334. package/dist/helpers/tarjan.js +29 -16
  335. package/dist/helpers/tarjan.js.map +1 -1
  336. package/dist/index.d.ts +55 -19
  337. package/dist/index.d.ts.map +1 -1
  338. package/dist/index.js +67 -15
  339. package/dist/index.js.map +1 -1
  340. package/dist/models/arch-call.d.ts +3 -2
  341. package/dist/models/arch-call.d.ts.map +1 -1
  342. package/dist/models/arch-call.js.map +1 -1
  343. package/dist/models/arch-function.d.ts +54 -8
  344. package/dist/models/arch-function.d.ts.map +1 -1
  345. package/dist/models/arch-function.js +145 -8
  346. package/dist/models/arch-function.js.map +1 -1
  347. package/dist/models/arch-jsx-element.d.ts.map +1 -1
  348. package/dist/models/arch-jsx-element.js +18 -19
  349. package/dist/models/arch-jsx-element.js.map +1 -1
  350. package/dist/models/slice.d.ts +33 -9
  351. package/dist/models/slice.d.ts.map +1 -1
  352. package/dist/models/slice.js +129 -19
  353. package/dist/models/slice.js.map +1 -1
  354. package/dist/predicates/call.d.ts.map +1 -1
  355. package/dist/predicates/call.js +5 -0
  356. package/dist/predicates/call.js.map +1 -1
  357. package/dist/predicates/identity.d.ts +21 -0
  358. package/dist/predicates/identity.d.ts.map +1 -1
  359. package/dist/predicates/identity.js +75 -3
  360. package/dist/predicates/identity.js.map +1 -1
  361. package/dist/predicates/index.d.ts +1 -1
  362. package/dist/predicates/index.d.ts.map +1 -1
  363. package/dist/predicates/index.js +1 -1
  364. package/dist/predicates/index.js.map +1 -1
  365. package/dist/predicates/jsx.d.ts.map +1 -1
  366. package/dist/predicates/jsx.js +2 -1
  367. package/dist/predicates/jsx.js.map +1 -1
  368. package/dist/predicates/metrics.d.ts +4 -2
  369. package/dist/predicates/metrics.d.ts.map +1 -1
  370. package/dist/predicates/metrics.js +4 -2
  371. package/dist/predicates/metrics.js.map +1 -1
  372. package/dist/predicates/module.d.ts +4 -12
  373. package/dist/predicates/module.d.ts.map +1 -1
  374. package/dist/predicates/module.js +57 -37
  375. package/dist/predicates/module.js.map +1 -1
  376. package/dist/presets/agent-guardrails.d.ts +49 -16
  377. package/dist/presets/agent-guardrails.d.ts.map +1 -1
  378. package/dist/presets/agent-guardrails.js +308 -19
  379. package/dist/presets/agent-guardrails.js.map +1 -1
  380. package/dist/presets/boundaries.d.ts +46 -3
  381. package/dist/presets/boundaries.d.ts.map +1 -1
  382. package/dist/presets/boundaries.js +132 -23
  383. package/dist/presets/boundaries.js.map +1 -1
  384. package/dist/presets/data-layer.d.ts +23 -3
  385. package/dist/presets/data-layer.d.ts.map +1 -1
  386. package/dist/presets/data-layer.js +58 -16
  387. package/dist/presets/data-layer.js.map +1 -1
  388. package/dist/presets/index.d.ts +10 -5
  389. package/dist/presets/index.d.ts.map +1 -1
  390. package/dist/presets/index.js +19 -3
  391. package/dist/presets/index.js.map +1 -1
  392. package/dist/presets/layered.d.ts +47 -3
  393. package/dist/presets/layered.d.ts.map +1 -1
  394. package/dist/presets/layered.js +66 -29
  395. package/dist/presets/layered.js.map +1 -1
  396. package/dist/presets/recommended.d.ts +81 -10
  397. package/dist/presets/recommended.d.ts.map +1 -1
  398. package/dist/presets/recommended.js +25 -23
  399. package/dist/presets/recommended.js.map +1 -1
  400. package/dist/presets/shared.d.ts +267 -2
  401. package/dist/presets/shared.d.ts.map +1 -1
  402. package/dist/presets/shared.js +367 -4
  403. package/dist/presets/shared.js.map +1 -1
  404. package/dist/rules/code-quality.d.ts.map +1 -1
  405. package/dist/rules/code-quality.js +15 -6
  406. package/dist/rules/code-quality.js.map +1 -1
  407. package/dist/rules/hygiene.d.ts +1 -1
  408. package/dist/rules/hygiene.js +1 -1
  409. package/dist/rules/metrics-function.d.ts +2 -1
  410. package/dist/rules/metrics-function.d.ts.map +1 -1
  411. package/dist/rules/metrics-function.js +63 -39
  412. package/dist/rules/metrics-function.js.map +1 -1
  413. package/dist/rules/metrics.d.ts +2 -1
  414. package/dist/rules/metrics.d.ts.map +1 -1
  415. package/dist/rules/metrics.js +80 -47
  416. package/dist/rules/metrics.js.map +1 -1
  417. package/dist/smells/clusters.d.ts +78 -0
  418. package/dist/smells/clusters.d.ts.map +1 -0
  419. package/dist/smells/clusters.js +124 -0
  420. package/dist/smells/clusters.js.map +1 -0
  421. package/dist/smells/duplicate-bodies.d.ts +92 -5
  422. package/dist/smells/duplicate-bodies.d.ts.map +1 -1
  423. package/dist/smells/duplicate-bodies.js +203 -73
  424. package/dist/smells/duplicate-bodies.js.map +1 -1
  425. package/dist/smells/duplicate-report.d.ts +90 -0
  426. package/dist/smells/duplicate-report.d.ts.map +1 -0
  427. package/dist/smells/duplicate-report.js +344 -0
  428. package/dist/smells/duplicate-report.js.map +1 -0
  429. package/dist/smells/fingerprint.d.ts +20 -1
  430. package/dist/smells/fingerprint.d.ts.map +1 -1
  431. package/dist/smells/fingerprint.js +27 -3
  432. package/dist/smells/fingerprint.js.map +1 -1
  433. package/dist/smells/inconsistent-siblings.d.ts +128 -7
  434. package/dist/smells/inconsistent-siblings.d.ts.map +1 -1
  435. package/dist/smells/inconsistent-siblings.js +288 -86
  436. package/dist/smells/inconsistent-siblings.js.map +1 -1
  437. package/dist/smells/sibling-files.d.ts +47 -0
  438. package/dist/smells/sibling-files.d.ts.map +1 -0
  439. package/dist/smells/sibling-files.js +104 -0
  440. package/dist/smells/sibling-files.js.map +1 -0
  441. package/dist/smells/similar-pairs.d.ts +28 -0
  442. package/dist/smells/similar-pairs.d.ts.map +1 -0
  443. package/dist/smells/similar-pairs.js +134 -0
  444. package/dist/smells/similar-pairs.js.map +1 -0
  445. package/dist/smells/smell-builder.d.ts +57 -12
  446. package/dist/smells/smell-builder.d.ts.map +1 -1
  447. package/dist/smells/smell-builder.js +112 -14
  448. package/dist/smells/smell-builder.js.map +1 -1
  449. package/dist/smells/variation.d.ts +47 -0
  450. package/dist/smells/variation.d.ts.map +1 -0
  451. package/dist/smells/variation.js +90 -0
  452. package/dist/smells/variation.js.map +1 -0
  453. package/dist/tsconfig/index.d.ts +19 -0
  454. package/dist/tsconfig/index.d.ts.map +1 -0
  455. package/dist/tsconfig/index.js +20 -0
  456. package/dist/tsconfig/index.js.map +1 -0
  457. package/dist/tsconfig/tsconfig-builder.d.ts +42 -9
  458. package/dist/tsconfig/tsconfig-builder.d.ts.map +1 -1
  459. package/dist/tsconfig/tsconfig-builder.js +65 -16
  460. package/dist/tsconfig/tsconfig-builder.js.map +1 -1
  461. package/package.json +4 -2
  462. package/dist/core/module-dependencies.d.ts +0 -28
  463. package/dist/core/module-dependencies.d.ts.map +0 -1
  464. package/dist/core/module-dependencies.js +0 -35
  465. package/dist/core/module-dependencies.js.map +0 -1
  466. package/dist/helpers/within.d.ts.map +0 -1
  467. package/dist/helpers/within.js.map +0 -1
@@ -0,0 +1,47 @@
1
+ import type { SourceFile } from 'ts-morph';
2
+ import type { ArchProject } from '../core/project.js';
3
+ import type { ExpressionMatcher } from '../helpers/matchers.js';
4
+ /**
5
+ * Which files the sibling detector looks at, and how they are grouped.
6
+ *
7
+ * Separated from the builder because selecting and grouping files is not
8
+ * detecting inconsistency — the builder collects a fluent configuration and
9
+ * hands it here. The scope arrives as a value rather than being read off a
10
+ * builder, so the grouping is callable and testable without one.
11
+ */
12
+ export interface SiblingScope {
13
+ readonly project: ArchProject;
14
+ readonly folders: readonly string[];
15
+ readonly ignorePaths: readonly string[];
16
+ readonly ignoreTests: boolean;
17
+ readonly minLines: number;
18
+ readonly pattern: ExpressionMatcher | undefined;
19
+ }
20
+ /** Partition files into matching and non-matching based on the pattern. */
21
+ export declare function partitionByPattern(files: SourceFile[], pattern: ExpressionMatcher, minLines: number): {
22
+ matching: SourceFile[];
23
+ nonMatching: SourceFile[];
24
+ };
25
+ /** Group source files by parent folder, applying all filters. */
26
+ export declare function groupFilesByFolder(scope: SiblingScope): Map<string, SourceFile[]>;
27
+ /** Single source of the inert message. Pure — takes the assessment, returns text. */
28
+ export declare function inertMessage(a: {
29
+ matching: number;
30
+ total: number;
31
+ }, patternDesc: string): string;
32
+ /**
33
+ * Dedupe key for the inert finding. `dedupeConfigFindings` keys on
34
+ * `${file} ${ruleId ?? rule} ${element}`; `file` is always `''` here and
35
+ * `rule`/`ruleId` fall back to `describe()`, which is scope-blind (reads
36
+ * `_pattern` only). So without a scope-aware `element`, two same-pattern/
37
+ * different-scope inert detectors with no explicit `.rule({id})` would
38
+ * collapse into one finding under `checkAll`.
39
+ *
40
+ * Folds in every field `groupFilesByFolder()`/`fileMatchesPattern()` read to
41
+ * decide what's examined — `_folders`, `_ignorePaths`, `_ignoreTests`,
42
+ * `_minLines` — sorted so option order cannot split one semantic scope into
43
+ * two keys. Two rules with identical scope correctly collapse to one finding
44
+ * (one edit fixes both); two rules differing in ANY of these stay distinct.
45
+ */
46
+ export declare function inertElement(scope: SiblingScope): string;
47
+ //# sourceMappingURL=sibling-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sibling-files.d.ts","sourceRoot":"","sources":["../../src/smells/sibling-files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAO/D;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAAA;CAChD;AAsBD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GACf;IAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAWvD;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAoCjF;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,CAAC,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAWhG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAKxD"}
@@ -0,0 +1,104 @@
1
+ import path from 'node:path';
2
+ import picomatch from 'picomatch';
3
+ import { collectFunctions } from '../models/arch-function.js';
4
+ import { searchFunctionBody } from '../helpers/body-traversal.js';
5
+ /** Paths treated as tests when `.ignoreTests()` is on. */
6
+ const TEST_PATTERNS = ['**/*.test.ts', '**/*.spec.ts', '**/__tests__/**'];
7
+ /** Check if a source file contains any function matching the pattern. */
8
+ function fileMatchesPattern(sf, pattern, minLines) {
9
+ // Detectors scan for a property of the code, not a user-declared subject
10
+ // set, so they always include object-literal functions. `functions()`
11
+ // keeps that opt-in because widening a selector silently changes every
12
+ // existing rule; a detector has no such contract to break, and a
13
+ // duplicated arrow under an object key — a resolver, a route handler, a
14
+ // reducer case — is exactly the copy-paste rot this exists to find.
15
+ for (const fn of collectFunctions(sf, { includeObjectLiteralFunctions: true })) {
16
+ const body = fn.getBody();
17
+ if (!body)
18
+ continue;
19
+ const lineCount = body.getText().split('\n').length;
20
+ if (lineCount < minLines)
21
+ continue;
22
+ if (searchFunctionBody(fn, pattern).found)
23
+ return true;
24
+ }
25
+ return false;
26
+ }
27
+ /** Partition files into matching and non-matching based on the pattern. */
28
+ export function partitionByPattern(files, pattern, minLines) {
29
+ const matching = [];
30
+ const nonMatching = [];
31
+ for (const sf of files) {
32
+ if (fileMatchesPattern(sf, pattern, minLines)) {
33
+ matching.push(sf);
34
+ }
35
+ else {
36
+ nonMatching.push(sf);
37
+ }
38
+ }
39
+ return { matching, nonMatching };
40
+ }
41
+ /** Group source files by parent folder, applying all filters. */
42
+ export function groupFilesByFolder(scope) {
43
+ const sourceFiles = scope.project.getSourceFiles();
44
+ const folderMatchers = scope.folders.map((g) => picomatch(g));
45
+ const ignoreMatchers = scope.ignorePaths.map((g) => picomatch(g));
46
+ const testMatchers = scope.ignoreTests ? TEST_PATTERNS.map((g) => picomatch(g)) : [];
47
+ const groups = new Map();
48
+ for (const sf of sourceFiles) {
49
+ const filePath = sf.getFilePath();
50
+ // Folder filter: if folders specified, file must match at least one
51
+ if (folderMatchers.length > 0 && !folderMatchers.some((m) => m(filePath))) {
52
+ continue;
53
+ }
54
+ // Ignore paths filter
55
+ if (ignoreMatchers.some((m) => m(filePath))) {
56
+ continue;
57
+ }
58
+ // Test file filter
59
+ if (testMatchers.some((m) => m(filePath))) {
60
+ continue;
61
+ }
62
+ const folder = path.dirname(filePath);
63
+ const existing = groups.get(folder);
64
+ if (existing) {
65
+ existing.push(sf);
66
+ }
67
+ else {
68
+ groups.set(folder, [sf]);
69
+ }
70
+ }
71
+ return groups;
72
+ }
73
+ /** Single source of the inert message. Pure — takes the assessment, returns text. */
74
+ export function inertMessage(a, patternDesc) {
75
+ return (`This detector examined ${String(a.total)} sibling files, but only ${String(a.matching)} of them ` +
76
+ `hold the pattern '${patternDesc}', and no folder is within an edit of a majority — so as written it ` +
77
+ `cannot produce a finding today. It reports a file that diverges from what its siblings do; with no ` +
78
+ `majority reachable by adopting files, there is no divergence to report. ` +
79
+ `If this rule asserts a convention the codebase is still adopting, replace it with ` +
80
+ `crossProject().side(...).beComplete(), which fails the day a file falls short — until adoption is ` +
81
+ `complete, so expect that red. If the intent is to police divergence rather than the convention itself, ` +
82
+ `widen the folder so a majority forms, or choose a pattern the sibling files already share.`);
83
+ }
84
+ /**
85
+ * Dedupe key for the inert finding. `dedupeConfigFindings` keys on
86
+ * `${file} ${ruleId ?? rule} ${element}`; `file` is always `''` here and
87
+ * `rule`/`ruleId` fall back to `describe()`, which is scope-blind (reads
88
+ * `_pattern` only). So without a scope-aware `element`, two same-pattern/
89
+ * different-scope inert detectors with no explicit `.rule({id})` would
90
+ * collapse into one finding under `checkAll`.
91
+ *
92
+ * Folds in every field `groupFilesByFolder()`/`fileMatchesPattern()` read to
93
+ * decide what's examined — `_folders`, `_ignorePaths`, `_ignoreTests`,
94
+ * `_minLines` — sorted so option order cannot split one semantic scope into
95
+ * two keys. Two rules with identical scope correctly collapse to one finding
96
+ * (one edit fixes both); two rules differing in ANY of these stay distinct.
97
+ */
98
+ export function inertElement(scope) {
99
+ const patternDesc = scope.pattern?.description ?? 'unknown pattern';
100
+ const folders = [...scope.folders].sort().join('|');
101
+ const ignorePaths = [...scope.ignorePaths].sort().join('|');
102
+ return `inert:${patternDesc}:${folders}:${ignorePaths}:${String(scope.ignoreTests)}:${String(scope.minLines)}`;
103
+ }
104
+ //# sourceMappingURL=sibling-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sibling-files.js","sourceRoot":"","sources":["../../src/smells/sibling-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { ArchFunction } from '../models/arch-function.js';
2
+ import type { Fingerprint } from './fingerprint.js';
3
+ /** A function paired with its structural fingerprint. */
4
+ interface FingerprintedFunction {
5
+ fn: ArchFunction;
6
+ fingerprint: Fingerprint;
7
+ }
8
+ /** Two bodies the detector considers similar enough to report, and by how much. */
9
+ export interface SimilarPair {
10
+ a: ArchFunction;
11
+ b: ArchFunction;
12
+ similarity: number;
13
+ /**
14
+ * The fingerprints the score came from, carried so a REPORTED pair can be
15
+ * asked what varies between the two bodies without a second AST pass.
16
+ *
17
+ * Optional because a caller may construct a pair by hand — the message
18
+ * builder degrades to the bare percentage rather than assuming.
19
+ */
20
+ fingerprintA?: Fingerprint;
21
+ fingerprintB?: Fingerprint;
22
+ }
23
+ /** Build fingerprints for all collected functions. */
24
+ export declare function fingerprintAll(functions: ArchFunction[]): FingerprintedFunction[];
25
+ /** Compare all pairs of fingerprints, collect those above threshold. */
26
+ export declare function findSimilarPairs(items: FingerprintedFunction[], minSimilarity: number, minDistinctVocabulary: number): SimilarPair[];
27
+ export {};
28
+ //# sourceMappingURL=similar-pairs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similar-pairs.d.ts","sourceRoot":"","sources":["../../src/smells/similar-pairs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,yDAAyD;AACzD,UAAU,qBAAqB;IAC7B,EAAE,EAAE,YAAY,CAAA;IAChB,WAAW,EAAE,WAAW,CAAA;CACzB;AAED,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,YAAY,CAAA;IACf,CAAC,EAAE,YAAY,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,YAAY,CAAC,EAAE,WAAW,CAAA;CAC3B;AA4BD,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,qBAAqB,EAAE,CAQjF;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAAE,EAC9B,aAAa,EAAE,MAAM,EACrB,qBAAqB,EAAE,MAAM,GAC5B,WAAW,EAAE,CAyEf"}
@@ -0,0 +1,134 @@
1
+ import { buildFingerprint, computeSimilarity } from './fingerprint.js';
2
+ /**
3
+ * The pairwise comparison behind `smells.duplicateBodies()`.
4
+ *
5
+ * Separated from the builder because it is the algorithm, not the DSL: the
6
+ * builder's job is to collect a fluent configuration and hand it here. The
7
+ * thresholds arrive as arguments rather than being read off a builder, so the
8
+ * comparison is callable and testable without one.
9
+ */
10
+ /**
11
+ * Does one function's body enclose the other's?
12
+ *
13
+ * Only meaningful within one source file; two bodies in different files can
14
+ * never nest. Compared on the BODY spans rather than the declarations, because
15
+ * that is what was fingerprinted.
16
+ */
17
+ function containsOther(a, b) {
18
+ const bodyA = a.getBody();
19
+ const bodyB = b.getBody();
20
+ if (!bodyA || !bodyB)
21
+ return false;
22
+ if (bodyA.getSourceFile() !== bodyB.getSourceFile())
23
+ return false;
24
+ const [startA, endA] = [bodyA.getStart(), bodyA.getEnd()];
25
+ const [startB, endB] = [bodyB.getStart(), bodyB.getEnd()];
26
+ return (startA <= startB && endA >= endB) || (startB <= startA && endB >= endA);
27
+ }
28
+ /** Build fingerprints for all collected functions. */
29
+ export function fingerprintAll(functions) {
30
+ const result = [];
31
+ for (const fn of functions) {
32
+ const body = fn.getBody();
33
+ if (!body)
34
+ continue;
35
+ result.push({ fn, fingerprint: buildFingerprint(body) });
36
+ }
37
+ return result;
38
+ }
39
+ /** Compare all pairs of fingerprints, collect those above threshold. */
40
+ export function findSimilarPairs(items, minSimilarity, minDistinctVocabulary) {
41
+ const pairs = [];
42
+ for (let i = 0; i < items.length; i++) {
43
+ for (let j = i + 1; j < items.length; j++) {
44
+ const a = items[i];
45
+ const b = items[j];
46
+ if (!a || !b)
47
+ continue;
48
+ // Fast rejection 1: if node counts differ too much, similarity cannot reach threshold
49
+ const maxCount = Math.max(a.fingerprint.nodeCount, b.fingerprint.nodeCount);
50
+ const minCount = Math.min(a.fingerprint.nodeCount, b.fingerprint.nodeCount);
51
+ if (maxCount > 0 && minCount / maxCount < minSimilarity) {
52
+ continue;
53
+ }
54
+ // Fast rejection 2 (plan 0103): neither body has enough distinct vocabulary
55
+ // for a match to be evidence of anything. `Math.min`, not sum or average —
56
+ // ONE small-vocabulary side is enough to make the pair uninformative
57
+ // regardless of the other side's size.
58
+ const minDistinct = Math.min(a.fingerprint.distinctVocabulary, b.fingerprint.distinctVocabulary);
59
+ if (minDistinct < minDistinctVocabulary) {
60
+ continue;
61
+ }
62
+ // A body compared against a body NESTED INSIDE IT can never be
63
+ // actionable: "extract the shared logic into one function" is impossible
64
+ // when one function already contains the other. The detector generates
65
+ // these itself, because it collects object-literal functions by design —
66
+ // so `const rule = () => ({ evaluate })` yields both the outer arrow and
67
+ // the `evaluate` inside it, and they are similar by construction.
68
+ //
69
+ // Measured on this repo: 10 of 173 pairs, every one of them noise.
70
+ if (containsOther(a.fn, b.fn))
71
+ continue;
72
+ // Bug 0230 — the PAIRWISE floor, where the two above are per-body.
73
+ // Plan 0103 asks "does this body carry enough vocabulary to be
74
+ // evidence?"; nothing asked "do these two carry any of the SAME
75
+ // vocabulary?". `computeSimilarity` cannot answer it and must not — it
76
+ // scores syntax kinds only, which is what makes it a type-2 clone score
77
+ // — so a pair could reach 1.00 on shape with an empty intersection.
78
+ // Shipped, on this repo: `asDeclared` against `InconsistentSiblings-
79
+ // Builder.scope`, two functions that each gather six of their own fields
80
+ // into a record and have not one identifier in common. "Extract the
81
+ // shared logic into one function" names something that does not exist.
82
+ //
83
+ // `=== 0`, never a threshold. Measured over all 89 pairs this repo
84
+ // produces: two share nothing, none share one or two, and the nearest
85
+ // real finding shares four. The gap is empty, so no tuned number is
86
+ // needed — and a non-zero floor would start suppressing copy-paste.
87
+ //
88
+ // Guarded by `minDistinct > 0`, and that is not a special case for a
89
+ // test. "These share no vocabulary" is evidence of unrelatedness only if
90
+ // there was vocabulary to share: two bodies that are pure control flow
91
+ // (`return true`) share their entire content, because the shape IS the
92
+ // content. Whether such a pair is worth reporting is the CALLER's
93
+ // decision, spelled `minDistinctVocabulary(0)`, and rejection 2 above
94
+ // has already applied it. Overriding it here would make that option
95
+ // silently mean something else.
96
+ if (minDistinct > 0 && sharedVocabulary(a.fingerprint, b.fingerprint) === 0)
97
+ continue;
98
+ const similarity = computeSimilarity(a.fingerprint, b.fingerprint);
99
+ if (similarity >= minSimilarity) {
100
+ pairs.push({
101
+ a: a.fn,
102
+ b: b.fn,
103
+ similarity,
104
+ fingerprintA: a.fingerprint,
105
+ fingerprintB: b.fingerprint,
106
+ });
107
+ }
108
+ }
109
+ }
110
+ return pairs;
111
+ }
112
+ /**
113
+ * How many distinct identifiers/literals both bodies use.
114
+ *
115
+ * Reads `Fingerprint.texts`, which `computeSimilarity` deliberately ignores:
116
+ * the score is over syntax kinds so that a renamed copy still scores as a
117
+ * clone. That is right for scoring and wrong for deciding whether two bodies
118
+ * are related at all, which is what this answers.
119
+ */
120
+ function sharedVocabulary(a, b) {
121
+ const theirs = new Set();
122
+ for (const text of b.texts)
123
+ if (text !== undefined)
124
+ theirs.add(text);
125
+ const counted = new Set();
126
+ for (const text of a.texts) {
127
+ if (text === undefined || counted.has(text))
128
+ continue;
129
+ if (theirs.has(text))
130
+ counted.add(text);
131
+ }
132
+ return counted.size;
133
+ }
134
+ //# sourceMappingURL=similar-pairs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similar-pairs.js","sourceRoot":"","sources":["../../src/smells/similar-pairs.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,8 @@
1
1
  import type { ArchProject } from '../core/project.js';
2
- import type { ArchViolation } from '../core/violation.js';
3
- import { TerminalBuilder } from '@nielspeter/eess';
4
- /**
5
- * Base class for smell detector builders.
6
- * Extends TerminalBuilder for shared terminal methods (check/warn/excluding/because/rule).
7
- *
8
- * SmellBuilder does NOT extend RuleBuilder — smell detectors have a
9
- * different chain grammar (no .that()/.should()) and execution model
10
- * (pairwise comparison rather than individual element evaluation).
11
- */
2
+ import type { CollectResult } from '../core/terminal-builder.js';
3
+ import type { ArchViolation } from '@nielspeter/eess';
4
+ import type { GlobNode } from '@nielspeter/eess';
5
+ import { TerminalBuilder } from '../core/terminal-builder.js';
12
6
  export declare abstract class SmellBuilder extends TerminalBuilder {
13
7
  protected readonly project: ArchProject;
14
8
  protected _folders: string[];
@@ -16,8 +10,27 @@ export declare abstract class SmellBuilder extends TerminalBuilder {
16
10
  protected _ignoreTests: boolean;
17
11
  protected _ignorePaths: string[];
18
12
  protected _groupByFolder: boolean;
13
+ /**
14
+ * This family's own narrowing, named — plan 0099 / ADR-010 part 4.
15
+ *
16
+ * `_minLines` defaults to 5 and neither `agentGuardrails` nor `strictBoundaries`
17
+ * exposes a knob for it, so a user whose bodies are all shorter is told their
18
+ * rule enforces nothing by a filter they never wrote and cannot reach. Naming it
19
+ * is the difference between an actionable finding and one that sends an agent
20
+ * hunting for filters that do not exist.
21
+ */
22
+ /** This family counts function bodies, not subjects — plan 0099. */
23
+ protected examinedUnitNoun(): string;
24
+ protected narrowingHint(): string | undefined;
19
25
  constructor(project: ArchProject);
20
- /** Scope detection to files matching the glob pattern. */
26
+ /**
27
+ * Scope detection to files matching the glob pattern.
28
+ *
29
+ * Note the name: this matches the **whole file path**, not the directory
30
+ * portion (`passesFileFilters` applies the matcher to `sf.getFilePath()`).
31
+ * So its declared kind is `file-path`, which is why plan 0069 derives `kind`
32
+ * from the string a matcher is applied to rather than from the method name.
33
+ */
21
34
  inFolder(glob: string): this;
22
35
  /** Ignore functions/files shorter than N lines. Default: 5. */
23
36
  minLines(n: number): this;
@@ -25,12 +38,44 @@ export declare abstract class SmellBuilder extends TerminalBuilder {
25
38
  ignoreTests(): this;
26
39
  /** Exclude files matching the given glob patterns. */
27
40
  ignorePaths(...globs: string[]): this;
41
+ /** The project this detector was built against. See `RuleBuilder.getProject`. */
42
+ getProject(): ArchProject;
43
+ /**
44
+ * The globs this detector was scoped with.
45
+ *
46
+ * `inFolder` is `discovery`: it defines the population to scan, so a glob
47
+ * matching nothing means the detector scans nothing. `ignorePaths` is
48
+ * `exclusion` and is never a fault — an exclusion matching zero files is
49
+ * remedy-optional (proposal 006).
50
+ *
51
+ * Repeated `inFolder()` calls OR together (`folderMatchers.some`), so they
52
+ * form one `any` node rather than one node each.
53
+ */
54
+ globs(): readonly GlobNode[];
28
55
  /** Group violation output by directory. */
29
56
  groupByFolder(): this;
57
+ /**
58
+ * An independent copy, carrying both scope lists.
59
+ *
60
+ * Without this, `ignorePaths()` on a copy would push into the array the
61
+ * original still points at — and a leaked *ignore* is a false green: the
62
+ * next detector off the same held builder would silently skip those files.
63
+ */
64
+ protected copy(): this;
30
65
  /** Delegate to detect() for the terminal builder pipeline. */
31
- protected collectViolations(): ArchViolation[];
66
+ protected collectViolations(): CollectResult;
32
67
  /** Subclasses implement: run detection, return violations. */
33
68
  protected abstract detect(): ArchViolation[];
69
+ /**
70
+ * Subclasses implement: units this detector examined — plan 0098.
71
+ *
72
+ * Declared here rather than left to the subclasses because
73
+ * `collectViolations()` is the seam and it cannot see a hook the subclass
74
+ * merely happens to have. Plan 0096 added `examinedUnits()` to both detectors
75
+ * and this class could not reach either; a new detector would have compiled
76
+ * with no evidence at all.
77
+ */
78
+ abstract examinedUnits(): number;
34
79
  /** Subclasses implement: human-readable rule description. */
35
80
  protected abstract describe(): string;
36
81
  }
@@ -1 +1 @@
1
- {"version":3,"file":"smell-builder.d.ts","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAElD;;;;;;;GAOG;AACH,8BAAsB,YAAa,SAAQ,eAAe;IAO5C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,WAAW;IANnD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAK;IACjC,SAAS,CAAC,SAAS,SAAI;IACvB,SAAS,CAAC,YAAY,UAAQ;IAC9B,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,CAAK;IACrC,SAAS,CAAC,cAAc,UAAQ;gBAED,OAAO,EAAE,WAAW;IAInD,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK5B,+DAA+D;IAC/D,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,+DAA+D;IAC/D,WAAW,IAAI,IAAI;IAKnB,sDAAsD;IACtD,WAAW,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAKrC,2CAA2C;IAC3C,aAAa,IAAI,IAAI;IAKrB,8DAA8D;IAC9D,SAAS,CAAC,iBAAiB,IAAI,aAAa,EAAE;IAI9C,8DAA8D;IAC9D,SAAS,CAAC,QAAQ,CAAC,MAAM,IAAI,aAAa,EAAE;IAE5C,6DAA6D;IAC7D,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,MAAM;CACtC"}
1
+ {"version":3,"file":"smell-builder.d.ts","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGhD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAc7D,8BAAsB,YAAa,SAAQ,eAAe;IAiD5C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,WAAW;IAhDnD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAK;IACjC,SAAS,CAAC,SAAS,EAAE,MAAM,CAAoB;IAC/C,SAAS,CAAC,YAAY,UAAQ;IAC9B,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,CAAK;IACrC,SAAS,CAAC,cAAc,UAAQ;IAEhC;;;;;;;;OAQG;IACH,oEAAoE;cACjD,gBAAgB,IAAI,MAAM;cAI1B,aAAa,IAAI,MAAM,GAAG,SAAS;gBA4BvB,OAAO,EAAE,WAAW;IAInD;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM5B,+DAA+D;IAC/D,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAMzB,+DAA+D;IAC/D,WAAW,IAAI,IAAI;IAMnB,sDAAsD;IACtD,WAAW,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAMrC,iFAAiF;IACjF,UAAU,IAAI,WAAW;IAIzB;;;;;;;;;;OAUG;IACM,KAAK,IAAI,SAAS,QAAQ,EAAE;IAuBrC,2CAA2C;IAC3C,aAAa,IAAI,IAAI;IAMrB;;;;;;OAMG;cACgB,IAAI,IAAI,IAAI;IAO/B,8DAA8D;IAC9D,SAAS,CAAC,iBAAiB,IAAI,aAAa;IAI5C,8DAA8D;IAC9D,SAAS,CAAC,QAAQ,CAAC,MAAM,IAAI,aAAa,EAAE;IAE5C;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,IAAI,MAAM;IAEhC,6DAA6D;IAC7D,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,MAAM;CACtC"}
@@ -1,4 +1,7 @@
1
- import { TerminalBuilder } from '@nielspeter/eess';
1
+ import { stampGlobs } from '@nielspeter/eess/internal';
2
+ import { globAnyOf, collectResult } from '@nielspeter/eess';
3
+ import { TerminalBuilder } from '../core/terminal-builder.js';
4
+ import { isProjectRelative } from '../core/project-relative.js';
2
5
  /**
3
6
  * Base class for smell detector builders.
4
7
  * Extends TerminalBuilder for shared terminal methods (check/warn/excluding/because/rule).
@@ -7,45 +10,140 @@ import { TerminalBuilder } from '@nielspeter/eess';
7
10
  * different chain grammar (no .that()/.should()) and execution model
8
11
  * (pairwise comparison rather than individual element evaluation).
9
12
  */
13
+ /** The `minLines` threshold applied when the author sets none. */
14
+ const DEFAULT_MIN_LINES = 5;
10
15
  export class SmellBuilder extends TerminalBuilder {
11
16
  project;
12
17
  _folders = [];
13
- _minLines = 5;
18
+ _minLines = DEFAULT_MIN_LINES;
14
19
  _ignoreTests = false;
15
20
  _ignorePaths = [];
16
21
  _groupByFolder = false;
22
+ /**
23
+ * This family's own narrowing, named — plan 0099 / ADR-010 part 4.
24
+ *
25
+ * `_minLines` defaults to 5 and neither `agentGuardrails` nor `strictBoundaries`
26
+ * exposes a knob for it, so a user whose bodies are all shorter is told their
27
+ * rule enforces nothing by a filter they never wrote and cannot reach. Naming it
28
+ * is the difference between an actionable finding and one that sends an agent
29
+ * hunting for filters that do not exist.
30
+ */
31
+ /** This family counts function bodies, not subjects — plan 0099. */
32
+ examinedUnitNoun() {
33
+ return 'function bodies';
34
+ }
35
+ narrowingHint() {
36
+ const applied = [`minLines(${String(this._minLines)})`];
37
+ if (this._folders.length > 0)
38
+ applied.push(`inFolder(${this._folders.join(', ')})`);
39
+ if (this._ignorePaths.length > 0)
40
+ applied.push(`ignorePaths(${this._ignorePaths.join(', ')})`);
41
+ if (this._ignoreTests)
42
+ applied.push('ignoreTests()');
43
+ // The "you did not write it" clause is TRUE only while the value is still the
44
+ // default. Rendered with an author-written `.minLines(500)` it said
45
+ // "minLines(500) — minLines defaults to 5, a default you did not write",
46
+ // which is false on the path that produced it: an agent goes hunting for
47
+ // where 5 comes from and never touches the 500 that is the actual fault.
48
+ // ADR-009 rule 2, on an unsuppressable hard failure.
49
+ // States the filters as FACT, and does not claim they caused the emptiness.
50
+ //
51
+ // "Its own narrowing removed them" asserts removal. Measured on a types-only
52
+ // corpus with the default threshold: there were zero function bodies, so
53
+ // `minLines` removed nothing — the sentence named a false cause and pointed at
54
+ // a knob that `agentGuardrails` and `strictBoundaries` expose no setter for,
55
+ // on an unsuppressable hard failure. `narrowingHint()`'s own docstring
56
+ // promises the caller "names the possibility rather than asserting a cause it
57
+ // cannot verify"; this is the family honouring that rather than the base
58
+ // softening it afterwards.
59
+ const wroteItThemselves = this._minLines !== DEFAULT_MIN_LINES;
60
+ return wroteItThemselves
61
+ ? `Its narrowing was: ${applied.join(', ')}.`
62
+ : `Its narrowing was: ${applied.join(', ')}` +
63
+ ` — minLines defaults to ${String(DEFAULT_MIN_LINES)}, a default you did not write.`;
64
+ }
17
65
  constructor(project) {
18
66
  super();
19
67
  this.project = project;
20
68
  }
21
- /** Scope detection to files matching the glob pattern. */
69
+ /**
70
+ * Scope detection to files matching the glob pattern.
71
+ *
72
+ * Note the name: this matches the **whole file path**, not the directory
73
+ * portion (`passesFileFilters` applies the matcher to `sf.getFilePath()`).
74
+ * So its declared kind is `file-path`, which is why plan 0069 derives `kind`
75
+ * from the string a matcher is applied to rather than from the method name.
76
+ */
22
77
  inFolder(glob) {
23
- this._folders.push(glob);
24
- return this;
78
+ const next = this.copy();
79
+ next._folders.push(glob);
80
+ return next;
25
81
  }
26
82
  /** Ignore functions/files shorter than N lines. Default: 5. */
27
83
  minLines(n) {
28
- this._minLines = n;
29
- return this;
84
+ const next = this.copy();
85
+ next._minLines = n;
86
+ return next;
30
87
  }
31
88
  /** Exclude test files (*.test.ts, *.spec.ts, __tests__/**). */
32
89
  ignoreTests() {
33
- this._ignoreTests = true;
34
- return this;
90
+ const next = this.copy();
91
+ next._ignoreTests = true;
92
+ return next;
35
93
  }
36
94
  /** Exclude files matching the given glob patterns. */
37
95
  ignorePaths(...globs) {
38
- this._ignorePaths.push(...globs);
39
- return this;
96
+ const next = this.copy();
97
+ next._ignorePaths.push(...globs);
98
+ return next;
99
+ }
100
+ /** The project this detector was built against. See `RuleBuilder.getProject`. */
101
+ getProject() {
102
+ return this.project;
103
+ }
104
+ /**
105
+ * The globs this detector was scoped with.
106
+ *
107
+ * `inFolder` is `discovery`: it defines the population to scan, so a glob
108
+ * matching nothing means the detector scans nothing. `ignorePaths` is
109
+ * `exclusion` and is never a fault — an exclusion matching zero files is
110
+ * remedy-optional (proposal 006).
111
+ *
112
+ * Repeated `inFolder()` calls OR together (`folderMatchers.some`), so they
113
+ * form one `any` node rather than one node each.
114
+ */
115
+ globs() {
116
+ const trees = [];
117
+ if (this._folders.length > 0) {
118
+ trees.push(stampGlobs(globAnyOf(this._folders, 'file-path', this._folders.every((g) => isProjectRelative(g)) ? 'normalized' : 'absolute'), 'discovery', (g) => `inFolder("${g.glob}")`));
119
+ }
120
+ for (const glob of this._ignorePaths) {
121
+ trees.push(stampGlobs(globAnyOf([glob], 'file-path'), 'exclusion', (g) => `ignorePaths("${g.glob}")`));
122
+ }
123
+ return trees;
40
124
  }
41
125
  /** Group violation output by directory. */
42
126
  groupByFolder() {
43
- this._groupByFolder = true;
44
- return this;
127
+ const next = this.copy();
128
+ next._groupByFolder = true;
129
+ return next;
130
+ }
131
+ /**
132
+ * An independent copy, carrying both scope lists.
133
+ *
134
+ * Without this, `ignorePaths()` on a copy would push into the array the
135
+ * original still points at — and a leaked *ignore* is a false green: the
136
+ * next detector off the same held builder would silently skip those files.
137
+ */
138
+ copy() {
139
+ const clone = super.copy();
140
+ clone._folders = [...this._folders];
141
+ clone._ignorePaths = [...this._ignorePaths];
142
+ return clone;
45
143
  }
46
144
  /** Delegate to detect() for the terminal builder pipeline. */
47
145
  collectViolations() {
48
- return this.detect();
146
+ return collectResult(this.detect(), { examined: this.examinedUnits() });
49
147
  }
50
148
  }
51
149
  //# sourceMappingURL=smell-builder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"smell-builder.js","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAElD;;;;;;;GAOG;AACH,MAAM,OAAgB,YAAa,SAAQ,eAAe;IAOzB;IANrB,QAAQ,GAAa,EAAE,CAAA;IACvB,SAAS,GAAG,CAAC,CAAA;IACb,YAAY,GAAG,KAAK,CAAA;IACpB,YAAY,GAAa,EAAE,CAAA;IAC3B,cAAc,GAAG,KAAK,CAAA;IAEhC,YAA+B,OAAoB;QACjD,KAAK,EAAE,CAAA;QADsB,YAAO,GAAP,OAAO,CAAa;IAEnD,CAAC;IAED,0DAA0D;IAC1D,QAAQ,CAAC,IAAY;QACnB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACxB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,+DAA+D;IAC/D,QAAQ,CAAC,CAAS;QAChB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;QAClB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,+DAA+D;IAC/D,WAAW;QACT,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QACxB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,sDAAsD;IACtD,WAAW,CAAC,GAAG,KAAe;QAC5B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;QAChC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,2CAA2C;IAC3C,aAAa;QACX,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC1B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,8DAA8D;IACpD,iBAAiB;QACzB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;CAOF"}
1
+ {"version":3,"file":"smell-builder.js","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"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"}