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