@grnsft/if
Version:
Impact Framework
371 lines • 54.3 kB
JavaScript
"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