UNPKG

mage-validator

Version:

Validation utility for MAGE user commands & topics (TypeScript)

407 lines 40.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const classTransformer = require("class-transformer"); const classValidator = require("class-validator"); const isObject = require("isobject"); const errors_1 = require("../errors"); /** * Validated topic * * Please note that you should import the default * of this module, not this class directly. * * Good: * * ```typescript * import ValidatedTopic from 'mage-validator' * ``` * * Bad: * * ```typescript * import { ValidatedTopic } from 'mage-validator' * ``` * * While the second line will work, it will provide you with * less type safety than the first import example. * * @export * @abstract * @class ValidatedTopic */ class ValidatedTopic { /** * Creates an instance of ValidatedTopic. * * @param {mage.core.IState} state * * @memberof ValidatedTopic */ constructor(state) { this.setTopic(this.constructor.name); if (state) { this.setState(state); } } /** * Return the current class name * * @static * @returns * * @memberof ValidatedTopic */ static getClassName() { /* istanbul ignore next */ if (!this._className) { return this.toString().split('(' || /s+/)[0].split(' ' || /s+/)[1]; } return this._className; } /** * Explicitly set the name of this class * * @static * @param {string} name * * @memberof ValidatedTopic */ static setClassName(name) { this._className = name; } /** * Create an instance from a generic object * * @static * @param {mage.core.IState} state * @param {*} data * @returns * * @memberof ValidatedTopic */ static async create(state, index, data) { let instance; if (data) { if (!isObject(data)) { throw new Error(`Received data is not an object (received ${JSON.stringify(data)})`); } instance = classTransformer.plainToClass(this, data); } else { instance = new this(); } instance.setTopic(this.getClassName()); instance.setState(state); await instance.setIndex(index); return instance; } /** * Utility method used to promisify archivist calls * * @static * @param {*} state * @param {*} method * @param {any[]} args * @param {Function} run * @returns * * @memberof ValidatedTopic */ static async execute(state, method, args, run) { return new Promise((resolve, reject) => { state.archivist[method](...args, (error, data) => { if (error) { return reject(error); } resolve(run(data)); }); }); } /** * Get a topic instance from backend vault(s) * * Mostly a wrapper around state.archivist.get. Note that * instead of using the `optional` option directly with this call, * you should consider using `tryGet` instead, which will trigger a * compile error if you do not check for an undefined return value. * * @static * @param {mage.core.IState} state * @param {archivist.IArchivistIndex} index * @param {archivist.IArchivistGetOptions} [options] * @returns * * @memberof ValidatedTopic */ static async get(state, index, options) { const topicName = this.getClassName(); return this.execute(state, 'get', [ topicName, index, options ], async (data) => { // If optional: true, and no data was found if (options && options.optional && !data) { return undefined; } return this.create(state, index, data); }); } /** * Try to get a topic instance from backend vault(s) * * Same as `get`, but is also states that `undefined' might be returned instead. * You should consider using this method instead of calling `get` directly * with the `optional: true` option. * * @static * @param {mage.core.IState} state * @param {archivist.IArchivistIndex} index * @param {archivist.IArchivistGetOptions} [options] * @returns * * @memberof ValidatedTopic */ static async tryGet(state, index, options) { if (!options) { options = {}; } options.optional = true; return this.get(state, index, options); } /** * Get instances from backend vault(s) * * Mostly a wrapper around state.archivist.mget. * * @static * @param {mage.core.IState} state * @param {archivist.IArchivistQuery[]} queries * @param {archivist.IArchivistGetOptions} [options] * @returns * * @memberof ValidatedTopic */ static async mget(state, indexes, options) { const topic = this.getClassName(); const queries = indexes.map((index) => ({ topic, index })); return this.execute(state, 'mget', [ queries, options ], async (list) => { const instances = []; for (const [i, data] of list.entries()) { if (!data) { continue; } const index = queries[i].index; const instance = await this.create(state, index, data); instances.push(instance); } return instances; }); } // // Todo: // // public static async mget(state: mage.core.IState, queries: archivist.INamedArchivistQuery, options?: archivist.IArchivistGetOptions) { // return new Promise((resolve, reject) => { // state.archivist.mget(queries, options, async (error, list) => { // if (error) { // return reject(error) // } // const instances = Object.keys(list).reduce((instances, name) => { // if (list[name]) { // instances[name] = this.create(state, queries[name], list[name]) // } // return instances // }, {}) // resolve(instances) // }) // }) // } /** * List all keys * * @static * @param {mage.core.IState} state * @param {archivist.IArchivistIndex} partialIndex * @param {archivist.IArchivistGetOptions} [options] * @returns * * @memberof ValidatedTopic */ static async list(state, partialIndex, options) { const topicName = this.getClassName(); return this.execute(state, 'list', [ topicName, partialIndex, options, ], (indexes) => indexes); } /** * Query data by partial index * * Essentially wraps state.archivist.list, then fetches the data * for each keys using state.archivist.mget. * * @static * @param {mage.core.IState} state * @param {archivist.IArchivistIndex} partialIndex * @param {archivist.IArchivistGetOptions} [options] * @returns * * @memberof ValidatedTopic */ static async query(state, partialIndex, options) { const topicName = this.getClassName(); return this.execute(state, 'list', [ topicName, partialIndex, options ], async (indexes) => { return this.mget(state, indexes, options); }); } /** * Get the topic for this instance * * @memberof ValidatedTopic */ getTopic() { return this._topic; } /** * Set the topic name for this instance * * @param {string} topicName * * @memberof ValidatedTopic */ setTopic(topicName) { Object.defineProperty(this, '_topic', { value: topicName, configurable: true }); } /** * Get the topic of that instance * * Should always return the class name of the instance. * * @returns * * @memberof ValidatedTopic */ getIndex() { return this._index; } /** * Set the index of this topic instance * * @param {archivist.IArchivistIndex} index * * @memberof ValidatedTopic */ async setIndex(indexData) { const Class = this.constructor; const Index = Class.indexType; const index = new Index(); for (const field of Class.index) { index[field] = indexData[field]; } const errors = await classValidator.validate(index); if (errors.length > 0) { throw new errors_1.ValidationError('Index validation failed', 'server', { topic: this.getTopic(), index: this.getIndex() }, errors); } Object.defineProperty(this, '_index', { value: index, configurable: true }); } /** * Retrieve the state object attached to the instance */ getState() { return this._state; } /** * Set the state this topic instance will be using. */ setState(state) { Object.defineProperty(this, '_state', { value: state, configurable: true }); } /** * Retrieve the actual data for this instance * * This should essentially be the same as simply accessing data on the * instance itself. */ getData() { return this; } /** * Record an add operation on the instance's state * * Essentially a wrapper for state.archivist.add */ async add(mediaType, encoding, expirationTime) { await this.validate('Validation failed on add'); return this.getState().archivist.add(this.getTopic(), this.getIndex(), this.getData(), mediaType, encoding, expirationTime); } /** * Record a set operation on the instance's state * * Essentially a wrapper for state.archivist.set. */ async set(mediaType, encoding, expirationTime) { await this.validate('Validation failed on set'); return this.getState().archivist.set(this.getTopic(), this.getIndex(), this.getData(), mediaType, encoding, expirationTime); } /** * Record a touch operation on the instance's state * * Essentially a wrapper for state.archivist.touch. */ async touch(expirationTime) { await this.validate('Validation failed on touch'); return this.getState().archivist.touch(this.getTopic(), this.getIndex(), expirationTime); } /** * Record a delete operation on the instance's state */ del() { return this.getState().archivist.del(this.getTopic(), this.getIndex()); } /** * Validate the current instance */ async validate(errorMessage, code) { const errors = await classValidator.validate(this); if (errors.length > 0) { this.raiseValidationError(errors, errorMessage, code); } } /** * Throw a ValidateError including relevant details */ raiseValidationError(errors, errorMessage, code) { const state = this.getState(); throw new errors_1.ValidationError(errorMessage || 'Validation failed', code || 'server', { actorId: state.actorId, userCommand: state.description, topic: this.getTopic(), index: this.getIndex() }, errors); } } ValidatedTopic.mediaType = 'application/json'; ValidatedTopic.vaults = {}; exports.default = ValidatedTopic; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVmFsaWRhdGVkVG9waWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xhc3Nlcy9WYWxpZGF0ZWRUb3BpYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHNEQUFxRDtBQUNyRCxrREFBaUQ7QUFDakQscUNBQXFDO0FBS3JDLHNDQUEyQztBQThEM0M7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRztBQUNIO0lBcVNFOzs7Ozs7T0FNRztJQUNILFlBQVksS0FBd0I7UUFDbEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBRXBDLElBQUksS0FBSyxFQUFFO1lBQ1QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQTtTQUNyQjtJQUNILENBQUM7SUExU0Q7Ozs7Ozs7T0FPRztJQUNJLE1BQU0sQ0FBQyxZQUFZO1FBQ3hCLDBCQUEwQjtRQUMxQixJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRTtZQUNwQixPQUFPLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxLQUFLLENBQUUsR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBRSxHQUFHLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7U0FDckU7UUFFRCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUE7SUFDeEIsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSSxNQUFNLENBQUMsWUFBWSxDQUFDLElBQVk7UUFDckMsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUE7SUFDeEIsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNJLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUV4QixLQUF1QixFQUN2QixLQUFRLEVBQ1IsSUFBbUI7UUFFbkIsSUFBSSxRQUFRLENBQUE7UUFFWixJQUFJLElBQUksRUFBRTtZQUNSLElBQUksQ0FBQyxRQUFRLENB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RUFBRSxJQUFJLENBQUMsQ0FBQTtTQUN0RDtJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLG9CQUFvQixDQUFDLE1BQWEsRUFBRSxZQUFxQixFQUFFLElBQWE7UUFDN0UsTUFBTSxLQUFLLEdBQVMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFBO1FBRW5DLE1BQU0sSUFBSSx3QkFBZSxDQUFDLFlBQVksSUFBSSxtQkFBbUIsRUFBRSxJQUFJLElBQUksUUFBUSxFQUFFO1lBQy9FLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTztZQUN0QixXQUFXLEVBQUUsS0FBSyxDQUFDLFdBQVc7WUFDOUIsS0FBSyxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUU7WUFDdEIsS0FBSyxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUU7U0FDdkIsRUFBRSxNQUFNLENBQUMsQ0FBQTtJQUNaLENBQUM7O0FBNWNzQix3QkFBUyxHQUFXLGtCQUFrQixDQUFBO0FBR3RDLHFCQUFNLEdBQUcsRUFBRSxDQUFBO0FBSnBDLGlDQThjQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNsYXNzVHJhbnNmb3JtZXIgZnJvbSAnY2xhc3MtdHJhbnNmb3JtZXInXG5pbXBvcnQgKiBhcyBjbGFzc1ZhbGlkYXRvciBmcm9tICdjbGFzcy12YWxpZGF0b3InXG5pbXBvcnQgaXNPYmplY3QgPSByZXF1aXJlKCdpc29iamVjdCcpXG5cbmltcG9ydCB7IGFyY2hpdmlzdCB9IGZyb20gJ21hZ2UnXG5pbXBvcnQgKiBhcyBtYWdlIGZyb20gJ21hZ2UnXG5cbmltcG9ydCB7IFZhbGlkYXRpb25FcnJvciB9IGZyb20gJy4uL2Vycm9ycydcblxuLy8gdHNsaW50OmRpc2FibGU6Y29tcGxldGVkLWRvY3NcbnR5cGUgS2V5ID0gc3RyaW5nIHwgbnVtYmVyIHwgc3ltYm9sXG5cbmV4cG9ydCB0eXBlIERpZmY8VCBleHRlbmRzIEtleSwgVSBleHRlbmRzIEtleT4gPSAoe1tQIGluIFRdOiBQIH0gJiB7W1AgaW4gVV06IG5ldmVyIH0gJiB7IFt4OiBzdHJpbmddOiBuZXZlciB9KVtUXVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZTpjb21wbGV0ZWQtZG9jc1xuZXhwb3J0IHR5cGUgT21pdDxULCBLIGV4dGVuZHMga2V5b2YgVD4gPSBQaWNrPFBhcnRpYWw8VD4sIERpZmY8a2V5b2YgVCwgSz4+XG5cbi8qKlxuICogQW5vbnltb3VzIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiBhIHZhbGlkYXRlZCB0b3BpY1xuICpcbiAqIEdlbmVyYWxseSBwYXNzZWQgdG8gVG9waWMuY3JlYXRlKClcbiAqL1xuZXhwb3J0IHR5cGUgVG9waWNEYXRhPFQgZXh0ZW5kcyBWYWxpZGF0ZWRUb3BpYz4gPSBPbWl0PFQsIGtleW9mIFZhbGlkYXRlZFRvcGljPlxuLyoqXG4gKiBQYXJ0aWFsIGluZGV4IHR5cGVcbiAqL1xuZXhwb3J0IHR5cGUgUGFydGlhbEluZGV4PEk+ID0ge1xuICBbSyBpbiBrZXlvZiBJXT86IHN0cmluZ1xufVxuXG4vKipcbiAqIFRoZSBJU3RhdGljVGhpcyBpbnRlcmZhY2UgaXMgcmVxdWlyZWRcbiAqIGZvciB1cyB0byBiZSBhYmxlIHRvIGNyZWF0ZSBzdGF0aWMgZmFjdG9yeSBmdW5jdGlvbnNcbiAqIHRoYXQgcmV0dXJuIGEgcHJvcGVybHkgdHlwZWQgb3V0cHV0LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIElTdGF0aWNUaGlzPEksIFQ+IHtcbiAgLy8gVG9kbzogYW55IHNob3VsZCBiZSBJIVxuICBpbmRleFR5cGU6IHsgbmV3KCk6IGFueSB9LFxuXG4gIG5ldyAoKTogVCxcblxuICBnZXRDbGFzc05hbWUoKTogc3RyaW5nLFxuXG4gIGV4ZWN1dGU8SSwgVCwgUj4oXG4gICAgdGhpczogSVN0YXRpY1RoaXM8SSwgVD4sXG4gICAgc3RhdGU6IGFueSxcbiAgICBtZXRob2Q6IGFueSxcbiAgICBhcmdzOiBhbnlbXSxcbiAgICBydW46IChkYXRhOiBhbnkpID0+IGFueSk6IFByb21pc2U8Uj4sXG5cbiAgY3JlYXRlPFQgZXh0ZW5kcyBWYWxpZGF0ZWRUb3BpYz4oXG4gICAgdGhpczogSVN0YXRpY1RoaXM8SSwgVD4sXG4gICAgc3RhdGU6IG1hZ2UuY29yZS5JU3RhdGUsXG4gICAgaW5kZXg6IEksXG4gICAgZGF0YT86IGFueSk6IFByb21pc2U8VD4sXG5cbiAgZ2V0PFQgZXh0ZW5kcyBWYWxpZGF0ZWRUb3BpYz4oXG4gICAgdGhpczogSVN0YXRpY1RoaXM8SSwgVD4sXG4gICAgc3RhdGU6IG1hZ2UuY29yZS5JU3RhdGUsXG4gICAgaW5kZXg6IEksXG4gICAgb3B0aW9ucz86IGFyY2hpdmlzdC5JQXJjaGl2aXN0R2V0T3B0aW9ucyk6IFByb21pc2U8VD5cblxuICBtZ2V0PFQgZXh0ZW5kcyBWYWxpZGF0ZWRUb3BpYz4oXG4gICAgdGhpczogSVN0YXRpY1RoaXM8SSwgVD4sXG4gICAgc3RhdGU6IG1hZ2UuY29yZS5JU3RhdGUsXG4gICAgaW5kZXhlczogSVtdLFxuICAgIG9wdGlvbnM/OiBhcmNoaXZpc3QuSUFyY2hpdmlzdEdldE9wdGlvbnMpOiBQcm9taXNlPFRbXT5cbn1cblxuLyoqXG4gKiBWYWxpZGF0ZWQgdG9waWNcbiAqXG4gKiBQbGVhc2Ugbm90ZSB0aGF0IHlvdSBzaG91bGQgaW1wb3J0IHRoZSBkZWZhdWx0XG4gKiBvZiB0aGlzIG1vZHVsZSwgbm90IHRoaXMgY2xhc3MgZGlyZWN0bHkuXG4gKlxuICogR29vZDpcbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgVmFsaWRhdGVkVG9waWMgZnJvbSAnbWFnZS12YWxpZGF0b3InXG4gKiBgYGBcbiAqXG4gKiBCYWQ6XG4gKlxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgVmFsaWRhdGVkVG9waWMgfSBmcm9tICdtYWdlLXZhbGlkYXRvcidcbiAqIGBgYFxuICpcbiAqIFdoaWxlIHRoZSBzZWNvbmQgbGluZSB3aWxsIHdvcmssIGl0IHdpbGwgcHJvdmlkZSB5b3Ugd2l0aFxuICogbGVzcyB0eXBlIHNhZmV0eSB0aGFuIHRoZSBmaXJzdCBpbXBvcnQgZXhhbXBsZS5cbiAqXG4gKiBAZXhwb3J0XG4gKiBAYWJzdHJhY3RcbiAqIEBjbGFzcyBWYWxpZGF0ZWRUb3BpY1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBWYWxpZGF0ZWRUb3BpYyB7XG4gIHB1YmxpYyBzdGF0aWMgcmVhZG9ubHkgbWVkaWFUeXBlOiBzdHJpbmcgPSAnYXBwbGljYXRpb24vanNvbidcbiAgcHVibGljIHN0YXRpYyByZWFkb25seSBpbmRleDogc3RyaW5nW11cbiAgcHVibGljIHN0YXRpYyByZWFkb25seSBpbmRleFR5cGU6IGFueVxuICBwdWJsaWMgc3RhdGljIHJlYWRvbmx5IHZhdWx0cyA9IHt9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgX2NsYXNzTmFtZTogc3RyaW5nXG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgY3VycmVudCBjbGFzcyBuYW1lXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQHJldHVybnNcbiAgICpcbiAgICogQG1lbWJlcm9mIFZhbGlkYXRlZFRvcGljXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGdldENsYXNzTmFtZSgpOiBzdHJpbmcge1xuICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICovXG4gICAgaWYgKCF0aGlzLl9jbGFzc05hbWUpIHtcbiAgICAgIHJldHVybiB0aGlzLnRvU3RyaW5nKCkuc3BsaXQgKCcoJyB8fCAvcysvKVswXS5zcGxpdCAoJyAnIHx8IC9zKy8pWzFdXG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX2NsYXNzTmFtZVxuICB9XG5cbiAgLyoqXG4gICAqIEV4cGxpY2l0bHkgc2V0IHRoZSBuYW1lIG9mIHRoaXMgY2xhc3NcbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgKlxuICAgKiBAbWVtYmVyb2YgVmFsaWRhdGVkVG9waWNcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgc2V0Q2xhc3NOYW1lKG5hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuX2NsYXNzTmFtZSA9IG5hbWVcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGUgYW4gaW5zdGFuY2UgZnJvbSBhIGdlbmVyaWMgb2JqZWN0XG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQHBhcmFtIHttYWdlLmNvcmUuSVN0YXRlfSBzdGF0ZVxuICAgKiBAcGFyYW0geyp9IGRhdGFcbiAgICogQHJldHVybnNcbiAgICpcbiAgICogQG1lbWJlcm9mIFZhbGlkYXRlZFRvcGljXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIGNyZWF0ZTxJIGV4dGVuZHMgYXJjaGl2aXN0LklBcmNoaXZpc3RJbmRleCwgVCBleHRlbmRzIFZhbGlkYXRlZFRvcGljPihcbiAgICB0aGlzOiBJU3RhdGljVGhpczxJLCBUPixcbiAgICBzdGF0ZTogbWFnZS5jb3JlLklTdGF0ZSxcbiAgICBpbmRleDogSSxcbiAgICBkYXRhPzogVG9waWNEYXRhPFQ+KTogUHJvbWlzZTxUPiB7XG5cbiAgICBsZXQgaW5zdGFuY2VcblxuICAgIGlmIChkYXRhKSB7XG4gICAgICBpZiAoIWlzT2JqZWN0KGRhdGEpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgUmVjZWl2ZWQgZGF0YSBpcyBub3QgYW4gb2JqZWN0IChyZWNlaXZlZCAke0pTT04uc3RyaW5naWZ5KGRhdGEpfSlgKVxuICAgICAgfVxuXG4gICAgICBpbnN0YW5jZSA9IGNsYXNzVHJhbnNmb3JtZXIucGxhaW5Ub0NsYXNzPFQsIG9iamVjdD4odGhpcywgZGF0YSlcbiAgICB9IGVsc2Uge1xuICAgICAgaW5zdGFuY2UgPSBuZXcgdGhpcygpXG4gICAgfVxuXG4gICAgaW5zdGFuY2Uuc2V0VG9waWModGhpcy5nZXRDbGFzc05hbWUoKSlcbiAgICBpbnN0YW5jZS5zZXRTdGF0ZShzdGF0ZSlcbiAgICBhd2FpdCBpbnN0YW5jZS5zZXRJbmRleChpbmRleClcblxuICAgIHJldHVybiBpbnN0YW5jZVxuICB9XG5cbiAgLyoqXG4gICAqIFV0aWxpdHkgbWV0aG9kIHVzZWQgdG8gcHJvbWlzaWZ5IGFyY2hpdmlzdCBjYWxsc1xuICAgKlxuICAgKiBAc3RhdGljXG4gICAqIEBwYXJhbSB7Kn0gc3RhdGVcbiAgICogQHBhcmFtIHsqfSBtZXRob2RcbiAgICogQHBhcmFtIHthbnlbXX0gYXJnc1xuICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBydW5cbiAgICogQHJldHVybnNcbiAgICpcbiAgICogQG1lbWJlcm9mIFZhbGlkYXRlZFRvcGljXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIGV4ZWN1dGU8SSwgVCwgUj4oXG4gICAgdGhpczogSVN0YXRpY1RoaXM8SSwgVD4sXG4gICAgc3RhdGU6IGFueSxcbiAgICBtZXRob2Q6IGFueSxcbiAgICBhcmdzOiBhbnlbXSxcbiAgICBydW46IChkYXRhOiBhbnkpID0+IGFueSk6IFByb21pc2U8Uj4ge1xuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPFI+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHN0YXRlLmFyY2hpdmlzdFttZXRob2RdKC4uLmFyZ3MsIChlcnJvcjogRXJyb3IsIGRhdGE6IFQpID0+IHtcbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgcmV0dXJuIHJlamVjdChlcnJvcilcbiAgICAgICAgfVxuXG4gICAgICAgIHJlc29sdmUocnVuKGRhdGEpKVxuICAgICAgfSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIHRvcGljIGluc3RhbmNlIGZyb20gYmFja2VuZCB2YXVsdChzKVxuICAgKlxuICAgKiBNb3N0bHkgYSB3cmFwcGVyIGFyb3VuZCBzdGF0ZS5hcmNoaXZpc3QuZ2V0LiBOb3RlIHRoYXRcbiAgICogaW5zdGVhZCBvZiB1c2luZyB0aGUgYG9wdGlvbmFsYCBvcHRpb24gZGlyZWN0bHkgd2l0aCB0aGlzIGNhbGwsXG4gICAqIHlvdSBzaG91bGQgY29uc2lkZXIgdXNpbmcgYHRyeUdldGAgaW5zdGVhZCwgd2hpY2ggd2lsbCB0cmlnZ2VyIGFcbiAgICogY29tcGlsZSBlcnJvciBpZiB5b3UgZG8gbm90IGNoZWNrIGZvciBhbiB1bmRlZmluZWQgcmV0dXJuIHZhbHVlLlxuICAgKlxuICAgKiBAc3RhdGljXG4gICAqIEBwYXJhbSB7bWFnZS5jb3JlLklTdGF0ZX0gc3RhdGVcbiAgICogQHBhcmFtIHthcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4fSBpbmRleFxuICAgKiBAcGFyYW0ge2FyY2hpdmlzdC5JQXJjaGl2aXN0R2V0T3B0aW9uc30gW29wdGlvbnNdXG4gICAqIEByZXR1cm5zXG4gICAqXG4gICAqIEBtZW1iZXJvZiBWYWxpZGF0ZWRUb3BpY1xuICAgKi9cbiAgcHVibGljIHN0YXRpYyBhc3luYyBnZXQ8SSBleHRlbmRzIGFyY2hpdmlzdC5JQXJjaGl2aXN0SW5kZXgsIFQgZXh0ZW5kcyBWYWxpZGF0ZWRUb3BpYz4oXG4gICAgdGhpczogSVN0YXRpY1RoaXM8SSwgVD4sXG4gICAgc3RhdGU6IG1hZ2UuY29yZS5JU3RhdGUsXG4gICAgaW5kZXg6IEksXG4gICAgb3B0aW9ucz86IGFyY2hpdmlzdC5JQXJjaGl2aXN0R2V0T3B0aW9ucyk6IFByb21pc2U8VD4ge1xuXG4gICAgY29uc3QgdG9waWNOYW1lID0gdGhpcy5nZXRDbGFzc05hbWUoKVxuXG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0ZTxJLCBULCBUPihzdGF0ZSwgJ2dldCcsIFtcbiAgICAgIHRvcGljTmFtZSxcbiAgICAgIGluZGV4LFxuICAgICAgb3B0aW9uc1xuICAgIF0sIGFzeW5jIChkYXRhOiBhbnkpID0+IHtcbiAgICAgIC8vIElmIG9wdGlvbmFsOiB0cnVlLCBhbmQgbm8gZGF0YSB3YXMgZm91bmRcbiAgICAgIGlmIChvcHRpb25zICYmIG9wdGlvbnMub3B0aW9uYWwgJiYgIWRhdGEpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcy5jcmVhdGUoc3RhdGUsIGluZGV4LCBkYXRhKVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogVHJ5IHRvIGdldCBhIHRvcGljIGluc3RhbmNlIGZyb20gYmFja2VuZCB2YXVsdChzKVxuICAgKlxuICAgKiBTYW1lIGFzIGBnZXRgLCBidXQgaXMgYWxzbyBzdGF0ZXMgdGhhdCBgdW5kZWZpbmVkJyBtaWdodCBiZSByZXR1cm5lZCBpbnN0ZWFkLlxuICAgKiBZb3Ugc2hvdWxkIGNvbnNpZGVyIHVzaW5nIHRoaXMgbWV0aG9kIGluc3RlYWQgb2YgY2FsbGluZyBgZ2V0YCBkaXJlY3RseVxuICAgKiB3aXRoIHRoZSBgb3B0aW9uYWw6IHRydWVgIG9wdGlvbi5cbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAcGFyYW0ge21hZ2UuY29yZS5JU3RhdGV9IHN0YXRlXG4gICAqIEBwYXJhbSB7YXJjaGl2aXN0LklBcmNoaXZpc3RJbmRleH0gaW5kZXhcbiAgICogQHBhcmFtIHthcmNoaXZpc3QuSUFyY2hpdmlzdEdldE9wdGlvbnN9IFtvcHRpb25zXVxuICAgKiBAcmV0dXJuc1xuICAgKlxuICAgKiBAbWVtYmVyb2YgVmFsaWRhdGVkVG9waWNcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgYXN5bmMgdHJ5R2V0PEkgZXh0ZW5kcyBhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4LCBUIGV4dGVuZHMgVmFsaWRhdGVkVG9waWM+KFxuICAgIHRoaXM6IElTdGF0aWNUaGlzPEksIFQ+LFxuICAgIHN0YXRlOiBtYWdlLmNvcmUuSVN0YXRlLFxuICAgIGluZGV4OiBJLFxuICAgIG9wdGlvbnM/OiBhcmNoaXZpc3QuSUFyY2hpdmlzdEdldE9wdGlvbnMpOiBQcm9taXNlPFQgfCB1bmRlZmluZWQ+IHtcblxuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgb3B0aW9ucyA9IHt9XG4gICAgfVxuXG4gICAgb3B0aW9ucy5vcHRpb25hbCA9IHRydWVcblxuICAgIHJldHVybiB0aGlzLmdldChzdGF0ZSwgaW5kZXgsIG9wdGlvbnMpXG4gIH1cblxuICAvKipcbiAgICogR2V0IGluc3RhbmNlcyBmcm9tIGJhY2tlbmQgdmF1bHQocylcbiAgICpcbiAgICogTW9zdGx5IGEgd3JhcHBlciBhcm91bmQgc3RhdGUuYXJjaGl2aXN0Lm1nZXQuXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQHBhcmFtIHttYWdlLmNvcmUuSVN0YXRlfSBzdGF0ZVxuICAgKiBAcGFyYW0ge2FyY2hpdmlzdC5JQXJjaGl2aXN0UXVlcnlbXX0gcXVlcmllc1xuICAgKiBAcGFyYW0ge2FyY2hpdmlzdC5JQXJjaGl2aXN0R2V0T3B0aW9uc30gW29wdGlvbnNdXG4gICAqIEByZXR1cm5zXG4gICAqXG4gICAqIEBtZW1iZXJvZiBWYWxpZGF0ZWRUb3BpY1xuICAgKi9cbiAgcHVibGljIHN0YXRpYyBhc3luYyBtZ2V0PEkgZXh0ZW5kcyBhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4LCBUIGV4dGVuZHMgVmFsaWRhdGVkVG9waWM+KFxuICAgIHRoaXM6IElTdGF0aWNUaGlzPEksIFQ+LFxuICAgIHN0YXRlOiBtYWdlLmNvcmUuSVN0YXRlLFxuICAgIGluZGV4ZXM6IElbXSxcbiAgICBvcHRpb25zPzogYXJjaGl2aXN0LklBcmNoaXZpc3RHZXRPcHRpb25zKTogUHJvbWlzZTxUW10+IHtcblxuICAgIGNvbnN0IHRvcGljID0gdGhpcy5nZXRDbGFzc05hbWUoKVxuICAgIGNvbnN0IHF1ZXJpZXM6IGFyY2hpdmlzdC5JQXJjaGl2aXN0UXVlcnlbXSA9IGluZGV4ZXMubWFwKChpbmRleCkgPT4gKHsgdG9waWMsIGluZGV4IH0pKVxuXG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0ZTxJLCBULCBUW10+KHN0YXRlLCAnbWdldCcsIFtcbiAgICAgIHF1ZXJpZXMsXG4gICAgICBvcHRpb25zXG4gICAgXSwgYXN5bmMgKGxpc3Q6IGFueSkgPT4ge1xuICAgICAgY29uc3QgaW5zdGFuY2VzOiBWYWxpZGF0ZWRUb3BpY1tdID0gW11cblxuICAgICAgZm9yIChjb25zdCBbaSwgZGF0YV0gb2YgbGlzdC5lbnRyaWVzKCkpIHtcbiAgICAgICAgaWYgKCFkYXRhKSB7XG4gICAgICAgICAgY29udGludWVcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGluZGV4ID0gcXVlcmllc1tpXS5pbmRleFxuICAgICAgICBjb25zdCBpbnN0YW5jZSA9IGF3YWl0IHRoaXMuY3JlYXRlKHN0YXRlLCBpbmRleCBhcyBJLCBkYXRhKVxuXG4gICAgICAgIGluc3RhbmNlcy5wdXNoKGluc3RhbmNlKVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gaW5zdGFuY2VzXG4gICAgfSlcbiAgfVxuXG4gIC8vXG4gIC8vIFRvZG86XG4gIC8vXG4gIC8vIHB1YmxpYyBzdGF0aWMgYXN5bmMgbWdldChzdGF0ZTogbWFnZS5jb3JlLklTdGF0ZSwgcXVlcmllczogYXJjaGl2aXN0LklOYW1lZEFyY2hpdmlzdFF1ZXJ5LCBvcHRpb25zPzogYXJjaGl2aXN0LklBcmNoaXZpc3RHZXRPcHRpb25zKSB7XG4gIC8vICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgLy8gICAgIHN0YXRlLmFyY2hpdmlzdC5tZ2V0KHF1ZXJpZXMsIG9wdGlvbnMsIGFzeW5jIChlcnJvciwgbGlzdCkgPT4ge1xuICAvLyAgICAgICBpZiAoZXJyb3IpIHtcbiAgLy8gICAgICAgICByZXR1cm4gcmVqZWN0KGVycm9yKVxuICAvLyAgICAgICB9XG5cbiAgLy8gICAgICAgY29uc3QgaW5zdGFuY2VzID0gT2JqZWN0LmtleXMobGlzdCkucmVkdWNlKChpbnN0YW5jZXMsIG5hbWUpID0+IHtcbiAgLy8gICAgICAgICBpZiAobGlzdFtuYW1lXSkge1xuICAvLyAgICAgICAgICAgaW5zdGFuY2VzW25hbWVdID0gdGhpcy5jcmVhdGUoc3RhdGUsIHF1ZXJpZXNbbmFtZV0sIGxpc3RbbmFtZV0pXG4gIC8vICAgICAgICAgfVxuXG4gIC8vICAgICAgICAgcmV0dXJuIGluc3RhbmNlc1xuICAvLyAgICAgICB9LCB7fSlcblxuICAvLyAgICAgICByZXNvbHZlKGluc3RhbmNlcylcbiAgLy8gICAgIH0pXG4gIC8vICAgfSlcbiAgLy8gfVxuXG4gIC8qKlxuICAgKiBMaXN0IGFsbCBrZXlzXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQHBhcmFtIHttYWdlLmNvcmUuSVN0YXRlfSBzdGF0ZVxuICAgKiBAcGFyYW0ge2FyY2hpdmlzdC5JQXJjaGl2aXN0SW5kZXh9IHBhcnRpYWxJbmRleFxuICAgKiBAcGFyYW0ge2FyY2hpdmlzdC5JQXJjaGl2aXN0R2V0T3B0aW9uc30gW29wdGlvbnNdXG4gICAqIEByZXR1cm5zXG4gICAqXG4gICAqIEBtZW1iZXJvZiBWYWxpZGF0ZWRUb3BpY1xuICAgKi9cbiAgcHVibGljIHN0YXRpYyBhc3luYyBsaXN0PEkgZXh0ZW5kcyBhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4LCBUIGV4dGVuZHMgVmFsaWRhdGVkVG9waWM+KFxuICAgIHRoaXM6IElTdGF0aWNUaGlzPEksIFQ+LFxuICAgIHN0YXRlOiBtYWdlLmNvcmUuSVN0YXRlLFxuICAgIHBhcnRpYWxJbmRleDogUGFydGlhbEluZGV4PEk+LFxuICAgIG9wdGlvbnM/OiBhcmNoaXZpc3QuSUFyY2hpdmlzdExpc3RPcHRpb25zKTogUHJvbWlzZTxhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4W10+IHtcblxuICAgIGNvbnN0IHRvcGljTmFtZSA9IHRoaXMuZ2V0Q2xhc3NOYW1lKClcblxuICAgIHJldHVybiB0aGlzLmV4ZWN1dGU8SSwgVCwgSVtdPihzdGF0ZSwgJ2xpc3QnLCBbXG4gICAgICB0b3BpY05hbWUsXG4gICAgICBwYXJ0aWFsSW5kZXgsXG4gICAgICBvcHRpb25zLFxuICAgIF0sIChpbmRleGVzOiBhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4W10pID0+IGluZGV4ZXMpXG4gIH1cblxuICAvKipcbiAgICogUXVlcnkgZGF0YSBieSBwYXJ0aWFsIGluZGV4XG4gICAqXG4gICAqIEVzc2VudGlhbGx5IHdyYXBzIHN0YXRlLmFyY2hpdmlzdC5saXN0LCB0aGVuIGZldGNoZXMgdGhlIGRhdGFcbiAgICogZm9yIGVhY2gga2V5cyB1c2luZyBzdGF0ZS5hcmNoaXZpc3QubWdldC5cbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAcGFyYW0ge21hZ2UuY29yZS5JU3RhdGV9IHN0YXRlXG4gICAqIEBwYXJhbSB7YXJjaGl2aXN0LklBcmNoaXZpc3RJbmRleH0gcGFydGlhbEluZGV4XG4gICAqIEBwYXJhbSB7YXJjaGl2aXN0LklBcmNoaXZpc3RHZXRPcHRpb25zfSBbb3B0aW9uc11cbiAgICogQHJldHVybnNcbiAgICpcbiAgICogQG1lbWJlcm9mIFZhbGlkYXRlZFRvcGljXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIHF1ZXJ5PEkgZXh0ZW5kcyBhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4LCBUIGV4dGVuZHMgVmFsaWRhdGVkVG9waWM+KFxuICAgIHRoaXM6IElTdGF0aWNUaGlzPEksIFQ+LFxuICAgIHN0YXRlOiBtYWdlLmNvcmUuSVN0YXRlLFxuICAgIHBhcnRpYWxJbmRleDogUGFydGlhbEluZGV4PEk+LFxuICAgIG9wdGlvbnM/OiBhcmNoaXZpc3QuSUFyY2hpdmlzdEdldE9wdGlvbnMgfCBhcmNoaXZpc3QuSUFyY2hpdmlzdExpc3RPcHRpb25zKTogUHJvbWlzZTxUW10+IHtcblxuICAgIGNvbnN0IHRvcGljTmFtZSA9IHRoaXMuZ2V0Q2xhc3NOYW1lKClcblxuICAgIHJldHVybiB0aGlzLmV4ZWN1dGU8SSwgVCwgVFtdPihzdGF0ZSwgJ2xpc3QnLCBbXG4gICAgICB0b3BpY05hbWUsXG4gICAgICBwYXJ0aWFsSW5kZXgsXG4gICAgICBvcHRpb25zXG4gICAgXSwgYXN5bmMgKGluZGV4ZXM6IElbXSkgPT4ge1xuICAgICAgcmV0dXJuIHRoaXMubWdldDxUPihzdGF0ZSwgaW5kZXhlcywgb3B0aW9ucyBhcyBhcmNoaXZpc3QuSUFyY2hpdmlzdEdldE9wdGlvbnMpXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIFZhbGlkYXRlZFRvcGljLlxuICAgKlxuICAgKiBAcGFyYW0ge21hZ2UuY29yZS5JU3RhdGV9IHN0YXRlXG4gICAqXG4gICAqIEBtZW1iZXJvZiBWYWxpZGF0ZWRUb3BpY1xuICAgKi9cbiAgY29uc3RydWN0b3Ioc3RhdGU/OiBtYWdlLmNvcmUuSVN0YXRlKSB7XG4gICAgdGhpcy5zZXRUb3BpYyh0aGlzLmNvbnN0cnVjdG9yLm5hbWUpXG5cbiAgICBpZiAoc3RhdGUpIHtcbiAgICAgIHRoaXMuc2V0U3RhdGUoc3RhdGUpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgdG9waWMgZm9yIHRoaXMgaW5zdGFuY2VcbiAgICpcbiAgICogQG1lbWJlcm9mIFZhbGlkYXRlZFRvcGljXG4gICAqL1xuICBwdWJsaWMgZ2V0VG9waWMoKSB7XG4gICAgcmV0dXJuIDxzdHJpbmc+ICg8YW55PiB0aGlzKS5fdG9waWNcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgdGhlIHRvcGljIG5hbWUgZm9yIHRoaXMgaW5zdGFuY2VcbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHRvcGljTmFtZVxuICAgKlxuICAgKiBAbWVtYmVyb2YgVmFsaWRhdGVkVG9waWNcbiAgICovXG4gIHB1YmxpYyBzZXRUb3BpYyh0b3BpY05hbWU6IHN0cmluZykge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCAnX3RvcGljJywge1xuICAgICAgdmFsdWU6IHRvcGljTmFtZSxcbiAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSB0b3BpYyBvZiB0aGF0IGluc3RhbmNlXG4gICAqXG4gICAqIFNob3VsZCBhbHdheXMgcmV0dXJuIHRoZSBjbGFzcyBuYW1lIG9mIHRoZSBpbnN0YW5jZS5cbiAgICpcbiAgICogQHJldHVybnNcbiAgICpcbiAgICogQG1lbWJlcm9mIFZhbGlkYXRlZFRvcGljXG4gICAqL1xuICBwdWJsaWMgZ2V0SW5kZXgoKSB7XG4gICAgcmV0dXJuIDxhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4PiAoPGFueT4gdGhpcykuX2luZGV4XG4gIH1cblxuICAvKipcbiAgICogU2V0IHRoZSBpbmRleCBvZiB0aGlzIHRvcGljIGluc3RhbmNlXG4gICAqXG4gICAqIEBwYXJhbSB7YXJjaGl2aXN0LklBcmNoaXZpc3RJbmRleH0gaW5kZXhcbiAgICpcbiAgICogQG1lbWJlcm9mIFZhbGlkYXRlZFRvcGljXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgc2V0SW5kZXgoaW5kZXhEYXRhOiBhcmNoaXZpc3QuSUFyY2hpdmlzdEluZGV4KSB7XG4gICAgY29uc3QgQ2xhc3M6IGFueSA9IHRoaXMuY29uc3RydWN0b3JcbiAgICBjb25zdCBJbmRleCA9IENsYXNzLmluZGV4VHlwZVxuICAgIGNvbnN0IGluZGV4ID0gbmV3IEluZGV4KClcblxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgQ2xhc3MuaW5kZXgpIHtcbiAgICAgIGluZGV4W2ZpZWxkXSA9IGluZGV4RGF0YVtmaWVsZF1cbiAgICB9XG5cbiAgICBjb25zdCBlcnJvcnMgPSBhd2FpdCBjbGFzc1ZhbGlkYXRvci52YWxpZGF0ZShpbmRleClcblxuICAgIGlmIChlcnJvcnMubGVuZ3RoID4gMCkge1xuICAgICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcignSW5kZXggdmFsaWRhdGlvbiBmYWlsZWQnLCAnc2VydmVyJywge1xuICAgICAgICB0b3BpYzogdGhpcy5nZXRUb3BpYygpLFxuICAgICAgICBpbmRleDogdGhpcy5nZXRJbmRleCgpXG4gICAgICB9LCBlcnJvcnMpXG4gICAgfVxuXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRoaXMsICdfaW5kZXgnLCB7XG4gICAgICB2YWx1ZTogaW5kZXgsXG4gICAgICBjb25maWd1cmFibGU6IHRydWVcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlIHRoZSBzdGF0ZSBvYmplY3QgYXR0YWNoZWQgdG8gdGhlIGluc3RhbmNlXG4gICAqL1xuICBwdWJsaWMgZ2V0U3RhdGUoKSB7XG4gICAgcmV0dXJuIDxtYWdlLmNvcmUuSVN0YXRlPiAoPGFueT4gdGhpcykuX3N0YXRlXG4gIH1cblxuICAvKipcbiAgICogU2V0IHRoZSBzdGF0ZSB0aGlzIHRvcGljIGluc3RhbmNlIHdpbGwgYmUgdXNpbmcuXG4gICAqL1xuICBwdWJsaWMgc2V0U3RhdGUoc3RhdGU6IG1hZ2UuY29yZS5JU3RhdGUpIHtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywgJ19zdGF0ZScsIHtcbiAgICAgIHZhbHVlOiBzdGF0ZSxcbiAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmUgdGhlIGFjdHVhbCBkYXRhIGZvciB0aGlzIGluc3RhbmNlXG4gICAqXG4gICAqIFRoaXMgc2hvdWxkIGVzc2VudGlhbGx5IGJlIHRoZSBzYW1lIGFzIHNpbXBseSBhY2Nlc3NpbmcgZGF0YSBvbiB0aGVcbiAgICogaW5zdGFuY2UgaXRzZWxmLlxuICAgKi9cbiAgcHVibGljIGdldERhdGEoKSB7XG4gICAgcmV0dXJuIHRoaXNcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmQgYW4gYWRkIG9wZXJhdGlvbiBvbiB0aGUgaW5zdGFuY2UncyBzdGF0ZVxuICAgKlxuICAgKiBFc3NlbnRpYWxseSBhIHdyYXBwZXIgZm9yIHN0YXRlLmFyY2hpdmlzdC5hZGRcbiAgICovXG4gIHB1YmxpYyBhc3luYyBhZGQobWVkaWFUeXBlPzogYXJjaGl2aXN0LkFyY2hpdmlzdE1lZGlhVHlwZSwgZW5jb2Rpbmc/OiBhcmNoaXZpc3QuQXJjaGl2aXN0RW5jb2RpbmcsIGV4cGlyYXRpb25UaW1lPzogbnVtYmVyKSB7XG4gICAgYXdhaXQgdGhpcy52YWxpZGF0ZSgnVmFsaWRhdGlvbiBmYWlsZWQgb24gYWRkJylcbiAgICByZXR1cm4gdGhpcy5nZXRTdGF0ZSgpLmFyY2hpdmlzdC5hZGQodGhpcy5nZXRUb3BpYygpLCB0aGlzLmdldEluZGV4KCksIHRoaXMuZ2V0RGF0YSgpLCBtZWRpYVR5cGUsIGVuY29kaW5nLCBleHBpcmF0aW9uVGltZSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmQgYSBzZXQgb3BlcmF0aW9uIG9uIHRoZSBpbnN0YW5jZSdzIHN0YXRlXG4gICAqXG4gICAqIEVzc2VudGlhbGx5IGEgd3JhcHBlciBmb3Igc3RhdGUuYXJjaGl2aXN0LnNldC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBzZXQobWVkaWFUeXBlPzogYXJjaGl2aXN0LkFyY2hpdmlzdE1lZGlhVHlwZSwgZW5jb2Rpbmc/OiBhcmNoaXZpc3QuQXJjaGl2aXN0RW5jb2RpbmcsIGV4cGlyYXRpb25UaW1lPzogbnVtYmVyKSB7XG4gICAgYXdhaXQgdGhpcy52YWxpZGF0ZSgnVmFsaWRhdGlvbiBmYWlsZWQgb24gc2V0JylcbiAgICByZXR1cm4gdGhpcy5nZXRTdGF0ZSgpLmFyY2hpdmlzdC5zZXQodGhpcy5nZXRUb3BpYygpLCB0aGlzLmdldEluZGV4KCksIHRoaXMuZ2V0RGF0YSgpLCBtZWRpYVR5cGUsIGVuY29kaW5nLCBleHBpcmF0aW9uVGltZSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmQgYSB0b3VjaCBvcGVyYXRpb24gb24gdGhlIGluc3RhbmNlJ3Mgc3RhdGVcbiAgICpcbiAgICogRXNzZW50aWFsbHkgYSB3cmFwcGVyIGZvciBzdGF0ZS5hcmNoaXZpc3QudG91Y2guXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdG91Y2goZXhwaXJhdGlvblRpbWU/OiBudW1iZXIpIHtcbiAgICBhd2FpdCB0aGlzLnZhbGlkYXRlKCdWYWxpZGF0aW9uIGZhaWxlZCBvbiB0b3VjaCcpXG4gICAgcmV0dXJuIHRoaXMuZ2V0U3RhdGUoKS5hcmNoaXZpc3QudG91Y2godGhpcy5nZXRUb3BpYygpLCB0aGlzLmdldEluZGV4KCksIGV4cGlyYXRpb25UaW1lKVxuICB9XG5cbiAgLyoqXG4gICAqIFJlY29yZCBhIGRlbGV0ZSBvcGVyYXRpb24gb24gdGhlIGluc3RhbmNlJ3Mgc3RhdGVcbiAgICovXG4gIHB1YmxpYyBkZWwoKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0U3RhdGUoKS5hcmNoaXZpc3QuZGVsKHRoaXMuZ2V0VG9waWMoKSwgdGhpcy5nZXRJbmRleCgpKVxuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIHRoZSBjdXJyZW50IGluc3RhbmNlXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdmFsaWRhdGUoZXJyb3JNZXNzYWdlPzogc3RyaW5nLCBjb2RlPzogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZXJyb3JzID0gYXdhaXQgY2xhc3NWYWxpZGF0b3IudmFsaWRhdGUodGhpcylcbiAgICBpZiAoZXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMucmFpc2VWYWxpZGF0aW9uRXJyb3IoZXJyb3JzLCBlcnJvck1lc3NhZ2UsIGNvZGUpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRocm93IGEgVmFsaWRhdGVFcnJvciBpbmNsdWRpbmcgcmVsZXZhbnQgZGV0YWlsc1xuICAgKi9cbiAgcHVibGljIHJhaXNlVmFsaWRhdGlvbkVycm9yKGVycm9yczogYW55W10sIGVycm9yTWVzc2FnZT86IHN0cmluZywgY29kZT86IHN0cmluZykge1xuICAgIGNvbnN0IHN0YXRlID0gPGFueT4gdGhpcy5nZXRTdGF0ZSgpXG5cbiAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKGVycm9yTWVzc2FnZSB8fCAnVmFsaWRhdGlvbiBmYWlsZWQnLCBjb2RlIHx8ICdzZXJ2ZXInLCB7XG4gICAgICBhY3RvcklkOiBzdGF0ZS5hY3RvcklkLFxuICAgICAgdXNlckNvbW1hbmQ6IHN0YXRlLmRlc2NyaXB0aW9uLFxuICAgICAgdG9waWM6IHRoaXMuZ2V0VG9waWMoKSxcbiAgICAgIGluZGV4OiB0aGlzLmdldEluZGV4KClcbiAgICB9LCBlcnJvcnMpXG4gIH1cbn1cbiJdfQ==