@dollhousemcp/mcp-server 1.5.2 → 1.6.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 (272) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +494 -111
  3. package/data/agents/code-reviewer.md +8 -1
  4. package/data/agents/research-assistant.md +8 -1
  5. package/data/agents/task-manager.md +8 -1
  6. package/data/ensembles/business-advisor.md +8 -1
  7. package/data/ensembles/creative-studio.md +8 -1
  8. package/data/ensembles/development-team.md +8 -1
  9. package/data/ensembles/security-analysis-team.md +8 -1
  10. package/data/memories/conversation-history.md +8 -1
  11. package/data/memories/learning-progress.md +8 -1
  12. package/data/memories/project-context.md +8 -1
  13. package/data/personas/business-consultant.md +8 -1
  14. package/data/personas/creative-writer.md +8 -1
  15. package/data/personas/debug-detective.md +8 -1
  16. package/data/personas/eli5-explainer.md +8 -1
  17. package/data/personas/security-analyst.md +8 -1
  18. package/data/personas/technical-analyst.md +8 -1
  19. package/data/skills/code-review.md +8 -1
  20. package/data/skills/creative-writing.md +8 -1
  21. package/data/skills/data-analysis.md +8 -1
  22. package/data/skills/penetration-testing.md +8 -1
  23. package/data/skills/research.md +8 -1
  24. package/data/skills/threat-modeling.md +8 -1
  25. package/data/skills/translation.md +8 -1
  26. package/data/templates/code-documentation.md +8 -1
  27. package/data/templates/email-professional.md +8 -1
  28. package/data/templates/meeting-notes.md +8 -1
  29. package/data/templates/penetration-test-report.md +8 -1
  30. package/data/templates/project-brief.md +8 -1
  31. package/data/templates/report-executive.md +8 -1
  32. package/data/templates/security-vulnerability-report.md +8 -1
  33. package/data/templates/threat-assessment-report.md +8 -1
  34. package/dist/auth/GitHubAuthManager.d.ts +6 -1
  35. package/dist/auth/GitHubAuthManager.d.ts.map +1 -1
  36. package/dist/auth/GitHubAuthManager.js +45 -18
  37. package/dist/benchmarks/IndexPerformanceBenchmark.d.ts +98 -0
  38. package/dist/benchmarks/IndexPerformanceBenchmark.d.ts.map +1 -0
  39. package/dist/benchmarks/IndexPerformanceBenchmark.js +531 -0
  40. package/dist/cache/CollectionCache.d.ts.map +1 -1
  41. package/dist/cache/CollectionCache.js +13 -3
  42. package/dist/cache/CollectionIndexCache.d.ts +77 -0
  43. package/dist/cache/CollectionIndexCache.d.ts.map +1 -0
  44. package/dist/cache/CollectionIndexCache.js +349 -0
  45. package/dist/cache/LRUCache.d.ts +93 -0
  46. package/dist/cache/LRUCache.d.ts.map +1 -0
  47. package/dist/cache/LRUCache.js +299 -0
  48. package/dist/cache/index.d.ts +1 -0
  49. package/dist/cache/index.d.ts.map +1 -1
  50. package/dist/cache/index.js +2 -1
  51. package/dist/collection/CollectionBrowser.d.ts +21 -1
  52. package/dist/collection/CollectionBrowser.d.ts.map +1 -1
  53. package/dist/collection/CollectionBrowser.js +130 -10
  54. package/dist/collection/CollectionIndexManager.d.ts +151 -0
  55. package/dist/collection/CollectionIndexManager.d.ts.map +1 -0
  56. package/dist/collection/CollectionIndexManager.js +499 -0
  57. package/dist/collection/CollectionSearch.d.ts +55 -0
  58. package/dist/collection/CollectionSearch.d.ts.map +1 -1
  59. package/dist/collection/CollectionSearch.js +338 -13
  60. package/dist/collection/CollectionSeeder.d.ts.map +1 -1
  61. package/dist/collection/CollectionSeeder.js +38 -1
  62. package/dist/collection/ElementInstaller.d.ts +31 -0
  63. package/dist/collection/ElementInstaller.d.ts.map +1 -1
  64. package/dist/collection/ElementInstaller.js +77 -15
  65. package/dist/collection/PersonaSubmitter.d.ts +1 -1
  66. package/dist/collection/PersonaSubmitter.d.ts.map +1 -1
  67. package/dist/collection/PersonaSubmitter.js +2 -2
  68. package/dist/collection/index.d.ts +1 -0
  69. package/dist/collection/index.d.ts.map +1 -1
  70. package/dist/collection/index.js +2 -1
  71. package/dist/config/ConfigManager.d.ts +78 -0
  72. package/dist/config/ConfigManager.d.ts.map +1 -0
  73. package/dist/config/ConfigManager.js +216 -0
  74. package/dist/config/element-types.d.ts +135 -0
  75. package/dist/config/element-types.d.ts.map +1 -0
  76. package/dist/config/element-types.js +108 -0
  77. package/dist/config/index.d.ts +2 -0
  78. package/dist/config/index.d.ts.map +1 -1
  79. package/dist/config/index.js +3 -1
  80. package/dist/config/portfolio-constants.d.ts +83 -0
  81. package/dist/config/portfolio-constants.d.ts.map +1 -0
  82. package/dist/config/portfolio-constants.js +99 -0
  83. package/dist/elements/BaseElement.d.ts +14 -2
  84. package/dist/elements/BaseElement.d.ts.map +1 -1
  85. package/dist/elements/BaseElement.js +88 -6
  86. package/dist/elements/agents/Agent.d.ts +10 -1
  87. package/dist/elements/agents/Agent.d.ts.map +1 -1
  88. package/dist/elements/agents/Agent.js +66 -19
  89. package/dist/elements/agents/AgentManager.d.ts +2 -0
  90. package/dist/elements/agents/AgentManager.d.ts.map +1 -1
  91. package/dist/elements/agents/AgentManager.js +12 -10
  92. package/dist/elements/skills/Skill.d.ts +10 -1
  93. package/dist/elements/skills/Skill.d.ts.map +1 -1
  94. package/dist/elements/skills/Skill.js +40 -3
  95. package/dist/elements/skills/SkillManager.d.ts +1 -0
  96. package/dist/elements/skills/SkillManager.d.ts.map +1 -1
  97. package/dist/elements/skills/SkillManager.js +10 -4
  98. package/dist/elements/templates/Template.d.ts +10 -1
  99. package/dist/elements/templates/Template.d.ts.map +1 -1
  100. package/dist/elements/templates/Template.js +35 -18
  101. package/dist/elements/templates/TemplateManager.d.ts +1 -1
  102. package/dist/elements/templates/TemplateManager.d.ts.map +1 -1
  103. package/dist/elements/templates/TemplateManager.js +6 -5
  104. package/dist/generated/version.d.ts +2 -2
  105. package/dist/generated/version.js +3 -3
  106. package/dist/index.barrel.d.ts +1 -2
  107. package/dist/index.barrel.d.ts.map +1 -1
  108. package/dist/index.barrel.js +2 -4
  109. package/dist/index.d.ts +143 -25
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +1883 -310
  112. package/dist/persona/PersonaElement.d.ts +10 -0
  113. package/dist/persona/PersonaElement.d.ts.map +1 -1
  114. package/dist/persona/PersonaElement.js +55 -32
  115. package/dist/persona/PersonaElementManager.d.ts.map +1 -1
  116. package/dist/persona/PersonaElementManager.js +13 -11
  117. package/dist/persona/PersonaLoader.d.ts.map +1 -1
  118. package/dist/persona/PersonaLoader.js +8 -2
  119. package/dist/persona/export-import/PersonaImporter.d.ts.map +1 -1
  120. package/dist/persona/export-import/PersonaImporter.js +24 -5
  121. package/dist/persona/export-import/PersonaSharer.d.ts +21 -0
  122. package/dist/persona/export-import/PersonaSharer.d.ts.map +1 -1
  123. package/dist/persona/export-import/PersonaSharer.js +198 -22
  124. package/dist/portfolio/DefaultElementProvider.d.ts +90 -0
  125. package/dist/portfolio/DefaultElementProvider.d.ts.map +1 -1
  126. package/dist/portfolio/DefaultElementProvider.js +499 -7
  127. package/dist/portfolio/GitHubPortfolioIndexer.d.ts +129 -0
  128. package/dist/portfolio/GitHubPortfolioIndexer.d.ts.map +1 -0
  129. package/dist/portfolio/GitHubPortfolioIndexer.js +475 -0
  130. package/dist/portfolio/MigrationManager.d.ts.map +1 -1
  131. package/dist/portfolio/MigrationManager.js +136 -3
  132. package/dist/portfolio/PortfolioIndexManager.d.ts +130 -0
  133. package/dist/portfolio/PortfolioIndexManager.d.ts.map +1 -0
  134. package/dist/portfolio/PortfolioIndexManager.js +478 -0
  135. package/dist/portfolio/PortfolioManager.d.ts +5 -0
  136. package/dist/portfolio/PortfolioManager.d.ts.map +1 -1
  137. package/dist/portfolio/PortfolioManager.js +61 -20
  138. package/dist/portfolio/PortfolioRepoManager.d.ts +75 -0
  139. package/dist/portfolio/PortfolioRepoManager.d.ts.map +1 -0
  140. package/dist/portfolio/PortfolioRepoManager.js +337 -0
  141. package/dist/portfolio/UnifiedIndexManager.d.ts +388 -0
  142. package/dist/portfolio/UnifiedIndexManager.d.ts.map +1 -0
  143. package/dist/portfolio/UnifiedIndexManager.js +1434 -0
  144. package/dist/portfolio/index.d.ts +15 -0
  145. package/dist/portfolio/index.d.ts.map +1 -0
  146. package/dist/portfolio/index.js +15 -0
  147. package/dist/portfolio/types.d.ts +7 -0
  148. package/dist/portfolio/types.d.ts.map +1 -1
  149. package/dist/portfolio/types.js +6 -1
  150. package/dist/security/InputValidator.d.ts.map +1 -1
  151. package/dist/security/InputValidator.js +50 -48
  152. package/dist/security/audit/SecurityAuditor.d.ts.map +1 -1
  153. package/dist/security/audit/SecurityAuditor.js +17 -9
  154. package/dist/security/audit/config/suppressions.d.ts.map +1 -1
  155. package/dist/security/audit/config/suppressions.js +19 -3
  156. package/dist/security/contentValidator.d.ts +2 -0
  157. package/dist/security/contentValidator.d.ts.map +1 -1
  158. package/dist/security/contentValidator.js +115 -4
  159. package/dist/security/secureYamlParser.d.ts +1 -0
  160. package/dist/security/secureYamlParser.d.ts.map +1 -1
  161. package/dist/security/secureYamlParser.js +29 -7
  162. package/dist/security/securityMonitor.d.ts +1 -1
  163. package/dist/security/securityMonitor.d.ts.map +1 -1
  164. package/dist/security/securityMonitor.js +1 -1
  165. package/dist/security/tokenManager.d.ts +1 -1
  166. package/dist/security/tokenManager.d.ts.map +1 -1
  167. package/dist/security/tokenManager.js +30 -10
  168. package/dist/server/ServerSetup.d.ts +22 -2
  169. package/dist/server/ServerSetup.d.ts.map +1 -1
  170. package/dist/server/ServerSetup.js +77 -12
  171. package/dist/server/tools/AuthTools.d.ts.map +1 -1
  172. package/dist/server/tools/AuthTools.js +33 -1
  173. package/dist/server/tools/BuildInfoTools.d.ts +25 -0
  174. package/dist/server/tools/BuildInfoTools.d.ts.map +1 -0
  175. package/dist/server/tools/BuildInfoTools.js +36 -0
  176. package/dist/server/tools/CollectionTools.d.ts.map +1 -1
  177. package/dist/server/tools/CollectionTools.js +55 -46
  178. package/dist/server/tools/ConfigTools.d.ts.map +1 -1
  179. package/dist/server/tools/ConfigTools.js +29 -1
  180. package/dist/server/tools/PersonaTools.d.ts +4 -2
  181. package/dist/server/tools/PersonaTools.d.ts.map +1 -1
  182. package/dist/server/tools/PersonaTools.js +5 -152
  183. package/dist/server/tools/PortfolioTools.d.ts +12 -0
  184. package/dist/server/tools/PortfolioTools.d.ts.map +1 -0
  185. package/dist/server/tools/PortfolioTools.js +221 -0
  186. package/dist/server/tools/index.d.ts +3 -1
  187. package/dist/server/tools/index.d.ts.map +1 -1
  188. package/dist/server/tools/index.js +4 -2
  189. package/dist/server/types.d.ts +40 -5
  190. package/dist/server/types.d.ts.map +1 -1
  191. package/dist/server/types.js +1 -1
  192. package/dist/services/BuildInfoService.d.ts +84 -0
  193. package/dist/services/BuildInfoService.d.ts.map +1 -0
  194. package/dist/services/BuildInfoService.js +271 -0
  195. package/dist/tools/portfolio/PortfolioElementAdapter.d.ts +54 -0
  196. package/dist/tools/portfolio/PortfolioElementAdapter.d.ts.map +1 -0
  197. package/dist/tools/portfolio/PortfolioElementAdapter.js +229 -0
  198. package/dist/tools/portfolio/submitToPortfolioTool.d.ts +164 -0
  199. package/dist/tools/portfolio/submitToPortfolioTool.d.ts.map +1 -0
  200. package/dist/tools/portfolio/submitToPortfolioTool.js +1523 -0
  201. package/dist/tools/portfolio/types.d.ts +41 -0
  202. package/dist/tools/portfolio/types.d.ts.map +1 -0
  203. package/dist/tools/portfolio/types.js +15 -0
  204. package/dist/types/collection.d.ts +51 -0
  205. package/dist/types/collection.d.ts.map +1 -1
  206. package/dist/types/collection.js +1 -1
  207. package/dist/utils/EarlyTerminationSearch.d.ts +41 -0
  208. package/dist/utils/EarlyTerminationSearch.d.ts.map +1 -0
  209. package/dist/utils/EarlyTerminationSearch.js +164 -0
  210. package/dist/utils/ErrorHandler.d.ts +86 -0
  211. package/dist/utils/ErrorHandler.d.ts.map +1 -0
  212. package/dist/utils/ErrorHandler.js +201 -0
  213. package/dist/utils/FileDiscoveryUtil.d.ts +53 -0
  214. package/dist/utils/FileDiscoveryUtil.d.ts.map +1 -0
  215. package/dist/utils/FileDiscoveryUtil.js +169 -0
  216. package/dist/utils/GitHubRateLimiter.d.ts +88 -0
  217. package/dist/utils/GitHubRateLimiter.d.ts.map +1 -0
  218. package/dist/utils/GitHubRateLimiter.js +315 -0
  219. package/dist/utils/PerformanceMonitor.d.ts +134 -0
  220. package/dist/utils/PerformanceMonitor.d.ts.map +1 -0
  221. package/dist/utils/PerformanceMonitor.js +347 -0
  222. package/dist/utils/RateLimiter.d.ts.map +1 -0
  223. package/dist/utils/RateLimiter.js +172 -0
  224. package/dist/utils/SecureDownloader.d.ts +241 -0
  225. package/dist/utils/SecureDownloader.d.ts.map +1 -0
  226. package/dist/utils/SecureDownloader.js +759 -0
  227. package/dist/utils/ToolCache.d.ts +82 -0
  228. package/dist/utils/ToolCache.d.ts.map +1 -0
  229. package/dist/utils/ToolCache.js +196 -0
  230. package/dist/utils/errorCodes.d.ts +136 -0
  231. package/dist/utils/errorCodes.d.ts.map +1 -0
  232. package/dist/utils/errorCodes.js +87 -0
  233. package/dist/utils/index.d.ts +3 -0
  234. package/dist/utils/index.d.ts.map +1 -1
  235. package/dist/utils/index.js +4 -1
  236. package/dist/utils/installation.d.ts +1 -1
  237. package/dist/utils/installation.d.ts.map +1 -1
  238. package/dist/utils/installation.js +9 -8
  239. package/dist/utils/searchUtils.d.ts +31 -0
  240. package/dist/utils/searchUtils.d.ts.map +1 -1
  241. package/dist/utils/searchUtils.js +62 -1
  242. package/package.json +17 -7
  243. package/dist/config/updateConfig.d.ts +0 -84
  244. package/dist/config/updateConfig.d.ts.map +0 -1
  245. package/dist/config/updateConfig.js +0 -148
  246. package/dist/server/tools/UpdateTools.d.ts +0 -10
  247. package/dist/server/tools/UpdateTools.d.ts.map +0 -1
  248. package/dist/server/tools/UpdateTools.js +0 -85
  249. package/dist/update/BackupManager.d.ts +0 -63
  250. package/dist/update/BackupManager.d.ts.map +0 -1
  251. package/dist/update/BackupManager.js +0 -370
  252. package/dist/update/DependencyChecker.d.ts +0 -41
  253. package/dist/update/DependencyChecker.d.ts.map +0 -1
  254. package/dist/update/DependencyChecker.js +0 -132
  255. package/dist/update/RateLimiter.d.ts.map +0 -1
  256. package/dist/update/RateLimiter.js +0 -172
  257. package/dist/update/SignatureVerifier.d.ts +0 -71
  258. package/dist/update/SignatureVerifier.d.ts.map +0 -1
  259. package/dist/update/SignatureVerifier.js +0 -214
  260. package/dist/update/UpdateChecker.d.ts +0 -132
  261. package/dist/update/UpdateChecker.d.ts.map +0 -1
  262. package/dist/update/UpdateChecker.js +0 -506
  263. package/dist/update/UpdateManager.d.ts +0 -60
  264. package/dist/update/UpdateManager.d.ts.map +0 -1
  265. package/dist/update/UpdateManager.js +0 -730
  266. package/dist/update/VersionManager.d.ts +0 -31
  267. package/dist/update/VersionManager.d.ts.map +0 -1
  268. package/dist/update/VersionManager.js +0 -181
  269. package/dist/update/index.d.ts +0 -9
  270. package/dist/update/index.d.ts.map +0 -1
  271. package/dist/update/index.js +0 -9
  272. /package/dist/{update → utils}/RateLimiter.d.ts +0 -0
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Comprehensive Performance Monitoring System
3
+ * Tracks search times, memory usage, cache performance, and system metrics
4
+ */
5
+ import { logger } from './logger.js';
6
+ import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
7
+ export class PerformanceMonitor {
8
+ static instance = null;
9
+ searchMetrics = [];
10
+ slowQueries = [];
11
+ memorySnapshots = [];
12
+ cacheMetrics = new Map();
13
+ // Configuration
14
+ maxMetricsHistory = 1000;
15
+ slowQueryThreshold = 100; // ms
16
+ memorySnapshotInterval = 30000; // 30 seconds
17
+ maxSlowQueries = 100;
18
+ // Timers and intervals
19
+ memoryMonitorInterval;
20
+ isMonitoring = false;
21
+ constructor() {
22
+ this.startMemoryMonitoring();
23
+ }
24
+ static getInstance() {
25
+ if (!this.instance) {
26
+ this.instance = new PerformanceMonitor();
27
+ }
28
+ return this.instance;
29
+ }
30
+ /**
31
+ * Start performance monitoring
32
+ */
33
+ startMonitoring() {
34
+ if (this.isMonitoring) {
35
+ return;
36
+ }
37
+ this.isMonitoring = true;
38
+ this.startMemoryMonitoring();
39
+ logger.info('Performance monitoring started', {
40
+ slowQueryThreshold: this.slowQueryThreshold,
41
+ maxMetricsHistory: this.maxMetricsHistory
42
+ });
43
+ }
44
+ /**
45
+ * Stop performance monitoring
46
+ */
47
+ stopMonitoring() {
48
+ this.isMonitoring = false;
49
+ if (this.memoryMonitorInterval) {
50
+ clearInterval(this.memoryMonitorInterval);
51
+ this.memoryMonitorInterval = undefined;
52
+ }
53
+ logger.info('Performance monitoring stopped');
54
+ }
55
+ /**
56
+ * Record search performance metrics
57
+ */
58
+ recordSearch(metrics) {
59
+ if (!this.isMonitoring) {
60
+ return;
61
+ }
62
+ // Normalize query string to prevent Unicode-based attacks
63
+ const validationResult = UnicodeValidator.normalize(metrics.query);
64
+ const normalizedMetrics = {
65
+ ...metrics,
66
+ query: validationResult.normalizedContent
67
+ };
68
+ this.searchMetrics.push(normalizedMetrics);
69
+ // Check if it's a slow query (use normalized metrics)
70
+ if (normalizedMetrics.duration > this.slowQueryThreshold) {
71
+ this.recordSlowQuery({
72
+ query: normalizedMetrics.query,
73
+ duration: normalizedMetrics.duration,
74
+ threshold: this.slowQueryThreshold,
75
+ sources: normalizedMetrics.sources,
76
+ resultCount: normalizedMetrics.resultCount,
77
+ memoryUsage: normalizedMetrics.memoryAfter,
78
+ timestamp: normalizedMetrics.timestamp
79
+ });
80
+ }
81
+ // Trim history if needed
82
+ if (this.searchMetrics.length > this.maxMetricsHistory) {
83
+ this.searchMetrics = this.searchMetrics.slice(-this.maxMetricsHistory);
84
+ }
85
+ // Log significant performance events (use normalized metrics)
86
+ if (normalizedMetrics.duration > this.slowQueryThreshold * 2) {
87
+ logger.warn('Very slow search detected', {
88
+ query: normalizedMetrics.query.substring(0, 50),
89
+ duration: normalizedMetrics.duration,
90
+ resultCount: normalizedMetrics.resultCount,
91
+ sources: normalizedMetrics.sources
92
+ });
93
+ }
94
+ }
95
+ /**
96
+ * Record cache performance metrics
97
+ */
98
+ recordCachePerformance(cacheName, stats) {
99
+ if (!this.isMonitoring) {
100
+ return;
101
+ }
102
+ // Normalize cache name to prevent Unicode-based attacks
103
+ const validationResult = UnicodeValidator.normalize(cacheName);
104
+ const normalizedCacheName = validationResult.normalizedContent;
105
+ this.cacheMetrics.set(normalizedCacheName, stats);
106
+ // Log cache performance warnings (use normalized cache name)
107
+ if (stats.hitRate < 0.5) {
108
+ logger.warn('Low cache hit rate detected', {
109
+ cache: normalizedCacheName,
110
+ hitRate: stats.hitRate,
111
+ totalOperations: stats.totalHits + stats.totalMisses
112
+ });
113
+ }
114
+ }
115
+ /**
116
+ * Get comprehensive performance metrics
117
+ */
118
+ getMetrics() {
119
+ return {
120
+ searchTimes: this.searchMetrics.map(m => m.duration),
121
+ memoryUsage: this.memorySnapshots.slice(-100), // Last 100 snapshots
122
+ cacheStats: this.aggregateCacheStats(),
123
+ systemStats: this.getSystemStats(),
124
+ timestamp: new Date()
125
+ };
126
+ }
127
+ /**
128
+ * Get search performance statistics
129
+ */
130
+ getSearchStats() {
131
+ if (this.searchMetrics.length === 0) {
132
+ return {
133
+ totalSearches: 0,
134
+ averageTime: 0,
135
+ medianTime: 0,
136
+ p95Time: 0,
137
+ p99Time: 0,
138
+ slowQueries: 0,
139
+ cacheHitRate: 0
140
+ };
141
+ }
142
+ const times = this.searchMetrics.map(m => m.duration).sort((a, b) => a - b);
143
+ const cacheHits = this.searchMetrics.filter(m => m.cacheHit).length;
144
+ return {
145
+ totalSearches: this.searchMetrics.length,
146
+ averageTime: times.reduce((sum, time) => sum + time, 0) / times.length,
147
+ medianTime: times[Math.floor(times.length / 2)],
148
+ p95Time: times[Math.floor(times.length * 0.95)],
149
+ p99Time: times[Math.floor(times.length * 0.99)],
150
+ slowQueries: this.slowQueries.length,
151
+ cacheHitRate: cacheHits / this.searchMetrics.length
152
+ };
153
+ }
154
+ /**
155
+ * Get memory usage statistics
156
+ */
157
+ getMemoryStats() {
158
+ if (this.memorySnapshots.length === 0) {
159
+ const current = this.takeMemorySnapshot();
160
+ return {
161
+ currentUsage: current,
162
+ peakUsage: current,
163
+ averageUsage: current,
164
+ growthRate: 0
165
+ };
166
+ }
167
+ const current = this.memorySnapshots[this.memorySnapshots.length - 1];
168
+ const peak = this.memorySnapshots.reduce((max, snapshot) => snapshot.heapUsed > max.heapUsed ? snapshot : max);
169
+ const totalHeap = this.memorySnapshots.reduce((sum, snapshot) => sum + snapshot.heapUsed, 0);
170
+ const totalRss = this.memorySnapshots.reduce((sum, snapshot) => sum + snapshot.rss, 0);
171
+ const average = {
172
+ heapUsed: totalHeap / this.memorySnapshots.length,
173
+ heapTotal: this.memorySnapshots.reduce((sum, s) => sum + s.heapTotal, 0) / this.memorySnapshots.length,
174
+ rss: totalRss / this.memorySnapshots.length,
175
+ external: this.memorySnapshots.reduce((sum, s) => sum + s.external, 0) / this.memorySnapshots.length,
176
+ timestamp: new Date()
177
+ };
178
+ // Calculate growth rate (MB per minute)
179
+ let growthRate = 0;
180
+ if (this.memorySnapshots.length > 1) {
181
+ const oldest = this.memorySnapshots[0];
182
+ const timeDiff = (current.timestamp.getTime() - oldest.timestamp.getTime()) / 60000; // minutes
183
+ const memoryDiff = (current.heapUsed - oldest.heapUsed) / (1024 * 1024); // MB
184
+ growthRate = timeDiff > 0 ? memoryDiff / timeDiff : 0;
185
+ }
186
+ return {
187
+ currentUsage: current,
188
+ peakUsage: peak,
189
+ averageUsage: average,
190
+ growthRate
191
+ };
192
+ }
193
+ /**
194
+ * Get slow queries with analysis
195
+ */
196
+ getSlowQueries(limit = 10) {
197
+ return this.slowQueries
198
+ .sort((a, b) => b.duration - a.duration)
199
+ .slice(0, limit);
200
+ }
201
+ /**
202
+ * Analyze performance trends
203
+ */
204
+ analyzeTrends() {
205
+ const recommendations = [];
206
+ // Analyze search performance trend
207
+ let performanceTrend = 'stable';
208
+ if (this.searchMetrics.length > 10) {
209
+ const recent = this.searchMetrics.slice(-10).map(m => m.duration);
210
+ const older = this.searchMetrics.slice(-20, -10).map(m => m.duration);
211
+ if (recent.length > 0 && older.length > 0) {
212
+ const recentAvg = recent.reduce((sum, t) => sum + t, 0) / recent.length;
213
+ const olderAvg = older.reduce((sum, t) => sum + t, 0) / older.length;
214
+ if (recentAvg > olderAvg * 1.2) {
215
+ performanceTrend = 'degrading';
216
+ recommendations.push('Search performance is degrading. Consider cache optimization or index rebuilding.');
217
+ }
218
+ else if (recentAvg < olderAvg * 0.8) {
219
+ performanceTrend = 'improving';
220
+ }
221
+ }
222
+ }
223
+ // Analyze memory trend
224
+ const memoryStats = this.getMemoryStats();
225
+ let memoryTrend = 'stable';
226
+ if (memoryStats.growthRate > 1) { // Growing by more than 1MB/minute
227
+ memoryTrend = 'growing';
228
+ recommendations.push('Memory usage is growing rapidly. Consider cache cleanup or memory limits.');
229
+ }
230
+ else if (memoryStats.growthRate < -1) {
231
+ memoryTrend = 'shrinking';
232
+ }
233
+ // Cache performance recommendations
234
+ const cacheStats = this.aggregateCacheStats();
235
+ if (cacheStats.hitRate < 0.6) {
236
+ recommendations.push('Cache hit rate is low. Consider adjusting cache size or TTL settings.');
237
+ }
238
+ // Slow query recommendations
239
+ if (this.slowQueries.length > 10) {
240
+ recommendations.push('Multiple slow queries detected. Consider query optimization or increased caching.');
241
+ }
242
+ return {
243
+ performanceTrend,
244
+ memoryTrend,
245
+ recommendations
246
+ };
247
+ }
248
+ /**
249
+ * Reset all performance metrics
250
+ */
251
+ reset() {
252
+ this.searchMetrics = [];
253
+ this.slowQueries = [];
254
+ this.memorySnapshots = [];
255
+ this.cacheMetrics.clear();
256
+ logger.info('Performance metrics reset');
257
+ }
258
+ /**
259
+ * Export metrics for external analysis
260
+ */
261
+ exportMetrics() {
262
+ const data = {
263
+ searchMetrics: this.searchMetrics,
264
+ slowQueries: this.slowQueries,
265
+ memorySnapshots: this.memorySnapshots,
266
+ cacheMetrics: Object.fromEntries(this.cacheMetrics),
267
+ exportTimestamp: new Date().toISOString()
268
+ };
269
+ return JSON.stringify(data, null, 2);
270
+ }
271
+ // Private methods
272
+ recordSlowQuery(query) {
273
+ this.slowQueries.push(query);
274
+ // Trim slow queries history
275
+ if (this.slowQueries.length > this.maxSlowQueries) {
276
+ this.slowQueries = this.slowQueries.slice(-this.maxSlowQueries);
277
+ }
278
+ }
279
+ startMemoryMonitoring() {
280
+ if (this.memoryMonitorInterval) {
281
+ clearInterval(this.memoryMonitorInterval);
282
+ }
283
+ this.memoryMonitorInterval = setInterval(() => {
284
+ if (this.isMonitoring) {
285
+ const snapshot = this.takeMemorySnapshot();
286
+ this.memorySnapshots.push(snapshot);
287
+ // Trim memory snapshots (keep last 200)
288
+ if (this.memorySnapshots.length > 200) {
289
+ this.memorySnapshots = this.memorySnapshots.slice(-200);
290
+ }
291
+ }
292
+ }, this.memorySnapshotInterval);
293
+ }
294
+ takeMemorySnapshot() {
295
+ const memUsage = process.memoryUsage();
296
+ return {
297
+ heapUsed: memUsage.heapUsed,
298
+ heapTotal: memUsage.heapTotal,
299
+ rss: memUsage.rss,
300
+ external: memUsage.external,
301
+ timestamp: new Date()
302
+ };
303
+ }
304
+ aggregateCacheStats() {
305
+ if (this.cacheMetrics.size === 0) {
306
+ return {
307
+ hitRate: 0,
308
+ avgHitTime: 0,
309
+ avgMissTime: 0,
310
+ totalHits: 0,
311
+ totalMisses: 0,
312
+ evictions: 0
313
+ };
314
+ }
315
+ let totalHits = 0;
316
+ let totalMisses = 0;
317
+ let totalEvictions = 0;
318
+ let weightedHitTime = 0;
319
+ let weightedMissTime = 0;
320
+ for (const stats of this.cacheMetrics.values()) {
321
+ totalHits += stats.totalHits;
322
+ totalMisses += stats.totalMisses;
323
+ totalEvictions += stats.evictions;
324
+ weightedHitTime += stats.avgHitTime * stats.totalHits;
325
+ weightedMissTime += stats.avgMissTime * stats.totalMisses;
326
+ }
327
+ return {
328
+ hitRate: totalHits + totalMisses > 0 ? totalHits / (totalHits + totalMisses) : 0,
329
+ avgHitTime: totalHits > 0 ? weightedHitTime / totalHits : 0,
330
+ avgMissTime: totalMisses > 0 ? weightedMissTime / totalMisses : 0,
331
+ totalHits,
332
+ totalMisses,
333
+ evictions: totalEvictions
334
+ };
335
+ }
336
+ getSystemStats() {
337
+ const os = require('os');
338
+ return {
339
+ cpuUsage: process.cpuUsage().user / 1000000, // Convert to seconds
340
+ loadAverage: os.loadavg(),
341
+ freeMemory: os.freemem(),
342
+ totalMemory: os.totalmem(),
343
+ uptime: process.uptime()
344
+ };
345
+ }
346
+ }
347
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVyZm9ybWFuY2VNb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL1BlcmZvcm1hbmNlTW9uaXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3JDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBd0Q5RSxNQUFNLE9BQU8sa0JBQWtCO0lBQ3JCLE1BQU0sQ0FBQyxRQUFRLEdBQThCLElBQUksQ0FBQztJQUVsRCxhQUFhLEdBQW9CLEVBQUUsQ0FBQztJQUNwQyxXQUFXLEdBQWdCLEVBQUUsQ0FBQztJQUM5QixlQUFlLEdBQWtCLEVBQUUsQ0FBQztJQUNwQyxZQUFZLEdBQWtDLElBQUksR0FBRyxFQUFFLENBQUM7SUFFaEUsZ0JBQWdCO0lBQ0MsaUJBQWlCLEdBQUcsSUFBSSxDQUFDO0lBQ3pCLGtCQUFrQixHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUs7SUFDL0Isc0JBQXNCLEdBQUcsS0FBSyxDQUFDLENBQUMsYUFBYTtJQUM3QyxjQUFjLEdBQUcsR0FBRyxDQUFDO0lBRXRDLHVCQUF1QjtJQUNmLHFCQUFxQixDQUFrQjtJQUN2QyxZQUFZLEdBQUcsS0FBSyxDQUFDO0lBRTdCO1FBQ0UsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7SUFDL0IsQ0FBQztJQUVNLE1BQU0sQ0FBQyxXQUFXO1FBQ3ZCLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDbkIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLGtCQUFrQixFQUFFLENBQUM7UUFDM0MsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxlQUFlO1FBQ2IsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDdEIsT0FBTztRQUNULENBQUM7UUFF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QUFDLElBQUksR0FBRyxPQUFPLEVBQUUscUJBQXFCO1lBQ2xFLFdBQVcsRUFBRSxFQUFFLENBQUMsT0FBTyxFQUFFO1lBQ3pCLFVBQVUsRUFBRSxFQUFFLENBQUMsT0FBTyxFQUFFO1lBQ3hCLFdBQVcsRUFBRSxFQUFFLENBQUMsUUFBUSxFQUFFO1lBQzFCLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTSxFQUFFO1NBQ3pCLENBQUM7SUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb21wcmVoZW5zaXZlIFBlcmZvcm1hbmNlIE1vbml0b3JpbmcgU3lzdGVtXG4gKiBUcmFja3Mgc2VhcmNoIHRpbWVzLCBtZW1vcnkgdXNhZ2UsIGNhY2hlIHBlcmZvcm1hbmNlLCBhbmQgc3lzdGVtIG1ldHJpY3NcbiAqL1xuXG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcblxuZXhwb3J0IGludGVyZmFjZSBQZXJmb3JtYW5jZU1ldHJpY3Mge1xuICBzZWFyY2hUaW1lczogbnVtYmVyW107XG4gIG1lbW9yeVVzYWdlOiBNZW1vcnlVc2FnZVtdO1xuICBjYWNoZVN0YXRzOiBDYWNoZVBlcmZvcm1hbmNlO1xuICBzeXN0ZW1TdGF0czogU3lzdGVtU3RhdHM7XG4gIHRpbWVzdGFtcDogRGF0ZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNZW1vcnlVc2FnZSB7XG4gIGhlYXBVc2VkOiBudW1iZXI7XG4gIGhlYXBUb3RhbDogbnVtYmVyO1xuICByc3M6IG51bWJlcjtcbiAgZXh0ZXJuYWw6IG51bWJlcjtcbiAgdGltZXN0YW1wOiBEYXRlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENhY2hlUGVyZm9ybWFuY2Uge1xuICBoaXRSYXRlOiBudW1iZXI7XG4gIGF2Z0hpdFRpbWU6IG51bWJlcjtcbiAgYXZnTWlzc1RpbWU6IG51bWJlcjtcbiAgdG90YWxIaXRzOiBudW1iZXI7XG4gIHRvdGFsTWlzc2VzOiBudW1iZXI7XG4gIGV2aWN0aW9uczogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFN5c3RlbVN0YXRzIHtcbiAgY3B1VXNhZ2U6IG51bWJlcjtcbiAgbG9hZEF2ZXJhZ2U6IG51bWJlcltdO1xuICBmcmVlTWVtb3J5OiBudW1iZXI7XG4gIHRvdGFsTWVtb3J5OiBudW1iZXI7XG4gIHVwdGltZTogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNlYXJjaE1ldHJpY3Mge1xuICBxdWVyeTogc3RyaW5nO1xuICBkdXJhdGlvbjogbnVtYmVyO1xuICByZXN1bHRDb3VudDogbnVtYmVyO1xuICBzb3VyY2VzOiBzdHJpbmdbXTtcbiAgY2FjaGVIaXQ6IGJvb2xlYW47XG4gIG1lbW9yeUJlZm9yZTogbnVtYmVyO1xuICBtZW1vcnlBZnRlcjogbnVtYmVyO1xuICB0aW1lc3RhbXA6IERhdGU7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2xvd1F1ZXJ5IHtcbiAgcXVlcnk6IHN0cmluZztcbiAgZHVyYXRpb246IG51bWJlcjtcbiAgdGhyZXNob2xkOiBudW1iZXI7XG4gIHNvdXJjZXM6IHN0cmluZ1tdO1xuICByZXN1bHRDb3VudDogbnVtYmVyO1xuICBtZW1vcnlVc2FnZTogbnVtYmVyO1xuICB0aW1lc3RhbXA6IERhdGU7XG59XG5cbmV4cG9ydCBjbGFzcyBQZXJmb3JtYW5jZU1vbml0b3Ige1xuICBwcml2YXRlIHN0YXRpYyBpbnN0YW5jZTogUGVyZm9ybWFuY2VNb25pdG9yIHwgbnVsbCA9IG51bGw7XG5cbiAgcHJpdmF0ZSBzZWFyY2hNZXRyaWNzOiBTZWFyY2hNZXRyaWNzW10gPSBbXTtcbiAgcHJpdmF0ZSBzbG93UXVlcmllczogU2xvd1F1ZXJ5W10gPSBbXTtcbiAgcHJpdmF0ZSBtZW1vcnlTbmFwc2hvdHM6IE1lbW9yeVVzYWdlW10gPSBbXTtcbiAgcHJpdmF0ZSBjYWNoZU1ldHJpY3M6IE1hcDxzdHJpbmcsIENhY2hlUGVyZm9ybWFuY2U+ID0gbmV3IE1hcCgpO1xuXG4gIC8vIENvbmZpZ3VyYXRpb25cbiAgcHJpdmF0ZSByZWFkb25seSBtYXhNZXRyaWNzSGlzdG9yeSA9IDEwMDA7XG4gIHByaXZhdGUgcmVhZG9ubHkgc2xvd1F1ZXJ5VGhyZXNob2xkID0gMTAwOyAvLyBtc1xuICBwcml2YXRlIHJlYWRvbmx5IG1lbW9yeVNuYXBzaG90SW50ZXJ2YWwgPSAzMDAwMDsgLy8gMzAgc2Vjb25kc1xuICBwcml2YXRlIHJlYWRvbmx5IG1heFNsb3dRdWVyaWVzID0gMTAwO1xuXG4gIC8vIFRpbWVycyBhbmQgaW50ZXJ2YWxzXG4gIHByaXZhdGUgbWVtb3J5TW9uaXRvckludGVydmFsPzogTm9kZUpTLlRpbWVvdXQ7XG4gIHByaXZhdGUgaXNNb25pdG9yaW5nID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLnN0YXJ0TWVtb3J5TW9uaXRvcmluZygpO1xuICB9XG5cbiAgcHVibGljIHN0YXRpYyBnZXRJbnN0YW5jZSgpOiBQZXJmb3JtYW5jZU1vbml0b3Ige1xuICAgIGlmICghdGhpcy5pbnN0YW5jZSkge1xuICAgICAgdGhpcy5pbnN0YW5jZSA9IG5ldyBQZXJmb3JtYW5jZU1vbml0b3IoKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuaW5zdGFuY2U7XG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgcGVyZm9ybWFuY2UgbW9uaXRvcmluZ1xuICAgKi9cbiAgc3RhcnRNb25pdG9yaW5nKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmlzTW9uaXRvcmluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuaXNNb25pdG9yaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnN0YXJ0TWVtb3J5TW9uaXRvcmluZygpO1xuICAgIFxuICAgIGxvZ2dlci5pbmZvKCdQZXJmb3JtYW5jZSBtb25pdG9yaW5nIHN0YXJ0ZWQnLCB7XG4gICAgICBzbG93UXVlcnlUaHJlc2hvbGQ6IHRoaXMuc2xvd1F1ZXJ5VGhyZXNob2xkLFxuICAgICAgbWF4TWV0cmljc0hpc3Rvcnk6IHRoaXMubWF4TWV0cmljc0hpc3RvcnlcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wIHBlcmZvcm1hbmNlIG1vbml0b3JpbmdcbiAgICovXG4gIHN0b3BNb25pdG9yaW5nKCk6IHZvaWQge1xuICAgIHRoaXMuaXNNb25pdG9yaW5nID0gZmFsc2U7XG4gICAgXG4gICAgaWYgKHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsKTtcbiAgICAgIHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsID0gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIGxvZ2dlci5pbmZvKCdQZXJmb3JtYW5jZSBtb25pdG9yaW5nIHN0b3BwZWQnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmQgc2VhcmNoIHBlcmZvcm1hbmNlIG1ldHJpY3NcbiAgICovXG4gIHJlY29yZFNlYXJjaChtZXRyaWNzOiBTZWFyY2hNZXRyaWNzKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzTW9uaXRvcmluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIE5vcm1hbGl6ZSBxdWVyeSBzdHJpbmcgdG8gcHJldmVudCBVbmljb2RlLWJhc2VkIGF0dGFja3NcbiAgICBjb25zdCB2YWxpZGF0aW9uUmVzdWx0ID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobWV0cmljcy5xdWVyeSk7XG4gICAgY29uc3Qgbm9ybWFsaXplZE1ldHJpY3MgPSB7XG4gICAgICAuLi5tZXRyaWNzLFxuICAgICAgcXVlcnk6IHZhbGlkYXRpb25SZXN1bHQubm9ybWFsaXplZENvbnRlbnRcbiAgICB9O1xuXG4gICAgdGhpcy5zZWFyY2hNZXRyaWNzLnB1c2gobm9ybWFsaXplZE1ldHJpY3MpO1xuXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhIHNsb3cgcXVlcnkgKHVzZSBub3JtYWxpemVkIG1ldHJpY3MpXG4gICAgaWYgKG5vcm1hbGl6ZWRNZXRyaWNzLmR1cmF0aW9uID4gdGhpcy5zbG93UXVlcnlUaHJlc2hvbGQpIHtcbiAgICAgIHRoaXMucmVjb3JkU2xvd1F1ZXJ5KHtcbiAgICAgICAgcXVlcnk6IG5vcm1hbGl6ZWRNZXRyaWNzLnF1ZXJ5LFxuICAgICAgICBkdXJhdGlvbjogbm9ybWFsaXplZE1ldHJpY3MuZHVyYXRpb24sXG4gICAgICAgIHRocmVzaG9sZDogdGhpcy5zbG93UXVlcnlUaHJlc2hvbGQsXG4gICAgICAgIHNvdXJjZXM6IG5vcm1hbGl6ZWRNZXRyaWNzLnNvdXJjZXMsXG4gICAgICAgIHJlc3VsdENvdW50OiBub3JtYWxpemVkTWV0cmljcy5yZXN1bHRDb3VudCxcbiAgICAgICAgbWVtb3J5VXNhZ2U6IG5vcm1hbGl6ZWRNZXRyaWNzLm1lbW9yeUFmdGVyLFxuICAgICAgICB0aW1lc3RhbXA6IG5vcm1hbGl6ZWRNZXRyaWNzLnRpbWVzdGFtcFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gVHJpbSBoaXN0b3J5IGlmIG5lZWRlZFxuICAgIGlmICh0aGlzLnNlYXJjaE1ldHJpY3MubGVuZ3RoID4gdGhpcy5tYXhNZXRyaWNzSGlzdG9yeSkge1xuICAgICAgdGhpcy5zZWFyY2hNZXRyaWNzID0gdGhpcy5zZWFyY2hNZXRyaWNzLnNsaWNlKC10aGlzLm1heE1ldHJpY3NIaXN0b3J5KTtcbiAgICB9XG5cbiAgICAvLyBMb2cgc2lnbmlmaWNhbnQgcGVyZm9ybWFuY2UgZXZlbnRzICh1c2Ugbm9ybWFsaXplZCBtZXRyaWNzKVxuICAgIGlmIChub3JtYWxpemVkTWV0cmljcy5kdXJhdGlvbiA+IHRoaXMuc2xvd1F1ZXJ5VGhyZXNob2xkICogMikge1xuICAgICAgbG9nZ2VyLndhcm4oJ1Zlcnkgc2xvdyBzZWFyY2ggZGV0ZWN0ZWQnLCB7XG4gICAgICAgIHF1ZXJ5OiBub3JtYWxpemVkTWV0cmljcy5xdWVyeS5zdWJzdHJpbmcoMCwgNTApLFxuICAgICAgICBkdXJhdGlvbjogbm9ybWFsaXplZE1ldHJpY3MuZHVyYXRpb24sXG4gICAgICAgIHJlc3VsdENvdW50OiBub3JtYWxpemVkTWV0cmljcy5yZXN1bHRDb3VudCxcbiAgICAgICAgc291cmNlczogbm9ybWFsaXplZE1ldHJpY3Muc291cmNlc1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlY29yZCBjYWNoZSBwZXJmb3JtYW5jZSBtZXRyaWNzXG4gICAqL1xuICByZWNvcmRDYWNoZVBlcmZvcm1hbmNlKGNhY2hlTmFtZTogc3RyaW5nLCBzdGF0czogQ2FjaGVQZXJmb3JtYW5jZSk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc01vbml0b3JpbmcpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBOb3JtYWxpemUgY2FjaGUgbmFtZSB0byBwcmV2ZW50IFVuaWNvZGUtYmFzZWQgYXR0YWNrc1xuICAgIGNvbnN0IHZhbGlkYXRpb25SZXN1bHQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShjYWNoZU5hbWUpO1xuICAgIGNvbnN0IG5vcm1hbGl6ZWRDYWNoZU5hbWUgPSB2YWxpZGF0aW9uUmVzdWx0Lm5vcm1hbGl6ZWRDb250ZW50O1xuXG4gICAgdGhpcy5jYWNoZU1ldHJpY3Muc2V0KG5vcm1hbGl6ZWRDYWNoZU5hbWUsIHN0YXRzKTtcblxuICAgIC8vIExvZyBjYWNoZSBwZXJmb3JtYW5jZSB3YXJuaW5ncyAodXNlIG5vcm1hbGl6ZWQgY2FjaGUgbmFtZSlcbiAgICBpZiAoc3RhdHMuaGl0UmF0ZSA8IDAuNSkge1xuICAgICAgbG9nZ2VyLndhcm4oJ0xvdyBjYWNoZSBoaXQgcmF0ZSBkZXRlY3RlZCcsIHtcbiAgICAgICAgY2FjaGU6IG5vcm1hbGl6ZWRDYWNoZU5hbWUsXG4gICAgICAgIGhpdFJhdGU6IHN0YXRzLmhpdFJhdGUsXG4gICAgICAgIHRvdGFsT3BlcmF0aW9uczogc3RhdHMudG90YWxIaXRzICsgc3RhdHMudG90YWxNaXNzZXNcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgY29tcHJlaGVuc2l2ZSBwZXJmb3JtYW5jZSBtZXRyaWNzXG4gICAqL1xuICBnZXRNZXRyaWNzKCk6IFBlcmZvcm1hbmNlTWV0cmljcyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNlYXJjaFRpbWVzOiB0aGlzLnNlYXJjaE1ldHJpY3MubWFwKG0gPT4gbS5kdXJhdGlvbiksXG4gICAgICBtZW1vcnlVc2FnZTogdGhpcy5tZW1vcnlTbmFwc2hvdHMuc2xpY2UoLTEwMCksIC8vIExhc3QgMTAwIHNuYXBzaG90c1xuICAgICAgY2FjaGVTdGF0czogdGhpcy5hZ2dyZWdhdGVDYWNoZVN0YXRzKCksXG4gICAgICBzeXN0ZW1TdGF0czogdGhpcy5nZXRTeXN0ZW1TdGF0cygpLFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgc2VhcmNoIHBlcmZvcm1hbmNlIHN0YXRpc3RpY3NcbiAgICovXG4gIGdldFNlYXJjaFN0YXRzKCk6IHtcbiAgICB0b3RhbFNlYXJjaGVzOiBudW1iZXI7XG4gICAgYXZlcmFnZVRpbWU6IG51bWJlcjtcbiAgICBtZWRpYW5UaW1lOiBudW1iZXI7XG4gICAgcDk1VGltZTogbnVtYmVyO1xuICAgIHA5OVRpbWU6IG51bWJlcjtcbiAgICBzbG93UXVlcmllczogbnVtYmVyO1xuICAgIGNhY2hlSGl0UmF0ZTogbnVtYmVyO1xuICB9IHtcbiAgICBpZiAodGhpcy5zZWFyY2hNZXRyaWNzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdG90YWxTZWFyY2hlczogMCxcbiAgICAgICAgYXZlcmFnZVRpbWU6IDAsXG4gICAgICAgIG1lZGlhblRpbWU6IDAsXG4gICAgICAgIHA5NVRpbWU6IDAsXG4gICAgICAgIHA5OVRpbWU6IDAsXG4gICAgICAgIHNsb3dRdWVyaWVzOiAwLFxuICAgICAgICBjYWNoZUhpdFJhdGU6IDBcbiAgICAgIH07XG4gICAgfVxuXG4gICAgY29uc3QgdGltZXMgPSB0aGlzLnNlYXJjaE1ldHJpY3MubWFwKG0gPT4gbS5kdXJhdGlvbikuc29ydCgoYSwgYikgPT4gYSAtIGIpO1xuICAgIGNvbnN0IGNhY2hlSGl0cyA9IHRoaXMuc2VhcmNoTWV0cmljcy5maWx0ZXIobSA9PiBtLmNhY2hlSGl0KS5sZW5ndGg7XG5cbiAgICByZXR1cm4ge1xuICAgICAgdG90YWxTZWFyY2hlczogdGhpcy5zZWFyY2hNZXRyaWNzLmxlbmd0aCxcbiAgICAgIGF2ZXJhZ2VUaW1lOiB0aW1lcy5yZWR1Y2UoKHN1bSwgdGltZSkgPT4gc3VtICsgdGltZSwgMCkgLyB0aW1lcy5sZW5ndGgsXG4gICAgICBtZWRpYW5UaW1lOiB0aW1lc1tNYXRoLmZsb29yKHRpbWVzLmxlbmd0aCAvIDIpXSxcbiAgICAgIHA5NVRpbWU6IHRpbWVzW01hdGguZmxvb3IodGltZXMubGVuZ3RoICogMC45NSldLFxuICAgICAgcDk5VGltZTogdGltZXNbTWF0aC5mbG9vcih0aW1lcy5sZW5ndGggKiAwLjk5KV0sXG4gICAgICBzbG93UXVlcmllczogdGhpcy5zbG93UXVlcmllcy5sZW5ndGgsXG4gICAgICBjYWNoZUhpdFJhdGU6IGNhY2hlSGl0cyAvIHRoaXMuc2VhcmNoTWV0cmljcy5sZW5ndGhcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBtZW1vcnkgdXNhZ2Ugc3RhdGlzdGljc1xuICAgKi9cbiAgZ2V0TWVtb3J5U3RhdHMoKToge1xuICAgIGN1cnJlbnRVc2FnZTogTWVtb3J5VXNhZ2U7XG4gICAgcGVha1VzYWdlOiBNZW1vcnlVc2FnZTtcbiAgICBhdmVyYWdlVXNhZ2U6IE1lbW9yeVVzYWdlO1xuICAgIGdyb3d0aFJhdGU6IG51bWJlcjsgLy8gTUIgcGVyIG1pbnV0ZVxuICB9IHtcbiAgICBpZiAodGhpcy5tZW1vcnlTbmFwc2hvdHMubGVuZ3RoID09PSAwKSB7XG4gICAgICBjb25zdCBjdXJyZW50ID0gdGhpcy50YWtlTWVtb3J5U25hcHNob3QoKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGN1cnJlbnRVc2FnZTogY3VycmVudCxcbiAgICAgICAgcGVha1VzYWdlOiBjdXJyZW50LFxuICAgICAgICBhdmVyYWdlVXNhZ2U6IGN1cnJlbnQsXG4gICAgICAgIGdyb3d0aFJhdGU6IDBcbiAgICAgIH07XG4gICAgfVxuXG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMubWVtb3J5U25hcHNob3RzW3RoaXMubWVtb3J5U25hcHNob3RzLmxlbmd0aCAtIDFdO1xuICAgIGNvbnN0IHBlYWsgPSB0aGlzLm1lbW9yeVNuYXBzaG90cy5yZWR1Y2UoKG1heCwgc25hcHNob3QpID0+IFxuICAgICAgc25hcHNob3QuaGVhcFVzZWQgPiBtYXguaGVhcFVzZWQgPyBzbmFwc2hvdCA6IG1heFxuICAgICk7XG5cbiAgICBjb25zdCB0b3RhbEhlYXAgPSB0aGlzLm1lbW9yeVNuYXBzaG90cy5yZWR1Y2UoKHN1bSwgc25hcHNob3QpID0+IHN1bSArIHNuYXBzaG90LmhlYXBVc2VkLCAwKTtcbiAgICBjb25zdCB0b3RhbFJzcyA9IHRoaXMubWVtb3J5U25hcHNob3RzLnJlZHVjZSgoc3VtLCBzbmFwc2hvdCkgPT4gc3VtICsgc25hcHNob3QucnNzLCAwKTtcbiAgICBjb25zdCBhdmVyYWdlOiBNZW1vcnlVc2FnZSA9IHtcbiAgICAgIGhlYXBVc2VkOiB0b3RhbEhlYXAgLyB0aGlzLm1lbW9yeVNuYXBzaG90cy5sZW5ndGgsXG4gICAgICBoZWFwVG90YWw6IHRoaXMubWVtb3J5U25hcHNob3RzLnJlZHVjZSgoc3VtLCBzKSA9PiBzdW0gKyBzLmhlYXBUb3RhbCwgMCkgLyB0aGlzLm1lbW9yeVNuYXBzaG90cy5sZW5ndGgsXG4gICAgICByc3M6IHRvdGFsUnNzIC8gdGhpcy5tZW1vcnlTbmFwc2hvdHMubGVuZ3RoLFxuICAgICAgZXh0ZXJuYWw6IHRoaXMubWVtb3J5U25hcHNob3RzLnJlZHVjZSgoc3VtLCBzKSA9PiBzdW0gKyBzLmV4dGVybmFsLCAwKSAvIHRoaXMubWVtb3J5U25hcHNob3RzLmxlbmd0aCxcbiAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKVxuICAgIH07XG5cbiAgICAvLyBDYWxjdWxhdGUgZ3Jvd3RoIHJhdGUgKE1CIHBlciBtaW51dGUpXG4gICAgbGV0IGdyb3d0aFJhdGUgPSAwO1xuICAgIGlmICh0aGlzLm1lbW9yeVNuYXBzaG90cy5sZW5ndGggPiAxKSB7XG4gICAgICBjb25zdCBvbGRlc3QgPSB0aGlzLm1lbW9yeVNuYXBzaG90c1swXTtcbiAgICAgIGNvbnN0IHRpbWVEaWZmID0gKGN1cnJlbnQudGltZXN0YW1wLmdldFRpbWUoKSAtIG9sZGVzdC50aW1lc3RhbXAuZ2V0VGltZSgpKSAvIDYwMDAwOyAvLyBtaW51dGVzXG4gICAgICBjb25zdCBtZW1vcnlEaWZmID0gKGN1cnJlbnQuaGVhcFVzZWQgLSBvbGRlc3QuaGVhcFVzZWQpIC8gKDEwMjQgKiAxMDI0KTsgLy8gTUJcbiAgICAgIGdyb3d0aFJhdGUgPSB0aW1lRGlmZiA+IDAgPyBtZW1vcnlEaWZmIC8gdGltZURpZmYgOiAwO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBjdXJyZW50VXNhZ2U6IGN1cnJlbnQsXG4gICAgICBwZWFrVXNhZ2U6IHBlYWssXG4gICAgICBhdmVyYWdlVXNhZ2U6IGF2ZXJhZ2UsXG4gICAgICBncm93dGhSYXRlXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgc2xvdyBxdWVyaWVzIHdpdGggYW5hbHlzaXNcbiAgICovXG4gIGdldFNsb3dRdWVyaWVzKGxpbWl0OiBudW1iZXIgPSAxMCk6IFNsb3dRdWVyeVtdIHtcbiAgICByZXR1cm4gdGhpcy5zbG93UXVlcmllc1xuICAgICAgLnNvcnQoKGEsIGIpID0+IGIuZHVyYXRpb24gLSBhLmR1cmF0aW9uKVxuICAgICAgLnNsaWNlKDAsIGxpbWl0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBbmFseXplIHBlcmZvcm1hbmNlIHRyZW5kc1xuICAgKi9cbiAgYW5hbHl6ZVRyZW5kcygpOiB7XG4gICAgcGVyZm9ybWFuY2VUcmVuZDogJ2ltcHJvdmluZycgfCAnZGVncmFkaW5nJyB8ICdzdGFibGUnO1xuICAgIG1lbW9yeVRyZW5kOiAnZ3Jvd2luZycgfCAnc2hyaW5raW5nJyB8ICdzdGFibGUnO1xuICAgIHJlY29tbWVuZGF0aW9uczogc3RyaW5nW107XG4gIH0ge1xuICAgIGNvbnN0IHJlY29tbWVuZGF0aW9uczogc3RyaW5nW10gPSBbXTtcbiAgICBcbiAgICAvLyBBbmFseXplIHNlYXJjaCBwZXJmb3JtYW5jZSB0cmVuZFxuICAgIGxldCBwZXJmb3JtYW5jZVRyZW5kOiAnaW1wcm92aW5nJyB8ICdkZWdyYWRpbmcnIHwgJ3N0YWJsZScgPSAnc3RhYmxlJztcbiAgICBpZiAodGhpcy5zZWFyY2hNZXRyaWNzLmxlbmd0aCA+IDEwKSB7XG4gICAgICBjb25zdCByZWNlbnQgPSB0aGlzLnNlYXJjaE1ldHJpY3Muc2xpY2UoLTEwKS5tYXAobSA9PiBtLmR1cmF0aW9uKTtcbiAgICAgIGNvbnN0IG9sZGVyID0gdGhpcy5zZWFyY2hNZXRyaWNzLnNsaWNlKC0yMCwgLTEwKS5tYXAobSA9PiBtLmR1cmF0aW9uKTtcbiAgICAgIFxuICAgICAgaWYgKHJlY2VudC5sZW5ndGggPiAwICYmIG9sZGVyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3QgcmVjZW50QXZnID0gcmVjZW50LnJlZHVjZSgoc3VtLCB0KSA9PiBzdW0gKyB0LCAwKSAvIHJlY2VudC5sZW5ndGg7XG4gICAgICAgIGNvbnN0IG9sZGVyQXZnID0gb2xkZXIucmVkdWNlKChzdW0sIHQpID0+IHN1bSArIHQsIDApIC8gb2xkZXIubGVuZ3RoO1xuICAgICAgICBcbiAgICAgICAgaWYgKHJlY2VudEF2ZyA+IG9sZGVyQXZnICogMS4yKSB7XG4gICAgICAgICAgcGVyZm9ybWFuY2VUcmVuZCA9ICdkZWdyYWRpbmcnO1xuICAgICAgICAgIHJlY29tbWVuZGF0aW9ucy5wdXNoKCdTZWFyY2ggcGVyZm9ybWFuY2UgaXMgZGVncmFkaW5nLiBDb25zaWRlciBjYWNoZSBvcHRpbWl6YXRpb24gb3IgaW5kZXggcmVidWlsZGluZy4nKTtcbiAgICAgICAgfSBlbHNlIGlmIChyZWNlbnRBdmcgPCBvbGRlckF2ZyAqIDAuOCkge1xuICAgICAgICAgIHBlcmZvcm1hbmNlVHJlbmQgPSAnaW1wcm92aW5nJztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEFuYWx5emUgbWVtb3J5IHRyZW5kXG4gICAgY29uc3QgbWVtb3J5U3RhdHMgPSB0aGlzLmdldE1lbW9yeVN0YXRzKCk7XG4gICAgbGV0IG1lbW9yeVRyZW5kOiAnZ3Jvd2luZycgfCAnc2hyaW5raW5nJyB8ICdzdGFibGUnID0gJ3N0YWJsZSc7XG4gICAgXG4gICAgaWYgKG1lbW9yeVN0YXRzLmdyb3d0aFJhdGUgPiAxKSB7IC8vIEdyb3dpbmcgYnkgbW9yZSB0aGFuIDFNQi9taW51dGVcbiAgICAgIG1lbW9yeVRyZW5kID0gJ2dyb3dpbmcnO1xuICAgICAgcmVjb21tZW5kYXRpb25zLnB1c2goJ01lbW9yeSB1c2FnZSBpcyBncm93aW5nIHJhcGlkbHkuIENvbnNpZGVyIGNhY2hlIGNsZWFudXAgb3IgbWVtb3J5IGxpbWl0cy4nKTtcbiAgICB9IGVsc2UgaWYgKG1lbW9yeVN0YXRzLmdyb3d0aFJhdGUgPCAtMSkge1xuICAgICAgbWVtb3J5VHJlbmQgPSAnc2hyaW5raW5nJztcbiAgICB9XG5cbiAgICAvLyBDYWNoZSBwZXJmb3JtYW5jZSByZWNvbW1lbmRhdGlvbnNcbiAgICBjb25zdCBjYWNoZVN0YXRzID0gdGhpcy5hZ2dyZWdhdGVDYWNoZVN0YXRzKCk7XG4gICAgaWYgKGNhY2hlU3RhdHMuaGl0UmF0ZSA8IDAuNikge1xuICAgICAgcmVjb21tZW5kYXRpb25zLnB1c2goJ0NhY2hlIGhpdCByYXRlIGlzIGxvdy4gQ29uc2lkZXIgYWRqdXN0aW5nIGNhY2hlIHNpemUgb3IgVFRMIHNldHRpbmdzLicpO1xuICAgIH1cblxuICAgIC8vIFNsb3cgcXVlcnkgcmVjb21tZW5kYXRpb25zXG4gICAgaWYgKHRoaXMuc2xvd1F1ZXJpZXMubGVuZ3RoID4gMTApIHtcbiAgICAgIHJlY29tbWVuZGF0aW9ucy5wdXNoKCdNdWx0aXBsZSBzbG93IHF1ZXJpZXMgZGV0ZWN0ZWQuIENvbnNpZGVyIHF1ZXJ5IG9wdGltaXphdGlvbiBvciBpbmNyZWFzZWQgY2FjaGluZy4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgcGVyZm9ybWFuY2VUcmVuZCxcbiAgICAgIG1lbW9yeVRyZW5kLFxuICAgICAgcmVjb21tZW5kYXRpb25zXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNldCBhbGwgcGVyZm9ybWFuY2UgbWV0cmljc1xuICAgKi9cbiAgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5zZWFyY2hNZXRyaWNzID0gW107XG4gICAgdGhpcy5zbG93UXVlcmllcyA9IFtdO1xuICAgIHRoaXMubWVtb3J5U25hcHNob3RzID0gW107XG4gICAgdGhpcy5jYWNoZU1ldHJpY3MuY2xlYXIoKTtcbiAgICBcbiAgICBsb2dnZXIuaW5mbygnUGVyZm9ybWFuY2UgbWV0cmljcyByZXNldCcpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4cG9ydCBtZXRyaWNzIGZvciBleHRlcm5hbCBhbmFseXNpc1xuICAgKi9cbiAgZXhwb3J0TWV0cmljcygpOiBzdHJpbmcge1xuICAgIGNvbnN0IGRhdGEgPSB7XG4gICAgICBzZWFyY2hNZXRyaWNzOiB0aGlzLnNlYXJjaE1ldHJpY3MsXG4gICAgICBzbG93UXVlcmllczogdGhpcy5zbG93UXVlcmllcyxcbiAgICAgIG1lbW9yeVNuYXBzaG90czogdGhpcy5tZW1vcnlTbmFwc2hvdHMsXG4gICAgICBjYWNoZU1ldHJpY3M6IE9iamVjdC5mcm9tRW50cmllcyh0aGlzLmNhY2hlTWV0cmljcyksXG4gICAgICBleHBvcnRUaW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKVxuICAgIH07XG5cbiAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkoZGF0YSwgbnVsbCwgMik7XG4gIH1cblxuICAvLyBQcml2YXRlIG1ldGhvZHNcblxuICBwcml2YXRlIHJlY29yZFNsb3dRdWVyeShxdWVyeTogU2xvd1F1ZXJ5KTogdm9pZCB7XG4gICAgdGhpcy5zbG93UXVlcmllcy5wdXNoKHF1ZXJ5KTtcblxuICAgIC8vIFRyaW0gc2xvdyBxdWVyaWVzIGhpc3RvcnlcbiAgICBpZiAodGhpcy5zbG93UXVlcmllcy5sZW5ndGggPiB0aGlzLm1heFNsb3dRdWVyaWVzKSB7XG4gICAgICB0aGlzLnNsb3dRdWVyaWVzID0gdGhpcy5zbG93UXVlcmllcy5zbGljZSgtdGhpcy5tYXhTbG93UXVlcmllcyk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdGFydE1lbW9yeU1vbml0b3JpbmcoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsKTtcbiAgICB9XG5cbiAgICB0aGlzLm1lbW9yeU1vbml0b3JJbnRlcnZhbCA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgIGlmICh0aGlzLmlzTW9uaXRvcmluZykge1xuICAgICAgICBjb25zdCBzbmFwc2hvdCA9IHRoaXMudGFrZU1lbW9yeVNuYXBzaG90KCk7XG4gICAgICAgIHRoaXMubWVtb3J5U25hcHNob3RzLnB1c2goc25hcHNob3QpO1xuXG4gICAgICAgIC8vIFRyaW0gbWVtb3J5IHNuYXBzaG90cyAoa2VlcCBsYXN0IDIwMClcbiAgICAgICAgaWYgKHRoaXMubWVtb3J5U25hcHNob3RzLmxlbmd0aCA+IDIwMCkge1xuICAgICAgICAgIHRoaXMubWVtb3J5U25hcHNob3RzID0gdGhpcy5tZW1vcnlTbmFwc2hvdHMuc2xpY2UoLTIwMCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LCB0aGlzLm1lbW9yeVNuYXBzaG90SW50ZXJ2YWwpO1xuICB9XG5cbiAgcHJpdmF0ZSB0YWtlTWVtb3J5U25hcHNob3QoKTogTWVtb3J5VXNhZ2Uge1xuICAgIGNvbnN0IG1lbVVzYWdlID0gcHJvY2Vzcy5tZW1vcnlVc2FnZSgpO1xuICAgIHJldHVybiB7XG4gICAgICBoZWFwVXNlZDogbWVtVXNhZ2UuaGVhcFVzZWQsXG4gICAgICBoZWFwVG90YWw6IG1lbVVzYWdlLmhlYXBUb3RhbCxcbiAgICAgIHJzczogbWVtVXNhZ2UucnNzLFxuICAgICAgZXh0ZXJuYWw6IG1lbVVzYWdlLmV4dGVybmFsLFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgYWdncmVnYXRlQ2FjaGVTdGF0cygpOiBDYWNoZVBlcmZvcm1hbmNlIHtcbiAgICBpZiAodGhpcy5jYWNoZU1ldHJpY3Muc2l6ZSA9PT0gMCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaGl0UmF0ZTogMCxcbiAgICAgICAgYXZnSGl0VGltZTogMCxcbiAgICAgICAgYXZnTWlzc1RpbWU6IDAsXG4gICAgICAgIHRvdGFsSGl0czogMCxcbiAgICAgICAgdG90YWxNaXNzZXM6IDAsXG4gICAgICAgIGV2aWN0aW9uczogMFxuICAgICAgfTtcbiAgICB9XG5cbiAgICBsZXQgdG90YWxIaXRzID0gMDtcbiAgICBsZXQgdG90YWxNaXNzZXMgPSAwO1xuICAgIGxldCB0b3RhbEV2aWN0aW9ucyA9IDA7XG4gICAgbGV0IHdlaWdodGVkSGl0VGltZSA9IDA7XG4gICAgbGV0IHdlaWdodGVkTWlzc1RpbWUgPSAwO1xuXG4gICAgZm9yIChjb25zdCBzdGF0cyBvZiB0aGlzLmNhY2hlTWV0cmljcy52YWx1ZXMoKSkge1xuICAgICAgdG90YWxIaXRzICs9IHN0YXRzLnRvdGFsSGl0cztcbiAgICAgIHRvdGFsTWlzc2VzICs9IHN0YXRzLnRvdGFsTWlzc2VzO1xuICAgICAgdG90YWxFdmljdGlvbnMgKz0gc3RhdHMuZXZpY3Rpb25zO1xuICAgICAgd2VpZ2h0ZWRIaXRUaW1lICs9IHN0YXRzLmF2Z0hpdFRpbWUgKiBzdGF0cy50b3RhbEhpdHM7XG4gICAgICB3ZWlnaHRlZE1pc3NUaW1lICs9IHN0YXRzLmF2Z01pc3NUaW1lICogc3RhdHMudG90YWxNaXNzZXM7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGhpdFJhdGU6IHRvdGFsSGl0cyArIHRvdGFsTWlzc2VzID4gMCA/IHRvdGFsSGl0cyAvICh0b3RhbEhpdHMgKyB0b3RhbE1pc3NlcykgOiAwLFxuICAgICAgYXZnSGl0VGltZTogdG90YWxIaXRzID4gMCA/IHdlaWdodGVkSGl0VGltZSAvIHRvdGFsSGl0cyA6IDAsXG4gICAgICBhdmdNaXNzVGltZTogdG90YWxNaXNzZXMgPiAwID8gd2VpZ2h0ZWRNaXNzVGltZSAvIHRvdGFsTWlzc2VzIDogMCxcbiAgICAgIHRvdGFsSGl0cyxcbiAgICAgIHRvdGFsTWlzc2VzLFxuICAgICAgZXZpY3Rpb25zOiB0b3RhbEV2aWN0aW9uc1xuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGdldFN5c3RlbVN0YXRzKCk6IFN5c3RlbVN0YXRzIHtcbiAgICBjb25zdCBvcyA9IHJlcXVpcmUoJ29zJyk7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIGNwdVVzYWdlOiBwcm9jZXNzLmNwdVVzYWdlKCkudXNlciAvIDEwMDAwMDAsIC8vIENvbnZlcnQgdG8gc2Vjb25kc1xuICAgICAgbG9hZEF2ZXJhZ2U6IG9zLmxvYWRhdmcoKSxcbiAgICAgIGZyZWVNZW1vcnk6IG9zLmZyZWVtZW0oKSxcbiAgICAgIHRvdGFsTWVtb3J5OiBvcy50b3RhbG1lbSgpLFxuICAgICAgdXB0aW1lOiBwcm9jZXNzLnVwdGltZSgpXG4gICAgfTtcbiAgfVxufSJdfQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateLimiter.d.ts","sourceRoot":"","sources":["../../src/utils/RateLimiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,MAAM,EAAE,iBAAiB;IAsBrC;;;OAGG;IACH,UAAU,IAAI,eAAe;IA0C7B;;;OAGG;IACH,YAAY,IAAI,IAAI;IAUpB;;OAEG;IACH,SAAS,IAAI,eAAe;IAW5B;;;OAGG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,QAAQ,IAAI,MAAM;CAKnB;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B;;OAEG;IACH,MAAM,CAAC,mBAAmB,IAAI,WAAW;IAQzC;;;OAGG;IACH,MAAM,CAAC,wBAAwB,IAAI,WAAW;IAQ9C;;;OAGG;IACH,MAAM,CAAC,mBAAmB,IAAI,WAAW;CAO1C"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * RateLimiter - Implements rate limiting for API calls to prevent abuse
3
+ *
4
+ * Features:
5
+ * - Token bucket algorithm for flexible rate limiting
6
+ * - Configurable limits per time window
7
+ * - Memory-efficient implementation
8
+ * - Thread-safe for concurrent requests
9
+ */
10
+ export class RateLimiter {
11
+ tokens;
12
+ lastRefill;
13
+ lastRequest;
14
+ maxTokens;
15
+ refillRate;
16
+ minDelay;
17
+ constructor(config) {
18
+ if (config.maxRequests <= 0) {
19
+ throw new Error('maxRequests must be positive');
20
+ }
21
+ if (config.windowMs <= 0) {
22
+ throw new Error('windowMs must be positive');
23
+ }
24
+ this.maxTokens = config.maxRequests;
25
+ this.tokens = this.maxTokens;
26
+ this.refillRate = this.maxTokens / config.windowMs;
27
+ // Validate refill rate to prevent division by zero
28
+ if (this.refillRate <= 0 || !isFinite(this.refillRate)) {
29
+ throw new Error('Invalid configuration: refill rate must be positive and finite');
30
+ }
31
+ this.lastRefill = Date.now();
32
+ this.lastRequest = 0;
33
+ this.minDelay = config.minDelayMs || 0;
34
+ }
35
+ /**
36
+ * Check if a request is allowed under the rate limit
37
+ * @returns Status object indicating if request is allowed
38
+ */
39
+ checkLimit() {
40
+ const now = Date.now();
41
+ // Refill tokens based on time elapsed
42
+ this.refillTokens(now);
43
+ // Check minimum delay between requests
44
+ if (this.minDelay > 0 && this.lastRequest > 0) {
45
+ const timeSinceLastRequest = now - this.lastRequest;
46
+ if (timeSinceLastRequest < this.minDelay) {
47
+ const retryAfterMs = this.minDelay - timeSinceLastRequest;
48
+ return {
49
+ allowed: false,
50
+ retryAfterMs,
51
+ remainingTokens: Math.floor(this.tokens),
52
+ resetTime: new Date(now + retryAfterMs)
53
+ };
54
+ }
55
+ }
56
+ // Check if we have tokens available
57
+ if (this.tokens < 1) {
58
+ // Calculate when the next token will be available
59
+ const tokensNeeded = 1 - this.tokens;
60
+ const msUntilNextToken = tokensNeeded / this.refillRate;
61
+ return {
62
+ allowed: false,
63
+ retryAfterMs: Math.ceil(msUntilNextToken),
64
+ remainingTokens: 0,
65
+ resetTime: new Date(now + msUntilNextToken)
66
+ };
67
+ }
68
+ // Request is allowed
69
+ return {
70
+ allowed: true,
71
+ remainingTokens: Math.floor(this.tokens),
72
+ resetTime: this.getResetTime()
73
+ };
74
+ }
75
+ /**
76
+ * Consume a token for an allowed request
77
+ * Should be called after checkLimit() returns allowed: true
78
+ */
79
+ consumeToken() {
80
+ const now = Date.now();
81
+ this.refillTokens(now);
82
+ if (this.tokens >= 1) {
83
+ this.tokens -= 1;
84
+ this.lastRequest = now;
85
+ }
86
+ }
87
+ /**
88
+ * Get current rate limit status without consuming a token
89
+ */
90
+ getStatus() {
91
+ const now = Date.now();
92
+ this.refillTokens(now);
93
+ return {
94
+ allowed: this.tokens >= 1,
95
+ remainingTokens: Math.floor(this.tokens),
96
+ resetTime: this.getResetTime()
97
+ };
98
+ }
99
+ /**
100
+ * Reset the rate limiter to full capacity
101
+ * Useful for testing or manual intervention
102
+ */
103
+ reset() {
104
+ this.tokens = this.maxTokens;
105
+ this.lastRefill = Date.now();
106
+ this.lastRequest = 0;
107
+ }
108
+ /**
109
+ * Refill tokens based on time elapsed
110
+ */
111
+ refillTokens(now) {
112
+ const timeSinceLastRefill = now - this.lastRefill;
113
+ const tokensToAdd = timeSinceLastRefill * this.refillRate;
114
+ this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
115
+ this.lastRefill = now;
116
+ }
117
+ /**
118
+ * Calculate when the rate limit window will reset
119
+ */
120
+ getResetTime() {
121
+ const now = Date.now();
122
+ const tokensToFull = this.maxTokens - this.tokens;
123
+ const msUntilFull = tokensToFull / this.refillRate;
124
+ return new Date(now + msUntilFull);
125
+ }
126
+ /**
127
+ * Get human-readable rate limit information
128
+ */
129
+ toString() {
130
+ const status = this.getStatus();
131
+ return `RateLimit: ${status.remainingTokens}/${this.maxTokens} tokens, ` +
132
+ `resets at ${status.resetTime.toISOString()}`;
133
+ }
134
+ }
135
+ /**
136
+ * Factory function to create common rate limiters
137
+ */
138
+ export class RateLimiterFactory {
139
+ /**
140
+ * GitHub API rate limiter (60 requests per hour for unauthenticated)
141
+ */
142
+ static createGitHubLimiter() {
143
+ return new RateLimiter({
144
+ maxRequests: 60,
145
+ windowMs: 60 * 60 * 1000, // 1 hour
146
+ minDelayMs: 1000 // 1 second minimum between requests
147
+ });
148
+ }
149
+ /**
150
+ * Conservative rate limiter for update checks
151
+ * Allows 10 checks per hour with 30 second minimum delay
152
+ */
153
+ static createUpdateCheckLimiter() {
154
+ return new RateLimiter({
155
+ maxRequests: 10,
156
+ windowMs: 60 * 60 * 1000, // 1 hour
157
+ minDelayMs: 30 * 1000 // 30 seconds between checks
158
+ });
159
+ }
160
+ /**
161
+ * Strict rate limiter for sensitive operations
162
+ * Allows 5 requests per hour with 1 minute minimum delay
163
+ */
164
+ static createStrictLimiter() {
165
+ return new RateLimiter({
166
+ maxRequests: 5,
167
+ windowMs: 60 * 60 * 1000, // 1 hour
168
+ minDelayMs: 60 * 1000 // 1 minute between requests
169
+ });
170
+ }
171
+ }
172
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmF0ZUxpbWl0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvUmF0ZUxpbWl0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFlSCxNQUFNLE9BQU8sV0FBVztJQUNkLE1BQU0sQ0FBUztJQUNmLFVBQVUsQ0FBUztJQUNuQixXQUFXLENBQVM7SUFDWCxTQUFTLENBQVM7SUFDbEIsVUFBVSxDQUFTO0lBQ25CLFFBQVEsQ0FBUztJQUVsQyxZQUFZLE1BQXlCO1FBQ25DLElBQUksTUFBTSxDQUFDLFdBQVcsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUM1QixNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLFFBQVEsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN6QixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQztRQUNwQyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDN0IsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUM7UUFFbkQsbURBQW1EO1FBQ25ELElBQUksSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7WUFDdkQsTUFBTSxJQUFJLEtBQUssQ0FBQyxnRUFBZ0UsQ0FBQyxDQUFDO1FBQ3BGLENBQUM7UUFFRCxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUM3QixJQUFJLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsUUFBUSxHQUFHLE1BQU0sQ0FBQyxV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TCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLHdCQUF3QjtRQUM3QixPQUFPLElBQUksV0FBVyxDQUFDO1lBQ3JCLFdBQVcsRUFBRSxFQUFFO1lBQ2YsUUFBUSxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLFNBQVM7WUFDbkMsVUFBVSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUMsNEJBQTRCO1NBQ25ELENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7O09BR0c7SUFDSCxNQUFNLENBQUMsbUJBQW1CO1FBQ3hCLE9BQU8sSUFBSSxXQUFXLENBQUM7WUFDckIsV0FBVyxFQUFFLENBQUM7WUFDZCxRQUFRLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLEVBQUUsU0FBUztZQUNuQyxVQUFVLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQyw0QkFBNEI7U0FDbkQsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSYXRlTGltaXRlciAtIEltcGxlbWVudHMgcmF0ZSBsaW1pdGluZyBmb3IgQVBJIGNhbGxzIHRvIHByZXZlbnQgYWJ1c2VcbiAqIFxuICogRmVhdHVyZXM6XG4gKiAtIFRva2VuIGJ1Y2tldCBhbGdvcml0aG0gZm9yIGZsZXhpYmxlIHJhdGUgbGltaXRpbmdcbiAqIC0gQ29uZmlndXJhYmxlIGxpbWl0cyBwZXIgdGltZSB3aW5kb3dcbiAqIC0gTWVtb3J5LWVmZmljaWVudCBpbXBsZW1lbnRhdGlvblxuICogLSBUaHJlYWQtc2FmZSBmb3IgY29uY3VycmVudCByZXF1ZXN0c1xuICovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUmF0ZUxpbWl0ZXJDb25maWcge1xuICBtYXhSZXF1ZXN0czogbnVtYmVyOyAgICAgIC8vIE1heGltdW0gcmVxdWVzdHMgYWxsb3dlZFxuICB3aW5kb3dNczogbnVtYmVyOyAgICAgICAgIC8vIFRpbWUgd2luZG93IGluIG1pbGxpc2Vjb25kc1xuICBtaW5EZWxheU1zPzogbnVtYmVyOyAgICAgIC8vIE1pbmltdW0gZGVsYXkgYmV0d2VlbiByZXF1ZXN0cyAob3B0aW9uYWwpXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmF0ZUxpbWl0U3RhdHVzIHtcbiAgYWxsb3dlZDogYm9vbGVhbjtcbiAgcmV0cnlBZnRlck1zPzogbnVtYmVyO1xuICByZW1haW5pbmdUb2tlbnM6IG51bWJlcjtcbiAgcmVzZXRUaW1lOiBEYXRlO1xufVxuXG5leHBvcnQgY2xhc3MgUmF0ZUxpbWl0ZXIge1xuICBwcml2YXRlIHRva2VuczogbnVtYmVyO1xuICBwcml2YXRlIGxhc3RSZWZpbGw6IG51bWJlcjtcbiAgcHJpdmF0ZSBsYXN0UmVxdWVzdDogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IG1heFRva2VuczogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IHJlZmlsbFJhdGU6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBtaW5EZWxheTogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKGNvbmZpZzogUmF0ZUxpbWl0ZXJDb25maWcpIHtcbiAgICBpZiAoY29uZmlnLm1heFJlcXVlc3RzIDw9IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWF4UmVxdWVzdHMgbXVzdCBiZSBwb3NpdGl2ZScpO1xuICAgIH1cbiAgICBpZiAoY29uZmlnLndpbmRvd01zIDw9IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignd2luZG93TXMgbXVzdCBiZSBwb3NpdGl2ZScpO1xuICAgIH1cblxuICAgIHRoaXMubWF4VG9rZW5zID0gY29uZmlnLm1heFJlcXVlc3RzO1xuICAgIHRoaXMudG9rZW5zID0gdGhpcy5tYXhUb2tlbnM7XG4gICAgdGhpcy5yZWZpbGxSYXRlID0gdGhpcy5tYXhUb2tlbnMgLyBjb25maWcud2luZG93TXM7XG4gICAgXG4gICAgLy8gVmFsaWRhdGUgcmVmaWxsIHJhdGUgdG8gcHJldmVudCBkaXZpc2lvbiBieSB6ZXJvXG4gICAgaWYgKHRoaXMucmVmaWxsUmF0ZSA8PSAwIHx8ICFpc0Zpbml0ZSh0aGlzLnJlZmlsbFJhdGUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY29uZmlndXJhdGlvbjogcmVmaWxsIHJhdGUgbXVzdCBiZSBwb3NpdGl2ZSBhbmQgZmluaXRlJyk7XG4gICAgfVxuICAgIFxuICAgIHRoaXMubGFzdFJlZmlsbCA9IERhdGUubm93KCk7XG4gICAgdGhpcy5sYXN0UmVxdWVzdCA9IDA7XG4gICAgdGhpcy5taW5EZWxheSA9IGNvbmZpZy5taW5EZWxheU1zIHx8IDA7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgYSByZXF1ZXN0IGlzIGFsbG93ZWQgdW5kZXIgdGhlIHJhdGUgbGltaXRcbiAgICogQHJldHVybnMgU3RhdHVzIG9iamVjdCBpbmRpY2F0aW5nIGlmIHJlcXVlc3QgaXMgYWxsb3dlZFxuICAgKi9cbiAgY2hlY2tMaW1pdCgpOiBSYXRlTGltaXRTdGF0dXMge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgXG4gICAgLy8gUmVmaWxsIHRva2VucyBiYXNlZCBvbiB0aW1lIGVsYXBzZWRcbiAgICB0aGlzLnJlZmlsbFRva2Vucyhub3cpO1xuXG4gICAgLy8gQ2hlY2sgbWluaW11bSBkZWxheSBiZXR3ZWVuIHJlcXVlc3RzXG4gICAgaWYgKHRoaXMubWluRGVsYXkgPiAwICYmIHRoaXMubGFzdFJlcXVlc3QgPiAwKSB7XG4gICAgICBjb25zdCB0aW1lU2luY2VMYXN0UmVxdWVzdCA9IG5vdyAtIHRoaXMubGFzdFJlcXVlc3Q7XG4gICAgICBpZiAodGltZVNpbmNlTGFzdFJlcXVlc3QgPCB0aGlzLm1pbkRlbGF5KSB7XG4gICAgICAgIGNvbnN0IHJldHJ5QWZ0ZXJNcyA9IHRoaXMubWluRGVsYXkgLSB0aW1lU2luY2VMYXN0UmVxdWVzdDtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBhbGxvd2VkOiBmYWxzZSxcbiAgICAgICAgICByZXRyeUFmdGVyTXMsXG4gICAgICAgICAgcmVtYWluaW5nVG9rZW5zOiBNYXRoLmZsb29yKHRoaXMudG9rZW5zKSxcbiAgICAgICAgICByZXNldFRpbWU6IG5ldyBEYXRlKG5vdyArIHJldHJ5QWZ0ZXJNcylcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDaGVjayBpZiB3ZSBoYXZlIHRva2VucyBhdmFpbGFibGVcbiAgICBpZiAodGhpcy50b2tlbnMgPCAxKSB7XG4gICAgICAvLyBDYWxjdWxhdGUgd2hlbiB0aGUgbmV4dCB0b2tlbiB3aWxsIGJlIGF2YWlsYWJsZVxuICAgICAgY29uc3QgdG9rZW5zTmVlZGVkID0gMSAtIHRoaXMudG9rZW5zO1xuICAgICAgY29uc3QgbXNVbnRpbE5leHRUb2tlbiA9IHRva2Vuc05lZWRlZCAvIHRoaXMucmVmaWxsUmF0ZTtcbiAgICAgIFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgYWxsb3dlZDogZmFsc2UsXG4gICAgICAgIHJldHJ5QWZ0ZXJNczogTWF0aC5jZWlsKG1zVW50aWxOZXh0VG9rZW4pLFxuICAgICAgICByZW1haW5pbmdUb2tlbnM6IDAsXG4gICAgICAgIHJlc2V0VGltZTogbmV3IERhdGUobm93ICsgbXNVbnRpbE5leHRUb2tlbilcbiAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gUmVxdWVzdCBpcyBhbGxvd2VkXG4gICAgcmV0dXJuIHtcbiAgICAgIGFsbG93ZWQ6IHRydWUsXG4gICAgICByZW1haW5pbmdUb2tlbnM6IE1hdGguZmxvb3IodGhpcy50b2tlbnMpLFxuICAgICAgcmVzZXRUaW1lOiB0aGlzLmdldFJlc2V0VGltZSgpXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25zdW1lIGEgdG9rZW4gZm9yIGFuIGFsbG93ZWQgcmVxdWVzdFxuICAgKiBTaG91bGQgYmUgY2FsbGVkIGFmdGVyIGNoZWNrTGltaXQoKSByZXR1cm5zIGFsbG93ZWQ6IHRydWVcbiAgICovXG4gIGNvbnN1bWVUb2tlbigpOiB2b2lkIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIHRoaXMucmVmaWxsVG9rZW5zKG5vdyk7XG4gICAgXG4gICAgaWYgKHRoaXMudG9rZW5zID49IDEpIHtcbiAgICAgIHRoaXMudG9rZW5zIC09IDE7XG4gICAgICB0aGlzLmxhc3RSZXF1ZXN0ID0gbm93O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgY3VycmVudCByYXRlIGxpbWl0IHN0YXR1cyB3aXRob3V0IGNvbnN1bWluZyBhIHRva2VuXG4gICAqL1xuICBnZXRTdGF0dXMoKTogUmF0ZUxpbWl0U3RhdHVzIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIHRoaXMucmVmaWxsVG9rZW5zKG5vdyk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgYWxsb3dlZDogdGhpcy50b2tlbnMgPj0gMSxcbiAgICAgIHJlbWFpbmluZ1Rva2VuczogTWF0aC5mbG9vcih0aGlzLnRva2VucyksXG4gICAgICByZXNldFRpbWU6IHRoaXMuZ2V0UmVzZXRUaW1lKClcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0IHRoZSByYXRlIGxpbWl0ZXIgdG8gZnVsbCBjYXBhY2l0eVxuICAgKiBVc2VmdWwgZm9yIHRlc3Rpbmcgb3IgbWFudWFsIGludGVydmVudGlvblxuICAgKi9cbiAgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy50b2tlbnMgPSB0aGlzLm1heFRva2VucztcbiAgICB0aGlzLmxhc3RSZWZpbGwgPSBEYXRlLm5vdygpO1xuICAgIHRoaXMubGFzdFJlcXVlc3QgPSAwO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZmlsbCB0b2tlbnMgYmFzZWQgb24gdGltZSBlbGFwc2VkXG4gICAqL1xuICBwcml2YXRlIHJlZmlsbFRva2Vucyhub3c6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHRpbWVTaW5jZUxhc3RSZWZpbGwgPSBub3cgLSB0aGlzLmxhc3RSZWZpbGw7XG4gICAgY29uc3QgdG9rZW5zVG9BZGQgPSB0aW1lU2luY2VMYXN0UmVmaWxsICogdGhpcy5yZWZpbGxSYXRlO1xuICAgIFxuICAgIHRoaXMudG9rZW5zID0gTWF0aC5taW4odGhpcy5tYXhUb2tlbnMsIHRoaXMudG9rZW5zICsgdG9rZW5zVG9BZGQpO1xuICAgIHRoaXMubGFzdFJlZmlsbCA9IG5vdztcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgd2hlbiB0aGUgcmF0ZSBsaW1pdCB3aW5kb3cgd2lsbCByZXNldFxuICAgKi9cbiAgcHJpdmF0ZSBnZXRSZXNldFRpbWUoKTogRGF0ZSB7XG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCB0b2tlbnNUb0Z1bGwgPSB0aGlzLm1heFRva2VucyAtIHRoaXMudG9rZW5zO1xuICAgIGNvbnN0IG1zVW50aWxGdWxsID0gdG9rZW5zVG9GdWxsIC8gdGhpcy5yZWZpbGxSYXRlO1xuICAgIHJldHVybiBuZXcgRGF0ZShub3cgKyBtc1VudGlsRnVsbCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGh1bWFuLXJlYWRhYmxlIHJhdGUgbGltaXQgaW5mb3JtYXRpb25cbiAgICovXG4gIHRvU3RyaW5nKCk6IHN0cmluZyB7XG4gICAgY29uc3Qgc3RhdHVzID0gdGhpcy5nZXRTdGF0dXMoKTtcbiAgICByZXR1cm4gYFJhdGVMaW1pdDogJHtzdGF0dXMucmVtYWluaW5nVG9rZW5zfS8ke3RoaXMubWF4VG9rZW5zfSB0b2tlbnMsIGAgK1xuICAgICAgICAgICBgcmVzZXRzIGF0ICR7c3RhdHVzLnJlc2V0VGltZS50b0lTT1N0cmluZygpfWA7XG4gIH1cbn1cblxuLyoqXG4gKiBGYWN0b3J5IGZ1bmN0aW9uIHRvIGNyZWF0ZSBjb21tb24gcmF0ZSBsaW1pdGVyc1xuICovXG5leHBvcnQgY2xhc3MgUmF0ZUxpbWl0ZXJGYWN0b3J5IHtcbiAgLyoqXG4gICAqIEdpdEh1YiBBUEkgcmF0ZSBsaW1pdGVyICg2MCByZXF1ZXN0cyBwZXIgaG91ciBmb3IgdW5hdXRoZW50aWNhdGVkKVxuICAgKi9cbiAgc3RhdGljIGNyZWF0ZUdpdEh1YkxpbWl0ZXIoKTogUmF0ZUxpbWl0ZXIge1xuICAgIHJldHVybiBuZXcgUmF0ZUxpbWl0ZXIoe1xuICAgICAgbWF4UmVxdWVzdHM6IDYwLFxuICAgICAgd2luZG93TXM6IDYwICogNjAgKiAxMDAwLCAvLyAxIGhvdXJcbiAgICAgIG1pbkRlbGF5TXM6IDEwMDAgLy8gMSBzZWNvbmQgbWluaW11bSBiZXR3ZWVuIHJlcXVlc3RzXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQ29uc2VydmF0aXZlIHJhdGUgbGltaXRlciBmb3IgdXBkYXRlIGNoZWNrc1xuICAgKiBBbGxvd3MgMTAgY2hlY2tzIHBlciBob3VyIHdpdGggMzAgc2Vjb25kIG1pbmltdW0gZGVsYXlcbiAgICovXG4gIHN0YXRpYyBjcmVhdGVVcGRhdGVDaGVja0xpbWl0ZXIoKTogUmF0ZUxpbWl0ZXIge1xuICAgIHJldHVybiBuZXcgUmF0ZUxpbWl0ZXIoe1xuICAgICAgbWF4UmVxdWVzdHM6IDEwLFxuICAgICAgd2luZG93TXM6IDYwICogNjAgKiAxMDAwLCAvLyAxIGhvdXJcbiAgICAgIG1pbkRlbGF5TXM6IDMwICogMTAwMCAvLyAzMCBzZWNvbmRzIGJldHdlZW4gY2hlY2tzXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU3RyaWN0IHJhdGUgbGltaXRlciBmb3Igc2Vuc2l0aXZlIG9wZXJhdGlvbnNcbiAgICogQWxsb3dzIDUgcmVxdWVzdHMgcGVyIGhvdXIgd2l0aCAxIG1pbnV0ZSBtaW5pbXVtIGRlbGF5XG4gICAqL1xuICBzdGF0aWMgY3JlYXRlU3RyaWN0TGltaXRlcigpOiBSYXRlTGltaXRlciB7XG4gICAgcmV0dXJuIG5ldyBSYXRlTGltaXRlcih7XG4gICAgICBtYXhSZXF1ZXN0czogNSxcbiAgICAgIHdpbmRvd01zOiA2MCAqIDYwICogMTAwMCwgLy8gMSBob3VyXG4gICAgICBtaW5EZWxheU1zOiA2MCAqIDEwMDAgLy8gMSBtaW51dGUgYmV0d2VlbiByZXF1ZXN0c1xuICAgIH0pO1xuICB9XG59Il19