@skillsmith/core 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 (686) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/src/__tests__/errors.test.d.ts +5 -0
  3. package/dist/src/__tests__/errors.test.d.ts.map +1 -0
  4. package/dist/src/__tests__/errors.test.js +115 -0
  5. package/dist/src/__tests__/errors.test.js.map +1 -0
  6. package/dist/src/activation/ActivationManager.d.ts +141 -0
  7. package/dist/src/activation/ActivationManager.d.ts.map +1 -0
  8. package/dist/src/activation/ActivationManager.js +282 -0
  9. package/dist/src/activation/ActivationManager.js.map +1 -0
  10. package/dist/src/activation/ZeroConfigActivator.d.ts +126 -0
  11. package/dist/src/activation/ZeroConfigActivator.d.ts.map +1 -0
  12. package/dist/src/activation/ZeroConfigActivator.js +259 -0
  13. package/dist/src/activation/ZeroConfigActivator.js.map +1 -0
  14. package/dist/src/activation/index.d.ts +8 -0
  15. package/dist/src/activation/index.d.ts.map +1 -0
  16. package/dist/src/activation/index.js +8 -0
  17. package/dist/src/activation/index.js.map +1 -0
  18. package/dist/src/analysis/CodebaseAnalyzer.d.ts +175 -0
  19. package/dist/src/analysis/CodebaseAnalyzer.d.ts.map +1 -0
  20. package/dist/src/analysis/CodebaseAnalyzer.js +495 -0
  21. package/dist/src/analysis/CodebaseAnalyzer.js.map +1 -0
  22. package/dist/src/analysis/index.d.ts +10 -0
  23. package/dist/src/analysis/index.d.ts.map +1 -0
  24. package/dist/src/analysis/index.js +10 -0
  25. package/dist/src/analysis/index.js.map +1 -0
  26. package/dist/src/analytics/AnalyticsRepository.d.ts +97 -0
  27. package/dist/src/analytics/AnalyticsRepository.d.ts.map +1 -0
  28. package/dist/src/analytics/AnalyticsRepository.js +376 -0
  29. package/dist/src/analytics/AnalyticsRepository.js.map +1 -0
  30. package/dist/src/analytics/ExperimentService.d.ts +70 -0
  31. package/dist/src/analytics/ExperimentService.d.ts.map +1 -0
  32. package/dist/src/analytics/ExperimentService.js +251 -0
  33. package/dist/src/analytics/ExperimentService.js.map +1 -0
  34. package/dist/src/analytics/ROIDashboardService.d.ts +55 -0
  35. package/dist/src/analytics/ROIDashboardService.d.ts.map +1 -0
  36. package/dist/src/analytics/ROIDashboardService.js +304 -0
  37. package/dist/src/analytics/ROIDashboardService.js.map +1 -0
  38. package/dist/src/analytics/UsageAnalyticsService.d.ts +55 -0
  39. package/dist/src/analytics/UsageAnalyticsService.d.ts.map +1 -0
  40. package/dist/src/analytics/UsageAnalyticsService.js +180 -0
  41. package/dist/src/analytics/UsageAnalyticsService.js.map +1 -0
  42. package/dist/src/analytics/anonymizer.d.ts +53 -0
  43. package/dist/src/analytics/anonymizer.d.ts.map +1 -0
  44. package/dist/src/analytics/anonymizer.js +107 -0
  45. package/dist/src/analytics/anonymizer.js.map +1 -0
  46. package/dist/src/analytics/constants.d.ts +19 -0
  47. package/dist/src/analytics/constants.d.ts.map +1 -0
  48. package/dist/src/analytics/constants.js +19 -0
  49. package/dist/src/analytics/constants.js.map +1 -0
  50. package/dist/src/analytics/index.d.ts +23 -0
  51. package/dist/src/analytics/index.d.ts.map +1 -0
  52. package/dist/src/analytics/index.js +23 -0
  53. package/dist/src/analytics/index.js.map +1 -0
  54. package/dist/src/analytics/metrics-aggregator.d.ts +98 -0
  55. package/dist/src/analytics/metrics-aggregator.d.ts.map +1 -0
  56. package/dist/src/analytics/metrics-aggregator.js +176 -0
  57. package/dist/src/analytics/metrics-aggregator.js.map +1 -0
  58. package/dist/src/analytics/metrics-exporter.d.ts +148 -0
  59. package/dist/src/analytics/metrics-exporter.d.ts.map +1 -0
  60. package/dist/src/analytics/metrics-exporter.js +244 -0
  61. package/dist/src/analytics/metrics-exporter.js.map +1 -0
  62. package/dist/src/analytics/schema.d.ts +20 -0
  63. package/dist/src/analytics/schema.d.ts.map +1 -0
  64. package/dist/src/analytics/schema.js +125 -0
  65. package/dist/src/analytics/schema.js.map +1 -0
  66. package/dist/src/analytics/storage.d.ts +76 -0
  67. package/dist/src/analytics/storage.d.ts.map +1 -0
  68. package/dist/src/analytics/storage.js +180 -0
  69. package/dist/src/analytics/storage.js.map +1 -0
  70. package/dist/src/analytics/types.d.ts +277 -0
  71. package/dist/src/analytics/types.d.ts.map +1 -0
  72. package/dist/src/analytics/types.js +10 -0
  73. package/dist/src/analytics/types.js.map +1 -0
  74. package/dist/src/analytics/usage-tracker.d.ts +132 -0
  75. package/dist/src/analytics/usage-tracker.d.ts.map +1 -0
  76. package/dist/src/analytics/usage-tracker.js +213 -0
  77. package/dist/src/analytics/usage-tracker.js.map +1 -0
  78. package/dist/src/benchmarks/BenchmarkRunner.d.ts +258 -0
  79. package/dist/src/benchmarks/BenchmarkRunner.d.ts.map +1 -0
  80. package/dist/src/benchmarks/BenchmarkRunner.js +453 -0
  81. package/dist/src/benchmarks/BenchmarkRunner.js.map +1 -0
  82. package/dist/src/benchmarks/IndexBenchmark.d.ts +101 -0
  83. package/dist/src/benchmarks/IndexBenchmark.d.ts.map +1 -0
  84. package/dist/src/benchmarks/IndexBenchmark.js +314 -0
  85. package/dist/src/benchmarks/IndexBenchmark.js.map +1 -0
  86. package/dist/src/benchmarks/MemoryProfiler.d.ts +266 -0
  87. package/dist/src/benchmarks/MemoryProfiler.d.ts.map +1 -0
  88. package/dist/src/benchmarks/MemoryProfiler.js +404 -0
  89. package/dist/src/benchmarks/MemoryProfiler.js.map +1 -0
  90. package/dist/src/benchmarks/SearchBenchmark.d.ts +71 -0
  91. package/dist/src/benchmarks/SearchBenchmark.d.ts.map +1 -0
  92. package/dist/src/benchmarks/SearchBenchmark.js +321 -0
  93. package/dist/src/benchmarks/SearchBenchmark.js.map +1 -0
  94. package/dist/src/benchmarks/cacheBenchmark.d.ts +75 -0
  95. package/dist/src/benchmarks/cacheBenchmark.d.ts.map +1 -0
  96. package/dist/src/benchmarks/cacheBenchmark.js +325 -0
  97. package/dist/src/benchmarks/cacheBenchmark.js.map +1 -0
  98. package/dist/src/benchmarks/cli.d.ts +17 -0
  99. package/dist/src/benchmarks/cli.d.ts.map +1 -0
  100. package/dist/src/benchmarks/cli.js +140 -0
  101. package/dist/src/benchmarks/cli.js.map +1 -0
  102. package/dist/src/benchmarks/embeddingBenchmark.d.ts +86 -0
  103. package/dist/src/benchmarks/embeddingBenchmark.d.ts.map +1 -0
  104. package/dist/src/benchmarks/embeddingBenchmark.js +329 -0
  105. package/dist/src/benchmarks/embeddingBenchmark.js.map +1 -0
  106. package/dist/src/benchmarks/index.d.ts +51 -0
  107. package/dist/src/benchmarks/index.d.ts.map +1 -0
  108. package/dist/src/benchmarks/index.js +201 -0
  109. package/dist/src/benchmarks/index.js.map +1 -0
  110. package/dist/src/benchmarks/stats.d.ts +56 -0
  111. package/dist/src/benchmarks/stats.d.ts.map +1 -0
  112. package/dist/src/benchmarks/stats.js +86 -0
  113. package/dist/src/benchmarks/stats.js.map +1 -0
  114. package/dist/src/cache/CacheEntry.d.ts +101 -0
  115. package/dist/src/cache/CacheEntry.d.ts.map +1 -0
  116. package/dist/src/cache/CacheEntry.js +238 -0
  117. package/dist/src/cache/CacheEntry.js.map +1 -0
  118. package/dist/src/cache/CacheManager.d.ts +167 -0
  119. package/dist/src/cache/CacheManager.d.ts.map +1 -0
  120. package/dist/src/cache/CacheManager.js +346 -0
  121. package/dist/src/cache/CacheManager.js.map +1 -0
  122. package/dist/src/cache/TieredCache.d.ts +97 -0
  123. package/dist/src/cache/TieredCache.d.ts.map +1 -0
  124. package/dist/src/cache/TieredCache.js +352 -0
  125. package/dist/src/cache/TieredCache.js.map +1 -0
  126. package/dist/src/cache/index.d.ts +63 -0
  127. package/dist/src/cache/index.d.ts.map +1 -0
  128. package/dist/src/cache/index.js +91 -0
  129. package/dist/src/cache/index.js.map +1 -0
  130. package/dist/src/cache/lru.d.ts +68 -0
  131. package/dist/src/cache/lru.d.ts.map +1 -0
  132. package/dist/src/cache/lru.js +105 -0
  133. package/dist/src/cache/lru.js.map +1 -0
  134. package/dist/src/cache/sqlite.d.ts +59 -0
  135. package/dist/src/cache/sqlite.d.ts.map +1 -0
  136. package/dist/src/cache/sqlite.js +159 -0
  137. package/dist/src/cache/sqlite.js.map +1 -0
  138. package/dist/src/db/quarantine-schema.d.ts +81 -0
  139. package/dist/src/db/quarantine-schema.d.ts.map +1 -0
  140. package/dist/src/db/quarantine-schema.js +110 -0
  141. package/dist/src/db/quarantine-schema.js.map +1 -0
  142. package/dist/src/db/schema.d.ts +65 -0
  143. package/dist/src/db/schema.d.ts.map +1 -0
  144. package/dist/src/db/schema.js +318 -0
  145. package/dist/src/db/schema.js.map +1 -0
  146. package/dist/src/embeddings/index.d.ts +158 -0
  147. package/dist/src/embeddings/index.d.ts.map +1 -0
  148. package/dist/src/embeddings/index.js +397 -0
  149. package/dist/src/embeddings/index.js.map +1 -0
  150. package/dist/src/errors/SkillsmithError.d.ts +118 -0
  151. package/dist/src/errors/SkillsmithError.d.ts.map +1 -0
  152. package/dist/src/errors/SkillsmithError.js +194 -0
  153. package/dist/src/errors/SkillsmithError.js.map +1 -0
  154. package/dist/src/errors/index.d.ts +36 -0
  155. package/dist/src/errors/index.d.ts.map +1 -0
  156. package/dist/src/errors/index.js +36 -0
  157. package/dist/src/errors/index.js.map +1 -0
  158. package/dist/src/errors.d.ts +72 -0
  159. package/dist/src/errors.d.ts.map +1 -0
  160. package/dist/src/errors.js +123 -0
  161. package/dist/src/errors.js.map +1 -0
  162. package/dist/src/index.d.ts +56 -0
  163. package/dist/src/index.d.ts.map +1 -0
  164. package/dist/src/index.js +73 -0
  165. package/dist/src/index.js.map +1 -0
  166. package/dist/src/index.test.d.ts +2 -0
  167. package/dist/src/index.test.d.ts.map +1 -0
  168. package/dist/src/index.test.js +35 -0
  169. package/dist/src/index.test.js.map +1 -0
  170. package/dist/src/indexer/GitHubIndexer.d.ts +107 -0
  171. package/dist/src/indexer/GitHubIndexer.d.ts.map +1 -0
  172. package/dist/src/indexer/GitHubIndexer.js +202 -0
  173. package/dist/src/indexer/GitHubIndexer.js.map +1 -0
  174. package/dist/src/indexer/PartitionStrategy.d.ts +115 -0
  175. package/dist/src/indexer/PartitionStrategy.d.ts.map +1 -0
  176. package/dist/src/indexer/PartitionStrategy.js +207 -0
  177. package/dist/src/indexer/PartitionStrategy.js.map +1 -0
  178. package/dist/src/indexer/SkillParser.d.ts +112 -0
  179. package/dist/src/indexer/SkillParser.d.ts.map +1 -0
  180. package/dist/src/indexer/SkillParser.js +281 -0
  181. package/dist/src/indexer/SkillParser.js.map +1 -0
  182. package/dist/src/indexer/SwarmIndexer.d.ts +163 -0
  183. package/dist/src/indexer/SwarmIndexer.d.ts.map +1 -0
  184. package/dist/src/indexer/SwarmIndexer.js +300 -0
  185. package/dist/src/indexer/SwarmIndexer.js.map +1 -0
  186. package/dist/src/indexer/index.d.ts +12 -0
  187. package/dist/src/indexer/index.d.ts.map +1 -0
  188. package/dist/src/indexer/index.js +16 -0
  189. package/dist/src/indexer/index.js.map +1 -0
  190. package/dist/src/learning/interfaces.d.ts +338 -0
  191. package/dist/src/learning/interfaces.d.ts.map +1 -0
  192. package/dist/src/learning/interfaces.js +13 -0
  193. package/dist/src/learning/interfaces.js.map +1 -0
  194. package/dist/src/learning/types.d.ts +284 -0
  195. package/dist/src/learning/types.d.ts.map +1 -0
  196. package/dist/src/learning/types.js +112 -0
  197. package/dist/src/learning/types.js.map +1 -0
  198. package/dist/src/matching/OverlapDetector.d.ts +152 -0
  199. package/dist/src/matching/OverlapDetector.d.ts.map +1 -0
  200. package/dist/src/matching/OverlapDetector.js +218 -0
  201. package/dist/src/matching/OverlapDetector.js.map +1 -0
  202. package/dist/src/matching/SkillMatcher.d.ts +125 -0
  203. package/dist/src/matching/SkillMatcher.d.ts.map +1 -0
  204. package/dist/src/matching/SkillMatcher.js +206 -0
  205. package/dist/src/matching/SkillMatcher.js.map +1 -0
  206. package/dist/src/matching/index.d.ts +14 -0
  207. package/dist/src/matching/index.d.ts.map +1 -0
  208. package/dist/src/matching/index.js +12 -0
  209. package/dist/src/matching/index.js.map +1 -0
  210. package/dist/src/pipeline/DailyIndexPipeline.d.ts +220 -0
  211. package/dist/src/pipeline/DailyIndexPipeline.d.ts.map +1 -0
  212. package/dist/src/pipeline/DailyIndexPipeline.js +320 -0
  213. package/dist/src/pipeline/DailyIndexPipeline.js.map +1 -0
  214. package/dist/src/pipeline/index.d.ts +9 -0
  215. package/dist/src/pipeline/index.d.ts.map +1 -0
  216. package/dist/src/pipeline/index.js +9 -0
  217. package/dist/src/pipeline/index.js.map +1 -0
  218. package/dist/src/repositories/CacheRepository.d.ts +60 -0
  219. package/dist/src/repositories/CacheRepository.d.ts.map +1 -0
  220. package/dist/src/repositories/CacheRepository.js +148 -0
  221. package/dist/src/repositories/CacheRepository.js.map +1 -0
  222. package/dist/src/repositories/IndexerRepository.d.ts +126 -0
  223. package/dist/src/repositories/IndexerRepository.d.ts.map +1 -0
  224. package/dist/src/repositories/IndexerRepository.js +270 -0
  225. package/dist/src/repositories/IndexerRepository.js.map +1 -0
  226. package/dist/src/repositories/QuarantineRepository.d.ts +255 -0
  227. package/dist/src/repositories/QuarantineRepository.d.ts.map +1 -0
  228. package/dist/src/repositories/QuarantineRepository.js +445 -0
  229. package/dist/src/repositories/QuarantineRepository.js.map +1 -0
  230. package/dist/src/repositories/SkillRepository.d.ts +78 -0
  231. package/dist/src/repositories/SkillRepository.d.ts.map +1 -0
  232. package/dist/src/repositories/SkillRepository.js +208 -0
  233. package/dist/src/repositories/SkillRepository.js.map +1 -0
  234. package/dist/src/scoring/QualityScorer.d.ts +188 -0
  235. package/dist/src/scoring/QualityScorer.d.ts.map +1 -0
  236. package/dist/src/scoring/QualityScorer.js +342 -0
  237. package/dist/src/scoring/QualityScorer.js.map +1 -0
  238. package/dist/src/scoring/index.d.ts +9 -0
  239. package/dist/src/scoring/index.d.ts.map +1 -0
  240. package/dist/src/scoring/index.js +9 -0
  241. package/dist/src/scoring/index.js.map +1 -0
  242. package/dist/src/scripts/__tests__/scan-imported-skills.test.d.ts +5 -0
  243. package/dist/src/scripts/__tests__/scan-imported-skills.test.d.ts.map +1 -0
  244. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +365 -0
  245. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -0
  246. package/dist/src/scripts/import-github-skills.d.ts +24 -0
  247. package/dist/src/scripts/import-github-skills.d.ts.map +1 -0
  248. package/dist/src/scripts/import-github-skills.js +545 -0
  249. package/dist/src/scripts/import-github-skills.js.map +1 -0
  250. package/dist/src/scripts/import-to-database.d.ts +60 -0
  251. package/dist/src/scripts/import-to-database.d.ts.map +1 -0
  252. package/dist/src/scripts/import-to-database.js +307 -0
  253. package/dist/src/scripts/import-to-database.js.map +1 -0
  254. package/dist/src/scripts/scan-imported-skills.d.ts +15 -0
  255. package/dist/src/scripts/scan-imported-skills.d.ts.map +1 -0
  256. package/dist/src/scripts/scan-imported-skills.js +405 -0
  257. package/dist/src/scripts/scan-imported-skills.js.map +1 -0
  258. package/dist/src/scripts/validate-skills.d.ts +180 -0
  259. package/dist/src/scripts/validate-skills.d.ts.map +1 -0
  260. package/dist/src/scripts/validate-skills.js +572 -0
  261. package/dist/src/scripts/validate-skills.js.map +1 -0
  262. package/dist/src/search/hybrid.d.ts +85 -0
  263. package/dist/src/search/hybrid.d.ts.map +1 -0
  264. package/dist/src/search/hybrid.js +291 -0
  265. package/dist/src/search/hybrid.js.map +1 -0
  266. package/dist/src/search/index.d.ts +6 -0
  267. package/dist/src/search/index.d.ts.map +1 -0
  268. package/dist/src/search/index.js +5 -0
  269. package/dist/src/search/index.js.map +1 -0
  270. package/dist/src/security/AuditLogger.d.ts +197 -0
  271. package/dist/src/security/AuditLogger.d.ts.map +1 -0
  272. package/dist/src/security/AuditLogger.js +398 -0
  273. package/dist/src/security/AuditLogger.js.map +1 -0
  274. package/dist/src/security/RateLimiter.d.ts +337 -0
  275. package/dist/src/security/RateLimiter.d.ts.map +1 -0
  276. package/dist/src/security/RateLimiter.js +782 -0
  277. package/dist/src/security/RateLimiter.js.map +1 -0
  278. package/dist/src/security/__tests__/pathValidation.test.d.ts +8 -0
  279. package/dist/src/security/__tests__/pathValidation.test.d.ts.map +1 -0
  280. package/dist/src/security/__tests__/pathValidation.test.js +249 -0
  281. package/dist/src/security/__tests__/pathValidation.test.js.map +1 -0
  282. package/dist/src/security/index.d.ts +18 -0
  283. package/dist/src/security/index.d.ts.map +1 -0
  284. package/dist/src/security/index.js +14 -0
  285. package/dist/src/security/index.js.map +1 -0
  286. package/dist/src/security/pathValidation.d.ts +95 -0
  287. package/dist/src/security/pathValidation.d.ts.map +1 -0
  288. package/dist/src/security/pathValidation.js +216 -0
  289. package/dist/src/security/pathValidation.js.map +1 -0
  290. package/dist/src/security/sanitization.d.ts +123 -0
  291. package/dist/src/security/sanitization.d.ts.map +1 -0
  292. package/dist/src/security/sanitization.js +378 -0
  293. package/dist/src/security/sanitization.js.map +1 -0
  294. package/dist/src/security/scanner.d.ts +151 -0
  295. package/dist/src/security/scanner.d.ts.map +1 -0
  296. package/dist/src/security/scanner.js +599 -0
  297. package/dist/src/security/scanner.js.map +1 -0
  298. package/dist/src/services/SearchService.d.ts +88 -0
  299. package/dist/src/services/SearchService.d.ts.map +1 -0
  300. package/dist/src/services/SearchService.js +305 -0
  301. package/dist/src/services/SearchService.js.map +1 -0
  302. package/dist/src/session/SessionContext.d.ts +116 -0
  303. package/dist/src/session/SessionContext.d.ts.map +1 -0
  304. package/dist/src/session/SessionContext.js +134 -0
  305. package/dist/src/session/SessionContext.js.map +1 -0
  306. package/dist/src/session/SessionHealthMonitor.d.ts +249 -0
  307. package/dist/src/session/SessionHealthMonitor.d.ts.map +1 -0
  308. package/dist/src/session/SessionHealthMonitor.js +302 -0
  309. package/dist/src/session/SessionHealthMonitor.js.map +1 -0
  310. package/dist/src/session/SessionManager.d.ts +179 -0
  311. package/dist/src/session/SessionManager.d.ts.map +1 -0
  312. package/dist/src/session/SessionManager.js +451 -0
  313. package/dist/src/session/SessionManager.js.map +1 -0
  314. package/dist/src/session/SessionRecovery.d.ts +84 -0
  315. package/dist/src/session/SessionRecovery.d.ts.map +1 -0
  316. package/dist/src/session/SessionRecovery.js +257 -0
  317. package/dist/src/session/SessionRecovery.js.map +1 -0
  318. package/dist/src/session/index.d.ts +40 -0
  319. package/dist/src/session/index.d.ts.map +1 -0
  320. package/dist/src/session/index.js +44 -0
  321. package/dist/src/session/index.js.map +1 -0
  322. package/dist/src/sources/BaseSourceAdapter.d.ts +144 -0
  323. package/dist/src/sources/BaseSourceAdapter.d.ts.map +1 -0
  324. package/dist/src/sources/BaseSourceAdapter.js +287 -0
  325. package/dist/src/sources/BaseSourceAdapter.js.map +1 -0
  326. package/dist/src/sources/GitHubSourceAdapter.d.ts +98 -0
  327. package/dist/src/sources/GitHubSourceAdapter.d.ts.map +1 -0
  328. package/dist/src/sources/GitHubSourceAdapter.js +269 -0
  329. package/dist/src/sources/GitHubSourceAdapter.js.map +1 -0
  330. package/dist/src/sources/GitLabSourceAdapter.d.ts +102 -0
  331. package/dist/src/sources/GitLabSourceAdapter.d.ts.map +1 -0
  332. package/dist/src/sources/GitLabSourceAdapter.js +310 -0
  333. package/dist/src/sources/GitLabSourceAdapter.js.map +1 -0
  334. package/dist/src/sources/ISourceAdapter.d.ts +110 -0
  335. package/dist/src/sources/ISourceAdapter.d.ts.map +1 -0
  336. package/dist/src/sources/ISourceAdapter.js +19 -0
  337. package/dist/src/sources/ISourceAdapter.js.map +1 -0
  338. package/dist/src/sources/LocalFilesystemAdapter.d.ts +112 -0
  339. package/dist/src/sources/LocalFilesystemAdapter.d.ts.map +1 -0
  340. package/dist/src/sources/LocalFilesystemAdapter.js +340 -0
  341. package/dist/src/sources/LocalFilesystemAdapter.js.map +1 -0
  342. package/dist/src/sources/RawUrlSourceAdapter.d.ts +128 -0
  343. package/dist/src/sources/RawUrlSourceAdapter.d.ts.map +1 -0
  344. package/dist/src/sources/RawUrlSourceAdapter.js +282 -0
  345. package/dist/src/sources/RawUrlSourceAdapter.js.map +1 -0
  346. package/dist/src/sources/SourceAdapterRegistry.d.ts +156 -0
  347. package/dist/src/sources/SourceAdapterRegistry.d.ts.map +1 -0
  348. package/dist/src/sources/SourceAdapterRegistry.js +242 -0
  349. package/dist/src/sources/SourceAdapterRegistry.js.map +1 -0
  350. package/dist/src/sources/SourceIndexer.d.ts +119 -0
  351. package/dist/src/sources/SourceIndexer.d.ts.map +1 -0
  352. package/dist/src/sources/SourceIndexer.js +285 -0
  353. package/dist/src/sources/SourceIndexer.js.map +1 -0
  354. package/dist/src/sources/index.d.ts +45 -0
  355. package/dist/src/sources/index.d.ts.map +1 -0
  356. package/dist/src/sources/index.js +51 -0
  357. package/dist/src/sources/index.js.map +1 -0
  358. package/dist/src/sources/shared.d.ts +125 -0
  359. package/dist/src/sources/shared.d.ts.map +1 -0
  360. package/dist/src/sources/shared.js +191 -0
  361. package/dist/src/sources/shared.js.map +1 -0
  362. package/dist/src/sources/types.d.ts +204 -0
  363. package/dist/src/sources/types.d.ts.map +1 -0
  364. package/dist/src/sources/types.js +6 -0
  365. package/dist/src/sources/types.js.map +1 -0
  366. package/dist/src/telemetry/index.d.ts +25 -0
  367. package/dist/src/telemetry/index.d.ts.map +1 -0
  368. package/dist/src/telemetry/index.js +32 -0
  369. package/dist/src/telemetry/index.js.map +1 -0
  370. package/dist/src/telemetry/metrics.d.ts +171 -0
  371. package/dist/src/telemetry/metrics.d.ts.map +1 -0
  372. package/dist/src/telemetry/metrics.js +401 -0
  373. package/dist/src/telemetry/metrics.js.map +1 -0
  374. package/dist/src/telemetry/prometheus.d.ts +81 -0
  375. package/dist/src/telemetry/prometheus.d.ts.map +1 -0
  376. package/dist/src/telemetry/prometheus.js +252 -0
  377. package/dist/src/telemetry/prometheus.js.map +1 -0
  378. package/dist/src/telemetry/tracer.d.ts +151 -0
  379. package/dist/src/telemetry/tracer.d.ts.map +1 -0
  380. package/dist/src/telemetry/tracer.js +391 -0
  381. package/dist/src/telemetry/tracer.js.map +1 -0
  382. package/dist/src/triggers/ContextScorer.d.ts +118 -0
  383. package/dist/src/triggers/ContextScorer.d.ts.map +1 -0
  384. package/dist/src/triggers/ContextScorer.js +265 -0
  385. package/dist/src/triggers/ContextScorer.js.map +1 -0
  386. package/dist/src/triggers/TriggerDetector.d.ts +178 -0
  387. package/dist/src/triggers/TriggerDetector.d.ts.map +1 -0
  388. package/dist/src/triggers/TriggerDetector.js +390 -0
  389. package/dist/src/triggers/TriggerDetector.js.map +1 -0
  390. package/dist/src/triggers/__tests__/ContextScorer.test.d.ts +6 -0
  391. package/dist/src/triggers/__tests__/ContextScorer.test.d.ts.map +1 -0
  392. package/dist/src/triggers/__tests__/ContextScorer.test.js +307 -0
  393. package/dist/src/triggers/__tests__/ContextScorer.test.js.map +1 -0
  394. package/dist/src/triggers/__tests__/TriggerDetector.test.d.ts +6 -0
  395. package/dist/src/triggers/__tests__/TriggerDetector.test.d.ts.map +1 -0
  396. package/dist/src/triggers/__tests__/TriggerDetector.test.js +249 -0
  397. package/dist/src/triggers/__tests__/TriggerDetector.test.js.map +1 -0
  398. package/dist/src/triggers/index.d.ts +8 -0
  399. package/dist/src/triggers/index.d.ts.map +1 -0
  400. package/dist/src/triggers/index.js +8 -0
  401. package/dist/src/triggers/index.js.map +1 -0
  402. package/dist/src/types/skill.d.ts +80 -0
  403. package/dist/src/types/skill.d.ts.map +1 -0
  404. package/dist/src/types/skill.js +5 -0
  405. package/dist/src/types/skill.js.map +1 -0
  406. package/dist/src/types.d.ts +88 -0
  407. package/dist/src/types.d.ts.map +1 -0
  408. package/dist/src/types.js +13 -0
  409. package/dist/src/types.js.map +1 -0
  410. package/dist/src/utils/index.d.ts +6 -0
  411. package/dist/src/utils/index.d.ts.map +1 -0
  412. package/dist/src/utils/index.js +6 -0
  413. package/dist/src/utils/index.js.map +1 -0
  414. package/dist/src/utils/logger.d.ts +172 -0
  415. package/dist/src/utils/logger.d.ts.map +1 -0
  416. package/dist/src/utils/logger.js +291 -0
  417. package/dist/src/utils/logger.js.map +1 -0
  418. package/dist/src/utils/retry.d.ts +97 -0
  419. package/dist/src/utils/retry.d.ts.map +1 -0
  420. package/dist/src/utils/retry.js +267 -0
  421. package/dist/src/utils/retry.js.map +1 -0
  422. package/dist/src/validation/index.d.ts +118 -0
  423. package/dist/src/validation/index.d.ts.map +1 -0
  424. package/dist/src/validation/index.js +434 -0
  425. package/dist/src/validation/index.js.map +1 -0
  426. package/dist/src/webhooks/WebhookHandler.d.ts +117 -0
  427. package/dist/src/webhooks/WebhookHandler.d.ts.map +1 -0
  428. package/dist/src/webhooks/WebhookHandler.js +349 -0
  429. package/dist/src/webhooks/WebhookHandler.js.map +1 -0
  430. package/dist/src/webhooks/WebhookPayload.d.ts +238 -0
  431. package/dist/src/webhooks/WebhookPayload.d.ts.map +1 -0
  432. package/dist/src/webhooks/WebhookPayload.js +244 -0
  433. package/dist/src/webhooks/WebhookPayload.js.map +1 -0
  434. package/dist/src/webhooks/WebhookQueue.d.ts +227 -0
  435. package/dist/src/webhooks/WebhookQueue.d.ts.map +1 -0
  436. package/dist/src/webhooks/WebhookQueue.js +328 -0
  437. package/dist/src/webhooks/WebhookQueue.js.map +1 -0
  438. package/dist/src/webhooks/index.d.ts +12 -0
  439. package/dist/src/webhooks/index.d.ts.map +1 -0
  440. package/dist/src/webhooks/index.js +15 -0
  441. package/dist/src/webhooks/index.js.map +1 -0
  442. package/dist/tests/Analytics.integration.test.d.ts +7 -0
  443. package/dist/tests/Analytics.integration.test.d.ts.map +1 -0
  444. package/dist/tests/Analytics.integration.test.js +367 -0
  445. package/dist/tests/Analytics.integration.test.js.map +1 -0
  446. package/dist/tests/AnalyticsRepository.test.d.ts +8 -0
  447. package/dist/tests/AnalyticsRepository.test.d.ts.map +1 -0
  448. package/dist/tests/AnalyticsRepository.test.js +399 -0
  449. package/dist/tests/AnalyticsRepository.test.js.map +1 -0
  450. package/dist/tests/AnalyticsStorage.test.d.ts +8 -0
  451. package/dist/tests/AnalyticsStorage.test.d.ts.map +1 -0
  452. package/dist/tests/AnalyticsStorage.test.js +271 -0
  453. package/dist/tests/AnalyticsStorage.test.js.map +1 -0
  454. package/dist/tests/AuditLogger.test.d.ts +8 -0
  455. package/dist/tests/AuditLogger.test.d.ts.map +1 -0
  456. package/dist/tests/AuditLogger.test.js +699 -0
  457. package/dist/tests/AuditLogger.test.js.map +1 -0
  458. package/dist/tests/BenchmarkRunner.test.d.ts +11 -0
  459. package/dist/tests/BenchmarkRunner.test.d.ts.map +1 -0
  460. package/dist/tests/BenchmarkRunner.test.js +641 -0
  461. package/dist/tests/BenchmarkRunner.test.js.map +1 -0
  462. package/dist/tests/CacheRepository.test.d.ts +5 -0
  463. package/dist/tests/CacheRepository.test.d.ts.map +1 -0
  464. package/dist/tests/CacheRepository.test.js +184 -0
  465. package/dist/tests/CacheRepository.test.js.map +1 -0
  466. package/dist/tests/CacheSecurity.test.d.ts +8 -0
  467. package/dist/tests/CacheSecurity.test.d.ts.map +1 -0
  468. package/dist/tests/CacheSecurity.test.js +273 -0
  469. package/dist/tests/CacheSecurity.test.js.map +1 -0
  470. package/dist/tests/CodebaseAnalyzer.test.d.ts +2 -0
  471. package/dist/tests/CodebaseAnalyzer.test.d.ts.map +1 -0
  472. package/dist/tests/CodebaseAnalyzer.test.js +347 -0
  473. package/dist/tests/CodebaseAnalyzer.test.js.map +1 -0
  474. package/dist/tests/DailyIndexPipeline.test.d.ts +7 -0
  475. package/dist/tests/DailyIndexPipeline.test.d.ts.map +1 -0
  476. package/dist/tests/DailyIndexPipeline.test.js +539 -0
  477. package/dist/tests/DailyIndexPipeline.test.js.map +1 -0
  478. package/dist/tests/EmbeddingService.test.d.ts +2 -0
  479. package/dist/tests/EmbeddingService.test.d.ts.map +1 -0
  480. package/dist/tests/EmbeddingService.test.js +252 -0
  481. package/dist/tests/EmbeddingService.test.js.map +1 -0
  482. package/dist/tests/ExperimentService.test.d.ts +7 -0
  483. package/dist/tests/ExperimentService.test.d.ts.map +1 -0
  484. package/dist/tests/ExperimentService.test.js +293 -0
  485. package/dist/tests/ExperimentService.test.js.map +1 -0
  486. package/dist/tests/GitHubIndexer.test.d.ts +10 -0
  487. package/dist/tests/GitHubIndexer.test.d.ts.map +1 -0
  488. package/dist/tests/GitHubIndexer.test.js +524 -0
  489. package/dist/tests/GitHubIndexer.test.js.map +1 -0
  490. package/dist/tests/GitHubSourceAdapter.test.d.ts +5 -0
  491. package/dist/tests/GitHubSourceAdapter.test.d.ts.map +1 -0
  492. package/dist/tests/GitHubSourceAdapter.test.js +385 -0
  493. package/dist/tests/GitHubSourceAdapter.test.js.map +1 -0
  494. package/dist/tests/MemoryProfiler.test.d.ts +12 -0
  495. package/dist/tests/MemoryProfiler.test.d.ts.map +1 -0
  496. package/dist/tests/MemoryProfiler.test.js +402 -0
  497. package/dist/tests/MemoryProfiler.test.js.map +1 -0
  498. package/dist/tests/OverlapDetector.test.d.ts +2 -0
  499. package/dist/tests/OverlapDetector.test.d.ts.map +1 -0
  500. package/dist/tests/OverlapDetector.test.js +340 -0
  501. package/dist/tests/OverlapDetector.test.js.map +1 -0
  502. package/dist/tests/QualityScorer.test.d.ts +7 -0
  503. package/dist/tests/QualityScorer.test.d.ts.map +1 -0
  504. package/dist/tests/QualityScorer.test.js +340 -0
  505. package/dist/tests/QualityScorer.test.js.map +1 -0
  506. package/dist/tests/QuarantineRepository.test.d.ts +7 -0
  507. package/dist/tests/QuarantineRepository.test.d.ts.map +1 -0
  508. package/dist/tests/QuarantineRepository.test.js +582 -0
  509. package/dist/tests/QuarantineRepository.test.js.map +1 -0
  510. package/dist/tests/ROIDashboardService.test.d.ts +7 -0
  511. package/dist/tests/ROIDashboardService.test.d.ts.map +1 -0
  512. package/dist/tests/ROIDashboardService.test.js +324 -0
  513. package/dist/tests/ROIDashboardService.test.js.map +1 -0
  514. package/dist/tests/RateLimiter.test.d.ts +7 -0
  515. package/dist/tests/RateLimiter.test.d.ts.map +1 -0
  516. package/dist/tests/RateLimiter.test.js +1017 -0
  517. package/dist/tests/RateLimiter.test.js.map +1 -0
  518. package/dist/tests/RawUrlSourceAdapter.security.test.d.ts +7 -0
  519. package/dist/tests/RawUrlSourceAdapter.security.test.d.ts.map +1 -0
  520. package/dist/tests/RawUrlSourceAdapter.security.test.js +455 -0
  521. package/dist/tests/RawUrlSourceAdapter.security.test.js.map +1 -0
  522. package/dist/tests/ScraperAdapters.test.d.ts +7 -0
  523. package/dist/tests/ScraperAdapters.test.d.ts.map +1 -0
  524. package/dist/tests/ScraperAdapters.test.js +748 -0
  525. package/dist/tests/ScraperAdapters.test.js.map +1 -0
  526. package/dist/tests/SearchQuality.test.d.ts +8 -0
  527. package/dist/tests/SearchQuality.test.d.ts.map +1 -0
  528. package/dist/tests/SearchQuality.test.js +397 -0
  529. package/dist/tests/SearchQuality.test.js.map +1 -0
  530. package/dist/tests/SearchService.test.d.ts +5 -0
  531. package/dist/tests/SearchService.test.d.ts.map +1 -0
  532. package/dist/tests/SearchService.test.js +218 -0
  533. package/dist/tests/SearchService.test.js.map +1 -0
  534. package/dist/tests/SecurityScanner.test.d.ts +6 -0
  535. package/dist/tests/SecurityScanner.test.d.ts.map +1 -0
  536. package/dist/tests/SecurityScanner.test.js +449 -0
  537. package/dist/tests/SecurityScanner.test.js.map +1 -0
  538. package/dist/tests/SessionHealthMonitor.test.d.ts +5 -0
  539. package/dist/tests/SessionHealthMonitor.test.d.ts.map +1 -0
  540. package/dist/tests/SessionHealthMonitor.test.js +449 -0
  541. package/dist/tests/SessionHealthMonitor.test.js.map +1 -0
  542. package/dist/tests/SessionManager.security.test.d.ts +10 -0
  543. package/dist/tests/SessionManager.security.test.d.ts.map +1 -0
  544. package/dist/tests/SessionManager.security.test.js +395 -0
  545. package/dist/tests/SessionManager.security.test.js.map +1 -0
  546. package/dist/tests/SessionManager.test.d.ts +8 -0
  547. package/dist/tests/SessionManager.test.d.ts.map +1 -0
  548. package/dist/tests/SessionManager.test.js +446 -0
  549. package/dist/tests/SessionManager.test.js.map +1 -0
  550. package/dist/tests/SkillMatcher.test.d.ts +2 -0
  551. package/dist/tests/SkillMatcher.test.d.ts.map +1 -0
  552. package/dist/tests/SkillMatcher.test.js +253 -0
  553. package/dist/tests/SkillMatcher.test.js.map +1 -0
  554. package/dist/tests/SkillRepository.test.d.ts +5 -0
  555. package/dist/tests/SkillRepository.test.d.ts.map +1 -0
  556. package/dist/tests/SkillRepository.test.js +237 -0
  557. package/dist/tests/SkillRepository.test.js.map +1 -0
  558. package/dist/tests/SwarmIndexer.test.d.ts +11 -0
  559. package/dist/tests/SwarmIndexer.test.d.ts.map +1 -0
  560. package/dist/tests/SwarmIndexer.test.js +374 -0
  561. package/dist/tests/SwarmIndexer.test.js.map +1 -0
  562. package/dist/tests/TieredCache.test.d.ts +7 -0
  563. package/dist/tests/TieredCache.test.d.ts.map +1 -0
  564. package/dist/tests/TieredCache.test.js +529 -0
  565. package/dist/tests/TieredCache.test.js.map +1 -0
  566. package/dist/tests/UsageAnalyticsService.test.d.ts +7 -0
  567. package/dist/tests/UsageAnalyticsService.test.d.ts.map +1 -0
  568. package/dist/tests/UsageAnalyticsService.test.js +238 -0
  569. package/dist/tests/UsageAnalyticsService.test.js.map +1 -0
  570. package/dist/tests/UsageTracker.test.d.ts +7 -0
  571. package/dist/tests/UsageTracker.test.d.ts.map +1 -0
  572. package/dist/tests/UsageTracker.test.js +196 -0
  573. package/dist/tests/UsageTracker.test.js.map +1 -0
  574. package/dist/tests/WebhookHandler.test.d.ts +10 -0
  575. package/dist/tests/WebhookHandler.test.d.ts.map +1 -0
  576. package/dist/tests/WebhookHandler.test.js +592 -0
  577. package/dist/tests/WebhookHandler.test.js.map +1 -0
  578. package/dist/tests/analytics/metrics-aggregator.test.d.ts +11 -0
  579. package/dist/tests/analytics/metrics-aggregator.test.d.ts.map +1 -0
  580. package/dist/tests/analytics/metrics-aggregator.test.js +273 -0
  581. package/dist/tests/analytics/metrics-aggregator.test.js.map +1 -0
  582. package/dist/tests/analytics/metrics-exporter.test.d.ts +11 -0
  583. package/dist/tests/analytics/metrics-exporter.test.d.ts.map +1 -0
  584. package/dist/tests/analytics/metrics-exporter.test.js +371 -0
  585. package/dist/tests/analytics/metrics-exporter.test.js.map +1 -0
  586. package/dist/tests/analytics/usage-tracker.test.d.ts +10 -0
  587. package/dist/tests/analytics/usage-tracker.test.d.ts.map +1 -0
  588. package/dist/tests/analytics/usage-tracker.test.js +151 -0
  589. package/dist/tests/analytics/usage-tracker.test.js.map +1 -0
  590. package/dist/tests/anonymizer.test.d.ts +8 -0
  591. package/dist/tests/anonymizer.test.d.ts.map +1 -0
  592. package/dist/tests/anonymizer.test.js +153 -0
  593. package/dist/tests/anonymizer.test.js.map +1 -0
  594. package/dist/tests/cache.test.d.ts +6 -0
  595. package/dist/tests/cache.test.d.ts.map +1 -0
  596. package/dist/tests/cache.test.js +170 -0
  597. package/dist/tests/cache.test.js.map +1 -0
  598. package/dist/tests/e2e/security/security.e2e.test.d.ts +8 -0
  599. package/dist/tests/e2e/security/security.e2e.test.d.ts.map +1 -0
  600. package/dist/tests/e2e/security/security.e2e.test.js +448 -0
  601. package/dist/tests/e2e/security/security.e2e.test.js.map +1 -0
  602. package/dist/tests/edge-cases/EdgeCases.test.d.ts +13 -0
  603. package/dist/tests/edge-cases/EdgeCases.test.d.ts.map +1 -0
  604. package/dist/tests/edge-cases/EdgeCases.test.js +844 -0
  605. package/dist/tests/edge-cases/EdgeCases.test.js.map +1 -0
  606. package/dist/tests/import-github-skills.test.d.ts +8 -0
  607. package/dist/tests/import-github-skills.test.d.ts.map +1 -0
  608. package/dist/tests/import-github-skills.test.js +390 -0
  609. package/dist/tests/import-github-skills.test.js.map +1 -0
  610. package/dist/tests/logger.test.d.ts +2 -0
  611. package/dist/tests/logger.test.d.ts.map +1 -0
  612. package/dist/tests/logger.test.js +417 -0
  613. package/dist/tests/logger.test.js.map +1 -0
  614. package/dist/tests/performance/LargeScalePerformance.test.d.ts +14 -0
  615. package/dist/tests/performance/LargeScalePerformance.test.d.ts.map +1 -0
  616. package/dist/tests/performance/LargeScalePerformance.test.js +558 -0
  617. package/dist/tests/performance/LargeScalePerformance.test.js.map +1 -0
  618. package/dist/tests/retry.test.d.ts +7 -0
  619. package/dist/tests/retry.test.d.ts.map +1 -0
  620. package/dist/tests/retry.test.js +302 -0
  621. package/dist/tests/retry.test.js.map +1 -0
  622. package/dist/tests/sanitization.test.d.ts +8 -0
  623. package/dist/tests/sanitization.test.d.ts.map +1 -0
  624. package/dist/tests/sanitization.test.js +413 -0
  625. package/dist/tests/sanitization.test.js.map +1 -0
  626. package/dist/tests/schema.test.d.ts +5 -0
  627. package/dist/tests/schema.test.d.ts.map +1 -0
  628. package/dist/tests/schema.test.js +167 -0
  629. package/dist/tests/schema.test.js.map +1 -0
  630. package/dist/tests/scripts/import-to-database.test.d.ts +11 -0
  631. package/dist/tests/scripts/import-to-database.test.d.ts.map +1 -0
  632. package/dist/tests/scripts/import-to-database.test.js +325 -0
  633. package/dist/tests/scripts/import-to-database.test.js.map +1 -0
  634. package/dist/tests/security/ContinuousSecurity.test.d.ts +6 -0
  635. package/dist/tests/security/ContinuousSecurity.test.d.ts.map +1 -0
  636. package/dist/tests/security/ContinuousSecurity.test.js +595 -0
  637. package/dist/tests/security/ContinuousSecurity.test.js.map +1 -0
  638. package/dist/tests/security/ReDoS.test.d.ts +8 -0
  639. package/dist/tests/security/ReDoS.test.d.ts.map +1 -0
  640. package/dist/tests/security/ReDoS.test.js +213 -0
  641. package/dist/tests/security/ReDoS.test.js.map +1 -0
  642. package/dist/tests/security.test.d.ts +5 -0
  643. package/dist/tests/security.test.d.ts.map +1 -0
  644. package/dist/tests/security.test.js +134 -0
  645. package/dist/tests/security.test.js.map +1 -0
  646. package/dist/tests/shared.test.d.ts +7 -0
  647. package/dist/tests/shared.test.d.ts.map +1 -0
  648. package/dist/tests/shared.test.js +480 -0
  649. package/dist/tests/shared.test.js.map +1 -0
  650. package/dist/tests/sources.test.d.ts +5 -0
  651. package/dist/tests/sources.test.d.ts.map +1 -0
  652. package/dist/tests/sources.test.js +369 -0
  653. package/dist/tests/sources.test.js.map +1 -0
  654. package/dist/tests/stats.test.d.ts +11 -0
  655. package/dist/tests/stats.test.d.ts.map +1 -0
  656. package/dist/tests/stats.test.js +124 -0
  657. package/dist/tests/stats.test.js.map +1 -0
  658. package/dist/tests/telemetry.test.d.ts +11 -0
  659. package/dist/tests/telemetry.test.d.ts.map +1 -0
  660. package/dist/tests/telemetry.test.js +424 -0
  661. package/dist/tests/telemetry.test.js.map +1 -0
  662. package/dist/tests/test-utils.d.ts +74 -0
  663. package/dist/tests/test-utils.d.ts.map +1 -0
  664. package/dist/tests/test-utils.js +98 -0
  665. package/dist/tests/test-utils.js.map +1 -0
  666. package/dist/tests/validate-skills.test.d.ts +5 -0
  667. package/dist/tests/validate-skills.test.d.ts.map +1 -0
  668. package/dist/tests/validate-skills.test.js +649 -0
  669. package/dist/tests/validate-skills.test.js.map +1 -0
  670. package/dist/tests/validation.test.d.ts +7 -0
  671. package/dist/tests/validation.test.d.ts.map +1 -0
  672. package/dist/tests/validation.test.js +495 -0
  673. package/dist/tests/validation.test.js.map +1 -0
  674. package/dist/tests/webhooks/WebhookHandler.idempotency.test.d.ts +8 -0
  675. package/dist/tests/webhooks/WebhookHandler.idempotency.test.d.ts.map +1 -0
  676. package/dist/tests/webhooks/WebhookHandler.idempotency.test.js +190 -0
  677. package/dist/tests/webhooks/WebhookHandler.idempotency.test.js.map +1 -0
  678. package/dist/tests/webhooks/WebhookPayload.security.test.d.ts +8 -0
  679. package/dist/tests/webhooks/WebhookPayload.security.test.d.ts.map +1 -0
  680. package/dist/tests/webhooks/WebhookPayload.security.test.js +204 -0
  681. package/dist/tests/webhooks/WebhookPayload.security.test.js.map +1 -0
  682. package/dist/vitest.config.d.ts +3 -0
  683. package/dist/vitest.config.d.ts.map +1 -0
  684. package/dist/vitest.config.js +13 -0
  685. package/dist/vitest.config.js.map +1 -0
  686. package/package.json +77 -0
@@ -0,0 +1,1017 @@
1
+ /**
2
+ * Rate Limiter Tests - SMI-730
3
+ *
4
+ * Comprehensive tests for token bucket rate limiting.
5
+ */
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
+ import { RateLimiter, InMemoryRateLimitStorage, RATE_LIMIT_PRESETS, createRateLimiterFromPreset, RateLimitQueueTimeoutError, RateLimitQueueFullError, } from '../src/security/RateLimiter.js';
8
+ describe('RateLimiter - Token Bucket Algorithm', () => {
9
+ let limiter;
10
+ let storage;
11
+ beforeEach(() => {
12
+ storage = new InMemoryRateLimitStorage();
13
+ });
14
+ afterEach(() => {
15
+ storage.dispose();
16
+ if (limiter) {
17
+ limiter.dispose();
18
+ }
19
+ });
20
+ describe('Basic Rate Limiting', () => {
21
+ it('should allow requests within limit', async () => {
22
+ limiter = new RateLimiter({
23
+ maxTokens: 10,
24
+ refillRate: 1,
25
+ windowMs: 60000,
26
+ }, storage);
27
+ // First 10 requests should succeed
28
+ for (let i = 0; i < 10; i++) {
29
+ const result = await limiter.checkLimit('test-key');
30
+ expect(result.allowed).toBe(true);
31
+ expect(result.remaining).toBe(9 - i);
32
+ }
33
+ });
34
+ it('should deny requests exceeding limit', async () => {
35
+ limiter = new RateLimiter({
36
+ maxTokens: 5,
37
+ refillRate: 1,
38
+ windowMs: 60000,
39
+ }, storage);
40
+ // Use up all tokens
41
+ for (let i = 0; i < 5; i++) {
42
+ await limiter.checkLimit('test-key');
43
+ }
44
+ // Next request should be denied
45
+ const result = await limiter.checkLimit('test-key');
46
+ expect(result.allowed).toBe(false);
47
+ expect(result.remaining).toBe(0);
48
+ expect(result.retryAfterMs).toBeGreaterThan(0);
49
+ expect(result.resetAt).toBeDefined();
50
+ });
51
+ it('should track different keys independently', async () => {
52
+ limiter = new RateLimiter({
53
+ maxTokens: 3,
54
+ refillRate: 1,
55
+ windowMs: 60000,
56
+ }, storage);
57
+ // Use all tokens for key1
58
+ for (let i = 0; i < 3; i++) {
59
+ await limiter.checkLimit('key1');
60
+ }
61
+ // key1 should be rate limited
62
+ const result1 = await limiter.checkLimit('key1');
63
+ expect(result1.allowed).toBe(false);
64
+ // key2 should still have tokens
65
+ const result2 = await limiter.checkLimit('key2');
66
+ expect(result2.allowed).toBe(true);
67
+ });
68
+ });
69
+ describe('Token Refill', () => {
70
+ it('should refill tokens over time', async () => {
71
+ limiter = new RateLimiter({
72
+ maxTokens: 10,
73
+ refillRate: 10, // 10 tokens per second
74
+ windowMs: 60000,
75
+ }, storage);
76
+ // Use all tokens
77
+ for (let i = 0; i < 10; i++) {
78
+ await limiter.checkLimit('test-key');
79
+ }
80
+ // Should be rate limited
81
+ let result = await limiter.checkLimit('test-key');
82
+ expect(result.allowed).toBe(false);
83
+ // Wait 500ms (should refill ~5 tokens)
84
+ await new Promise((resolve) => setTimeout(resolve, 500));
85
+ // Should have ~5 tokens now
86
+ result = await limiter.checkLimit('test-key');
87
+ expect(result.allowed).toBe(true);
88
+ expect(result.remaining).toBeGreaterThanOrEqual(3);
89
+ expect(result.remaining).toBeLessThanOrEqual(5);
90
+ });
91
+ it('should not exceed max tokens on refill', async () => {
92
+ limiter = new RateLimiter({
93
+ maxTokens: 5,
94
+ refillRate: 10, // Fast refill
95
+ windowMs: 60000,
96
+ }, storage);
97
+ // Use one token
98
+ await limiter.checkLimit('test-key');
99
+ // Wait for refill
100
+ await new Promise((resolve) => setTimeout(resolve, 200));
101
+ // Check remaining - should cap at maxTokens
102
+ const result = await limiter.checkLimit('test-key');
103
+ expect(result.remaining).toBeLessThanOrEqual(4);
104
+ });
105
+ });
106
+ describe('Token Cost', () => {
107
+ it('should consume multiple tokens per request', async () => {
108
+ limiter = new RateLimiter({
109
+ maxTokens: 10,
110
+ refillRate: 1,
111
+ windowMs: 60000,
112
+ }, storage);
113
+ // Request costing 5 tokens
114
+ const result1 = await limiter.checkLimit('test-key', 5);
115
+ expect(result1.allowed).toBe(true);
116
+ expect(result1.remaining).toBe(5);
117
+ // Request costing 3 tokens
118
+ const result2 = await limiter.checkLimit('test-key', 3);
119
+ expect(result2.allowed).toBe(true);
120
+ expect(result2.remaining).toBe(2);
121
+ // Request costing 5 tokens - should fail
122
+ const result3 = await limiter.checkLimit('test-key', 5);
123
+ expect(result3.allowed).toBe(false);
124
+ expect(result3.remaining).toBe(2);
125
+ });
126
+ it('should calculate correct retry time for high cost', async () => {
127
+ limiter = new RateLimiter({
128
+ maxTokens: 10,
129
+ refillRate: 2, // 2 tokens per second
130
+ windowMs: 60000,
131
+ }, storage);
132
+ // Use all tokens
133
+ await limiter.checkLimit('test-key', 10);
134
+ // Request costing 4 tokens - need to wait ~2 seconds
135
+ const result = await limiter.checkLimit('test-key', 4);
136
+ expect(result.allowed).toBe(false);
137
+ expect(result.retryAfterMs).toBeGreaterThanOrEqual(1900);
138
+ expect(result.retryAfterMs).toBeLessThanOrEqual(2100);
139
+ });
140
+ });
141
+ describe('Reset Functionality', () => {
142
+ it('should reset rate limit for a key', async () => {
143
+ limiter = new RateLimiter({
144
+ maxTokens: 3,
145
+ refillRate: 1,
146
+ windowMs: 60000,
147
+ }, storage);
148
+ // Use all tokens
149
+ for (let i = 0; i < 3; i++) {
150
+ await limiter.checkLimit('test-key');
151
+ }
152
+ // Should be rate limited
153
+ let result = await limiter.checkLimit('test-key');
154
+ expect(result.allowed).toBe(false);
155
+ // Reset
156
+ await limiter.reset('test-key');
157
+ // Should have full tokens again
158
+ result = await limiter.checkLimit('test-key');
159
+ expect(result.allowed).toBe(true);
160
+ expect(result.remaining).toBe(2);
161
+ });
162
+ });
163
+ describe('State Inspection', () => {
164
+ it('should return current bucket state', async () => {
165
+ limiter = new RateLimiter({
166
+ maxTokens: 10,
167
+ refillRate: 1,
168
+ windowMs: 60000,
169
+ }, storage);
170
+ // Make some requests
171
+ await limiter.checkLimit('test-key');
172
+ await limiter.checkLimit('test-key');
173
+ // Get state
174
+ const state = await limiter.getState('test-key');
175
+ expect(state).toBeDefined();
176
+ expect(state?.tokens).toBeCloseTo(8, 0);
177
+ expect(state?.lastRefill).toBeGreaterThan(0);
178
+ });
179
+ it('should return null for non-existent key', async () => {
180
+ limiter = new RateLimiter({
181
+ maxTokens: 10,
182
+ refillRate: 1,
183
+ windowMs: 60000,
184
+ }, storage);
185
+ const state = await limiter.getState('non-existent');
186
+ expect(state).toBeNull();
187
+ });
188
+ });
189
+ describe('Error Handling', () => {
190
+ it('should gracefully handle storage errors with fail-open (default)', async () => {
191
+ // Mock storage that throws errors
192
+ const errorStorage = {
193
+ get: vi.fn().mockRejectedValue(new Error('Storage error')),
194
+ set: vi.fn().mockRejectedValue(new Error('Storage error')),
195
+ delete: vi.fn().mockRejectedValue(new Error('Storage error')),
196
+ };
197
+ limiter = new RateLimiter({
198
+ maxTokens: 10,
199
+ refillRate: 1,
200
+ windowMs: 60000,
201
+ }, errorStorage);
202
+ // Should allow request despite error (graceful degradation)
203
+ const result = await limiter.checkLimit('test-key');
204
+ expect(result.allowed).toBe(true);
205
+ expect(result.remaining).toBe(10);
206
+ expect(result.limit).toBe(10);
207
+ });
208
+ it('should deny requests with fail-closed mode on storage errors', async () => {
209
+ // Mock storage that throws errors
210
+ const errorStorage = {
211
+ get: vi.fn().mockRejectedValue(new Error('Storage error')),
212
+ set: vi.fn().mockRejectedValue(new Error('Storage error')),
213
+ delete: vi.fn().mockRejectedValue(new Error('Storage error')),
214
+ };
215
+ limiter = new RateLimiter({
216
+ maxTokens: 10,
217
+ refillRate: 1,
218
+ windowMs: 60000,
219
+ failMode: 'closed',
220
+ }, errorStorage);
221
+ // Should deny request due to fail-closed mode
222
+ const result = await limiter.checkLimit('test-key');
223
+ expect(result.allowed).toBe(false);
224
+ expect(result.remaining).toBe(0);
225
+ expect(result.limit).toBe(10);
226
+ expect(result.retryAfterMs).toBe(60000);
227
+ expect(result.resetAt).toBeDefined();
228
+ });
229
+ it('should explicitly allow requests with fail-open mode on storage errors', async () => {
230
+ // Mock storage that throws errors
231
+ const errorStorage = {
232
+ get: vi.fn().mockRejectedValue(new Error('Storage error')),
233
+ set: vi.fn().mockRejectedValue(new Error('Storage error')),
234
+ delete: vi.fn().mockRejectedValue(new Error('Storage error')),
235
+ };
236
+ limiter = new RateLimiter({
237
+ maxTokens: 10,
238
+ refillRate: 1,
239
+ windowMs: 60000,
240
+ failMode: 'open',
241
+ }, errorStorage);
242
+ // Should allow request due to fail-open mode
243
+ const result = await limiter.checkLimit('test-key');
244
+ expect(result.allowed).toBe(true);
245
+ expect(result.remaining).toBe(10);
246
+ expect(result.limit).toBe(10);
247
+ });
248
+ it('should use fail-closed for STRICT preset', async () => {
249
+ // Mock storage that throws errors
250
+ const errorStorage = {
251
+ get: vi.fn().mockRejectedValue(new Error('Storage error')),
252
+ set: vi.fn().mockRejectedValue(new Error('Storage error')),
253
+ delete: vi.fn().mockRejectedValue(new Error('Storage error')),
254
+ };
255
+ limiter = new RateLimiter(RATE_LIMIT_PRESETS.STRICT, errorStorage);
256
+ // STRICT preset uses fail-closed, so should deny on error
257
+ const result = await limiter.checkLimit('test-key');
258
+ expect(result.allowed).toBe(false);
259
+ });
260
+ it('should use fail-open for STANDARD preset', async () => {
261
+ // Mock storage that throws errors
262
+ const errorStorage = {
263
+ get: vi.fn().mockRejectedValue(new Error('Storage error')),
264
+ set: vi.fn().mockRejectedValue(new Error('Storage error')),
265
+ delete: vi.fn().mockRejectedValue(new Error('Storage error')),
266
+ };
267
+ limiter = new RateLimiter(RATE_LIMIT_PRESETS.STANDARD, errorStorage);
268
+ // STANDARD preset uses fail-open, so should allow on error
269
+ const result = await limiter.checkLimit('test-key');
270
+ expect(result.allowed).toBe(true);
271
+ });
272
+ });
273
+ describe('Key Prefix', () => {
274
+ it('should use custom key prefix', async () => {
275
+ limiter = new RateLimiter({
276
+ maxTokens: 10,
277
+ refillRate: 1,
278
+ windowMs: 60000,
279
+ keyPrefix: 'custom',
280
+ }, storage);
281
+ await limiter.checkLimit('test-key');
282
+ const stats = storage.getStats();
283
+ expect(stats.keys[0]).toContain('custom:test-key');
284
+ });
285
+ });
286
+ describe('Presets', () => {
287
+ it('should create limiter from STRICT preset', () => {
288
+ limiter = createRateLimiterFromPreset('STRICT', storage);
289
+ expect(limiter).toBeInstanceOf(RateLimiter);
290
+ });
291
+ it('should create limiter from STANDARD preset', () => {
292
+ limiter = createRateLimiterFromPreset('STANDARD', storage);
293
+ expect(limiter).toBeInstanceOf(RateLimiter);
294
+ });
295
+ it('should create limiter from RELAXED preset', () => {
296
+ limiter = createRateLimiterFromPreset('RELAXED', storage);
297
+ expect(limiter).toBeInstanceOf(RateLimiter);
298
+ });
299
+ it('should create limiter from GENEROUS preset', () => {
300
+ limiter = createRateLimiterFromPreset('GENEROUS', storage);
301
+ expect(limiter).toBeInstanceOf(RateLimiter);
302
+ });
303
+ it('should create limiter from HIGH_THROUGHPUT preset', () => {
304
+ limiter = createRateLimiterFromPreset('HIGH_THROUGHPUT', storage);
305
+ expect(limiter).toBeInstanceOf(RateLimiter);
306
+ });
307
+ it('should enforce STRICT limits correctly', async () => {
308
+ limiter = createRateLimiterFromPreset('STRICT', storage);
309
+ // STRICT allows 10 requests per minute
310
+ for (let i = 0; i < 10; i++) {
311
+ const result = await limiter.checkLimit('test-key');
312
+ expect(result.allowed).toBe(true);
313
+ }
314
+ // 11th request should fail
315
+ const result = await limiter.checkLimit('test-key');
316
+ expect(result.allowed).toBe(false);
317
+ });
318
+ });
319
+ describe('Metrics Tracking - SMI-752', () => {
320
+ it('should accumulate metrics for allowed requests', async () => {
321
+ limiter = new RateLimiter({
322
+ maxTokens: 10,
323
+ refillRate: 1,
324
+ windowMs: 60000,
325
+ }, storage);
326
+ // Make 5 allowed requests
327
+ for (let i = 0; i < 5; i++) {
328
+ await limiter.checkLimit('test-key');
329
+ }
330
+ const metrics = limiter.getMetrics('test-key');
331
+ expect(metrics).toBeDefined();
332
+ expect(metrics.allowed).toBe(5);
333
+ expect(metrics.blocked).toBe(0);
334
+ expect(metrics.errors).toBe(0);
335
+ expect(metrics.lastReset).toBeInstanceOf(Date);
336
+ });
337
+ it('should accumulate metrics for blocked requests', async () => {
338
+ limiter = new RateLimiter({
339
+ maxTokens: 3,
340
+ refillRate: 0.1,
341
+ windowMs: 60000,
342
+ }, storage);
343
+ // Make 3 allowed requests
344
+ for (let i = 0; i < 3; i++) {
345
+ await limiter.checkLimit('test-key');
346
+ }
347
+ // Make 2 blocked requests
348
+ await limiter.checkLimit('test-key');
349
+ await limiter.checkLimit('test-key');
350
+ const metrics = limiter.getMetrics('test-key');
351
+ expect(metrics.allowed).toBe(3);
352
+ expect(metrics.blocked).toBe(2);
353
+ expect(metrics.errors).toBe(0);
354
+ });
355
+ it('should include metrics in result', async () => {
356
+ limiter = new RateLimiter({
357
+ maxTokens: 5,
358
+ refillRate: 1,
359
+ windowMs: 60000,
360
+ }, storage);
361
+ const result = await limiter.checkLimit('test-key');
362
+ expect(result.metrics).toBeDefined();
363
+ expect(result.metrics?.allowed).toBe(1);
364
+ });
365
+ it('should track errors in metrics', async () => {
366
+ const errorStorage = {
367
+ get: vi.fn().mockRejectedValue(new Error('Storage error')),
368
+ set: vi.fn().mockRejectedValue(new Error('Storage error')),
369
+ delete: vi.fn().mockRejectedValue(new Error('Storage error')),
370
+ };
371
+ limiter = new RateLimiter({
372
+ maxTokens: 10,
373
+ refillRate: 1,
374
+ windowMs: 60000,
375
+ failMode: 'open',
376
+ }, errorStorage);
377
+ // Make 3 requests that will error
378
+ for (let i = 0; i < 3; i++) {
379
+ await limiter.checkLimit('test-key');
380
+ }
381
+ const metrics = limiter.getMetrics('test-key');
382
+ expect(metrics.errors).toBe(3);
383
+ // Fail-open counts as allowed
384
+ expect(metrics.allowed).toBe(3);
385
+ });
386
+ it('should getMetrics() return all metrics when no key specified', async () => {
387
+ limiter = new RateLimiter({
388
+ maxTokens: 10,
389
+ refillRate: 1,
390
+ windowMs: 60000,
391
+ }, storage);
392
+ // Make requests for multiple keys
393
+ await limiter.checkLimit('key1');
394
+ await limiter.checkLimit('key1');
395
+ await limiter.checkLimit('key2');
396
+ await limiter.checkLimit('key3');
397
+ const allMetrics = limiter.getMetrics();
398
+ expect(allMetrics).toBeInstanceOf(Map);
399
+ expect(allMetrics.size).toBe(3);
400
+ expect(allMetrics.get('key1')?.allowed).toBe(2);
401
+ expect(allMetrics.get('key2')?.allowed).toBe(1);
402
+ expect(allMetrics.get('key3')?.allowed).toBe(1);
403
+ });
404
+ it('should resetMetrics() clear data for specific key', async () => {
405
+ limiter = new RateLimiter({
406
+ maxTokens: 10,
407
+ refillRate: 1,
408
+ windowMs: 60000,
409
+ }, storage);
410
+ // Make requests
411
+ await limiter.checkLimit('key1');
412
+ await limiter.checkLimit('key2');
413
+ // Reset only key1
414
+ limiter.resetMetrics('key1');
415
+ expect(limiter.getMetrics('key1')).toBeUndefined();
416
+ expect(limiter.getMetrics('key2')).toBeDefined();
417
+ });
418
+ it('should resetMetrics() clear all data when no key specified', async () => {
419
+ limiter = new RateLimiter({
420
+ maxTokens: 10,
421
+ refillRate: 1,
422
+ windowMs: 60000,
423
+ }, storage);
424
+ // Make requests for multiple keys
425
+ await limiter.checkLimit('key1');
426
+ await limiter.checkLimit('key2');
427
+ await limiter.checkLimit('key3');
428
+ // Reset all
429
+ limiter.resetMetrics();
430
+ const allMetrics = limiter.getMetrics();
431
+ expect(allMetrics.size).toBe(0);
432
+ });
433
+ it('should call onLimitExceeded callback when limit is exceeded', async () => {
434
+ const onLimitExceeded = vi.fn();
435
+ limiter = new RateLimiter({
436
+ maxTokens: 3,
437
+ refillRate: 0.1,
438
+ windowMs: 60000,
439
+ onLimitExceeded,
440
+ }, storage);
441
+ // Use up all tokens
442
+ for (let i = 0; i < 3; i++) {
443
+ await limiter.checkLimit('test-key');
444
+ }
445
+ // This should trigger the callback
446
+ await limiter.checkLimit('test-key');
447
+ expect(onLimitExceeded).toHaveBeenCalledTimes(1);
448
+ expect(onLimitExceeded).toHaveBeenCalledWith('test-key', expect.objectContaining({
449
+ allowed: 3,
450
+ blocked: 1,
451
+ errors: 0,
452
+ }));
453
+ });
454
+ it('should call onLimitExceeded callback on each exceeded request', async () => {
455
+ const onLimitExceeded = vi.fn();
456
+ limiter = new RateLimiter({
457
+ maxTokens: 2,
458
+ refillRate: 0.01,
459
+ windowMs: 60000,
460
+ onLimitExceeded,
461
+ }, storage);
462
+ // Use up all tokens
463
+ await limiter.checkLimit('test-key');
464
+ await limiter.checkLimit('test-key');
465
+ // These should trigger callbacks
466
+ await limiter.checkLimit('test-key');
467
+ await limiter.checkLimit('test-key');
468
+ await limiter.checkLimit('test-key');
469
+ expect(onLimitExceeded).toHaveBeenCalledTimes(3);
470
+ });
471
+ it('should not call onLimitExceeded for allowed requests', async () => {
472
+ const onLimitExceeded = vi.fn();
473
+ limiter = new RateLimiter({
474
+ maxTokens: 10,
475
+ refillRate: 1,
476
+ windowMs: 60000,
477
+ onLimitExceeded,
478
+ }, storage);
479
+ // Make allowed requests
480
+ for (let i = 0; i < 5; i++) {
481
+ await limiter.checkLimit('test-key');
482
+ }
483
+ expect(onLimitExceeded).not.toHaveBeenCalled();
484
+ });
485
+ it('should clear metrics on dispose', async () => {
486
+ limiter = new RateLimiter({
487
+ maxTokens: 10,
488
+ refillRate: 1,
489
+ windowMs: 60000,
490
+ }, storage);
491
+ // Make requests
492
+ await limiter.checkLimit('test-key');
493
+ // Dispose should clear metrics
494
+ limiter.dispose();
495
+ const allMetrics = limiter.getMetrics();
496
+ expect(allMetrics.size).toBe(0);
497
+ });
498
+ });
499
+ });
500
+ describe('InMemoryRateLimitStorage', () => {
501
+ let storage;
502
+ beforeEach(() => {
503
+ storage = new InMemoryRateLimitStorage();
504
+ });
505
+ afterEach(() => {
506
+ storage.dispose();
507
+ });
508
+ describe('Basic Operations', () => {
509
+ it('should store and retrieve bucket', async () => {
510
+ const bucket = {
511
+ tokens: 10,
512
+ lastRefill: Date.now(),
513
+ firstRequest: Date.now(),
514
+ };
515
+ await storage.set('test-key', bucket, 60000);
516
+ const retrieved = await storage.get('test-key');
517
+ expect(retrieved).toEqual(bucket);
518
+ });
519
+ it('should return null for non-existent key', async () => {
520
+ const result = await storage.get('non-existent');
521
+ expect(result).toBeNull();
522
+ });
523
+ it('should delete key', async () => {
524
+ const bucket = {
525
+ tokens: 10,
526
+ lastRefill: Date.now(),
527
+ firstRequest: Date.now(),
528
+ };
529
+ await storage.set('test-key', bucket, 60000);
530
+ await storage.delete('test-key');
531
+ const result = await storage.get('test-key');
532
+ expect(result).toBeNull();
533
+ });
534
+ it('should clear all keys', async () => {
535
+ const bucket = {
536
+ tokens: 10,
537
+ lastRefill: Date.now(),
538
+ firstRequest: Date.now(),
539
+ };
540
+ await storage.set('key1', bucket, 60000);
541
+ await storage.set('key2', bucket, 60000);
542
+ await storage.clear?.();
543
+ const stats = storage.getStats();
544
+ expect(stats.size).toBe(0);
545
+ });
546
+ });
547
+ describe('TTL and Expiration', () => {
548
+ it('should expire entries after TTL', async () => {
549
+ const bucket = {
550
+ tokens: 10,
551
+ lastRefill: Date.now(),
552
+ firstRequest: Date.now(),
553
+ };
554
+ // Set with 100ms TTL
555
+ await storage.set('test-key', bucket, 100);
556
+ // Should exist immediately
557
+ let result = await storage.get('test-key');
558
+ expect(result).toEqual(bucket);
559
+ // Wait for expiration
560
+ await new Promise((resolve) => setTimeout(resolve, 150));
561
+ // Should be expired
562
+ result = await storage.get('test-key');
563
+ expect(result).toBeNull();
564
+ });
565
+ it('should clean up expired entries periodically', async () => {
566
+ // Create storage with fast cleanup (100ms)
567
+ const fastStorage = new InMemoryRateLimitStorage(100);
568
+ const bucket = {
569
+ tokens: 10,
570
+ lastRefill: Date.now(),
571
+ firstRequest: Date.now(),
572
+ };
573
+ // Add multiple entries with short TTL
574
+ await fastStorage.set('key1', bucket, 50);
575
+ await fastStorage.set('key2', bucket, 50);
576
+ await fastStorage.set('key3', bucket, 50);
577
+ // Wait for cleanup
578
+ await new Promise((resolve) => setTimeout(resolve, 200));
579
+ const stats = fastStorage.getStats();
580
+ expect(stats.size).toBe(0);
581
+ fastStorage.dispose();
582
+ });
583
+ });
584
+ describe('Statistics', () => {
585
+ it('should return accurate stats', async () => {
586
+ const bucket = {
587
+ tokens: 10,
588
+ lastRefill: Date.now(),
589
+ firstRequest: Date.now(),
590
+ };
591
+ await storage.set('key1', bucket, 60000);
592
+ await storage.set('key2', bucket, 60000);
593
+ const stats = storage.getStats();
594
+ expect(stats.size).toBe(2);
595
+ expect(stats.keys).toContain('key1');
596
+ expect(stats.keys).toContain('key2');
597
+ });
598
+ });
599
+ });
600
+ describe('Rate Limiting Scenarios', () => {
601
+ let storage;
602
+ beforeEach(() => {
603
+ storage = new InMemoryRateLimitStorage();
604
+ });
605
+ afterEach(() => {
606
+ storage.dispose();
607
+ });
608
+ describe('API Endpoint Rate Limiting', () => {
609
+ it('should limit search API requests per IP', async () => {
610
+ const limiter = new RateLimiter(RATE_LIMIT_PRESETS.STANDARD, storage);
611
+ const ip = '192.168.1.1';
612
+ // Simulate 30 search requests (at limit)
613
+ for (let i = 0; i < 30; i++) {
614
+ const result = await limiter.checkLimit(`ip:${ip}`);
615
+ expect(result.allowed).toBe(true);
616
+ }
617
+ // 31st request should be denied
618
+ const result = await limiter.checkLimit(`ip:${ip}`);
619
+ expect(result.allowed).toBe(false);
620
+ expect(result.retryAfterMs).toBeGreaterThan(0);
621
+ });
622
+ it('should limit install operations per user', async () => {
623
+ // Stricter limits for install operations
624
+ const limiter = new RateLimiter({
625
+ maxTokens: 5,
626
+ refillRate: 5 / 60, // 5 per minute
627
+ windowMs: 60000,
628
+ }, storage);
629
+ const userId = 'user-123';
630
+ // User can install 5 skills
631
+ for (let i = 0; i < 5; i++) {
632
+ const result = await limiter.checkLimit(`user:${userId}:install`);
633
+ expect(result.allowed).toBe(true);
634
+ }
635
+ // 6th install should be denied
636
+ const result = await limiter.checkLimit(`user:${userId}:install`);
637
+ expect(result.allowed).toBe(false);
638
+ });
639
+ });
640
+ describe('Source Adapter Rate Limiting', () => {
641
+ it('should limit GitHub API requests', async () => {
642
+ const limiter = new RateLimiter({
643
+ maxTokens: 60,
644
+ refillRate: 1, // 60 per minute
645
+ windowMs: 60000,
646
+ }, storage);
647
+ // Simulate batch of API calls
648
+ for (let i = 0; i < 60; i++) {
649
+ const result = await limiter.checkLimit('adapter:github');
650
+ expect(result.allowed).toBe(true);
651
+ }
652
+ // Next call should be rate limited
653
+ const result = await limiter.checkLimit('adapter:github');
654
+ expect(result.allowed).toBe(false);
655
+ });
656
+ it('should limit raw URL fetches', async () => {
657
+ const limiter = new RateLimiter(RATE_LIMIT_PRESETS.STANDARD, storage);
658
+ // Simulate URL fetches
659
+ for (let i = 0; i < 30; i++) {
660
+ const result = await limiter.checkLimit('adapter:raw-url');
661
+ expect(result.allowed).toBe(true);
662
+ }
663
+ const result = await limiter.checkLimit('adapter:raw-url');
664
+ expect(result.allowed).toBe(false);
665
+ });
666
+ });
667
+ describe('Burst Traffic', () => {
668
+ it('should allow burst within token capacity', async () => {
669
+ // Use fake timers to prevent token refill during parallel execution
670
+ vi.useFakeTimers();
671
+ const limiter = new RateLimiter({
672
+ maxTokens: 20,
673
+ refillRate: 5, // Refill slowly
674
+ windowMs: 60000,
675
+ }, storage);
676
+ // Burst of 20 requests - should all succeed
677
+ // Use sequential execution to ensure deterministic token consumption
678
+ for (let i = 0; i < 20; i++) {
679
+ const result = await limiter.checkLimit('test-key');
680
+ expect(result.allowed).toBe(true);
681
+ }
682
+ // 21st should fail (all tokens consumed)
683
+ const result = await limiter.checkLimit('test-key');
684
+ expect(result.allowed).toBe(false);
685
+ vi.useRealTimers();
686
+ });
687
+ });
688
+ });
689
+ /**
690
+ * Queue functionality tests - SMI-1013
691
+ */
692
+ describe('RateLimiter - Queue Support (SMI-1013)', () => {
693
+ let limiter;
694
+ let storage;
695
+ beforeEach(() => {
696
+ storage = new InMemoryRateLimitStorage();
697
+ });
698
+ afterEach(() => {
699
+ storage.dispose();
700
+ if (limiter) {
701
+ limiter.dispose();
702
+ }
703
+ });
704
+ describe('waitForToken()', () => {
705
+ it('should return immediately when tokens are available', async () => {
706
+ limiter = new RateLimiter({
707
+ maxTokens: 10,
708
+ refillRate: 1,
709
+ windowMs: 60000,
710
+ enableQueue: true,
711
+ queueTimeoutMs: 5000,
712
+ }, storage);
713
+ const result = await limiter.waitForToken('test-key');
714
+ expect(result.allowed).toBe(true);
715
+ expect(result.queued).toBe(false);
716
+ expect(result.remaining).toBe(9);
717
+ });
718
+ it('should queue request when no tokens available', async () => {
719
+ limiter = new RateLimiter({
720
+ maxTokens: 2,
721
+ refillRate: 10, // Fast refill: 10 tokens/sec
722
+ windowMs: 60000,
723
+ enableQueue: true,
724
+ queueTimeoutMs: 5000,
725
+ }, storage);
726
+ // Use all tokens
727
+ await limiter.waitForToken('test-key');
728
+ await limiter.waitForToken('test-key');
729
+ // Third request should be queued and wait for refill
730
+ const startTime = Date.now();
731
+ const result = await limiter.waitForToken('test-key');
732
+ const elapsed = Date.now() - startTime;
733
+ expect(result.allowed).toBe(true);
734
+ expect(result.queued).toBe(true);
735
+ expect(result.queueWaitMs).toBeGreaterThan(0);
736
+ expect(elapsed).toBeGreaterThan(50); // Should have waited for tokens
737
+ });
738
+ it('should respect configured limit with sequential requests', async () => {
739
+ limiter = new RateLimiter({
740
+ maxTokens: 3,
741
+ refillRate: 10, // Fast refill for quick test
742
+ windowMs: 60000,
743
+ enableQueue: true,
744
+ queueTimeoutMs: 5000,
745
+ }, storage);
746
+ // Make 3 requests - should all succeed immediately
747
+ const r1 = await limiter.waitForToken('test-key');
748
+ const r2 = await limiter.waitForToken('test-key');
749
+ const r3 = await limiter.waitForToken('test-key');
750
+ expect(r1.allowed).toBe(true);
751
+ expect(r2.allowed).toBe(true);
752
+ expect(r3.allowed).toBe(true);
753
+ // These should not have been queued (immediate success)
754
+ expect(r1.queued).toBe(false);
755
+ expect(r2.queued).toBe(false);
756
+ expect(r3.queued).toBe(false);
757
+ });
758
+ it('should timeout when queue wait exceeds limit', async () => {
759
+ limiter = new RateLimiter({
760
+ maxTokens: 1,
761
+ refillRate: 0.01, // Very slow: 0.01 tokens/sec (100 seconds per token)
762
+ windowMs: 60000,
763
+ enableQueue: true,
764
+ queueTimeoutMs: 100, // 100ms timeout
765
+ }, storage);
766
+ // Use the only token
767
+ await limiter.waitForToken('test-key');
768
+ // Next request should timeout
769
+ await expect(limiter.waitForToken('test-key')).rejects.toThrow(RateLimitQueueTimeoutError);
770
+ });
771
+ it('should throw RateLimitQueueFullError when queue is at capacity', async () => {
772
+ limiter = new RateLimiter({
773
+ maxTokens: 1,
774
+ refillRate: 0.01, // Very slow
775
+ windowMs: 60000,
776
+ enableQueue: true,
777
+ queueTimeoutMs: 60000,
778
+ maxQueueSize: 2,
779
+ }, storage);
780
+ // Use the only token
781
+ await limiter.waitForToken('test-key');
782
+ // Queue 2 requests (max queue size) - add catch to prevent unhandled rejections
783
+ const p1 = limiter.waitForToken('test-key').catch(() => { });
784
+ const p2 = limiter.waitForToken('test-key').catch(() => { });
785
+ // Third queued request should fail immediately
786
+ await expect(limiter.waitForToken('test-key')).rejects.toThrow(RateLimitQueueFullError);
787
+ // Cleanup happens in afterEach
788
+ });
789
+ it('should fall back to checkLimit when queue is disabled', async () => {
790
+ limiter = new RateLimiter({
791
+ maxTokens: 2,
792
+ refillRate: 0.01,
793
+ windowMs: 60000,
794
+ enableQueue: false, // Queue disabled
795
+ }, storage);
796
+ // Use all tokens
797
+ await limiter.waitForToken('test-key');
798
+ await limiter.waitForToken('test-key');
799
+ // Third request should be denied immediately (no queuing)
800
+ const result = await limiter.waitForToken('test-key');
801
+ expect(result.allowed).toBe(false);
802
+ });
803
+ });
804
+ describe('getQueueStatus()', () => {
805
+ it('should return 0 for empty queue', async () => {
806
+ limiter = new RateLimiter({
807
+ maxTokens: 10,
808
+ refillRate: 1,
809
+ windowMs: 60000,
810
+ enableQueue: true,
811
+ queueTimeoutMs: 60000,
812
+ }, storage);
813
+ // No requests queued
814
+ const status = limiter.getQueueStatus('test-key');
815
+ expect(status).toBe(0);
816
+ });
817
+ it('should return all queue info when no key specified', async () => {
818
+ limiter = new RateLimiter({
819
+ maxTokens: 10,
820
+ refillRate: 1,
821
+ windowMs: 60000,
822
+ enableQueue: true,
823
+ queueTimeoutMs: 60000,
824
+ }, storage);
825
+ // No queued requests yet
826
+ const status = limiter.getQueueStatus();
827
+ expect(status.totalQueued).toBe(0);
828
+ expect(status.queues.size).toBe(0);
829
+ });
830
+ });
831
+ describe('clearQueue()', () => {
832
+ it('should not throw when clearing empty queue', () => {
833
+ limiter = new RateLimiter({
834
+ maxTokens: 10,
835
+ refillRate: 1,
836
+ windowMs: 60000,
837
+ enableQueue: true,
838
+ queueTimeoutMs: 60000,
839
+ }, storage);
840
+ // Clearing empty queue should not throw
841
+ expect(() => limiter.clearQueue('nonexistent-key')).not.toThrow();
842
+ expect(() => limiter.clearQueue()).not.toThrow();
843
+ });
844
+ });
845
+ describe('Queue with metrics', () => {
846
+ it('should track metrics for allowed requests with queue enabled', async () => {
847
+ limiter = new RateLimiter({
848
+ maxTokens: 5,
849
+ refillRate: 1,
850
+ windowMs: 60000,
851
+ enableQueue: true,
852
+ queueTimeoutMs: 5000,
853
+ }, storage);
854
+ // Make requests that succeed immediately
855
+ await limiter.waitForToken('test-key');
856
+ await limiter.waitForToken('test-key');
857
+ const metrics = limiter.getMetrics('test-key');
858
+ expect(metrics).toBeDefined();
859
+ expect(metrics.allowed).toBe(2);
860
+ expect(metrics.blocked).toBe(0);
861
+ });
862
+ });
863
+ describe('dispose()', () => {
864
+ it('should stop queue processor interval on dispose', () => {
865
+ const localLimiter = new RateLimiter({
866
+ maxTokens: 10,
867
+ refillRate: 1,
868
+ windowMs: 60000,
869
+ enableQueue: true,
870
+ }, storage);
871
+ // Dispose should not throw
872
+ expect(() => localLimiter.dispose()).not.toThrow();
873
+ });
874
+ it('should clean up resources without queue enabled', () => {
875
+ const localLimiter = new RateLimiter({
876
+ maxTokens: 10,
877
+ refillRate: 1,
878
+ windowMs: 60000,
879
+ enableQueue: false,
880
+ }, storage);
881
+ // Dispose should not throw
882
+ expect(() => localLimiter.dispose()).not.toThrow();
883
+ });
884
+ });
885
+ });
886
+ /**
887
+ * Memory bounds and cleanup tests - Code Review Wave 3
888
+ */
889
+ describe('RateLimiter - Memory Bounds (Wave 3 Fixes)', () => {
890
+ let limiter;
891
+ let storage;
892
+ beforeEach(() => {
893
+ storage = new InMemoryRateLimitStorage();
894
+ });
895
+ afterEach(() => {
896
+ storage.dispose();
897
+ if (limiter) {
898
+ limiter.dispose();
899
+ }
900
+ });
901
+ describe('Metrics Memory Bounds', () => {
902
+ it('should track lastUpdated in metrics', async () => {
903
+ limiter = new RateLimiter({
904
+ maxTokens: 10,
905
+ refillRate: 1,
906
+ windowMs: 60000,
907
+ }, storage);
908
+ await limiter.checkLimit('test-key');
909
+ const metrics = limiter.getMetrics('test-key');
910
+ expect(metrics.lastUpdated).toBeInstanceOf(Date);
911
+ expect(metrics.lastUpdated.getTime()).toBeGreaterThan(0);
912
+ });
913
+ it('should evict oldest metrics when max unique keys exceeded', async () => {
914
+ // Create limiter and manually test eviction logic by checking metrics behavior
915
+ limiter = new RateLimiter({
916
+ maxTokens: 10,
917
+ refillRate: 1,
918
+ windowMs: 60000,
919
+ }, storage);
920
+ // Make requests for multiple keys
921
+ for (let i = 0; i < 100; i++) {
922
+ await limiter.checkLimit(`key-${i}`);
923
+ }
924
+ // Should have all 100 keys (under the 10000 limit)
925
+ const allMetrics = limiter.getMetrics();
926
+ expect(allMetrics.size).toBe(100);
927
+ });
928
+ });
929
+ describe('Queue Memory Bounds', () => {
930
+ it('should reject new queues when max unique keys for queues is reached', async () => {
931
+ limiter = new RateLimiter({
932
+ maxTokens: 1,
933
+ refillRate: 0.001, // Very slow
934
+ windowMs: 60000,
935
+ enableQueue: true,
936
+ queueTimeoutMs: 60000,
937
+ maxQueueSize: 1,
938
+ }, storage);
939
+ // Use token for first key
940
+ await limiter.waitForToken('key-1');
941
+ // Queue one request for key-1
942
+ const p1 = limiter.waitForToken('key-1').catch(() => { });
943
+ // Trying to add another request should fail (queue full for this key)
944
+ await expect(limiter.waitForToken('key-1')).rejects.toThrow(RateLimitQueueFullError);
945
+ // Cleanup
946
+ limiter.clearQueue();
947
+ });
948
+ it('should clean up empty queues during processing', async () => {
949
+ limiter = new RateLimiter({
950
+ maxTokens: 10,
951
+ refillRate: 10,
952
+ windowMs: 60000,
953
+ enableQueue: true,
954
+ queueTimeoutMs: 5000,
955
+ }, storage);
956
+ // Make immediate requests (not queued)
957
+ await limiter.waitForToken('key-1');
958
+ await limiter.waitForToken('key-2');
959
+ // Queue status should not have empty queues cluttering memory
960
+ const status = limiter.getQueueStatus();
961
+ expect(status.totalQueued).toBe(0);
962
+ });
963
+ });
964
+ describe('Queue Request ID Uniqueness', () => {
965
+ it('should use unique IDs for queued requests', async () => {
966
+ limiter = new RateLimiter({
967
+ maxTokens: 1,
968
+ refillRate: 10, // Fast refill
969
+ windowMs: 60000,
970
+ enableQueue: true,
971
+ queueTimeoutMs: 5000,
972
+ maxQueueSize: 10,
973
+ }, storage);
974
+ // Use the only token
975
+ await limiter.waitForToken('test-key');
976
+ // Queue multiple requests rapidly
977
+ const promises = [
978
+ limiter.waitForToken('test-key'),
979
+ limiter.waitForToken('test-key'),
980
+ limiter.waitForToken('test-key'),
981
+ ];
982
+ // All should eventually complete (no ID collisions)
983
+ const results = await Promise.all(promises);
984
+ results.forEach((r) => {
985
+ expect(r.allowed).toBe(true);
986
+ expect(r.queued).toBe(true);
987
+ });
988
+ });
989
+ });
990
+ describe('Concurrent Queue Processing Safety', () => {
991
+ it('should handle concurrent queue operations safely', async () => {
992
+ limiter = new RateLimiter({
993
+ maxTokens: 2,
994
+ refillRate: 20, // Fast refill for test
995
+ windowMs: 60000,
996
+ enableQueue: true,
997
+ queueTimeoutMs: 5000,
998
+ maxQueueSize: 20,
999
+ }, storage);
1000
+ // Use tokens
1001
+ await limiter.waitForToken('test-key');
1002
+ await limiter.waitForToken('test-key');
1003
+ // Queue many concurrent requests
1004
+ const promises = [];
1005
+ for (let i = 0; i < 10; i++) {
1006
+ promises.push(limiter.waitForToken('test-key'));
1007
+ }
1008
+ // All should complete without errors
1009
+ const results = await Promise.all(promises);
1010
+ expect(results.length).toBe(10);
1011
+ results.forEach((r) => {
1012
+ expect(r.allowed).toBe(true);
1013
+ });
1014
+ });
1015
+ });
1016
+ });
1017
+ //# sourceMappingURL=RateLimiter.test.js.map