UNPKG

@rudderstack/integrations-lib

Version:

A comprehensive TypeScript library providing shared utilities, SDKs, and tools for RudderStack integrations and destinations.

409 lines 50 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.configureBatchProcessingDefaults = configureBatchProcessingDefaults; exports.mapInBatches = mapInBatches; exports.filterInBatches = filterInBatches; exports.groupByInBatches = groupByInBatches; exports.reduceInBatches = reduceInBatches; exports.flatMapInBatches = flatMapInBatches; exports.forEachInBatches = forEachInBatches; // Default configuration values (internal) let defaultBatchSize = 10; let defaultYieldThreshold = 10; let defaultSequentialProcessing = true; /** * Configure global defaults for batch processing operations * * @param config - Configuration options for batch processing * @returns The current configuration after applying changes * * @example * ```typescript * // Set both defaults * configureBatchProcessingDefaults({ batchSize: 20, yieldThreshold: 5 }); * * // Set only batch size * configureBatchProcessingDefaults({ batchSize: 50 }); * * // Enable concurrent processing within batches * configureBatchProcessingDefaults({ sequentialProcessing: false }); * * // Get current configuration * const currentConfig = configureBatchProcessingDefaults(); * ``` */ function configureBatchProcessingDefaults(config) { if (config) { if (config.batchSize !== undefined) { if (!Number.isInteger(config.batchSize) || config.batchSize <= 0) { throw new Error('batchSize must be a positive integer'); } defaultBatchSize = config.batchSize; } if (config.yieldThreshold !== undefined) { if (!Number.isInteger(config.yieldThreshold) || config.yieldThreshold < 0) { throw new Error('yieldThreshold must be a non-negative integer'); } defaultYieldThreshold = config.yieldThreshold; } if (config.sequentialProcessing !== undefined) { defaultSequentialProcessing = Boolean(config.sequentialProcessing); } } return { batchSize: defaultBatchSize, yieldThreshold: defaultYieldThreshold, sequentialProcessing: defaultSequentialProcessing, }; } /** * Utility function to defer execution to the next tick of the event loop. * This prevents blocking the event loop during heavy batch operations by * yielding control back to the event loop using setImmediate. * * @param startTime - The timestamp when the current batch processing started * @param yieldThreshold - Time threshold in milliseconds before yielding control * @returns A promise that resolves to a boolean indicating whether a yield occurred * @internal */ function defer(startTime, yieldThreshold) { const elapsed = Date.now() - startTime; // Only yield if we've exceeded the threshold if (elapsed >= yieldThreshold) { return new Promise((resolve) => { setImmediate(() => resolve(true)); }); } // Otherwise continue immediately return Promise.resolve(false); } /** * Helper to get batch processing options with defaults applied * @param options - User provided options * @returns Options with defaults applied * @internal */ function getOptions(options) { const batchSize = options?.batchSize ?? defaultBatchSize; const yieldThreshold = options?.yieldThreshold ?? defaultYieldThreshold; const sequentialProcessing = options?.sequentialProcessing ?? defaultSequentialProcessing; if (!Number.isInteger(batchSize) || batchSize <= 0) { throw new Error('batchSize must be a positive integer'); } if (!Number.isInteger(yieldThreshold) || yieldThreshold < 0) { throw new Error('yieldThreshold must be a non-negative integer'); } return { batchSize, yieldThreshold, sequentialProcessing, }; } /** * Helper to process an array in batches, yielding as needed. * @param items - The array to process * @param options - Batch processing options * @param batchHandler - Function to handle each batch: (batch, batchStartIndex) => Promise<any> * @returns Promise<void> */ async function processInBatches(items, options, batchHandler) { let i = 0; let startTime = Date.now(); const { batchSize } = options; const n = items.length; // Allocate a single batch array and reuses it for all batches const batch = Array(batchSize); while (i < n) { const len = Math.min(batchSize, n - i); for (let j = 0; j < len; j += 1) { batch[j] = items[i + j]; } batch.length = len; // eslint-disable-next-line no-await-in-loop await batchHandler(batch, i); i += batchSize; // eslint-disable-next-line no-await-in-loop const didYield = await defer(startTime, options.yieldThreshold); if (didYield) startTime = Date.now(); } } /** * Maps over an array in batches to avoid blocking the event loop. * Processes items in chunks and yields control back to the event loop between batches * when the processing time exceeds the threshold. * * @template T - The type of items in the input array * @template R - The type of items in the result array * @param items - The array to map over * @param mapFn - The mapping function to apply to each item. Receives the item and its index. * @param options - Batch processing options * @returns A promise that resolves to the mapped array * @throws {Error} When batchSize is not a positive integer * * @example * ```typescript * // Synchronous mapping with default options (sequential processing) * const doubled = await mapInBatches([1, 2, 3, 4], (x) => x * 2); * * // With concurrent processing within batches * const doubled = await mapInBatches([1, 2, 3, 4], (x) => x * 2, { sequentialProcessing: false }); * ``` */ function mapInBatches(items, mapFn, options) { const opts = getOptions(options); return (async () => { const result = []; await processInBatches(items, opts, async (batch, batchStart) => { if (opts.sequentialProcessing) { // Process items sequentially for (let j = 0; j < batch.length; j += 1) { // eslint-disable-next-line no-await-in-loop -- sequential processing is required const mapped = await mapFn(batch[j], batchStart + j); result.push(mapped); } } else { // Process items concurrently const mapped = await Promise.all(batch.map((item, j) => mapFn(item, batchStart + j))); result.push(...mapped); } }); return result; })(); } /** * Filters an array in batches to avoid blocking the event loop. * Processes items in chunks and yields control back to the event loop between batches * when the processing time exceeds the threshold. * * @template T - The type of items in the array * @param items - The array to filter * @param predicate - The predicate function to test each item. Receives the item and its index. * @param options - Batch processing options * @returns A promise that resolves to the filtered array * @throws {Error} When batchSize is not a positive integer * * @example * ```typescript * // Synchronous filtering with default options * const evens = await filterInBatches([1, 2, 3, 4, 5], (x) => x % 2 === 0); * // Result: [2, 4] * * // With custom batch size * const evens = await filterInBatches([1, 2, 3, 4, 5], (x) => x % 2 === 0, { batchSize: 2 }); * ``` */ function filterInBatches(items, predicate, options) { const opts = getOptions(options); return (async () => { const result = []; await processInBatches(items, opts, async (batch, batchStart) => { if (opts.sequentialProcessing) { // Process items sequentially for (let j = 0; j < batch.length; j += 1) { // eslint-disable-next-line no-await-in-loop -- sequential processing is required const passes = await predicate(batch[j], batchStart + j); if (passes) result.push(batch[j]); } } else { // Process items concurrently const flags = await Promise.all(batch.map((item, j) => predicate(item, batchStart + j))); for (let j = 0; j < batch.length; j += 1) { if (flags[j]) result.push(batch[j]); } } }); return result; })(); } /** * Groups an array by a key function in batches to avoid blocking the event loop. * Processes items in chunks and yields control back to the event loop between batches * when the processing time exceeds the threshold. * * @template T - The type of items in the array * @template K - The type of the grouping key (must extend PropertyKey) * @param items - The array to group * @param keyFn - The function to extract the grouping key from each item. Receives the item and its index. * @param options - Batch processing options * @returns A promise that resolves to an object with grouped items * @throws {Error} When batchSize is not a positive integer * * @example * ```typescript * // Group by property with default options * const byType = await groupByInBatches( * [{type: 'A', value: 1}, {type: 'B', value: 2}, {type: 'A', value: 3}], * (item) => item.type * ); * // Result: {A: [{type: 'A', value: 1}, {type: 'A', value: 3}], B: [{type: 'B', value: 2}]} * * // With custom batch size * const byType = await groupByInBatches( * [{type: 'A', value: 1}, {type: 'B', value: 2}, {type: 'A', value: 3}], * (item) => item.type, * { batchSize: 2 } * ); * ``` */ function groupByInBatches(items, keyFn, options) { const opts = getOptions(options); return (async () => { const result = {}; await processInBatches(items, opts, async (batch, batchStart) => { if (opts.sequentialProcessing) { // Process items sequentially for (let j = 0; j < batch.length; j += 1) { // eslint-disable-next-line no-await-in-loop -- sequential processing is required const key = await keyFn(batch[j], batchStart + j); if (!result[key]) result[key] = []; result[key].push(batch[j]); } } else { // Process items concurrently const keys = await Promise.all(batch.map((item, j) => keyFn(item, batchStart + j))); for (let j = 0; j < batch.length; j += 1) { const key = keys[j]; if (!result[key]) result[key] = []; result[key].push(batch[j]); } } }); return result; })(); } /** * Reduces an array in batches to avoid blocking the event loop. * Processes items in chunks and yields control back to the event loop between batches * when the processing time exceeds the threshold. Sequential processing is always used for the reducer function, * irrespective of the `sequentialProcessing` option. * * @template T - The type of items in the array * @template R - The type of the accumulator/result * @param items - The array to reduce * @param reducer - The reducer function. Receives the accumulator, current item, and index. * @param initialValue - The initial value for the accumulator * @param options - Batch processing options * @returns A promise that resolves to the reduced value * @throws {Error} When batchSize is not a positive integer * * @example * ```typescript * // Sum numbers with default options * const sum = await reduceInBatches([1, 2, 3, 4], (acc, x) => acc + x, 0); * // Result: 10 * * // With custom batch size * const sum = await reduceInBatches([1, 2, 3, 4], (acc, x) => acc + x, 0, { batchSize: 2 }); * ``` */ function reduceInBatches(items, reducer, initialValue, options) { const opts = getOptions(options); return (async () => { let acc = initialValue; await processInBatches(items, opts, async (batch, batchStart) => { // Always sequential, regardless of sequentialProcessing option for (let j = 0; j < batch.length; j += 1) { // eslint-disable-next-line no-await-in-loop -- sequential processing is required acc = await reducer(acc, batch[j], batchStart + j); } }); return acc; })(); } /** * FlatMaps an array in batches to avoid blocking the event loop. * Processes items in chunks, flattens the results, and yields control back to the event loop between batches * when the processing time exceeds the threshold. * * @template T - The type of items in the input array * @template R - The type of items in the flattened result array * @param items - The array to flatMap over * @param mapFn - The mapping function that returns an array for each item. Receives the item and its index. * @param options - Batch processing options * @returns A promise that resolves to the flattened mapped array * @throws {Error} When batchSize is not a positive integer * * @example * ```typescript * // Duplicate each item with default options * const duplicated = await flatMapInBatches([1, 2, 3], (x) => [x, x]); * // Result: [1, 1, 2, 2, 3, 3] * * // With custom batch size * const duplicated = await flatMapInBatches([1, 2, 3], (x) => [x, x], { batchSize: 2 }); * ``` */ function flatMapInBatches(items, mapFn, options) { const opts = getOptions(options); return (async () => { const result = []; await processInBatches(items, opts, async (batch, batchStart) => { if (opts.sequentialProcessing) { // Process items sequentially for (let j = 0; j < batch.length; j += 1) { // eslint-disable-next-line no-await-in-loop -- sequential processing is required const mapped = await mapFn(batch[j], batchStart + j); result.push(...mapped); } } else { // Process items concurrently const mapped = await Promise.all(batch.map((item, j) => mapFn(item, batchStart + j))); mapped.forEach((arr) => { result.push(...arr); }); } }); return result; })(); } /** * forEach over an array in batches to avoid blocking the event loop. * Processes items in chunks and yields control back to the event loop between batches * when the processing time exceeds the threshold. * * @template T - The type of items in the input array * @param items - The array to iterate over * @param fn - The function to apply to each item. Receives the item and its index. Can be async. * @param options - Batch processing options * @returns A promise that resolves when all items have been processed * @throws {Error} When batchSize is not a positive integer * * @example * ```typescript * // Process items in batches with default options * await forEachInBatches([1, 2, 3, 4], async (x) => { * await doSomething(x); * }); * * // With custom batch size * await forEachInBatches([1, 2, 3, 4], async (x) => { * await doSomething(x); * }, { batchSize: 2 }); * ``` */ function forEachInBatches(items, fn, options) { const opts = getOptions(options); return processInBatches(items, opts, async (batch, batchStart) => { if (opts.sequentialProcessing) { // Process items sequentially for (let j = 0; j < batch.length; j += 1) { // eslint-disable-next-line no-await-in-loop -- sequential processing is required await fn(batch[j], batchStart + j); } } else { // Process items concurrently await Promise.all(batch.map((item, j) => fn(item, batchStart + j))); } }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmF0Y2gtcHJvY2Vzc2luZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9iYXRjaC1wcm9jZXNzaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBa0RBLDRFQTRCQztBQTBHRCxvQ0F5QkM7QUF3QkQsMENBMkJDO0FBZ0NELDRDQThCQztBQTJCRCwwQ0FtQkM7QUF5QkQsNENBMkJDO0FBMkJELDRDQW1CQztBQTFiRCwwQ0FBMEM7QUFDMUMsSUFBSSxnQkFBZ0IsR0FBRyxFQUFFLENBQUM7QUFDMUIsSUFBSSxxQkFBcUIsR0FBRyxFQUFFLENBQUM7QUFDL0IsSUFBSSwyQkFBMkIsR0FBRyxJQUFJLENBQUM7QUFFdkM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsU0FBZ0IsZ0NBQWdDLENBQzlDLE1BQStCO0lBRS9CLElBQUksTUFBTSxFQUFFLENBQUM7UUFDWCxJQUFJLE1BQU0sQ0FBQyxTQUFTLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDbkMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxTQUFTLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ2pFLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQztZQUMxRCxDQUFDO1lBQ0QsZ0JBQWdCLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQztRQUN0QyxDQUFDO1FBRUQsSUFBSSxNQUFNLENBQUMsY0FBYyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ3hDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxNQUFNLENBQUMsY0FBYyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUMxRSxNQUFNLElBQUksS0FBSyxDQUFDLCtDQUErQyxDQUFDLENBQUM7WUFDbkUsQ0FBQztZQUNELHFCQUFxQixHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUM7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Tiw2QkFBNkI7WUFDN0IsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLFVBQVUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdEUsQ0FBQztJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogT3B0aW9ucyBmb3IgYmF0Y2ggcHJvY2Vzc2luZyBvcGVyYXRpb25zXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQmF0Y2hQcm9jZXNzaW5nT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBOdW1iZXIgb2YgaXRlbXMgdG8gcHJvY2VzcyBpbiBlYWNoIGJhdGNoIChkZWZhdWx0OiAxMClcbiAgICogTXVzdCBiZSBhIHBvc2l0aXZlIGludGVnZXIuXG4gICAqL1xuICBiYXRjaFNpemU/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFRpbWUgdGhyZXNob2xkIGluIG1pbGxpc2Vjb25kcyAoZGVmYXVsdDogMTApIGJlZm9yZSB5aWVsZGluZyBjb250cm9sIGJhY2sgdG8gdGhlIGV2ZW50IGxvb3AuXG4gICAqIFNldCB0byAwIHRvIHlpZWxkIGFmdGVyIGV2ZXJ5IGJhdGNoLiBNdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIGludGVnZXIuXG4gICAqL1xuICB5aWVsZFRocmVzaG9sZD86IG51bWJlcjtcblxuICAvKipcbiAgICogV2hldGhlciB0byBwcm9jZXNzIGl0ZW1zIHNlcXVlbnRpYWxseSB3aXRoaW4gZWFjaCBiYXRjaC4gV2hlbiB0cnVlIChkZWZhdWx0KSwgZWFjaCBpdGVtIGluIGEgYmF0Y2hcbiAgICogd2lsbCBiZSBwcm9jZXNzZWQgb25lIGF0IGEgdGltZS4gV2hlbiBmYWxzZSwgYWxsIGl0ZW1zIGluIGEgYmF0Y2ggd2lsbCBiZSBwcm9jZXNzZWQgY29uY3VycmVudGx5LlxuICAgKiBDb25zaWRlciB0aGUgaW1wbGljYXRpb25zIG9mIGNvbmN1cnJlbmN5IG9uIHlvdXIgcHJvY2Vzc2luZyBsb2dpYyBiZWZvcmUgc2V0dGluZyB0aGlzIHRvIGZhbHNlLCBlLmcuIHJhY2UgY29uZGl0aW9ucywgcmF0ZSBsaW1pdHMsIG1lbW9yeSBwcmVzc3VyZSwgZXRjLlxuICAgKi9cbiAgc2VxdWVudGlhbFByb2Nlc3Npbmc/OiBib29sZWFuO1xufVxuXG4vLyBEZWZhdWx0IGNvbmZpZ3VyYXRpb24gdmFsdWVzIChpbnRlcm5hbClcbmxldCBkZWZhdWx0QmF0Y2hTaXplID0gMTA7XG5sZXQgZGVmYXVsdFlpZWxkVGhyZXNob2xkID0gMTA7XG5sZXQgZGVmYXVsdFNlcXVlbnRpYWxQcm9jZXNzaW5nID0gdHJ1ZTtcblxuLyoqXG4gKiBDb25maWd1cmUgZ2xvYmFsIGRlZmF1bHRzIGZvciBiYXRjaCBwcm9jZXNzaW5nIG9wZXJhdGlvbnNcbiAqXG4gKiBAcGFyYW0gY29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciBiYXRjaCBwcm9jZXNzaW5nXG4gKiBAcmV0dXJucyBUaGUgY3VycmVudCBjb25maWd1cmF0aW9uIGFmdGVyIGFwcGx5aW5nIGNoYW5nZXNcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gU2V0IGJvdGggZGVmYXVsdHNcbiAqIGNvbmZpZ3VyZUJhdGNoUHJvY2Vzc2luZ0RlZmF1bHRzKHsgYmF0Y2hTaXplOiAyMCwgeWllbGRUaHJlc2hvbGQ6IDUgfSk7XG4gKlxuICogLy8gU2V0IG9ubHkgYmF0Y2ggc2l6ZVxuICogY29uZmlndXJlQmF0Y2hQcm9jZXNzaW5nRGVmYXVsdHMoeyBiYXRjaFNpemU6IDUwIH0pO1xuICpcbiAqIC8vIEVuYWJsZSBjb25jdXJyZW50IHByb2Nlc3Npbmcgd2l0aGluIGJhdGNoZXNcbiAqIGNvbmZpZ3VyZUJhdGNoUHJvY2Vzc2luZ0RlZmF1bHRzKHsgc2VxdWVudGlhbFByb2Nlc3Npbmc6IGZhbHNlIH0pO1xuICpcbiAqIC8vIEdldCBjdXJyZW50IGNvbmZpZ3VyYXRpb25cbiAqIGNvbnN0IGN1cnJlbnRDb25maWcgPSBjb25maWd1cmVCYXRjaFByb2Nlc3NpbmdEZWZhdWx0cygpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb25maWd1cmVCYXRjaFByb2Nlc3NpbmdEZWZhdWx0cyhcbiAgY29uZmlnPzogQmF0Y2hQcm9jZXNzaW5nT3B0aW9ucyxcbik6IEJhdGNoUHJvY2Vzc2luZ09wdGlvbnMge1xuICBpZiAoY29uZmlnKSB7XG4gICAgaWYgKGNvbmZpZy5iYXRjaFNpemUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGNvbmZpZy5iYXRjaFNpemUpIHx8IGNvbmZpZy5iYXRjaFNpemUgPD0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2JhdGNoU2l6ZSBtdXN0IGJlIGEgcG9zaXRpdmUgaW50ZWdlcicpO1xuICAgICAgfVxuICAgICAgZGVmYXVsdEJhdGNoU2l6ZSA9IGNvbmZpZy5iYXRjaFNpemU7XG4gICAgfVxuXG4gICAgaWYgKGNvbmZpZy55aWVsZFRocmVzaG9sZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAoIU51bWJlci5pc0ludGVnZXIoY29uZmlnLnlpZWxkVGhyZXNob2xkKSB8fCBjb25maWcueWllbGRUaHJlc2hvbGQgPCAwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcigneWllbGRUaHJlc2hvbGQgbXVzdCBiZSBhIG5vbi1uZWdhdGl2ZSBpbnRlZ2VyJyk7XG4gICAgICB9XG4gICAgICBkZWZhdWx0WWllbGRUaHJlc2hvbGQgPSBjb25maWcueWllbGRUaHJlc2hvbGQ7XG4gICAgfVxuXG4gICAgaWYgKGNvbmZpZy5zZXF1ZW50aWFsUHJvY2Vzc2luZyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBkZWZhdWx0U2VxdWVudGlhbFByb2Nlc3NpbmcgPSBCb29sZWFuKGNvbmZpZy5zZXF1ZW50aWFsUHJvY2Vzc2luZyk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBiYXRjaFNpemU6IGRlZmF1bHRCYXRjaFNpemUsXG4gICAgeWllbGRUaHJlc2hvbGQ6IGRlZmF1bHRZaWVsZFRocmVzaG9sZCxcbiAgICBzZXF1ZW50aWFsUHJvY2Vzc2luZzogZGVmYXVsdFNlcXVlbnRpYWxQcm9jZXNzaW5nLFxuICB9O1xufVxuXG4vKipcbiAqIFV0aWxpdHkgZnVuY3Rpb24gdG8gZGVmZXIgZXhlY3V0aW9uIHRvIHRoZSBuZXh0IHRpY2sgb2YgdGhlIGV2ZW50IGxvb3AuXG4gKiBUaGlzIHByZXZlbnRzIGJsb2NraW5nIHRoZSBldmVudCBsb29wIGR1cmluZyBoZWF2eSBiYXRjaCBvcGVyYXRpb25zIGJ5XG4gKiB5aWVsZGluZyBjb250cm9sIGJhY2sgdG8gdGhlIGV2ZW50IGxvb3AgdXNpbmcgc2V0SW1tZWRpYXRlLlxuICpcbiAqIEBwYXJhbSBzdGFydFRpbWUgLSBUaGUgdGltZXN0YW1wIHdoZW4gdGhlIGN1cnJlbnQgYmF0Y2ggcHJvY2Vzc2luZyBzdGFydGVkXG4gKiBAcGFyYW0geWllbGRUaHJlc2hvbGQgLSBUaW1lIHRocmVzaG9sZCBpbiBtaWxsaXNlY29uZHMgYmVmb3JlIHlpZWxkaW5nIGNvbnRyb2xcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgYSB5aWVsZCBvY2N1cnJlZFxuICogQGludGVybmFsXG4gKi9cbmZ1bmN0aW9uIGRlZmVyKHN0YXJ0VGltZTogbnVtYmVyLCB5aWVsZFRocmVzaG9sZDogbnVtYmVyKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGNvbnN0IGVsYXBzZWQgPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lO1xuXG4gIC8vIE9ubHkgeWllbGQgaWYgd2UndmUgZXhjZWVkZWQgdGhlIHRocmVzaG9sZFxuICBpZiAoZWxhcHNlZCA+PSB5aWVsZFRocmVzaG9sZCkge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgc2V0SW1tZWRpYXRlKCgpID0+IHJlc29sdmUodHJ1ZSkpO1xuICAgIH0pO1xuICB9XG5cbiAgLy8gT3RoZXJ3aXNlIGNvbnRpbnVlIGltbWVkaWF0ZWx5XG4gIHJldHVybiBQcm9taXNlLnJlc29sdmUoZmFsc2UpO1xufVxuXG4vKipcbiAqIEhlbHBlciB0byBnZXQgYmF0Y2ggcHJvY2Vzc2luZyBvcHRpb25zIHdpdGggZGVmYXVsdHMgYXBwbGllZFxuICogQHBhcmFtIG9wdGlvbnMgLSBVc2VyIHByb3ZpZGVkIG9wdGlvbnNcbiAqIEByZXR1cm5zIE9wdGlvbnMgd2l0aCBkZWZhdWx0cyBhcHBsaWVkXG4gKiBAaW50ZXJuYWxcbiAqL1xuZnVuY3Rpb24gZ2V0T3B0aW9ucyhvcHRpb25zPzogQmF0Y2hQcm9jZXNzaW5nT3B0aW9ucyk6IFJlcXVpcmVkPEJhdGNoUHJvY2Vzc2luZ09wdGlvbnM+IHtcbiAgY29uc3QgYmF0Y2hTaXplID0gb3B0aW9ucz8uYmF0Y2hTaXplID8/IGRlZmF1bHRCYXRjaFNpemU7XG4gIGNvbnN0IHlpZWxkVGhyZXNob2xkID0gb3B0aW9ucz8ueWllbGRUaHJlc2hvbGQgPz8gZGVmYXVsdFlpZWxkVGhyZXNob2xkO1xuICBjb25zdCBzZXF1ZW50aWFsUHJvY2Vzc2luZyA9IG9wdGlvbnM/LnNlcXVlbnRpYWxQcm9jZXNzaW5nID8/IGRlZmF1bHRTZXF1ZW50aWFsUHJvY2Vzc2luZztcblxuICBpZiAoIU51bWJlci5pc0ludGVnZXIoYmF0Y2hTaXplKSB8fCBiYXRjaFNpemUgPD0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcignYmF0Y2hTaXplIG11c3QgYmUgYSBwb3NpdGl2ZSBpbnRlZ2VyJyk7XG4gIH1cbiAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKHlpZWxkVGhyZXNob2xkKSB8fCB5aWVsZFRocmVzaG9sZCA8IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3lpZWxkVGhyZXNob2xkIG11c3QgYmUgYSBub24tbmVnYXRpdmUgaW50ZWdlcicpO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBiYXRjaFNpemUsXG4gICAgeWllbGRUaHJlc2hvbGQsXG4gICAgc2VxdWVudGlhbFByb2Nlc3NpbmcsXG4gIH07XG59XG5cbi8qKlxuICogSGVscGVyIHRvIHByb2Nlc3MgYW4gYXJyYXkgaW4gYmF0Y2hlcywgeWllbGRpbmcgYXMgbmVlZGVkLlxuICogQHBhcmFtIGl0ZW1zIC0gVGhlIGFycmF5IHRvIHByb2Nlc3NcbiAqIEBwYXJhbSBvcHRpb25zIC0gQmF0Y2ggcHJvY2Vzc2luZyBvcHRpb25zXG4gKiBAcGFyYW0gYmF0Y2hIYW5kbGVyIC0gRnVuY3Rpb24gdG8gaGFuZGxlIGVhY2ggYmF0Y2g6IChiYXRjaCwgYmF0Y2hTdGFydEluZGV4KSA9PiBQcm9taXNlPGFueT5cbiAqIEByZXR1cm5zIFByb21pc2U8dm9pZD5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHJvY2Vzc0luQmF0Y2hlczxUPihcbiAgaXRlbXM6IFRbXSxcbiAgb3B0aW9uczogUmVxdWlyZWQ8QmF0Y2hQcm9jZXNzaW5nT3B0aW9ucz4sXG4gIGJhdGNoSGFuZGxlcjogKGJhdGNoOiBUW10sIGJhdGNoU3RhcnQ6IG51bWJlcikgPT4gUHJvbWlzZTx2b2lkPixcbik6IFByb21pc2U8dm9pZD4ge1xuICBsZXQgaSA9IDA7XG4gIGxldCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICBjb25zdCB7IGJhdGNoU2l6ZSB9ID0gb3B0aW9ucztcbiAgY29uc3QgbiA9IGl0ZW1zLmxlbmd0aDtcbiAgLy8gQWxsb2NhdGUgYSBzaW5nbGUgYmF0Y2ggYXJyYXkgYW5kIHJldXNlcyBpdCBmb3IgYWxsIGJhdGNoZXNcbiAgY29uc3QgYmF0Y2g6IFRbXSA9IEFycmF5KGJhdGNoU2l6ZSk7XG4gIHdoaWxlIChpIDwgbikge1xuICAgIGNvbnN0IGxlbiA9IE1hdGgubWluKGJhdGNoU2l6ZSwgbiAtIGkpO1xuICAgIGZvciAobGV0IGogPSAwOyBqIDwgbGVuOyBqICs9IDEpIHtcbiAgICAgIGJhdGNoW2pdID0gaXRlbXNbaSArIGpdO1xuICAgIH1cbiAgICBiYXRjaC5sZW5ndGggPSBsZW47XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWF3YWl0LWluLWxvb3BcbiAgICBhd2FpdCBiYXRjaEhhbmRsZXIoYmF0Y2gsIGkpO1xuICAgIGkgKz0gYmF0Y2hTaXplO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1hd2FpdC1pbi1sb29wXG4gICAgY29uc3QgZGlkWWllbGQgPSBhd2FpdCBkZWZlcihzdGFydFRpbWUsIG9wdGlvbnMueWllbGRUaHJlc2hvbGQpO1xuICAgIGlmIChkaWRZaWVsZCkgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgfVxufVxuXG4vKipcbiAqIE1hcHMgb3ZlciBhbiBhcnJheSBpbiBiYXRjaGVzIHRvIGF2b2lkIGJsb2NraW5nIHRoZSBldmVudCBsb29wLlxuICogUHJvY2Vzc2VzIGl0ZW1zIGluIGNodW5rcyBhbmQgeWllbGRzIGNvbnRyb2wgYmFjayB0byB0aGUgZXZlbnQgbG9vcCBiZXR3ZWVuIGJhdGNoZXNcbiAqIHdoZW4gdGhlIHByb2Nlc3NpbmcgdGltZSBleGNlZWRzIHRoZSB0aHJlc2hvbGQuXG4gKlxuICogQHRlbXBsYXRlIFQgLSBUaGUgdHlwZSBvZiBpdGVtcyBpbiB0aGUgaW5wdXQgYXJyYXlcbiAqIEB0ZW1wbGF0ZSBSIC0gVGhlIHR5cGUgb2YgaXRlbXMgaW4gdGhlIHJlc3VsdCBhcnJheVxuICogQHBhcmFtIGl0ZW1zIC0gVGhlIGFycmF5IHRvIG1hcCBvdmVyXG4gKiBAcGFyYW0gbWFwRm4gLSBUaGUgbWFwcGluZyBmdW5jdGlvbiB0byBhcHBseSB0byBlYWNoIGl0ZW0uIFJlY2VpdmVzIHRoZSBpdGVtIGFuZCBpdHMgaW5kZXguXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEJhdGNoIHByb2Nlc3Npbmcgb3B0aW9uc1xuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIG1hcHBlZCBhcnJheVxuICogQHRocm93cyB7RXJyb3J9IFdoZW4gYmF0Y2hTaXplIGlzIG5vdCBhIHBvc2l0aXZlIGludGVnZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gU3luY2hyb25vdXMgbWFwcGluZyB3aXRoIGRlZmF1bHQgb3B0aW9ucyAoc2VxdWVudGlhbCBwcm9jZXNzaW5nKVxuICogY29uc3QgZG91YmxlZCA9IGF3YWl0IG1hcEluQmF0Y2hlcyhbMSwgMiwgMywgNF0sICh4KSA9PiB4ICogMik7XG4gKlxuICogLy8gV2l0aCBjb25jdXJyZW50IHByb2Nlc3Npbmcgd2l0aGluIGJhdGNoZXNcbiAqIGNvbnN0IGRvdWJsZWQgPSBhd2FpdCBtYXBJbkJhdGNoZXMoWzEsIDIsIDMsIDRdLCAoeCkgPT4geCAqIDIsIHsgc2VxdWVudGlhbFByb2Nlc3Npbmc6IGZhbHNlIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtYXBJbkJhdGNoZXM8VCwgUj4oXG4gIGl0ZW1zOiBUW10sXG4gIG1hcEZuOiAoaXRlbTogVCwgaW5kZXg6IG51bWJlcikgPT4gUiB8IFByb21pc2U8Uj4sXG4gIG9wdGlvbnM/OiBCYXRjaFByb2Nlc3NpbmdPcHRpb25zLFxuKTogUHJvbWlzZTxSW10+IHtcbiAgY29uc3Qgb3B0cyA9IGdldE9wdGlvbnMob3B0aW9ucyk7XG5cbiAgcmV0dXJuIChhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgcmVzdWx0OiBSW10gPSBbXTtcbiAgICBhd2FpdCBwcm9jZXNzSW5CYXRjaGVzKGl0ZW1zLCBvcHRzLCBhc3luYyAoYmF0Y2gsIGJhdGNoU3RhcnQpID0+IHtcbiAgICAgIGlmIChvcHRzLnNlcXVlbnRpYWxQcm9jZXNzaW5nKSB7XG4gICAgICAgIC8vIFByb2Nlc3MgaXRlbXMgc2VxdWVudGlhbGx5XG4gICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgYmF0Y2gubGVuZ3RoOyBqICs9IDEpIHtcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tYXdhaXQtaW4tbG9vcCAtLSBzZXF1ZW50aWFsIHByb2Nlc3NpbmcgaXMgcmVxdWlyZWRcbiAgICAgICAgICBjb25zdCBtYXBwZWQgPSBhd2FpdCBtYXBGbihiYXRjaFtqXSwgYmF0Y2hTdGFydCArIGopO1xuICAgICAgICAgIHJlc3VsdC5wdXNoKG1hcHBlZCk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFByb2Nlc3MgaXRlbXMgY29uY3VycmVudGx5XG4gICAgICAgIGNvbnN0IG1hcHBlZCA9IGF3YWl0IFByb21pc2UuYWxsKGJhdGNoLm1hcCgoaXRlbSwgaikgPT4gbWFwRm4oaXRlbSwgYmF0Y2hTdGFydCArIGopKSk7XG4gICAgICAgIHJlc3VsdC5wdXNoKC4uLm1hcHBlZCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSkoKTtcbn1cblxuLyoqXG4gKiBGaWx0ZXJzIGFuIGFycmF5IGluIGJhdGNoZXMgdG8gYXZvaWQgYmxvY2tpbmcgdGhlIGV2ZW50IGxvb3AuXG4gKiBQcm9jZXNzZXMgaXRlbXMgaW4gY2h1bmtzIGFuZCB5aWVsZHMgY29udHJvbCBiYWNrIHRvIHRoZSBldmVudCBsb29wIGJldHdlZW4gYmF0Y2hlc1xuICogd2hlbiB0aGUgcHJvY2Vzc2luZyB0aW1lIGV4Y2VlZHMgdGhlIHRocmVzaG9sZC5cbiAqXG4gKiBAdGVtcGxhdGUgVCAtIFRoZSB0eXBlIG9mIGl0ZW1zIGluIHRoZSBhcnJheVxuICogQHBhcmFtIGl0ZW1zIC0gVGhlIGFycmF5IHRvIGZpbHRlclxuICogQHBhcmFtIHByZWRpY2F0ZSAtIFRoZSBwcmVkaWNhdGUgZnVuY3Rpb24gdG8gdGVzdCBlYWNoIGl0ZW0uIFJlY2VpdmVzIHRoZSBpdGVtIGFuZCBpdHMgaW5kZXguXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEJhdGNoIHByb2Nlc3Npbmcgb3B0aW9uc1xuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGZpbHRlcmVkIGFycmF5XG4gKiBAdGhyb3dzIHtFcnJvcn0gV2hlbiBiYXRjaFNpemUgaXMgbm90IGEgcG9zaXRpdmUgaW50ZWdlclxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBTeW5jaHJvbm91cyBmaWx0ZXJpbmcgd2l0aCBkZWZhdWx0IG9wdGlvbnNcbiAqIGNvbnN0IGV2ZW5zID0gYXdhaXQgZmlsdGVySW5CYXRjaGVzKFsxLCAyLCAzLCA0LCA1XSwgKHgpID0+IHggJSAyID09PSAwKTtcbiAqIC8vIFJlc3VsdDogWzIsIDRdXG4gKlxuICogLy8gV2l0aCBjdXN0b20gYmF0Y2ggc2l6ZVxuICogY29uc3QgZXZlbnMgPSBhd2FpdCBmaWx0ZXJJbkJhdGNoZXMoWzEsIDIsIDMsIDQsIDVdLCAoeCkgPT4geCAlIDIgPT09IDAsIHsgYmF0Y2hTaXplOiAyIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJJbkJhdGNoZXM8VD4oXG4gIGl0ZW1zOiBUW10sXG4gIHByZWRpY2F0ZTogKGl0ZW06IFQsIGluZGV4OiBudW1iZXIpID0+IGJvb2xlYW4gfCBQcm9taXNlPGJvb2xlYW4+LFxuICBvcHRpb25zPzogQmF0Y2hQcm9jZXNzaW5nT3B0aW9ucyxcbik6IFByb21pc2U8VFtdPiB7XG4gIGNvbnN0IG9wdHMgPSBnZXRPcHRpb25zKG9wdGlvbnMpO1xuXG4gIHJldHVybiAoYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHJlc3VsdDogVFtdID0gW107XG4gICAgYXdhaXQgcHJvY2Vzc0luQmF0Y2hlcyhpdGVtcywgb3B0cywgYXN5bmMgKGJhdGNoLCBiYXRjaFN0YXJ0KSA9PiB7XG4gICAgICBpZiAob3B0cy5zZXF1ZW50aWFsUHJvY2Vzc2luZykge1xuICAgICAgICAvLyBQcm9jZXNzIGl0ZW1zIHNlcXVlbnRpYWxseVxuICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IGJhdGNoLmxlbmd0aDsgaiArPSAxKSB7XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWF3YWl0LWluLWxvb3AgLS0gc2VxdWVudGlhbCBwcm9jZXNzaW5nIGlzIHJlcXVpcmVkXG4gICAgICAgICAgY29uc3QgcGFzc2VzID0gYXdhaXQgcHJlZGljYXRlKGJhdGNoW2pdLCBiYXRjaFN0YXJ0ICsgaik7XG4gICAgICAgICAgaWYgKHBhc3NlcykgcmVzdWx0LnB1c2goYmF0Y2hbal0pO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBQcm9jZXNzIGl0ZW1zIGNvbmN1cnJlbnRseVxuICAgICAgICBjb25zdCBmbGFncyA9IGF3YWl0IFByb21pc2UuYWxsKGJhdGNoLm1hcCgoaXRlbSwgaikgPT4gcHJlZGljYXRlKGl0ZW0sIGJhdGNoU3RhcnQgKyBqKSkpO1xuICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IGJhdGNoLmxlbmd0aDsgaiArPSAxKSB7XG4gICAgICAgICAgaWYgKGZsYWdzW2pdKSByZXN1bHQucHVzaChiYXRjaFtqXSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9KSgpO1xufVxuXG4vKipcbiAqIEdyb3VwcyBhbiBhcnJheSBieSBhIGtleSBmdW5jdGlvbiBpbiBiYXRjaGVzIHRvIGF2b2lkIGJsb2NraW5nIHRoZSBldmVudCBsb29wLlxuICogUHJvY2Vzc2VzIGl0ZW1zIGluIGNodW5rcyBhbmQgeWllbGRzIGNvbnRyb2wgYmFjayB0byB0aGUgZXZlbnQgbG9vcCBiZXR3ZWVuIGJhdGNoZXNcbiAqIHdoZW4gdGhlIHByb2Nlc3NpbmcgdGltZSBleGNlZWRzIHRoZSB0aHJlc2hvbGQuXG4gKlxuICogQHRlbXBsYXRlIFQgLSBUaGUgdHlwZSBvZiBpdGVtcyBpbiB0aGUgYXJyYXlcbiAqIEB0ZW1wbGF0ZSBLIC0gVGhlIHR5cGUgb2YgdGhlIGdyb3VwaW5nIGtleSAobXVzdCBleHRlbmQgUHJvcGVydHlLZXkpXG4gKiBAcGFyYW0gaXRlbXMgLSBUaGUgYXJyYXkgdG8gZ3JvdXBcbiAqIEBwYXJhbSBrZXlGbiAtIFRoZSBmdW5jdGlvbiB0byBleHRyYWN0IHRoZSBncm91cGluZyBrZXkgZnJvbSBlYWNoIGl0ZW0uIFJlY2VpdmVzIHRoZSBpdGVtIGFuZCBpdHMgaW5kZXguXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEJhdGNoIHByb2Nlc3Npbmcgb3B0aW9uc1xuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IHdpdGggZ3JvdXBlZCBpdGVtc1xuICogQHRocm93cyB7RXJyb3J9IFdoZW4gYmF0Y2hTaXplIGlzIG5vdCBhIHBvc2l0aXZlIGludGVnZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gR3JvdXAgYnkgcHJvcGVydHkgd2l0aCBkZWZhdWx0IG9wdGlvbnNcbiAqIGNvbnN0IGJ5VHlwZSA9IGF3YWl0IGdyb3VwQnlJbkJhdGNoZXMoXG4gKiAgIFt7dHlwZTogJ0EnLCB2YWx1ZTogMX0sIHt0eXBlOiAnQicsIHZhbHVlOiAyfSwge3R5cGU6ICdBJywgdmFsdWU6IDN9XSxcbiAqICAgKGl0ZW0pID0+IGl0ZW0udHlwZVxuICogKTtcbiAqIC8vIFJlc3VsdDoge0E6IFt7dHlwZTogJ0EnLCB2YWx1ZTogMX0sIHt0eXBlOiAnQScsIHZhbHVlOiAzfV0sIEI6IFt7dHlwZTogJ0InLCB2YWx1ZTogMn1dfVxuICpcbiAqIC8vIFdpdGggY3VzdG9tIGJhdGNoIHNpemVcbiAqIGNvbnN0IGJ5VHlwZSA9IGF3YWl0IGdyb3VwQnlJbkJhdGNoZXMoXG4gKiAgIFt7dHlwZTogJ0EnLCB2YWx1ZTogMX0sIHt0eXBlOiAnQicsIHZhbHVlOiAyfSwge3R5cGU6ICdBJywgdmFsdWU6IDN9XSxcbiAqICAgKGl0ZW0pID0+IGl0ZW0udHlwZSxcbiAqICAgeyBiYXRjaFNpemU6IDIgfVxuICogKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ3JvdXBCeUluQmF0Y2hlczxULCBLIGV4dGVuZHMgUHJvcGVydHlLZXk+KFxuICBpdGVtczogVFtdLFxuICBrZXlGbjogKGl0ZW06IFQsIGluZGV4OiBudW1iZXIpID0+IEsgfCBQcm9taXNlPEs+LFxuICBvcHRpb25zPzogQmF0Y2hQcm9jZXNzaW5nT3B0aW9ucyxcbik6IFByb21pc2U8UmVjb3JkPEssIFRbXT4+IHtcbiAgY29uc3Qgb3B0cyA9IGdldE9wdGlvbnMob3B0aW9ucyk7XG5cbiAgcmV0dXJuIChhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgcmVzdWx0OiBSZWNvcmQ8SywgVFtdPiA9IHt9IGFzIFJlY29yZDxLLCBUW10+O1xuICAgIGF3YWl0IHByb2Nlc3NJbkJhdGNoZXMoaXRlbXMsIG9wdHMsIGFzeW5jIChiYXRjaCwgYmF0Y2hTdGFydCkgPT4ge1xuICAgICAgaWYgKG9wdHMuc2VxdWVudGlhbFByb2Nlc3NpbmcpIHtcbiAgICAgICAgLy8gUHJvY2VzcyBpdGVtcyBzZXF1ZW50aWFsbHlcbiAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBiYXRjaC5sZW5ndGg7IGogKz0gMSkge1xuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1hd2FpdC1pbi1sb29wIC0tIHNlcXVlbnRpYWwgcHJvY2Vzc2luZyBpcyByZXF1aXJlZFxuICAgICAgICAgIGNvbnN0IGtleSA9IGF3YWl0IGtleUZuKGJhdGNoW2pdLCBiYXRjaFN0YXJ0ICsgaik7XG4gICAgICAgICAgaWYgKCFyZXN1bHRba2V5XSkgcmVzdWx0W2tleV0gPSBbXTtcbiAgICAgICAgICByZXN1bHRba2V5XS5wdXNoKGJhdGNoW2pdKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gUHJvY2VzcyBpdGVtcyBjb25jdXJyZW50bHlcbiAgICAgICAgY29uc3Qga2V5cyA9IGF3YWl0IFByb21pc2UuYWxsKGJhdGNoLm1hcCgoaXRlbSwgaikgPT4ga2V5Rm4oaXRlbSwgYmF0Y2hTdGFydCArIGopKSk7XG4gICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgYmF0Y2gubGVuZ3RoOyBqICs9IDEpIHtcbiAgICAgICAgICBjb25zdCBrZXkgPSBrZXlzW2pdO1xuICAgICAgICAgIGlmICghcmVzdWx0W2tleV0pIHJlc3VsdFtrZXldID0gW107XG4gICAgICAgICAgcmVzdWx0W2tleV0ucHVzaChiYXRjaFtqXSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9KSgpO1xufVxuXG4vKipcbiAqIFJlZHVjZXMgYW4gYXJyYXkgaW4gYmF0Y2hlcyB0byBhdm9pZCBibG9ja2luZyB0aGUgZXZlbnQgbG9vcC5cbiAqIFByb2Nlc3NlcyBpdGVtcyBpbiBjaHVua3MgYW5kIHlpZWxkcyBjb250cm9sIGJhY2sgdG8gdGhlIGV2ZW50IGxvb3AgYmV0d2VlbiBiYXRjaGVzXG4gKiB3aGVuIHRoZSBwcm9jZXNzaW5nIHRpbWUgZXhjZWVkcyB0aGUgdGhyZXNob2xkLiBTZXF1ZW50aWFsIHByb2Nlc3NpbmcgaXMgYWx3YXlzIHVzZWQgZm9yIHRoZSByZWR1Y2VyIGZ1bmN0aW9uLFxuICogaXJyZXNwZWN0aXZlIG9mIHRoZSBgc2VxdWVudGlhbFByb2Nlc3NpbmdgIG9wdGlvbi5cbiAqXG4gKiBAdGVtcGxhdGUgVCAtIFRoZSB0eXBlIG9mIGl0ZW1zIGluIHRoZSBhcnJheVxuICogQHRlbXBsYXRlIFIgLSBUaGUgdHlwZSBvZiB0aGUgYWNjdW11bGF0b3IvcmVzdWx0XG4gKiBAcGFyYW0gaXRlbXMgLSBUaGUgYXJyYXkgdG8gcmVkdWNlXG4gKiBAcGFyYW0gcmVkdWNlciAtIFRoZSByZWR1Y2VyIGZ1bmN0aW9uLiBSZWNlaXZlcyB0aGUgYWNjdW11bGF0b3IsIGN1cnJlbnQgaXRlbSwgYW5kIGluZGV4LlxuICogQHBhcmFtIGluaXRpYWxWYWx1ZSAtIFRoZSBpbml0aWFsIHZhbHVlIGZvciB0aGUgYWNjdW11bGF0b3JcbiAqIEBwYXJhbSBvcHRpb25zIC0gQmF0Y2ggcHJvY2Vzc2luZyBvcHRpb25zXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVkdWNlZCB2YWx1ZVxuICogQHRocm93cyB7RXJyb3J9IFdoZW4gYmF0Y2hTaXplIGlzIG5vdCBhIHBvc2l0aXZlIGludGVnZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gU3VtIG51bWJlcnMgd2l0aCBkZWZhdWx0IG9wdGlvbnNcbiAqIGNvbnN0IHN1bSA9IGF3YWl0IHJlZHVjZUluQmF0Y2hlcyhbMSwgMiwgMywgNF0sIChhY2MsIHgpID0+IGFjYyArIHgsIDApO1xuICogLy8gUmVzdWx0OiAxMFxuICpcbiAqIC8vIFdpdGggY3VzdG9tIGJhdGNoIHNpemVcbiAqIGNvbnN0IHN1bSA9IGF3YWl0IHJlZHVjZUluQmF0Y2hlcyhbMSwgMiwgMywgNF0sIChhY2MsIHgpID0+IGFjYyArIHgsIDAsIHsgYmF0Y2hTaXplOiAyIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWR1Y2VJbkJhdGNoZXM8VCwgUj4oXG4gIGl0ZW1zOiBUW10sXG4gIHJlZHVjZXI6IChhY2M6IFIsIGl0ZW06IFQsIGluZGV4OiBudW1iZXIpID0+IFIgfCBQcm9taXNlPFI+LFxuICBpbml0aWFsVmFsdWU6IFIsXG4gIG9wdGlvbnM/OiBCYXRjaFByb2Nlc3NpbmdPcHRpb25zLFxuKTogUHJvbWlzZTxSPiB7XG4gIGNvbnN0IG9wdHMgPSBnZXRPcHRpb25zKG9wdGlvbnMpO1xuXG4gIHJldHVybiAoYXN5bmMgKCkgPT4ge1xuICAgIGxldCBhY2MgPSBpbml0aWFsVmFsdWU7XG4gICAgYXdhaXQgcHJvY2Vzc0luQmF0Y2hlcyhpdGVtcywgb3B0cywgYXN5bmMgKGJhdGNoLCBiYXRjaFN0YXJ0KSA9PiB7XG4gICAgICAvLyBBbHdheXMgc2VxdWVudGlhbCwgcmVnYXJkbGVzcyBvZiBzZXF1ZW50aWFsUHJvY2Vzc2luZyBvcHRpb25cbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgYmF0Y2gubGVuZ3RoOyBqICs9IDEpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWF3YWl0LWluLWxvb3AgLS0gc2VxdWVudGlhbCBwcm9jZXNzaW5nIGlzIHJlcXVpcmVkXG4gICAgICAgIGFjYyA9IGF3YWl0IHJlZHVjZXIoYWNjLCBiYXRjaFtqXSwgYmF0Y2hTdGFydCArIGopO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBhY2M7XG4gIH0pKCk7XG59XG5cbi8qKlxuICogRmxhdE1hcHMgYW4gYXJyYXkgaW4gYmF0Y2hlcyB0byBhdm9pZCBibG9ja2luZyB0aGUgZXZlbnQgbG9vcC5cbiAqIFByb2Nlc3NlcyBpdGVtcyBpbiBjaHVua3MsIGZsYXR0ZW5zIHRoZSByZXN1bHRzLCBhbmQgeWllbGRzIGNvbnRyb2wgYmFjayB0byB0aGUgZXZlbnQgbG9vcCBiZXR3ZWVuIGJhdGNoZXNcbiAqIHdoZW4gdGhlIHByb2Nlc3NpbmcgdGltZSBleGNlZWRzIHRoZSB0aHJlc2hvbGQuXG4gKlxuICogQHRlbXBsYXRlIFQgLSBUaGUgdHlwZSBvZiBpdGVtcyBpbiB0aGUgaW5wdXQgYXJyYXlcbiAqIEB0ZW1wbGF0ZSBSIC0gVGhlIHR5cGUgb2YgaXRlbXMgaW4gdGhlIGZsYXR0ZW5lZCByZXN1bHQgYXJyYXlcbiAqIEBwYXJhbSBpdGVtcyAtIFRoZSBhcnJheSB0byBmbGF0TWFwIG92ZXJcbiAqIEBwYXJhbSBtYXBGbiAtIFRoZSBtYXBwaW5nIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBhcnJheSBmb3IgZWFjaCBpdGVtLiBSZWNlaXZlcyB0aGUgaXRlbSBhbmQgaXRzIGluZGV4LlxuICogQHBhcmFtIG9wdGlvbnMgLSBCYXRjaCBwcm9jZXNzaW5nIG9wdGlvbnNcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmbGF0dGVuZWQgbWFwcGVkIGFycmF5XG4gKiBAdGhyb3dzIHtFcnJvcn0gV2hlbiBiYXRjaFNpemUgaXMgbm90IGEgcG9zaXRpdmUgaW50ZWdlclxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBEdXBsaWNhdGUgZWFjaCBpdGVtIHdpdGggZGVmYXVsdCBvcHRpb25zXG4gKiBjb25zdCBkdXBsaWNhdGVkID0gYXdhaXQgZmxhdE1hcEluQmF0Y2hlcyhbMSwgMiwgM10sICh4KSA9PiBbeCwgeF0pO1xuICogLy8gUmVzdWx0OiBbMSwgMSwgMiwgMiwgMywgM11cbiAqXG4gKiAvLyBXaXRoIGN1c3RvbSBiYXRjaCBzaXplXG4gKiBjb25zdCBkdXBsaWNhdGVkID0gYXdhaXQgZmxhdE1hcEluQmF0Y2hlcyhbMSwgMiwgM10sICh4KSA9PiBbeCwgeF0sIHsgYmF0Y2hTaXplOiAyIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmbGF0TWFwSW5CYXRjaGVzPFQsIFI+KFxuICBpdGVtczogVFtdLFxuICBtYXBGbjogKGl0ZW06IFQsIGluZGV4OiBudW1iZXIpID0+IFJbXSB8IFByb21pc2U8UltdPixcbiAgb3B0aW9ucz86IEJhdGNoUHJvY2Vzc2luZ09wdGlvbnMsXG4pOiBQcm9taXNlPFJbXT4ge1xuICBjb25zdCBvcHRzID0gZ2V0T3B0aW9ucyhvcHRpb25zKTtcblxuICByZXR1cm4gKGFzeW5jICgpID0+IHtcbiAgICBjb25zdCByZXN1bHQ6IFJbXSA9IFtdO1xuICAgIGF3YWl0IHByb2Nlc3NJbkJhdGNoZXMoaXRlbXMsIG9wdHMsIGFzeW5jIChiYXRjaCwgYmF0Y2hTdGFydCkgPT4ge1xuICAgICAgaWYgKG9wdHMuc2VxdWVudGlhbFByb2Nlc3NpbmcpIHtcbiAgICAgICAgLy8gUHJvY2VzcyBpdGVtcyBzZXF1ZW50aWFsbHlcbiAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBiYXRjaC5sZW5ndGg7IGogKz0gMSkge1xuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1hd2FpdC1pbi1sb29wIC0tIHNlcXVlbnRpYWwgcHJvY2Vzc2luZyBpcyByZXF1aXJlZFxuICAgICAgICAgIGNvbnN0IG1hcHBlZCA9IGF3YWl0IG1hcEZuKGJhdGNoW2pdLCBiYXRjaFN0YXJ0ICsgaik7XG4gICAgICAgICAgcmVzdWx0LnB1c2goLi4ubWFwcGVkKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gUHJvY2VzcyBpdGVtcyBjb25jdXJyZW50bHlcbiAgICAgICAgY29uc3QgbWFwcGVkID0gYXdhaXQgUHJvbWlzZS5hbGwoYmF0Y2gubWFwKChpdGVtLCBqKSA9PiBtYXBGbihpdGVtLCBiYXRjaFN0YXJ0ICsgaikpKTtcbiAgICAgICAgbWFwcGVkLmZvckVhY2goKGFycikgPT4ge1xuICAgICAgICAgIHJlc3VsdC5wdXNoKC4uLmFycik7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH0pKCk7XG59XG5cbi8qKlxuICogZm9yRWFjaCBvdmVyIGFuIGFycmF5IGluIGJhdGNoZXMgdG8gYXZvaWQgYmxvY2tpbmcgdGhlIGV2ZW50IGxvb3AuXG4gKiBQcm9jZXNzZXMgaXRlbXMgaW4gY2h1bmtzIGFuZCB5aWVsZHMgY29udHJvbCBiYWNrIHRvIHRoZSBldmVudCBsb29wIGJldHdlZW4gYmF0Y2hlc1xuICogd2hlbiB0aGUgcHJvY2Vzc2luZyB0aW1lIGV4Y2VlZHMgdGhlIHRocmVzaG9sZC5cbiAqXG4gKiBAdGVtcGxhdGUgVCAtIFRoZSB0eXBlIG9mIGl0ZW1zIGluIHRoZSBpbnB1dCBhcnJheVxuICogQHBhcmFtIGl0ZW1zIC0gVGhlIGFycmF5IHRvIGl0ZXJhdGUgb3ZlclxuICogQHBhcmFtIGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGFwcGx5IHRvIGVhY2ggaXRlbS4gUmVjZWl2ZXMgdGhlIGl0ZW0gYW5kIGl0cyBpbmRleC4gQ2FuIGJlIGFzeW5jLlxuICogQHBhcmFtIG9wdGlvbnMgLSBCYXRjaCBwcm9jZXNzaW5nIG9wdGlvbnNcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gYWxsIGl0ZW1zIGhhdmUgYmVlbiBwcm9jZXNzZWRcbiAqIEB0aHJvd3Mge0Vycm9yfSBXaGVuIGJhdGNoU2l6ZSBpcyBub3QgYSBwb3NpdGl2ZSBpbnRlZ2VyXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIFByb2Nlc3MgaXRlbXMgaW4gYmF0Y2hlcyB3aXRoIGRlZmF1bHQgb3B0aW9uc1xuICogYXdhaXQgZm9yRWFjaEluQmF0Y2hlcyhbMSwgMiwgMywgNF0sIGFzeW5jICh4KSA9PiB7XG4gKiAgIGF3YWl0IGRvU29tZXRoaW5nKHgpO1xuICogfSk7XG4gKlxuICogLy8gV2l0aCBjdXN0b20gYmF0Y2ggc2l6ZVxuICogYXdhaXQgZm9yRWFjaEluQmF0Y2hlcyhbMSwgMiwgMywgNF0sIGFzeW5jICh4KSA9PiB7XG4gKiAgIGF3YWl0IGRvU29tZXRoaW5nKHgpO1xuICogfSwgeyBiYXRjaFNpemU6IDIgfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvckVhY2hJbkJhdGNoZXM8VD4oXG4gIGl0ZW1zOiBUW10sXG4gIGZuOiAoaXRlbTogVCwgaW5kZXg6IG51bWJlcikgPT4gdm9pZCB8IFByb21pc2U8dm9pZD4sXG4gIG9wdGlvbnM/OiBCYXRjaFByb2Nlc3NpbmdPcHRpb25zLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IG9wdHMgPSBnZXRPcHRpb25zKG9wdGlvbnMpO1xuXG4gIHJldHVybiBwcm9jZXNzSW5CYXRjaGVzKGl0ZW1zLCBvcHRzLCBhc3luYyAoYmF0Y2gsIGJhdGNoU3RhcnQpID0+IHtcbiAgICBpZiAob3B0cy5zZXF1ZW50aWFsUHJvY2Vzc2luZykge1xuICAgICAgLy8gUHJvY2VzcyBpdGVtcyBzZXF1ZW50aWFsbHlcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgYmF0Y2gubGVuZ3RoOyBqICs9IDEpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWF3YWl0LWluLWxvb3AgLS0gc2VxdWVudGlhbCBwcm9jZXNzaW5nIGlzIHJlcXVpcmVkXG4gICAgICAgIGF3YWl0IGZuKGJhdGNoW2pdLCBiYXRjaFN0YXJ0ICsgaik7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFByb2Nlc3MgaXRlbXMgY29uY3VycmVudGx5XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChiYXRjaC5tYXAoKGl0ZW0sIGopID0+IGZuKGl0ZW0sIGJhdGNoU3RhcnQgKyBqKSkpO1xuICAgIH1cbiAgfSk7XG59XG4iXX0=