UNPKG

aws-delivlib

Version:

A fabulous library for defining continuous pipelines for building, testing and releasing code libraries.

117 lines • 17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldBlockPipeline = void 0; // eslint-disable-next-line @typescript-eslint/no-require-imports,import/no-extraneous-dependencies const ical = require('node-ical'); /** * Evaluates whether a deployment pipeline should have promotions suspended due to the imminent start of a blocked * time window. * * @param ical is an iCal document that describes "blocked" time windows (there needs to be an event only for times * during which promotions should not happen). * @param now is the reference time considered when assessing the need to block or not. * @param advanceMarginSec how many seconds from `now` should be free of any "blocked" time window for the pipeline to * not be blocked (defaults to 1 hour). * * @returns the events that represent the blocked time, or `undefined` if `now` is not "blocked". */ function shouldBlockPipeline(icalData, now = new Date(), advanceMarginSec = 3600) { validateTz(); const events = ical.parseICS(icalData.toString('utf8')); const blocks = containingEventsWithMargin(events, now, advanceMarginSec); return blocks.length > 0 ? blocks[0] : undefined; } exports.shouldBlockPipeline = shouldBlockPipeline; /** * A function to build a CalendarEvent given a start date and a duration. * * @param start a start date for the event * @param duration a duration for the event in milliseconds * @param summary a summary to apply to the event */ function buildEventForDuration(start, duration, summary) { const end = new Date(start.getTime() + duration); return { summary, start, end, datetype: 'date-time', type: 'VEVENT', }; } /** * If the event is not recurring (i.e. event.rrule is null or undefined), then * the event will be returned. * * If the event is recurring, this method calculates the recurring events surrounding * the provided date. If the date provided is equal to the start of an event, * the event for that date and the following event will be returend. If * CalendarEvent.rrule is not null, then the event is considered recurring. * * @param event a calendar event. * @param date the date for which the previous and next event should be returned. */ function flattenEvent(event, date) { if (event.rrule) { const events = []; // Calculate the duration of initial event in the recurring series. const duration = new Date(event.end).getTime() - new Date(event.start).getTime(); // Obtain the start date of the most recent event in the series, inclusive of // 'date' and calculate a new event based on the duration of the initial. const previousEventStart = event.rrule.before(date, true); if (previousEventStart) { events.push(buildEventForDuration(previousEventStart, duration, event.summary)); } // Obtain the start date of the next event in the series, exclusive of // 'date' and calculate a new event based on the duration of the initial. const nextEventStart = event.rrule.after(date, false); if (nextEventStart) { events.push(buildEventForDuration(nextEventStart, duration, event.summary)); } return events; } else { return [event]; } } function containingEventsWithMargin(events, date, advanceMarginSec) { const bufferedDate = new Date(date.getTime() + advanceMarginSec * 1000); return Object.values(events) .filter(e => e.type === 'VEVENT') .reduce((arr, e) => { arr.push(...flattenEvent(e, date)); return arr; }, []) .filter(e => overlaps(e, { start: date, end: bufferedDate })); } /** * Checks whether an event occurs within a specified time period, which should match the following: * |------------------<=========LEFT=========>-------------------------> * <WITHIN LEFT> * <OVERLAP AT START> * <OVERLAP AT END> * <===COMPLETELY INCLUDES LEFT=====> * |------------------<=========LEFT=========>-------------------------> * * @param left the first time window. * @param right the second time window. * * @returns true if `left` and `right` overlap */ function overlaps(left, right) { // Neutering out the milliseconds portions, so they don't interfere [left.start, left.end, right.start, right.end].forEach(d => d.setMilliseconds(0)); return isBetween(right.start, left.start, left.end) || isBetween(right.end, left.start, left.end) || isBetween(left.start, right.start, right.end) || isBetween(left.end, right.start, right.end); } function isBetween(date, left, right) { return date >= left && date <= right; } function validateTz() { if (new Date().getTimezoneOffset() !== 0) { throw new Error('Because of a bug in "node-ical", this module can only be used when the system time zone is set to UTC. Run this command again with "TZ=UTC"'); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZS13aW5kb3cuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0aW1lLXdpbmRvdy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSxtR0FBbUc7QUFDbkcsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBeUJsQzs7Ozs7Ozs7Ozs7R0FXRztBQUNILFNBQWdCLG1CQUFtQixDQUFDLFFBQXlCLEVBQUUsR0FBRyxHQUFHLElBQUksSUFBSSxFQUFFLEVBQUUsZ0JBQWdCLEdBQUcsSUFBSTtJQUN0RyxVQUFVLEVBQUUsQ0FBQztJQUNiLE1BQU0sTUFBTSxHQUFXLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ2hFLE1BQU0sTUFBTSxHQUFHLDBCQUEwQixDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztJQUN6RSxPQUFPLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUNuRCxDQUFDO0FBTEQsa0RBS0M7QUFFRDs7Ozs7O0dBTUc7QUFDSCxTQUFTLHFCQUFxQixDQUFDLEtBQVcsRUFBRSxRQUFnQixFQUFFLE9BQWU7SUFDM0UsTUFBTSxHQUFHLEdBQUcsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxHQUFHLFFBQVEsQ0FBQyxDQUFDO0lBQ2pELE9BQU87UUFDTCxPQUFPO1FBQ1AsS0FBSztRQUNMLEdBQUc7UUFDSCxRQUFRLEVBQUUsV0FBVztRQUNyQixJQUFJLEVBQUUsUUFBUTtLQUNmLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxTQUFTLFlBQVksQ0FBQyxLQUFvQixFQUFFLElBQVU7SUFDcEQsSUFBSSxLQUFLLENBQUMsS0FBSyxFQUFFO1FBQ2YsTUFBTSxNQUFN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QUFFLElBQVUsRUFBRSxLQUFXO0lBQ3BELE9BQU8sSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLElBQUksS0FBSyxDQUFDO0FBQ3ZDLENBQUM7QUFFRCxTQUFTLFVBQVU7SUFDakIsSUFBSSxJQUFJLElBQUksRUFBRSxDQUFDLGlCQUFpQixFQUFFLEtBQUssQ0FBQyxFQUFFO1FBQ3hDLE1BQU0sSUFBSSxLQUFLLENBQUMsNklBQTZJLENBQUMsQ0FBQztLQUNoSztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzXG5pbXBvcnQgeyBSUnVsZSB9IGZyb20gJ3JydWxlJztcbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzLGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llc1xuY29uc3QgaWNhbCA9IHJlcXVpcmUoJ25vZGUtaWNhbCcpO1xuXG4vKipcbiAqIEEgY2FsZW5kYXIgZXZlbnQgZGVzY3JpYmluZyBhIFwiYmxvY2tlZFwiIHRpbWUgd2luZG93LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENhbGVuZGFyRXZlbnQge1xuICAvKiogVGhlIGRlc2NyaXB0aW9uIG9mIHRoZSBldmVudCAqL1xuICBzdW1tYXJ5OiBzdHJpbmc7XG4gIC8qKiBUaGUgdGltZSBhdCB3aGljaCB0aGUgYmxvY2sgc3RhcnRzICovXG4gIHN0YXJ0OiBEYXRlO1xuICAvKiogVGhlIHRpbWUgYXQgd2hpY2ggdGhlIGJsb2NrIGVuZHMgKi9cbiAgZW5kOiBEYXRlO1xuICAvKiogVGhlIHRpbWUgYXQgd2hpY2ggdGhlIGV2ZW50IHdhcyBsYXN0IG1vZGlmaWVkLiAqL1xuICBkdHN0YW1wPzogRGF0ZTtcbiAgLyoqIFRoZSB0eXBlIG9mIGEgY2FsZW5kYXIgZXZlbnQgKi9cbiAgdHlwZTogJ1ZFVkVOVCcgfCBzdHJpbmc7XG4gIC8qKiBQYXJhbWV0ZXJzIHRvIHRoZSBldmVudCwgaWYgYW55LiAqL1xuICBwYXJhbXM/OiBhbnlbXTtcbiAgLyoqIFRoZSB0eXBlIG9mIHRoZSBib3VuZGFyaWVzIGZvciB0aGUgZXZlbnQgKi9cbiAgZGF0ZXR5cGU6ICdkYXRlLXRpbWUnO1xuICAvKiogQSByZWN1cnJlbmNlIHJ1bGUgZm9yIHRoZSBldmVudC4gKi9cbiAgcnJ1bGU/OiBSUnVsZTtcbn1cbnR5cGUgRXZlbnRzID0geyBbdXVpZDogc3RyaW5nXTogQ2FsZW5kYXJFdmVudCB9O1xuXG4vKipcbiAqIEV2YWx1YXRlcyB3aGV0aGVyIGEgZGVwbG95bWVudCBwaXBlbGluZSBzaG91bGQgaGF2ZSBwcm9tb3Rpb25zIHN1c3BlbmRlZCBkdWUgdG8gdGhlIGltbWluZW50IHN0YXJ0IG9mIGEgYmxvY2tlZFxuICogdGltZSB3aW5kb3cuXG4gKlxuICogQHBhcmFtIGljYWwgaXMgYW4gaUNhbCBkb2N1bWVudCB0aGF0IGRlc2NyaWJlcyBcImJsb2NrZWRcIiB0aW1lIHdpbmRvd3MgKHRoZXJlIG5lZWRzIHRvIGJlIGFuIGV2ZW50IG9ubHkgZm9yIHRpbWVzXG4gKiAgICAgICAgICAgICBkdXJpbmcgd2hpY2ggcHJvbW90aW9ucyBzaG91bGQgbm90IGhhcHBlbikuXG4gKiBAcGFyYW0gbm93ICBpcyB0aGUgcmVmZXJlbmNlIHRpbWUgY29uc2lkZXJlZCB3aGVuIGFzc2Vzc2luZyB0aGUgbmVlZCB0byBibG9jayBvciBub3QuXG4gKiBAcGFyYW0gYWR2YW5jZU1hcmdpblNlYyBob3cgbWFueSBzZWNvbmRzIGZyb20gYG5vd2Agc2hvdWxkIGJlIGZyZWUgb2YgYW55IFwiYmxvY2tlZFwiIHRpbWUgd2luZG93IGZvciB0aGUgcGlwZWxpbmUgdG9cbiAqICAgICAgICAgICAgIG5vdCBiZSBibG9ja2VkIChkZWZhdWx0cyB0byAxIGhvdXIpLlxuICpcbiAqIEByZXR1cm5zIHRoZSBldmVudHMgdGhhdCByZXByZXNlbnQgdGhlIGJsb2NrZWQgdGltZSwgb3IgYHVuZGVmaW5lZGAgaWYgYG5vd2AgaXMgbm90IFwiYmxvY2tlZFwiLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2hvdWxkQmxvY2tQaXBlbGluZShpY2FsRGF0YTogc3RyaW5nIHwgQnVmZmVyLCBub3cgPSBuZXcgRGF0ZSgpLCBhZHZhbmNlTWFyZ2luU2VjID0gMzYwMCk6IENhbGVuZGFyRXZlbnQgfCB1bmRlZmluZWQge1xuICB2YWxpZGF0ZVR6KCk7XG4gIGNvbnN0IGV2ZW50czogRXZlbnRzID0gaWNhbC5wYXJzZUlDUyhpY2FsRGF0YS50b1N0cmluZygndXRmOCcpKTtcbiAgY29uc3QgYmxvY2tzID0gY29udGFpbmluZ0V2ZW50c1dpdGhNYXJnaW4oZXZlbnRzLCBub3csIGFkdmFuY2VNYXJnaW5TZWMpO1xuICByZXR1cm4gYmxvY2tzLmxlbmd0aCA+IDAgPyBibG9ja3NbMF0gOiB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogQSBmdW5jdGlvbiB0byBidWlsZCBhIENhbGVuZGFyRXZlbnQgZ2l2ZW4gYSBzdGFydCBkYXRlIGFuZCBhIGR1cmF0aW9uLlxuICpcbiAqIEBwYXJhbSBzdGFydCBhIHN0YXJ0IGRhdGUgZm9yIHRoZSBldmVudFxuICogQHBhcmFtIGR1cmF0aW9uIGEgZHVyYXRpb24gZm9yIHRoZSBldmVudCBpbiBtaWxsaXNlY29uZHNcbiAqIEBwYXJhbSBzdW1tYXJ5IGEgc3VtbWFyeSB0byBhcHBseSB0byB0aGUgZXZlbnRcbiAqL1xuZnVuY3Rpb24gYnVpbGRFdmVudEZvckR1cmF0aW9uKHN0YXJ0OiBEYXRlLCBkdXJhdGlvbjogbnVtYmVyLCBzdW1tYXJ5OiBzdHJpbmcpOiBDYWxlbmRhckV2ZW50IHtcbiAgY29uc3QgZW5kID0gbmV3IERhdGUoc3RhcnQuZ2V0VGltZSgpICsgZHVyYXRpb24pO1xuICByZXR1cm4ge1xuICAgIHN1bW1hcnksXG4gICAgc3RhcnQsXG4gICAgZW5kLFxuICAgIGRhdGV0eXBlOiAnZGF0ZS10aW1lJyxcbiAgICB0eXBlOiAnVkVWRU5UJyxcbiAgfTtcbn1cblxuLyoqXG4gKiBJZiB0aGUgZXZlbnQgaXMgbm90IHJlY3VycmluZyAoaS5lLiBldmVudC5ycnVsZSBpcyBudWxsIG9yIHVuZGVmaW5lZCksIHRoZW5cbiAqIHRoZSBldmVudCB3aWxsIGJlIHJldHVybmVkLlxuICpcbiAqIElmIHRoZSBldmVudCBpcyByZWN1cnJpbmcsIHRoaXMgbWV0aG9kIGNhbGN1bGF0ZXMgdGhlIHJlY3VycmluZyBldmVudHMgc3Vycm91bmRpbmdcbiAqIHRoZSBwcm92aWRlZCBkYXRlLiBJZiB0aGUgZGF0ZSBwcm92aWRlZCBpcyBlcXVhbCB0byB0aGUgc3RhcnQgb2YgYW4gZXZlbnQsXG4gKiB0aGUgZXZlbnQgZm9yIHRoYXQgZGF0ZSBhbmQgdGhlIGZvbGxvd2luZyBldmVudCB3aWxsIGJlIHJldHVyZW5kLiBJZlxuICogQ2FsZW5kYXJFdmVudC5ycnVsZSBpcyBub3QgbnVsbCwgdGhlbiB0aGUgZXZlbnQgaXMgY29uc2lkZXJlZCByZWN1cnJpbmcuXG4gKlxuICogQHBhcmFtIGV2ZW50IGEgY2FsZW5kYXIgZXZlbnQuXG4gKiBAcGFyYW0gZGF0ZSB0aGUgZGF0ZSBmb3Igd2hpY2ggdGhlIHByZXZpb3VzIGFuZCBuZXh0IGV2ZW50IHNob3VsZCBiZSByZXR1cm5lZC5cbiAqL1xuZnVuY3Rpb24gZmxhdHRlbkV2ZW50KGV2ZW50OiBDYWxlbmRhckV2ZW50LCBkYXRlOiBEYXRlKTogQ2FsZW5kYXJFdmVudFtdIHtcbiAgaWYgKGV2ZW50LnJydWxlKSB7XG4gICAgY29uc3QgZXZlbnRzOiBDYWxlbmRhckV2ZW50W10gPSBbXTtcblxuICAgIC8vIENhbGN1bGF0ZSB0aGUgZHVyYXRpb24gb2YgaW5pdGlhbCBldmVudCBpbiB0aGUgcmVjdXJyaW5nIHNlcmllcy5cbiAgICBjb25zdCBkdXJhdGlvbiA9IG5ldyBEYXRlKGV2ZW50LmVuZCkuZ2V0VGltZSgpIC0gbmV3IERhdGUoZXZlbnQuc3RhcnQpLmdldFRpbWUoKTtcblxuICAgIC8vIE9idGFpbiB0aGUgc3RhcnQgZGF0ZSBvZiB0aGUgbW9zdCByZWNlbnQgZXZlbnQgaW4gdGhlIHNlcmllcywgaW5jbHVzaXZlIG9mXG4gICAgLy8gJ2RhdGUnIGFuZCBjYWxjdWxhdGUgYSBuZXcgZXZlbnQgYmFzZWQgb24gdGhlIGR1cmF0aW9uIG9mIHRoZSBpbml0aWFsLlxuICAgIGNvbnN0IHByZXZpb3VzRXZlbnRTdGFydCA9IGV2ZW50LnJydWxlLmJlZm9yZShkYXRlLCB0cnVlKTtcbiAgICBpZiAocHJldmlvdXNFdmVudFN0YXJ0KSB7XG4gICAgICBldmVudHMucHVzaChidWlsZEV2ZW50Rm9yRHVyYXRpb24ocHJldmlvdXNFdmVudFN0YXJ0LCBkdXJhdGlvbiwgZXZlbnQuc3VtbWFyeSkpO1xuICAgIH1cblxuICAgIC8vIE9idGFpbiB0aGUgc3RhcnQgZGF0ZSBvZiB0aGUgbmV4dCBldmVudCBpbiB0aGUgc2VyaWVzLCBleGNsdXNpdmUgb2ZcbiAgICAvLyAnZGF0ZScgYW5kIGNhbGN1bGF0ZSBhIG5ldyBldmVudCBiYXNlZCBvbiB0aGUgZHVyYXRpb24gb2YgdGhlIGluaXRpYWwuXG4gICAgY29uc3QgbmV4dEV2ZW50U3RhcnQgPSBldmVudC5ycnVsZS5hZnRlcihkYXRlLCBmYWxzZSk7XG4gICAgaWYgKG5leHRFdmVudFN0YXJ0KSB7XG4gICAgICBldmVudHMucHVzaChidWlsZEV2ZW50Rm9yRHVyYXRpb24obmV4dEV2ZW50U3RhcnQsIGR1cmF0aW9uLCBldmVudC5zdW1tYXJ5KSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGV2ZW50cztcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gW2V2ZW50XTtcbiAgfVxufVxuXG5mdW5jdGlvbiBjb250YWluaW5nRXZlbnRzV2l0aE1hcmdpbihldmVudHM6IEV2ZW50cywgZGF0ZTogRGF0ZSwgYWR2YW5jZU1hcmdpblNlYzogbnVtYmVyKTogQ2FsZW5kYXJFdmVudFtdIHtcbiAgY29uc3QgYnVmZmVyZWREYXRlID0gbmV3IERhdGUoZGF0ZS5nZXRUaW1lKCkgKyBhZHZhbmNlTWFyZ2luU2VjICogMV8wMDApO1xuXG4gIHJldHVybiBPYmplY3QudmFsdWVzKGV2ZW50cylcbiAgICAuZmlsdGVyKGUgPT4gZS50eXBlID09PSAnVkVWRU5UJylcbiAgICAucmVkdWNlKChhcnIsIGUpID0+IHtcbiAgICAgIGFyci5wdXNoKC4uLmZsYXR0ZW5FdmVudChlLCBkYXRlKSk7XG4gICAgICByZXR1cm4gYXJyO1xuICAgIH0sIFtdIGFzIENhbGVuZGFyRXZlbnRbXSlcbiAgICAuZmlsdGVyKGUgPT4gb3ZlcmxhcHMoZSwgeyBzdGFydDogZGF0ZSwgZW5kOiBidWZmZXJlZERhdGUgfSkpO1xufVxuXG4vKipcbiAqIENoZWNrcyB3aGV0aGVyIGFuIGV2ZW50IG9jY3VycyB3aXRoaW4gYSBzcGVjaWZpZWQgdGltZSBwZXJpb2QsIHdoaWNoIHNob3VsZCBtYXRjaCB0aGUgZm9sbG93aW5nOlxuICogfC0tLS0tLS0tLS0tLS0tLS0tLTw9PT09PT09PT1MRUZUPT09PT09PT09Pi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+XG4gKiAgICAgICAgICAgICAgICAgICAgICAgICA8V0lUSElOIExFRlQ+XG4gKiAgICAgICAgICAgIDxPVkVSTEFQIEFUIFNUQVJUPlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxPVkVSTEFQIEFUIEVORD5cbiAqICAgICAgICAgICAgICAgPD09PUNPTVBMRVRFTFkgSU5DTFVERVMgTEVGVD09PT09PlxuICogfC0tLS0tLS0tLS0tLS0tLS0tLTw9PT09PT09PT1MRUZUPT09PT09PT09Pi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+XG4gKlxuICogQHBhcmFtIGxlZnQgIHRoZSBmaXJzdCB0aW1lIHdpbmRvdy5cbiAqIEBwYXJhbSByaWdodCB0aGUgc2Vjb25kIHRpbWUgd2luZG93LlxuICpcbiAqIEByZXR1cm5zIHRydWUgaWYgYGxlZnRgIGFuZCBgcmlnaHRgIG92ZXJsYXBcbiAqL1xuZnVuY3Rpb24gb3ZlcmxhcHMobGVmdDogeyBzdGFydDogRGF0ZTsgZW5kOiBEYXRlIH0sIHJpZ2h0OiB7IHN0YXJ0OiBEYXRlOyBlbmQ6IERhdGUgfSk6IGJvb2xlYW4ge1xuICAvLyBOZXV0ZXJpbmcgb3V0IHRoZSBtaWxsaXNlY29uZHMgcG9ydGlvbnMsIHNvIHRoZXkgZG9uJ3QgaW50ZXJmZXJlXG4gIFtsZWZ0LnN0YXJ0LCBsZWZ0LmVuZCwgcmlnaHQuc3RhcnQsIHJpZ2h0LmVuZF0uZm9yRWFjaChkID0+IGQuc2V0TWlsbGlzZWNvbmRzKDApKTtcblxuICByZXR1cm4gaXNCZXR3ZWVuKHJpZ2h0LnN0YXJ0LCBsZWZ0LnN0YXJ0LCBsZWZ0LmVuZClcbiAgICB8fCBpc0JldHdlZW4ocmlnaHQuZW5kLCBsZWZ0LnN0YXJ0LCBsZWZ0LmVuZClcbiAgICB8fCBpc0JldHdlZW4obGVmdC5zdGFydCwgcmlnaHQuc3RhcnQsIHJpZ2h0LmVuZClcbiAgICB8fCBpc0JldHdlZW4obGVmdC5lbmQsIHJpZ2h0LnN0YXJ0LCByaWdodC5lbmQpO1xufVxuXG5mdW5jdGlvbiBpc0JldHdlZW4oZGF0ZTogRGF0ZSwgbGVmdDogRGF0ZSwgcmlnaHQ6IERhdGUpOiBib29sZWFuIHtcbiAgcmV0dXJuIGRhdGUgPj0gbGVmdCAmJiBkYXRlIDw9IHJpZ2h0O1xufVxuXG5mdW5jdGlvbiB2YWxpZGF0ZVR6KCkge1xuICBpZiAobmV3IERhdGUoKS5nZXRUaW1lem9uZU9mZnNldCgpICE9PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdCZWNhdXNlIG9mIGEgYnVnIGluIFwibm9kZS1pY2FsXCIsIHRoaXMgbW9kdWxlIGNhbiBvbmx5IGJlIHVzZWQgd2hlbiB0aGUgc3lzdGVtIHRpbWUgem9uZSBpcyBzZXQgdG8gVVRDLiBSdW4gdGhpcyBjb21tYW5kIGFnYWluIHdpdGggXCJUWj1VVENcIicpO1xuICB9XG59XG4iXX0=