@meddv/ngx-mqtt
Version:
This library isn't just a wrapper around MQTT.js for angular. It uses observables and takes care of subscription handling and message routing.
372 lines • 51.8 kB
JavaScript
import { EventEmitter, Inject, Injectable } from '@angular/core';
import mqtt from 'mqtt';
import { BehaviorSubject, merge, Observable, Subject, Subscription, using } from 'rxjs';
import { filter, publish, publishReplay, refCount } from 'rxjs/operators';
import { MqttConnectionState } from './mqtt.model';
import { MqttClientService, MqttServiceConfig } from './mqtt.module';
import * as i0 from "@angular/core";
const connect = mqtt.connect;
// A javascript function that takes two objects and merges them recursively
function mergeDeep(target, ...sources) {
if (!sources.length) {
return target;
}
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) {
Object.assign(target, { [key]: {} });
}
mergeDeep(target[key], source[key]);
}
else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
function isObject(item) {
return item && typeof item === 'object' && !Array.isArray(item);
}
/**
* With an instance of MqttService, you can observe and subscribe to MQTT in multiple places, e.g. in different components,
* to only subscribe to the broker once per MQTT filter.
* It also handles proper unsubscription from the broker, if the last observable with a filter is closed.
*/
export class MqttService {
/**
* The constructor needs [connection options]{@link IMqttServiceOptions} regarding the broker and some
* options to configure behavior of this service, like if the connection to the broker
* should be established on creation of this service or not.
*/
constructor(options, client) {
this.options = options;
/** a map of all mqtt observables by filter */
this.observables = {};
/** the connection state */
this.state = new BehaviorSubject(MqttConnectionState.CLOSED);
/** an observable of the last mqtt message */
this.messages = new Subject();
this._clientId = this._generateClientId();
this._connectTimeout = 10000;
this._reconnectPeriod = 10000;
this._onConnect = new EventEmitter();
this._onReconnect = new EventEmitter();
this._onClose = new EventEmitter();
this._onOffline = new EventEmitter();
this._onError = new EventEmitter();
this._onEnd = new EventEmitter();
this._onMessage = new EventEmitter();
this._onSuback = new EventEmitter();
this._onPacketsend = new EventEmitter();
this._onPacketreceive = new EventEmitter();
this._handleOnConnect = (e) => {
if (this.options.connectOnCreate === true) {
Object.keys(this.observables).forEach((filterString) => {
this.client.subscribe(filterString);
});
}
this.state.next(MqttConnectionState.CONNECTED);
this._onConnect.emit(e);
};
this._handleOnReconnect = () => {
if (this.options.connectOnCreate === true) {
Object.keys(this.observables).forEach((filterString) => {
this.client.subscribe(filterString);
});
}
this.state.next(MqttConnectionState.CONNECTING);
this._onReconnect.emit();
};
this._handleOnClose = () => {
this.state.next(MqttConnectionState.CLOSED);
this._onClose.emit();
};
this._handleOnOffline = () => {
this._onOffline.emit();
};
this._handleOnError = (e) => {
this._onError.emit(e);
console.error(e);
};
this._handleOnEnd = () => {
this._onEnd.emit();
};
this._handleOnMessage = (topic, payload, packet) => {
this._onMessage.emit(packet);
if (packet.cmd === 'publish') {
this.messages.next(packet);
}
};
this._handleOnPacketsend = (e) => {
this._onPacketsend.emit(e);
};
this._handleOnPacketreceive = (e) => {
this._onPacketreceive.emit(e);
};
if (options.connectOnCreate !== false) {
this.connect({}, client);
}
this.state.subscribe();
}
/**
* gets the _clientId
*/
get clientId() {
return this._clientId;
}
/** An EventEmitter to listen to connect messages */
get onConnect() {
return this._onConnect;
}
/** An EventEmitter to listen to reconnect messages */
get onReconnect() {
return this._onReconnect;
}
/** An EventEmitter to listen to close messages */
get onClose() {
return this._onClose;
}
/** An EventEmitter to listen to offline events */
get onOffline() {
return this._onOffline;
}
/** An EventEmitter to listen to error events */
get onError() {
return this._onError;
}
/** An EventEmitter to listen to close messages */
get onEnd() {
return this._onEnd;
}
/** An EventEmitter to listen to message events */
get onMessage() {
return this._onMessage;
}
/** An EventEmitter to listen to packetsend messages */
get onPacketsend() {
return this._onPacketsend;
}
/** An EventEmitter to listen to packetreceive messages */
get onPacketreceive() {
return this._onPacketreceive;
}
/** An EventEmitter to listen to suback events */
get onSuback() {
return this._onSuback;
}
/** An actual connection state **/
get connectionState() {
return this.state.value;
}
/**
* This static method shall be used to determine whether a MQTT
* topic matches a given filter. The matching rules are specified in the MQTT
* standard documentation and in the library test suite.
*
* @param {string} filter A filter may contain wildcards like '#' and '+'.
* @param {string} topic A topic may not contain wildcards.
* @return {boolean} true on match and false otherwise.
*/
static filterMatchesTopic(filterString, topic) {
if (filterString[0] === '#' && topic[0] === '$') {
return false;
}
// Preparation: split and reverse on '/'. The JavaScript split function is sane.
const fs = (filterString || '').split('/').reverse();
const ts = (topic || '').split('/').reverse();
// This function is tail recursive and compares both arrays one element at a time.
const match = () => {
// Cutting of the last element of both the filter and the topic using pop().
const f = fs.pop();
const t = ts.pop();
switch (f) {
// In case the filter level is '#', this is a match no matter whether
// the topic is undefined on this level or not ('#' matches parent element as well!).
case '#':
return true;
// In case the filter level is '+', we shall dive into the recursion only if t is not undefined.
case '+':
return t ? match() : false;
// In all other cases the filter level must match the topic level,
// both must be defined and the filter tail must match the topic
// tail (which is determined by the recursive call of match()).
default:
return f === t && (f === undefined ? true : match());
}
};
return match();
}
/**
* connect manually connects to the mqtt broker.
*/
connect(opts, client) {
const options = mergeDeep(this.options || {}, opts);
const protocol = options.protocol || 'ws';
const hostname = options.hostname || 'localhost';
if (options.url) {
this._url = options.url;
}
else {
this._url = `${protocol}://${hostname}`;
this._url += options.port ? `:${options.port}` : '';
this._url += options.path ? `${options.path}` : '';
}
this.state.next(MqttConnectionState.CONNECTING);
const mergedOptions = mergeDeep({
clientId: this._clientId,
reconnectPeriod: this._reconnectPeriod,
connectTimeout: this._connectTimeout
}, options);
if (this.client) {
this.client.end(true);
}
if (!client) {
this.client = connect(this._url, mergedOptions);
}
else {
this.client = client;
}
this._clientId = mergedOptions.clientId;
this.client.on('connect', this._handleOnConnect);
this.client.on('reconnect', this._handleOnReconnect);
this.client.on('close', this._handleOnClose);
this.client.on('offline', this._handleOnOffline);
this.client.on('error', this._handleOnError);
this.client.stream.on('error', this._handleOnError);
this.client.on('end', this._handleOnEnd);
this.client.on('message', this._handleOnMessage);
this.client.on('packetsend', this._handleOnPacketsend);
this.client.on('packetreceive', this._handleOnPacketreceive);
}
/**
* disconnect disconnects from the mqtt client.
* This method `should` be executed when leaving the application.
*/
disconnect(force = true) {
if (!this.client) {
throw new Error('mqtt client not connected');
}
this.client.end(force);
}
/**
* With this method, you can observe messages for a mqtt topic.
* The observable will only emit messages matching the filter.
* The first one subscribing to the resulting observable executes a mqtt subscribe.
* The last one unsubscribing this filter executes a mqtt unsubscribe.
* Every new subscriber gets the latest message.
*/
observeRetained(filterString, opts = { qos: 1 }) {
return this._generalObserve(filterString, () => publishReplay(1), opts);
}
/**
* With this method, you can observe messages for a mqtt topic.
* The observable will only emit messages matching the filter.
* The first one subscribing to the resulting observable executes a mqtt subscribe.
* The last one unsubscribing this filter executes a mqtt unsubscribe.
*/
observe(filterString, opts = { qos: 1 }) {
return this._generalObserve(filterString, () => publish(), opts);
}
/**
* With this method, you can observe messages for a mqtt topic.
* The observable will only emit messages matching the filter.
* The first one subscribing to the resulting observable executes a mqtt subscribe.
* The last one unsubscribing this filter executes a mqtt unsubscribe.
* Depending on the publish function, the messages will either be replayed after new
* subscribers subscribe or the messages are just passed through
*/
_generalObserve(filterString, publishFn, opts) {
if (!this.client) {
throw new Error('mqtt client not connected');
}
if (!this.observables[filterString]) {
const rejected = new Subject();
this.observables[filterString] = using(
// resourceFactory: Do the actual ref-counting MQTT subscription.
// refcount is decreased on unsubscribe.
() => {
const subscription = new Subscription();
this.client.subscribe(filterString, opts, (err, granted) => {
if (granted) { // granted can be undefined when an error occurs when the client is disconnecting
granted.forEach((granted_) => {
if (granted_.qos === 128) {
delete this.observables[granted_.topic];
this.client.unsubscribe(granted_.topic);
rejected.error(`subscription for '${granted_.topic}' rejected!`);
}
this._onSuback.emit({ filter: filterString, granted: granted_.qos !== 128 });
});
}
});
subscription.add(() => {
delete this.observables[filterString];
this.client.unsubscribe(filterString);
});
return subscription;
},
// observableFactory: Create the observable that is consumed from.
// This part is not executed until the Observable returned by
// `observe` gets actually subscribed.
(subscription) => merge(rejected, this.messages))
.pipe(filter((msg) => MqttService.filterMatchesTopic(filterString, msg.topic)), publishFn(), refCount());
}
return this.observables[filterString];
}
/**
* This method returns an observable for a topic with optional options.
* After subscribing, the actual mqtt publication will be executed and
* the observable will emit an empty value and completes, if publishing was successful
* or throws an error, if the publication fails.
*/
publish(topic, message, options = {}) {
if (!this.client) {
throw new Error('mqtt client not connected');
}
return Observable.create((obs) => {
this.client.publish(topic, message, options, (error) => {
if (error) {
obs.error(error);
}
else {
obs.next();
obs.complete();
}
});
});
}
/**
* This method publishes a message for a topic with optional options.
* If an error occurs, it will throw.
*/
unsafePublish(topic, message, options = {}) {
if (!this.client) {
throw new Error('mqtt client not connected');
}
this.client.publish(topic, message, options, (error) => {
if (error) {
throw (error);
}
});
}
_generateClientId() {
return 'client-' + Math.random().toString(36).substr(2, 19);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: MqttService, deps: [{ token: MqttServiceConfig }, { token: MqttClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: MqttService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: MqttService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Inject,
args: [MqttServiceConfig]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [MqttClientService]
}] }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXF0dC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LW1xdHQvc3JjL2xpYi9tcXR0LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBSWpFLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUl4QixPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQVksT0FBTyxFQUFFLFlBQVksRUFBa0IsS0FBSyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2xILE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxRQUFRLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUxRSxPQUFPLEVBU0wsbUJBQW1CLEVBQ3BCLE1BQU0sY0FBYyxDQUFDO0FBRXRCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7QUFsQnJFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7QUFvQjdCLDJFQUEyRTtBQUMzRSxTQUFTLFNBQVMsQ0FBQyxNQUFXLEVBQUUsR0FBRyxPQUFjO0lBQy9DLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDcEIsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUNELE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUUvQixJQUFJLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUN6QyxLQUFLLE1BQU0sR0FBRyxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ3pCLElBQUksUUFBUSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQzFCLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDakIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQ3ZDLENBQUM7Z0JBQ0QsU0FBUyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUN0QyxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDaEQsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxTQUFTLENBQUMsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLENBQUM7QUFDdkMsQ0FBQztBQUVELFNBQVMsUUFBUSxDQUFDLElBQVM7SUFDekIsT0FBTyxJQUFJLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNsRSxDQUFDO0FBRUQ7Ozs7R0FJRztBQUlILE1BQU0sT0FBTyxXQUFXO0lBR3RCOzs7O09BSUc7SUFDSCxZQUNxQyxPQUE0QixFQUNwQyxNQUFtQjtRQURYLFlBQU8sR0FBUCxPQUFPLENBQXFCO1FBd0VqRSw4Q0FBOEM7UUFDdkMsZ0JBQVcsR0FBeUQsRUFBRSxDQUFDO1FBQzlFLDJCQUEyQjtRQUNwQixVQUFLLEdBQXlDLElBQUksZUFBZSxDQUFzQixtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxSCw2Q0FBNkM7UUFDdEMsYUFBUSxHQUEwQixJQUFJLE9BQU8sRUFBZ0IsQ0FBQztRQUU3RCxjQUFTLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDckMsb0JBQWUsR0FBRyxLQUFLLENBQUM7UUFDeEIscUJBQWdCLEdBQUcsS0FBSyxDQUFDO1FBR3pCLGVBQVUsR0FBa0MsSUFBSSxZQUFZLEVBQW1CLENBQUM7UUFDaEYsaUJBQVksR0FBdUIsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUM1RCxhQUFRLEdBQXVCLElBQUksWUFBWSxFQUFRLENBQUM7UUFDeEQsZUFBVSxHQUF1QixJQUFJLFlBQVksRUFBUSxDQUFDO1FBQzFELGFBQVEsR0FBZ0MsSUFBSSxZQUFZLEVBQWlCLENBQUM7UUFDMUUsV0FBTSxHQUF1QixJQUFJLFlBQVksRUFBUSxDQUFDO1FBQ3RELGVBQVUsR0FBeUIsSUFBSSxZQUFZLEVBQVUsQ0FBQztRQUM5RCxjQUFTLEdBQWlDLElBQUksWUFBWSxFQUFrQixDQUFDO1FBQzdFLGtCQUFhLEdBQXFDLElBQUksWUFBWSxFQUFzQixDQUFDO1FBQ3pGLHFCQUFnQixHQUF3QyxJQUFJLFlBQVksRUFBeUIsQ0FBQztRQTRNbEcscUJBQWdCLEdBQUcsQ0FBQyxDQUFrQixFQUFFLEVBQUU7WUFDaEQsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDMUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsWUFBb0IsRUFBRSxFQUFFO29CQUM3RCxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFDdEMsQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDO1lBQ0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDL0MsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDMUIsQ0FBQyxDQUFBO1FBRU8sdUJBQWtCLEdBQUcsR0FBRyxFQUFFO1lBQ2hDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLEtBQUssSUFBSSxFQUFFLENBQUM7Z0JBQzFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLFlBQW9CLEVBQUUsRUFBRTtvQkFDN0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBQ3RDLENBQUMsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztZQUNELElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ2hELElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDM0IsQ0FBQyxDQUFBO1FBRU8sbUJBQWMsR0FBRyxHQUFHLEVBQUU7WUFDNUIsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDNUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN2QixDQUFDLENBQUE7UUFFTyxxQkFBZ0IsR0FBRyxHQUFHLEVBQUU7WUFDOUIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN6QixDQUFDLENBQUE7UUFFTyxtQkFBYyxHQUFHLENBQUMsQ0FBZ0IsRUFBRSxFQUFFO1lBQzVDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3RCLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkIsQ0FBQyxDQUFBO1FBRU8saUJBQVksR0FBRyxHQUFHLEVBQUU7WUFDMUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNyQixDQUFDLENBQUE7UUFFTyxxQkFBZ0IsR0FBRyxDQUFDLEtBQWEsRUFBRSxPQUFlLEVBQUUsTUFBYyxFQUFFLEVBQUU7WUFDNUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDN0IsSUFBSSxNQUFNLENBQUMsR0FBRyxLQUFLLFNBQVMsRUFBRSxDQUFDO2dCQUM3QixJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFhLENBQUMsQ0FBQztZQUNwQyxDQUFDO1FBQ0gsQ0FBQyxDQUFBO1FBRU8sd0JBQW1CLEdBQUcsQ0FBQyxDQUFxQixFQUFFLEVBQUU7WUFDdEQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDN0IsQ0FBQyxDQUFBO1FBRU8sMkJBQXNCLEdBQUcsQ0FBQyxDQUF3QixFQUFFLEVBQUU7WUFDNUQsSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNoQyxDQUFDLENBQUE7UUF6VkMsSUFBSSxPQUFPLENBQUMsZUFBZSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQzNCLENBQUM7UUFFRCxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDeEIsQ0FBQztJQUVELG9EQUFvRDtJQUNwRCxJQUFXLFNBQVM7UUFDbEIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxzREFBc0Q7SUFDdEQsSUFBVyxXQUFXO1FBQ3BCLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQztJQUMzQixDQUFDO0lBRUQsa0RBQWtEO0lBQ2xELElBQVcsT0FBTztRQUNoQixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUM7SUFDdkIsQ0FBQztJQUVELGtEQUFrRDtJQUNsRCxJQUFXLFNBQVM7UUFDbEIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxnREFBZ0Q7SUFDaEQsSUFBVyxPQUFPO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBRUQsa0RBQWtEO0lBQ2xELElBQVcsS0FBSztRQUNkLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNyQixDQUFDO0lBRUQsa0RBQWtEO0lBQ2xELElBQVcsU0FBUztRQUNsQixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDekIsQ0FBQztJQUVELHVEQUF1RDtJQUN2RCxJQUFXLFlBQVk7UUFDckIsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDO0lBQzVCLENBQUM7SUFFRCwwREFBMEQ7SUFDMUQsSUFBVyxlQUFlO1FBQ3hCLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDO0lBQy9CLENBQUM7SUFFRCxpREFBaUQ7SUFDakQsSUFBVyxRQUFRO1FBQ2pCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUN4QixDQUFDO0lBRUQsa0NBQWtDO0lBQ2xDLElBQVcsZUFBZTtRQUN4QixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDO0lBQzFCLENBQUM7SUF5QkQ7Ozs7Ozs7O09BUUc7SUFDSSxNQUFNLENBQUMsa0JBQWtCLENBQUMsWUFBb0IsRUFBRSxLQUFhO1FBQ2xFLElBQUksWUFBWSxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7WUFDaEQsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsZ0ZBQWdGO1FBQ2hGLE1BQU0sRUFBRSxHQUFHLENBQUMsWUFBWSxJQUFJLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNyRCxNQUFNLEVBQUUsR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDOUMsa0ZBQWtGO1FBQ2xGLE1BQU0sS0FBSyxHQUFHLEdBQVksRUFBRTtZQUMxQiw0RUFBNEU7WUFDNUUsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ25CLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNuQixRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUNWLHFFQUFxRTtnQkFDckUscUZBQXFGO2dCQUNyRixLQUFLLEdBQUc7b0JBQ04sT0FBTyxJQUFJLENBQUM7Z0JBQ2QsZ0dBQWdHO2dCQUNoRyxLQUFLLEdBQUc7b0JBQ04sT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7Z0JBQzdCLGtFQUFrRTtnQkFDbEUsZ0VBQWdFO2dCQUNoRSwrREFBK0Q7Z0JBQy9EO29CQUNFLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUN6RCxDQUFDO1FBQ0gsQ0FBQyxDQUFDO1FBQ0YsT0FBTyxLQUFLLEVBQUUsQ0FBQztJQUNqQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPLENBQUMsSUFBMEIsRUFBRSxNQUFtQjtRQUM1RCxNQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDcEQsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUM7UUFDMUMsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsSUFBSSxXQUFXLENBQUM7UUFDakQsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDaEIsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDO1FBQzFCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLFFBQVEsTUFBTSxRQUFRLEVBQUUsQ0FBQztZQUN4QyxJQUFJLENBQUMsSUFBSSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDcEQsSUFBSSxDQUFDLElBQUksSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3JELENBQUM7UUFDRCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNoRCxNQUFNLGFBQWEsR0FBRyxTQUFTLENBQUM7WUFDOUIsUUFBUSxFQUFFLElBQUksQ0FBQyxTQUFTO1lBQ3hCLGVBQWUsRUFBRSxJQUFJLENBQUMsZ0JBQWdCO1lBQ3RDLGNBQWMsRUFBRSxJQUFJLENBQUMsZUFBZTtTQUNyQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRVosSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDaEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztRQUVELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDbEQsQ0FBQzthQUFNLENBQUM7WUFDTixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUN2QixDQUFDO1FBQ0QsSUFBSSxDQUFDLFNBQVMsR0FBRyxhQUFhLENBQUMsUUFBUSxDQUFDO1FBRXhDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNqRCxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFDckQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUM3QyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDakQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUM1QyxJQUFJLENBQUMsTUFBYyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUM3RCxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ3pDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNqRCxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxZQUFZLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDdkQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQy9ELENBQUM7SUFFRDs7O09BR0c7SUFDSSxVQUFVLENBQUMsS0FBSyxHQUFHLElBQUk7UUFDNUIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNqQixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUNELElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxlQUFlLENBQUMsWUFBb0IsRUFBRSxPQUFnQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUU7UUFDckYsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLFlBQVksRUFBRSxHQUFHLEVBQUUsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDMUUsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksT0FBTyxDQUFDLFlBQW9CLEVBQUUsT0FBZ0MsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFO1FBQzdFLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxZQUFZLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDbkUsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSyxlQUFlLENBQUMsWUFBb0IsRUFBRSxTQUFtQixFQUFFLElBQTZCO1FBQzlGLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDO1lBQ3BDLE1BQU0sUUFBUSxHQUEwQixJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ3RELElBQUksQ0FBQyxXQUFXLENBQUMsWUFBWSxDQUFDLEdBQUcsS0FBSztZQUNwQyxpRUFBaUU7WUFDakUsd0NBQXdDO1lBQ3hDLEdBQUcsRUFBRTtnQkFDSCxNQUFNLFlBQVksR0FBaUIsSUFBSSxZQUFZLEVBQUUsQ0FBQztnQkFDdEQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLElBQUksRUFBRSxDQUFDLEdBQVEsRUFBRSxPQUF5QyxFQUFFLEVBQUU7b0JBQ2hHLElBQUksT0FBTyxFQUFFLENBQUMsQ0FBQyxpRkFBaUY7d0JBQzlGLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxRQUE0QixFQUFFLEVBQUU7NEJBQy9DLElBQUksUUFBUSxDQUFDLEdBQUcsS0FBSyxHQUFHLEVBQUUsQ0FBQztnQ0FDekIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztnQ0FDeEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dDQUN4QyxRQUFRLENBQUMsS0FBSyxDQUFDLHFCQUFxQixRQUFRLENBQUMsS0FBSyxhQUFhLENBQUMsQ0FBQzs0QkFDbkUsQ0FBQzs0QkFDRCxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxHQUFHLEtBQUssR0FBRyxFQUFFLENBQUMsQ0FBQzt3QkFDL0UsQ0FBQyxDQUFDLENBQUM7b0JBQ0wsQ0FBQztnQkFDSCxDQUFDLENBQUMsQ0FBQztnQkFDSCxZQUFZLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRTtvQkFDcEIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDO29CQUN0QyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFDeEMsQ0FBQyxDQUFDLENBQUM7Z0JBQ0gsT0FBTyxZQUFZLENBQUM7WUFDdEIsQ0FBQztZQUNELGtFQUFrRTtZQUNsRSw2REFBNkQ7WUFDN0Qsc0NBQXNDO1lBQ3RDLENBQUMsWUFBbUMsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7aUJBQ3ZFLElBQUksQ0FDSCxNQUFNLENBQUMsQ0FBQyxHQUFpQixFQUFFLEVBQUUsQ0FBQyxXQUFXLENBQUMsa0JBQWtCLENBQUMsWUFBWSxFQUFFLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUN0RixTQUFTLEVBQUUsRUFDWCxRQUFRLEVBQUUsQ0FDaUIsQ0FBQztRQUNsQyxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLE9BQU8sQ0FBQyxLQUFhLEVBQUUsT0FBd0IsRUFBRSxVQUFpQyxFQUFFO1FBQ3pGLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFDRCxPQUFPLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFtQixFQUFFLEVBQUU7WUFDL0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxLQUF3QixFQUFFLEVBQUU7Z0JBQ3hFLElBQUksS0FBSyxFQUFFLENBQUM7b0JBQ1YsR0FBRyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDbkIsQ0FBQztxQkFBTSxDQUFDO29CQUNOLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDWCxHQUFHLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQ2pCLENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNJLGFBQWEsQ0FBQyxLQUFhLEVBQUUsT0FBd0IsRUFBRSxVQUEyQixFQUFFO1FBQ3pGLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDLEtBQXdCLEVBQUUsRUFBRTtZQUN4RSxJQUFJLEtBQUssRUFBRSxDQUFDO2dCQUNWLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNoQixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBdURPLGlCQUFpQjtRQUN2QixPQUFPLFNBQVMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDOUQsQ0FBQzs4R0F6V1UsV0FBVyxrQkFTWixpQkFBaUIsYUFDakIsaUJBQWlCO2tIQVZoQixXQUFXLGNBRlYsTUFBTTs7MkZBRVAsV0FBVztrQkFIdkIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkI7OzBCQVVJLE1BQU07MkJBQUMsaUJBQWlCOzswQkFDeEIsTUFBTTsyQkFBQyxpQkFBaUIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBFdmVudEVtaXR0ZXIsIEluamVjdCwgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBJQ2xpZW50UHVibGlzaE9wdGlvbnMsIElDbGllbnRTdWJzY3JpYmVPcHRpb25zLCBJU3Vic2NyaXB0aW9uR3JhbnR9IGZyb20gJ21xdHQnO1xyXG5pbXBvcnQgeyBQYWNrZXQgfSBmcm9tICdtcXR0LXBhY2tldCc7XHJcblxyXG5pbXBvcnQgbXF0dCBmcm9tICdtcXR0JztcclxuY29uc3QgY29ubmVjdCA9IG1xdHQuY29ubmVjdDtcclxudHlwZSBNcXR0Q2xpZW50ID0gbXF0dC5NcXR0Q2xpZW50O1xyXG5cclxuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBtZXJnZSwgT2JzZXJ2YWJsZSwgT2JzZXJ2ZXIsIFN1YmplY3QsIFN1YnNjcmlwdGlvbiwgVW5zdWJzY3JpYmFibGUsIHVzaW5nIH0gZnJvbSAncnhqcyc7XHJcbmltcG9ydCB7IGZpbHRlciwgcHVibGlzaCwgcHVibGlzaFJlcGxheSwgcmVmQ291bnQgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XHJcblxyXG5pbXBvcnQge1xyXG4gIElNcXR0TWVzc2FnZSxcclxuICBJTXF0dFNlcnZpY2VPcHRpb25zLFxyXG4gIElPbkNvbm5lY3RFdmVudCxcclxuICBJT25FcnJvckV2ZW50LFxyXG4gIElPblBhY2tldHJlY2VpdmVFdmVudCxcclxuICBJT25QYWNrZXRzZW5kRXZlbnQsXHJcbiAgSU9uU3ViYWNrRXZlbnQsXHJcbiAgSVB1Ymxpc2hPcHRpb25zLFxyXG4gIE1xdHRDb25uZWN0aW9uU3RhdGVcclxufSBmcm9tICcuL21xdHQubW9kZWwnO1xyXG5cclxuaW1wb3J0IHsgTXF0dENsaWVudFNlcnZpY2UsIE1xdHRTZXJ2aWNlQ29uZmlnIH0gZnJvbSAnLi9tcXR0Lm1vZHVsZSc7XHJcblxyXG4vLyBBIGphdmFzY3JpcHQgZnVuY3Rpb24gdGhhdCB0YWtlcyB0d28gb2JqZWN0cyBhbmQgbWVyZ2VzIHRoZW0gcmVjdXJzaXZlbHlcclxuZnVuY3Rpb24gbWVyZ2VEZWVwKHRhcmdldDogYW55LCAuLi5zb3VyY2VzOiBhbnlbXSk6IGFueSB7XHJcbiAgaWYgKCFzb3VyY2VzLmxlbmd0aCkge1xyXG4gICAgcmV0dXJuIHRhcmdldDtcclxuICB9XHJcbiAgY29uc3Qgc291cmNlID0gc291cmNlcy5zaGlmdCgpO1xyXG5cclxuICBpZiAoaXNPYmplY3QodGFyZ2V0KSAmJiBpc09iamVjdChzb3VyY2UpKSB7XHJcbiAgICBmb3IgKGNvbnN0IGtleSBpbiBzb3VyY2UpIHtcclxuICAgICAgaWYgKGlzT2JqZWN0KHNvdXJjZVtrZXldKSkge1xyXG4gICAgICAgIGlmICghdGFyZ2V0W2tleV0pIHtcclxuICAgICAgICAgIE9iamVjdC5hc3NpZ24odGFyZ2V0LCB7IFtrZXldOiB7fSB9KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgbWVyZ2VEZWVwKHRhcmdldFtrZXldLCBzb3VyY2Vba2V5XSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgT2JqZWN0LmFzc2lnbih0YXJnZXQsIHsgW2tleV06IHNvdXJjZVtrZXldIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gbWVyZ2VEZWVwKHRhcmdldCwgLi4uc291cmNlcyk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGlzT2JqZWN0KGl0ZW06IGFueSk6IGl0ZW0gaXMgT2JqZWN0IHtcclxuICByZXR1cm4gaXRlbSAmJiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkoaXRlbSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXaXRoIGFuIGluc3RhbmNlIG9mIE1xdHRTZXJ2aWNlLCB5b3UgY2FuIG9ic2VydmUgYW5kIHN1YnNjcmliZSB0byBNUVRUIGluIG11bHRpcGxlIHBsYWNlcywgZS5nLiBpbiBkaWZmZXJlbnQgY29tcG9uZW50cyxcclxuICogdG8gb25seSBzdWJzY3JpYmUgdG8gdGhlIGJyb2tlciBvbmNlIHBlciBNUVRUIGZpbHRlci5cclxuICogSXQgYWxzbyBoYW5kbGVzIHByb3BlciB1bnN1YnNjcmlwdGlvbiBmcm9tIHRoZSBicm9rZXIsIGlmIHRoZSBsYXN0IG9ic2VydmFibGUgd2l0aCBhIGZpbHRlciBpcyBjbG9zZWQuXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7XHJcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgTXF0dFNlcnZpY2Uge1xyXG4gIHByaXZhdGUgY2xpZW50ITogTXF0dENsaWVudDtcclxuXHJcbiAgLyoqXHJcbiAgICogVGhlIGNvbnN0cnVjdG9yIG5lZWRzIFtjb25uZWN0aW9uIG9wdGlvbnNde0BsaW5rIElNcXR0U2VydmljZU9wdGlvbnN9IHJlZ2FyZGluZyB0aGUgYnJva2VyIGFuZCBzb21lXHJcbiAgICogb3B0aW9ucyB0byBjb25maWd1cmUgYmVoYXZpb3Igb2YgdGhpcyBzZXJ2aWNlLCBsaWtlIGlmIHRoZSBjb25uZWN0aW9uIHRvIHRoZSBicm9rZXJcclxuICAgKiBzaG91bGQgYmUgZXN0YWJsaXNoZWQgb24gY3JlYXRpb24gb2YgdGhpcyBzZXJ2aWNlIG9yIG5vdC5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihcclxuICAgIEBJbmplY3QoTXF0dFNlcnZpY2VDb25maWcpIHByaXZhdGUgb3B0aW9uczogSU1xdHRTZXJ2aWNlT3B0aW9ucyxcclxuICAgIEBJbmplY3QoTXF0dENsaWVudFNlcnZpY2UpIGNsaWVudD86IE1xdHRDbGllbnRcclxuICApIHtcclxuICAgIGlmIChvcHRpb25zLmNvbm5lY3RPbkNyZWF0ZSAhPT0gZmFsc2UpIHtcclxuICAgICAgdGhpcy5jb25uZWN0KHt9LCBjbGllbnQpO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuc3RhdGUuc3Vic2NyaWJlKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBnZXRzIHRoZSBfY2xpZW50SWRcclxuICAgKi9cclxuICBwdWJsaWMgZ2V0IGNsaWVudElkKCkge1xyXG4gICAgcmV0dXJuIHRoaXMuX2NsaWVudElkO1xyXG4gIH1cclxuXHJcbiAgLyoqIEFuIEV2ZW50RW1pdHRlciB0byBsaXN0ZW4gdG8gY29ubmVjdCBtZXNzYWdlcyAqL1xyXG4gIHB1YmxpYyBnZXQgb25Db25uZWN0KCk6IEV2ZW50RW1pdHRlcjxJT25Db25uZWN0RXZlbnQ+IHtcclxuICAgIHJldHVybiB0aGlzLl9vbkNvbm5lY3Q7XHJcbiAgfVxyXG5cclxuICAvKiogQW4gRXZlbnRFbWl0dGVyIHRvIGxpc3RlbiB0byByZWNvbm5lY3QgbWVzc2FnZXMgKi9cclxuICBwdWJsaWMgZ2V0IG9uUmVjb25uZWN0KCk6IEV2ZW50RW1pdHRlcjx2b2lkPiB7XHJcbiAgICByZXR1cm4gdGhpcy5fb25SZWNvbm5lY3Q7XHJcbiAgfVxyXG5cclxuICAvKiogQW4gRXZlbnRFbWl0dGVyIHRvIGxpc3RlbiB0byBjbG9zZSBtZXNzYWdlcyAqL1xyXG4gIHB1YmxpYyBnZXQgb25DbG9zZSgpOiBFdmVudEVtaXR0ZXI8dm9pZD4ge1xyXG4gICAgcmV0dXJuIHRoaXMuX29uQ2xvc2U7XHJcbiAgfVxyXG5cclxuICAvKiogQW4gRXZlbnRFbWl0dGVyIHRvIGxpc3RlbiB0byBvZmZsaW5lIGV2ZW50cyAqL1xyXG4gIHB1YmxpYyBnZXQgb25PZmZsaW5lKCk6IEV2ZW50RW1pdHRlcjx2b2lkPiB7XHJcbiAgICByZXR1cm4gdGhpcy5fb25PZmZsaW5lO1xyXG4gIH1cclxuXHJcbiAgLyoqIEFuIEV2ZW50RW1pdHRlciB0byBsaXN0ZW4gdG8gZXJyb3IgZXZlbnRzICovXHJcbiAgcHVibGljIGdldCBvbkVycm9yKCk6IEV2ZW50RW1pdHRlcjxJT25FcnJvckV2ZW50PiB7XHJcbiAgICByZXR1cm4gdGhpcy5fb25FcnJvcjtcclxuICB9XHJcblxyXG4gIC8qKiBBbiBFdmVudEVtaXR0ZXIgdG8gbGlzdGVuIHRvIGNsb3NlIG1lc3NhZ2VzICovXHJcbiAgcHVibGljIGdldCBvbkVuZCgpOiBFdmVudEVtaXR0ZXI8dm9pZD4ge1xyXG4gICAgcmV0dXJuIHRoaXMuX29uRW5kO1xyXG4gIH1cclxuXHJcbiAgLyoqIEFuIEV2ZW50RW1pdHRlciB0byBsaXN0ZW4gdG8gbWVzc2FnZSBldmVudHMgKi9cclxuICBwdWJsaWMgZ2V0IG9uTWVzc2FnZSgpOiBFdmVudEVtaXR0ZXI8UGFja2V0PiB7XHJcbiAgICByZXR1cm4gdGhpcy5fb25NZXNzYWdlO1xyXG4gIH1cclxuXHJcbiAgLyoqIEFuIEV2ZW50RW1pdHRlciB0byBsaXN0ZW4gdG8gcGFja2V0c2VuZCBtZXNzYWdlcyAqL1xyXG4gIHB1YmxpYyBnZXQgb25QYWNrZXRzZW5kKCk6IEV2ZW50RW1pdHRlcjxJT25QYWNrZXRzZW5kRXZlbnQ+IHtcclxuICAgIHJldHVybiB0aGlzLl9vblBhY2tldHNlbmQ7XHJcbiAgfVxyXG5cclxuICAvKiogQW4gRXZlbnRFbWl0dGVyIHRvIGxpc3RlbiB0byBwYWNrZXRyZWNlaXZlIG1lc3NhZ2VzICovXHJcbiAgcHVibGljIGdldCBvblBhY2tldHJlY2VpdmUoKTogRXZlbnRFbWl0dGVyPElPblBhY2tldHJlY2VpdmVFdmVudD4ge1xyXG4gICAgcmV0dXJuIHRoaXMuX29uUGFja2V0cmVjZWl2ZTtcclxuICB9XHJcblxyXG4gIC8qKiBBbiBFdmVudEVtaXR0ZXIgdG8gbGlzdGVuIHRvIHN1YmFjayBldmVudHMgKi9cclxuICBwdWJsaWMgZ2V0IG9uU3ViYWNrKCk6IEV2ZW50RW1pdHRlcjxJT25TdWJhY2tFdmVudD4ge1xyXG4gICAgcmV0dXJuIHRoaXMuX29uU3ViYWNrO1xyXG4gIH1cclxuXHJcbiAgLyoqIEFuIGFjdHVhbCBjb25uZWN0aW9uIHN0YXRlICoqL1xyXG4gIHB1YmxpYyBnZXQgY29ubmVjdGlvblN0YXRlKCk6IE1xdHRDb25uZWN0aW9uU3RhdGUge1xyXG4gICAgcmV0dXJuIHRoaXMuc3RhdGUudmFsdWU7XHJcbiAgfVxyXG5cclxuICAvKiogYSBtYXAgb2YgYWxsIG1xdHQgb2JzZXJ2YWJsZXMgYnkgZmlsdGVyICovXHJcbiAgcHVibGljIG9ic2VydmFibGVzOiB7IFtmaWx0ZXJTdHJpbmc6IHN0cmluZ106IE9ic2VydmFibGU8SU1xdHRNZXNzYWdlPiB9ID0ge307XHJcbiAgLyoqIHRoZSBjb25uZWN0aW9uIHN0YXRlICovXHJcbiAgcHVibGljIHN0YXRlOiBCZWhhdmlvclN1YmplY3Q8TXF0dENvbm5lY3Rpb25TdGF0ZT4gPSBuZXcgQmVoYXZpb3JTdWJqZWN0PE1xdHRDb25uZWN0aW9uU3RhdGU+KE1xdHRDb25uZWN0aW9uU3RhdGUuQ0xPU0VEKTtcclxuICAvKiogYW4gb2JzZXJ2YWJsZSBvZiB0aGUgbGFzdCBtcXR0IG1lc3NhZ2UgKi9cclxuICBwdWJsaWMgbWVzc2FnZXM6IFN1YmplY3Q8SU1xdHRNZXNzYWdlPiA9IG5ldyBTdWJqZWN0PElNcXR0TWVzc2FnZT4oKTtcclxuXHJcbiAgcHJpdmF0ZSBfY2xpZW50SWQgPSB0aGlzLl9nZW5lcmF0ZUNsaWVudElkKCk7XHJcbiAgcHJpdmF0ZSBfY29ubmVjdFRpbWVvdXQgPSAxMDAwMDtcclxuICBwcml2YXRlIF9yZWNvbm5lY3RQZXJpb2QgPSAxMDAwMDtcclxuICBwcml2YXRlIF91cmwhOiBzdHJpbmc7XHJcblxyXG4gIHByaXZhdGUgX29uQ29ubmVjdDogRXZlbnRFbWl0dGVyPElPbkNvbm5lY3RFdmVudD4gPSBuZXcgRXZlbnRFbWl0dGVyPElPbkNvbm5lY3RFdmVudD4oKTtcclxuICBwcml2YXRlIF9vblJlY29ubmVjdDogRXZlbnRFbWl0dGVyPHZvaWQ+ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xyXG4gIHByaXZhdGUgX29uQ2xvc2U6IEV2ZW50RW1pdHRlcjx2b2lkPiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuICBwcml2YXRlIF9vbk9mZmxpbmU6IEV2ZW50RW1pdHRlcjx2b2lkPiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuICBwcml2YXRlIF9vbkVycm9yOiBFdmVudEVtaXR0ZXI8SU9uRXJyb3JFdmVudD4gPSBuZXcgRXZlbnRFbWl0dGVyPElPbkVycm9yRXZlbnQ+KCk7XHJcbiAgcHJpdmF0ZSBfb25FbmQ6IEV2ZW50RW1pdHRlcjx2b2lkPiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuICBwcml2YXRlIF9vbk1lc3NhZ2U6IEV2ZW50RW1pdHRlcjxQYWNrZXQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxQYWNrZXQ+KCk7XHJcbiAgcHJpdmF0ZSBfb25TdWJhY2s6IEV2ZW50RW1pdHRlcjxJT25TdWJhY2tFdmVudD4gPSBuZXcgRXZlbnRFbWl0dGVyPElPblN1YmFja0V2ZW50PigpO1xyXG4gIHByaXZhdGUgX29uUGFja2V0c2VuZDogRXZlbnRFbWl0dGVyPElPblBhY2tldHNlbmRFdmVudD4gPSBuZXcgRXZlbnRFbWl0dGVyPElPblBhY2tldHNlbmRFdmVudD4oKTtcclxuICBwcml2YXRlIF9vblBhY2tldHJlY2VpdmU6IEV2ZW50RW1pdHRlcjxJT25QYWNrZXRyZWNlaXZlRXZlbnQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxJT25QYWNrZXRyZWNlaXZlRXZlbnQ+KCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgc3RhdGljIG1ldGhvZCBzaGFsbCBiZSB1c2VkIHRvIGRldGVybWluZSB3aGV0aGVyIGEgTVFUVFxyXG4gICAqIHRvcGljIG1hdGNoZXMgYSBnaXZlbiBmaWx0ZXIuIFRoZSBtYXRjaGluZyBydWxlcyBhcmUgc3BlY2lmaWVkIGluIHRoZSBNUVRUXHJcbiAgICogc3RhbmRhcmQgZG9jdW1lbnRhdGlvbiBhbmQgaW4gdGhlIGxpYnJhcnkgdGVzdCBzdWl0ZS5cclxuICAgKlxyXG4gICAqIEBwYXJhbSAge3N0cmluZ30gIGZpbHRlciBBIGZpbHRlciBtYXkgY29udGFpbiB3aWxkY2FyZHMgbGlrZSAnIycgYW5kICcrJy5cclxuICAgKiBAcGFyYW0gIHtzdHJpbmd9ICB0b3BpYyAgQSB0b3BpYyBtYXkgbm90IGNvbnRhaW4gd2lsZGNhcmRzLlxyXG4gICAqIEByZXR1cm4ge2Jvb2xlYW59ICAgICAgICB0cnVlIG9uIG1hdGNoIGFuZCBmYWxzZSBvdGhlcndpc2UuXHJcbiAgICovXHJcbiAgcHVibGljIHN0YXRpYyBmaWx0ZXJNYXRjaGVzVG9waWMoZmlsdGVyU3RyaW5nOiBzdHJpbmcsIHRvcGljOiBzdHJpbmcpOiBib29sZWFuIHtcclxuICAgIGlmIChmaWx0ZXJTdHJpbmdbMF0gPT09ICcjJyAmJiB0b3BpY1swXSA9PT0gJyQnKSB7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuICAgIC8vIFByZXBhcmF0aW9uOiBzcGxpdCBhbmQgcmV2ZXJzZSBvbiAnLycuIFRoZSBKYXZhU2NyaXB0IHNwbGl0IGZ1bmN0aW9uIGlzIHNhbmUuXHJcbiAgICBjb25zdCBmcyA9IChmaWx0ZXJTdHJpbmcgfHwgJycpLnNwbGl0KCcvJykucmV2ZXJzZSgpO1xyXG4gICAgY29uc3QgdHMgPSAodG9waWMgfHwgJycpLnNwbGl0KCcvJykucmV2ZXJzZSgpO1xyXG4gICAgLy8gVGhpcyBmdW5jdGlvbiBpcyB0YWlsIHJlY3Vyc2l2ZSBhbmQgY29tcGFyZXMgYm90aCBhcnJheXMgb25lIGVsZW1lbnQgYXQgYSB0aW1lLlxyXG4gICAgY29uc3QgbWF0Y2ggPSAoKTogYm9vbGVhbiA9PiB7XHJcbiAgICAgIC8vIEN1dHRpbmcgb2YgdGhlIGxhc3QgZWxlbWVudCBvZiBib3RoIHRoZSBmaWx0ZXIgYW5kIHRoZSB0b3BpYyB1c2luZyBwb3AoKS5cclxuICAgICAgY29uc3QgZiA9IGZzLnBvcCgpO1xyXG4gICAgICBjb25zdCB0ID0gdHMucG9wKCk7XHJcbiAgICAgIHN3aXRjaCAoZikge1xyXG4gICAgICAgIC8vIEluIGNhc2UgdGhlIGZpbHRlciBsZXZlbCBpcyAnIycsIHRoaXMgaXMgYSBtYXRjaCBubyBtYXR0ZXIgd2hldGhlclxyXG4gICAgICAgIC8vIHRoZSB0b3BpYyBpcyB1bmRlZmluZWQgb24gdGhpcyBsZXZlbCBvciBub3QgKCcjJyBtYXRjaGVzIHBhcmVudCBlbGVtZW50IGFzIHdlbGwhKS5cclxuICAgICAgICBjYXNlICcjJzpcclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIC8vIEluIGNhc2UgdGhlIGZpbHRlciBsZXZlbCBpcyAnKycsIHdlIHNoYWxsIGRpdmUgaW50byB0aGUgcmVjdXJzaW9uIG9ubHkgaWYgdCBpcyBub3QgdW5kZWZpbmVkLlxyXG4gICAgICAgIGNhc2UgJysnOlxyXG4gICAgICAgICAgcmV0dXJuIHQgPyBtYXRjaCgpIDogZmFsc2U7XHJcbiAgICAgICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzIHRoZSBmaWx0ZXIgbGV2ZWwgbXVzdCBtYXRjaCB0aGUgdG9waWMgbGV2ZWwsXHJcbiAgICAgICAgLy8gYm90aCBtdXN0IGJlIGRlZmluZWQgYW5kIHRoZSBmaWx0ZXIgdGFpbCBtdXN0IG1hdGNoIHRoZSB0b3BpY1xyXG4gICAgICAgIC8vIHRhaWwgKHdoaWNoIGlzIGRldGVybWluZWQgYnkgdGhlIHJlY3Vyc2l2ZSBjYWxsIG9mIG1hdGNoKCkpLlxyXG4gICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICByZXR1cm4gZiA9PT0gdCAmJiAoZiA9PT0gdW5kZWZpbmVkID8gdHJ1ZSA6IG1hdGNoKCkpO1xyXG4gICAgICB9XHJcbiAgICB9O1xyXG4gICAgcmV0dXJuIG1hdGNoKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBjb25uZWN0IG1hbnVhbGx5IGNvbm5lY3RzIHRvIHRoZSBtcXR0IGJyb2tlci5cclxuICAgKi9cclxuICBwdWJsaWMgY29ubmVjdChvcHRzPzogSU1xdHRTZXJ2aWNlT3B0aW9ucywgY2xpZW50PzogTXF0dENsaWVudCkge1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IG1lcmdlRGVlcCh0aGlzLm9wdGlvbnMgfHwge30sIG9wdHMpO1xyXG4gICAgY29uc3QgcHJvdG9jb2wgPSBvcHRpb25zLnByb3RvY29sIHx8ICd3cyc7XHJcbiAgICBjb25zdCBob3N0bmFtZSA9IG9wdGlvbnMuaG9zdG5hbWUgfHwgJ2xvY2FsaG9zdCc7XHJcbiAgICBpZiAob3B0aW9ucy51cmwpIHtcclxuICAgICAgdGhpcy5fdXJsID0gb3B0aW9ucy51cmw7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLl91cmwgPSBgJHtwcm90b2NvbH06Ly8ke2hvc3RuYW1lfWA7XHJcbiAgICAgIHRoaXMuX3VybCArPSBvcHRpb25zLnBvcnQgPyBgOiR7b3B0aW9ucy5wb3J0fWAgOiAnJztcclxuICAgICAgdGhpcy5fdXJsICs9IG9wdGlvbnMucGF0aCA/IGAke29wdGlvbnMucGF0aH1gIDogJyc7XHJcbiAgICB9XHJcbiAgICB0aGlzLnN0YXRlLm5leHQoTXF0dENvbm5lY3Rpb25TdGF0ZS5DT05ORUNUSU5HKTtcclxuICAgIGNvbnN0IG1lcmdlZE9wdGlvbnMgPSBtZXJnZURlZXAoe1xyXG4gICAgICBjbGllbnRJZDogdGhpcy5fY2xpZW50SWQsXHJcbiAgICAgIHJlY29ubmVjdFBlcmlvZDogdGhpcy5fcmVjb25uZWN0UGVyaW9kLFxyXG4gICAgICBjb25uZWN0VGltZW91dDogdGhpcy5fY29ubmVjdFRpbWVvdXRcclxuICAgIH0sIG9wdGlvbnMpO1xyXG5cclxuICAgIGlmICh0aGlzLmNsaWVudCkge1xyXG4gICAgICB0aGlzLmNsaWVudC5lbmQodHJ1ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFjbGllbnQpIHtcclxuICAgICAgdGhpcy5jbGllbnQgPSBjb25uZWN0KHRoaXMuX3VybCwgbWVyZ2VkT3B0aW9ucyk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLmNsaWVudCA9IGNsaWVudDtcclxuICAgIH1cclxuICAgIHRoaXMuX2NsaWVudElkID0gbWVyZ2VkT3B0aW9ucy5jbGllbnRJZDtcclxuXHJcbiAgICB0aGlzLmNsaWVudC5vbignY29ubmVjdCcsIHRoaXMuX2hhbmRsZU9uQ29ubmVjdCk7XHJcbiAgICB0aGlzLmNsaWVudC5vbigncmVjb25uZWN0JywgdGhpcy5faGFuZGxlT25SZWNvbm5lY3QpO1xyXG4gICAgdGhpcy5jbGllbnQub24oJ2Nsb3NlJywgdGhpcy5faGFuZGxlT25DbG9zZSk7XHJcbiAgICB0aGlzLmNsaWVudC5vbignb2ZmbGluZScsIHRoaXMuX2hhbmRsZU9uT2ZmbGluZSk7XHJcbiAgICB0aGlzLmNsaWVudC5vbignZXJyb3InLCB0aGlzLl9oYW5kbGVPbkVycm9yKTtcclxuICAgICh0aGlzLmNsaWVudCBhcyBhbnkpLnN0cmVhbS5vbignZXJyb3InLCB0aGlzLl9oYW5kbGVPbkVycm9yKTtcclxuICAgIHRoaXMuY2xpZW50Lm9uKCdlbmQnLCB0aGlzLl9oYW5kbGVPbkVuZCk7XHJcbiAgICB0aGlzLmNsaWVudC5vbignbWVzc2FnZScsIHRoaXMuX2hhbmRsZU9uTWVzc2FnZSk7XHJcbiAgICB0aGlzLmNsaWVudC5vbigncGFja2V0c2VuZCcsIHRoaXMuX2hhbmRsZU9uUGFja2V0c2VuZCk7XHJcbiAgICB0aGlzLmNsaWVudC5vbigncGFja2V0cmVjZWl2ZScsIHRoaXMuX2hhbmRsZU9uUGFja2V0cmVjZWl2ZSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBkaXNjb25uZWN0IGRpc2Nvbm5lY3RzIGZyb20gdGhlIG1xdHQgY2xpZW50LlxyXG4gICAqIFRoaXMgbWV0aG9kIGBzaG91bGRgIGJlIGV4ZWN1dGVkIHdoZW4gbGVhdmluZyB0aGUgYXBwbGljYXRpb24uXHJcbiAgICovXHJcbiAgcHVibGljIGRpc2Nvbm5lY3QoZm9yY2UgPSB0cnVlKSB7XHJcbiAgICBpZiAoIXRoaXMuY2xpZW50KSB7XHJcbiAgICAgIHRocm93IG5ldyBFcnJvcignbXF0dCBjbGllbnQgbm90IGNvbm5lY3RlZCcpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5jbGllbnQuZW5kKGZvcmNlKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFdpdGggdGhpcyBtZXRob2QsIHlvdSBjYW4gb2JzZXJ2ZSBtZXNzYWdlcyBmb3IgYSBtcXR0IHRvcGljLlxyXG4gICAqIFRoZSBvYnNlcnZhYmxlIHdpbGwgb25seSBlbWl0IG1lc3NhZ2VzIG1hdGNoaW5nIHRoZSBmaWx0ZXIuXHJcbiAgICogVGhlIGZpcnN0IG9uZSBzdWJzY3JpYmluZyB0byB0aGUgcmVzdWx0aW5nIG9ic2VydmFibGUgZXhlY3V0ZXMgYSBtcXR0IHN1YnNjcmliZS5cclxuICAgKiBUaGUgbGFzdCBvbmUgdW5zdWJzY3JpYmluZyB0aGlzIGZpbHRlciBleGVjdXRlcyBhIG1xdHQgdW5zdWJzY3JpYmUuXHJcbiAgICogRXZlcnkgbmV3IHN1YnNjcmliZXIgZ2V0cyB0aGUgbGF0ZXN0IG1lc3NhZ2UuXHJcbiAgICovXHJcbiAgcHVibGljIG9ic2VydmVSZXRhaW5lZChmaWx0ZXJTdHJpbmc6IHN0cmluZywgb3B0czogSUNsaWVudFN1YnNjcmliZU9wdGlvbnMgPSB7IHFvczogMSB9KTogT2JzZXJ2YWJsZTxJTXF0dE1lc3NhZ2U+IHtcclxuICAgIHJldHVybiB0aGlzLl9nZW5lcmFsT2JzZXJ2ZShmaWx0ZXJTdHJpbmcsICgpID0+IHB1Ymxpc2hSZXBsYXkoMSksIG9wdHMpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogV2l0aCB0aGlzIG1ldGhvZCwgeW91IGNhbiBvYnNlcnZlIG1lc3NhZ2VzIGZvciBhIG1xdHQgdG9waWMuXHJcbiAgICogVGhlIG9ic2VydmFibGUgd2lsbCBvbmx5IGVtaXQgbWVzc2FnZXMgbWF0Y2hpbmcgdGhlIGZpbHRlci5cclxuICAgKiBUaGUgZmlyc3Qgb25lIHN1YnNjcmliaW5nIHRvIHRoZSByZXN1bHRpbmcgb2JzZXJ2YWJsZSBleGVjdXRlcyBhIG1xdHQgc3Vic2NyaWJlLlxyXG4gICAqIFRoZSBsYXN0IG9uZSB1bnN1YnNjcmliaW5nIHRoaXMgZmlsdGVyIGV4ZWN1dGVzIGEgbXF0dCB1bnN1YnNjcmliZS5cclxuICAgKi9cclxuICBwdWJsaWMgb2JzZXJ2ZShmaWx0ZXJTdHJpbmc6IHN0cmluZywgb3B0czogSUNsaWVudFN1YnNjcmliZU9wdGlvbnMgPSB7IHFvczogMSB9KTogT2JzZXJ2YWJsZTxJTXF0dE1lc3NhZ2U+IHtcclxuICAgIHJldHVybiB0aGlzLl9nZW5lcmFsT2JzZXJ2ZShmaWx0ZXJTdHJpbmcsICgpID0+IHB1Ymxpc2goKSwgb3B0cyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBXaXRoIHRoaXMgbWV0aG9kLCB5b3UgY2FuIG9ic2VydmUgbWVzc2FnZXMgZm9yIGEgbXF0dCB0b3BpYy5cclxuICAgKiBUaGUgb2JzZXJ2YWJsZSB3aWxsIG9ubHkgZW1pdCBtZXNzYWdlcyBtYXRjaGluZyB0aGUgZmlsdGVyLlxyXG4gICAqIFRoZSBmaXJzdCBvbmUgc3Vic2NyaWJpbmcgdG8gdGhlIHJlc3VsdGluZyBvYnNlcnZhYmxlIGV4ZWN1dGVzIGEgbXF0dCBzdWJzY3JpYmUuXHJcbiAgICogVGhlIGxhc3Qgb25lIHVuc3Vic2NyaWJpbmcgdGhpcyBmaWx0ZXIgZXhlY3V0ZXMgYSBtcXR0IHVuc3Vic2NyaWJlLlxyXG4gICAqIERlcGVuZGluZyBvbiB0aGUgcHVibGlzaCBmdW5jdGlvbiwgdGhlIG1lc3NhZ2VzIHdpbGwgZWl0aGVyIGJlIHJlcGxheWVkIGFmdGVyIG5ld1xyXG4gICAqIHN1YnNjcmliZXJzIHN1YnNjcmliZSBvciB0aGUgbWVzc2FnZXMgYXJlIGp1c3QgcGFzc2VkIHRocm91Z2hcclxuICAgKi9cclxuICBwcml2YXRlIF9nZW5lcmFsT2JzZXJ2ZShmaWx0ZXJTdHJpbmc6IHN0cmluZywgcHVibGlzaEZuOiBGdW5jdGlvbiwgb3B0czogSUNsaWVudFN1YnNjcmliZU9wdGlvbnMpOiBPYnNlcnZhYmxlPElNcXR0TWVzc2FnZT4ge1xyXG4gICAgaWYgKCF0aGlzLmNsaWVudCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21xdHQgY2xpZW50IG5vdCBjb25uZWN0ZWQnKTtcclxuICAgIH1cclxuICAgIGlmICghdGhpcy5vYnNlcnZhYmxlc1tmaWx0ZXJTdHJpbmddKSB7XHJcbiAgICAgIGNvbnN0IHJlamVjdGVkOiBTdWJqZWN0PElNcXR0TWVzc2FnZT4gPSBuZXcgU3ViamVjdCgpO1xyXG4gICAgICB0aGlzLm9ic2VydmFibGVzW2ZpbHRlclN0cmluZ10gPSB1c2luZyhcclxuICAgICAgICAvLyByZXNvdXJjZUZhY3Rvcnk6IERvIHRoZSBhY3R1YWwgcmVmLWNvdW50aW5nIE1RVFQgc3Vic2NyaXB0aW9uLlxyXG4gICAgICAgIC8vIHJlZmNvdW50IGlzIGRlY3JlYXNlZCBvbiB1bnN1YnNjcmliZS5cclxuICAgICAgICAoKSA9PiB7XHJcbiAgICAgICAgICBjb25zdCBzdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbiA9IG5ldyBTdWJzY3JpcHRpb24oKTtcclxuICAgICAgICAgIHRoaXMuY2xpZW50LnN1YnNjcmliZShmaWx0ZXJTdHJpbmcsIG9wdHMsIChlcnI6IGFueSwgZ3JhbnRlZDogSVN1YnNjcmlwdGlvbkdyYW50W10gfCB1bmRlZmluZWQpID0+IHtcclxuICAgICAgICAgICAgaWYgKGdyYW50ZWQpIHsgLy8gZ3JhbnRlZCBjYW4gYmUgdW5kZWZpbmVkIHdoZW4gYW4gZXJyb3Igb2NjdXJzIHdoZW4gdGhlIGNsaWVudCBpcyBkaXNjb25uZWN0aW5nXHJcbiAgICAgICAgICAgICAgZ3JhbnRlZC5mb3JFYWNoKChncmFudGVkXzogSVN1YnNjcmlwdGlvbkdyYW50KSA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAoZ3JhbnRlZF8ucW9zID09PSAxMjgpIHtcclxuICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMub2JzZXJ2YWJsZXNbZ3JhbnRlZF8udG9waWNdO1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLmNsaWVudC51bnN1YnNjcmliZShncmFudGVkXy50b3BpYyk7XHJcbiAgICAgICAgICAgICAgICAgIHJlamVjdGVkLmVycm9yKGBzdWJzY3JpcHRpb24gZm9yICcke2dyYW50ZWRfLnRvcGljfScgcmVqZWN0ZWQhYCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9vblN1YmFjay5lbWl0KHsgZmlsdGVyOiBmaWx0ZXJTdHJpbmcsIGdyYW50ZWQ6IGdyYW50ZWRfLnFvcyAhPT0gMTI4IH0pO1xyXG4gICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICAgIHN1YnNjcmlwdGlvbi5hZGQoKCkgPT4ge1xyXG4gICAgICAgICAgICBkZWxldGUgdGhpcy5vYnNlcnZhYmxlc1tmaWx0ZXJTdHJpbmddO1xyXG4gICAgICAgICAgICB0aGlzLmNsaWVudC51bnN1YnNjcmliZShmaWx0ZXJTdHJpbmcpO1xyXG4gICAgICAgICAgfSk7XHJcbiAgICAgICAgICByZXR1cm4gc3Vic2NyaXB0aW9uO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgLy8gb2JzZXJ2YWJsZUZhY3Rvcnk6IENyZWF0ZSB0aGUgb2JzZXJ2YWJsZSB0aGF0IGlzIGNvbnN1bWVkIGZyb20uXHJcbiAgICAgICAgLy8gVGhpcyBwYXJ0IGlzIG5vdCBleGVjdXRlZCB1bnRpbCB0aGUgT2JzZXJ2YWJsZSByZXR1cm5lZCBieVxyXG4gICAgICAgIC8vIGBvYnNlcnZlYCBnZXRzIGFjdHVhbGx5IHN1YnNjcmliZWQuXHJcbiAgICAgICAgKHN1YnNjcmlwdGlvbjogVW5zdWJzY3JpYmFibGUgfCB2b2lkKSA9PiBtZXJnZShyZWplY3RlZCwgdGhpcy5tZXNzYWdlcykpXHJcbiAgICAgICAgLnBpcGUoXHJcbiAgICAgICAgICBmaWx0ZXIoKG1zZzogSU1xdHRNZXNzYWdlKSA9PiBNcXR0U2VydmljZS5maWx0ZXJNYXRjaGVzVG9waWMoZmlsdGVyU3RyaW5nLCBtc2cudG9waWMpKSxcclxuICAgICAgICAgIHB1Ymxpc2hGbigpLFxyXG4gICAgICAgICAgcmVmQ291bnQoKVxyXG4gICAgICAgICkgYXMgT2JzZXJ2YWJsZTxJTXF0dE1lc3NhZ2U+O1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRoaXMub2JzZXJ2YWJsZXNbZmlsdGVyU3RyaW5nXTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIHJldHVybnMgYW4gb2JzZXJ2YWJsZSBmb3IgYSB0b3BpYyB3aXRoIG9wdGlvbmFsIG9wdGlvbnMuXHJcbiAgICogQWZ0ZXIgc3Vic2NyaWJpbmcsIHRoZSBhY3R1YWwgbXF0dCBwdWJsaWNhdGlvbiB3aWxsIGJlIGV4ZWN1dGVkIGFuZFxyXG4gICAqIHRoZSBvYnNlcnZhYmxlIHdpbGwgZW1pdCBhbiBlbXB0eSB2YWx1ZSBhbmQgY29tcGxldGVzLCBpZiBwdWJsaXNoaW5nIHdhcyBzdWNjZXNzZnVsXHJcbiAgICogb3IgdGhyb3dzIGFuIGVycm9yLCBpZiB0aGUgcHVibGljYXRpb24gZmFpbHMuXHJcbiAgICovXHJcbiAgcHVibGljIHB1Ymxpc2godG9waWM6IHN0cmluZywgbWVzc2FnZTogc3RyaW5nIHwgQnVmZmVyLCBvcHRpb25zOiBJQ2xpZW50UHVibGlzaE9wdGlvbnMgPSB7fSk6IE9ic2VydmFibGU8dm9pZD4ge1xyXG4gICAgaWYgKCF0aGlzLmNsaWVudCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21xdHQgY2xpZW50IG5vdCBjb25uZWN0ZWQnKTtcclxuICAgIH1cclxuICAgIHJldHVybiBPYnNlcnZhYmxlLmNyZWF0ZSgob2JzOiBPYnNlcnZlcjx2b2lkPikgPT4ge1xyXG4gICAgICB0aGlzLmNsaWVudC5wdWJsaXNoKHRvcGljLCBtZXNzYWdlLCBvcHRpb25zLCAoZXJyb3I6IEVycm9yIHwgdW5kZWZpbmVkKSA9PiB7XHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICBvYnMuZXJyb3IoZXJyb3IpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBvYnMubmV4dCgpO1xyXG4gICAgICAgICAgb2JzLmNvbXBsZXRlKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgcHVibGlzaGVzIGEgbWVzc2FnZSBmb3IgYSB0b3BpYyB3aXRoIG9wdGlvbmFsIG9wdGlvbnMuXHJcbiAgICogSWYgYW4gZXJyb3Igb2NjdXJzLCBpdCB3aWxsIHRocm93LlxyXG4gICAqL1xyXG4gIHB1YmxpYyB1bnNhZmVQdWJsaXNoKHRvcGljOiBzdHJpbmcsIG1lc3NhZ2U6IHN0cmluZyB8IEJ1ZmZlciwgb3B0aW9uczogSVB1Ymxpc2hPcHRpb25zID0ge30pOiB2b2lkIHtcclxuICAgIGlmICghdGhpcy5jbGllbnQpIHtcclxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtcXR0IGNsaWVudCBub3QgY29ubmVjdGVkJyk7XHJcbiAgICB9XHJcbiAgICB0aGlzLmNsaWVudC5wdWJsaXNoKHRvcGljLCBtZXNzYWdlLCBvcHRpb25zLCAoZXJyb3I6IEVycm9yIHwgdW5kZWZpbmVkKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IChlcnJvcik7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBfaGFuZGxlT25Db25uZWN0ID0gKGU6IElPbkNvbm5lY3RFdmVudCkgPT4ge1xyXG4gICAgaWYgKHRoaXMub3B0aW9ucy5jb25uZWN0T25DcmVhdGUgPT09IHRydWUpIHtcclxuICAgICAgT2JqZWN0LmtleXModGhpcy5vYnNlcnZhYmxlcykuZm9yRWFjaCgoZmlsdGVyU3RyaW5nOiBzdHJpbmcpID0+IHtcclxuICAgICAgICB0aGlzLmNsaWVudC5zdWJzY3JpYmUoZmlsdGVyU3RyaW5nKTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICB0aGlzLnN0YXRlLm5leHQoTXF0dENvbm5lY3Rpb25TdGF0ZS5DT05ORUNURUQpO1xyXG4gICAgdGhpcy5fb25Db25uZWN0LmVtaXQoZSk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIF9oYW5kbGVPblJlY29ubmVjdCA9ICgpID0+IHtcclxuICAgIGlmICh0aGlzLm9wdGlvbnMuY29ubmVjdE9uQ3JlYXRlID09PSB0cnVlKSB7XHJcbiAgICAgIE9iamVjdC5rZXlzKHRoaXMub2JzZXJ2YWJsZXMpLmZvckVhY2goKGZpbHRlclN0cmluZzogc3RyaW5nKSA9PiB7XHJcbiAgICAgICAgdGhpcy5jbGllbnQuc3Vic2NyaWJlKGZpbHRlclN0cmluZyk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgdGhpcy5zdGF0ZS5uZXh0KE1xdHRDb25uZWN0aW9uU3RhdGUuQ09OTkVDVElORyk7XHJcbiAgICB0aGlzLl9vblJlY29ubmVjdC5lbWl0KCk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIF9oYW5kbGVPbkNsb3NlID0gKCkgPT4ge1xyXG4gICAgdGhpcy5zdGF0ZS5uZXh0KE1xdHRDb25uZWN0aW9uU3RhdGUuQ0xPU0VEKTtcclxuICAgIHRoaXMuX29uQ2xvc2UuZW1pdCgpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBfaGFuZGxlT25PZmZsaW5lID0gKCkgPT4ge1xyXG4gICAgdGhpcy5fb25PZmZsaW5lLmVtaXQoKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgX2hhbmRsZU9uRXJyb3IgPSAoZTogSU9uRXJyb3JFdmVudCkgPT4ge1xyXG4gICAgdGhpcy5fb25FcnJvci5lbWl0KGUpO1xyXG4gICAgY29uc29sZS5lcnJvcihlKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgX2hhbmRsZU9uRW5kID0gKCkgPT4ge1xyXG4gICAgdGhpcy5fb25FbmQuZW1pdCgpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBfaGFuZGxlT25NZXNzYWdlID0gKHRvcGljOiBzdHJpbmcsIHBheWxvYWQ6IEJ1ZmZlciwgcGFja2V0OiBQYWNrZXQpID0+IHtcclxuICAgIHRoaXMuX29uTWVzc2FnZS5lbWl0KHBhY2tldCk7XHJcbiAgICBpZiAocGFja2V0LmNtZCA9PT0gJ3B1Ymxpc2gnKSB7XHJcbiAgICAgIHRoaXMubWVzc2FnZXMubmV4dChwYWNrZXQgYXMgYW55KTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgX2hhbmRsZU9uUGFja2V0c2VuZCA9IChlOiBJT25QYWNrZXRzZW5kRXZlbnQpID0+IHtcclxuICAgIHRoaXMuX29uUGFja2V0c2VuZC5lbWl0KGUpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBfaGFuZGxlT25QYWNrZXRyZWNlaXZlID0gKGU6IElPblBhY2tldHJlY2VpdmVFdmVudCkgPT4ge1xyXG4gICAgdGhpcy5fb25QYWNrZXRyZWNlaXZlLmVtaXQoZSk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIF9nZW5lcmF0ZUNsaWVudElkKCkge1xyXG4gICAgcmV0dXJuICdjbGllbnQtJyArIE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cigyLCAxOSk7XHJcbiAgfVxyXG59XHJcbiJdfQ==