@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,1523 @@
1
+ /**
2
+ * Tool for submitting content to GitHub portfolio repositories
3
+ * Replaces the broken issue-based submission with direct repository saves
4
+ *
5
+ * FIXES IMPLEMENTED (PR #503):
6
+ * 1. TYPE SAFETY FIX #1 (Issue #497): Changed apiCache from 'any' to proper APICache type
7
+ * 2. TYPE SAFETY FIX #2 (Issue #497): Replaced complex type casting with PortfolioElementAdapter
8
+ * 3. PERFORMANCE (PR #496 recommendation): Using FileDiscoveryUtil for optimized file search
9
+ */
10
+ import { GitHubAuthManager } from '../../auth/GitHubAuthManager.js';
11
+ import { PortfolioRepoManager } from '../../portfolio/PortfolioRepoManager.js';
12
+ import { TokenManager } from '../../security/tokenManager.js';
13
+ import { ContentValidator } from '../../security/contentValidator.js';
14
+ import { PortfolioManager } from '../../portfolio/PortfolioManager.js';
15
+ import { PortfolioIndexManager } from '../../portfolio/PortfolioIndexManager.js';
16
+ import { ElementType } from '../../portfolio/types.js';
17
+ import { logger } from '../../utils/logger.js';
18
+ import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
19
+ import { SecurityMonitor } from '../../security/securityMonitor.js';
20
+ import { PortfolioElementAdapter } from './PortfolioElementAdapter.js';
21
+ import { FileDiscoveryUtil } from '../../utils/FileDiscoveryUtil.js';
22
+ import { ErrorHandler } from '../../utils/ErrorHandler.js';
23
+ import { FILE_SIZE_LIMITS, RETRY_CONFIG, SEARCH_CONFIG, getValidatedTimeout, calculateRetryDelay } from '../../config/portfolio-constants.js';
24
+ import { githubRateLimiter } from '../../utils/GitHubRateLimiter.js';
25
+ import { EarlyTerminationSearch } from '../../utils/EarlyTerminationSearch.js';
26
+ import * as path from 'path';
27
+ import * as fs from 'fs/promises';
28
+ export class SubmitToPortfolioTool {
29
+ authManager;
30
+ portfolioManager;
31
+ contentValidator;
32
+ constructor(apiCache) {
33
+ // TYPE SAFETY FIX #1: Proper typing for apiCache parameter
34
+ // Previously: constructor(apiCache: any)
35
+ // Now: constructor(apiCache: APICache) with proper import
36
+ this.authManager = new GitHubAuthManager(apiCache);
37
+ this.portfolioManager = new PortfolioRepoManager();
38
+ this.contentValidator = new ContentValidator();
39
+ }
40
+ /**
41
+ * Validates and normalizes input parameters to prevent Unicode attacks and ensure data safety
42
+ * @param params The input parameters from the user
43
+ * @returns Validation result with normalized name or error response
44
+ */
45
+ async validateAndNormalizeParams(params) {
46
+ // Normalize user input to prevent Unicode attacks (DMCP-SEC-004)
47
+ const normalizedName = UnicodeValidator.normalize(params.name);
48
+ if (!normalizedName.isValid) {
49
+ SecurityMonitor.logSecurityEvent({
50
+ type: 'UNICODE_VALIDATION_ERROR',
51
+ severity: 'MEDIUM',
52
+ source: 'SubmitToPortfolioTool.execute',
53
+ details: `Invalid Unicode in element name: ${normalizedName.detectedIssues?.[0] || 'unknown error'}`
54
+ });
55
+ return {
56
+ success: false,
57
+ error: {
58
+ success: false,
59
+ message: `Invalid characters in element name: ${normalizedName.detectedIssues?.[0] || 'unknown error'}`,
60
+ error: 'INVALID_INPUT'
61
+ }
62
+ };
63
+ }
64
+ return {
65
+ success: true,
66
+ safeName: normalizedName.normalizedContent
67
+ };
68
+ }
69
+ /**
70
+ * Checks if the user is authenticated with GitHub
71
+ * @returns Authentication check result with status or error response
72
+ */
73
+ async checkAuthentication() {
74
+ const authStatus = await this.authManager.getAuthStatus();
75
+ if (!authStatus.isAuthenticated) {
76
+ // Log authentication required (using existing event type)
77
+ logger.warn('User attempted portfolio submission without authentication');
78
+ return {
79
+ success: false,
80
+ error: {
81
+ success: false,
82
+ message: 'Not authenticated. Please authenticate first using the GitHub OAuth flow.\n\n' +
83
+ 'Visit: https://docs.anthropic.com/en/docs/claude-code/oauth-setup\n' +
84
+ 'Or run: gh auth login --web',
85
+ error: 'NOT_AUTHENTICATED'
86
+ }
87
+ };
88
+ }
89
+ return {
90
+ success: true,
91
+ authStatus
92
+ };
93
+ }
94
+ /**
95
+ * Discovers content locally with smart type detection
96
+ * @param safeName The normalized name to search for
97
+ * @param explicitType Optional explicit element type provided by user
98
+ * @param originalName Original user-provided name for error messages
99
+ * @returns Content discovery result with element type and path or error response
100
+ */
101
+ async discoverContentWithTypeDetection(safeName, explicitType, originalName) {
102
+ let elementType = explicitType;
103
+ let localPath = null;
104
+ if (elementType) {
105
+ // Type explicitly provided - search in that specific directory only
106
+ localPath = await this.findLocalContent(safeName, elementType);
107
+ if (!localPath) {
108
+ // UX IMPROVEMENT: Provide helpful suggestions for finding content
109
+ const portfolioManager = PortfolioManager.getInstance();
110
+ const elementDir = portfolioManager.getElementDir(elementType);
111
+ return {
112
+ success: false,
113
+ error: {
114
+ success: false,
115
+ message: `Could not find ${elementType} named "${originalName || safeName}" in local portfolio.\n\n` +
116
+ `**Searched in**: ${elementDir}\n\n` +
117
+ `**Troubleshooting Tips**:\n` +
118
+ `• Check if the file exists using your file explorer\n` +
119
+ `• Try using the exact filename (without extension)\n` +
120
+ `• Use \`list_portfolio\` to see all available ${elementType}\n` +
121
+ `• If unsure of the type, omit --type and let the system detect it\n\n` +
122
+ `**Common name formats that work**:\n` +
123
+ `• "my-element" (kebab-case)\n` +
124
+ `• "My Element" (with spaces)\n` +
125
+ `• "MyElement" (PascalCase)\n` +
126
+ `• Partial matches are supported`,
127
+ error: 'CONTENT_NOT_FOUND'
128
+ }
129
+ };
130
+ }
131
+ }
132
+ else {
133
+ // CRITICAL FIX: No type provided - implement smart detection across ALL element types
134
+ // This prevents the previous hardcoded default to PERSONA and enables proper type detection
135
+ const detectionResult = await this.detectElementType(safeName);
136
+ if (!detectionResult.found) {
137
+ // UX IMPROVEMENT: Enhanced guidance with specific suggestions
138
+ const availableTypes = Object.values(ElementType).join(', ');
139
+ // Get suggestions for similar names
140
+ const suggestions = await this.generateNameSuggestions(safeName);
141
+ let message = `Content "${originalName || safeName}" not found in portfolio.\n\n`;
142
+ message += `🔍 **Searched in all element types**: ${availableTypes}\n\n`;
143
+ if (suggestions.length > 0) {
144
+ message += `💡 **Did you mean one of these?**\n`;
145
+ for (const suggestion of suggestions.slice(0, SEARCH_CONFIG.MAX_SUGGESTIONS)) {
146
+ message += ` • "${suggestion.name}" (${suggestion.type})\n`;
147
+ }
148
+ message += `\n`;
149
+ }
150
+ message += `🛠️ **Troubleshooting Steps**:\n`;
151
+ message += `1. 📝 Use \`list_portfolio\` to see all available content\n`;
152
+ message += `2. 🔍 Check exact spelling and try variations:\n`;
153
+ message += ` • "${(originalName || safeName).toLowerCase()}" (lowercase)\n`;
154
+ message += ` • "${(originalName || safeName).replace(/[^a-z0-9]/gi, '-').toLowerCase()}" (normalized)\n`;
155
+ if ((originalName || safeName).includes('.')) {
156
+ message += ` • "${(originalName || safeName).replace(/\./g, '')}" (no dots)\n`;
157
+ }
158
+ message += `3. 🎯 Specify element type: \`submit_content "${originalName || safeName}" --type=personas\`\n`;
159
+ message += `4. 📁 Check if file exists in portfolio directories\n\n`;
160
+ message += `📝 **Tip**: The system searches filenames AND metadata names with fuzzy matching.`;
161
+ return {
162
+ success: false,
163
+ error: {
164
+ success: false,
165
+ message,
166
+ error: 'CONTENT_NOT_FOUND'
167
+ }
168
+ };
169
+ }
170
+ if (detectionResult.matches.length > 1) {
171
+ // Multiple matches found - ask user to specify type
172
+ const matchDetails = detectionResult.matches.map(m => `- ${m.type}: ${m.path}`).join('\n');
173
+ return {
174
+ success: false,
175
+ error: {
176
+ success: false,
177
+ message: `Content "${originalName || safeName}" found in multiple element types:\n\n${matchDetails}\n\n` +
178
+ `Please specify the element type using the --type parameter to avoid ambiguity.`,
179
+ error: 'MULTIPLE_MATCHES_FOUND'
180
+ }
181
+ };
182
+ }
183
+ // Single match found - use it
184
+ const match = detectionResult.matches[0];
185
+ elementType = match.type;
186
+ localPath = match.path;
187
+ logger.info(`Smart detection: Found "${safeName}" as ${elementType}`, {
188
+ name: safeName,
189
+ detectedType: elementType,
190
+ path: localPath
191
+ });
192
+ }
193
+ return {
194
+ success: true,
195
+ elementType,
196
+ localPath
197
+ };
198
+ }
199
+ /**
200
+ * Validates file size and content security before processing
201
+ * @param localPath Path to the local file to validate
202
+ * @returns Validation result with content or error response
203
+ */
204
+ async validateFileAndContent(localPath) {
205
+ // SECURITY ENHANCEMENT (Task #7): Validate file path before processing
206
+ const pathValidation = await this.validatePortfolioPath(localPath);
207
+ if (!pathValidation.isValid) {
208
+ return {
209
+ success: false,
210
+ error: pathValidation.error
211
+ };
212
+ }
213
+ // Use the validated safe path for all subsequent operations
214
+ const safePath = pathValidation.safePath;
215
+ // Validate file size before reading
216
+ const stats = await fs.stat(safePath);
217
+ if (stats.size > FILE_SIZE_LIMITS.MAX_FILE_SIZE) {
218
+ SecurityMonitor.logSecurityEvent({
219
+ type: 'RATE_LIMIT_EXCEEDED',
220
+ severity: 'MEDIUM',
221
+ source: 'SubmitToPortfolioTool.execute',
222
+ details: `File size ${stats.size} exceeds limit of ${FILE_SIZE_LIMITS.MAX_FILE_SIZE}`
223
+ });
224
+ return {
225
+ success: false,
226
+ error: {
227
+ success: false,
228
+ message: `File size exceeds ${FILE_SIZE_LIMITS.MAX_FILE_SIZE_MB}MB limit`,
229
+ error: 'FILE_TOO_LARGE'
230
+ }
231
+ };
232
+ }
233
+ // Validate content security
234
+ const content = await fs.readFile(safePath, 'utf-8');
235
+ const validationResult = ContentValidator.validateAndSanitize(content);
236
+ if (!validationResult.isValid && validationResult.severity === 'critical') {
237
+ SecurityMonitor.logSecurityEvent({
238
+ type: 'CONTENT_INJECTION_ATTEMPT',
239
+ severity: 'HIGH',
240
+ source: 'SubmitToPortfolioTool.execute',
241
+ details: `Critical security issues detected: ${validationResult.detectedPatterns?.join(', ')}`
242
+ });
243
+ return {
244
+ success: false,
245
+ error: {
246
+ success: false,
247
+ message: `Content validation failed: ${validationResult.detectedPatterns?.join(', ')}`,
248
+ error: 'VALIDATION_FAILED'
249
+ }
250
+ };
251
+ }
252
+ return {
253
+ success: true,
254
+ content
255
+ };
256
+ }
257
+ /**
258
+ * Prepares metadata for the portfolio element
259
+ * @param safeName The normalized name of the element
260
+ * @param elementType The type of the element
261
+ * @param authStatus Authentication status containing username
262
+ * @returns Metadata object for the element
263
+ */
264
+ prepareElementMetadata(safeName, elementType, authStatus) {
265
+ return {
266
+ name: safeName,
267
+ description: `${elementType} submitted from local portfolio`,
268
+ author: authStatus.username || 'unknown',
269
+ created: new Date().toISOString(),
270
+ updated: new Date().toISOString(),
271
+ version: '1.0.0'
272
+ };
273
+ }
274
+ /**
275
+ * Validates GitHub token and checks for expiration before usage
276
+ * SECURITY ENHANCEMENT (Task #5): Token expiration validation to prevent stale token usage
277
+ * @param token The GitHub token to validate
278
+ * @returns Validation result with status and expiration info
279
+ */
280
+ async validateTokenBeforeUsage(token) {
281
+ try {
282
+ // Check token format first (basic validation)
283
+ if (!TokenManager.validateTokenFormat(token)) {
284
+ SecurityMonitor.logSecurityEvent({
285
+ type: 'TOKEN_VALIDATION_FAILURE',
286
+ severity: 'MEDIUM',
287
+ source: 'SubmitToPortfolioTool.validateTokenBeforeUsage',
288
+ details: 'Token has invalid format'
289
+ });
290
+ return {
291
+ isValid: false,
292
+ error: {
293
+ success: false,
294
+ message: 'Invalid token format. Please re-authenticate.',
295
+ error: 'INVALID_TOKEN_FORMAT'
296
+ }
297
+ };
298
+ }
299
+ // Validate token with GitHub API to check expiration and permissions
300
+ const validationResult = await TokenManager.validateTokenScopes(token, {
301
+ required: ['repo'],
302
+ optional: ['user:email']
303
+ });
304
+ if (!validationResult.isValid) {
305
+ SecurityMonitor.logSecurityEvent({
306
+ type: 'TOKEN_VALIDATION_FAILURE',
307
+ severity: 'MEDIUM',
308
+ source: 'SubmitToPortfolioTool.validateTokenBeforeUsage',
309
+ details: `Token validation failed: ${validationResult.error}`
310
+ });
311
+ return {
312
+ isValid: false,
313
+ error: {
314
+ success: false,
315
+ message: 'GitHub token is invalid or expired. Please re-authenticate.',
316
+ error: 'TOKEN_VALIDATION_FAILED'
317
+ }
318
+ };
319
+ }
320
+ // Check if token is near expiration (rate limit reset time can indicate token freshness)
321
+ let isNearExpiry = false;
322
+ if (validationResult.rateLimit?.resetTime) {
323
+ const now = new Date();
324
+ const timeUntilReset = validationResult.rateLimit.resetTime.getTime() - now.getTime();
325
+ const oneHour = 60 * 60 * 1000;
326
+ // Consider token "near expiry" if rate limit reset is more than 23 hours away
327
+ // (GitHub rate limits reset every hour, so this suggests token age)
328
+ if (timeUntilReset > 23 * oneHour) {
329
+ isNearExpiry = true;
330
+ logger.warn('GitHub token may be near expiration', {
331
+ tokenPrefix: TokenManager.getTokenPrefix(token),
332
+ rateLimitResetTime: validationResult.rateLimit.resetTime,
333
+ recommendation: 'Consider re-authenticating for long operations'
334
+ });
335
+ }
336
+ }
337
+ // Log successful validation
338
+ SecurityMonitor.logSecurityEvent({
339
+ type: 'TOKEN_VALIDATION_SUCCESS',
340
+ severity: 'LOW',
341
+ source: 'SubmitToPortfolioTool.validateTokenBeforeUsage',
342
+ details: 'GitHub token validated successfully before usage',
343
+ metadata: {
344
+ tokenType: TokenManager.getTokenType(token),
345
+ scopes: validationResult.scopes,
346
+ rateLimitRemaining: validationResult.rateLimit?.remaining,
347
+ isNearExpiry
348
+ }
349
+ });
350
+ return {
351
+ isValid: true,
352
+ isNearExpiry
353
+ };
354
+ }
355
+ catch (error) {
356
+ // Handle rate limit exceeded specifically
357
+ if (error?.code === 'RATE_LIMIT_EXCEEDED') {
358
+ logger.warn('Token validation rate limited, allowing operation to proceed with cached status');
359
+ return { isValid: true }; // Allow to proceed if rate limited, as basic format check passed
360
+ }
361
+ SecurityMonitor.logSecurityEvent({
362
+ type: 'TOKEN_VALIDATION_FAILURE',
363
+ severity: 'HIGH',
364
+ source: 'SubmitToPortfolioTool.validateTokenBeforeUsage',
365
+ details: `Token validation error: ${error.message || 'unknown error'}`
366
+ });
367
+ return {
368
+ isValid: false,
369
+ error: {
370
+ success: false,
371
+ message: 'Unable to validate GitHub token. Please check your connection and try again.',
372
+ error: 'TOKEN_VALIDATION_ERROR'
373
+ }
374
+ };
375
+ }
376
+ }
377
+ /**
378
+ * Enhanced path validation for portfolio operations with comprehensive security checks
379
+ * SECURITY ENHANCEMENT (Task #7): Additional validation for special characters and malicious patterns
380
+ * @param filePath The file path to validate
381
+ * @returns Validation result with secure path or error response
382
+ */
383
+ async validatePortfolioPath(filePath) {
384
+ try {
385
+ // Basic null/undefined check
386
+ if (!filePath || typeof filePath !== 'string') {
387
+ SecurityMonitor.logSecurityEvent({
388
+ type: 'PATH_TRAVERSAL_ATTEMPT',
389
+ severity: 'MEDIUM',
390
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
391
+ details: 'Invalid path provided - null, undefined, or non-string'
392
+ });
393
+ return {
394
+ isValid: false,
395
+ error: {
396
+ success: false,
397
+ message: 'Invalid file path provided',
398
+ error: 'INVALID_PATH'
399
+ }
400
+ };
401
+ }
402
+ // Check for suspicious patterns that could indicate path traversal or injection
403
+ const suspiciousPatterns = [
404
+ /\.\./, // Path traversal
405
+ /\/\.\./, // Unix path traversal
406
+ /\\\.\./, // Windows path traversal
407
+ /\x00/, // Null bytes
408
+ /[\x01-\x1f\x7f-\x9f]/, // Control characters
409
+ /[<>:"|?*]/, // Invalid filename characters on Windows
410
+ /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i, // Reserved Windows names
411
+ /^\./, // Hidden files (starting with dot)
412
+ /\s+$/, // Trailing whitespace
413
+ /^[\s]*$/, // Only whitespace
414
+ /%[0-9a-fA-F]{2}/, // URL encoding (potential bypass attempt)
415
+ /\\x[0-9a-fA-F]{2}/, // Hex encoding
416
+ /\$\{.*\}/, // Template literal injection
417
+ /`.*`/, // Backtick injection
418
+ /[\\\/]{2,}/ // Multiple consecutive slashes
419
+ ];
420
+ for (const pattern of suspiciousPatterns) {
421
+ if (pattern.test(filePath)) {
422
+ SecurityMonitor.logSecurityEvent({
423
+ type: 'PATH_TRAVERSAL_ATTEMPT',
424
+ severity: 'HIGH',
425
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
426
+ details: `Suspicious pattern detected in file path: ${pattern.source}`,
427
+ metadata: {
428
+ pathLength: filePath.length,
429
+ pattern: pattern.source
430
+ }
431
+ });
432
+ return {
433
+ isValid: false,
434
+ error: {
435
+ success: false,
436
+ message: 'File path contains invalid or suspicious characters',
437
+ error: 'SUSPICIOUS_PATH_PATTERN'
438
+ }
439
+ };
440
+ }
441
+ }
442
+ // Check path length (prevent buffer overflow attempts)
443
+ const MAX_PATH_LENGTH = process.platform === 'win32' ? 260 : 4096;
444
+ if (filePath.length > MAX_PATH_LENGTH) {
445
+ SecurityMonitor.logSecurityEvent({
446
+ type: 'PATH_TRAVERSAL_ATTEMPT',
447
+ severity: 'MEDIUM',
448
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
449
+ details: `File path exceeds maximum length: ${filePath.length} > ${MAX_PATH_LENGTH}`
450
+ });
451
+ return {
452
+ isValid: false,
453
+ error: {
454
+ success: false,
455
+ message: 'File path is too long',
456
+ error: 'PATH_TOO_LONG'
457
+ }
458
+ };
459
+ }
460
+ // Normalize path to resolve any relative components safely
461
+ let normalizedPath;
462
+ try {
463
+ // Remove null bytes and normalize
464
+ const cleanPath = filePath.replace(/\x00/g, '');
465
+ normalizedPath = path.normalize(cleanPath);
466
+ // Ensure the normalized path doesn't escape the intended directory
467
+ if (normalizedPath.includes('..') || normalizedPath.startsWith('/') ||
468
+ (process.platform === 'win32' && /^[a-zA-Z]:/.test(normalizedPath))) {
469
+ throw new Error('Path normalization resulted in directory traversal');
470
+ }
471
+ }
472
+ catch (error) {
473
+ SecurityMonitor.logSecurityEvent({
474
+ type: 'PATH_TRAVERSAL_ATTEMPT',
475
+ severity: 'HIGH',
476
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
477
+ details: `Path normalization failed: ${error instanceof Error ? error.message : 'unknown error'}`
478
+ });
479
+ return {
480
+ isValid: false,
481
+ error: {
482
+ success: false,
483
+ message: 'File path could not be safely processed',
484
+ error: 'PATH_NORMALIZATION_FAILED'
485
+ }
486
+ };
487
+ }
488
+ // Validate file extension (only allow safe extensions for portfolio content)
489
+ const allowedExtensions = ['.md', '.markdown', '.txt', '.yml', '.yaml', '.json'];
490
+ const fileExtension = path.extname(normalizedPath).toLowerCase();
491
+ if (fileExtension && !allowedExtensions.includes(fileExtension)) {
492
+ SecurityMonitor.logSecurityEvent({
493
+ type: 'CONTENT_INJECTION_ATTEMPT',
494
+ severity: 'MEDIUM',
495
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
496
+ details: `Disallowed file extension: ${fileExtension}`,
497
+ metadata: {
498
+ allowedExtensions: allowedExtensions.join(', ')
499
+ }
500
+ });
501
+ return {
502
+ isValid: false,
503
+ error: {
504
+ success: false,
505
+ message: `File extension '${fileExtension}' is not allowed. Allowed extensions: ${allowedExtensions.join(', ')}`,
506
+ error: 'INVALID_FILE_EXTENSION'
507
+ }
508
+ };
509
+ }
510
+ // Validate filename characters (only allow safe characters)
511
+ const basename = path.basename(normalizedPath);
512
+ const safeFilenamePattern = /^[a-zA-Z0-9\-_.\s()[\]{}]+$/;
513
+ if (basename && !safeFilenamePattern.test(basename)) {
514
+ SecurityMonitor.logSecurityEvent({
515
+ type: 'CONTENT_INJECTION_ATTEMPT',
516
+ severity: 'MEDIUM',
517
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
518
+ details: 'Filename contains potentially dangerous characters',
519
+ metadata: {
520
+ filename: basename,
521
+ allowedPattern: safeFilenamePattern.source
522
+ }
523
+ });
524
+ return {
525
+ isValid: false,
526
+ error: {
527
+ success: false,
528
+ message: 'Filename contains invalid characters. Only letters, numbers, spaces, hyphens, underscores, dots, and common brackets are allowed.',
529
+ error: 'INVALID_FILENAME_CHARACTERS'
530
+ }
531
+ };
532
+ }
533
+ // Log successful validation
534
+ SecurityMonitor.logSecurityEvent({
535
+ type: 'CONTENT_INJECTION_ATTEMPT',
536
+ severity: 'LOW',
537
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
538
+ details: 'File path validation successful',
539
+ metadata: {
540
+ originalPathLength: filePath.length,
541
+ normalizedPathLength: normalizedPath.length,
542
+ fileExtension: fileExtension || 'none'
543
+ }
544
+ });
545
+ return {
546
+ isValid: true,
547
+ safePath: normalizedPath
548
+ };
549
+ }
550
+ catch (error) {
551
+ SecurityMonitor.logSecurityEvent({
552
+ type: 'PATH_TRAVERSAL_ATTEMPT',
553
+ severity: 'HIGH',
554
+ source: 'SubmitToPortfolioTool.validatePortfolioPath',
555
+ details: `Path validation error: ${error instanceof Error ? error.message : 'unknown error'}`
556
+ });
557
+ return {
558
+ isValid: false,
559
+ error: {
560
+ success: false,
561
+ message: 'Unable to validate file path. Please check the file path and try again.',
562
+ error: 'PATH_VALIDATION_ERROR'
563
+ }
564
+ };
565
+ }
566
+ }
567
+ /**
568
+ * Smart token management for long operations with refresh-like capabilities
569
+ * SECURITY ENHANCEMENT (Task #14): Token refresh logic for long operations
570
+ *
571
+ * Note: GitHub OAuth device flow tokens don't have traditional refresh tokens,
572
+ * but we can implement smart validation and guidance for long operations
573
+ *
574
+ * @param operationType Type of operation being performed
575
+ * @returns Token management result with recommendations
576
+ */
577
+ async manageTokenForLongOperation(operationType) {
578
+ try {
579
+ // Get current token
580
+ const token = await TokenManager.getGitHubTokenAsync();
581
+ if (!token) {
582
+ return {
583
+ canProceed: false,
584
+ error: {
585
+ success: false,
586
+ message: 'No GitHub token available. Please authenticate first.',
587
+ error: 'NO_TOKEN'
588
+ }
589
+ };
590
+ }
591
+ // Validate token for the specific operation
592
+ const validation = await this.validateTokenBeforeUsage(token);
593
+ if (!validation.isValid) {
594
+ return {
595
+ canProceed: false,
596
+ error: validation.error
597
+ };
598
+ }
599
+ // Check if this is a long operation that might benefit from fresh authentication
600
+ const longOperations = ['portfolio_creation', 'collection_submission'];
601
+ const isLongOperation = longOperations.includes(operationType);
602
+ // Get token type to determine refresh capabilities
603
+ const tokenType = TokenManager.getTokenType(token);
604
+ let refreshRecommended = false;
605
+ // For long operations, check token age and recommend refresh if needed
606
+ if (isLongOperation && validation.isNearExpiry) {
607
+ refreshRecommended = true;
608
+ SecurityMonitor.logSecurityEvent({
609
+ type: 'TOKEN_VALIDATION_SUCCESS',
610
+ severity: 'LOW',
611
+ source: 'SubmitToPortfolioTool.manageTokenForLongOperation',
612
+ details: 'Long operation detected with aging token - refresh recommended',
613
+ metadata: {
614
+ operationType,
615
+ tokenType,
616
+ refreshRecommended: true
617
+ }
618
+ });
619
+ logger.warn('Long operation with potentially aging token detected', {
620
+ operationType,
621
+ tokenType,
622
+ recommendation: 'Consider re-authenticating if operation fails'
623
+ });
624
+ }
625
+ // For OAuth tokens in long operations, we can provide guidance
626
+ if (tokenType === 'OAuth Access Token' && isLongOperation) {
627
+ logger.info('OAuth token detected for long operation', {
628
+ operationType,
629
+ tokenType,
630
+ guidance: 'OAuth tokens are time-limited. If operation fails, re-authenticate using setup_github_auth'
631
+ });
632
+ }
633
+ // Log successful token management
634
+ SecurityMonitor.logSecurityEvent({
635
+ type: 'TOKEN_VALIDATION_SUCCESS',
636
+ severity: 'LOW',
637
+ source: 'SubmitToPortfolioTool.manageTokenForLongOperation',
638
+ details: 'Token management successful for long operation',
639
+ metadata: {
640
+ operationType,
641
+ tokenType,
642
+ isLongOperation,
643
+ refreshRecommended
644
+ }
645
+ });
646
+ return {
647
+ canProceed: true,
648
+ token,
649
+ refreshRecommended
650
+ };
651
+ }
652
+ catch (error) {
653
+ SecurityMonitor.logSecurityEvent({
654
+ type: 'TOKEN_VALIDATION_FAILURE',
655
+ severity: 'MEDIUM',
656
+ source: 'SubmitToPortfolioTool.manageTokenForLongOperation',
657
+ details: `Token management error: ${error.message || 'unknown error'}`
658
+ });
659
+ return {
660
+ canProceed: false,
661
+ error: {
662
+ success: false,
663
+ message: 'Unable to manage token for operation. Please check your authentication and try again.',
664
+ error: 'TOKEN_MANAGEMENT_ERROR'
665
+ }
666
+ };
667
+ }
668
+ }
669
+ /**
670
+ * Provides user guidance for token refresh when operations fail due to token issues
671
+ * SECURITY ENHANCEMENT (Task #14): User guidance for authentication refresh
672
+ */
673
+ formatTokenRefreshGuidance(operationType, tokenType) {
674
+ let guidance = '\n\n🔄 **Token Refresh Guidance**:\n';
675
+ if (tokenType === 'OAuth Access Token') {
676
+ guidance += '• Your OAuth token may have expired\n';
677
+ guidance += '• Run `setup_github_auth` to authenticate again\n';
678
+ guidance += '• This will generate a fresh token for continued access\n';
679
+ }
680
+ else if (tokenType === 'Personal Access Token') {
681
+ guidance += '• Your Personal Access Token may have expired\n';
682
+ guidance += '• Check your GitHub settings: https://github.com/settings/tokens\n';
683
+ guidance += '• Generate a new token if needed and update GITHUB_TOKEN environment variable\n';
684
+ }
685
+ else {
686
+ guidance += '• Your GitHub token may have expired or been revoked\n';
687
+ guidance += '• Re-authenticate using `setup_github_auth`\n';
688
+ guidance += '• Ensure your token has the required permissions\n';
689
+ }
690
+ guidance += `\n**Operation**: ${operationType}\n`;
691
+ guidance += '**Required scopes**: repo, user:email\n\n';
692
+ guidance += '💡 **Tip**: Fresh tokens work better for complex operations like portfolio creation.';
693
+ return guidance;
694
+ }
695
+ /**
696
+ * Sets up GitHub repository access and ensures portfolio repository exists
697
+ * @param authStatus Authentication status containing username
698
+ * @returns Setup result or error response
699
+ */
700
+ async setupGitHubRepository(authStatus) {
701
+ // SECURITY ENHANCEMENT (Task #14): Smart token management for long operations
702
+ const tokenManagement = await this.manageTokenForLongOperation('portfolio_creation');
703
+ if (!tokenManagement.canProceed) {
704
+ return {
705
+ success: false,
706
+ error: tokenManagement.error
707
+ };
708
+ }
709
+ const token = tokenManagement.token;
710
+ // Provide user guidance if refresh is recommended for this long operation
711
+ if (tokenManagement.refreshRecommended) {
712
+ const tokenType = TokenManager.getTokenType(token);
713
+ const guidance = this.formatTokenRefreshGuidance('portfolio creation', tokenType);
714
+ logger.warn(`Token refresh recommended for portfolio creation:${guidance}`);
715
+ }
716
+ this.portfolioManager.setToken(token);
717
+ // Check if portfolio exists and create if needed
718
+ const username = authStatus.username || 'unknown';
719
+ const portfolioExists = await this.portfolioManager.checkPortfolioExists(username);
720
+ if (!portfolioExists) {
721
+ logger.info('Creating portfolio repository...');
722
+ // Request consent for portfolio creation
723
+ const repoUrl = await this.portfolioManager.createPortfolio(username, true);
724
+ if (!repoUrl) {
725
+ return {
726
+ success: false,
727
+ error: {
728
+ success: false,
729
+ message: 'Failed to create portfolio repository',
730
+ error: 'CREATE_FAILED'
731
+ }
732
+ };
733
+ }
734
+ }
735
+ return { success: true };
736
+ }
737
+ /**
738
+ * Submits element to portfolio and handles the complete response workflow
739
+ * @param safeName The normalized name of the element
740
+ * @param elementType The type of the element
741
+ * @param metadata The metadata for the element
742
+ * @param content The content of the element
743
+ * @param authStatus Authentication status containing username and token
744
+ * @returns Complete submission result with success message or error
745
+ */
746
+ async submitElementAndHandleResponse(safeName, elementType, metadata, content, authStatus) {
747
+ // Create element structure to save
748
+ const element = {
749
+ type: elementType,
750
+ metadata,
751
+ content
752
+ };
753
+ // TYPE SAFETY FIX #2: Use adapter pattern instead of complex type casting
754
+ // Previously: element as unknown as Parameters<typeof this.portfolioManager.saveElement>[0]
755
+ // Now: Clean adapter pattern that implements IElement interface properly
756
+ const adapter = new PortfolioElementAdapter(element);
757
+ // UX IMPROVEMENT: Add retry logic for transient failures
758
+ const fileUrl = await this.saveElementWithRetry(adapter, safeName, elementType);
759
+ if (!fileUrl) {
760
+ return {
761
+ success: false,
762
+ message: 'Failed to save element to GitHub portfolio after multiple attempts.\n\n' +
763
+ '💡 **Troubleshooting Tips**:\n' +
764
+ '• Check your GitHub authentication: `gh auth status`\n' +
765
+ '• Verify repository permissions\n' +
766
+ '• Try again in a few minutes (GitHub API rate limits)\n' +
767
+ '• Check GitHub status: https://status.github.com',
768
+ error: 'SAVE_FAILED'
769
+ };
770
+ }
771
+ // Log successful submission (DMCP-SEC-006)
772
+ logger.info(`Successfully submitted ${safeName} to GitHub portfolio`, {
773
+ elementType,
774
+ username: authStatus.username,
775
+ fileUrl
776
+ });
777
+ // SECURITY ENHANCEMENT (Task #14): Smart token management for collection submission
778
+ const collectionTokenManagement = await this.manageTokenForLongOperation('collection_submission');
779
+ if (!collectionTokenManagement.canProceed) {
780
+ // Token management failed for collection submission, but main submission succeeded
781
+ const errorMessage = collectionTokenManagement.error?.message || 'Token management failed';
782
+ return {
783
+ success: true,
784
+ message: `✅ Successfully uploaded ${safeName} to your GitHub portfolio!\n📁 Portfolio URL: ${fileUrl}\n\n⚠️ Collection submission skipped: ${errorMessage}`,
785
+ url: fileUrl
786
+ };
787
+ }
788
+ const token = collectionTokenManagement.token;
789
+ // Provide refresh guidance if recommended for collection submission
790
+ if (collectionTokenManagement.refreshRecommended) {
791
+ const tokenType = TokenManager.getTokenType(token);
792
+ logger.info('Collection submission proceeding with aging token', {
793
+ tokenType,
794
+ recommendation: 'If collection submission fails, try re-authenticating with setup_github_auth'
795
+ });
796
+ }
797
+ // ENHANCEMENT (Issue #549): Ask user if they want to submit to collection
798
+ // This completes the community contribution workflow
799
+ const collectionSubmissionResult = await this.promptForCollectionSubmission({
800
+ elementName: safeName,
801
+ elementType,
802
+ portfolioUrl: fileUrl,
803
+ username: authStatus.username || 'unknown',
804
+ metadata,
805
+ token
806
+ });
807
+ // Build the response message based on what happened
808
+ let message = `✅ Successfully uploaded ${safeName} to your GitHub portfolio!\n`;
809
+ message += `📁 Portfolio URL: ${fileUrl}\n\n`;
810
+ if (collectionSubmissionResult.submitted) {
811
+ message += `🎉 Also submitted to DollhouseMCP collection for community review!\n`;
812
+ message += `📋 Issue: ${collectionSubmissionResult.issueUrl}`;
813
+ }
814
+ else if (collectionSubmissionResult.declined) {
815
+ message += `💡 You can submit to the collection later using the same command.`;
816
+ }
817
+ else if (collectionSubmissionResult.error) {
818
+ message += `⚠️ Collection submission failed: ${collectionSubmissionResult.error}\n`;
819
+ message += `💡 You can manually submit at: https://github.com/DollhouseMCP/collection/issues/new`;
820
+ }
821
+ return {
822
+ success: true,
823
+ message,
824
+ url: fileUrl
825
+ };
826
+ }
827
+ async execute(params) {
828
+ try {
829
+ // Validate and normalize input parameters
830
+ const validationResult = await this.validateAndNormalizeParams(params);
831
+ if (!validationResult.success) {
832
+ return validationResult.error;
833
+ }
834
+ const safeName = validationResult.safeName;
835
+ // Check authentication status
836
+ const authResult = await this.checkAuthentication();
837
+ if (!authResult.success) {
838
+ return authResult.error;
839
+ }
840
+ const authStatus = authResult.authStatus;
841
+ // Find content locally with smart type detection
842
+ const contentResult = await this.discoverContentWithTypeDetection(safeName, params.type, params.name);
843
+ if (!contentResult.success) {
844
+ return contentResult.error;
845
+ }
846
+ const elementType = contentResult.elementType;
847
+ const localPath = contentResult.localPath;
848
+ // Validate file and content security
849
+ const securityResult = await this.validateFileAndContent(localPath);
850
+ if (!securityResult.success) {
851
+ return securityResult.error;
852
+ }
853
+ const content = securityResult.content;
854
+ // Get user consent (placeholder for now - could add interactive prompt later)
855
+ logger.info(`Preparing to submit ${safeName} to GitHub portfolio`);
856
+ // Prepare metadata for element
857
+ const metadata = this.prepareElementMetadata(safeName, elementType, authStatus);
858
+ // Set up GitHub repository access
859
+ const repoResult = await this.setupGitHubRepository(authStatus);
860
+ if (!repoResult.success) {
861
+ return repoResult.error;
862
+ }
863
+ // Submit element to portfolio and handle collection submission
864
+ return await this.submitElementAndHandleResponse(safeName, elementType, metadata, content, authStatus);
865
+ }
866
+ catch (error) {
867
+ // SECURITY ENHANCEMENT (Task #14): Enhanced error handling with token refresh guidance
868
+ ErrorHandler.logError('submitToPortfolio', error, {
869
+ elementName: params.name,
870
+ elementType: params.type
871
+ });
872
+ // Check if error is token-related and provide refresh guidance
873
+ const errorMessage = error instanceof Error ? error.message : String(error);
874
+ const isTokenError = errorMessage.toLowerCase().includes('token') ||
875
+ errorMessage.toLowerCase().includes('auth') ||
876
+ errorMessage.toLowerCase().includes('401') ||
877
+ errorMessage.toLowerCase().includes('403');
878
+ let formattedError = ErrorHandler.formatForResponse(error);
879
+ if (isTokenError) {
880
+ try {
881
+ // Get current token to determine type for guidance
882
+ const currentToken = await TokenManager.getGitHubTokenAsync();
883
+ if (currentToken) {
884
+ const tokenType = TokenManager.getTokenType(currentToken);
885
+ const refreshGuidance = this.formatTokenRefreshGuidance('portfolio submission', tokenType);
886
+ // Append refresh guidance to error message
887
+ if (formattedError.message) {
888
+ formattedError.message += refreshGuidance;
889
+ }
890
+ }
891
+ }
892
+ catch (tokenError) {
893
+ // If we can't get token info, provide generic guidance
894
+ formattedError.message += '\n\n🔄 **Authentication Issue**: Try running `setup_github_auth` to refresh your authentication.';
895
+ }
896
+ }
897
+ return formattedError;
898
+ }
899
+ }
900
+ /**
901
+ * Prompts user to submit content to the DollhouseMCP collection
902
+ * ENHANCEMENT (Issue #549): Complete the community contribution workflow
903
+ */
904
+ async promptForCollectionSubmission(params) {
905
+ try {
906
+ // Create a simple prompt message for the user
907
+ // Note: In MCP context, we can't do interactive prompts, so we'll need to
908
+ // either make this automatic or require a parameter
909
+ // For now, let's check if the user has set an environment variable
910
+ // to auto-submit to collection (opt-in behavior)
911
+ const autoSubmit = process.env.DOLLHOUSE_AUTO_SUBMIT_TO_COLLECTION === 'true';
912
+ if (!autoSubmit) {
913
+ // User hasn't opted in to auto-submission
914
+ logger.info('Collection submission skipped (set DOLLHOUSE_AUTO_SUBMIT_TO_COLLECTION=true to enable)');
915
+ return { submitted: false, declined: true };
916
+ }
917
+ logger.info('Auto-submitting to DollhouseMCP collection...');
918
+ // Create the issue in the collection repository
919
+ const issueUrl = await this.createCollectionIssue({
920
+ ...params,
921
+ token: params.token
922
+ });
923
+ if (issueUrl) {
924
+ logger.info('Successfully created collection submission issue', { issueUrl });
925
+ return { submitted: true, declined: false, issueUrl };
926
+ }
927
+ else {
928
+ return { submitted: false, declined: false, error: 'Failed to create issue' };
929
+ }
930
+ }
931
+ catch (error) {
932
+ logger.error('Error in collection submission prompt', { error });
933
+ return {
934
+ submitted: false,
935
+ declined: false,
936
+ error: error instanceof Error ? error.message : 'Unknown error'
937
+ };
938
+ }
939
+ }
940
+ /**
941
+ * Creates an issue in the DollhouseMCP/collection repository
942
+ * ENHANCEMENT (Issue #549): GitHub API integration for collection submission
943
+ */
944
+ async createCollectionIssue(params) {
945
+ try {
946
+ // Format the issue title
947
+ const title = `[${params.elementType}] Add ${params.elementName} by @${params.username}`;
948
+ // Format the issue body with all relevant information
949
+ const body = `## New ${params.elementType} Submission
950
+
951
+ ` +
952
+ `**Name**: ${params.elementName}\n` +
953
+ `**Author**: @${params.username}\n` +
954
+ `**Type**: ${params.elementType}\n` +
955
+ `**Description**: ${params.metadata.description || 'No description provided'}\n\n` +
956
+ `### Portfolio Link\n` +
957
+ `${params.portfolioUrl}\n\n` +
958
+ `### Metadata\n` +
959
+ `\`\`\`json\n${JSON.stringify(params.metadata, null, 2)}\n\`\`\`\n\n` +
960
+ `### Review Checklist\n` +
961
+ `- [ ] Content is appropriate and follows community guidelines\n` +
962
+ `- [ ] No security vulnerabilities or malicious patterns\n` +
963
+ `- [ ] Metadata is complete and accurate\n` +
964
+ `- [ ] Element works as described\n` +
965
+ `- [ ] No duplicate of existing collection content\n\n` +
966
+ `---\n` +
967
+ `*This submission was created automatically via the DollhouseMCP submit_content tool.*`;
968
+ // Determine labels based on element type
969
+ const labels = [
970
+ 'contribution', // All submissions get this
971
+ 'pending-review', // Needs review
972
+ params.elementType.toLowerCase() // Element type label
973
+ ];
974
+ // PERFORMANCE OPTIMIZATION (Task #6): Use GitHub rate limiter for API calls
975
+ // This prevents hitting GitHub rate limits and provides better error handling
976
+ const issueUrl = await githubRateLimiter.queueRequest('create-collection-issue', async () => {
977
+ const url = 'https://api.github.com/repos/DollhouseMCP/collection/issues';
978
+ // Create AbortController for timeout
979
+ const controller = new AbortController();
980
+ const timeoutId = setTimeout(() => controller.abort(), getValidatedTimeout());
981
+ try {
982
+ const response = await fetch(url, {
983
+ method: 'POST',
984
+ headers: {
985
+ 'Accept': 'application/vnd.github.v3+json',
986
+ 'Authorization': `Bearer ${params.token}`,
987
+ 'Content-Type': 'application/json',
988
+ 'User-Agent': 'DollhouseMCP/1.0'
989
+ },
990
+ body: JSON.stringify({
991
+ title,
992
+ body,
993
+ labels
994
+ }),
995
+ signal: controller.signal
996
+ });
997
+ clearTimeout(timeoutId);
998
+ // PERFORMANCE OPTIMIZATION (Task #15): Enhanced rate limit logging
999
+ // Log rate limit headers for diagnostics
1000
+ const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining');
1001
+ const rateLimitReset = response.headers.get('X-RateLimit-Reset');
1002
+ const rateLimitLimit = response.headers.get('X-RateLimit-Limit');
1003
+ logger.debug('GitHub API rate limit status', {
1004
+ operation: 'create-collection-issue',
1005
+ remaining: rateLimitRemaining,
1006
+ limit: rateLimitLimit,
1007
+ resetTime: rateLimitReset ? new Date(parseInt(rateLimitReset) * 1000) : undefined,
1008
+ responseStatus: response.status
1009
+ });
1010
+ // Log warning if approaching rate limit
1011
+ if (rateLimitRemaining && parseInt(rateLimitRemaining) < 100) {
1012
+ logger.warn('Approaching GitHub API rate limit', {
1013
+ operation: 'create-collection-issue',
1014
+ remaining: rateLimitRemaining,
1015
+ resetTime: rateLimitReset ? new Date(parseInt(rateLimitReset) * 1000) : undefined,
1016
+ recommendation: 'Consider reducing API usage frequency or authenticating for higher limits'
1017
+ });
1018
+ }
1019
+ if (!response.ok) {
1020
+ const errorText = await response.text();
1021
+ logger.error('GitHub API error creating issue', {
1022
+ status: response.status,
1023
+ statusText: response.statusText,
1024
+ error: errorText,
1025
+ rateLimitRemaining,
1026
+ rateLimitReset
1027
+ });
1028
+ if (response.status === 404) {
1029
+ logger.error('Collection repository not found or no access');
1030
+ }
1031
+ else if (response.status === 403) {
1032
+ logger.error('Permission denied to create issue in collection repo');
1033
+ }
1034
+ else if (response.status === 401) {
1035
+ logger.error('Authentication failed for collection submission');
1036
+ }
1037
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
1038
+ }
1039
+ const data = await response.json();
1040
+ return data.html_url;
1041
+ }
1042
+ catch (fetchError) {
1043
+ // Re-throw to outer catch block
1044
+ throw fetchError;
1045
+ }
1046
+ finally {
1047
+ clearTimeout(timeoutId);
1048
+ }
1049
+ }, 'high' // High priority for collection submission
1050
+ );
1051
+ return issueUrl;
1052
+ }
1053
+ catch (error) {
1054
+ // Handle timeout specifically
1055
+ if (error.name === 'AbortError') {
1056
+ logger.error(`GitHub API request timeout after ${getValidatedTimeout()}ms`);
1057
+ }
1058
+ else {
1059
+ logger.error('Failed to create collection issue', {
1060
+ error: error.message || error
1061
+ });
1062
+ }
1063
+ return null;
1064
+ }
1065
+ }
1066
+ async findLocalContent(name, type) {
1067
+ try {
1068
+ // METADATA INDEX FIX: Use portfolio index for fast metadata-based lookups
1069
+ // This solves the critical issue where "Safe Roundtrip Tester" couldn't be found
1070
+ // because findLocalContent only searched filenames, not metadata names
1071
+ const indexManager = PortfolioIndexManager.getInstance();
1072
+ // UX IMPROVEMENT: Enhanced search with fuzzy matching
1073
+ const indexEntry = await indexManager.findByName(name, {
1074
+ elementType: type,
1075
+ fuzzyMatch: true
1076
+ });
1077
+ if (indexEntry) {
1078
+ logger.debug('Found content via metadata index', {
1079
+ searchName: name,
1080
+ metadataName: indexEntry.metadata.name,
1081
+ filename: indexEntry.filename,
1082
+ filePath: indexEntry.filePath,
1083
+ type
1084
+ });
1085
+ return indexEntry.filePath;
1086
+ }
1087
+ // FALLBACK: Use original file discovery if index lookup fails
1088
+ // This maintains backward compatibility and handles edge cases
1089
+ logger.debug('Index lookup failed, falling back to file discovery', { name, type });
1090
+ const portfolioManager = PortfolioManager.getInstance();
1091
+ const portfolioDir = portfolioManager.getElementDir(type);
1092
+ // UX IMPROVEMENT: Try multiple search strategies for better user experience
1093
+ let file = await FileDiscoveryUtil.findFile(portfolioDir, name, {
1094
+ extensions: ['.md', '.json', '.yaml', '.yml'],
1095
+ partialMatch: true,
1096
+ cacheResults: true
1097
+ });
1098
+ // If not found, try normalizing the name (e.g., "J.A.R.V.I.S." -> "j-a-r-v-i-s")
1099
+ if (!file) {
1100
+ const normalizedName = name.toLowerCase()
1101
+ .replace(/[^a-z0-9]/gi, '-') // Replace non-alphanumeric with dashes
1102
+ .replace(/-+/g, '-') // Replace multiple dashes with single dash
1103
+ .replace(/^-|-$/g, ''); // Remove leading/trailing dashes
1104
+ if (normalizedName !== name.toLowerCase()) {
1105
+ logger.debug('Trying normalized name search', {
1106
+ original: name,
1107
+ normalized: normalizedName,
1108
+ type
1109
+ });
1110
+ file = await FileDiscoveryUtil.findFile(portfolioDir, normalizedName, {
1111
+ extensions: ['.md', '.json', '.yaml', '.yml'],
1112
+ partialMatch: true,
1113
+ cacheResults: true
1114
+ });
1115
+ }
1116
+ }
1117
+ // If still not found, try searching by display name patterns
1118
+ if (!file) {
1119
+ // Try common variations like removing dots, spaces, etc.
1120
+ const variations = [
1121
+ name.replace(/\./g, ''), // Remove dots: "J.A.R.V.I.S." -> "JARVIS"
1122
+ name.replace(/\s+/g, '-'), // Replace spaces with dashes
1123
+ name.replace(/[\s\.]/g, ''), // Remove spaces and dots
1124
+ name.replace(/[\s\.]/g, '-'), // Replace spaces and dots with dashes
1125
+ ].filter(v => v !== name && v.length > 0);
1126
+ for (const variation of variations) {
1127
+ file = await FileDiscoveryUtil.findFile(portfolioDir, variation, {
1128
+ extensions: ['.md', '.json', '.yaml', '.yml'],
1129
+ partialMatch: true,
1130
+ cacheResults: true
1131
+ });
1132
+ if (file) {
1133
+ logger.debug('Found content using name variation', {
1134
+ original: name,
1135
+ variation,
1136
+ file,
1137
+ type
1138
+ });
1139
+ break;
1140
+ }
1141
+ }
1142
+ }
1143
+ if (file) {
1144
+ logger.debug('Found local content file via fallback', { name, type, file });
1145
+ return file;
1146
+ }
1147
+ logger.debug('No content found', { name, type });
1148
+ return null;
1149
+ }
1150
+ catch (error) {
1151
+ logger.error('Error finding local content', {
1152
+ name,
1153
+ type,
1154
+ error: error instanceof Error ? error.message : String(error)
1155
+ });
1156
+ return null;
1157
+ }
1158
+ }
1159
+ /**
1160
+ * Smart element type detection - searches across ALL element types for content
1161
+ * PERFORMANCE OPTIMIZATION (Task #9): Uses early termination for exact matches
1162
+ * This replaces the previous hardcoded default to PERSONA and enables proper type detection
1163
+ *
1164
+ * @param name The content name to search for
1165
+ * @returns Detection result with found matches across all element types
1166
+ */
1167
+ async detectElementType(name) {
1168
+ try {
1169
+ // PERFORMANCE OPTIMIZATION (Task #9): Use early termination search utility
1170
+ // Create search functions for each element type
1171
+ const elementTypes = Object.values(ElementType);
1172
+ const searchFunctions = elementTypes.map((type) => async () => {
1173
+ try {
1174
+ const filePath = await this.findLocalContent(name, type);
1175
+ if (filePath) {
1176
+ return { type: type, path: filePath };
1177
+ }
1178
+ return null;
1179
+ }
1180
+ catch (error) {
1181
+ // Log unexpected errors but don't fail the search
1182
+ if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR') {
1183
+ logger.debug(`Error searching ${type} directory for content detection`, {
1184
+ name,
1185
+ type,
1186
+ error: error?.message || String(error),
1187
+ code: error?.code
1188
+ });
1189
+ }
1190
+ // Return null instead of throwing to let other searches continue
1191
+ return null;
1192
+ }
1193
+ });
1194
+ // PERFORMANCE OPTIMIZATION (Task #9): Define exact match criteria
1195
+ const isExactMatch = (match) => {
1196
+ const filename = path.basename(match.path, path.extname(match.path));
1197
+ return filename.toLowerCase() === name.toLowerCase();
1198
+ };
1199
+ // Execute searches with early termination optimization
1200
+ const searchResults = await EarlyTerminationSearch.executeWithEarlyTermination(searchFunctions, isExactMatch, {
1201
+ operationName: 'element-type-detection',
1202
+ timeoutAfterExactMatch: 1000, // Wait 1 second for other searches after exact match
1203
+ maxParallelSearches: 8 // Limit concurrent searches to avoid overwhelming the system
1204
+ });
1205
+ // PERFORMANCE OPTIMIZATION (Task #8): Enhanced batch operation reporting
1206
+ const batchResults = {
1207
+ name,
1208
+ totalSearches: searchResults.totalSearches,
1209
+ completedSearches: searchResults.completedSearches,
1210
+ matches: searchResults.matches.length,
1211
+ failures: searchResults.failures.length,
1212
+ exactMatchFound: !!searchResults.exactMatch,
1213
+ exactMatchType: searchResults.exactMatch?.type,
1214
+ earlyTerminationTriggered: searchResults.earlyTerminationTriggered,
1215
+ performanceGain: searchResults.performanceGain,
1216
+ matchedTypes: searchResults.matches.map(m => m.type),
1217
+ failedTypes: searchResults.failures.map(f => elementTypes[f.index]).filter(Boolean)
1218
+ };
1219
+ logger.debug('Element type detection completed with early termination optimization', batchResults);
1220
+ // PERFORMANCE OPTIMIZATION (Task #8): Clear reporting of partial failures
1221
+ if (searchResults.failures.length > 0) {
1222
+ logger.warn('Some element type searches failed during batch operation', {
1223
+ name,
1224
+ failures: searchResults.failures.map(f => ({
1225
+ type: elementTypes[f.index] || 'unknown',
1226
+ error: f.error.substring(0, 100) // Truncate long error messages
1227
+ })),
1228
+ successRate: `${searchResults.completedSearches}/${searchResults.totalSearches}`,
1229
+ impactOnResults: searchResults.matches.length > 0
1230
+ ? 'No impact - matches found in successful searches'
1231
+ : 'Potential impact - no matches found'
1232
+ });
1233
+ // If we have failures and no matches, provide actionable guidance
1234
+ if (searchResults.matches.length === 0 && searchResults.failures.length > 0) {
1235
+ logger.warn('Batch operation had failures and no matches found', {
1236
+ name,
1237
+ recommendation: 'Consider checking file permissions or portfolio structure',
1238
+ failureCount: searchResults.failures.length,
1239
+ totalSearches: searchResults.totalSearches
1240
+ });
1241
+ }
1242
+ }
1243
+ // Log performance gains from early termination
1244
+ if (searchResults.earlyTerminationTriggered) {
1245
+ logger.info('Early termination optimization applied successfully', {
1246
+ name,
1247
+ exactMatchType: searchResults.exactMatch?.type,
1248
+ performanceGain: searchResults.performanceGain,
1249
+ searchesCompleted: searchResults.completedSearches,
1250
+ searchesTotal: searchResults.totalSearches
1251
+ });
1252
+ }
1253
+ return {
1254
+ found: searchResults.matches.length > 0,
1255
+ matches: searchResults.matches
1256
+ };
1257
+ }
1258
+ catch (error) {
1259
+ logger.error('Error in element type detection', {
1260
+ name,
1261
+ error: error instanceof Error ? error.message : String(error)
1262
+ });
1263
+ // Return empty result on detection failure
1264
+ return {
1265
+ found: false,
1266
+ matches: []
1267
+ };
1268
+ }
1269
+ }
1270
+ /**
1271
+ * UX IMPROVEMENT: Generate name suggestions for similar content
1272
+ * PERFORMANCE OPTIMIZATION (Task #8): Enhanced batch operation handling with clear partial failure reporting
1273
+ * Helps users find content when exact matches fail
1274
+ */
1275
+ async generateNameSuggestions(searchName) {
1276
+ try {
1277
+ const suggestions = [];
1278
+ const searchLower = searchName.toLowerCase();
1279
+ const elementTypes = Object.values(ElementType);
1280
+ // Track batch operation results for better diagnostics
1281
+ const batchResults = {
1282
+ searchName,
1283
+ totalTypes: elementTypes.length,
1284
+ successfulScans: 0,
1285
+ failedScans: 0,
1286
+ failureDetails: [],
1287
+ totalSuggestions: 0,
1288
+ suggestionsByType: {}
1289
+ };
1290
+ // Process all element types for suggestions
1291
+ for (const elementType of elementTypes) {
1292
+ try {
1293
+ const portfolioManager = PortfolioManager.getInstance();
1294
+ const elementDir = portfolioManager.getElementDir(elementType);
1295
+ // Get files in this directory
1296
+ const files = await FileDiscoveryUtil.findFile(elementDir, '*', {
1297
+ extensions: ['.md', '.json', '.yaml', '.yml'],
1298
+ partialMatch: false,
1299
+ cacheResults: true
1300
+ });
1301
+ let typeSuggestions = 0;
1302
+ if (Array.isArray(files)) {
1303
+ for (const filePath of files) {
1304
+ const basename = path.basename(filePath, path.extname(filePath));
1305
+ // Calculate similarity using simple metrics
1306
+ if (this.calculateSimilarity(searchLower, basename.toLowerCase()) > SEARCH_CONFIG.MIN_SIMILARITY_SCORE) {
1307
+ suggestions.push({
1308
+ name: basename,
1309
+ type: elementType
1310
+ });
1311
+ typeSuggestions++;
1312
+ }
1313
+ }
1314
+ }
1315
+ else if (files) {
1316
+ const basename = path.basename(files, path.extname(files));
1317
+ if (this.calculateSimilarity(searchLower, basename.toLowerCase()) > SEARCH_CONFIG.MIN_SIMILARITY_SCORE) {
1318
+ suggestions.push({
1319
+ name: basename,
1320
+ type: elementType
1321
+ });
1322
+ typeSuggestions++;
1323
+ }
1324
+ }
1325
+ batchResults.successfulScans++;
1326
+ batchResults.suggestionsByType[elementType] = typeSuggestions;
1327
+ }
1328
+ catch (error) {
1329
+ // PERFORMANCE OPTIMIZATION (Task #8): Track and report partial failures
1330
+ batchResults.failedScans++;
1331
+ batchResults.failureDetails.push({
1332
+ type: elementType,
1333
+ error: error instanceof Error ? error.message : String(error)
1334
+ });
1335
+ // Log individual failures for diagnostics
1336
+ logger.debug('Failed to scan element type for suggestions', {
1337
+ elementType,
1338
+ searchName,
1339
+ error: error instanceof Error ? error.message : String(error)
1340
+ });
1341
+ }
1342
+ }
1343
+ batchResults.totalSuggestions = suggestions.length;
1344
+ // PERFORMANCE OPTIMIZATION (Task #8): Comprehensive batch operation reporting
1345
+ logger.debug('Name suggestion batch operation completed', {
1346
+ ...batchResults,
1347
+ successRate: `${batchResults.successfulScans}/${batchResults.totalTypes}`,
1348
+ // Don't log full failure details at debug level to avoid spam
1349
+ hasFailures: batchResults.failedScans > 0
1350
+ });
1351
+ // Report failures clearly if they occurred
1352
+ if (batchResults.failedScans > 0) {
1353
+ logger.warn('Some element type scans failed during name suggestion generation', {
1354
+ searchName,
1355
+ failedTypes: batchResults.failureDetails.map(f => f.type),
1356
+ successfulTypes: batchResults.successfulScans,
1357
+ impactOnResults: batchResults.totalSuggestions > 0
1358
+ ? 'Partial impact - suggestions found from successful scans'
1359
+ : 'Potential impact - no suggestions generated',
1360
+ recommendation: batchResults.totalSuggestions === 0 && batchResults.failedScans > 0
1361
+ ? 'Check portfolio directory structure and file permissions'
1362
+ : 'Suggestion generation partially successful despite some failures'
1363
+ });
1364
+ }
1365
+ // Sort by similarity (higher is better) and return top suggestions
1366
+ const sortedSuggestions = suggestions.sort((a, b) => {
1367
+ const simA = this.calculateSimilarity(searchLower, a.name.toLowerCase());
1368
+ const simB = this.calculateSimilarity(searchLower, b.name.toLowerCase());
1369
+ return simB - simA;
1370
+ });
1371
+ logger.debug('Name suggestions generated successfully', {
1372
+ searchName,
1373
+ totalSuggestions: sortedSuggestions.length,
1374
+ topSuggestions: sortedSuggestions.slice(0, 3).map(s => s.name)
1375
+ });
1376
+ return sortedSuggestions;
1377
+ }
1378
+ catch (error) {
1379
+ logger.warn('Failed to generate name suggestions - batch operation failed completely', {
1380
+ searchName,
1381
+ error: error instanceof Error ? error.message : String(error),
1382
+ recommendation: 'Check portfolio structure and permissions'
1383
+ });
1384
+ return [];
1385
+ }
1386
+ }
1387
+ /**
1388
+ * Simple similarity calculation using Levenshtein-like approach
1389
+ * Returns value between 0 and 1, where 1 is identical
1390
+ */
1391
+ calculateSimilarity(str1, str2) {
1392
+ // Handle exact matches
1393
+ if (str1 === str2)
1394
+ return 1;
1395
+ // Handle substring matches
1396
+ if (str1.includes(str2) || str2.includes(str1))
1397
+ return 0.8;
1398
+ // Handle partial matches
1399
+ const longer = str1.length > str2.length ? str1 : str2;
1400
+ const shorter = str1.length > str2.length ? str2 : str1;
1401
+ if (longer.length === 0)
1402
+ return 0;
1403
+ // Count common characters
1404
+ let common = 0;
1405
+ for (let i = 0; i < shorter.length; i++) {
1406
+ if (longer.includes(shorter[i])) {
1407
+ common++;
1408
+ }
1409
+ }
1410
+ return common / longer.length;
1411
+ }
1412
+ /**
1413
+ * UX IMPROVEMENT: Save element with automatic retry logic for transient failures
1414
+ * Handles common GitHub API issues like rate limits and temporary network problems
1415
+ */
1416
+ async saveElementWithRetry(adapter, elementName, elementType, maxRetries = RETRY_CONFIG.MAX_ATTEMPTS) {
1417
+ let lastError = null;
1418
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
1419
+ try {
1420
+ logger.debug(`Attempting to save element (attempt ${attempt}/${maxRetries})`, {
1421
+ elementName,
1422
+ elementType,
1423
+ attempt
1424
+ });
1425
+ const fileUrl = await this.portfolioManager.saveElement(adapter, true);
1426
+ if (fileUrl) {
1427
+ if (attempt > 1) {
1428
+ logger.info(`Element saved successfully after ${attempt} attempts`, {
1429
+ elementName,
1430
+ elementType,
1431
+ fileUrl
1432
+ });
1433
+ }
1434
+ return fileUrl;
1435
+ }
1436
+ // If saveElement returns null, treat as a failure but don't retry immediately
1437
+ lastError = new Error(`saveElement returned null on attempt ${attempt}`);
1438
+ }
1439
+ catch (error) {
1440
+ lastError = error;
1441
+ const isRetryable = this.isRetryableError(error);
1442
+ logger.warn(`Save attempt ${attempt} failed`, {
1443
+ elementName,
1444
+ elementType,
1445
+ attempt,
1446
+ error: error.message,
1447
+ isRetryable,
1448
+ willRetry: isRetryable && attempt < maxRetries
1449
+ });
1450
+ // If this is not a retryable error, fail immediately
1451
+ if (!isRetryable) {
1452
+ logger.error('Non-retryable error encountered, aborting retries', {
1453
+ elementName,
1454
+ error: error.message
1455
+ });
1456
+ break;
1457
+ }
1458
+ // If we have more attempts, wait before retrying
1459
+ if (attempt < maxRetries) {
1460
+ const delay = calculateRetryDelay(attempt);
1461
+ logger.debug(`Waiting ${delay}ms before retry`, { attempt, delay });
1462
+ await new Promise(resolve => setTimeout(resolve, delay));
1463
+ }
1464
+ }
1465
+ }
1466
+ // All attempts failed
1467
+ logger.error(`All ${maxRetries} save attempts failed`, {
1468
+ elementName,
1469
+ elementType,
1470
+ lastError: lastError?.message
1471
+ });
1472
+ return null;
1473
+ }
1474
+ /**
1475
+ * Determine if an error is worth retrying
1476
+ * Retryable: network issues, rate limits, temporary GitHub API problems
1477
+ * Non-retryable: authentication issues, validation errors, permanent failures
1478
+ */
1479
+ isRetryableError(error) {
1480
+ const errorMessage = error?.message?.toLowerCase() || '';
1481
+ const errorCode = error?.code;
1482
+ const statusCode = error?.status || error?.statusCode;
1483
+ // Network and timeout errors
1484
+ if (errorCode === 'ENOTFOUND' || errorCode === 'ECONNRESET' || errorCode === 'ETIMEDOUT') {
1485
+ return true;
1486
+ }
1487
+ // GitHub API rate limits
1488
+ if (statusCode === 429 || errorMessage.includes('rate limit')) {
1489
+ return true;
1490
+ }
1491
+ // Temporary GitHub API issues
1492
+ if (statusCode >= 500 && statusCode < 600) {
1493
+ return true;
1494
+ }
1495
+ // Temporary GitHub API problems
1496
+ if (errorMessage.includes('temporarily unavailable') ||
1497
+ errorMessage.includes('service unavailable') ||
1498
+ errorMessage.includes('internal server error')) {
1499
+ return true;
1500
+ }
1501
+ // Connection issues
1502
+ if (errorMessage.includes('connection') &&
1503
+ (errorMessage.includes('timeout') || errorMessage.includes('reset'))) {
1504
+ return true;
1505
+ }
1506
+ // Don't retry authentication or permission issues
1507
+ if (statusCode === 401 || statusCode === 403 ||
1508
+ errorMessage.includes('unauthorized') ||
1509
+ errorMessage.includes('forbidden') ||
1510
+ errorMessage.includes('authentication')) {
1511
+ return false;
1512
+ }
1513
+ // Don't retry validation errors
1514
+ if (statusCode === 400 || statusCode === 422 ||
1515
+ errorMessage.includes('invalid') ||
1516
+ errorMessage.includes('validation')) {
1517
+ return false;
1518
+ }
1519
+ // Default to not retrying for unknown errors to avoid infinite loops
1520
+ return false;
1521
+ }
1522
+ }
1523
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3VibWl0VG9Qb3J0Zm9saW9Ub29sLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3Rvb2xzL3BvcnRmb2xpby9zdWJtaXRUb1BvcnRmb2xpb1Rvb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFFSCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNwRSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUMvRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDdEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDdkUsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFDakYsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQUNqRixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFHcEUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDdkUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDckUsT0FBTyxFQUFFLFlBQVksRUFBaUIsTUFBTSw2QkFBNkIsQ0FBQztBQUMxRSxPQUFPLEVBRUwsZ0JBQWdCLEVBQ2hCLFlBQVksRUFDWixhQUFhLEVBRWIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNwQixNQUFNLHFDQUFxQyxDQUFDO0FBQzdDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxz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UUFBUSxDQUFDLFlBQVksQ0FBQztZQUNuQyxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLElBQUksWUFBWSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDekUsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBRUQsa0RBQWtEO1FBQ2xELElBQUksVUFBVSxLQUFLLEdBQUcsSUFBSSxVQUFVLEtBQUssR0FBRztZQUN4QyxZQUFZLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQztZQUNyQyxZQUFZLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQztZQUNsQyxZQUFZLENBQUMsUUFBUSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztZQUM1QyxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCxnQ0FBZ0M7UUFDaEMsSUFBSSxVQUFVLEtBQUssR0FBRyxJQUFJLFVBQVUsS0FBSyxHQUFHO1lBQ3hDLFlBQVksQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDO1lBQ2hDLFlBQVksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUN4QyxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCxxRUFBcUU7UUFDckUsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRvb2wgZm9yIHN1Ym1pdHRpbmcgY29udGVudCB0byBHaXRIdWIgcG9ydGZvbGlvIHJlcG9zaXRvcmllc1xuICogUmVwbGFjZXMgdGhlIGJyb2tlbiBpc3N1ZS1iYXNlZCBzdWJtaXNzaW9uIHdpdGggZGlyZWN0IHJlcG9zaXRvcnkgc2F2ZXNcbiAqIFxuICogRklYRVMgSU1QTEVNRU5URUQgKFBSICM1MDMpOlxuICogMS4gVFlQRSBTQUZFVFkgRklYICMxIChJc3N1ZSAjNDk3KTogQ2hhbmdlZCBhcGlDYWNoZSBmcm9tICdhbnknIHRvIHByb3BlciBBUElDYWNoZSB0eXBlXG4gKiAyLiBUWVBFIFNBRkVUWSBGSVggIzIgKElzc3VlICM0OTcpOiBSZXBsYWNlZCBjb21wbGV4IHR5cGUgY2FzdGluZyB3aXRoIFBvcnRmb2xpb0VsZW1lbnRBZGFwdGVyXG4gKiAzLiBQRVJGT1JNQU5DRSAoUFIgIzQ5NiByZWNvbW1lbmRhdGlvbik6IFVzaW5nIEZpbGVEaXNjb3ZlcnlVdGlsIGZvciBvcHRpbWl6ZWQgZmlsZSBzZWFyY2hcbiAqL1xuXG5pbXBvcnQgeyBHaXRIdWJBdXRoTWFuYWdlciB9IGZyb20gJy4uLy4uL2F1dGgvR2l0SHViQXV0aE1hbmFnZXIuanMnO1xuaW1wb3J0IHsgUG9ydGZvbGlvUmVwb01hbmFnZXIgfSBmcm9tICcuLi8uLi9wb3J0Zm9saW8vUG9ydGZvbGlvUmVwb01hbmFnZXIuanMnO1xuaW1wb3J0IHsgVG9rZW5NYW5hZ2VyIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvdG9rZW5NYW5hZ2VyLmpzJztcbmltcG9ydCB7IENvbnRlbnRWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9jb250ZW50VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFBvcnRmb2xpb01hbmFnZXIgfSBmcm9tICcuLi8uLi9wb3J0Zm9saW8vUG9ydGZvbGlvTWFuYWdlci5qcyc7XG5pbXBvcnQgeyBQb3J0Zm9saW9JbmRleE1hbmFnZXIgfSBmcm9tICcuLi8uLi9wb3J0Zm9saW8vUG9ydGZvbGlvSW5kZXhNYW5hZ2VyLmpzJztcbmltcG9ydCB7IEVsZW1lbnRUeXBlIH0gZnJvbSAnLi4vLi4vcG9ydGZvbGlvL3R5cGVzLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5TW9uaXRvciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3NlY3VyaXR5TW9uaXRvci5qcyc7XG5pbXBvcnQgeyBQYXRoVmFsaWRhdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvcGF0aFZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBBUElDYWNoZSB9IGZyb20gJy4uLy4uL2NhY2hlL0FQSUNhY2hlLmpzJztcbmltcG9ydCB7IFBvcnRmb2xpb0VsZW1lbnRBZGFwdGVyIH0gZnJvbSAnLi9Qb3J0Zm9saW9FbGVtZW50QWRhcHRlci5qcyc7XG5pbXBvcnQgeyBGaWxlRGlzY292ZXJ5VXRpbCB9IGZyb20gJy4uLy4uL3V0aWxzL0ZpbGVEaXNjb3ZlcnlVdGlsLmpzJztcbmltcG9ydCB7IEVycm9ySGFuZGxlciwgRXJyb3JDYXRlZ29yeSB9IGZyb20gJy4uLy4uL3V0aWxzL0Vycm9ySGFuZGxlci5qcyc7XG5pbXBvcnQgeyBcbiAgR0lUSFVCX0FQSV9USU1FT1VULCBcbiAgRklMRV9TSVpFX0xJTUlUUywgXG4gIFJFVFJZX0NPTkZJRywgXG4gIFNFQVJDSF9DT05GSUcsXG4gIFBvcnRmb2xpb0VsZW1lbnRNZXRhZGF0YSxcbiAgZ2V0VmFsaWRhdGVkVGltZW91dCxcbiAgY2FsY3VsYXRlUmV0cnlEZWxheVxufSBmcm9tICcuLi8uLi9jb25maWcvcG9ydGZvbGlvLWNvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBnaXRodWJSYXRlTGltaXRlciB9IGZyb20gJy4uLy4uL3V0aWxzL0dpdEh1YlJhdGVMaW1pdGVyLmpzJztcbmltcG9ydCB7IEVhcmx5VGVybWluYXRpb25TZWFyY2ggfSBmcm9tICcuLi8uLi91dGlscy9FYXJseVRlcm1pbmF0aW9uU2VhcmNoLmpzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcy9wcm9taXNlcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3VibWl0VG9Qb3J0Zm9saW9QYXJhbXMge1xuICBuYW1lOiBzdHJpbmc7XG4gIHR5cGU/OiBFbGVtZW50VHlwZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQb3J0Zm9saW9FbGVtZW50IHtcbiAgdHlwZTogRWxlbWVudFR5cGU7XG4gIG1ldGFkYXRhOiBQb3J0Zm9saW9FbGVtZW50TWV0YWRhdGE7XG4gIGNvbnRlbnQ6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTdWJtaXRUb1BvcnRmb2xpb1Jlc3VsdCB7XG4gIHN1Y2Nlc3M6IGJvb2xlYW47XG4gIG1lc3NhZ2U6IHN0cmluZztcbiAgdXJsPzogc3RyaW5nO1xuICBlcnJvcj86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBFbGVtZW50RGV0ZWN0aW9uTWF0Y2gge1xuICB0eXBlOiBFbGVtZW50VHlwZTtcbiAgcGF0aDogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEVsZW1lbnREZXRlY3Rpb25SZXN1bHQge1xuICBmb3VuZDogYm9vbGVhbjtcbiAgbWF0Y2hlczogRWxlbWVudERldGVjdGlvbk1hdGNoW107XG59XG5cbmV4cG9ydCBjbGFzcyBTdWJtaXRUb1BvcnRmb2xpb1Rvb2wge1xuICBwcml2YXRlIGF1dGhNYW5hZ2VyOiBHaXRIdWJBdXRoTWFuYWdlcjtcbiAgcHJpdmF0ZSBwb3J0Zm9saW9NYW5hZ2VyOiBQb3J0Zm9saW9SZXBvTWFuYWdlcjtcbiAgcHJpdmF0ZSBjb250ZW50VmFsaWRhdG9yOiBDb250ZW50VmFsaWRhdG9yO1xuXG4gIGNvbnN0cnVjdG9yKGFwaUNhY2hlOiBBUElDYWNoZSkge1xuICAgIC8vIFRZUEUgU0FGRVRZIEZJWCAjMTogUHJvcGVyIHR5cGluZyBmb3IgYXBpQ2FjaGUgcGFyYW1ldGVyXG4gICAgLy8gUHJldmlvdXNseTogY29uc3RydWN0b3IoYXBpQ2FjaGU6IGFueSlcbiAgICAvLyBOb3c6IGNvbnN0cnVjdG9yKGFwaUNhY2hlOiBBUElDYWNoZSkgd2l0aCBwcm9wZXIgaW1wb3J0XG4gICAgdGhpcy5hdXRoTWFuYWdlciA9IG5ldyBHaXRIdWJBdXRoTWFuYWdlcihhcGlDYWNoZSk7XG4gICAgdGhpcy5wb3J0Zm9saW9NYW5hZ2VyID0gbmV3IFBvcnRmb2xpb1JlcG9NYW5hZ2VyKCk7XG4gICAgdGhpcy5jb250ZW50VmFsaWRhdG9yID0gbmV3IENvbnRlbnRWYWxpZGF0b3IoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZXMgYW5kIG5vcm1hbGl6ZXMgaW5wdXQgcGFyYW1ldGVycyB0byBwcmV2ZW50IFVuaWNvZGUgYXR0YWNrcyBhbmQgZW5zdXJlIGRhdGEgc2FmZXR5XG4gICAqIEBwYXJhbSBwYXJhbXMgVGhlIGlucHV0IHBhcmFtZXRlcnMgZnJvbSB0aGUgdXNlclxuICAgKiBAcmV0dXJucyBWYWxpZGF0aW9uIHJlc3VsdCB3aXRoIG5vcm1hbGl6ZWQgbmFtZSBvciBlcnJvciByZXNwb25zZVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2YWxpZGF0ZUFuZE5vcm1hbGl6ZVBhcmFtcyhwYXJhbXM6IFN1Ym1pdFRvUG9ydGZvbGlvUGFyYW1zKTogUHJvbWlzZTx7XG4gICAgc3VjY2VzczogYm9vbGVhbjtcbiAgICBzYWZlTmFtZT86IHN0cmluZztcbiAgICBlcnJvcj86IFN1Ym1pdFRvUG9ydGZvbGlvUmVzdWx0O1xuICB9PiB7XG4gICAgLy8gTm9ybWFsaXplIHVzZXIgaW5wdXQgdG8gcHJldmVudCBVbmljb2RlIGF0dGFja3MgKERNQ1AtU0VDLTAwNClcbiAgICBjb25zdCBub3JtYWxpemVkTmFtZSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHBhcmFtcy5uYW1lKTtcbiAgICBpZiAoIW5vcm1hbGl6ZWROYW1lLmlzVmFsaWQpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1VOSUNPREVfVkFMSURBVElPTl9FUlJPUicsXG4gICAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgICAgc291cmNlOiAnU3VibWl0VG9Qb3J0Zm9saW9Ub29sLmV4ZWN1dGUnLFxuICAgICAgICBkZXRhaWxzOiBgSW52YWxpZCBVbmljb2RlIGluIGVsZW1lbnQgbmFtZTogJHtub3JtYWxpemVkTmFtZS5kZXRlY3RlZElzc3Vlcz8uWzBdIHx8ICd1bmtub3duIGVycm9yJ31gXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBlcnJvcjoge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIGNoYXJhY3RlcnMgaW4gZWxlbWVudCBuYW1lOiAke25vcm1hbGl6ZWROYW1lLmRldGVjdGVkSXNzdWVzPy5bMF0gfHwgJ3Vua25vd24gZXJyb3InfWAsXG4gICAgICAgICAgZXJyb3I6ICdJTlZBTElEX0lOUFVUJ1xuICAgICAgICB9XG4gICAgICB9O1xuICAgIH1cbiAgICBcbiAgICByZXR1cm4ge1xuICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgIHNhZmVOYW1lOiBub3JtYWxpemVkTmFtZS5ub3JtYWxpemVkQ29udGVudFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHRoZSB1c2VyIGlzIGF1dGhlbnRpY2F0ZWQgd2l0aCBHaXRIdWJcbiAgICogQHJldHVybnMgQXV0aGVudGljYXRpb24gY2hlY2sgcmVzdWx0IHdpdGggc3RhdHVzIG9yIGVycm9yIHJlc3BvbnNlXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGNoZWNrQXV0aGVudGljYXRpb24oKTogUHJvbWlzZTx7XG4gICAgc3VjY2VzczogYm9vbGVhbjtcbiAgICBhdXRoU3RhdHVzPzogYW55O1xuICAgIGVycm9yPzogU3VibWl0VG9Qb3J0Zm9saW9SZXN1bHQ7XG4gIH0+IHtcbiAgICBjb25zdCBhdXRoU3RhdHVzID0gYXdhaXQgdGhpcy5hdXRoTWFuYWdlci5nZXRBdXRoU3RhdHVzKCk7XG4gICAgaWYgKCFhdXRoU3RhdHVzLmlzQXV0aGVudGljYXRlZCkge1xuICAgICAgLy8gTG9nIGF1dGhlbnRpY2F0aW9uIHJlcXVpcmVkICh1c2luZyBleGlzdGluZyBldmVudCB0eXBlKVxuICAgICAgbG9nZ2VyLndhcm4oJ1VzZXIgYXR0ZW1wdGVkIHBvcnRmb2xpbyBzdWJtaXNzaW9uIHdpdGhvdXQgYXV0aGVudGljYXRpb24nKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBlcnJvcjoge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIG1lc3NhZ2U6ICdOb3QgYXV0aGVudGljYXRlZC4gUGxlYXNlIGF1dGhlbnRpY2F0ZSBmaXJzdCB1c2luZyB0aGUgR2l0SHViIE9BdXRoIGZsb3cuXFxuXFxuJyArXG4gICAgICAgICAgICAgICAgICAgJ1Zpc2l0OiBodHRwczovL2RvY3MuYW50aHJvcGljLmNvbS9lbi9kb2NzL2NsYXVkZS1jb2RlL29hdXRoLXNldHVwXFxuJyArXG4gICAgICAgICAgICAgICAgICAgJ09yIHJ1bjogZ2ggYXV0aCBsb2dpbiAtLXdlYicsXG4gICAgICAgICAgZXJyb3I6ICdOT1RfQVVUSEVOVElDQVRFRCdcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgIGF1dGhTdGF0dXNcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIERpc2NvdmVycyBjb250ZW50IGxvY2FsbHkgd2l0aCBzbWFydCB0eXBlIGRldGVjdGlvblxuICAgKiBAcGFyYW0gc2FmZU5hbWUgVGhlIG5vcm1hbGl6ZWQgbmFtZSB0byBzZWFyY2ggZm9yXG4gICAqIEBwYXJhbSBleHBsaWNpdFR5cGUgT3B0aW9uYWwgZXhwbGljaXQgZWxlbWVudCB0eXBlIHByb3ZpZGVkIGJ5IHVzZXJcbiAgICogQHBhcmFtIG9yaWdpbmFsTmFtZSBPcmlnaW5hbCB1c2VyLXByb3ZpZGVkIG5hbWUgZm9yIGVycm9yIG1lc3NhZ2VzXG4gICAqIEByZXR1cm5zIENvbnRlbnQgZGlzY292ZXJ5IHJlc3VsdCB3aXRoIGVsZW1lbnQgdHlwZSBhbmQgcGF0aCBvciBlcnJvciByZXNwb25zZVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBkaXNjb3ZlckNvbnRlbnRXaXRoVHlwZURldGVjdGlvbihcbiAgICBzYWZlTmFtZTogc3RyaW5nLCBcbiAgICBleHBsaWNpdFR5cGU/OiBFbGVtZW50VHlwZSwgXG4gICAgb3JpZ2luYWxOYW1lPzogc3RyaW5nXG4gICk6IFByb21pc2U8e1xuICAgIHN1Y2Nlc3M6IGJvb2xlYW47XG4gICAgZWxlbWVudFR5cGU/OiBFbGVtZW50VHlwZTtcbiAgICBsb2NhbFBhdGg/OiBzdHJpbmc7XG4gICAgZXJyb3I/OiBTdWJtaXRUb1BvcnRmb2xpb1Jlc3VsdDtcbiAgfT4ge1xuICAgIGxldCBlbGVtZW50VHlwZSA9IGV4cGxpY2l0VHlwZTtcbiAgICBsZXQgbG9jYWxQYXRoOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgICBcbiAgICBpZiAoZWxlbWVudFR5cGUpIHtcbiAgICAgIC8vIFR5cGUgZXhwbGljaXRseSBwcm92aWRlZCAtIHNlYXJjaCBpbiB0aGF0IHNwZWNpZmljIGRpcmVjdG9yeSBvbmx5XG4gICAgICBsb2NhbFBhdGggPSBhd2FpdCB0aGlzLmZpbmRMb2NhbENvbnRlbnQoc2FmZU5hbWUsIGVsZW1lbnRUeXBlKTtcbiAgICAgIGlmICghbG9jYWxQYXRoKSB7XG4gICAgICAgIC8vIFVYIElNUFJPVkVNRU5UOiBQcm92aWRlIGhlbHBmdWwgc3VnZ2VzdGlvbnMgZm9yIGZpbmRpbmcgY29udGVudFxuICAgICAgICBjb25zdCBwb3J0Zm9saW9NYW5hZ2VyID0gUG9ydGZvbGlvTWFuYWdlci5nZXRJbnN0YW5jZSgpO1xuICAgICAgICBjb25zdCBlbGVtZW50RGlyID0gcG9ydGZvbGlvTWFuYWdlci5nZXRFbGVtZW50RGlyKGVsZW1lbnRUeXBlKTtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZTogYENvdWxkIG5vdCBmaW5kICR7ZWxlbWVudFR5cGV9IG5hbWVkIFwiJHtvcmlnaW5hbE5hbWUgfHwgc2FmZU5hbWV9XCIgaW4gbG9jYWwgcG9ydGZvbGlvLlxcblxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBgKipTZWFyY2hlZCBpbioqOiAke2VsZW1lbnREaXJ9XFxuXFxuYCArXG4gICAgICAgICAgICAgICAgICAgIGAqKlRyb3VibGVzaG9vdGluZyBUaXBzKio6XFxuYCArXG4gICAgICAgICAgICAgICAgICAgIGDigKIgQ2hlY2sgaWYgdGhlIGZpbGUgZXhpc3RzIHVzaW5nIHlvdXIgZmlsZSBleHBsb3JlclxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBg4oCiIFRyeSB1c2luZyB0aGUgZXhhY3QgZmlsZW5hbWUgKHdpdGhvdXQgZXh0ZW5zaW9uKVxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBg4oCiIFVzZSBcXGBsaXN0X3BvcnRmb2xpb1xcYCB0byBzZWUgYWxsIGF2YWlsYWJsZSAke2VsZW1lbnRUeXBlfVxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBg4oCiIElmIHVuc3VyZSBvZiB0aGUgdHlwZSwgb21pdCAtLXR5cGUgYW5kIGxldCB0aGUgc3lzdGVtIGRldGVjdCBpdFxcblxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBgKipDb21tb24gbmFtZSBmb3JtYXRzIHRoYXQgd29yayoqOlxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBg4oCiIFwibXktZWxlbWVudFwiIChrZWJhYi1jYXNlKVxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBg4oCiIFwiTXkgRWxlbWVudFwiICh3aXRoIHNwYWNlcylcXG5gICtcbiAgICAgICAgICAgICAgICAgICAgYOKAoiBcIk15RWxlbWVudFwiIChQYXNjYWxDYXNlKVxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBg4oCiIFBhcnRpYWwgbWF0Y2hlcyBhcmUgc3VwcG9ydGVkYCxcbiAgICAgICAgICAgIGVycm9yOiAnQ09OVEVOVF9OT1RfRk9VTkQnXG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDUklUSUNBTCBGSVg6IE5vIHR5cGUgcHJvdmlkZWQgLSBpbXBsZW1lbnQgc21hcnQgZGV0ZWN0aW9uIGFjcm9zcyBBTEwgZWxlbWVudCB0eXBlc1xuICAgICAgLy8gVGhpcyBwcmV2ZW50cyB0aGUgcHJldmlvdXMgaGFyZGNvZGVkIGRlZmF1bHQgdG8gUEVSU09OQSBhbmQgZW5hYmxlcyBwcm9wZXIgdHlwZSBkZXRlY3Rpb25cbiAgICAgIGNvbnN0IGRldGVjdGlvblJlc3VsdCA9IGF3YWl0IHRoaXMuZGV0ZWN0RWxlbWVudFR5cGUoc2FmZU5hbWUpO1xuICAgICAgXG4gICAgICBpZiAoIWRldGVjdGlvblJlc3VsdC5mb3VuZCkge1xuICAgICAgICAvLyBVWCBJTVBST1ZFTUVOVDogRW5oYW5jZWQgZ3VpZGFuY2Ugd2l0aCBzcGVjaWZpYyBzdWdnZXN0aW9uc1xuICAgICAgICBjb25zdCBhdmFpbGFibGVUeXBlcyA9IE9iamVjdC52YWx1ZXMoRWxlbWVudFR5cGUpLmpvaW4oJywgJyk7XG4gICAgICAgIFxuICAgICAgICAvLyBHZXQgc3VnZ2VzdGlvbnMgZm9yIHNpbWlsYXIgbmFtZXNcbiAgICAgICAgY29uc3Qgc3VnZ2VzdGlvbnMgPSBhd2FpdCB0aGlzLmdlbmVyYXRlTmFtZVN1Z2dlc3Rpb25zKHNhZmVOYW1lKTtcbiAgICAgICAgXG4gICAgICAgIGxldCBtZXNzYWdlID0gYENvbnRlbnQgXCIke29yaWdpbmFsTmFtZSB8fCBzYWZlTmFtZX1cIiBub3QgZm91bmQgaW4gcG9ydGZvbGlvLlxcblxcbmA7XG4gICAgICAgIG1lc3NhZ2UgKz0gYPCflI0gKipTZWFyY2hlZCBpbiBhbGwgZWxlbWVudCB0eXBlcyoqOiAke2F2YWlsYWJsZVR5cGVzfVxcblxcbmA7XG4gICAgICAgIFxuICAgICAgICBpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCkge1xuICAgICAgICAgIG1lc3NhZ2UgKz0gYPCfkqEgKipEaWQgeW91IG1lYW4gb25lIG9mIHRoZXNlPyoqXFxuYDtcbiAgICAgICAgICBmb3IgKGNvbnN0IHN1Z2dlc3Rpb24gb2Ygc3VnZ2VzdGlvbnMuc2xpY2UoMCwgU0VBUkNIX0NPTkZJRy5NQVhfU1VHR0VTVElPTlMpKSB7XG4gICAgICAgICAgICBtZXNzYWdlICs9IGAgIOKAoiBcIiR7c3VnZ2VzdGlvbi5uYW1lfVwiICgke3N1Z2dlc3Rpb24udHlwZX0pXFxuYDtcbiAgICAgICAgICB9XG4gICAgICAgICAgbWVzc2FnZSArPSBgXFxuYDtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgbWVzc2FnZSArPSBg8J+boO+4jyAqKlRyb3VibGVzaG9vdGluZyBTdGVwcyoqOlxcbmA7XG4gICAgICAgIG1lc3NhZ2UgKz0gYDEuIPCfk50gVXNlIFxcYGxpc3RfcG9ydGZvbGlvXFxgIHRvIHNlZSBhbGwgYXZhaWxhYmxlIGNvbnRlbnRcXG5gO1xuICAgICAgICBtZXNzYWdlICs9IGAyLiDwn5SNIENoZWNrIGV4YWN0IHNwZWxsaW5nIGFuZCB0cnkgdmFyaWF0aW9uczpcXG5gO1xuICAgICAgICBtZXNzYWdlICs9IGAgICDigKIgXCIkeyhvcmlnaW5hbE5hbWUgfHwgc2FmZU5hbWUpLnRvTG93ZXJDYXNlKCl9XCIgKGxvd2VyY2FzZSlcXG5gO1xuICAgICAgICBtZXNzYWdlICs9IGAgICDigKIgXCIkeyhvcmlnaW5hbE5hbWUgfHwgc2FmZU5hbWUpLnJlcGxhY2UoL1teYS16MC05XS9naSwgJy0nKS50b0xvd2VyQ2FzZSgpfVwiIChub3JtYWxpemVkKVxcbmA7XG4gICAgICAgIGlmICgob3JpZ2luYWxOYW1lIHx8IHNhZmVOYW1lKS5pbmNsdWRlcygnLicpKSB7XG4gICAgICAgICAgbWVzc2FnZSArPSBgICAg4oCiIFwiJHsob3JpZ2luYWxOYW1lIHx8IHNhZmVOYW1lKS5yZXBsYWNlKC9cXC4vZywgJycpfVwiIChubyBkb3RzKVxcbmA7XG4gICAgICAgIH1cbiAgICAgICAgbWVzc2FnZSArPSBgMy4g8J+OryBTcGVjaWZ5IGVsZW1lbnQgdHlwZTogXFxgc3VibWl0X2NvbnRlbnQgXCIke29yaWdpbmFsTmFtZSB8fCBzYWZlTmFtZX1cIiAtLXR5cGU9cGVyc29uYXNcXGBcXG5gO1xuICAgICAgICBtZXNzYWdlICs9IGA0LiDwn5OBIENoZWNrIGlmIGZpbGUgZXhpc3RzIGluIHBvcnRmb2xpbyBkaXJlY3Rvcmllc1xcblxcbmA7XG4gICAgICAgIG1lc3NhZ2UgKz0gYPCfk50gKipUaXAqKjogVGhlIHN5c3RlbSBzZWFyY2hlcyBmaWxlbmFtZXMgQU5EIG1ldGFkYXRhIG5hbWVzIHdpdGggZnV6enkgbWF0Y2hpbmcuYDtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZSxcbiAgICAgICAgICAgIGVycm9yOiAnQ09OVEVOVF9OT1RfRk9VTkQnXG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICBpZiAoZGV0ZWN0aW9uUmVzdWx0Lm1hdGNoZXMubGVuZ3RoID4gMSkge1xuICAgICAgICAvLyBNdWx0aXBsZSBtYXRjaGVzIGZvdW5kIC0gYXNrIHVzZXIgdG8gc3BlY2lmeSB0eXBlXG4gICAgICAgIGNvbnN0IG1hdGNoRGV0YWlscyA9IGRldGVjdGlvblJlc3VsdC5tYXRjaGVzLm1hcChtID0+IGAtICR7bS50eXBlfTogJHttLnBhdGh9YCkuam9pbignXFxuJyk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZTogYENvbnRlbnQgXCIke29yaWdpbmFsTmFtZSB8fCBzYWZlTmFtZX1cIiBmb3VuZCBpbiBtdWx0aXBsZSBlbGVtZW50IHR5cGVzOlxcblxcbiR7bWF0Y2hEZXRhaWxzfVxcblxcbmAgK1xuICAgICAgICAgICAgICAgICAgICBgUGxlYXNlIHNwZWNpZnkgdGhlIGVsZW1lbnQgdHlwZSB1c2luZyB0aGUgLS10eXBlIHBhcmFtZXRlciB0byBhdm9pZCBhbWJpZ3VpdHkuYCxcbiAgICAgICAgICAgIGVycm9yOiAnTVVMVElQTEVfTUFUQ0hFU19GT1VORCdcbiAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFNpbmdsZSBtYXRjaCBmb3VuZCAtIHVzZSBpdFxuICAgICAgY29uc3QgbWF0Y2ggPSBkZXRlY3Rpb25SZXN1bHQubWF0Y2hlc1swXTtcbiAgICAgIGVsZW1lbnRUeXBlID0gbWF0Y2gudHlwZTtcbiAgICAgIGxvY2FsUGF0aCA9IG1hdGNoLnBhdGg7XG4gICAgICBcbiAgICAgIGxvZ2dlci5pbmZvKGBTbWFydCBkZXRlY3Rpb246IEZvdW5kIFwiJHtzYWZlTmFtZX1cIiBhcyAke2VsZW1lbnRUeXBlfWAsIHsgXG4gICAgICAgIG5hbWU6IHNhZmVOYW1lLFxuICAgICAgICBkZXRlY3RlZFR5cGU6IGVsZW1lbnRUeXBlLFxuICAgICAgICBwYXRoOiBsb2NhbFBhdGhcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgZWxlbWVudFR5cGUsXG4gICAgICBsb2NhbFBhdGhcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlcyBmaWxlIHNpemUgYW5kIGNvbnRlbnQgc2VjdXJpdHkgYmVmb3JlIHByb2Nlc3NpbmdcbiAgICogQHBhcmFtIGxvY2FsUGF0aCBQYXRoIHRvIHRoZSBsb2NhbCBmaWxlIHRvIHZhbGlkYXRlXG4gICAqIEByZXR1cm5zIFZhbGlkYXRpb24gcmVzdWx0IHdpdGggY29udGVudCBvciBlcnJvciByZXNwb25zZVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2YWxpZGF0ZUZpbGVBbmRDb250ZW50KGxvY2FsUGF0aDogc3RyaW5nKTogUHJvbWlzZTx7XG4gICAgc3VjY2VzczogYm9vbGVhbjtcbiAgICBjb250ZW50Pzogc3RyaW5nO1xuICAgIGVycm9yPzogU3VibWl0VG9Qb3J0Zm9saW9SZXN1bHQ7XG4gIH0+IHtcbiAgICAvLyBTRUNVUklUWSBFTkhBTkNFTUVOVCAoVGFzayAjNyk6IFZhbGlkYXRlIGZpbGUgcGF0aCBiZWZvcmUgcHJvY2Vzc2luZ1xuICAgIGNvbnN0IHBhdGhWYWxpZGF0aW9uID0gYXdhaXQgdGhpcy52YWxpZGF0ZVBvcnRmb2xpb1BhdGgobG9jYWxQYXRoKTtcbiAgICBpZiAoIXBhdGhWYWxpZGF0aW9uLmlzVmFsaWQpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBlcnJvcjogcGF0aFZhbGlkYXRpb24uZXJyb3JcbiAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gVXNlIHRoZSB2YWxpZGF0ZWQgc2FmZSBwYXRoIGZvciBhbGwgc3Vic2VxdWVudCBvcGVyYXRpb25zXG4gICAgY29uc3Qgc2FmZVBhdGggPSBwYXRoVmFsaWRhdGlvbi5zYWZlUGF0aCE7XG5cbiAgICAvLyBWYWxpZGF0ZSBmaWxlIHNpemUgYmVmb3JlIHJlYWRpbmdcbiAgICBjb25zdCBzdGF0cyA9IGF3YWl0IGZzLnN0YXQoc2FmZVBhdGgpO1xuICAgIGlmIChzdGF0cy5zaXplID4gRklMRV9TSVpFX0xJTUlUUy5NQVhfRklMRV9TSVpFKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdSQVRFX0xJTUlUX0VYQ0VFREVEJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICBzb3VyY2U6ICdTdWJtaXRUb1BvcnRmb2xpb1Rvb2wuZXhlY3V0ZScsXG4gICAgICAgIGRldGFpbHM6IGBGaWxlIHNpemUgJHtzdGF0cy5zaXplfSBleGNlZWRzIGxpbWl0IG9mICR7RklMRV9TSVpFX0xJTUlUUy5NQVhfRklMRV9TSVpFfWBcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIGVycm9yOiB7XG4gICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgbWVzc2FnZTogYEZpbGUgc2l6ZSBleGNlZWRzICR7RklMRV9TSVpFX0xJTUlUUy5NQVhfRklMRV9TSVpFX01CfU1CIGxpbWl0YCxcbiAgICAgICAgICBlcnJvcjogJ0ZJTEVfVE9PX0xBUkdFJ1xuICAgICAgICB9XG4gICAgICB9O1xuICAgIH1cblxuICAgIC8vIFZhbGlkYXRlIGNvbnRlbnQgc2VjdXJpdHlcbiAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUoc2FmZVBhdGgsICd1dGYtOCcpO1xuICAgIGNvbnN0IHZhbGlkYXRpb25SZXN1bHQgPSBDb250ZW50VmFsaWRhdG9yLnZhbGlkYXRlQW5kU2FuaXRpemUoY29udGVudCk7XG5cbiAgICBpZiAoIXZhbGlkYXRpb25SZXN1bHQuaXNWYWxpZCAmJiB2YWxpZGF0aW9uUmVzdWx0LnNldmVyaXR5ID09PSAnY3JpdGljYWwnKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdDT05URU5UX0lOSkVDVElPTl9BVFRFTVBUJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdISUdIJyxcbiAgICAgICAgc291cmNlOiAnU3VibWl0VG9Qb3J0Zm9saW9Ub29sLmV4ZWN1dGUnLFxuICAgICAgICBkZXRhaWxzOiBgQ3JpdGljYWwgc2VjdXJpdHkgaXNzdWVzIGRldGVjdGVkOiAke3ZhbGlkYXRpb25SZXN1bHQuZGV0ZWN0ZWRQYXR0ZXJucz8uam9pbignLCAnKX1gXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBlcnJvcjoge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIG1lc3NhZ2U6IGBDb250ZW50IHZhbGlkYXRpb24gZmFpbGVkOiAke3ZhbGlkYXRpb25SZXN1bHQuZGV0ZWN0ZWRQYXR0ZXJucz8uam9pbignLCAnKX1gLFxuICAgICAgICAgIGVycm9yOiAnVkFMSURBVElPTl9GQUlMRUQnXG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICBjb250ZW50XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmVwYXJlcyBtZXRhZGF0YSBmb3IgdGhlIHBvcnRmb2xpbyBlbGVtZW50XG4gICAqIEBwYXJhbSBzYWZlTmFtZSBUaGUgbm9ybWFsaXplZCBuYW1lIG9mIHRoZSBlbGVtZW50XG4gICAqIEBwYXJhbSBlbGVtZW50VHlwZSBUaGUgdHlwZSBvZiB0aGUgZWxlbWVudFxuICAgKiBAcGFyYW0gYXV0aFN0YXR1cyBBdXRoZW50aWNhdGlvbiBzdGF0dXMgY29udGFpbmluZyB1c2VybmFtZVxuICAgKiBAcmV0dXJucyBNZXRhZGF0YSBvYmplY3QgZm9yIHRoZSBlbGVtZW50XG4gICAqL1xuICBwcml2YXRlIHByZXBhcmVFbGVtZW50TWV0YWRhdGEoXG4gICAgc2FmZU5hbWU6IHN0cmluZywgXG4gICAgZWxlbWVudFR5cGU6IEVsZW1lbnRUeXBlLCBcbiAgICBhdXRoU3RhdHVzOiBhbnlcbiAgKTogUG9ydGZvbGlvRWxlbWVudE1ldGFkYXRhIHtcbiAgICByZXR1cm4ge1xuICAgICAgbmFtZTogc2FmZU5hbWUsXG4gICAgICBkZXNjcmlwdGlvbjogYCR7ZWxlbWVudFR5cGV9IHN1Ym1pdHRlZCBmcm9tIGxvY2FsIHBvcnRmb2xpb2AsXG4gICAgICBhdXRob3I6IGF1dGhTdGF0dXMudXNlcm5hbWUgfHwgJ3Vua25vd24nLFxuICAgICAgY3JlYXRlZDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgdXBkYXRlZDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgdmVyc2lvbjogJzEuMC4wJ1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGVzIEdpdEh1YiB0b2tlbiBhbmQgY2hlY2tzIGZvciBleHBpcmF0aW9uIGJlZm9yZSB1c2FnZVxuICAgKiBTRUNVUklUWSBFTkhBTkNFTUVOVCAoVGFzayAjNSk6IFRva2VuIGV4cGlyYXRpb24gdmFsaWRhdGlvbiB0byBwcmV2ZW50IHN0YWxlIHRva2VuIHVzYWdlXG4gICAqIEBwYXJhbSB0b2tlbiBUaGUgR2l0SHViIHRva2VuIHRvIHZhbGlkYXRlXG4gICAqIEByZXR1cm5zIFZhbGlkYXRpb24gcmVzdWx0IHdpdGggc3RhdHVzIGFuZCBleHBpcmF0aW9uIGluZm9cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdmFsaWRhdGVUb2tlbkJlZm9yZVVzYWdlKHRva2VuOiBzdHJpbmcpOiBQcm9taXNlPHtcbiAgICBpc1ZhbGlkOiBib29sZWFuO1xuICAgIGlzTmVhckV4cGlyeT86IGJvb2xlYW47XG4gICAgZXJyb3I/OiBTdWJtaXRUb1BvcnRmb2xpb1Jlc3VsdDtcbiAgfT4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayB0b2tlbiBmb3JtYXQgZmlyc3QgKGJhc2ljIHZhbGlkYXRpb24pXG4gICAgICBpZiAoIVRva2VuTWFuYWdlci52YWxpZGF0ZVRva2VuRm9ybWF0KHRva2VuKSkge1xuICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgdHlwZTogJ1RPS0VOX1ZBTElEQVRJT05fRkFJTFVSRScsXG4gICAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC52YWxpZGF0ZVRva2VuQmVmb3JlVXNhZ2UnLFxuICAgICAgICAgIGRldGFpbHM6ICdUb2tlbiBoYXMgaW52YWxpZCBmb3JtYXQnXG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBpc1ZhbGlkOiBmYWxzZSxcbiAgICAgICAgICBlcnJvcjoge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBtZXNzYWdlOiAnSW52YWxpZCB0b2tlbiBmb3JtYXQuIFBsZWFzZSByZS1hdXRoZW50aWNhdGUuJyxcbiAgICAgICAgICAgIGVycm9yOiAnSU5WQUxJRF9UT0tFTl9GT1JNQVQnXG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICAvLyBWYWxpZGF0ZSB0b2tlbiB3aXRoIEdpdEh1YiBBUEkgdG8gY2hlY2sgZXhwaXJhdGlvbiBhbmQgcGVybWlzc2lvbnNcbiAgICAgIGNvbnN0IHZhbGlkYXRpb25SZXN1bHQgPSBhd2FpdCBUb2tlbk1hbmFnZXIudmFsaWRhdGVUb2tlblNjb3Blcyh0b2tlbiwge1xuICAgICAgICByZXF1aXJlZDogWydyZXBvJ10sXG4gICAgICAgIG9wdGlvbmFsOiBbJ3VzZXI6ZW1haWwnXVxuICAgICAgfSk7XG5cbiAgICAgIGlmICghdmFsaWRhdGlvblJlc3VsdC5pc1ZhbGlkKSB7XG4gICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICB0eXBlOiAnVE9LRU5fVkFMSURBVElPTl9GQUlMVVJFJyxcbiAgICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgICAgc291cmNlOiAnU3VibWl0VG9Qb3J0Zm9saW9Ub29sLnZhbGlkYXRlVG9rZW5CZWZvcmVVc2FnZScsXG4gICAgICAgICAgZGV0YWlsczogYFRva2VuIHZhbGlkYXRpb24gZmFpbGVkOiAke3ZhbGlkYXRpb25SZXN1bHQuZXJyb3J9YFxuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGlzVmFsaWQ6IGZhbHNlLFxuICAgICAgICAgIGVycm9yOiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdHaXRIdWIgdG9rZW4gaXMgaW52YWxpZCBvciBleHBpcmVkLiBQbGVhc2UgcmUtYXV0aGVudGljYXRlLicsXG4gICAgICAgICAgICBlcnJvcjogJ1RPS0VOX1ZBTElEQVRJT05fRkFJTEVEJ1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdG9rZW4gaXMgbmVhciBleHBpcmF0aW9uIChyYXRlIGxpbWl0IHJlc2V0IHRpbWUgY2FuIGluZGljYXRlIHRva2VuIGZyZXNobmVzcylcbiAgICAgIGxldCBpc05lYXJFeHBpcnkgPSBmYWxzZTtcbiAgICAgIGlmICh2YWxpZGF0aW9uUmVzdWx0LnJhdGVMaW1pdD8ucmVzZXRUaW1lKSB7XG4gICAgICAgIGNvbnN0IG5vdyA9IG5ldyBEYXRlKCk7XG4gICAgICAgIGNvbnN0IHRpbWVVbnRpbFJlc2V0ID0gdmFsaWRhdGlvblJlc3VsdC5yYXRlTGltaXQucmVzZXRUaW1lLmdldFRpbWUoKSAtIG5vdy5nZXRUaW1lKCk7XG4gICAgICAgIGNvbnN0IG9uZUhvdXIgPSA2MCAqIDYwICogMTAwMDtcbiAgICAgICAgXG4gICAgICAgIC8vIENvbnNpZGVyIHRva2VuIFwibmVhciBleHBpcnlcIiBpZiByYXRlIGxpbWl0IHJlc2V0IGlzIG1vcmUgdGhhbiAyMyBob3VycyBhd2F5XG4gICAgICAgIC8vIChHaXRIdWIgcmF0ZSBsaW1pdHMgcmVzZXQgZXZlcnkgaG91ciwgc28gdGhpcyBzdWdnZXN0cyB0b2tlbiBhZ2UpXG4gICAgICAgIGlmICh0aW1lVW50aWxSZXNldCA+IDIzICogb25lSG91cikge1xuICAgICAgICAgIGlzTmVhckV4cGlyeSA9IHRydWU7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oJ0dpdEh1YiB0b2tlbiBtYXkgYmUgbmVhciBleHBpcmF0aW9uJywge1xuICAgICAgICAgICAgdG9rZW5QcmVmaXg6IFRva2VuTWFuYWdlci5nZXRUb2tlblByZWZpeCh0b2tlbiksXG4gICAgICAgICAgICByYXRlTGltaXRSZXNldFRpbWU6IHZhbGlkYXRpb25SZXN1bHQucmF0ZUxpbWl0LnJlc2V0VGltZSxcbiAgICAgICAgICAgIHJlY29tbWVuZGF0aW9uOiAnQ29uc2lkZXIgcmUtYXV0aGVudGljYXRpbmcgZm9yIGxvbmcgb3BlcmF0aW9ucydcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBMb2cgc3VjY2Vzc2Z1bCB2YWxpZGF0aW9uXG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdUT0tFTl9WQUxJREFUSU9OX1NVQ0NFU1MnLFxuICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC52YWxpZGF0ZVRva2VuQmVmb3JlVXNhZ2UnLFxuICAgICAgICBkZXRhaWxzOiAnR2l0SHViIHRva2VuIHZhbGlkYXRlZCBzdWNjZXNzZnVsbHkgYmVmb3JlIHVzYWdlJyxcbiAgICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgICB0b2tlblR5cGU6IFRva2VuTWFuYWdlci5nZXRUb2tlblR5cGUodG9rZW4pLFxuICAgICAgICAgIHNjb3BlczogdmFsaWRhdGlvblJlc3VsdC5zY29wZXMsXG4gICAgICAgICAgcmF0ZUxpbWl0UmVtYWluaW5nOiB2YWxpZGF0aW9uUmVzdWx0LnJhdGVMaW1pdD8ucmVtYWluaW5nLFxuICAgICAgICAgIGlzTmVhckV4cGlyeVxuICAgICAgICB9XG4gICAgICB9KTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaXNWYWxpZDogdHJ1ZSxcbiAgICAgICAgaXNOZWFyRXhwaXJ5XG4gICAgICB9O1xuXG4gICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgLy8gSGFuZGxlIHJhdGUgbGltaXQgZXhjZWVkZWQgc3BlY2lmaWNhbGx5XG4gICAgICBpZiAoZXJyb3I/LmNvZGUgPT09ICdSQVRFX0xJTUlUX0VYQ0VFREVEJykge1xuICAgICAgICBsb2dnZXIud2FybignVG9rZW4gdmFsaWRhdGlvbiByYXRlIGxpbWl0ZWQsIGFsbG93aW5nIG9wZXJhdGlvbiB0byBwcm9jZWVkIHdpdGggY2FjaGVkIHN0YXR1cycpO1xuICAgICAgICByZXR1cm4geyBpc1ZhbGlkOiB0cnVlIH07IC8vIEFsbG93IHRvIHByb2NlZWQgaWYgcmF0ZSBsaW1pdGVkLCBhcyBiYXNpYyBmb3JtYXQgY2hlY2sgcGFzc2VkXG4gICAgICB9XG5cbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1RPS0VOX1ZBTElEQVRJT05fRkFJTFVSRScsXG4gICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC52YWxpZGF0ZVRva2VuQmVmb3JlVXNhZ2UnLFxuICAgICAgICBkZXRhaWxzOiBgVG9rZW4gdmFsaWRhdGlvbiBlcnJvcjogJHtlcnJvci5tZXNzYWdlIHx8ICd1bmtub3duIGVycm9yJ31gXG4gICAgICB9KTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaXNWYWxpZDogZmFsc2UsXG4gICAgICAgIGVycm9yOiB7XG4gICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgbWVzc2FnZTogJ1VuYWJsZSB0byB2YWxpZGF0ZSBHaXRIdWIgdG9rZW4uIFBsZWFzZSBjaGVjayB5b3VyIGNvbm5lY3Rpb24gYW5kIHRyeSBhZ2Fpbi4nLFxuICAgICAgICAgIGVycm9yOiAnVE9LRU5fVkFMSURBVElPTl9FUlJPUidcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRW5oYW5jZWQgcGF0aCB2YWxpZGF0aW9uIGZvciBwb3J0Zm9saW8gb3BlcmF0aW9ucyB3aXRoIGNvbXByZWhlbnNpdmUgc2VjdXJpdHkgY2hlY2tzXG4gICAqIFNFQ1VSSVRZIEVOSEFOQ0VNRU5UIChUYXNrICM3KTogQWRkaXRpb25hbCB2YWxpZGF0aW9uIGZvciBzcGVjaWFsIGNoYXJhY3RlcnMgYW5kIG1hbGljaW91cyBwYXR0ZXJuc1xuICAgKiBAcGFyYW0gZmlsZVBhdGggVGhlIGZpbGUgcGF0aCB0byB2YWxpZGF0ZVxuICAgKiBAcmV0dXJucyBWYWxpZGF0aW9uIHJlc3VsdCB3aXRoIHNlY3VyZSBwYXRoIG9yIGVycm9yIHJlc3BvbnNlXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHZhbGlkYXRlUG9ydGZvbGlvUGF0aChmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTx7XG4gICAgaXNWYWxpZDogYm9vbGVhbjtcbiAgICBzYWZlUGF0aD86IHN0cmluZztcbiAgICBlcnJvcj86IFN1Ym1pdFRvUG9ydGZvbGlvUmVzdWx0O1xuICB9PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIEJhc2ljIG51bGwvdW5kZWZpbmVkIGNoZWNrXG4gICAgICBpZiAoIWZpbGVQYXRoIHx8IHR5cGVvZiBmaWxlUGF0aCAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdQQVRIX1RSQVZFUlNBTF9BVFRFTVBUJyxcbiAgICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgICAgc291cmNlOiAnU3VibWl0VG9Qb3J0Zm9saW9Ub29sLnZhbGlkYXRlUG9ydGZvbGlvUGF0aCcsXG4gICAgICAgICAgZGV0YWlsczogJ0ludmFsaWQgcGF0aCBwcm92aWRlZCAtIG51bGwsIHVuZGVmaW5lZCwgb3Igbm9uLXN0cmluZydcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGlzVmFsaWQ6IGZhbHNlLFxuICAgICAgICAgIGVycm9yOiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdJbnZhbGlkIGZpbGUgcGF0aCBwcm92aWRlZCcsXG4gICAgICAgICAgICBlcnJvcjogJ0lOVkFMSURfUEFUSCdcbiAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGZvciBzdXNwaWNpb3VzIHBhdHRlcm5zIHRoYXQgY291bGQgaW5kaWNhdGUgcGF0aCB0cmF2ZXJzYWwgb3IgaW5qZWN0aW9uXG4gICAgICBjb25zdCBzdXNwaWNpb3VzUGF0dGVybnMgPSBbXG4gICAgICAgIC9cXC5cXC4vLCAgICAgICAgICAgICAgICAgICAgLy8gUGF0aCB0cmF2ZXJzYWxcbiAgICAgICAgL1xcL1xcLlxcLi8sICAgICAgICAgICAgICAgICAgLy8gVW5peCBwYXRoIHRyYXZlcnNhbFxuICAgICAgICAvXFxcXFxcLlxcLi8sICAgICAgICAgICAgICAgICAgLy8gV2luZG93cyBwYXRoIHRyYXZlcnNhbFxuICAgICAgICAvXFx4MDAvLCAgICAgICAgICAgICAgICAgICAgLy8gTnVsbCBieXRlc1xuICAgICAgICAvW1xceDAxLVxceDFmXFx4N2YtXFx4OWZdLywgICAgLy8gQ29udHJvbCBjaGFyYWN0ZXJzXG4gICAgICAgIC9bPD46XCJ8PypdLywgICAgICAgICAgICAgICAvLyBJbnZhbGlkIGZpbGVuYW1lIGNoYXJhY3RlcnMgb24gV2luZG93c1xuICAgICAgICAvXihDT058UFJOfEFVWHxOVUx8Q09NWzEtOV18TFBUWzEtOV0pJC9pLCAvLyBSZXNlcnZlZCBXaW5kb3dzIG5hbWVzXG4gICAgICAgIC9eXFwuLywgICAgICAgICAgICAgICAgICAgICAvLyBIaWRkZW4gZmlsZXMgKHN0YXJ0aW5nIHdpdGggZG90KVxuICAgICAgICAvXFxzKyQvLCAgICAgICAgICAgICAgICAgICAgLy8gVHJhaWxpbmcgd2hpdGVzcGFjZVxuICAgICAgICAvXltcXHNdKiQvLCAgICAgICAgICAgICAgICAgLy8gT25seSB3aGl0ZXNwYWNlXG4gICAgICAgIC8lWzAtOWEtZkEtRl17Mn0vLCAgICAgICAgIC8vIFVSTCBlbmNvZGluZyAocG90ZW50aWFsIGJ5cGFzcyBhdHRlbXB0KVxuICAgICAgICAvXFxcXHhbMC05YS1mQS1GXXsyfS8sICAgICAgIC8vIEhleCBlbmNvZGluZ1xuICAgICAgICAvXFwkXFx7LipcXH0vLCAgICAgICAgICAgICAgICAvLyBUZW1wbGF0ZSBsaXRlcmFsIGluamVjdGlvblxuICAgICAgICAvYC4qYC8sICAgICAgICAgICAgICAgICAgICAvLyBCYWNrdGljayBpbmplY3Rpb25cbiAgICAgICAgL1tcXFxcXFwvXXsyLH0vICAgICAgICAgICAgICAgLy8gTXVsdGlwbGUgY29uc2VjdXRpdmUgc2xhc2hlc1xuICAgICAgXTtcblxuICAgICAgZm9yIChjb25zdCBwYXR0ZXJuIG9mIHN1c3BpY2lvdXNQYXR0ZXJucykge1xuICAgICAgICBpZiAocGF0dGVybi50ZXN0KGZpbGVQYXRoKSkge1xuICAgICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICAgIHR5cGU6ICdQQVRIX1RSQVZFUlNBTF9BVFRFTVBUJyxcbiAgICAgICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgICAgICBzb3VyY2U6ICdTdWJtaXRUb1BvcnRmb2xpb1Rvb2wudmFsaWRhdGVQb3J0Zm9saW9QYXRoJyxcbiAgICAgICAgICAgIGRldGFpbHM6IGBTdXNwaWNpb3VzIHBhdHRlcm4gZGV0ZWN0ZWQgaW4gZmlsZSBwYXRoOiAke3BhdHRlcm4uc291cmNlfWAsXG4gICAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgICBwYXRoTGVuZ3RoOiBmaWxlUGF0aC5sZW5ndGgsXG4gICAgICAgICAgICAgIHBhdHRlcm46IHBhdHRlcm4uc291cmNlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGlzVmFsaWQ6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6ICdGaWxlIHBhdGggY29udGFpbnMgaW52YWxpZCBvciBzdXNwaWNpb3VzIGNoYXJhY3RlcnMnLFxuICAgICAgICAgICAgICBlcnJvcjogJ1NVU1BJQ0lPVVNfUEFUSF9QQVRURVJOJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgcGF0aCBsZW5ndGggKHByZXZlbnQgYnVmZmVyIG92ZXJmbG93IGF0dGVtcHRzKVxuICAgICAgY29uc3QgTUFYX1BBVEhfTEVOR1RIID0gcHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJyA/IDI2MCA6IDQwOTY7XG4gICAgICBpZiAoZmlsZVBhdGgubGVuZ3RoID4gTUFYX1BBVEhfTEVOR1RIKSB7XG4gICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICB0eXBlOiAnUEFUSF9UUkFWRVJTQUxfQVRURU1QVCcsXG4gICAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC52YWxpZGF0ZVBvcnRmb2xpb1BhdGgnLFxuICAgICAgICAgIGRldGFpbHM6IGBGaWxlIHBhdGggZXhjZWVkcyBtYXhpbXVtIGxlbmd0aDogJHtmaWxlUGF0aC5sZW5ndGh9ID4gJHtNQVhfUEFUSF9MRU5HVEh9YFxuICAgICAgICB9KTtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgaXNWYWxpZDogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZTogJ0ZpbGUgcGF0aCBpcyB0b28gbG9uZycsXG4gICAgICAgICAgICBlcnJvcjogJ1BBVEhfVE9PX0xPTkcnXG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICAvLyBOb3JtYWxpemUgcGF0aCB0byByZXNvbHZlIGFueSByZWxhdGl2ZSBjb21wb25lbnRzIHNhZmVseVxuICAgICAgbGV0IG5vcm1hbGl6ZWRQYXRoOiBzdHJpbmc7XG4gICAgICB0cnkge1xuICAgICAgICAvLyBSZW1vdmUgbnVsbCBieXRlcyBhbmQgbm9ybWFsaXplXG4gICAgICAgIGNvbnN0IGNsZWFuUGF0aCA9IGZpbGVQYXRoLnJlcGxhY2UoL1xceDAwL2csICcnKTtcbiAgICAgICAgbm9ybWFsaXplZFBhdGggPSBwYXRoLm5vcm1hbGl6ZShjbGVhblBhdGgpO1xuICAgICAgICBcbiAgICAgICAgLy8gRW5zdXJlIHRoZSBub3JtYWxpemVkIHBhdGggZG9lc24ndCBlc2NhcGUgdGhlIGludGVuZGVkIGRpcmVjdG9yeVxuICAgICAgICBpZiAobm9ybWFsaXplZFBhdGguaW5jbHVkZXMoJy4uJykgfHwgbm9ybWFsaXplZFBhdGguc3RhcnRzV2l0aCgnLycpIHx8IFxuICAgICAgICAgICAgKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicgJiYgL15bYS16QS1aXTovLnRlc3Qobm9ybWFsaXplZFBhdGgpKSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGF0aCBub3JtYWxpemF0aW9uIHJlc3VsdGVkIGluIGRpcmVjdG9yeSB0cmF2ZXJzYWwnKTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdQQVRIX1RSQVZFUlNBTF9BVFRFTVBUJyxcbiAgICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC52YWxpZGF0ZVBvcnRmb2xpb1BhdGgnLFxuICAgICAgICAgIGRldGFpbHM6IGBQYXRoIG5vcm1hbGl6YXRpb24gZmFpbGVkOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ3Vua25vd24gZXJyb3InfWBcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGlzVmFsaWQ6IGZhbHNlLFxuICAgICAgICAgIGVycm9yOiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdGaWxlIHBhdGggY291bGQgbm90IGJlIHNhZmVseSBwcm9jZXNzZWQnLFxuICAgICAgICAgICAgZXJyb3I6ICdQQVRIX05PUk1BTElaQVRJT05fRkFJTEVEJ1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgLy8gVmFsaWRhdGUgZmlsZSBleHRlbnNpb24gKG9ubHkgYWxsb3cgc2FmZSBleHRlbnNpb25zIGZvciBwb3J0Zm9saW8gY29udGVudClcbiAgICAgIGNvbnN0IGFsbG93ZWRFeHRlbnNpb25zID0gWycubWQnLCAnLm1hcmtkb3duJywgJy50eHQnLCAnLnltbCcsICcueWFtbCcsICcuanNvbiddO1xuICAgICAgY29uc3QgZmlsZUV4dGVuc2lvbiA9IHBhdGguZXh0bmFtZShub3JtYWxpemVkUGF0aCkudG9Mb3dlckNhc2UoKTtcbiAgICAgIFxuICAgICAgaWYgKGZpbGVFeHRlbnNpb24gJiYgIWFsbG93ZWRFeHRlbnNpb25zLmluY2x1ZGVzKGZpbGVFeHRlbnNpb24pKSB7XG4gICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICB0eXBlOiAnQ09OVEVOVF9JTkpFQ1RJT05fQVRURU1QVCcsXG4gICAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC52YWxpZGF0ZVBvcnRmb2xpb1BhdGgnLFxuICAgICAgICAgIGRldGFpbHM6IGBEaXNhbGxvd2VkIGZpbGUgZXh0ZW5zaW9uOiAke2ZpbGVFeHRlbnNpb259YCxcbiAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgYWxsb3dlZEV4dGVuc2lvbnM6IGFsbG93ZWRFeHRlbnNpb25zLmpvaW4oJywgJylcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBpc1ZhbGlkOiBmYWxzZSxcbiAgICAgICAgICBlcnJvcjoge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBtZXNzYWdlOiBgRmlsZSBleHRlbnNpb24gJyR7ZmlsZUV4dGVuc2lvbn0nIGlzIG5vdCBhbGxvd2VkLiBBbGxvd2VkIGV4dGVuc2lvbnM6ICR7YWxsb3dlZEV4dGVuc2lvbnMuam9pbignLCAnKX1gLFxuICAgICAgICAgICAgZXJyb3I6ICdJTlZBTElEX0ZJTEVfRVhURU5TSU9OJ1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgLy8gVmFsaWRhdGUgZmlsZW5hbWUgY2hhcmFjdGVycyAob25seSBhbGxvdyBzYWZlIGNoYXJhY3RlcnMpXG4gICAgICBjb25zdCBiYXNlbmFtZSA9IHBhdGguYmFzZW5hbWUobm9ybWFsaXplZFBhdGgpO1xuICAgICAgY29uc3Qgc2FmZUZpbGVuYW1lUGF0dGVybiA9IC9eW2EtekEtWjAtOVxcLV8uXFxzKClbXFxde31dKyQvO1xuICAgICAgXG4gICAgICBpZiAoYmFzZW5hbWUgJiYgIXNhZmVGaWxlbmFtZVBhdHRlcm4udGVzdChiYXNlbmFtZSkpIHtcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdDT05URU5UX0lOSkVDVElPTl9BVFRFTVBUJyxcbiAgICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgICAgc291cmNlOiAnU3VibWl0VG9Qb3J0Zm9saW9Ub29sLnZhbGlkYXRlUG9ydGZvbGlvUGF0aCcsXG4gICAgICAgICAgZGV0YWlsczogJ0ZpbGVuYW1lIGNvbnRhaW5zIHBvdGVudGlhbGx5IGRhbmdlcm91cyBjaGFyYWN0ZXJzJyxcbiAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgZmlsZW5hbWU6IGJhc2VuYW1lLFxuICAgICAgICAgICAgYWxsb3dlZFBhdHRlcm46IHNhZmVGaWxlbmFtZVBhdHRlcm4uc291cmNlXG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgaXNWYWxpZDogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZTogJ0ZpbGVuYW1lIGNvbnRhaW5zIGludmFsaWQgY2hhcmFjdGVycy4gT25seSBsZXR0ZXJzLCBudW1iZXJzLCBzcGFjZXMsIGh5cGhlbnMsIHVuZGVyc2NvcmVzLCBkb3RzLCBhbmQgY29tbW9uIGJyYWNrZXRzIGFyZSBhbGxvd2VkLicsXG4gICAgICAgICAgICBlcnJvcjogJ0lOVkFMSURfRklMRU5BTUVfQ0hBUkFDVEVSUydcbiAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIC8vIExvZyBzdWNjZXNzZnVsIHZhbGlkYXRpb25cbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ0NPTlRFTlRfSU5KRUNUSU9OX0FUVEVNUFQnLFxuICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC52YWxpZGF0ZVBvcnRmb2xpb1BhdGgnLFxuICAgICAgICBkZXRhaWxzOiAnRmlsZSBwYXRoIHZhbGlkYXRpb24gc3VjY2Vzc2Z1bCcsXG4gICAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgICAgb3JpZ2luYWxQYXRoTGVuZ3RoOiBmaWxlUGF0aC5sZW5ndGgsXG4gICAgICAgICAgbm9ybWFsaXplZFBhdGhMZW5ndGg6IG5vcm1hbGl6ZWRQYXRoLmxlbmd0aCxcbiAgICAgICAgICBmaWxlRXh0ZW5zaW9uOiBmaWxlRXh0ZW5zaW9uIHx8ICdub25lJ1xuICAgICAgICB9XG4gICAgICB9KTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaXNWYWxpZDogdHJ1ZSxcbiAgICAgICAgc2FmZVBhdGg6IG5vcm1hbGl6ZWRQYXRoXG4gICAgICB9O1xuXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1BBVEhfVFJBVkVSU0FMX0FUVEVNUFQnLFxuICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICBzb3VyY2U6ICdTdWJtaXRUb1BvcnRmb2xpb1Rvb2wudmFsaWRhdGVQb3J0Zm9saW9QYXRoJyxcbiAgICAgICAgZGV0YWlsczogYFBhdGggdmFsaWRhdGlvbiBlcnJvcjogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICd1bmtub3duIGVycm9yJ31gXG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaXNWYWxpZDogZmFsc2UsXG4gICAgICAgIGVycm9yOiB7XG4gICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgbWVzc2FnZTogJ1VuYWJsZSB0byB2YWxpZGF0ZSBmaWxlIHBhdGguIFBsZWFzZSBjaGVjayB0aGUgZmlsZSBwYXRoIGFuZCB0cnkgYWdhaW4uJyxcbiAgICAgICAgICBlcnJvcjogJ1BBVEhfVkFMSURBVElPTl9FUlJPUidcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU21hcnQgdG9rZW4gbWFuYWdlbWVudCBmb3IgbG9uZyBvcGVyYXRpb25zIHdpdGggcmVmcmVzaC1saWtlIGNhcGFiaWxpdGllc1xuICAgKiBTRUNVUklUWSBFTkhBTkNFTUVOVCAoVGFzayAjMTQpOiBUb2tlbiByZWZyZXNoIGxvZ2ljIGZvciBsb25nIG9wZXJhdGlvbnNcbiAgICogXG4gICAqIE5vdGU6IEdpdEh1YiBPQXV0aCBkZXZpY2UgZmxvdyB0b2tlbnMgZG9uJ3QgaGF2ZSB0cmFkaXRpb25hbCByZWZyZXNoIHRva2VucyxcbiAgICogYnV0IHdlIGNhbiBpbXBsZW1lbnQgc21hcnQgdmFsaWRhdGlvbiBhbmQgZ3VpZGFuY2UgZm9yIGxvbmcgb3BlcmF0aW9uc1xuICAgKiBcbiAgICogQHBhcmFtIG9wZXJhdGlvblR5cGUgVHlwZSBvZiBvcGVyYXRpb24gYmVpbmcgcGVyZm9ybWVkXG4gICAqIEByZXR1cm5zIFRva2VuIG1hbmFnZW1lbnQgcmVzdWx0IHdpdGggcmVjb21tZW5kYXRpb25zXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIG1hbmFnZVRva2VuRm9yTG9uZ09wZXJhdGlvbihvcGVyYXRpb25UeXBlOiAncG9ydGZvbGlvX2NyZWF0aW9uJyB8ICdjb2xsZWN0aW9uX3N1Ym1pc3Npb24nIHwgJ2ZpbGVfdXBsb2FkJyk6IFByb21pc2U8e1xuICAgIGNhblByb2NlZWQ6IGJvb2xlYW47XG4gICAgdG9rZW4/OiBzdHJpbmc7XG4gICAgcmVmcmVzaFJlY29tbWVuZGVkPzogYm9vbGVhbjtcbiAgICBlcnJvcj86IFN1Ym1pdFRvUG9ydGZvbGlvUmVzdWx0O1xuICB9PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIEdldCBjdXJyZW50IHRva2VuXG4gICAgICBjb25zdCB0b2tlbiA9IGF3YWl0IFRva2VuTWFuYWdlci5nZXRHaXRIdWJUb2tlbkFzeW5jKCk7XG4gICAgICBpZiAoIXRva2VuKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgY2FuUHJvY2VlZDogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZTogJ05vIEdpdEh1YiB0b2tlbiBhdmFpbGFibGUuIFBsZWFzZSBhdXRoZW50aWNhdGUgZmlyc3QuJyxcbiAgICAgICAgICAgIGVycm9yOiAnTk9fVE9LRU4nXG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICAvLyBWYWxpZGF0ZSB0b2tlbiBmb3IgdGhlIHNwZWNpZmljIG9wZXJhdGlvblxuICAgICAgY29uc3QgdmFsaWRhdGlvbiA9IGF3YWl0IHRoaXMudmFsaWRhdGVUb2tlbkJlZm9yZVVzYWdlKHRva2VuKTtcbiAgICAgIGlmICghdmFsaWRhdGlvbi5pc1ZhbGlkKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgY2FuUHJvY2VlZDogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IHZhbGlkYXRpb24uZXJyb3JcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBpcyBhIGxvbmcgb3BlcmF0aW9uIHRoYXQgbWlnaHQgYmVuZWZpdCBmcm9tIGZyZXNoIGF1dGhlbnRpY2F0aW9uXG4gICAgICBjb25zdCBsb25nT3BlcmF0aW9ucyA9IFsncG9ydGZvbGlvX2NyZWF0aW9uJywgJ2NvbGxlY3Rpb25fc3VibWlzc2lvbiddO1xuICAgICAgY29uc3QgaXNMb25nT3BlcmF0aW9uID0gbG9uZ09wZXJhdGlvbnMuaW5jbHVkZXMob3BlcmF0aW9uVHlwZSk7XG5cbiAgICAgIC8vIEdldCB0b2tlbiB0eXBlIHRvIGRldGVybWluZSByZWZyZXNoIGNhcGFiaWxpdGllc1xuICAgICAgY29uc3QgdG9rZW5UeXBlID0gVG9rZW5NYW5hZ2VyLmdldFRva2VuVHlwZSh0b2tlbik7XG4gICAgICBsZXQgcmVmcmVzaFJlY29tbWVuZGVkID0gZmFsc2U7XG5cbiAgICAgIC8vIEZvciBsb25nIG9wZXJhdGlvbnMsIGNoZWNrIHRva2VuIGFnZSBhbmQgcmVjb21tZW5kIHJlZnJlc2ggaWYgbmVlZGVkXG4gICAgICBpZiAoaXNMb25nT3BlcmF0aW9uICYmIHZhbGlkYXRpb24uaXNOZWFyRXhwaXJ5KSB7XG4gICAgICAgIHJlZnJlc2hSZWNvbW1lbmRlZCA9IHRydWU7XG4gICAgICAgIFxuICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgdHlwZTogJ1RPS0VOX1ZBTElEQVRJT05fU1VDQ0VTUycsXG4gICAgICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgICAgIHNvdXJjZTogJ1N1Ym1pdFRvUG9ydGZvbGlvVG9vbC5tYW5hZ2VUb2tlbkZvckxvbmdPcGVyYXRpb24nLFxuICAgICAgICAgIGRldGFpbHM6ICdMb25nIG9wZXJhdGlvbiBkZXRlY3RlZCB3aXRoIGFnaW5nIHRva2VuIC0gcmVmcmVzaCByZWNvbW1lbmRlZCcsXG4gICAgICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgICAgIG9wZXJhdGlvblR5cGUsXG4gICAgICAgICAgICB0b2tlblR5cGUsXG4gICAgICAgICAgICByZWZyZXNoUmVjb21tZW5kZWQ6IHRydWVcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGxvZ2dlci53YXJuKCdMb25nIG9wZXJhdGlvbiB3aXRoIHBvdGVudGlhbGx5IGFnaW5nIHRva2VuIGRldGVjdGVkJywge1xuICAgICAgICAgIG9wZXJhdGlvblR5cGUsXG4gICAgICAgICAgdG9rZW5UeXBlLFxuICAgICAgICAgIHJlY29tbWVuZGF0aW9uOiAnQ29uc2lkZXIgcmUtYXV0aGVudGljYXRpbmcgaWYgb3BlcmF0aW9uIGZhaWxzJ1xuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgLy8gRm9yIE9BdXRoIHRva2VucyBpbiBsb25nIG9wZXJhdGlvbnMsIHdlIGNhbiBwcm92aWRlIGd1aWRhbmNlXG4gICAgICBpZiAodG9rZW5UeXBlID09PSAnT0F1dGggQWNjZXNzIFRva2VuJyAmJiBpc0xvbmdPcGVyYXRpb24pIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ09BdXRoIHRva2VuIGRldGVjdGVkIGZvciBsb25nIG9wZXJhdGlvbicsIHtcbiAgICAgICAgICBvcGVyYXRpb25UeXBlLFxuICAgICAgICAgIHRva2VuVHlwZSxcbiAgICAgICAgICBndWlkYW5jZTogJ09BdXRoIHRva2VucyBhcmUgdGltZS1saW1pdGVkLiBJZiBvcGVyYXRpb24gZmFpbHMsIHJlLWF1dGhlbnRpY2F0ZSB1c2luZyBzZXR1cF9naXRodWJfYXV0aCdcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIExvZyBzdWNjZXNzZnVsIHRva2VuIG1hbmFnZW1lbnRcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1RPS0VOX1ZBTElEQVRJT05fU1VDQ0VTUycsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnU3VibWl0VG9Qb3J0Zm9saW9Ub29sLm1hbmFnZVRva2VuRm9yTG9uZ09wZXJhdGlvbicsXG4gICAgICAgIGRldGFpbHM6ICdUb2tlbiBtYW5hZ2VtZW50IHN1Y2Nlc3NmdWwgZm9yIGxvbmcgb3BlcmF0aW9uJyxcbiAgICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgICBvcGVyYXRpb25UeXBlLFxuICAgICAgICAgIHRva2VuVHlwZSxcbiAgICAgICAgICBpc0xvbmdPcGVyYXRpb24sXG4gICAgICAgICAgcmVmcmVzaFJlY29tbWVuZGVkXG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBjYW5Qcm9jZWVkOiB0cnVlLFxuICAgICAgICB0b2tlbixcbiAgICAgICAgcmVmcmVzaFJlY29tbWVuZGVkXG4gICAgICB9O1xuXG4gICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnVE9LRU5fVkFMSURBVElPTl9GQUlMVVJFJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICBzb3VyY2U6ICdTdWJtaXRUb1BvcnRmb2xpb1Rvb2wubWFuYWdlVG9rZW5Gb3JMb25nT3BlcmF0aW9uJyxcbiAgICAgICAgZGV0YWlsczogYFRva2VuIG1hbmFnZW1lbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZSB8fCAndW5rbm93biBlcnJvcid9YFxuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNhblByb2NlZWQ6IGZhbHNlLFxuICAgICAgICBlcnJvcjoge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIG1lc3NhZ2U6ICdVbmFibGUgdG8gbWFuYWdlIHRva2VuIGZvciBvcGVyYXRpb24uIFBsZWFzZSBjaGVjayB5b3VyIGF1dGhlbnRpY2F0aW9uIGFuZCB0cnkgYWdhaW4uJyxcbiAgICAgICAgICBlcnJvcjogJ1RPS0VOX01BTkFHRU1FTlRfRVJST1InXG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFByb3ZpZGVzIHVzZXIgZ3VpZGFuY2UgZm9yIHRva2VuIHJlZnJlc2ggd2hlbiBvcGVyYXRpb25zIGZhaWwgZHVlIHRvIHRva2VuIGlzc3Vlc1xuICAgKiBTRUNVUklUWSBFTkhBTkNFTUVOVCAoVGFzayAjMTQpOiBVc2VyIGd1aWRhbmNlIGZvciBhdXRoZW50aWNhdGlvbiByZWZyZXNoXG4gICAqL1xuICBwcml2YXRlIGZvcm1hdFRva2VuUmVmcmVzaEd1aWRhbmNlKG9wZXJhdGlvblR5cGU6IHN0cmluZywgdG9rZW5UeXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGxldCBndWlkYW5jZSA9ICdcXG5cXG7wn5SEICoqVG9rZW4gUmVmcmVzaCBHdWlkYW5jZSoqOlxcbic7XG4gICAgXG4gICAgaWYgKHRva2VuVHlwZSA9PT0gJ09BdXRoIEFjY2VzcyBUb2tlbicpIHtcbiAgICAgIGd1aWRhbmNlICs9ICfigKIgWW91ciBPQXV0aCB0b2tlbiBtYXkgaGF2ZSBleHBpcmVkXFxuJztcbiAgICAgIGd1aWRhbmNlICs9ICfigKIgUnVuIGBzZXR1cF9naXRodWJfYXV0aGAgdG8gYXV0aGVudGljYXRlIGFnYWluXFxuJztcbiAgICAgIGd1aWRhbmNlICs9ICfigKIgVGhpcyB3aWxsIGdlbmVyYXRlIGEgZnJlc2ggdG9rZW4gZm9yIGNvbnRpbnVlZCBhY2Nlc3NcXG4nO1xuICAgIH0gZWxzZSBpZiAodG9rZW5UeXBlID09PSAnUGVyc29uYWwgQWNjZXNzIFRva2VuJykge1xuICAgICAgZ3VpZGFuY2UgKz0gJ+KAoiBZb3VyIFBlcnNvbmFsIEFjY2VzcyBUb2tlbiBtYXkgaGF2ZSBleHBpcmVkXFxuJztcbiAgICAgIGd1aWRhbmNlICs9ICfigKIgQ2hlY2sgeW91ciBHaXRIdWIgc2V0dGluZ3M6IGh0dHBzOi8vZ2l0aHViLmNvbS9zZXR0aW5ncy90b2tlbnNcXG4nO1xuICAgICAgZ3VpZGFuY2UgKz0gJ+KAoiBHZW5lcmF0ZSBhIG5ldyB0b2tlbiBpZiBuZWVkZWQgYW5kIHVwZGF0ZSBHSVRIVUJfVE9LRU4gZW52aXJvbm1lbnQgdmFyaWFibGVcXG4nO1xuICAgIH0gZWxzZSB7XG4gICAgICBndWlkYW5jZSArPSAn4oCiIFlvdXIgR2l0SHViIHRva2VuIG1heSBoYXZlIGV4cGlyZWQgb3IgYmVlbiByZXZva2VkXFxuJztcbiAgICAgIGd1aWRhbmNlICs9ICfigKIgUmUtYXV0aGVudGljYXRlIHVzaW5nIGBzZXR1cF9naXRodWJfYXV0aGBcXG4nO1xuICAgICAgZ3VpZGFuY2UgKz0gJ+KAoiBFbnN1cmUgeW91ciB0b2tlbiBoYXMgdGhlIHJlcXVpcmVkIHBlcm1pc3Npb25zXFxuJztcbiAgICB9XG5cbiAgICBndWlkYW5jZSArPSBgXFxuKipPcGVyYXRpb24qKjogJHtvcGVyYXRpb25UeXBlfVxcbmA7XG4gICAgZ3VpZGFuY2UgKz0gJyoqUmVxdWlyZWQgc2NvcGVzKio6IHJlcG8sIHVzZXI6ZW1haWxcXG5cXG4nO1xuICAgIGd1aWRhbmNlICs9ICfwn5KhICoqVGlwKio6IEZyZXNoIHRva2VucyB3b3JrIGJldHRlciBmb3IgY29tcGxleCBvcGVyYXRpb25zIGxpa2UgcG9ydGZvbGlvIGNyZWF0aW9uLic7XG5cbiAgICByZXR1cm4gZ3VpZGFuY2U7XG4gIH1cblxuICAvKipcbiAgICogU2V0cyB1cCBHaXRIdWIgcmVwb3NpdG9yeSBhY2Nlc3MgYW5kIGVuc3VyZXMgcG9ydGZvbGlvIHJlcG9zaXRvcnkgZXhpc3RzXG4gICAqIEBwYXJhbSBhdXRoU3RhdHVzIEF1dGhlbnRpY2F0aW9uIHN0YXR1cyBjb250YWluaW5nIHVzZXJuYW1lXG4gICAqIEByZXR1cm5zIFNldHVwIHJlc3VsdCBvciBlcnJvciByZXNwb25zZVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzZXR1cEdpdEh1YlJlcG9zaXRvcnkoYXV0aFN0YXR1czogYW55KTogUHJvbWlzZTx7XG4gICAgc3VjY2VzczogYm9vbGVhbjtcbiAgICBlcnJvcj86IFN1Ym1pdFRvUG9ydGZvbGlvUmVzdWx0O1xuICB9PiB7XG4gICAgLy8gU0VDVVJJVFkgRU5IQU5DRU1FTlQgKFRhc2sgIzE0KTogU21hcnQgdG9rZW4gbWFuYWdlbWVudCBmb3IgbG9uZyBvcGVyYXRpb25zXG4gICAgY29uc3QgdG9rZW5NYW5hZ2VtZW50ID0gYXdhaXQgdGhpcy5tYW5hZ2VUb2tlbkZvckxvbmdPcGVyYXRpb24oJ3BvcnRmb2xpb19jcmVhdGlvbicpO1xuICAgIGlmICghdG9rZW5NYW5hZ2VtZW50LmNhblByb2NlZWQpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBlcnJvcjogdG9rZW5NYW5hZ2VtZW50LmVycm9yXG4gICAgICB9O1xuICAgIH1cblxuICAgIGNvbnN0IHRva2VuID0gdG9rZW5NYW5hZ2VtZW50LnRva2VuITtcblxuICAgIC8vIFByb3ZpZGUgdXNlciBndWlkYW5jZSBpZiByZWZyZXNoIGlzIHJlY29tbWVuZGVkIGZvciB0aGlzIGxvbmcgb3BlcmF0aW9uXG4gICAgaWYgKHRva2VuTWFuYWdlbWVudC5yZWZyZXNoUmVjb21tZW5kZWQpIHtcbiAgICAgIGNvbnN0IHRva2VuVHlwZSA9IFRva2VuTWFuYWdlci5nZXRUb2tlblR5cGUodG9rZW4pO1xuICAgICAgY29uc3QgZ3VpZGFuY2UgPSB0aGlzLmZvcm1hdFRva2VuUmVmcmVzaEd1aWRhbmNlKCdwb3J0Zm9saW8gY3JlYXRpb24nLCB0b2tlblR5cGUpO1xuICAgICAgbG9nZ2VyLndhcm4oYFRva2VuIHJlZnJlc2ggcmVjb21tZW5kZWQgZm9yIHBvcnRmb2xpbyBjcmVhdGlvbjoke2d1aWRhbmNlfWApO1xuICAgIH1cblxuICAgIHRoaXMucG9ydGZvbGlvTWFuYWdlci5zZXRUb2tlbih0b2tlbik7XG5cbiAgICAvLyBDaGVjayBpZiBwb3J0Zm9saW8gZXhpc3RzIGFuZCBjcmVhdGUgaWYgbmVlZGVkXG4gICAgY29uc3QgdXNlcm5hbWUgPSBhdXRoU3RhdHVzLnVzZXJuYW1lIHx8ICd1bmtub3duJztcbiAgICBjb25zdCBwb3J0Zm9saW9FeGlzdHMgPSBhd2FpdCB0aGlzLnBvcnRmb2xpb01hbmFnZXIuY2hlY2tQb3J0Zm9saW9FeGlzdHModXNlcm5hbWUpO1xuICAgIFxuICAgIGlmICghcG9ydGZvbGlvRXhpc3RzKSB7XG4gICAgICBsb2dnZXIuaW5mbygnQ3JlYXRpbmcgcG9ydGZvbGlvIHJlcG9zaXRvcnkuLi4nKTtcbiAgICAgIC8vIFJlcXVlc3QgY29uc2VudCBmb3IgcG9ydGZvbGlvIGNyZWF0aW9uXG4gICAgICBjb25zdCByZXBvVXJsID0gYXdhaXQgdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmNyZWF0ZVBvcnRmb2xpbyh1c2VybmFtZSwgdHJ1ZSk7XG4gICAgICBpZiAoIXJlcG9VcmwpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICBlcnJvcjoge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBtZXNzYWdlOiAnRmFpbGVkIHRvIGNyZWF0ZSBwb3J0Zm9saW8gcmVwb3NpdG9yeScsXG4gICAgICAgICAgICBlcnJvcjogJ0NSRUFURV9GQUlMRUQnXG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJtaXRzIGVsZW1lbnQgdG8gcG9ydGZvbGlvIGFuZCBoYW5kbGVzIHRoZSBjb21wbGV0ZSByZXNwb25zZSB3b3JrZmxvd1xuICAgKiBAcGFyYW0gc2FmZU5hbWUgVGhlIG5vcm1hbGl6ZWQgbmFtZSBvZiB0aGUgZWxlbWVudFxuICAgKiBAcGFyYW0gZWxlbWVudFR5cGUgVGhlIHR5cGUgb2YgdGhlIGVsZW1lbnRcbiAgICogQHBhcmFtIG1ldGFkYXRhIFRoZSBtZXRhZGF0YSBmb3IgdGhlIGVsZW1lbnRcbiAgICogQHBhcmFtIGNvbnRlbnQgVGhlIGNvbnRlbnQgb2YgdGhlIGVsZW1lbnRcbiAgICogQHBhcmFtIGF1dGhTdGF0dXMgQXV0aGVudGljYXRpb24gc3RhdHVzIGNvbnRhaW5pbmcgdXNlcm5hbWUgYW5kIHRva2VuXG4gICAqIEByZXR1cm5zIENvbXBsZXRlIHN1Ym1pc3Npb24gcmVzdWx0IHdpdGggc3VjY2VzcyBtZXNzYWdlIG9yIGVycm9yXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHN1Ym1pdEVsZW1lbnRBbmRIYW5kbGVSZXNwb25zZShcbiAgICBzYWZlTmFtZTogc3RyaW5nLFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSxcbiAgICBtZXRhZGF0YTogUG9ydGZvbGlvRWxlbWVudE1ldGFkYXRhLFxuICAgIGNvbnRlbnQ6IHN0cmluZyxcbiAgICBhdXRoU3RhdHVzOiBhbnlcbiAgKTogUHJvbWlzZTxTdWJtaXRUb1BvcnRmb2xpb1Jlc3VsdD4ge1xuICAgIC8vIENyZWF0ZSBlbGVtZW50IHN0cnVjdHVyZSB0byBzYXZlXG4gICAgY29uc3QgZWxlbWVudDogUG9ydGZvbGlvRWxlbWVudCA9IHtcbiAgICAgIHR5cGU6IGVsZW1lbnRUeXBlLFxuICAgICAgbWV0YWRhdGEsXG4gICAgICBjb250ZW50XG4gICAgfTtcbiAgICBcbiAgICAvLyBUWVBFIFNBRkVUWSBGSVggIzI6IFVzZSBhZGFwdGVyIHBhdHRlcm4gaW5zdGVhZCBvZiBjb21wbGV4IHR5cGUgY2FzdGluZ1xuICAgIC8vIFByZXZpb3VzbHk6IGVsZW1lbnQgYXMgdW5rbm93biBhcyBQYXJhbWV0ZXJzPHR5cGVvZiB0aGlzLnBvcnRmb2xpb01hbmFnZXIuc2F2ZUVsZW1lbnQ+WzBdXG4gICAgLy8gTm93OiBDbGVhbiBhZGFwdGVyIHBhdHRlcm4gdGhhdCBpbXBsZW1lbnRzIElFbGVtZW50IGludGVyZmFjZSBwcm9wZXJseVxuICAgIGNvbnN0IGFkYXB0ZXIgPSBuZXcgUG9ydGZvbGlvRWxlbWVudEFkYXB0ZXIoZWxlbWVudCk7XG4gICAgXG4gICAgLy8gVVggSU1QUk9WRU1FTlQ6IEFkZCByZXRyeSBsb2dpYyBmb3IgdHJhbnNpZW50IGZhaWx1cmVzXG4gICAgY29uc3QgZmlsZVVybCA9IGF3YWl0IHRoaXMuc2F2ZUVsZW1lbnRXaXRoUmV0cnkoYWRhcHRlciwgc2FmZU5hbWUsIGVsZW1lbnRUeXBlKTtcbiAgICBcbiAgICBpZiAoIWZpbGVVcmwpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBtZXNzYWdlOiAnRmFpbGVkIHRvIHNhdmUgZWxlbWVudCB0byBHaXRIdWIgcG9ydGZvbGlvIGFmdGVyIG11bHRpcGxlIGF0dGVtcHRzLlxcblxcbicgK1xuICAgICAgICAgICAgICAgICfwn5KhICoqVHJvdWJsZXNob290aW5nIFRpcHMqKjpcXG4nICtcbiAgICAgICAgICAgICAgICAn4oCiIENoZWNrIHlvdXIgR2l0SHViIGF1dGhlbnRpY2F0aW9uOiBgZ2ggYXV0aCBzdGF0dXNgXFxuJyArXG4gICAgICAgICAgICAgICAgJ+KAoiBWZXJpZnkgcmVwb3NpdG9yeSBwZXJtaXNzaW9uc1xcbicgK1xuICAgICAgICAgICAgICAgICfigKIgVHJ5IGFnYWluIGluIGEgZmV3IG1pbnV0ZXMgKEdpdEh1YiBBUEkgcmF0ZSBsaW1pdHMpXFxuJyArXG4gICAgICAgICAgICAgICAgJ+KAoiBDaGVjayBHaXRIdWIgc3RhdHVzOiBodHRwczovL3N0YXR1cy5naXRodWIuY29tJyxcbiAgICAgICAgZXJyb3I6ICdTQVZFX0ZBSUxFRCdcbiAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gTG9nIHN1Y2Nlc3NmdWwgc3VibWlzc2lvbiAoRE1DUC1TRUMtMDA2KVxuICAgIGxvZ2dlci5pbmZvKGBTdWNjZXNzZnVsbHkgc3VibWl0dGVkICR7c2FmZU5hbWV9IHRvIEdpdEh1YiBwb3J0Zm9saW9gLCB7XG4gICAgICBlbGVtZW50VHlwZSxcbiAgICAgIHVzZXJuYW1lOiBhdXRoU3RhdHVzLnVzZXJuYW1lLFxuICAgICAgZmlsZVVybFxuICAgIH0pO1xuXG4gICAgLy8gU0VDVVJJVFkgRU5IQU5DRU1FTlQgKFRhc2sgIzE0KTogU21hcnQgdG9rZW4gbWFuYWdlbWVudCBmb3IgY29sbGVjdGlvbiBzdWJtaXNzaW9uXG4gICAgY29uc3QgY29sbGVjdGlvblRva2VuTWFuYWdlbWVudCA9IGF3YWl0IHRoaXMubWFuYWdlVG9rZW5Gb3JMb25nT3BlcmF0aW9uKCdjb2xsZWN0aW9uX3N1Ym1pc3Npb24nKTtcbiAgICBpZiAoIWNvbGxlY3Rpb25Ub2tlbk1hbmFnZW1lbnQuY2FuUHJvY2VlZCkge1xuICAgICAgLy8gVG9rZW4gbWFuYWdlbWVudCBmYWlsZWQgZm9yIGNvbGxlY3Rpb24gc3VibWlzc2lvbiwgYnV0IG1haW4gc3VibWlzc2lvbiBzdWNjZWVkZWRcbiAgICAgIGNvbnN0IGVycm9yTWVzc2FnZSA9IGNvbGxlY3Rpb25Ub2tlbk1hbmFnZW1lbnQuZXJyb3I/Lm1lc3NhZ2UgfHwgJ1Rva2VuIG1hbmFnZW1lbnQgZmFpbGVkJztcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgIG1lc3NhZ2U6IGDinIUgU3VjY2Vzc2Z1bGx5IHVwbG9hZGVkICR7c2FmZU5hbWV9IHRvIHlvdXIgR2l0SHViIHBvcnRmb2xpbyFcXG7wn5OBIFBvcnRmb2xpbyBVUkw6ICR7ZmlsZVVybH1cXG5cXG7imqDvuI8gQ29sbGVjdGlvbiBzdWJtaXNzaW9uIHNraXBwZWQ6ICR7ZXJyb3JNZXNzYWdlfWAsXG4gICAgICAgIHVybDogZmlsZVVybFxuICAgICAgfTtcbiAgICB9XG5cbiAgICBjb25zdCB0b2tlbiA9IGNvbGxlY3Rpb25Ub2tlbk1hbmFnZW1lbnQudG9rZW4hO1xuXG4gICAgLy8gUHJvdmlkZSByZWZyZXNoIGd1aWRhbmNlIGlmIHJlY29tbWVuZGVkIGZvciBjb2xsZWN0aW9uIHN1Ym1pc3Npb25cbiAgICBpZiAoY29sbGVjdGlvblRva2VuTWFuYWdlbWVudC5yZWZyZXNoUmVjb21tZW5kZWQpIHtcbiAgICAgIGNvbnN0IHRva2VuVHlwZSA9IFRva2VuTWFuYWdlci5nZXRUb2tlblR5cGUodG9rZW4pO1xuICAgICAgbG9nZ2VyLmluZm8oJ0NvbGxlY3Rpb24gc3VibWlzc2lvbiBwcm9jZWVkaW5nIHdpdGggYWdpbmcgdG9rZW4nLCB7XG4gICAgICAgIHRva2VuVHlwZSxcbiAgICAgICAgcmVjb21tZW5kYXRpb246ICdJZiBjb2xsZWN0aW9uIHN1Ym1pc3Npb24gZmFpbHMsIHRyeSByZS1hdXRoZW50aWNhdGluZyB3aXRoIHNldHVwX2dpdGh1Yl9hdXRoJ1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gRU5IQU5DRU1FTlQgKElzc3VlICM1NDkpOiBBc2sgdXNlciBpZiB0aGV5IHdhbnQgdG8gc3VibWl0IHRvIGNvbGxlY3Rpb25cbiAgICAvLyBUaGlzIGNvbXBsZXRlcyB0aGUgY29tbXVuaXR5IGNvbnRyaWJ1dGlvbiB3b3JrZmxvd1xuICAgIGNvbnN0IGNvbGxlY3Rpb25TdWJtaXNzaW9uUmVzdWx0ID0gYXdhaXQgdGhpcy5wcm9tcHRGb3JDb2xsZWN0aW9uU3VibWlzc2lvbih7XG4gICAgICBlbGVtZW50TmFtZTogc2FmZU5hbWUsXG4gICAgICBlbGVtZW50VHlwZSxcbiAgICAgIHBvcnRmb2xpb1VybDogZmlsZVVybCxcbiAgICAgIHVzZXJuYW1lOiBhdXRoU3RhdHVzLnVzZXJuYW1lIHx8ICd1bmtub3duJyxcbiAgICAgIG1ldGFkYXRhLFxuICAgICAgdG9rZW5cbiAgICB9KTtcblxuICAgIC8vIEJ1aWxkIHRoZSByZXNwb25zZSBtZXNzYWdlIGJhc2VkIG9uIHdoYXQgaGFwcGVuZWRcbiAgICBsZXQgbWVzc2FnZSA9IGDinIUgU3VjY2Vzc2Z1bGx5IHVwbG9hZGVkICR7c2FmZU5hbWV9IHRvIHlvdXIgR2l0SHViIHBvcnRmb2xpbyFcXG5gO1xuICAgIG1lc3NhZ2UgKz0gYPCfk4EgUG9ydGZvbGlvIFVSTDogJHtmaWxlVXJsfVxcblxcbmA7XG4gICAgXG4gICAgaWYgKGNvbGxlY3Rpb25TdWJtaXNzaW9uUmVzdWx0LnN1Ym1pdHRlZCkge1xuICAgICAgbWVzc2FnZSArPSBg8J+OiSBBbHNvIHN1Ym1pdHRlZCB0byBEb2xsaG91c2VNQ1AgY29sbGVjdGlvbiBmb3IgY29tbXVuaXR5IHJldmlldyFcXG5gO1xuICAgICAgbWVzc2FnZSArPSBg8J+TiyBJc3N1ZTogJHtjb2xsZWN0aW9uU3VibWlzc2lvblJlc3VsdC5pc3N1ZVVybH1gO1xuICAgIH0gZWxzZSBpZiAoY29sbGVjdGlvblN1Ym1pc3Npb25SZXN1bHQuZGVjbGluZWQpIHtcbiAgICAgIG1lc3NhZ2UgKz0gYPCfkqEgWW91IGNhbiBzdWJtaXQgdG8gdGhlIGNvbGxlY3Rpb24gbGF0ZXIgdXNpbmcgdGhlIHNhbWUgY29tbWFuZC5gO1xuICAgIH0gZWxzZSBpZiAoY29sbGVjdGlvblN1Ym1pc3Npb25SZXN1bHQuZXJyb3IpIHtcbiAgICAgIG1lc3NhZ2UgKz0gYOKaoO+4jyBDb2xsZWN0aW9uIHN1Ym1pc3Npb24gZmFpbGVkOiAke2NvbGxlY3Rpb25TdWJtaXNzaW9uUmVzdWx0LmVycm9yfVxcbmA7XG4gICAgICBtZXNzYWdlICs9IGDwn5KhIFlvdSBjYW4gbWFudWFsbHkgc3VibWl0IGF0OiBodHRwczovL2dpdGh1Yi5jb20vRG9sbGhvdXNlTUNQL2NvbGxlY3Rpb24vaXNzdWVzL25ld2A7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICBtZXNzYWdlLFxuICAgICAgdXJsOiBmaWxlVXJsXG4gICAgfTtcbiAgfVxuXG4gIGFzeW5jIGV4ZWN1dGUocGFyYW1zOiBTdWJtaXRUb1BvcnRmb2xpb1BhcmFtcyk6IFByb21pc2U8U3VibWl0VG9Qb3J0Zm9saW9SZXN1bHQ+IHtcbiAgICB0cnkge1xuICAgICAgLy8gVmFsaWRhdGUgYW5kIG5vcm1hbGl6ZSBpbnB1dCBwYXJhbWV0ZXJzXG4gICAgICBjb25zdCB2YWxpZGF0aW9uUmVzdWx0ID0gYXdhaXQgdGhpcy52YWxpZGF0ZUFuZE5vcm1hbGl6ZVBhcmFtcyhwYXJhbXMpO1xuICAgICAgaWYgKCF2YWxpZGF0aW9uUmVzdWx0LnN1Y2Nlc3MpIHtcbiAgICAgICAgcmV0dXJuIHZhbGlkYXRpb25SZXN1bHQuZXJyb3IhO1xuICAgICAgfVxuICAgICAgY29uc3Qgc2FmZU5hbWUgPSB2YWxpZGF0aW9uUmVzdWx0LnNhZmVOYW1lITtcblxuICAgICAgLy8gQ2hlY2sgYXV0aGVudGljYXRpb24gc3RhdHVzXG4gICAgICBjb25zdCBhdXRoUmVzdWx0ID0gYXdhaXQgdGhpcy5jaGVja0F1dGhlbnRpY2F0aW9uKCk7XG4gICAgICBpZiAoIWF1dGhSZXN1bHQuc3VjY2Vzcykge1xuICAgICAgICByZXR1cm4gYXV0aFJlc3VsdC5lcnJvciE7XG4gICAgICB9XG4gICAgICBjb25zdCBhdXRoU3RhdHVzID0gYXV0aFJlc3VsdC5hdXRoU3RhdHVzITtcblxuICAgICAgLy8gRmluZCBjb250ZW50IGxvY2FsbHkgd2l0aCBzbWFydCB0eXBlIGRldGVjdGlvblxuICAgICAgY29uc3QgY29udGVudFJlc3VsdCA9IGF3YWl0IHRoaXMuZGlzY292ZXJDb250ZW50V2l0aFR5cGVEZXRlY3Rpb24oc2FmZU5hbWUhLCBwYXJhbXMudHlwZSwgcGFyYW1zLm5hbWUpO1xuICAgICAgaWYgKCFjb250ZW50UmVzdWx0LnN1Y2Nlc3MpIHtcbiAgICAgICAgcmV0dXJuIGNvbnRlbnRSZXN1bHQuZXJyb3IhO1xuICAgICAgfVxuICAgICAgY29uc3QgZWxlbWVudFR5cGUgPSBjb250ZW50UmVzdWx0LmVsZW1lbnRUeXBlITtcbiAgICAgIGNvbnN0IGxvY2FsUGF0aCA9IGNvbnRlbnRSZXN1bHQubG9jYWxQYXRoITtcblxuICAgICAgLy8gVmFsaWRhdGUgZmlsZSBhbmQgY29udGVudCBzZWN1cml0eVxuICAgICAgY29uc3Qgc2VjdXJpdHlSZXN1bHQgPSBhd2FpdCB0aGlzLnZhbGlkYXRlRmlsZUFuZENvbnRlbnQobG9jYWxQYXRoKTtcbiAgICAgIGlmICghc2VjdXJpdHlSZXN1bHQuc3VjY2Vzcykge1xuICAgICAgICByZXR1cm4gc2VjdXJpdHlSZXN1bHQuZXJyb3IhO1xuICAgICAgfVxuICAgICAgY29uc3QgY29udGVudCA9IHNlY3VyaXR5UmVzdWx0LmNvbnRlbnQhO1xuXG4gICAgICAvLyBHZXQgdXNlciBjb25zZW50IChwbGFjZWhvbGRlciBmb3Igbm93IC0gY291bGQgYWRkIGludGVyYWN0aXZlIHByb21wdCBsYXRlcilcbiAgICAgIGxvZ2dlci5pbmZvKGBQcmVwYXJpbmcgdG8gc3VibWl0ICR7c2FmZU5hbWV9IHRvIEdpdEh1YiBwb3J0Zm9saW9gKTtcblxuICAgICAgLy8gUHJlcGFyZSBtZXRhZGF0YSBmb3IgZWxlbWVudFxuICAgICAgY29uc3QgbWV0YWRhdGEgPSB0aGlzLnByZXBhcmVFbGVtZW50TWV0YWRhdGEoc2FmZU5hbWUhLCBlbGVtZW50VHlwZSwgYXV0aFN0YXR1cyk7XG5cbiAgICAgIC8vIFNldCB1cCBHaXRIdWIgcmVwb3NpdG9yeSBhY2Nlc3NcbiAgICAgIGNvbnN0IHJlcG9SZXN1bHQgPSBhd2FpdCB0aGlzLnNldHVwR2l0SHViUmVwb3NpdG9yeShhdXRoU3RhdHVzKTtcbiAgICAgIGlmICghcmVwb1Jlc3VsdC5zdWNjZXNzKSB7XG4gICAgICAgIHJldHVybiByZXBvUmVzdWx0LmVycm9yITtcbiAgICAgIH1cblxuICAgICAgLy8gU3VibWl0IGVsZW1lbnQgdG8gcG9ydGZvbGlvIGFuZCBoYW5kbGUgY29sbGVjdGlvbiBzdWJtaXNzaW9uXG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5zdWJtaXRFbGVtZW50QW5kSGFuZGxlUmVzcG9uc2UoXG4gICAgICAgIHNhZmVOYW1lISwgXG4gICAgICAgIGVsZW1lbnRUeXBlLCBcbiAgICAgICAgbWV0YWRhdGEsIFxuICAgICAgICBjb250ZW50LCBcbiAgICAgICAgYXV0aFN0YXR1c1xuICAgICAgKTtcblxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBTRUNVUklUWSBFTkhBTkNFTUVOVCAoVGFzayAjMTQpOiBFbmhhbmNlZCBlcnJvciBoYW5kbGluZyB3aXRoIHRva2VuIHJlZnJlc2ggZ3VpZGFuY2VcbiAgICAgIEVycm9ySGFuZGxlci5sb2dFcnJvcignc3VibWl0VG9Qb3J0Zm9saW8nLCBlcnJvciwge1xuICAgICAgICBlbGVtZW50TmFtZTogcGFyYW1zLm5hbWUsXG4gICAgICAgIGVsZW1lbnRUeXBlOiBwYXJhbXMudHlwZVxuICAgICAgfSk7XG5cbiAgICAgIC8vIENoZWNrIGlmIGVycm9yIGlzIHRva2VuLXJlbGF0ZWQgYW5kIHByb3ZpZGUgcmVmcmVzaCBndWlkYW5jZVxuICAgICAgY29uc3QgZXJyb3JNZXNzYWdlID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpO1xuICAgICAgY29uc3QgaXNUb2tlbkVycm9yID0gZXJyb3JNZXNzYWdlLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJ3Rva2VuJykgfHwgXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yTWVzc2FnZS50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKCdhdXRoJykgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JNZXNzYWdlLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJzQwMScpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yTWVzc2FnZS50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKCc0MDMnKTtcblxuICAgICAgbGV0IGZvcm1hdHRlZEVycm9yID0gRXJyb3JIYW5kbGVyLmZvcm1hdEZvclJlc3BvbnNlKGVycm9yKTtcblxuICAgICAgaWYgKGlzVG9rZW5FcnJvcikge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIEdldCBjdXJyZW50IHRva2VuIHRvIGRldGVybWluZSB0eXBlIGZvciBndWlkYW5jZVxuICAgICAgICAgIGNvbnN0IGN1cnJlbnRUb2tlbiA9IGF3YWl0IFRva2VuTWFuYWdlci5nZXRHaXRIdWJUb2tlbkFzeW5jKCk7XG4gICAgICAgICAgaWYgKGN1cnJlbnRUb2tlbikge1xuICAgICAgICAgICAgY29uc3QgdG9rZW5UeXBlID0gVG9rZW5NYW5hZ2VyLmdldFRva2VuVHlwZShjdXJyZW50VG9rZW4pO1xuICAgICAgICAgICAgY29uc3QgcmVmcmVzaEd1aWRhbmNlID0gdGhpcy5mb3JtYXRUb2tlblJlZnJlc2hHdWlkYW5jZSgncG9ydGZvbGlvIHN1Ym1pc3Npb24nLCB0b2tlblR5cGUpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBBcHBlbmQgcmVmcmVzaCBndWlkYW5jZSB0byBlcnJvciBtZXNzYWdlXG4gICAgICAgICAgICBpZiAoZm9ybWF0dGVkRXJyb3IubWVzc2FnZSkge1xuICAgICAgICAgICAgICBmb3JtYXR0ZWRFcnJvci5tZXNzYWdlICs9IHJlZnJlc2hHdWlkYW5jZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKHRva2VuRXJyb3IpIHtcbiAgICAgICAgICAvLyBJZiB3ZSBjYW4ndCBnZXQgdG9rZW4gaW5mbywgcHJvdmlkZSBnZW5lcmljIGd1aWRhbmNlXG4gICAgICAgICAgZm9ybWF0dGVkRXJyb3IubWVzc2FnZSArPSAnXFxuXFxu8J+UhCAqKkF1dGhlbnRpY2F0aW9uIElzc3VlKio6IFRyeSBydW5uaW5nIGBzZXR1cF9naXRodWJfYXV0aGAgdG8gcmVmcmVzaCB5b3VyIGF1dGhlbnRpY2F0aW9uLic7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGZvcm1hdHRlZEVycm9yO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9tcHRzIHVzZXIgdG8gc3VibWl0IGNvbnRlbnQgdG8gdGhlIERvbGxob3VzZU1DUCBjb2xsZWN0aW9uXG4gICAqIEVOSEFOQ0VNRU5UIChJc3N1ZSAjNTQ5KTogQ29tcGxldGUgdGhlIGNvbW11bml0eSBjb250cmlidXRpb24gd29ya2Zsb3dcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcHJvbXB0Rm9yQ29sbGVjdGlvblN1Ym1pc3Npb24ocGFyYW1zOiB7XG4gICAgZWxlbWVudE5hbWU6IHN0cmluZztcbiAgICBlbGVtZW50VHlwZTogRWxlbWVudFR5cGU7XG4gICAgcG9ydGZvbGlvVXJsOiBzdHJpbmc7XG4gICAgdXNlcm5hbWU6IHN0cmluZztcbiAgICBtZXRhZGF0YTogUG9ydGZvbGlvRWxlbWVudE1ldGFkYXRhO1xuICAgIHRva2VuOiBzdHJpbmc7XG4gIH0pOiBQcm9taXNlPHsgc3VibWl0dGVkOiBib29sZWFuOyBkZWNsaW5lZDogYm9vbGVhbjsgZXJyb3I/OiBzdHJpbmc7IGlzc3VlVXJsPzogc3RyaW5nIH0+IHtcbiAgICB0cnkge1xuICAgICAgLy8gQ3JlYXRlIGEgc2ltcGxlIHByb21wdCBtZXNzYWdlIGZvciB0aGUgdXNlclxuICAgICAgLy8gTm90ZTogSW4gTUNQIGNvbnRleHQsIHdlIGNhbid0IGRvIGludGVyYWN0aXZlIHByb21wdHMsIHNvIHdlJ2xsIG5lZWQgdG9cbiAgICAgIC8vIGVpdGhlciBtYWtlIHRoaXMgYXV0b21hdGljIG9yIHJlcXVpcmUgYSBwYXJhbWV0ZXJcbiAgICAgIFxuICAgICAgLy8gRm9yIG5vdywgbGV0J3MgY2hlY2sgaWYgdGhlIHVzZXIgaGFzIHNldCBhbiBlbnZpcm9ubWVudCB2YXJpYWJsZVxuICAgICAgLy8gdG8gYXV0by1zdWJtaXQgdG8gY29sbGVjdGlvbiAob3B0LWluIGJlaGF2aW9yKVxuICAgICAgY29uc3QgYXV0b1N1Ym1pdCA9IHByb2Nlc3MuZW52LkRPTExIT1VTRV9BVVRPX1NVQk1JVF9UT19DT0xMRUNUSU9OID09PSAndHJ1ZSc7XG4gICAgICBcbiAgICAgIGlmICghYXV0b1N1Ym1pdCkge1xuICAgICAgICAvLyBVc2VyIGhhc24ndCBvcHRlZCBpbiB0byBhdXRvLXN1Ym1pc3Npb25cbiAgICAgICAgbG9nZ2VyLmluZm8oJ0NvbGxlY3Rpb24gc3VibWlzc2lvbiBza2lwcGVkIChzZXQgRE9MTEhPVVNFX0FVVE9fU1VCTUlUX1RPX0NPTExFQ1RJT049dHJ1ZSB0byBlbmFibGUpJyk7XG4gICAgICAgIHJldHVybiB7IHN1Ym1pdHRlZDogZmFsc2UsIGRlY2xpbmVkOiB0cnVlIH07XG4gICAgICB9XG5cbiAgICAgIGxvZ2dlci5pbmZvKCdBdXRvLXN1Ym1pdHRpbmcgdG8gRG9sbGhvdXNlTUNQIGNvbGxlY3Rpb24uLi4nKTtcblxuICAgICAgLy8gQ3JlYXRlIHRoZSBpc3N1ZSBpbiB0aGUgY29sbGVjdGlvbiByZXBvc2l0b3J5XG4gICAgICBjb25zdCBpc3N1ZVVybCA9IGF3YWl0IHRoaXMuY3JlYXRlQ29sbGVjdGlvbklzc3VlKHtcbiAgICAgICAgLi4ucGFyYW1zLFxuICAgICAgICB0b2tlbjogcGFyYW1zLnRva2VuXG4gICAgICB9KTtcblxuICAgICAgaWYgKGlzc3VlVXJsKSB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdTdWNjZXNzZnVsbHkgY3JlYXRlZCBjb2xsZWN0aW9uIHN1Ym1pc3Npb24gaXNzdWUnLCB7IGlzc3VlVXJsIH0pO1xuICAgICAgICByZXR1cm4geyBzdWJtaXR0ZWQ6IHRydWUsIGRlY2xpbmVkOiBmYWxzZSwgaXNzdWVVcmwgfTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB7IHN1Ym1pdHRlZDogZmFsc2UsIGRlY2xpbmVkOiBmYWxzZSwgZXJyb3I6ICdGYWlsZWQgdG8gY3JlYXRlIGlzc3VlJyB9O1xuICAgICAgfVxuXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRXJyb3IgaW4gY29sbGVjdGlvbiBzdWJtaXNzaW9uIHByb21wdCcsIHsgZXJyb3IgfSk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdWJtaXR0ZWQ6IGZhbHNlLFxuICAgICAgICBkZWNsaW5lZDogZmFsc2UsXG4gICAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJ1xuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhbiBpc3N1ZSBpbiB0aGUgRG9sbGhvdXNlTUNQL2NvbGxlY3Rpb24gcmVwb3NpdG9yeVxuICAgKiBFTkhBTkNFTUVOVCAoSXNzdWUgIzU0OSk6IEdpdEh1YiBBUEkgaW50ZWdyYXRpb24gZm9yIGNvbGxlY3Rpb24gc3VibWlzc2lvblxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBjcmVhdGVDb2xsZWN0aW9uSXNzdWUocGFyYW1zOiB7XG4gICAgZWxlbWVudE5hbWU6IHN0cmluZztcbiAgICBlbGVtZW50VHlwZTogRWxlbWVudFR5cGU7XG4gICAgcG9ydGZvbGlvVXJsOiBzdHJpbmc7XG4gICAgdXNlcm5hbWU6IHN0cmluZztcbiAgICBtZXRhZGF0YTogUG9ydGZvbGlvRWxlbWVudE1ldGFkYXRhO1xuICAgIHRva2VuOiBzdHJpbmc7XG4gIH0pOiBQcm9taXNlPHN0cmluZyB8IG51bGw+IHtcbiAgICB0cnkge1xuXG4gICAgICAvLyBGb3JtYXQgdGhlIGlzc3VlIHRpdGxlXG4gICAgICBjb25zdCB0aXRsZSA9IGBbJHtwYXJhbXMuZWxlbWVudFR5cGV9XSBBZGQgJHtwYXJhbXMuZWxlbWVudE5hbWV9IGJ5IEAke3BhcmFtcy51c2VybmFtZX1gO1xuXG4gICAgICAvLyBGb3JtYXQgdGhlIGlzc3VlIGJvZHkgd2l0aCBhbGwgcmVsZXZhbnQgaW5mb3JtYXRpb25cbiAgICAgIGNvbnN0IGJvZHkgPSBgIyMgTmV3ICR7cGFyYW1zLmVsZW1lbnRUeXBlfSBTdWJtaXNzaW9uXG5cbmAgK1xuICAgICAgICBgKipOYW1lKio6ICR7cGFyYW1zLmVsZW1lbnROYW1lfVxcbmAgK1xuICAgICAgICBgKipBdXRob3IqKjogQCR7cGFyYW1zLnVzZXJuYW1lfVxcbmAgK1xuICAgICAgICBgKipUeXBlKio6ICR7cGFyYW1zLmVsZW1lbnRUeXBlfVxcbmAgK1xuICAgICAgICBgKipEZXNjcmlwdGlvbioqOiAke3BhcmFtcy5tZXRhZGF0YS5kZXNjcmlwdGlvbiB8fCAnTm8gZGVzY3JpcHRpb24gcHJvdmlkZWQnfVxcblxcbmAgK1xuICAgICAgICBgIyMjIFBvcnRmb2xpbyBMaW5rXFxuYCArXG4gICAgICAgIGAke3BhcmFtcy5wb3J0Zm9saW9Vcmx9XFxuXFxuYCArXG4gICAgICAgIGAjIyMgTWV0YWRhdGFcXG5gICtcbiAgICAgICAgYFxcYFxcYFxcYGpzb25cXG4ke0pTT04uc3RyaW5naWZ5KHBhcmFtcy5tZXRhZGF0YSwgbnVsbCwgMil9XFxuXFxgXFxgXFxgXFxuXFxuYCArXG4gICAgICAgIGAjIyMgUmV2aWV3IENoZWNrbGlzdFxcbmAgK1xuICAgICAgICBgLSBbIF0gQ29udGVudCBpcyBhcHByb3ByaWF0ZSBhbmQgZm9sbG93cyBjb21tdW5pdHkgZ3VpZGVsaW5lc1xcbmAgK1xuICAgICAgICBgLSBbIF0gTm8gc2VjdXJpdHkgdnVsbmVyYWJpbGl0aWVzIG9yIG1hbGljaW91cyBwYXR0ZXJuc1xcbmAgK1xuICAgICAgICBgLSBbIF0gTWV0YWRhdGEgaXMgY29tcGxldGUgYW5kIGFjY3VyYXRlXFxuYCArXG4gICAgICAgIGAtIFsgXSBFbGVtZW50IHdvcmtzIGFzIGRlc2NyaWJlZFxcbmAgK1xuICAgICAgICBgLSBbIF0gTm8gZHVwbGljYXRlIG9mIGV4aXN0aW5nIGNvbGxlY3Rpb24gY29udGVudFxcblxcbmAgK1xuICAgICAgICBgLS0tXFxuYCArXG4gICAgICAgIGAqVGhpcyBzdWJtaXNzaW9uIHdhcyBjcmVhdGVkIGF1dG9tYXRpY2FsbHkgdmlhIHRoZSBEb2xsaG91c2VNQ1Agc3VibWl0X2NvbnRlbnQgdG9vbC4qYDtcblxuICAgICAgLy8gRGV0ZXJtaW5lIGxhYmVscyBiYXNlZCBvbiBlbGVtZW50IHR5cGVcbiAgICAgIGNvbnN0IGxhYmVscyA9IFtcbiAgICAgICAgJ2NvbnRyaWJ1dGlvbicsICAvLyBBbGwgc3VibWlzc2lvbnMgZ2V0IHRoaXNcbiAgICAgICAgJ3BlbmRpbmctcmV2aWV3JywgLy8gTmVlZHMgcmV2aWV3XG4gICAgICAgIHBhcmFtcy5lbGVtZW50VHlwZS50b0xvd2VyQ2FzZSgpIC8vIEVsZW1lbnQgdHlwZSBsYWJlbFxuICAgICAgXTtcblxuICAgICAgLy8gUEVSRk9STUFOQ0UgT1BUSU1JWkFUSU9OIChUYXNrICM2KTogVXNlIEdpdEh1YiByYXRlIGxpbWl0ZXIgZm9yIEFQSSBjYWxsc1xuICAgICAgLy8gVGhpcyBwcmV2ZW50cyBoaXR0aW5nIEdpdEh1YiByYXRlIGxpbWl0cyBhbmQgcHJvdmlkZXMgYmV0dGVyIGVycm9yIGhhbmRsaW5nXG4gICAgICBjb25zdCBpc3N1ZVVybCA9IGF3YWl0IGdpdGh1YlJhdGVMaW1pdGVyLnF1ZXVlUmVxdWVzdChcbiAgICAgICAgJ2NyZWF0ZS1jb2xsZWN0aW9uLWlzc3VlJyxcbiAgICAgICAgYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHVybCA9ICdodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL0RvbGxob3VzZU1DUC9jb2xsZWN0aW9uL2lzc3Vlcyc7XG4gICAgICAgICAgXG4gICAgICAgICAgLy8gQ3JlYXRlIEFib3J0Q29udHJvbGxlciBmb3IgdGltZW91dFxuICAgICAgICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgICAgICAgY29uc3QgdGltZW91dElkID0gc2V0VGltZW91dCgoKSA9PiBjb250cm9sbGVyLmFib3J0KCksIGdldFZhbGlkYXRlZFRpbWVvdXQoKSk7XG4gICAgICAgICAgXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godXJsLCB7XG4gICAgICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgICAgICAgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi92bmQuZ2l0aHViLnYzK2pzb24nLFxuICAgICAgICAgICAgICAgICdBdXRob3JpemF0aW9uJzogYEJlYXJlciAke3BhcmFtcy50b2tlbn1gLFxuICAgICAgICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICAgICAgICAgICAgJ1VzZXItQWdlbnQnOiAnRG9sbGhvdXNlTUNQLzEuMCdcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgICAgICAgIHRpdGxlLFxuICAgICAgICAgICAgICAgIGJvZHksXG4gICAgICAgICAgICAgICAgbGFiZWxzXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG5cbiAgICAgICAgICAgIC8vIFBFUkZPUk1BTkNFIE9QVElNSVpBVElPTiAoVGFzayAjMTUpOiBFbmhhbmNlZCByYXRlIGxpbWl0IGxvZ2dpbmdcbiAgICAgICAgICAgIC8vIExvZyByYXRlIGxpbWl0IGhlYWRlcnMgZm9yIGRpYWdub3N0aWNzXG4gICAgICAgICAgICBjb25zdCByYXRlTGltaXRSZW1haW5pbmcgPSByZXNwb25zZS5oZWFkZXJzLmdldCgnWC1SYXRlTGltaXQtUmVtYWluaW5nJyk7XG4gICAgICAgICAgICBjb25zdCByYXRlTGltaXRSZXNldCA9IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdYLVJhdGVMaW1pdC1SZXNldCcpO1xuICAgICAgICAgICAgY29uc3QgcmF0ZUxpbWl0TGltaXQgPSByZXNwb25zZS5oZWFkZXJzLmdldCgnWC1SYXRlTGltaXQtTGltaXQnKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKCdHaXRIdWIgQVBJIHJhdGUgbGltaXQgc3RhdHVzJywge1xuICAgICAgICAgICAgICBvcGVyYXRpb246ICdjcmVhdGUtY29sbGVjdGlvbi1pc3N1ZScsXG4gICAgICAgICAgICAgIHJlbWFpbmluZzogcmF0ZUxpbWl0UmVtYWluaW5nLFxuICAgICAgICAgICAgICBsaW1pdDogcmF0ZUxpbWl0TGltaXQsXG4gICAgICAgICAgICAgIHJlc2V0VGltZTogcmF0ZUxpbWl0UmVzZXQgPyBuZXcgRGF0ZShwYXJzZUludChyYXRlTGltaXRSZXNldCkgKiAxMDAwKSA6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgcmVzcG9uc2VTdGF0dXM6IHJlc3BvbnNlLnN0YXR1c1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIExvZyB3YXJuaW5nIGlmIGFwcHJvYWNoaW5nIHJhdGUgbGltaXRcbiAgICAgICAgICAgIGlmIChyYXRlTGltaXRSZW1haW5pbmcgJiYgcGFyc2VJbnQocmF0ZUxpbWl0UmVtYWluaW5nKSA8IDEwMCkge1xuICAgICAgICAgICAgICBsb2dnZXIud2FybignQXBwcm9hY2hpbmcgR2l0SHViIEFQSSByYXRlIGxpbWl0Jywge1xuICAgICAgICAgICAgICAgIG9wZXJhdGlvbjogJ2NyZWF0ZS1jb2xsZWN0aW9uLWlzc3VlJyxcbiAgICAgICAgICAgICAgICByZW1haW5pbmc6IHJhdGVMaW1pdFJlbWFpbmluZyxcbiAgICAgICAgICAgICAgICByZXNldFRpbWU6IHJhdGVMaW1pdFJlc2V0ID8gbmV3IERhdGUocGFyc2VJbnQocmF0ZUxpbWl0UmVzZXQpICogMTAwMCkgOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgcmVjb21tZW5kYXRpb246ICdDb25zaWRlciByZWR1Y2luZyBBUEkgdXNhZ2UgZnJlcXVlbmN5IG9yIGF1dGhlbnRpY2F0aW5nIGZvciBoaWdoZXIgbGltaXRzJ1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICBjb25zdCBlcnJvclRleHQgPSBhd2FpdCByZXNwb25zZS50ZXh0KCk7XG4gICAgICAgICAgICAgIGxvZ2dlci5lcnJvcignR2l0SHViIEFQSSBlcnJvciBjcmVhdGluZyBpc3N1ZScsIHsgXG4gICAgICAgICAgICAgICAgc3RhdHVzOiByZXNwb25zZS5zdGF0dXMsIFxuICAgICAgICAgICAgICAgIHN0YXR1c1RleHQ6IHJlc3BvbnNlLnN0YXR1c1RleHQsXG4gICAgICAgICAgICAgICAgZXJyb3I6IGVycm9yVGV4dCxcbiAgICAgICAgICAgICAgICByYXRlTGltaXRSZW1haW5pbmcsXG4gICAgICAgICAgICAgICAgcmF0ZUxpbWl0UmVzZXRcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIFxuICAgICAgICAgICAgICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA0MDQpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIuZXJyb3IoJ0NvbGxlY3Rpb24gcmVwb3NpdG9yeSBub3QgZm91bmQgb3Igbm8gYWNjZXNzJyk7XG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA0MDMpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIuZXJyb3IoJ1Blcm1pc3Npb24gZGVuaWVkIHRvIGNyZWF0ZSBpc3N1ZSBpbiBjb2xsZWN0aW9uIHJlcG8nKTtcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChyZXNwb25zZS5zdGF0dXMgPT09IDQwMSkge1xuICAgICAgICAgICAgICAgIGxvZ2dlci5lcnJvcignQXV0aGVudGljYXRpb24gZmFpbGVkIGZvciBjb2xsZWN0aW9uIHN1Ym1pc3Npb24nKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEdpdEh1YiBBUEkgZXJyb3I6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgICAgICAgICByZXR1cm4gZGF0YS5odG1sX3VybDtcbiAgICAgICAgICAgIFxuICAgICAgICAgIH0gY2F0Y2ggKGZldGNoRXJyb3I6IGFueSkge1xuICAgICAgICAgICAgLy8gUmUtdGhyb3cgdG8gb3V0ZXIgY2F0Y2ggYmxvY2tcbiAgICAgICAgICAgIHRocm93IGZldGNoRXJyb3I7XG4gICAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0SWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgJ2hpZ2gnIC8vIEhpZ2ggcHJpb3JpdHkgZm9yIGNvbGxlY3Rpb24gc3VibWlzc2lvblxuICAgICAgKTtcbiAgICAgIFxuICAgICAgcmV0dXJuIGlzc3VlVXJsO1xuXG4gICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgLy8gSGFuZGxlIHRpbWVvdXQgc3BlY2lmaWNhbGx5XG4gICAgICBpZiAoZXJyb3IubmFtZSA9PT0gJ0Fib3J0RXJyb3InKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcihgR2l0SHViIEFQSSByZXF1ZXN0IHRpbWVvdXQgYWZ0ZXIgJHtnZXRWYWxpZGF0ZWRUaW1lb3V0KCl9bXNgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGNyZWF0ZSBjb2xsZWN0aW9uIGlzc3VlJywgeyBcbiAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCBlcnJvclxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmluZExvY2FsQ29udGVudChuYW1lOiBzdHJpbmcsIHR5cGU6IEVsZW1lbnRUeXBlKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIE1FVEFEQVRBIElOREVYIEZJWDogVXNlIHBvcnRmb2xpbyBpbmRleCBmb3IgZmFzdCBtZXRhZGF0YS1iYXNlZCBsb29rdXBzXG4gICAgICAvLyBUaGlzIHNvbHZlcyB0aGUgY3JpdGljYWwgaXNzdWUgd2hlcmUgXCJTYWZlIFJvdW5kdHJpcCBUZXN0ZXJcIiBjb3VsZG4ndCBiZSBmb3VuZFxuICAgICAgLy8gYmVjYXVzZSBmaW5kTG9jYWxDb250ZW50IG9ubHkgc2VhcmNoZWQgZmlsZW5hbWVzLCBub3QgbWV0YWRhdGEgbmFtZXNcbiAgICAgIGNvbnN0IGluZGV4TWFuYWdlciA9IFBvcnRmb2xpb0luZGV4TWFuYWdlci5nZXRJbnN0YW5jZSgpO1xuICAgICAgXG4gICAgICAvLyBVWCBJTVBST1ZFTUVOVDogRW5oYW5jZWQgc2VhcmNoIHdpdGggZnV6enkgbWF0Y2hpbmdcbiAgICAgIGNvbnN0IGluZGV4RW50cnkgPSBhd2FpdCBpbmRleE1hbmFnZXIuZmluZEJ5TmFtZShuYW1lLCB7IFxuICAgICAgICBlbGVtZW50VHlwZTogdHlwZSxcbiAgICAgICAgZnV6enlNYXRjaDogdHJ1ZVxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIGlmIChpbmRleEVudHJ5KSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnRm91bmQgY29udGVudCB2aWEgbWV0YWRhdGEgaW5kZXgnLCB7IFxuICAgICAgICAgIHNlYXJjaE5hbWU6IG5hbWUsIFxuICAgICAgICAgIG1ldGFkYXRhTmFtZTogaW5kZXhFbnRyeS5tZXRhZGF0YS5uYW1lLFxuICAgICAgICAgIGZpbGVuYW1lOiBpbmRleEVudHJ5LmZpbGVuYW1lLFxuICAgICAgICAgIGZpbGVQYXRoOiBpbmRleEVudHJ5LmZpbGVQYXRoLFxuICAgICAgICAgIHR5cGUgXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gaW5kZXhFbnRyeS5maWxlUGF0aDtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gRkFMTEJBQ0s6IFVzZSBvcmlnaW5hbCBmaWxlIGRpc2NvdmVyeSBpZiBpbmRleCBsb29rdXAgZmFpbHNcbiAgICAgIC8vIFRoaXMgbWFpbnRhaW5zIGJhY2t3YXJkIGNvbXBhdGliaWxpdHkgYW5kIGhhbmRsZXMgZWRnZSBjYXNlc1xuICAgICAgbG9nZ2VyLmRlYnVnKCdJbmRleCBsb29rdXAgZmFpbGVkLCBmYWxsaW5nIGJhY2sgdG8gZmlsZSBkaXNjb3ZlcnknLCB7IG5hbWUsIHR5cGUgfSk7XG4gICAgICBcbiAgICAgIGNvbnN0IHBvcnRmb2xpb01hbmFnZXIgPSBQb3J0Zm9saW9NYW5hZ2VyLmdldEluc3RhbmNlKCk7XG4gICAgICBjb25zdCBwb3J0Zm9saW9EaXIgPSBwb3J0Zm9saW9NYW5hZ2VyLmdldEVsZW1lbnREaXIodHlwZSk7XG4gICAgICBcbiAgICAgIC8vIFVYIElNUFJPVkVNRU5UOiBUcnkgbXVsdGlwbGUgc2VhcmNoIHN0cmF0ZWdpZXMgZm9yIGJldHRlciB1c2VyIGV4cGVyaWVuY2VcbiAgICAgIGxldCBmaWxlID0gYXdhaXQgRmlsZURpc2NvdmVyeVV0aWwuZmluZEZpbGUocG9ydGZvbGlvRGlyLCBuYW1lLCB7XG4gICAgICAgIGV4dGVuc2lvbnM6IFsnLm1kJywgJy5qc29uJywgJy55YW1sJywgJy55bWwnXSxcbiAgICAgICAgcGFydGlhbE1hdGNoOiB0cnVlLFxuICAgICAgICBjYWNoZVJlc3VsdHM6IHRydWVcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICAvLyBJZiBub3QgZm91bmQsIHRyeSBub3JtYWxpemluZyB0aGUgbmFtZSAoZS5nLiwgXCJKLkEuUi5WLkkuUy5cIiAtPiBcImotYS1yLXYtaS1zXCIpXG4gICAgICBpZiAoIWZpbGUpIHtcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZE5hbWUgPSBuYW1lLnRvTG93ZXJDYXNlKClcbiAgICAgICAgICAucmVwbGFjZSgvW15hLXowLTldL2dpLCAnLScpICAvLyBSZXBsYWNlIG5vbi1hbHBoYW51bWVyaWMgd2l0aCBkYXNoZXNcbiAgICAgICAgICAucmVwbGFjZSgvLSsvZywgJy0nKSAgICAgICAgIC8vIFJlcGxhY2UgbXVsdGlwbGUgZGFzaGVzIHdpdGggc2luZ2xlIGRhc2hcbiAgICAgICAgICAucmVwbGFjZSgvXi18LSQvZywgJycpOyAgICAgIC8vIFJlbW92ZSBsZWFkaW5nL3RyYWlsaW5nIGRhc2hlc1xuICAgICAgICAgIFxuICAgICAgICBpZiAobm9ybWFsaXplZE5hbWUgIT09IG5hbWUudG9Mb3dlckNhc2UoKSkge1xuICAgICAgICAgIGxvZ2dlci5kZWJ1ZygnVHJ5aW5nIG5vcm1hbGl6ZWQgbmFtZSBzZWFyY2gnLCB7IFxuICAgICAgICAgICAgb3JpZ2luYWw6IG5hbWUsIFxuICAgICAgICAgICAgbm9ybWFsaXplZDogbm9ybWFsaXplZE5hbWUsXG4gICAgICAgICAgICB0eXBlIFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIFxuICAgICAgICAgIGZpbGUgPSBhd2FpdCBGaWxlRGlzY292ZXJ5VXRpbC5maW5kRmlsZShwb3J0Zm9saW9EaXIsIG5vcm1hbGl6ZWROYW1lLCB7XG4gICAgICAgICAgICBleHRlbnNpb25zOiBbJy5tZCcsICcuanNvbicsICcueWFtbCcsICcueW1sJ10sXG4gICAgICAgICAgICBwYXJ0aWFsTWF0Y2g6IHRydWUsXG4gICAgICAgICAgICBjYWNoZVJlc3VsdHM6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBJZiBzdGlsbCBub3QgZm91bmQsIHRyeSBzZWFyY2hpbmcgYnkgZGlzcGxheSBuYW1lIHBhdHRlcm5zXG4gICAgICBpZiAoIWZpbGUpIHtcbiAgICAgICAgLy8gVHJ5IGNvbW1vbiB2YXJpYXRpb25zIGxpa2UgcmVtb3ZpbmcgZG90cywgc3BhY2VzLCBldGMuXG4gICAgICAgIGNvbnN0IHZhcmlhdGlvbnMgPSBbXG4gICAgICAgICAgbmFtZS5yZXBsYWNlKC9cXC4vZywgJycpLCAgICAgICAgLy8gUmVtb3ZlIGRvdHM6IFwiSi5BLlIuVi5JLlMuXCIgLT4gXCJKQVJWSVNcIlxuICAgICAgICAgIG5hbWUucmVwbGFjZSgvXFxzKy9nLCAnLScpLCAgICAgIC8vIFJlcGxhY2Ugc3BhY2VzIHdpdGggZGFzaGVzXG4gICAgICAgICAgbmFtZS5yZXBsYWNlKC9bXFxzXFwuXS9nLCAnJyksICAgIC8vIFJlbW92ZSBzcGFjZXMgYW5kIGRvdHNcbiAgICAgICAgICBuYW1lLnJlcGxhY2UoL1tcXHNcXC5dL2csICctJyksICAgLy8gUmVwbGFjZSBzcGFjZXMgYW5kIGRvdHMgd2l0aCBkYXNoZXNcbiAgICAgICAgXS5maWx0ZXIodiA9PiB2ICE9PSBuYW1lICYmIHYubGVuZ3RoID4gMCk7XG4gICAgICAgIFxuICAgICAgICBmb3IgKGNvbnN0IHZhcmlhdGlvbiBvZiB2YXJpYXRpb25zKSB7XG4gICAgICAgICAgZmlsZSA9IGF3YWl0IEZpbGVEaXNjb3ZlcnlVdGlsLmZpbmRGaWxlKHBvcnRmb2xpb0RpciwgdmFyaWF0aW9uLCB7XG4gICAgICAgICAgICBleHRlbnNpb25zOiBbJy5tZCcsICcuanNvbicsICcueWFtbCcsICcueW1sJ10sXG4gICAgICAgICAgICBwYXJ0aWFsTWF0Y2g6IHRydWUsXG4gICAgICAgICAgICBjYWNoZVJlc3VsdHM6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBcbiAgICAgICAgICBpZiAoZmlsZSkge1xuICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKCdGb3VuZCBjb250ZW50IHVzaW5nIG5hbWUgdmFyaWF0aW9uJywge1xuICAgICAgICAgICAgICBvcmlnaW5hbDogbmFtZSxcbiAgICAgICAgICAgICAgdmFyaWF0aW9uLFxuICAgICAgICAgICAgICBmaWxlLFxuICAgICAgICAgICAgICB0eXBlXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICBpZiAoZmlsZSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ZvdW5kIGxvY2FsIGNvbnRlbnQgZmlsZSB2aWEgZmFsbGJhY2snLCB7IG5hbWUsIHR5cGUsIGZpbGUgfSk7XG4gICAgICAgIHJldHVybiBmaWxlO1xuICAgICAgfVxuICAgICAgXG4gICAgICBsb2dnZXIuZGVidWcoJ05vIGNvbnRlbnQgZm91bmQnLCB7IG5hbWUsIHR5cGUgfSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0Vycm9yIGZpbmRpbmcgbG9jYWwgY29udGVudCcsIHtcbiAgICAgICAgbmFtZSxcbiAgICAgICAgdHlwZSxcbiAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgfSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU21hcnQgZWxlbWVudCB0eXBlIGRldGVjdGlvbiAtIHNlYXJjaGVzIGFjcm9zcyBBTEwgZWxlbWVudCB0eXBlcyBmb3IgY29udGVudFxuICAgKiBQRVJGT1JNQU5DRSBPUFRJTUlaQVRJT04gKFRhc2sgIzkpOiBVc2VzIGVhcmx5IHRlcm1pbmF0aW9uIGZvciBleGFjdCBtYXRjaGVzXG4gICAqIFRoaXMgcmVwbGFjZXMgdGhlIHByZXZpb3VzIGhhcmRjb2RlZCBkZWZhdWx0IHRvIFBFUlNPTkEgYW5kIGVuYWJsZXMgcHJvcGVyIHR5cGUgZGV0ZWN0aW9uXG4gICAqIFxuICAgKiBAcGFyYW0gbmFtZSBUaGUgY29udGVudCBuYW1lIHRvIHNlYXJjaCBmb3JcbiAgICogQHJldHVybnMgRGV0ZWN0aW9uIHJlc3VsdCB3aXRoIGZvdW5kIG1hdGNoZXMgYWNyb3NzIGFsbCBlbGVtZW50IHR5cGVzXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGRldGVjdEVsZW1lbnRUeXBlKG5hbWU6IHN0cmluZyk6IFByb21pc2U8RWxlbWVudERldGVjdGlvblJlc3VsdD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBQRVJGT1JNQU5DRSBPUFRJTUlaQVRJT04gKFRhc2sgIzkpOiBVc2UgZWFybHkgdGVybWluYXRpb24gc2VhcmNoIHV0aWxpdHlcbiAgICAgIC8vIENyZWF0ZSBzZWFyY2ggZnVuY3Rpb25zIGZvciBlYWNoIGVsZW1lbnQgdHlwZVxuICAgICAgY29uc3QgZWxlbWVudFR5cGVzID0gT2JqZWN0LnZhbHVlcyhFbGVtZW50VHlwZSk7XG4gICAgICBjb25zdCBzZWFyY2hGdW5jdGlvbnMgPSBlbGVtZW50VHlwZXMubWFwKCh0eXBlKSA9PiBhc3luYyAoKSA9PiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZmlsZVBhdGggPSBhd2FpdCB0aGlzLmZpbmRMb2NhbENvbnRlbnQobmFtZSwgdHlwZSk7XG4gICAgICAgICAgaWYgKGZpbGVQYXRoKSB7XG4gICAgICAgICAgICByZXR1cm4geyB0eXBlOiB0eXBlIGFzIEVsZW1lbnRUeXBlLCBwYXRoOiBmaWxlUGF0aCB9O1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIC8vIExvZyB1bmV4cGVjdGVkIGVycm9ycyBidXQgZG9uJ3QgZmFpbCB0aGUgc2VhcmNoXG4gICAgICAgICAgaWYgKGVycm9yPy5jb2RlICE9PSAnRU5PRU5UJyAmJiBlcnJvcj8uY29kZSAhPT0gJ0VOT1RESVInKSB7XG4gICAgICAgICAgICBsb2dnZXIuZGVidWcoYEVycm9yIHNlYXJjaGluZyAke3R5cGV9IGRpcmVjdG9yeSBmb3IgY29udGVudCBkZXRlY3Rpb25gLCB7IFxuICAgICAgICAgICAgICBuYW1lLFxuICAgICAgICAgICAgICB0eXBlLFxuICAgICAgICAgICAgICBlcnJvcjogZXJyb3I/Lm1lc3NhZ2UgfHwgU3RyaW5nKGVycm9yKSxcbiAgICAgICAgICAgICAgY29kZTogZXJyb3I/LmNvZGUgXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gUmV0dXJuIG51bGwgaW5zdGVhZCBvZiB0aHJvd2luZyB0byBsZXQgb3RoZXIgc2VhcmNoZXMgY29udGludWVcbiAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIC8vIFBFUkZPUk1BTkNFIE9QVElNSVpBVElPTiAoVGFzayAjOSk6IERlZmluZSBleGFjdCBtYXRjaCBjcml0ZXJpYVxuICAgICAgY29uc3QgaXNFeGFjdE1hdGNoID0gKG1hdGNoOiBFbGVtZW50RGV0ZWN0aW9uTWF0Y2gpOiBib29sZWFuID0+IHtcbiAgICAgICAgY29uc3QgZmlsZW5hbWUgPSBwYXRoLmJhc2VuYW1lKG1hdGNoLnBhdGgsIHBhdGguZXh0bmFtZShtYXRjaC5wYXRoKSk7XG4gICAgICAgIHJldHVybiBmaWxlbmFtZS50b0xvd2VyQ2FzZSgpID09PSBuYW1lLnRvTG93ZXJDYXNlKCk7XG4gICAgICB9O1xuXG4gICAgICAvLyBFeGVjdXRlIHNlYXJjaGVzIHdpdGggZWFybHkgdGVybWluYXRpb24gb3B0aW1pemF0aW9uXG4gICAgICBjb25zdCBzZWFyY2hSZXN1bHRzID0gYXdhaXQgRWFybHlUZXJtaW5hdGlvblNlYXJjaC5leGVjdXRlV2l0aEVhcmx5VGVybWluYXRpb24oXG4gICAgICAgIHNlYXJjaEZ1bmN0aW9ucyxcbiAgICAgICAgaXNFeGFjdE1hdGNoLFxuICAgICAgICB7XG4gICAgICAgICAgb3BlcmF0aW9uTmFtZTogJ2VsZW1lbnQtdHlwZS1kZXRlY3Rpb24nLFxuICAgICAgICAgIHRpbWVvdXRBZnRlckV4YWN0TWF0Y2g6IDEwMDAsIC8vIFdhaXQgMSBzZWNvbmQgZm9yIG90aGVyIHNlYXJjaGVzIGFmdGVyIGV4YWN0IG1hdGNoXG4gICAgICAgICAgbWF4UGFyYWxsZWxTZWFyY2hlczogOCAvLyBMaW1pdCBjb25jdXJyZW50IHNlYXJjaGVzIHRvIGF2b2lkIG92ZXJ3aGVsbWluZyB0aGUgc3lzdGVtXG4gICAgICAgIH1cbiAgICAgICk7XG5cbiAgICAgIC8vIFBFUkZPUk1BTkNFIE9QVElNSVpBVElPTiAoVGFzayAjOCk6IEVuaGFuY2VkIGJhdGNoIG9wZXJhdGlvbiByZXBvcnRpbmdcbiAgICAgIGNvbnN0IGJhdGNoUmVzdWx0cyA9IHtcbiAgICAgICAgbmFtZSxcbiAgICAgICAgdG90YWxTZWFyY2hlczogc2VhcmNoUmVzdWx0cy50b3RhbFNlYXJjaGVzLFxuICAgICAgICBjb21wbGV0ZWRTZWFyY2hlczogc2VhcmNoUmVzdWx0cy5jb21wbGV0ZWRTZWFyY2hlcyxcbiAgICAgICAgbWF0Y2hlczogc2VhcmNoUmVzdWx0cy5tYXRjaGVzLmxlbmd0aCxcbiAgICAgICAgZmFpbHVyZXM6IHNlYXJjaFJlc3VsdHMuZmFpbHVyZXMubGVuZ3RoLFxuICAgICAgICBleGFjdE1hdGNoRm91bmQ6ICEhc2VhcmNoUmVzdWx0cy5leGFjdE1hdGNoLFxuICAgICAgICBleGFjdE1hdGNoVHlwZTogc2VhcmNoUmVzdWx0cy5leGFjdE1hdGNoPy50eXBlLFxuICAgICAgICBlYXJseVRlcm1pbmF0aW9uVHJpZ2dlcmVkOiBzZWFyY2hSZXN1bHRzLmVhcmx5VGVybWluYXRpb25UcmlnZ2VyZWQsXG4gICAgICAgIHBlcmZvcm1hbmNlR2Fpbjogc2VhcmNoUmVzdWx0cy5wZXJmb3JtYW5jZUdhaW4sXG4gICAgICAgIG1hdGNoZWRUeXBlczogc2VhcmNoUmVzdWx0cy5tYXRjaGVzLm1hcChtID0+IG0udHlwZSksXG4gICAgICAgIGZhaWxlZFR5cGVzOiBzZWFyY2hSZXN1bHRzLmZhaWx1cmVzLm1hcChmID0+IGVsZW1lbnRUeXBlc1tmLmluZGV4XSkuZmlsdGVyKEJvb2xlYW4pXG4gICAgICB9O1xuXG4gICAgICBsb2dnZXIuZGVidWcoJ0VsZW1lbnQgdHlwZSBkZXRlY3Rpb24gY29tcGxldGVkIHdpdGggZWFybHkgdGVybWluYXRpb24gb3B0aW1pemF0aW9uJywgYmF0Y2hSZXN1bHRzKTtcblxuICAgICAgLy8gUEVSRk9STUFOQ0UgT1BUSU1JWkFUSU9OIChUYXNrICM4KTogQ2xlYXIgcmVwb3J0aW5nIG9mIHBhcnRpYWwgZmFpbHVyZXNcbiAgICAgIGlmIChzZWFyY2hSZXN1bHRzLmZhaWx1cmVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1NvbWUgZWxlbWVudCB0eXBlIHNlYXJjaGVzIGZhaWxlZCBkdXJpbmcgYmF0Y2ggb3BlcmF0aW9uJywge1xuICAgICAgICAgIG5hbWUsXG4gICAgICAgICAgZmFpbHVyZXM6IHNlYXJjaFJlc3VsdHMuZmFpbHVyZXMubWFwKGYgPT4gKHtcbiAgICAgICAgICAgIHR5cGU6IGVsZW1lbnRUeXBlc1tmLmluZGV4XSB8fCAndW5rbm93bicsXG4gICAgICAgICAgICBlcnJvcjogZi5lcnJvci5zdWJzdHJpbmcoMCwgMTAwKSAvLyBUcnVuY2F0ZSBsb25nIGVycm9yIG1lc3NhZ2VzXG4gICAgICAgICAgfSkpLFxuICAgICAgICAgIHN1Y2Nlc3NSYXRlOiBgJHtzZWFyY2hSZXN1bHRzLmNvbXBsZXRlZFNlYXJjaGVzfS8ke3NlYXJjaFJlc3VsdHMudG90YWxTZWFyY2hlc31gLFxuICAgICAgICAgIGltcGFjdE9uUmVzdWx0czogc2VhcmNoUmVzdWx0cy5tYXRjaGVzLmxlbmd0aCA+IDAgXG4gICAgICAgICAgICA/ICdObyBpbXBhY3QgLSBtYXRjaGVzIGZvdW5kIGluIHN1Y2Nlc3NmdWwgc2VhcmNoZXMnIFxuICAgICAgICAgICAgOiAnUG90ZW50aWFsIGltcGFjdCAtIG5vIG1hdGNoZXMgZm91bmQnXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIElmIHdlIGhhdmUgZmFpbHVyZXMgYW5kIG5vIG1hdGNoZXMsIHByb3ZpZGUgYWN0aW9uYWJsZSBndWlkYW5jZVxuICAgICAgICBpZiAoc2VhcmNoUmVzdWx0cy5tYXRjaGVzLmxlbmd0aCA9PT0gMCAmJiBzZWFyY2hSZXN1bHRzLmZhaWx1cmVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBsb2dnZXIud2FybignQmF0Y2ggb3BlcmF0aW9uIGhhZCBmYWlsdXJlcyBhbmQgbm8gbWF0Y2hlcyBmb3VuZCcsIHtcbiAgICAgICAgICAgIG5hbWUsXG4gICAgICAgICAgICByZWNvbW1lbmRhdGlvbjogJ0NvbnNpZGVyIGNoZWNraW5nIGZpbGUgcGVybWlzc2lvbnMgb3IgcG9ydGZvbGlvIHN0cnVjdHVyZScsXG4gICAgICAgICAgICBmYWlsdXJlQ291bnQ6IHNlYXJjaFJlc3VsdHMuZmFpbHVyZXMubGVuZ3RoLFxuICAgICAgICAgICAgdG90YWxTZWFyY2hlczogc2VhcmNoUmVzdWx0cy50b3RhbFNlYXJjaGVzXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gTG9nIHBlcmZvcm1hbmNlIGdhaW5zIGZyb20gZWFybHkgdGVybWluYXRpb25cbiAgICAgIGlmIChzZWFyY2hSZXN1bHRzLmVhcmx5VGVybWluYXRpb25UcmlnZ2VyZWQpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ0Vhcmx5IHRlcm1pbmF0aW9uIG9wdGltaXphdGlvbiBhcHBsaWVkIHN1Y2Nlc3NmdWxseScsIHtcbiAgICAgICAgICBuYW1lLFxuICAgICAgICAgIGV4YWN0TWF0Y2hUeXBlOiBzZWFyY2hSZXN1bHRzLmV4YWN0TWF0Y2g/LnR5cGUsXG4gICAgICAgICAgcGVyZm9ybWFuY2VHYWluOiBzZWFyY2hSZXN1bHRzLnBlcmZvcm1hbmNlR2FpbixcbiAgICAgICAgICBzZWFyY2hlc0NvbXBsZXRlZDogc2VhcmNoUmVzdWx0cy5jb21wbGV0ZWRTZWFyY2hlcyxcbiAgICAgICAgICBzZWFyY2hlc1RvdGFsOiBzZWFyY2hSZXN1bHRzLnRvdGFsU2VhcmNoZXNcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGZvdW5kOiBzZWFyY2hSZXN1bHRzLm1hdGNoZXMubGVuZ3RoID4gMCxcbiAgICAgICAgbWF0Y2hlczogc2VhcmNoUmVzdWx0cy5tYXRjaGVzXG4gICAgICB9O1xuXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRXJyb3IgaW4gZWxlbWVudCB0eXBlIGRldGVjdGlvbicsIHtcbiAgICAgICAgbmFtZSxcbiAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIC8vIFJldHVybiBlbXB0eSByZXN1bHQgb24gZGV0ZWN0aW9uIGZhaWx1cmVcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGZvdW5kOiBmYWxzZSxcbiAgICAgICAgbWF0Y2hlczogW11cbiAgICAgIH07XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFVYIElNUFJPVkVNRU5UOiBHZW5lcmF0ZSBuYW1lIHN1Z2dlc3Rpb25zIGZvciBzaW1pbGFyIGNvbnRlbnRcbiAgICogUEVSRk9STUFOQ0UgT1BUSU1JWkFUSU9OIChUYXNrICM4KTogRW5oYW5jZWQgYmF0Y2ggb3BlcmF0aW9uIGhhbmRsaW5nIHdpdGggY2xlYXIgcGFydGlhbCBmYWlsdXJlIHJlcG9ydGluZ1xuICAgKiBIZWxwcyB1c2VycyBmaW5kIGNvbnRlbnQgd2hlbiBleGFjdCBtYXRjaGVzIGZhaWxcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVOYW1lU3VnZ2VzdGlvbnMoc2VhcmNoTmFtZTogc3RyaW5nKTogUHJvbWlzZTxBcnJheTx7bmFtZTogc3RyaW5nLCB0eXBlOiBzdHJpbmd9Pj4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzdWdnZXN0aW9uczogQXJyYXk8e25hbWU6IHN0cmluZywgdHlwZTogc3RyaW5nfT4gPSBbXTtcbiAgICAgIGNvbnN0IHNlYXJjaExvd2VyID0gc2VhcmNoTmFtZS50b0xvd2VyQ2FzZSgpO1xuICAgICAgY29uc3QgZWxlbWVudFR5cGVzID0gT2JqZWN0LnZhbHVlcyhFbGVtZW50VHlwZSk7XG4gICAgICBcbiAgICAgIC8vIFRyYWNrIGJhdGNoIG9wZXJhdGlvbiByZXN1bHRzIGZvciBiZXR0ZXIgZGlhZ25vc3RpY3NcbiAgICAgIGNvbnN0IGJhdGNoUmVzdWx0cyA9IHtcbiAgICAgICAgc2VhcmNoTmFtZSxcbiAgICAgICAgdG90YWxUeXBlczogZWxlbWVudFR5cGVzLmxlbmd0aCxcbiAgICAgICAgc3VjY2Vzc2Z1bFNjYW5zOiAwLFxuICAgICAgICBmYWlsZWRTY2FuczogMCxcbiAgICAgICAgZmFpbHVyZURldGFpbHM6IFtdIGFzIEFycmF5PHsgdHlwZTogRWxlbWVudFR5cGU7IGVycm9yOiBzdHJpbmcgfT4sXG4gICAgICAgIHRvdGFsU3VnZ2VzdGlvbnM6IDAsXG4gICAgICAgIHN1Z2dlc3Rpb25zQnlUeXBlOiB7fSBhcyBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+XG4gICAgICB9O1xuICAgICAgXG4gICAgICAvLyBQcm9jZXNzIGFsbCBlbGVtZW50IHR5cGVzIGZvciBzdWdnZXN0aW9uc1xuICAgICAgZm9yIChjb25zdCBlbGVtZW50VHlwZSBvZiBlbGVtZW50VHlwZXMpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBwb3J0Zm9saW9NYW5hZ2VyID0gUG9ydGZvbGlvTWFuYWdlci5nZXRJbnN0YW5jZSgpO1xuICAgICAgICAgIGNvbnN0IGVsZW1lbnREaXIgPSBwb3J0Zm9saW9NYW5hZ2VyLmdldEVsZW1lbnREaXIoZWxlbWVudFR5cGUpO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIEdldCBmaWxlcyBpbiB0aGlzIGRpcmVjdG9yeVxuICAgICAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgRmlsZURpc2NvdmVyeVV0aWwuZmluZEZpbGUoZWxlbWVudERpciwgJyonLCB7XG4gICAgICAgICAgICBleHRlbnNpb25zOiBbJy5tZCcsICcuanNvbicsICcueWFtbCcsICcueW1sJ10sXG4gICAgICAgICAgICBwYXJ0aWFsTWF0Y2g6IGZhbHNlLFxuICAgICAgICAgICAgY2FjaGVSZXN1bHRzOiB0cnVlXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgXG4gICAgICAgICAgbGV0IHR5cGVTdWdnZXN0aW9ucyA9IDA7XG4gICAgICAgICAgXG4gICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoZmlsZXMpKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGZpbGVQYXRoIG9mIGZpbGVzKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGJhc2VuYW1lID0gcGF0aC5iYXNlbmFtZShmaWxlUGF0aCwgcGF0aC5leHRuYW1lKGZpbGVQYXRoKSk7XG4gICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAvLyBDYWxjdWxhdGUgc2ltaWxhcml0eSB1c2luZyBzaW1wbGUgbWV0cmljc1xuICAgICAgICAgICAgICBpZiAodGhpcy5jYWxjdWxhdGVTaW1pbGFyaXR5KHNlYXJjaExvd2VyLCBiYXNlbmFtZS50b0xvd2VyQ2FzZSgpKSA+IFNFQVJDSF9DT05GSUcuTUlOX1NJTUlMQVJJVFlfU0NPUkUpIHtcbiAgICAgICAgICAgICAgICBzdWdnZXN0aW9ucy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgIG5hbWU6IGJhc2VuYW1lLFxuICAgICAgICAgICAgICAgICAgdHlwZTogZWxlbWVudFR5cGVcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB0eXBlU3VnZ2VzdGlvbnMrKztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAoZmlsZXMpIHtcbiAgICAgICAgICAgIGNvbnN0IGJhc2VuYW1lID0gcGF0aC5iYXNlbmFtZShmaWxlcywgcGF0aC5leHRuYW1lKGZpbGVzKSk7XG4gICAgICAgICAgICBpZiAodGhpcy5jYWxjdWxhdGVTaW1pbGFyaXR5KHNlYXJjaExvd2VyLCBiYXNlbmFtZS50b0xvd2VyQ2FzZSgpKSA+IFNFQVJDSF9DT05GSUcuTUlOX1NJTUlMQVJJVFlfU0NPUkUpIHtcbiAgICAgICAgICAgICAgc3VnZ2VzdGlvbnMucHVzaCh7XG4gICAgICAgICAgICAgICAgbmFtZTogYmFzZW5hbWUsXG4gICAgICAgICAgICAgICAgdHlwZTogZWxlbWVudFR5cGVcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIHR5cGVTdWdnZXN0aW9ucysrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBcbiAgICAgICAgICBiYXRjaFJlc3VsdHMuc3VjY2Vzc2Z1bFNjYW5zKys7XG4gICAgICAgICAgYmF0Y2hSZXN1bHRzLnN1Z2dlc3Rpb25zQnlUeXBlW2VsZW1lbnRUeXBlXSA9IHR5cGVTdWdnZXN0aW9ucztcbiAgICAgICAgICBcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAvLyBQRVJGT1JNQU5DRSBPUFRJTUlaQVRJT04gKFRhc2sgIzgpOiBUcmFjayBhbmQgcmVwb3J0IHBhcnRpYWwgZmFpbHVyZXNcbiAgICAgICAgICBiYXRjaFJlc3VsdHMuZmFpbGVkU2NhbnMrKztcbiAgICAgICAgICBiYXRjaFJlc3VsdHMuZmFpbHVyZURldGFpbHMucHVzaCh7XG4gICAgICAgICAgICB0eXBlOiBlbGVtZW50VHlwZSxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcilcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBMb2cgaW5kaXZpZHVhbCBmYWlsdXJlcyBmb3IgZGlhZ25vc3RpY3NcbiAgICAgICAgICBsb2dnZXIuZGVidWcoJ0ZhaWxlZCB0byBzY2FuIGVsZW1lbnQgdHlwZSBmb3Igc3VnZ2VzdGlvbnMnLCB7XG4gICAgICAgICAgICBlbGVtZW50VHlwZSxcbiAgICAgICAgICAgIHNlYXJjaE5hbWUsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgYmF0Y2hSZXN1bHRzLnRvdGFsU3VnZ2VzdGlvbnMgPSBzdWdnZXN0aW9ucy5sZW5ndGg7XG4gICAgICBcbiAgICAgIC8vIFBFUkZPUk1BTkNFIE9QVElNSVpBVElPTiAoVGFzayAjOCk6IENvbXByZWhlbnNpdmUgYmF0Y2ggb3BlcmF0aW9uIHJlcG9ydGluZ1xuICAgICAgbG9nZ2VyLmRlYnVnKCdOYW1lIHN1Z2dlc3Rpb24gYmF0Y2ggb3BlcmF0aW9uIGNvbXBsZXRlZCcsIHtcbiAgICAgICAgLi4uYmF0Y2hSZXN1bHRzLFxuICAgICAgICBzdWNjZXNzUmF0ZTogYCR7YmF0Y2hSZXN1bHRzLnN1Y2Nlc3NmdWxTY2Fuc30vJHtiYXRjaFJlc3VsdHMudG90YWxUeXBlc31gLFxuICAgICAgICAvLyBEb24ndCBsb2cgZnVsbCBmYWlsdXJlIGRldGFpbHMgYXQgZGVidWcgbGV2ZWwgdG8gYXZvaWQgc3BhbVxuICAgICAgICBoYXNGYWlsdXJlczogYmF0Y2hSZXN1bHRzLmZhaWxlZFNjYW5zID4gMFxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIC8vIFJlcG9ydCBmYWlsdXJlcyBjbGVhcmx5IGlmIHRoZXkgb2NjdXJyZWRcbiAgICAgIGlmIChiYXRjaFJlc3VsdHMuZmFpbGVkU2NhbnMgPiAwKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdTb21lIGVsZW1lbnQgdHlwZSBzY2FucyBmYWlsZWQgZHVyaW5nIG5hbWUgc3VnZ2VzdGlvbiBnZW5lcmF0aW9uJywge1xuICAgICAgICAgIHNlYXJjaE5hbWUsXG4gICAgICAgICAgZmFpbGVkVHlwZXM6IGJhdGNoUmVzdWx0cy5mYWlsdXJlRGV0YWlscy5tYXAoZiA9PiBmLnR5cGUpLFxuICAgICAgICAgIHN1Y2Nlc3NmdWxUeXBlczogYmF0Y2hSZXN1bHRzLnN1Y2Nlc3NmdWxTY2FucyxcbiAgICAgICAgICBpbXBhY3RPblJlc3VsdHM6IGJhdGNoUmVzdWx0cy50b3RhbFN1Z2dlc3Rpb25zID4gMCBcbiAgICAgICAgICAgID8gJ1BhcnRpYWwgaW1wYWN0IC0gc3VnZ2VzdGlvbnMgZm91bmQgZnJvbSBzdWNjZXNzZnVsIHNjYW5zJyBcbiAgICAgICAgICAgIDogJ1BvdGVudGlhbCBpbXBhY3QgLSBubyBzdWdnZXN0aW9ucyBnZW5lcmF0ZWQnLFxuICAgICAgICAgIHJlY29tbWVuZGF0aW9uOiBiYXRjaFJlc3VsdHMudG90YWxTdWdnZXN0aW9ucyA9PT0gMCAmJiBiYXRjaFJlc3VsdHMuZmFpbGVkU2NhbnMgPiAwXG4gICAgICAgICAgICA/ICdDaGVjayBwb3J0Zm9saW8gZGlyZWN0b3J5IHN0cnVjdHVyZSBhbmQgZmlsZSBwZXJtaXNzaW9ucydcbiAgICAgICAgICAgIDogJ1N1Z2dlc3Rpb24gZ2VuZXJhdGlvbiBwYXJ0aWFsbHkgc3VjY2Vzc2Z1bCBkZXNwaXRlIHNvbWUgZmFpbHVyZXMnXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBTb3J0IGJ5IHNpbWlsYXJpdHkgKGhpZ2hlciBpcyBiZXR0ZXIpIGFuZCByZXR1cm4gdG9wIHN1Z2dlc3Rpb25zXG4gICAgICBjb25zdCBzb3J0ZWRTdWdnZXN0aW9ucyA9IHN1Z2dlc3Rpb25zLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgICAgY29uc3Qgc2ltQSA9IHRoaXMuY2FsY3VsYXRlU2ltaWxhcml0eShzZWFyY2hMb3dlciwgYS5uYW1lLnRvTG93ZXJDYXNlKCkpO1xuICAgICAgICBjb25zdCBzaW1CID0gdGhpcy5jYWxjdWxhdGVTaW1pbGFyaXR5KHNlYXJjaExvd2VyLCBiLm5hbWUudG9Mb3dlckNhc2UoKSk7XG4gICAgICAgIHJldHVybiBzaW1CIC0gc2ltQTtcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICBsb2dnZXIuZGVidWcoJ05hbWUgc3VnZ2VzdGlvbnMgZ2VuZXJhdGVkIHN1Y2Nlc3NmdWxseScsIHtcbiAgICAgICAgc2VhcmNoTmFtZSxcbiAgICAgICAgdG90YWxTdWdnZXN0aW9uczogc29ydGVkU3VnZ2VzdGlvbnMubGVuZ3RoLFxuICAgICAgICB0b3BTdWdnZXN0aW9uczogc29ydGVkU3VnZ2VzdGlvbnMuc2xpY2UoMCwgMykubWFwKHMgPT4gcy5uYW1lKVxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHJldHVybiBzb3J0ZWRTdWdnZXN0aW9ucztcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIud2FybignRmFpbGVkIHRvIGdlbmVyYXRlIG5hbWUgc3VnZ2VzdGlvbnMgLSBiYXRjaCBvcGVyYXRpb24gZmFpbGVkIGNvbXBsZXRlbHknLCB7IFxuICAgICAgICBzZWFyY2hOYW1lLCBcbiAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKSxcbiAgICAgICAgcmVjb21tZW5kYXRpb246ICdDaGVjayBwb3J0Zm9saW8gc3RydWN0dXJlIGFuZCBwZXJtaXNzaW9ucydcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFNpbXBsZSBzaW1pbGFyaXR5IGNhbGN1bGF0aW9uIHVzaW5nIExldmVuc2h0ZWluLWxpa2UgYXBwcm9hY2hcbiAgICogUmV0dXJucyB2YWx1ZSBiZXR3ZWVuIDAgYW5kIDEsIHdoZXJlIDEgaXMgaWRlbnRpY2FsXG4gICAqL1xuICBwcml2YXRlIGNhbGN1bGF0ZVNpbWlsYXJpdHkoc3RyMTogc3RyaW5nLCBzdHIyOiBzdHJpbmcpOiBudW1iZXIge1xuICAgIC8vIEhhbmRsZSBleGFjdCBtYXRjaGVzXG4gICAgaWYgKHN0cjEgPT09IHN0cjIpIHJldHVybiAxO1xuICAgIFxuICAgIC8vIEhhbmRsZSBzdWJzdHJpbmcgbWF0Y2hlc1xuICAgIGlmIChzdHIxLmluY2x1ZGVzKHN0cjIpIHx8IHN0cjIuaW5jbHVkZXMoc3RyMSkpIHJldHVybiAwLjg7XG4gICAgXG4gICAgLy8gSGFuZGxlIHBhcnRpYWwgbWF0Y2hlc1xuICAgIGNvbnN0IGxvbmdlciA9IHN0cjEubGVuZ3RoID4gc3RyMi5sZW5ndGggPyBzdHIxIDogc3RyMjtcbiAgICBjb25zdCBzaG9ydGVyID0gc3RyMS5sZW5ndGggPiBzdHIyLmxlbmd0aCA/IHN0cjIgOiBzdHIxO1xuICAgIFxuICAgIGlmIChsb25nZXIubGVuZ3RoID09PSAwKSByZXR1cm4gMDtcbiAgICBcbiAgICAvLyBDb3VudCBjb21tb24gY2hhcmFjdGVyc1xuICAgIGxldCBjb21tb24gPSAwO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2hvcnRlci5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKGxvbmdlci5pbmNsdWRlcyhzaG9ydGVyW2ldKSkge1xuICAgICAgICBjb21tb24rKztcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGNvbW1vbiAvIGxvbmdlci5sZW5ndGg7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBVWCBJTVBST1ZFTUVOVDogU2F2ZSBlbGVtZW50IHdpdGggYXV0b21hdGljIHJldHJ5IGxvZ2ljIGZvciB0cmFuc2llbnQgZmFpbHVyZXNcbiAgICogSGFuZGxlcyBjb21tb24gR2l0SHViIEFQSSBpc3N1ZXMgbGlrZSByYXRlIGxpbWl0cyBhbmQgdGVtcG9yYXJ5IG5ldHdvcmsgcHJvYmxlbXNcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2F2ZUVsZW1lbnRXaXRoUmV0cnkoXG4gICAgYWRhcHRlcjogUG9ydGZvbGlvRWxlbWVudEFkYXB0ZXIsIFxuICAgIGVsZW1lbnROYW1lOiBzdHJpbmcsIFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSxcbiAgICBtYXhSZXRyaWVzOiBudW1iZXIgPSBSRVRSWV9DT05GSUcuTUFYX0FUVEVNUFRTXG4gICk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAgIGxldCBsYXN0RXJyb3I6IGFueSA9IG51bGw7XG4gICAgXG4gICAgZm9yIChsZXQgYXR0ZW1wdCA9IDE7IGF0dGVtcHQgPD0gbWF4UmV0cmllczsgYXR0ZW1wdCsrKSB7XG4gICAgICB0cnkge1xuICAgICAgICBsb2dnZXIuZGVidWcoYEF0dGVtcHRpbmcgdG8gc2F2ZSBlbGVtZW50IChhdHRlbXB0ICR7YXR0ZW1wdH0vJHttYXhSZXRyaWVzfSlgLCB7XG4gICAgICAgICAgZWxlbWVudE5hbWUsXG4gICAgICAgICAgZWxlbWVudFR5cGUsXG4gICAgICAgICAgYXR0ZW1wdFxuICAgICAgICB9KTtcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IGZpbGVVcmwgPSBhd2FpdCB0aGlzLnBvcnRmb2xpb01hbmFnZXIuc2F2ZUVsZW1lbnQoYWRhcHRlciwgdHJ1ZSk7XG4gICAgICAgIFxuICAgICAgICBpZiAoZmlsZVVybCkge1xuICAgICAgICAgIGlmIChhdHRlbXB0ID4gMSkge1xuICAgICAgICAgICAgbG9nZ2VyLmluZm8oYEVsZW1lbnQgc2F2ZWQgc3VjY2Vzc2Z1bGx5IGFmdGVyICR7YXR0ZW1wdH0gYXR0ZW1wdHNgLCB7XG4gICAgICAgICAgICAgIGVsZW1lbnROYW1lLFxuICAgICAgICAgICAgICBlbGVtZW50VHlwZSxcbiAgICAgICAgICAgICAgZmlsZVVybFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBmaWxlVXJsO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyBJZiBzYXZlRWxlbWVudCByZXR1cm5zIG51bGwsIHRyZWF0IGFzIGEgZmFpbHVyZSBidXQgZG9uJ3QgcmV0cnkgaW1tZWRpYXRlbHlcbiAgICAgICAgbGFzdEVycm9yID0gbmV3IEVycm9yKGBzYXZlRWxlbWVudCByZXR1cm5lZCBudWxsIG9uIGF0dGVtcHQgJHthdHRlbXB0fWApO1xuICAgICAgICBcbiAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgbGFzdEVycm9yID0gZXJyb3I7XG4gICAgICAgIGNvbnN0IGlzUmV0cnlhYmxlID0gdGhpcy5pc1JldHJ5YWJsZUVycm9yKGVycm9yKTtcbiAgICAgICAgXG4gICAgICAgIGxvZ2dlci53YXJuKGBTYXZlIGF0dGVtcHQgJHthdHRlbXB0fSBmYWlsZWRgLCB7XG4gICAgICAgICAgZWxlbWVudE5hbWUsXG4gICAgICAgICAgZWxlbWVudFR5cGUsXG4gICAgICAgICAgYXR0ZW1wdCxcbiAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSxcbiAgICAgICAgICBpc1JldHJ5YWJsZSxcbiAgICAgICAgICB3aWxsUmV0cnk6IGlzUmV0cnlhYmxlICYmIGF0dGVtcHQgPCBtYXhSZXRyaWVzXG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgLy8gSWYgdGhpcyBpcyBub3QgYSByZXRyeWFibGUgZXJyb3IsIGZhaWwgaW1tZWRpYXRlbHlcbiAgICAgICAgaWYgKCFpc1JldHJ5YWJsZSkge1xuICAgICAgICAgIGxvZ2dlci5lcnJvcignTm9uLXJldHJ5YWJsZSBlcnJvciBlbmNvdW50ZXJlZCwgYWJvcnRpbmcgcmV0cmllcycsIHtcbiAgICAgICAgICAgIGVsZW1lbnROYW1lLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2VcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLy8gSWYgd2UgaGF2ZSBtb3JlIGF0dGVtcHRzLCB3YWl0IGJlZm9yZSByZXRyeWluZ1xuICAgICAgICBpZiAoYXR0ZW1wdCA8IG1heFJldHJpZXMpIHtcbiAgICAgICAgICBjb25zdCBkZWxheSA9IGNhbGN1bGF0ZVJldHJ5RGVsYXkoYXR0ZW1wdCk7XG4gICAgICAgICAgbG9nZ2VyLmRlYnVnKGBXYWl0aW5nICR7ZGVsYXl9bXMgYmVmb3JlIHJldHJ5YCwgeyBhdHRlbXB0LCBkZWxheSB9KTtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgZGVsYXkpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBBbGwgYXR0ZW1wdHMgZmFpbGVkXG4gICAgbG9nZ2VyLmVycm9yKGBBbGwgJHttYXhSZXRyaWVzfSBzYXZlIGF0dGVtcHRzIGZhaWxlZGAsIHtcbiAgICAgIGVsZW1lbnROYW1lLFxuICAgICAgZWxlbWVudFR5cGUsXG4gICAgICBsYXN0RXJyb3I6IGxhc3RFcnJvcj8ubWVzc2FnZVxuICAgIH0pO1xuICAgIFxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIFxuICAvKipcbiAgICogRGV0ZXJtaW5lIGlmIGFuIGVycm9yIGlzIHdvcnRoIHJldHJ5aW5nXG4gICAqIFJldHJ5YWJsZTogbmV0d29yayBpc3N1ZXMsIHJhdGUgbGltaXRzLCB0ZW1wb3JhcnkgR2l0SHViIEFQSSBwcm9ibGVtc1xuICAgKiBOb24tcmV0cnlhYmxlOiBhdXRoZW50aWNhdGlvbiBpc3N1ZXMsIHZhbGlkYXRpb24gZXJyb3JzLCBwZXJtYW5lbnQgZmFpbHVyZXNcbiAgICovXG4gIHByaXZhdGUgaXNSZXRyeWFibGVFcnJvcihlcnJvcjogYW55KTogYm9vbGVhbiB7XG4gICAgY29uc3QgZXJyb3JNZXNzYWdlID0gZXJyb3I/Lm1lc3NhZ2U/LnRvTG93ZXJDYXNlKCkgfHwgJyc7XG4gICAgY29uc3QgZXJyb3JDb2RlID0gZXJyb3I/LmNvZGU7XG4gICAgY29uc3Qgc3RhdHVzQ29kZSA9IGVycm9yPy5zdGF0dXMgfHwgZXJyb3I/LnN0YXR1c0NvZGU7XG4gICAgXG4gICAgLy8gTmV0d29yayBhbmQgdGltZW91dCBlcnJvcnNcbiAgICBpZiAoZXJyb3JDb2RlID09PSAnRU5PVEZPVU5EJyB8fCBlcnJvckNvZGUgPT09ICdFQ09OTlJFU0VUJyB8fCBlcnJvckNvZGUgPT09ICdFVElNRURPVVQnKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgXG4gICAgLy8gR2l0SHViIEFQSSByYXRlIGxpbWl0c1xuICAgIGlmIChzdGF0dXNDb2RlID09PSA0MjkgfHwgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdyYXRlIGxpbWl0JykpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBcbiAgICAvLyBUZW1wb3JhcnkgR2l0SHViIEFQSSBpc3N1ZXNcbiAgICBpZiAoc3RhdHVzQ29kZSA+PSA1MDAgJiYgc3RhdHVzQ29kZSA8IDYwMCkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIFxuICAgIC8vIFRlbXBvcmFyeSBHaXRIdWIgQVBJIHByb2JsZW1zXG4gICAgaWYgKGVycm9yTWVzc2FnZS5pbmNsdWRlcygndGVtcG9yYXJpbHkgdW5hdmFpbGFibGUnKSB8fCBcbiAgICAgICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdzZXJ2aWNlIHVuYXZhaWxhYmxlJykgfHxcbiAgICAgICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdpbnRlcm5hbCBzZXJ2ZXIgZXJyb3InKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIFxuICAgIC8vIENvbm5lY3Rpb24gaXNzdWVzXG4gICAgaWYgKGVycm9yTWVzc2FnZS5pbmNsdWRlcygnY29ubmVjdGlvbicpICYmIFxuICAgICAgICAoZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCd0aW1lb3V0JykgfHwgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdyZXNldCcpKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIFxuICAgIC8vIERvbid0IHJldHJ5IGF1dGhlbnRpY2F0aW9uIG9yIHBlcm1pc3Npb24gaXNzdWVzXG4gICAgaWYgKHN0YXR1c0NvZGUgPT09IDQwMSB8fCBzdGF0dXNDb2RlID09PSA0MDMgfHwgXG4gICAgICAgIGVycm9yTWVzc2FnZS5pbmNsdWRlcygndW5hdXRob3JpemVkJykgfHwgXG4gICAgICAgIGVycm9yTWVzc2FnZS5pbmNsdWRlcygnZm9yYmlkZGVuJykgfHxcbiAgICAgICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdhdXRoZW50aWNhdGlvbicpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIFxuICAgIC8vIERvbid0IHJldHJ5IHZhbGlkYXRpb24gZXJyb3JzXG4gICAgaWYgKHN0YXR1c0NvZGUgPT09IDQwMCB8fCBzdGF0dXNDb2RlID09PSA0MjIgfHxcbiAgICAgICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdpbnZhbGlkJykgfHxcbiAgICAgICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCd2YWxpZGF0aW9uJykpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgXG4gICAgLy8gRGVmYXVsdCB0byBub3QgcmV0cnlpbmcgZm9yIHVua25vd24gZXJyb3JzIHRvIGF2b2lkIGluZmluaXRlIGxvb3BzXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59Il19