@synerty/vortexjs
Version:
Custom observable data serialisation and routing based on Angular 2+
433 lines • 62.8 kB
JavaScript
import { Inject, Injectable } from "@angular/core";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { filter, takeUntil } from "rxjs/operators";
import { VortexService } from "../VortexService";
import { TupleSelector } from "../TupleSelector";
import { Payload } from "../Payload";
import { PayloadEndpoint } from "../PayloadEndpoint";
import { NgLifeCycleEvents } from "../../util/NgLifeCycleEvents";
import { dateStr, dictKeysFromObject } from "../UtilMisc";
import { VortexStatusService } from "../VortexStatusService";
import { PayloadResponse } from "../PayloadResponse";
import { TupleOfflineStorageService } from "../storage/TupleOfflineStorageService";
import * as i0 from "@angular/core";
export class TupleDataObservableNameService {
name;
additionalFilt;
constructor(name, additionalFilt = {}) {
this.name = name;
this.additionalFilt = additionalFilt;
}
equals(other) {
if (other == null)
return false;
if (this.name != other.name)
return false;
return (JSON.stringify(this.additionalFilt) ==
JSON.stringify(other.additionalFilt));
}
toString() {
return `${this.name}:${JSON.stringify(this.additionalFilt)}`;
}
}
export class CachedSubscribedData {
tupleSelector;
subject$ = new BehaviorSubject(null);
get observable() {
return this.subject$.pipe(filter((v) => v != null));
}
/** Last Server Payload Date
* If the server has responded with a payload, this is the date in the payload
* @type {Date | null}
*/
lastServerPayloadDate = null;
lastServerAskDate = null;
cacheEnabled = true;
storageEnabled = true;
askServerEnabled = true;
constructor(tupleSelector) {
this.tupleSelector = tupleSelector;
this.touch();
}
// The date the cache is scheduled to be torn down.
// This will be X time after we notice that it has no subscribers
tearDownDate = null;
TEARDOWN_WAIT = 30 * 1000; // 30 seconds, in milliseconds
markForTearDown() {
if (this.tearDownDate == null)
this.tearDownDate = Date.now();
}
resetTearDown() {
this.tearDownDate = null;
this.touch();
}
isReadyForTearDown() {
return (this.tearDownDate != null &&
this.tearDownDate + this.TEARDOWN_WAIT <= Date.now());
}
get tuples() {
return this.subject$.getValue();
}
set tuples(tuples) {
this.touch();
this.subject$.next(tuples);
}
/** Last Touched
*
* The last date that this cache was touched (subscribed or updated)
* @type {Date | null}
*/
FLUSH_WAIT = 120 * 1000; // 2 minutes, in milliseconds
_lastTouched = null;
touch() {
this._lastTouched = Date.now();
}
isReadyForFlush() {
return (this._lastTouched != null &&
this._lastTouched + this.FLUSH_WAIT <= Date.now());
}
flush() {
this.lastServerAskDate = null;
this.lastServerPayloadDate = null;
this.subject$.next(null);
}
}
export class TupleDataOfflineObserverService extends NgLifeCycleEvents {
vortexService;
vortexStatusService;
tupleDataObservableName;
tupleOfflineStorageService;
endpoint;
filt;
cacheByTupleSelector = {};
constructor(vortexService, vortexStatusService, tupleDataObservableName, tupleOfflineStorageService) {
super();
this.vortexService = vortexService;
this.vortexStatusService = vortexStatusService;
this.tupleDataObservableName = tupleDataObservableName;
this.tupleOfflineStorageService = tupleOfflineStorageService;
this.filt = Object.assign({
name: tupleDataObservableName.name,
key: "tupleDataObservable",
}, tupleDataObservableName.additionalFilt);
this.endpoint = new PayloadEndpoint(this, this.filt);
this.endpoint.observable.subscribe((payloadEnvelope) => {
payloadEnvelope
.decodePayload()
.then((payload) => {
this.receivePayload(payloadEnvelope, payload, payloadEnvelope.encodedPayload);
})
.catch((e) => {
console.log(`${dateStr()} TupleDataOfflineObserverService:Error decoding payload ${e}`);
});
});
vortexStatusService.isOnline
.pipe(takeUntil(this.onDestroyEvent), filter((online) => online === true))
.subscribe((online) => this.vortexOnlineChanged());
// Cleanup dead subscribers every 30 seconds.
let cleanupTimer = setInterval(() => this.cleanupDeadCaches(), 2000);
this.onDestroyEvent.subscribe(() => clearInterval(cleanupTimer));
}
_nameService() {
return this.tupleDataObservableName;
}
pollForTuples(tupleSelector, useCache = true) {
// --- If the data exists in the cache, then return it
let tsStr = tupleSelector.toOrderedJsonStr();
if (useCache && this.cacheByTupleSelector.hasOwnProperty(tsStr)) {
let cachedData = this.cacheByTupleSelector[tsStr];
cachedData.resetTearDown();
if (cachedData.cacheEnabled &&
cachedData.lastServerPayloadDate != null) {
return Promise.resolve(cachedData.tuples);
}
}
// --- Else, we want the data from the server
// The PayloadEndpoint for this observable should also pickup and process
// the response. So that will take care of the cache update and notify of
// subscribers.
let startFilt = Object.assign({ subscribe: false, useCache: useCache }, this.filt, {
tupleSelector: tupleSelector,
});
// Optionally typed, No need to worry about the fact that we convert this
// and then TypeScript doesn't recognise that data type change
let promise = new Payload(startFilt).makePayloadEnvelope();
promise = promise.then((payloadEnvelope) => {
return new PayloadResponse(this.vortexService, payloadEnvelope);
});
promise = promise.then((payloadEnvelope) => {
return payloadEnvelope.decodePayload();
});
promise = promise.then((payload) => payload.tuples);
return promise;
}
/** Flush Cache
*
* The Data Offine Observer can be used to offline cache data by observing a large
* amounts of data, more data then the user would normally look at.
*
* If it's being used like this then the cache should be flushed during the process
* to ensure it's not all being kept in memory.
*
* @param {TupleSelector} tupleSelector The tuple selector to flush the cache for
*/
flushCache(tupleSelector) {
let tsStr = tupleSelector.toOrderedJsonStr();
if (this.cacheByTupleSelector.hasOwnProperty(tsStr)) {
let cachedData = this.cacheByTupleSelector[tsStr];
cachedData.flush();
}
this.cleanupDeadCaches();
}
/** Promise from to Tuple Selector
*
* See the subscribeToTupleSelector method for more details.
* The promise will fire on the first emit of data.
*
* Do not use this when there will be no data present,
* or it may cause a memory leak.
*
* @param {TupleSelector} tupleSelector
* @param {boolean} disableCache
* @param {boolean} disableStorage
* @param {boolean} disableAskServer Use this to store and observe data completely
* within the angular app.
* @returns {Subject<Tuple[]>}
*/
promiseFromTupleSelector(tupleSelector, disableCache = false, disableStorage = false, disableAskServer = false) {
return firstValueFrom(this.subscribeToTupleSelector(tupleSelector, disableCache, disableStorage, disableAskServer));
}
/** Subscribe to Tuple Selector
*
* Get an observable that will be fired when any new data updates are available
* Data is loaded from the local db cache, while it waits for the server to respond.
* * either from the server, or if they are locally updated with updateOfflineState()
*
* @param {TupleSelector} tupleSelector
* @param {boolean} disableCache
* @param {boolean} disableStorage
* @param {boolean} disableAskServer Use this to store and observe data completely
* within the angular app.
* @returns {Subject<Tuple[]>}
*/
subscribeToTupleSelector(tupleSelector, disableCache = false, disableStorage = false, disableAskServer = false) {
let tsStr = tupleSelector.toOrderedJsonStr();
let cachedData = null;
// If the cache exists, use it
if (this.cacheByTupleSelector.hasOwnProperty(tsStr)) {
cachedData = this.cacheByTupleSelector[tsStr];
// Maybe disable cache
cachedData.cacheEnabled = cachedData.cacheEnabled && !disableCache;
// Maybe disable storage
cachedData.storageEnabled =
cachedData.storageEnabled && !disableStorage;
// Maybe disable ask server
cachedData.askServerEnabled =
cachedData.askServerEnabled && !disableAskServer;
// If the cache is enabled, and we have tuple data, then notify
if (cachedData.cacheEnabled && cachedData.tuples != null) {
return cachedData.observable;
}
// ELSE, Create the cache
}
else {
cachedData = new CachedSubscribedData(tupleSelector);
cachedData.cacheEnabled = !disableCache;
cachedData.storageEnabled = !disableStorage;
cachedData.askServerEnabled = !disableAskServer;
this.cacheByTupleSelector[tsStr] = cachedData;
}
// If asking the server is enabled and we have not asked the server, then ask
if (cachedData.askServerEnabled &&
cachedData.lastServerAskDate == null) {
this.tellServerWeWantData([tupleSelector], disableCache);
}
// If the tuples are null (because it's new or been flushed),
// Then ask the local DB again for it.
if (cachedData.storageEnabled && cachedData.tuples == null) {
this.tupleOfflineStorageService
.loadTuplesEncoded(tupleSelector)
.then((vortexMsgOrNull) => {
// There is no data, return
if (vortexMsgOrNull == null) {
// Only return if we're expecting data from the server
if (cachedData.askServerEnabled) {
return;
}
// Otherwise return an empty array
this.notifyObservers(cachedData, tupleSelector, []);
return;
}
return Payload.fromEncodedPayload(vortexMsgOrNull).then((payload) => {
// If the server has responded before we loaded the data, then just
// ignore the cached data.
if (cachedData.lastServerPayloadDate != null)
return;
// Update the tuples, and notify if them
cachedData.tuples = payload.tuples;
this.notifyObservers(cachedData, tupleSelector, payload.tuples);
});
})
.catch((err) => {
this.vortexStatusService.logError(`loadTuples failed : ${err}`);
throw new Error(err);
});
}
cachedData.resetTearDown();
return cachedData.observable;
}
/** Update Offline State
*
* This method updates the offline stored data, which will be used until the next
* update from the server comes along.
* @param tupleSelector The tuple selector to update tuples for
* @param tuples The new data to store
*/
async updateOfflineState(tupleSelector, tuples) {
let tsStr = tupleSelector.toOrderedJsonStr();
let cachedData = null;
// Make note of the last server update date
let lastServerDate = null;
if (this.cacheByTupleSelector.hasOwnProperty(tsStr)) {
cachedData = this.cacheByTupleSelector[tsStr];
lastServerDate = cachedData.lastServerPayloadDate;
}
// AND store the data locally
await this.storeDataLocally(tupleSelector, tuples);
if (cachedData == null) {
return;
}
if (lastServerDate != cachedData.lastServerPayloadDate)
return;
cachedData.tuples = tuples;
this.notifyObservers(cachedData, tupleSelector, tuples);
}
cleanupDeadCaches() {
for (let key of dictKeysFromObject(this.cacheByTupleSelector)) {
let cachedData = this.cacheByTupleSelector[key];
// If no activity has occured on the cache, then flush it
if (cachedData.isReadyForFlush()) {
console.log(`${dateStr()} Flushing cache due to expiry ${key}`);
cachedData.flush();
}
// Tear down happens 30s after the last subscriber unsubscribes
// If there are subscribers, then reset the teardown clock
if (cachedData.subject$.observers.length !== 0) {
cachedData.resetTearDown();
// Tear down the cahce, including telling the server we're no longer
// observing the data
}
else if (cachedData.isReadyForTearDown()) {
console.log(`${dateStr()} Tearing down cache ${key}`);
cachedData.flush();
delete this.cacheByTupleSelector[key];
this.tellServerWeWantData([cachedData.tupleSelector], null, true);
// if there are no subscribers, then mark it for tear down (30s time)
}
else {
cachedData.markForTearDown();
}
}
}
vortexOnlineChanged() {
this.cleanupDeadCaches();
let tupleSelectors = [];
for (let key of dictKeysFromObject(this.cacheByTupleSelector)) {
let cache = this.cacheByTupleSelector[key];
if (cache.askServerEnabled)
tupleSelectors.push(TupleSelector.fromJsonStr(key));
}
this.tellServerWeWantData(tupleSelectors);
}
receivePayload(payloadEnvelope, payload, encodedPayload) {
const tupleSelector = payloadEnvelope.filt["tupleSelector"];
const tsStr = tupleSelector.toOrderedJsonStr();
if (!this.cacheByTupleSelector.hasOwnProperty(tsStr))
return;
const cachedData = this.cacheByTupleSelector[tsStr];
const lastDate = cachedData.lastServerPayloadDate;
if (payloadEnvelope.date == null) {
throw new Error("payload.date can not be null");
}
const thisDate = new Date(payloadEnvelope.date);
// If the data is old, then disregard it.
if (lastDate != null &&
// Newer dates have higher numbers
// If this date is less than the last date
thisDate.getTime() < lastDate.getTime()) {
return;
}
cachedData.lastServerPayloadDate = thisDate;
cachedData.tuples = payload.tuples;
this.notifyObserversAndStore(cachedData, tupleSelector, payload.tuples, encodedPayload);
}
tellServerWeWantData(tupleSelectors, disableCache = false, unsubscribe = false) {
if (!this.vortexStatusService.snapshot.isOnline)
return;
let startFilt = Object.assign({ subscribe: true }, this.filt);
let payloads = [];
for (let tupleSelector of tupleSelectors) {
let tsStr = tupleSelector.toOrderedJsonStr();
if (this.cacheByTupleSelector.hasOwnProperty(tsStr))
this.cacheByTupleSelector[tsStr].lastServerAskDate = new Date();
let filt = Object.assign({}, startFilt, {
tupleSelector: tupleSelector,
unsubscribe: unsubscribe,
});
if (disableCache != null)
filt["disableCache"] = disableCache;
payloads.push(new Payload(filt));
}
this.vortexService.sendPayload(payloads);
}
notifyObservers(cachedData, tupleSelector, tuples) {
// Notify Observers
try {
cachedData.tuples = tuples;
}
catch (e) {
// NOTE: Observables automatically remove observers when the raise exceptions.
console.log(`${dateStr()} ERROR: TupleDataObserverService.notifyObservers, observable has been removed
${e.toString()}
${tupleSelector.toOrderedJsonStr()}`);
}
}
notifyObserversAndStore(cachedData, tupleSelector, tuples, encodedPayload = null) {
this.notifyObservers(cachedData, tupleSelector, tuples);
// AND store the data locally
if (cachedData.storageEnabled)
this.storeDataLocally(tupleSelector, tuples, encodedPayload);
}
storeDataLocally(tupleSelector, tuples, encodedPayload = null) {
let errFunc = (err) => {
this.vortexStatusService.logError(`saveTuples failed : ${err}`);
throw new Error(err);
};
if (encodedPayload == null) {
return this.tupleOfflineStorageService
.saveTuples(tupleSelector, tuples)
.catch(errFunc);
}
return this.tupleOfflineStorageService
.saveTuplesEncoded(tupleSelector, encodedPayload)
.catch(errFunc);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleDataOfflineObserverService, deps: [{ token: VortexService }, { token: VortexStatusService }, { token: TupleDataObservableNameService }, { token: TupleOfflineStorageService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleDataOfflineObserverService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleDataOfflineObserverService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [VortexService]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [VortexStatusService]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [TupleDataObservableNameService]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [TupleOfflineStorageService]
}] }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHVwbGVEYXRhT2ZmbGluZU9ic2VydmVyU2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy92b3J0ZXgvb2JzZXJ2YWJsZS1zZXJ2aWNlL1R1cGxlRGF0YU9mZmxpbmVPYnNlcnZlclNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQXVCLE1BQU0sTUFBTSxDQUFDO0FBQzVFLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbkQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRWpELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNqRCxPQUFPLEVBQWdCLE9BQU8sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUNuRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDakUsT0FBTyxFQUFFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMxRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM3RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFckQsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7O0FBRW5GLE1BQU0sT0FBTyw4QkFBOEI7SUFFNUI7SUFDQTtJQUZYLFlBQ1csSUFBWSxFQUNaLGlCQUFzQixFQUFFO1FBRHhCLFNBQUksR0FBSixJQUFJLENBQVE7UUFDWixtQkFBYyxHQUFkLGNBQWMsQ0FBVTtJQUNoQyxDQUFDO0lBRUosTUFBTSxDQUFDLEtBQXFDO1FB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QUFJLEVBQUU7WUFDeEIsT0FBTyxJQUFJLENBQUMsMEJBQTBCO2lCQUNqQyxVQUFVLENBQUMsYUFBYSxFQUFFLE1BQU0sQ0FBQztpQkFDakMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQ3ZCO1FBRUQsT0FBTyxJQUFJLENBQUMsMEJBQTBCO2FBQ2pDLGlCQUFpQixDQUFDLGFBQWEsRUFBRSxjQUFjLENBQUM7YUFDaEQsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3hCLENBQUM7d0dBdmNRLCtCQUErQixrQkFRNUIsYUFBYSxhQUNiLG1CQUFtQixhQUNuQiw4QkFBOEIsYUFDOUIsMEJBQTBCOzRHQVg3QiwrQkFBK0I7OzRGQUEvQiwrQkFBK0I7a0JBRDNDLFVBQVU7OzBCQVNGLE1BQU07MkJBQUMsYUFBYTs7MEJBQ3BCLE1BQU07MkJBQUMsbUJBQW1COzswQkFDMUIsTUFBTTsyQkFBQyw4QkFBOEI7OzBCQUNyQyxNQUFNOzJCQUFDLDBCQUEwQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdCwgSW5qZWN0YWJsZSB9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIGZpcnN0VmFsdWVGcm9tLCBPYnNlcnZhYmxlLCBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IGZpbHRlciwgdGFrZVVudGlsIH0gZnJvbSBcInJ4anMvb3BlcmF0b3JzXCI7XG5pbXBvcnQgeyBWb3J0ZXhTZXJ2aWNlIH0gZnJvbSBcIi4uL1ZvcnRleFNlcnZpY2VcIjtcbmltcG9ydCB7IFR1cGxlIH0gZnJvbSBcIi4uL2V4cG9ydHNcIjtcbmltcG9ydCB7IFR1cGxlU2VsZWN0b3IgfSBmcm9tIFwiLi4vVHVwbGVTZWxlY3RvclwiO1xuaW1wb3J0IHsgSVBheWxvYWRGaWx0LCBQYXlsb2FkIH0gZnJvbSBcIi4uL1BheWxvYWRcIjtcbmltcG9ydCB7IFBheWxvYWRFbmRwb2ludCB9IGZyb20gXCIuLi9QYXlsb2FkRW5kcG9pbnRcIjtcbmltcG9ydCB7IE5nTGlmZUN5Y2xlRXZlbnRzIH0gZnJvbSBcIi4uLy4uL3V0aWwvTmdMaWZlQ3ljbGVFdmVudHNcIjtcbmltcG9ydCB7IGRhdGVTdHIsIGRpY3RLZXlzRnJvbU9iamVjdCB9IGZyb20gXCIuLi9VdGlsTWlzY1wiO1xuaW1wb3J0IHsgVm9ydGV4U3RhdHVzU2VydmljZSB9IGZyb20gXCIuLi9Wb3J0ZXhTdGF0dXNTZXJ2aWNlXCI7XG5pbXBvcnQgeyBQYXlsb2FkUmVzcG9uc2UgfSBmcm9tIFwiLi4vUGF5bG9hZFJlc3BvbnNlXCI7XG5pbXBvcnQgeyBQYXlsb2FkRW52ZWxvcGUgfSBmcm9tIFwiLi4vUGF5bG9hZEVudmVsb3BlXCI7XG5pbXBvcnQgeyBUdXBsZU9mZmxpbmVTdG9yYWdlU2VydmljZSB9IGZyb20gXCIuLi9zdG9yYWdlL1R1cGxlT2ZmbGluZVN0b3JhZ2VTZXJ2aWNlXCI7XG5cbmV4cG9ydCBjbGFzcyBUdXBsZURhdGFPYnNlcnZhYmxlTmFtZVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwdWJsaWMgbmFtZTogc3RyaW5nLFxuICAgICAgICBwdWJsaWMgYWRkaXRpb25hbEZpbHQ6IGFueSA9IHt9LFxuICAgICkge31cblxuICAgIGVxdWFscyhvdGhlcjogVHVwbGVEYXRhT2JzZXJ2YWJsZU5hbWVTZXJ2aWNlKTogYm9vbGVhbiB7XG4gICAgICAgIGlmIChvdGhlciA9PSBudWxsKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIGlmICh0aGlzLm5hbWUgIT0gb3RoZXIubmFtZSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkodGhpcy5hZGRpdGlvbmFsRmlsdCkgPT1cbiAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KG90aGVyLmFkZGl0aW9uYWxGaWx0KVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHRvU3RyaW5nKCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiBgJHt0aGlzLm5hbWV9OiR7SlNPTi5zdHJpbmdpZnkodGhpcy5hZGRpdGlvbmFsRmlsdCl9YDtcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBDYWNoZWRTdWJzY3JpYmVkRGF0YSB7XG4gICAgc3ViamVjdCQ6IEJlaGF2aW9yU3ViamVjdDxUdXBsZVtdPiA9IG5ldyBCZWhhdmlvclN1YmplY3Q8VHVwbGVbXSB8IG51bGw+KFxuICAgICAgICBudWxsLFxuICAgICk7XG5cbiAgICBnZXQgb2JzZXJ2YWJsZSgpOiBPYnNlcnZhYmxlPFR1cGxlW10+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3ViamVjdCQucGlwZShmaWx0ZXIoKHYpID0+IHYgIT0gbnVsbCkpO1xuICAgIH1cblxuICAgIC8qKiBMYXN0IFNlcnZlciBQYXlsb2FkIERhdGVcbiAgICAgKiBJZiB0aGUgc2VydmVyIGhhcyByZXNwb25kZWQgd2l0aCBhIHBheWxvYWQsIHRoaXMgaXMgdGhlIGRhdGUgaW4gdGhlIHBheWxvYWRcbiAgICAgKiBAdHlwZSB7RGF0ZSB8IG51bGx9XG4gICAgICovXG4gICAgbGFzdFNlcnZlclBheWxvYWREYXRlOiBEYXRlIHwgbnVsbCA9IG51bGw7XG4gICAgbGFzdFNlcnZlckFza0RhdGU6IERhdGUgfCBudWxsID0gbnVsbDtcblxuICAgIGNhY2hlRW5hYmxlZCA9IHRydWU7XG4gICAgc3RvcmFnZUVuYWJsZWQgPSB0cnVlO1xuICAgIGFza1NlcnZlckVuYWJsZWQgPSB0cnVlO1xuXG4gICAgY29uc3RydWN0b3IocHVibGljIHR1cGxlU2VsZWN0b3I6IFR1cGxlU2VsZWN0b3IpIHtcbiAgICAgICAgdGhpcy50b3VjaCgpO1xuICAgIH1cblxuICAgIC8vIFRoZSBkYXRlIHRoZSBjYWNoZSBpcyBzY2hlZHVsZWQgdG8gYmUgdG9ybiBkb3duLlxuICAgIC8vIFRoaXMgd2lsbCBiZSBYIHRpbWUgYWZ0ZXIgd2Ugbm90aWNlIHRoYXQgaXQgaGFzIG5vIHN1YnNjcmliZXJzXG4gICAgcHJpdmF0ZSB0ZWFyRG93bkRhdGU6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAgIHByaXZhdGUgVEVBUkRPV05fV0FJVCA9IDMwICogMTAwMDsgLy8gMzAgc2Vjb25kcywgaW4gbWlsbGlzZWNvbmRzXG5cbiAgICBtYXJrRm9yVGVhckRvd24oKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLnRlYXJEb3duRGF0ZSA9PSBudWxsKSB0aGlzLnRlYXJEb3duRGF0ZSA9IERhdGUubm93KCk7XG4gICAgfVxuXG4gICAgcmVzZXRUZWFyRG93bigpOiB2b2lkIHtcbiAgICAgICAgdGhpcy50ZWFyRG93bkRhdGUgPSBudWxsO1xuICAgICAgICB0aGlzLnRvdWNoKCk7XG4gICAgfVxuXG4gICAgaXNSZWFkeUZvclRlYXJEb3duKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgdGhpcy50ZWFyRG93bkRhdGUgIT0gbnVsbCAmJlxuICAgICAgICAgICAgdGhpcy50ZWFyRG93bkRhdGUgKyB0aGlzLlRFQVJET1dOX1dBSVQgPD0gRGF0ZS5ub3coKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIGdldCB0dXBsZXMoKTogVHVwbGVbXSB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5zdWJqZWN0JC5nZXRWYWx1ZSgpO1xuICAgIH1cblxuICAgIHNldCB0dXBsZXModHVwbGVzOiBUdXBsZVtdKSB7XG4gICAgICAgIHRoaXMudG91Y2goKTtcbiAgICAgICAgdGhpcy5zdWJqZWN0JC5uZXh0KHR1cGxlcyk7XG4gICAgfVxuXG4gICAgLyoqIExhc3QgVG91Y2hlZFxuICAgICAqXG4gICAgICogVGhlIGxhc3QgZGF0ZSB0aGF0IHRoaXMgY2FjaGUgd2FzIHRvdWNoZWQgKHN1YnNjcmliZWQgb3IgdXBkYXRlZClcbiAgICAgKiBAdHlwZSB7RGF0ZSB8IG51bGx9XG4gICAgICovXG4gICAgcHJpdmF0ZSBGTFVTSF9XQUlUID0gMTIwICogMTAwMDsgLy8gMiBtaW51dGVzLCBpbiBtaWxsaXNlY29uZHNcbiAgICBwcml2YXRlIF9sYXN0VG91Y2hlZDogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG5cbiAgICB0b3VjaCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5fbGFzdFRvdWNoZWQgPSBEYXRlLm5vdygpO1xuICAgIH1cblxuICAgIGlzUmVhZHlGb3JGbHVzaCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIHRoaXMuX2xhc3RUb3VjaGVkICE9IG51bGwgJiZcbiAgICAgICAgICAgIHRoaXMuX2xhc3RUb3VjaGVkICsgdGhpcy5GTFVTSF9XQUlUIDw9IERhdGUubm93KClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBmbHVzaCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5sYXN0U2VydmVyQXNrRGF0ZSA9IG51bGw7XG4gICAgICAgIHRoaXMubGFzdFNlcnZlclBheWxvYWREYXRlID0gbnVsbDtcbiAgICAgICAgdGhpcy5zdWJqZWN0JC5uZXh0KG51bGwpO1xuICAgIH1cbn1cblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFR1cGxlRGF0YU9mZmxpbmVPYnNlcnZlclNlcnZpY2UgZXh0ZW5kcyBOZ0xpZmVDeWNsZUV2ZW50cyB7XG4gICAgcHJpdmF0ZSBlbmRwb2ludDogUGF5bG9hZEVuZHBvaW50O1xuICAgIHByaXZhdGUgZmlsdDogSVBheWxvYWRGaWx0O1xuICAgIHByaXZhdGUgY2FjaGVCeVR1cGxlU2VsZWN0b3I6IHtcbiAgICAgICAgW3R1cGxlU2VsZWN0b3I6IHN0cmluZ106IENhY2hlZFN1YnNjcmliZWREYXRhO1xuICAgIH0gPSB7fTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBASW5qZWN0KFZvcnRleFNlcnZpY2UpIHByaXZhdGUgdm9ydGV4U2VydmljZSxcbiAgICAgICAgQEluamVjdChWb3J0ZXhTdGF0dXNTZXJ2aWNlKSBwcml2YXRlIHZvcnRleFN0YXR1c1NlcnZpY2UsXG4gICAgICAgIEBJbmplY3QoVHVwbGVEYXRhT2JzZXJ2YWJsZU5hbWVTZXJ2aWNlKSBwcml2YXRlIHR1cGxlRGF0YU9ic2VydmFibGVOYW1lLFxuICAgICAgICBASW5qZWN0KFR1cGxlT2ZmbGluZVN0b3JhZ2VTZXJ2aWNlKSBwcml2YXRlIHR1cGxlT2ZmbGluZVN0b3JhZ2VTZXJ2aWNlLFxuICAgICkge1xuICAgICAgICBzdXBlcigpO1xuXG4gICAgICAgIHRoaXMuZmlsdCA9IE9iamVjdC5hc3NpZ24oXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogdHVwbGVEYXRhT2JzZXJ2YWJsZU5hbWUubmFtZSxcbiAgICAgICAgICAgICAgICBrZXk6IFwidHVwbGVEYXRhT2JzZXJ2YWJsZVwiLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHR1cGxlRGF0YU9ic2VydmFibGVOYW1lLmFkZGl0aW9uYWxGaWx0LFxuICAgICAgICApO1xuXG4gICAgICAgIHRoaXMuZW5kcG9pbnQgPSBuZXcgUGF5bG9hZEVuZHBvaW50KHRoaXMsIHRoaXMuZmlsdCk7XG4gICAgICAgIHRoaXMuZW5kcG9pbnQub2JzZXJ2YWJsZS5zdWJzY3JpYmUoXG4gICAgICAgICAgICAocGF5bG9hZEVudmVsb3BlOiBQYXlsb2FkRW52ZWxvcGUpID0+IHtcbiAgICAgICAgICAgICAgICBwYXlsb2FkRW52ZWxvcGVcbiAgICAgICAgICAgICAgICAgICAgLmRlY29kZVBheWxvYWQoKVxuICAgICAgICAgICAgICAgICAgICAudGhlbigocGF5bG9hZDogUGF5bG9hZCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWNlaXZlUGF5bG9hZChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXlsb2FkRW52ZWxvcGUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF5bG9hZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXlsb2FkRW52ZWxvcGUuZW5jb2RlZFBheWxvYWQsXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAuY2F0Y2goKGUpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gVHVwbGVEYXRhT2ZmbGluZU9ic2VydmVyU2VydmljZTpFcnJvciBkZWNvZGluZyBwYXlsb2FkICR7ZX1gLFxuICAgICAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICApO1xuXG4gICAgICAgIHZvcnRleFN0YXR1c1NlcnZpY2UuaXNPbmxpbmVcbiAgICAgICAgICAgIC5waXBlKFxuICAgICAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLm9uRGVzdHJveUV2ZW50KSxcbiAgICAgICAgICAgICAgICBmaWx0ZXIoKG9ubGluZSkgPT4gb25saW5lID09PSB0cnVlKSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKG9ubGluZSkgPT4gdGhpcy52b3J0ZXhPbmxpbmVDaGFuZ2VkKCkpO1xuXG4gICAgICAgIC8vIENsZWFudXAgZGVhZCBzdWJzY3JpYmVycyBldmVyeSAzMCBzZWNvbmRzLlxuICAgICAgICBsZXQgY2xlYW51cFRpbWVyID0gc2V0SW50ZXJ2YWwoKCkgPT4gdGhpcy5jbGVhbnVwRGVhZENhY2hlcygpLCAyMDAwKTtcbiAgICAgICAgdGhpcy5vbkRlc3Ryb3lFdmVudC5zdWJzY3JpYmUoKCkgPT4gY2xlYXJJbnRlcnZhbChjbGVhbnVwVGltZXIpKTtcbiAgICB9XG5cbiAgICBfbmFtZVNlcnZpY2UoKTogVHVwbGVEYXRhT2JzZXJ2YWJsZU5hbWVTZXJ2aWNlIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudHVwbGVEYXRhT2JzZXJ2YWJsZU5hbWU7XG4gICAgfVxuXG4gICAgcG9sbEZvclR1cGxlcyhcbiAgICAgICAgdHVwbGVTZWxlY3RvcjogVHVwbGVTZWxlY3RvcixcbiAgICAgICAgdXNlQ2FjaGU6IGJvb2xlYW4gPSB0cnVlLFxuICAgICk6IFByb21pc2U8VHVwbGVbXT4ge1xuICAgICAgICAvLyAtLS0gSWYgdGhlIGRhdGEgZXhpc3RzIGluIHRoZSBjYWNoZSwgdGhlbiByZXR1cm4gaXRcbiAgICAgICAgbGV0IHRzU3RyID0gdHVwbGVTZWxlY3Rvci50b09yZGVyZWRKc29uU3RyKCk7XG5cbiAgICAgICAgaWYgKHVzZUNhY2hlICYmIHRoaXMuY2FjaGVCeVR1cGxlU2VsZWN0b3IuaGFzT3duUHJvcGVydHkodHNTdHIpKSB7XG4gICAgICAgICAgICBsZXQgY2FjaGVkRGF0YSA9IHRoaXMuY2FjaGVCeVR1cGxlU2VsZWN0b3JbdHNTdHJdO1xuICAgICAgICAgICAgY2FjaGVkRGF0YS5yZXNldFRlYXJEb3duKCk7XG5cbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBjYWNoZWREYXRhLmNhY2hlRW5hYmxlZCAmJlxuICAgICAgICAgICAgICAgIGNhY2hlZERhdGEubGFzdFNlcnZlclBheWxvYWREYXRlICE9IG51bGxcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoY2FjaGVkRGF0YS50dXBsZXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gLS0tIEVsc2UsIHdlIHdhbnQgdGhlIGRhdGEgZnJvbSB0aGUgc2VydmVyXG4gICAgICAgIC8vIFRoZSBQYXlsb2FkRW5kcG9pbnQgZm9yIHRoaXMgb2JzZXJ2YWJsZSBzaG91bGQgYWxzbyBwaWNrdXAgYW5kIHByb2Nlc3NcbiAgICAgICAgLy8gdGhlIHJlc3BvbnNlLiBTbyB0aGF0IHdpbGwgdGFrZSBjYXJlIG9mIHRoZSBjYWNoZSB1cGRhdGUgYW5kIG5vdGlmeSBvZlxuICAgICAgICAvLyBzdWJzY3JpYmVycy5cblxuICAgICAgICBsZXQgc3RhcnRGaWx0ID0gT2JqZWN0LmFzc2lnbihcbiAgICAgICAgICAgIHsgc3Vic2NyaWJlOiBmYWxzZSwgdXNlQ2FjaGU6IHVzZUNhY2hlIH0sXG4gICAgICAgICAgICB0aGlzLmZpbHQsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdHVwbGVTZWxlY3RvcjogdHVwbGVTZWxlY3RvcixcbiAgICAgICAgICAgIH0sXG4gICAgICAgICk7XG5cbiAgICAgICAgLy8gT3B0aW9uYWxseSB0eXBlZCwgTm8gbmVlZCB0byB3b3JyeSBhYm91dCB0aGUgZmFjdCB0aGF0IHdlIGNvbnZlcnQgdGhpc1xuICAgICAgICAvLyBhbmQgdGhlbiBUeXBlU2NyaXB0IGRvZXNuJ3QgcmVjb2duaXNlIHRoYXQgZGF0YSB0eXBlIGNoYW5nZVxuICAgICAgICBsZXQgcHJvbWlzZTogYW55ID0gbmV3IFBheWxvYWQoc3RhcnRGaWx0KS5tYWtlUGF5bG9hZEVudmVsb3BlKCk7XG5cbiAgICAgICAgcHJvbWlzZSA9IHByb21pc2UudGhlbigocGF5bG9hZEVudmVsb3BlOiBQYXlsb2FkRW52ZWxvcGUpID0+IHtcbiAgICAgICAgICAgIHJldHVybiBuZXcgUGF5bG9hZFJlc3BvbnNlKHRoaXMudm9ydGV4U2VydmljZSwgcGF5bG9hZEVudmVsb3BlKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcHJvbWlzZSA9IHByb21pc2UudGhlbigocGF5bG9hZEVudmVsb3BlOiBQYXlsb2FkRW52ZWxvcGUpID0+IHtcbiAgICAgICAgICAgIHJldHVybiBwYXlsb2FkRW52ZWxvcGUuZGVjb2RlUGF5bG9hZCgpO1xuICAgICAgICB9KTtcblxuICAgICAgICBwcm9taXNlID0gcHJvbWlzZS50aGVuKChwYXlsb2FkOiBQYXlsb2FkKSA9PiBwYXlsb2FkLnR1cGxlcyk7XG5cbiAgICAgICAgcmV0dXJuIHByb21pc2U7XG4gICAgfVxuXG4gICAgLyoqIEZsdXNoIENhY2hlXG4gICAgICpcbiAgICAgKiBUaGUgRGF0YSBPZmZpbmUgT2JzZXJ2ZXIgY2FuIGJlIHVzZWQgdG8gb2ZmbGluZSBjYWNoZSBkYXRhIGJ5IG9ic2VydmluZyBhIGxhcmdlXG4gICAgICogYW1vdW50cyBvZiBkYXRhLCBtb3JlIGRhdGEgdGhlbiB0aGUgdXNlciB3b3VsZCBub3JtYWxseSBsb29rIGF0LlxuICAgICAqXG4gICAgICogSWYgaXQncyBiZWluZyB1c2VkIGxpa2UgdGhpcyB0aGVuIHRoZSBjYWNoZSBzaG91bGQgYmUgZmx1c2hlZCBkdXJpbmcgdGhlIHByb2Nlc3NcbiAgICAgKiB0byBlbnN1cmUgaXQncyBub3QgYWxsIGJlaW5nIGtlcHQgaW4gbWVtb3J5LlxuICAgICAqXG4gICAgICogQHBhcmFtIHtUdXBsZVNlbGVjdG9yfSB0dXBsZVNlbGVjdG9yIFRoZSB0dXBsZSBzZWxlY3RvciB0byBmbHVzaCB0aGUgY2FjaGUgZm9yXG4gICAgICovXG4gICAgZmx1c2hDYWNoZSh0dXBsZVNlbGVjdG9yOiBUdXBsZVNlbGVjdG9yKTogdm9pZCB7XG4gICAgICAgIGxldCB0c1N0ciA9IHR1cGxlU2VsZWN0b3IudG9PcmRlcmVkSnNvblN0cigpO1xuICAgICAgICBpZiAodGhpcy5jYWNoZUJ5VHVwbGVTZWxlY3Rvci5oYXNPd25Qcm9wZXJ0eSh0c1N0cikpIHtcbiAgICAgICAgICAgIGxldCBjYWNoZWREYXRhID0gdGhpcy5jYWNoZUJ5VHVwbGVTZWxlY3Rvclt0c1N0cl07XG4gICAgICAgICAgICBjYWNoZWREYXRhLmZsdXNoKCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5jbGVhbnVwRGVhZENhY2hlcygpO1xuICAgIH1cblxuICAgIC8qKiBQcm9taXNlIGZyb20gdG8gVHVwbGUgU2VsZWN0b3JcbiAgICAgKlxuICAgICAqIFNlZSB0aGUgc3Vic2NyaWJlVG9UdXBsZVNlbGVjdG9yIG1ldGhvZCBmb3IgbW9yZSBkZXRhaWxzLlxuICAgICAqIFRoZSBwcm9taXNlIHdpbGwgZmlyZSBvbiB0aGUgZmlyc3QgZW1pdCBvZiBkYXRhLlxuICAgICAqXG4gICAgICogRG8gbm90IHVzZSB0aGlzIHdoZW4gdGhlcmUgd2lsbCBiZSBubyBkYXRhIHByZXNlbnQsXG4gICAgICogb3IgaXQgbWF5IGNhdXNlIGEgbWVtb3J5IGxlYWsuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge1R1cGxlU2VsZWN0b3J9IHR1cGxlU2VsZWN0b3JcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGRpc2FibGVDYWNoZVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gZGlzYWJsZVN0b3JhZ2VcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGRpc2FibGVBc2tTZXJ2ZXIgVXNlIHRoaXMgdG8gc3RvcmUgYW5kIG9ic2VydmUgZGF0YSBjb21wbGV0ZWx5XG4gICAgICogICAgICB3aXRoaW4gdGhlIGFuZ3VsYXIgYXBwLlxuICAgICAqIEByZXR1cm5zIHtTdWJqZWN0PFR1cGxlW10+fVxuICAgICAqL1xuICAgIHByb21pc2VGcm9tVHVwbGVTZWxlY3RvcihcbiAgICAgICAgdHVwbGVTZWxlY3RvcjogVHVwbGVTZWxlY3RvcixcbiAgICAgICAgZGlzYWJsZUNhY2hlOiBib29sZWFuID0gZmFsc2UsXG4gICAgICAgIGRpc2FibGVTdG9yYWdlOiBib29sZWFuID0gZmFsc2UsXG4gICAgICAgIGRpc2FibGVBc2tTZXJ2ZXI6IGJvb2xlYW4gPSBmYWxzZSxcbiAgICApOiBQcm9taXNlPFR1cGxlW10+IHtcbiAgICAgICAgcmV0dXJuIGZpcnN0VmFsdWVGcm9tKFxuICAgICAgICAgICAgdGhpcy5zdWJzY3JpYmVUb1R1cGxlU2VsZWN0b3IoXG4gICAgICAgICAgICAgICAgdHVwbGVTZWxlY3RvcixcbiAgICAgICAgICAgICAgICBkaXNhYmxlQ2FjaGUsXG4gICAgICAgICAgICAgICAgZGlzYWJsZVN0b3JhZ2UsXG4gICAgICAgICAgICAgICAgZGlzYWJsZUFza1NlcnZlcixcbiAgICAgICAgICAgICksXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgLyoqIFN1YnNjcmliZSB0byBUdXBsZSBTZWxlY3RvclxuICAgICAqXG4gICAgICogR2V0IGFuIG9ic2VydmFibGUgdGhhdCB3aWxsIGJlIGZpcmVkIHdoZW4gYW55IG5ldyBkYXRhIHVwZGF0ZXMgYXJlIGF2YWlsYWJsZVxuICAgICAqIERhdGEgaXMgbG9hZGVkIGZyb20gdGhlIGxvY2FsIGRiIGNhY2hlLCB3aGlsZSBpdCB3YWl0cyBmb3IgdGhlIHNlcnZlciB0byByZXNwb25kLlxuICAgICAqICogZWl0aGVyIGZyb20gdGhlIHNlcnZlciwgb3IgaWYgdGhleSBhcmUgbG9jYWxseSB1cGRhdGVkIHdpdGggdXBkYXRlT2ZmbGluZVN0YXRlKClcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7VHVwbGVTZWxlY3Rvcn0gdHVwbGVTZWxlY3RvclxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gZGlzYWJsZUNhY2hlXG4gICAgICogQHBhcmFtIHtib29sZWFufSBkaXNhYmxlU3RvcmFnZVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gZGlzYWJsZUFza1NlcnZlciBVc2UgdGhpcyB0byBzdG9yZSBhbmQgb2JzZXJ2ZSBkYXRhIGNvbXBsZXRlbHlcbiAgICAgKiAgICAgIHdpdGhpbiB0aGUgYW5ndWxhciBhcHAuXG4gICAgICogQHJldHVybnMge1N1YmplY3Q8VHVwbGVbXT59XG4gICAgICovXG4gICAgc3Vic2NyaWJlVG9UdXBsZVNlbGVjdG9yKFxuICAgICAgICB0dXBsZVNlbGVjdG9yOiBUdXBsZVNlbGVjdG9yLFxuICAgICAgICBkaXNhYmxlQ2FjaGU6IGJvb2xlYW4gPSBmYWxzZSxcbiAgICAgICAgZGlzYWJsZVN0b3JhZ2U6IGJvb2xlYW4gPSBmYWxzZSxcbiAgICAgICAgZGlzYWJsZUFza1NlcnZlcjogYm9vbGVhbiA9IGZhbHNlLFxuICAgICk6IE9ic2VydmFibGU8VHVwbGVbXT4ge1xuICAgICAgICBsZXQgdHNTdHIgPSB0dXBsZVNlbGVjdG9yLnRvT3JkZXJlZEpzb25TdHIoKTtcbiAgICAgICAgbGV0IGNhY2hlZERhdGE6IENhY2hlZFN1YnNjcmliZWREYXRhIHwgbnVsbCA9IG51bGw7XG5cbiAgICAgICAgLy8gSWYgdGhlIGNhY2hlIGV4aXN0cywgdXNlIGl0XG4gICAgICAgIGlmICh0aGlzLmNhY2hlQnlUdXBsZVNlbGVjdG9yLmhhc093blByb3BlcnR5KHRzU3RyKSkge1xuICAgICAgICAgICAgY2FjaGVkRGF0YSA9IHRoaXMuY2FjaGVCeVR1cGxlU2VsZWN0b3JbdHNTdHJdO1xuICAgICAgICAgICAgLy8gTWF5YmUgZGlzYWJsZSBjYWNoZVxuICAgICAgICAgICAgY2FjaGVkRGF0YS5jYWNoZUVuYWJsZWQgPSBjYWNoZWREYXRhLmNhY2hlRW5hYmxlZCAmJiAhZGlzYWJsZUNhY2hlO1xuICAgICAgICAgICAgLy8gTWF5YmUgZGlzYWJsZSBzdG9yYWdlXG4gICAgICAgICAgICBjYWNoZWREYXRhLnN0b3JhZ2VFbmFibGVkID1cbiAgICAgICAgICAgICAgICBjYWNoZWREYXRhLnN0b3JhZ2VFbmFibGVkICYmICFkaXNhYmxlU3RvcmFnZTtcbiAgICAgICAgICAgIC8vIE1heWJlIGRpc2FibGUgYXNrIHNlcnZlclxuICAgICAgICAgICAgY2FjaGVkRGF0YS5hc2tTZXJ2ZXJFbmFibGVkID1cbiAgICAgICAgICAgICAgICBjYWNoZWREYXRhLmFza1NlcnZlckVuYWJsZWQgJiYgIWRpc2FibGVBc2tTZXJ2ZXI7XG5cbiAgICAgICAgICAgIC8vIElmIHRoZSBjYWNoZSBpcyBlbmFibGVkLCBhbmQgd2UgaGF2ZSB0dXBsZSBkYXRhLCB0aGVuIG5vdGlmeVxuICAgICAgICAgICAgaWYgKGNhY2hlZERhdGEuY2FjaGVFbmFibGVkICYmIGNhY2hlZERhdGEudHVwbGVzICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gY2FjaGVkRGF0YS5vYnNlcnZhYmxlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBFTFNFLCBDcmVhdGUgdGhlI