@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
164 lines • 24.3 kB
JavaScript
/**
* EarlyTerminationSearch - Utility for parallel searches with early termination
*
* Optimizes search operations by terminating when exact matches are found,
* while still providing comprehensive results for diagnostics.
*/
import { logger } from './logger.js';
export class EarlyTerminationSearch {
/**
* Perform parallel searches with early termination when exact match found
* @param searches Array of search functions to execute
* @param isExactMatch Function to determine if a result is an exact match
* @param options Configuration options
*/
static async executeWithEarlyTermination(searches, isExactMatch, options) {
const { operationName = 'search', timeoutAfterExactMatch = 1000, maxParallelSearches = 10 } = options || {};
const startTime = Date.now();
const results = {
matches: [],
totalSearches: searches.length,
completedSearches: 0,
failures: [],
earlyTerminationTriggered: false
};
if (searches.length === 0) {
logger.debug(`${operationName}: No searches to execute`);
return results;
}
// Execute searches in batches if there are many
const batches = [];
for (let i = 0; i < searches.length; i += maxParallelSearches) {
batches.push(searches.slice(i, i + maxParallelSearches));
}
let exactMatchFound = false;
let exactMatch = null;
for (const batch of batches) {
// Create promises for the current batch
const batchPromises = batch.map(async (search, batchIndex) => {
const globalIndex = batches.indexOf(batch) * maxParallelSearches + batchIndex;
try {
const result = await search();
if (result) {
// Check if this is an exact match
if (!exactMatchFound && isExactMatch(result)) {
exactMatchFound = true;
exactMatch = result;
logger.debug(`${operationName}: Exact match found at index ${globalIndex}`, {
operationName,
exactMatchIndex: globalIndex,
timeToExactMatch: Date.now() - startTime
});
}
return { success: true, result, index: globalIndex };
}
return { success: true, result: null, index: globalIndex };
}
catch (error) {
return {
success: false,
result: null,
error: error?.message || String(error),
index: globalIndex
};
}
});
// If we found an exact match in a previous batch, use timeout for remaining batches
if (exactMatchFound && timeoutAfterExactMatch > 0) {
logger.debug(`${operationName}: Using early termination timeout for remaining searches`, {
operationName,
timeoutMs: timeoutAfterExactMatch,
remainingBatches: batches.length - batches.indexOf(batch)
});
try {
// Race between batch completion and timeout
const batchResults = await Promise.race([
Promise.allSettled(batchPromises),
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('Early termination timeout'));
}, timeoutAfterExactMatch);
})
]);
this.processBatchResults(batchResults, results);
}
catch (error) {
if (error.message === 'Early termination timeout') {
results.earlyTerminationTriggered = true;
results.performanceGain = `Terminated early after exact match, saving ${searches.length - results.completedSearches} searches`;
logger.info(`${operationName}: Early termination triggered`, {
operationName,
exactMatch: exactMatch ? 'found' : 'none',
completedSearches: results.completedSearches,
totalSearches: results.totalSearches,
timeSaved: `${Date.now() - startTime}ms`
});
break;
}
throw error;
}
}
else {
// Normal execution without timeout
const batchResults = await Promise.allSettled(batchPromises);
this.processBatchResults(batchResults, results);
}
// If we found an exact match and this is the first batch, we can potentially terminate
if (exactMatchFound && batches.indexOf(batch) === 0 && batches.length > 1) {
logger.debug(`${operationName}: Considering early termination after first batch`, {
operationName,
exactMatchFound: true,
remainingBatches: batches.length - 1
});
// Continue to next batch with timeout
}
}
// Add exact match to results if found
if (exactMatch) {
results.exactMatch = exactMatch;
// Ensure exact match is in matches array
if (!results.matches.some(m => m === exactMatch)) {
results.matches.unshift(exactMatch); // Put exact match first
}
}
const totalTime = Date.now() - startTime;
logger.debug(`${operationName}: Search operation completed`, {
operationName,
totalTime: `${totalTime}ms`,
totalSearches: results.totalSearches,
completedSearches: results.completedSearches,
matches: results.matches.length,
failures: results.failures.length,
exactMatchFound: !!results.exactMatch,
earlyTerminationTriggered: results.earlyTerminationTriggered,
performanceGain: results.performanceGain
});
return results;
}
/**
* Process batch results and update the main results object
*/
static processBatchResults(batchResults, results) {
for (const promiseResult of batchResults) {
if (promiseResult.status === 'fulfilled') {
const { success, result, error, index } = promiseResult.value;
results.completedSearches++;
if (success && result) {
results.matches.push(result);
}
else if (!success && error) {
results.failures.push({ index, error });
}
}
else {
// Promise itself was rejected
results.completedSearches++;
results.failures.push({
index: -1, // Unknown index for rejected promise
error: promiseResult.reason?.message || String(promiseResult.reason)
});
}
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRWFybHlUZXJtaW5hdGlvblNlYXJjaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9FYXJseVRlcm1pbmF0aW9uU2VhcmNoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7OztHQUtHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQWtCckMsTUFBTSxPQUFPLHNCQUFzQjtJQUNqQzs7Ozs7T0FLRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsMkJBQTJCLENBQ3RDLFFBQXdDLEVBQ3hDLFlBQW9DLEVBQ3BDLE9BSUM7UUFFRCxNQUFNLEVBQ0osYUFBYSxHQUFHLFFBQVEsRUFDeEIsc0JBQXNCLEdBQUcsSUFBSSxFQUM3QixtQkFBbUIsR0FBRyxFQUFFLEVBQ3pCLEdBQUcsT0FBTyxJQUFJLEVBQUUsQ0FBQztRQUVsQixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsTUFBTSxPQUFPLEdBQThCO1lBQ3pDLE9BQU8sRUFBRSxFQUFFO1lBQ1gsYUFBYSxFQUFFLFFBQVEsQ0FBQyxNQUFNO1lBQzlCLGlCQUFpQixFQUFFLENBQUM7WUFDcEIsUUFBUSxFQUFFLEVBQUU7WUFDWix5QkFBeUIsRUFBRSxLQUFLO1NBQ2pDLENBQUM7UUFFRixJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDMUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLGFBQWEsMEJBQTBCLENBQUMsQ0FBQztZQUN6RCxPQUFPLE9BQU8sQ0FBQztRQUNqQixDQUFDO1FBRUQsZ0RBQWdEO1FBQ2hELE1BQU0sT0FBTyxHQUEwQyxFQUFFLENBQUM7UUFDMUQsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLG1CQUFtQixFQUFFLENBQUM7WUFDOUQsT0FBTyxDQUFDLElBQUksQ0FBQyxR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SyxDQUFDO2dCQUM5RCxPQUFPLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztnQkFFNUIsSUFBSSxPQUFPLElBQUksTUFBTSxFQUFFLENBQUM7b0JBQ3RCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUMvQixDQUFDO3FCQUFNLElBQUksQ0FBQyxPQUFPLElBQUksS0FBSyxFQUFFLENBQUM7b0JBQzdCLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFDLENBQUM7WUFDSCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sOEJBQThCO2dCQUM5QixPQUFPLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztnQkFDNUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7b0JBQ3BCLEtBQUssRUFBRSxDQUFDLENBQUMsRUFBRSxxQ0FBcUM7b0JBQ2hELEtBQUssRUFBRSxhQUFhLENBQUMsTUFBTSxFQUFFLE9BQU8sSUFBSSxNQUFNLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQztpQkFDckUsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEVhcmx5VGVybWluYXRpb25TZWFyY2ggLSBVdGlsaXR5IGZvciBwYXJhbGxlbCBzZWFyY2hlcyB3aXRoIGVhcmx5IHRlcm1pbmF0aW9uXG4gKiBcbiAqIE9wdGltaXplcyBzZWFyY2ggb3BlcmF0aW9ucyBieSB0ZXJtaW5hdGluZyB3aGVuIGV4YWN0IG1hdGNoZXMgYXJlIGZvdW5kLFxuICogd2hpbGUgc3RpbGwgcHJvdmlkaW5nIGNvbXByZWhlbnNpdmUgcmVzdWx0cyBmb3IgZGlhZ25vc3RpY3MuXG4gKi9cblxuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFNlYXJjaFJlc3VsdDxUPiB7XG4gIHN1Y2Nlc3M6IGJvb2xlYW47XG4gIHJlc3VsdDogVCB8IG51bGw7XG4gIGVycm9yPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEVhcmx5VGVybWluYXRpb25SZXN1bHQ8VD4ge1xuICBtYXRjaGVzOiBUW107XG4gIGV4YWN0TWF0Y2g/OiBUO1xuICB0b3RhbFNlYXJjaGVzOiBudW1iZXI7XG4gIGNvbXBsZXRlZFNlYXJjaGVzOiBudW1iZXI7XG4gIGZhaWx1cmVzOiBBcnJheTx7IGluZGV4OiBudW1iZXI7IGVycm9yOiBzdHJpbmcgfT47XG4gIGVhcmx5VGVybWluYXRpb25UcmlnZ2VyZWQ6IGJvb2xlYW47XG4gIHBlcmZvcm1hbmNlR2Fpbj86IHN0cmluZztcbn1cblxuZXhwb3J0IGNsYXNzIEVhcmx5VGVybWluYXRpb25TZWFyY2gge1xuICAvKipcbiAgICogUGVyZm9ybSBwYXJhbGxlbCBzZWFyY2hlcyB3aXRoIGVhcmx5IHRlcm1pbmF0aW9uIHdoZW4gZXhhY3QgbWF0Y2ggZm91bmRcbiAgICogQHBhcmFtIHNlYXJjaGVzIEFycmF5IG9mIHNlYXJjaCBmdW5jdGlvbnMgdG8gZXhlY3V0ZVxuICAgKiBAcGFyYW0gaXNFeGFjdE1hdGNoIEZ1bmN0aW9uIHRvIGRldGVybWluZSBpZiBhIHJlc3VsdCBpcyBhbiBleGFjdCBtYXRjaFxuICAgKiBAcGFyYW0gb3B0aW9ucyBDb25maWd1cmF0aW9uIG9wdGlvbnNcbiAgICovXG4gIHN0YXRpYyBhc3luYyBleGVjdXRlV2l0aEVhcmx5VGVybWluYXRpb248VD4oXG4gICAgc2VhcmNoZXM6IEFycmF5PCgpID0+IFByb21pc2U8VCB8IG51bGw+PixcbiAgICBpc0V4YWN0TWF0Y2g6IChyZXN1bHQ6IFQpID0+IGJvb2xlYW4sXG4gICAgb3B0aW9ucz86IHtcbiAgICAgIG9wZXJhdGlvbk5hbWU/OiBzdHJpbmc7XG4gICAgICB0aW1lb3V0QWZ0ZXJFeGFjdE1hdGNoPzogbnVtYmVyOyAvLyBtcyB0byB3YWl0IGZvciBvdGhlciBzZWFyY2hlcyBhZnRlciBleGFjdCBtYXRjaFxuICAgICAgbWF4UGFyYWxsZWxTZWFyY2hlcz86IG51bWJlcjsgLy8gbGltaXQgY29uY3VycmVudCBzZWFyY2hlc1xuICAgIH1cbiAgKTogUHJvbWlzZTxFYXJseVRlcm1pbmF0aW9uUmVzdWx0PFQ+PiB7XG4gICAgY29uc3Qge1xuICAgICAgb3BlcmF0aW9uTmFtZSA9ICdzZWFyY2gnLFxuICAgICAgdGltZW91dEFmdGVyRXhhY3RNYXRjaCA9IDEwMDAsXG4gICAgICBtYXhQYXJhbGxlbFNlYXJjaGVzID0gMTBcbiAgICB9ID0gb3B0aW9ucyB8fCB7fTtcblxuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgY29uc3QgcmVzdWx0czogRWFybHlUZXJtaW5hdGlvblJlc3VsdDxUPiA9IHtcbiAgICAgIG1hdGNoZXM6IFtdLFxuICAgICAgdG90YWxTZWFyY2hlczogc2VhcmNoZXMubGVuZ3RoLFxuICAgICAgY29tcGxldGVkU2VhcmNoZXM6IDAsXG4gICAgICBmYWlsdXJlczogW10sXG4gICAgICBlYXJseVRlcm1pbmF0aW9uVHJpZ2dlcmVkOiBmYWxzZVxuICAgIH07XG5cbiAgICBpZiAoc2VhcmNoZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBsb2dnZXIuZGVidWcoYCR7b3BlcmF0aW9uTmFtZX06IE5vIHNlYXJjaGVzIHRvIGV4ZWN1dGVgKTtcbiAgICAgIHJldHVybiByZXN1bHRzO1xuICAgIH1cblxuICAgIC8vIEV4ZWN1dGUgc2VhcmNoZXMgaW4gYmF0Y2hlcyBpZiB0aGVyZSBhcmUgbWFueVxuICAgIGNvbnN0IGJhdGNoZXM6IEFycmF5PEFycmF5PCgpID0+IFByb21pc2U8VCB8IG51bGw+Pj4gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNlYXJjaGVzLmxlbmd0aDsgaSArPSBtYXhQYXJhbGxlbFNlYXJjaGVzKSB7XG4gICAgICBiYXRjaGVzLnB1c2goc2VhcmNoZXMuc2xpY2UoaSwgaSArIG1heFBhcmFsbGVsU2VhcmNoZXMpKTtcbiAgICB9XG5cbiAgICBsZXQgZXhhY3RNYXRjaEZvdW5kID0gZmFsc2U7XG4gICAgbGV0IGV4YWN0TWF0Y2g6IFQgfCBudWxsID0gbnVsbDtcblxuICAgIGZvciAoY29uc3QgYmF0Y2ggb2YgYmF0Y2hlcykge1xuICAgICAgLy8gQ3JlYXRlIHByb21pc2VzIGZvciB0aGUgY3VycmVudCBiYXRjaFxuICAgICAgY29uc3QgYmF0Y2hQcm9taXNlcyA9IGJhdGNoLm1hcChhc3luYyAoc2VhcmNoLCBiYXRjaEluZGV4KSA9PiB7XG4gICAgICAgIGNvbnN0IGdsb2JhbEluZGV4ID0gYmF0Y2hlcy5pbmRleE9mKGJhdGNoKSAqIG1heFBhcmFsbGVsU2VhcmNoZXMgKyBiYXRjaEluZGV4O1xuICAgICAgICBcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzZWFyY2goKTtcbiAgICAgICAgICBcbiAgICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgICAvLyBDaGVjayBpZiB0aGlzIGlzIGFuIGV4YWN0IG1hdGNoXG4gICAgICAgICAgICBpZiAoIWV4YWN0TWF0Y2hGb3VuZCAmJiBpc0V4YWN0TWF0Y2gocmVzdWx0KSkge1xuICAgICAgICAgICAgICBleGFjdE1hdGNoRm91bmQgPSB0cnVlO1xuICAgICAgICAgICAgICBleGFjdE1hdGNoID0gcmVzdWx0O1xuICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKGAke29wZXJhdGlvbk5hbWV9OiBFeGFjdCBtYXRjaCBmb3VuZCBhdCBpbmRleCAke2dsb2JhbEluZGV4fWAsIHtcbiAgICAgICAgICAgICAgICBvcGVyYXRpb25OYW1lLFxuICAgICAgICAgICAgICAgIGV4YWN0TWF0Y2hJbmRleDogZ2xvYmFsSW5kZXgsXG4gICAgICAgICAgICAgICAgdGltZVRvRXhhY3RNYXRjaDogRGF0ZS5ub3coKSAtIHN0YXJ0VGltZVxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSwgcmVzdWx0LCBpbmRleDogZ2xvYmFsSW5kZXggfTtcbiAgICAgICAgICB9XG4gICAgICAgICAgXG4gICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSwgcmVzdWx0OiBudWxsLCBpbmRleDogZ2xvYmFsSW5kZXggfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIHJlc3VsdDogbnVsbCxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvcj8ubWVzc2FnZSB8fCBTdHJpbmcoZXJyb3IpLFxuICAgICAgICAgICAgaW5kZXg6IGdsb2JhbEluZGV4XG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIC8vIElmIHdlIGZvdW5kIGFuIGV4YWN0IG1hdGNoIGluIGEgcHJldmlvdXMgYmF0Y2gsIHVzZSB0aW1lb3V0IGZvciByZW1haW5pbmcgYmF0Y2hlc1xuICAgICAgaWYgKGV4YWN0TWF0Y2hGb3VuZCAmJiB0aW1lb3V0QWZ0ZXJFeGFjdE1hdGNoID4gMCkge1xuICAgICAgICBsb2dnZXIuZGVidWcoYCR7b3BlcmF0aW9uTmFtZX06IFVzaW5nIGVhcmx5IHRlcm1pbmF0aW9uIHRpbWVvdXQgZm9yIHJlbWFpbmluZyBzZWFyY2hlc2AsIHtcbiAgICAgICAgICBvcGVyYXRpb25OYW1lLFxuICAgICAgICAgIHRpbWVvdXRNczogdGltZW91dEFmdGVyRXhhY3RNYXRjaCxcbiAgICAgICAgICByZW1haW5pbmdCYXRjaGVzOiBiYXRjaGVzLmxlbmd0aCAtIGJhdGNoZXMuaW5kZXhPZihiYXRjaClcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAvLyBSYWNlIGJldHdlZW4gYmF0Y2ggY29tcGxldGlvbiBhbmQgdGltZW91dFxuICAgICAgICAgIGNvbnN0IGJhdGNoUmVzdWx0cyA9IGF3YWl0IFByb21pc2UucmFjZShbXG4gICAgICAgICAgICBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hQcm9taXNlcyksXG4gICAgICAgICAgICBuZXcgUHJvbWlzZTxuZXZlcj4oKF8sIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKCdFYXJseSB0ZXJtaW5hdGlvbiB0aW1lb3V0JykpO1xuICAgICAgICAgICAgICB9LCB0aW1lb3V0QWZ0ZXJFeGFjdE1hdGNoKTtcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgXSk7XG5cbiAgICAgICAgICB0aGlzLnByb2Nlc3NCYXRjaFJlc3VsdHMoYmF0Y2hSZXN1bHRzLCByZXN1bHRzKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGlmIChlcnJvci5tZXNzYWdlID09PSAnRWFybHkgdGVybWluYXRpb24gdGltZW91dCcpIHtcbiAgICAgICAgICAgIHJlc3VsdHMuZWFybHlUZXJtaW5hdGlvblRyaWdnZXJlZCA9IHRydWU7XG4gICAgICAgICAgICByZXN1bHRzLnBlcmZvcm1hbmNlR2FpbiA9IGBUZXJtaW5hdGVkIGVhcmx5IGFmdGVyIGV4YWN0IG1hdGNoLCBzYXZpbmcgJHtzZWFyY2hlcy5sZW5ndGggLSByZXN1bHRzLmNvbXBsZXRlZFNlYXJjaGVzfSBzZWFyY2hlc2A7XG4gICAgICAgICAgICBsb2dnZXIuaW5mbyhgJHtvcGVyYXRpb25OYW1lfTogRWFybHkgdGVybWluYXRpb24gdHJpZ2dlcmVkYCwge1xuICAgICAgICAgICAgICBvcGVyYXRpb25OYW1lLFxuICAgICAgICAgICAgICBleGFjdE1hdGNoOiBleGFjdE1hdGNoID8gJ2ZvdW5kJyA6ICdub25lJyxcbiAgICAgICAgICAgICAgY29tcGxldGVkU2VhcmNoZXM6IHJlc3VsdHMuY29tcGxldGVkU2VhcmNoZXMsXG4gICAgICAgICAgICAgIHRvdGFsU2VhcmNoZXM6IHJlc3VsdHMudG90YWxTZWFyY2hlcyxcbiAgICAgICAgICAgICAgdGltZVNhdmVkOiBgJHtEYXRlLm5vdygpIC0gc3RhcnRUaW1lfW1zYFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIE5vcm1hbCBleGVjdXRpb24gd2l0aG91dCB0aW1lb3V0XG4gICAgICAgIGNvbnN0IGJhdGNoUmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChiYXRjaFByb21pc2VzKTtcbiAgICAgICAgdGhpcy5wcm9jZXNzQmF0Y2hSZXN1bHRzKGJhdGNoUmVzdWx0cywgcmVzdWx0cyk7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIHdlIGZvdW5kIGFuIGV4YWN0IG1hdGNoIGFuZCB0aGlzIGlzIHRoZSBmaXJzdCBiYXRjaCwgd2UgY2FuIHBvdGVudGlhbGx5IHRlcm1pbmF0ZVxuICAgICAgaWYgKGV4YWN0TWF0Y2hGb3VuZCAmJiBiYXRjaGVzLmluZGV4T2YoYmF0Y2gpID09PSAwICYmIGJhdGNoZXMubGVuZ3RoID4gMSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoYCR7b3BlcmF0aW9uTmFtZX06IENvbnNpZGVyaW5nIGVhcmx5IHRlcm1pbmF0aW9uIGFmdGVyIGZpcnN0IGJhdGNoYCwge1xuICAgICAgICAgIG9wZXJhdGlvbk5hbWUsXG4gICAgICAgICAgZXhhY3RNYXRjaEZvdW5kOiB0cnVlLFxuICAgICAgICAgIHJlbWFpbmluZ0JhdGNoZXM6IGJhdGNoZXMubGVuZ3RoIC0gMVxuICAgICAgICB9KTtcbiAgICAgICAgLy8gQ29udGludWUgdG8gbmV4dCBiYXRjaCB3aXRoIHRpbWVvdXRcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBZGQgZXhhY3QgbWF0Y2ggdG8gcmVzdWx0cyBpZiBmb3VuZFxuICAgIGlmIChleGFjdE1hdGNoKSB7XG4gICAgICByZXN1bHRzLmV4YWN0TWF0Y2ggPSBleGFjdE1hdGNoO1xuICAgICAgLy8gRW5zdXJlIGV4YWN0IG1hdGNoIGlzIGluIG1hdGNoZXMgYXJyYXlcbiAgICAgIGlmICghcmVzdWx0cy5tYXRjaGVzLnNvbWUobSA9PiBtID09PSBleGFjdE1hdGNoKSkge1xuICAgICAgICByZXN1bHRzLm1hdGNoZXMudW5zaGlmdChleGFjdE1hdGNoKTsgLy8gUHV0IGV4YWN0IG1hdGNoIGZpcnN0XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgdG90YWxUaW1lID0gRGF0ZS5ub3coKSAtIHN0YXJ0VGltZTtcbiAgICBsb2dnZXIuZGVidWcoYCR7b3BlcmF0aW9uTmFtZX06IFNlYXJjaCBvcGVyYXRpb24gY29tcGxldGVkYCwge1xuICAgICAgb3BlcmF0aW9uTmFtZSxcbiAgICAgIHRvdGFsVGltZTogYCR7dG90YWxUaW1lfW1zYCxcbiAgICAgIHRvdGFsU2VhcmNoZXM6IHJlc3VsdHMudG90YWxTZWFyY2hlcyxcbiAgICAgIGNvbXBsZXRlZFNlYXJjaGVzOiByZXN1bHRzLmNvbXBsZXRlZFNlYXJjaGVzLFxuICAgICAgbWF0Y2hlczogcmVzdWx0cy5tYXRjaGVzLmxlbmd0aCxcbiAgICAgIGZhaWx1cmVzOiByZXN1bHRzLmZhaWx1cmVzLmxlbmd0aCxcbiAgICAgIGV4YWN0TWF0Y2hGb3VuZDogISFyZXN1bHRzLmV4YWN0TWF0Y2gsXG4gICAgICBlYXJseVRlcm1pbmF0aW9uVHJpZ2dlcmVkOiByZXN1bHRzLmVhcmx5VGVybWluYXRpb25UcmlnZ2VyZWQsXG4gICAgICBwZXJmb3JtYW5jZUdhaW46IHJlc3VsdHMucGVyZm9ybWFuY2VHYWluXG4gICAgfSk7XG5cbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9jZXNzIGJhdGNoIHJlc3VsdHMgYW5kIHVwZGF0ZSB0aGUgbWFpbiByZXN1bHRzIG9iamVjdFxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcHJvY2Vzc0JhdGNoUmVzdWx0czxUPihcbiAgICBiYXRjaFJlc3VsdHM6IFByb21pc2VTZXR0bGVkUmVzdWx0PHsgc3VjY2VzczogYm9vbGVhbjsgcmVzdWx0OiBUIHwgbnVsbDsgZXJyb3I/OiBzdHJpbmc7IGluZGV4OiBudW1iZXIgfT5bXSxcbiAgICByZXN1bHRzOiBFYXJseVRlcm1pbmF0aW9uUmVzdWx0PFQ+XG4gICk6IHZvaWQge1xuICAgIGZvciAoY29uc3QgcHJvbWlzZVJlc3VsdCBvZiBiYXRjaFJlc3VsdHMpIHtcbiAgICAgIGlmIChwcm9taXNlUmVzdWx0LnN0YXR1cyA9PT0gJ2Z1bGZpbGxlZCcpIHtcbiAgICAgICAgY29uc3QgeyBzdWNjZXNzLCByZXN1bHQsIGVycm9yLCBpbmRleCB9ID0gcHJvbWlzZVJlc3VsdC52YWx1ZTtcbiAgICAgICAgcmVzdWx0cy5jb21wbGV0ZWRTZWFyY2hlcysrO1xuICAgICAgICBcbiAgICAgICAgaWYgKHN1Y2Nlc3MgJiYgcmVzdWx0KSB7XG4gICAgICAgICAgcmVzdWx0cy5tYXRjaGVzLnB1c2gocmVzdWx0KTtcbiAgICAgICAgfSBlbHNlIGlmICghc3VjY2VzcyAmJiBlcnJvcikge1xuICAgICAgICAgIHJlc3VsdHMuZmFpbHVyZXMucHVzaCh7IGluZGV4LCBlcnJvciB9KTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gUHJvbWlzZSBpdHNlbGYgd2FzIHJlamVjdGVkXG4gICAgICAgIHJlc3VsdHMuY29tcGxldGVkU2VhcmNoZXMrKztcbiAgICAgICAgcmVzdWx0cy5mYWlsdXJlcy5wdXNoKHsgXG4gICAgICAgICAgaW5kZXg6IC0xLCAvLyBVbmtub3duIGluZGV4IGZvciByZWplY3RlZCBwcm9taXNlXG4gICAgICAgICAgZXJyb3I6IHByb21pc2VSZXN1bHQucmVhc29uPy5tZXNzYWdlIHx8IFN0cmluZyhwcm9taXNlUmVzdWx0LnJlYXNvbilcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG59Il19