UNPKG

aws-cdk

Version:

AWS CDK CLI, the command line tool for CDK apps

151 lines 21.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BackgroundStackRefresh = exports.ActiveAssetCache = void 0; exports.refreshStacks = refreshStacks; const api_1 = require("../../../../@aws-cdk/tmp-toolkit-helpers/src/api"); const private_1 = require("../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private"); class ActiveAssetCache { constructor() { this.stacks = new Set(); } rememberStack(stackTemplate) { this.stacks.add(stackTemplate); } contains(asset) { for (const stack of this.stacks) { if (stack.includes(asset)) { return true; } } return false; } } exports.ActiveAssetCache = ActiveAssetCache; async function paginateSdkCall(cb) { let finished = false; let nextToken; while (!finished) { nextToken = await cb(nextToken); if (nextToken === undefined) { finished = true; } } } /** * Fetches all relevant stack templates from CloudFormation. It ignores the following stacks: * - stacks in DELETE_COMPLETE or DELETE_IN_PROGRESS stage * - stacks that are using a different bootstrap qualifier */ async function fetchAllStackTemplates(cfn, ioHelper, qualifier) { const stackNames = []; await paginateSdkCall(async (nextToken) => { const stacks = await cfn.listStacks({ NextToken: nextToken }); // We ignore stacks with these statuses because their assets are no longer live const ignoredStatues = ['CREATE_FAILED', 'DELETE_COMPLETE', 'DELETE_IN_PROGRESS', 'DELETE_FAILED', 'REVIEW_IN_PROGRESS']; stackNames.push(...(stacks.StackSummaries ?? []) .filter((s) => !ignoredStatues.includes(s.StackStatus)) .map((s) => s.StackId ?? s.StackName)); return stacks.NextToken; }); await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Parsing through ${stackNames.length} stacks`)); const templates = []; for (const stack of stackNames) { let summary; summary = await cfn.getTemplateSummary({ StackName: stack, }); if (bootstrapFilter(summary.Parameters, qualifier)) { // This stack is definitely bootstrapped to a different qualifier so we can safely ignore it continue; } else { const template = await cfn.getTemplate({ StackName: stack, }); templates.push((template.TemplateBody ?? '') + JSON.stringify(summary?.Parameters)); } } await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg('Done parsing through stacks')); return templates; } /** * Filter out stacks that we KNOW are using a different bootstrap qualifier * This is mostly necessary for the integration tests that can run the same app (with the same assets) * under different qualifiers. * This is necessary because a stack under a different bootstrap could coincidentally reference the same hash * and cause a false negative (cause an asset to be preserved when its isolated) * This is intentionally done in a way where we ONLY filter out stacks that are meant for a different qualifier * because we are okay with false positives. */ function bootstrapFilter(parameters, qualifier) { const bootstrapVersion = parameters?.find((p) => p.ParameterKey === 'BootstrapVersion'); const splitBootstrapVersion = bootstrapVersion?.DefaultValue?.split('/'); // We find the qualifier in a specific part of the bootstrap version parameter return (qualifier && splitBootstrapVersion && splitBootstrapVersion.length == 4 && splitBootstrapVersion[2] != qualifier); } async function refreshStacks(cfn, ioHelper, activeAssets, qualifier) { try { const stacks = await fetchAllStackTemplates(cfn, ioHelper, qualifier); for (const stack of stacks) { activeAssets.rememberStack(stack); } } catch (err) { throw new api_1.ToolkitError(`Error refreshing stacks: ${err}`); } } /** * Class that controls scheduling of the background stack refresh */ class BackgroundStackRefresh { constructor(props) { this.props = props; this.queuedPromises = []; this.lastRefreshTime = Date.now(); } start() { // Since start is going to be called right after the first invocation of refreshStacks, // lets wait some time before beginning the background refresh. this.timeout = setTimeout(() => this.refresh(), 300000); // 5 minutes } async refresh() { const startTime = Date.now(); await refreshStacks(this.props.cfn, this.props.ioHelper, this.props.activeAssets, this.props.qualifier); this.justRefreshedStacks(); // If the last invocation of refreshStacks takes <5 minutes, the next invocation starts 5 minutes after the last one started. // If the last invocation of refreshStacks takes >5 minutes, the next invocation starts immediately. this.timeout = setTimeout(() => this.refresh(), Math.max(startTime + 300000 - Date.now(), 0)); } justRefreshedStacks() { this.lastRefreshTime = Date.now(); for (const p of this.queuedPromises.splice(0, this.queuedPromises.length)) { p(undefined); } } /** * Checks if the last successful background refresh happened within the specified time frame. * If the last refresh is older than the specified time frame, it returns a Promise that resolves * when the next background refresh completes or rejects if the refresh takes too long. */ noOlderThan(ms) { const horizon = Date.now() - ms; // The last refresh happened within the time frame if (this.lastRefreshTime >= horizon) { return Promise.resolve(); } // The last refresh happened earlier than the time frame // We will wait for the latest refresh to land or reject if it takes too long return Promise.race([ new Promise(resolve => this.queuedPromises.push(resolve)), new Promise((_, reject) => setTimeout(() => reject(new api_1.ToolkitError('refreshStacks took too long; the background thread likely threw an error')), ms)), ]); } stop() { clearTimeout(this.timeout); } } exports.BackgroundStackRefresh = BackgroundStackRefresh; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhY2stcmVmcmVzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInN0YWNrLXJlZnJlc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBbUdBLHNDQVNDO0FBM0dELDBFQUFnRjtBQUNoRix5RkFBZ0c7QUFHaEcsTUFBYSxnQkFBZ0I7SUFBN0I7UUFDbUIsV0FBTSxHQUFnQixJQUFJLEdBQUcsRUFBRSxDQUFDO0lBY25ELENBQUM7SUFaUSxhQUFhLENBQUMsYUFBcUI7UUFDeEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDakMsQ0FBQztJQUVNLFFBQVEsQ0FBQyxLQUFhO1FBQzNCLEtBQUssTUFBTSxLQUFLLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hDLElBQUksS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMxQixPQUFPLElBQUksQ0FBQztZQUNkLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0Y7QUFmRCw0Q0FlQztBQUVELEtBQUssVUFBVSxlQUFlLENBQUMsRUFBdUQ7SUFDcEYsSUFBSSxRQUFRLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLElBQUksU0FBNkIsQ0FBQztJQUNsQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDakIsU0FBUyxHQUFHLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ2hDLElBQUksU0FBUyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzVCLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFDbEIsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxHQUEwQixFQUFFLFFBQWtCLEVBQUUsU0FBa0I7SUFDdEcsTUFBTSxVQUFVLEdBQWEsRUFBRSxDQUFDO0lBQ2hDLE1BQU0sZUFBZSxDQUFDLEtBQUssRUFBRSxT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ekIsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDbEMsS0FBSyxNQUFNLENBQUMsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQzFFLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNmLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLFdBQVcsQ0FBQyxFQUFVO1FBQzNCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7UUFFaEMsa0RBQWtEO1FBQ2xELElBQUksSUFBSSxDQUFDLGVBQWUsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNwQyxPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMzQixDQUFDO1FBRUQsd0RBQXdEO1FBQ3hELDZFQUE2RTtRQUM3RSxPQUFPLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFDbEIsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUN6RCxJQUFJLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxrQkFBWSxDQUFDLDBFQUEwRSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztTQUN2SixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0sSUFBSTtRQUNULFlBQVksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0IsQ0FBQztDQUNGO0FBekRELHdEQXlEQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgUGFyYW1ldGVyRGVjbGFyYXRpb24gfSBmcm9tICdAYXdzLXNkay9jbGllbnQtY2xvdWRmb3JtYXRpb24nO1xuaW1wb3J0IHsgVG9vbGtpdEVycm9yIH0gZnJvbSAnLi4vLi4vLi4vLi4vQGF3cy1jZGsvdG1wLXRvb2xraXQtaGVscGVycy9zcmMvYXBpJztcbmltcG9ydCB7IElPLCB0eXBlIElvSGVscGVyIH0gZnJvbSAnLi4vLi4vLi4vLi4vQGF3cy1jZGsvdG1wLXRvb2xraXQtaGVscGVycy9zcmMvYXBpL2lvL3ByaXZhdGUnO1xuaW1wb3J0IHR5cGUgeyBJQ2xvdWRGb3JtYXRpb25DbGllbnQgfSBmcm9tICcuLi9hd3MtYXV0aCc7XG5cbmV4cG9ydCBjbGFzcyBBY3RpdmVBc3NldENhY2hlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBzdGFja3M6IFNldDxzdHJpbmc+ID0gbmV3IFNldCgpO1xuXG4gIHB1YmxpYyByZW1lbWJlclN0YWNrKHN0YWNrVGVtcGxhdGU6IHN0cmluZykge1xuICAgIHRoaXMuc3RhY2tzLmFkZChzdGFja1RlbXBsYXRlKTtcbiAgfVxuXG4gIHB1YmxpYyBjb250YWlucyhhc3NldDogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgZm9yIChjb25zdCBzdGFjayBvZiB0aGlzLnN0YWNrcykge1xuICAgICAgaWYgKHN0YWNrLmluY2x1ZGVzKGFzc2V0KSkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHBhZ2luYXRlU2RrQ2FsbChjYjogKG5leHRUb2tlbj86IHN0cmluZykgPT4gUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+KSB7XG4gIGxldCBmaW5pc2hlZCA9IGZhbHNlO1xuICBsZXQgbmV4dFRva2VuOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIHdoaWxlICghZmluaXNoZWQpIHtcbiAgICBuZXh0VG9rZW4gPSBhd2FpdCBjYihuZXh0VG9rZW4pO1xuICAgIGlmIChuZXh0VG9rZW4gPT09IHVuZGVmaW5lZCkge1xuICAgICAgZmluaXNoZWQgPSB0cnVlO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEZldGNoZXMgYWxsIHJlbGV2YW50IHN0YWNrIHRlbXBsYXRlcyBmcm9tIENsb3VkRm9ybWF0aW9uLiBJdCBpZ25vcmVzIHRoZSBmb2xsb3dpbmcgc3RhY2tzOlxuICogLSBzdGFja3MgaW4gREVMRVRFX0NPTVBMRVRFIG9yIERFTEVURV9JTl9QUk9HUkVTUyBzdGFnZVxuICogLSBzdGFja3MgdGhhdCBhcmUgdXNpbmcgYSBkaWZmZXJlbnQgYm9vdHN0cmFwIHF1YWxpZmllclxuICovXG5hc3luYyBmdW5jdGlvbiBmZXRjaEFsbFN0YWNrVGVtcGxhdGVzKGNmbjogSUNsb3VkRm9ybWF0aW9uQ2xpZW50LCBpb0hlbHBlcjogSW9IZWxwZXIsIHF1YWxpZmllcj86IHN0cmluZykge1xuICBjb25zdCBzdGFja05hbWVzOiBzdHJpbmdbXSA9IFtdO1xuICBhd2FpdCBwYWdpbmF0ZVNka0NhbGwoYXN5bmMgKG5leHRUb2tlbikgPT4ge1xuICAgIGNvbnN0IHN0YWNrcyA9IGF3YWl0IGNmbi5saXN0U3RhY2tzKHsgTmV4dFRva2VuOiBuZXh0VG9rZW4gfSk7XG5cbiAgICAvLyBXZSBpZ25vcmUgc3RhY2tzIHdpdGggdGhlc2Ugc3RhdHVzZXMgYmVjYXVzZSB0aGVpciBhc3NldHMgYXJlIG5vIGxvbmdlciBsaXZlXG4gICAgY29uc3QgaWdub3JlZFN0YXR1ZXMgPSBbJ0NSRUFURV9GQUlMRUQnLCAnREVMRVRFX0NPTVBMRVRFJywgJ0RFTEVURV9JTl9QUk9HUkVTUycsICdERUxFVEVfRkFJTEVEJywgJ1JFVklFV19JTl9QUk9HUkVTUyddO1xuICAgIHN0YWNrTmFtZXMucHVzaChcbiAgICAgIC4uLihzdGFja3MuU3RhY2tTdW1tYXJpZXMgPz8gW10pXG4gICAgICAgIC5maWx0ZXIoKHM6IGFueSkgPT4gIWlnbm9yZWRTdGF0dWVzLmluY2x1ZGVzKHMuU3RhY2tTdGF0dXMpKVxuICAgICAgICAubWFwKChzOiBhbnkpID0+IHMuU3RhY2tJZCA/PyBzLlN0YWNrTmFtZSksXG4gICAgKTtcblxuICAgIHJldHVybiBzdGFja3MuTmV4dFRva2VuO1xuICB9KTtcblxuICBhd2FpdCBpb0hlbHBlci5ub3RpZnkoSU8uREVGQVVMVF9UT09MS0lUX0RFQlVHLm1zZyhgUGFyc2luZyB0aHJvdWdoICR7c3RhY2tOYW1lcy5sZW5ndGh9IHN0YWNrc2ApKTtcblxuICBjb25zdCB0ZW1wbGF0ZXM6IHN0cmluZ1tdID0gW107XG4gIGZvciAoY29uc3Qgc3RhY2sgb2Ygc3RhY2tOYW1lcykge1xuICAgIGxldCBzdW1tYXJ5O1xuICAgIHN1bW1hcnkgPSBhd2FpdCBjZm4uZ2V0VGVtcGxhdGVTdW1tYXJ5KHtcbiAgICAgIFN0YWNrTmFtZTogc3RhY2ssXG4gICAgfSk7XG5cbiAgICBpZiAoYm9vdHN0cmFwRmlsdGVyKHN1bW1hcnkuUGFyYW1ldGVycywgcXVhbGlmaWVyKSkge1xuICAgICAgLy8gVGhpcyBzdGFjayBpcyBkZWZpbml0ZWx5IGJvb3RzdHJhcHBlZCB0byBhIGRpZmZlcmVudCBxdWFsaWZpZXIgc28gd2UgY2FuIHNhZmVseSBpZ25vcmUgaXRcbiAgICAgIGNvbnRpbnVlO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCB0ZW1wbGF0ZSA9IGF3YWl0IGNmbi5nZXRUZW1wbGF0ZSh7XG4gICAgICAgIFN0YWNrTmFtZTogc3RhY2ssXG4gICAgICB9KTtcblxuICAgICAgdGVtcGxhdGVzLnB1c2goKHRlbXBsYXRlLlRlbXBsYXRlQm9keSA/PyAnJykgKyBKU09OLnN0cmluZ2lmeShzdW1tYXJ5Py5QYXJhbWV0ZXJzKSk7XG4gICAgfVxuICB9XG5cbiAgYXdhaXQgaW9IZWxwZXIubm90aWZ5KElPLkRFRkFVTFRfVE9PTEtJVF9ERUJVRy5tc2coJ0RvbmUgcGFyc2luZyB0aHJvdWdoIHN0YWNrcycpKTtcblxuICByZXR1cm4gdGVtcGxhdGVzO1xufVxuXG4vKipcbiAqIEZpbHRlciBvdXQgc3RhY2tzIHRoYXQgd2UgS05PVyBhcmUgdXNpbmcgYSBkaWZmZXJlbnQgYm9vdHN0cmFwIHF1YWxpZmllclxuICogVGhpcyBpcyBtb3N0bHkgbmVjZXNzYXJ5IGZvciB0aGUgaW50ZWdyYXRpb24gdGVzdHMgdGhhdCBjYW4gcnVuIHRoZSBzYW1lIGFwcCAod2l0aCB0aGUgc2FtZSBhc3NldHMpXG4gKiB1bmRlciBkaWZmZXJlbnQgcXVhbGlmaWVycy5cbiAqIFRoaXMgaXMgbmVjZXNzYXJ5IGJlY2F1c2UgYSBzdGFjayB1bmRlciBhIGRpZmZlcmVudCBib290c3RyYXAgY291bGQgY29pbmNpZGVudGFsbHkgcmVmZXJlbmNlIHRoZSBzYW1lIGhhc2hcbiAqIGFuZCBjYXVzZSBhIGZhbHNlIG5lZ2F0aXZlIChjYXVzZSBhbiBhc3NldCB0byBiZSBwcmVzZXJ2ZWQgd2hlbiBpdHMgaXNvbGF0ZWQpXG4gKiBUaGlzIGlzIGludGVudGlvbmFsbHkgZG9uZSBpbiBhIHdheSB3aGVyZSB3ZSBPTkxZIGZpbHRlciBvdXQgc3RhY2tzIHRoYXQgYXJlIG1lYW50IGZvciBhIGRpZmZlcmVudCBxdWFsaWZpZXJcbiAqIGJlY2F1c2Ugd2UgYXJlIG9rYXkgd2l0aCBmYWxzZSBwb3NpdGl2ZXMuXG4gKi9cbmZ1bmN0aW9uIGJvb3RzdHJhcEZpbHRlcihwYXJhbWV0ZXJzPzogUGFyYW1ldGVyRGVjbGFyYXRpb25bXSwgcXVhbGlmaWVyPzogc3RyaW5nKSB7XG4gIGNvbnN0IGJvb3RzdHJhcFZlcnNpb24gPSBwYXJhbWV0ZXJzPy5maW5kKChwKSA9PiBwLlBhcmFtZXRlcktleSA9PT0gJ0Jvb3RzdHJhcFZlcnNpb24nKTtcbiAgY29uc3Qgc3BsaXRCb290c3RyYXBWZXJzaW9uID0gYm9vdHN0cmFwVmVyc2lvbj8uRGVmYXVsdFZhbHVlPy5zcGxpdCgnLycpO1xuICAvLyBXZSBmaW5kIHRoZSBxdWFsaWZpZXIgaW4gYSBzcGVjaWZpYyBwYXJ0IG9mIHRoZSBib290c3RyYXAgdmVyc2lvbiBwYXJhbWV0ZXJcbiAgcmV0dXJuIChxdWFsaWZpZXIgJiZcbiAgICAgICAgICBzcGxpdEJvb3RzdHJhcFZlcnNpb24gJiZcbiAgICAgICAgICBzcGxpdEJvb3RzdHJhcFZlcnNpb24ubGVuZ3RoID09IDQgJiZcbiAgICAgICAgICBzcGxpdEJvb3RzdHJhcFZlcnNpb25bMl0gIT0gcXVhbGlmaWVyKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlZnJlc2hTdGFja3MoY2ZuOiBJQ2xvdWRGb3JtYXRpb25DbGllbnQsIGlvSGVscGVyOiBJb0hlbHBlciwgYWN0aXZlQXNzZXRzOiBBY3RpdmVBc3NldENhY2hlLCBxdWFsaWZpZXI/OiBzdHJpbmcpIHtcbiAgdHJ5IHtcbiAgICBjb25zdCBzdGFja3MgPSBhd2FpdCBmZXRjaEFsbFN0YWNrVGVtcGxhdGVzKGNmbiwgaW9IZWxwZXIsIHF1YWxpZmllcik7XG4gICAgZm9yIChjb25zdCBzdGFjayBvZiBzdGFja3MpIHtcbiAgICAgIGFjdGl2ZUFzc2V0cy5yZW1lbWJlclN0YWNrKHN0YWNrKTtcbiAgICB9XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHRocm93IG5ldyBUb29sa2l0RXJyb3IoYEVycm9yIHJlZnJlc2hpbmcgc3RhY2tzOiAke2Vycn1gKTtcbiAgfVxufVxuXG4vKipcbiAqIEJhY2tncm91bmQgU3RhY2sgUmVmcmVzaCBwcm9wZXJ0aWVzXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQmFja2dyb3VuZFN0YWNrUmVmcmVzaFByb3BzIHtcbiAgLyoqXG4gICAqIFRoZSBDRk4gU0RLIGhhbmRsZXJcbiAgICovXG4gIHJlYWRvbmx5IGNmbjogSUNsb3VkRm9ybWF0aW9uQ2xpZW50O1xuXG4gIC8qKlxuICAgKiBVc2VkIHRvIHNlbmQgbWVzc2FnZXMuXG4gICAqL1xuICByZWFkb25seSBpb0hlbHBlcjogSW9IZWxwZXI7XG5cbiAgLyoqXG4gICAqIEFjdGl2ZSBBc3NldCBzdG9yYWdlXG4gICAqL1xuICByZWFkb25seSBhY3RpdmVBc3NldHM6IEFjdGl2ZUFzc2V0Q2FjaGU7XG5cbiAgLyoqXG4gICAqIFN0YWNrIGJvb3RzdHJhcCBxdWFsaWZpZXJcbiAgICovXG4gIHJlYWRvbmx5IHF1YWxpZmllcj86IHN0cmluZztcbn1cblxuLyoqXG4gKiBDbGFzcyB0aGF0IGNvbnRyb2xzIHNjaGVkdWxpbmcgb2YgdGhlIGJhY2tncm91bmQgc3RhY2sgcmVmcmVzaFxuICovXG5leHBvcnQgY2xhc3MgQmFja2dyb3VuZFN0YWNrUmVmcmVzaCB7XG4gIHByaXZhdGUgdGltZW91dD86IE5vZGVKUy5UaW1lb3V0O1xuICBwcml2YXRlIGxhc3RSZWZyZXNoVGltZTogbnVtYmVyO1xuICBwcml2YXRlIHF1ZXVlZFByb21pc2VzOiBBcnJheTwodmFsdWU6IHVua25vd24pID0+IHZvaWQ+ID0gW107XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBwcm9wczogQmFja2dyb3VuZFN0YWNrUmVmcmVzaFByb3BzKSB7XG4gICAgdGhpcy5sYXN0UmVmcmVzaFRpbWUgPSBEYXRlLm5vdygpO1xuICB9XG5cbiAgcHVibGljIHN0YXJ0KCkge1xuICAgIC8vIFNpbmNlIHN0YXJ0IGlzIGdvaW5nIHRvIGJlIGNhbGxlZCByaWdodCBhZnRlciB0aGUgZmlyc3QgaW52b2NhdGlvbiBvZiByZWZyZXNoU3RhY2tzLFxuICAgIC8vIGxldHMgd2FpdCBzb21lIHRpbWUgYmVmb3JlIGJlZ2lubmluZyB0aGUgYmFja2dyb3VuZCByZWZyZXNoLlxuICAgIHRoaXMudGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4gdGhpcy5yZWZyZXNoKCksIDMwMF8wMDApOyAvLyA1IG1pbnV0ZXNcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVmcmVzaCgpIHtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuXG4gICAgYXdhaXQgcmVmcmVzaFN0YWNrcyh0aGlzLnByb3BzLmNmbiwgdGhpcy5wcm9wcy5pb0hlbHBlciwgdGhpcy5wcm9wcy5hY3RpdmVBc3NldHMsIHRoaXMucHJvcHMucXVhbGlmaWVyKTtcbiAgICB0aGlzLmp1c3RSZWZyZXNoZWRTdGFja3MoKTtcblxuICAgIC8vIElmIHRoZSBsYXN0IGludm9jYXRpb24gb2YgcmVmcmVzaFN0YWNrcyB0YWtlcyA8NSBtaW51dGVzLCB0aGUgbmV4dCBpbnZvY2F0aW9uIHN0YXJ0cyA1IG1pbnV0ZXMgYWZ0ZXIgdGhlIGxhc3Qgb25lIHN0YXJ0ZWQuXG4gICAgLy8gSWYgdGhlIGxhc3QgaW52b2NhdGlvbiBvZiByZWZyZXNoU3RhY2tzIHRha2VzID41IG1pbnV0ZXMsIHRoZSBuZXh0IGludm9jYXRpb24gc3RhcnRzIGltbWVkaWF0ZWx5LlxuICAgIHRoaXMudGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4gdGhpcy5yZWZyZXNoKCksIE1hdGgubWF4KHN0YXJ0VGltZSArIDMwMF8wMDAgLSBEYXRlLm5vdygpLCAwKSk7XG4gIH1cblxuICBwcml2YXRlIGp1c3RSZWZyZXNoZWRTdGFja3MoKSB7XG4gICAgdGhpcy5sYXN0UmVmcmVzaFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGZvciAoY29uc3QgcCBvZiB0aGlzLnF1ZXVlZFByb21pc2VzLnNwbGljZSgwLCB0aGlzLnF1ZXVlZFByb21pc2VzLmxlbmd0aCkpIHtcbiAgICAgIHAodW5kZWZpbmVkKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHRoZSBsYXN0IHN1Y2Nlc3NmdWwgYmFja2dyb3VuZCByZWZyZXNoIGhhcHBlbmVkIHdpdGhpbiB0aGUgc3BlY2lmaWVkIHRpbWUgZnJhbWUuXG4gICAqIElmIHRoZSBsYXN0IHJlZnJlc2ggaXMgb2xkZXIgdGhhbiB0aGUgc3BlY2lmaWVkIHRpbWUgZnJhbWUsIGl0IHJldHVybnMgYSBQcm9taXNlIHRoYXQgcmVzb2x2ZXNcbiAgICogd2hlbiB0aGUgbmV4dCBiYWNrZ3JvdW5kIHJlZnJlc2ggY29tcGxldGVzIG9yIHJlamVjdHMgaWYgdGhlIHJlZnJlc2ggdGFrZXMgdG9vIGxvbmcuXG4gICAqL1xuICBwdWJsaWMgbm9PbGRlclRoYW4obXM6IG51bWJlcikge1xuICAgIGNvbnN0IGhvcml6b24gPSBEYXRlLm5vdygpIC0gbXM7XG5cbiAgICAvLyBUaGUgbGFzdCByZWZyZXNoIGhhcHBlbmVkIHdpdGhpbiB0aGUgdGltZSBmcmFtZVxuICAgIGlmICh0aGlzLmxhc3RSZWZyZXNoVGltZSA+PSBob3Jpem9uKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfVxuXG4gICAgLy8gVGhlIGxhc3QgcmVmcmVzaCBoYXBwZW5lZCBlYXJsaWVyIHRoYW4gdGhlIHRpbWUgZnJhbWVcbiAgICAvLyBXZSB3aWxsIHdhaXQgZm9yIHRoZSBsYXRlc3QgcmVmcmVzaCB0byBsYW5kIG9yIHJlamVjdCBpZiBpdCB0YWtlcyB0b28gbG9uZ1xuICAgIHJldHVybiBQcm9taXNlLnJhY2UoW1xuICAgICAgbmV3IFByb21pc2UocmVzb2x2ZSA9PiB0aGlzLnF1ZXVlZFByb21pc2VzLnB1c2gocmVzb2x2ZSkpLFxuICAgICAgbmV3IFByb21pc2UoKF8sIHJlamVjdCkgPT4gc2V0VGltZW91dCgoKSA9PiByZWplY3QobmV3IFRvb2xraXRFcnJvcigncmVmcmVzaFN0YWNrcyB0b29rIHRvbyBsb25nOyB0aGUgYmFja2dyb3VuZCB0aHJlYWQgbGlrZWx5IHRocmV3IGFuIGVycm9yJykpLCBtcykpLFxuICAgIF0pO1xuICB9XG5cbiAgcHVibGljIHN0b3AoKSB7XG4gICAgY2xlYXJUaW1lb3V0KHRoaXMudGltZW91dCk7XG4gIH1cbn1cbiJdfQ==