@rudderstack/integrations-lib
Version:
A comprehensive TypeScript library providing shared utilities, SDKs, and tools for RudderStack integrations and destinations.
288 lines ⢠34.4 kB
JavaScript
;
/**
* Benchmark utilities for performance testing
*
* Provides easy-to-use tools for measuring function performance,
* memory usage, and creating performance comparisons.
*
* @example
* ```typescript
* import { benchmark, createBenchmarkSuite } from './benchmark';
*
* // Simple benchmark
* const result = await benchmark('myFunction', () => myFunction(data));
* console.log(`Duration: ${result.duration}ms`);
*
* // Benchmark suite
* const suite = createBenchmarkSuite('Array Operations');
* suite.add('native map', () => data.map(x => x * 2));
* suite.add('custom map', () => customMap(data, x => x * 2));
* await suite.run();
* ```
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BenchmarkSuite = void 0;
exports.benchmark = benchmark;
exports.compareBenchmarks = compareBenchmarks;
exports.createBenchmarkSuite = createBenchmarkSuite;
exports.measureMemory = measureMemory;
/**
* Force garbage collection if available
*/
function forceGarbageCollection() {
if (typeof global !== 'undefined' && global.gc) {
global.gc();
}
}
/**
* Calculate standard deviation of an array of numbers
*/
function calculateStdDev(values) {
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
const squaredDiffs = values.map((val) => (val - mean) ** 2);
const avgSquaredDiff = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;
return Math.sqrt(avgSquaredDiff);
}
/**
* Run a benchmark for a single function
*
* @param name - Name of the benchmark
* @param fn - Function to benchmark
* @param options - Benchmark configuration options
* @returns Promise resolving to benchmark results
*
* @example
* ```typescript
* const result = await benchmark('Array.map', () => {
* return largeArray.map(x => x * 2);
* }, { iterations: 10, warmupRuns: 3 });
*
* console.log(`${result.name}: ${result.avgDuration.toFixed(2)}ms avg`);
* ```
*/
// eslint-disable-next-line require-await
async function benchmark(name, fn, options = {}) {
const { iterations = 1, warmupRuns = 1, forceGC = false, measureMemory: shouldMeasureMemory = true, timeout = 30000, } = options;
// Timeout protection
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => reject(new Error(`Benchmark "${name}" timed out after ${timeout}ms`)), timeout);
});
const benchmarkPromise = async () => {
// Warmup runs
await Promise.all(Array.from({ length: warmupRuns }, async () => {
await fn();
}));
if (forceGC) {
forceGarbageCollection();
}
const durations = [];
const startMemory = shouldMeasureMemory ? process.memoryUsage().heapUsed : 0;
const overallStart = performance.now();
// Actual benchmark runs
for (let i = 0; i < iterations; i += 1) {
if (forceGC && i > 0) {
forceGarbageCollection();
}
const iterationStart = performance.now();
// eslint-disable-next-line no-await-in-loop
await fn();
const iterationEnd = performance.now();
durations.push(iterationEnd - iterationStart);
}
const overallEnd = performance.now();
const endMemory = shouldMeasureMemory ? process.memoryUsage().heapUsed : 0;
const totalDuration = overallEnd - overallStart;
const avgDuration = durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / durations.length : 0;
const minDuration = durations.length > 0 ? Math.min(...durations) : 0;
const maxDuration = durations.length > 0 ? Math.max(...durations) : 0;
const stdDev = durations.length > 0 ? calculateStdDev(durations) : 0;
const opsPerSecond = totalDuration > 0 ? iterations / (totalDuration / 1000) : 0;
const memoryUsed = endMemory - startMemory;
return {
name,
duration: totalDuration,
opsPerSecond,
memoryUsed,
iterations,
avgDuration,
stdDev,
minDuration,
maxDuration,
};
};
return Promise.race([benchmarkPromise(), timeoutPromise]).finally(() => {
clearTimeout(timeoutId);
});
}
/**
* Compare multiple functions with the same benchmark configuration
*
* @param benchmarks - Object with benchmark names and functions
* @param options - Benchmark configuration options
* @returns Promise resolving to array of benchmark results
*
* @example
* ```typescript
* const results = await compareBenchmarks({
* 'native map': () => data.map(x => x * 2),
* 'for loop': () => {
* const result = [];
* for (let i = 0; i < data.length; i++) {
* result.push(data[i] * 2);
* }
* return result;
* }
* }, { iterations: 5 });
*
* results.forEach(result => {
* console.log(`${result.name}: ${result.avgDuration.toFixed(2)}ms`);
* });
* ```
*/
async function compareBenchmarks(benchmarks, options = {}) {
const results = await Promise.all(Object.entries(benchmarks).map(([name, fn]) => benchmark(name, fn, options)));
return results.sort((a, b) => a.avgDuration - b.avgDuration);
}
/**
* Benchmark suite for organizing and running multiple related benchmarks
*/
class BenchmarkSuite {
constructor(suiteName, options = {}) {
this.suiteName = suiteName;
this.benchmarks = [];
this.options = options;
}
/**
* Add a benchmark to the suite
*
* @param name - Name of the benchmark
* @param fn - Function to benchmark
* @returns This suite instance for chaining
*/
add(name, fn) {
this.benchmarks.push({ name, fn });
return this;
}
/**
* Run all benchmarks in the suite
*
* @param customOptions - Override suite options for this run
* @returns Promise resolving to array of benchmark results
*/
async run(customOptions) {
const options = { ...this.options, ...customOptions };
const results = [];
// eslint-disable-next-line no-console
console.log(`\nš Running benchmark suite: ${this.suiteName}`);
// eslint-disable-next-line no-console
console.log('='.repeat(60));
// eslint-disable-next-line no-restricted-syntax
for (const { name, fn } of this.benchmarks) {
// eslint-disable-next-line no-console
console.log(`\nā±ļø Running: ${name}...`);
try {
// eslint-disable-next-line no-await-in-loop
const result = await benchmark(name, fn, options);
results.push(result);
// eslint-disable-next-line no-console
console.log(` ā
${result.avgDuration.toFixed(2)}ms avg (${result.opsPerSecond.toFixed(0)} ops/sec)`);
if (options.measureMemory) {
// eslint-disable-next-line no-console
console.log(` š¾ Memory: ${(result.memoryUsed / 1024 / 1024).toFixed(2)}MB`);
}
}
catch (error) {
// eslint-disable-next-line no-console
console.log(` ā Failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Summary
if (results.length > 1) {
// eslint-disable-next-line no-console
console.log(`\nš Summary (sorted by performance):`);
const sortedResults = [...results].sort((a, b) => a.avgDuration - b.avgDuration);
const fastest = sortedResults[0];
sortedResults.forEach((result, index) => {
const relative = result.avgDuration / fastest.avgDuration;
let icon = ' ';
if (index === 0)
icon = 'š„';
else if (index === 1)
icon = 'š„';
else if (index === 2)
icon = 'š„';
// eslint-disable-next-line no-console
console.log(` ${icon} ${result.name.padEnd(25)} | ${result.avgDuration.toFixed(2)}ms | ${relative.toFixed(2)}x`);
});
}
// eslint-disable-next-line no-console
console.log(`\nā
Suite completed: ${results.length}/${this.benchmarks.length} benchmarks successful\n`);
return results;
}
/**
* Get the number of benchmarks in the suite
*/
get size() {
return this.benchmarks.length;
}
/**
* Clear all benchmarks from the suite
*/
clear() {
this.benchmarks = [];
return this;
}
}
exports.BenchmarkSuite = BenchmarkSuite;
/**
* Create a new benchmark suite
*
* @param suiteName - Name of the benchmark suite
* @param options - Default options for all benchmarks in the suite
* @returns New BenchmarkSuite instance
*
* @example
* ```typescript
* const suite = createBenchmarkSuite('String Operations', {
* iterations: 10,
* warmupRuns: 2
* });
*
* suite
* .add('concat', () => str1 + str2)
* .add('template', () => `${str1}${str2}`)
* .add('join', () => [str1, str2].join(''));
*
* await suite.run();
* ```
*/
function createBenchmarkSuite(suiteName, options) {
return new BenchmarkSuite(suiteName, options);
}
/**
* Utility to measure memory usage of a function
*
* @param fn - Function to measure
* @param forceGC - Whether to force garbage collection before measuring
* @returns Memory usage in bytes
*
* @example
* ```typescript
* const memoryUsed = await measureMemory(() => {
* return new Array(100000).fill(0).map(x => ({ value: x }));
* });
*
* console.log(`Memory used: ${(memoryUsed / 1024 / 1024).toFixed(2)}MB`);
* ```
*/
async function measureMemory(fn, forceGC = true) {
if (forceGC) {
forceGarbageCollection();
}
const startMemory = process.memoryUsage().heapUsed;
await fn();
const endMemory = process.memoryUsage().heapUsed;
return endMemory - startMemory;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmVuY2htYXJrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2JlbmNobWFyay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHOzs7QUFvRkgsOEJBZ0ZDO0FBMkJELDhDQVNDO0FBd0lELG9EQUtDO0FBa0JELHNDQVVDO0FBbFVEOztHQUVHO0FBQ0gsU0FBUyxzQkFBc0I7SUFDN0IsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLElBQUksTUFBTSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQy9DLE1BQU0sQ0FBQyxFQUFFLEVBQUUsQ0FBQztJQUNkLENBQUM7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLGVBQWUsQ0FBQyxNQUFnQjtJQUN2QyxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUMsR0FBRyxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ3ZFLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQzVELE1BQU0sY0FBYyxHQUFHLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsR0FBRyxFQUFFLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDdkYsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0FBQ25DLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILHlDQUF5QztBQUNsQyxLQUFLLFVBQVUsU0FBUyxDQUM3QixJQUFZLEVBQ1osRUFBcUIsRUFDckIsVUFBNEIs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QUksY0FBYyxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNoRCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0ksS0FBSyxVQUFVLGFBQWEsQ0FBQyxFQUFxQixFQUFFLE9BQU8sR0FBRyxJQUFJO0lBQ3ZFLElBQUksT0FBTyxFQUFFLENBQUM7UUFDWixzQkFBc0IsRUFBRSxDQUFDO0lBQzNCLENBQUM7SUFFRCxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDO0lBQ25ELE1BQU0sRUFBRSxFQUFFLENBQUM7SUFDWCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDO0lBRWpELE9BQU8sU0FBUyxHQUFHLFdBQVcsQ0FBQztBQUNqQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBCZW5jaG1hcmsgdXRpbGl0aWVzIGZvciBwZXJmb3JtYW5jZSB0ZXN0aW5nXG4gKlxuICogUHJvdmlkZXMgZWFzeS10by11c2UgdG9vbHMgZm9yIG1lYXN1cmluZyBmdW5jdGlvbiBwZXJmb3JtYW5jZSxcbiAqIG1lbW9yeSB1c2FnZSwgYW5kIGNyZWF0aW5nIHBlcmZvcm1hbmNlIGNvbXBhcmlzb25zLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBiZW5jaG1hcmssIGNyZWF0ZUJlbmNobWFya1N1aXRlIH0gZnJvbSAnLi9iZW5jaG1hcmsnO1xuICpcbiAqIC8vIFNpbXBsZSBiZW5jaG1hcmtcbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGJlbmNobWFyaygnbXlGdW5jdGlvbicsICgpID0+IG15RnVuY3Rpb24oZGF0YSkpO1xuICogY29uc29sZS5sb2coYER1cmF0aW9uOiAke3Jlc3VsdC5kdXJhdGlvbn1tc2ApO1xuICpcbiAqIC8vIEJlbmNobWFyayBzdWl0ZVxuICogY29uc3Qgc3VpdGUgPSBjcmVhdGVCZW5jaG1hcmtTdWl0ZSgnQXJyYXkgT3BlcmF0aW9ucycpO1xuICogc3VpdGUuYWRkKCduYXRpdmUgbWFwJywgKCkgPT4gZGF0YS5tYXAoeCA9PiB4ICogMikpO1xuICogc3VpdGUuYWRkKCdjdXN0b20gbWFwJywgKCkgPT4gY3VzdG9tTWFwKGRhdGEsIHggPT4geCAqIDIpKTtcbiAqIGF3YWl0IHN1aXRlLnJ1bigpO1xuICogYGBgXG4gKi9cblxuLyoqXG4gKiBSZXN1bHQgb2YgYSBiZW5jaG1hcmsgbWVhc3VyZW1lbnRcbiAqL1xuZXhwb3J0IHR5cGUgQmVuY2htYXJrUmVzdWx0ID0ge1xuICAvKiogTmFtZSBvZiB0aGUgYmVuY2htYXJrICovXG4gIG5hbWU6IHN0cmluZztcbiAgLyoqIFRvdGFsIGR1cmF0aW9uIGluIG1pbGxpc2Vjb25kcyAqL1xuICBkdXJhdGlvbjogbnVtYmVyO1xuICAvKiogT3BlcmF0aW9ucyBwZXIgc2Vjb25kICovXG4gIG9wc1BlclNlY29uZDogbnVtYmVyO1xuICAvKiogTWVtb3J5IHVzZWQgaW4gYnl0ZXMgKGhlYXAgZGlmZmVyZW5jZSkgKi9cbiAgbWVtb3J5VXNlZDogbnVtYmVyO1xuICAvKiogTnVtYmVyIG9mIGl0ZXJhdGlvbnMgcnVuICovXG4gIGl0ZXJhdGlvbnM6IG51bWJlcjtcbiAgLyoqIEF2ZXJhZ2UgZHVyYXRpb24gcGVyIGl0ZXJhdGlvbiBpbiBtaWxsaXNlY29uZHMgKi9cbiAgYXZnRHVyYXRpb246IG51bWJlcjtcbiAgLyoqIFN0YW5kYXJkIGRldmlhdGlvbiBvZiBkdXJhdGlvbnMgKi9cbiAgc3RkRGV2OiBudW1iZXI7XG4gIC8qKiBNaW5pbXVtIGR1cmF0aW9uIG9ic2VydmVkICovXG4gIG1pbkR1cmF0aW9uOiBudW1iZXI7XG4gIC8qKiBNYXhpbXVtIGR1cmF0aW9uIG9ic2VydmVkICovXG4gIG1heER1cmF0aW9uOiBudW1iZXI7XG59O1xuXG4vKipcbiAqIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgYmVuY2htYXJrc1xuICovXG5leHBvcnQgdHlwZSBCZW5jaG1hcmtPcHRpb25zID0ge1xuICAvKiogTnVtYmVyIG9mIGl0ZXJhdGlvbnMgdG8gcnVuIChkZWZhdWx0OiAxKSAqL1xuICBpdGVyYXRpb25zPzogbnVtYmVyO1xuICAvKiogTnVtYmVyIG9mIHdhcm11cCBydW5zIGJlZm9yZSBtZWFzdXJpbmcgKGRlZmF1bHQ6IDEpICovXG4gIHdhcm11cFJ1bnM/OiBudW1iZXI7XG4gIC8qKiBXaGV0aGVyIHRvIGZvcmNlIGdhcmJhZ2UgY29sbGVjdGlvbiBiZXR3ZWVuIHJ1bnMgKGRlZmF1bHQ6IGZhbHNlKSAqL1xuICBmb3JjZUdDPzogYm9vbGVhbjtcbiAgLyoqIFdoZXRoZXIgdG8gbWVhc3VyZSBtZW1vcnkgdXNhZ2UgKGRlZmF1bHQ6IHRydWUpICovXG4gIG1lYXN1cmVNZW1vcnk/OiBib29sZWFuO1xuICAvKiogVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIHRoZSBlbnRpcmUgYmVuY2htYXJrIChkZWZhdWx0OiAzMDAwMCkgKi9cbiAgdGltZW91dD86IG51bWJlcjtcbn07XG5cbi8qKlxuICogQmVuY2htYXJrIGZ1bmN0aW9uIHR5cGVcbiAqL1xuZXhwb3J0IHR5cGUgQmVuY2htYXJrRnVuY3Rpb248VCA9IHVua25vd24+ID0gKCkgPT4gVCB8IFByb21pc2U8VD47XG5cbi8qKlxuICogRm9yY2UgZ2FyYmFnZSBjb2xsZWN0aW9uIGlmIGF2YWlsYWJsZVxuICovXG5mdW5jdGlvbiBmb3JjZUdhcmJhZ2VDb2xsZWN0aW9uKCk6IHZvaWQge1xuICBpZiAodHlwZW9mIGdsb2JhbCAhPT0gJ3VuZGVmaW5lZCcgJiYgZ2xvYmFsLmdjKSB7XG4gICAgZ2xvYmFsLmdjKCk7XG4gIH1cbn1cblxuLyoqXG4gKiBDYWxjdWxhdGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGFuIGFycmF5IG9mIG51bWJlcnNcbiAqL1xuZnVuY3Rpb24gY2FsY3VsYXRlU3RkRGV2KHZhbHVlczogbnVtYmVyW10pOiBudW1iZXIge1xuICBjb25zdCBtZWFuID0gdmFsdWVzLnJlZHVjZSgoc3VtLCB2YWwpID0+IHN1bSArIHZhbCwgMCkgLyB2YWx1ZXMubGVuZ3RoO1xuICBjb25zdCBzcXVhcmVkRGlmZnMgPSB2YWx1ZXMubWFwKCh2YWwpID0+ICh2YWwgLSBtZWFuKSAqKiAyKTtcbiAgY29uc3QgYXZnU3F1YXJlZERpZmYgPSBzcXVhcmVkRGlmZnMucmVkdWNlKChzdW0sIHZhbCkgPT4gc3VtICsgdmFsLCAwKSAvIHZhbHVlcy5sZW5ndGg7XG4gIHJldHVybiBNYXRoLnNxcnQoYXZnU3F1YXJlZERpZmYpO1xufVxuXG4vKipcbiAqIFJ1biBhIGJlbmNobWFyayBmb3IgYSBzaW5nbGUgZnVuY3Rpb25cbiAqXG4gKiBAcGFyYW0gbmFtZSAtIE5hbWUgb2YgdGhlIGJlbmNobWFya1xuICogQHBhcmFtIGZuIC0gRnVuY3Rpb24gdG8gYmVuY2htYXJrXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEJlbmNobWFyayBjb25maWd1cmF0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIGJlbmNobWFyayByZXN1bHRzXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGJlbmNobWFyaygnQXJyYXkubWFwJywgKCkgPT4ge1xuICogICByZXR1cm4gbGFyZ2VBcnJheS5tYXAoeCA9PiB4ICogMik7XG4gKiB9LCB7IGl0ZXJhdGlvbnM6IDEwLCB3YXJtdXBSdW5zOiAzIH0pO1xuICpcbiAqIGNvbnNvbGUubG9nKGAke3Jlc3VsdC5uYW1lfTogJHtyZXN1bHQuYXZnRHVyYXRpb24udG9GaXhlZCgyKX1tcyBhdmdgKTtcbiAqIGBgYFxuICovXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVxdWlyZS1hd2FpdFxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJlbmNobWFyayhcbiAgbmFtZTogc3RyaW5nLFxuICBmbjogQmVuY2htYXJrRnVuY3Rpb24sXG4gIG9wdGlvbnM6IEJlbmNobWFya09wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8QmVuY2htYXJrUmVzdWx0PiB7XG4gIGNvbnN0IHtcbiAgICBpdGVyYXRpb25zID0gMSxcbiAgICB3YXJtdXBSdW5zID0gMSxcbiAgICBmb3JjZUdDID0gZmFsc2UsXG4gICAgbWVhc3VyZU1lbW9yeTogc2hvdWxkTWVhc3VyZU1lbW9yeSA9IHRydWUsXG4gICAgdGltZW91dCA9IDMwMDAwLFxuICB9ID0gb3B0aW9ucztcblxuICAvLyBUaW1lb3V0IHByb3RlY3Rpb25cbiAgbGV0IHRpbWVvdXRJZDogTm9kZUpTLlRpbWVvdXQ7XG4gIGNvbnN0IHRpbWVvdXRQcm9taXNlID0gbmV3IFByb21pc2U8bmV2ZXI+KChfLCByZWplY3QpID0+IHtcbiAgICB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KFxuICAgICAgKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihgQmVuY2htYXJrIFwiJHtuYW1lfVwiIHRpbWVkIG91dCBhZnRlciAke3RpbWVvdXR9bXNgKSksXG4gICAgICB0aW1lb3V0LFxuICAgICk7XG4gIH0pO1xuXG4gIGNvbnN0IGJlbmNobWFya1Byb21pc2UgPSBhc3luYyAoKTogUHJvbWlzZTxCZW5jaG1hcmtSZXN1bHQ+ID0+IHtcbiAgICAvLyBXYXJtdXAgcnVuc1xuICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgQXJyYXkuZnJvbSh7IGxlbmd0aDogd2FybXVwUnVucyB9LCBhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IGZuKCk7XG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgaWYgKGZvcmNlR0MpIHtcbiAgICAgIGZvcmNlR2FyYmFnZUNvbGxlY3Rpb24oKTtcbiAgICB9XG5cbiAgICBjb25zdCBkdXJhdGlvbnM6IG51bWJlcltdID0gW107XG4gICAgY29uc3Qgc3RhcnRNZW1vcnkgPSBzaG91bGRNZWFzdXJlTWVtb3J5ID8gcHJvY2Vzcy5tZW1vcnlVc2FnZSgpLmhlYXBVc2VkIDogMDtcbiAgICBjb25zdCBvdmVyYWxsU3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcblxuICAgIC8vIEFjdHVhbCBiZW5jaG1hcmsgcnVuc1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaXRlcmF0aW9uczsgaSArPSAxKSB7XG4gICAgICBpZiAoZm9yY2VHQyAmJiBpID4gMCkge1xuICAgICAgICBmb3JjZUdhcmJhZ2VDb2xsZWN0aW9uKCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGl0ZXJhdGlvblN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tYXdhaXQtaW4tbG9vcFxuICAgICAgYXdhaXQgZm4oKTtcbiAgICAgIGNvbnN0IGl0ZXJhdGlvbkVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gICAgICBkdXJhdGlvbnMucHVzaChpdGVyYXRpb25FbmQgLSBpdGVyYXRpb25TdGFydCk7XG4gICAgfVxuXG4gICAgY29uc3Qgb3ZlcmFsbEVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIGNvbnN0IGVuZE1lbW9yeSA9IHNob3VsZE1lYXN1cmVNZW1vcnkgPyBwcm9jZXNzLm1lbW9yeVVzYWdlKCkuaGVhcFVzZWQgOiAwO1xuXG4gICAgY29uc3QgdG90YWxEdXJhdGlvbiA9IG92ZXJhbGxFbmQgLSBvdmVyYWxsU3RhcnQ7XG4gICAgY29uc3QgYXZnRHVyYXRpb24gPVxuICAgICAgZHVyYXRpb25zLmxlbmd0aCA+IDAgPyBkdXJhdGlvbnMucmVkdWNlKChzdW0sIGQpID0+IHN1bSArIGQsIDApIC8gZHVyYXRpb25zLmxlbmd0aCA6IDA7XG4gICAgY29uc3QgbWluRHVyYXRpb24gPSBkdXJhdGlvbnMubGVuZ3RoID4gMCA/IE1hdGgubWluKC4uLmR1cmF0aW9ucykgOiAwO1xuICAgIGNvbnN0IG1heER1cmF0aW9uID0gZHVyYXRpb25zLmxlbmd0aCA+IDAgPyBNYXRoLm1heCguLi5kdXJhdGlvbnMpIDogMDtcbiAgICBjb25zdCBzdGREZXYgPSBkdXJhdGlvbnMubGVuZ3RoID4gMCA/IGNhbGN1bGF0ZVN0ZERldihkdXJhdGlvbnMpIDogMDtcbiAgICBjb25zdCBvcHNQZXJTZWNvbmQgPSB0b3RhbER1cmF0aW9uID4gMCA/IGl0ZXJhdGlvbnMgLyAodG90YWxEdXJhdGlvbiAvIDEwMDApIDogMDtcbiAgICBjb25zdCBtZW1vcnlVc2VkID0gZW5kTWVtb3J5IC0gc3RhcnRNZW1vcnk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgbmFtZSxcbiAgICAgIGR1cmF0aW9uOiB0b3RhbER1cmF0aW9uLFxuICAgICAgb3BzUGVyU2Vjb25kLFxuICAgICAgbWVtb3J5VXNlZCxcbiAgICAgIGl0ZXJhdGlvbnMsXG4gICAgICBhdmdEdXJhdGlvbixcbiAgICAgIHN0ZERldixcbiAgICAgIG1pbkR1cmF0aW9uLFxuICAgICAgbWF4RHVyYXRpb24sXG4gICAgfTtcbiAgfTtcblxuICByZXR1cm4gUHJvbWlzZS5yYWNlKFtiZW5jaG1hcmtQcm9taXNlKCksIHRpbWVvdXRQcm9taXNlXSkuZmluYWxseSgoKSA9PiB7XG4gICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gIH0pO1xufVxuXG4vKipcbiAqIENvbXBhcmUgbXVsdGlwbGUgZnVuY3Rpb25zIHdpdGggdGhlIHNhbWUgYmVuY2htYXJrIGNvbmZpZ3VyYXRpb25cbiAqXG4gKiBAcGFyYW0gYmVuY2htYXJrcyAtIE9iamVjdCB3aXRoIGJlbmNobWFyayBuYW1lcyBhbmQgZnVuY3Rpb25zXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEJlbmNobWFyayBjb25maWd1cmF0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIGFycmF5IG9mIGJlbmNobWFyayByZXN1bHRzXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBjb21wYXJlQmVuY2htYXJrcyh7XG4gKiAgICduYXRpdmUgbWFwJzogKCkgPT4gZGF0YS5tYXAoeCA9PiB4ICogMiksXG4gKiAgICdmb3IgbG9vcCc6ICgpID0+IHtcbiAqICAgICBjb25zdCByZXN1bHQgPSBbXTtcbiAqICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGRhdGEubGVuZ3RoOyBpKyspIHtcbiAqICAgICAgIHJlc3VsdC5wdXNoKGRhdGFbaV0gKiAyKTtcbiAqICAgICB9XG4gKiAgICAgcmV0dXJuIHJlc3VsdDtcbiAqICAgfVxuICogfSwgeyBpdGVyYXRpb25zOiA1IH0pO1xuICpcbiAqIHJlc3VsdHMuZm9yRWFjaChyZXN1bHQgPT4ge1xuICogICBjb25zb2xlLmxvZyhgJHtyZXN1bHQubmFtZX06ICR7cmVzdWx0LmF2Z0R1cmF0aW9uLnRvRml4ZWQoMil9bXNgKTtcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjb21wYXJlQmVuY2htYXJrcyhcbiAgYmVuY2htYXJrczogUmVjb3JkPHN0cmluZywgQmVuY2htYXJrRnVuY3Rpb24+LFxuICBvcHRpb25zOiBCZW5jaG1hcmtPcHRpb25zID0ge30sXG4pOiBQcm9taXNlPEJlbmNobWFya1Jlc3VsdFtdPiB7XG4gIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICBPYmplY3QuZW50cmllcyhiZW5jaG1hcmtzKS5tYXAoKFtuYW1lLCBmbl0pID0+IGJlbmNobWFyayhuYW1lLCBmbiwgb3B0aW9ucykpLFxuICApO1xuXG4gIHJldHVybiByZXN1bHRzLnNvcnQoKGEsIGIpID0+IGEuYXZnRHVyYXRpb24gLSBiLmF2Z0R1cmF0aW9uKTtcbn1cblxuLyoqXG4gKiBCZW5jaG1hcmsgc3VpdGUgZm9yIG9yZ2FuaXppbmcgYW5kIHJ1bm5pbmcgbXVsdGlwbGUgcmVsYXRlZCBiZW5jaG1hcmtzXG4gKi9cbmV4cG9ydCBjbGFzcyBCZW5jaG1hcmtTdWl0ZSB7XG4gIHByaXZhdGUgYmVuY2htYXJrczogQXJyYXk8eyBuYW1lOiBzdHJpbmc7IGZuOiBCZW5jaG1hcmtGdW5jdGlvbiB9PiA9IFtdO1xuXG4gIHByaXZhdGUgb3B0aW9uczogQmVuY2htYXJrT3B0aW9ucztcblxuICBjb25zdHJ1Y3RvcihwdWJsaWMgcmVhZG9ubHkgc3VpdGVOYW1lOiBzdHJpbmcsIG9wdGlvbnM6IEJlbmNobWFya09wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnM7XG4gIH1cblxuICAvKipcbiAgICogQWRkIGEgYmVuY2htYXJrIHRvIHRoZSBzdWl0ZVxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSAtIE5hbWUgb2YgdGhlIGJlbmNobWFya1xuICAgKiBAcGFyYW0gZm4gLSBGdW5jdGlvbiB0byBiZW5jaG1hcmtcbiAgICogQHJldHVybnMgVGhpcyBzdWl0ZSBpbnN0YW5jZSBmb3IgY2hhaW5pbmdcbiAgICovXG4gIGFkZChuYW1lOiBzdHJpbmcsIGZuOiBCZW5jaG1hcmtGdW5jdGlvbik6IHRoaXMge1xuICAgIHRoaXMuYmVuY2htYXJrcy5wdXNoKHsgbmFtZSwgZm4gfSk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvKipcbiAgICogUnVuIGFsbCBiZW5jaG1hcmtzIGluIHRoZSBzdWl0ZVxuICAgKlxuICAgKiBAcGFyYW0gY3VzdG9tT3B0aW9ucyAtIE92ZXJyaWRlIHN1aXRlIG9wdGlvbnMgZm9yIHRoaXMgcnVuXG4gICAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIGFycmF5IG9mIGJlbmNobWFyayByZXN1bHRzXG4gICAqL1xuICBhc3luYyBydW4oY3VzdG9tT3B0aW9ucz86IEJlbmNobWFya09wdGlvbnMpOiBQcm9taXNlPEJlbmNobWFya1Jlc3VsdFtdPiB7XG4gICAgY29uc3Qgb3B0aW9ucyA9IHsgLi4udGhpcy5vcHRpb25zLCAuLi5jdXN0b21PcHRpb25zIH07XG4gICAgY29uc3QgcmVzdWx0czogQmVuY2htYXJrUmVzdWx0W10gPSBbXTtcblxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgY29uc29sZS5sb2coYFxcbvCfmoAgUnVubmluZyBiZW5jaG1hcmsgc3VpdGU6ICR7dGhpcy5zdWl0ZU5hbWV9YCk7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICBjb25zb2xlLmxvZygnPScucmVwZWF0KDYwKSk7XG5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tcmVzdHJpY3RlZC1zeW50YXhcbiAgICBmb3IgKGNvbnN0IHsgbmFtZSwgZm4gfSBvZiB0aGlzLmJlbmNobWFya3MpIHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICBjb25zb2xlLmxvZyhgXFxu4o+x77iPICBSdW5uaW5nOiAke25hbWV9Li4uYCk7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1hd2FpdC1pbi1sb29wXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGJlbmNobWFyayhuYW1lLCBmbiwgb3B0aW9ucyk7XG4gICAgICAgIHJlc3VsdHMucHVzaChyZXN1bHQpO1xuXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGAgICDinIUgJHtyZXN1bHQuYXZnRHVyYXRpb24udG9GaXhlZCgyKX1tcyBhdmcgKCR7cmVzdWx0Lm9wc1BlclNlY29uZC50b0ZpeGVkKFxuICAgICAgICAgICAgMCxcbiAgICAgICAgICApfSBvcHMvc2VjKWAsXG4gICAgICAgICk7XG4gICAgICAgIGlmIChvcHRpb25zLm1lYXN1cmVNZW1vcnkpIHtcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgICAgICAgIGNvbnNvbGUubG9nKGAgICDwn5K+IE1lbW9yeTogJHsocmVzdWx0Lm1lbW9yeVVzZWQgLyAxMDI0IC8gMTAyNCkudG9GaXhlZCgyKX1NQmApO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgICAgICBjb25zb2xlLmxvZyhgICAg4p2MIEZhaWxlZDogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJ31gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBTdW1tYXJ5XG4gICAgaWYgKHJlc3VsdHMubGVuZ3RoID4gMSkge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUubG9nKGBcXG7wn5OKIFN1bW1hcnkgKHNvcnRlZCBieSBwZXJmb3JtYW5jZSk6YCk7XG4gICAgICBjb25zdCBzb3J0ZWRSZXN1bHRzID0gWy4uLnJlc3VsdHNdLnNvcnQoKGEsIGIpID0+IGEuYXZnRHVyYXRpb24gLSBiLmF2Z0R1cmF0aW9uKTtcbiAgICAgIGNvbnN0IGZhc3Rlc3QgPSBzb3J0ZWRSZXN1bHRzWzBdO1xuXG4gICAgICBzb3J0ZWRSZXN1bHRzLmZvckVhY2goKHJlc3VsdCwgaW5kZXgpID0+IHtcbiAgICAgICAgY29uc3QgcmVsYXRpdmUgPSByZXN1bHQuYXZnRHVyYXRpb24gLyBmYXN0ZXN0LmF2Z0R1cmF0aW9uO1xuICAgICAgICBsZXQgaWNvbiA9ICcgICc7XG4gICAgICAgIGlmIChpbmRleCA9PT0gMCkgaWNvbiA9ICfwn6WHJztcbiAgICAgICAgZWxzZSBpZiAoaW5kZXggPT09IDEpIGljb24gPSAn8J+liCc7XG4gICAgICAgIGVsc2UgaWYgKGluZGV4ID09PSAyKSBpY29uID0gJ/CfpYknO1xuXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGAgICAke2ljb259ICR7cmVzdWx0Lm5hbWUucGFkRW5kKDI1KX0gfCAke3Jlc3VsdC5hdmdEdXJhdGlvbi50b0ZpeGVkKFxuICAgICAgICAgICAgMixcbiAgICAgICAgICApfW1zIHwgJHtyZWxhdGl2ZS50b0ZpeGVkKDIpfXhgLFxuICAgICAgICApO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGBcXG7inIUgU3VpdGUgY29tcGxldGVkOiAke3Jlc3VsdHMubGVuZ3RofS8ke3RoaXMuYmVuY2htYXJrcy5sZW5ndGh9IGJlbmNobWFya3Mgc3VjY2Vzc2Z1bFxcbmAsXG4gICAgKTtcblxuICAgIHJldHVybiByZXN1bHRzO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgbnVtYmVyIG9mIGJlbmNobWFya3MgaW4gdGhlIHN1aXRlXG4gICAqL1xuICBnZXQgc2l6ZSgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmJlbmNobWFya3MubGVuZ3RoO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFyIGFsbCBiZW5jaG1hcmtzIGZyb20gdGhlIHN1aXRlXG4gICAqL1xuICBjbGVhcigpOiB0aGlzIHtcbiAgICB0aGlzLmJlbmNobWFya3MgPSBbXTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxufVxuXG4vKipcbiAqIENyZWF0ZSBhIG5ldyBiZW5jaG1hcmsgc3VpdGVcbiAqXG4gKiBAcGFyYW0gc3VpdGVOYW1lIC0gTmFtZSBvZiB0aGUgYmVuY2htYXJrIHN1aXRlXG4gKiBAcGFyYW0gb3B0aW9ucyAtIERlZmF1bHQgb3B0aW9ucyBmb3IgYWxsIGJlbmNobWFya3MgaW4gdGhlIHN1aXRlXG4gKiBAcmV0dXJucyBOZXcgQmVuY2htYXJrU3VpdGUgaW5zdGFuY2VcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3Qgc3VpdGUgPSBjcmVhdGVCZW5jaG1hcmtTdWl0ZSgnU3RyaW5nIE9wZXJhdGlvbnMnLCB7XG4gKiAgIGl0ZXJhdGlvbnM6IDEwLFxuICogICB3YXJtdXBSdW5zOiAyXG4gKiB9KTtcbiAqXG4gKiBzdWl0ZVxuICogICAuYWRkKCdjb25jYXQnLCAoKSA9PiBzdHIxICsgc3RyMilcbiAqICAgLmFkZCgndGVtcGxhdGUnLCAoKSA9PiBgJHtzdHIxfSR7c3RyMn1gKVxuICogICAuYWRkKCdqb2luJywgKCkgPT4gW3N0cjEsIHN0cjJdLmpvaW4oJycpKTtcbiAqXG4gKiBhd2FpdCBzdWl0ZS5ydW4oKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlQmVuY2htYXJrU3VpdGUoXG4gIHN1aXRlTmFtZTogc3RyaW5nLFxuICBvcHRpb25zPzogQmVuY2htYXJrT3B0aW9ucyxcbik6IEJlbmNobWFya1N1aXRlIHtcbiAgcmV0dXJuIG5ldyBCZW5jaG1hcmtTdWl0ZShzdWl0ZU5hbWUsIG9wdGlvbnMpO1xufVxuXG4vKipcbiAqIFV0aWxpdHkgdG8gbWVhc3VyZSBtZW1vcnkgdXNhZ2Ugb2YgYSBmdW5jdGlvblxuICpcbiAqIEBwYXJhbSBmbiAtIEZ1bmN0aW9uIHRvIG1lYXN1cmVcbiAqIEBwYXJhbSBmb3JjZUdDIC0gV2hldGhlciB0byBmb3JjZSBnYXJiYWdlIGNvbGxlY3Rpb24gYmVmb3JlIG1lYXN1cmluZ1xuICogQHJldHVybnMgTWVtb3J5IHVzYWdlIGluIGJ5dGVzXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IG1lbW9yeVVzZWQgPSBhd2FpdCBtZWFzdXJlTWVtb3J5KCgpID0+IHtcbiAqICAgcmV0dXJuIG5ldyBBcnJheSgxMDAwMDApLmZpbGwoMCkubWFwKHggPT4gKHsgdmFsdWU6IHggfSkpO1xuICogfSk7XG4gKlxuICogY29uc29sZS5sb2coYE1lbW9yeSB1c2VkOiAkeyhtZW1vcnlVc2VkIC8gMTAyNCAvIDEwMjQpLnRvRml4ZWQoMil9TUJgKTtcbiAqIGBgYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWVhc3VyZU1lbW9yeShmbjogQmVuY2htYXJrRnVuY3Rpb24sIGZvcmNlR0MgPSB0cnVlKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgaWYgKGZvcmNlR0MpIHtcbiAgICBmb3JjZUdhcmJhZ2VDb2xsZWN0aW9uKCk7XG4gIH1cblxuICBjb25zdCBzdGFydE1lbW9yeSA9IHByb2Nlc3MubWVtb3J5VXNhZ2UoKS5oZWFwVXNlZDtcbiAgYXdhaXQgZm4oKTtcbiAgY29uc3QgZW5kTWVtb3J5ID0gcHJvY2Vzcy5tZW1vcnlVc2FnZSgpLmhlYXBVc2VkO1xuXG4gIHJldHVybiBlbmRNZW1vcnkgLSBzdGFydE1lbW9yeTtcbn1cbiJdfQ==