@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
298 lines • 41.3 kB
JavaScript
import { Injectable, NgModuleRef, EnvironmentInjector, createNgModule } from '@angular/core';
import { reduce, forEach, get, union, camelCase } from 'lodash-es';
import { BehaviorSubject, Subject } from 'rxjs';
import { shareReplay } from 'rxjs/operators';
import { StandalonePluginInjector } from '../common';
import { cloneDeep } from 'lodash-es';
import * as i0 from "@angular/core";
export class PluginsResolveService {
/**
* Takes a list of remotes and turns it into an object containing union of corresponding remotes.
* @param remotes List of the remotes.
* @returns Returns object with merged remotes.
*
* **Example**
* ```typescript
* const remotesA:ApplicationRemotePlugins = { contextPathA: ['moduleA', 'moduleB'] };
* const remotesB:ApplicationRemotePlugins = { contextPathA: ['moduleA'], contextPathB: ['moduleZ'] };
* const mergedRemotes:ApplicationRemotePlugins = mergeRemotes([remotesA, remotesB]);
* // Result
* {
* contextPathA: ['moduleA', 'moduleB'],
* contextPathB: ['moduleZ']
* }
*
* ```
*/
static mergeRemotes(remotes) {
return reduce(remotes, (allRemotes, mfRemote) => {
forEach(mfRemote, (remoteModules, remoteContextPath) => {
const currentRemotes = get(allRemotes, remoteContextPath, []);
allRemotes[remoteContextPath] = union(currentRemotes, remoteModules);
});
return allRemotes;
}, {});
}
static removeRemotes(remotesToRemoveFrom, remotesToRemove) {
const keysToRemove = Object.keys(remotesToRemove || {});
if (!keysToRemove.length) {
return remotesToRemoveFrom;
}
const currentKeys = Object.keys(remotesToRemoveFrom);
const keysPresentInBoth = currentKeys.filter(key => keysToRemove.includes(key));
if (!keysPresentInBoth.length) {
return remotesToRemoveFrom;
}
remotesToRemoveFrom = cloneDeep(remotesToRemoveFrom);
for (const remoteContextPath of keysPresentInBoth) {
const remoteModulesToBeRemoved = remotesToRemove[remoteContextPath];
if (!Array.isArray(remoteModulesToBeRemoved) || !remoteModulesToBeRemoved?.length) {
continue;
}
let currentModules = remotesToRemoveFrom[remoteContextPath];
if (!Array.isArray(currentModules) || !currentModules?.length) {
delete remotesToRemoveFrom[remoteContextPath];
continue;
}
currentModules = currentModules.filter(module => !remoteModulesToBeRemoved.includes(module));
if (currentModules.length) {
remotesToRemoveFrom[remoteContextPath] = currentModules;
}
else {
delete remotesToRemoveFrom[remoteContextPath];
}
}
return remotesToRemoveFrom;
}
constructor(injector) {
this.injector = injector;
this.urlRemotesCache = null;
this.remoteScriptSet = new Set();
this._injectors$ = new Subject();
this._refresh$ = new Subject();
this._pluginDetails$ = new Subject();
this._allPluginsLoaded$ = new BehaviorSubject(false);
this._contextPathsFromWhereRemotesHaveBeenLoaded$ = new BehaviorSubject([]);
this._loadedPluginNames$ = new BehaviorSubject([]);
this.injectors$ = this._injectors$.asObservable().pipe(
// not specifying the bufferSize of shareReplay so all injectors are received on subscription
shareReplay());
this.refresh$ = this._refresh$.asObservable().pipe(shareReplay(1));
this.pluginDetails$ = this._pluginDetails$.asObservable().pipe(
// not specifying the bufferSize of shareReplay so all details are received on subscription
shareReplay());
this.allPluginsLoaded$ = this._allPluginsLoaded$.asObservable();
this.contextPathsFromWhereRemotesHaveBeenLoaded$ =
this._contextPathsFromWhereRemotesHaveBeenLoaded$.asObservable();
this.loadedPluginNames$ = this._loadedPluginNames$.asObservable();
}
/**
* Loads plugins by resolving the remote NgModules and injecting it. Also attaching
* the hooks onto the root injector.
* @param remoteModules The remote plugins to load as factory name mapping array.
*/
resolveRemotePlugins(remoteModules) {
this.loadModulesDynamically(remoteModules);
this.refreshHooks();
this.markPluginsAsLoaded();
}
/**
* Loads modules and handles hooking correctly.
* @param remoteNgModules The modules to load.
*/
loadModulesDynamically(remoteModules) {
for (const { factory, name } of remoteModules) {
try {
const moduleOrProviders = factory[name];
if (Array.isArray(moduleOrProviders)) {
this.loadProviders(moduleOrProviders, name);
continue;
}
else {
this.loadModule(moduleOrProviders);
}
const newLoadedPluginNames = [...this._loadedPluginNames$.value, name];
this._loadedPluginNames$.next(newLoadedPluginNames);
}
catch (ex) {
console.error(`Failed to load ${name}`, ex);
}
}
}
loadProviders(providers, name) {
const injector = new StandalonePluginInjector({
providers: providers,
name: `pluginsInjector-${name}`,
parent: this.injector
});
this._injectors$.next(injector);
}
loadModule(remoteNgModule) {
let moduleRef;
if (remoteNgModule instanceof NgModuleRef) {
// AOT
moduleRef = remoteNgModule;
}
else {
// JIT
moduleRef = createNgModule(remoteNgModule, this.injector);
}
this._injectors$.next(moduleRef.injector);
this._pluginDetails$.next({ moduleRef, remoteNgModule });
return moduleRef;
}
/**
* Will refresh all current registered hooks.
*/
refreshHooks() {
this._refresh$.next();
}
markPluginsAsLoaded() {
this._allPluginsLoaded$.next(true);
}
/**
* Loads a list of remotes so that a particular application can use them.
* The request is made to the following address: /apps/<contextPath>/remoteEntry.js
* @param remotes List of remotes to be loaded.
* @returns Returns the list of loaded modules from remotes.
*/
async loadRemotes(remotes) {
if (!remotes) {
return [];
}
const date = new Date();
const remoteModules = [];
for (const pluginId in remotes) {
if (remotes.hasOwnProperty(pluginId)) {
const moduleNames = remotes[pluginId];
const url = `/apps/${pluginId}/remoteEntry.js?nocache=${date.getTime()}`;
let atLeastOneModuleLoadedSuccessfully = false;
for (const moduleName of moduleNames) {
try {
remoteModules.push(await this.loadRemoteModule(url, pluginId, moduleName));
atLeastOneModuleLoadedSuccessfully = true;
}
catch (ex) {
console.warn(`Could not load remote module '%s' from url:`, moduleName, url);
}
}
// Only add successfully loaded remotes to the list
if (atLeastOneModuleLoadedSuccessfully) {
this._contextPathsFromWhereRemotesHaveBeenLoaded$.next(this._contextPathsFromWhereRemotesHaveBeenLoaded$.value.concat(pluginId));
}
}
}
return remoteModules;
}
/**
* Takes a list of remotes and turns it into an object containing union of corresponding remotes.
* @param mfRemotes List of the remotes.
* @returns Returns object with merged remotes.
* @deprecated Use the static function mergeRemotes as this is a pure function.
*
* **Example**
* ```typescript
* const remotesA:ApplicationRemotePlugins = { contextPathA: ['moduleA', 'moduleB'] };
* const remotesB:ApplicationRemotePlugins = { contextPathA: ['moduleA'], contextPathB: ['moduleZ'] };
* const mergedRemotes:ApplicationRemotePlugins = mergeMFRemotes([remotesA, remotesB]);
* // Result
* {
* contextPathA: ['moduleA', 'moduleB'],
* contextPathB: ['moduleZ']
* }
*
* ```
*/
mergeMFRemotes(mfRemotes) {
return PluginsResolveService.mergeRemotes(mfRemotes);
}
/**
* Clears URL remotes cache.
*/
clearURLRemotesCache() {
this.urlRemotesCache = null;
}
/**
* Retrieves the remotes list from the URL.
* @returns Returns the list of remotes.
*/
loadUrlRemotes() {
if (!this.urlRemotesCache) {
const params = new URLSearchParams(window.location.search);
const remotes = params.get('remotes');
if (remotes) {
try {
this.urlRemotesCache = JSON.parse(decodeURIComponent(remotes));
}
catch (error) {
console.warn(`Failed to parse remotes: ${error}`);
}
}
}
return this.urlRemotesCache;
}
async loadRemoteModule(remoteEntryUrl, remoteContextPath, exposedModule) {
if (!this.remoteScriptSet.has(remoteEntryUrl)) {
this.remoteScriptSet.add(remoteEntryUrl);
await this.loadRemoteEntry(remoteEntryUrl);
}
let contextPath = remoteContextPath;
if (contextPath.includes('@')) {
contextPath = remoteContextPath.split('@')[0];
}
return await this.lookupExposedModule(camelCase(contextPath), exposedModule);
}
loadRemoteEntry(remoteEntryUrl) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = remoteEntryUrl;
script.onerror = reject;
script.onload = () => {
resolve(); // window is the global namespace
};
document.body.append(script);
});
}
async lookupExposedModule(remoteName, exposedModule) {
// Initializes the share scope. This fills it with known provided modules from this build and all remotes
try {
await __webpack_init_sharing__('default');
}
catch (ex) {
console.error(`Module %s could not be loaded. Module Federation is not enabled in this application.`, exposedModule, ex);
}
let container = window[remoteName];
/**
* MTM-60850: In case of e.g. the cockpit app being cloned to a different context path
* the self scoped plugins of cockpit will be loaded from the new context path.
* But the remoteEntry.js will still register the remotes with the original context path,
* as this is hardcoded during compile time.
*
* We therefore add a fallback to the original context path in case there is no container for the remoteName
*/
if (!container) {
const fallbackRemoteName = camelCase(__ORIGINAL_CONTEXT_PATH__);
console.warn(`Attribute "%s" not defined on window object while trying to load "%s". Using "%s" as fallback.`, remoteName, exposedModule, fallbackRemoteName);
container = window[fallbackRemoteName];
}
// Initialize the container, it may provide shared modules
let factory;
try {
await container.init(__webpack_share_scopes__.default);
factory = (await container.get(exposedModule))();
}
catch (ex) {
console.error(`Module %s could not be loaded.`, exposedModule, ex);
}
return { name: exposedModule, factory };
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PluginsResolveService, deps: [{ token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PluginsResolveService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PluginsResolveService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i0.EnvironmentInjector }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy1yZXNvbHZlLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9jb3JlL3BsdWdpbnMvcGx1Z2lucy1yZXNvbHZlLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLFVBQVUsRUFDVixXQUFXLEVBQ1gsbUJBQW1CLEVBRW5CLGNBQWMsRUFHZixNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNuRSxPQUFPLEVBQUUsZUFBZSxFQUFjLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUM1RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3JELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxXQUFXLENBQUM7O0FBZ0J0QyxNQUFNLE9BQU8scUJBQXFCO0lBQ2hDOzs7Ozs7Ozs7Ozs7Ozs7OztPQWlCRztJQUNILE1BQU0sQ0FBQyxZQUFZLENBQUMsT0FBbUM7UUFDckQsT0FBTyxNQUFNLENBQ1gsT0FBTyxFQUNQLENBQUMsVUFBb0MsRUFBRSxRQUFrQyxFQUFFLEVBQUU7WUFDM0UsT0FBTyxDQUFDLFFBQVEsRUFBRSxDQUFDLGFBQXVCLEVBQUUsaUJBQXlCLEVBQUUsRUFBRTtnQkFDdkUsTUFBTSxjQUFjLEdBQUcsR0FBRyxDQUFDLFVBQVUsRUFBRSxpQkFBaUIsRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDOUQsVUFBVSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsS0FBSyxDQUFDLGNBQWMsRUFBRSxhQUFhLENBQUMsQ0FBQztZQUN2RSxDQUFDLENBQUMsQ0FBQztZQUVILE9BQU8sVUFBVSxDQUFDO1FBQ3BCLENBQUMsRUFDRCxFQUFFLENBQ0gsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLENBQUMsYUFBYSxDQUNsQixtQkFBNkMsRUFDN0MsZUFBMEM7UUFFMUMsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxlQUFlLElBQUksRUFBRSxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUN6QixPQUFPLG1CQUFtQixDQUFDO1FBQzdCLENBQUM7UUFDRCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDckQsTUFBTSxpQkFBaUIsR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ2hGLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM5QixPQUFPLG1CQUFtQixDQUFDO1FBQzdCLENBQUM7UUFDRCxtQkFBbUIsR0FBRyxTQUFTLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQUNyRCxLQUFLLE1BQU0saUJBQWlCLElBQUksaUJBQWlCLEVBQUUsQ0FBQztZQUNsRCxNQUFNLHdCQUF3QixHQUFHLGVBQWUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1lBQ3BFLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLHdCQUF3QixDQUFDLElBQUksQ0FBQyx3QkFBd0IsRUFBRSxNQUFNLEVBQUUsQ0FBQztnQkFDbEYsU0FBUztZQUNYLENBQUM7WUFFRCxJQUFJLGNBQWMsR0FBRyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1lBQzVELElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLE1BQU0sRUFBRSxDQUFDO2dCQUM5RCxPQUFPLG1CQUFtQixDQUFDLGlCQUFpQixDQUFDLENBQUM7Z0JBQzlDLFNBQVM7WUFDWCxDQUFDO1lBRUQsY0FBYyxHQUFHLGNBQWMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDLHdCQUF3QixDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQzdGLElBQUksY0FBYyxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUMxQixtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLGNBQWMsQ0FBQztZQUMxRCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sT0FBTyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1lBQ2hELENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxtQkFBbUIsQ0FBQztJQUM3QixDQUFDO0lBbUNELFlBQW9CLFFBQTZCO1FBQTdCLGFBQVEsR0FBUixRQUFRLENBQXFCO1FBWnpDLG9CQUFlLEdBQTZCLElBQUksQ0FBQztRQUNqRCxvQkFBZSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7UUFDcEMsZ0JBQVcsR0FBRyxJQUFJLE9BQU8sRUFBa0MsQ0FBQztRQUM1RCxjQUFTLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUNoQyxvQkFBZSxHQUFHLElBQUksT0FBTyxFQUdqQyxDQUFDO1FBQ0csdUJBQWtCLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7UUFDekQsaURBQTRDLEdBQUcsSUFBSSxlQUFlLENBQVcsRUFBRSxDQUFDLENBQUM7UUFDakYsd0JBQW1CLEdBQUcsSUFBSSxlQUFlLENBQVcsRUFBRSxDQUFDLENBQUM7UUFHOUQsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxDQUFDLElBQUk7UUFDcEQsNkZBQTZGO1FBQzdGLFdBQVcsRUFBRSxDQUNkLENBQUM7UUFDRixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25FLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxJQUFJO1FBQzVELDJGQUEyRjtRQUMzRixXQUFXLEVBQUUsQ0FDZCxDQUFDO1FBQ0YsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNoRSxJQUFJLENBQUMsMkNBQTJDO1lBQzlDLElBQUksQ0FBQyw0Q0FBNEMsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUVuRSxJQUFJLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ3BFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsb0JBQW9CLENBQUMsYUFBK0M7UUFDbEUsSUFBSSxDQUFDLHNCQUFzQixDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzNDLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNwQixJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztJQUM3QixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsc0JBQXNCLENBQUMsYUFBK0M7UUFDcEUsS0FBSyxNQUFNLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLGFBQWEsRUFBRSxDQUFDO1lBQzlDLElBQUksQ0FBQztnQkFDSCxNQUFNLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDeEMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztvQkFDckMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsQ0FBQztvQkFDNUMsU0FBUztnQkFDWCxDQUFDO3FCQUFNLENBQUM7b0JBQ04sSUFBSSxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO2dCQUNyQyxDQUFDO2dCQUNELE1BQU0sb0JBQW9CLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQ3ZFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQztZQUN0RCxDQUFDO1lBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztnQkFDWixPQUFPLENBQUMsS0FBSyxDQUFDLGtCQUFrQixJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUM5QyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRCxhQUFhLENBQUMsU0FBcUIsRUFBRSxJQUFZO1FBQy9DLE1BQU0sUUFBUSxHQUFHLElBQUksd0JBQXdCLENBQUM7WUFDNUMsU0FBUyxFQUFFLFNBQVM7WUFDcEIsSUFBSSxFQUFFLG1CQUFtQixJQUFJLEVBQUU7WUFDL0IsTUFBTSxFQUFFLElBQUksQ0FBQyxRQUFRO1NBQ3RCLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2xDLENBQUM7SUFFRCxVQUFVLENBQWMsY0FBd0M7UUFDOUQsSUFBSSxTQUF5QixDQUFDO1FBQzlCLElBQUksY0FBYyxZQUFZLFdBQVcsRUFBRSxDQUFDO1lBQzFDLE1BQU07WUFDTixTQUFTLEdBQUcsY0FBYyxDQUFDO1FBQzdCLENBQUM7YUFBTSxDQUFDO1lBQ04sTUFBTTtZQUNOLFNBQVMsR0FBRyxjQUFjLENBQUMsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM1RCxDQUFDO1FBQ0QsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzFDLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLEVBQUUsU0FBUyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUM7UUFDekQsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsWUFBWTtRQUNWLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVELG1CQUFtQjtRQUNqQixJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxXQUFXLENBQUMsT0FBaUM7UUFDakQsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDO1FBQ0QsTUFBTSxJQUFJLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN4QixNQUFNLGFBQWEsR0FBRyxFQUFFLENBQUM7UUFDekIsS0FBSyxNQUFNLFFBQVEsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUMvQixJQUFJLE9BQU8sQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztnQkFDckMsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUN0QyxNQUFNLEdBQUcsR0FBRyxTQUFTLFFBQVEsMkJBQTJCLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO2dCQUN6RSxJQUFJLGtDQUFrQyxHQUFHLEtBQUssQ0FBQztnQkFDL0MsS0FBSyxNQUFNLFVBQVUsSUFBSSxXQUFXLEVBQUUsQ0FBQztvQkFDckMsSUFBSSxDQUFDO3dCQUNILGFBQWEsQ0FBQyxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxFQUFFLFFBQVEsRUFBRSxVQUFVLENBQUMsQ0FBQyxDQUFDO3dCQUMzRSxrQ0FBa0MsR0FBRyxJQUFJLENBQUM7b0JBQzVDLENBQUM7b0JBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQzt3QkFDWixPQUFPLENBQUMsSUFBSSxDQUFDLDZDQUE2QyxFQUFFLFVBQVUsRUFBRSxHQUFHLENBQUMsQ0FBQztvQkFDL0UsQ0FBQztnQkFDSCxDQUFDO2dCQUVELG1EQUFtRDtnQkFDbkQsSUFBSSxrQ0FBa0MsRUFBRSxDQUFDO29CQUN2QyxJQUFJLENBQUMsNENBQTRDLENBQUMsSUFBSSxDQUNwRCxJQUFJLENBQUMsNENBQTRDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FDekUsQ0FBQztnQkFDSixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFDRCxPQUFPLGFBQWEsQ0FBQztJQUN2QixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQWtCRztJQUNILGNBQWMsQ0FBQyxTQUFxQztRQUNsRCxPQUFPLHFCQUFxQixDQUFDLFlBQVksQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxvQkFBb0I7UUFDbEIsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUM7SUFDOUIsQ0FBQztJQUVEOzs7T0FHRztJQUNILGNBQWM7UUFDWixJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQzFCLE1BQU0sTUFBTSxHQUFHLElBQUksZUFBZSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDM0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUN0QyxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLElBQUksQ0FBQztvQkFDSCxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztnQkFDakUsQ0FBQztnQkFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO29CQUNmLE9BQU8sQ0FBQyxJQUFJLENBQUMsNEJBQTRCLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQ3BELENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQztJQUM5QixDQUFDO0lBRU8sS0FBSyxDQUFDLGdCQUFnQixDQUM1QixjQUFzQixFQUN0QixpQkFBeUIsRUFDekIsYUFBcUI7UUFFckIsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUM7WUFDOUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLENBQUM7WUFDekMsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQzdDLENBQUM7UUFDRCxJQUFJLFdBQVcsR0FBVyxpQkFBaUIsQ0FBQztRQUM1QyxJQUFJLFdBQVcsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM5QixXQUFXLEdBQUcsaUJBQWlCLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hELENBQUM7UUFDRCxPQUFPLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsRUFBRSxhQUFhLENBQUMsQ0FBQztJQUMvRSxDQUFDO0lBRU8sZUFBZSxDQUFDLGNBQXNCO1FBQzVDLE9BQU8sSUFBSSxPQUFPLENBQU8sQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDM0MsTUFBTSxNQUFNLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNoRCxNQUFNLENBQUMsR0FBRyxHQUFHLGNBQWMsQ0FBQztZQUU1QixNQUFNLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztZQUV4QixNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtnQkFDbkIsT0FBTyxFQUFFLENBQUMsQ0FBQyxpQ0FBaUM7WUFDOUMsQ0FBQyxDQUFDO1lBRUYsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDL0IsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU8sS0FBSyxDQUFDLG1CQUFtQixDQUMvQixVQUFrQixFQUNsQixhQUFxQjtRQUVyQix5R0FBeUc7UUFDekcsSUFBSSxDQUFDO1lBQ0gsTUFBTSx3QkFBd0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM1QyxDQUFDO1FBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztZQUNaLE9BQU8sQ0FBQyxLQUFLLENBQ1gsc0ZBQXNGLEVBQ3RGLGFBQWEsRUFDYixFQUFFLENBQ0gsQ0FBQztRQUNKLENBQUM7UUFDRCxJQUFJLFNBQVMsR0FBYyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFFOUM7Ozs7Ozs7V0FPRztRQUNILElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNmLE1BQU0sa0JBQWtCLEdBQVcsU0FBUyxDQUFDLHlCQUF5QixDQUFDLENBQUM7WUFDeEUsT0FBTyxDQUFDLElBQUksQ0FDVixnR0FBZ0csRUFDaEcsVUFBVSxFQUNWLGFBQWEsRUFDYixrQkFBa0IsQ0FDbkIsQ0FBQztZQUNGLFNBQVMsR0FBRyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUN6QyxDQUFDO1FBRUQsMERBQTBEO1FBQzFELElBQUksT0FBZ0IsQ0FBQztRQUNyQixJQUFJLENBQUM7WUFDSCxNQUFNLFNBQVMsQ0FBQyxJQUFJLENBQUMsd0JBQXdCLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDdkQsT0FBTyxHQUFHLENBQUMsTUFBTSxTQUFTLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNuRCxDQUFDO1FBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztZQUNaLE9BQU8sQ0FBQyxLQUFLLENBQUMsZ0NBQWdDLEVBQUUsYUFBYSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3JFLENBQUM7UUFDRCxPQUFPLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxPQUFPLEVBQUUsQ0FBQztJQUMxQyxDQUFDOytHQTdWVSxxQkFBcUI7bUhBQXJCLHFCQUFxQixjQUZwQixNQUFNOzs0RkFFUCxxQkFBcUI7a0JBSGpDLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgSW5qZWN0YWJsZSxcbiAgTmdNb2R1bGVSZWYsXG4gIEVudmlyb25tZW50SW5qZWN0b3IsXG4gIFR5cGUsXG4gIGNyZWF0ZU5nTW9kdWxlLFxuICBJbmplY3RvcixcbiAgUHJvdmlkZXJcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnMgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQgeyByZWR1Y2UsIGZvckVhY2gsIGdldCwgdW5pb24sIGNhbWVsQ2FzZSB9IGZyb20gJ2xvZGFzaC1lcyc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUsIFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHNoYXJlUmVwbGF5IH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgU3RhbmRhbG9uZVBsdWdpbkluamVjdG9yIH0gZnJvbSAnLi4vY29tbW9uJztcbmltcG9ydCB7IGNsb25lRGVlcCB9IGZyb20gJ2xvZGFzaC1lcyc7XG5cbnR5cGUgU2NvcGUgPSB1bmtub3duO1xudHlwZSBGYWN0b3J5ID0gKCkgPT4gYW55O1xuXG5kZWNsYXJlIGNvbnN0IF9fd2VicGFja19pbml0X3NoYXJpbmdfXzogKHNoYXJlU2NvcGU6IHN0cmluZykgPT4gUHJvbWlzZTx2b2lkPjtcbmRlY2xhcmUgY29uc3QgX193ZWJwYWNrX3NoYXJlX3Njb3Blc19fOiB7IGRlZmF1bHQ6IFNjb3BlIH07XG5kZWNsYXJlIGNvbnN0IF9fT1JJR0lOQUxfQ09OVEVYVF9QQVRIX186IHN0cmluZztcblxuaW50ZXJmYWNlIENvbnRhaW5lciB7XG4gIGluaXQoc2hhcmVTY29wZTogU2NvcGUpOiB2b2lkO1xuICBnZXQobW9kdWxlOiBzdHJpbmcpOiBGYWN0b3J5O1xufVxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgUGx1Z2luc1Jlc29sdmVTZXJ2aWNlIHtcbiAgLyoqXG4gICAqIFRha2VzIGEgbGlzdCBvZiByZW1vdGVzIGFuZCB0dXJucyBpdCBpbnRvIGFuIG9iamVjdCBjb250YWluaW5nIHVuaW9uIG9mIGNvcnJlc3BvbmRpbmcgcmVtb3Rlcy5cbiAgICogQHBhcmFtIHJlbW90ZXMgTGlzdCBvZiB0aGUgcmVtb3Rlcy5cbiAgICogQHJldHVybnMgUmV0dXJucyBvYmplY3Qgd2l0aCBtZXJnZWQgcmVtb3Rlcy5cbiAgICpcbiAgICogKipFeGFtcGxlKipcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCByZW1vdGVzQTpBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnMgPSB7IGNvbnRleHRQYXRoQTogWydtb2R1bGVBJywgJ21vZHVsZUInXSB9O1xuICAgKiBjb25zdCByZW1vdGVzQjpBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnMgPSB7IGNvbnRleHRQYXRoQTogWydtb2R1bGVBJ10sIGNvbnRleHRQYXRoQjogWydtb2R1bGVaJ10gfTtcbiAgICogY29uc3QgbWVyZ2VkUmVtb3RlczpBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnMgPSBtZXJnZVJlbW90ZXMoW3JlbW90ZXNBLCByZW1vdGVzQl0pO1xuICAgKiAvLyBSZXN1bHRcbiAgICoge1xuICAgKiAgY29udGV4dFBhdGhBOiBbJ21vZHVsZUEnLCAnbW9kdWxlQiddLFxuICAgKiAgY29udGV4dFBhdGhCOiBbJ21vZHVsZVonXVxuICAgKiB9XG4gICAqXG4gICAqIGBgYFxuICAgKi9cbiAgc3RhdGljIG1lcmdlUmVtb3RlcyhyZW1vdGVzOiBBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnNbXSk6IEFwcGxpY2F0aW9uUmVtb3RlUGx1Z2lucyB7XG4gICAgcmV0dXJuIHJlZHVjZShcbiAgICAgIHJlbW90ZXMsXG4gICAgICAoYWxsUmVtb3RlczogQXBwbGljYXRpb25SZW1vdGVQbHVnaW5zLCBtZlJlbW90ZTogQXBwbGljYXRpb25SZW1vdGVQbHVnaW5zKSA9PiB7XG4gICAgICAgIGZvckVhY2gobWZSZW1vdGUsIChyZW1vdGVNb2R1bGVzOiBzdHJpbmdbXSwgcmVtb3RlQ29udGV4dFBhdGg6IHN0cmluZykgPT4ge1xuICAgICAgICAgIGNvbnN0IGN1cnJlbnRSZW1vdGVzID0gZ2V0KGFsbFJlbW90ZXMsIHJlbW90ZUNvbnRleHRQYXRoLCBbXSk7XG4gICAgICAgICAgYWxsUmVtb3Rlc1tyZW1vdGVDb250ZXh0UGF0aF0gPSB1bmlvbihjdXJyZW50UmVtb3RlcywgcmVtb3RlTW9kdWxlcyk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBhbGxSZW1vdGVzO1xuICAgICAgfSxcbiAgICAgIHt9XG4gICAgKTtcbiAgfVxuXG4gIHN0YXRpYyByZW1vdmVSZW1vdGVzKFxuICAgIHJlbW90ZXNUb1JlbW92ZUZyb206IEFwcGxpY2F0aW9uUmVtb3RlUGx1Z2lucyxcbiAgICByZW1vdGVzVG9SZW1vdmU/OiBBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnNcbiAgKTogQXBwbGljYXRpb25SZW1vdGVQbHVnaW5zIHtcbiAgICBjb25zdCBrZXlzVG9SZW1vdmUgPSBPYmplY3Qua2V5cyhyZW1vdGVzVG9SZW1vdmUgfHwge30pO1xuICAgIGlmICgha2V5c1RvUmVtb3ZlLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHJlbW90ZXNUb1JlbW92ZUZyb207XG4gICAgfVxuICAgIGNvbnN0IGN1cnJlbnRLZXlzID0gT2JqZWN0LmtleXMocmVtb3Rlc1RvUmVtb3ZlRnJvbSk7XG4gICAgY29uc3Qga2V5c1ByZXNlbnRJbkJvdGggPSBjdXJyZW50S2V5cy5maWx0ZXIoa2V5ID0+IGtleXNUb1JlbW92ZS5pbmNsdWRlcyhrZXkpKTtcbiAgICBpZiAoIWtleXNQcmVzZW50SW5Cb3RoLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHJlbW90ZXNUb1JlbW92ZUZyb207XG4gICAgfVxuICAgIHJlbW90ZXNUb1JlbW92ZUZyb20gPSBjbG9uZURlZXAocmVtb3Rlc1RvUmVtb3ZlRnJvbSk7XG4gICAgZm9yIChjb25zdCByZW1vdGVDb250ZXh0UGF0aCBvZiBrZXlzUHJlc2VudEluQm90aCkge1xuICAgICAgY29uc3QgcmVtb3RlTW9kdWxlc1RvQmVSZW1vdmVkID0gcmVtb3Rlc1RvUmVtb3ZlW3JlbW90ZUNvbnRleHRQYXRoXTtcbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShyZW1vdGVNb2R1bGVzVG9CZVJlbW92ZWQpIHx8ICFyZW1vdGVNb2R1bGVzVG9CZVJlbW92ZWQ/Lmxlbmd0aCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgbGV0IGN1cnJlbnRNb2R1bGVzID0gcmVtb3Rlc1RvUmVtb3ZlRnJvbVtyZW1vdGVDb250ZXh0UGF0aF07XG4gICAgICBpZiAoIUFycmF5LmlzQXJyYXkoY3VycmVudE1vZHVsZXMpIHx8ICFjdXJyZW50TW9kdWxlcz8ubGVuZ3RoKSB7XG4gICAgICAgIGRlbGV0ZSByZW1vdGVzVG9SZW1vdmVGcm9tW3JlbW90ZUNvbnRleHRQYXRoXTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGN1cnJlbnRNb2R1bGVzID0gY3VycmVudE1vZHVsZXMuZmlsdGVyKG1vZHVsZSA9PiAhcmVtb3RlTW9kdWxlc1RvQmVSZW1vdmVkLmluY2x1ZGVzKG1vZHVsZSkpO1xuICAgICAgaWYgKGN1cnJlbnRNb2R1bGVzLmxlbmd0aCkge1xuICAgICAgICByZW1vdGVzVG9SZW1vdmVGcm9tW3JlbW90ZUNvbnRleHRQYXRoXSA9IGN1cnJlbnRNb2R1bGVzO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZGVsZXRlIHJlbW90ZXNUb1JlbW92ZUZyb21bcmVtb3RlQ29udGV4dFBhdGhdO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiByZW1vdGVzVG9SZW1vdmVGcm9tO1xuICB9XG5cbiAgLyoqXG4gICAqIEVtaXRzIGFsbCBpbmplY3RvcnMgb2YgYWxyZWFkeSBsb2FkZWQgcGx1Z2lucyBvbiBzdWJzY3JpcHRpb24uXG4gICAqL1xuICBpbmplY3RvcnMkOiBPYnNlcnZhYmxlPEVudmlyb25tZW50SW5qZWN0b3IgfCBJbmplY3Rvcj47XG4gIC8qKlxuICAgKiBFbWl0cyBvbmNlIHJlbW90ZVBsdWdpbnMgaGF2ZSBiZWVuIHJlc29sdmVkLlxuICAgKi9cbiAgcmVmcmVzaCQ6IE9ic2VydmFibGU8dm9pZD47XG4gIC8qKlxuICAgKiBFbWl0cyBhbGwgcGx1Z2luIGRldGFpbHMgb2YgYWxyZWFkeSBsb2FkZWQgcGx1Z2lucyBvbiBzdWJzY3JpcHRpb24uXG4gICAqL1xuICBwbHVnaW5EZXRhaWxzJDogT2JzZXJ2YWJsZTx7XG4gICAgcmVtb3RlTmdNb2R1bGU6IE5nTW9kdWxlUmVmPHVua25vd24+IHwgVHlwZTx1bmtub3duPjtcbiAgICBtb2R1bGVSZWY6IE5nTW9kdWxlUmVmPHVua25vd24+O1xuICB9PjtcbiAgYWxsUGx1Z2luc0xvYWRlZCQ6IE9ic2VydmFibGU8Ym9vbGVhbj47XG4gIGxvYWRlZFBsdWdpbk5hbWVzJDogT2JzZXJ2YWJsZTxzdHJpbmdbXT47XG4gIC8qKlxuICAgKiBFbWl0cyBhbGwgY29udGV4dFBhdGhzIChpbmNsdWRpbmcgdGhlIGNvcnJlc3BvbmRpbmcgdmVyc2lvbi90YWcsIGlmIHByb3ZpZGVkKSB0aGF0IGhhdmUgYmVlbiBhbHJlYWR5IGxvYWRlZCBvbiBzdGFydHVwIGFuZCBmdXJ0aGVyIGFueSBuZXdsb2FkIGxvYWRlZC5cbiAgICovXG4gIGNvbnRleHRQYXRoc0Zyb21XaGVyZVJlbW90ZXNIYXZlQmVlbkxvYWRlZCQ6IE9ic2VydmFibGU8c3RyaW5nW10+O1xuICBwcml2YXRlIHVybFJlbW90ZXNDYWNoZTogQXBwbGljYXRpb25SZW1vdGVQbHVnaW5zID0gbnVsbDtcbiAgcHJpdmF0ZSByZW1vdGVTY3JpcHRTZXQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgcHJpdmF0ZSBfaW5qZWN0b3JzJCA9IG5ldyBTdWJqZWN0PEVudmlyb25tZW50SW5qZWN0b3IgfCBJbmplY3Rvcj4oKTtcbiAgcHJpdmF0ZSBfcmVmcmVzaCQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICBwcml2YXRlIF9wbHVnaW5EZXRhaWxzJCA9IG5ldyBTdWJqZWN0PHtcbiAgICByZW1vdGVOZ01vZHVsZTogTmdNb2R1bGVSZWY8dW5rbm93bj4gfCBUeXBlPHVua25vd24+O1xuICAgIG1vZHVsZVJlZjogTmdNb2R1bGVSZWY8dW5rbm93bj47XG4gIH0+KCk7XG4gIHByaXZhdGUgX2FsbFBsdWdpbnNMb2FkZWQkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gIHByaXZhdGUgX2NvbnRleHRQYXRoc0Zyb21XaGVyZVJlbW90ZXNIYXZlQmVlbkxvYWRlZCQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PHN0cmluZ1tdPihbXSk7XG4gIHByaXZhdGUgX2xvYWRlZFBsdWdpbk5hbWVzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8c3RyaW5nW10+KFtdKTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGluamVjdG9yOiBFbnZpcm9ubWVudEluamVjdG9yKSB7XG4gICAgdGhpcy5pbmplY3RvcnMkID0gdGhpcy5faW5qZWN0b3JzJC5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgLy8gbm90IHNwZWNpZnlpbmcgdGhlIGJ1ZmZlclNpemUgb2Ygc2hhcmVSZXBsYXkgc28gYWxsIGluamVjdG9ycyBhcmUgcmVjZWl2ZWQgb24gc3Vic2NyaXB0aW9uXG4gICAgICBzaGFyZVJlcGxheSgpXG4gICAgKTtcbiAgICB0aGlzLnJlZnJlc2gkID0gdGhpcy5fcmVmcmVzaCQuYXNPYnNlcnZhYmxlKCkucGlwZShzaGFyZVJlcGxheSgxKSk7XG4gICAgdGhpcy5wbHVnaW5EZXRhaWxzJCA9IHRoaXMuX3BsdWdpbkRldGFpbHMkLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICAvLyBub3Qgc3BlY2lmeWluZyB0aGUgYnVmZmVyU2l6ZSBvZiBzaGFyZVJlcGxheSBzbyBhbGwgZGV0YWlscyBhcmUgcmVjZWl2ZWQgb24gc3Vic2NyaXB0aW9uXG4gICAgICBzaGFyZVJlcGxheSgpXG4gICAgKTtcbiAgICB0aGlzLmFsbFBsdWdpbnNMb2FkZWQkID0gdGhpcy5fYWxsUGx1Z2luc0xvYWRlZCQuYXNPYnNlcnZhYmxlKCk7XG4gICAgdGhpcy5jb250ZXh0UGF0aHNGcm9tV2hlcmVSZW1vdGVzSGF2ZUJlZW5Mb2FkZWQkID1cbiAgICAgIHRoaXMuX2NvbnRleHRQYXRoc0Zyb21XaGVyZVJlbW90ZXNIYXZlQmVlbkxvYWRlZCQuYXNPYnNlcnZhYmxlKCk7XG5cbiAgICB0aGlzLmxvYWRlZFBsdWdpbk5hbWVzJCA9IHRoaXMuX2xvYWRlZFBsdWdpbk5hbWVzJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMb2FkcyBwbHVnaW5zIGJ5IHJlc29sdmluZyB0aGUgcmVtb3RlIE5nTW9kdWxlcyBhbmQgaW5qZWN0aW5nIGl0LiBBbHNvIGF0dGFjaGluZ1xuICAgKiB0aGUgaG9va3Mgb250byB0aGUgcm9vdCBpbmplY3Rvci5cbiAgICogQHBhcmFtIHJlbW90ZU1vZHVsZXMgVGhlIHJlbW90ZSBwbHVnaW5zIHRvIGxvYWQgYXMgZmFjdG9yeSBuYW1lIG1hcHBpbmcgYXJyYXkuXG4gICAqL1xuICByZXNvbHZlUmVtb3RlUGx1Z2lucyhyZW1vdGVNb2R1bGVzOiBBcnJheTx7IGZhY3Rvcnk7IG5hbWU6IHN0cmluZyB9Pikge1xuICAgIHRoaXMubG9hZE1vZHVsZXNEeW5hbWljYWxseShyZW1vdGVNb2R1bGVzKTtcbiAgICB0aGlzLnJlZnJlc2hIb29rcygpO1xuICAgIHRoaXMubWFya1BsdWdpbnNBc0xvYWRlZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIExvYWRzIG1vZHVsZXMgYW5kIGhhbmRsZXMgaG9va2luZyBjb3JyZWN0bHkuXG4gICAqIEBwYXJhbSByZW1vdGVOZ01vZHVsZXMgVGhlIG1vZHVsZXMgdG8gbG9hZC5cbiAgICovXG4gIGxvYWRNb2R1bGVzRHluYW1pY2FsbHkocmVtb3RlTW9kdWxlczogQXJyYXk8eyBmYWN0b3J5OyBuYW1lOiBzdHJpbmcgfT4pIHtcbiAgICBmb3IgKGNvbnN0IHsgZmFjdG9yeSwgbmFtZSB9IG9mIHJlbW90ZU1vZHVsZXMpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IG1vZHVsZU9yUHJvdmlkZXJzID0gZmFjdG9yeVtuYW1lXTtcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkobW9kdWxlT3JQcm92aWRlcnMpKSB7XG4gICAgICAgICAgdGhpcy5sb2FkUHJvdmlkZXJzKG1vZHVsZU9yUHJvdmlkZXJzLCBuYW1lKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLmxvYWRNb2R1bGUobW9kdWxlT3JQcm92aWRlcnMpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IG5ld0xvYWRlZFBsdWdpbk5hbWVzID0gWy4uLnRoaXMuX2xvYWRlZFBsdWdpbk5hbWVzJC52YWx1ZSwgbmFtZV07XG4gICAgICAgIHRoaXMuX2xvYWRlZFBsdWdpbk5hbWVzJC5uZXh0KG5ld0xvYWRlZFBsdWdpbk5hbWVzKTtcbiAgICAgIH0gY2F0Y2ggKGV4KSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxlZCB0byBsb2FkICR7bmFtZX1gLCBleCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgbG9hZFByb3ZpZGVycyhwcm92aWRlcnM6IFByb3ZpZGVyW10sIG5hbWU6IHN0cmluZykge1xuICAgIGNvbnN0IGluamVjdG9yID0gbmV3IFN0YW5kYWxvbmVQbHVnaW5JbmplY3Rvcih7XG4gICAgICBwcm92aWRlcnM6IHByb3ZpZGVycyxcbiAgICAgIG5hbWU6IGBwbHVnaW5zSW5qZWN0b3ItJHtuYW1lfWAsXG4gICAgICBwYXJlbnQ6IHRoaXMuaW5qZWN0b3JcbiAgICB9KTtcbiAgICB0aGlzLl9pbmplY3RvcnMkLm5leHQoaW5qZWN0b3IpO1xuICB9XG5cbiAgbG9hZE1vZHVsZTxUID0gdW5rbm93bj4ocmVtb3RlTmdNb2R1bGU6IE5nTW9kdWxlUmVmPFQ+IHwgVHlwZTxUPikge1xuICAgIGxldCBtb2R1bGVSZWY6IE5nTW9kdWxlUmVmPFQ+O1xuICAgIGlmIChyZW1vdGVOZ01vZHVsZSBpbnN0YW5jZW9mIE5nTW9kdWxlUmVmKSB7XG4gICAgICAvLyBBT1RcbiAgICAgIG1vZHVsZVJlZiA9IHJlbW90ZU5nTW9kdWxlO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBKSVRcbiAgICAgIG1vZHVsZVJlZiA9IGNyZWF0ZU5nTW9kdWxlKHJlbW90ZU5nTW9kdWxlLCB0aGlzLmluamVjdG9yKTtcbiAgICB9XG4gICAgdGhpcy5faW5qZWN0b3JzJC5uZXh0KG1vZHVsZVJlZi5pbmplY3Rvcik7XG4gICAgdGhpcy5fcGx1Z2luRGV0YWlscyQubmV4dCh7IG1vZHVsZVJlZiwgcmVtb3RlTmdNb2R1bGUgfSk7XG4gICAgcmV0dXJuIG1vZHVsZVJlZjtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaWxsIHJlZnJlc2ggYWxsIGN1cnJlbnQgcmVnaXN0ZXJlZCBob29rcy5cbiAgICovXG4gIHJlZnJlc2hIb29rcygpIHtcbiAgICB0aGlzLl9yZWZyZXNoJC5uZXh0KCk7XG4gIH1cblxuICBtYXJrUGx1Z2luc0FzTG9hZGVkKCkge1xuICAgIHRoaXMuX2FsbFBsdWdpbnNMb2FkZWQkLm5leHQodHJ1ZSk7XG4gIH1cblxuICAvKipcbiAgICogTG9hZHMgYSBsaXN0IG9mIHJlbW90ZXMgc28gdGhhdCBhIHBhcnRpY3VsYXIgYXBwbGljYXRpb24gY2FuIHVzZSB0aGVtLlxuICAgKiBUaGUgcmVxdWVzdCBpcyBtYWRlIHRvIHRoZSBmb2xsb3dpbmcgYWRkcmVzczogL2FwcHMvPGNvbnRleHRQYXRoPi9yZW1vdGVFbnRyeS5qc1xuICAgKiBAcGFyYW0gcmVtb3RlcyBMaXN0IG9mIHJlbW90ZXMgdG8gYmUgbG9hZGVkLlxuICAgKiBAcmV0dXJucyBSZXR1cm5zIHRoZSBsaXN0IG9mIGxvYWRlZCBtb2R1bGVzIGZyb20gcmVtb3Rlcy5cbiAgICovXG4gIGFzeW5jIGxvYWRSZW1vdGVzKHJlbW90ZXM6IEFwcGxpY2F0aW9uUmVtb3RlUGx1Z2lucykge1xuICAgIGlmICghcmVtb3Rlcykge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoKTtcbiAgICBjb25zdCByZW1vdGVNb2R1bGVzID0gW107XG4gICAgZm9yIChjb25zdCBwbHVnaW5JZCBpbiByZW1vdGVzKSB7XG4gICAgICBpZiAocmVtb3Rlcy5oYXNPd25Qcm9wZXJ0eShwbHVnaW5JZCkpIHtcbiAgICAgICAgY29uc3QgbW9kdWxlTmFtZXMgPSByZW1vdGVzW3BsdWdpbklkXTtcbiAgICAgICAgY29uc3QgdXJsID0gYC9hcHBzLyR7cGx1Z2luSWR9L3JlbW90ZUVudHJ5LmpzP25vY2FjaGU9JHtkYXRlLmdldFRpbWUoKX1gO1xuICAgICAgICBsZXQgYXRMZWFzdE9uZU1vZHVsZUxvYWRlZFN1Y2Nlc3NmdWxseSA9IGZhbHNlO1xuICAgICAgICBmb3IgKGNvbnN0IG1vZHVsZU5hbWUgb2YgbW9kdWxlTmFtZXMpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgcmVtb3RlTW9kdWxlcy5wdXNoKGF3YWl0IHRoaXMubG9hZFJlbW90ZU1vZHVsZSh1cmwsIHBsdWdpbklkLCBtb2R1bGVOYW1lKSk7XG4gICAgICAgICAgICBhdExlYXN0T25lTW9kdWxlTG9hZGVkU3VjY2Vzc2Z1bGx5ID0gdHJ1ZTtcbiAgICAgICAgICB9IGNhdGNoIChleCkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGBDb3VsZCBub3QgbG9hZCByZW1vdGUgbW9kdWxlICclcycgZnJvbSB1cmw6YCwgbW9kdWxlTmFtZSwgdXJsKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBPbmx5IGFkZCBzdWNjZXNzZnVsbHkgbG9hZGVkIHJlbW90ZXMgdG8gdGhlIGxpc3RcbiAgICAgICAgaWYgKGF0TGVhc3RPbmVNb2R1bGVMb2FkZWRTdWNjZXNzZnVsbHkpIHtcbiAgICAgICAgICB0aGlzLl9jb250ZXh0UGF0aHNGcm9tV2hlcmVSZW1vdGVzSGF2ZUJlZW5Mb2FkZWQkLm5leHQoXG4gICAgICAgICAgICB0aGlzLl9jb250ZXh0UGF0aHNGcm9tV2hlcmVSZW1vdGVzSGF2ZUJlZW5Mb2FkZWQkLnZhbHVlLmNvbmNhdChwbHVnaW5JZClcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZW1vdGVNb2R1bGVzO1xuICB9XG5cbiAgLyoqXG4gICAqIFRha2VzIGEgbGlzdCBvZiByZW1vdGVzIGFuZCB0dXJucyBpdCBpbnRvIGFuIG9iamVjdCBjb250YWluaW5nIHVuaW9uIG9mIGNvcnJlc3BvbmRpbmcgcmVtb3Rlcy5cbiAgICogQHBhcmFtIG1mUmVtb3RlcyBMaXN0IG9mIHRoZSByZW1vdGVzLlxuICAgKiBAcmV0dXJucyBSZXR1cm5zIG9iamVjdCB3aXRoIG1lcmdlZCByZW1vdGVzLlxuICAgKiBAZGVwcmVjYXRlZCBVc2UgdGhlIHN0YXRpYyBmdW5jdGlvbiBtZXJnZVJlbW90ZXMgYXMgdGhpcyBpcyBhIHB1cmUgZnVuY3Rpb24uXG4gICAqXG4gICAqICoqRXhhbXBsZSoqXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgcmVtb3Rlc0E6QXBwbGljYXRpb25SZW1vdGVQbHVnaW5zID0geyBjb250ZXh0UGF0aEE6IFsnbW9kdWxlQScsICdtb2R1bGVCJ10gfTtcbiAgICogY29uc3QgcmVtb3Rlc0I6QXBwbGljYXRpb25SZW1vdGVQbHVnaW5zID0geyBjb250ZXh0UGF0aEE6IFsnbW9kdWxlQSddLCBjb250ZXh0UGF0aEI6IFsnbW9kdWxlWiddIH07XG4gICAqIGNvbnN0IG1lcmdlZFJlbW90ZXM6QXBwbGljYXRpb25SZW1vdGVQbHVnaW5zID0gbWVyZ2VNRlJlbW90ZXMoW3JlbW90ZXNBLCByZW1vdGVzQl0pO1xuICAgKiAvLyBSZXN1bHRcbiAgICoge1xuICAgKiAgY29udGV4dFBhdGhBOiBbJ21vZHVsZUEnLCAnbW9kdWxlQiddLFxuICAgKiAgY29udGV4dFBhdGhCOiBbJ21vZHVsZVonXVxuICAgKiB9XG4gICAqXG4gICAqIGBgYFxuICAgKi9cbiAgbWVyZ2VNRlJlbW90ZXMobWZSZW1vdGVzOiBBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnNbXSk6IEFwcGxpY2F0aW9uUmVtb3RlUGx1Z2lucyB7XG4gICAgcmV0dXJuIFBsdWdpbnNSZXNvbHZlU2VydmljZS5tZXJnZVJlbW90ZXMobWZSZW1vdGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhcnMgVVJMIHJlbW90ZXMgY2FjaGUuXG4gICAqL1xuICBjbGVhclVSTFJlbW90ZXNDYWNoZSgpIHtcbiAgICB0aGlzLnVybFJlbW90ZXNDYWNoZSA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmVzIHRoZSByZW1vdGVzIGxpc3QgZnJvbSB0aGUgVVJMLlxuICAgKiBAcmV0dXJucyBSZXR1cm5zIHRoZSBsaXN0IG9mIHJlbW90ZXMuXG4gICAqL1xuICBsb2FkVXJsUmVtb3RlcygpIHtcbiAgICBpZiAoIXRoaXMudXJsUmVtb3Rlc0NhY2hlKSB7XG4gICAgICBjb25zdCBwYXJhbXMgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHdpbmRvdy5sb2NhdGlvbi5zZWFyY2gpO1xuICAgICAgY29uc3QgcmVtb3RlcyA9IHBhcmFtcy5nZXQoJ3JlbW90ZXMnKTtcbiAgICAgIGlmIChyZW1vdGVzKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdGhpcy51cmxSZW1vdGVzQ2FjaGUgPSBKU09OLnBhcnNlKGRlY29kZVVSSUNvbXBvbmVudChyZW1vdGVzKSk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKGBGYWlsZWQgdG8gcGFyc2UgcmVtb3RlczogJHtlcnJvcn1gKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGhpcy51cmxSZW1vdGVzQ2FjaGU7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGxvYWRSZW1vdGVNb2R1bGUoXG4gICAgcmVtb3RlRW50cnlVcmw6IHN0cmluZyxcbiAgICByZW1vdGVDb250ZXh0UGF0aDogc3RyaW5nLFxuICAgIGV4cG9zZWRNb2R1bGU6IHN0cmluZ1xuICApOiBQcm9taXNlPHsgbmFtZTogc3RyaW5nOyBmYWN0b3J5OiBGYWN0b3J5IH0+IHtcbiAgICBpZiAoIXRoaXMucmVtb3RlU2NyaXB0U2V0LmhhcyhyZW1vdGVFbnRyeVVybCkpIHtcbiAgICAgIHRoaXMucmVtb3RlU2NyaXB0U2V0LmFkZChyZW1vdGVFbnRyeVVybCk7XG4gICAgICBhd2FpdCB0aGlzLmxvYWRSZW1vdGVFbnRyeShyZW1vdGVFbnRyeVVybCk7XG4gICAgfVxuICAgIGxldCBjb250ZXh0UGF0aDogc3RyaW5nID0gcmVtb3RlQ29udGV4dFBhdGg7XG4gICAgaWYgKGNvbnRleHRQYXRoLmluY2x1ZGVzKCdAJykpIHtcbiAgICAgIGNvbnRleHRQYXRoID0gcmVtb3RlQ29udGV4dFBhdGguc3BsaXQoJ0AnKVswXTtcbiAgICB9XG4gICAgcmV0dXJuIGF3YWl0IHRoaXMubG9va3VwRXhwb3NlZE1vZHVsZShjYW1lbENhc2UoY29udGV4dFBhdGgpLCBleHBvc2VkTW9kdWxlKTtcbiAgfVxuXG4gIHByaXZhdGUgbG9hZFJlbW90ZUVudHJ5KHJlbW90ZUVudHJ5VXJsOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3Qgc2NyaXB0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2NyaXB0Jyk7XG4gICAgICBzY3JpcHQuc3JjID0gcmVtb3RlRW50cnlVcmw7XG5cbiAgICAgIHNjcmlwdC5vbmVycm9yID0gcmVqZWN0O1xuXG4gICAgICBzY3JpcHQub25sb2FkID0gKCkgPT4ge1xuICAgICAgICByZXNvbHZlKCk7IC8vIHdpbmRvdyBpcyB0aGUgZ2xvYmFsIG5hbWVzcGFjZVxuICAgICAgfTtcblxuICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmQoc2NyaXB0KTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9va3VwRXhwb3NlZE1vZHVsZShcbiAgICByZW1vdGVOYW1lOiBzdHJpbmcsXG4gICAgZXhwb3NlZE1vZHVsZTogc3RyaW5nXG4gICk6IFByb21pc2U8eyBuYW1lOiBzdHJpbmc7IGZhY3Rvcnk6IEZhY3RvcnkgfT4ge1xuICAgIC8vIEluaXRpYWxpemVzIHRoZSBzaGFyZSBzY29wZS4gVGhpcyBmaWxscyBpdCB3aXRoIGtub3duIHByb3ZpZGVkIG1vZHVsZXMgZnJvbSB0aGlzIGJ1aWxkIGFuZCBhbGwgcmVtb3Rlc1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBfX3dlYnBhY2tfaW5pdF9zaGFyaW5nX18oJ2RlZmF1bHQnKTtcbiAgICB9IGNhdGNoIChleCkge1xuICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgYE1vZHVsZSAlcyBjb3VsZCBub3QgYmUgbG9hZGVkLiBNb2R1bGUgRmVkZXJhdGlvbiBpcyBub3QgZW5hYmxlZCBpbiB0aGlzIGFwcGxpY2F0aW9uLmAsXG4gICAgICAgIGV4cG9zZWRNb2R1bGUsXG4gICAgICAgIGV4XG4gICAgICApO1xuICAgIH1cbiAgICBsZXQgY29udGFpbmVyOiBDb250YWluZXIgPSB3aW5kb3dbcmVtb3RlTmFtZV07XG5cbiAgICAvKipcbiAgICAgKiBNVE0tNjA4NTA6IEluIGNhc2Ugb2YgZS5nLiB0aGUgY29ja3BpdCBhcHAgYmVpbmcgY2xvbmVkIHRvIGEgZGlmZmVyZW50IGNvbnRleHQgcGF0aFxuICAgICAqIHRoZSBzZWxmIHNjb3BlZCBwbHVnaW5zIG9mIGNvY2twaXQgd2lsbCBiZSBsb2FkZWQgZnJvbSB0aGUgbmV3IGNvbnRleHQgcGF0aC5cbiAgICAgKiBCdXQgdGhlIHJlbW90ZUVudHJ5LmpzIHdpbGwgc3RpbGwgcmVnaXN0ZXIgdGhlIHJlbW90ZXMgd2l0aCB0aGUgb3JpZ2luYWwgY29udGV4dCBwYXRoLFxuICAgICAqIGFzIHRoaXMgaXMgaGFyZGNvZGVkIGR1cmluZyBjb21waWxlIHRpbWUuXG4gICAgICpcbiAgICAgKiBXZSB0aGVyZWZvcmUgYWRkIGEgZmFsbGJhY2sgdG8gdGhlIG9yaWdpbmFsIGNvbnRleHQgcGF0aCBpbiBjYXNlIHRoZXJlIGlzIG5vIGNvbnRhaW5lciBmb3IgdGhlIHJlbW90ZU5hbWVcbiAgICAgKi9cbiAgICBpZiAoIWNvbnRhaW5lcikge1xuICAgICAgY29uc3QgZmFsbGJhY2tSZW1vdGVOYW1lOiBzdHJpbmcgPSBjYW1lbENhc2UoX19PUklHSU5BTF9DT05URVhUX1BBVEhfXyk7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgIGBBdHRyaWJ1dGUgXCIlc1wiIG5vdCBkZWZpbmVkIG9uIHdpbmRvdyBvYmplY3Qgd2hpbGUgdHJ5aW5nIHRvIGxvYWQgXCIlc1wiLiBVc2luZyBcIiVzXCIgYXMgZmFsbGJhY2suYCxcbiAgICAgICAgcmVtb3RlTmFtZSxcbiAgICAgICAgZXhwb3NlZE1vZHVsZSxcbiAgICAgICAgZmFsbGJhY2tSZW1vdGVOYW1lXG4gICAgICApO1xuICAgICAgY29udGFpbmVyID0gd2luZG93W2ZhbGxiYWNrUmVtb3RlTmFtZV07XG4gICAgfVxuXG4gICAgLy8gSW5pdGlhbGl6ZSB0aGUgY29udGFpbmVyLCBpdCBtYXkgcHJvdmlkZSBzaGFyZWQgbW9kdWxlc1xuICAgIGxldCBmYWN0b3J5OiBGYWN0b3J5O1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBjb250YWluZXIuaW5pdChfX3dlYnBhY2tfc2hhcmVfc2NvcGVzX18uZGVmYXVsdCk7XG4gICAgICBmYWN0b3J5ID0gKGF3YWl0IGNvbnRhaW5lci5nZXQoZXhwb3NlZE1vZHVsZSkpKCk7XG4gICAgfSBjYXRjaCAoZXgpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYE1vZHVsZSAlcyBjb3VsZCBub3QgYmUgbG9hZGVkLmAsIGV4cG9zZWRNb2R1bGUsIGV4KTtcbiAgICB9XG4gICAgcmV0dXJuIHsgbmFtZTogZXhwb3NlZE1vZHVsZSwgZmFjdG9yeSB9O1xuICB9XG59XG4iXX0=