UNPKG

@grnsft/if

Version:

Impact Framework

371 lines 54.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TimeSync = void 0; const types_1 = require("node:util/types"); const luxon_1 = require("luxon"); const zod_1 = require("zod"); const utils_1 = require("@grnsft/if-core/utils"); const validations_1 = require("../../../common/util/validations"); const config_1 = require("../../config"); const aggregate_1 = require("../../lib/aggregate"); const interfaces_1 = require("@grnsft/if-core/interfaces"); luxon_1.Settings.defaultZone = 'utc'; const { ConfigError, InvalidDateInInputError, InvalidPaddingError, InvalidInputError, } = utils_1.ERRORS; const { INCOMPATIBLE_RESOLUTION_WITH_INTERVAL, INCOMPATIBLE_RESOLUTION_WITH_GAPS, INCOMPATIBLE_RESOLUTION_WITH_INPUTS, INVALID_OBSERVATION_OVERLAP, AVOIDING_PADDING_BY_EDGES, INVALID_DATE_TYPE, START_LOWER_END, MISSING_CONFIG, } = config_1.STRINGS; /** * Time synchronization plugin converted into framework integrated tool. * It can't be requested in `initialize.plugins` section anymore. Instead describe configuration in context. * @example * ```yaml * name: time-sync * description: sample in time sync lib * tags: sample, time, sync * time-sync: * start-time: '2023-12-12T00:00:00.000Z' * end-time: '2023-12-12T00:01:00.000Z' * interval: 5 * allow-padding: true * ``` */ exports.TimeSync = (0, interfaces_1.PluginFactory)({ metadata: { inputs: { timestamp: { description: 'refers to the time of occurrence of the input', unit: 'RFC3339', 'aggregation-method': { time: 'none', component: 'none', }, }, duration: { description: 'refers to the duration of the input', unit: 'seconds', 'aggregation-method': { time: 'sum', component: 'none', }, }, }, }, configValidation: (config) => { if (!config || !Object.keys(config)?.length) { throw new ConfigError(MISSING_CONFIG); } const schema = zod_1.z .object({ 'start-time': zod_1.z.string().datetime(), 'end-time': zod_1.z.string().datetime(), interval: zod_1.z.number(), 'allow-padding': zod_1.z.boolean(), 'upsampling-resolution': zod_1.z.number().min(1).optional(), }) .refine(data => data['start-time'] < data['end-time'], { message: START_LOWER_END, }); return (0, validations_1.validate)(schema, config); }, inputValidation: (input, _config, index) => { const schema = zod_1.z.object({ timestamp: zod_1.z.string().datetime({}).or(zod_1.z.date()), duration: zod_1.z.number(), }); return (0, validations_1.validate)(schema, input, index); }, implementation: async (inputs, config, mapping) => { /** * Checks if a given duration is compatible with a given timeStep. If not, throws an error */ const validateIntervalForResample = (duration, timeStep, errorMessage) => { if (duration % timeStep !== 0) { throw new ConfigError(errorMessage); } }; /** * Dates are passed to `time-sync` both in ISO 8601 format * and as a Date object (from the deserialization of a YAML file). * If the YAML parser fails to identify as a date, it passes as a string. */ const parseDate = (date) => { if (!date) { return luxon_1.DateTime.invalid('Invalid date'); } if ((0, types_1.isDate)(date)) { return luxon_1.DateTime.fromJSDate(date); } if (typeof date === 'string') { return luxon_1.DateTime.fromISO(date); } throw new InvalidDateInInputError(INVALID_DATE_TYPE(date)); }; /** * Calculates minimal factor. */ const convertPerInterval = (value, duration, timeStep) => { const samplesNumber = duration / timeStep; return value / samplesNumber; }; /** * Normalize time per given second. */ const normalizeTimePerSecond = (currentRoundMoment, i) => { const thisMoment = parseDate(currentRoundMoment).startOf('second'); return thisMoment.plus({ seconds: i }); }; /** * Breaks down input per minimal time unit. */ const breakDownInput = (input, i, params) => { const metrics = Object.keys(input); const timeStep = params.upsamplingResolution; return metrics.reduce((acc, metric) => { const aggregationParams = (0, aggregate_1.getAggregationInfoFor)(metric); if (metric === 'timestamp') { const perSecond = normalizeTimePerSecond(input.timestamp, i); acc[metric] = perSecond.toUTC().toISO() ?? ''; return acc; } if (metric === 'duration') { acc[metric] = timeStep; return acc; } if (aggregationParams.time === 'none') { acc[metric] = null; return acc; } acc[metric] = aggregationParams.time === 'sum' ? convertPerInterval(input[metric], input['duration'], timeStep) : input[metric]; return acc; }, {}); }; /** * Populates object to fill the gaps in observational timeline using zeroish values. */ const fillWithZeroishInput = (input, missingTimestamp, timeStep) => { const metrics = Object.keys(input); return metrics.reduce((acc, metric) => { if (metric === 'timestamp') { acc[metric] = missingTimestamp.startOf('second').toUTC().toISO() ?? ''; return acc; } if (metric === 'duration') { acc[metric] = timeStep; return acc; } if (metric === 'time-reserved' || (mapping && mapping['time-reserved'] && metric === mapping['time-reserved'])) { acc[metric] = acc['duration']; return acc; } const aggregationParams = (0, aggregate_1.getAggregationInfoFor)(metric); if (aggregationParams.time === 'none') { acc[metric] = null; return acc; } if (aggregationParams.time === 'avg' || aggregationParams.time === 'sum') { acc[metric] = 0; return acc; } if (aggregationParams.time === 'copy') { acc[metric] = input[metric]; return acc; } return acc; }, {}); }; /** * Checks if `error on padding` is enabled and padding is needed. If so, then throws error. */ const validatePadding = (pad, params) => { const { start, end } = pad; const isPaddingNeeded = start || end; if (!params.allowPadding && isPaddingNeeded) { throw new InvalidPaddingError(AVOIDING_PADDING_BY_EDGES(start, end)); } }; /** * Checks if padding is needed either at start of the timeline or the end and returns status. */ const checkForPadding = (inputs, params) => { const startDiffInSeconds = parseDate(inputs[0].timestamp) .diff(params.startTime) .as('seconds'); const lastInput = inputs[inputs.length - 1]; const endDiffInSeconds = parseDate(lastInput.timestamp) .plus({ second: eval(lastInput.duration) }) .diff(params.endTime) .as('seconds'); return { start: startDiffInSeconds > 0, end: endDiffInSeconds < 0, }; }; /** * Iterates over given inputs frame, meanwhile checking if aggregation method is `sum`, then calculates it. * For methods is `avg` and `none` calculating average of the frame. */ const resampleInputFrame = (inputsInTimeslot) => inputsInTimeslot.reduce((acc, input, index, inputs) => { const metrics = Object.keys(input); metrics.forEach(metric => { const aggregationParams = (0, aggregate_1.getAggregationInfoFor)(metric); if (metric === 'timestamp') { acc[metric] = inputs[0][metric]; return; } if (metric === 'duration') { aggregationParams.time = 'sum'; } if (aggregationParams.time === 'none') { acc[metric] = null; return; } acc[metric] = acc[metric] ?? 0; if (aggregationParams.time === 'sum') { acc[metric] += input[metric]; return; } if (aggregationParams.time === 'copy') { acc[metric] = input[metric]; return; } /** * If timeslot contains records more than one, then divide each metric by the timeslot length, * so that their sum yields the timeslot average. */ if (inputsInTimeslot.length > 1 && index === inputsInTimeslot.length - 1) { acc[metric] /= inputsInTimeslot.length; return; } acc[metric] += input[metric]; }); return acc; }, {}); /** * Takes each array frame with interval length, then aggregating them together as from units.yaml file. */ const resampleInputs = (inputs, params) => inputs.reduce((acc, _input, index, inputs) => { const frameStart = (index * params.interval) / params.upsamplingResolution; const frameEnd = ((index + 1) * params.interval) / params.upsamplingResolution; const inputsFrame = inputs.slice(frameStart, frameEnd); const resampledInput = resampleInputFrame(inputsFrame); /** Checks if resampled input is not empty, then includes in result. */ if (Object.keys(resampledInput).length > 0) { acc.push(resampledInput); } return acc; }, []); /** * Pads zeroish inputs from the beginning or at the end of the inputs if needed. */ const padInputs = (inputs, pad, params) => { const { start, end } = pad; const paddedFromBeginning = []; if (start) { paddedFromBeginning.push(...getZeroishInputPerSecondBetweenRange({ startDate: params.startTime, endDate: parseDate(inputs[0].timestamp), timeStep: params.upsamplingResolution, }, inputs[0])); } const paddedArray = paddedFromBeginning.concat(inputs); if (end) { const lastInput = inputs[inputs.length - 1]; const lastInputEnd = parseDate(lastInput.timestamp).plus({ seconds: eval(lastInput.duration), }); paddedArray.push(...getZeroishInputPerSecondBetweenRange({ startDate: lastInputEnd, endDate: params.endTime, timeStep: params.upsamplingResolution, }, lastInput)); } return paddedArray; }; /** * Brakes down the given range by 1 second, and generates zeroish values. */ const getZeroishInputPerSecondBetweenRange = (params, input) => { const array = []; validateIntervalForResample(params.endDate.diff(params.startDate).as('seconds'), params.timeStep, INCOMPATIBLE_RESOLUTION_WITH_GAPS); const dateRange = luxon_1.Interval.fromDateTimes(params.startDate, params.endDate); for (const interval of dateRange.splitBy({ second: params.timeStep })) { array.push(fillWithZeroishInput(input, // as far as I can tell, start will never be null // because if we pass an invalid start/endDate to // Interval, we get a zero length array as the range interval.start || luxon_1.DateTime.invalid('not expected - start is null'), params.timeStep)); } return array; }; /* * Checks if input's timestamp is included in global specified period then leaves it, otherwise. */ const trimInputsByGlobalTimeline = (inputs, params) => inputs.reduce((acc, item) => { const { timestamp } = item; if (parseDate(timestamp) >= params.startTime && parseDate(timestamp) <= params.endTime) { acc.push(item); } return acc; }, []); /** Implementation */ const timeParams = { startTime: luxon_1.DateTime.fromISO(config['start-time']), endTime: luxon_1.DateTime.fromISO(config['end-time']), interval: config.interval, allowPadding: config['allow-padding'], upsamplingResolution: config['upsampling-resolution'] ? config['upsampling-resolution'] : 1, }; validateIntervalForResample(timeParams.interval, timeParams.upsamplingResolution, INCOMPATIBLE_RESOLUTION_WITH_INTERVAL); const pad = checkForPadding(inputs, timeParams); validatePadding(pad, timeParams); const paddedInputs = padInputs(inputs, pad, timeParams); const flattenInputs = paddedInputs.reduce((acc, input, index) => { const currentMoment = parseDate(input.timestamp); /** Checks if not the first input, then check consistency with previous ones. */ if (index > 0) { const previousInput = paddedInputs[index - 1]; const previousInputTimestamp = parseDate(previousInput.timestamp); /** Checks for timestamps overlap. */ if (parseDate(previousInput.timestamp).plus({ seconds: eval(previousInput.duration), }) > currentMoment) { throw new InvalidInputError(INVALID_OBSERVATION_OVERLAP); } const compareableTime = previousInputTimestamp.plus({ seconds: eval(previousInput.duration), }); const timelineGapSize = currentMoment .diff(compareableTime) .as('seconds'); validateIntervalForResample(input.duration, timeParams.upsamplingResolution, INCOMPATIBLE_RESOLUTION_WITH_INPUTS); if (timelineGapSize > 1) { /** Checks if there is gap in timeline. */ acc.push(...getZeroishInputPerSecondBetweenRange({ startDate: compareableTime, endDate: currentMoment, timeStep: timeParams.upsamplingResolution, }, input)); } } /** Break down current observation. */ for (let i = 0; i <= input.duration - timeParams.upsamplingResolution; i += timeParams.upsamplingResolution) { const normalizedInput = breakDownInput(input, i, timeParams); acc.push(normalizedInput); } return trimInputsByGlobalTimeline(acc, timeParams); }, []); const sortedInputs = flattenInputs.sort((a, b) => parseDate(a.timestamp).diff(parseDate(b.timestamp)).as('seconds')); return resampleInputs(sortedInputs, timeParams); }, }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvaWYtcnVuL2J1aWx0aW5zL3RpbWUtc3luYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyQ0FBdUM7QUFFdkMsaUNBQXVFO0FBQ3ZFLDZCQUFzQjtBQUN0QixpREFBNkM7QUFVN0Msa0VBQTBEO0FBRTFELHlDQUFxQztBQUNyQyxtREFBMEQ7QUFDMUQsMkRBQXlEO0FBRXpELGdCQUFRLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQztBQUU3QixNQUFNLEVBQ0osV0FBVyxFQUNYLHVCQUF1QixFQUN2QixtQkFBbUIsRUFDbkIsaUJBQWlCLEdBQ2xCLEdBQUcsY0FBTSxDQUFDO0FBRVgsTUFBTSxFQUNKLHFDQUFxQyxFQUNyQyxpQ0FBaUMsRUFDakMsbUNBQW1DLEVBQ25DLDJCQUEyQixFQUMzQix5QkFBeUIsRUFDekIsaUJBQWlCLEVBQ2pCLGVBQWUsRUFDZixjQUFjLEdBQ2YsR0FBRyxnQkFBTyxDQUFDO0FBRVo7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDVSxRQUFBLFFBQVEsR0FBRyxJQUFBLDBCQUFhLEVBQXVCO0lBQzFELFFBQVEsRUFBRTtRQUNSLE1BQU0sRUFBRTtZQUNOLFNBQVMsRUFBRTtnQkFDVCxXQUFXLEVBQUUsK0NBQStDO2dCQUM1RCxJQUFJLEVBQUUsU0FBUztnQkFDZixvQkFBb0IsRUFBRTtvQkFDcEIsSUFBSSxFQUFFLE1BQU07b0JBQ1osU0FBUyxFQUFFLE1BQU07aUJBQ2xCO2FBQ0Y7WUFDRCxRQUFRLEVBQUU7Z0JBQ1IsV0FBVyxFQUFFLHFDQUFxQztnQkFDbEQsSUFBSSxFQUFFLFNBQVM7Z0JBQ2Ysb0JBQW9CLEVBQUU7b0JBQ3BCLElBQUksRUFBRSxLQUFLO29CQUNYLFNBQVMsRUFBRSxNQUFNO2lCQUNsQjthQUNGO1NBQ0Y7S0FDRjtJQUNELGdCQUFnQixFQUFFLENBQUMsTUFBb0IsRUFBd0IsRUFBRTtRQUMvRCxJ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Q0FBQztDQUNGLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7aXNEYXRlfSBmcm9tICdub2RlOnV0aWwvdHlwZXMnO1xuXG5pbXBvcnQge1NldHRpbmdzLCBEYXRlVGltZSwgRGF0ZVRpbWVNYXliZVZhbGlkLCBJbnRlcnZhbH0gZnJvbSAnbHV4b24nO1xuaW1wb3J0IHt6fSBmcm9tICd6b2QnO1xuaW1wb3J0IHtFUlJPUlN9IGZyb20gJ0Bncm5zZnQvaWYtY29yZS91dGlscyc7XG5pbXBvcnQge1xuICBQbHVnaW5QYXJhbXMsXG4gIFBhZGRpbmdSZWNlaXB0LFxuICBNYXBwaW5nUGFyYW1zLFxuICBDb25maWdQYXJhbXMsXG4gIFRpbWVQYXJhbXMsXG4gIFRpbWVOb3JtYWxpemVyQ29uZmlnLFxufSBmcm9tICdAZ3Juc2Z0L2lmLWNvcmUvdHlwZXMnO1xuXG5pbXBvcnQge3ZhbGlkYXRlfSBmcm9tICcuLi8uLi8uLi9jb21tb24vdXRpbC92YWxpZGF0aW9ucyc7XG5cbmltcG9ydCB7U1RSSU5HU30gZnJvbSAnLi4vLi4vY29uZmlnJztcbmltcG9ydCB7Z2V0QWdncmVnYXRpb25JbmZvRm9yfSBmcm9tICcuLi8uLi9saWIvYWdncmVnYXRlJztcbmltcG9ydCB7UGx1Z2luRmFjdG9yeX0gZnJvbSAnQGdybnNmdC9pZi1jb3JlL2ludGVyZmFjZXMnO1xuXG5TZXR0aW5ncy5kZWZhdWx0Wm9uZSA9ICd1dGMnO1xuXG5jb25zdCB7XG4gIENvbmZpZ0Vycm9yLFxuICBJbnZhbGlkRGF0ZUluSW5wdXRFcnJvcixcbiAgSW52YWxpZFBhZGRpbmdFcnJvcixcbiAgSW52YWxpZElucHV0RXJyb3IsXG59ID0gRVJST1JTO1xuXG5jb25zdCB7XG4gIElOQ09NUEFUSUJMRV9SRVNPTFVUSU9OX1dJVEhfSU5URVJWQUwsXG4gIElOQ09NUEFUSUJMRV9SRVNPTFVUSU9OX1dJVEhfR0FQUyxcbiAgSU5DT01QQVRJQkxFX1JFU09MVVRJT05fV0lUSF9JTlBVVFMsXG4gIElOVkFMSURfT0JTRVJWQVRJT05fT1ZFUkxBUCxcbiAgQVZPSURJTkdfUEFERElOR19CWV9FREdFUyxcbiAgSU5WQUxJRF9EQVRFX1RZUEUsXG4gIFNUQVJUX0xPV0VSX0VORCxcbiAgTUlTU0lOR19DT05GSUcsXG59ID0gU1RSSU5HUztcblxuLyoqXG4gKiBUaW1lIHN5bmNocm9uaXphdGlvbiBwbHVnaW4gY29udmVydGVkIGludG8gZnJhbWV3b3JrIGludGVncmF0ZWQgdG9vbC5cbiAqIEl0IGNhbid0IGJlIHJlcXVlc3RlZCBpbiBgaW5pdGlhbGl6ZS5wbHVnaW5zYCBzZWN0aW9uIGFueW1vcmUuIEluc3RlYWQgZGVzY3JpYmUgY29uZmlndXJhdGlvbiBpbiBjb250ZXh0LlxuICogQGV4YW1wbGVcbiAqIGBgYHlhbWxcbiAqIG5hbWU6IHRpbWUtc3luY1xuICogZGVzY3JpcHRpb246IHNhbXBsZSBpbiB0aW1lIHN5bmMgbGliXG4gKiB0YWdzOiBzYW1wbGUsIHRpbWUsIHN5bmNcbiAqIHRpbWUtc3luYzpcbiAqICAgc3RhcnQtdGltZTogJzIwMjMtMTItMTJUMDA6MDA6MDAuMDAwWidcbiAqICAgZW5kLXRpbWU6ICcyMDIzLTEyLTEyVDAwOjAxOjAwLjAwMFonXG4gKiAgIGludGVydmFsOiA1XG4gKiAgIGFsbG93LXBhZGRpbmc6IHRydWVcbiAqIGBgYFxuICovXG5leHBvcnQgY29uc3QgVGltZVN5bmMgPSBQbHVnaW5GYWN0b3J5PFRpbWVOb3JtYWxpemVyQ29uZmlnPih7XG4gIG1ldGFkYXRhOiB7XG4gICAgaW5wdXRzOiB7XG4gICAgICB0aW1lc3RhbXA6IHtcbiAgICAgICAgZGVzY3JpcHRpb246ICdyZWZlcnMgdG8gdGhlIHRpbWUgb2Ygb2NjdXJyZW5jZSBvZiB0aGUgaW5wdXQnLFxuICAgICAgICB1bml0OiAnUkZDMzMzOScsXG4gICAgICAgICdhZ2dyZWdhdGlvbi1tZXRob2QnOiB7XG4gICAgICAgICAgdGltZTogJ25vbmUnLFxuICAgICAgICAgIGNvbXBvbmVudDogJ25vbmUnLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGR1cmF0aW9uOiB7XG4gICAgICAgIGRlc2NyaXB0aW9uOiAncmVmZXJzIHRvIHRoZSBkdXJhdGlvbiBvZiB0aGUgaW5wdXQnLFxuICAgICAgICB1bml0OiAnc2Vjb25kcycsXG4gICAgICAgICdhZ2dyZWdhdGlvbi1tZXRob2QnOiB7XG4gICAgICAgICAgdGltZTogJ3N1bScsXG4gICAgICAgICAgY29tcG9uZW50OiAnbm9uZScsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0sXG4gIH0sXG4gIGNvbmZpZ1ZhbGlkYXRpb246IChjb25maWc6IENvbmZpZ1BhcmFtcyk6IFRpbWVOb3JtYWxpemVyQ29uZmlnID0+IHtcbiAgICBpZiAoIWNvbmZpZyB8fCAhT2JqZWN0LmtleXMoY29uZmlnKT8ubGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgQ29uZmlnRXJyb3IoTUlTU0lOR19DT05GSUcpO1xuICAgIH1cblxuICAgIGNvbnN0IHNjaGVtYSA9IHpcbiAgICAgIC5vYmplY3Qoe1xuICAgICAgICAnc3RhcnQtdGltZSc6IHouc3RyaW5nKCkuZGF0ZXRpbWUoKSxcbiAgICAgICAgJ2VuZC10aW1lJzogei5zdHJpbmcoKS5kYXRldGltZSgpLFxuICAgICAgICBpbnRlcnZhbDogei5udW1iZXIoKSxcbiAgICAgICAgJ2FsbG93LXBhZGRpbmcnOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgJ3Vwc2FtcGxpbmctcmVzb2x1dGlvbic6IHoubnVtYmVyKCkubWluKDEpLm9wdGlvbmFsKCksXG4gICAgICB9KVxuICAgICAgLnJlZmluZShkYXRhID0+IGRhdGFbJ3N0YXJ0LXRpbWUnXSA8IGRhdGFbJ2VuZC10aW1lJ10sIHtcbiAgICAgICAgbWVzc2FnZTogU1RBUlRfTE9XRVJfRU5ELFxuICAgICAgfSk7XG5cbiAgICByZXR1cm4gdmFsaWRhdGU8ei5pbmZlcjx0eXBlb2Ygc2NoZW1hPj4oc2NoZW1hLCBjb25maWcpO1xuICB9LFxuICBpbnB1dFZhbGlkYXRpb246IChpbnB1dDogUGx1Z2luUGFyYW1zLCBfY29uZmlnOiBhbnksIGluZGV4PzogbnVtYmVyKSA9PiB7XG4gICAgY29uc3Qgc2NoZW1hID0gei5vYmplY3Qoe1xuICAgICAgdGltZXN0YW1wOiB6LnN0cmluZygpLmRhdGV0aW1lKHt9KS5vcih6LmRhdGUoKSksXG4gICAgICBkdXJhdGlvbjogei5udW1iZXIoKSxcbiAgICB9KTtcblxuICAgIHJldHVybiB2YWxpZGF0ZTx6LmluZmVyPHR5cGVvZiBzY2hlbWE+PihzY2hlbWEsIGlucHV0LCBpbmRleCk7XG4gIH0sXG4gIGltcGxlbWVudGF0aW9uOiBhc3luYyAoXG4gICAgaW5wdXRzOiBQbHVnaW5QYXJhbXNbXSxcbiAgICBjb25maWcsXG4gICAgbWFwcGluZz86IE1hcHBpbmdQYXJhbXNcbiAgKSA9PiB7XG4gICAgLyoqXG4gICAgICogQ2hlY2tzIGlmIGEgZ2l2ZW4gZHVyYXRpb24gaXMgY29tcGF0aWJsZSB3aXRoIGEgZ2l2ZW4gdGltZVN0ZXAuIElmIG5vdCwgdGhyb3dzIGFuIGVycm9yXG4gICAgICovXG4gICAgY29uc3QgdmFsaWRhdGVJbnRlcnZhbEZvclJlc2FtcGxlID0gKFxuICAgICAgZHVyYXRpb246IG51bWJlcixcbiAgICAgIHRpbWVTdGVwOiBudW1iZXIsXG4gICAgICBlcnJvck1lc3NhZ2U6IHN0cmluZ1xuICAgICkgPT4ge1xuICAgICAgaWYgKGR1cmF0aW9uICUgdGltZVN0ZXAgIT09IDApIHtcbiAgICAgICAgdGhyb3cgbmV3IENvbmZpZ0Vycm9yKGVycm9yTWVzc2FnZSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERhdGVzIGFyZSBwYXNzZWQgdG8gYHRpbWUtc3luY2AgYm90aCBpbiBJU08gODYwMSBmb3JtYXRcbiAgICAgKiBhbmQgYXMgYSBEYXRlIG9iamVjdCAoZnJvbSB0aGUgZGVzZXJpYWxpemF0aW9uIG9mIGEgWUFNTCBmaWxlKS5cbiAgICAgKiBJZiB0aGUgWUFNTCBwYXJzZXIgZmFpbHMgdG8gaWRlbnRpZnkgYXMgYSBkYXRlLCBpdCBwYXNzZXMgYXMgYSBzdHJpbmcuXG4gICAgICovXG4gICAgY29uc3QgcGFyc2VEYXRlID0gKGRhdGU6IERhdGUgfCBzdHJpbmcpID0+IHtcbiAgICAgIGlmICghZGF0ZSkge1xuICAgICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZCgnSW52YWxpZCBkYXRlJyk7XG4gICAgICB9XG5cbiAgICAgIGlmIChpc0RhdGUoZGF0ZSkpIHtcbiAgICAgICAgcmV0dXJuIERhdGVUaW1lLmZyb21KU0RhdGUoZGF0ZSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0eXBlb2YgZGF0ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgcmV0dXJuIERhdGVUaW1lLmZyb21JU08oZGF0ZSk7XG4gICAgICB9XG5cbiAgICAgIHRocm93IG5ldyBJbnZhbGlkRGF0ZUluSW5wdXRFcnJvcihJTlZBTElEX0RBVEVfVFlQRShkYXRlKSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZXMgbWluaW1hbCBmYWN0b3IuXG4gICAgICovXG4gICAgY29uc3QgY29udmVydFBlckludGVydmFsID0gKFxuICAgICAgdmFsdWU6IG51bWJlcixcbiAgICAgIGR1cmF0aW9uOiBudW1iZXIsXG4gICAgICB0aW1lU3RlcDogbnVtYmVyXG4gICAgKSA9PiB7XG4gICAgICBjb25zdCBzYW1wbGVzTnVtYmVyID0gZHVyYXRpb24gLyB0aW1lU3RlcDtcblxuICAgICAgcmV0dXJuIHZhbHVlIC8gc2FtcGxlc051bWJlcjtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogTm9ybWFsaXplIHRpbWUgcGVyIGdpdmVuIHNlY29uZC5cbiAgICAgKi9cbiAgICBjb25zdCBub3JtYWxpemVUaW1lUGVyU2Vjb25kID0gKFxuICAgICAgY3VycmVudFJvdW5kTW9tZW50OiBEYXRlIHwgc3RyaW5nLFxuICAgICAgaTogbnVtYmVyXG4gICAgKSA9PiB7XG4gICAgICBjb25zdCB0aGlzTW9tZW50ID0gcGFyc2VEYXRlKGN1cnJlbnRSb3VuZE1vbWVudCkuc3RhcnRPZignc2Vjb25kJyk7XG5cbiAgICAgIHJldHVybiB0aGlzTW9tZW50LnBsdXMoe3NlY29uZHM6IGl9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQnJlYWtzIGRvd24gaW5wdXQgcGVyIG1pbmltYWwgdGltZSB1bml0LlxuICAgICAqL1xuICAgIGNvbnN0IGJyZWFrRG93bklucHV0ID0gKFxuICAgICAgaW5wdXQ6IFBsdWdpblBhcmFtcyxcbiAgICAgIGk6IG51bWJlcixcbiAgICAgIHBhcmFtczogVGltZVBhcmFtc1xuICAgICkgPT4ge1xuICAgICAgY29uc3QgbWV0cmljcyA9IE9iamVjdC5rZXlzKGlucHV0KTtcbiAgICAgIGNvbnN0IHRpbWVTdGVwID0gcGFyYW1zLnVwc2FtcGxpbmdSZXNvbHV0aW9uO1xuXG4gICAgICByZXR1cm4gbWV0cmljcy5yZWR1Y2UoKGFjYywgbWV0cmljKSA9PiB7XG4gICAgICAgIGNvbnN0IGFnZ3JlZ2F0aW9uUGFyYW1zID0gZ2V0QWdncmVnYXRpb25JbmZvRm9yKG1ldHJpYyk7XG5cbiAgICAgICAgaWYgKG1ldHJpYyA9PT0gJ3RpbWVzdGFtcCcpIHtcbiAgICAgICAgICBjb25zdCBwZXJTZWNvbmQgPSBub3JtYWxpemVUaW1lUGVyU2Vjb25kKGlucHV0LnRpbWVzdGFtcCwgaSk7XG4gICAgICAgICAgYWNjW21ldHJpY10gPSBwZXJTZWNvbmQudG9VVEMoKS50b0lTTygpID8/ICcnO1xuXG4gICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChtZXRyaWMgPT09ICdkdXJhdGlvbicpIHtcbiAgICAgICAgICBhY2NbbWV0cmljXSA9IHRpbWVTdGVwO1xuXG4gICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChhZ2dyZWdhdGlvblBhcmFtcy50aW1lID09PSAnbm9uZScpIHtcbiAgICAgICAgICBhY2NbbWV0cmljXSA9IG51bGw7XG5cbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9XG5cbiAgICAgICAgYWNjW21ldHJpY10gPVxuICAgICAgICAgIGFnZ3JlZ2F0aW9uUGFyYW1zLnRpbWUgPT09ICdzdW0nXG4gICAgICAgICAgICA/IGNvbnZlcnRQZXJJbnRlcnZhbChpbnB1dFttZXRyaWNdLCBpbnB1dFsnZHVyYXRpb24nXSwgdGltZVN0ZXApXG4gICAgICAgICAgICA6IGlucHV0W21ldHJpY107XG5cbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgIH0sIHt9IGFzIFBsdWdpblBhcmFtcyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFBvcHVsYXRlcyBvYmplY3QgdG8gZmlsbCB0aGUgZ2FwcyBpbiBvYnNlcnZhdGlvbmFsIHRpbWVsaW5lIHVzaW5nIHplcm9pc2ggdmFsdWVzLlxuICAgICAqL1xuICAgIGNvbnN0IGZpbGxXaXRoWmVyb2lzaElucHV0ID0gKFxuICAgICAgaW5wdXQ6IFBsdWdpblBhcmFtcyxcbiAgICAgIG1pc3NpbmdUaW1lc3RhbXA6IERhdGVUaW1lTWF5YmVWYWxpZCxcbiAgICAgIHRpbWVTdGVwOiBudW1iZXJcbiAgICApID0+IHtcbiAgICAgIGNvbnN0IG1ldHJpY3MgPSBPYmplY3Qua2V5cyhpbnB1dCk7XG4gICAgICByZXR1cm4gbWV0cmljcy5yZWR1Y2UoKGFjYywgbWV0cmljKSA9PiB7XG4gICAgICAgIGlmIChtZXRyaWMgPT09ICd0aW1lc3RhbXAnKSB7XG4gICAgICAgICAgYWNjW21ldHJpY10gPVxuICAgICAgICAgICAgbWlzc2luZ1RpbWVzdGFtcC5zdGFydE9mKCdzZWNvbmQnKS50b1VUQygpLnRvSVNPKCkgPz8gJyc7XG5cbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG1ldHJpYyA9PT0gJ2R1cmF0aW9uJykge1xuICAgICAgICAgIGFjY1ttZXRyaWNdID0gdGltZVN0ZXA7XG5cbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKFxuICAgICAgICAgIG1ldHJpYyA9PT0gJ3RpbWUtcmVzZXJ2ZWQnIHx8XG4gICAgICAgICAgKG1hcHBpbmcgJiZcbiAgICAgICAgICAgIG1hcHBpbmdbJ3RpbWUtcmVzZXJ2ZWQnXSAmJlxuICAgICAgICAgICAgbWV0cmljID09PSBtYXBwaW5nWyd0aW1lLXJlc2VydmVkJ10pXG4gICAgICAgICkge1xuICAgICAgICAgIGFjY1ttZXRyaWNdID0gYWNjWydkdXJhdGlvbiddO1xuXG4gICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGFnZ3JlZ2F0aW9uUGFyYW1zID0gZ2V0QWdncmVnYXRpb25JbmZvRm9yKG1ldHJpYyk7XG5cbiAgICAgICAgaWYgKGFnZ3JlZ2F0aW9uUGFyYW1zLnRpbWUgPT09ICdub25lJykge1xuICAgICAgICAgIGFjY1ttZXRyaWNdID0gbnVsbDtcblxuICAgICAgICAgIHJldHVybiBhY2M7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoXG4gICAgICAgICAgYWdncmVnYXRpb25QYXJhbXMudGltZSA9PT0gJ2F2ZycgfHxcbiAgICAgICAgICBhZ2dyZWdhdGlvblBhcmFtcy50aW1lID09PSAnc3VtJ1xuICAgICAgICApIHtcbiAgICAgICAgICBhY2NbbWV0cmljXSA9IDA7XG5cbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGFnZ3JlZ2F0aW9uUGFyYW1zLnRpbWUgPT09ICdjb3B5Jykge1xuICAgICAgICAgIGFjY1ttZXRyaWNdID0gaW5wdXRbbWV0cmljXTtcbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgIH0sIHt9IGFzIFBsdWdpblBhcmFtcyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiBgZXJyb3Igb24gcGFkZGluZ2AgaXMgZW5hYmxlZCBhbmQgcGFkZGluZyBpcyBuZWVkZWQuIElmIHNvLCB0aGVuIHRocm93cyBlcnJvci5cbiAgICAgKi9cbiAgICBjb25zdCB2YWxpZGF0ZVBhZGRpbmcgPSAocGFkOiBQYWRkaW5nUmVjZWlwdCwgcGFyYW1zOiBUaW1lUGFyYW1zKTogdm9pZCA9PiB7XG4gICAgICBjb25zdCB7c3RhcnQsIGVuZH0gPSBwYWQ7XG4gICAgICBjb25zdCBpc1BhZGRpbmdOZWVkZWQgPSBzdGFydCB8fCBlbmQ7XG5cbiAgICAgIGlmICghcGFyYW1zLmFsbG93UGFkZGluZyAmJiBpc1BhZGRpbmdOZWVkZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEludmFsaWRQYWRkaW5nRXJyb3IoQVZPSURJTkdfUEFERElOR19CWV9FREdFUyhzdGFydCwgZW5kKSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiBwYWRkaW5nIGlzIG5lZWRlZCBlaXRoZXIgYXQgc3RhcnQgb2YgdGhlIHRpbWVsaW5lIG9yIHRoZSBlbmQgYW5kIHJldHVybnMgc3RhdHVzLlxuICAgICAqL1xuICAgIGNvbnN0IGNoZWNrRm9yUGFkZGluZyA9IChcbiAgICAgIGlucHV0czogUGx1Z2luUGFyYW1zW10sXG4gICAgICBwYXJhbXM6IFRpbWVQYXJhbXNcbiAgICApOiBQYWRkaW5nUmVjZWlwdCA9PiB7XG4gICAgICBjb25zdCBzdGFydERpZmZJblNlY29uZHMgPSBwYXJzZURhdGUoaW5wdXRzWzBdLnRpbWVzdGFtcClcbiAgICAgICAgLmRpZmYocGFyYW1zLnN0YXJ0VGltZSlcbiAgICAgICAgLmFzKCdzZWNvbmRzJyk7XG5cbiAgICAgIGNvbnN0IGxhc3RJbnB1dCA9IGlucHV0c1tpbnB1dHMubGVuZ3RoIC0gMV07XG5cbiAgICAgIGNvbnN0IGVuZERpZmZJblNlY29uZHMgPSBwYXJzZURhdGUobGFzdElucHV0LnRpbWVzdGFtcClcbiAgICAgICAgLnBsdXMoe3NlY29uZDogZXZhbChsYXN0SW5wdXQuZHVyYXRpb24pfSlcbiAgICAgICAgLmRpZmYocGFyYW1zLmVuZFRpbWUpXG4gICAgICAgIC5hcygnc2Vjb25kcycpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc3RhcnQ6IHN0YXJ0RGlmZkluU2Vjb25kcyA+IDAsXG4gICAgICAgIGVuZDogZW5kRGlmZkluU2Vjb25kcyA8IDAsXG4gICAgICB9O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBJdGVyYXRlcyBvdmVyIGdpdmVuIGlucHV0cyBmcmFtZSwgbWVhbndoaWxlIGNoZWNraW5nIGlmIGFnZ3JlZ2F0aW9uIG1ldGhvZCBpcyBgc3VtYCwgdGhlbiBjYWxjdWxhdGVzIGl0LlxuICAgICAqIEZvciBtZXRob2RzIGlzIGBhdmdgIGFuZCBgbm9uZWAgY2FsY3VsYXRpbmcgYXZlcmFnZSBvZiB0aGUgZnJhbWUuXG4gICAgICovXG4gICAgY29uc3QgcmVzYW1wbGVJbnB1dEZyYW1lID0gKGlucHV0c0luVGltZXNsb3Q6IFBsdWdpblBhcmFtc1tdKSA9PlxuICAgICAgaW5wdXRzSW5UaW1lc2xvdC5yZWR1Y2UoKGFjYywgaW5wdXQsIGluZGV4LCBpbnB1dHMpID0+IHtcbiAgICAgICAgY29uc3QgbWV0cmljcyA9IE9iamVjdC5rZXlzKGlucHV0KTtcblxuICAgICAgICBtZXRyaWNzLmZvckVhY2gobWV0cmljID0+IHtcbiAgICAgICAgICBjb25zdCBhZ2dyZWdhdGlvblBhcmFtcyA9IGdldEFnZ3JlZ2F0aW9uSW5mb0ZvcihtZXRyaWMpO1xuXG4gICAgICAgICAgaWYgKG1ldHJpYyA9PT0gJ3RpbWVzdGFtcCcpIHtcbiAgICAgICAgICAgIGFjY1ttZXRyaWNdID0gaW5wdXRzWzBdW21ldHJpY107XG5cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAobWV0cmljID09PSAnZHVyYXRpb24nKSB7XG4gICAgICAgICAgICBhZ2dyZWdhdGlvblBhcmFtcy50aW1lID0gJ3N1bSc7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKGFnZ3JlZ2F0aW9uUGFyYW1zLnRpbWUgPT09ICdub25lJykge1xuICAgICAgICAgICAgYWNjW21ldHJpY10gPSBudWxsO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGFjY1ttZXRyaWNdID0gYWNjW21ldHJpY10gPz8gMDtcblxuICAgICAgICAgIGlmIChhZ2dyZWdhdGlvblBhcmFtcy50aW1lID09PSAnc3VtJykge1xuICAgICAgICAgICAgYWNjW21ldHJpY10gKz0gaW5wdXRbbWV0cmljXTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChhZ2dyZWdhdGlvblBhcmFtcy50aW1lID09PSAnY29weScpIHtcbiAgICAgICAgICAgIGFjY1ttZXRyaWNdID0gaW5wdXRbbWV0cmljXTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8qKlxuICAgICAgICAgICAqIElmIHRpbWVzbG90IGNvbnRhaW5zIHJlY29yZHMgbW9yZSB0aGFuIG9uZSwgdGhlbiBkaXZpZGUgZWFjaCBtZXRyaWMgYnkgdGhlIHRpbWVzbG90IGxlbmd0aCxcbiAgICAgICAgICAgKiAgc28gdGhhdCB0aGVpciBzdW0geWllbGRzIHRoZSB0aW1lc2xvdCBhdmVyYWdlLlxuICAgICAgICAgICAqL1xuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIGlucHV0c0luVGltZXNsb3QubGVuZ3RoID4gMSAmJlxuICAgICAgICAgICAgaW5kZXggPT09IGlucHV0c0luVGltZXNsb3QubGVuZ3RoIC0gMVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgYWNjW21ldHJpY10gLz0gaW5wdXRzSW5UaW1lc2xvdC5sZW5ndGg7XG5cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBhY2NbbWV0cmljXSArPSBpbnB1dFttZXRyaWNdO1xuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSwge30gYXMgUGx1Z2luUGFyYW1zKTtcblxuICAgIC8qKlxuICAgICAqIFRha2VzIGVhY2ggYXJyYXkgZnJhbWUgd2l0aCBpbnRlcnZhbCBsZW5ndGgsIHRoZW4gYWdncmVnYXRpbmcgdGhlbSB0b2dldGhlciBhcyBmcm9tIHVuaXRzLnlhbWwgZmlsZS5cbiAgICAgKi9cbiAgICBjb25zdCByZXNhbXBsZUlucHV0cyA9IChpbnB1dHM6IFBsdWdpblBhcmFtc1tdLCBwYXJhbXM6IFRpbWVQYXJhbXMpID0+XG4gICAgICBpbnB1dHMucmVkdWNlKChhY2M6IFBsdWdpblBhcmFtc1tdLCBfaW5wdXQsIGluZGV4LCBpbnB1dHMpID0+IHtcbiAgICAgICAgY29uc3QgZnJhbWVTdGFydCA9XG4gICAgICAgICAgKGluZGV4ICogcGFyYW1zLmludGVydmFsKSAvIHBhcmFtcy51cHNhbXBsaW5nUmVzb2x1dGlvbjtcbiAgICAgICAgY29uc3QgZnJhbWVFbmQgPVxuICAgICAgICAgICgoaW5kZXggKyAxKSAqIHBhcmFtcy5pbnRlcnZhbCkgLyBwYXJhbXMudXBzYW1wbGluZ1Jlc29sdXRpb247XG5cbiAgICAgICAgY29uc3QgaW5wdXRzRnJhbWUgPSBpbnB1dHMuc2xpY2UoZnJhbWVTdGFydCwgZnJhbWVFbmQpO1xuICAgICAgICBjb25zdCByZXNhbXBsZWRJbnB1dCA9IHJlc2FtcGxlSW5wdXRGcmFtZShpbnB1dHNGcmFtZSk7XG5cbiAgICAgICAgLyoqIENoZWNrcyBpZiByZXNhbXBsZWQgaW5wdXQgaXMgbm90IGVtcHR5LCB0aGVuIGluY2x1ZGVzIGluIHJlc3VsdC4gKi9cbiAgICAgICAgaWYgKE9iamVjdC5rZXlzKHJlc2FtcGxlZElucHV0KS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgYWNjLnB1c2gocmVzYW1wbGVkSW5wdXQpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgIH0sIFtdIGFzIFBsdWdpblBhcmFtc1tdKTtcblxuICAgIC8qKlxuICAgICAqIFBhZHMgemVyb2lzaCBpbnB1dHMgZnJvbSB0aGUgYmVnaW5uaW5nIG9yIGF0IHRoZSBlbmQgb2YgdGhlIGlucHV0cyBpZiBuZWVkZWQuXG4gICAgICovXG4gICAgY29uc3QgcGFkSW5wdXRzID0gKFxuICAgICAgaW5wdXRzOiBQbHVnaW5QYXJhbXNbXSxcbiAgICAgIHBhZDogUGFkZGluZ1JlY2VpcHQsXG4gICAgICBwYXJhbXM6IFRpbWVQYXJhbXNcbiAgICApOiBQbHVnaW5QYXJhbXNbXSA9PiB7XG4gICAgICBjb25zdCB7c3RhcnQsIGVuZH0gPSBwYWQ7XG4gICAgICBjb25zdCBwYWRkZWRGcm9tQmVnaW5uaW5nID0gW107XG5cbiAgICAgIGlmIChzdGFydCkge1xuICAgICAgICBwYWRkZWRGcm9tQmVnaW5uaW5nLnB1c2goXG4gICAgICAgICAgLi4uZ2V0WmVyb2lzaElucHV0UGVyU2Vjb25kQmV0d2VlblJhbmdlKFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBzdGFydERhdGU6IHBhcmFtcy5zdGFydFRpbWUsXG4gICAgICAgICAgICAgIGVuZERhdGU6IHBhcnNlRGF0ZShpbnB1dHNbMF0udGltZXN0YW1wKSxcbiAgICAgICAgICAgICAgdGltZVN0ZXA6IHBhcmFtcy51cHNhbXBsaW5nUmVzb2x1dGlvbixcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBpbnB1dHNbMF1cbiAgICAgICAgICApXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBhZGRlZEFycmF5ID0gcGFkZGVkRnJvbUJlZ2lubmluZy5jb25jYXQoaW5wdXRzKTtcblxuICAgICAgaWYgKGVuZCkge1xuICAgICAgICBjb25zdCBsYXN0SW5wdXQgPSBpbnB1dHNbaW5wdXRzLmxlbmd0aCAtIDFdO1xuICAgICAgICBjb25zdCBsYXN0SW5wdXRFbmQgPSBwYXJzZURhdGUobGFzdElucHV0LnRpbWVzdGFtcCkucGx1cyh7XG4gICAgICAgICAgc2Vjb25kczogZXZhbChsYXN0SW5wdXQuZHVyYXRpb24pLFxuICAgICAgICB9KTtcbiAgICAgICAgcGFkZGVkQXJyYXkucHVzaChcbiAgICAgICAgICAuLi5nZXRaZXJvaXNoSW5wdXRQZXJTZWNvbmRCZXR3ZWVuUmFuZ2UoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHN0YXJ0RGF0ZTogbGFzdElucHV0RW5kLFxuICAgICAgICAgICAgICBlbmREYXRlOiBwYXJhbXMuZW5kVGltZSxcbiAgICAgICAgICAgICAgdGltZVN0ZXA6IHBhcmFtcy51cHNhbXBsaW5nUmVzb2x1dGlvbixcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBsYXN0SW5wdXRcbiAgICAgICAgICApXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBwYWRkZWRBcnJheTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQnJha2VzIGRvd24gdGhlIGdpdmVuIHJhbmdlIGJ5IDEgc2Vjb25kLCBhbmQgZ2VuZXJhdGVzIHplcm9pc2ggdmFsdWVzLlxuICAgICAqL1xuICAgIGNvbnN0IGdldFplcm9pc2hJbnB1dFBlclNlY29uZEJldHdlZW5SYW5nZSA9IChcbiAgICAgIHBhcmFtczogUGx1Z2luUGFyYW1zLFxuICAgICAgaW5wdXQ6IFBsdWdpblBhcmFtc1xuICAgICkgPT4ge1xuICAgICAgY29uc3QgYXJyYXk6IFBsdWdpblBhcmFtc1tdID0gW107XG4gICAgICB2YWxpZGF0ZUludGVydmFsRm9yUmVzYW1wbGUoXG4gICAgICAgIHBhcmFtcy5lbmREYXRlLmRpZmYocGFyYW1zLnN0YXJ0RGF0ZSkuYXMoJ3NlY29uZHMnKSxcbiAgICAgICAgcGFyYW1zLnRpbWVTdGVwLFxuICAgICAgICBJTkNPTVBBVElCTEVfUkVTT0xVVElPTl9XSVRIX0dBUFNcbiAgICAgICk7XG4gICAgICBjb25zdCBkYXRlUmFuZ2UgPSBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKFxuICAgICAgICBwYXJhbXMuc3RhcnREYXRlLFxuICAgICAgICBwYXJhbXMuZW5kRGF0ZVxuICAgICAgKTtcblxuICAgICAgZm9yIChjb25zdCBpbnRlcnZhbCBvZiBkYXRlUmFuZ2Uuc3BsaXRCeSh7c2Vjb25kOiBwYXJhbXMudGltZVN0ZXB9KSkge1xuICAgICAgICBhcnJheS5wdXNoKFxuICAgICAgICAgIGZpbGxXaXRoWmVyb2lzaElucHV0KFxuICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICAvLyBhcyBmYXIgYXMgSSBjYW4gdGVsbCwgc3RhcnQgd2lsbCBuZXZlciBiZSBudWxsXG4gICAgICAgICAgICAvLyBiZWNhdXNlIGlmIHdlIHBhc3MgYW4gaW52YWxpZCBzdGFydC9lbmREYXRlIHRvXG4gICAgICAgICAgICAvLyBJbnRlcnZhbCwgd2UgZ2V0IGEgemVybyBsZW5ndGggYXJyYXkgYXMgdGhlIHJhbmdlXG4gICAgICAgICAgICBpbnRlcnZhbC5zdGFydCB8fCBEYXRlVGltZS5pbnZhbGlkKCdub3QgZXhwZWN0ZWQgLSBzdGFydCBpcyBudWxsJyksXG4gICAgICAgICAgICBwYXJhbXMudGltZVN0ZXBcbiAgICAgICAgICApXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBhcnJheTtcbiAgICB9O1xuXG4gICAgLypcbiAgICAgKiBDaGVja3MgaWYgaW5wdXQncyB0aW1lc3RhbXAgaXMgaW5jbHVkZWQgaW4gZ2xvYmFsIHNwZWNpZmllZCBwZXJpb2QgdGhlbiBsZWF2ZXMgaXQsIG90aGVyd2lzZS5cbiAgICAgKi9cbiAgICBjb25zdCB0cmltSW5wdXRzQnlHbG9iYWxUaW1lbGluZSA9IChcbiAgICAgIGlucHV0czogUGx1Z2luUGFyYW1zW10sXG4gICAgICBwYXJhbXM6IFRpbWVQYXJhbXNcbiAgICApOiBQbHVnaW5QYXJhbXNbXSA9PlxuICAgICAgaW5wdXRzLnJlZHVjZSgoYWNjOiBQbHVnaW5QYXJhbXN