ngx-pwa
Version:
Provides functionality around the progressive web app functionality in angular. Most notably the an approach to cache POST, UPDATE and DELETE requests.
260 lines • 37.9 kB
JavaScript
import { __decorate, __param } from "tslib";
import { isPlatformBrowser } from '@angular/common';
import { Inject, InjectionToken, PLATFORM_ID } from '@angular/core';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { LodashUtilities } from '../encapsulation/lodash.utilities';
import { HttpMethod } from '../models/http-method.enum';
// eslint-disable-next-line jsdoc/require-jsdoc, typescript/typedef
export const NGX_PWA_OFFLINE_SERVICE = new InjectionToken('Provider for the OfflineService used eg. in the offline request interceptor.', {
providedIn: 'root',
factory: () => {
// eslint-disable-next-line no-console
console.error(
// eslint-disable-next-line stylistic/max-len
'No OfflineService has been provided for the token NGX_OFFLINE_SERVICE\nAdd this to your app.module.ts provider array:\n{\n provide: NGX_PWA_OFFLINE_SERVICE,\n useExisting: MyOfflineService\n}');
}
});
/**
* The base class for an offline service.
*/
let NgxPwaOfflineService = class NgxPwaOfflineService {
http;
snackBar;
zone;
platformId;
/**
* The key under which any requests are saved in local storage.
*/
CACHED_REQUESTS_KEY = 'requests';
/**
* The prefix of offline generated ids.
* Is used to check if a request still has unresolved dependencies.
*/
OFFLINE_ID_PREFIX = 'offline';
/**
* A snackbar message to display when the synchronization of all cached requests has been finished.
*/
ALL_SYNC_FINISHED_SNACK_BAR_MESSAGE = 'Synchronization finished';
/**
* A snackbar message to display when the synchronization of all cached requests fails.
*/
ALL_SYNC_FAILED_SNACK_BAR_MESSAGE = 'Synchronization failed, please try again later';
/**
* A snackbar message to display when the synchronization of a single cached requests has been finished.
*/
SINGLE_SYNC_FINISHED_SNACK_BAR_MESSAGE = 'Synchronization finished';
/**
* A snackbar message to display when the synchronization of a single cached requests fails.
*/
SINGLE_SYNC_FAILED_SNACK_BAR_MESSAGE = 'Synchronization failed, please try again later';
/**
* Whether or not the user has no internet connection.
*/
isOffline = false;
/**
* A subject of all the requests that have been done while offline.
* Needs to be used for applying offline data or syncing the requests to the api.
*/
cachedRequestsSubject;
// eslint-disable-next-line jsdoc/require-returns
/**
* The currently stored cached requests (if there are any).
*/
get cachedRequests() {
return this.cachedRequestsSubject.value;
}
set cachedRequests(cachedRequests) {
if (!isPlatformBrowser(this.platformId)) {
return;
}
localStorage.setItem(this.CACHED_REQUESTS_KEY, JSON.stringify(cachedRequests));
this.cachedRequestsSubject.next(cachedRequests);
}
constructor(http, snackBar, zone, platformId) {
this.http = http;
this.snackBar = snackBar;
this.zone = zone;
this.platformId = platformId;
if (!isPlatformBrowser(platformId)) {
this.isOffline = false;
this.cachedRequestsSubject = new BehaviorSubject([]);
return;
}
this.isOffline = !navigator.onLine;
window.ononline = () => this.isOffline = !navigator.onLine;
window.onoffline = () => this.isOffline = !navigator.onLine;
const stringData = localStorage.getItem(this.CACHED_REQUESTS_KEY);
const requestsData = stringData ? JSON.parse(stringData) : [];
this.cachedRequestsSubject = new BehaviorSubject(requestsData);
}
/**
* Applies any offline data that has been cached to the given values.
* @param type - The type of the provided entities. Is needed to check if any cached requests of the same type exist.
* @param entities - The already existing data.
* @returns The already existing entities extended/modified by the offline cached requests.
*/
applyOfflineData(type, entities) {
if (!this.cachedRequests.length) {
return entities;
}
const res = Array.from(entities);
const cachedRequests = this.cachedRequests.filter(req => req.metadata.type === type);
for (const req of cachedRequests) {
switch (req.request.method) {
case HttpMethod.POST: {
res.push(req.request.body);
break;
}
case HttpMethod.PATCH: {
const patchIdKey = req.metadata.idKey;
const index = res.findIndex(e => req.request.urlWithParams.includes(`${e[patchIdKey]}`));
res[index] = this.updateOffline(req.request.body, res[index]);
break;
}
case HttpMethod.DELETE: {
const deleteIdKey = req.metadata.idKey;
res.splice(res.findIndex(e => req.request.urlWithParams.includes(`${e[deleteIdKey]}`)), 1);
break;
}
default: {
// eslint-disable-next-line no-console
console.error('There was an unknown http-method in one of your cached offline requests:', req.request.method);
break;
}
}
}
return res;
}
/**
* Applies an UPDATE to an entity without sending a request to the server.
* @param changes - The changes that should be made to the entity.
* @param entity - The entity that should be updated.
* @returns The updated entity.
*/
updateOffline(changes, entity) {
for (const key in changes) {
entity[key] = changes[key];
}
return entity;
}
/**
* Sends a specific cached request to the server.
* @param request - The request that should be synced.
*/
async sync(request) {
const cachedRequestsPriorChanges = LodashUtilities.cloneDeep(this.cachedRequests);
try {
const res = await this.syncSingleRequest(request);
this.zone.run(() => {
this.snackBar.open(this.SINGLE_SYNC_FINISHED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 });
});
this.removeSingleRequest(request);
this.updateOfflineIdsInRequests(request, res);
}
catch {
this.zone.run(() => {
this.snackBar.open(this.SINGLE_SYNC_FAILED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 });
});
this.cachedRequests = cachedRequestsPriorChanges;
}
}
/**
* Sends all cached requests to the server. Tries to handle dependencies of requests on each other.
*/
async syncAll() {
const cachedRequestsPriorChanges = LodashUtilities.cloneDeep(this.cachedRequests);
try {
await this.syncAllRecursive();
this.zone.run(() => {
this.snackBar.open(this.ALL_SYNC_FINISHED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 });
});
this.cachedRequests = [];
}
catch {
this.zone.run(() => {
this.snackBar.open(this.ALL_SYNC_FAILED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 });
});
this.cachedRequests = cachedRequestsPriorChanges;
}
}
/**
* The recursive method used to syn all requests to the api.
*/
async syncAllRecursive() {
// eslint-disable-next-line stylistic/max-len
const request = this.cachedRequests.find(r => !this.hasUnresolvedDependency(r));
if (!request) {
return;
}
const res = await this.syncSingleRequest(request);
this.updateOfflineIdsInRequests(request, res);
await this.syncAllRecursive();
}
/**
* Sends a single cached request to the server.
* @param request - The request that should be synced.
* @returns A promise of the request result.
*/
async syncSingleRequest(request) {
if (this.isOffline || this.hasUnresolvedDependency(request)) {
throw new Error('Could not sync the request');
}
const requestObservable = this.request(request);
if (!requestObservable) {
throw new Error('Could not sync the request');
}
return await firstValueFrom(requestObservable);
}
updateOfflineIdsInRequests(request, res) {
if (this.cachedRequests.length && request.request.body != undefined) {
const idKey = request.metadata.idKey;
if (res[idKey] != undefined) {
const requestsString = `${this.cachedRequests}`.split(request.request.body[idKey]).join(res[idKey]);
this.cachedRequests = JSON.parse(requestsString);
}
}
}
/**
* Calls http.post/patch/delete etc. On the provided request.
* @param request - The request that should be sent.
* @returns The observable of the request or undefined if something went wrong.
*/
request(request) {
switch (request.request.method) {
case HttpMethod.POST: {
return this.http.post(request.request.urlWithParams, LodashUtilities.omit(request.request.body, request.metadata.idKey));
}
case HttpMethod.PATCH: {
return this.http.patch(request.request.urlWithParams, request.request.body);
}
case HttpMethod.DELETE: {
return this.http.delete(request.request.urlWithParams);
}
default: {
return undefined;
}
}
}
/**
* Checks if the given request has an unresolved dependency by looking for the keyword 'offline' inside of it.
* @param request - The request that should be checked.
* @returns Whether or no the given request has an unresolved dependency.
*/
hasUnresolvedDependency(request) {
return request.request.urlWithParams.includes(this.OFFLINE_ID_PREFIX)
|| `${request.request.body}`.includes(this.OFFLINE_ID_PREFIX);
}
/**
* Removes a single request from the cache.
* @param request - The request that should be removed.
*/
removeSingleRequest(request) {
this.cachedRequests.splice(this.cachedRequests.indexOf(request), 1);
this.cachedRequests = this.cachedRequests;
}
};
NgxPwaOfflineService = __decorate([
__param(3, Inject(PLATFORM_ID))
], NgxPwaOfflineService);
export { NgxPwaOfflineService };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2ZmbGluZS5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXB3YS9zcmMvc2VydmljZXMvb2ZmbGluZS5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUVwRCxPQUFPLEVBQUUsTUFBTSxFQUFFLGNBQWMsRUFBVSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFNUUsT0FBTyxFQUFFLGVBQWUsRUFBYyxjQUFjLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFbkUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQVF4RCxtRUFBbUU7QUFDbkUsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsSUFBSSxjQUFjLENBQ3JELDhFQUE4RSxFQUM5RTtJQUNJLFVBQVUsRUFBRSxNQUFNO0lBQ2xCLE9BQU8sRUFBRSxHQUFHLEVBQUU7UUFDZCxzQ0FBc0M7UUFDbEMsT0FBTyxDQUFDLEtBQUs7UUFDVCw2Q0FBNkM7UUFDN0MsdU1BQXVNLENBQzFNLENBQUM7SUFDTixDQUFDO0NBQ0osQ0FDSixDQUFDO0FBaUJGOztHQUVHO0FBQ0ksSUFBTSxvQkFBb0IsR0FBMUIsTUFBTSxvQkFBb0I7SUE0RFI7SUFDQTtJQUNBO0lBRUE7SUEvRHJCOztPQUVHO0lBQ00sbUJBQW1CLEdBQVcsVUFBVSxDQUFDO0lBRWxEOzs7T0FHRztJQUNNLGlCQUFpQixHQUFXLFNBQVMsQ0FBQztJQUUvQzs7T0FFRztJQUNnQixtQ0FBbUMsR0FBVywwQkFBMEIsQ0FBQztJQUU1Rjs7T0FFRztJQUNnQixpQ0FBaUMsR0FBVyxnREFBZ0QsQ0FBQztJQUVoSDs7T0FFRztJQUNnQixzQ0FBc0MsR0FBVywwQkFBMEIsQ0FBQztJQUUvRjs7T0FFRztJ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QUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUM7SUFDOUMsQ0FBQztDQUNKLENBQUE7QUEzUVksb0JBQW9CO0lBK0R4QixXQUFBLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQTtHQS9EZixvQkFBb0IsQ0EyUWhDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgaXNQbGF0Zm9ybUJyb3dzZXIgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgSHR0cENsaWVudCwgSHR0cFJlcXVlc3QgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBJbmplY3QsIEluamVjdGlvblRva2VuLCBOZ1pvbmUsIFBMQVRGT1JNX0lEIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBNYXRTbmFja0JhciB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3NuYWNrLWJhcic7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUsIGZpcnN0VmFsdWVGcm9tIH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IExvZGFzaFV0aWxpdGllcyB9IGZyb20gJy4uL2VuY2Fwc3VsYXRpb24vbG9kYXNoLnV0aWxpdGllcyc7XG5pbXBvcnQgeyBIdHRwTWV0aG9kIH0gZnJvbSAnLi4vbW9kZWxzL2h0dHAtbWV0aG9kLmVudW0nO1xuaW1wb3J0IHsgUmVxdWVzdE1ldGFkYXRhSW50ZXJuYWwgfSBmcm9tICcuLi9tb2RlbHMvcmVxdWVzdC1tZXRhZGF0YS1pbnRlcm5hbC5tb2RlbCc7XG5cbi8qKlxuICogVGhlIEdlbmVyaWMgQmFzZSBFbnRpdHlUeXBlLlxuICovXG5leHBvcnQgdHlwZSBCYXNlRW50aXR5VHlwZTxUPiA9IHsgW0sgaW4ga2V5b2YgVF06IHVua25vd24gfTtcblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGpzZG9jL3JlcXVpcmUtanNkb2MsIHR5cGVzY3JpcHQvdHlwZWRlZlxuZXhwb3J0IGNvbnN0IE5HWF9QV0FfT0ZGTElORV9TRVJWSUNFID0gbmV3IEluamVjdGlvblRva2VuKFxuICAgICdQcm92aWRlciBmb3IgdGhlIE9mZmxpbmVTZXJ2aWNlIHVzZWQgZWcuIGluIHRoZSBvZmZsaW5lIHJlcXVlc3QgaW50ZXJjZXB0b3IuJyxcbiAgICB7XG4gICAgICAgIHByb3ZpZGVkSW46ICdyb290JyxcbiAgICAgICAgZmFjdG9yeTogKCkgPT4ge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgc3R5bGlzdGljL21heC1sZW5cbiAgICAgICAgICAgICAgICAnTm8gT2ZmbGluZVNlcnZpY2UgaGFzIGJlZW4gcHJvdmlkZWQgZm9yIHRoZSB0b2tlbiBOR1hfT0ZGTElORV9TRVJWSUNFXFxuQWRkIHRoaXMgdG8geW91ciBhcHAubW9kdWxlLnRzIHByb3ZpZGVyIGFycmF5OlxcbntcXG4gICAgcHJvdmlkZTogTkdYX1BXQV9PRkZMSU5FX1NFUlZJQ0UsXFxuICAgIHVzZUV4aXN0aW5nOiBNeU9mZmxpbmVTZXJ2aWNlXFxufSdcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9XG4pO1xuXG4vKipcbiAqIFRoZSB0eXBlIG9mIGEgY2FjaGVkIG9mZmxpbmUgcmVxdWVzdC5cbiAqIENvbnRhaW5zIHRoZSBodHRwIHJlcXVlc3QgYXMgd2VsbCBhcyBzb21lIG1ldGFkYXRhLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENhY2hlZFJlcXVlc3Q8VD4ge1xuICAgIC8qKlxuICAgICAqIFRoZSBhY3R1YWwgaHR0cCByZXF1ZXN0LlxuICAgICAqL1xuICAgIHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PFQ+LFxuICAgIC8qKlxuICAgICAqIFRoZSBtZXRhZGF0YSBmb3IgdGhhdCByZXF1ZXN0LlxuICAgICAqL1xuICAgIG1ldGFkYXRhOiBSZXF1ZXN0TWV0YWRhdGFJbnRlcm5hbFxufVxuXG4vKipcbiAqIFRoZSBiYXNlIGNsYXNzIGZvciBhbiBvZmZsaW5lIHNlcnZpY2UuXG4gKi9cbmV4cG9ydCBjbGFzcyBOZ3hQd2FPZmZsaW5lU2VydmljZSB7XG4gICAgLyoqXG4gICAgICogVGhlIGtleSB1bmRlciB3aGljaCBhbnkgcmVxdWVzdHMgYXJlIHNhdmVkIGluIGxvY2FsIHN0b3JhZ2UuXG4gICAgICovXG4gICAgcmVhZG9ubHkgQ0FDSEVEX1JFUVVFU1RTX0tFWTogc3RyaW5nID0gJ3JlcXVlc3RzJztcblxuICAgIC8qKlxuICAgICAqIFRoZSBwcmVmaXggb2Ygb2ZmbGluZSBnZW5lcmF0ZWQgaWRzLlxuICAgICAqIElzIHVzZWQgdG8gY2hlY2sgaWYgYSByZXF1ZXN0IHN0aWxsIGhhcyB1bnJlc29sdmVkIGRlcGVuZGVuY2llcy5cbiAgICAgKi9cbiAgICByZWFkb25seSBPRkZMSU5FX0lEX1BSRUZJWDogc3RyaW5nID0gJ29mZmxpbmUnO1xuXG4gICAgLyoqXG4gICAgICogQSBzbmFja2JhciBtZXNzYWdlIHRvIGRpc3BsYXkgd2hlbiB0aGUgc3luY2hyb25pemF0aW9uIG9mIGFsbCBjYWNoZWQgcmVxdWVzdHMgaGFzIGJlZW4gZmluaXNoZWQuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IEFMTF9TWU5DX0ZJTklTSEVEX1NOQUNLX0JBUl9NRVNTQUdFOiBzdHJpbmcgPSAnU3luY2hyb25pemF0aW9uIGZpbmlzaGVkJztcblxuICAgIC8qKlxuICAgICAqIEEgc25hY2tiYXIgbWVzc2FnZSB0byBkaXNwbGF5IHdoZW4gdGhlIHN5bmNocm9uaXphdGlvbiBvZiBhbGwgY2FjaGVkIHJlcXVlc3RzIGZhaWxzLlxuICAgICAqL1xuICAgIHByb3RlY3RlZCByZWFkb25seSBBTExfU1lOQ19GQUlMRURfU05BQ0tfQkFSX01FU1NBR0U6IHN0cmluZyA9ICdTeW5jaHJvbml6YXRpb24gZmFpbGVkLCBwbGVhc2UgdHJ5IGFnYWluIGxhdGVyJztcblxuICAgIC8qKlxuICAgICAqIEEgc25hY2tiYXIgbWVzc2FnZSB0byBkaXNwbGF5IHdoZW4gdGhlIHN5bmNocm9uaXphdGlvbiBvZiBhIHNpbmdsZSBjYWNoZWQgcmVxdWVzdHMgaGFzIGJlZW4gZmluaXNoZWQuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IFNJTkdMRV9TWU5DX0ZJTklTSEVEX1NOQUNLX0JBUl9NRVNTQUdFOiBzdHJpbmcgPSAnU3luY2hyb25pemF0aW9uIGZpbmlzaGVkJztcblxuICAgIC8qKlxuICAgICAqIEEgc25hY2tiYXIgbWVzc2FnZSB0byBkaXNwbGF5IHdoZW4gdGhlIHN5bmNocm9uaXphdGlvbiBvZiBhIHNpbmdsZSBjYWNoZWQgcmVxdWVzdHMgZmFpbHMuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IFNJTkdMRV9TWU5DX0ZBSUxFRF9TTkFDS19CQVJfTUVTU0FHRTogc3RyaW5nID0gJ1N5bmNocm9uaXphdGlvbiBmYWlsZWQsIHBsZWFzZSB0cnkgYWdhaW4gbGF0ZXInO1xuXG4gICAgLyoqXG4gICAgICogV2hldGhlciBvciBub3QgdGhlIHVzZXIgaGFzIG5vIGludGVybmV0IGNvbm5lY3Rpb24uXG4gICAgICovXG4gICAgaXNPZmZsaW5lOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKipcbiAgICAgKiBBIHN1YmplY3Qgb2YgYWxsIHRoZSByZXF1ZXN0cyB0aGF0IGhhdmUgYmVlbiBkb25lIHdoaWxlIG9mZmxpbmUuXG4gICAgICogTmVlZHMgdG8gYmUgdXNlZCBmb3IgYXBwbHlpbmcgb2ZmbGluZSBkYXRhIG9yIHN5bmNpbmcgdGhlIHJlcXVlc3RzIHRvIHRoZSBhcGkuXG4gICAgICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBjYWNoZWRSZXF1ZXN0c1N1YmplY3Q6IEJlaGF2aW9yU3ViamVjdDxDYWNoZWRSZXF1ZXN0PHVua25vd24+W10+O1xuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGpzZG9jL3JlcXVpcmUtcmV0dXJuc1xuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50bHkgc3RvcmVkIGNhY2hlZCByZXF1ZXN0cyAoaWYgdGhlcmUgYXJlIGFueSkuXG4gICAgICovXG4gICAgZ2V0IGNhY2hlZFJlcXVlc3RzKCk6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNhY2hlZFJlcXVlc3RzU3ViamVjdC52YWx1ZTtcbiAgICB9XG5cbiAgICBzZXQgY2FjaGVkUmVxdWVzdHMoY2FjaGVkUmVxdWVzdHM6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSkge1xuICAgICAgICBpZiAoIWlzUGxhdGZvcm1Ccm93c2VyKHRoaXMucGxhdGZvcm1JZCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSh0aGlzLkNBQ0hFRF9SRVFVRVNUU19LRVksIEpTT04uc3RyaW5naWZ5KGNhY2hlZFJlcXVlc3RzKSk7XG4gICAgICAgIHRoaXMuY2FjaGVkUmVxdWVzdHNTdWJqZWN0Lm5leHQoY2FjaGVkUmVxdWVzdHMpO1xuICAgIH1cblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IGh0dHA6IEh0dHBDbGllbnQsXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgc25hY2tCYXI6IE1hdFNuYWNrQmFyLFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IHpvbmU6IE5nWm9uZSxcbiAgICAgICAgQEluamVjdChQTEFURk9STV9JRClcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBwbGF0Zm9ybUlkOiBPYmplY3RcbiAgICApIHtcbiAgICAgICAgaWYgKCFpc1BsYXRmb3JtQnJvd3NlcihwbGF0Zm9ybUlkKSkge1xuICAgICAgICAgICAgdGhpcy5pc09mZmxpbmUgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuY2FjaGVkUmVxdWVzdHNTdWJqZWN0ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxDYWNoZWRSZXF1ZXN0PHVua25vd24+W10+KFtdKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmlzT2ZmbGluZSA9ICFuYXZpZ2F0b3Iub25MaW5lO1xuICAgICAgICB3aW5kb3cub25vbmxpbmUgPSAoKSA9PiB0aGlzLmlzT2ZmbGluZSA9ICFuYXZpZ2F0b3Iub25MaW5lO1xuICAgICAgICB3aW5kb3cub25vZmZsaW5lID0gKCkgPT4gdGhpcy5pc09mZmxpbmUgPSAhbmF2aWdhdG9yLm9uTGluZTtcblxuICAgICAgICBjb25zdCBzdHJpbmdEYXRhOiBzdHJpbmcgfCBudWxsID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0odGhpcy5DQUNIRURfUkVRVUVTVFNfS0VZKTtcbiAgICAgICAgY29uc3QgcmVxdWVzdHNEYXRhOiBDYWNoZWRSZXF1ZXN0PHVua25vd24+W10gPSBzdHJpbmdEYXRhID8gSlNPTi5wYXJzZShzdHJpbmdEYXRhKSBhcyBDYWNoZWRSZXF1ZXN0PHVua25vd24+W10gOiBbXTtcbiAgICAgICAgdGhpcy5jYWNoZWRSZXF1ZXN0c1N1YmplY3QgPSBuZXcgQmVoYXZpb3JTdWJqZWN0KHJlcXVlc3RzRGF0YSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQXBwbGllcyBhbnkgb2ZmbGluZSBkYXRhIHRoYXQgaGFzIGJlZW4gY2FjaGVkIHRvIHRoZSBnaXZlbiB2YWx1ZXMuXG4gICAgICogQHBhcmFtIHR5cGUgLSBUaGUgdHlwZSBvZiB0aGUgcHJvdmlkZWQgZW50aXRpZXMuIElzIG5lZWRlZCB0byBjaGVjayBpZiBhbnkgY2FjaGVkIHJlcXVlc3RzIG9mIHRoZSBzYW1lIHR5cGUgZXhpc3QuXG4gICAgICogQHBhcmFtIGVudGl0aWVzIC0gVGhlIGFscmVhZHkgZXhpc3RpbmcgZGF0YS5cbiAgICAgKiBAcmV0dXJucyBUaGUgYWxyZWFkeSBleGlzdGluZyBlbnRpdGllcyBleHRlbmRlZC9tb2RpZmllZCBieSB0aGUgb2ZmbGluZSBjYWNoZWQgcmVxdWVzdHMuXG4gICAgICovXG4gICAgYXBwbHlPZmZsaW5lRGF0YTxFbnRpdHlUeXBlIGV4dGVuZHMgQmFzZUVudGl0eVR5cGU8RW50aXR5VHlwZT4+KFxuICAgICAgICB0eXBlOiBzdHJpbmcsXG4gICAgICAgIGVudGl0aWVzOiBFbnRpdHlUeXBlW11cbiAgICApOiBFbnRpdHlUeXBlW10ge1xuICAgICAgICBpZiAoIXRoaXMuY2FjaGVkUmVxdWVzdHMubGVuZ3RoKSB7XG4gICAgICAgICAgICByZXR1cm4gZW50aXRpZXM7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVzOiBFbnRpdHlUeXBlW10gPSBBcnJheS5mcm9tKGVudGl0aWVzKTtcbiAgICAgICAgY29uc3QgY2FjaGVkUmVxdWVzdHM6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSA9IHRoaXMuY2FjaGVkUmVxdWVzdHMuZmlsdGVyKHJlcSA9PiByZXEubWV0YWRhdGEudHlwZSA9PT0gdHlwZSk7XG4gICAgICAgIGZvciAoY29uc3QgcmVxIG9mIGNhY2hlZFJlcXVlc3RzKSB7XG4gICAgICAgICAgICBzd2l0Y2ggKHJlcS5yZXF1ZXN0Lm1ldGhvZCkge1xuICAgICAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5QT1NUOiB7XG4gICAgICAgICAgICAgICAgICAgIHJlcy5wdXNoKHJlcS5yZXF1ZXN0LmJvZHkgYXMgRW50aXR5VHlwZSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjYXNlIEh0dHBNZXRob2QuUEFUQ0g6IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGF0Y2hJZEtleToga2V5b2YgRW50aXR5VHlwZSA9IHJlcS5tZXRhZGF0YS5pZEtleTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5kZXg6IG51bWJlciA9IHJlcy5maW5kSW5kZXgoZSA9PiByZXEucmVxdWVzdC51cmxXaXRoUGFyYW1zLmluY2x1ZGVzKGAke2VbcGF0Y2hJZEtleV19YCkpO1xuICAgICAgICAgICAgICAgICAgICByZXNbaW5kZXhdID0gdGhpcy51cGRhdGVPZmZsaW5lKHJlcS5yZXF1ZXN0LmJvZHkgYXMgRW50aXR5VHlwZSwgcmVzW2luZGV4XSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjYXNlIEh0dHBNZXRob2QuREVMRVRFOiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGRlbGV0ZUlkS2V5OiBrZXlvZiBFbnRpdHlUeXBlID0gcmVxLm1ldGFkYXRhLmlkS2V5O1xuICAgICAgICAgICAgICAgICAgICByZXMuc3BsaWNlKHJlcy5maW5kSW5kZXgoZSA9PiByZXEucmVxdWVzdC51cmxXaXRoUGFyYW1zLmluY2x1ZGVzKGAke2VbZGVsZXRlSWRLZXldfWApKSwgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1RoZXJlIHdhcyBhbiB1bmtub3duIGh0dHAtbWV0aG9kIGluIG9uZSBvZiB5b3VyIGNhY2hlZCBvZmZsaW5lIHJlcXVlc3RzOicsIHJlcS5yZXF1ZXN0Lm1ldGhvZCk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFwcGxpZXMgYW4gVVBEQVRFIHRvIGFuIGVudGl0eSB3aXRob3V0IHNlbmRpbmcgYSByZXF1ZXN0IHRvIHRoZSBzZXJ2ZXIuXG4gICAgICogQHBhcmFtIGNoYW5nZXMgLSBUaGUgY2hhbmdlcyB0aGF0IHNob3VsZCBiZSBtYWRlIHRvIHRoZSBlbnRpdHkuXG4gICAgICogQHBhcmFtIGVudGl0eSAtIFRoZSBlbnRpdHkgdGhhdCBzaG91bGQgYmUgdXBkYXRlZC5cbiAgICAgKiBAcmV0dXJucyBUaGUgdXBkYXRlZCBlbnRpdHkuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHVwZGF0ZU9mZmxpbmU8RW50aXR5VHlwZSBleHRlbmRzIEJhc2VFbnRpdHlUeXBlPEVudGl0eVR5cGU+PihcbiAgICAgICAgY2hhbmdlczogUGFydGlhbDxFbnRpdHlUeXBlPixcbiAgICAgICAgZW50aXR5OiBFbnRpdHlUeXBlXG4gICAgKTogRW50aXR5VHlwZSB7XG4gICAgICAgIGZvciAoY29uc3Qga2V5IGluIGNoYW5nZXMpIHtcbiAgICAgICAgICAgIGVudGl0eVtrZXldID0gY2hhbmdlc1trZXldIGFzIEVudGl0eVR5cGVbRXh0cmFjdDxrZXlvZiBFbnRpdHlUeXBlLCBzdHJpbmc+XTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZW50aXR5O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNlbmRzIGEgc3BlY2lmaWMgY2FjaGVkIHJlcXVlc3QgdG8gdGhlIHNlcnZlci5cbiAgICAgKiBAcGFyYW0gcmVxdWVzdCAtIFRoZSByZXF1ZXN0IHRoYXQgc2hvdWxkIGJlIHN5bmNlZC5cbiAgICAgKi9cbiAgICBhc3luYyBzeW5jPFQ+KHJlcXVlc3Q6IENhY2hlZFJlcXVlc3Q8VD4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgY2FjaGVkUmVxdWVzdHNQcmlvckNoYW5nZXM6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSA9IExvZGFzaFV0aWxpdGllcy5jbG9uZURlZXAodGhpcy5jYWNoZWRSZXF1ZXN0cyk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXM6IEF3YWl0ZWQ8VD4gPSBhd2FpdCB0aGlzLnN5bmNTaW5nbGVSZXF1ZXN0KHJlcXVlc3QpO1xuICAgICAgICAgICAgdGhpcy56b25lLnJ1bigoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zbmFja0Jhci5vcGVuKHRoaXMuU0lOR0xFX1NZTkNfRklOSVNIRURfU05BQ0tfQkFSX01FU1NBR0UsIHVuZGVmaW5lZCwgeyBkdXJhdGlvbjogMjUwMCB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5yZW1vdmVTaW5nbGVSZXF1ZXN0KHJlcXVlc3QpO1xuICAgICAgICAgICAgdGhpcy51cGRhdGVPZmZsaW5lSWRzSW5SZXF1ZXN0cyhyZXF1ZXN0LCByZXMpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIHtcbiAgICAgICAgICAgIHRoaXMuem9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc25hY2tCYXIub3Blbih0aGlzLlNJTkdMRV9TWU5DX0ZBSUxFRF9TTkFDS19CQVJfTUVTU0FHRSwgdW5kZWZpbmVkLCB7IGR1cmF0aW9uOiAyNTAwIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gY2FjaGVkUmVxdWVzdHNQcmlvckNoYW5nZXM7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kcyBhbGwgY2FjaGVkIHJlcXVlc3RzIHRvIHRoZSBzZXJ2ZXIuIFRyaWVzIHRvIGhhbmRsZSBkZXBlbmRlbmNpZXMgb2YgcmVxdWVzdHMgb24gZWFjaCBvdGhlci5cbiAgICAgKi9cbiAgICBhc3luYyBzeW5jQWxsKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBjYWNoZWRSZXF1ZXN0c1ByaW9yQ2hhbmdlczogQ2FjaGVkUmVxdWVzdDx1bmtub3duPltdID0gTG9kYXNoVXRpbGl0aWVzLmNsb25lRGVlcCh0aGlzLmNhY2hlZFJlcXVlc3RzKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc3luY0FsbFJlY3Vyc2l2ZSgpO1xuICAgICAgICAgICAgdGhpcy56b25lLnJ1bigoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zbmFja0Jhci5vcGVuKHRoaXMuQUxMX1NZTkNfRklOSVNIRURfU05BQ0tfQkFSX01FU1NBR0UsIHVuZGVmaW5lZCwgeyBkdXJhdGlvbjogMjUwMCB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5jYWNoZWRSZXF1ZXN0cyA9IFtdO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIHtcbiAgICAgICAgICAgIHRoaXMuem9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc25hY2tCYXIub3Blbih0aGlzLkFMTF9TWU5DX0ZBSUxFRF9TTkFDS19CQVJfTUVTU0FHRSwgdW5kZWZpbmVkLCB7IGR1cmF0aW9uOiAyNTAwIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gY2FjaGVkUmVxdWVzdHNQcmlvckNoYW5nZXM7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUaGUgcmVjdXJzaXZlIG1ldGhvZCB1c2VkIHRvIHN5biBhbGwgcmVxdWVzdHMgdG8gdGhlIGFwaS5cbiAgICAgKi9cbiAgICBwcm90ZWN0ZWQgYXN5bmMgc3luY0FsbFJlY3Vyc2l2ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHN0eWxpc3RpYy9tYXgtbGVuXG4gICAgICAgIGNvbnN0IHJlcXVlc3Q6IENhY2hlZFJlcXVlc3Q8QmFzZUVudGl0eVR5cGU8dW5rbm93bj4+IHwgdW5kZWZpbmVkID0gdGhpcy5jYWNoZWRSZXF1ZXN0cy5maW5kKHIgPT4gIXRoaXMuaGFzVW5yZXNvbHZlZERlcGVuZGVuY3kocikpIGFzIENhY2hlZFJlcXVlc3Q8QmFzZUVudGl0eVR5cGU8dW5rbm93bj4+IHwgdW5kZWZpbmVkO1xuICAgICAgICBpZiAoIXJlcXVlc3QpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCByZXM6IEJhc2VFbnRpdHlUeXBlPHVua25vd24+ID0gYXdhaXQgdGhpcy5zeW5jU2luZ2xlUmVxdWVzdChyZXF1ZXN0KTtcbiAgICAgICAgdGhpcy51cGRhdGVPZmZsaW5lSWRzSW5SZXF1ZXN0cyhyZXF1ZXN0LCByZXMpO1xuICAgICAgICBhd2FpdCB0aGlzLnN5bmNBbGxSZWN1cnNpdmUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kcyBhIHNpbmdsZSBjYWNoZWQgcmVxdWVzdCB0byB0aGUgc2VydmVyLlxuICAgICAqIEBwYXJhbSByZXF1ZXN0IC0gVGhlIHJlcXVlc3QgdGhhdCBzaG91bGQgYmUgc3luY2VkLlxuICAgICAqIEByZXR1cm5zIEEgcHJvbWlzZSBvZiB0aGUgcmVxdWVzdCByZXN1bHQuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIGFzeW5jIHN5bmNTaW5nbGVSZXF1ZXN0PFQ+KFxuICAgICAgICByZXF1ZXN0OiBDYWNoZWRSZXF1ZXN0PFQ+XG4gICAgKTogUHJvbWlzZTxUPiB7XG4gICAgICAgIGlmICh0aGlzLmlzT2ZmbGluZSB8fCB0aGlzLmhhc1VucmVzb2x2ZWREZXBlbmRlbmN5KHJlcXVlc3QpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBzeW5jIHRoZSByZXF1ZXN0Jyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVxdWVzdE9ic2VydmFibGU6IE9ic2VydmFibGU8VD4gfCB1bmRlZmluZWQgPSB0aGlzLnJlcXVlc3QocmVxdWVzdCk7XG4gICAgICAgIGlmICghcmVxdWVzdE9ic2VydmFibGUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IHN5bmMgdGhlIHJlcXVlc3QnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXdhaXQgZmlyc3RWYWx1ZUZyb20ocmVxdWVzdE9ic2VydmFibGUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgdXBkYXRlT2ZmbGluZUlkc0luUmVxdWVzdHM8VD4ocmVxdWVzdDogQ2FjaGVkUmVxdWVzdDxUPiwgcmVzOiBUKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmNhY2hlZFJlcXVlc3RzLmxlbmd0aCAmJiByZXF1ZXN0LnJlcXVlc3QuYm9keSAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGNvbnN0IGlkS2V5OiBrZXlvZiBCYXNlRW50aXR5VHlwZTx1bmtub3duPiA9IHJlcXVlc3QubWV0YWRhdGEuaWRLZXk7XG4gICAgICAgICAgICBpZiAocmVzW2lkS2V5XSAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCByZXF1ZXN0c1N0cmluZzogc3RyaW5nID0gYCR7dGhpcy5jYWNoZWRSZXF1ZXN0c31gLnNwbGl0KHJlcXVlc3QucmVxdWVzdC5ib2R5W2lkS2V5XSkuam9pbihyZXNbaWRLZXldKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gSlNPTi5wYXJzZShyZXF1ZXN0c1N0cmluZykgYXMgQ2FjaGVkUmVxdWVzdDxUPltdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsbHMgaHR0cC5wb3N0L3BhdGNoL2RlbGV0ZSBldGMuIE9uIHRoZSBwcm92aWRlZCByZXF1ZXN0LlxuICAgICAqIEBwYXJhbSByZXF1ZXN0IC0gVGhlIHJlcXVlc3QgdGhhdCBzaG91bGQgYmUgc2VudC5cbiAgICAgKiBAcmV0dXJucyBUaGUgb2JzZXJ2YWJsZSBvZiB0aGUgcmVxdWVzdCBvciB1bmRlZmluZWQgaWYgc29tZXRoaW5nIHdlbnQgd3JvbmcuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlcXVlc3Q8RW50aXR5VHlwZSBleHRlbmRzIEJhc2VFbnRpdHlUeXBlPEVudGl0eVR5cGU+PihcbiAgICAgICAgcmVxdWVzdDogQ2FjaGVkUmVxdWVzdDxFbnRpdHlUeXBlPlxuICAgICk6IE9ic2VydmFibGU8RW50aXR5VHlwZT4gfCB1bmRlZmluZWQge1xuICAgICAgICBzd2l0Y2ggKHJlcXVlc3QucmVxdWVzdC5tZXRob2QpIHtcbiAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5QT1NUOiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuaHR0cC5wb3N0PEVudGl0eVR5cGU+KFxuICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LnJlcXVlc3QudXJsV2l0aFBhcmFtcyxcbiAgICAgICAgICAgICAgICAgICAgTG9kYXNoVXRpbGl0aWVzLm9taXQocmVxdWVzdC5yZXF1ZXN0LmJvZHksIHJlcXVlc3QubWV0YWRhdGEuaWRLZXkpXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5QQVRDSDoge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmh0dHAucGF0Y2g8RW50aXR5VHlwZT4ocmVxdWVzdC5yZXF1ZXN0LnVybFdpdGhQYXJhbXMsIHJlcXVlc3QucmVxdWVzdC5ib2R5KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5ERUxFVEU6IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5odHRwLmRlbGV0ZTxFbnRpdHlUeXBlPihyZXF1ZXN0LnJlcXVlc3QudXJsV2l0aFBhcmFtcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcmVxdWVzdCBoYXMgYW4gdW5yZXNvbHZlZCBkZXBlbmRlbmN5IGJ5IGxvb2tpbmcgZm9yIHRoZSBrZXl3b3JkICdvZmZsaW5lJyBpbnNpZGUgb2YgaXQuXG4gICAgICogQHBhcmFtIHJlcXVlc3QgLSBUaGUgcmVxdWVzdCB0aGF0IHNob3VsZCBiZSBjaGVja2VkLlxuICAgICAqIEByZXR1cm5zIFdoZXRoZXIgb3Igbm8gdGhlIGdpdmVuIHJlcXVlc3QgaGFzIGFuIHVucmVzb2x2ZWQgZGVwZW5kZW5jeS5cbiAgICAgKi9cbiAgICBoYXNVbnJlc29sdmVkRGVwZW5kZW5jeShyZXF1ZXN0OiBDYWNoZWRSZXF1ZXN0PHVua25vd24+KTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiByZXF1ZXN0LnJlcXVlc3QudXJsV2l0aFBhcmFtcy5pbmNsdWRlcyh0aGlzLk9GRkxJTkVfSURfUFJFRklYKVxuICAgICAgICAgICAgfHwgYCR7cmVxdWVzdC5yZXF1ZXN0LmJvZHl9YC5pbmNsdWRlcyh0aGlzLk9GRkxJTkVfSURfUFJFRklYKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGEgc2luZ2xlIHJlcXVlc3QgZnJvbSB0aGUgY2FjaGUuXG4gICAgICogQHBhcmFtIHJlcXVlc3QgLSBUaGUgcmVxdWVzdCB0aGF0IHNob3VsZCBiZSByZW1vdmVkLlxuICAgICAqL1xuICAgIHJlbW92ZVNpbmdsZVJlcXVlc3QocmVxdWVzdDogQ2FjaGVkUmVxdWVzdDx1bmtub3duPik6IHZvaWQge1xuICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzLnNwbGljZSh0aGlzLmNhY2hlZFJlcXVlc3RzLmluZGV4T2YocmVxdWVzdCksIDEpO1xuICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gdGhpcy5jYWNoZWRSZXF1ZXN0cztcbiAgICB9XG59Il19