@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,690 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Search Index - High-performance indexed search for Memory elements
|
|
3
|
+
*
|
|
4
|
+
* Addresses issue #984: Implement search indexing for Memory element scalability
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Tag index for O(1) tag-based queries
|
|
8
|
+
* - Content index using inverted index pattern
|
|
9
|
+
* - Temporal index for date range queries
|
|
10
|
+
* - Privacy level index for filtered access
|
|
11
|
+
* - Incremental index updates
|
|
12
|
+
* - Optional persistence
|
|
13
|
+
* - Configurable thresholds
|
|
14
|
+
*/
|
|
15
|
+
import { MEMORY_CONSTANTS, MEMORY_SECURITY_EVENTS } from './constants.js';
|
|
16
|
+
import { logger } from '../../utils/logger.js';
|
|
17
|
+
import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
|
|
18
|
+
import { SecurityMonitor } from '../../security/securityMonitor.js';
|
|
19
|
+
/**
|
|
20
|
+
* Inverted index for content search
|
|
21
|
+
*/
|
|
22
|
+
class ContentIndex {
|
|
23
|
+
termToEntries = new Map();
|
|
24
|
+
entryToTerms = new Map();
|
|
25
|
+
config;
|
|
26
|
+
constructor(config) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Add an entry to the content index
|
|
31
|
+
*/
|
|
32
|
+
addEntry(entryId, content) {
|
|
33
|
+
const terms = this.extractTerms(content, { entryId });
|
|
34
|
+
// Store terms for this entry
|
|
35
|
+
this.entryToTerms.set(entryId, terms);
|
|
36
|
+
// Update inverted index
|
|
37
|
+
for (const term of terms) {
|
|
38
|
+
let entries = this.termToEntries.get(term);
|
|
39
|
+
if (!entries) {
|
|
40
|
+
entries = new Set();
|
|
41
|
+
this.termToEntries.set(term, entries);
|
|
42
|
+
}
|
|
43
|
+
entries.add(entryId);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Remove an entry from the index
|
|
48
|
+
*/
|
|
49
|
+
removeEntry(entryId) {
|
|
50
|
+
const terms = this.entryToTerms.get(entryId);
|
|
51
|
+
if (!terms)
|
|
52
|
+
return;
|
|
53
|
+
// Remove from inverted index
|
|
54
|
+
for (const term of terms) {
|
|
55
|
+
const entries = this.termToEntries.get(term);
|
|
56
|
+
if (entries) {
|
|
57
|
+
entries.delete(entryId);
|
|
58
|
+
if (entries.size === 0) {
|
|
59
|
+
this.termToEntries.delete(term);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Remove entry terms
|
|
64
|
+
this.entryToTerms.delete(entryId);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Search for entries containing query terms
|
|
68
|
+
*/
|
|
69
|
+
search(query) {
|
|
70
|
+
const queryTerms = this.extractTerms(query);
|
|
71
|
+
const scores = new Map();
|
|
72
|
+
for (const term of queryTerms) {
|
|
73
|
+
const entries = this.termToEntries.get(term);
|
|
74
|
+
if (entries) {
|
|
75
|
+
for (const entryId of entries) {
|
|
76
|
+
scores.set(entryId, (scores.get(entryId) || 0) + 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return scores;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Extract searchable terms from content
|
|
84
|
+
* SECURITY FIX: Added audit logging for Unicode validation operations
|
|
85
|
+
*/
|
|
86
|
+
extractTerms(content, context) {
|
|
87
|
+
// Normalize for security
|
|
88
|
+
const normalized = UnicodeValidator.normalize(content);
|
|
89
|
+
if (!normalized.isValid) {
|
|
90
|
+
logger.warn('Invalid Unicode in content for indexing', {
|
|
91
|
+
entryId: context?.entryId
|
|
92
|
+
});
|
|
93
|
+
// SECURITY FIX: Log security event for audit trail (DMCP-SEC-006)
|
|
94
|
+
SecurityMonitor.logSecurityEvent({
|
|
95
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_UNICODE_VALIDATION_FAILED,
|
|
96
|
+
severity: 'LOW',
|
|
97
|
+
source: 'MemorySearchIndex.extractTerms',
|
|
98
|
+
details: `Invalid Unicode detected during term extraction${context?.entryId ? ` for entry ${context.entryId}` : ''}`,
|
|
99
|
+
metadata: {
|
|
100
|
+
entryId: context?.entryId,
|
|
101
|
+
issueCount: 0 // UnicodeValidationResult doesn't expose issues
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return new Set();
|
|
105
|
+
}
|
|
106
|
+
const text = normalized.normalizedContent.toLowerCase();
|
|
107
|
+
const terms = new Set();
|
|
108
|
+
// Simple tokenization (can be improved with better NLP)
|
|
109
|
+
const words = text.match(/\b\w+\b/g) || [];
|
|
110
|
+
let termCount = 0;
|
|
111
|
+
for (const word of words) {
|
|
112
|
+
if (word.length >= (this.config.minTermLength || 2)) {
|
|
113
|
+
terms.add(word);
|
|
114
|
+
termCount++;
|
|
115
|
+
if (termCount >= (this.config.maxTermsPerEntry || 100)) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return terms;
|
|
121
|
+
}
|
|
122
|
+
clear() {
|
|
123
|
+
this.termToEntries.clear();
|
|
124
|
+
this.entryToTerms.clear();
|
|
125
|
+
}
|
|
126
|
+
get size() {
|
|
127
|
+
return this.termToEntries.size;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Date range index using sorted array for efficient range queries
|
|
132
|
+
*/
|
|
133
|
+
class TemporalIndex {
|
|
134
|
+
entries = [];
|
|
135
|
+
addEntry(entryId, date) {
|
|
136
|
+
const timestamp = date.getTime();
|
|
137
|
+
// Binary search for insertion point
|
|
138
|
+
let left = 0;
|
|
139
|
+
let right = this.entries.length;
|
|
140
|
+
while (left < right) {
|
|
141
|
+
const mid = Math.floor((left + right) / 2);
|
|
142
|
+
if (this.entries[mid].timestamp < timestamp) {
|
|
143
|
+
left = mid + 1;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
right = mid;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Insert at correct position to maintain sort
|
|
150
|
+
this.entries.splice(left, 0, { id: entryId, timestamp });
|
|
151
|
+
}
|
|
152
|
+
removeEntry(entryId) {
|
|
153
|
+
const index = this.entries.findIndex(e => e.id === entryId);
|
|
154
|
+
if (index >= 0) {
|
|
155
|
+
this.entries.splice(index, 1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Find entries within date range
|
|
160
|
+
*/
|
|
161
|
+
searchRange(from, to) {
|
|
162
|
+
const results = new Set();
|
|
163
|
+
const fromTime = from?.getTime() || 0;
|
|
164
|
+
const toTime = to?.getTime() || Date.now();
|
|
165
|
+
// Binary search for start
|
|
166
|
+
let start = 0;
|
|
167
|
+
let end = this.entries.length;
|
|
168
|
+
while (start < end) {
|
|
169
|
+
const mid = Math.floor((start + end) / 2);
|
|
170
|
+
if (this.entries[mid].timestamp < fromTime) {
|
|
171
|
+
start = mid + 1;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
end = mid;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Collect all entries in range
|
|
178
|
+
for (let i = start; i < this.entries.length; i++) {
|
|
179
|
+
const entry = this.entries[i];
|
|
180
|
+
if (entry.timestamp > toTime)
|
|
181
|
+
break;
|
|
182
|
+
results.add(entry.id);
|
|
183
|
+
}
|
|
184
|
+
return results;
|
|
185
|
+
}
|
|
186
|
+
clear() {
|
|
187
|
+
this.entries = [];
|
|
188
|
+
}
|
|
189
|
+
get size() {
|
|
190
|
+
return this.entries.length;
|
|
191
|
+
}
|
|
192
|
+
serialize() {
|
|
193
|
+
return this.entries;
|
|
194
|
+
}
|
|
195
|
+
deserialize(data) {
|
|
196
|
+
this.entries = data || [];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Main search index for Memory elements
|
|
201
|
+
*/
|
|
202
|
+
export class MemorySearchIndex {
|
|
203
|
+
config;
|
|
204
|
+
// Indexes
|
|
205
|
+
tagIndex = new Map();
|
|
206
|
+
privacyIndex = new Map();
|
|
207
|
+
contentIndex = null;
|
|
208
|
+
temporalIndex = new TemporalIndex();
|
|
209
|
+
// Cached entries for scoring
|
|
210
|
+
entriesCache = new Map();
|
|
211
|
+
// Index state
|
|
212
|
+
isBuilt = false;
|
|
213
|
+
isBuilding = false;
|
|
214
|
+
buildQueue = null;
|
|
215
|
+
stats = {
|
|
216
|
+
isIndexed: false,
|
|
217
|
+
entryCount: 0,
|
|
218
|
+
tagCount: 0,
|
|
219
|
+
termCount: 0,
|
|
220
|
+
memoryUsageBytes: 0
|
|
221
|
+
};
|
|
222
|
+
// Memory management
|
|
223
|
+
maxMemoryBytes;
|
|
224
|
+
memoryUsageBytes = 0;
|
|
225
|
+
constructor(config = {}) {
|
|
226
|
+
this.config = {
|
|
227
|
+
indexThreshold: config.indexThreshold || 100,
|
|
228
|
+
enableContentIndex: config.enableContentIndex !== false,
|
|
229
|
+
maxTermsPerEntry: config.maxTermsPerEntry || 100,
|
|
230
|
+
minTermLength: config.minTermLength || 2,
|
|
231
|
+
enablePersistence: config.enablePersistence || false,
|
|
232
|
+
maxMemoryMB: config.maxMemoryMB || 100,
|
|
233
|
+
enableLRUEviction: config.enableLRUEviction !== false
|
|
234
|
+
};
|
|
235
|
+
// Configure memory limit (default 100MB, configurable)
|
|
236
|
+
this.maxMemoryBytes = (this.config.maxMemoryMB || 100) * 1024 * 1024;
|
|
237
|
+
if (this.config.enableContentIndex) {
|
|
238
|
+
this.contentIndex = new ContentIndex(this.config);
|
|
239
|
+
}
|
|
240
|
+
logger.debug('MemorySearchIndex created', this.config);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Build or rebuild the index from entries
|
|
244
|
+
* FIX: Added race condition protection with isBuilding flag and build queue
|
|
245
|
+
*/
|
|
246
|
+
async buildIndex(entries) {
|
|
247
|
+
// If already building, wait for the current build to complete
|
|
248
|
+
if (this.isBuilding && this.buildQueue) {
|
|
249
|
+
logger.debug('Index build already in progress, waiting...');
|
|
250
|
+
return this.buildQueue;
|
|
251
|
+
}
|
|
252
|
+
// Create build promise to handle concurrent calls
|
|
253
|
+
this.buildQueue = this._doBuildIndex(entries);
|
|
254
|
+
return this.buildQueue;
|
|
255
|
+
}
|
|
256
|
+
async _doBuildIndex(entries) {
|
|
257
|
+
// Prevent concurrent builds
|
|
258
|
+
if (this.isBuilding) {
|
|
259
|
+
logger.warn('Attempted concurrent index build, skipping');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
this.isBuilding = true;
|
|
263
|
+
const startTime = Date.now();
|
|
264
|
+
try {
|
|
265
|
+
// Clear existing indexes
|
|
266
|
+
this.clear();
|
|
267
|
+
// Check if we should index based on threshold
|
|
268
|
+
if (entries.size < (this.config.indexThreshold || 100)) {
|
|
269
|
+
logger.debug('Not building index - below threshold', {
|
|
270
|
+
entryCount: entries.size,
|
|
271
|
+
threshold: this.config.indexThreshold
|
|
272
|
+
});
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// Build indexes
|
|
276
|
+
for (const [id, entry] of entries) {
|
|
277
|
+
this.addToIndex(id, entry);
|
|
278
|
+
}
|
|
279
|
+
// Calculate memory usage
|
|
280
|
+
this.updateMemoryUsage();
|
|
281
|
+
// SECURITY FIX: Log security event for index build operation (DMCP-SEC-006)
|
|
282
|
+
SecurityMonitor.logSecurityEvent({
|
|
283
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_CREATED, // Using CREATED for index build
|
|
284
|
+
severity: 'LOW',
|
|
285
|
+
source: 'MemorySearchIndex.buildIndex',
|
|
286
|
+
details: `Memory search index built successfully with ${entries.size} entries`,
|
|
287
|
+
metadata: {
|
|
288
|
+
entryCount: entries.size,
|
|
289
|
+
tagCount: this.tagIndex.size,
|
|
290
|
+
memoryUsageBytes: this.memoryUsageBytes
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
// Update stats
|
|
294
|
+
const buildTime = Date.now() - startTime;
|
|
295
|
+
this.stats = {
|
|
296
|
+
isIndexed: true,
|
|
297
|
+
entryCount: entries.size,
|
|
298
|
+
tagCount: this.tagIndex.size,
|
|
299
|
+
termCount: this.contentIndex?.size || 0,
|
|
300
|
+
lastBuilt: new Date(),
|
|
301
|
+
buildTimeMs: buildTime,
|
|
302
|
+
memoryUsageBytes: this.memoryUsageBytes
|
|
303
|
+
};
|
|
304
|
+
this.isBuilt = true;
|
|
305
|
+
logger.info('Memory search index built', this.stats);
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
logger.error('Failed to build memory search index', error);
|
|
309
|
+
// Reset state on failure
|
|
310
|
+
this.clear();
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
this.isBuilding = false;
|
|
315
|
+
this.buildQueue = null;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Add a single entry to the index (incremental update)
|
|
320
|
+
*/
|
|
321
|
+
addEntry(entry) {
|
|
322
|
+
if (!this.isBuilt)
|
|
323
|
+
return;
|
|
324
|
+
this.addToIndex(entry.id, entry);
|
|
325
|
+
this.stats.entryCount++;
|
|
326
|
+
logger.debug('Entry added to index', { id: entry.id });
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Remove an entry from the index
|
|
330
|
+
*/
|
|
331
|
+
removeEntry(entryId) {
|
|
332
|
+
if (!this.isBuilt)
|
|
333
|
+
return;
|
|
334
|
+
const entry = this.entriesCache.get(entryId);
|
|
335
|
+
if (!entry)
|
|
336
|
+
return;
|
|
337
|
+
// Remove from tag index
|
|
338
|
+
if (entry.metadata?.tags) {
|
|
339
|
+
for (const tag of entry.metadata.tags) {
|
|
340
|
+
const entries = this.tagIndex.get(tag);
|
|
341
|
+
if (entries) {
|
|
342
|
+
entries.delete(entryId);
|
|
343
|
+
if (entries.size === 0) {
|
|
344
|
+
this.tagIndex.delete(tag);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Remove from privacy index
|
|
350
|
+
const privacyLevel = entry.metadata?.privacyLevel || entry.privacyLevel || MEMORY_CONSTANTS.DEFAULT_PRIVACY_LEVEL;
|
|
351
|
+
const privacyEntries = this.privacyIndex.get(privacyLevel);
|
|
352
|
+
if (privacyEntries) {
|
|
353
|
+
privacyEntries.delete(entryId);
|
|
354
|
+
}
|
|
355
|
+
// Remove from content index
|
|
356
|
+
this.contentIndex?.removeEntry(entryId);
|
|
357
|
+
// Remove from temporal index
|
|
358
|
+
this.temporalIndex.removeEntry(entryId);
|
|
359
|
+
// Remove from cache
|
|
360
|
+
this.entriesCache.delete(entryId);
|
|
361
|
+
this.stats.entryCount--;
|
|
362
|
+
logger.debug('Entry removed from index', { id: entryId });
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Search the index with multiple criteria
|
|
366
|
+
*/
|
|
367
|
+
search(query, entries) {
|
|
368
|
+
if (!this.isBuilt) {
|
|
369
|
+
logger.debug('Index not built, falling back to linear search');
|
|
370
|
+
return this.linearSearch(query, entries);
|
|
371
|
+
}
|
|
372
|
+
// Start with all entries or filtered by privacy
|
|
373
|
+
let candidateIds = null;
|
|
374
|
+
// Filter by privacy level
|
|
375
|
+
if (query.privacyLevel) {
|
|
376
|
+
candidateIds = new Set(this.privacyIndex.get(query.privacyLevel) || []);
|
|
377
|
+
}
|
|
378
|
+
// Filter by tags (intersection)
|
|
379
|
+
if (query.tags && query.tags.length > 0) {
|
|
380
|
+
const tagResults = new Set();
|
|
381
|
+
for (const tag of query.tags) {
|
|
382
|
+
const entries = this.tagIndex.get(tag.toLowerCase());
|
|
383
|
+
if (entries) {
|
|
384
|
+
if (candidateIds) {
|
|
385
|
+
// Intersection with existing candidates
|
|
386
|
+
for (const id of entries) {
|
|
387
|
+
if (candidateIds.has(id)) {
|
|
388
|
+
tagResults.add(id);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
// First filter
|
|
394
|
+
entries.forEach(id => tagResults.add(id));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
candidateIds = tagResults;
|
|
399
|
+
}
|
|
400
|
+
// Filter by date range
|
|
401
|
+
if (query.dateFrom || query.dateTo) {
|
|
402
|
+
const dateResults = this.temporalIndex.searchRange(query.dateFrom, query.dateTo);
|
|
403
|
+
if (candidateIds) {
|
|
404
|
+
// Intersection
|
|
405
|
+
const intersection = new Set();
|
|
406
|
+
for (const id of candidateIds) {
|
|
407
|
+
if (dateResults.has(id)) {
|
|
408
|
+
intersection.add(id);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
candidateIds = intersection;
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
candidateIds = dateResults;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Score by content if provided
|
|
418
|
+
const contentScores = query.content && this.contentIndex
|
|
419
|
+
? this.contentIndex.search(query.content)
|
|
420
|
+
: new Map();
|
|
421
|
+
// Build results
|
|
422
|
+
const results = [];
|
|
423
|
+
const searchEntries = candidateIds || new Set(entries.keys());
|
|
424
|
+
for (const id of searchEntries) {
|
|
425
|
+
const entry = entries.get(id);
|
|
426
|
+
if (!entry)
|
|
427
|
+
continue;
|
|
428
|
+
// Calculate score
|
|
429
|
+
let score = 1;
|
|
430
|
+
// Content relevance score
|
|
431
|
+
if (contentScores.has(id)) {
|
|
432
|
+
score += contentScores.get(id) * 2;
|
|
433
|
+
}
|
|
434
|
+
// Tag match bonus
|
|
435
|
+
if (query.tags && entry.metadata?.tags) {
|
|
436
|
+
const matchedTags = query.tags.filter(tag => entry.metadata?.tags?.includes(tag));
|
|
437
|
+
score += matchedTags.length;
|
|
438
|
+
}
|
|
439
|
+
results.push({
|
|
440
|
+
entry,
|
|
441
|
+
score,
|
|
442
|
+
matches: {
|
|
443
|
+
tags: query.tags?.filter(tag => entry.metadata?.tags?.includes(tag)),
|
|
444
|
+
terms: query.content ? ['indexed'] : undefined
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
// Sort by score and apply pagination
|
|
449
|
+
results.sort((a, b) => b.score - a.score);
|
|
450
|
+
const offset = query.offset || 0;
|
|
451
|
+
const limit = query.limit || 100;
|
|
452
|
+
return results.slice(offset, offset + limit);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Fallback linear search when index is not available
|
|
456
|
+
*/
|
|
457
|
+
linearSearch(query, entries) {
|
|
458
|
+
const results = [];
|
|
459
|
+
for (const [id, entry] of entries) {
|
|
460
|
+
// Check privacy level
|
|
461
|
+
if (query.privacyLevel && entry.metadata?.privacyLevel !== query.privacyLevel) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
// Check date range
|
|
465
|
+
if (query.dateFrom && entry.metadata?.timestamp && entry.metadata.timestamp < query.dateFrom) {
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (query.dateTo && entry.metadata?.timestamp && entry.metadata.timestamp > query.dateTo) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
// Check tags
|
|
472
|
+
if (query.tags && query.tags.length > 0) {
|
|
473
|
+
const hasAllTags = query.tags.every(tag => entry.metadata?.tags?.includes(tag));
|
|
474
|
+
if (!hasAllTags)
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
// Check content (simple substring match)
|
|
478
|
+
let score = 1;
|
|
479
|
+
if (query.content) {
|
|
480
|
+
const normalized = UnicodeValidator.normalize(query.content);
|
|
481
|
+
if (normalized.isValid) {
|
|
482
|
+
const searchTerm = normalized.normalizedContent.toLowerCase();
|
|
483
|
+
if (entry.content.toLowerCase().includes(searchTerm)) {
|
|
484
|
+
score += 2;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
results.push({
|
|
489
|
+
entry,
|
|
490
|
+
score,
|
|
491
|
+
matches: {
|
|
492
|
+
tags: query.tags?.filter(tag => entry.metadata?.tags?.includes(tag))
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
// Sort and paginate
|
|
497
|
+
results.sort((a, b) => b.score - a.score);
|
|
498
|
+
const offset = query.offset || 0;
|
|
499
|
+
const limit = query.limit || 100;
|
|
500
|
+
return results.slice(offset, offset + limit);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Internal helper to add entry to all indexes
|
|
504
|
+
*/
|
|
505
|
+
addToIndex(id, entry) {
|
|
506
|
+
// Cache entry
|
|
507
|
+
this.entriesCache.set(id, entry);
|
|
508
|
+
// Add to tag index
|
|
509
|
+
if (entry.metadata?.tags) {
|
|
510
|
+
for (const tag of entry.metadata.tags) {
|
|
511
|
+
const normalizedTag = tag.toLowerCase();
|
|
512
|
+
let entries = this.tagIndex.get(normalizedTag);
|
|
513
|
+
if (!entries) {
|
|
514
|
+
entries = new Set();
|
|
515
|
+
this.tagIndex.set(normalizedTag, entries);
|
|
516
|
+
}
|
|
517
|
+
entries.add(id);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// Add to privacy index
|
|
521
|
+
const privacyLevel = entry.metadata?.privacyLevel || entry.privacyLevel || MEMORY_CONSTANTS.DEFAULT_PRIVACY_LEVEL;
|
|
522
|
+
let privacyEntries = this.privacyIndex.get(privacyLevel);
|
|
523
|
+
if (!privacyEntries) {
|
|
524
|
+
privacyEntries = new Set();
|
|
525
|
+
this.privacyIndex.set(privacyLevel, privacyEntries);
|
|
526
|
+
}
|
|
527
|
+
privacyEntries.add(id);
|
|
528
|
+
// Add to content index
|
|
529
|
+
if (this.contentIndex) {
|
|
530
|
+
this.contentIndex.addEntry(id, entry.content);
|
|
531
|
+
}
|
|
532
|
+
// Add to temporal index
|
|
533
|
+
this.temporalIndex.addEntry(id, entry.metadata?.timestamp || entry.timestamp);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Clear all indexes
|
|
537
|
+
*/
|
|
538
|
+
clear() {
|
|
539
|
+
this.tagIndex.clear();
|
|
540
|
+
this.privacyIndex.clear();
|
|
541
|
+
this.contentIndex?.clear();
|
|
542
|
+
this.temporalIndex.clear();
|
|
543
|
+
this.entriesCache.clear();
|
|
544
|
+
this.isBuilt = false;
|
|
545
|
+
this.stats = {
|
|
546
|
+
isIndexed: false,
|
|
547
|
+
entryCount: 0,
|
|
548
|
+
tagCount: 0,
|
|
549
|
+
termCount: 0
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Get index statistics
|
|
554
|
+
*/
|
|
555
|
+
getStats() {
|
|
556
|
+
return { ...this.stats };
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Check if index is built
|
|
560
|
+
*/
|
|
561
|
+
get isIndexed() {
|
|
562
|
+
return this.isBuilt;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Calculate and update memory usage
|
|
566
|
+
* FIX: Added memory monitoring for content index
|
|
567
|
+
*/
|
|
568
|
+
updateMemoryUsage() {
|
|
569
|
+
let totalBytes = 0;
|
|
570
|
+
// Estimate tag index memory
|
|
571
|
+
for (const [tag, entries] of this.tagIndex) {
|
|
572
|
+
totalBytes += tag.length * 2; // UTF-16 encoding
|
|
573
|
+
totalBytes += entries.size * 32; // Estimated ID size
|
|
574
|
+
}
|
|
575
|
+
// Estimate content index memory
|
|
576
|
+
if (this.contentIndex) {
|
|
577
|
+
// Rough estimate: each term + entry IDs
|
|
578
|
+
totalBytes += this.contentIndex.size * 100;
|
|
579
|
+
}
|
|
580
|
+
// Estimate temporal index memory
|
|
581
|
+
totalBytes += this.temporalIndex.size * 48; // Date + ID
|
|
582
|
+
// Estimate cache memory
|
|
583
|
+
for (const entry of this.entriesCache.values()) {
|
|
584
|
+
totalBytes += JSON.stringify(entry).length * 2;
|
|
585
|
+
}
|
|
586
|
+
this.memoryUsageBytes = totalBytes;
|
|
587
|
+
// Check if we're exceeding memory limit
|
|
588
|
+
if (totalBytes > this.maxMemoryBytes) {
|
|
589
|
+
logger.warn('Memory search index exceeding limit', {
|
|
590
|
+
usedMB: Math.round(totalBytes / 1024 / 1024),
|
|
591
|
+
limitMB: Math.round(this.maxMemoryBytes / 1024 / 1024)
|
|
592
|
+
});
|
|
593
|
+
// Trigger LRU eviction if enabled
|
|
594
|
+
if (this.config.enableLRUEviction) {
|
|
595
|
+
this.evictLRUEntries();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Evict least recently used entries to free memory
|
|
601
|
+
* FIX: Added LRU eviction for memory management
|
|
602
|
+
*/
|
|
603
|
+
evictLRUEntries() {
|
|
604
|
+
// Get entries sorted by access time (would need to track this)
|
|
605
|
+
// For now, evict oldest 20% of entries
|
|
606
|
+
const entriesToEvict = Math.floor(this.entriesCache.size * 0.2);
|
|
607
|
+
const entriesArray = Array.from(this.entriesCache.keys());
|
|
608
|
+
for (let i = 0; i < entriesToEvict; i++) {
|
|
609
|
+
const idToEvict = entriesArray[i];
|
|
610
|
+
this.removeEntry(idToEvict);
|
|
611
|
+
}
|
|
612
|
+
logger.info('Evicted LRU entries', {
|
|
613
|
+
evictedCount: entriesToEvict,
|
|
614
|
+
remainingCount: this.entriesCache.size
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Serialize index to JSON for persistence
|
|
619
|
+
* FIX: Added index serialization for cold start optimization
|
|
620
|
+
*/
|
|
621
|
+
serialize() {
|
|
622
|
+
if (!this.isBuilt) {
|
|
623
|
+
throw new Error('Cannot serialize unbuilt index');
|
|
624
|
+
}
|
|
625
|
+
const indexData = {
|
|
626
|
+
version: '1.0.0',
|
|
627
|
+
stats: this.stats,
|
|
628
|
+
tagIndex: Array.from(this.tagIndex.entries()).map(([tag, ids]) => ({
|
|
629
|
+
tag,
|
|
630
|
+
ids: Array.from(ids)
|
|
631
|
+
})),
|
|
632
|
+
privacyIndex: Array.from(this.privacyIndex.entries()).map(([level, ids]) => ({
|
|
633
|
+
level,
|
|
634
|
+
ids: Array.from(ids)
|
|
635
|
+
})),
|
|
636
|
+
temporalIndex: this.temporalIndex.serialize(),
|
|
637
|
+
// Note: Content index is not serialized due to size
|
|
638
|
+
// It will be rebuilt on load if needed
|
|
639
|
+
};
|
|
640
|
+
return JSON.stringify(indexData);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Deserialize index from JSON
|
|
644
|
+
* FIX: Added index deserialization for faster startup
|
|
645
|
+
*/
|
|
646
|
+
deserialize(data, entries) {
|
|
647
|
+
try {
|
|
648
|
+
const indexData = JSON.parse(data);
|
|
649
|
+
// Validate version
|
|
650
|
+
if (indexData.version !== '1.0.0') {
|
|
651
|
+
throw new Error(`Unsupported index version: ${indexData.version}`);
|
|
652
|
+
}
|
|
653
|
+
// Clear existing indexes
|
|
654
|
+
this.clear();
|
|
655
|
+
// Restore tag index
|
|
656
|
+
for (const { tag, ids } of indexData.tagIndex) {
|
|
657
|
+
this.tagIndex.set(tag, new Set(ids));
|
|
658
|
+
}
|
|
659
|
+
// Restore privacy index
|
|
660
|
+
for (const { level, ids } of indexData.privacyIndex) {
|
|
661
|
+
this.privacyIndex.set(level, new Set(ids));
|
|
662
|
+
}
|
|
663
|
+
// Restore temporal index
|
|
664
|
+
this.temporalIndex.deserialize(indexData.temporalIndex);
|
|
665
|
+
// Restore entries cache from provided entries
|
|
666
|
+
for (const [id, entry] of entries) {
|
|
667
|
+
if (indexData.tagIndex.some((item) => item.ids.includes(id))) {
|
|
668
|
+
this.entriesCache.set(id, entry);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// Rebuild content index if enabled (can't serialize efficiently)
|
|
672
|
+
if (this.config.enableContentIndex) {
|
|
673
|
+
this.contentIndex = new ContentIndex(this.config);
|
|
674
|
+
for (const [id, entry] of this.entriesCache) {
|
|
675
|
+
this.contentIndex.addEntry(id, entry.content);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// Update stats
|
|
679
|
+
this.stats = indexData.stats;
|
|
680
|
+
this.isBuilt = true;
|
|
681
|
+
logger.info('Memory search index deserialized', this.stats);
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
logger.error('Failed to deserialize index, rebuilding', error);
|
|
685
|
+
// Fall back to building from scratch
|
|
686
|
+
this.buildIndex(entries);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVtb3J5U2VhcmNoSW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvbWVtb3JpZXMvTWVtb3J5U2VhcmNoSW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUdILE9BQU8sRUFBZ0IsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4RixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDakYsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBMkVwRTs7R0FFRztBQUNILE1BQU0sWUFBWTtJQUNSLGFBQWEsR0FBRyxJQUFJLEdBQUcsRUFBdUIsQ0FBQztJQUMvQyxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFDckMsTUFBTSxDQUFvQjtJQUUzQyxZQUFZLE1BQXlCO1FBQ25DLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNILFFBQVEsQ0FBQyxPQUFlLEVBQUUsT0FBZTtRQUN2QyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFFdEQsNkJBQTZCO1FBQzdCLElBQUksQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUV0Qyx3QkFBd0I7UUFDeEIsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN6QixJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2IsT0FBTyxHQUFHLElB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aUU7WUFDakUsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLGtCQUFrQixFQUFFLENBQUM7Z0JBQ25DLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxZQUFZLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNsRCxLQUFLLE1BQU0sQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO29CQUM1QyxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUNoRCxDQUFDO1lBQ0gsQ0FBQztZQUVELGVBQWU7WUFDZixJQUFJLENBQUMsS0FBSyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUM7WUFDN0IsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7WUFFcEIsTUFBTSxDQUFDLElBQUksQ0FBQyxrQ0FBa0MsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDOUQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLENBQUMsS0FBSyxDQUFDLHlDQUF5QyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQy9ELHFDQUFxQztZQUNyQyxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzNCLENBQUM7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE1lbW9yeSBTZWFyY2ggSW5kZXggLSBIaWdoLXBlcmZvcm1hbmNlIGluZGV4ZWQgc2VhcmNoIGZvciBNZW1vcnkgZWxlbWVudHNcbiAqXG4gKiBBZGRyZXNzZXMgaXNzdWUgIzk4NDogSW1wbGVtZW50IHNlYXJjaCBpbmRleGluZyBmb3IgTWVtb3J5IGVsZW1lbnQgc2NhbGFiaWxpdHlcbiAqXG4gKiBGZWF0dXJlczpcbiAqIC0gVGFnIGluZGV4IGZvciBPKDEpIHRhZy1iYXNlZCBxdWVyaWVzXG4gKiAtIENvbnRlbnQgaW5kZXggdXNpbmcgaW52ZXJ0ZWQgaW5kZXggcGF0dGVyblxuICogLSBUZW1wb3JhbCBpbmRleCBmb3IgZGF0ZSByYW5nZSBxdWVyaWVzXG4gKiAtIFByaXZhY3kgbGV2ZWwgaW5kZXggZm9yIGZpbHRlcmVkIGFjY2Vzc1xuICogLSBJbmNyZW1lbnRhbCBpbmRleCB1cGRhdGVzXG4gKiAtIE9wdGlvbmFsIHBlcnNpc3RlbmNlXG4gKiAtIENvbmZpZ3VyYWJsZSB0aHJlc2hvbGRzXG4gKi9cblxuaW1wb3J0IHsgTWVtb3J5RW50cnkgfSBmcm9tICcuL01lbW9yeS5qcyc7XG5pbXBvcnQgeyBQcml2YWN5TGV2ZWwsIE1FTU9SWV9DT05TVEFOVFMsIE1FTU9SWV9TRUNVUklUWV9FVkVOVFMgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi8uLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFNlYXJjaEluZGV4Q29uZmlnIHtcbiAgLyoqXG4gICAqIEVuYWJsZSBpbmRleGluZyB3aGVuIG1lbW9yeSBoYXMgbW9yZSB0aGFuIHRoaXMgbWFueSBlbnRyaWVzXG4gICAqIERlZmF1bHQ6IDEwMFxuICAgKi9cbiAgaW5kZXhUaHJlc2hvbGQ/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIEVuYWJsZSBjb250ZW50IGluZGV4IChtb3JlIG1lbW9yeSBpbnRlbnNpdmUpXG4gICAqIERlZmF1bHQ6IHRydWVcbiAgICovXG4gIGVuYWJsZUNvbnRlbnRJbmRleD86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIE1heGltdW0gbnVtYmVyIG9mIHRlcm1zIHRvIGluZGV4IHBlciBlbnRyeVxuICAgKiBEZWZhdWx0OiAxMDBcbiAgICovXG4gIG1heFRlcm1zUGVyRW50cnk/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE1pbmltdW0gdGVybSBsZW5ndGggdG8gaW5kZXhcbiAgICogRGVmYXVsdDogMlxuICAgKi9cbiAgbWluVGVybUxlbmd0aD86IG51bWJlcjtcblxuICAvKipcbiAgICogRW5hYmxlIGluZGV4IHBlcnNpc3RlbmNlIHRvIGRpc2tcbiAgICogRGVmYXVsdDogZmFsc2VcbiAgICovXG4gIGVuYWJsZVBlcnNpc3RlbmNlPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogTWF4aW11bSBtZW1vcnkgdXNhZ2UgaW4gTUJcbiAgICogRGVmYXVsdDogMTAwXG4gICAqL1xuICBtYXhNZW1vcnlNQj86IG51bWJlcjtcblxuICAvKipcbiAgICogRW5hYmxlIExSVSBldmljdGlvbiB3aGVuIG1lbW9yeSBsaW1pdCBpcyByZWFjaGVkXG4gICAqIERlZmF1bHQ6IHRydWVcbiAgICovXG4gIGVuYWJsZUxSVUV2aWN0aW9uPzogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZWFyY2hJbmRleFN0YXRzIHtcbiAgaXNJbmRleGVkOiBib29sZWFuO1xuICBlbnRyeUNvdW50OiBudW1iZXI7XG4gIHRhZ0NvdW50OiBudW1iZXI7XG4gIHRlcm1Db3VudDogbnVtYmVyO1xuICBsYXN0QnVpbHQ/OiBEYXRlO1xuICBidWlsZFRpbWVNcz86IG51bWJlcjtcbiAgbWVtb3J5VXNhZ2VCeXRlcz86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZWFyY2hRdWVyeSB7XG4gIHRhZ3M/OiBzdHJpbmdbXTtcbiAgY29udGVudD86IHN0cmluZztcbiAgZGF0ZUZyb20/OiBEYXRlO1xuICBkYXRlVG8/OiBEYXRlO1xuICBwcml2YWN5TGV2ZWw/OiBQcml2YWN5TGV2ZWw7XG4gIGxpbWl0PzogbnVtYmVyO1xuICBvZmZzZXQ/OiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VhcmNoUmVzdWx0IHtcbiAgZW50cnk6IE1lbW9yeUVudHJ5O1xuICBzY29yZTogbnVtYmVyO1xuICBtYXRjaGVzOiB7XG4gICAgdGFncz86IHN0cmluZ1tdO1xuICAgIHRlcm1zPzogc3RyaW5nW107XG4gIH07XG59XG5cbi8qKlxuICogSW52ZXJ0ZWQgaW5kZXggZm9yIGNvbnRlbnQgc2VhcmNoXG4gKi9cbmNsYXNzIENvbnRlbnRJbmRleCB7XG4gIHByaXZhdGUgdGVybVRvRW50cmllcyA9IG5ldyBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4oKTtcbiAgcHJpdmF0ZSBlbnRyeVRvVGVybXMgPSBuZXcgTWFwPHN0cmluZywgU2V0PHN0cmluZz4+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBTZWFyY2hJbmRleENvbmZpZztcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IFNlYXJjaEluZGV4Q29uZmlnKSB7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gIH1cblxuICAvKipcbiAgICogQWRkIGFuIGVudHJ5IHRvIHRoZSBjb250ZW50IGluZGV4XG4gICAqL1xuICBhZGRFbnRyeShlbnRyeUlkOiBzdHJpbmcsIGNvbnRlbnQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHRlcm1zID0gdGhpcy5leHRyYWN0VGVybXMoY29udGVudCwgeyBlbnRyeUlkIH0pO1xuXG4gICAgLy8gU3RvcmUgdGVybXMgZm9yIHRoaXMgZW50cnlcbiAgICB0aGlzLmVudHJ5VG9UZXJtcy5zZXQoZW50cnlJZCwgdGVybXMpO1xuXG4gICAgLy8gVXBkYXRlIGludmVydGVkIGluZGV4XG4gICAgZm9yIChjb25zdCB0ZXJtIG9mIHRlcm1zKSB7XG4gICAgICBsZXQgZW50cmllcyA9IHRoaXMudGVybVRvRW50cmllcy5nZXQodGVybSk7XG4gICAgICBpZiAoIWVudHJpZXMpIHtcbiAgICAgICAgZW50cmllcyA9IG5ldyBTZXQoKTtcbiAgICAgICAgdGhpcy50ZXJtVG9FbnRyaWVzLnNldCh0ZXJtLCBlbnRyaWVzKTtcbiAgICAgIH1cbiAgICAgIGVudHJpZXMuYWRkKGVudHJ5SWQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmUgYW4gZW50cnkgZnJvbSB0aGUgaW5kZXhcbiAgICovXG4gIHJlbW92ZUVudHJ5KGVudHJ5SWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHRlcm1zID0gdGhpcy5lbnRyeVRvVGVybXMuZ2V0KGVudHJ5SWQpO1xuICAgIGlmICghdGVybXMpIHJldHVybjtcblxuICAgIC8vIFJlbW92ZSBmcm9tIGludmVydGVkIGluZGV4XG4gICAgZm9yIChjb25zdCB0ZXJtIG9mIHRlcm1zKSB7XG4gICAgICBjb25zdCBlbnRyaWVzID0gdGhpcy50ZXJtVG9FbnRyaWVzLmdldCh0ZXJtKTtcbiAgICAgIGlmIChlbnRyaWVzKSB7XG4gICAgICAgIGVudHJpZXMuZGVsZXRlKGVudHJ5SWQpO1xuICAgICAgICBpZiAoZW50cmllcy5zaXplID09PSAwKSB7XG4gICAgICAgICAgdGhpcy50ZXJtVG9FbnRyaWVzLmRlbGV0ZSh0ZXJtKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFJlbW92ZSBlbnRyeSB0ZXJtc1xuICAgIHRoaXMuZW50cnlUb1Rlcm1zLmRlbGV0ZShlbnRyeUlkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWFyY2ggZm9yIGVudHJpZXMgY29udGFpbmluZyBxdWVyeSB0ZXJtc1xuICAgKi9cbiAgc2VhcmNoKHF1ZXJ5OiBzdHJpbmcpOiBNYXA8c3RyaW5nLCBudW1iZXI+IHtcbiAgICBjb25zdCBxdWVyeVRlcm1zID0gdGhpcy5leHRyYWN0VGVybXMocXVlcnkpO1xuICAgIGNvbnN0IHNjb3JlcyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG5cbiAgICBmb3IgKGNvbnN0IHRlcm0gb2YgcXVlcnlUZXJtcykge1xuICAgICAgY29uc3QgZW50cmllcyA9IHRoaXMudGVybVRvRW50cmllcy5nZXQodGVybSk7XG4gICAgICBpZiAoZW50cmllcykge1xuICAgICAgICBmb3IgKGNvbnN0IGVudHJ5SWQgb2YgZW50cmllcykge1xuICAgICAgICAgIHNjb3Jlcy5zZXQoZW50cnlJZCwgKHNjb3Jlcy5nZXQoZW50cnlJZCkgfHwgMCkgKyAxKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzY29yZXM7XG4gIH1cblxuICAvKipcbiAgICogRXh0cmFjdCBzZWFyY2hhYmxlIHRlcm1zIGZyb20gY29udGVudFxuICAgKiBTRUNVUklUWSBGSVg6IEFkZGVkIGF1ZGl0IGxvZ2dpbmcgZm9yIFVuaWNvZGUgdmFsaWRhdGlvbiBvcGVyYXRpb25zXG4gICAqL1xuICBwcml2YXRlIGV4dHJhY3RUZXJtcyhjb250ZW50OiBzdHJpbmcsIGNvbnRleHQ/OiB7IGVudHJ5SWQ/OiBzdHJpbmcgfSk6IFNldDxzdHJpbmc+IHtcbiAgICAvLyBOb3JtYWxpemUgZm9yIHNlY3VyaXR5XG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKGNvbnRlbnQpO1xuICAgIGlmICghbm9ybWFsaXplZC5pc1ZhbGlkKSB7XG4gICAgICBsb2dnZXIud2FybignSW52YWxpZCBVbmljb2RlIGluIGNvbnRlbnQgZm9yIGluZGV4aW5nJywge1xuICAgICAgICBlbnRyeUlkOiBjb250ZXh0Py5lbnRyeUlkXG4gICAgICB9KTtcblxuICAgICAgLy8gU0VDVVJJVFkgRklYOiBMb2cgc2VjdXJpdHkgZXZlbnQgZm9yIGF1ZGl0IHRyYWlsIChETUNQLVNFQy0wMDYpXG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX1VOSUNPREVfVkFMSURBVElPTl9GQUlMRUQsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnTWVtb3J5U2VhcmNoSW5kZXguZXh0cmFjdFRlcm1zJyxcbiAgICAgICAgZGV0YWlsczogYEludmFsaWQgVW5pY29kZSBkZXRlY3RlZCBkdXJpbmcgdGVybSBleHRyYWN0aW9uJHtjb250ZXh0Py5lbnRyeUlkID8gYCBmb3IgZW50cnkgJHtjb250ZXh0LmVudHJ5SWR9YCA6ICcnfWAsXG4gICAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgICAgZW50cnlJZDogY29udGV4dD8uZW50cnlJZCxcbiAgICAgICAgICBpc3N1ZUNvdW50OiAwICAvLyBVbmljb2RlVmFsaWRhdGlvblJlc3VsdCBkb2Vzbid0IGV4cG9zZSBpc3N1ZXNcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiBuZXcgU2V0KCk7XG4gICAgfVxuXG4gICAgY29uc3QgdGV4dCA9IG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQudG9Mb3dlckNhc2UoKTtcbiAgICBjb25zdCB0ZXJtcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gICAgLy8gU2ltcGxlIHRva2VuaXphdGlvbiAoY2FuIGJlIGltcHJvdmVkIHdpdGggYmV0dGVyIE5MUClcbiAgICBjb25zdCB3b3JkcyA9IHRleHQubWF0Y2goL1xcYlxcdytcXGIvZykgfHwgW107XG5cbiAgICBsZXQgdGVybUNvdW50ID0gMDtcbiAgICBmb3IgKGNvbnN0IHdvcmQgb2Ygd29yZHMpIHtcbiAgICAgIGlmICh3b3JkLmxlbmd0aCA+PSAodGhpcy5jb25maWcubWluVGVybUxlbmd0aCB8fCAyKSkge1xuICAgICAgICB0ZXJtcy5hZGQod29yZCk7XG4gICAgICAgIHRlcm1Db3VudCsrO1xuICAgICAgICBpZiAodGVybUNvdW50ID49ICh0aGlzLmNvbmZpZy5tYXhUZXJtc1BlckVudHJ5IHx8IDEwMCkpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0ZXJtcztcbiAgfVxuXG4gIGNsZWFyKCk6IHZvaWQge1xuICAgIHRoaXMudGVybVRvRW50cmllcy5jbGVhcigpO1xuICAgIHRoaXMuZW50cnlUb1Rlcm1zLmNsZWFyKCk7XG4gIH1cblxuICBnZXQgc2l6ZSgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnRlcm1Ub0VudHJpZXMuc2l6ZTtcbiAgfVxufVxuXG4vKipcbiAqIERhdGUgcmFuZ2UgaW5kZXggdXNpbmcgc29ydGVkIGFycmF5IGZvciBlZmZpY2llbnQgcmFuZ2UgcXVlcmllc1xuICovXG5jbGFzcyBUZW1wb3JhbEluZGV4IHtcbiAgcHJpdmF0ZSBlbnRyaWVzOiBBcnJheTx7IGlkOiBzdHJpbmc7IHRpbWVzdGFtcDogbnVtYmVyIH0+ID0gW107XG5cbiAgYWRkRW50cnkoZW50cnlJZDogc3RyaW5nLCBkYXRlOiBEYXRlKTogdm9pZCB7XG4gICAgY29uc3QgdGltZXN0YW1wID0gZGF0ZS5nZXRUaW1lKCk7XG5cbiAgICAvLyBCaW5hcnkgc2VhcmNoIGZvciBpbnNlcnRpb24gcG9pbnRcbiAgICBsZXQgbGVmdCA9IDA7XG4gICAgbGV0IHJpZ2h0ID0gdGhpcy5lbnRyaWVzLmxlbmd0aDtcblxuICAgIHdoaWxlIChsZWZ0IDwgcmlnaHQpIHtcbiAgICAgIGNvbnN0IG1pZCA9IE1hdGguZmxvb3IoKGxlZnQgKyByaWdodCkgLyAyKTtcbiAgICAgIGlmICh0aGlzLmVudHJpZXNbbWlkXS50aW1lc3RhbXAgPCB0aW1lc3RhbXApIHtcbiAgICAgICAgbGVmdCA9IG1pZCArIDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByaWdodCA9IG1pZDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJbnNlcnQgYXQgY29ycmVjdCBwb3NpdGlvbiB0byBtYWludGFpbiBzb3J0XG4gICAgdGhpcy5lbnRyaWVzLnNwbGljZShsZWZ0LCAwLCB7IGlkOiBlbnRyeUlkLCB0aW1lc3RhbXAgfSk7XG4gIH1cblxuICByZW1vdmVFbnRyeShlbnRyeUlkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBpbmRleCA9IHRoaXMuZW50cmllcy5maW5kSW5kZXgoZSA9PiBlLmlkID09PSBlbnRyeUlkKTtcbiAgICBpZiAoaW5kZXggPj0gMCkge1xuICAgICAgdGhpcy5lbnRyaWVzLnNwbGljZShpbmRleCwgMSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZpbmQgZW50cmllcyB3aXRoaW4gZGF0ZSByYW5nZVxuICAgKi9cbiAgc2VhcmNoUmFuZ2UoZnJvbT86IERhdGUsIHRvPzogRGF0ZSk6IFNldDxzdHJpbmc+IHtcbiAgICBjb25zdCByZXN1bHRzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgY29uc3QgZnJvbVRpbWUgPSBmcm9tPy5nZXRUaW1lKCkgfHwgMDtcbiAgICBjb25zdCB0b1RpbWUgPSB0bz8uZ2V0VGltZSgpIHx8IERhdGUubm93KCk7XG5cbiAgICAvLyBCaW5hcnkgc2VhcmNoIGZvciBzdGFydFxuICAgIGxldCBzdGFydCA9IDA7XG4gICAgbGV0IGVuZCA9IHRoaXMuZW50cmllcy5sZW5ndGg7XG5cbiAgICB3aGlsZSAoc3RhcnQgPCBlbmQpIHtcbiAgICAgIGNvbnN0IG1pZCA9IE1hdGguZmxvb3IoKHN0YXJ0ICsgZW5kKSAvIDIpO1xuICAgICAgaWYgKHRoaXMuZW50cmllc1ttaWRdLnRpbWVzdGFtcCA8IGZyb21UaW1lKSB7XG4gICAgICAgIHN0YXJ0ID0gbWlkICsgMTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGVuZCA9IG1pZDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDb2xsZWN0IGFsbCBlbnRyaWVzIGluIHJhbmdlXG4gICAgZm9yIChsZXQgaSA9IHN0YXJ0OyBpIDwgdGhpcy5lbnRyaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBlbnRyeSA9IHRoaXMuZW50cmllc1tpXTtcbiAgICAgIGlmIChlbnRyeS50aW1lc3RhbXAgPiB0b1RpbWUpIGJyZWFrO1xuICAgICAgcmVzdWx0cy5hZGQoZW50cnkuaWQpO1xuICAgIH1cblxuICAgIHJldHVybiByZXN1bHRzO1xuICB9XG5cbiAgY2xlYXIoKTogdm9pZCB7XG4gICAgdGhpcy5lbnRyaWVzID0gW107XG4gIH1cblxuICBnZXQgc2l6ZSgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmVudHJpZXMubGVuZ3RoO1xuICB9XG5cbiAgc2VyaWFsaXplKCk6IGFueSB7XG4gICAgcmV0dXJuIHRoaXMuZW50cmllcztcbiAgfVxuXG4gIGRlc2VyaWFsaXplKGRhdGE6IGFueSk6IHZvaWQge1xuICAgIHRoaXMuZW50cmllcyA9IGRhdGEgfHwgW107XG4gIH1cbn1cblxuLyoqXG4gKiBNYWluIHNlYXJjaCBpbmRleCBmb3IgTWVtb3J5IGVsZW1lbnRzXG4gKi9cbmV4cG9ydCBjbGFzcyBNZW1vcnlTZWFyY2hJbmRleCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBTZWFyY2hJbmRleENvbmZpZztcblxuICAvLyBJbmRleGVzXG4gIHByaXZhdGUgdGFnSW5kZXggPSBuZXcgTWFwPHN0cmluZywgU2V0PHN0cmluZz4+KCk7XG4gIHByaXZhdGUgcHJpdmFjeUluZGV4ID0gbmV3IE1hcDxQcml2YWN5TGV2ZWwsIFNldDxzdHJpbmc+PigpO1xuICBwcml2YXRlIGNvbnRlbnRJbmRleDogQ29udGVudEluZGV4IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgdGVtcG9yYWxJbmRleCA9IG5ldyBUZW1wb3JhbEluZGV4KCk7XG5cbiAgLy8gQ2FjaGVkIGVudHJpZXMgZm9yIHNjb3JpbmdcbiAgcHJpdmF0ZSBlbnRyaWVzQ2FjaGUgPSBuZXcgTWFwPHN0cmluZywgTWVtb3J5RW50cnk+KCk7XG5cbiAgLy8gSW5kZXggc3RhdGVcbiAgcHJpdmF0ZSBpc0J1aWx0ID0gZmFsc2U7XG4gIHByaXZhdGUgaXNCdWlsZGluZyA9IGZhbHNlO1xuICBwcml2YXRlIGJ1aWxkUXVldWU6IFByb21pc2U8dm9pZD4gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBzdGF0czogU2VhcmNoSW5kZXhTdGF0cyA9IHtcbiAgICBpc0luZGV4ZWQ6IGZhbHNlLFxuICAgIGVudHJ5Q291bnQ6IDAsXG4gICAgdGFnQ291bnQ6IDAsXG4gICAgdGVybUNvdW50OiAwLFxuICAgIG1lbW9yeVVzYWdlQnl0ZXM6IDBcbiAgfTtcblxuICAvLyBNZW1vcnkgbWFuYWdlbWVudFxuICBwcml2YXRlIHJlYWRvbmx5IG1heE1lbW9yeUJ5dGVzOiBudW1iZXI7XG4gIHByaXZhdGUgbWVtb3J5VXNhZ2VCeXRlcyA9IDA7XG5cbiAgY29uc3RydWN0b3IoY29uZmlnOiBTZWFyY2hJbmRleENvbmZpZyA9IHt9KSB7XG4gICAgdGhpcy5jb25maWcgPSB7XG4gICAgICBpbmRleFRocmVzaG9sZDogY29uZmlnLmluZGV4VGhyZXNob2xkIHx8IDEwMCxcbiAgICAgIGVuYWJsZUNvbnRlbnRJbmRleDogY29uZmlnLmVuYWJsZUNvbnRlbnRJbmRleCAhPT0gZmFsc2UsXG4gICAgICBtYXhUZXJtc1BlckVudHJ5OiBjb25maWcubWF4VGVybXNQZXJFbnRyeSB8fCAxMDAsXG4gICAgICBtaW5UZXJtTGVuZ3RoOiBjb25maWcubWluVGVybUxlbmd0aCB8fCAyLFxuICAgICAgZW5hYmxlUGVyc2lzdGVuY2U6IGNvbmZpZy5lbmFibGVQZXJzaXN0ZW5jZSB8fCBmYWxzZSxcbiAgICAgIG1heE1lbW9yeU1COiBjb25maWcubWF4TWVtb3J5TUIgfHwgMTAwLFxuICAgICAgZW5hYmxlTFJVRXZpY3Rpb246IGNvbmZpZy5lbmFibGVMUlVFdmljdGlvbiAhPT0gZmFsc2VcbiAgICB9O1xuXG4gICAgLy8gQ29uZmlndXJlIG1lbW9yeSBsaW1pdCAoZGVmYXVsdCAxMDBNQiwgY29uZmlndXJhYmxlKVxuICAgIHRoaXMubWF4TWVtb3J5Qnl0ZXMgPSAodGhpcy5jb25maWcubWF4TWVtb3J5TUIgfHwgMTAwKSAqIDEwMjQgKiAxMDI0O1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLmVuYWJsZUNvbnRlbnRJbmRleCkge1xuICAgICAgdGhpcy5jb250ZW50SW5kZXggPSBuZXcgQ29udGVudEluZGV4KHRoaXMuY29uZmlnKTtcbiAgICB9XG5cbiAgICBsb2dnZXIuZGVidWcoJ01lbW9yeVNlYXJjaEluZGV4IGNyZWF0ZWQnLCB0aGlzLmNvbmZpZyk7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGQgb3IgcmVidWlsZCB0aGUgaW5kZXggZnJvbSBlbnRyaWVzXG4gICAqIEZJWDogQWRkZWQgcmFjZSBjb25kaXRpb24gcHJvdGVjdGlvbiB3aXRoIGlzQnVpbGRpbmcgZmxhZyBhbmQgYnVpbGQgcXVldWVcbiAgICovXG4gIGFzeW5jIGJ1aWxkSW5kZXgoZW50cmllczogTWFwPHN0cmluZywgTWVtb3J5RW50cnk+KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gSWYgYWxyZWFkeSBidWlsZGluZywgd2FpdCBmb3IgdGhlIGN1cnJlbnQgYnVpbGQgdG8gY29tcGxldGVcbiAgICBpZiAodGhpcy5pc0J1aWxkaW5nICYmIHRoaXMuYnVpbGRRdWV1ZSkge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdJbmRleCBidWlsZCBhbHJlYWR5IGluIHByb2dyZXNzLCB3YWl0aW5nLi4uJyk7XG4gICAgICByZXR1cm4gdGhpcy5idWlsZFF1ZXVlO1xuICAgIH1cblxuICAgIC8vIENyZWF0ZSBidWlsZCBwcm9taXNlIHRvIGhhbmRsZSBjb25jdXJyZW50IGNhbGxzXG4gICAgdGhpcy5idWlsZFF1ZXVlID0gdGhpcy5fZG9CdWlsZEluZGV4KGVudHJpZXMpO1xuICAgIHJldHVybiB0aGlzLmJ1aWxkUXVldWU7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIF9kb0J1aWxkSW5kZXgoZW50cmllczogTWFwPHN0cmluZywgTWVtb3J5RW50cnk+KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gUHJldmVudCBjb25jdXJyZW50IGJ1aWxkc1xuICAgIGlmICh0aGlzLmlzQnVpbGRpbmcpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdBdHRlbXB0ZWQgY29uY3VycmVudCBpbmRleCBidWlsZCwgc2tpcHBpbmcnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmlzQnVpbGRpbmcgPSB0cnVlO1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG5cbiAgICB0cnkge1xuICAgICAgLy8gQ2xlYXIgZXhpc3RpbmcgaW5kZXhlc1xuICAgICAgdGhpcy5jbGVhcigpO1xuXG4gICAgICAvLyBDaGVjayBpZiB3ZSBzaG91bGQgaW5kZXggYmFzZWQgb24gdGhyZXNob2xkXG4gICAgICBpZiAoZW50cmllcy5zaXplIDwgKHRoaXMuY29uZmlnLmluZGV4VGhyZXNob2xkIHx8IDEwMCkpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdOb3QgYnVpbGRpbmcgaW5kZXggLSBiZWxvdyB0aHJlc2hvbGQnLCB7XG4gICAgICAgICAgZW50cnlDb3VudDogZW50cmllcy5zaXplLFxuICAgICAgICAgIHRocmVzaG9sZDogdGhpcy5jb25maWcuaW5kZXhUaHJlc2hvbGRcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgLy8gQnVpbGQgaW5kZXhlc1xuICAgICAgZm9yIChjb25zdCBbaWQsIGVudHJ5XSBvZiBlbnRyaWVzKSB7XG4gICAgICAgIHRoaXMuYWRkVG9JbmRleChpZCwgZW50cnkpO1xuICAgICAgfVxuXG4gICAgICAvLyBDYWxjdWxhdGUgbWVtb3J5IHVzYWdlXG4gICAgICB0aGlzLnVwZGF0ZU1lbW9yeVVzYWdlKCk7XG5cbiAgICAgIC8vIFNFQ1VSSVRZIEZJWDogTG9nIHNlY3VyaXR5IGV2ZW50IGZvciBpbmRleCBidWlsZCBvcGVyYXRpb24gKERNQ1AtU0VDLTAwNilcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogTUVNT1JZX1NFQ1VSSVRZX0VWRU5UUy5NRU1PUllfQ1JFQVRFRCBhcyBhbnksICAvLyBVc2luZyBDUkVBVEVEIGZvciBpbmRleCBidWlsZFxuICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgIHNvdXJjZTogJ01lbW9yeVNlYXJjaEluZGV4LmJ1aWxkSW5kZXgnLFxuICAgICAgICBkZXRhaWxzOiBgTWVtb3J5IHNlYXJjaCBpbmRleCBidWlsdCBzdWNjZXNzZnVsbHkgd2l0aCAke2VudHJpZXMuc2l6ZX0gZW50cmllc2AsXG4gICAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgICAgZW50cnlDb3VudDogZW50cmllcy5zaXplLFxuICAgICAgICAgIHRhZ0NvdW50OiB0aGlzLnRhZ0luZGV4LnNpemUsXG4gICAgICAgICAgbWVtb3J5VXNhZ2VCeXRlczogdGhpcy5tZW1vcnlVc2FnZUJ5dGVzXG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICAvLyBVcGRhdGUgc3RhdHNcbiAgICAgIGNvbnN0IGJ1aWxkVGltZSA9IERhdGUubm93KCkgLSBzdGFydFRpbWU7XG4gICAgICB0aGlzLnN0YXRzID0ge1xuICAgICAgICBpc0luZGV4ZWQ6IHRydWUsXG4gICAgICAgIGVudHJ5Q291bnQ6IGVudHJpZXMuc2l6ZSxcbiAgICAgICAgdGFnQ291bnQ6IHRoaXMudGFnSW5kZXguc2l6ZSxcbiAgICAgICAgdGVybUNvdW50OiB0aGlzLmNvbnRlbnRJbmRleD8uc2l6ZSB8fCAwLFxuICAgICAgICBsYXN0QnVpbHQ6IG5ldyBEYXRlKCksXG4gICAgICAgIGJ1aWxkVGltZU1zOiBidWlsZFRpbWUsXG4gICAgICAgIG1lbW9yeVVzYWdlQnl0ZXM6IHRoaXMubWVtb3J5VXNhZ2VCeXRlc1xuICAgICAgfTtcblxuICAgICAgdGhpcy5pc0J1aWx0ID0gdHJ1ZTtcblxuICAgICAgbG9nZ2VyLmluZm8oJ01lbW9yeSBzZWFyY2ggaW5kZXggYnVpbHQnLCB0aGlzLnN0YXRzKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gYnVpbGQgbWVtb3J5IHNlYXJjaCBpbmRleCcsIGVycm9yKTtcbiAgICAgIC8vIFJlc2V0IHN0YXRlIG9uIGZhaWx1cmVcbiAgICAgIHRoaXMuY2xlYXIoKTtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLmlzQnVpbGRpbmcgPSBmYWxzZTtcbiAgICAgIHRoaXMuYnVpbGRRdWV1ZSA9IG51bGw7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIHNpbmdsZSBlbnRyeSB0byB0aGUgaW5kZXggKGluY3JlbWVudGFsIHVwZGF0ZSlcbiAgICovXG4gIGFkZEVudHJ5KGVudHJ5OiBNZW1vcnlFbnRyeSk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc0J1aWx0KSByZXR1cm47XG5cbiAgICB0aGlzLmFkZFRvSW5kZXgoZW50cnkuaWQsIGVudHJ5KTtcbiAgICB0aGlzLnN0YXRzLmVudHJ5Q291bnQrKztcblxuICAgIGxvZ2dlci5kZWJ1ZygnRW50cnkgYWRkZWQgdG8gaW5kZXgnLCB7IGlkOiBlbnRyeS5pZCB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmUgYW4gZW50cnkgZnJvbSB0aGUgaW5kZXhcbiAgICovXG4gIHJlbW92ZUVudHJ5KGVudHJ5SWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc0J1aWx0KSByZXR1cm47XG5cbiAgICBjb25zdCBlbnRyeSA9IHRoaXMuZW50cmllc0NhY2hlLmdldChlbnRyeUlkKTtcbiAgICBpZiAoIWVudHJ5KSByZXR1cm47XG5cbiAgICAvLyBSZW1vdmUgZnJvbSB0YWcgaW5kZXhcbiAgICBpZiAoZW50cnkubWV0YWRhdGE/LnRhZ3MpIHtcbiAgICAgIGZvciAoY29uc3QgdGFnIG9mIGVudHJ5Lm1ldGFkYXRhLnRhZ3MpIHtcbiAgICAgICAgY29uc3QgZW50cmllcyA9IHRoaXMudGFnSW5kZXguZ2V0KHRhZyk7XG4gICAgICAgIGlmIChlbnRyaWVzKSB7XG4gICAgICAgICAgZW50cmllcy5kZWxldGUoZW50cnlJZCk7XG4gICAgICAgICAgaWYgKGVudHJpZXMuc2l6ZSA9PT0gMCkge1xuICAgICAgICAgICAgdGhpcy50YWdJbmRleC5kZWxldGUodGFnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBSZW1vdmUgZnJvbSBwcml2YWN5IGluZGV4XG4gICAgY29uc3QgcHJpdmFjeUxldmVsID0gZW50cnkubWV0YWRhdGE/LnByaXZhY3lMZXZlbCB8fCBlbnRyeS5wcml2YWN5TGV2ZWwgfHwgTUVNT1JZX0NPTlNUQU5UUy5ERUZBVUxUX1BSSVZBQ1lfTEVWRUw7XG4gICAgY29uc3QgcHJpdmFjeUVudHJpZXMgPSB0aGlzLnByaXZhY3lJbmRleC5nZXQocHJpdmFjeUxldmVsKTtcbiAgICBpZiAocHJpdmFjeUVudHJpZXMpIHtcbiAgICAgIHByaXZhY3lFbnRyaWVzLmRlbGV0ZShlbnRyeUlkKTtcbiAgICB9XG5cbiAgICAvLyBSZW1vdmUgZnJvbSBjb250ZW50IGluZGV4XG4gICAgdGhpcy5jb250ZW50SW5kZXg/LnJlbW92ZUVudHJ5KGVudHJ5SWQpO1xuXG4gICAgLy8gUmVtb3ZlIGZyb20gdGVtcG9yYWwgaW5kZXhcbiAgICB0aGlzLnRlbXBvcmFsSW5kZXgucmVtb3ZlRW50cnkoZW50cnlJZCk7XG5cbiAgICAvLyBSZW1vdmUgZnJvbSBjYWNoZVxuICAgIHRoaXMuZW50cmllc0NhY2hlLmRlbGV0ZShlbnRyeUlkKTtcblxuICAgIHRoaXMuc3RhdHMuZW50cnlDb3VudC0tO1xuXG4gICAgbG9nZ2VyLmRlYnVnKCdFbnRyeSByZW1vdmVkIGZyb20gaW5kZXgnLCB7IGlkOiBlbnRyeUlkIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlYXJjaCB0aGUgaW5kZXggd2l0aCBtdWx0aXBsZSBjcml0ZXJpYVxuICAgKi9cbiAgc2VhcmNoKHF1ZXJ5OiBTZWFyY2hRdWVyeSwgZW50cmllczogTWFwPHN0cmluZywgTWVtb3J5RW50cnk+KTogU2VhcmNoUmVzdWx0W10ge1xuICAgIGlmICghdGhpcy5pc0J1aWx0KSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ0luZGV4IG5vdCBidWlsdCwgZmFsbGluZyBiYWNrIHRvIGxpbmVhciBzZWFyY2gnKTtcbiAgICAgIHJldHVybiB0aGlzLmxpbmVhclNlYXJjaChxdWVyeSwgZW50cmllcyk7XG4gICAgfVxuXG4gICAgLy8gU3RhcnQgd2l0aCBhbGwgZW50cmllcyBvciBmaWx0ZXJlZCBieSBwcml2YWN5XG4gICAgbGV0IGNhbmRpZGF0ZUlkczogU2V0PHN0cmluZz4gfCBudWxsID0gbnVsbDtcblxuICAgIC8vIEZpbHRlciBieSBwcml2YWN5IGxldmVsXG4gICAgaWYgKHF1ZXJ5LnByaXZhY3lMZXZlbCkge1xuICAgICAgY2FuZGlkYXRlSWRzID0gbmV3IFNldCh0aGlzLnByaXZhY3lJbmRleC5nZXQocXVlcnkucHJpdmFjeUxldmVsKSB8fCBbXSk7XG4gICAgfVxuXG4gICAgLy8gRmlsdGVyIGJ5IHRhZ3MgKGludGVyc2VjdGlvbilcbiAgICBpZiAocXVlcnkudGFncyAmJiBxdWVyeS50YWdzLmxlbmd0aCA+IDApIHtcbiAgICAgIGNvbnN0IHRhZ1Jlc3VsdHMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICAgIGZvciAoY29uc3QgdGFnIG9mIHF1ZXJ5LnRhZ3MpIHtcbiAgICAgICAgY29uc3QgZW50cmllcyA9IHRoaXMudGFnSW5kZXguZ2V0KHRhZy50b0xvd2VyQ2FzZSgpKTtcbiAgICAgICAgaWYgKGVudHJpZXMpIHtcbiAgICAgICAgICBpZiAoY2FuZGlkYXRlSWRzKSB7XG4gICAgICAgICAgICAvLyBJbnRlcnNlY3Rpb24gd2l0aCBleGlzdGluZyBjYW5kaWRhdGVzXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGlkIG9mIGVudHJpZXMpIHtcbiAgICAgICAgICAgICAgaWYgKGNhbmRpZGF0ZUlkcy5oYXMoaWQpKSB7XG4gICAgICAgICAgICAgICAgdGFnUmVzdWx0cy5hZGQoaWQpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIEZpcnN0IGZpbHRlclxuICAgICAgICAgICAgZW50cmllcy5mb3JFYWNoKGlkID0+IHRhZ1Jlc3VsdHMuYWRkKGlkKSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjYW5kaWRhdGVJZHMgPSB0YWdSZXN1bHRzO1xuICAgIH1cblxuICAgIC8vIEZpbHRlciBieSBkYXRlIHJhbmdlXG4gICAgaWYgKHF1ZXJ5LmRhdGVGcm9tIHx8IHF1ZXJ5LmRhdGVUbykge1xuICAgICAgY29uc3QgZGF0ZVJlc3VsdHMgPSB0aGlzLnRlbXBvcmFsSW5kZXguc2VhcmNoUmFuZ2UocXVlcnkuZGF0ZUZyb20sIHF1ZXJ5LmRhdGVUbyk7XG4gICAgICBpZiAoY2FuZGlkYXRlSWRzKSB7XG4gICAgICAgIC8vIEludGVyc2VjdGlvblxuICAgICAgICBjb25zdCBpbnRlcnNlY3Rpb24gPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICAgICAgZm9yIChjb25zdCBpZCBvZiBjYW5kaWRhdGVJZHMpIHtcbiAgICAgICAgICBpZiAoZGF0ZVJlc3VsdHMuaGFzKGlkKSkge1xuICAgICAgICAgICAgaW50ZXJzZWN0aW9uLmFkZChpZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNhbmRpZGF0ZUlkcyA9IGludGVyc2VjdGlvbjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNhbmRpZGF0ZUlkcyA9IGRhdGVSZXN1bHRzO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFNjb3JlIGJ5IGNvbnRlbnQgaWYgcHJvdmlkZWRcbiAgICBjb25zdCBjb250ZW50U2NvcmVzID0gcXVlcnkuY29udGVudCAmJiB0aGlzLmNvbnRlbnRJbmRleFxuICAgICAgPyB0aGlzLmNvbnRlbnRJbmRleC5zZWFyY2gocXVlcnkuY29udGVudClcbiAgICAgIDogbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcblxuICAgIC8vIEJ1aWxkIHJlc3VsdHNcbiAgICBjb25zdCByZXN1bHRzOiBTZWFyY2hSZXN1bHRbXSA9IFtdO1xuICAgIGNvbnN0IHNlYXJjaEVudHJpZXMgPSBjYW5kaWRhdGVJZHMgfHwgbmV3IFNldChlbnRyaWVzLmtleXMoKSk7XG5cbiAgICBmb3IgKGNvbnN0IGlkIG9mIHNlYXJjaEVudHJpZXMpIHtcbiAgICAgIGNvbnN0IGVudHJ5ID0gZW50cmllcy5nZXQoaWQpO1xuICAgICAgaWYgKCFlbnRyeSkgY29udGludWU7XG5cbiAgICAgIC8vIENhbGN1bGF0ZSBzY29yZVxuICAgICAgbGV0IHNjb3JlID0gMTtcblxuICAgICAgLy8gQ29udGVudCByZWxldmFuY2Ugc2NvcmVcbiAgICAgIGlmIChjb250ZW50U2NvcmVzLmhhcyhpZCkpIHtcbiAgICAgICAgc2NvcmUgKz0gY29udGVudFNjb3Jlcy5nZXQoaWQpISAqIDI7XG4gICAgICB9XG5cbiAgICAgIC8vIFRhZyBtYXRjaCBib251c1xuICAgICAgaWYgKHF1ZXJ5LnRhZ3MgJiYgZW50cnkubWV0YWRhdGE/LnRhZ3MpIHtcbiAgICAgICAgY29uc3QgbWF0Y2hlZFRhZ3MgPSBxdWVyeS50YWdzLmZpbHRlcih0YWcgPT5cbiAgICAgICAgICBlbnRyeS5tZXRhZGF0YT8udGFncz8uaW5jbHVkZXModGFnKVxuICAgICAgICApO1xuICAgICAgICBzY29yZSArPSBtYXRjaGVkVGFncy5sZW5ndGg7XG4gICAgICB9XG5cbiAgICAgIHJlc3VsdHMucHVzaCh7XG4gICAgICAgIGVudHJ5LFxuICAgICAgICBzY29yZSxcbiAgICAgICAgbWF0Y2hlczoge1xuICAgICAgICAgIHRhZ3M6IHF1ZXJ5LnRhZ3M/LmZpbHRlcih0YWcgPT4gZW50cnkubWV0YWRhdGE/LnRhZ3M/LmluY2x1ZGVzKHRhZykpLFxuICAgICAgICAgIHRlcm1zOiBxdWVyeS5jb250ZW50ID8gWydpbmRleGVkJ10gOiB1bmRlZmluZWRcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gU29ydCBieSBzY29yZSBhbmQgYXBwbHkgcGFnaW5hdGlvblxuICAgIHJlc3VsdHMuc29ydCgoYSwgYikgPT4gYi5zY29yZSAtIGEuc2NvcmUpO1xuXG4gICAgY29uc3Qgb2Zmc2V0ID0gcXVlcnkub2Zmc2V0IHx8IDA7XG4gICAgY29uc3QgbGltaXQgPSBxdWVyeS5saW1pdCB8fCAxMDA7XG5cbiAgICByZXR1cm4gcmVzdWx0cy5zbGljZShvZmZzZXQsIG9mZnNldCArIGxpbWl0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGYWxsYmFjayBsaW5lYXIgc2VhcmNoIHdoZW4gaW5kZXggaXMgbm90IGF2YWlsYWJsZVxuICAgKi9cbiAgcHJpdmF0ZSBsaW5lYXJTZWFyY2gocXVlcnk6IFNlYXJjaFF1ZXJ5LCBlbnRyaWVzOiBNYXA8c3RyaW5nLCBNZW1vcnlFbnRyeT4pOiBTZWFyY2hSZXN1bHRbXSB7XG4gICAgY29uc3QgcmVzdWx0czogU2VhcmNoUmVzdWx0W10gPSBbXTtcblxuICAgIGZvciAoY29uc3QgW2lkLCBlbnRyeV0gb2YgZW50cmllcykge1xuICAgICAgLy8gQ2hlY2sgcHJpdmFjeSBsZXZlbFxuICAgICAgaWYgKHF1ZXJ5LnByaXZhY3lMZXZlbCAmJiBlbnRyeS5tZXRhZGF0YT8ucHJpdmFjeUxldmVsICE9PSBxdWVyeS5wcml2YWN5TGV2ZWwpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGRhdGUgcmFuZ2VcbiAgICAgIGlmIChxdWVyeS5kYXRlRnJvbSAmJiBlbnRyeS5tZXRhZGF0YT8udGltZXN0YW1wICYmIGVudHJ5Lm1ldGFkYXRhLnRpbWVzdGFtcCA8IHF1ZXJ5LmRhdGVGcm9tKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHF1ZXJ5LmRhdGVUbyAmJiBlbnRyeS5tZXRhZGF0YT8udGltZXN0YW1wICYmIGVudHJ5Lm1ldGFkYXRhLnRpbWVzdGFtcCA+IHF1ZXJ5LmRhdGVUbykge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgdGFnc1xuICAgICAgaWYgKHF1ZXJ5LnRhZ3MgJiYgcXVlcnkudGFncy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IGhhc0FsbFRhZ3MgPSBxdWVyeS50YWdzLmV2ZXJ5KHRhZyA9PlxuICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhPy50YWdzPy5pbmNsdWRlcyh0YWcpXG4gICAgICAgICk7XG4gICAgICAgIGlmICghaGFzQWxsVGFncykgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGNvbnRlbnQgKHNpbXBsZSBzdWJzdHJpbmcgbWF0Y2gpXG4gICAgICBsZXQgc2NvcmUgPSAxO1xuICAgICAgaWYgKHF1ZXJ5LmNvbnRlbnQpIHtcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZCA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHF1ZXJ5LmNvbnRlbnQpO1xuICAgICAgICBpZiAobm9ybWFsaXplZC5pc1ZhbGlkKSB7XG4gICAgICAgICAgY29uc3Qgc2VhcmNoVGVybSA9IG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgICBpZiAoZW50cnkuY29udGVudC50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKHNlYXJjaFRlcm0pKSB7XG4gICAgICAgICAgICBzY29yZSArPSAyO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXN1bHRzLnB1c2goe1xuICAgICAgICBlbnRyeSxcbiAgICAgICAgc2NvcmUsXG4gICAgICAgIG1hdGNoZXM6IHtcbiAgICAgICAgICB0YWdzOiBxdWVyeS50YWdzPy5maWx0ZXIodGFnID0+IGVudHJ5Lm1ldGFkYXRhPy50YWdzPy5pbmNsdWRlcyh0YWcpKVxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBTb3J0IGFuZCBwYWdpbmF0ZVxuICAgIHJlc3VsdHMuc29ydCgoYSwgYikgPT4gYi5zY29yZSAtIGEuc2NvcmUpO1xuICAgIGNvbnN0IG9mZnNldCA9IHF1ZXJ5Lm9mZnNldCB8fCAwO1xuICAgIGNvbnN0IGxpbWl0ID0gcXVlcnkubGltaXQgfHwgMTAwO1xuXG4gICAgcmV0dXJuIHJlc3VsdHMuc2xpY2Uob2Zmc2V0LCBvZmZzZXQgKyBsaW1pdCk7XG4gIH1cblxuICAvKipcbiAgICogSW50ZXJuYWwgaGVscGVyIHRvIGFkZCBlbnRyeSB0byBhbGwgaW5kZXhlc1xuICAgKi9cbiAgcHJpdmF0ZSBhZGRUb0luZGV4KGlkOiBzdHJpbmcsIGVudHJ5OiBNZW1vcnlFbnRyeSk6IHZvaWQge1xuICAgIC8vIENhY2hlIGVudHJ5XG4gICAgdGhpcy5lbnRyaWVzQ2FjaGUuc2V0KGlkLCBlbnRyeSk7XG5cbiAgICAvLyBBZGQgdG8gdGFnIGluZGV4XG4gICAgaWYgKGVudHJ5Lm1ldGFkYXRhPy50YWdzKSB7XG4gICAgICBmb3IgKGNvbnN0IHRhZyBvZiBlbnRyeS5tZXRhZGF0YS50YWdzKSB7XG4gICAgICAgIGNvbnN0IG5vcm1hbGl6ZWRUYWcgPSB0YWcudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgbGV0IGVudHJpZXMgPSB0aGlzLnRhZ0luZGV4LmdldChub3JtYWxpemVkVGFnKTtcbiAgICAgICAgaWYgKCFlbnRyaWVzKSB7XG4gICAgICAgICAgZW50cmllcyA9IG5ldyBTZXQoKTtcbiAgICAgICAgICB0aGlzLnRhZ0luZGV4LnNldChub3JtYWxpemVkVGFnLCBlbnRyaWVzKTtcbiAgICAgICAgfVxuICAgICAgICBlbnRyaWVzLmFkZChpZCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQWRkIHRvIHByaXZhY3kgaW5kZXhcbiAgICBjb25zdCBwcml2YWN5TGV2ZWwgPSBlbnRyeS5tZXRhZGF0YT8ucHJpdmFjeUxldmVsIHx8IGVudHJ5LnByaXZhY3lMZXZlbCB8fCBNRU1PUllfQ09OU1RBTlRTLkRFRkFVTFRfUFJJVkFDWV9MRVZFTDtcbiAgICBsZXQgcHJpdmFjeUVudHJpZXMgPSB0aGlzLnByaXZhY3lJbmRleC5nZXQocHJpdmFjeUxldmVsKTtcbiAgICBpZiAoIXByaXZhY3lFbnRyaWVzKSB7XG4gICAgICBwcml2YWN5RW50cmllcyA9IG5ldyBTZXQoKTtcbiAgICAgIHRoaXMucHJpdmFjeUluZGV4LnNldChwcml2YWN5TGV2ZWwsIHByaXZhY3lFbnRyaWVzKTtcbiAgICB9XG4gICAgcHJpdmFjeUVudHJpZXMuYWRkKGlkKTtcblxuICAgIC8vIEFkZCB0byBjb250ZW50IGluZGV4XG4gICAgaWYgKHRoaXMuY29udGVudEluZGV4KSB7XG4gICAgICB0aGlzLmNvbnRlbnRJbmRleC5hZGRFbnRyeShpZCwgZW50cnkuY29udGVudCk7XG4gICAgfVxuXG4gICAgLy8gQWRkIHRvIHRlbXBvcmFsIGluZGV4XG4gICAgdGhpcy50ZW1wb3JhbEluZGV4LmFkZEVudHJ5KGlkLCBlbnRyeS5tZXRhZGF0YT8udGltZXN0YW1wIHx8IGVudHJ5LnRpbWVzdGFtcCk7XG4gIH1cblxuICAvKipcbiAgICogQ2xlYXIgYWxsIGluZGV4ZXNcbiAgICovXG4gIGNsZWFyKCk6IHZvaWQge1xuICAgIHRoaXMudGFnSW5kZXguY2xlYXIoKTtcbiAgICB0aGlzLnByaXZhY3lJbmRleC5jbGVhcigpO1xuICAgIHRoaXMuY29udGVudEluZGV4Py5jbGVhcigpO1xuICAgIHRoaXMudGVtcG9yYWxJbmRleC5jbGVhcigpO1xuICAgIHRoaXMuZW50cmllc0NhY2hlLmNsZWFyKCk7XG4gICAgdGhpcy5pc0J1aWx0ID0gZmFsc2U7XG4gICAgdGhpcy5zdGF0cyA9IHtcbiAgICAgIGlzSW5kZXhlZDogZmFsc2UsXG4gICAgICBlbnRyeUNvdW50OiAwLFxuICAgICAgdGFnQ291bnQ6IDAsXG4gICAgICB0ZXJtQ291bnQ6IDBcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBpbmRleCBzdGF0aXN0aWNzXG4gICAqL1xuICBnZXRTdGF0cygpOiBTZWFyY2hJbmRleFN0YXRzIHtcbiAgICByZXR1cm4geyAuLi50aGlzLnN0YXRzIH07XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgaW5kZXggaXMgYnVpbHRcbiAgICovXG4gIGdldCBpc0luZGV4ZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuaXNCdWlsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgYW5kIHVwZGF0ZSBtZW1vcnkgdXNhZ2VcbiAgICogRklYOiBBZGRlZCBtZW1vcnkgbW9uaXRvcmluZyBmb3IgY29udGVudCBpbmRleFxuICAgKi9cbiAgcHJpdmF0ZSB1cGRhdGVNZW1vcnlVc2FnZSgpOiB2b2lkIHtcbiAgICBsZXQgdG90YWxCeXRlcyA9IDA7XG5cbiAgICAvLyBFc3RpbWF0ZSB0YWcgaW5kZXggbWVtb3J5XG4gICAgZm9yIChjb25zdCBbdGFnLCBlbnRyaWVzXSBvZiB0aGlzLnRhZ0luZGV4KSB7XG4gICAgICB0b3RhbEJ5dGVzICs9IHRhZy5sZW5ndGggKiAyOyAvLyBVVEYtMTYgZW5jb2RpbmdcbiAgICAgIHRvdGFsQnl0ZXMgKz0gZW50cmllcy5zaXplICogMzI7IC8vIEVzdGltYXRlZCBJRCBzaXplXG4gICAgfVxuXG4gICAgLy8gRXN0aW1hdGUgY29udGVudCBpbmRleCBtZW1vcnlcbiAgICBpZiAodGhpcy5jb250ZW50SW5kZXgpIHtcbiAgICAgIC8vIFJvdWdoIGVzdGltYXRlOiBlYWNoIHRlcm0gKyBlbnRyeSBJRHNcbiAgICAgIHRvdGFsQnl0ZXMgKz0gdGhpcy5jb250ZW50SW5kZXguc2l6ZSAqIDEwMDtcbiAgICB9XG5cbiAgICAvLyBFc3RpbWF0ZSB0ZW1wb3JhbCBpbmRleCBtZW1vcnlcbiAgICB0b3RhbEJ5dGVzICs9IHRoaXMudGVtcG9yYWxJbmRleC5zaXplICogNDg7IC8vIERhdGUgKyBJRFxuXG4gICAgLy8gRXN0aW1hdGUgY2FjaGUgbWVtb3J5XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiB0aGlzLmVudHJpZXNDYWNoZS52YWx1ZXMoKSkge1xuICAgICAgdG90YWxCeXRlcyArPSBKU09OLnN0cmluZ2lmeShlbnRyeSkubGVuZ3RoICogMjtcbiAgICB9XG5cbiAgICB0aGlzLm1lbW9yeVVzYWdlQnl0ZXMgPSB0b3RhbEJ5dGVzO1xuXG4gICAgLy8gQ2hlY2sgaWYgd2UncmUgZXhjZWVkaW5nIG1lbW9yeSBsaW1pdFxuICAgIGlmICh0b3RhbEJ5dGVzID4gdGhpcy5tYXhNZW1vcnlCeXRlcykge1xuICAgICAgbG9nZ2VyLndhcm4oJ01lbW9yeSBzZWFyY2ggaW5kZXggZXhjZWVkaW5nIGxpbWl0Jywge1xuICAgICAgICB1c2VkTUI6IE1hdGgucm91bmQodG90YWxCeXRlcyAvIDEwMjQgLyAxMDI0KSxcbiAgICAgICAgbGltaXRNQjogTWF0aC5yb3VuZCh0aGlzLm1heE1lbW9yeUJ5dGVzIC8gMTAyNCAvIDEwMjQpXG4gICAgICB9KTtcblxuICAgICAgLy8gVHJpZ2dlciBMUlUgZXZpY3Rpb24gaWYgZW5hYmxlZFxuICAgICAgaWYgKHRoaXMuY29uZmlnLmVuYWJsZUxSVUV2aWN0aW9uKSB7XG4gICAgICAgIHRoaXMuZXZpY3RMUlVFbnRyaWVzKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEV2aWN0IGxlYXN0IHJlY2VudGx5IHVzZWQgZW50cmllcyB0byBmcmVlIG1lbW9yeVxuICAgKiBGSVg6IEFkZGVkIExSVSBldmljdGlvbiBmb3IgbWVtb3J5IG1hbmFnZW1lbnRcbiAgICovXG4gIHByaXZhdGUgZXZpY3RMUlVFbnRyaWVzKCk6IHZvaWQge1xuICAgIC8vIEdldCBlbnRyaWVzIHNvcnRlZCBieSBhY2Nlc3MgdGltZSAod291bGQgbmVlZCB0byB0cmFjayB0aGlzKVxuICAgIC8vIEZvciBub3csIGV2aWN0IG9sZGVzdCAyMCUgb2YgZW50cmllc1xuICAgIGNvbnN0IGVudHJpZXNUb0V2aWN0ID0gTWF0aC5mbG9vcih0aGlzLmVudHJpZXNDYWNoZS5zaXplICogMC4yKTtcbiAgICBjb25zdCBlbnRyaWVzQXJyYXkgPSBBcnJheS5mcm9tKHRoaXMuZW50cmllc0NhY2hlLmtleXMoKSk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVudHJpZXNUb0V2aWN0OyBpKyspIHtcbiAgICAgIGNvbnN0IGlkVG9FdmljdCA9IGVudHJpZXNBcnJheVtpXTtcbiAgICAgIHRoaXMucmVtb3ZlRW50cnkoaWRUb0V2aWN0KTtcbiAgICB9XG5cbiAgICBsb2dnZXIuaW5mbygnRXZpY3RlZCBMUlUgZW50cmllcycsIHtcbiAgICAgIGV2aWN0ZWRDb3VudDogZW50cmllc1RvRXZpY3QsXG4gICAgICByZW1haW5pbmdDb3VudDogdGhpcy5lbnRyaWVzQ2FjaGUuc2l6ZVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlcmlhbGl6ZSBpbmRleCB0byBKU09OIGZvciBwZXJzaXN0ZW5jZVxuICAgKiBGSVg6IEFkZGVkIGluZGV4IHNlcmlhbGl6YXRpb24gZm9yIGNvbGQgc3RhcnQgb3B0aW1pemF0aW9uXG4gICAqL1xuICBzZXJpYWxpemUoKTogc3RyaW5nIHtcbiAgICBpZiAoIXRoaXMuaXNCdWlsdCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3Qgc2VyaWFsaXplIHVuYnVpbHQgaW5kZXgnKTtcbiAgICB9XG5cbiAgICBjb25zdCBpbmRleERhdGEgPSB7XG4gICAgICB2ZXJzaW9uOiAnMS4wLjAnLFxuICAgICAgc3RhdHM6IHRoaXMuc3RhdHMsXG4gICAgICB0YWdJbmRleDogQXJyYXkuZnJvbSh0aGlzLnRhZ0luZGV4LmVudHJpZXMoKSkubWFwKChbdGFnLCBpZHNdKSA9PiAoe1xuICAgICAgICB0YWcsXG4gICAgICAgIGlkczogQXJyYXkuZnJvbShpZHMpXG4gICAgICB9KSksXG4gICAgICBwcml2YWN5SW5kZXg6IEFycmF5LmZyb20odGhpcy5wcml2YWN5SW5kZXguZW50cmllcygpKS5tYXAoKFtsZXZlbCwgaWRzXSkgPT4gKHtcbiAgICAgICAgbGV2ZWwsXG4gICAgICAgIGlkczogQXJyYXkuZnJvbShpZHMpXG4gICAgICB9KSksXG4gICAgICB0ZW1wb3JhbEluZGV4OiB0aGlzLnRlbXBvcmFsSW5kZXguc2VyaWFsaXplKCksXG4gICAgICAvLyBOb3RlOiBDb250ZW50IGluZGV4IGlzIG5vdCBzZXJpYWxpemVkIGR1ZSB0byBzaXplXG4gICAgICAvLyBJdCB3aWxsIGJlIHJlYnVpbHQgb24gbG9hZCBpZiBuZWVkZWRcbiAgICB9O1xuXG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGluZGV4RGF0YSk7XG4gIH1cblxuICAvKipcbiAgICogRGVzZXJpYWxpemUgaW5kZXggZnJvbSBKU09OXG4gICAqIEZJWDogQWRkZWQgaW5kZXggZGVzZXJpYWxpemF0aW9uIGZvciBmYXN0ZXIgc3RhcnR1cFxuICAgKi9cbiAgZGVzZXJpYWxpemUoZGF0YTogc3RyaW5nLCBlbnRyaWVzOiBNYXA8c3RyaW5nLCBNZW1vcnlFbnRyeT4pOiB2b2lkIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgaW5kZXhEYXRhID0gSlNPTi5wYXJzZShkYXRhKTtcblxuICAgICAgLy8gVmFsaWRhdGUgdmVyc2lvblxuICAgICAgaWYgKGluZGV4RGF0YS52ZXJzaW9uICE9PSAnMS4wLjAnKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgaW5kZXggdmVyc2lvbjogJHtpbmRleERhdGEudmVyc2lvbn1gKTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2xlYXIgZXhpc3RpbmcgaW5kZXhlc1xuICAgICAgdGhpcy5jbGVhcigpO1xuXG4gICAgICAvLyBSZXN0b3JlIHRhZyBpbmRleFxuICAgICAgZm9yIChjb25zdCB7IHRhZywgaWRzIH0gb2YgaW5kZXhEYXRhLnRhZ0luZGV4KSB7XG4gICAgICAgIHRoaXMudGFnSW5kZXguc2V0KHRhZywgbmV3IFNldChpZHMpKTtcbiAgICAgIH1cblxuICAgICAgLy8gUmVzdG9yZSBwcml2YWN5IGluZGV4XG4gICAgICBmb3IgKGNvbnN0IHsgbGV2ZWwsIGlkcyB9IG9mIGluZGV4RGF0YS5wcml2YWN5SW5kZXgpIHtcbiAgICAgICAgdGhpcy5wcml2YWN5SW5kZXguc2V0KGxldmVsIGFzIFByaXZhY3lMZXZlbCwgbmV3IFNldChpZHMpKTtcbiAgICAgIH1cblxuICAgICAgLy8gUmVzdG9yZSB0ZW1wb3JhbCBpbmRleFxuICAgICAgdGhpcy50ZW1wb3JhbEluZGV4LmRlc2VyaWFsaXplKGluZGV4RGF0YS50ZW1wb3JhbEluZGV4KTtcblxuICAgICAgLy8gUmVzdG9yZSBlbnRyaWVzIGNhY2hlIGZyb20gcHJvdmlkZWQgZW50cmllc1xuICAgICAgZm9yIChjb25zdCBbaWQsIGVudHJ5XSBvZiBlbnRyaWVzKSB7XG4gICAgICAgIGlmIChpbmRleERhdGEudGFnSW5kZXguc29tZSgoaXRlbTogYW55KSA9PiBpdGVtLmlkcy5pbmNsdWRlcyhpZCkpKSB7XG4gICAgICAgICAgdGhpcy5lbnRyaWVzQ2FjaGUuc2V0KGlkLCBlbnRyeSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gUmVidWlsZCBjb250ZW50IGluZGV4IGlmIGVuYWJsZWQgKGNhbid0IHNlcmlhbGl6ZSBlZmZpY2llbnRseSlcbiAgICAgIGlmICh0aGlzLmNvbmZpZy5lbmFibGVDb250ZW50SW5kZXgpIHtcbiAgICAgICAgdGhpcy5jb250ZW50SW5kZXggPSBuZXcgQ29udGVudEluZGV4KHRoaXMuY29uZmlnKTtcbiAgICAgICAgZm9yIChjb25zdCBbaWQsIGVudHJ5XSBvZiB0aGlzLmVudHJpZXNDYWNoZSkge1xuICAgICAgICAgIHRoaXMuY29udGVudEluZGV4LmFkZEVudHJ5KGlkLCBlbnRyeS5jb250ZW50KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBVcGRhdGUgc3RhdHNcbiAgICAgIHRoaXMuc3RhdHMgPSBpbmRleERhdGEuc3RhdHM7XG4gICAgICB0aGlzLmlzQnVpbHQgPSB0cnVlO1xuXG4gICAgICBsb2dnZXIuaW5mbygnTWVtb3J5IHNlYXJjaCBpbmRleCBkZXNlcmlhbGl6ZWQnLCB0aGlzLnN0YXRzKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gZGVzZXJpYWxpemUgaW5kZXgsIHJlYnVpbGRpbmcnLCBlcnJvcik7XG4gICAgICAvLyBGYWxsIGJhY2sgdG8gYnVpbGRpbmcgZnJvbSBzY3JhdGNoXG4gICAgICB0aGlzLmJ1aWxkSW5kZXgoZW50cmllcyk7XG4gICAgfVxuICB9XG59Il19
|