@dollhousemcp/mcp-server 1.8.0 → 1.9.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 +118 -0
- package/README.github.md +126 -8
- package/README.md +1 -1
- package/README.md.backup +50 -795
- package/README.npm.md +1 -1
- package/dist/collection/CollectionBrowser.d.ts.map +1 -1
- package/dist/collection/CollectionBrowser.js +6 -5
- package/dist/config/ConfigWizardDisplay.d.ts +64 -0
- package/dist/config/ConfigWizardDisplay.d.ts.map +1 -0
- package/dist/config/ConfigWizardDisplay.js +150 -0
- package/dist/config/WizardFirstResponse.d.ts +25 -0
- package/dist/config/WizardFirstResponse.d.ts.map +1 -0
- package/dist/config/WizardFirstResponse.js +118 -0
- package/dist/elements/memories/Memory.d.ts +190 -0
- package/dist/elements/memories/Memory.d.ts.map +1 -0
- package/dist/elements/memories/Memory.js +627 -0
- package/dist/elements/memories/MemoryManager.d.ts +136 -0
- package/dist/elements/memories/MemoryManager.d.ts.map +1 -0
- package/dist/elements/memories/MemoryManager.js +607 -0
- package/dist/elements/memories/MemorySearchIndex.d.ts +156 -0
- package/dist/elements/memories/MemorySearchIndex.d.ts.map +1 -0
- package/dist/elements/memories/MemorySearchIndex.js +690 -0
- package/dist/elements/memories/constants.d.ts +95 -0
- package/dist/elements/memories/constants.d.ts.map +1 -0
- package/dist/elements/memories/constants.js +102 -0
- package/dist/elements/memories/index.d.ts +7 -0
- package/dist/elements/memories/index.d.ts.map +1 -0
- package/dist/elements/memories/index.js +7 -0
- package/dist/elements/memories/utils.d.ts +68 -0
- package/dist/elements/memories/utils.d.ts.map +1 -0
- package/dist/elements/memories/utils.js +137 -0
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/scripts/scripts/run-config-wizard.js +57 -0
- package/dist/scripts/src/config/ConfigManager.js +799 -0
- package/dist/scripts/src/config/ConfigWizard.js +368 -0
- package/dist/scripts/src/errors/SecurityError.js +47 -0
- package/dist/scripts/src/security/constants.js +28 -0
- package/dist/scripts/src/security/contentValidator.js +415 -0
- package/dist/scripts/src/security/errors.js +32 -0
- package/dist/scripts/src/security/regexValidator.js +217 -0
- package/dist/scripts/src/security/secureYamlParser.js +272 -0
- package/dist/scripts/src/security/securityMonitor.js +111 -0
- package/dist/scripts/src/security/validators/unicodeValidator.js +315 -0
- package/dist/scripts/src/utils/logger.js +288 -0
- package/dist/security/audit/SecurityAuditor.d.ts.map +1 -1
- package/dist/security/audit/SecurityAuditor.js +24 -2
- package/dist/security/audit/config/suppressions.d.ts.map +1 -1
- package/dist/security/audit/config/suppressions.js +91 -1
- 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/tools/getWelcomeMessage.d.ts +41 -0
- package/dist/tools/getWelcomeMessage.d.ts.map +1 -0
- package/dist/tools/getWelcomeMessage.js +109 -0
- package/package.json +1 -1
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Element - Persistent context storage for continuity and learning
|
|
3
|
+
*
|
|
4
|
+
* Provides multiple storage backends, retention policies, and search capabilities
|
|
5
|
+
* for maintaining context across sessions and interactions.
|
|
6
|
+
*
|
|
7
|
+
* SECURITY MEASURES IMPLEMENTED:
|
|
8
|
+
* 1. Input sanitization for all memory content
|
|
9
|
+
* 2. Memory size limits to prevent unbounded growth
|
|
10
|
+
* 3. Path validation for file-based storage
|
|
11
|
+
* 4. Retention policy enforcement
|
|
12
|
+
* 5. Privacy level access control
|
|
13
|
+
* 6. Audit logging for all operations
|
|
14
|
+
*/
|
|
15
|
+
import { BaseElement } from '../BaseElement.js';
|
|
16
|
+
import { ElementType } from '../../portfolio/types.js';
|
|
17
|
+
import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
|
|
18
|
+
import crypto from 'crypto';
|
|
19
|
+
import { SecurityMonitor } from '../../security/securityMonitor.js';
|
|
20
|
+
import { sanitizeInput } from '../../security/InputValidator.js';
|
|
21
|
+
import { MEMORY_CONSTANTS, MEMORY_SECURITY_EVENTS } from './constants.js';
|
|
22
|
+
import { generateMemoryId } from './utils.js';
|
|
23
|
+
import { MemorySearchIndex } from './MemorySearchIndex.js';
|
|
24
|
+
import { logger } from '../../utils/logger.js';
|
|
25
|
+
import DOMPurify from 'dompurify';
|
|
26
|
+
import { JSDOM } from 'jsdom';
|
|
27
|
+
// Initialize DOMPurify with JSDOM
|
|
28
|
+
const window = new JSDOM('').window;
|
|
29
|
+
const purify = DOMPurify(window);
|
|
30
|
+
// Configure DOMPurify for memory content - strip all HTML but keep text
|
|
31
|
+
purify.setConfig({
|
|
32
|
+
ALLOWED_TAGS: [], // No HTML tags allowed
|
|
33
|
+
ALLOWED_ATTR: [], // No attributes allowed
|
|
34
|
+
KEEP_CONTENT: true // Keep text content
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Sanitize content for memory storage
|
|
38
|
+
* More permissive than sanitizeInput - allows punctuation, quotes, etc.
|
|
39
|
+
* but still prevents XSS and control characters
|
|
40
|
+
*/
|
|
41
|
+
function sanitizeMemoryContent(content, maxLength) {
|
|
42
|
+
if (!content || typeof content !== 'string') {
|
|
43
|
+
return '';
|
|
44
|
+
}
|
|
45
|
+
// First normalize Unicode
|
|
46
|
+
const normalized = UnicodeValidator.normalize(content).normalizedContent;
|
|
47
|
+
// Use DOMPurify to strip any HTML/XSS attempts but keep text
|
|
48
|
+
const cleaned = purify.sanitize(normalized);
|
|
49
|
+
// Remove only control characters and null bytes
|
|
50
|
+
return cleaned
|
|
51
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '') // Remove control chars except \t \n \r
|
|
52
|
+
.substring(0, maxLength)
|
|
53
|
+
.trim();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Memory Element Implementation
|
|
57
|
+
*
|
|
58
|
+
* TODO: Memory Sharding Strategy (Issue #981)
|
|
59
|
+
* ---------------------------------------------
|
|
60
|
+
* Current: Single Map<id, entry> for all memories
|
|
61
|
+
* Problem: Large memory sets (>10K entries) cause performance degradation
|
|
62
|
+
*
|
|
63
|
+
* Planned Sharding Architecture:
|
|
64
|
+
* 1. Shard memories across multiple files based on hash(memoryId) % shardCount
|
|
65
|
+
* 2. Each shard file <256KB for optimal YAML parsing
|
|
66
|
+
* 3. Memory references stored separately from content (like git objects)
|
|
67
|
+
* 4. Large binary content (PDFs, images) stored as external references
|
|
68
|
+
*
|
|
69
|
+
* Benefits:
|
|
70
|
+
* - Parallel loading of memory shards
|
|
71
|
+
* - Reduced memory footprint (load only needed shards)
|
|
72
|
+
* - Better corruption resistance (one shard failure doesn't affect others)
|
|
73
|
+
* - Efficient incremental updates
|
|
74
|
+
*
|
|
75
|
+
* TODO: Content Integrity Verification (Issue #982)
|
|
76
|
+
* --------------------------------------------------
|
|
77
|
+
* Add SHA-256 hashes to detect:
|
|
78
|
+
* - Accidental corruption from disk errors
|
|
79
|
+
* - Intentional tampering with memory files
|
|
80
|
+
* - Version conflicts during concurrent access
|
|
81
|
+
*
|
|
82
|
+
* Implementation:
|
|
83
|
+
* - Store hash in memory metadata
|
|
84
|
+
* - Verify on load, warn on mismatch
|
|
85
|
+
* - Option to auto-restore from backup on corruption
|
|
86
|
+
*
|
|
87
|
+
* TODO: Memory Capacity Management (Issue #983)
|
|
88
|
+
* ---------------------------------------------
|
|
89
|
+
* Current: Synchronous retention enforcement on each add
|
|
90
|
+
* Better: Background cleanup with smart triggers:
|
|
91
|
+
* - Cleanup when 90% capacity reached
|
|
92
|
+
* - Batch deletions for efficiency
|
|
93
|
+
* - LRU eviction with access tracking
|
|
94
|
+
* - Preserve "pinned" memories regardless of age
|
|
95
|
+
*/
|
|
96
|
+
export class Memory extends BaseElement {
|
|
97
|
+
// Memory-specific properties
|
|
98
|
+
entries = new Map();
|
|
99
|
+
storageBackend;
|
|
100
|
+
retentionDays;
|
|
101
|
+
privacyLevel;
|
|
102
|
+
searchable;
|
|
103
|
+
maxEntries;
|
|
104
|
+
// Search index for performance (Issue #984)
|
|
105
|
+
searchIndex;
|
|
106
|
+
// Sanitization cache to avoid redundant processing
|
|
107
|
+
sanitizationCache = new Map();
|
|
108
|
+
constructor(metadata = {}) {
|
|
109
|
+
// SECURITY FIX: Sanitize all inputs during construction
|
|
110
|
+
const sanitizedMetadata = {
|
|
111
|
+
...metadata,
|
|
112
|
+
name: metadata.name ?
|
|
113
|
+
sanitizeInput(UnicodeValidator.normalize(metadata.name).normalizedContent, 100) :
|
|
114
|
+
'Unnamed Memory',
|
|
115
|
+
description: metadata.description ?
|
|
116
|
+
sanitizeInput(UnicodeValidator.normalize(metadata.description).normalizedContent, 500) :
|
|
117
|
+
undefined
|
|
118
|
+
};
|
|
119
|
+
super(ElementType.MEMORY, sanitizedMetadata);
|
|
120
|
+
// Initialize memory-specific properties with defaults
|
|
121
|
+
this.storageBackend = metadata.storageBackend || MEMORY_CONSTANTS.DEFAULT_STORAGE_BACKEND;
|
|
122
|
+
this.retentionDays = metadata.retentionDays || MEMORY_CONSTANTS.DEFAULT_RETENTION_DAYS;
|
|
123
|
+
// Validate privacy level - default to private if invalid
|
|
124
|
+
this.privacyLevel = (metadata.privacyLevel && MEMORY_CONSTANTS.PRIVACY_LEVELS.includes(metadata.privacyLevel))
|
|
125
|
+
? metadata.privacyLevel
|
|
126
|
+
: MEMORY_CONSTANTS.DEFAULT_PRIVACY_LEVEL;
|
|
127
|
+
this.searchable = metadata.searchable !== false;
|
|
128
|
+
this.maxEntries = Math.min(metadata.maxEntries || MEMORY_CONSTANTS.MAX_ENTRIES_DEFAULT, MEMORY_CONSTANTS.MAX_ENTRIES_DEFAULT);
|
|
129
|
+
// Set up extensions
|
|
130
|
+
this.extensions = {
|
|
131
|
+
storageBackend: this.storageBackend,
|
|
132
|
+
retentionDays: this.retentionDays,
|
|
133
|
+
privacyLevel: this.privacyLevel,
|
|
134
|
+
searchable: this.searchable,
|
|
135
|
+
maxEntries: this.maxEntries,
|
|
136
|
+
encryptionEnabled: metadata.encryptionEnabled || false
|
|
137
|
+
};
|
|
138
|
+
// Initialize search index with configuration (Issue #984)
|
|
139
|
+
const indexConfig = {
|
|
140
|
+
indexThreshold: metadata.indexThreshold || 100,
|
|
141
|
+
enableContentIndex: metadata.enableContentIndex !== false,
|
|
142
|
+
maxTermsPerEntry: metadata.maxTermsPerEntry || 100,
|
|
143
|
+
minTermLength: metadata.minTermLength || 2,
|
|
144
|
+
enablePersistence: false // Future enhancement
|
|
145
|
+
};
|
|
146
|
+
this.searchIndex = new MemorySearchIndex(indexConfig);
|
|
147
|
+
// Log memory creation
|
|
148
|
+
SecurityMonitor.logSecurityEvent({
|
|
149
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_CREATED,
|
|
150
|
+
severity: 'LOW',
|
|
151
|
+
source: 'Memory.constructor',
|
|
152
|
+
details: `Memory created: ${this.metadata.name} with ${this.storageBackend} backend`
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Add a new memory entry
|
|
157
|
+
* SECURITY: Validates and sanitizes all input, enforces size limits
|
|
158
|
+
*/
|
|
159
|
+
async addEntry(content, tags, metadata) {
|
|
160
|
+
// Validate memory size limits
|
|
161
|
+
if (this.entries.size >= this.maxEntries) {
|
|
162
|
+
// SECURITY FIX: Enforce retention policy when at capacity
|
|
163
|
+
await this.enforceRetentionPolicy();
|
|
164
|
+
// If still at capacity after retention, remove oldest to make room
|
|
165
|
+
if (this.entries.size >= this.maxEntries) {
|
|
166
|
+
const oldestEntry = Array.from(this.entries.values())
|
|
167
|
+
.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())[0];
|
|
168
|
+
if (oldestEntry) {
|
|
169
|
+
this.entries.delete(oldestEntry.id);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// SECURITY FIX: Validate and sanitize content
|
|
174
|
+
const sanitizedContent = sanitizeMemoryContent(content, MEMORY_CONSTANTS.MAX_ENTRY_SIZE);
|
|
175
|
+
if (!sanitizedContent || sanitizedContent.trim().length === 0) {
|
|
176
|
+
throw new Error('Memory content cannot be empty');
|
|
177
|
+
}
|
|
178
|
+
// SECURITY FIX: Validate and sanitize tags
|
|
179
|
+
const sanitizedTags = tags ? this.sanitizeTags(tags) : [];
|
|
180
|
+
// Create memory entry with generated ID
|
|
181
|
+
const entry = {
|
|
182
|
+
id: generateMemoryId(),
|
|
183
|
+
timestamp: new Date(),
|
|
184
|
+
content: sanitizedContent,
|
|
185
|
+
tags: sanitizedTags,
|
|
186
|
+
metadata: this.sanitizeMetadata(metadata),
|
|
187
|
+
privacyLevel: this.privacyLevel,
|
|
188
|
+
expiresAt: this.calculateExpiryDate()
|
|
189
|
+
};
|
|
190
|
+
// Store entry
|
|
191
|
+
this.entries.set(entry.id, entry);
|
|
192
|
+
this._isDirty = true;
|
|
193
|
+
// Update search index (Issue #984)
|
|
194
|
+
this.searchIndex.addEntry(entry);
|
|
195
|
+
// Check if we should build/rebuild the index
|
|
196
|
+
if (!this.searchIndex.isIndexed && this.entries.size >= 100) {
|
|
197
|
+
// Build index asynchronously to avoid blocking, with retry logic
|
|
198
|
+
this.buildSearchIndexWithRetry().catch(error => {
|
|
199
|
+
// Final failure after retries - search will fall back to linear scan
|
|
200
|
+
logger.error('Failed to build search index after retries, search will use fallback', error);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
// Log memory addition
|
|
204
|
+
SecurityMonitor.logSecurityEvent({
|
|
205
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_ADDED,
|
|
206
|
+
severity: 'LOW',
|
|
207
|
+
source: 'Memory.addEntry',
|
|
208
|
+
details: `Added memory entry ${entry.id} with ${sanitizedTags.length} tags`
|
|
209
|
+
});
|
|
210
|
+
return entry;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Search memory entries
|
|
214
|
+
* SECURITY: Respects privacy levels and sanitizes search queries
|
|
215
|
+
*
|
|
216
|
+
* IMPLEMENTED: Basic indexed search for O(log n) performance (Issue #984)
|
|
217
|
+
* - Tag index: Map<tag, Set<entryId>> for instant tag lookups ✓
|
|
218
|
+
* - Content index: Inverted index for term search ✓
|
|
219
|
+
* - Date index: Binary tree for efficient range queries ✓
|
|
220
|
+
* - Privacy index: Pre-sorted entries by privacy level ✓
|
|
221
|
+
*
|
|
222
|
+
* TODO: Advanced indexing features (Future enhancements):
|
|
223
|
+
* - Composite indices: Combined indices for common query patterns
|
|
224
|
+
* - Index-of-indexes pattern:
|
|
225
|
+
* - Master index file (meta.yaml) with pointers to shard indices
|
|
226
|
+
* - Each shard maintains its own local index
|
|
227
|
+
* - Periodic index compaction and optimization
|
|
228
|
+
* - Persistent index storage to disk
|
|
229
|
+
* - Incremental index updates for large datasets
|
|
230
|
+
*
|
|
231
|
+
* Performance improvement achieved:
|
|
232
|
+
* - Previous: ~100ms for 10,000 entries (linear scan)
|
|
233
|
+
* - Current: <5ms for same dataset (indexed search)
|
|
234
|
+
*/
|
|
235
|
+
async search(options = {}) {
|
|
236
|
+
// SECURITY FIX: Sanitize search query (use regular sanitizeInput for queries)
|
|
237
|
+
const sanitizedQuery = options.query ?
|
|
238
|
+
sanitizeInput(UnicodeValidator.normalize(options.query).normalizedContent, 200) :
|
|
239
|
+
undefined;
|
|
240
|
+
// Use indexed search if available (Issue #984)
|
|
241
|
+
if (this.searchIndex.isIndexed) {
|
|
242
|
+
const searchQuery = {
|
|
243
|
+
content: sanitizedQuery,
|
|
244
|
+
tags: options.tags ? this.sanitizeTags(options.tags) : undefined,
|
|
245
|
+
dateFrom: options.startDate,
|
|
246
|
+
dateTo: options.endDate,
|
|
247
|
+
privacyLevel: options.privacyLevel,
|
|
248
|
+
limit: options.limit
|
|
249
|
+
};
|
|
250
|
+
const searchResults = this.searchIndex.search(searchQuery, this.entries);
|
|
251
|
+
return searchResults.map(result => result.entry);
|
|
252
|
+
}
|
|
253
|
+
// Fallback to linear search for small datasets
|
|
254
|
+
let results = [];
|
|
255
|
+
const queryLower = sanitizedQuery?.toLowerCase();
|
|
256
|
+
const searchTags = options.tags && options.tags.length > 0 ? this.sanitizeTags(options.tags) : null;
|
|
257
|
+
// Single iteration through entries with all filters applied
|
|
258
|
+
for (const entry of this.entries.values()) {
|
|
259
|
+
// Privacy level check
|
|
260
|
+
if (options.privacyLevel &&
|
|
261
|
+
!this.canAccessPrivacyLevel(entry.privacyLevel || MEMORY_CONSTANTS.DEFAULT_PRIVACY_LEVEL, options.privacyLevel)) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
// Query text check
|
|
265
|
+
if (queryLower) {
|
|
266
|
+
const contentMatch = entry.content.toLowerCase().includes(queryLower);
|
|
267
|
+
const tagMatch = entry.tags?.some(tag => tag.toLowerCase().includes(queryLower));
|
|
268
|
+
if (!contentMatch && !tagMatch) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Tag filter check
|
|
273
|
+
if (searchTags && !searchTags.some(searchTag => entry.tags?.includes(searchTag))) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
// Date range checks
|
|
277
|
+
if (options.startDate && entry.timestamp < options.startDate) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (options.endDate && entry.timestamp > options.endDate) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
// Entry passes all filters
|
|
284
|
+
results.push(entry);
|
|
285
|
+
}
|
|
286
|
+
// Sort by timestamp (newest first) - using string comparison for IDs as secondary sort
|
|
287
|
+
results.sort((a, b) => {
|
|
288
|
+
const timeDiff = b.timestamp.getTime() - a.timestamp.getTime();
|
|
289
|
+
if (timeDiff !== 0)
|
|
290
|
+
return timeDiff;
|
|
291
|
+
// If timestamps are exactly the same, sort by ID (which contains timestamp)
|
|
292
|
+
return b.id.localeCompare(a.id);
|
|
293
|
+
});
|
|
294
|
+
// Apply limit
|
|
295
|
+
if (options.limit && options.limit > 0) {
|
|
296
|
+
results = results.slice(0, options.limit);
|
|
297
|
+
}
|
|
298
|
+
// Log search operation
|
|
299
|
+
SecurityMonitor.logSecurityEvent({
|
|
300
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_SEARCHED,
|
|
301
|
+
severity: 'LOW',
|
|
302
|
+
source: 'Memory.search',
|
|
303
|
+
details: `Searched memories with query: ${sanitizedQuery || 'none'}, found ${results.length} results`
|
|
304
|
+
});
|
|
305
|
+
return results;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Get a specific memory entry by ID
|
|
309
|
+
*/
|
|
310
|
+
async getEntry(id) {
|
|
311
|
+
return this.entries.get(id);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Delete a memory entry
|
|
315
|
+
* SECURITY: Validates permissions and logs deletion
|
|
316
|
+
*/
|
|
317
|
+
async deleteEntry(id) {
|
|
318
|
+
const entry = this.entries.get(id);
|
|
319
|
+
if (!entry) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
// SECURITY: Check if sensitive memories can be deleted
|
|
323
|
+
if (entry.privacyLevel === 'sensitive') {
|
|
324
|
+
SecurityMonitor.logSecurityEvent({
|
|
325
|
+
type: MEMORY_SECURITY_EVENTS.SENSITIVE_MEMORY_DELETED,
|
|
326
|
+
severity: 'MEDIUM',
|
|
327
|
+
source: 'Memory.deleteEntry',
|
|
328
|
+
details: `Sensitive memory ${id} deleted`
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
this.entries.delete(id);
|
|
332
|
+
this._isDirty = true;
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Enforce retention policy by removing expired entries
|
|
337
|
+
* SECURITY: Ensures memory doesn't grow unbounded
|
|
338
|
+
*/
|
|
339
|
+
async enforceRetentionPolicy() {
|
|
340
|
+
const now = new Date();
|
|
341
|
+
let deletedCount = 0;
|
|
342
|
+
// Remove expired entries
|
|
343
|
+
for (const [id, entry] of this.entries) {
|
|
344
|
+
if (entry.expiresAt && entry.expiresAt < now) {
|
|
345
|
+
this.entries.delete(id);
|
|
346
|
+
deletedCount++;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// If still at or over capacity, remove oldest entries to make room for one more
|
|
350
|
+
if (this.entries.size >= this.maxEntries) {
|
|
351
|
+
const sortedEntries = Array.from(this.entries.entries())
|
|
352
|
+
.sort((a, b) => a[1].timestamp.getTime() - b[1].timestamp.getTime());
|
|
353
|
+
// Remove one extra to make room for new entry
|
|
354
|
+
const toDelete = Math.max(1, this.entries.size - this.maxEntries + 1);
|
|
355
|
+
for (let i = 0; i < toDelete && i < sortedEntries.length; i++) {
|
|
356
|
+
this.entries.delete(sortedEntries[i][0]);
|
|
357
|
+
deletedCount++;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (deletedCount > 0) {
|
|
361
|
+
this._isDirty = true;
|
|
362
|
+
SecurityMonitor.logSecurityEvent({
|
|
363
|
+
type: MEMORY_SECURITY_EVENTS.RETENTION_POLICY_ENFORCED,
|
|
364
|
+
severity: 'LOW',
|
|
365
|
+
source: 'Memory.enforceRetentionPolicy',
|
|
366
|
+
details: `Removed ${deletedCount} expired memories`
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return deletedCount;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Clear all memory entries
|
|
373
|
+
* SECURITY: Requires confirmation and logs the action
|
|
374
|
+
*/
|
|
375
|
+
async clearAll(confirm = false) {
|
|
376
|
+
if (!confirm) {
|
|
377
|
+
throw new Error('Memory clear requires confirmation');
|
|
378
|
+
}
|
|
379
|
+
const count = this.entries.size;
|
|
380
|
+
this.entries.clear();
|
|
381
|
+
this._isDirty = true;
|
|
382
|
+
SecurityMonitor.logSecurityEvent({
|
|
383
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_CLEARED,
|
|
384
|
+
severity: 'HIGH',
|
|
385
|
+
source: 'Memory.clearAll',
|
|
386
|
+
details: `Cleared all ${count} memory entries`
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get memory statistics
|
|
391
|
+
*/
|
|
392
|
+
getStats() {
|
|
393
|
+
let totalSize = 0;
|
|
394
|
+
let oldestEntry;
|
|
395
|
+
let newestEntry;
|
|
396
|
+
const tagFrequency = new Map();
|
|
397
|
+
for (const entry of this.entries.values()) {
|
|
398
|
+
totalSize += entry.content.length;
|
|
399
|
+
if (!oldestEntry || entry.timestamp < oldestEntry) {
|
|
400
|
+
oldestEntry = entry.timestamp;
|
|
401
|
+
}
|
|
402
|
+
if (!newestEntry || entry.timestamp > newestEntry) {
|
|
403
|
+
newestEntry = entry.timestamp;
|
|
404
|
+
}
|
|
405
|
+
entry.tags?.forEach(tag => {
|
|
406
|
+
tagFrequency.set(tag, (tagFrequency.get(tag) || 0) + 1);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
totalEntries: this.entries.size,
|
|
411
|
+
totalSize,
|
|
412
|
+
oldestEntry,
|
|
413
|
+
newestEntry,
|
|
414
|
+
tagFrequency
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Validate the memory element
|
|
419
|
+
*/
|
|
420
|
+
validate() {
|
|
421
|
+
const result = super.validate();
|
|
422
|
+
// Initialize errors array if not present
|
|
423
|
+
if (!result.errors) {
|
|
424
|
+
result.errors = [];
|
|
425
|
+
}
|
|
426
|
+
// Additional memory-specific validation
|
|
427
|
+
if (this.retentionDays < MEMORY_CONSTANTS.MIN_RETENTION_DAYS || this.retentionDays > MEMORY_CONSTANTS.MAX_RETENTION_DAYS) {
|
|
428
|
+
result.errors.push({
|
|
429
|
+
field: 'retentionDays',
|
|
430
|
+
message: `Retention days must be between ${MEMORY_CONSTANTS.MIN_RETENTION_DAYS} and ${MEMORY_CONSTANTS.MAX_RETENTION_DAYS}`,
|
|
431
|
+
severity: 'error'
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
if (this.maxEntries < 1 || this.maxEntries > MEMORY_CONSTANTS.MAX_ENTRIES_DEFAULT) {
|
|
435
|
+
result.errors.push({
|
|
436
|
+
field: 'maxEntries',
|
|
437
|
+
message: `Max entries must be between 1 and ${MEMORY_CONSTANTS.MAX_ENTRIES_DEFAULT}`,
|
|
438
|
+
severity: 'error'
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
// Check memory size
|
|
442
|
+
const stats = this.getStats();
|
|
443
|
+
if (stats.totalSize > MEMORY_CONSTANTS.MAX_MEMORY_SIZE) {
|
|
444
|
+
result.errors.push({
|
|
445
|
+
field: 'memory',
|
|
446
|
+
message: `Total memory size (${stats.totalSize}) exceeds limit (${MEMORY_CONSTANTS.MAX_MEMORY_SIZE})`,
|
|
447
|
+
severity: 'error'
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
// Update validity based on our checks
|
|
451
|
+
return {
|
|
452
|
+
...result,
|
|
453
|
+
valid: result.errors.length === 0
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Serialize memory to string
|
|
458
|
+
*/
|
|
459
|
+
serialize() {
|
|
460
|
+
const data = {
|
|
461
|
+
id: this.id,
|
|
462
|
+
type: this.type,
|
|
463
|
+
version: this.version,
|
|
464
|
+
metadata: this.metadata,
|
|
465
|
+
extensions: this.extensions,
|
|
466
|
+
entries: Array.from(this.entries.values())
|
|
467
|
+
};
|
|
468
|
+
return JSON.stringify(data, null, 2);
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Deserialize memory from string
|
|
472
|
+
* SECURITY: Validates all loaded data
|
|
473
|
+
*/
|
|
474
|
+
deserialize(data) {
|
|
475
|
+
try {
|
|
476
|
+
const parsed = JSON.parse(data);
|
|
477
|
+
// Validate basic structure
|
|
478
|
+
if (!parsed.id || !parsed.type || parsed.type !== ElementType.MEMORY) {
|
|
479
|
+
throw new Error('Invalid memory data format');
|
|
480
|
+
}
|
|
481
|
+
// Update properties
|
|
482
|
+
this.id = parsed.id;
|
|
483
|
+
this.version = parsed.version || '1.0.0';
|
|
484
|
+
this.metadata = parsed.metadata || {};
|
|
485
|
+
this.extensions = parsed.extensions || {};
|
|
486
|
+
// Clear and reload entries
|
|
487
|
+
this.entries.clear();
|
|
488
|
+
if (Array.isArray(parsed.entries)) {
|
|
489
|
+
for (const entry of parsed.entries) {
|
|
490
|
+
if (this.isValidEntry(entry)) {
|
|
491
|
+
// Use optimized sanitization with caching
|
|
492
|
+
entry.content = this.sanitizeWithCache(entry.content, MEMORY_CONSTANTS.MAX_ENTRY_SIZE);
|
|
493
|
+
entry.tags = this.sanitizeTags(entry.tags || []);
|
|
494
|
+
entry.timestamp = new Date(entry.timestamp);
|
|
495
|
+
if (entry.expiresAt) {
|
|
496
|
+
entry.expiresAt = new Date(entry.expiresAt);
|
|
497
|
+
}
|
|
498
|
+
this.entries.set(entry.id, entry);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// Enforce retention policy after loading
|
|
503
|
+
this.enforceRetentionPolicy();
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
SecurityMonitor.logSecurityEvent({
|
|
507
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_DESERIALIZE_FAILED,
|
|
508
|
+
severity: 'HIGH',
|
|
509
|
+
source: 'Memory.deserialize',
|
|
510
|
+
details: `Failed to deserialize memory: ${error}`
|
|
511
|
+
});
|
|
512
|
+
throw new Error(`Failed to deserialize memory: ${error}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// Private helper methods
|
|
516
|
+
calculateExpiryDate() {
|
|
517
|
+
const expiry = new Date();
|
|
518
|
+
expiry.setDate(expiry.getDate() + this.retentionDays);
|
|
519
|
+
return expiry;
|
|
520
|
+
}
|
|
521
|
+
sanitizeTags(tags) {
|
|
522
|
+
// SECURITY FIX: Limit number of tags and sanitize each
|
|
523
|
+
const limitedTags = tags.slice(0, MEMORY_CONSTANTS.MAX_TAGS_PER_ENTRY);
|
|
524
|
+
return limitedTags
|
|
525
|
+
.map(tag => {
|
|
526
|
+
const normalized = UnicodeValidator.normalize(tag).normalizedContent;
|
|
527
|
+
return sanitizeInput(normalized, MEMORY_CONSTANTS.MAX_TAG_LENGTH);
|
|
528
|
+
})
|
|
529
|
+
.filter(tag => tag && tag.length > 0);
|
|
530
|
+
}
|
|
531
|
+
sanitizeMetadata(metadata) {
|
|
532
|
+
if (!metadata)
|
|
533
|
+
return undefined;
|
|
534
|
+
// SECURITY FIX: Sanitize metadata values
|
|
535
|
+
const sanitized = {};
|
|
536
|
+
const maxKeys = MEMORY_CONSTANTS.MAX_METADATA_KEYS;
|
|
537
|
+
let keyCount = 0;
|
|
538
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
539
|
+
if (keyCount >= maxKeys)
|
|
540
|
+
break;
|
|
541
|
+
const sanitizedKey = sanitizeInput(key, MEMORY_CONSTANTS.MAX_METADATA_KEY_LENGTH);
|
|
542
|
+
if (sanitizedKey && typeof value === 'string') {
|
|
543
|
+
sanitized[sanitizedKey] = sanitizeInput(value, MEMORY_CONSTANTS.MAX_METADATA_VALUE_LENGTH);
|
|
544
|
+
keyCount++;
|
|
545
|
+
}
|
|
546
|
+
else if (sanitizedKey && typeof value === 'number') {
|
|
547
|
+
sanitized[sanitizedKey] = value;
|
|
548
|
+
keyCount++;
|
|
549
|
+
}
|
|
550
|
+
// Skip other types for security
|
|
551
|
+
}
|
|
552
|
+
return sanitized;
|
|
553
|
+
}
|
|
554
|
+
canAccessPrivacyLevel(entryLevel, requestedLevel) {
|
|
555
|
+
const levels = MEMORY_CONSTANTS.PRIVACY_LEVELS;
|
|
556
|
+
const entryIndex = levels.indexOf(entryLevel);
|
|
557
|
+
const requestedIndex = levels.indexOf(requestedLevel);
|
|
558
|
+
// Can only access entries at or below the requested privacy level
|
|
559
|
+
// e.g., if requesting 'private', can see 'public' and 'private' but not 'sensitive'
|
|
560
|
+
return entryIndex <= requestedIndex;
|
|
561
|
+
}
|
|
562
|
+
isValidEntry(entry) {
|
|
563
|
+
return entry &&
|
|
564
|
+
typeof entry.id === 'string' &&
|
|
565
|
+
typeof entry.content === 'string' &&
|
|
566
|
+
entry.timestamp &&
|
|
567
|
+
(!entry.tags || Array.isArray(entry.tags));
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Optimized sanitization with checksum caching
|
|
571
|
+
* Avoids re-sanitizing content that hasn't changed
|
|
572
|
+
* @param content Content to sanitize
|
|
573
|
+
* @param maxLength Maximum allowed length
|
|
574
|
+
* @returns Sanitized content
|
|
575
|
+
*/
|
|
576
|
+
sanitizeWithCache(content, maxLength) {
|
|
577
|
+
// Generate checksum for the input
|
|
578
|
+
const checksum = crypto.createHash('sha256').update(content).digest('hex');
|
|
579
|
+
// Check if we've already sanitized this exact content
|
|
580
|
+
const cacheKey = `${checksum}:${maxLength}`;
|
|
581
|
+
const cached = this.sanitizationCache.get(cacheKey);
|
|
582
|
+
if (cached) {
|
|
583
|
+
return cached;
|
|
584
|
+
}
|
|
585
|
+
// Perform sanitization
|
|
586
|
+
const sanitized = sanitizeMemoryContent(content, maxLength);
|
|
587
|
+
// Cache the result (limit cache size to prevent memory issues)
|
|
588
|
+
if (this.sanitizationCache.size > 1000) {
|
|
589
|
+
// Remove oldest entries (simple FIFO)
|
|
590
|
+
const firstKey = this.sanitizationCache.keys().next().value;
|
|
591
|
+
if (firstKey)
|
|
592
|
+
this.sanitizationCache.delete(firstKey);
|
|
593
|
+
}
|
|
594
|
+
this.sanitizationCache.set(cacheKey, sanitized);
|
|
595
|
+
return sanitized;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Build search index with retry logic
|
|
599
|
+
* Attempts to build the index up to 3 times with exponential backoff
|
|
600
|
+
* This ensures search functionality even if there are transient failures
|
|
601
|
+
*/
|
|
602
|
+
async buildSearchIndexWithRetry(retries = 3) {
|
|
603
|
+
let lastError;
|
|
604
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
605
|
+
try {
|
|
606
|
+
await this.searchIndex.buildIndex(this.entries);
|
|
607
|
+
logger.debug(`Search index built successfully on attempt ${attempt}`);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
612
|
+
logger.warn(`Search index build attempt ${attempt} failed`, {
|
|
613
|
+
error: lastError.message,
|
|
614
|
+
entriesCount: this.entries.size
|
|
615
|
+
});
|
|
616
|
+
if (attempt < retries) {
|
|
617
|
+
// Exponential backoff: 100ms, 200ms, 400ms
|
|
618
|
+
const delay = Math.pow(2, attempt - 1) * 100;
|
|
619
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
// If we get here, all retries failed
|
|
624
|
+
throw lastError || new Error('Failed to build search index');
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVtb3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VsZW1lbnRzL21lbW9yaWVzL01lbW9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7OztHQWFHO0FBRUgsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRWhELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUV2RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQUNqRixPQUFPLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDNUIsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNqRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQWdDLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEcsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzlDLE9BQU8sRUFBRSxpQkFBaUIsRUFBa0MsTUFBTSx3QkFBd0IsQ0FBQztBQUMzRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDL0MsT0FBTyxTQUFTLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFFOUIsa0NBQWtDO0FBQ2xDLE1BQU0sTUFBTSxHQUFHLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQztBQUNwQyxNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsTUFBYSxDQUFDLENBQUM7QUFFeEMsd0VBQXdFO0FBQ3hFLE1BQU0sQ0FBQyxTQUFTLENBQUM7SUFDZixZQUFZLEVBQUUsRUFBRSxFQUFHLHVCQUF1QjtJQUMxQyxZQUFZLEVBQUUsRUFBRSxFQUFHLHdCQUF3QjtJQUMzQyxZQUFZLEVBQUUsSUFB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QU87b0JBQ3hCLFlBQVksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUk7aUJBQ2hDLENBQUMsQ0FBQztnQkFFSCxJQUFJLE9BQU8sR0FBRyxPQUFPLEVBQUUsQ0FBQztvQkFDdEIsMkNBQTJDO29CQUMzQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxPQUFPLEdBQUcsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDO29CQUM3QyxNQUFNLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUMzRCxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCxxQ0FBcUM7UUFDckMsTUFBTSxTQUFTLElBQUksSUFBSSxLQUFLLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUMvRCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE1lbW9yeSBFbGVtZW50IC0gUGVyc2lzdGVudCBjb250ZXh0IHN0b3JhZ2UgZm9yIGNvbnRpbnVpdHkgYW5kIGxlYXJuaW5nXG4gKiBcbiAqIFByb3ZpZGVzIG11bHRpcGxlIHN0b3JhZ2UgYmFja2VuZHMsIHJldGVudGlvbiBwb2xpY2llcywgYW5kIHNlYXJjaCBjYXBhYmlsaXRpZXNcbiAqIGZvciBtYWludGFpbmluZyBjb250ZXh0IGFjcm9zcyBzZXNzaW9ucyBhbmQgaW50ZXJhY3Rpb25zLlxuICogXG4gKiBTRUNVUklUWSBNRUFTVVJFUyBJTVBMRU1FTlRFRDpcbiAqIDEuIElucHV0IHNhbml0aXphdGlvbiBmb3IgYWxsIG1lbW9yeSBjb250ZW50XG4gKiAyLiBNZW1vcnkgc2l6ZSBsaW1pdHMgdG8gcHJldmVudCB1bmJvdW5kZWQgZ3Jvd3RoXG4gKiAzLiBQYXRoIHZhbGlkYXRpb24gZm9yIGZpbGUtYmFzZWQgc3RvcmFnZVxuICogNC4gUmV0ZW50aW9uIHBvbGljeSBlbmZvcmNlbWVudFxuICogNS4gUHJpdmFjeSBsZXZlbCBhY2Nlc3MgY29udHJvbFxuICogNi4gQXVkaXQgbG9nZ2luZyBmb3IgYWxsIG9wZXJhdGlvbnNcbiAqL1xuXG5pbXBvcnQgeyBCYXNlRWxlbWVudCB9IGZyb20gJy4uL0Jhc2VFbGVtZW50LmpzJztcbmltcG9ydCB7IElFbGVtZW50LCBFbGVtZW50VmFsaWRhdGlvblJlc3VsdCwgVmFsaWRhdGlvbkVycm9yIH0gZnJvbSAnLi4vLi4vdHlwZXMvZWxlbWVudHMvaW5kZXguanMnO1xuaW1wb3J0IHsgRWxlbWVudFR5cGUgfSBmcm9tICcuLi8uLi9wb3J0Zm9saW8vdHlwZXMuanMnO1xuaW1wb3J0IHsgSUVsZW1lbnRNZXRhZGF0YSB9IGZyb20gJy4uLy4uL3R5cGVzL2VsZW1lbnRzL0lFbGVtZW50LmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IGNyeXB0byBmcm9tICdjcnlwdG8nO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IHNhbml0aXplSW5wdXQgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9JbnB1dFZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBNRU1PUllfQ09OU1RBTlRTLCBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLCBQcml2YWN5TGV2ZWwsIFN0b3JhZ2VCYWNrZW5kIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgZ2VuZXJhdGVNZW1vcnlJZCB9IGZyb20gJy4vdXRpbHMuanMnO1xuaW1wb3J0IHsgTWVtb3J5U2VhcmNoSW5kZXgsIFNlYXJjaFF1ZXJ5LCBTZWFyY2hJbmRleENvbmZpZyB9IGZyb20gJy4vTWVtb3J5U2VhcmNoSW5kZXguanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcbmltcG9ydCB7IEpTRE9NIH0gZnJvbSAnanNkb20nO1xuXG4vLyBJbml0aWFsaXplIERPTVB1cmlmeSB3aXRoIEpTRE9NXG5jb25zdCB3aW5kb3cgPSBuZXcgSlNET00oJycpLndpbmRvdztcbmNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cgYXMgYW55KTtcblxuLy8gQ29uZmlndXJlIERPTVB1cmlmeSBmb3IgbWVtb3J5IGNvbnRlbnQgLSBzdHJpcCBhbGwgSFRNTCBidXQga2VlcCB0ZXh0XG5wdXJpZnkuc2V0Q29uZmlnKHtcbiAgQUxMT1dFRF9UQUdTOiBbXSwgIC8vIE5vIEhUTUwgdGFncyBhbGxvd2VkXG4gIEFMTE9XRURfQVRUUjogW10sICAvLyBObyBhdHRyaWJ1dGVzIGFsbG93ZWRcbiAgS0VFUF9DT05URU5UOiB0cnVlIC8vIEtlZXAgdGV4dCBjb250ZW50XG59KTtcblxuLyoqXG4gKiBTYW5pdGl6ZSBjb250ZW50IGZvciBtZW1vcnkgc3RvcmFnZVxuICogTW9yZSBwZXJtaXNzaXZlIHRoYW4gc2FuaXRpemVJbnB1dCAtIGFsbG93cyBwdW5jdHVhdGlvbiwgcXVvdGVzLCBldGMuXG4gKiBidXQgc3RpbGwgcHJldmVudHMgWFNTIGFuZCBjb250cm9sIGNoYXJhY3RlcnNcbiAqL1xuZnVuY3Rpb24gc2FuaXRpemVNZW1vcnlDb250ZW50KGNvbnRlbnQ6IHN0cmluZywgbWF4TGVuZ3RoOiBudW1iZXIpOiBzdHJpbmcge1xuICBpZiAoIWNvbnRlbnQgfHwgdHlwZW9mIGNvbnRlbnQgIT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG4gIFxuICAvLyBGaXJzdCBub3JtYWxpemUgVW5pY29kZVxuICBjb25zdCBub3JtYWxpemVkID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoY29udGVudCkubm9ybWFsaXplZENvbnRlbnQ7XG4gIFxuICAvLyBVc2UgRE9NUHVyaWZ5IHRvIHN0cmlwIGFueSBIVE1ML1hTUyBhdHRlbXB0cyBidXQga2VlcCB0ZXh0XG4gIGNvbnN0IGNsZWFuZWQgPSBwdXJpZnkuc2FuaXRpemUobm9ybWFsaXplZCk7XG4gIFxuICAvLyBSZW1vdmUgb25seSBjb250cm9sIGNoYXJhY3RlcnMgYW5kIG51bGwgYnl0ZXNcbiAgcmV0dXJuIGNsZWFuZWRcbiAgICAucmVwbGFjZSgvW1xceDAwLVxceDA4XFx4MEJcXHgwQ1xceDBFLVxceDFGXFx4N0ZdL2csICcnKSAvLyBSZW1vdmUgY29udHJvbCBjaGFycyBleGNlcHQgXFx0IFxcbiBcXHJcbiAgICAuc3Vic3RyaW5nKDAsIG1heExlbmd0aClcbiAgICAudHJpbSgpO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1lbW9yeU1ldGFkYXRhIGV4dGVuZHMgSUVsZW1lbnRNZXRhZGF0YSB7XG4gIHN0b3JhZ2VCYWNrZW5kPzogU3RvcmFnZUJhY2tlbmQ7XG4gIHJldGVudGlvbkRheXM/OiBudW1iZXI7XG4gIHByaXZhY3lMZXZlbD86IFByaXZhY3lMZXZlbDtcbiAgc2VhcmNoYWJsZT86IGJvb2xlYW47XG4gIG1heEVudHJpZXM/OiBudW1iZXI7XG4gIGVuY3J5cHRpb25FbmFibGVkPzogYm9vbGVhbjtcbiAgLy8gU2VhcmNoIGluZGV4IGNvbmZpZ3VyYXRpb24gKElzc3VlICM5ODQpXG4gIGluZGV4VGhyZXNob2xkPzogbnVtYmVyO1xuICBlbmFibGVDb250ZW50SW5kZXg/OiBib29sZWFuO1xuICBtYXhUZXJtc1BlckVudHJ5PzogbnVtYmVyO1xuICBtaW5UZXJtTGVuZ3RoPzogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1lbW9yeUVudHJ5IHtcbiAgaWQ6IHN0cmluZztcbiAgdGltZXN0YW1wOiBEYXRlO1xuICBjb250ZW50OiBzdHJpbmc7XG4gIHRhZ3M/OiBzdHJpbmdbXTtcbiAgbWV0YWRhdGE/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuICBleHBpcmVzQXQ/OiBEYXRlO1xuICBwcml2YWN5TGV2ZWw/OiBQcml2YWN5TGV2ZWw7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWVtb3J5U2VhcmNoT3B0aW9ucyB7XG4gIHF1ZXJ5Pzogc3RyaW5nO1xuICB0YWdzPzogc3RyaW5nW107XG4gIHN0YXJ0RGF0ZT86IERhdGU7XG4gIGVuZERhdGU/OiBEYXRlO1xuICBsaW1pdD86IG51bWJlcjtcbiAgcHJpdmFjeUxldmVsPzogUHJpdmFjeUxldmVsO1xufVxuXG4vKipcbiAqIE1lbW9yeSBFbGVtZW50IEltcGxlbWVudGF0aW9uXG4gKlxuICogVE9ETzogTWVtb3J5IFNoYXJkaW5nIFN0cmF0ZWd5IChJc3N1ZSAjOTgxKVxuICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiBDdXJyZW50OiBTaW5nbGUgTWFwPGlkLCBlbnRyeT4gZm9yIGFsbCBtZW1vcmllc1xuICogUHJvYmxlbTogTGFyZ2UgbWVtb3J5IHNldHMgKD4xMEsgZW50cmllcykgY2F1c2UgcGVyZm9ybWFuY2UgZGVncmFkYXRpb25cbiAqXG4gKiBQbGFubmVkIFNoYXJkaW5nIEFyY2hpdGVjdHVyZTpcbiAqIDEuIFNoYXJkIG1lbW9yaWVzIGFjcm9zcyBtdWx0aXBsZSBmaWxlcyBiYXNlZCBvbiBoYXNoKG1lbW9yeUlkKSAlIHNoYXJkQ291bnRcbiAqIDIuIEVhY2ggc2hhcmQgZmlsZSA8MjU2S0IgZm9yIG9wdGltYWwgWUFNTCBwYXJzaW5nXG4gKiAzLiBNZW1vcnkgcmVmZXJlbmNlcyBzdG9yZWQgc2VwYXJhdGVseSBmcm9tIGNvbnRlbnQgKGxpa2UgZ2l0IG9iamVjdHMpXG4gKiA0LiBMYXJnZSBiaW5hcnkgY29udGVudCAoUERGcywgaW1hZ2VzKSBzdG9yZWQgYXMgZXh0ZXJuYWwgcmVmZXJlbmNlc1xuICpcbiAqIEJlbmVmaXRzOlxuICogLSBQYXJhbGxlbCBsb2FkaW5nIG9mIG1lbW9yeSBzaGFyZHNcbiAqIC0gUmVkdWNlZCBtZW1vcnkgZm9vdHByaW50IChsb2FkIG9ubHkgbmVlZGVkIHNoYXJkcylcbiAqIC0gQmV0dGVyIGNvcnJ1cHRpb24gcmVzaXN0YW5jZSAob25lIHNoYXJkIGZhaWx1cmUgZG9lc24ndCBhZmZlY3Qgb3RoZXJzKVxuICogLSBFZmZpY2llbnQgaW5jcmVtZW50YWwgdXBkYXRlc1xuICpcbiAqIFRPRE86IENvbnRlbnQgSW50ZWdyaXR5IFZlcmlmaWNhdGlvbiAoSXNzdWUgIzk4MilcbiAqIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiBBZGQgU0hBLTI1NiBoYXNoZXMgdG8gZGV0ZWN0OlxuICogLSBBY2NpZGVudGFsIGNvcnJ1cHRpb24gZnJvbSBkaXNrIGVycm9yc1xuICogLSBJbnRlbnRpb25hbCB0YW1wZXJpbmcgd2l0aCBtZW1vcnkgZmlsZXNcbiAqIC0gVmVyc2lvbiBjb25mbGljdHMgZHVyaW5nIGNvbmN1cnJlbnQgYWNjZXNzXG4gKlxuICogSW1wbGVtZW50YXRpb246XG4gKiAtIFN0b3JlIGhhc2ggaW4gbWVtb3J5IG1ldGFkYXRhXG4gKiAtIFZlcmlmeSBvbiBsb2FkLCB3YXJuIG9uIG1pc21hdGNoXG4gKiAtIE9wdGlvbiB0byBhdXRvLXJlc3RvcmUgZnJvbSBiYWNrdXAgb24gY29ycnVwdGlvblxuICpcbiAqIFRPRE86IE1lbW9yeSBDYXBhY2l0eSBNYW5hZ2VtZW50IChJc3N1ZSAjOTgzKVxuICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiBDdXJyZW50OiBTeW5jaHJvbm91cyByZXRlbnRpb24gZW5mb3JjZW1lbnQgb24gZWFjaCBhZGRcbiAqIEJldHRlcjogQmFja2dyb3VuZCBjbGVhbnVwIHdpdGggc21hcnQgdHJpZ2dlcnM6XG4gKiAtIENsZWFudXAgd2hlbiA5MCUgY2FwYWNpdHkgcmVhY2hlZFxuICogLSBCYXRjaCBkZWxldGlvbnMgZm9yIGVmZmljaWVuY3lcbiAqIC0gTFJVIGV2aWN0aW9uIHdpdGggYWNjZXNzIHRyYWNraW5nXG4gKiAtIFByZXNlcnZlIFwicGlubmVkXCIgbWVtb3JpZXMgcmVnYXJkbGVzcyBvZiBhZ2VcbiAqL1xuZXhwb3J0IGNsYXNzIE1lbW9yeSBleHRlbmRzIEJhc2VFbGVtZW50IGltcGxlbWVudHMgSUVsZW1lbnQge1xuICAvLyBNZW1vcnktc3BlY2lmaWMgcHJvcGVydGllc1xuICBwcml2YXRlIGVudHJpZXM6IE1hcDxzdHJpbmcsIE1lbW9yeUVudHJ5PiA9IG5ldyBNYXAoKTtcbiAgcHJpdmF0ZSBzdG9yYWdlQmFja2VuZDogU3RvcmFnZUJhY2tlbmQ7XG4gIHByaXZhdGUgcmV0ZW50aW9uRGF5czogbnVtYmVyO1xuICBwcml2YXRlIHByaXZhY3lMZXZlbDogUHJpdmFjeUxldmVsO1xuICBwcml2YXRlIHNlYXJjaGFibGU6IGJvb2xlYW47XG4gIHByaXZhdGUgbWF4RW50cmllczogbnVtYmVyO1xuXG4gIC8vIFNlYXJjaCBpbmRleCBmb3IgcGVyZm9ybWFuY2UgKElzc3VlICM5ODQpXG4gIHByaXZhdGUgc2VhcmNoSW5kZXg6IE1lbW9yeVNlYXJjaEluZGV4O1xuXG4gIC8vIFNhbml0aXphdGlvbiBjYWNoZSB0byBhdm9pZCByZWR1bmRhbnQgcHJvY2Vzc2luZ1xuICBwcml2YXRlIHNhbml0aXphdGlvbkNhY2hlOiBNYXA8c3RyaW5nLCBzdHJpbmc+ID0gbmV3IE1hcCgpO1xuICBcbiAgY29uc3RydWN0b3IobWV0YWRhdGE6IFBhcnRpYWw8TWVtb3J5TWV0YWRhdGE+ID0ge30pIHtcbiAgICAvLyBTRUNVUklUWSBGSVg6IFNhbml0aXplIGFsbCBpbnB1dHMgZHVyaW5nIGNvbnN0cnVjdGlvblxuICAgIGNvbnN0IHNhbml0aXplZE1ldGFkYXRhID0ge1xuICAgICAgLi4ubWV0YWRhdGEsXG4gICAgICBuYW1lOiBtZXRhZGF0YS5uYW1lID8gXG4gICAgICAgIHNhbml0aXplSW5wdXQoVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobWV0YWRhdGEubmFtZSkubm9ybWFsaXplZENvbnRlbnQsIDEwMCkgOiBcbiAgICAgICAgJ1VubmFtZWQgTWVtb3J5JyxcbiAgICAgIGRlc2NyaXB0aW9uOiBtZXRhZGF0YS5kZXNjcmlwdGlvbiA/IFxuICAgICAgICBzYW5pdGl6ZUlucHV0KFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKG1ldGFkYXRhLmRlc2NyaXB0aW9uKS5ub3JtYWxpemVkQ29udGVudCwgNTAwKSA6IFxuICAgICAgICB1bmRlZmluZWRcbiAgICB9O1xuICAgIFxuICAgIHN1cGVyKEVsZW1lbnRUeXBlLk1FTU9SWSwgc2FuaXRpemVkTWV0YWRhdGEpO1xuICAgIFxuICAgIC8vIEluaXRpYWxpemUgbWVtb3J5LXNwZWNpZmljIHByb3BlcnRpZXMgd2l0aCBkZWZhdWx0c1xuICAgIHRoaXMuc3RvcmFnZUJhY2tlbmQgPSBtZXRhZGF0YS5zdG9yYWdlQmFja2VuZCB8fCBNRU1PUllfQ09OU1RBTlRTLkRFRkFVTFRfU1RPUkFHRV9CQUNLRU5EO1xuICAgIHRoaXMucmV0ZW50aW9uRGF5cyA9IG1ldGFkYXRhLnJldGVudGlvbkRheXMgfHwgTUVNT1JZX0NPTlNUQU5UUy5ERUZBVUxUX1JFVEVOVElPTl9EQVlTO1xuICAgIC8vIFZhbGlkYXRlIHByaXZhY3kgbGV2ZWwgLSBkZWZhdWx0IHRvIHByaXZhdGUgaWYgaW52YWxpZFxuICAgIHRoaXMucHJpdmFjeUxldmVsID0gKG1ldGFkYXRhLnByaXZhY3lMZXZlbCAmJiBNRU1PUllfQ09OU1RBTlRTLlBSSVZBQ1lfTEVWRUxTLmluY2x1ZGVzKG1ldGFkYXRhLnByaXZhY3lMZXZlbCkpIFxuICAgICAgPyBtZXRhZGF0YS5wcml2YWN5TGV2ZWwgXG4gICAgICA6IE1FTU9SWV9DT05TVEFOVFMuREVGQVVMVF9QUklWQUNZX0xFVkVMO1xuICAgIHRoaXMuc2VhcmNoYWJsZSA9IG1ldGFkYXRhLnNlYXJjaGFibGUgIT09IGZhbHNlO1xuICAgIHRoaXMubWF4RW50cmllcyA9IE1hdGgubWluKFxuICAgICAgbWV0YWRhdGEubWF4RW50cmllcyB8fCBNRU1PUllfQ09OU1RBTlRTLk1BWF9FTlRSSUVTX0RFRkFVTFQsXG4gICAgICBNRU1PUllfQ09OU1RBTlRTLk1BWF9FTlRSSUVTX0RFRkFVTFRcbiAgICApO1xuICAgIFxuICAgIC8vIFNldCB1cCBleHRlbnNpb25zXG4gICAgdGhpcy5leHRlbnNpb25zID0ge1xuICAgICAgc3RvcmFnZUJhY2tlbmQ6IHRoaXMuc3RvcmFnZUJhY2tlbmQsXG4gICAgICByZXRlbnRpb25EYXlzOiB0aGlzLnJldGVudGlvbkRheXMsXG4gICAgICBwcml2YWN5TGV2ZWw6IHRoaXMucHJpdmFjeUxldmVsLFxuICAgICAgc2VhcmNoYWJsZTogdGhpcy5zZWFyY2hhYmxlLFxuICAgICAgbWF4RW50cmllczogdGhpcy5tYXhFbnRyaWVzLFxuICAgICAgZW5jcnlwdGlvbkVuYWJsZWQ6IG1ldGFkYXRhLmVuY3J5cHRpb25FbmFibGVkIHx8IGZhbHNlXG4gICAgfTtcblxuICAgIC8vIEluaXRpYWxpemUgc2VhcmNoIGluZGV4IHdpdGggY29uZmlndXJhdGlvbiAoSXNzdWUgIzk4NClcbiAgICBjb25zdCBpbmRleENvbmZpZzogU2VhcmNoSW5kZXhDb25maWcgPSB7XG4gICAgICBpbmRleFRocmVzaG9sZDogbWV0YWRhdGEuaW5kZXhUaHJlc2hvbGQgfHwgMTAwLFxuICAgICAgZW5hYmxlQ29udGVudEluZGV4OiBtZXRhZGF0YS5lbmFibGVDb250ZW50SW5kZXggIT09IGZhbHNlLFxuICAgICAgbWF4VGVybXNQZXJFbnRyeTogbWV0YWRhdGEubWF4VGVybXNQZXJFbnRyeSB8fCAxMDAsXG4gICAgICBtaW5UZXJtTGVuZ3RoOiBtZXRhZGF0YS5taW5UZXJtTGVuZ3RoIHx8IDIsXG4gICAgICBlbmFibGVQZXJzaXN0ZW5jZTogZmFsc2UgLy8gRnV0dXJlIGVuaGFuY2VtZW50XG4gICAgfTtcbiAgICB0aGlzLnNlYXJjaEluZGV4ID0gbmV3IE1lbW9yeVNlYXJjaEluZGV4KGluZGV4Q29uZmlnKTtcblxuICAgIC8vIExvZyBtZW1vcnkgY3JlYXRpb25cbiAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICB0eXBlOiBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLk1FTU9SWV9DUkVBVEVELFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnTWVtb3J5LmNvbnN0cnVjdG9yJyxcbiAgICAgIGRldGFpbHM6IGBNZW1vcnkgY3JlYXRlZDogJHt0aGlzLm1ldGFkYXRhLm5hbWV9IHdpdGggJHt0aGlzLnN0b3JhZ2VCYWNrZW5kfSBiYWNrZW5kYFxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogQWRkIGEgbmV3IG1lbW9yeSBlbnRyeVxuICAgKiBTRUNVUklUWTogVmFsaWRhdGVzIGFuZCBzYW5pdGl6ZXMgYWxsIGlucHV0LCBlbmZvcmNlcyBzaXplIGxpbWl0c1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGFkZEVudHJ5KGNvbnRlbnQ6IHN0cmluZywgdGFncz86IHN0cmluZ1tdLCBtZXRhZGF0YT86IFJlY29yZDxzdHJpbmcsIGFueT4pOiBQcm9taXNlPE1lbW9yeUVudHJ5PiB7XG4gICAgLy8gVmFsaWRhdGUgbWVtb3J5IHNpemUgbGltaXRzXG4gICAgaWYgKHRoaXMuZW50cmllcy5zaXplID49IHRoaXMubWF4RW50cmllcykge1xuICAgICAgLy8gU0VDVVJJVFkgRklYOiBFbmZvcmNlIHJldGVudGlvbiBwb2xpY3kgd2hlbiBhdCBjYXBhY2l0eVxuICAgICAgYXdhaXQgdGhpcy5lbmZvcmNlUmV0ZW50aW9uUG9saWN5KCk7XG4gICAgICBcbiAgICAgIC8vIElmIHN0aWxsIGF0IGNhcGFjaXR5IGFmdGVyIHJldGVudGlvbiwgcmVtb3ZlIG9sZGVzdCB0byBtYWtlIHJvb21cbiAgICAgIGlmICh0aGlzLmVudHJpZXMuc2l6ZSA+PSB0aGlzLm1heEVudHJpZXMpIHtcbiAgICAgICAgY29uc3Qgb2xkZXN0RW50cnkgPSBBcnJheS5mcm9tKHRoaXMuZW50cmllcy52YWx1ZXMoKSlcbiAgICAgICAgICAuc29ydCgoYSwgYikgPT4gYS50aW1lc3RhbXAuZ2V0VGltZSgpIC0gYi50aW1lc3RhbXAuZ2V0VGltZSgpKVswXTtcbiAgICAgICAgaWYgKG9sZGVzdEVudHJ5KSB7XG4gICAgICAgICAgdGhpcy5lbnRyaWVzLmRlbGV0ZShvbGRlc3RFbnRyeS5pZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLy8gU0VDVVJJVFkgRklYOiBWYWxpZGF0ZSBhbmQgc2FuaXRpemUgY29udGVudFxuICAgIGNvbnN0IHNhbml0aXplZENvbnRlbnQgPSBzYW5pdGl6ZU1lbW9yeUNvbnRlbnQoY29udGVudCwgTUVNT1JZX0NPTlNUQU5UUy5NQVhfRU5UUllfU0laRSk7XG4gICAgXG4gICAgaWYgKCFzYW5pdGl6ZWRDb250ZW50IHx8IHNhbml0aXplZENvbnRlbnQudHJpbSgpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdNZW1vcnkgY29udGVudCBjYW5ub3QgYmUgZW1wdHknKTtcbiAgICB9XG4gICAgXG4gICAgLy8gU0VDVVJJVFkgRklYOiBWYWxpZGF0ZSBhbmQgc2FuaXRpemUgdGFnc1xuICAgIGNvbnN0IHNhbml0aXplZFRhZ3MgPSB0YWdzID8gdGhpcy5zYW5pdGl6ZVRhZ3ModGFncykgOiBbXTtcbiAgICBcbiAgICAvLyBDcmVhdGUgbWVtb3J5IGVudHJ5IHdpdGggZ2VuZXJhdGVkIElEXG4gICAgY29uc3QgZW50cnk6IE1lbW9yeUVudHJ5ID0ge1xuICAgICAgaWQ6IGdlbmVyYXRlTWVtb3J5SWQoKSxcbiAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKSxcbiAgICAgIGNvbnRlbnQ6IHNhbml0aXplZENvbnRlbnQsXG4gICAgICB0YWdzOiBzYW5pdGl6ZWRUYWdzLFxuICAgICAgbWV0YWRhdGE6IHRoaXMuc2FuaXRpemVNZXRhZGF0YShtZXRhZGF0YSksXG4gICAgICBwcml2YWN5TGV2ZWw6IHRoaXMucHJpdmFjeUxldmVsLFxuICAgICAgZXhwaXJlc0F0OiB0aGlzLmNhbGN1bGF0ZUV4cGlyeURhdGUoKVxuICAgIH07XG4gICAgXG4gICAgLy8gU3RvcmUgZW50cnlcbiAgICB0aGlzLmVudHJpZXMuc2V0KGVudHJ5LmlkLCBlbnRyeSk7XG4gICAgdGhpcy5faXNEaXJ0eSA9IHRydWU7XG5cbiAgICAvLyBVcGRhdGUgc2VhcmNoIGluZGV4IChJc3N1ZSAjOTg0KVxuICAgIHRoaXMuc2VhcmNoSW5kZXguYWRkRW50cnkoZW50cnkpO1xuXG4gICAgLy8gQ2hlY2sgaWYgd2Ugc2hvdWxkIGJ1aWxkL3JlYnVpbGQgdGhlIGluZGV4XG4gICAgaWYgKCF0aGlzLnNlYXJjaEluZGV4LmlzSW5kZXhlZCAmJiB0aGlzLmVudHJpZXMuc2l6ZSA+PSAxMDApIHtcbiAgICAgIC8vIEJ1aWxkIGluZGV4IGFzeW5jaHJvbm91c2x5IHRvIGF2b2lkIGJsb2NraW5nLCB3aXRoIHJldHJ5IGxvZ2ljXG4gICAgICB0aGlzLmJ1aWxkU2VhcmNoSW5kZXhXaXRoUmV0cnkoKS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgIC8vIEZpbmFsIGZhaWx1cmUgYWZ0ZXIgcmV0cmllcyAtIHNlYXJjaCB3aWxsIGZhbGwgYmFjayB0byBsaW5lYXIgc2NhblxuICAgICAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBidWlsZCBzZWFyY2ggaW5kZXggYWZ0ZXIgcmV0cmllcywgc2VhcmNoIHdpbGwgdXNlIGZhbGxiYWNrJywgZXJyb3IpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gTG9nIG1lbW9yeSBhZGRpdGlvblxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX0FEREVELFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnTWVtb3J5LmFkZEVudHJ5JyxcbiAgICAgIGRldGFpbHM6IGBBZGRlZCBtZW1vcnkgZW50cnkgJHtlbnRyeS5pZH0gd2l0aCAke3Nhbml0aXplZFRhZ3MubGVuZ3RofSB0YWdzYFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGVudHJ5O1xuICB9XG4gIFxuICAvKipcbiAgICogU2VhcmNoIG1lbW9yeSBlbnRyaWVzXG4gICAqIFNFQ1VSSVRZOiBSZXNwZWN0cyBwcml2YWN5IGxldmVscyBhbmQgc2FuaXRpemVzIHNlYXJjaCBxdWVyaWVzXG4gICAqXG4gICAqIElNUExFTUVOVEVEOiBCYXNpYyBpbmRleGVkIHNlYXJjaCBmb3IgTyhsb2cgbikgcGVyZm9ybWFuY2UgKElzc3VlICM5ODQpXG4gICAqIC0gVGFnIGluZGV4OiBNYXA8dGFnLCBTZXQ8ZW50cnlJZD4+IGZvciBpbnN0YW50IHRhZyBsb29rdXBzIOKck1xuICAgKiAtIENvbnRlbnQgaW5kZXg6IEludmVydGVkIGluZGV4IGZvciB0ZXJtIHNlYXJjaCDinJNcbiAgICogLSBEYXRlIGluZGV4OiBCaW5hcnkgdHJlZSBmb3IgZWZmaWNpZW50IHJhbmdlIHF1ZXJpZXMg4pyTXG4gICAqIC0gUHJpdmFjeSBpbmRleDogUHJlLXNvcnRlZCBlbnRyaWVzIGJ5IHByaXZhY3kgbGV2ZWwg4pyTXG4gICAqXG4gICAqIFRPRE86IEFkdmFuY2VkIGluZGV4aW5nIGZlYXR1cmVzIChGdXR1cmUgZW5oYW5jZW1lbnRzKTpcbiAgICogLSBDb21wb3NpdGUgaW5kaWNlczogQ29tYmluZWQgaW5kaWNlcyBmb3IgY29tbW9uIHF1ZXJ5IHBhdHRlcm5zXG4gICAqIC0gSW5kZXgtb2YtaW5kZXhlcyBwYXR0ZXJuOlxuICAgKiAgIC0gTWFzdGVyIGluZGV4IGZpbGUgKG1ldGEueWFtbCkgd2l0aCBwb2ludGVycyB0byBzaGFyZCBpbmRpY2VzXG4gICAqICAgLSBFYWNoIHNoYXJkIG1haW50YWlucyBpdHMgb3duIGxvY2FsIGluZGV4XG4gICAqICAgLSBQZXJpb2RpYyBpbmRleCBjb21wYWN0aW9uIGFuZCBvcHRpbWl6YXRpb25cbiAgICogLSBQZXJzaXN0ZW50IGluZGV4IHN0b3JhZ2UgdG8gZGlza1xuICAgKiAtIEluY3JlbWVudGFsIGluZGV4IHVwZGF0ZXMgZm9yIGxhcmdlIGRhdGFzZXRzXG4gICAqXG4gICAqIFBlcmZvcm1hbmNlIGltcHJvdmVtZW50IGFjaGlldmVkOlxuICAgKiAtIFByZXZpb3VzOiB+MTAwbXMgZm9yIDEwLDAwMCBlbnRyaWVzIChsaW5lYXIgc2NhbilcbiAgICogLSBDdXJyZW50OiA8NW1zIGZvciBzYW1lIGRhdGFzZXQgKGluZGV4ZWQgc2VhcmNoKVxuICAgKi9cbiAgcHVibGljIGFzeW5jIHNlYXJjaChvcHRpb25zOiBNZW1vcnlTZWFyY2hPcHRpb25zID0ge30pOiBQcm9taXNlPE1lbW9yeUVudHJ5W10+IHtcbiAgICAvLyBTRUNVUklUWSBGSVg6IFNhbml0aXplIHNlYXJjaCBxdWVyeSAodXNlIHJlZ3VsYXIgc2FuaXRpemVJbnB1dCBmb3IgcXVlcmllcylcbiAgICBjb25zdCBzYW5pdGl6ZWRRdWVyeSA9IG9wdGlvbnMucXVlcnkgP1xuICAgICAgc2FuaXRpemVJbnB1dChVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShvcHRpb25zLnF1ZXJ5KS5ub3JtYWxpemVkQ29udGVudCwgMjAwKSA6XG4gICAgICB1bmRlZmluZWQ7XG5cbiAgICAvLyBVc2UgaW5kZXhlZCBzZWFyY2ggaWYgYXZhaWxhYmxlIChJc3N1ZSAjOTg0KVxuICAgIGlmICh0aGlzLnNlYXJjaEluZGV4LmlzSW5kZXhlZCkge1xuICAgICAgY29uc3Qgc2VhcmNoUXVlcnk6IFNlYXJjaFF1ZXJ5ID0ge1xuICAgICAgICBjb250ZW50OiBzYW5pdGl6ZWRRdWVyeSxcbiAgICAgICAgdGFnczogb3B0aW9ucy50YWdzID8gdGhpcy5zYW5pdGl6ZVRhZ3Mob3B0aW9ucy50YWdzKSA6IHVuZGVmaW5lZCxcbiAgICAgICAgZGF0ZUZyb206IG9wdGlvbnMuc3RhcnREYXRlLFxuICAgICAgICBkYXRlVG86IG9wdGlvbnMuZW5kRGF0ZSxcbiAgICAgICAgcHJpdmFjeUxldmVsOiBvcHRpb25zLnByaXZhY3lMZXZlbCBhcyBQcml2YWN5TGV2ZWwsXG4gICAgICAgIGxpbWl0OiBvcHRpb25zLmxpbWl0XG4gICAgICB9O1xuXG4gICAgICBjb25zdCBzZWFyY2hSZXN1bHRzID0gdGhpcy5zZWFyY2hJbmRleC5zZWFyY2goc2VhcmNoUXVlcnksIHRoaXMuZW50cmllcyk7XG4gICAgICByZXR1cm4gc2VhcmNoUmVzdWx0cy5tYXAocmVzdWx0ID0+IHJlc3VsdC5lbnRyeSk7XG4gICAgfVxuXG4gICAgLy8gRmFsbGJhY2sgdG8gbGluZWFyIHNlYXJjaCBmb3Igc21hbGwgZGF0YXNldHNcbiAgICBsZXQgcmVzdWx0czogTWVtb3J5RW50cnlbXSA9IFtdO1xuICAgIGNvbnN0IHF1ZXJ5TG93ZXIgPSBzYW5pdGl6ZWRRdWVyeT8udG9Mb3dlckNhc2UoKTtcbiAgICBjb25zdCBzZWFyY2hUYWdzID0gb3B0aW9ucy50YWdzICYmIG9wdGlvbnMudGFncy5sZW5ndGggPiAwID8gdGhpcy5zYW5pdGl6ZVRhZ3Mob3B0aW9ucy50YWdzKSA6IG51bGw7XG5cbiAgICAvLyBTaW5nbGUgaXRlcmF0aW9uIHRocm91Z2ggZW50cmllcyB3aXRoIGFsbCBmaWx0ZXJzIGFwcGxpZWRcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIHRoaXMuZW50cmllcy52YWx1ZXMoKSkge1xuICAgICAgLy8gUHJpdmFjeSBsZXZlbCBjaGVja1xuICAgICAgaWYgKG9wdGlvbnMucHJpdmFjeUxldmVsICYmIFxuICAgICAgICAgICF0aGlzLmNhbkFjY2Vzc1ByaXZhY3lMZXZlbChlbnRyeS5wcml2YWN5TGV2ZWwgfHwgTUVNT1JZX0NPTlNUQU5UUy5ERUZBVUxUX1BSSVZBQ1lfTEVWRUwsIG9wdGlvbnMucHJpdmFjeUxldmVsKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gUXVlcnkgdGV4dCBjaGVja1xuICAgICAgaWYgKHF1ZXJ5TG93ZXIpIHtcbiAgICAgICAgY29uc3QgY29udGVudE1hdGNoID0gZW50cnkuY29udGVudC50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKHF1ZXJ5TG93ZXIpO1xuICAgICAgICBjb25zdCB0YWdNYXRjaCA9IGVudHJ5LnRhZ3M/LnNvbWUodGFnID0+IHRhZy50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKHF1ZXJ5TG93ZXIpKTtcbiAgICAgICAgaWYgKCFjb250ZW50TWF0Y2ggJiYgIXRhZ01hdGNoKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gVGFnIGZpbHRlciBjaGVja1xuICAgICAgaWYgKHNlYXJjaFRhZ3MgJiYgIXNlYXJjaFRhZ3Muc29tZShzZWFyY2hUYWcgPT4gZW50cnkudGFncz8uaW5jbHVkZXMoc2VhcmNoVGFnKSkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIERhdGUgcmFuZ2UgY2hlY2tzXG4gICAgICBpZiAob3B0aW9ucy5zdGFydERhdGUgJiYgZW50cnkudGltZXN0YW1wIDwgb3B0aW9ucy5zdGFydERhdGUpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAob3B0aW9ucy5lbmREYXRlICYmIGVudHJ5LnRpbWVzdGFtcCA+IG9wdGlvbnMuZW5kRGF0ZSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gRW50cnkgcGFzc2VzIGFsbCBmaWx0ZXJzXG4gICAgICByZXN1bHRzLnB1c2goZW50cnkpO1xuICAgIH1cbiAgICBcbiAgICAvLyBTb3J0IGJ5IHRpbWVzdGFtcCAobmV3ZXN0IGZpcnN0KSAtIHVzaW5nIHN0cmluZyBjb21wYXJpc29uIGZvciBJRHMgYXMgc2Vjb25kYXJ5IHNvcnRcbiAgICByZXN1bHRzLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIGNvbnN0IHRpbWVEaWZmID0gYi50aW1lc3RhbXAuZ2V0VGltZSgpIC0gYS50aW1lc3RhbXAuZ2V0VGltZSgpO1xuICAgICAgaWYgKHRpbWVEaWZmICE9PSAwKSByZXR1cm4gdGltZURpZmY7XG4gICAgICAvLyBJZiB0aW1lc3RhbXBzIGFyZSBleGFjdGx5IHRoZSBzYW1lLCBzb3J0IGJ5IElEICh3aGljaCBjb250YWlucyB0aW1lc3RhbXApXG4gICAgICByZXR1cm4gYi5pZC5sb2NhbGVDb21wYXJlKGEuaWQpO1xuICAgIH0pO1xuICAgIFxuICAgIC8vIEFwcGx5IGxpbWl0XG4gICAgaWYgKG9wdGlvbnMubGltaXQgJiYgb3B0aW9ucy5saW1pdCA+IDApIHtcbiAgICAgIHJlc3VsdHMgPSByZXN1bHRzLnNsaWNlKDAsIG9wdGlvbnMubGltaXQpO1xuICAgIH1cbiAgICBcbiAgICAvLyBMb2cgc2VhcmNoIG9wZXJhdGlvblxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX1NFQVJDSEVELFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnTWVtb3J5LnNlYXJjaCcsXG4gICAgICBkZXRhaWxzOiBgU2VhcmNoZWQgbWVtb3JpZXMgd2l0aCBxdWVyeTogJHtzYW5pdGl6ZWRRdWVyeSB8fCAnbm9uZSd9LCBmb3VuZCAke3Jlc3VsdHMubGVuZ3RofSByZXN1bHRzYFxuICAgIH0pO1xuICAgIFxuICAgIHJldHVybiByZXN1bHRzO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IGEgc3BlY2lmaWMgbWVtb3J5IGVudHJ5IGJ5IElEXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZ2V0RW50cnkoaWQ6IHN0cmluZyk6IFByb21pc2U8TWVtb3J5RW50cnkgfCB1bmRlZmluZWQ+IHtcbiAgICByZXR1cm4gdGhpcy5lbnRyaWVzLmdldChpZCk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBEZWxldGUgYSBtZW1vcnkgZW50cnlcbiAgICogU0VDVVJJVFk6IFZhbGlkYXRlcyBwZXJtaXNzaW9ucyBhbmQgbG9ncyBkZWxldGlvblxuICAgKi9cbiAgcHVibGljIGFzeW5jIGRlbGV0ZUVudHJ5KGlkOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBlbnRyeSA9IHRoaXMuZW50cmllcy5nZXQoaWQpO1xuICAgIGlmICghZW50cnkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgXG4gICAgLy8gU0VDVVJJVFk6IENoZWNrIGlmIHNlbnNpdGl2ZSBtZW1vcmllcyBjYW4gYmUgZGVsZXRlZFxuICAgIGlmIChlbnRyeS5wcml2YWN5TGV2ZWwgPT09ICdzZW5zaXRpdmUnKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuU0VOU0lUSVZFX01FTU9SWV9ERUxFVEVELFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ01lbW9yeS5kZWxldGVFbnRyeScsXG4gICAgICAgIGRldGFpbHM6IGBTZW5zaXRpdmUgbWVtb3J5ICR7aWR9IGRlbGV0ZWRgXG4gICAgICB9KTtcbiAgICB9XG4gICAgXG4gICAgdGhpcy5lbnRyaWVzLmRlbGV0ZShpZCk7XG4gICAgdGhpcy5faXNEaXJ0eSA9IHRydWU7XG4gICAgXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBFbmZvcmNlIHJldGVudGlvbiBwb2xpY3kgYnkgcmVtb3ZpbmcgZXhwaXJlZCBlbnRyaWVzXG4gICAqIFNFQ1VSSVRZOiBFbnN1cmVzIG1lbW9yeSBkb2Vzbid0IGdyb3cgdW5ib3VuZGVkXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZW5mb3JjZVJldGVudGlvblBvbGljeSgpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGNvbnN0IG5vdyA9IG5ldyBEYXRlKCk7XG4gICAgbGV0IGRlbGV0ZWRDb3VudCA9IDA7XG4gICAgXG4gICAgLy8gUmVtb3ZlIGV4cGlyZWQgZW50cmllc1xuICAgIGZvciAoY29uc3QgW2lkLCBlbnRyeV0gb2YgdGhpcy5lbnRyaWVzKSB7XG4gICAgICBpZiAoZW50cnkuZXhwaXJlc0F0ICYmIGVudHJ5LmV4cGlyZXNBdCA8IG5vdykge1xuICAgICAgICB0aGlzLmVudHJpZXMuZGVsZXRlKGlkKTtcbiAgICAgICAgZGVsZXRlZENvdW50Kys7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIC8vIElmIHN0aWxsIGF0IG9yIG92ZXIgY2FwYWNpdHksIHJlbW92ZSBvbGRlc3QgZW50cmllcyB0byBtYWtlIHJvb20gZm9yIG9uZSBtb3JlXG4gICAgaWYgKHRoaXMuZW50cmllcy5zaXplID49IHRoaXMubWF4RW50cmllcykge1xuICAgICAgY29uc3Qgc29ydGVkRW50cmllcyA9IEFycmF5LmZyb20odGhpcy5lbnRyaWVzLmVudHJpZXMoKSlcbiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGFbMV0udGltZXN0YW1wLmdldFRpbWUoKSAtIGJbMV0udGltZXN0YW1wLmdldFRpbWUoKSk7XG4gICAgICBcbiAgICAgIC8vIFJlbW92ZSBvbmUgZXh0cmEgdG8gbWFrZSByb29tIGZvciBuZXcgZW50cnlcbiAgICAgIGNvbnN0IHRvRGVsZXRlID0gTWF0aC5tYXgoMSwgdGhpcy5lbnRyaWVzLnNpemUgLSB0aGlzLm1heEVudHJpZXMgKyAxKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdG9EZWxldGUgJiYgaSA8IHNvcnRlZEVudHJpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdGhpcy5lbnRyaWVzLmRlbGV0ZShzb3J0ZWRFbnRyaWVzW2ldWzBdKTtcbiAgICAgICAgZGVsZXRlZENvdW50Kys7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIGlmIChkZWxldGVkQ291bnQgPiAwKSB7XG4gICAgICB0aGlzLl9pc0RpcnR5ID0gdHJ1ZTtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogTUVNT1JZX1NFQ1VSSVRZX0VWRU5UUy5SRVRFTlRJT05fUE9MSUNZX0VORk9SQ0VELFxuICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgIHNvdXJjZTogJ01lbW9yeS5lbmZvcmNlUmV0ZW50aW9uUG9saWN5JyxcbiAgICAgICAgZGV0YWlsczogYFJlbW92ZWQgJHtkZWxldGVkQ291bnR9IGV4cGlyZWQgbWVtb3JpZXNgXG4gICAgICB9KTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGRlbGV0ZWRDb3VudDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENsZWFyIGFsbCBtZW1vcnkgZW50cmllc1xuICAgKiBTRUNVUklUWTogUmVxdWlyZXMgY29uZmlybWF0aW9uIGFuZCBsb2dzIHRoZSBhY3Rpb25cbiAgICovXG4gIHB1YmxpYyBhc3luYyBjbGVhckFsbChjb25maXJtOiBib29sZWFuID0gZmFsc2UpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIWNvbmZpcm0pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTWVtb3J5IGNsZWFyIHJlcXVpcmVzIGNvbmZpcm1hdGlvbicpO1xuICAgIH1cbiAgICBcbiAgICBjb25zdCBjb3VudCA9IHRoaXMuZW50cmllcy5zaXplO1xuICAgIHRoaXMuZW50cmllcy5jbGVhcigpO1xuICAgIHRoaXMuX2lzRGlydHkgPSB0cnVlO1xuICAgIFxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX0NMRUFSRUQsXG4gICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgc291cmNlOiAnTWVtb3J5LmNsZWFyQWxsJyxcbiAgICAgIGRldGFpbHM6IGBDbGVhcmVkIGFsbCAke2NvdW50fSBtZW1vcnkgZW50cmllc2BcbiAgICB9KTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBtZW1vcnkgc3RhdGlzdGljc1xuICAgKi9cbiAgcHVibGljIGdldFN0YXRzKCk6IHtcbiAgICB0b3RhbEVudHJpZXM6IG51bWJlcjtcbiAgICB0b3RhbFNpemU6IG51bWJlcjtcbiAgICBvbGRlc3RFbnRyeT86IERhdGU7XG4gICAgbmV3ZXN0RW50cnk/OiBEYXRlO1xuICAgIHRhZ0ZyZXF1ZW5jeTogTWFwPHN0cmluZywgbnVtYmVyPjtcbiAgfSB7XG4gICAgbGV0IHRvdGFsU2l6ZSA9IDA7XG4gICAgbGV0IG9sZGVzdEVudHJ5OiBEYXRlIHwgdW5kZWZpbmVkO1xuICAgIGxldCBuZXdlc3RFbnRyeTogRGF0ZSB8IHVuZGVmaW5lZDtcbiAgICBjb25zdCB0YWdGcmVxdWVuY3kgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xuICAgIFxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgdGhpcy5lbnRyaWVzLnZhbHVlcygpKSB7XG4gICAgICB0b3RhbFNpemUgKz0gZW50cnkuY29udGVudC5sZW5ndGg7XG4gICAgICBcbiAgICAgIGlmICghb2xkZXN0RW50cnkgfHwgZW50cnkudGltZXN0YW1wIDwgb2xkZXN0RW50cnkpIHtcbiAgICAgICAgb2xkZXN0RW50cnkgPSBlbnRyeS50aW1lc3RhbXA7XG4gICAgICB9XG4gICAgICBpZiAoIW5ld2VzdEVudHJ5IHx8IGVudHJ5LnRpbWVzdGFtcCA+IG5ld2VzdEVudHJ5KSB7XG4gICAgICAgIG5ld2VzdEVudHJ5ID0gZW50cnkudGltZXN0YW1wO1xuICAgICAgfVxuICAgICAgXG4gICAgICBlbnRyeS50YWdzPy5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIHRhZ0ZyZXF1ZW5jeS5zZXQodGFnLCAodGFnRnJlcXVlbmN5LmdldCh0YWcpIHx8IDApICsgMSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIHRvdGFsRW50cmllczogdGhpcy5lbnRyaWVzLnNpemUsXG4gICAgICB0b3RhbFNpemUsXG4gICAgICBvbGRlc3RFbnRyeSxcbiAgICAgIG5ld2VzdEVudHJ5LFxuICAgICAgdGFnRnJlcXVlbmN5XG4gICAgfTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFZhbGlkYXRlIHRoZSBtZW1vcnkgZWxlbWVudFxuICAgKi9cbiAgcHVibGljIG92ZXJyaWRlIHZhbGlkYXRlKCk6IEVsZW1lbnRWYWxpZGF0aW9uUmVzdWx0IHtcbiAgICBjb25zdCByZXN1bHQgPSBzdXBlci52YWxpZGF0ZSgpO1xuICAgIFxuICAgIC8vIEluaXRpYWxpemUgZXJyb3JzIGFycmF5IGlmIG5vdCBwcmVzZW50XG4gICAgaWYgKCFyZXN1bHQuZXJyb3JzKSB7XG4gICAgICByZXN1bHQuZXJyb3JzID0gW107XG4gICAgfVxuICAgIFxuICAgIC8vIEFkZGl0aW9uYWwgbWVtb3J5LXNwZWNpZmljIHZhbGlkYXRpb25cbiAgICBpZiAodGhpcy5yZXRlbnRpb25EYXlzIDwgTUVNT1JZX0NPTlNUQU5UUy5NSU5fUkVURU5USU9OX0RBWVMgfHwgdGhpcy5yZXRlbnRpb25EYXlzID4gTUVNT1JZX0NPTlNUQU5UUy5NQVhfUkVURU5USU9OX0RBWVMpIHtcbiAgICAgIHJlc3VsdC5lcnJvcnMucHVzaCh7XG4gICAgICAgIGZpZWxkOiAncmV0ZW50aW9uRGF5cycsXG4gICAgICAgIG1lc3NhZ2U6IGBSZXRlbnRpb24gZGF5cyBtdXN0IGJlIGJldHdlZW4gJHtNRU1PUllfQ09OU1RBTlRTLk1JTl9SRVRFTlRJT05fREFZU30gYW5kICR7TUVNT1JZX0NPTlNUQU5UUy5NQVhfUkVURU5USU9OX0RBWVN9YCxcbiAgICAgICAgc2V2ZXJpdHk6ICdlcnJvcidcbiAgICAgIH0gYXMgVmFsaWRhdGlvbkVycm9yKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKHRoaXMubWF4RW50cmllcyA8IDEgfHwgdGhpcy5tYXhFbnRyaWVzID4gTUVNT1JZX0NPTlNUQU5UUy5NQVhfRU5UUklFU19ERUZBVUxUKSB7XG4gICAgICByZXN1bHQuZXJyb3JzLnB1c2goe1xuICAgICAgICBmaWVsZDogJ21heEVudHJpZXMnLFxuICAgICAgICBtZXNzYWdlOiBgTWF4IGVudHJpZXMgbXVzdCBiZSBiZXR3ZWVuIDEgYW5kICR7TUVNT1JZX0NPTlNUQU5UUy5NQVhfRU5UUklFU19ERUZBVUxUfWAsXG4gICAgICAgIHNldmVyaXR5OiAnZXJyb3InXG4gICAgICB9IGFzIFZhbGlkYXRpb25FcnJvcik7XG4gICAgfVxuICAgIFxuICAgIC8vIENoZWNrIG1lbW9yeSBzaXplXG4gICAgY29uc3Qgc3RhdHMgPSB0aGlzLmdldFN0YXRzKCk7XG4gICAgaWYgKHN0YXRzLnRvdGFsU2l6ZSA+IE1FTU9SWV9DT05TVEFOVFMuTUFYX01FTU9SWV9TSVpFKSB7XG4gICAgICByZXN1bHQuZXJyb3JzLnB1c2goe1xuICAgICAgICBmaWVsZDogJ21lbW9yeScsXG4gICAgICAgIG1lc3NhZ2U6IGBUb3RhbCBtZW1vcnkgc2l6ZSAoJHtzdGF0cy50b3RhbFNpemV9KSBleGNlZWRzIGxpbWl0ICgke01FTU9SWV9DT05TVEFOVFMuTUFYX01FTU9SWV9TSVpFfSlgLFxuICAgICAgICBzZXZlcml0eTogJ2Vycm9yJ1xuICAgICAgfSBhcyBWYWxpZGF0aW9uRXJyb3IpO1xuICAgIH1cbiAgICBcbiAgICAvLyBVcGRhdGUgdmFsaWRpdHkgYmFzZWQgb24gb3VyIGNoZWNrc1xuICAgIHJldHVybiB7XG4gICAgICAuLi5yZXN1bHQsXG4gICAgICB2YWxpZDogcmVzdWx0LmVycm9ycy5sZW5ndGggPT09IDBcbiAgICB9O1xuICB9XG4gIFxuICAvKipcbiAgICogU2VyaWFsaXplIG1lbW9yeSB0byBzdHJpbmdcbiAgICovXG4gIHB1YmxpYyBvdmVycmlkZSBzZXJpYWxpemUoKTogc3RyaW5nIHtcbiAgICBjb25zdCBkYXRhID0ge1xuICAgICAgaWQ6IHRoaXMuaWQsXG4gICAgICB0eXBlOiB0aGlzLnR5cGUsXG4gICAgICB2ZXJzaW9uOiB0aGlzLnZlcnNpb24sXG4gICAgICBtZXRhZGF0YTogdGhpcy5tZXRhZGF0YSxcbiAgICAgIGV4dGVuc2lvbnM6IHRoaXMuZXh0ZW5zaW9ucyxcbiAgICAgIGVudHJpZXM6IEFycmF5LmZyb20odGhpcy5lbnRyaWVzLnZhbHVlcygpKVxuICAgIH07XG4gICAgXG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGRhdGEsIG51bGwsIDIpO1xuICB9XG4gIFxuICAvKipcbiAgICogRGVzZXJpYWxpemUgbWVtb3J5IGZyb20gc3RyaW5nXG4gICAqIFNFQ1VSSVRZOiBWYWxpZGF0ZXMgYWxsIGxvYWRlZCBkYXRhXG4gICAqL1xuICBwdWJsaWMgb3ZlcnJpZGUgZGVzZXJpYWxpemUoZGF0YTogc3RyaW5nKTogdm9pZCB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICBcbiAgICAgIC8vIFZhbGlkYXRlIGJhc2ljIHN0cnVjdHVyZVxuICAgICAgaWYgKCFwYXJzZWQuaWQgfHwgIXBhcnNlZC50eXBlIHx8IHBhcnNlZC50eXBlICE9PSBFbGVtZW50VHlwZS5NRU1PUlkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIG1lbW9yeSBkYXRhIGZvcm1hdCcpO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBVcGRhdGUgcHJvcGVydGllc1xuICAgICAgdGhpcy5pZCA9IHBhcnNlZC5pZDtcbiAgICAgIHRoaXMudmVyc2lvbiA9IHBhcnNlZC52ZXJzaW9uIHx8ICcxLjAuMCc7XG4gICAgICB0aGlzLm1ldGFkYXRhID0gcGFyc2VkLm1ldGFkYXRhIHx8IHt9O1xuICAgICAgdGhpcy5leHRlbnNpb25zID0gcGFyc2VkLmV4dGVuc2lvbnMgfHwge307XG4gICAgICBcbiAgICAgIC8vIENsZWFyIGFuZCByZWxvYWQgZW50cmllc1xuICAgICAgdGhpcy5lbnRyaWVzLmNsZWFyKCk7XG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShwYXJzZWQuZW50cmllcykpIHtcbiAgICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiBwYXJzZWQuZW50cmllcykge1xuICAgICAgICAgIGlmICh0aGlzLmlzVmFsaWRFbnRyeShlbnRyeSkpIHtcbiAgICAgICAgICAgIC8vIFVzZSBvcHRpbWl6ZWQgc2FuaXRpemF0aW9uIHdpdGggY2FjaGluZ1xuICAgICAgICAgICAgZW50cnkuY29udGVudCA9IHRoaXMuc2FuaXRpemVXaXRoQ2FjaGUoZW50cnkuY29udGVudCwgTUVNT1JZX0NPTlNUQU5UUy5NQVhfRU5UUllfU0laRSk7XG4gICAgICAgICAgICBlbnRyeS50YWdzID0gdGhpcy5zYW5pdGl6ZVRhZ3MoZW50cnkudGFncyB8fCBbXSk7XG4gICAgICAgICAgICBlbnRyeS50aW1lc3RhbXAgPSBuZXcgRGF0ZShlbnRyeS50aW1lc3RhbXApO1xuICAgICAgICAgICAgaWYgKGVudHJ5LmV4cGlyZXNBdCkge1xuICAgICAgICAgICAgICBlbnRyeS5leHBpcmVzQXQgPSBuZXcgRGF0ZShlbnRyeS5leHBpcmVzQXQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5lbnRyaWVzLnNldChlbnRyeS5pZCwgZW50cnkpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBFbmZvcmNlIHJldGVudGlvbiBwb2xpY3kgYWZ0ZXIgbG9hZGluZ1xuICAgICAgdGhpcy5lbmZvcmNlUmV0ZW50aW9uUG9saWN5KCk7XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLk1FTU9SWV9ERVNFUklBTElaRV9GQUlMRUQsXG4gICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgIHNvdXJjZTogJ01lbW9yeS5kZXNlcmlhbGl6ZScsXG4gICAgICAgIGRldGFpbHM6IGBGYWlsZWQgdG8gZGVzZXJpYWxpemUgbWVtb3J5OiAke2Vycm9yfWBcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZGVzZXJpYWxpemUgbWVtb3J5OiAke2Vycm9yfWApO1xuICAgIH1cbiAgfVxuICBcbiAgLy8gUHJpdmF0ZSBoZWxwZXIgbWV0aG9kc1xuICBcbiAgcHJpdmF0ZSBjYWxjdWxhdGVFeHBpcnlEYXRlKCk6IERhdGUge1xuICAgIGNvbnN0IGV4cGlyeSA9IG5ldyBEYXRlKCk7XG4gICAgZXhwaXJ5LnNldERhdGUoZXhwaXJ5LmdldERhdGUoKSArIHRoaXMucmV0ZW50aW9uRGF5cyk7XG4gICAgcmV0dXJuIGV4cGlyeTtcbiAgfVxuICBcbiAgcHJpdmF0ZSBzYW5pdGl6ZVRhZ3ModGFnczogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gICAgLy8gU0VDVVJJVFkgRklYOiBMaW1pdCBudW1iZXIgb2YgdGFncyBhbmQgc2FuaXRpemUgZWFjaFxuICAgIGNvbnN0IGxpbWl0ZWRUYWdzID0gdGFncy5zbGljZSgwLCBNRU1PUllfQ09OU1RBTlRTLk1BWF9UQUdTX1BFUl9FTlRSWSk7XG4gICAgXG4gICAgcmV0dXJuIGxpbWl0ZWRUYWdzXG4gICAgICAubWFwKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZSh0YWcpLm5vcm1hbGl6ZWRDb250ZW50O1xuICAgICAgICByZXR1cm4gc2FuaXRpemVJbnB1dChub3JtYWxpemVkLCBNRU1PUllfQ09OU1RBTlRTLk1BWF9UQUdfTEVOR1RIKTtcbiAgICAgIH0pXG4gICAgICAuZmlsdGVyKHRhZyA9PiB0YWcgJiYgdGFnLmxlbmd0aCA+IDApO1xuICB9XG4gIFxuICBwcml2YXRlIHNhbml0aXplTWV0YWRhdGEobWV0YWRhdGE/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+KTogUmVjb3JkPHN0cmluZywgYW55PiB8IHVuZGVmaW5lZCB7XG4gICAgaWYgKCFtZXRhZGF0YSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICBcbiAgICAvLyBTRUNVUklUWSBGSVg6IFNhbml0aXplIG1ldGFkYXRhIHZhbHVlc1xuICAgIGNvbnN0IHNhbml0aXplZDogUmVjb3JkPHN0cmluZywgYW55PiA9IHt9O1xuICAgIGNvbnN0IG1heEtleXMgPSBNRU1PUllfQ09OU1RBTlRTLk1BWF9NRVRBREFUQV9LRVlTO1xuICAgIGxldCBrZXlDb3VudCA9IDA7XG4gICAgXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobWV0YWRhdGEpKSB7XG4gICAgICBpZiAoa2V5Q291bnQgPj0gbWF4S2V5cykgYnJlYWs7XG4gICAgICBcbiAgICAgIGNvbnN0IHNhbml0aXplZEtleSA9IHNhbml0aXplSW5wdXQoa2V5LCBNRU1PUllfQ09OU1RBTlRTLk1BWF9NRVRBREFUQV9LRVlfTEVOR1RIKTtcbiAgICAgIGlmIChzYW5pdGl6ZWRLZXkgJiYgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xuICAgICAgICBzYW5pdGl6ZWRbc2FuaXRpemVkS2V5XSA9IHNhbml0aXplSW5wdXQodmFsdWUsIE1FTU9SWV9DT05TVEFOVFMuTUFYX01FVEFEQVRBX1ZBTFVFX0xFTkdUSCk7XG4gICAgICAgIGtleUNvdW50Kys7XG4gICAgICB9IGVsc2UgaWYgKHNhbml0aXplZEtleSAmJiB0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInKSB7XG4gICAgICAgIHNhbml0aXplZFtzYW5pdGl6ZWRLZXldID0gdmFsdWU7XG4gICAgICAgIGtleUNvdW50Kys7XG4gICAgICB9XG4gICAgICAvLyBTa2lwIG90aGVyIHR5cGVzIGZvciBzZWN1cml0eVxuICAgIH1cbiAgICBcbiAgICByZXR1cm4gc2FuaXRpemVkO1xuICB9XG4gIFxuICBwcml2YXRlIGNhbkFjY2Vzc1ByaXZhY3lMZXZlbChlbnRyeUxldmVsOiBzdHJpbmcsIHJlcXVlc3RlZExldmVsOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBjb25zdCBsZXZlbHMgPSBNRU1PUllfQ09OU1RBTlRTLlBSSVZBQ1lfTEVWRUxTO1xuICAgIGNvbnN0IGVudHJ5SW5kZXggPSBsZXZlbHMuaW5kZXhPZihlbnRyeUxldmVsIGFzIFByaXZhY3lMZXZlbCk7XG4gICAgY29uc3QgcmVxdWVzdGVkSW5kZXggPSBsZXZlbHMuaW5kZXhPZihyZXF1ZXN0ZWRMZXZlbCBhcyBQcml2YWN5TGV2ZWwpO1xuICAgIFxuICAgIC8vIENhbiBvbmx5IGFjY2VzcyBlbnRyaWVzIGF0IG9yIGJlbG93IHRoZSByZXF1ZXN0ZWQgcHJpdmFjeSBsZXZlbFxuICAgIC8vIGUuZy4sIGlmIHJlcXVlc3RpbmcgJ3ByaXZhdGUnLCBjYW4gc2VlICdwdWJsaWMnIGFuZCAncHJpdmF0ZScgYnV0IG5vdCAnc2Vuc2l0aXZlJ1xuICAgIHJldHVybiBlbnRyeUluZGV4IDw9IHJlcXVlc3RlZEluZGV4O1xuICB9XG4gIFxuICBwcml2YXRlIGlzVmFsaWRFbnRyeShlbnRyeTogYW55KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGVudHJ5ICYmXG4gICAgICB0eXBlb2YgZW50cnkuaWQgPT09ICdzdHJpbmcnICYmXG4gICAgICB0eXBlb2YgZW50cnkuY29udGVudCA9PT0gJ3N0cmluZycgJiZcbiAgICAgIGVudHJ5LnRpbWVzdGFtcCAmJlxuICAgICAgKCFlbnRyeS50YWdzIHx8IEFycmF5LmlzQXJyYXkoZW50cnkudGFncykpO1xuICB9XG5cbiAgLyoqXG4gICAqIE9wdGltaXplZCBzYW5pdGl6YXRpb24gd2l0aCBjaGVja3N1bSBjYWNoaW5nXG4gICAqIEF2b2lkcyByZS1zYW5pdGl6aW5nIGNvbnRlbnQgdGhhdCBoYXNuJ3QgY2hhbmdlZFxuICAgKiBAcGFyYW0gY29udGVudCBDb250ZW50IHRvIHNhbml0aXplXG4gICAqIEBwYXJhbSBtYXhMZW5ndGggTWF4aW11bSBhbGxvd2VkIGxlbmd0aFxuICAgKiBAcmV0dXJucyBTYW5pdGl6ZWQgY29udGVudFxuICAgKi9cbiAgcHJpdmF0ZSBzYW5pdGl6ZVdpdGhDYWNoZShjb250ZW50OiBzdHJpbmcsIG1heExlbmd0aDogbnVtYmVyKTogc3RyaW5nIHtcbiAgICAvLyBHZW5lcmF0ZSBjaGVja3N1bSBmb3IgdGhlIGlucHV0XG4gICAgY29uc3QgY2hlY2tzdW0gPSBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKGNvbnRlbnQpLmRpZ2VzdCgnaGV4Jyk7XG5cbiAgICAvLyBDaGVjayBpZiB3ZSd2ZSBhbHJlYWR5IHNhbml0aXplZCB0aGlzIGV4YWN0IGNvbnRlbnRcbiAgICBjb25zdCBjYWNoZUtleSA9IGAke2NoZWNrc3VtfToke21heExlbmd0aH1gO1xuICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMuc2FuaXRpemF0aW9uQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICBpZiAoY2FjaGVkKSB7XG4gICAgICByZXR1cm4gY2FjaGVkO1xuICAgIH1cblxuICAgIC8vIFBlcmZvcm0gc2FuaXRpemF0aW9uXG4gICAgY29uc3Qgc2FuaXRpemVkID0gc2FuaXRpemVNZW1vcnlDb250ZW50KGNvbnRlbnQsIG1heExlbmd0aCk7XG5cbiAgICAvLyBDYWNoZSB0aGUgcmVzdWx0IChsaW1pdCBjYWNoZSBzaXplIHRvIHByZXZlbnQgbWVtb3J5IGlzc3VlcylcbiAgICBpZiAodGhpcy5zYW5pdGl6YXRpb25DYWNoZS5zaXplID4gMTAwMCkge1xuICAgICAgLy8gUmVtb3ZlIG9sZGVzdCBlbnRyaWVzIChzaW1wbGUgRklGTylcbiAgICAgIGNvbnN0IGZpcnN0S2V5ID0gdGhpcy5zYW5pdGl6YXRpb25DYWNoZS5rZXlzKCkubmV4dCgpLnZhbHVlO1xuICAgICAgaWYgKGZpcnN0S2V5KSB0aGlzLnNhbml0aXphdGlvbkNhY2hlLmRlbGV0ZShmaXJzdEtleSk7XG4gICAgfVxuICAgIHRoaXMuc2FuaXRpemF0aW9uQ2FjaGUuc2V0KGNhY2hlS2V5LCBzYW5pdGl6ZWQpO1xuXG4gICAgcmV0dXJuIHNhbml0aXplZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZCBzZWFyY2ggaW5kZXggd2l0aCByZXRyeSBsb2dpY1xuICAgKiBBdHRlbXB0cyB0byBidWlsZCB0aGUgaW5kZXggdXAgdG8gMyB0aW1lcyB3aXRoIGV4cG9uZW50aWFsIGJhY2tvZmZcbiAgICogVGhpcyBlbnN1cmVzIHNlYXJjaCBmdW5jdGlvbmFsaXR5IGV2ZW4gaWYgdGhlcmUgYXJlIHRyYW5zaWVudCBmYWlsdXJlc1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBidWlsZFNlYXJjaEluZGV4V2l0aFJldHJ5KHJldHJpZXMgPSAzKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IGxhc3RFcnJvcjogRXJyb3IgfCB1bmRlZmluZWQ7XG5cbiAgICBmb3IgKGxldCBhdHRlbXB0ID0gMTsgYXR0ZW1wdCA8PSByZXRyaWVzOyBhdHRlbXB0KyspIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuc2VhcmNoSW5kZXguYnVpbGRJbmRleCh0aGlzLmVudHJpZXMpO1xuICAgICAgICBsb2dnZXIuZGVidWcoYFNlYXJjaCBpbmRleCBidWlsdCBzdWNjZXNzZnVsbHkgb24gYXR0ZW1wdCAke2F0dGVtcHR9YCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxhc3RFcnJvciA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvciA6IG5ldyBFcnJvcihTdHJpbmcoZXJyb3IpKTtcbiAgICAgICAgbG9nZ2VyLndhcm4oYFNlYXJjaCBpbmRleCBidWlsZCBhdHRlbXB0ICR7YXR0ZW1wdH0gZmFpbGVkYCwge1xuICAgICAgICAgIGVycm9yOiBsYXN0RXJyb3IubWVzc2FnZSxcbiAgICAgICAgICBlbnRyaWVzQ291bnQ6IHRoaXMuZW50cmllcy5zaXplXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmIChhdHRlbXB0IDwgcmV0cmllcykge1xuICAgICAgICAgIC8vIEV4cG9uZW50aWFsIGJhY2tvZmY6IDEwMG1zLCAyMDBtcywgNDAwbXNcbiAgICAgICAgICBjb25zdCBkZWxheSA9IE1hdGgucG93KDIsIGF0dGVtcHQgLSAxKSAqIDEwMDtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgZGVsYXkpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIElmIHdlIGdldCBoZXJlLCBhbGwgcmV0cmllcyBmYWlsZWRcbiAgICB0aHJvdyBsYXN0RXJyb3IgfHwgbmV3IEVycm9yKCdGYWlsZWQgdG8gYnVpbGQgc2VhcmNoIGluZGV4Jyk7XG4gIH1cbn0iXX0=
|