@spartacus/core
Version:
Spartacus - the core framework
411 lines • 60 kB
JavaScript
import { Injectable } from '@angular/core';
import { select } from '@ngrx/store';
import { combineLatest, EMPTY, of, Subscription, timer, using, } from 'rxjs';
import { debounce, distinctUntilChanged, filter, map, pairwise, shareReplay, switchMap, switchMapTo, take, tap, withLatestFrom, } from 'rxjs/operators';
import { OCC_CART_ID_CURRENT, OCC_USER_ID_ANONYMOUS, OCC_USER_ID_GUEST, } from '../../occ/utils/occ-constants';
import { EMAIL_PATTERN } from '../../util/regex-pattern';
import { activeCartInitialState } from '../store/reducers/multi-cart.reducer';
import { MultiCartSelectors } from '../store/selectors/index';
import { getCartIdByUserId, isTempCartId } from '../utils/utils';
import * as i0 from "@angular/core";
import * as i1 from "@ngrx/store";
import * as i2 from "./multi-cart.service";
import * as i3 from "../../auth/index";
export class ActiveCartService {
constructor(store, multiCartService, userIdService) {
this.store = store;
this.multiCartService = multiCartService;
this.userIdService = userIdService;
this.subscription = new Subscription();
// This stream is used for referencing carts in API calls.
this.activeCartId$ = this.userIdService.getUserId().pipe(
// We want to wait with initialization of cartId until we have userId initialized
// We have take(1) to not trigger this stream, when userId changes.
take(1), switchMapTo(this.store), select(MultiCartSelectors.getActiveCartId),
// We also wait until we initialize cart from localStorage. Before that happens cartId in store === null
filter((cartId) => cartId !== activeCartInitialState), map((cartId) => {
if (cartId === '') {
// We fallback to current when we don't have particular cart id -> cartId === '', because that's how you reference latest user cart.
return OCC_CART_ID_CURRENT;
}
return cartId;
}));
// Stream with active cart entity
this.cartSelector$ = this.activeCartId$.pipe(switchMap((cartId) => this.multiCartService.getCartEntity(cartId)));
this.initActiveCart();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
initActiveCart() {
// Any change of user id is also interesting for us, because we have to merge/load/switch cart in those cases.
this.subscription.add(this.userIdService
.getUserId()
.pipe(
// We never trigger cart merge/load on app initialization here and that's why we wait with pairwise for a change of userId (not initialization).
pairwise(), switchMap(([previousUserId, userId]) =>
// We need cartId once we have the previous and current userId. We don't want to subscribe to cartId stream before.
combineLatest([
of(previousUserId),
of(userId),
this.activeCartId$,
]).pipe(take(1))))
.subscribe(([previousUserId, userId, cartId]) => {
// Only change of user and not a logout (current user id !== anonymous) should trigger loading mechanism
if (this.isJustLoggedIn(userId, previousUserId)) {
this.loadOrMerge(cartId, userId, previousUserId);
}
}));
// Stream for getting the cart value
const activeCartValue$ = this.cartSelector$.pipe(map((cartEntity) => {
return {
cart: cartEntity.value,
isStable: !cartEntity.loading && cartEntity.processesCount === 0,
loaded: (cartEntity.error || cartEntity.success) && !cartEntity.loading,
};
}),
// we want to emit empty carts even if those are not stable
// on merge cart action we want to switch to empty cart so no one would use old cartId which can be already obsolete
// so on merge action the resulting stream looks like this: old_cart -> {} -> new_cart
filter(({ isStable, cart }) => isStable || this.isEmpty(cart)));
// Responsible for loading cart when it's not (eg. app initialization when we have only cart id)
const activeCartLoading$ = activeCartValue$.pipe(withLatestFrom(this.activeCartId$, this.userIdService.getUserId()), tap(([{ cart, loaded, isStable }, cartId, userId]) => {
if (isStable &&
this.isEmpty(cart) &&
!loaded &&
!isTempCartId(cartId)) {
this.load(cartId, userId);
}
}));
this.activeCart$ = using(() => activeCartLoading$.subscribe(), () => activeCartValue$).pipe(
// Normalization for empty cart value. It will always be returned as empty object.
map(({ cart }) => (cart ? cart : {})), distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: true }));
}
/**
* Returns active cart
*/
getActive() {
return this.activeCart$;
}
/**
* Waits for the cart to be stable before returning the active cart.
*/
takeActive() {
return this.isStable().pipe(filter((isStable) => isStable), switchMap(() => this.getActive()), filter((cart) => !!cart), take(1));
}
/**
* Returns active cart id
*/
getActiveCartId() {
return this.activeCart$.pipe(withLatestFrom(this.userIdService.getUserId()), map(([cart, userId]) => getCartIdByUserId(cart, userId)), distinctUntilChanged());
}
/**
* Returns cart entries
*/
getEntries() {
return this.activeCartId$.pipe(switchMap((cartId) => this.multiCartService.getEntries(cartId)), distinctUntilChanged());
}
/**
* Returns last cart entry for provided product code.
* Needed to cover processes where multiple entries can share the same product code
* (e.g. promotions or configurable products)
*
* @param productCode
*/
getLastEntry(productCode) {
return this.activeCartId$.pipe(switchMap((cartId) => this.multiCartService.getLastEntry(cartId, productCode)), distinctUntilChanged());
}
/**
* Returns cart loading state
*/
getLoading() {
return this.cartSelector$.pipe(map((cartEntity) => cartEntity.loading), distinctUntilChanged());
}
/**
* Returns true when cart is stable (not loading and not pending processes on cart)
*/
isStable() {
// Debounce is used here, to avoid flickering when we switch between different cart entities.
// For example during `addEntry` method. We might try to load current cart, so `current cart will be then active id.
// After load fails we might create new cart so we switch to `temp-${uuid}` cart entity used when creating cart.
// At the end we finally switch to cart `code` for cart id. Between those switches cart `isStable` function should not flicker.
return this.activeCartId$.pipe(switchMap((cartId) => this.multiCartService.isStable(cartId)), debounce((state) => (state ? timer(0) : EMPTY)), distinctUntilChanged());
}
/**
* Loads cart or upon login, whenever there's an existing cart, merge it into the current user cart
* cartId will be defined (not '', null, undefined)
*/
loadOrMerge(cartId, userId, previousUserId) {
if (cartId === OCC_CART_ID_CURRENT) {
this.multiCartService.loadCart({
userId,
cartId: OCC_CART_ID_CURRENT,
extraData: {
active: true,
},
});
}
else if (this.isGuestCart()) {
this.guestCartMerge(cartId);
}
else if (userId !== previousUserId &&
userId !== OCC_USER_ID_ANONYMOUS &&
previousUserId !== OCC_USER_ID_ANONYMOUS) {
// This case covers the case when you are logged in and then asm user logs in and you don't want to merge, but only load emulated user cart
// Similarly when you are logged in as asm user and you logout and want to resume previous user session
this.multiCartService.loadCart({
userId,
cartId,
extraData: {
active: true,
},
});
}
else {
// We have particular cart locally, but we logged in, so we need to combine this with current cart or make it ours.
this.multiCartService.mergeToCurrentCart({
userId,
cartId,
extraData: {
active: true,
},
});
}
}
/**
* Loads cart in every case apart from anonymous user and current cart combination
*/
load(cartId, userId) {
if (!(userId === OCC_USER_ID_ANONYMOUS && cartId === OCC_CART_ID_CURRENT)) {
this.multiCartService.loadCart({
userId,
cartId,
extraData: {
active: true,
},
});
}
}
/**
* Adds entries from guest cart to user cart
*/
addEntriesGuestMerge(cartEntries) {
const entriesToAdd = cartEntries.map((entry) => ({
productCode: entry.product.code,
quantity: entry.quantity,
}));
this.requireLoadedCartForGuestMerge()
.pipe(withLatestFrom(this.userIdService.getUserId()))
.subscribe(([cartState, userId]) => {
this.multiCartService.addEntries(userId, getCartIdByUserId(cartState.value, userId), entriesToAdd);
});
}
/**
* Helper method for requiring loaded cart that is not a guest cart (guest cart is filtered out).
* Used when merging guest cart with user cart.
*/
requireLoadedCartForGuestMerge() {
return this.requireLoadedCart(this.cartSelector$.pipe(filter(() => !this.isGuestCart())));
}
isCartCreating(cartState, cartId) {
// cart creating is always represented with loading flags
// when all loading flags are false it means that we restored wrong cart id
// could happen on context change or reload right in the middle on cart create call
return (isTempCartId(cartId) &&
(cartState.loading || cartState.success || cartState.error));
}
requireLoadedCart(customCartSelector$) {
// For guest cart merge we want to filter guest cart in the whole stream
// We have to wait with load/create/addEntry after guest cart will be deleted.
// That's why you can provide custom selector with this filter applied.
const cartSelector$ = customCartSelector$
? customCartSelector$
: this.cartSelector$;
return cartSelector$.pipe(filter((cartState) => !cartState.loading),
// Avoid load/create call when there are new cart creating at the moment
withLatestFrom(this.activeCartId$), filter(([cartState, cartId]) => !this.isCartCreating(cartState, cartId)), map(([cartState]) => cartState), take(1), withLatestFrom(this.userIdService.getUserId()), tap(([cartState, userId]) => {
// Try to load the cart, because it might have been created on another device between our login and add entry call
if (this.isEmpty(cartState.value) && userId !== OCC_USER_ID_ANONYMOUS) {
this.load(OCC_CART_ID_CURRENT, userId);
}
}), switchMap(() => {
return cartSelector$;
}), filter((cartState) => !cartState.loading),
// create cart can happen to anonymous user if it is not empty or to any other user if it is loaded and empty
withLatestFrom(this.userIdService.getUserId()), filter(([cartState, userId]) => userId === OCC_USER_ID_ANONYMOUS ||
cartState.success ||
cartState.error), take(1), tap(([cartState, userId]) => {
if (this.isEmpty(cartState.value)) {
this.multiCartService.createCart({
userId,
extraData: {
active: true,
},
});
}
}), switchMap(() => {
return cartSelector$;
}), filter((cartState) => !cartState.loading), filter((cartState) => cartState.success || cartState.error),
// wait for active cart id to point to code/guid to avoid some work on temp cart entity
withLatestFrom(this.activeCartId$), filter(([cartState, cartId]) => !this.isCartCreating(cartState, cartId)), map(([cartState]) => cartState), filter((cartState) => !this.isEmpty(cartState.value)), take(1));
}
/**
* Add entry to active cart
*
* @param productCode
* @param quantity
*/
addEntry(productCode, quantity) {
// TODO(#13645): Support multiple, simultaneous invocation of this function, when cart is not loaded/created
this.requireLoadedCart()
.pipe(withLatestFrom(this.userIdService.getUserId()))
.subscribe(([cartState, userId]) => {
this.multiCartService.addEntry(userId, getCartIdByUserId(cartState.value, userId), productCode, quantity);
});
}
/**
* Remove entry
*
* @param entry
*/
removeEntry(entry) {
this.activeCartId$
.pipe(withLatestFrom(this.userIdService.getUserId()), take(1))
.subscribe(([cartId, userId]) => {
this.multiCartService.removeEntry(userId, cartId, entry.entryNumber);
});
}
/**
* Update entry
*
* @param entryNumber
* @param quantity
*/
updateEntry(entryNumber, quantity) {
this.activeCartId$
.pipe(withLatestFrom(this.userIdService.getUserId()), take(1))
.subscribe(([cartId, userId]) => {
this.multiCartService.updateEntry(userId, cartId, entryNumber, quantity);
});
}
/**
* Returns cart entry
*
* @param productCode
*/
getEntry(productCode) {
return this.activeCartId$.pipe(switchMap((cartId) => this.multiCartService.getEntry(cartId, productCode)), distinctUntilChanged());
}
/**
* Assign email to cart
*
* @param email
*/
addEmail(email) {
this.activeCartId$
.pipe(withLatestFrom(this.userIdService.getUserId()), take(1))
.subscribe(([cartId, userId]) => {
this.multiCartService.assignEmail(cartId, userId, email);
});
}
/**
* Get assigned user to cart
*/
getAssignedUser() {
return this.getActive().pipe(map((cart) => cart.user));
}
// TODO: Make cart required param in 4.0 and drop the subscribe/unsubscribe.
/**
* Returns true for guest cart
*/
isGuestCart(cart) {
if (!cart) {
this.activeCart$
.subscribe((activeCart) => (cart = activeCart))
.unsubscribe();
}
const cartUser = cart === null || cart === void 0 ? void 0 : cart.user;
return (cartUser &&
(cartUser.name === OCC_USER_ID_GUEST ||
this.isEmail(cartUser.uid.split('|').slice(1).join('|'))));
}
/**
* Add multiple entries to a cart
*
* @param cartEntries : list of entries to add (OrderEntry[])
*/
addEntries(cartEntries) {
const entriesToAdd = cartEntries.map((entry) => {
var _a;
return ({
productCode: (_a = entry.product) === null || _a === void 0 ? void 0 : _a.code,
quantity: entry.quantity,
});
});
this.requireLoadedCart()
.pipe(withLatestFrom(this.userIdService.getUserId()))
.subscribe(([cartState, userId]) => {
if (cartState.value) {
this.multiCartService.addEntries(userId, getCartIdByUserId(cartState.value, userId), entriesToAdd);
}
});
}
/**
* Indicates if given string is matching email pattern
*/
isEmail(str) {
if (str) {
return str.match(EMAIL_PATTERN) ? true : false;
}
return false;
}
// TODO: Remove once backend is updated
/**
* Temporary method to merge guest cart with user cart because of backend limitation
* This is for an edge case
*/
guestCartMerge(cartId) {
let cartEntries;
this.getEntries()
.pipe(take(1))
.subscribe((entries) => {
cartEntries = entries;
this.multiCartService.deleteCart(cartId, OCC_USER_ID_ANONYMOUS);
this.addEntriesGuestMerge(cartEntries);
});
}
/**
* Indicates if given cart is empty.
* Returns true is cart is undefined, null or is an empty object.
*/
isEmpty(cart) {
return (!cart || (typeof cart === 'object' && Object.keys(cart).length === 0));
}
/**
* Indicates if a given user is logged in on account different than preceding user account
*/
isJustLoggedIn(userId, previousUserId) {
return (userId !== OCC_USER_ID_ANONYMOUS && // not logged out
previousUserId !== userId // *just* logged in / switched to ASM emulation
);
}
/**
* Reloads active cart
*/
reloadActiveCart() {
combineLatest([this.getActiveCartId(), this.userIdService.takeUserId()])
.pipe(take(1), map(([cartId, userId]) => {
this.multiCartService.loadCart({ cartId, userId });
}))
.subscribe();
}
}
ActiveCartService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: ActiveCartService, deps: [{ token: i1.Store }, { token: i2.MultiCartService }, { token: i3.UserIdService }], target: i0.ɵɵFactoryTarget.Injectable });
ActiveCartService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: ActiveCartService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: ActiveCartService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: function () { return [{ type: i1.Store }, { type: i2.MultiCartService }, { type: i3.UserIdService }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aXZlLWNhcnQuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2NvcmUvc3JjL2NhcnQvZmFjYWRlL2FjdGl2ZS1jYXJ0LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBYSxNQUFNLGVBQWUsQ0FBQztBQUN0RCxPQUFPLEVBQUUsTUFBTSxFQUFTLE1BQU0sYUFBYSxDQUFDO0FBQzVDLE9BQU8sRUFDTCxhQUFhLEVBQ2IsS0FBSyxFQUVMLEVBQUUsRUFDRixZQUFZLEVBQ1osS0FBSyxFQUNMLEtBQUssR0FDTixNQUFNLE1BQU0sQ0FBQztBQUNkLE9BQU8sRUFDTCxRQUFRLEVBQ1Isb0JBQW9CLEVBQ3BCLE1BQU0sRUFDTixHQUFHLEVBQ0gsUUFBUSxFQUNSLFdBQVcsRUFDWCxTQUFTLEVBQ1QsV0FBVyxFQUNYLElBQUksRUFDSixHQUFHLEVBQ0gsY0FBYyxHQUNmLE1BQU0sZ0JBQWdCLENBQUM7QUFLeEIsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixxQkFBcUIsRUFDckIsaUJBQWlCLEdBQ2xCLE1BQU0sK0JBQStCLENBQUM7QUFFdkMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRXpELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQzlFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzlELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxZQUFZLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7Ozs7QUFNakUsTUFBTSxPQUFPLGlCQUFpQjtJQTJCNUIsWUFDWSxLQUFnQyxFQUNoQyxnQkFBa0MsRUFDbEMsYUFBNEI7UUFGNUIsVUFBSyxHQUFMLEtBQUssQ0FBMkI7UUFDaEMscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUNsQyxrQkFBYSxHQUFiLGFBQWEsQ0FB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QUksQ0FBQyxvQkFBb0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN6QyxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7O09BR0c7SUFDTyxPQUFPLENBQUMsSUFBVTtRQUMxQixPQUFPLENBQ0wsQ0FBQyxJQUFJLElBQUksQ0FBQyxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLENBQ3RFLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDTyxjQUFjLENBQUMsTUFBYyxFQUFFLGNBQXNCO1FBQzdELE9BQU8sQ0FDTCxNQUFNLEtBQUsscUJBQXFCLElBQUksaUJBQWlCO1lBQ3JELGNBQWMsS0FBSyxNQUFNLENBQUMsK0NBQStDO1NBQzFFLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSCxnQkFBZ0I7UUFDZCxhQUFhLENBQUMsQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO2FBQ3JFLElBQUksQ0FDSCxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQ1AsR0FBRyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEVBQUUsRUFBRTtZQUN2QixJQUFJLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDckQsQ0FBQyxDQUFDLENBQ0g7YUFDQSxTQUFTLEVBQUUsQ0FBQztJQUNqQixDQUFDOzs4R0EvaEJVLGlCQUFpQjtrSEFBakIsaUJBQWlCLGNBRmhCLE1BQU07MkZBRVAsaUJBQWlCO2tCQUg3QixVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIE9uRGVzdHJveSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgc2VsZWN0LCBTdG9yZSB9IGZyb20gJ0BuZ3J4L3N0b3JlJztcbmltcG9ydCB7XG4gIGNvbWJpbmVMYXRlc3QsXG4gIEVNUFRZLFxuICBPYnNlcnZhYmxlLFxuICBvZixcbiAgU3Vic2NyaXB0aW9uLFxuICB0aW1lcixcbiAgdXNpbmcsXG59IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtcbiAgZGVib3VuY2UsXG4gIGRpc3RpbmN0VW50aWxDaGFuZ2VkLFxuICBmaWx0ZXIsXG4gIG1hcCxcbiAgcGFpcndpc2UsXG4gIHNoYXJlUmVwbGF5LFxuICBzd2l0Y2hNYXAsXG4gIHN3aXRjaE1hcFRvLFxuICB0YWtlLFxuICB0YXAsXG4gIHdpdGhMYXRlc3RGcm9tLFxufSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBVc2VySWRTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vYXV0aC9pbmRleCc7XG5pbXBvcnQgeyBDYXJ0IH0gZnJvbSAnLi4vLi4vbW9kZWwvY2FydC5tb2RlbCc7XG5pbXBvcnQgeyBVc2VyIH0gZnJvbSAnLi4vLi4vbW9kZWwvbWlzYy5tb2RlbCc7XG5pbXBvcnQgeyBPcmRlckVudHJ5IH0gZnJvbSAnLi4vLi4vbW9kZWwvb3JkZXIubW9kZWwnO1xuaW1wb3J0IHtcbiAgT0NDX0NBUlRfSURfQ1VSUkVOVCxcbiAgT0NDX1VTRVJfSURfQU5PTllNT1VTLFxuICBPQ0NfVVNFUl9JRF9HVUVTVCxcbn0gZnJvbSAnLi4vLi4vb2NjL3V0aWxzL29jYy1jb25zdGFudHMnO1xuaW1wb3J0IHsgUHJvY2Vzc2VzTG9hZGVyU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS91dGlscy9wcm9jZXNzZXMtbG9hZGVyL3Byb2Nlc3Nlcy1sb2FkZXItc3RhdGUnO1xuaW1wb3J0IHsgRU1BSUxfUEFUVEVSTiB9IGZyb20gJy4uLy4uL3V0aWwvcmVnZXgtcGF0dGVybic7XG5pbXBvcnQgeyBTdGF0ZVdpdGhNdWx0aUNhcnQgfSBmcm9tICcuLi9zdG9yZS9tdWx0aS1jYXJ0LXN0YXRlJztcbmltcG9ydCB7IGFjdGl2ZUNhcnRJbml0aWFsU3RhdGUgfSBmcm9tICcuLi9zdG9yZS9yZWR1Y2Vycy9tdWx0aS1jYXJ0LnJlZHVjZXInO1xuaW1wb3J0IHsgTXVsdGlDYXJ0U2VsZWN0b3JzIH0gZnJvbSAnLi4vc3RvcmUvc2VsZWN0b3JzL2luZGV4JztcbmltcG9ydCB7IGdldENhcnRJZEJ5VXNlcklkLCBpc1RlbXBDYXJ0SWQgfSBmcm9tICcuLi91dGlscy91dGlscyc7XG5pbXBvcnQgeyBNdWx0aUNhcnRTZXJ2aWNlIH0gZnJvbSAnLi9tdWx0aS1jYXJ0LnNlcnZpY2UnO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290Jyxcbn0pXG5leHBvcnQgY2xhc3MgQWN0aXZlQ2FydFNlcnZpY2UgaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICBwcm90ZWN0ZWQgYWN0aXZlQ2FydCQ6IE9ic2VydmFibGU8Q2FydD47XG4gIHByb3RlY3RlZCBzdWJzY3JpcHRpb24gPSBuZXcgU3Vic2NyaXB0aW9uKCk7XG5cbiAgLy8gVGhpcyBzdHJlYW0gaXMgdXNlZCBmb3IgcmVmZXJlbmNpbmcgY2FydHMgaW4gQVBJIGNhbGxzLlxuICBwcm90ZWN0ZWQgYWN0aXZlQ2FydElkJCA9IHRoaXMudXNlcklkU2VydmljZS5nZXRVc2VySWQoKS5waXBlKFxuICAgIC8vIFdlIHdhbnQgdG8gd2FpdCB3aXRoIGluaXRpYWxpemF0aW9uIG9mIGNhcnRJZCB1bnRpbCB3ZSBoYXZlIHVzZXJJZCBpbml0aWFsaXplZFxuICAgIC8vIFdlIGhhdmUgdGFrZSgxKSB0byBub3QgdHJpZ2dlciB0aGlzIHN0cmVhbSwgd2hlbiB1c2VySWQgY2hhbmdlcy5cbiAgICB0YWtlKDEpLFxuICAgIHN3aXRjaE1hcFRvKHRoaXMuc3RvcmUpLFxuICAgIHNlbGVjdChNdWx0aUNhcnRTZWxlY3RvcnMuZ2V0QWN0aXZlQ2FydElkKSxcbiAgICAvLyBXZSBhbHNvIHdhaXQgdW50aWwgd2UgaW5pdGlhbGl6ZSBjYXJ0IGZyb20gbG9jYWxTdG9yYWdlLiBCZWZvcmUgdGhhdCBoYXBwZW5zIGNhcnRJZCBpbiBzdG9yZSA9PT0gbnVsbFxuICAgIGZpbHRlcigoY2FydElkKSA9PiBjYXJ0SWQgIT09IGFjdGl2ZUNhcnRJbml0aWFsU3RhdGUpLFxuICAgIG1hcCgoY2FydElkKSA9PiB7XG4gICAgICBpZiAoY2FydElkID09PSAnJykge1xuICAgICAgICAvLyBXZSBmYWxsYmFjayB0byBjdXJyZW50IHdoZW4gd2UgZG9uJ3QgaGF2ZSBwYXJ0aWN1bGFyIGNhcnQgaWQgLT4gY2FydElkID09PSAnJywgYmVjYXVzZSB0aGF0J3MgaG93IHlvdSByZWZlcmVuY2UgbGF0ZXN0IHVzZXIgY2FydC5cbiAgICAgICAgcmV0dXJuIE9DQ19DQVJUX0lEX0NVUlJFTlQ7XG4gICAgICB9XG4gICAgICByZXR1cm4gY2FydElkO1xuICAgIH0pXG4gICk7XG5cbiAgLy8gU3RyZWFtIHdpdGggYWN0aXZlIGNhcnQgZW50aXR5XG4gIHByb3RlY3RlZCBjYXJ0U2VsZWN0b3IkID0gdGhpcy5hY3RpdmVDYXJ0SWQkLnBpcGUoXG4gICAgc3dpdGNoTWFwKChjYXJ0SWQpID0+IHRoaXMubXVsdGlDYXJ0U2VydmljZS5nZXRDYXJ0RW50aXR5KGNhcnRJZCkpXG4gICk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJvdGVjdGVkIHN0b3JlOiBTdG9yZTxTdGF0ZVdpdGhNdWx0aUNhcnQ+LFxuICAgIHByb3RlY3RlZCBtdWx0aUNhcnRTZXJ2aWNlOiBNdWx0aUNhcnRTZXJ2aWNlLFxuICAgIHByb3RlY3RlZCB1c2VySWRTZXJ2aWNlOiBVc2VySWRTZXJ2aWNlXG4gICkge1xuICAgIHRoaXMuaW5pdEFjdGl2ZUNhcnQoKTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuc3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaW5pdEFjdGl2ZUNhcnQoKSB7XG4gICAgLy8gQW55IGNoYW5nZSBvZiB1c2VyIGlkIGlzIGFsc28gaW50ZXJlc3RpbmcgZm9yIHVzLCBiZWNhdXNlIHdlIGhhdmUgdG8gbWVyZ2UvbG9hZC9zd2l0Y2ggY2FydCBpbiB0aG9zZSBjYXNlcy5cbiAgICB0aGlzLnN1YnNjcmlwdGlvbi5hZGQoXG4gICAgICB0aGlzLnVzZXJJZFNlcnZpY2VcbiAgICAgICAgLmdldFVzZXJJZCgpXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIC8vIFdlIG5ldmVyIHRyaWdnZXIgY2FydCBtZXJnZS9sb2FkIG9uIGFwcCBpbml0aWFsaXphdGlvbiBoZXJlIGFuZCB0aGF0J3Mgd2h5IHdlIHdhaXQgd2l0aCBwYWlyd2lzZSBmb3IgYSBjaGFuZ2Ugb2YgdXNlcklkIChub3QgaW5pdGlhbGl6YXRpb24pLlxuICAgICAgICAgIHBhaXJ3aXNlKCksXG4gICAgICAgICAgc3dpdGNoTWFwKChbcHJldmlvdXNVc2VySWQsIHVzZXJJZF0pID0+XG4gICAgICAgICAgICAvLyBXZSBuZWVkIGNhcnRJZCBvbmNlIHdlIGhhdmUgdGhlIHByZXZpb3VzIGFuZCBjdXJyZW50IHVzZXJJZC4gV2UgZG9uJ3Qgd2FudCB0byBzdWJzY3JpYmUgdG8gY2FydElkIHN0cmVhbSBiZWZvcmUuXG4gICAgICAgICAgICBjb21iaW5lTGF0ZXN0KFtcbiAgICAgICAgICAgICAgb2YocHJldmlvdXNVc2VySWQpLFxuICAgICAgICAgICAgICBvZih1c2VySWQpLFxuICAgICAgICAgICAgICB0aGlzLmFjdGl2ZUNhcnRJZCQsXG4gICAgICAgICAgICBdKS5waXBlKHRha2UoMSkpXG4gICAgICAgICAgKVxuICAgICAgICApXG4gICAgICAgIC5zdWJzY3JpYmUoKFtwcmV2aW91c1VzZXJJZCwgdXNlcklkLCBjYXJ0SWRdKSA9PiB7XG4gICAgICAgICAgLy8gT25seSBjaGFuZ2Ugb2YgdXNlciBhbmQgbm90IGEgbG9nb3V0IChjdXJyZW50IHVzZXIgaWQgIT09IGFub255bW91cykgc2hvdWxkIHRyaWdnZXIgbG9hZGluZyBtZWNoYW5pc21cbiAgICAgICAgICBpZiAodGhpcy5pc0p1c3RMb2dnZWRJbih1c2VySWQsIHByZXZpb3VzVXNlcklkKSkge1xuICAgICAgICAgICAgdGhpcy5sb2FkT3JNZXJnZShjYXJ0SWQsIHVzZXJJZCwgcHJldmlvdXNVc2VySWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICApO1xuXG4gICAgLy8gU3RyZWFtIGZvciBnZXR0aW5nIHRoZSBjYXJ0IHZhbHVlXG4gICAgY29uc3QgYWN0aXZlQ2FydFZhbHVlJCA9IHRoaXMuY2FydFNlbGVjdG9yJC5waXBlKFxuICAgICAgbWFwKFxuICAgICAgICAoXG4gICAgICAgICAgY2FydEVudGl0eTogUHJvY2Vzc2VzTG9hZGVyU3RhdGU8Q2FydD5cbiAgICAgICAgKToge1xuICAgICAgICAgIGNhcnQ6IENhcnQ7XG4gICAgICAgICAgaXNTdGFibGU6IGJvb2xlYW47XG4gICAgICAgICAgbG9hZGVkOiBib29sZWFuO1xuICAgICAgICB9ID0+IHtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgY2FydDogY2FydEVudGl0eS52YWx1ZSxcbiAgICAgICAgICAgIGlzU3RhYmxlOiAhY2FydEVudGl0eS5sb2FkaW5nICYmIGNhcnRFbnRpdHkucHJvY2Vzc2VzQ291bnQgPT09IDAsXG4gICAgICAgICAgICBsb2FkZWQ6XG4gICAgICAgICAgICAgIChjYXJ0RW50aXR5LmVycm9yIHx8IGNhcnRFbnRpdHkuc3VjY2VzcykgJiYgIWNhcnRFbnRpdHkubG9hZGluZyxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICApLFxuICAgICAgLy8gd2Ugd2FudCB0byBlbWl0IGVtcHR5IGNhcnRzIGV2ZW4gaWYgdGhvc2UgYXJlIG5vdCBzdGFibGVcbiAgICAgIC8vIG9uIG1lcmdlIGNhcnQgYWN0aW9uIHdlIHdhbnQgdG8gc3dpdGNoIHRvIGVtcHR5IGNhcnQgc28gbm8gb25lIHdvdWxkIHVzZSBvbGQgY2FydElkIHdoaWNoIGNhbiBiZSBhbHJlYWR5IG9ic29sZXRlXG4gICAgICAvLyBzbyBvbiBtZXJnZSBhY3Rpb24gdGhlIHJlc3VsdGluZyBzdHJlYW0gbG9va3MgbGlrZSB0aGlzOiBvbGRfY2FydCAtPiB7fSAtPiBuZXdfY2FydFxuICAgICAgZmlsdGVyKCh7IGlzU3RhYmxlLCBjYXJ0IH0pID0+IGlzU3RhYmxlIHx8IHRoaXMuaXNFbXB0eShjYXJ0KSlcbiAgICApO1xuXG4gICAgLy8gUmVzcG9uc2libGUgZm9yIGxvYWRpbmcgY2FydCB3aGVuIGl0J3Mgbm90IChlZy4gYXBwIGluaXRpYWxpemF0aW9uIHdoZW4gd2UgaGF2ZSBvbmx5IGNhcnQgaWQpXG4gICAgY29uc3QgYWN0aXZlQ2FydExvYWRpbmckID0gYWN0aXZlQ2FydFZhbHVlJC5waXBlKFxuICAgICAgd2l0aExhdGVzdEZyb20odGhpcy5hY3RpdmVDYXJ0SWQkLCB0aGlzLnVzZXJJZFNlcnZpY2UuZ2V0VXNlcklkKCkpLFxuICAgICAgdGFwKChbeyBjYXJ0LCBsb2FkZWQsIGlzU3RhYmxlIH0sIGNhcnRJZCwgdXNlcklkXSkgPT4ge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgaXNTdGFibGUgJiZcbiAgICAgICAgICB0aGlzLmlzRW1wdHkoY2FydCkgJiZcbiAgICAgICAgICAhbG9hZGVkICYmXG4gICAgICAgICAgIWlzVGVtcENhcnRJZChjYXJ0SWQpXG4gICAgICAgICkge1xuICAgICAgICAgIHRoaXMubG9hZChjYXJ0SWQsIHVzZXJJZCk7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgKTtcblxuICAgIHRoaXMuYWN0aXZlQ2FydCQgPSB1c2luZyhcbiAgICAgICgpID0+IGFjdGl2ZUNhcnRMb2FkaW5nJC5zdWJzY3JpYmUoKSxcbiAgICAgICgpID0+IGFjdGl2ZUNhcnRWYWx1ZSRcbiAgICApLnBpcGUoXG4gICAgICAvLyBOb3JtYWxpemF0aW9uIGZvciBlbXB0eSBjYXJ0IHZhbHVlLiBJdCB3aWxsIGFsd2F5cyBiZSByZXR1cm5lZCBhcyBlbXB0eSBvYmplY3QuXG4gICAgICBtYXAoKHsgY2FydCB9KSA9PiAoY2FydCA/IGNhcnQgOiB7fSkpLFxuICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSxcbiAgICAgIHNoYXJlUmVwbGF5KHsgYnVmZmVyU2l6ZTogMSwgcmVmQ291bnQ6IHRydWUgfSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWN0aXZlIGNhcnRcbiAgICovXG4gIGdldEFjdGl2ZSgpOiBPYnNlcnZhYmxlPENhcnQ+IHtcbiAgICByZXR1cm4gdGhpcy5hY3RpdmVDYXJ0JDtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYWl0cyBmb3IgdGhlIGNhcnQgdG8gYmUgc3RhYmxlIGJlZm9yZSByZXR1cm5pbmcgdGhlIGFjdGl2ZSBjYXJ0LlxuICAgKi9cbiAgdGFrZUFjdGl2ZSgpOiBPYnNlcnZhYmxlPENhcnQ+IHtcbiAgICByZXR1cm4gdGhpcy5pc1N0YWJsZSgpLnBpcGUoXG4gICAgICBmaWx0ZXIoKGlzU3RhYmxlKSA9PiBpc1N0YWJsZSksXG4gICAgICBzd2l0Y2hNYXAoKCkgPT4gdGhpcy5nZXRBY3RpdmUoKSksXG4gICAgICBmaWx0ZXIoKGNhcnQpID0+ICEhY2FydCksXG4gICAgICB0YWtlKDEpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjdGl2ZSBjYXJ0IGlkXG4gICAqL1xuICBnZXRBY3RpdmVDYXJ0SWQoKTogT2JzZXJ2YWJsZTxzdHJpbmc+IHtcbiAgICByZXR1cm4gdGhpcy5hY3RpdmVDYXJ0JC5waXBlKFxuICAgICAgd2l0aExhdGVzdEZyb20odGhpcy51c2VySWRTZXJ2aWNlLmdldFVzZXJJZCgpKSxcbiAgICAgIG1hcCgoW2NhcnQsIHVzZXJJZF0pID0+IGdldENhcnRJZEJ5VXNlcklkKGNhcnQsIHVzZXJJZCkpLFxuICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBjYXJ0IGVudHJpZXNcbiAgICovXG4gIGdldEVudHJpZXMoKTogT2JzZXJ2YWJsZTxPcmRlckVudHJ5W10+IHtcbiAgICByZXR1cm4gdGhpcy5hY3RpdmVDYXJ0SWQkLnBpcGUoXG4gICAgICBzd2l0Y2hNYXAoKGNhcnRJZCkgPT4gdGhpcy5tdWx0aUNhcnRTZXJ2aWNlLmdldEVudHJpZXMoY2FydElkKSksXG4gICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGxhc3QgY2FydCBlbnRyeSBmb3IgcHJvdmlkZWQgcHJvZHVjdCBjb2RlLlxuICAgKiBOZWVkZWQgdG8gY292ZXIgcHJvY2Vzc2VzIHdoZXJlIG11bHRpcGxlIGVudHJpZXMgY2FuIHNoYXJlIHRoZSBzYW1lIHByb2R1Y3QgY29kZVxuICAgKiAoZS5nLiBwcm9tb3Rpb25zIG9yIGNvbmZpZ3VyYWJsZSBwcm9kdWN0cylcbiAgICpcbiAgICogQHBhcmFtIHByb2R1Y3RDb2RlXG4gICAqL1xuICBnZXRMYXN0RW50cnkocHJvZHVjdENvZGU6IHN0cmluZyk6IE9ic2VydmFibGU8T3JkZXJFbnRyeT4ge1xuICAgIHJldHVybiB0aGlzLmFjdGl2ZUNhcnRJZCQucGlwZShcbiAgICAgIHN3aXRjaE1hcCgoY2FydElkKSA9PlxuICAgICAgICB0aGlzLm11bHRpQ2FydFNlcnZpY2UuZ2V0TGFzdEVudHJ5KGNhcnRJZCwgcHJvZHVjdENvZGUpXG4gICAgICApLFxuICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBjYXJ0IGxvYWRpbmcgc3RhdGVcbiAgICovXG4gIGdldExvYWRpbmcoKTogT2JzZXJ2YWJsZTxib29sZWFuPiB7XG4gICAgcmV0dXJuIHRoaXMuY2FydFNlbGVjdG9yJC5waXBlKFxuICAgICAgbWFwKChjYXJ0RW50aXR5KSA9PiBjYXJ0RW50aXR5LmxvYWRpbmcpLFxuICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0cnVlIHdoZW4gY2FydCBpcyBzdGFibGUgKG5vdCBsb2FkaW5nIGFuZCBub3QgcGVuZGluZyBwcm9jZXNzZXMgb24gY2FydClcbiAgICovXG4gIGlzU3RhYmxlKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIC8vIERlYm91bmNlIGlzIHVzZWQgaGVyZSwgdG8gYXZvaWQgZmxpY2tlcmluZyB3aGVuIHdlIHN3aXRjaCBiZXR3ZWVuIGRpZmZlcmVudCBjYXJ0IGVudGl0aWVzLlxuICAgIC8vIEZvciBleGFtcGxlIGR1cmluZyBgYWRkRW50cnlgIG1ldGhvZC4gV2UgbWlnaHQgdHJ5IHRvIGxvYWQgY3VycmVudCBjYXJ0LCBzbyBgY3VycmVudCBjYXJ0IHdpbGwgYmUgdGhlbiBhY3RpdmUgaWQuXG4gICAgLy8gQWZ0ZXIgbG9hZCBmYWlscyB3ZSBtaWdodCBjcmVhdGUgbmV3IGNhcnQgc28gd2Ugc3dpdGNoIHRvIGB0ZW1wLSR7dXVpZH1gIGNhcnQgZW50aXR5IHVzZWQgd2hlbiBjcmVhdGluZyBjYXJ0LlxuICAgIC8vIEF0IHRoZSBlbmQgd2UgZmluYWxseSBzd2l0Y2ggdG8gY2FydCBgY29kZWAgZm9yIGNhcnQgaWQuIEJldHdlZW4gdGhvc2Ugc3dpdGNoZXMgY2FydCBgaXNTdGFibGVgIGZ1bmN0aW9uIHNob3VsZCBub3QgZmxpY2tlci5cbiAgICByZXR1cm4gdGhpcy5hY3RpdmVDYXJ0SWQkLnBpcGUoXG4gICAgICBzd2l0Y2hNYXAoKGNhcnRJZCkgPT4gdGhpcy5tdWx0aUNhcnRTZXJ2aWNlLmlzU3RhYmxlKGNhcnRJZCkpLFxuICAgICAgZGVib3VuY2UoKHN0YXRlKSA9PiAoc3RhdGUgPyB0aW1lcigwKSA6IEVNUFRZKSksXG4gICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMb2FkcyBjYXJ0IG9yIHVwb24gbG9naW4sIHdoZW5ldmVyIHRoZXJlJ3MgYW4gZXhpc3RpbmcgY2FydCwgbWVyZ2UgaXQgaW50byB0aGUgY3VycmVudCB1c2VyIGNhcnRcbiAgICogY2FydElkIHdpbGwgYmUgZGVmaW5lZCAobm90ICcnLCBudWxsLCB1bmRlZmluZWQpXG4gICAqL1xuICBwcm90ZWN0ZWQgbG9hZE9yTWVyZ2UoXG4gICAgY2FydElkOiBzdHJpbmcsXG4gICAgdXNlcklkOiBzdHJpbmcsXG4gICAgcHJldmlvdXNVc2VySWQ6IHN0cmluZ1xuICApOiB2b2lkIHtcbiAgICBpZiAoY2FydElkID09PSBPQ0NfQ0FSVF9JRF9DVVJSRU5UKSB7XG4gICAgICB0aGlzLm11bHRpQ2FydFNlcnZpY2UubG9hZENhcnQoe1xuICAgICAgICB1c2VySWQsXG4gICAgICAgIGNhcnRJZDogT0NDX0NBUlRfSURfQ1VSUkVOVCxcbiAgICAgICAgZXh0cmFEYXRhOiB7XG4gICAgICAgICAgYWN0aXZlOiB0cnVlLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSBlbHNlIGlmICh0aGlzLmlzR3Vlc3RDYXJ0KCkpIHtcbiAgICAgIHRoaXMuZ3Vlc3RDYXJ0TWVyZ2UoY2FydElkKTtcbiAgICB9IGVsc2UgaWYgKFxuICAgICAgdXNlcklkICE9PSBwcmV2aW91c1VzZXJJZCAmJlxuICAgICAgdXNlcklkICE9PSBPQ0NfVVNFUl9JRF9BTk9OWU1PVVMgJiZcbiAgICAgIHByZXZpb3VzVXNlcklkICE9PSBPQ0NfVVNFUl9JRF9BTk9OWU1PVVNcbiAgICApIHtcbiAgICAgIC8vIFRoaXMgY2FzZSBjb3ZlcnMgdGhlIGNhc2Ugd2hlbiB5b3UgYXJlIGxvZ2dlZCBpbiBhbmQgdGhlbiBhc20gdXNlciBsb2dzIGluIGFuZCB5b3UgZG9uJ3Qgd2FudCB0byBtZXJnZSwgYnV0IG9ubHkgbG9hZCBlbXVsYXRlZCB1c2VyIGNhcnRcbiAgICAgIC8vIFNpbWlsYXJseSB3aGVuIHlvdSBhcmUgbG9nZ2VkIGluIGFzIGFzbSB1c2VyIGFuZCB5b3UgbG9nb3V0IGFuZCB3YW50IHRvIHJlc3VtZSBwcmV2aW91cyB1c2VyIHNlc3Npb25cbiAgICAgIHRoaXMubXVsdGlDYXJ0U2VydmljZS5sb2FkQ2FydCh7XG4gICAgICAgIHVzZXJJZCxcbiAgICAgICAgY2FydElkLFxuICAgICAgICBleHRyYURhdGE6IHtcbiAgICAgICAgICBhY3RpdmU6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gV2UgaGF2ZSBwYXJ0aWN1bGFyIGNhcnQgbG9jYWxseSwgYnV0IHdlIGxvZ2dlZCBpbiwgc28gd2UgbmVlZCB0byBjb21iaW5lIHRoaXMgd2l0aCBjdXJyZW50IGNhcnQgb3IgbWFrZSBpdCBvdXJzLlxuICAgICAgdGhpcy5tdWx0aUNhcnRTZXJ2aWNlLm1lcmdlVG9DdXJyZW50Q2FydCh7XG4gICAgICAgIHVzZXJJZCxcbiAgICAgICAgY2FydElkLFxuICAgICAgICBleHRyYURhdGE6IHtcbiAgICAgICAgICBhY3RpdmU6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTG9hZHMgY2FydCBpbiBldmVyeSBjYXNlIGFwYXJ0IGZyb20gYW5vbnltb3VzIHVzZXIgYW5kIGN1cnJlbnQgY2FydCBjb21iaW5hdGlvblxuICAgKi9cbiAgcHJvdGVjdGVkIGxvYWQoY2FydElkOiBzdHJpbmcsIHVzZXJJZDogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKCEodXNlcklkID09PSBPQ0NfVVNFUl9JRF9BTk9OWU1PVVMgJiYgY2FydElkID09PSBPQ0NfQ0FSVF9JRF9DVVJSRU5UKSkge1xuICAgICAgdGhpcy5tdWx0aUNhcnRTZXJ2aWNlLmxvYWRDYXJ0KHtcbiAgICAgICAgdXNlcklkLFxuICAgICAgICBjYXJ0SWQsXG4gICAgICAgIGV4dHJhRGF0YToge1xuICAgICAgICAgIGFjdGl2ZTogdHJ1ZSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIGVudHJpZXMgZnJvbSBndWVzdCBjYXJ0IHRvIHVzZXIgY2FydFxuICAgKi9cbiAgcHJvdGVjdGVkIGFkZEVudHJpZXNHdWVzdE1lcmdlKGNhcnRFbnRyaWVzOiBPcmRlckVudHJ5W10pIHtcbiAgICBjb25zdCBlbnRyaWVzVG9BZGQgPSBjYXJ0RW50cmllcy5tYXAoKGVudHJ5KSA9PiAoe1xuICAgICAgcHJvZHVjdENvZGU6IGVudHJ5LnByb2R1Y3QuY29kZSxcbiAgICAgIHF1YW50aXR5OiBlbnRyeS5xdWFudGl0eSxcbiAgICB9KSk7XG4gICAgdGhpcy5yZXF1aXJlTG9hZGVkQ2FydEZvckd1ZXN0TWVyZ2UoKVxuICAgICAgLnBpcGUod2l0aExhdGVzdEZyb20odGhpcy51c2VySWRTZXJ2aWNlLmdldFVzZXJJZCgpKSlcbiAgICAgIC5zdWJzY3JpYmUoKFtjYXJ0U3RhdGUsIHVzZXJJZF0pID0+IHtcbiAgICAgICAgdGhpcy5tdWx0aUNhcnRTZXJ2aWNlLmFkZEVudHJpZXMoXG4gICAgICAgICAgdXNlcklkLFxuICAgICAgICAgIGdldENhcnRJZEJ5VXNlcklkKGNhcnRTdGF0ZS52YWx1ZSwgdXNlcklkKSxcbiAgICAgICAgICBlbnRyaWVzVG9BZGRcbiAgICAgICAgKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEhlbHBlciBtZXRob2QgZm9yIHJlcXVpcmluZyBsb2FkZWQgY2FydCB0aGF0IGlzIG5vdCBhIGd1ZXN0IGNhcnQgKGd1ZXN0IGNhcnQgaXMgZmlsdGVyZWQgb3V0KS5cbiAgICogVXNlZCB3aGVuIG1lcmdpbmcgZ3Vlc3QgY2FydCB3aXRoIHVzZXIgY2FydC5cbiAgICovXG4gIHByb3RlY3RlZCByZXF1aXJlTG9hZGVkQ2FydEZvckd1ZXN0TWVyZ2UoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWlyZUxvYWRlZENhcnQoXG4gICAgICB0aGlzLmNhcnRTZWxlY3RvciQucGlwZShmaWx0ZXIoKCkgPT4gIXRoaXMuaXNHdWVzdENhcnQoKSkpXG4gICAgKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBpc0NhcnRDcmVhdGluZyhcbiAgICBjYXJ0U3RhdGU6IFByb2Nlc3Nlc0xvYWRlclN0YXRlPENhcnQ+LFxuICAgIGNhcnRJZDogc3RyaW5nXG4gICkge1xuICAgIC8vIGNhcnQgY3JlYXRpbmcgaXMgYWx3YXlzIHJlcHJlc2VudGVkIHdpdGggbG9hZGluZyBmbGFnc1xuICAgIC8vIHdoZW4gYWxsIGxvYWRpbmcgZmxhZ3MgYXJlIGZhbHNlIGl0IG1lYW5zIHRoYXQgd2UgcmVzdG9yZWQgd3JvbmcgY2FydCBpZFxuICAgIC8vIGNvdWxkIGhhcHBlbiBvbiBjb250ZXh0IGNoYW5nZSBvciByZWxvYWQgcmlnaHQgaW4gdGhlIG1pZGRsZSBvbiBjYXJ0IGNyZWF0ZSBjYWxsXG4gICAgcmV0dXJuIChcbiAgICAgIGlzVGVtcENhcnRJZChjYXJ0SWQpICYmXG4gICAgICAoY2FydFN0YXRlLmxvYWRpbmcgfHwgY2FydFN0YXRlLnN1Y2Nlc3MgfHwgY2FydFN0YXRlLmVycm9yKVxuICAgICk7XG4gIH1cblxuICByZX