@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,499 @@
1
+ /**
2
+ * Collection Index Manager with Background Refresh and Robust Caching
3
+ *
4
+ * This manager implements:
5
+ * - 1-hour TTL with local file caching
6
+ * - Background refresh without blocking operations
7
+ * - Exponential backoff retry logic
8
+ * - Configurable timeouts via environment variables
9
+ * - Return stale cache while refreshing in background
10
+ * - Comprehensive error handling for production use
11
+ */
12
+ import * as fs from 'fs/promises';
13
+ import * as path from 'path';
14
+ import * as os from 'os';
15
+ import { logger } from '../utils/logger.js';
16
+ export class CollectionIndexManager {
17
+ INDEX_URL = 'https://raw.githubusercontent.com/DollhouseMCP/collection/main/public/collection-index.json';
18
+ TTL_MS;
19
+ FETCH_TIMEOUT_MS;
20
+ MAX_RETRIES;
21
+ BASE_RETRY_DELAY_MS;
22
+ MAX_RETRY_DELAY_MS;
23
+ CACHE_FILE;
24
+ cachedIndex = null;
25
+ backgroundRefreshPromise = null;
26
+ isRefreshing = false;
27
+ circuitBreakerFailures = 0;
28
+ circuitBreakerLastFailure = 0;
29
+ CIRCUIT_BREAKER_THRESHOLD = 5;
30
+ CIRCUIT_BREAKER_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
31
+ REFRESH_THRESHOLD = 0.8; // Refresh when 80% of TTL has passed
32
+ JITTER_FACTOR = 0.25; // ±25% randomness for jitter
33
+ // Default configuration constants
34
+ DEFAULT_TTL_MS = 60 * 60 * 1000; // 1 hour
35
+ DEFAULT_MAX_RETRIES = 3;
36
+ DEFAULT_BASE_RETRY_DELAY_MS = 1000;
37
+ DEFAULT_MAX_RETRY_DELAY_MS = 30000;
38
+ DEFAULT_FETCH_TIMEOUT_MS = 5000; // 5 seconds
39
+ CHECKSUM_LENGTH = 8;
40
+ JSON_INDENT = 2;
41
+ constructor(config = {}) {
42
+ // Configuration with environment variable overrides
43
+ this.TTL_MS = config.ttlMs || this.DEFAULT_TTL_MS;
44
+ this.FETCH_TIMEOUT_MS = this.parseFetchTimeout(config.fetchTimeoutMs);
45
+ this.MAX_RETRIES = config.maxRetries || this.DEFAULT_MAX_RETRIES;
46
+ this.BASE_RETRY_DELAY_MS = config.baseRetryDelayMs || this.DEFAULT_BASE_RETRY_DELAY_MS;
47
+ this.MAX_RETRY_DELAY_MS = config.maxRetryDelayMs || this.DEFAULT_MAX_RETRY_DELAY_MS;
48
+ // Cache directory - use ~/.dollhouse/cache/collection-index.json as specified
49
+ const cacheDir = config.cacheDir || path.join(os.homedir(), '.dollhouse', 'cache');
50
+ this.CACHE_FILE = path.join(cacheDir, 'collection-index.json');
51
+ logger.debug('CollectionIndexManager initialized', {
52
+ ttlMs: this.TTL_MS,
53
+ fetchTimeoutMs: this.FETCH_TIMEOUT_MS,
54
+ cacheFile: this.CACHE_FILE,
55
+ maxRetries: this.MAX_RETRIES
56
+ });
57
+ }
58
+ /**
59
+ * Parse fetch timeout from config or environment variable
60
+ */
61
+ parseFetchTimeout(configValue) {
62
+ // Check environment variable first
63
+ const envTimeout = process.env.COLLECTION_FETCH_TIMEOUT;
64
+ if (envTimeout) {
65
+ const parsed = parseInt(envTimeout, 10);
66
+ if (!isNaN(parsed) && parsed > 0) {
67
+ logger.debug(`Using COLLECTION_FETCH_TIMEOUT from environment: ${parsed}ms`);
68
+ return parsed;
69
+ }
70
+ logger.warn(`Invalid COLLECTION_FETCH_TIMEOUT value: ${envTimeout}, using default`);
71
+ }
72
+ // Fall back to config value or default
73
+ return configValue || this.DEFAULT_FETCH_TIMEOUT_MS;
74
+ }
75
+ /**
76
+ * Get collection index with stale-while-revalidate pattern
77
+ * Returns cached data immediately if available, refreshes in background
78
+ */
79
+ async getIndex() {
80
+ try {
81
+ // Load from memory cache first
82
+ if (!this.cachedIndex) {
83
+ await this.loadFromDisk();
84
+ }
85
+ // Check if we should return stale cache while refreshing
86
+ const shouldRefresh = this.shouldRefreshCache();
87
+ if (this.cachedIndex && !this.isCacheExpired()) {
88
+ // Cache is valid, return immediately
89
+ logger.debug('Returning valid cached collection index');
90
+ // Start background refresh if needed but not already running
91
+ if (shouldRefresh && !this.isRefreshing) {
92
+ this.startBackgroundRefresh();
93
+ }
94
+ return this.cachedIndex.data;
95
+ }
96
+ // If we have stale cache, return it while refreshing in background
97
+ if (this.cachedIndex && this.isCacheExpired()) {
98
+ logger.debug('Returning stale cache while refreshing in background');
99
+ // Start background refresh if not already running
100
+ if (!this.isRefreshing) {
101
+ this.startBackgroundRefresh();
102
+ }
103
+ return this.cachedIndex.data;
104
+ }
105
+ // No cache available, must fetch synchronously
106
+ logger.debug('No cache available, fetching collection index synchronously');
107
+ const freshIndex = await this.fetchWithRetry();
108
+ await this.updateCache(freshIndex);
109
+ return freshIndex.data;
110
+ }
111
+ catch (error) {
112
+ logger.error('Failed to get collection index', { error: this.getErrorMessage(error) });
113
+ // If we have any cached data (even expired), return it as last resort
114
+ if (this.cachedIndex) {
115
+ logger.warn('Returning expired cache as last resort');
116
+ return this.cachedIndex.data;
117
+ }
118
+ throw new Error(`Collection index not available: ${this.getErrorMessage(error)}`);
119
+ }
120
+ }
121
+ /**
122
+ * Force refresh the collection index
123
+ */
124
+ async forceRefresh() {
125
+ logger.debug('Force refreshing collection index');
126
+ try {
127
+ const freshIndex = await this.fetchWithRetry();
128
+ await this.updateCache(freshIndex);
129
+ return freshIndex.data;
130
+ }
131
+ catch (error) {
132
+ logger.error('Force refresh failed', { error: this.getErrorMessage(error) });
133
+ // If we have cached data, return it
134
+ if (this.cachedIndex) {
135
+ logger.warn('Force refresh failed, returning cached data');
136
+ return this.cachedIndex.data;
137
+ }
138
+ throw error;
139
+ }
140
+ }
141
+ /**
142
+ * Check if cache should be refreshed (within TTL but getting close to expiry)
143
+ */
144
+ shouldRefreshCache() {
145
+ if (!this.cachedIndex)
146
+ return true;
147
+ const age = Date.now() - this.cachedIndex.timestamp;
148
+ const refreshThreshold = this.TTL_MS * this.REFRESH_THRESHOLD;
149
+ return age > refreshThreshold;
150
+ }
151
+ /**
152
+ * Check if cache is expired
153
+ */
154
+ isCacheExpired() {
155
+ if (!this.cachedIndex)
156
+ return true;
157
+ const age = Date.now() - this.cachedIndex.timestamp;
158
+ return age > this.TTL_MS;
159
+ }
160
+ /**
161
+ * Start background refresh without blocking
162
+ */
163
+ startBackgroundRefresh() {
164
+ if (this.isRefreshing) {
165
+ logger.debug('Background refresh already in progress');
166
+ return;
167
+ }
168
+ // Check circuit breaker
169
+ if (this.isCircuitBreakerOpen()) {
170
+ logger.debug('Circuit breaker open, skipping background refresh');
171
+ return;
172
+ }
173
+ this.isRefreshing = true;
174
+ this.backgroundRefreshPromise = this.performBackgroundRefresh()
175
+ .catch(error => {
176
+ logger.debug('Background refresh failed', { error: this.getErrorMessage(error) });
177
+ this.recordCircuitBreakerFailure();
178
+ })
179
+ .finally(() => {
180
+ this.isRefreshing = false;
181
+ this.backgroundRefreshPromise = null;
182
+ });
183
+ }
184
+ /**
185
+ * Perform background refresh
186
+ */
187
+ async performBackgroundRefresh() {
188
+ logger.debug('Starting background refresh of collection index');
189
+ try {
190
+ const freshIndex = await this.fetchWithRetry();
191
+ await this.updateCache(freshIndex);
192
+ // Reset circuit breaker on success
193
+ this.circuitBreakerFailures = 0;
194
+ logger.debug('Background refresh completed successfully');
195
+ }
196
+ catch (error) {
197
+ logger.debug('Background refresh failed', { error: this.getErrorMessage(error) });
198
+ throw error;
199
+ }
200
+ }
201
+ /**
202
+ * Fetch collection index with retry logic and exponential backoff
203
+ */
204
+ async fetchWithRetry() {
205
+ let lastError = null;
206
+ for (let attempt = 0; attempt <= this.MAX_RETRIES; attempt++) {
207
+ try {
208
+ if (attempt > 0) {
209
+ const delay = this.calculateRetryDelay(attempt);
210
+ logger.debug(`Retrying fetch in ${delay}ms (attempt ${attempt + 1}/${this.MAX_RETRIES + 1})`);
211
+ await this.sleep(delay);
212
+ }
213
+ return await this.fetchCollectionIndex();
214
+ }
215
+ catch (error) {
216
+ lastError = error instanceof Error ? error : new Error(String(error));
217
+ logger.debug(`Fetch attempt ${attempt + 1} failed`, {
218
+ error: this.getErrorMessage(lastError),
219
+ willRetry: attempt < this.MAX_RETRIES
220
+ });
221
+ }
222
+ }
223
+ throw lastError || new Error('All fetch attempts failed');
224
+ }
225
+ /**
226
+ * Calculate retry delay with exponential backoff and jitter
227
+ */
228
+ calculateRetryDelay(attempt) {
229
+ // Exponential backoff: baseDelay * (2 ^ attempt)
230
+ const exponentialDelay = this.BASE_RETRY_DELAY_MS * Math.pow(2, attempt - 1);
231
+ // Cap at maximum delay
232
+ const cappedDelay = Math.min(exponentialDelay, this.MAX_RETRY_DELAY_MS);
233
+ // Add jitter to prevent thundering herd
234
+ return this.addJitter(cappedDelay);
235
+ }
236
+ /**
237
+ * Add jitter (±25% randomness) to a delay to prevent thundering herd problems
238
+ */
239
+ addJitter(delay) {
240
+ const jitter = delay * this.JITTER_FACTOR * (Math.random() - 0.5);
241
+ return Math.max(0, delay + jitter);
242
+ }
243
+ /**
244
+ * Fetch collection index from GitHub
245
+ */
246
+ async fetchCollectionIndex() {
247
+ const controller = new AbortController();
248
+ const timeoutId = setTimeout(() => controller.abort(), this.FETCH_TIMEOUT_MS);
249
+ try {
250
+ // Build headers for conditional requests
251
+ const headers = {
252
+ 'Accept': 'application/json',
253
+ 'User-Agent': 'DollhouseMCP/1.0',
254
+ 'Cache-Control': 'no-cache'
255
+ };
256
+ // Add conditional headers if we have cached data
257
+ if (this.cachedIndex?.etag) {
258
+ headers['If-None-Match'] = this.cachedIndex.etag;
259
+ }
260
+ if (this.cachedIndex?.lastModified) {
261
+ headers['If-Modified-Since'] = this.cachedIndex.lastModified;
262
+ }
263
+ logger.debug('Fetching collection index', {
264
+ url: this.INDEX_URL,
265
+ timeout: this.FETCH_TIMEOUT_MS,
266
+ hasEtag: !!this.cachedIndex?.etag
267
+ });
268
+ const response = await fetch(this.INDEX_URL, {
269
+ headers,
270
+ signal: controller.signal
271
+ });
272
+ clearTimeout(timeoutId);
273
+ // Handle 304 Not Modified
274
+ if (response.status === 304 && this.cachedIndex) {
275
+ logger.debug('Collection index not modified (304), updating cache timestamp');
276
+ this.cachedIndex.timestamp = Date.now();
277
+ await this.saveToDisk();
278
+ return {
279
+ data: this.cachedIndex.data,
280
+ etag: this.cachedIndex.etag,
281
+ lastModified: this.cachedIndex.lastModified
282
+ };
283
+ }
284
+ if (!response.ok) {
285
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
286
+ }
287
+ const indexData = await response.json();
288
+ // Validate the index structure
289
+ this.validateIndexStructure(indexData);
290
+ const etag = response.headers.get('etag') || undefined;
291
+ const lastModified = response.headers.get('last-modified') || undefined;
292
+ logger.debug('Collection index fetched successfully', {
293
+ totalElements: indexData.total_elements,
294
+ version: indexData.version,
295
+ hasEtag: !!etag
296
+ });
297
+ return { data: indexData, etag, lastModified };
298
+ }
299
+ catch (error) {
300
+ clearTimeout(timeoutId);
301
+ if (error instanceof Error && error.name === 'AbortError') {
302
+ throw new Error(`Fetch timeout after ${this.FETCH_TIMEOUT_MS}ms`);
303
+ }
304
+ throw error;
305
+ }
306
+ }
307
+ /**
308
+ * Validate collection index structure
309
+ */
310
+ validateIndexStructure(index) {
311
+ if (!index || typeof index !== 'object') {
312
+ throw new Error('Invalid index: not an object');
313
+ }
314
+ if (typeof index.version !== 'string') {
315
+ throw new Error('Invalid index: missing or invalid version');
316
+ }
317
+ if (typeof index.generated !== 'string') {
318
+ throw new Error('Invalid index: missing or invalid generated timestamp');
319
+ }
320
+ if (typeof index.total_elements !== 'number') {
321
+ throw new Error('Invalid index: missing or invalid total_elements');
322
+ }
323
+ if (!index.index || typeof index.index !== 'object') {
324
+ throw new Error('Invalid index: missing or invalid index object');
325
+ }
326
+ if (!index.metadata || typeof index.metadata !== 'object') {
327
+ throw new Error('Invalid index: missing or invalid metadata');
328
+ }
329
+ }
330
+ /**
331
+ * Update cache with new data
332
+ */
333
+ async updateCache(fetchResult) {
334
+ const checksum = this.calculateChecksum(fetchResult.data);
335
+ this.cachedIndex = {
336
+ data: fetchResult.data,
337
+ timestamp: Date.now(),
338
+ etag: fetchResult.etag,
339
+ lastModified: fetchResult.lastModified,
340
+ version: fetchResult.data.version,
341
+ checksum
342
+ };
343
+ await this.saveToDisk();
344
+ logger.debug('Collection index cache updated', {
345
+ version: fetchResult.data.version,
346
+ totalElements: fetchResult.data.total_elements,
347
+ checksum
348
+ });
349
+ }
350
+ /**
351
+ * Calculate checksum for data integrity verification
352
+ */
353
+ calculateChecksum(data) {
354
+ // Simple checksum based on key properties
355
+ const checksumData = {
356
+ version: data.version,
357
+ generated: data.generated,
358
+ total_elements: data.total_elements
359
+ };
360
+ return Buffer.from(JSON.stringify(checksumData)).toString('base64').substring(0, this.CHECKSUM_LENGTH);
361
+ }
362
+ /**
363
+ * Load cache from disk
364
+ */
365
+ async loadFromDisk() {
366
+ try {
367
+ const data = await fs.readFile(this.CACHE_FILE, 'utf8');
368
+ const cached = JSON.parse(data);
369
+ // Validate cache structure
370
+ if (!cached.data || !cached.timestamp || !cached.version) {
371
+ logger.debug('Invalid cache structure, ignoring');
372
+ return;
373
+ }
374
+ // Verify checksum if available
375
+ if (cached.checksum) {
376
+ const expectedChecksum = this.calculateChecksum(cached.data);
377
+ if (cached.checksum !== expectedChecksum) {
378
+ logger.debug('Cache checksum mismatch, ignoring cached data');
379
+ return;
380
+ }
381
+ }
382
+ this.cachedIndex = cached;
383
+ const age = Date.now() - cached.timestamp;
384
+ const isExpired = age > this.TTL_MS;
385
+ logger.debug('Loaded collection index from disk cache', {
386
+ version: cached.version,
387
+ age: Math.round(age / 1000),
388
+ isExpired,
389
+ totalElements: cached.data.total_elements
390
+ });
391
+ }
392
+ catch (error) {
393
+ if (error.code !== 'ENOENT') {
394
+ logger.debug('Failed to load cache from disk', { error: this.getErrorMessage(error) });
395
+ }
396
+ }
397
+ }
398
+ /**
399
+ * Save cache to disk
400
+ */
401
+ async saveToDisk() {
402
+ if (!this.cachedIndex)
403
+ return;
404
+ try {
405
+ // Ensure cache directory exists
406
+ await fs.mkdir(path.dirname(this.CACHE_FILE), { recursive: true });
407
+ const cacheData = JSON.stringify(this.cachedIndex, null, this.JSON_INDENT);
408
+ await fs.writeFile(this.CACHE_FILE, cacheData, 'utf8');
409
+ logger.debug('Collection index cache saved to disk');
410
+ }
411
+ catch (error) {
412
+ logger.debug('Failed to save cache to disk', { error: this.getErrorMessage(error) });
413
+ // Don't throw - cache persistence failures shouldn't break functionality
414
+ }
415
+ }
416
+ /**
417
+ * Circuit breaker logic
418
+ */
419
+ isCircuitBreakerOpen() {
420
+ if (this.circuitBreakerFailures < this.CIRCUIT_BREAKER_THRESHOLD) {
421
+ return false;
422
+ }
423
+ const timeSinceLastFailure = Date.now() - this.circuitBreakerLastFailure;
424
+ return timeSinceLastFailure < this.CIRCUIT_BREAKER_TIMEOUT_MS;
425
+ }
426
+ recordCircuitBreakerFailure() {
427
+ this.circuitBreakerFailures++;
428
+ this.circuitBreakerLastFailure = Date.now();
429
+ if (this.circuitBreakerFailures >= this.CIRCUIT_BREAKER_THRESHOLD) {
430
+ logger.warn('Circuit breaker opened due to repeated failures', {
431
+ failures: this.circuitBreakerFailures,
432
+ timeoutMs: this.CIRCUIT_BREAKER_TIMEOUT_MS
433
+ });
434
+ }
435
+ }
436
+ /**
437
+ * Utility methods
438
+ */
439
+ sleep(ms) {
440
+ return new Promise(resolve => setTimeout(resolve, ms));
441
+ }
442
+ getErrorMessage(error) {
443
+ if (error instanceof Error) {
444
+ return error.message;
445
+ }
446
+ return String(error);
447
+ }
448
+ /**
449
+ * Get cache statistics for monitoring
450
+ */
451
+ getCacheStats() {
452
+ if (!this.cachedIndex) {
453
+ return {
454
+ isValid: false,
455
+ age: 0,
456
+ hasCache: false,
457
+ isRefreshing: this.isRefreshing,
458
+ circuitBreakerFailures: this.circuitBreakerFailures,
459
+ circuitBreakerOpen: this.isCircuitBreakerOpen()
460
+ };
461
+ }
462
+ const age = Date.now() - this.cachedIndex.timestamp;
463
+ return {
464
+ isValid: !this.isCacheExpired(),
465
+ age: Math.round(age / 1000), // age in seconds
466
+ hasCache: true,
467
+ version: this.cachedIndex.version,
468
+ totalElements: this.cachedIndex.data.total_elements,
469
+ isRefreshing: this.isRefreshing,
470
+ circuitBreakerFailures: this.circuitBreakerFailures,
471
+ circuitBreakerOpen: this.isCircuitBreakerOpen()
472
+ };
473
+ }
474
+ /**
475
+ * Clear all cache data
476
+ */
477
+ async clearCache() {
478
+ this.cachedIndex = null;
479
+ this.circuitBreakerFailures = 0;
480
+ try {
481
+ await fs.unlink(this.CACHE_FILE);
482
+ logger.debug('Collection index cache file deleted');
483
+ }
484
+ catch (error) {
485
+ if (error.code !== 'ENOENT') {
486
+ logger.debug('Failed to delete cache file', { error: this.getErrorMessage(error) });
487
+ }
488
+ }
489
+ }
490
+ /**
491
+ * Wait for any ongoing background refresh to complete
492
+ */
493
+ async waitForBackgroundRefresh() {
494
+ if (this.backgroundRefreshPromise) {
495
+ await this.backgroundRefreshPromise;
496
+ }
497
+ }
498
+ }
499
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29sbGVjdGlvbkluZGV4TWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb2xsZWN0aW9uL0NvbGxlY3Rpb25JbmRleE1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7R0FVRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBRXpCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQW9CNUMsTUFBTSxPQUFPLHNCQUFzQjtJQUNoQixTQUFTLEdBQUcsNkZBQTZGLENBQUM7SUFDMUcsTUFBTSxDQUFTO0lBQ2YsZ0JBQWdCLENBQVM7SUFDekIsV0FBVyxDQUFTO0lBQ3BCLG1CQUFtQixDQUFTO0lBQzVCLGtCQUFrQixDQUFTO0lBQzNCLFVBQVUsQ0FBUztJQUU1QixXQUFXLEdBQXFDLElBQUksQ0FBQztJQUNyRCx3QkFBd0IsR0FBeUIsSUFBSSxDQUFDO0lBQ3RELFlBQVksR0FBRyxLQUFLLENBQUM7SUFDckIsc0JBQXNCLEdBQUcsQ0FBQyxDQUFDO0lBQzNCLHlCQUF5QixHQUFHLENBQUMsQ0FBQztJQUNyQix5QkFBeUIsR0FBRyxDQUFDLENBQUM7SUFDOUIsMEJBQTBCLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyxZQUFZO0lBQ3hELGlCQUFpQixHQUFHLEdBQUcsQ0FBQyxDQUFDLHFDQUFxQztJQUM5RCxhQUFhLEdBQUcsSUFBSSxDQUFDLENBQUMsNkJBQTZCO0lBRXBFLGtDQUFrQztJQUNqQixjQUFjLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyxTQUFTO0lBQzFDLG1CQUFtQixHQUFHLENBQUMsQ0FBQztJQUN4QiwyQkFBMkIsR0FBRyxJQUFJLENBQUM7SUFD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O0lBQ0gsS0FBSyxDQUFDLHdCQUF3QjtRQUM1QixJQUFJLElBQUksQ0FBQyx3QkFBd0IsRUFBRSxDQUFDO1lBQ2xDLE1BQU0sSUFBSSxDQUFDLHdCQUF3QixDQUFDO1FBQ3RDLENBQUM7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvbGxlY3Rpb24gSW5kZXggTWFuYWdlciB3aXRoIEJhY2tncm91bmQgUmVmcmVzaCBhbmQgUm9idXN0IENhY2hpbmdcbiAqIFxuICogVGhpcyBtYW5hZ2VyIGltcGxlbWVudHM6XG4gKiAtIDEtaG91ciBUVEwgd2l0aCBsb2NhbCBmaWxlIGNhY2hpbmdcbiAqIC0gQmFja2dyb3VuZCByZWZyZXNoIHdpdGhvdXQgYmxvY2tpbmcgb3BlcmF0aW9uc1xuICogLSBFeHBvbmVudGlhbCBiYWNrb2ZmIHJldHJ5IGxvZ2ljXG4gKiAtIENvbmZpZ3VyYWJsZSB0aW1lb3V0cyB2aWEgZW52aXJvbm1lbnQgdmFyaWFibGVzXG4gKiAtIFJldHVybiBzdGFsZSBjYWNoZSB3aGlsZSByZWZyZXNoaW5nIGluIGJhY2tncm91bmRcbiAqIC0gQ29tcHJlaGVuc2l2ZSBlcnJvciBoYW5kbGluZyBmb3IgcHJvZHVjdGlvbiB1c2VcbiAqL1xuXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcy9wcm9taXNlcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0IHsgQ29sbGVjdGlvbkluZGV4IH0gZnJvbSAnLi4vdHlwZXMvY29sbGVjdGlvbic7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIENvbGxlY3Rpb25JbmRleENhY2hlRW50cnkge1xuICBkYXRhOiBDb2xsZWN0aW9uSW5kZXg7XG4gIHRpbWVzdGFtcDogbnVtYmVyO1xuICBldGFnPzogc3RyaW5nO1xuICBsYXN0TW9kaWZpZWQ/OiBzdHJpbmc7XG4gIHZlcnNpb246IHN0cmluZztcbiAgY2hlY2tzdW06IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb2xsZWN0aW9uSW5kZXhNYW5hZ2VyQ29uZmlnIHtcbiAgdHRsTXM/OiBudW1iZXI7XG4gIGZldGNoVGltZW91dE1zPzogbnVtYmVyO1xuICBtYXhSZXRyaWVzPzogbnVtYmVyO1xuICBiYXNlUmV0cnlEZWxheU1zPzogbnVtYmVyO1xuICBtYXhSZXRyeURlbGF5TXM/OiBudW1iZXI7XG4gIGNhY2hlRGlyPzogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgQ29sbGVjdGlvbkluZGV4TWFuYWdlciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgSU5ERVhfVVJMID0gJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9Eb2xsaG91c2VNQ1AvY29sbGVjdGlvbi9tYWluL3B1YmxpYy9jb2xsZWN0aW9uLWluZGV4Lmpzb24nO1xuICBwcml2YXRlIHJlYWRvbmx5IFRUTF9NUzogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IEZFVENIX1RJTUVPVVRfTVM6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBNQVhfUkVUUklFUzogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IEJBU0VfUkVUUllfREVMQVlfTVM6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBNQVhfUkVUUllfREVMQVlfTVM6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBDQUNIRV9GSUxFOiBzdHJpbmc7XG4gIFxuICBwcml2YXRlIGNhY2hlZEluZGV4OiBDb2xsZWN0aW9uSW5kZXhDYWNoZUVudHJ5IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgYmFja2dyb3VuZFJlZnJlc2hQcm9taXNlOiBQcm9taXNlPHZvaWQ+IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgaXNSZWZyZXNoaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgY2lyY3VpdEJyZWFrZXJGYWlsdXJlcyA9IDA7XG4gIHByaXZhdGUgY2lyY3VpdEJyZWFrZXJMYXN0RmFpbHVyZSA9IDA7XG4gIHByaXZhdGUgcmVhZG9ubHkgQ0lSQ1VJVF9CUkVBS0VSX1RIUkVTSE9MRCA9IDU7XG4gIHByaXZhdGUgcmVhZG9ubHkgQ0lSQ1VJVF9CUkVBS0VSX1RJTUVPVVRfTVMgPSA1ICogNjAgKiAxMDAwOyAvLyA1IG1pbnV0ZXNcbiAgcHJpdmF0ZSByZWFkb25seSBSRUZSRVNIX1RIUkVTSE9MRCA9IDAuODsgLy8gUmVmcmVzaCB3aGVuIDgwJSBvZiBUVEwgaGFzIHBhc3NlZFxuICBwcml2YXRlIHJlYWRvbmx5IEpJVFRFUl9GQUNUT1IgPSAwLjI1OyAvLyDCsTI1JSByYW5kb21uZXNzIGZvciBqaXR0ZXJcbiAgXG4gIC8vIERlZmF1bHQgY29uZmlndXJhdGlvbiBjb25zdGFudHNcbiAgcHJpdmF0ZSByZWFkb25seSBERUZBVUxUX1RUTF9NUyA9IDYwICogNjAgKiAxMDAwOyAvLyAxIGhvdXJcbiAgcHJpdmF0ZSByZWFkb25seSBERUZBVUxUX01BWF9SRVRSSUVTID0gMztcbiAgcHJpdmF0ZSByZWFkb25seSBERUZBVUxUX0JBU0VfUkVUUllfREVMQVlfTVMgPSAxMDAwO1xuICBwcml2YXRlIHJlYWRvbmx5IERFRkFVTFRfTUFYX1JFVFJZX0RFTEFZX01TID0gMzAwMDA7XG4gIHByaXZhdGUgcmVhZG9ubHkgREVGQVVMVF9GRVRDSF9USU1FT1VUX01TID0gNTAwMDsgLy8gNSBzZWNvbmRzXG4gIHByaXZhdGUgcmVhZG9ubHkgQ0hFQ0tTVU1fTEVOR1RIID0gODtcbiAgcHJpdmF0ZSByZWFkb25seSBKU09OX0lOREVOVCA9IDI7XG4gIFxuICBjb25zdHJ1Y3Rvcihjb25maWc6IENvbGxlY3Rpb25JbmRleE1hbmFnZXJDb25maWcgPSB7fSkge1xuICAgIC8vIENvbmZpZ3VyYXRpb24gd2l0aCBlbnZpcm9ubWVudCB2YXJpYWJsZSBvdmVycmlkZXNcbiAgICB0aGlzLlRUTF9NUyA9IGNvbmZpZy50dGxNcyB8fCB0aGlzLkRFRkFVTFRfVFRMX01TO1xuICAgIHRoaXMuRkVUQ0hfVElNRU9VVF9NUyA9IHRoaXMucGFyc2VGZXRjaFRpbWVvdXQoY29uZmlnLmZldGNoVGltZW91dE1zKTtcbiAgICB0aGlzLk1BWF9SRVRSSUVTID0gY29uZmlnLm1heFJldHJpZXMgfHwgdGhpcy5ERUZBVUxUX01BWF9SRVRSSUVTO1xuICAgIHRoaXMuQkFTRV9SRVRSWV9ERUxBWV9NUyA9IGNvbmZpZy5iYXNlUmV0cnlEZWxheU1zIHx8IHRoaXMuREVGQVVMVF9CQVNFX1JFVFJZX0RFTEFZX01TO1xuICAgIHRoaXMuTUFYX1JFVFJZX0RFTEFZX01TID0gY29uZmlnLm1heFJldHJ5RGVsYXlNcyB8fCB0aGlzLkRFRkFVTFRfTUFYX1JFVFJZX0RFTEFZX01TO1xuICAgIFxuICAgIC8vIENhY2hlIGRpcmVjdG9yeSAtIHVzZSB+Ly5kb2xsaG91c2UvY2FjaGUvY29sbGVjdGlvbi1pbmRleC5qc29uIGFzIHNwZWNpZmllZFxuICAgIGNvbnN0IGNhY2hlRGlyID0gY29uZmlnLmNhY2hlRGlyIHx8IHBhdGguam9pbihvcy5ob21lZGlyKCksICcuZG9sbGhvdXNlJywgJ2NhY2hlJyk7XG4gICAgdGhpcy5DQUNIRV9GSUxFID0gcGF0aC5qb2luKGNhY2hlRGlyLCAnY29sbGVjdGlvbi1pbmRleC5qc29uJyk7XG4gICAgXG4gICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uSW5kZXhNYW5hZ2VyIGluaXRpYWxpemVkJywge1xuICAgICAgdHRsTXM6IHRoaXMuVFRMX01TLFxuICAgICAgZmV0Y2hUaW1lb3V0TXM6IHRoaXMuRkVUQ0hfVElNRU9VVF9NUyxcbiAgICAgIGNhY2hlRmlsZTogdGhpcy5DQUNIRV9GSUxFLFxuICAgICAgbWF4UmV0cmllczogdGhpcy5NQVhfUkVUUklFU1xuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogUGFyc2UgZmV0Y2ggdGltZW91dCBmcm9tIGNvbmZpZyBvciBlbnZpcm9ubWVudCB2YXJpYWJsZVxuICAgKi9cbiAgcHJpdmF0ZSBwYXJzZUZldGNoVGltZW91dChjb25maWdWYWx1ZT86IG51bWJlcik6IG51bWJlciB7XG4gICAgLy8gQ2hlY2sgZW52aXJvbm1lbnQgdmFyaWFibGUgZmlyc3RcbiAgICBjb25zdCBlbnZUaW1lb3V0ID0gcHJvY2Vzcy5lbnYuQ09MTEVDVElPTl9GRVRDSF9USU1FT1VUO1xuICAgIGlmIChlbnZUaW1lb3V0KSB7XG4gICAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUludChlbnZUaW1lb3V0LCAxMCk7XG4gICAgICBpZiAoIWlzTmFOKHBhcnNlZCkgJiYgcGFyc2VkID4gMCkge1xuICAgICAgICBsb2dnZXIuZGVidWcoYFVzaW5nIENPTExFQ1RJT05fRkVUQ0hfVElNRU9VVCBmcm9tIGVudmlyb25tZW50OiAke3BhcnNlZH1tc2ApO1xuICAgICAgICByZXR1cm4gcGFyc2VkO1xuICAgICAgfVxuICAgICAgbG9nZ2VyLndhcm4oYEludmFsaWQgQ09MTEVDVElPTl9GRVRDSF9USU1FT1VUIHZhbHVlOiAke2VudlRpbWVvdXR9LCB1c2luZyBkZWZhdWx0YCk7XG4gICAgfVxuICAgIFxuICAgIC8vIEZhbGwgYmFjayB0byBjb25maWcgdmFsdWUgb3IgZGVmYXVsdFxuICAgIHJldHVybiBjb25maWdWYWx1ZSB8fCB0aGlzLkRFRkFVTFRfRkVUQ0hfVElNRU9VVF9NUztcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBjb2xsZWN0aW9uIGluZGV4IHdpdGggc3RhbGUtd2hpbGUtcmV2YWxpZGF0ZSBwYXR0ZXJuXG4gICAqIFJldHVybnMgY2FjaGVkIGRhdGEgaW1tZWRpYXRlbHkgaWYgYXZhaWxhYmxlLCByZWZyZXNoZXMgaW4gYmFja2dyb3VuZFxuICAgKi9cbiAgYXN5bmMgZ2V0SW5kZXgoKTogUHJvbWlzZTxDb2xsZWN0aW9uSW5kZXg+IHtcbiAgICB0cnkge1xuICAgICAgLy8gTG9hZCBmcm9tIG1lbW9yeSBjYWNoZSBmaXJzdFxuICAgICAgaWYgKCF0aGlzLmNhY2hlZEluZGV4KSB7XG4gICAgICAgIGF3YWl0IHRoaXMubG9hZEZyb21EaXNrKCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGlmIHdlIHNob3VsZCByZXR1cm4gc3RhbGUgY2FjaGUgd2hpbGUgcmVmcmVzaGluZ1xuICAgICAgY29uc3Qgc2hvdWxkUmVmcmVzaCA9IHRoaXMuc2hvdWxkUmVmcmVzaENhY2hlKCk7XG4gICAgICBcbiAgICAgIGlmICh0aGlzLmNhY2hlZEluZGV4ICYmICF0aGlzLmlzQ2FjaGVFeHBpcmVkKCkpIHtcbiAgICAgICAgLy8gQ2FjaGUgaXMgdmFsaWQsIHJldHVybiBpbW1lZGlhdGVseVxuICAgICAgICBsb2dnZXIuZGVidWcoJ1JldHVybmluZyB2YWxpZCBjYWNoZWQgY29sbGVjdGlvbiBpbmRleCcpO1xuICAgICAgICBcbiAgICAgICAgLy8gU3RhcnQgYmFja2dyb3VuZCByZWZyZXNoIGlmIG5lZWRlZCBidXQgbm90IGFscmVhZHkgcnVubmluZ1xuICAgICAgICBpZiAoc2hvdWxkUmVmcmVzaCAmJiAhdGhpcy5pc1JlZnJlc2hpbmcpIHtcbiAgICAgICAgICB0aGlzLnN0YXJ0QmFja2dyb3VuZFJlZnJlc2goKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHRoaXMuY2FjaGVkSW5kZXguZGF0YTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gSWYgd2UgaGF2ZSBzdGFsZSBjYWNoZSwgcmV0dXJuIGl0IHdoaWxlIHJlZnJlc2hpbmcgaW4gYmFja2dyb3VuZFxuICAgICAgaWYgKHRoaXMuY2FjaGVkSW5kZXggJiYgdGhpcy5pc0NhY2hlRXhwaXJlZCgpKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnUmV0dXJuaW5nIHN0YWxlIGNhY2hlIHdoaWxlIHJlZnJlc2hpbmcgaW4gYmFja2dyb3VuZCcpO1xuICAgICAgICBcbiAgICAgICAgLy8gU3RhcnQgYmFja2dyb3VuZCByZWZyZXNoIGlmIG5vdCBhbHJlYWR5IHJ1bm5pbmdcbiAgICAgICAgaWYgKCF0aGlzLmlzUmVmcmVzaGluZykge1xuICAgICAgICAgIHRoaXMuc3RhcnRCYWNrZ3JvdW5kUmVmcmVzaCgpO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gdGhpcy5jYWNoZWRJbmRleC5kYXRhO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBObyBjYWNoZSBhdmFpbGFibGUsIG11c3QgZmV0Y2ggc3luY2hyb25vdXNseVxuICAgICAgbG9nZ2VyLmRlYnVnKCdObyBjYWNoZSBhdmFpbGFibGUsIGZldGNoaW5nIGNvbGxlY3Rpb24gaW5kZXggc3luY2hyb25vdXNseScpO1xuICAgICAgY29uc3QgZnJlc2hJbmRleCA9IGF3YWl0IHRoaXMuZmV0Y2hXaXRoUmV0cnkoKTtcbiAgICAgIGF3YWl0IHRoaXMudXBkYXRlQ2FjaGUoZnJlc2hJbmRleCk7XG4gICAgICByZXR1cm4gZnJlc2hJbmRleC5kYXRhO1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGdldCBjb2xsZWN0aW9uIGluZGV4JywgeyBlcnJvcjogdGhpcy5nZXRFcnJvck1lc3NhZ2UoZXJyb3IpIH0pO1xuICAgICAgXG4gICAgICAvLyBJZiB3ZSBoYXZlIGFueSBjYWNoZWQgZGF0YSAoZXZlbiBleHBpcmVkKSwgcmV0dXJuIGl0IGFzIGxhc3QgcmVzb3J0XG4gICAgICBpZiAodGhpcy5jYWNoZWRJbmRleCkge1xuICAgICAgICBsb2dnZXIud2FybignUmV0dXJuaW5nIGV4cGlyZWQgY2FjaGUgYXMgbGFzdCByZXNvcnQnKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2FjaGVkSW5kZXguZGF0YTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb2xsZWN0aW9uIGluZGV4IG5vdCBhdmFpbGFibGU6ICR7dGhpcy5nZXRFcnJvck1lc3NhZ2UoZXJyb3IpfWApO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEZvcmNlIHJlZnJlc2ggdGhlIGNvbGxlY3Rpb24gaW5kZXhcbiAgICovXG4gIGFzeW5jIGZvcmNlUmVmcmVzaCgpOiBQcm9taXNlPENvbGxlY3Rpb25JbmRleD4ge1xuICAgIGxvZ2dlci5kZWJ1ZygnRm9yY2UgcmVmcmVzaGluZyBjb2xsZWN0aW9uIGluZGV4Jyk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZyZXNoSW5kZXggPSBhd2FpdCB0aGlzLmZldGNoV2l0aFJldHJ5KCk7XG4gICAgICBhd2FpdCB0aGlzLnVwZGF0ZUNhY2hlKGZyZXNoSW5kZXgpO1xuICAgICAgcmV0dXJuIGZyZXNoSW5kZXguZGF0YTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGb3JjZSByZWZyZXNoIGZhaWxlZCcsIHsgZXJyb3I6IHRoaXMuZ2V0RXJyb3JNZXNzYWdlKGVycm9yKSB9KTtcbiAgICAgIFxuICAgICAgLy8gSWYgd2UgaGF2ZSBjYWNoZWQgZGF0YSwgcmV0dXJuIGl0XG4gICAgICBpZiAodGhpcy5jYWNoZWRJbmRleCkge1xuICAgICAgICBsb2dnZXIud2FybignRm9yY2UgcmVmcmVzaCBmYWlsZWQsIHJldHVybmluZyBjYWNoZWQgZGF0YScpO1xuICAgICAgICByZXR1cm4gdGhpcy5jYWNoZWRJbmRleC5kYXRhO1xuICAgICAgfVxuICAgICAgXG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiBjYWNoZSBzaG91bGQgYmUgcmVmcmVzaGVkICh3aXRoaW4gVFRMIGJ1dCBnZXR0aW5nIGNsb3NlIHRvIGV4cGlyeSlcbiAgICovXG4gIHByaXZhdGUgc2hvdWxkUmVmcmVzaENhY2hlKCk6IGJvb2xlYW4ge1xuICAgIGlmICghdGhpcy5jYWNoZWRJbmRleCkgcmV0dXJuIHRydWU7XG4gICAgXG4gICAgY29uc3QgYWdlID0gRGF0ZS5ub3coKSAtIHRoaXMuY2FjaGVkSW5kZXgudGltZXN0YW1wO1xuICAgIGNvbnN0IHJlZnJlc2hUaHJlc2hvbGQgPSB0aGlzLlRUTF9NUyAqIHRoaXMuUkVGUkVTSF9USFJFU0hPTEQ7XG4gICAgXG4gICAgcmV0dXJuIGFnZSA+IHJlZnJlc2hUaHJlc2hvbGQ7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiBjYWNoZSBpcyBleHBpcmVkXG4gICAqL1xuICBwcml2YXRlIGlzQ2FjaGVFeHBpcmVkKCk6IGJvb2xlYW4ge1xuICAgIGlmICghdGhpcy5jYWNoZWRJbmRleCkgcmV0dXJuIHRydWU7XG4gICAgXG4gICAgY29uc3QgYWdlID0gRGF0ZS5ub3coKSAtIHRoaXMuY2FjaGVkSW5kZXgudGltZXN0YW1wO1xuICAgIHJldHVybiBhZ2UgPiB0aGlzLlRUTF9NUztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFN0YXJ0IGJhY2tncm91bmQgcmVmcmVzaCB3aXRob3V0IGJsb2NraW5nXG4gICAqL1xuICBwcml2YXRlIHN0YXJ0QmFja2dyb3VuZFJlZnJlc2goKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuaXNSZWZyZXNoaW5nKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ0JhY2tncm91bmQgcmVmcmVzaCBhbHJlYWR5IGluIHByb2dyZXNzJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIFxuICAgIC8vIENoZWNrIGNpcmN1aXQgYnJlYWtlclxuICAgIGlmICh0aGlzLmlzQ2lyY3VpdEJyZWFrZXJPcGVuKCkpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnQ2lyY3VpdCBicmVha2VyIG9wZW4sIHNraXBwaW5nIGJhY2tncm91bmQgcmVmcmVzaCcpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBcbiAgICB0aGlzLmlzUmVmcmVzaGluZyA9IHRydWU7XG4gICAgXG4gICAgdGhpcy5iYWNrZ3JvdW5kUmVmcmVzaFByb21pc2UgPSB0aGlzLnBlcmZvcm1CYWNrZ3JvdW5kUmVmcmVzaCgpXG4gICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0JhY2tncm91bmQgcmVmcmVzaCBmYWlsZWQnLCB7IGVycm9yOiB0aGlzLmdldEVycm9yTWVzc2FnZShlcnJvcikgfSk7XG4gICAgICAgIHRoaXMucmVjb3JkQ2lyY3VpdEJyZWFrZXJGYWlsdXJlKCk7XG4gICAgICB9KVxuICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICB0aGlzLmlzUmVmcmVzaGluZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmJhY2tncm91bmRSZWZyZXNoUHJvbWlzZSA9IG51bGw7XG4gICAgICB9KTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFBlcmZvcm0gYmFja2dyb3VuZCByZWZyZXNoXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHBlcmZvcm1CYWNrZ3JvdW5kUmVmcmVzaCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBsb2dnZXIuZGVidWcoJ1N0YXJ0aW5nIGJhY2tncm91bmQgcmVmcmVzaCBvZiBjb2xsZWN0aW9uIGluZGV4Jyk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZyZXNoSW5kZXggPSBhd2FpdCB0aGlzLmZldGNoV2l0aFJldHJ5KCk7XG4gICAgICBhd2FpdCB0aGlzLnVwZGF0ZUNhY2hlKGZyZXNoSW5kZXgpO1xuICAgICAgXG4gICAgICAvLyBSZXNldCBjaXJjdWl0IGJyZWFrZXIgb24gc3VjY2Vzc1xuICAgICAgdGhpcy5jaXJjdWl0QnJlYWtlckZhaWx1cmVzID0gMDtcbiAgICAgIFxuICAgICAgbG9nZ2VyLmRlYnVnKCdCYWNrZ3JvdW5kIHJlZnJlc2ggY29tcGxldGVkIHN1Y2Nlc3NmdWxseScpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ0JhY2tncm91bmQgcmVmcmVzaCBmYWlsZWQnLCB7IGVycm9yOiB0aGlzLmdldEVycm9yTWVzc2FnZShlcnJvcikgfSk7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBGZXRjaCBjb2xsZWN0aW9uIGluZGV4IHdpdGggcmV0cnkgbG9naWMgYW5kIGV4cG9uZW50aWFsIGJhY2tvZmZcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hXaXRoUmV0cnkoKTogUHJvbWlzZTx7IGRhdGE6IENvbGxlY3Rpb25JbmRleDsgZXRhZz86IHN0cmluZzsgbGFzdE1vZGlmaWVkPzogc3RyaW5nIH0+IHtcbiAgICBsZXQgbGFzdEVycm9yOiBFcnJvciB8IG51bGwgPSBudWxsO1xuICAgIFxuICAgIGZvciAobGV0IGF0dGVtcHQgPSAwOyBhdHRlbXB0IDw9IHRoaXMuTUFYX1JFVFJJRVM7IGF0dGVtcHQrKykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKGF0dGVtcHQgPiAwKSB7XG4gICAgICAgICAgY29uc3QgZGVsYXkgPSB0aGlzLmNhbGN1bGF0ZVJldHJ5RGVsYXkoYXR0ZW1wdCk7XG4gICAgICAgICAgbG9nZ2VyLmRlYnVnKGBSZXRyeWluZyBmZXRjaCBpbiAke2RlbGF5fW1zIChhdHRlbXB0ICR7YXR0ZW1wdCArIDF9LyR7dGhpcy5NQVhfUkVUUklFUyArIDF9KWApO1xuICAgICAgICAgIGF3YWl0IHRoaXMuc2xlZXAoZGVsYXkpO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5mZXRjaENvbGxlY3Rpb25JbmRleCgpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbGFzdEVycm9yID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yIDogbmV3IEVycm9yKFN0cmluZyhlcnJvcikpO1xuICAgICAgICBsb2dnZXIuZGVidWcoYEZldGNoIGF0dGVtcHQgJHthdHRlbXB0ICsgMX0gZmFpbGVkYCwgeyBcbiAgICAgICAgICBlcnJvcjogdGhpcy5nZXRFcnJvck1lc3NhZ2UobGFzdEVycm9yKSxcbiAgICAgICAgICB3aWxsUmV0cnk6IGF0dGVtcHQgPCB0aGlzLk1BWF9SRVRSSUVTXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICB0aHJvdyBsYXN0RXJyb3IgfHwgbmV3IEVycm9yKCdBbGwgZmV0Y2ggYXR0ZW1wdHMgZmFpbGVkJyk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgcmV0cnkgZGVsYXkgd2l0aCBleHBvbmVudGlhbCBiYWNrb2ZmIGFuZCBqaXR0ZXJcbiAgICovXG4gIHByaXZhdGUgY2FsY3VsYXRlUmV0cnlEZWxheShhdHRlbXB0OiBudW1iZXIpOiBudW1iZXIge1xuICAgIC8vIEV4cG9uZW50aWFsIGJhY2tvZmY6IGJhc2VEZWxheSAqICgyIF4gYXR0ZW1wdClcbiAgICBjb25zdCBleHBvbmVudGlhbERlbGF5ID0gdGhpcy5CQVNFX1JFVFJZX0RFTEFZX01TICogTWF0aC5wb3coMiwgYXR0ZW1wdCAtIDEpO1xuICAgIFxuICAgIC8vIENhcCBhdCBtYXhpbXVtIGRlbGF5XG4gICAgY29uc3QgY2FwcGVkRGVsYXkgPSBNYXRoLm1pbihleHBvbmVudGlhbERlbGF5LCB0aGlzLk1BWF9SRVRSWV9ERUxBWV9NUyk7XG4gICAgXG4gICAgLy8gQWRkIGppdHRlciB0byBwcmV2ZW50IHRodW5kZXJpbmcgaGVyZFxuICAgIHJldHVybiB0aGlzLmFkZEppdHRlcihjYXBwZWREZWxheSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBBZGQgaml0dGVyICjCsTI1JSByYW5kb21uZXNzKSB0byBhIGRlbGF5IHRvIHByZXZlbnQgdGh1bmRlcmluZyBoZXJkIHByb2JsZW1zXG4gICAqL1xuICBwcml2YXRlIGFkZEppdHRlcihkZWxheTogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBjb25zdCBqaXR0ZXIgPSBkZWxheSAqIHRoaXMuSklUVEVSX0ZBQ1RPUiAqIChNYXRoLnJhbmRvbSgpIC0gMC41KTtcbiAgICByZXR1cm4gTWF0aC5tYXgoMCwgZGVsYXkgKyBqaXR0ZXIpO1xuICB9XG4gIFxuICAvKipcbiAgICogRmV0Y2ggY29sbGVjdGlvbiBpbmRleCBmcm9tIEdpdEh1YlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBmZXRjaENvbGxlY3Rpb25JbmRleCgpOiBQcm9taXNlPHsgZGF0YTogQ29sbGVjdGlvbkluZGV4OyBldGFnPzogc3RyaW5nOyBsYXN0TW9kaWZpZWQ/OiBzdHJpbmcgfT4ge1xuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgY29uc3QgdGltZW91dElkID0gc2V0VGltZW91dCgoKSA9PiBjb250cm9sbGVyLmFib3J0KCksIHRoaXMuRkVUQ0hfVElNRU9VVF9NUyk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIC8vIEJ1aWxkIGhlYWRlcnMgZm9yIGNvbmRpdGlvbmFsIHJlcXVlc3RzXG4gICAgICBjb25zdCBoZWFkZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICAgICAnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICAnVXNlci1BZ2VudCc6ICdEb2xsaG91c2VNQ1AvMS4wJyxcbiAgICAgICAgJ0NhY2hlLUNvbnRyb2wnOiAnbm8tY2FjaGUnXG4gICAgICB9O1xuICAgICAgXG4gICAgICAvLyBBZGQgY29uZGl0aW9uYWwgaGVhZGVycyBpZiB3ZSBoYXZlIGNhY2hlZCBkYXRhXG4gICAgICBpZiAodGhpcy5jYWNoZWRJbmRleD8uZXRhZykge1xuICAgICAgICBoZWFkZXJzWydJZi1Ob25lLU1hdGNoJ10gPSB0aGlzLmNhY2hlZEluZGV4LmV0YWc7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5jYWNoZWRJbmRleD8ubGFzdE1vZGlmaWVkKSB7XG4gICAgICAgIGhlYWRlcnNbJ0lmLU1vZGlmaWVkLVNpbmNlJ10gPSB0aGlzLmNhY2hlZEluZGV4Lmxhc3RNb2RpZmllZDtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgbG9nZ2VyLmRlYnVnKCdGZXRjaGluZyBjb2xsZWN0aW9uIGluZGV4JywgeyBcbiAgICAgICAgdXJsOiB0aGlzLklOREVYX1VSTCxcbiAgICAgICAgdGltZW91dDogdGhpcy5GRVRDSF9USU1FT1VUX01TLFxuICAgICAgICBoYXNFdGFnOiAhIXRoaXMuY2FjaGVkSW5kZXg/LmV0YWdcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMuSU5ERVhfVVJMLCB7XG4gICAgICAgIGhlYWRlcnMsXG4gICAgICAgIHNpZ25hbDogY29udHJvbGxlci5zaWduYWxcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICBjbGVhclRpbWVvdXQodGltZW91dElkKTtcbiAgICAgIFxuICAgICAgLy8gSGFuZGxlIDMwNCBOb3QgTW9kaWZpZWRcbiAgICAgIGlmIChyZXNwb25zZS5zdGF0dXMgPT09IDMwNCAmJiB0aGlzLmNhY2hlZEluZGV4KSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnQ29sbGVjdGlvbiBpbmRleCBub3QgbW9kaWZpZWQgKDMwNCksIHVwZGF0aW5nIGNhY2hlIHRpbWVzdGFtcCcpO1xuICAgICAgICB0aGlzLmNhY2hlZEluZGV4LnRpbWVzdGFtcCA9IERhdGUubm93KCk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2F2ZVRvRGlzaygpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGRhdGE6IHRoaXMuY2FjaGVkSW5kZXguZGF0YSxcbiAgICAgICAgICBldGFnOiB0aGlzLmNhY2hlZEluZGV4LmV0YWcsXG4gICAgICAgICAgbGFzdE1vZGlmaWVkOiB0aGlzLmNhY2hlZEluZGV4Lmxhc3RNb2RpZmllZFxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSFRUUCAke3Jlc3BvbnNlLnN0YXR1c306ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgY29uc3QgaW5kZXhEYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpIGFzIENvbGxlY3Rpb25JbmRleDtcbiAgICAgIFxuICAgICAgLy8gVmFsaWRhdGUgdGhlIGluZGV4IHN0cnVjdHVyZVxuICAgICAgdGhpcy52YWxpZGF0ZUluZGV4U3RydWN0dXJlKGluZGV4RGF0YSk7XG4gICAgICBcbiAgICAgIGNvbnN0IGV0YWcgPSByZXNwb25zZS5oZWFkZXJzLmdldCgnZXRhZycpIHx8IHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IGxhc3RNb2RpZmllZCA9IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdsYXN0LW1vZGlmaWVkJykgfHwgdW5kZWZpbmVkO1xuICAgICAgXG4gICAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gaW5kZXggZmV0Y2hlZCBzdWNjZXNzZnVsbHknLCB7XG4gICAgICAgIHRvdGFsRWxlbWVudHM6IGluZGV4RGF0YS50b3RhbF9lbGVtZW50cyxcbiAgICAgICAgdmVyc2lvbjogaW5kZXhEYXRhLnZlcnNpb24sXG4gICAgICAgIGhhc0V0YWc6ICEhZXRhZ1xuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHJldHVybiB7IGRhdGE6IGluZGV4RGF0YSwgZXRhZywgbGFzdE1vZGlmaWVkIH07XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gICAgICBcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yICYmIGVycm9yLm5hbWUgPT09ICdBYm9ydEVycm9yJykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZldGNoIHRpbWVvdXQgYWZ0ZXIgJHt0aGlzLkZFVENIX1RJTUVPVVRfTVN9bXNgKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogVmFsaWRhdGUgY29sbGVjdGlvbiBpbmRleCBzdHJ1Y3R1cmVcbiAgICovXG4gIHByaXZhdGUgdmFsaWRhdGVJbmRleFN0cnVjdHVyZShpbmRleDogYW55KTogYXNzZXJ0cyBpbmRleCBpcyBDb2xsZWN0aW9uSW5kZXgge1xuICAgIGlmICghaW5kZXggfHwgdHlwZW9mIGluZGV4ICE9PSAnb2JqZWN0Jykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGluZGV4OiBub3QgYW4gb2JqZWN0Jyk7XG4gICAgfVxuICAgIFxuICAgIGlmICh0eXBlb2YgaW5kZXgudmVyc2lvbiAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBpbmRleDogbWlzc2luZyBvciBpbnZhbGlkIHZlcnNpb24nKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKHR5cGVvZiBpbmRleC5nZW5lcmF0ZWQgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgaW5kZXg6IG1pc3Npbmcgb3IgaW52YWxpZCBnZW5lcmF0ZWQgdGltZXN0YW1wJyk7XG4gICAgfVxuICAgIFxuICAgIGlmICh0eXBlb2YgaW5kZXgudG90YWxfZWxlbWVudHMgIT09ICdudW1iZXInKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgaW5kZXg6IG1pc3Npbmcgb3IgaW52YWxpZCB0b3RhbF9lbGVtZW50cycpO1xuICAgIH1cbiAgICBcbiAgICBpZiAoIWluZGV4LmluZGV4IHx8IHR5cGVvZiBpbmRleC5pbmRleCAhPT0gJ29iamVjdCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBpbmRleDogbWlzc2luZyBvciBpbnZhbGlkIGluZGV4IG9iamVjdCcpO1xuICAgIH1cbiAgICBcbiAgICBpZiAoIWluZGV4Lm1ldGFkYXRhIHx8IHR5cGVvZiBpbmRleC5tZXRhZGF0YSAhPT0gJ29iamVjdCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBpbmRleDogbWlzc2luZyBvciBpbnZhbGlkIG1ldGFkYXRhJyk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogVXBkYXRlIGNhY2hlIHdpdGggbmV3IGRhdGFcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdXBkYXRlQ2FjaGUoZmV0Y2hSZXN1bHQ6IHsgZGF0YTogQ29sbGVjdGlvbkluZGV4OyBldGFnPzogc3RyaW5nOyBsYXN0TW9kaWZpZWQ/OiBzdHJpbmcgfSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNoZWNrc3VtID0gdGhpcy5jYWxjdWxhdGVDaGVja3N1bShmZXRjaFJlc3VsdC5kYXRhKTtcbiAgICBcbiAgICB0aGlzLmNhY2hlZEluZGV4ID0ge1xuICAgICAgZGF0YTogZmV0Y2hSZXN1bHQuZGF0YSxcbiAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgIGV0YWc6IGZldGNoUmVzdWx0LmV0YWcsXG4gICAgICBsYXN0TW9kaWZpZWQ6IGZldGNoUmVzdWx0Lmxhc3RNb2RpZmllZCxcbiAgICAgIHZlcnNpb246IGZldGNoUmVzdWx0LmRhdGEudmVyc2lvbixcbiAgICAgIGNoZWNrc3VtXG4gICAgfTtcbiAgICBcbiAgICBhd2FpdCB0aGlzLnNhdmVUb0Rpc2soKTtcbiAgICBcbiAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gaW5kZXggY2FjaGUgdXBkYXRlZCcsIHtcbiAgICAgIHZlcnNpb246IGZldGNoUmVzdWx0LmRhdGEudmVyc2lvbixcbiAgICAgIHRvdGFsRWxlbWVudHM6IGZldGNoUmVzdWx0LmRhdGEudG90YWxfZWxlbWVudHMsXG4gICAgICBjaGVja3N1bVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2FsY3VsYXRlIGNoZWNrc3VtIGZvciBkYXRhIGludGVncml0eSB2ZXJpZmljYXRpb25cbiAgICovXG4gIHByaXZhdGUgY2FsY3VsYXRlQ2hlY2tzdW0oZGF0YTogQ29sbGVjdGlvbkluZGV4KTogc3RyaW5nIHtcbiAgICAvLyBTaW1wbGUgY2hlY2tzdW0gYmFzZWQgb24ga2V5IHByb3BlcnRpZXNcbiAgICBjb25zdCBjaGVja3N1bURhdGEgPSB7XG4gICAgICB2ZXJzaW9uOiBkYXRhLnZlcnNpb24sXG4gICAgICBnZW5lcmF0ZWQ6IGRhdGEuZ2VuZXJhdGVkLFxuICAgICAgdG90YWxfZWxlbWVudHM6IGRhdGEudG90YWxfZWxlbWVudHNcbiAgICB9O1xuICAgIHJldHVybiBCdWZmZXIuZnJvbShKU09OLnN0cmluZ2lmeShjaGVja3N1bURhdGEpKS50b1N0cmluZygnYmFzZTY0Jykuc3Vic3RyaW5nKDAsIHRoaXMuQ0hFQ0tTVU1fTEVOR1RIKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIExvYWQgY2FjaGUgZnJvbSBkaXNrXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGxvYWRGcm9tRGlzaygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IGZzLnJlYWRGaWxlKHRoaXMuQ0FDSEVfRklMRSwgJ3V0ZjgnKTtcbiAgICAgIGNvbnN0IGNhY2hlZCA9IEpTT04ucGFyc2UoZGF0YSkgYXMgQ29sbGVjdGlvbkluZGV4Q2FjaGVFbnRyeTtcbiAgICAgIFxuICAgICAgLy8gVmFsaWRhdGUgY2FjaGUgc3RydWN0dXJlXG4gICAgICBpZiAoIWNhY2hlZC5kYXRhIHx8ICFjYWNoZWQudGltZXN0YW1wIHx8ICFjYWNoZWQudmVyc2lvbikge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ludmFsaWQgY2FjaGUgc3RydWN0dXJlLCBpZ25vcmluZycpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFZlcmlmeSBjaGVja3N1bSBpZiBhdmFpbGFibGVcbiAgICAgIGlmIChjYWNoZWQuY2hlY2tzdW0pIHtcbiAgICAgICAgY29uc3QgZXhwZWN0ZWRDaGVja3N1bSA9IHRoaXMuY2FsY3VsYXRlQ2hlY2tzdW0oY2FjaGVkLmRhdGEpO1xuICAgICAgICBpZiAoY2FjaGVkLmNoZWNrc3VtICE9PSBleHBlY3RlZENoZWNrc3VtKSB7XG4gICAgICAgICAgbG9nZ2VyLmRlYnVnKCdDYWNoZSBjaGVja3N1bSBtaXNtYXRjaCwgaWdub3JpbmcgY2FjaGVkIGRhdGEnKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgdGhpcy5jYWNoZWRJbmRleCA9IGNhY2hlZDtcbiAgICAgIFxuICAgICAgY29uc3QgYWdlID0gRGF0ZS5ub3coKSAtIGNhY2hlZC50aW1lc3RhbXA7XG4gICAgICBjb25zdCBpc0V4cGlyZWQgPSBhZ2UgPiB0aGlzLlRUTF9NUztcbiAgICAgIFxuICAgICAgbG9nZ2VyLmRlYnVnKCdMb2FkZWQgY29sbGVjdGlvbiBpbmRleCBmcm9tIGRpc2sgY2FjaGUnLCB7XG4gICAgICAgIHZlcnNpb246IGNhY2hlZC52ZXJzaW9uLFxuICAgICAgICBhZ2U6IE1hdGgucm91bmQoYWdlIC8gMTAwMCksXG4gICAgICAgIGlzRXhwaXJlZCxcbiAgICAgICAgdG90YWxFbGVtZW50czogY2FjaGVkLmRhdGEudG90YWxfZWxlbWVudHNcbiAgICAgIH0pO1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlICE9PSAnRU5PRU5UJykge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ZhaWxlZCB0byBsb2FkIGNhY2hlIGZyb20gZGlzaycsIHsgZXJyb3I6IHRoaXMuZ2V0RXJyb3JNZXNzYWdlKGVycm9yKSB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBTYXZlIGNhY2hlIHRvIGRpc2tcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2F2ZVRvRGlzaygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuY2FjaGVkSW5kZXgpIHJldHVybjtcbiAgICBcbiAgICB0cnkge1xuICAgICAgLy8gRW5zdXJlIGNhY2hlIGRpcmVjdG9yeSBleGlzdHNcbiAgICAgIGF3YWl0IGZzLm1rZGlyKHBhdGguZGlybmFtZSh0aGlzLkNBQ0hFX0ZJTEUpLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgIFxuICAgICAgY29uc3QgY2FjaGVEYXRhID0gSlNPTi5zdHJpbmdpZnkodGhpcy5jYWNoZWRJbmRleCwgbnVsbCwgdGhpcy5KU09OX0lOREVOVCk7XG4gICAgICBhd2FpdCBmcy53cml0ZUZpbGUodGhpcy5DQUNIRV9GSUxFLCBjYWNoZURhdGEsICd1dGY4Jyk7XG4gICAgICBcbiAgICAgIGxvZ2dlci5kZWJ1ZygnQ29sbGVjdGlvbiBpbmRleCBjYWNoZSBzYXZlZCB0byBkaXNrJyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnRmFpbGVkIHRvIHNhdmUgY2FjaGUgdG8gZGlzaycsIHsgZXJyb3I6IHRoaXMuZ2V0RXJyb3JNZXNzYWdlKGVycm9yKSB9KTtcbiAgICAgIC8vIERvbid0IHRocm93IC0gY2FjaGUgcGVyc2lzdGVuY2UgZmFpbHVyZXMgc2hvdWxkbid0IGJyZWFrIGZ1bmN0aW9uYWxpdHlcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaXJjdWl0IGJyZWFrZXIgbG9naWNcbiAgICovXG4gIHByaXZhdGUgaXNDaXJjdWl0QnJlYWtlck9wZW4oKTogYm9vbGVhbiB7XG4gICAgaWYgKHRoaXMuY2lyY3VpdEJyZWFrZXJGYWlsdXJlcyA8IHRoaXMuQ0lSQ1VJVF9CUkVBS0VSX1RIUkVTSE9MRCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBcbiAgICBjb25zdCB0aW1lU2luY2VMYXN0RmFpbHVyZSA9IERhdGUubm93KCkgLSB0aGlzLmNpcmN1aXRCcmVha2VyTGFzdEZhaWx1cmU7XG4gICAgcmV0dXJuIHRpbWVTaW5jZUxhc3RGYWlsdXJlIDwgdGhpcy5DSVJDVUlUX0JSRUFLRVJfVElNRU9VVF9NUztcbiAgfVxuICBcbiAgcHJpdmF0ZSByZWNvcmRDaXJjdWl0QnJlYWtlckZhaWx1cmUoKTogdm9pZCB7XG4gICAgdGhpcy5jaXJjdWl0QnJlYWtlckZhaWx1cmVzKys7XG4gICAgdGhpcy5jaXJjdWl0QnJlYWtlckxhc3RGYWlsdXJlID0gRGF0ZS5ub3coKTtcbiAgICBcbiAgICBpZiAodGhpcy5jaXJjdWl0QnJlYWtlckZhaWx1cmVzID49IHRoaXMuQ0lSQ1VJVF9CUkVBS0VSX1RIUkVTSE9MRCkge1xuICAgICAgbG9nZ2VyLndhcm4oJ0NpcmN1aXQgYnJlYWtlciBvcGVuZWQgZHVlIHRvIHJlcGVhdGVkIGZhaWx1cmVzJywge1xuICAgICAgICBmYWlsdXJlczogdGhpcy5jaXJjdWl0QnJlYWtlckZhaWx1cmVzLFxuICAgICAgICB0aW1lb3V0TXM6IHRoaXMuQ0lSQ1VJVF9CUkVBS0VSX1RJTUVPVVRfTVNcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFV0aWxpdHkgbWV0aG9kc1xuICAgKi9cbiAgcHJpdmF0ZSBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCBtcykpO1xuICB9XG4gIFxuICBwcml2YXRlIGdldEVycm9yTWVzc2FnZShlcnJvcjogdW5rbm93bik6IHN0cmluZyB7XG4gICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgIHJldHVybiBlcnJvci5tZXNzYWdlO1xuICAgIH1cbiAgICByZXR1cm4gU3RyaW5nKGVycm9yKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBjYWNoZSBzdGF0aXN0aWNzIGZvciBtb25pdG9yaW5nXG4gICAqL1xuICBnZXRDYWNoZVN0YXRzKCk6IHtcbiAgICBpc1ZhbGlkOiBib29sZWFuO1xuICAgIGFnZTogbnVtYmVyO1xuICAgIGhhc0NhY2hlOiBib29sZWFuO1xuICAgIHZlcnNpb24/OiBzdHJpbmc7XG4gICAgdG90YWxFbGVtZW50cz86IG51bWJlcjtcbiAgICBpc1JlZnJlc2hpbmc6IGJvb2xlYW47XG4gICAgY2lyY3VpdEJyZWFrZXJGYWlsdXJlczogbnVtYmVyO1xuICAgIGNpcmN1aXRCcmVha2VyT3BlbjogYm9vbGVhbjtcbiAgfSB7XG4gICAgaWYgKCF0aGlzLmNhY2hlZEluZGV4KSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBpc1ZhbGlkOiBmYWxzZSxcbiAgICAgICAgYWdlOiAwLFxuICAgICAgICBoYXNDYWNoZTogZmFsc2UsXG4gICAgICAgIGlzUmVmcmVzaGluZzogdGhpcy5pc1JlZnJlc2hpbmcsXG4gICAgICAgIGNpcmN1aXRCcmVha2VyRmFpbHVyZXM6IHRoaXMuY2lyY3VpdEJyZWFrZXJGYWlsdXJlcyxcbiAgICAgICAgY2lyY3VpdEJyZWFrZXJPcGVuOiB0aGlzLmlzQ2lyY3VpdEJyZWFrZXJPcGVuKClcbiAgICAgIH07XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IGFnZSA9IERhdGUubm93KCkgLSB0aGlzLmNhY2hlZEluZGV4LnRpbWVzdGFtcDtcbiAgICBcbiAgICByZXR1cm4ge1xuICAgICAgaXNWYWxpZDogIXRoaXMuaXNDYWNoZUV4cGlyZWQoKSxcbiAgICAgIGFnZTogTWF0aC5yb3VuZChhZ2UgLyAxMDAwKSwgLy8gYWdlIGluIHNlY29uZHNcbiAgICAgIGhhc0NhY2hlOiB0cnVlLFxuICAgICAgdmVyc2lvbjogdGhpcy5jYWNoZWRJbmRleC52ZXJzaW9uLFxuICAgICAgdG90YWxFbGVtZW50czogdGhpcy5jYWNoZWRJbmRleC5kYXRhLnRvdGFsX2VsZW1lbnRzLFxuICAgICAgaXNSZWZyZXNoaW5nOiB0aGlzLmlzUmVmcmVzaGluZyxcbiAgICAgIGNpcmN1aXRCcmVha2VyRmFpbHVyZXM6IHRoaXMuY2lyY3VpdEJyZWFrZXJGYWlsdXJlcyxcbiAgICAgIGNpcmN1aXRCcmVha2VyT3BlbjogdGhpcy5pc0NpcmN1aXRCcmVha2VyT3BlbigpXG4gICAgfTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENsZWFyIGFsbCBjYWNoZSBkYXRhXG4gICAqL1xuICBhc3luYyBjbGVhckNhY2hlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuY2FjaGVkSW5kZXggPSBudWxsO1xuICAgIHRoaXMuY2lyY3VpdEJyZWFrZXJGYWlsdXJlcyA9IDA7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLkNBQ0hFX0ZJTEUpO1xuICAgICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIGluZGV4IGNhY2hlIGZpbGUgZGVsZXRlZCcpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSAhPT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdGYWlsZWQgdG8gZGVsZXRlIGNhY2hlIGZpbGUnLCB7IGVycm9yOiB0aGlzLmdldEVycm9yTWVzc2FnZShlcnJvcikgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogV2FpdCBmb3IgYW55IG9uZ29pbmcgYmFja2dyb3VuZCByZWZyZXNoIHRvIGNvbXBsZXRlXG4gICAqL1xuICBhc3luYyB3YWl0Rm9yQmFja2dyb3VuZFJlZnJlc2goKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuYmFja2dyb3VuZFJlZnJlc2hQcm9taXNlKSB7XG4gICAgICBhd2FpdCB0aGlzLmJhY2tncm91bmRSZWZyZXNoUHJvbWlzZTtcbiAgICB9XG4gIH1cbn0iXX0=
@@ -3,11 +3,18 @@
3
3
  */
4
4
  import { GitHubClient } from './GitHubClient.js';
5
5
  import { CollectionCache } from '../cache/CollectionCache.js';
6
+ import { SearchResults, SearchOptions } from '../types/collection.js';
6
7
  export declare class CollectionSearch {
7
8
  private githubClient;
8
9
  private collectionCache;
10
+ private indexCache;
9
11
  private searchBaseUrl;
10
12
  constructor(githubClient: GitHubClient, collectionCache?: CollectionCache);
13
+ /**
14
+ * Enhanced search using collection index with pagination and filtering
15
+ * Falls back to API search and cache when index is unavailable
16
+ */
17
+ searchCollectionWithOptions(query: string, options?: SearchOptions): Promise<SearchResults>;
11
18
  /**
12
19
  * Search collection for content matching query
13
20
  * Falls back to cached data when GitHub API is not available or not authenticated
@@ -21,6 +28,10 @@ export declare class CollectionSearch {
21
28
  * Search seed data for matching items with fuzzy matching
22
29
  */
23
30
  private searchSeedData;
31
+ /**
32
+ * Fuzzy matching algorithm for partial string matches
33
+ */
34
+ private fuzzyMatch;
24
35
  /**
25
36
  * Convert cache items to GitHub API format for consistent response structure
26
37
  */
@@ -33,5 +44,49 @@ export declare class CollectionSearch {
33
44
  * Format search results
34
45
  */
35
46
  formatSearchResults(items: any[], query: string, personaIndicator?: string): string;
47
+ /**
48
+ * Search from collection index with full featured search and pagination
49
+ */
50
+ private searchFromIndex;
51
+ /**
52
+ * Flatten index entries from all categories into a single array
53
+ */
54
+ private flattenIndexEntries;
55
+ /**
56
+ * Perform search matching on index entries
57
+ */
58
+ private performIndexSearch;
59
+ /**
60
+ * Sort search results by relevance, name, or date
61
+ */
62
+ private sortSearchResults;
63
+ /**
64
+ * Calculate relevance score for search results
65
+ */
66
+ private calculateRelevanceScore;
67
+ /**
68
+ * Convert legacy search results to new SearchResults format
69
+ */
70
+ private convertLegacyResults;
71
+ /**
72
+ * Extract element type from file path
73
+ */
74
+ private extractTypeFromPath;
75
+ /**
76
+ * Extract category from file path
77
+ */
78
+ private extractCategoryFromPath;
79
+ /**
80
+ * Create empty search results for error cases
81
+ */
82
+ private createEmptySearchResults;
83
+ /**
84
+ * Enhanced format for search results with pagination info
85
+ */
86
+ formatSearchResultsWithPagination(results: SearchResults, personaIndicator?: string): string;
87
+ /**
88
+ * Get cache statistics for debugging
89
+ */
90
+ getCacheStats(): Promise<any>;
36
91
  }
37
92
  //# sourceMappingURL=CollectionSearch.d.ts.map