driftdetect-detectors 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 (509) hide show
  1. package/dist/accessibility/alt-text.d.ts +63 -0
  2. package/dist/accessibility/alt-text.d.ts.map +1 -0
  3. package/dist/accessibility/alt-text.js +100 -0
  4. package/dist/accessibility/alt-text.js.map +1 -0
  5. package/dist/accessibility/aria-roles.d.ts +65 -0
  6. package/dist/accessibility/aria-roles.d.ts.map +1 -0
  7. package/dist/accessibility/aria-roles.js +87 -0
  8. package/dist/accessibility/aria-roles.js.map +1 -0
  9. package/dist/accessibility/focus-management.d.ts +62 -0
  10. package/dist/accessibility/focus-management.d.ts.map +1 -0
  11. package/dist/accessibility/focus-management.js +88 -0
  12. package/dist/accessibility/focus-management.js.map +1 -0
  13. package/dist/accessibility/heading-hierarchy.d.ts +66 -0
  14. package/dist/accessibility/heading-hierarchy.d.ts.map +1 -0
  15. package/dist/accessibility/heading-hierarchy.js +94 -0
  16. package/dist/accessibility/heading-hierarchy.js.map +1 -0
  17. package/dist/accessibility/index.d.ts +25 -0
  18. package/dist/accessibility/index.d.ts.map +1 -0
  19. package/dist/accessibility/index.js +21 -0
  20. package/dist/accessibility/index.js.map +1 -0
  21. package/dist/accessibility/keyboard-nav.d.ts +63 -0
  22. package/dist/accessibility/keyboard-nav.d.ts.map +1 -0
  23. package/dist/accessibility/keyboard-nav.js +86 -0
  24. package/dist/accessibility/keyboard-nav.js.map +1 -0
  25. package/dist/accessibility/semantic-html.d.ts +76 -0
  26. package/dist/accessibility/semantic-html.d.ts.map +1 -0
  27. package/dist/accessibility/semantic-html.js +204 -0
  28. package/dist/accessibility/semantic-html.js.map +1 -0
  29. package/dist/api/client-patterns.d.ts +121 -0
  30. package/dist/api/client-patterns.d.ts.map +1 -0
  31. package/dist/api/client-patterns.js +478 -0
  32. package/dist/api/client-patterns.js.map +1 -0
  33. package/dist/api/error-format.d.ts +140 -0
  34. package/dist/api/error-format.d.ts.map +1 -0
  35. package/dist/api/error-format.js +614 -0
  36. package/dist/api/error-format.js.map +1 -0
  37. package/dist/api/http-methods.d.ts +255 -0
  38. package/dist/api/http-methods.d.ts.map +1 -0
  39. package/dist/api/http-methods.js +890 -0
  40. package/dist/api/http-methods.js.map +1 -0
  41. package/dist/api/index.d.ts +16 -0
  42. package/dist/api/index.d.ts.map +1 -0
  43. package/dist/api/index.js +37 -0
  44. package/dist/api/index.js.map +1 -0
  45. package/dist/api/pagination.d.ts +133 -0
  46. package/dist/api/pagination.d.ts.map +1 -0
  47. package/dist/api/pagination.js +521 -0
  48. package/dist/api/pagination.js.map +1 -0
  49. package/dist/api/response-envelope.d.ts +261 -0
  50. package/dist/api/response-envelope.d.ts.map +1 -0
  51. package/dist/api/response-envelope.js +1050 -0
  52. package/dist/api/response-envelope.js.map +1 -0
  53. package/dist/api/retry-patterns.d.ts +117 -0
  54. package/dist/api/retry-patterns.d.ts.map +1 -0
  55. package/dist/api/retry-patterns.js +480 -0
  56. package/dist/api/retry-patterns.js.map +1 -0
  57. package/dist/api/route-structure.d.ts +128 -0
  58. package/dist/api/route-structure.d.ts.map +1 -0
  59. package/dist/api/route-structure.js +738 -0
  60. package/dist/api/route-structure.js.map +1 -0
  61. package/dist/auth/audit-logging.d.ts +80 -0
  62. package/dist/auth/audit-logging.d.ts.map +1 -0
  63. package/dist/auth/audit-logging.js +370 -0
  64. package/dist/auth/audit-logging.js.map +1 -0
  65. package/dist/auth/index.d.ts +33 -0
  66. package/dist/auth/index.d.ts.map +1 -0
  67. package/dist/auth/index.js +49 -0
  68. package/dist/auth/index.js.map +1 -0
  69. package/dist/auth/middleware-usage.d.ts +65 -0
  70. package/dist/auth/middleware-usage.d.ts.map +1 -0
  71. package/dist/auth/middleware-usage.js +192 -0
  72. package/dist/auth/middleware-usage.js.map +1 -0
  73. package/dist/auth/permission-checks.d.ts +60 -0
  74. package/dist/auth/permission-checks.d.ts.map +1 -0
  75. package/dist/auth/permission-checks.js +159 -0
  76. package/dist/auth/permission-checks.js.map +1 -0
  77. package/dist/auth/rbac-patterns.d.ts +68 -0
  78. package/dist/auth/rbac-patterns.d.ts.map +1 -0
  79. package/dist/auth/rbac-patterns.js +143 -0
  80. package/dist/auth/rbac-patterns.js.map +1 -0
  81. package/dist/auth/resource-ownership.d.ts +77 -0
  82. package/dist/auth/resource-ownership.d.ts.map +1 -0
  83. package/dist/auth/resource-ownership.js +324 -0
  84. package/dist/auth/resource-ownership.js.map +1 -0
  85. package/dist/auth/token-handling.d.ts +64 -0
  86. package/dist/auth/token-handling.d.ts.map +1 -0
  87. package/dist/auth/token-handling.js +151 -0
  88. package/dist/auth/token-handling.js.map +1 -0
  89. package/dist/base/ast-detector.d.ts +421 -0
  90. package/dist/base/ast-detector.d.ts.map +1 -0
  91. package/dist/base/ast-detector.js +699 -0
  92. package/dist/base/ast-detector.js.map +1 -0
  93. package/dist/base/base-detector.d.ts +366 -0
  94. package/dist/base/base-detector.d.ts.map +1 -0
  95. package/dist/base/base-detector.js +170 -0
  96. package/dist/base/base-detector.js.map +1 -0
  97. package/dist/base/index.d.ts +12 -0
  98. package/dist/base/index.d.ts.map +1 -0
  99. package/dist/base/index.js +17 -0
  100. package/dist/base/index.js.map +1 -0
  101. package/dist/base/regex-detector.d.ts +421 -0
  102. package/dist/base/regex-detector.d.ts.map +1 -0
  103. package/dist/base/regex-detector.js +537 -0
  104. package/dist/base/regex-detector.js.map +1 -0
  105. package/dist/base/structural-detector.d.ts +424 -0
  106. package/dist/base/structural-detector.d.ts.map +1 -0
  107. package/dist/base/structural-detector.js +731 -0
  108. package/dist/base/structural-detector.js.map +1 -0
  109. package/dist/base/types.d.ts +53 -0
  110. package/dist/base/types.d.ts.map +1 -0
  111. package/dist/base/types.js +5 -0
  112. package/dist/base/types.js.map +1 -0
  113. package/dist/components/component-structure.d.ts +163 -0
  114. package/dist/components/component-structure.d.ts.map +1 -0
  115. package/dist/components/component-structure.js +500 -0
  116. package/dist/components/component-structure.js.map +1 -0
  117. package/dist/components/composition.d.ts +287 -0
  118. package/dist/components/composition.d.ts.map +1 -0
  119. package/dist/components/composition.js +1123 -0
  120. package/dist/components/composition.js.map +1 -0
  121. package/dist/components/duplicate-detection.d.ts +251 -0
  122. package/dist/components/duplicate-detection.d.ts.map +1 -0
  123. package/dist/components/duplicate-detection.js +804 -0
  124. package/dist/components/duplicate-detection.js.map +1 -0
  125. package/dist/components/index.d.ts +16 -0
  126. package/dist/components/index.d.ts.map +1 -0
  127. package/dist/components/index.js +51 -0
  128. package/dist/components/index.js.map +1 -0
  129. package/dist/components/near-duplicate.d.ts +402 -0
  130. package/dist/components/near-duplicate.d.ts.map +1 -0
  131. package/dist/components/near-duplicate.js +1090 -0
  132. package/dist/components/near-duplicate.js.map +1 -0
  133. package/dist/components/props-patterns.d.ts +194 -0
  134. package/dist/components/props-patterns.d.ts.map +1 -0
  135. package/dist/components/props-patterns.js +795 -0
  136. package/dist/components/props-patterns.js.map +1 -0
  137. package/dist/components/ref-forwarding.d.ts +250 -0
  138. package/dist/components/ref-forwarding.d.ts.map +1 -0
  139. package/dist/components/ref-forwarding.js +832 -0
  140. package/dist/components/ref-forwarding.js.map +1 -0
  141. package/dist/components/state-patterns.d.ts +291 -0
  142. package/dist/components/state-patterns.d.ts.map +1 -0
  143. package/dist/components/state-patterns.js +970 -0
  144. package/dist/components/state-patterns.js.map +1 -0
  145. package/dist/config/config-validation.d.ts +74 -0
  146. package/dist/config/config-validation.d.ts.map +1 -0
  147. package/dist/config/config-validation.js +446 -0
  148. package/dist/config/config-validation.js.map +1 -0
  149. package/dist/config/default-values.d.ts +72 -0
  150. package/dist/config/default-values.d.ts.map +1 -0
  151. package/dist/config/default-values.js +386 -0
  152. package/dist/config/default-values.js.map +1 -0
  153. package/dist/config/env-naming.d.ts +73 -0
  154. package/dist/config/env-naming.d.ts.map +1 -0
  155. package/dist/config/env-naming.js +429 -0
  156. package/dist/config/env-naming.js.map +1 -0
  157. package/dist/config/environment-detection.d.ts +72 -0
  158. package/dist/config/environment-detection.d.ts.map +1 -0
  159. package/dist/config/environment-detection.js +400 -0
  160. package/dist/config/environment-detection.js.map +1 -0
  161. package/dist/config/feature-flags.d.ts +72 -0
  162. package/dist/config/feature-flags.d.ts.map +1 -0
  163. package/dist/config/feature-flags.js +384 -0
  164. package/dist/config/feature-flags.js.map +1 -0
  165. package/dist/config/index.d.ts +27 -0
  166. package/dist/config/index.d.ts.map +1 -0
  167. package/dist/config/index.js +43 -0
  168. package/dist/config/index.js.map +1 -0
  169. package/dist/config/required-optional.d.ts +71 -0
  170. package/dist/config/required-optional.d.ts.map +1 -0
  171. package/dist/config/required-optional.js +344 -0
  172. package/dist/config/required-optional.js.map +1 -0
  173. package/dist/data-access/connection-pooling.d.ts +63 -0
  174. package/dist/data-access/connection-pooling.d.ts.map +1 -0
  175. package/dist/data-access/connection-pooling.js +297 -0
  176. package/dist/data-access/connection-pooling.js.map +1 -0
  177. package/dist/data-access/dto-patterns.d.ts +64 -0
  178. package/dist/data-access/dto-patterns.d.ts.map +1 -0
  179. package/dist/data-access/dto-patterns.js +291 -0
  180. package/dist/data-access/dto-patterns.js.map +1 -0
  181. package/dist/data-access/index.d.ts +31 -0
  182. package/dist/data-access/index.d.ts.map +1 -0
  183. package/dist/data-access/index.js +49 -0
  184. package/dist/data-access/index.js.map +1 -0
  185. package/dist/data-access/n-plus-one.d.ts +60 -0
  186. package/dist/data-access/n-plus-one.d.ts.map +1 -0
  187. package/dist/data-access/n-plus-one.js +264 -0
  188. package/dist/data-access/n-plus-one.js.map +1 -0
  189. package/dist/data-access/query-patterns.d.ts +64 -0
  190. package/dist/data-access/query-patterns.d.ts.map +1 -0
  191. package/dist/data-access/query-patterns.js +314 -0
  192. package/dist/data-access/query-patterns.js.map +1 -0
  193. package/dist/data-access/repository-pattern.d.ts +62 -0
  194. package/dist/data-access/repository-pattern.d.ts.map +1 -0
  195. package/dist/data-access/repository-pattern.js +257 -0
  196. package/dist/data-access/repository-pattern.js.map +1 -0
  197. package/dist/data-access/transaction-patterns.d.ts +61 -0
  198. package/dist/data-access/transaction-patterns.d.ts.map +1 -0
  199. package/dist/data-access/transaction-patterns.js +277 -0
  200. package/dist/data-access/transaction-patterns.js.map +1 -0
  201. package/dist/data-access/validation-patterns.d.ts +62 -0
  202. package/dist/data-access/validation-patterns.d.ts.map +1 -0
  203. package/dist/data-access/validation-patterns.js +301 -0
  204. package/dist/data-access/validation-patterns.js.map +1 -0
  205. package/dist/documentation/deprecation.d.ts +62 -0
  206. package/dist/documentation/deprecation.d.ts.map +1 -0
  207. package/dist/documentation/deprecation.js +83 -0
  208. package/dist/documentation/deprecation.js.map +1 -0
  209. package/dist/documentation/example-code.d.ts +64 -0
  210. package/dist/documentation/example-code.d.ts.map +1 -0
  211. package/dist/documentation/example-code.js +79 -0
  212. package/dist/documentation/example-code.js.map +1 -0
  213. package/dist/documentation/index.d.ts +22 -0
  214. package/dist/documentation/index.d.ts.map +1 -0
  215. package/dist/documentation/index.js +19 -0
  216. package/dist/documentation/index.js.map +1 -0
  217. package/dist/documentation/jsdoc-patterns.d.ts +72 -0
  218. package/dist/documentation/jsdoc-patterns.d.ts.map +1 -0
  219. package/dist/documentation/jsdoc-patterns.js +92 -0
  220. package/dist/documentation/jsdoc-patterns.js.map +1 -0
  221. package/dist/documentation/readme-structure.d.ts +67 -0
  222. package/dist/documentation/readme-structure.d.ts.map +1 -0
  223. package/dist/documentation/readme-structure.js +76 -0
  224. package/dist/documentation/readme-structure.js.map +1 -0
  225. package/dist/documentation/todo-patterns.d.ts +67 -0
  226. package/dist/documentation/todo-patterns.d.ts.map +1 -0
  227. package/dist/documentation/todo-patterns.js +73 -0
  228. package/dist/documentation/todo-patterns.js.map +1 -0
  229. package/dist/errors/async-errors.d.ts +72 -0
  230. package/dist/errors/async-errors.d.ts.map +1 -0
  231. package/dist/errors/async-errors.js +214 -0
  232. package/dist/errors/async-errors.js.map +1 -0
  233. package/dist/errors/circuit-breaker.d.ts +53 -0
  234. package/dist/errors/circuit-breaker.d.ts.map +1 -0
  235. package/dist/errors/circuit-breaker.js +241 -0
  236. package/dist/errors/circuit-breaker.js.map +1 -0
  237. package/dist/errors/error-codes.d.ts +73 -0
  238. package/dist/errors/error-codes.d.ts.map +1 -0
  239. package/dist/errors/error-codes.js +211 -0
  240. package/dist/errors/error-codes.js.map +1 -0
  241. package/dist/errors/error-logging.d.ts +73 -0
  242. package/dist/errors/error-logging.d.ts.map +1 -0
  243. package/dist/errors/error-logging.js +256 -0
  244. package/dist/errors/error-logging.js.map +1 -0
  245. package/dist/errors/error-propagation.d.ts +73 -0
  246. package/dist/errors/error-propagation.d.ts.map +1 -0
  247. package/dist/errors/error-propagation.js +244 -0
  248. package/dist/errors/error-propagation.js.map +1 -0
  249. package/dist/errors/exception-hierarchy.d.ts +75 -0
  250. package/dist/errors/exception-hierarchy.d.ts.map +1 -0
  251. package/dist/errors/exception-hierarchy.js +259 -0
  252. package/dist/errors/exception-hierarchy.js.map +1 -0
  253. package/dist/errors/index.d.ts +31 -0
  254. package/dist/errors/index.d.ts.map +1 -0
  255. package/dist/errors/index.js +49 -0
  256. package/dist/errors/index.js.map +1 -0
  257. package/dist/errors/try-catch-placement.d.ts +73 -0
  258. package/dist/errors/try-catch-placement.d.ts.map +1 -0
  259. package/dist/errors/try-catch-placement.js +214 -0
  260. package/dist/errors/try-catch-placement.js.map +1 -0
  261. package/dist/index.d.ts +221 -0
  262. package/dist/index.d.ts.map +1 -0
  263. package/dist/index.js +245 -0
  264. package/dist/index.js.map +1 -0
  265. package/dist/logging/context-fields.d.ts +48 -0
  266. package/dist/logging/context-fields.d.ts.map +1 -0
  267. package/dist/logging/context-fields.js +160 -0
  268. package/dist/logging/context-fields.js.map +1 -0
  269. package/dist/logging/correlation-ids.d.ts +44 -0
  270. package/dist/logging/correlation-ids.d.ts.map +1 -0
  271. package/dist/logging/correlation-ids.js +144 -0
  272. package/dist/logging/correlation-ids.js.map +1 -0
  273. package/dist/logging/health-checks.d.ts +45 -0
  274. package/dist/logging/health-checks.d.ts.map +1 -0
  275. package/dist/logging/health-checks.js +165 -0
  276. package/dist/logging/health-checks.js.map +1 -0
  277. package/dist/logging/index.d.ts +31 -0
  278. package/dist/logging/index.d.ts.map +1 -0
  279. package/dist/logging/index.js +49 -0
  280. package/dist/logging/index.js.map +1 -0
  281. package/dist/logging/log-levels.d.ts +46 -0
  282. package/dist/logging/log-levels.d.ts.map +1 -0
  283. package/dist/logging/log-levels.js +178 -0
  284. package/dist/logging/log-levels.js.map +1 -0
  285. package/dist/logging/metric-naming.d.ts +46 -0
  286. package/dist/logging/metric-naming.d.ts.map +1 -0
  287. package/dist/logging/metric-naming.js +157 -0
  288. package/dist/logging/metric-naming.js.map +1 -0
  289. package/dist/logging/pii-redaction.d.ts +44 -0
  290. package/dist/logging/pii-redaction.d.ts.map +1 -0
  291. package/dist/logging/pii-redaction.js +166 -0
  292. package/dist/logging/pii-redaction.js.map +1 -0
  293. package/dist/logging/structured-format.d.ts +53 -0
  294. package/dist/logging/structured-format.d.ts.map +1 -0
  295. package/dist/logging/structured-format.js +235 -0
  296. package/dist/logging/structured-format.js.map +1 -0
  297. package/dist/performance/bundle-size.d.ts +79 -0
  298. package/dist/performance/bundle-size.d.ts.map +1 -0
  299. package/dist/performance/bundle-size.js +276 -0
  300. package/dist/performance/bundle-size.js.map +1 -0
  301. package/dist/performance/caching-patterns.d.ts +78 -0
  302. package/dist/performance/caching-patterns.d.ts.map +1 -0
  303. package/dist/performance/caching-patterns.js +257 -0
  304. package/dist/performance/caching-patterns.js.map +1 -0
  305. package/dist/performance/code-splitting.d.ts +86 -0
  306. package/dist/performance/code-splitting.d.ts.map +1 -0
  307. package/dist/performance/code-splitting.js +447 -0
  308. package/dist/performance/code-splitting.js.map +1 -0
  309. package/dist/performance/debounce-throttle.d.ts +75 -0
  310. package/dist/performance/debounce-throttle.d.ts.map +1 -0
  311. package/dist/performance/debounce-throttle.js +232 -0
  312. package/dist/performance/debounce-throttle.js.map +1 -0
  313. package/dist/performance/index.d.ts +28 -0
  314. package/dist/performance/index.d.ts.map +1 -0
  315. package/dist/performance/index.js +39 -0
  316. package/dist/performance/index.js.map +1 -0
  317. package/dist/performance/lazy-loading.d.ts +75 -0
  318. package/dist/performance/lazy-loading.d.ts.map +1 -0
  319. package/dist/performance/lazy-loading.js +233 -0
  320. package/dist/performance/lazy-loading.js.map +1 -0
  321. package/dist/performance/memoization.d.ts +75 -0
  322. package/dist/performance/memoization.d.ts.map +1 -0
  323. package/dist/performance/memoization.js +251 -0
  324. package/dist/performance/memoization.js.map +1 -0
  325. package/dist/registry/detector-registry.d.ts +266 -0
  326. package/dist/registry/detector-registry.d.ts.map +1 -0
  327. package/dist/registry/detector-registry.js +526 -0
  328. package/dist/registry/detector-registry.js.map +1 -0
  329. package/dist/registry/index.d.ts +10 -0
  330. package/dist/registry/index.d.ts.map +1 -0
  331. package/dist/registry/index.js +10 -0
  332. package/dist/registry/index.js.map +1 -0
  333. package/dist/registry/loader.d.ts +232 -0
  334. package/dist/registry/loader.d.ts.map +1 -0
  335. package/dist/registry/loader.js +419 -0
  336. package/dist/registry/loader.js.map +1 -0
  337. package/dist/registry/types.d.ts +111 -0
  338. package/dist/registry/types.d.ts.map +1 -0
  339. package/dist/registry/types.js +19 -0
  340. package/dist/registry/types.js.map +1 -0
  341. package/dist/security/csp-headers.d.ts +78 -0
  342. package/dist/security/csp-headers.d.ts.map +1 -0
  343. package/dist/security/csp-headers.js +401 -0
  344. package/dist/security/csp-headers.js.map +1 -0
  345. package/dist/security/csrf-protection.d.ts +72 -0
  346. package/dist/security/csrf-protection.d.ts.map +1 -0
  347. package/dist/security/csrf-protection.js +344 -0
  348. package/dist/security/csrf-protection.js.map +1 -0
  349. package/dist/security/index.d.ts +30 -0
  350. package/dist/security/index.d.ts.map +1 -0
  351. package/dist/security/index.js +48 -0
  352. package/dist/security/index.js.map +1 -0
  353. package/dist/security/input-sanitization.d.ts +74 -0
  354. package/dist/security/input-sanitization.d.ts.map +1 -0
  355. package/dist/security/input-sanitization.js +373 -0
  356. package/dist/security/input-sanitization.js.map +1 -0
  357. package/dist/security/rate-limiting.d.ts +81 -0
  358. package/dist/security/rate-limiting.d.ts.map +1 -0
  359. package/dist/security/rate-limiting.js +535 -0
  360. package/dist/security/rate-limiting.js.map +1 -0
  361. package/dist/security/secret-management.d.ts +83 -0
  362. package/dist/security/secret-management.d.ts.map +1 -0
  363. package/dist/security/secret-management.js +547 -0
  364. package/dist/security/secret-management.js.map +1 -0
  365. package/dist/security/sql-injection.d.ts +76 -0
  366. package/dist/security/sql-injection.d.ts.map +1 -0
  367. package/dist/security/sql-injection.js +383 -0
  368. package/dist/security/sql-injection.js.map +1 -0
  369. package/dist/security/xss-prevention.d.ts +80 -0
  370. package/dist/security/xss-prevention.d.ts.map +1 -0
  371. package/dist/security/xss-prevention.js +416 -0
  372. package/dist/security/xss-prevention.js.map +1 -0
  373. package/dist/structural/barrel-exports.d.ts +178 -0
  374. package/dist/structural/barrel-exports.d.ts.map +1 -0
  375. package/dist/structural/barrel-exports.js +553 -0
  376. package/dist/structural/barrel-exports.js.map +1 -0
  377. package/dist/structural/circular-deps.d.ts +140 -0
  378. package/dist/structural/circular-deps.d.ts.map +1 -0
  379. package/dist/structural/circular-deps.js +422 -0
  380. package/dist/structural/circular-deps.js.map +1 -0
  381. package/dist/structural/co-location.d.ts +202 -0
  382. package/dist/structural/co-location.d.ts.map +1 -0
  383. package/dist/structural/co-location.js +640 -0
  384. package/dist/structural/co-location.js.map +1 -0
  385. package/dist/structural/directory-structure.d.ts +151 -0
  386. package/dist/structural/directory-structure.d.ts.map +1 -0
  387. package/dist/structural/directory-structure.js +457 -0
  388. package/dist/structural/directory-structure.js.map +1 -0
  389. package/dist/structural/file-naming.d.ts +61 -0
  390. package/dist/structural/file-naming.d.ts.map +1 -0
  391. package/dist/structural/file-naming.js +231 -0
  392. package/dist/structural/file-naming.js.map +1 -0
  393. package/dist/structural/import-ordering.d.ts +212 -0
  394. package/dist/structural/import-ordering.d.ts.map +1 -0
  395. package/dist/structural/import-ordering.js +821 -0
  396. package/dist/structural/import-ordering.js.map +1 -0
  397. package/dist/structural/index.d.ts +23 -0
  398. package/dist/structural/index.d.ts.map +1 -0
  399. package/dist/structural/index.js +26 -0
  400. package/dist/structural/index.js.map +1 -0
  401. package/dist/structural/module-boundaries.d.ts +164 -0
  402. package/dist/structural/module-boundaries.d.ts.map +1 -0
  403. package/dist/structural/module-boundaries.js +616 -0
  404. package/dist/structural/module-boundaries.js.map +1 -0
  405. package/dist/structural/package-boundaries.d.ts +182 -0
  406. package/dist/structural/package-boundaries.d.ts.map +1 -0
  407. package/dist/structural/package-boundaries.js +602 -0
  408. package/dist/structural/package-boundaries.js.map +1 -0
  409. package/dist/styling/class-naming.d.ts +263 -0
  410. package/dist/styling/class-naming.d.ts.map +1 -0
  411. package/dist/styling/class-naming.js +892 -0
  412. package/dist/styling/class-naming.js.map +1 -0
  413. package/dist/styling/color-usage.d.ts +213 -0
  414. package/dist/styling/color-usage.d.ts.map +1 -0
  415. package/dist/styling/color-usage.js +732 -0
  416. package/dist/styling/color-usage.js.map +1 -0
  417. package/dist/styling/design-tokens.d.ts +212 -0
  418. package/dist/styling/design-tokens.d.ts.map +1 -0
  419. package/dist/styling/design-tokens.js +748 -0
  420. package/dist/styling/design-tokens.js.map +1 -0
  421. package/dist/styling/index.d.ts +16 -0
  422. package/dist/styling/index.d.ts.map +1 -0
  423. package/dist/styling/index.js +56 -0
  424. package/dist/styling/index.js.map +1 -0
  425. package/dist/styling/responsive.d.ts +304 -0
  426. package/dist/styling/responsive.d.ts.map +1 -0
  427. package/dist/styling/responsive.js +888 -0
  428. package/dist/styling/responsive.js.map +1 -0
  429. package/dist/styling/spacing-scale.d.ts +248 -0
  430. package/dist/styling/spacing-scale.d.ts.map +1 -0
  431. package/dist/styling/spacing-scale.js +865 -0
  432. package/dist/styling/spacing-scale.js.map +1 -0
  433. package/dist/styling/tailwind-patterns.d.ts +305 -0
  434. package/dist/styling/tailwind-patterns.d.ts.map +1 -0
  435. package/dist/styling/tailwind-patterns.js +1181 -0
  436. package/dist/styling/tailwind-patterns.js.map +1 -0
  437. package/dist/styling/typography.d.ts +281 -0
  438. package/dist/styling/typography.d.ts.map +1 -0
  439. package/dist/styling/typography.js +1004 -0
  440. package/dist/styling/typography.js.map +1 -0
  441. package/dist/styling/z-index-scale.d.ts +270 -0
  442. package/dist/styling/z-index-scale.d.ts.map +1 -0
  443. package/dist/styling/z-index-scale.js +714 -0
  444. package/dist/styling/z-index-scale.js.map +1 -0
  445. package/dist/testing/co-location.d.ts +42 -0
  446. package/dist/testing/co-location.d.ts.map +1 -0
  447. package/dist/testing/co-location.js +134 -0
  448. package/dist/testing/co-location.js.map +1 -0
  449. package/dist/testing/describe-naming.d.ts +47 -0
  450. package/dist/testing/describe-naming.d.ts.map +1 -0
  451. package/dist/testing/describe-naming.js +150 -0
  452. package/dist/testing/describe-naming.js.map +1 -0
  453. package/dist/testing/file-naming.d.ts +44 -0
  454. package/dist/testing/file-naming.d.ts.map +1 -0
  455. package/dist/testing/file-naming.js +131 -0
  456. package/dist/testing/file-naming.js.map +1 -0
  457. package/dist/testing/fixture-patterns.d.ts +52 -0
  458. package/dist/testing/fixture-patterns.d.ts.map +1 -0
  459. package/dist/testing/fixture-patterns.js +228 -0
  460. package/dist/testing/fixture-patterns.js.map +1 -0
  461. package/dist/testing/index.d.ts +31 -0
  462. package/dist/testing/index.d.ts.map +1 -0
  463. package/dist/testing/index.js +49 -0
  464. package/dist/testing/index.js.map +1 -0
  465. package/dist/testing/mock-patterns.d.ts +53 -0
  466. package/dist/testing/mock-patterns.d.ts.map +1 -0
  467. package/dist/testing/mock-patterns.js +264 -0
  468. package/dist/testing/mock-patterns.js.map +1 -0
  469. package/dist/testing/setup-teardown.d.ts +55 -0
  470. package/dist/testing/setup-teardown.d.ts.map +1 -0
  471. package/dist/testing/setup-teardown.js +262 -0
  472. package/dist/testing/setup-teardown.js.map +1 -0
  473. package/dist/testing/test-structure.d.ts +51 -0
  474. package/dist/testing/test-structure.d.ts.map +1 -0
  475. package/dist/testing/test-structure.js +225 -0
  476. package/dist/testing/test-structure.js.map +1 -0
  477. package/dist/types/any-usage.d.ts +99 -0
  478. package/dist/types/any-usage.d.ts.map +1 -0
  479. package/dist/types/any-usage.js +641 -0
  480. package/dist/types/any-usage.js.map +1 -0
  481. package/dist/types/file-location.d.ts +76 -0
  482. package/dist/types/file-location.d.ts.map +1 -0
  483. package/dist/types/file-location.js +395 -0
  484. package/dist/types/file-location.js.map +1 -0
  485. package/dist/types/generic-patterns.d.ts +97 -0
  486. package/dist/types/generic-patterns.d.ts.map +1 -0
  487. package/dist/types/generic-patterns.js +615 -0
  488. package/dist/types/generic-patterns.js.map +1 -0
  489. package/dist/types/index.d.ts +31 -0
  490. package/dist/types/index.d.ts.map +1 -0
  491. package/dist/types/index.js +43 -0
  492. package/dist/types/index.js.map +1 -0
  493. package/dist/types/interface-vs-type.d.ts +81 -0
  494. package/dist/types/interface-vs-type.d.ts.map +1 -0
  495. package/dist/types/interface-vs-type.js +440 -0
  496. package/dist/types/interface-vs-type.js.map +1 -0
  497. package/dist/types/naming-conventions.d.ts +84 -0
  498. package/dist/types/naming-conventions.d.ts.map +1 -0
  499. package/dist/types/naming-conventions.js +455 -0
  500. package/dist/types/naming-conventions.js.map +1 -0
  501. package/dist/types/type-assertions.d.ts +98 -0
  502. package/dist/types/type-assertions.d.ts.map +1 -0
  503. package/dist/types/type-assertions.js +639 -0
  504. package/dist/types/type-assertions.js.map +1 -0
  505. package/dist/types/utility-types.d.ts +110 -0
  506. package/dist/types/utility-types.d.ts.map +1 -0
  507. package/dist/types/utility-types.js +547 -0
  508. package/dist/types/utility-types.js.map +1 -0
  509. package/package.json +44 -0
@@ -0,0 +1,804 @@
1
+ /**
2
+ * Duplicate Detection - AST-based duplicate component detection
3
+ *
4
+ * Detects duplicate and near-duplicate components using AST comparison
5
+ * with configurable similarity thresholds. Supports exact duplicates (100%),
6
+ * near-duplicates (80%+), and structural duplicates (same AST structure,
7
+ * different identifiers).
8
+ *
9
+ * @requirements 8.3 - THE Component_Detector SHALL detect duplicate components with 80%+ similarity
10
+ * @requirements 8.4 - THE Component_Detector SHALL detect near-duplicate components that should be abstracted
11
+ */
12
+ import { ASTDetector } from '../base/index.js';
13
+ // ============================================================================
14
+ // Constants
15
+ // ============================================================================
16
+ /**
17
+ * Default configuration for duplicate detection
18
+ */
19
+ export const DEFAULT_DUPLICATE_CONFIG = {
20
+ similarityThreshold: 0.8,
21
+ detectStructural: true,
22
+ minComponentSize: 5,
23
+ ignoreWhitespace: true,
24
+ normalizeIdentifiers: true,
25
+ };
26
+ /**
27
+ * Node types that represent identifiers
28
+ */
29
+ export const IDENTIFIER_NODE_TYPES = new Set([
30
+ 'identifier',
31
+ 'property_identifier',
32
+ 'shorthand_property_identifier',
33
+ 'type_identifier',
34
+ ]);
35
+ /**
36
+ * Node types that represent literals
37
+ */
38
+ export const LITERAL_NODE_TYPES = new Set([
39
+ 'string',
40
+ 'string_fragment',
41
+ 'number',
42
+ 'true',
43
+ 'false',
44
+ 'null',
45
+ 'undefined',
46
+ ]);
47
+ // ============================================================================
48
+ // Helper Functions - AST Normalization
49
+ // ============================================================================
50
+ /**
51
+ * Normalize an AST node for comparison
52
+ * Strips position info and optionally normalizes identifiers
53
+ */
54
+ export function normalizeASTNode(node, normalizeIdentifiers = true, identifierMap = new Map()) {
55
+ let value;
56
+ // Handle identifiers - normalize to placeholders for structural comparison
57
+ if (normalizeIdentifiers && IDENTIFIER_NODE_TYPES.has(node.type)) {
58
+ const originalName = node.text;
59
+ if (!identifierMap.has(originalName)) {
60
+ identifierMap.set(originalName, `$id${identifierMap.size}`);
61
+ }
62
+ value = identifierMap.get(originalName);
63
+ }
64
+ else if (LITERAL_NODE_TYPES.has(node.type)) {
65
+ // Keep literal values for comparison
66
+ value = node.text;
67
+ }
68
+ const normalizedChildren = node.children.map(child => normalizeASTNode(child, normalizeIdentifiers, identifierMap));
69
+ const result = {
70
+ type: node.type,
71
+ children: normalizedChildren,
72
+ originalText: node.text,
73
+ };
74
+ if (value !== undefined) {
75
+ result.value = value;
76
+ }
77
+ return result;
78
+ }
79
+ /**
80
+ * Generate a hash for a normalized AST node
81
+ * Used for quick equality checks
82
+ */
83
+ export function hashNormalizedNode(node) {
84
+ const parts = [node.type];
85
+ if (node.value !== undefined) {
86
+ parts.push(node.value);
87
+ }
88
+ for (const child of node.children) {
89
+ parts.push(hashNormalizedNode(child));
90
+ }
91
+ return parts.join('|');
92
+ }
93
+ /**
94
+ * Count the number of nodes in a normalized AST
95
+ */
96
+ export function countNodes(node) {
97
+ let count = 1;
98
+ for (const child of node.children) {
99
+ count += countNodes(child);
100
+ }
101
+ return count;
102
+ }
103
+ // ============================================================================
104
+ // Helper Functions - Similarity Calculation
105
+ // ============================================================================
106
+ /**
107
+ * Calculate similarity between two normalized AST nodes
108
+ * Returns a score from 0.0 to 1.0
109
+ */
110
+ export function calculateASTSimilarity(node1, node2) {
111
+ // If types don't match, no similarity
112
+ if (node1.type !== node2.type) {
113
+ return 0;
114
+ }
115
+ // Base score for matching type
116
+ let score = 1;
117
+ let totalWeight = 1;
118
+ // Compare values if present
119
+ if (node1.value !== undefined || node2.value !== undefined) {
120
+ totalWeight += 1;
121
+ if (node1.value === node2.value) {
122
+ score += 1;
123
+ }
124
+ }
125
+ // Compare children
126
+ const maxChildren = Math.max(node1.children.length, node2.children.length);
127
+ const minChildren = Math.min(node1.children.length, node2.children.length);
128
+ if (maxChildren > 0) {
129
+ // Calculate child similarity using optimal alignment
130
+ const childSimilarities = calculateChildSimilarities(node1.children, node2.children);
131
+ const childScore = childSimilarities.reduce((sum, s) => sum + s, 0);
132
+ const childWeight = maxChildren;
133
+ score += childScore;
134
+ totalWeight += childWeight;
135
+ // Penalty for different number of children
136
+ if (maxChildren !== minChildren) {
137
+ const penalty = (maxChildren - minChildren) * 0.5;
138
+ score -= penalty;
139
+ }
140
+ }
141
+ return Math.max(0, Math.min(1, score / totalWeight));
142
+ }
143
+ /**
144
+ * Calculate similarities between two lists of child nodes
145
+ * Uses a greedy matching algorithm for efficiency
146
+ */
147
+ function calculateChildSimilarities(children1, children2) {
148
+ if (children1.length === 0 || children2.length === 0) {
149
+ return [];
150
+ }
151
+ const similarities = [];
152
+ const used2 = new Set();
153
+ // For each child in list 1, find best match in list 2
154
+ for (const child1 of children1) {
155
+ let bestScore = 0;
156
+ let bestIndex = -1;
157
+ for (let j = 0; j < children2.length; j++) {
158
+ if (used2.has(j))
159
+ continue;
160
+ const child2 = children2[j];
161
+ if (!child2)
162
+ continue;
163
+ const score = calculateASTSimilarity(child1, child2);
164
+ if (score > bestScore) {
165
+ bestScore = score;
166
+ bestIndex = j;
167
+ }
168
+ }
169
+ if (bestIndex >= 0) {
170
+ used2.add(bestIndex);
171
+ }
172
+ similarities.push(bestScore);
173
+ }
174
+ // Add zeros for unmatched children in list 2
175
+ for (let j = 0; j < children2.length; j++) {
176
+ if (!used2.has(j)) {
177
+ similarities.push(0);
178
+ }
179
+ }
180
+ return similarities;
181
+ }
182
+ /**
183
+ * Calculate text-based similarity using Levenshtein distance
184
+ * Used as a fallback when AST is not available
185
+ */
186
+ export function calculateTextSimilarity(text1, text2) {
187
+ // Normalize whitespace
188
+ const normalized1 = text1.replace(/\s+/g, ' ').trim();
189
+ const normalized2 = text2.replace(/\s+/g, ' ').trim();
190
+ if (normalized1 === normalized2) {
191
+ return 1.0;
192
+ }
193
+ const maxLen = Math.max(normalized1.length, normalized2.length);
194
+ if (maxLen === 0) {
195
+ return 1.0;
196
+ }
197
+ const distance = levenshteinDistance(normalized1, normalized2);
198
+ return 1 - (distance / maxLen);
199
+ }
200
+ /**
201
+ * Calculate Levenshtein distance between two strings
202
+ */
203
+ function levenshteinDistance(str1, str2) {
204
+ const m = str1.length;
205
+ const n = str2.length;
206
+ // Use two rows instead of full matrix for memory efficiency
207
+ let prevRow = new Array(n + 1).fill(0).map((_, i) => i);
208
+ let currRow = new Array(n + 1).fill(0);
209
+ for (let i = 1; i <= m; i++) {
210
+ currRow[0] = i;
211
+ for (let j = 1; j <= n; j++) {
212
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
213
+ currRow[j] = Math.min((prevRow[j] ?? 0) + 1, // deletion
214
+ (currRow[j - 1] ?? 0) + 1, // insertion
215
+ (prevRow[j - 1] ?? 0) + cost // substitution
216
+ );
217
+ }
218
+ [prevRow, currRow] = [currRow, prevRow];
219
+ }
220
+ return prevRow[n] ?? 0;
221
+ }
222
+ /**
223
+ * Determine the type of duplication based on similarity
224
+ */
225
+ export function determineDuplicateType(similarity, hasIdentifierDifferences) {
226
+ if (similarity >= 0.99) {
227
+ return 'exact';
228
+ }
229
+ if (hasIdentifierDifferences && similarity >= 0.8) {
230
+ return 'structural';
231
+ }
232
+ return 'near';
233
+ }
234
+ // ============================================================================
235
+ // Helper Functions - Component Extraction
236
+ // ============================================================================
237
+ /**
238
+ * Check if a node represents a React component
239
+ */
240
+ export function isReactComponentNode(node, content) {
241
+ // Check for function/arrow function that returns JSX
242
+ if (node.type === 'function_declaration' ||
243
+ node.type === 'arrow_function' ||
244
+ node.type === 'function_expression') {
245
+ // Component names should be PascalCase
246
+ const name = extractComponentNameFromNode(node, content);
247
+ if (!name || !/^[A-Z]/.test(name)) {
248
+ return false;
249
+ }
250
+ // Check if it returns JSX
251
+ const nodeText = node.text;
252
+ return nodeText.includes('<') && (nodeText.includes('/>') || nodeText.includes('</'));
253
+ }
254
+ return false;
255
+ }
256
+ /**
257
+ * Extract component name from an AST node
258
+ */
259
+ export function extractComponentNameFromNode(node, content) {
260
+ // For function declarations, get the name directly
261
+ if (node.type === 'function_declaration') {
262
+ const nameNode = node.children.find(c => c.type === 'identifier');
263
+ return nameNode?.text;
264
+ }
265
+ // For arrow functions and function expressions, look for variable declaration
266
+ const lines = content.split('\n');
267
+ const line = lines[node.startPosition.row];
268
+ if (line) {
269
+ const match = line.match(/(?:const|let|var|export\s+(?:const|let|var)?)\s+([A-Z][a-zA-Z0-9]*)\s*[=:]/);
270
+ if (match && match[1]) {
271
+ return match[1];
272
+ }
273
+ }
274
+ return undefined;
275
+ }
276
+ /**
277
+ * Extract component info from an AST node
278
+ */
279
+ export function extractComponentInfo(node, filePath, content, config) {
280
+ const name = extractComponentNameFromNode(node, content);
281
+ if (!name) {
282
+ return null;
283
+ }
284
+ const normalizedAST = normalizeASTNode(node, config.normalizeIdentifiers);
285
+ const nodeCount = countNodes(normalizedAST);
286
+ // Skip components that are too small
287
+ if (nodeCount < config.minComponentSize) {
288
+ return null;
289
+ }
290
+ return {
291
+ name,
292
+ filePath,
293
+ line: node.startPosition.row + 1,
294
+ column: node.startPosition.column + 1,
295
+ normalizedAST,
296
+ sourceCode: node.text,
297
+ astHash: hashNormalizedNode(normalizedAST),
298
+ };
299
+ }
300
+ // ============================================================================
301
+ // Helper Functions - Difference Detection
302
+ // ============================================================================
303
+ /**
304
+ * Find differences between two normalized AST nodes
305
+ */
306
+ export function findDifferences(node1, node2, line1 = 1, line2 = 1) {
307
+ const differences = [];
308
+ // Type difference
309
+ if (node1.type !== node2.type) {
310
+ differences.push({
311
+ type: 'structure',
312
+ location1: { line: line1, column: 1 },
313
+ location2: { line: line2, column: 1 },
314
+ value1: node1.type,
315
+ value2: node2.type,
316
+ });
317
+ return differences;
318
+ }
319
+ // Value difference
320
+ if (node1.value !== node2.value) {
321
+ const diffType = IDENTIFIER_NODE_TYPES.has(node1.type) ? 'identifier' : 'literal';
322
+ differences.push({
323
+ type: diffType,
324
+ location1: { line: line1, column: 1 },
325
+ location2: { line: line2, column: 1 },
326
+ value1: node1.originalText || node1.value || '',
327
+ value2: node2.originalText || node2.value || '',
328
+ });
329
+ }
330
+ // Compare children
331
+ const maxChildren = Math.max(node1.children.length, node2.children.length);
332
+ for (let i = 0; i < maxChildren; i++) {
333
+ const child1 = node1.children[i];
334
+ const child2 = node2.children[i];
335
+ if (!child1 || !child2) {
336
+ differences.push({
337
+ type: 'structure',
338
+ location1: { line: line1, column: 1 },
339
+ location2: { line: line2, column: 1 },
340
+ value1: child1?.type || '(missing)',
341
+ value2: child2?.type || '(missing)',
342
+ });
343
+ }
344
+ else {
345
+ const childDiffs = findDifferences(child1, child2, line1, line2);
346
+ differences.push(...childDiffs);
347
+ }
348
+ }
349
+ return differences;
350
+ }
351
+ /**
352
+ * Check if differences are only identifier-based
353
+ */
354
+ export function hasOnlyIdentifierDifferences(differences) {
355
+ return differences.every(d => d.type === 'identifier');
356
+ }
357
+ // ============================================================================
358
+ // Helper Functions - Duplicate Analysis
359
+ // ============================================================================
360
+ /**
361
+ * Compare two components and return duplicate info if similar enough
362
+ */
363
+ export function compareComponents(comp1, comp2, config) {
364
+ // Quick check using hash for exact duplicates
365
+ if (comp1.astHash === comp2.astHash) {
366
+ return {
367
+ component1: comp1,
368
+ component2: comp2,
369
+ duplicateType: 'exact',
370
+ similarity: 1.0,
371
+ differences: [],
372
+ };
373
+ }
374
+ // Calculate similarity
375
+ let similarity;
376
+ let differences = [];
377
+ if (comp1.normalizedAST && comp2.normalizedAST) {
378
+ similarity = calculateASTSimilarity(comp1.normalizedAST, comp2.normalizedAST);
379
+ if (similarity >= config.similarityThreshold) {
380
+ differences = findDifferences(comp1.normalizedAST, comp2.normalizedAST, comp1.line, comp2.line);
381
+ }
382
+ }
383
+ else {
384
+ // Fallback to text similarity
385
+ similarity = calculateTextSimilarity(comp1.sourceCode, comp2.sourceCode);
386
+ }
387
+ // Check if similarity meets threshold
388
+ if (similarity < config.similarityThreshold) {
389
+ return null;
390
+ }
391
+ const hasIdentifierDiffs = hasOnlyIdentifierDifferences(differences);
392
+ const duplicateType = determineDuplicateType(similarity, hasIdentifierDiffs);
393
+ // Skip structural duplicates if not configured
394
+ if (duplicateType === 'structural' && !config.detectStructural) {
395
+ return null;
396
+ }
397
+ return {
398
+ component1: comp1,
399
+ component2: comp2,
400
+ duplicateType,
401
+ similarity,
402
+ differences,
403
+ };
404
+ }
405
+ /**
406
+ * Analyze a list of components for duplicates
407
+ */
408
+ export function analyzeDuplicates(components, config = DEFAULT_DUPLICATE_CONFIG) {
409
+ const duplicates = [];
410
+ const componentGroups = new Map();
411
+ // Group by hash for quick exact duplicate detection
412
+ for (const comp of components) {
413
+ const existing = componentGroups.get(comp.astHash) || [];
414
+ existing.push(comp);
415
+ componentGroups.set(comp.astHash, existing);
416
+ }
417
+ // Find exact duplicates from hash groups
418
+ for (const group of componentGroups.values()) {
419
+ if (group.length > 1) {
420
+ for (let i = 0; i < group.length; i++) {
421
+ for (let j = i + 1; j < group.length; j++) {
422
+ const comp1 = group[i];
423
+ const comp2 = group[j];
424
+ if (comp1 && comp2) {
425
+ duplicates.push({
426
+ component1: comp1,
427
+ component2: comp2,
428
+ duplicateType: 'exact',
429
+ similarity: 1.0,
430
+ differences: [],
431
+ });
432
+ }
433
+ }
434
+ }
435
+ }
436
+ }
437
+ // Find near-duplicates by comparing across groups
438
+ const groupKeys = Array.from(componentGroups.keys());
439
+ for (let i = 0; i < groupKeys.length; i++) {
440
+ for (let j = i + 1; j < groupKeys.length; j++) {
441
+ const group1 = componentGroups.get(groupKeys[i] ?? '') || [];
442
+ const group2 = componentGroups.get(groupKeys[j] ?? '') || [];
443
+ for (const comp1 of group1) {
444
+ for (const comp2 of group2) {
445
+ const pair = compareComponents(comp1, comp2, config);
446
+ if (pair) {
447
+ duplicates.push(pair);
448
+ }
449
+ }
450
+ }
451
+ }
452
+ }
453
+ // Build similarity groups
454
+ const similarityGroups = buildSimilarityGroups(components, duplicates);
455
+ return {
456
+ duplicates,
457
+ similarityGroups,
458
+ totalComponents: components.length,
459
+ uniqueComponents: similarityGroups.filter(g => g.length === 1).length,
460
+ };
461
+ }
462
+ /**
463
+ * Build groups of similar components using union-find
464
+ */
465
+ function buildSimilarityGroups(components, duplicates) {
466
+ // Create a map from component key to index
467
+ const componentIndex = new Map();
468
+ components.forEach((comp, idx) => {
469
+ componentIndex.set(`${comp.filePath}:${comp.line}`, idx);
470
+ });
471
+ // Union-find data structure
472
+ const parent = components.map((_, i) => i);
473
+ function find(x) {
474
+ if (parent[x] !== x) {
475
+ parent[x] = find(parent[x] ?? x);
476
+ }
477
+ return parent[x] ?? x;
478
+ }
479
+ function union(x, y) {
480
+ const px = find(x);
481
+ const py = find(y);
482
+ if (px !== py) {
483
+ parent[px] = py;
484
+ }
485
+ }
486
+ // Union components that are duplicates
487
+ for (const dup of duplicates) {
488
+ const idx1 = componentIndex.get(`${dup.component1.filePath}:${dup.component1.line}`);
489
+ const idx2 = componentIndex.get(`${dup.component2.filePath}:${dup.component2.line}`);
490
+ if (idx1 !== undefined && idx2 !== undefined) {
491
+ union(idx1, idx2);
492
+ }
493
+ }
494
+ // Group components by their root
495
+ const groups = new Map();
496
+ components.forEach((comp, idx) => {
497
+ const root = find(idx);
498
+ const group = groups.get(root) || [];
499
+ group.push(comp);
500
+ groups.set(root, group);
501
+ });
502
+ return Array.from(groups.values());
503
+ }
504
+ /**
505
+ * Generate a refactoring suggestion for duplicates
506
+ */
507
+ export function generateRefactoringSuggestion(pair) {
508
+ const { component1, component2, duplicateType, similarity } = pair;
509
+ if (duplicateType === 'exact') {
510
+ return `Components '${component1.name}' and '${component2.name}' are identical. ` +
511
+ `Consider extracting to a shared component.`;
512
+ }
513
+ if (duplicateType === 'structural') {
514
+ return `Components '${component1.name}' and '${component2.name}' have the same structure ` +
515
+ `but different identifiers. Consider creating a generic component with props.`;
516
+ }
517
+ const percentSimilar = Math.round(similarity * 100);
518
+ return `Components '${component1.name}' and '${component2.name}' are ${percentSimilar}% similar. ` +
519
+ `Consider abstracting common logic into a shared component.`;
520
+ }
521
+ // ============================================================================
522
+ // Duplicate Detector Class
523
+ // ============================================================================
524
+ /**
525
+ * Detector for duplicate and near-duplicate components
526
+ *
527
+ * Uses AST-based comparison to detect:
528
+ * - Exact duplicates (100% identical)
529
+ * - Near-duplicates (80%+ similarity)
530
+ * - Structural duplicates (same structure, different identifiers)
531
+ *
532
+ * @requirements 8.3 - THE Component_Detector SHALL detect duplicate components with 80%+ similarity
533
+ */
534
+ export class DuplicateDetector extends ASTDetector {
535
+ id = 'components/duplicate-detection';
536
+ category = 'components';
537
+ subcategory = 'duplicates';
538
+ name = 'Duplicate Component Detector';
539
+ description = 'Detects duplicate and near-duplicate components using AST-based comparison with 80%+ similarity threshold';
540
+ supportedLanguages = ['typescript', 'javascript'];
541
+ config;
542
+ constructor(config = {}) {
543
+ super();
544
+ this.config = { ...DEFAULT_DUPLICATE_CONFIG, ...config };
545
+ }
546
+ /**
547
+ * Detect duplicate components in the project
548
+ */
549
+ async detect(context) {
550
+ const patterns = [];
551
+ const violations = [];
552
+ // Extract components from all project files
553
+ const allComponents = this.extractAllComponents(context);
554
+ if (allComponents.length < 2) {
555
+ return this.createEmptyResult();
556
+ }
557
+ // Analyze for duplicates
558
+ const analysis = analyzeDuplicates(allComponents, this.config);
559
+ // Create pattern matches for unique components
560
+ if (analysis.uniqueComponents > 0) {
561
+ patterns.push({
562
+ patternId: 'component-uniqueness',
563
+ location: { file: context.file, line: 1, column: 1 },
564
+ confidence: analysis.uniqueComponents / analysis.totalComponents,
565
+ isOutlier: false,
566
+ });
567
+ }
568
+ // Create violations for duplicates involving the current file
569
+ for (const duplicate of analysis.duplicates) {
570
+ if (this.involvesCurrentFile(duplicate, context.file)) {
571
+ const violation = this.createDuplicateViolation(duplicate, context.file);
572
+ violations.push(violation);
573
+ }
574
+ }
575
+ const confidence = analysis.totalComponents > 0
576
+ ? analysis.uniqueComponents / analysis.totalComponents
577
+ : 1.0;
578
+ return this.createResult(patterns, violations, confidence);
579
+ }
580
+ /**
581
+ * Extract all components from project files
582
+ */
583
+ extractAllComponents(context) {
584
+ const components = [];
585
+ // Extract from current file using AST if available
586
+ if (context.ast) {
587
+ const fileComponents = this.extractComponentsFromAST(context.ast, context.file, context.content);
588
+ components.push(...fileComponents);
589
+ }
590
+ else {
591
+ // Fallback to regex-based extraction
592
+ const fileComponents = this.extractComponentsFromContent(context.file, context.content);
593
+ components.push(...fileComponents);
594
+ }
595
+ // For a complete analysis, we would also extract from other project files
596
+ // This is a simplified version that focuses on the current file
597
+ // In a full implementation, this would use the project context
598
+ return components;
599
+ }
600
+ /**
601
+ * Extract components from an AST
602
+ */
603
+ extractComponentsFromAST(ast, filePath, content) {
604
+ const components = [];
605
+ const functionNodes = this.findNodesByTypes(ast, [
606
+ 'function_declaration',
607
+ 'arrow_function',
608
+ 'function_expression',
609
+ ]);
610
+ for (const node of functionNodes) {
611
+ if (isReactComponentNode(node, content)) {
612
+ const info = extractComponentInfo(node, filePath, content, this.config);
613
+ if (info) {
614
+ components.push(info);
615
+ }
616
+ }
617
+ }
618
+ return components;
619
+ }
620
+ /**
621
+ * Extract components from content using regex (fallback)
622
+ */
623
+ extractComponentsFromContent(filePath, content) {
624
+ const components = [];
625
+ // Match component patterns
626
+ const patterns = [
627
+ // Arrow function: const Button = ({ ... }) => ...
628
+ /(?:export\s+)?const\s+([A-Z][a-zA-Z0-9]*)\s*(?::\s*(?:React\.)?(?:FC|FunctionComponent)\s*<[^>]*>\s*)?=\s*\([^)]*\)\s*=>\s*[\s\S]*?(?=\n(?:export\s+)?(?:const|function|class)\s+[A-Z]|\n*$)/g,
629
+ // Function declaration: function Button({ ... }) { ... }
630
+ /(?:export\s+)?function\s+([A-Z][a-zA-Z0-9]*)\s*\([^)]*\)\s*\{[\s\S]*?\n\}/g,
631
+ ];
632
+ for (const pattern of patterns) {
633
+ let match;
634
+ while ((match = pattern.exec(content)) !== null) {
635
+ const name = match[1];
636
+ if (!name)
637
+ continue;
638
+ const sourceCode = match[0];
639
+ const beforeMatch = content.slice(0, match.index);
640
+ const line = beforeMatch.split('\n').length;
641
+ // Create a simple normalized representation
642
+ const normalizedAST = this.createSimpleNormalizedAST(sourceCode);
643
+ components.push({
644
+ name,
645
+ filePath,
646
+ line,
647
+ column: 1,
648
+ normalizedAST,
649
+ sourceCode,
650
+ astHash: hashNormalizedNode(normalizedAST),
651
+ });
652
+ }
653
+ }
654
+ return components;
655
+ }
656
+ /**
657
+ * Create a simple normalized AST from source code (fallback)
658
+ */
659
+ createSimpleNormalizedAST(sourceCode) {
660
+ // Normalize whitespace and create a simple structure
661
+ const normalized = sourceCode.replace(/\s+/g, ' ').trim();
662
+ return {
663
+ type: 'component',
664
+ value: normalized,
665
+ children: [],
666
+ originalText: sourceCode,
667
+ };
668
+ }
669
+ /**
670
+ * Check if a duplicate pair involves the current file
671
+ */
672
+ involvesCurrentFile(pair, currentFile) {
673
+ return pair.component1.filePath === currentFile ||
674
+ pair.component2.filePath === currentFile;
675
+ }
676
+ /**
677
+ * Create a violation for a duplicate pair
678
+ */
679
+ createDuplicateViolation(pair, currentFile) {
680
+ const { component1, component2, duplicateType, similarity } = pair;
681
+ // Determine which component is in the current file
682
+ const currentComponent = component1.filePath === currentFile ? component1 : component2;
683
+ const otherComponent = component1.filePath === currentFile ? component2 : component1;
684
+ const percentSimilar = Math.round(similarity * 100);
685
+ const suggestion = generateRefactoringSuggestion(pair);
686
+ const severityMap = {
687
+ 'exact': 'warning',
688
+ 'near': 'info',
689
+ 'structural': 'info',
690
+ };
691
+ const range = {
692
+ start: { line: currentComponent.line, character: currentComponent.column },
693
+ end: { line: currentComponent.line, character: currentComponent.column + currentComponent.name.length },
694
+ };
695
+ return {
696
+ id: `duplicate-${currentComponent.name}-${otherComponent.name}-${currentFile.replace(/[^a-zA-Z0-9]/g, '-')}`,
697
+ patternId: 'components/duplicate-detection',
698
+ severity: severityMap[duplicateType],
699
+ file: currentFile,
700
+ range,
701
+ message: `${duplicateType === 'exact' ? 'Exact' : duplicateType === 'structural' ? 'Structural' : 'Near'} duplicate detected: '${currentComponent.name}' is ${percentSimilar}% similar to '${otherComponent.name}' in ${otherComponent.filePath}:${otherComponent.line}`,
702
+ explanation: suggestion,
703
+ expected: 'Unique component or intentional reuse through shared abstraction',
704
+ actual: `${percentSimilar}% similarity with ${otherComponent.name}`,
705
+ quickFix: this.createExtractQuickFix(pair, currentFile),
706
+ aiExplainAvailable: true,
707
+ aiFixAvailable: true,
708
+ firstSeen: new Date(),
709
+ occurrences: 1,
710
+ };
711
+ }
712
+ /**
713
+ * Create a quick fix for extracting duplicates into a shared component
714
+ */
715
+ createExtractQuickFix(pair, currentFile) {
716
+ const { component1, component2, duplicateType } = pair;
717
+ const sharedName = this.generateSharedComponentName(component1.name, component2.name);
718
+ let title;
719
+ let preview;
720
+ if (duplicateType === 'exact') {
721
+ title = `Extract to shared component '${sharedName}'`;
722
+ preview = `Create a shared component '${sharedName}' and replace both usages`;
723
+ }
724
+ else if (duplicateType === 'structural') {
725
+ title = `Create generic component '${sharedName}' with props`;
726
+ preview = `Create a generic component that accepts the differing values as props`;
727
+ }
728
+ else {
729
+ title = `Refactor to shared component '${sharedName}'`;
730
+ preview = `Extract common logic into a shared component`;
731
+ }
732
+ return {
733
+ title,
734
+ kind: 'refactor',
735
+ edit: {
736
+ changes: {},
737
+ documentChanges: [
738
+ { uri: currentFile, edits: [] },
739
+ ],
740
+ },
741
+ isPreferred: true,
742
+ confidence: pair.similarity,
743
+ preview,
744
+ };
745
+ }
746
+ /**
747
+ * Generate a name for a shared component
748
+ */
749
+ generateSharedComponentName(name1, name2) {
750
+ // Find common prefix
751
+ let commonPrefix = '';
752
+ const minLen = Math.min(name1.length, name2.length);
753
+ for (let i = 0; i < minLen; i++) {
754
+ if (name1[i] === name2[i]) {
755
+ commonPrefix += name1[i];
756
+ }
757
+ else {
758
+ break;
759
+ }
760
+ }
761
+ if (commonPrefix.length >= 3) {
762
+ return `Shared${commonPrefix}`;
763
+ }
764
+ // Use the shorter name as base
765
+ const baseName = name1.length <= name2.length ? name1 : name2;
766
+ return `Shared${baseName}`;
767
+ }
768
+ /**
769
+ * Generate a quick fix for a violation
770
+ */
771
+ generateQuickFix(violation) {
772
+ // Extract component names from the violation message
773
+ const match = violation.message.match(/similar to '([^']+)'/);
774
+ if (!match || !match[1]) {
775
+ return null;
776
+ }
777
+ const otherComponentName = match[1];
778
+ const currentComponentName = violation.message.match(/duplicate detected: '([^']+)'/)?.[1] || 'Component';
779
+ const sharedName = this.generateSharedComponentName(currentComponentName, otherComponentName);
780
+ return {
781
+ title: `Extract to shared component '${sharedName}'`,
782
+ kind: 'refactor',
783
+ edit: {
784
+ changes: {},
785
+ documentChanges: [
786
+ { uri: violation.file, edits: [] },
787
+ ],
788
+ },
789
+ isPreferred: true,
790
+ confidence: 0.8,
791
+ preview: `Create a shared component '${sharedName}' to eliminate duplication`,
792
+ };
793
+ }
794
+ }
795
+ // ============================================================================
796
+ // Factory Function
797
+ // ============================================================================
798
+ /**
799
+ * Create a new DuplicateDetector instance
800
+ */
801
+ export function createDuplicateDetector(config) {
802
+ return new DuplicateDetector(config);
803
+ }
804
+ //# sourceMappingURL=duplicate-detection.js.map