UNPKG

@loona/angular

Version:

App State Management done with GraphQL (angular integration)

583 lines (573 loc) 42.7 kB
import { InjectionToken, Injectable, ErrorHandler, Inject, NgModule, Injector } from '@angular/core'; import { __extends, __assign, __spread } from 'tslib'; import { Observable, BehaviorSubject, Subject, queueScheduler, merge, throwError, from } from 'rxjs'; import { Apollo } from 'apollo-angular'; import { observeOn, tap, catchError } from 'rxjs/operators'; import { isMutation, getMutation, isDocument, Manager, withUpdates, getActionType, buildActionFromResult, buildActionFromError, buildContext, getNameOfMutation, isMutationAsAction, METADATA_KEY, buildGetCacheKey, LoonaLink } from '@loona/core'; export { Manager, LoonaLink, State, Mutation, Update, Resolve, Effect } from '@loona/core'; import { ApolloCache } from 'apollo-cache'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ /** @type {?} */ var INITIAL_STATE = new InjectionToken('Loona/State'); /** @type {?} */ var CHILD_STATE = new InjectionToken('Loona/ChildState'); /** @type {?} */ var LOONA_CACHE = new InjectionToken('Loona/Cache'); /** @type {?} */ var INIT = '@@init'; /** @type {?} */ var ROOT_EFFECTS_INIT = '@@effects/init'; /** @type {?} */ var UPDATE_EFFECTS = '@@effects/update'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ /** * @template V */ var /** * @template V */ Actions = /** @class */ (function (_super) { __extends(Actions, _super); function Actions() { return _super !== null && _super.apply(this, arguments) || this; } return Actions; }(Observable)); var ScannedActions = /** @class */ (function (_super) { __extends(ScannedActions, _super); function ScannedActions() { return _super !== null && _super.apply(this, arguments) || this; } /** * @return {?} */ ScannedActions.prototype.ngOnDestroy = /** * @return {?} */ function () { this.complete(); }; ScannedActions.decorators = [ { type: Injectable } ]; return ScannedActions; }(Subject)); var InnerActions = /** @class */ (function (_super) { __extends(InnerActions, _super); function InnerActions() { return _super.call(this, { type: INIT }) || this; } /** * @param {?} action * @return {?} */ InnerActions.prototype.next = /** * @param {?} action * @return {?} */ function (action) { if (typeof action === 'undefined') { throw new TypeError("Actions must be objects"); } else if (typeof action.type === 'undefined') { throw new TypeError("Actions must have a type property"); } _super.prototype.next.call(this, action); }; /** * @return {?} */ InnerActions.prototype.complete = /** * @return {?} */ function () { }; /** * @return {?} */ InnerActions.prototype.ngOnDestroy = /** * @return {?} */ function () { _super.prototype.complete.call(this); }; InnerActions.decorators = [ { type: Injectable } ]; /** @nocollapse */ InnerActions.ctorParameters = function () { return []; }; return InnerActions; }(BehaviorSubject)); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ var Loona = /** @class */ (function () { function Loona(apollo, manager, actions, scannedActions, errorHandler) { this.apollo = apollo; this.manager = manager; this.actions = actions; this.direct$ = new Subject(); this.queue$ = merge(actions, this.direct$).pipe(observeOn(queueScheduler)); this.queue$.subscribe({ next: function (action) { scannedActions.next(action); }, error: function (error) { errorHandler.handleError(error); }, }); } /** * @template T, V * @param {?} queryOrOptions * @param {?=} variables * @param {?=} options * @return {?} */ Loona.prototype.query = /** * @template T, V * @param {?} queryOrOptions * @param {?=} variables * @param {?=} options * @return {?} */ function (queryOrOptions, variables, options) { return this.apollo.watchQuery(isDocument(queryOrOptions) ? __assign({ query: queryOrOptions, variables: variables }, options) : queryOrOptions); }; /** * @template T, V * @param {?} mutationOrOptions * @param {?=} variables * @param {?=} options * @return {?} */ Loona.prototype.mutate = /** * @template T, V * @param {?} mutationOrOptions * @param {?=} variables * @param {?=} options * @return {?} */ function (mutationOrOptions, variables, options) { var _this = this; /** @type {?} */ var config = isDocument(mutationOrOptions) ? __assign({ mutation: mutationOrOptions, variables: variables }, options) : mutationOrOptions; return this.apollo .mutate(withUpdates(config, this.manager)) .pipe(tap(function (result) { _this.direct$.next(buildActionFromResult(config, result)); }), catchError(function (error) { _this.direct$.next(buildActionFromError(config, error)); return throwError(error); })); }; /** * @param {?} action * @return {?} */ Loona.prototype.dispatch = /** * @param {?} action * @return {?} */ function (action) { if (isMutation(action)) { /** @type {?} */ var mutation = getMutation(action); this.mutate(__assign({ mutation: mutation }, action)).subscribe(); } else { this.actions.next(__assign({ type: getActionType(action) }, action)); } }; /** * @template T * @return {?} */ Loona.prototype.extract = /** * @template T * @return {?} */ function () { return this.apollo.getClient().extract(); }; /** * @return {?} */ Loona.prototype.reset = /** * @return {?} */ function () { this.apollo.getClient().resetStore(); }; /** * @param {?} state * @return {?} */ Loona.prototype.restore = /** * @param {?} state * @return {?} */ function (state) { this.apollo.getClient().restore(state); }; Loona.decorators = [ { type: Injectable } ]; /** @nocollapse */ Loona.ctorParameters = function () { return [ { type: Apollo }, { type: Manager }, { type: InnerActions }, { type: ScannedActions }, { type: ErrorHandler } ]; }; return Loona; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ var Effects = /** @class */ (function () { function Effects(loona, apollo, cache) { this.effects = {}; this.getContext = function () { return (__assign({}, buildContext({ cache: cache, getCacheKey: buildGetCacheKey(cache), }, apollo.getClient()), { dispatch: loona.dispatch.bind(loona) })); }; } /** * @param {?} instance * @param {?=} meta * @return {?} */ Effects.prototype.addEffects = /** * @param {?} instance * @param {?=} meta * @return {?} */ function (instance, meta) { var _this = this; if (!meta) { return; } var _loop_1 = function (type) { if (!this_1.effects[type]) { this_1.effects[type] = []; } meta[type].forEach(function (_a) { var propName = _a.propName; _this.effects[type].push(instance[propName].bind(instance)); }); }; var this_1 = this; for (var type in meta) { _loop_1(type); } }; /** * @param {?} action * @return {?} */ Effects.prototype.runEffects = /** * @param {?} action * @return {?} */ function (action) { var _this = this; /** @type {?} */ var type = action.type; if (isMutationAsAction(action)) { type = getNameOfMutation(action.options.mutation); } /** @type {?} */ var effectsToRun = this.effects[type]; if (effectsToRun) { effectsToRun.forEach(function (effect) { effect(action, _this.getContext()); }); } }; Effects.decorators = [ { type: Injectable } ]; /** @nocollapse */ Effects.ctorParameters = function () { return [ { type: Loona }, { type: Apollo }, { type: ApolloCache, decorators: [{ type: Inject, args: [LOONA_CACHE,] }] } ]; }; return Effects; }()); var EffectsRunner = /** @class */ (function () { function EffectsRunner(effects, scannedActions) { this.effects = effects; this.scannedActions = scannedActions; this.actionsSubscription = null; } /** * @return {?} */ EffectsRunner.prototype.start = /** * @return {?} */ function () { var _this = this; if (!this.actionsSubscription) { this.actionsSubscription = this.scannedActions.subscribe(function (action) { _this.effects.runEffects(action); }); } }; /** * @return {?} */ EffectsRunner.prototype.ngOnDestroy = /** * @return {?} */ function () { if (this.actionsSubscription) { this.actionsSubscription.unsubscribe(); this.actionsSubscription = null; } }; EffectsRunner.decorators = [ { type: Injectable } ]; /** @nocollapse */ EffectsRunner.ctorParameters = function () { return [ { type: Effects }, { type: ScannedActions } ]; }; return EffectsRunner; }()); /** * @return {?} */ function mapStates() { /** @type {?} */ var names = []; /** @type {?} */ var add = function (state) { names.push(state.constructor && state.constructor.name); }; return { names: names, add: add }; } /** * @param {?} state * @param {?} injector * @return {?} */ function extractState(state, injector) { return { instance: injector.get(state), meta: state[METADATA_KEY], }; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ /** * @template T * @param {?} val * @return {?} */ function isObservable(val) { return val instanceof Observable; } /** * @param {?} resolver * @return {?} */ function handleObservable(resolver) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } /** @type {?} */ var result; try { result = resolver.apply(void 0, __spread(args)); } catch (e) { return Promise.reject(e); } return result instanceof Promise || isObservable(result) ? from(result).toPromise() : Promise.resolve(result); }; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ var LoonaRootModule = /** @class */ (function () { function LoonaRootModule(effects, states, loona, manager, runner, injector) { var _this = this; this.effects = effects; runner.start(); var _a = mapStates(), names = _a.names, add = _a.add; states.forEach(function (state) { var _a = extractState(state, injector), instance = _a.instance, meta = _a.meta; manager.addState(instance, meta, handleObservable); _this.addEffects(instance, meta.effects); add(instance); }); loona.dispatch({ type: ROOT_EFFECTS_INIT, states: names, }); } /** * @param {?} state * @param {?=} meta * @return {?} */ LoonaRootModule.prototype.addEffects = /** * @param {?} state * @param {?=} meta * @return {?} */ function (state, meta) { this.effects.addEffects(state, meta); }; LoonaRootModule.decorators = [ { type: NgModule } ]; /** @nocollapse */ LoonaRootModule.ctorParameters = function () { return [ { type: Effects }, { type: Array, decorators: [{ type: Inject, args: [INITIAL_STATE,] }] }, { type: Loona }, { type: Manager }, { type: EffectsRunner }, { type: Injector } ]; }; return LoonaRootModule; }()); var LoonaChildModule = /** @class */ (function () { function LoonaChildModule(states, injector, manager, loona, rootModule) { var _a = mapStates(), names = _a.names, add = _a.add; states.forEach(function (state) { var _a = extractState(state, injector), instance = _a.instance, meta = _a.meta; manager.addState(instance, meta, handleObservable); rootModule.addEffects(instance, meta.effects); add(instance); }); loona.dispatch({ type: UPDATE_EFFECTS, states: names, }); } LoonaChildModule.decorators = [ { type: NgModule } ]; /** @nocollapse */ LoonaChildModule.ctorParameters = function () { return [ { type: Array, decorators: [{ type: Inject, args: [CHILD_STATE,] }] }, { type: Injector }, { type: Manager }, { type: Loona }, { type: LoonaRootModule } ]; }; return LoonaChildModule; }()); var LoonaModule = /** @class */ (function () { function LoonaModule() { } /** * @param {?=} states * @return {?} */ LoonaModule.forRoot = /** * @param {?=} states * @return {?} */ function (states) { if (states === void 0) { states = []; } return { ngModule: LoonaRootModule, providers: __spread([ Loona, InnerActions, ScannedActions, { provide: Actions, useExisting: ScannedActions, } ], states, [ { provide: INITIAL_STATE, useValue: states }, { provide: Manager, useFactory: managerFactory, deps: [LOONA_CACHE, Injector], }, { provide: LoonaLink, useFactory: linkFactory, deps: [Manager], }, Effects, EffectsRunner, ]), }; }; /** * @param {?=} states * @return {?} */ LoonaModule.forChild = /** * @param {?=} states * @return {?} */ function (states) { if (states === void 0) { states = []; } return { ngModule: LoonaChildModule, providers: __spread(states, [{ provide: CHILD_STATE, useValue: states }]), }; }; LoonaModule.decorators = [ { type: NgModule } ]; return LoonaModule; }()); /** * @param {?} manager * @return {?} */ function linkFactory(manager) { return new LoonaLink(manager); } /** * @param {?} cache * @param {?} injector * @return {?} */ function managerFactory(cache, injector) { /** @type {?} */ var manager = new Manager({ cache: cache, getClient: function () { return injector.get(Apollo).getClient(); }, }); return manager; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ export { Actions, Loona, LoonaModule, INITIAL_STATE, CHILD_STATE, LOONA_CACHE, INIT, UPDATE_EFFECTS, ROOT_EFFECTS_INIT, InnerActions as ɵb, ScannedActions as ɵa, Effects as ɵg, EffectsRunner as ɵh, LoonaChildModule as ɵd, LoonaRootModule as ɵc, linkFactory as ɵe, managerFactory as ɵf }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9vbmEuYW5ndWxhci5qcy5tYXAiLCJzb3VyY2VzIjpbIm5nOi8vQGxvb25hL2FuZ3VsYXIvdG9rZW5zLnRzIiwibmc6Ly9AbG9vbmEvYW5ndWxhci9hY3Rpb25zLnRzIiwibmc6Ly9AbG9vbmEvYW5ndWxhci9jbGllbnQudHMiLCJuZzovL0Bsb29uYS9hbmd1bGFyL2VmZmVjdHMudHMiLCJuZzovL0Bsb29uYS9hbmd1bGFyL3V0aWxzLnRzIiwibmc6Ly9AbG9vbmEvYW5ndWxhci9tb2R1bGUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtJbmplY3Rpb25Ub2tlbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge0Fwb2xsb0NhY2hlfSBmcm9tICdhcG9sbG8tY2FjaGUnO1xuaW1wb3J0IHtTdGF0ZUNsYXNzLCBNZXRhZGF0YX0gZnJvbSAnQGxvb25hL2NvcmUnO1xuXG5leHBvcnQgY29uc3QgSU5JVElBTF9TVEFURSA9IG5ldyBJbmplY3Rpb25Ub2tlbjxTdGF0ZUNsYXNzPE1ldGFkYXRhPj4oXG4gICdMb29uYS9TdGF0ZScsXG4pO1xuZXhwb3J0IGNvbnN0IENISUxEX1NUQVRFID0gbmV3IEluamVjdGlvblRva2VuPFN0YXRlQ2xhc3M8TWV0YWRhdGE+PihcbiAgJ0xvb25hL0NoaWxkU3RhdGUnLFxuKTtcbmV4cG9ydCBjb25zdCBMT09OQV9DQUNIRSA9IG5ldyBJbmplY3Rpb25Ub2tlbjxBcG9sbG9DYWNoZTxhbnk+PignTG9vbmEvQ2FjaGUnKTtcblxuZXhwb3J0IGNvbnN0IElOSVQgPSAnQEBpbml0JztcbmV4cG9ydCBjb25zdCBST09UX0VGRkVDVFNfSU5JVCA9ICdAQGVmZmVjdHMvaW5pdCc7XG5leHBvcnQgY29uc3QgVVBEQVRFX0VGRkVDVFMgPSAnQEBlZmZlY3RzL3VwZGF0ZSc7XG4iLCJpbXBvcnQge0luamVjdGFibGUsIE9uRGVzdHJveX0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge09ic2VydmFibGUsIEJlaGF2aW9yU3ViamVjdCwgU3ViamVjdH0gZnJvbSAncnhqcyc7XG5pbXBvcnQge0FjdGlvbn0gZnJvbSAnQGxvb25hL2NvcmUnO1xuXG5pbXBvcnQge0lOSVR9IGZyb20gJy4vdG9rZW5zJztcblxuZXhwb3J0IGNsYXNzIEFjdGlvbnM8ViA9IEFjdGlvbj4gZXh0ZW5kcyBPYnNlcnZhYmxlPFY+IHt9XG5cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBTY2FubmVkQWN0aW9ucyBleHRlbmRzIFN1YmplY3Q8QWN0aW9uPiBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG4gIG5nT25EZXN0cm95KCkge1xuICAgIHRoaXMuY29tcGxldGUoKTtcbiAgfVxufVxuXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgSW5uZXJBY3Rpb25zIGV4dGVuZHMgQmVoYXZpb3JTdWJqZWN0PEFjdGlvbj4gaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBzdXBlcih7dHlwZTogSU5JVH0pO1xuICB9XG5cbiAgbmV4dChhY3Rpb246IEFjdGlvbikge1xuICAgIGlmICh0eXBlb2YgYWN0aW9uID09PSAndW5kZWZpbmVkJykge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgQWN0aW9ucyBtdXN0IGJlIG9iamVjdHNgKTtcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBhY3Rpb24udHlwZSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYEFjdGlvbnMgbXVzdCBoYXZlIGEgdHlwZSBwcm9wZXJ0eWApO1xuICAgIH1cblxuICAgIHN1cGVyLm5leHQoYWN0aW9uKTtcbiAgfVxuXG4gIGNvbXBsZXRlKCkge31cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICBzdXBlci5jb21wbGV0ZSgpO1xuICB9XG59XG4iLCJpbXBvcnQge0luamVjdGFibGUsIEVycm9ySGFuZGxlcn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge0Fwb2xsbywgUXVlcnlSZWZ9IGZyb20gJ2Fwb2xsby1hbmd1bGFyJztcbmltcG9ydCB7XG4gIFdhdGNoUXVlcnlPcHRpb25zLFxuICBNdXRhdGlvbk9wdGlvbnMgYXMgQ29yZU11dGF0aW9uT3B0aW9ucyxcbn0gZnJvbSAnYXBvbGxvLWNsaWVudCc7XG5pbXBvcnQge0ZldGNoUmVzdWx0fSBmcm9tICdhcG9sbG8tbGluayc7XG5pbXBvcnQge09ic2VydmFibGUsIFN1YmplY3QsIHF1ZXVlU2NoZWR1bGVyLCBtZXJnZSwgdGhyb3dFcnJvcn0gZnJvbSAncnhqcyc7XG5pbXBvcnQge29ic2VydmVPbiwgdGFwLCBjYXRjaEVycm9yfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQge0RvY3VtZW50Tm9kZX0gZnJvbSAnZ3JhcGhxbCc7XG5pbXBvcnQge1xuICBpc011dGF0aW9uLFxuICBnZXRNdXRhdGlvbixcbiAgQWN0aW9uLFxuICBpc0RvY3VtZW50LFxuICBNYW5hZ2VyLFxuICB3aXRoVXBkYXRlcyxcbiAgZ2V0QWN0aW9uVHlwZSxcbiAgYnVpbGRBY3Rpb25Gcm9tUmVzdWx0LFxuICBidWlsZEFjdGlvbkZyb21FcnJvcixcbn0gZnJvbSAnQGxvb25hL2NvcmUnO1xuXG5pbXBvcnQge0lubmVyQWN0aW9ucywgU2Nhbm5lZEFjdGlvbnN9IGZyb20gJy4vYWN0aW9ucyc7XG5cbmV4cG9ydCB0eXBlIE9taXQ8VCwgSyBleHRlbmRzIGtleW9mIFQ+ID0gUGljazxULCBFeGNsdWRlPGtleW9mIFQsIEs+PjtcblxuZXhwb3J0IGludGVyZmFjZSBRdWVyeU9wdGlvbnNcbiAgZXh0ZW5kcyBPbWl0PFdhdGNoUXVlcnlPcHRpb25zLCAncXVlcnknIHwgJ3ZhcmlhYmxlcyc+IHt9XG5cbmV4cG9ydCBpbnRlcmZhY2UgTXV0YXRpb25PcHRpb25zXG4gIGV4dGVuZHMgT21pdDxDb3JlTXV0YXRpb25PcHRpb25zLCAnbXV0YXRpb24nIHwgJ3ZhcmlhYmxlcyc+IHt9XG5cbmV4cG9ydCB0eXBlIFIgPSBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuXG5leHBvcnQgaW50ZXJmYWNlIFR5cGVkVmFyaWFibGVzPFY+IHtcbiAgdmFyaWFibGVzPzogVjtcbn1cblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIExvb25hIHtcbiAgcHJpdmF0ZSBxdWV1ZSQ6IE9ic2VydmFibGU8QWN0aW9uPjtcbiAgcHJpdmF0ZSBkaXJlY3QkID0gbmV3IFN1YmplY3Q8QWN0aW9uPigpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgYXBvbGxvOiBBcG9sbG8sXG4gICAgcHJpdmF0ZSBtYW5hZ2VyOiBNYW5hZ2VyLFxuICAgIHByaXZhdGUgYWN0aW9uczogSW5uZXJBY3Rpb25zLFxuICAgIHNjYW5uZWRBY3Rpb25zOiBTY2FubmVkQWN0aW9ucyxcbiAgICBlcnJvckhhbmRsZXI6IEVycm9ySGFuZGxlcixcbiAgKSB7XG4gICAgdGhpcy5xdWV1ZSQgPSBtZXJnZShhY3Rpb25zLCB0aGlzLmRpcmVjdCQpLnBpcGUob2JzZXJ2ZU9uKHF1ZXVlU2NoZWR1bGVyKSk7XG4gICAgdGhpcy5xdWV1ZSQuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IGFjdGlvbiA9PiB7XG4gICAgICAgIHNjYW5uZWRBY3Rpb25zLm5leHQoYWN0aW9uKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogZXJyb3IgPT4ge1xuICAgICAgICBlcnJvckhhbmRsZXIuaGFuZGxlRXJyb3IoZXJyb3IpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHF1ZXJ5PFQsIFYgPSBhbnk+KFxuICAgIHF1ZXJ5OiBEb2N1bWVudE5vZGUsXG4gICAgdmFyaWFibGVzPzogVixcbiAgICBvcHRpb25zPzogUXVlcnlPcHRpb25zLFxuICApOiBRdWVyeVJlZjxULCBWPjtcblxuICBxdWVyeTxULCBWID0gYW55PihcbiAgICBvcHRpb25zOiBXYXRjaFF1ZXJ5T3B0aW9ucyAmIFR5cGVkVmFyaWFibGVzPFY+LFxuICApOiBRdWVyeVJlZjxULCBWPjtcblxuICBxdWVyeTxULCBWID0gYW55PihcbiAgICBxdWVyeU9yT3B0aW9uczogRG9jdW1lbnROb2RlIHwgKFdhdGNoUXVlcnlPcHRpb25zICYgVHlwZWRWYXJpYWJsZXM8Vj4pLFxuICAgIHZhcmlhYmxlcz86IFYsXG4gICAgb3B0aW9ucz86IFF1ZXJ5T3B0aW9ucyxcbiAgKTogUXVlcnlSZWY8VCwgVj4ge1xuICAgIHJldHVybiB0aGlzLmFwb2xsby53YXRjaFF1ZXJ5PFQsIFY+KFxuICAgICAgaXNEb2N1bWVudChxdWVyeU9yT3B0aW9ucylcbiAgICAgICAgPyB7XG4gICAgICAgICAgICBxdWVyeTogcXVlcnlPck9wdGlvbnMsXG4gICAgICAgICAgICB2YXJpYWJsZXMsXG4gICAgICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICAgIH1cbiAgICAgICAgOiBxdWVyeU9yT3B0aW9ucyxcbiAgICApO1xuICB9XG5cbiAgbXV0YXRlPFQsIFYgPSBSPihcbiAgICBtdXRhdGlvbjogRG9jdW1lbnROb2RlLFxuICAgIHZhcmlhYmxlcz86IFYsXG4gICAgb3B0aW9ucz86IE11dGF0aW9uT3B0aW9ucyxcbiAgKTogT2JzZXJ2YWJsZTxGZXRjaFJlc3VsdDxUPj47XG5cbiAgbXV0YXRlPFQsIFYgPSBSPihcbiAgICBvcHRpb25zOiBDb3JlTXV0YXRpb25PcHRpb25zPFQsIFY+LFxuICApOiBPYnNlcnZhYmxlPEZldGNoUmVzdWx0PFQ+PjtcblxuICBtdXRhdGU8VCwgViA9IFI+KFxuICAgIG11dGF0aW9uT3JPcHRpb25zOiBEb2N1bWVudE5vZGUgfCBDb3JlTXV0YXRpb25PcHRpb25zPFQsIFY+LFxuICAgIHZhcmlhYmxlcz86IFYsXG4gICAgb3B0aW9ucz86IE11dGF0aW9uT3B0aW9ucyxcbiAgKTogT2JzZXJ2YWJsZTxGZXRjaFJlc3VsdDxUPj4ge1xuICAgIGNvbnN0IGNvbmZpZyA9IGlzRG9jdW1lbnQobXV0YXRpb25Pck9wdGlvbnMpXG4gICAgICA/IHtcbiAgICAgICAgICBtdXRhdGlvbjogbXV0YXRpb25Pck9wdGlvbnMsXG4gICAgICAgICAgdmFyaWFibGVzLFxuICAgICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICAgIH1cbiAgICAgIDogbXV0YXRpb25Pck9wdGlvbnM7XG5cbiAgICByZXR1cm4gdGhpcy5hcG9sbG9cbiAgICAgIC5tdXRhdGU8VCwgVj4od2l0aFVwZGF0ZXM8VCwgVj4oY29uZmlnLCB0aGlzLm1hbmFnZXIpKVxuICAgICAgLnBpcGUoXG4gICAgICAgIHRhcChyZXN1bHQgPT4ge1xuICAgICAgICAgIHRoaXMuZGlyZWN0JC5uZXh0KGJ1aWxkQWN0aW9uRnJvbVJlc3VsdChjb25maWcsIHJlc3VsdCkpO1xuICAgICAgICB9KSxcbiAgICAgICAgY2F0Y2hFcnJvcihlcnJvciA9PiB7XG4gICAgICAgICAgdGhpcy5kaXJlY3QkLm5leHQoYnVpbGRBY3Rpb25Gcm9tRXJyb3IoY29uZmlnLCBlcnJvcikpO1xuICAgICAgICAgIHJldHVybiB0aHJvd0Vycm9yKGVycm9yKTtcbiAgICAgICAgfSksXG4gICAgICApO1xuICB9XG5cbiAgZGlzcGF0Y2goYWN0aW9uOiBhbnkpOiB2b2lkIHtcbiAgICBpZiAoaXNNdXRhdGlvbihhY3Rpb24pKSB7XG4gICAgICBjb25zdCBtdXRhdGlvbiA9IGdldE11dGF0aW9uKGFjdGlvbik7XG5cbiAgICAgIHRoaXMubXV0YXRlKHtcbiAgICAgICAgbXV0YXRpb24sXG4gICAgICAgIC4uLmFjdGlvbixcbiAgICAgIH0pLnN1YnNjcmliZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmFjdGlvbnMubmV4dCh7XG4gICAgICAgIHR5cGU6IGdldEFjdGlvblR5cGUoYWN0aW9uKSxcbiAgICAgICAgLi4uYWN0aW9uLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgZXh0cmFjdDxUID0gYW55PigpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hcG9sbG8uZ2V0Q2xpZW50KCkuZXh0cmFjdCgpO1xuICB9XG5cbiAgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5hcG9sbG8uZ2V0Q2xpZW50KCkucmVzZXRTdG9yZSgpO1xuICB9XG5cbiAgcmVzdG9yZShzdGF0ZTogYW55KTogdm9pZCB7XG4gICAgdGhpcy5hcG9sbG8uZ2V0Q2xpZW50KCkucmVzdG9yZShzdGF0ZSk7XG4gIH1cbn1cbiIsImltcG9ydCB7XG4gIGJ1aWxkQ29udGV4dCxcbiAgZ2V0TmFtZU9mTXV0YXRpb24sXG4gIGlzTXV0YXRpb25Bc0FjdGlvbixcbiAgTWV0YWRhdGEsXG4gIEVmZmVjdE1ldGhvZCxcbiAgQWN0aW9uLFxuICBFZmZlY3RDb250ZXh0LFxuICBTdGF0ZUNsYXNzLFxuICBNRVRBREFUQV9LRVksXG4gIGJ1aWxkR2V0Q2FjaGVLZXksXG59IGZyb20gJ0Bsb29uYS9jb3JlJztcbmltcG9ydCB7SW5qZWN0YWJsZSwgSW5qZWN0LCBPbkRlc3Ryb3ksIEluamVjdG9yfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7QXBvbGxvQ2FjaGV9IGZyb20gJ2Fwb2xsby1jYWNoZSc7XG5pbXBvcnQge0Fwb2xsb30gZnJvbSAnYXBvbGxvLWFuZ3VsYXInO1xuaW1wb3J0IHtTdWJzY3JpcHRpb259IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQge0xvb25hfSBmcm9tICcuL2NsaWVudCc7XG5pbXBvcnQge0xPT05BX0NBQ0hFfSBmcm9tICcuL3Rva2Vucyc7XG5pbXBvcnQge1NjYW5uZWRBY3Rpb25zfSBmcm9tICcuL2FjdGlvbnMnO1xuXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgRWZmZWN0cyB7XG4gIGVmZmVjdHM6IFJlY29yZDxzdHJpbmcsIEFycmF5PEVmZmVjdE1ldGhvZD4+ID0ge307XG4gIGdldENvbnRleHQ6ICgpID0+IEVmZmVjdENvbnRleHQ7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgbG9vbmE6IExvb25hLFxuICAgIGFwb2xsbzogQXBvbGxvLFxuICAgIEBJbmplY3QoTE9PTkFfQ0FDSEUpIGNhY2hlOiBBcG9sbG9DYWNoZTxhbnk+LFxuICApIHtcbiAgICB0aGlzLmdldENvbnRleHQgPSAoKSA9PiAoe1xuICAgICAgLi4uYnVpbGRDb250ZXh0KFxuICAgICAgICB7XG4gICAgICAgICAgY2FjaGUsXG4gICAgICAgICAgZ2V0Q2FjaGVLZXk6IGJ1aWxkR2V0Q2FjaGVLZXkoY2FjaGUpLFxuICAgICAgICB9LFxuICAgICAgICBhcG9sbG8uZ2V0Q2xpZW50KCksXG4gICAgICApLFxuICAgICAgZGlzcGF0Y2g6IGxvb25hLmRpc3BhdGNoLmJpbmQobG9vbmEpLFxuICAgIH0pO1xuICB9XG5cbiAgYWRkRWZmZWN0cyhpbnN0YW5jZTogYW55LCBtZXRhPzogTWV0YWRhdGEuRWZmZWN0cykge1xuICAgIGlmICghbWV0YSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgdHlwZSBpbiBtZXRhKSB7XG4gICAgICBpZiAoIXRoaXMuZWZmZWN0c1t0eXBlXSkge1xuICAgICAgICB0aGlzLmVmZmVjdHNbdHlwZV0gPSBbXTtcbiAgICAgIH1cblxuICAgICAgbWV0YVt0eXBlXS5mb3JFYWNoKCh7cHJvcE5hbWV9KSA9PiB7XG4gICAgICAgIHRoaXMuZWZmZWN0c1t0eXBlXS5wdXNoKGluc3RhbmNlW3Byb3BOYW1lXS5iaW5kKGluc3RhbmNlKSk7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBydW5FZmZlY3RzKGFjdGlvbjogQWN0aW9uKSB7XG4gICAgbGV0IHR5cGUgPSBhY3Rpb24udHlwZTtcblxuICAgIGlmIChpc011dGF0aW9uQXNBY3Rpb24oYWN0aW9uKSkge1xuICAgICAgdHlwZSA9IGdldE5hbWVPZk11dGF0aW9uKGFjdGlvbi5vcHRpb25zLm11dGF0aW9uKTtcbiAgICB9XG5cbiAgICBjb25zdCBlZmZlY3RzVG9SdW4gPSB0aGlzLmVmZmVjdHNbdHlwZV07XG5cbiAgICBpZiAoZWZmZWN0c1RvUnVuKSB7XG4gICAgICBlZmZlY3RzVG9SdW4uZm9yRWFjaChlZmZlY3QgPT4ge1xuICAgICAgICBlZmZlY3QoYWN0aW9uLCB0aGlzLmdldENvbnRleHQoKSk7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbn1cblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEVmZmVjdHNSdW5uZXIgaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICBwcml2YXRlIGFjdGlvbnNTdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbiB8IG51bGwgPSBudWxsO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgZWZmZWN0czogRWZmZWN0cyxcbiAgICBwcml2YXRlIHNjYW5uZWRBY3Rpb25zOiBTY2FubmVkQWN0aW9ucyxcbiAgKSB7fVxuXG4gIHN0YXJ0KCkge1xuICAgIGlmICghdGhpcy5hY3Rpb25zU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmFjdGlvbnNTdWJzY3JpcHRpb24gPSB0aGlzLnNjYW5uZWRBY3Rpb25zLnN1YnNjcmliZShhY3Rpb24gPT4ge1xuICAgICAgICB0aGlzLmVmZmVjdHMucnVuRWZmZWN0cyhhY3Rpb24pO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgaWYgKHRoaXMuYWN0aW9uc1N1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5hY3Rpb25zU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgICB0aGlzLmFjdGlvbnNTdWJzY3JpcHRpb24gPSBudWxsO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gbWFwU3RhdGVzKCkge1xuICBjb25zdCBuYW1lczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3QgYWRkID0gKHN0YXRlOiBhbnkpID0+IHtcbiAgICBuYW1lcy5wdXNoKHN0YXRlLmNvbnN0cnVjdG9yICYmIHN0YXRlLmNvbnN0cnVjdG9yLm5hbWUpO1xuICB9O1xuXG4gIHJldHVybiB7bmFtZXMsIGFkZH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0U3RhdGUoXG4gIHN0YXRlOiBTdGF0ZUNsYXNzPE1ldGFkYXRhPixcbiAgaW5qZWN0b3I6IEluamVjdG9yLFxuKToge1xuICBpbnN0YW5jZTogYW55O1xuICBtZXRhOiBNZXRhZGF0YTtcbn0ge1xuICByZXR1cm4ge1xuICAgIGluc3RhbmNlOiBpbmplY3Rvci5nZXQoc3RhdGUpLFxuICAgIG1ldGE6IHN0YXRlW01FVEFEQVRBX0tFWV0sXG4gIH07XG59XG4iLCJpbXBvcnQge09ic2VydmFibGUsIGZyb219IGZyb20gJ3J4anMnO1xuXG5leHBvcnQgZnVuY3Rpb24gaXNPYnNlcnZhYmxlPFQgPSBhbnk+KHZhbDogYW55KTogdmFsIGlzIE9ic2VydmFibGU8VD4ge1xuICByZXR1cm4gdmFsIGluc3RhbmNlb2YgT2JzZXJ2YWJsZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGhhbmRsZU9ic2VydmFibGUocmVzb2x2ZXI6IGFueSkge1xuICByZXR1cm4gKC4uLmFyZ3M6IGFueVtdKSA9PiB7XG4gICAgbGV0IHJlc3VsdDogYW55O1xuXG4gICAgdHJ5IHtcbiAgICAgIHJlc3VsdCA9IHJlc29sdmVyKC4uLmFyZ3MpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0IGluc3RhbmNlb2YgUHJvbWlzZSB8fCBpc09ic2VydmFibGUocmVzdWx0KVxuICAgICAgPyBmcm9tKHJlc3VsdCkudG9Qcm9taXNlKClcbiAgICAgIDogUHJvbWlzZS5yZXNvbHZlKHJlc3VsdCk7XG4gIH07XG59XG4iLCJpbXBvcnQge05nTW9kdWxlLCBNb2R1bGVXaXRoUHJvdmlkZXJzLCBJbmplY3RvciwgSW5qZWN0fSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7QXBvbGxvQ2FjaGV9IGZyb20gJ2Fwb2xsby1jYWNoZSc7XG5pbXBvcnQge0Fwb2xsb30gZnJvbSAnYXBvbGxvLWFuZ3VsYXInO1xuaW1wb3J0IHtNYW5hZ2VyLCBMb29uYUxpbmssIFN0YXRlQ2xhc3MsIE1ldGFkYXRhfSBmcm9tICdAbG9vbmEvY29yZSc7XG5cbmltcG9ydCB7TG9vbmF9IGZyb20gJy4vY2xpZW50JztcbmltcG9ydCB7SW5uZXJBY3Rpb25zLCBTY2FubmVkQWN0aW9ucywgQWN0aW9uc30gZnJvbSAnLi9hY3Rpb25zJztcbmltcG9ydCB7RWZmZWN0c1J1bm5lciwgRWZmZWN0cywgbWFwU3RhdGVzLCBleHRyYWN0U3RhdGV9IGZyb20gJy4vZWZmZWN0cyc7XG5pbXBvcnQge1xuICBJTklUSUFMX1NUQVRFLFxuICBDSElMRF9TVEFURSxcbiAgTE9PTkFfQ0FDSEUsXG4gIFJPT1RfRUZGRUNUU19JTklULFxuICBVUERBVEVfRUZGRUNUUyxcbn0gZnJvbSAnLi90b2tlbnMnO1xuaW1wb3J0IHtoYW5kbGVPYnNlcnZhYmxlfSBmcm9tICcuL3V0aWxzJztcblxuQE5nTW9kdWxlKClcbmV4cG9ydCBjbGFzcyBMb29uYVJvb3RNb2R1bGUge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGVmZmVjdHM6IEVmZmVjdHMsXG4gICAgQEluamVjdChJTklUSUFMX1NUQVRFKSBzdGF0ZXM6IFN0YXRlQ2xhc3M8TWV0YWRhdGE+W10sXG4gICAgbG9vbmE6IExvb25hLFxuICAgIG1hbmFnZXI6IE1hbmFnZXIsXG4gICAgcnVubmVyOiBFZmZlY3RzUnVubmVyLFxuICAgIGluamVjdG9yOiBJbmplY3RvcixcbiAgKSB7XG4gICAgcnVubmVyLnN0YXJ0KCk7XG5cbiAgICBjb25zdCB7bmFtZXMsIGFkZH0gPSBtYXBTdGF0ZXMoKTtcblxuICAgIHN0YXRlcy5mb3JFYWNoKHN0YXRlID0+IHtcbiAgICAgIGNvbnN0IHtpbnN0YW5jZSwgbWV0YX0gPSBleHRyYWN0U3RhdGUoc3RhdGUsIGluamVjdG9yKTtcblxuICAgICAgbWFuYWdlci5hZGRTdGF0ZShpbnN0YW5jZSwgbWV0YSwgaGFuZGxlT2JzZXJ2YWJsZSk7XG4gICAgICB0aGlzLmFkZEVmZmVjdHMoaW5zdGFuY2UsIG1ldGEuZWZmZWN0cyk7XG4gICAgICBhZGQoaW5zdGFuY2UpO1xuICAgIH0pO1xuXG4gICAgbG9vbmEuZGlzcGF0Y2goe1xuICAgICAgdHlwZTogUk9PVF9FRkZFQ1RTX0lOSVQsXG4gICAgICBzdGF0ZXM6IG5hbWVzLFxuICAgIH0pO1xuICB9XG5cbiAgYWRkRWZmZWN0cyhzdGF0ZTogYW55LCBtZXRhPzogTWV0YWRhdGEuRWZmZWN0cykge1xuICAgIHRoaXMuZWZmZWN0cy5hZGRFZmZlY3RzKHN0YXRlLCBtZXRhKTtcbiAgfVxufVxuXG5ATmdNb2R1bGUoKVxuZXhwb3J0IGNsYXNzIExvb25hQ2hpbGRNb2R1bGUge1xuICBjb25zdHJ1Y3RvcihcbiAgICBASW5qZWN0KENISUxEX1NUQVRFKSBzdGF0ZXM6IFN0YXRlQ2xhc3M8TWV0YWRhdGE+W10sXG4gICAgaW5qZWN0b3I6IEluamVjdG9yLFxuICAgIG1hbmFnZXI6IE1hbmFnZXIsXG4gICAgbG9vbmE6IExvb25hLFxuICAgIHJvb3RNb2R1bGU6IExvb25hUm9vdE1vZHVsZSxcbiAgKSB7XG4gICAgY29uc3Qge25hbWVzLCBhZGR9ID0gbWFwU3RhdGVzKCk7XG5cbiAgICBzdGF0ZXMuZm9yRWFjaChzdGF0ZSA9PiB7XG4gICAgICBjb25zdCB7aW5zdGFuY2UsIG1ldGF9ID0gZXh0cmFjdFN0YXRlKHN0YXRlLCBpbmplY3Rvcik7XG5cbiAgICAgIG1hbmFnZXIuYWRkU3RhdGUoaW5zdGFuY2UsIG1ldGEsIGhhbmRsZU9ic2VydmFibGUpO1xuICAgICAgcm9vdE1vZHVsZS5hZGRFZmZlY3RzKGluc3RhbmNlLCBtZXRhLmVmZmVjdHMpO1xuICAgICAgYWRkKGluc3RhbmNlKTtcbiAgICB9KTtcblxuICAgIGxvb25hLmRpc3BhdGNoKHtcbiAgICAgIHR5cGU6IFVQREFURV9FRkZFQ1RTLFxuICAgICAgc3RhdGVzOiBuYW1lcyxcbiAgICB9KTtcbiAgfVxufVxuXG5ATmdNb2R1bGUoKVxuZXhwb3J0IGNsYXNzIExvb25hTW9kdWxlIHtcbiAgc3RhdGljIGZvclJvb3Qoc3RhdGVzOiBhbnlbXSA9IFtdKTogTW9kdWxlV2l0aFByb3ZpZGVycyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG5nTW9kdWxlOiBMb29uYVJvb3RNb2R1bGUsXG4gICAgICBwcm92aWRlcnM6IFtcbiAgICAgICAgTG9vbmEsXG4gICAgICAgIElubmVyQWN0aW9ucyxcbiAgICAgICAgU2Nhbm5lZEFjdGlvbnMsXG4gICAgICAgIHtcbiAgICAgICAgICBwcm92aWRlOiBBY3Rpb25zLFxuICAgICAgICAgIHVzZUV4aXN0aW5nOiBTY2FubmVkQWN0aW9ucyxcbiAgICAgICAgfSxcbiAgICAgICAgLi4uc3RhdGVzLFxuICAgICAgICB7cHJvdmlkZTogSU5JVElBTF9TVEFURSwgdXNlVmFsdWU6IHN0YXRlc30sXG4gICAgICAgIHtcbiAgICAgICAgICBwcm92aWRlOiBNYW5hZ2VyLFxuICAgICAgICAgIHVzZUZhY3Rvcnk6IG1hbmFnZXJGYWN0b3J5LFxuICAgICAgICAgIGRlcHM6IFtMT09OQV9DQUNIRSwgSW5qZWN0b3JdLFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgcHJvdmlkZTogTG9vbmFMaW5rLFxuICAgICAgICAgIHVzZUZhY3Rvcnk6IGxpbmtGYWN0b3J5LFxuICAgICAgICAgIGRlcHM6IFtNYW5hZ2VyXSxcbiAgICAgICAgfSxcbiAgICAgICAgRWZmZWN0cyxcbiAgICAgICAgRWZmZWN0c1J1bm5lcixcbiAgICAgIF0sXG4gICAgfTtcbiAgfVxuXG4gIHN0YXRpYyBmb3JDaGlsZChzdGF0ZXM6IGFueVtdID0gW10pOiBNb2R1bGVXaXRoUHJvdmlkZXJzIHtcbiAgICByZXR1cm4ge1xuICAgICAgbmdNb2R1bGU6IExvb25hQ2hpbGRNb2R1bGUsXG4gICAgICBwcm92aWRlcnM6IFsuLi5zdGF0ZXMsIHtwcm92aWRlOiBDSElMRF9TVEFURSwgdXNlVmFsdWU6IHN0YXRlc31dLFxuICAgIH07XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxpbmtGYWN0b3J5KG1hbmFnZXI6IE1hbmFnZXIpOiBMb29uYUxpbmsge1xuICByZXR1cm4gbmV3IExvb25hTGluayhtYW5hZ2VyKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1hbmFnZXJGYWN0b3J5KFxuICBjYWNoZTogQXBvbGxvQ2FjaGU8YW55PixcbiAgaW5qZWN0b3I6IEluamVjdG9yLFxuKTogTWFuYWdlciB7XG4gIGNvbnN0IG1hbmFnZXIgPSBuZXcgTWFuYWdlcih7XG4gICAgY2FjaGUsXG4gICAgZ2V0Q2xpZW50OiAoKSA9PiBpbmplY3Rvci5nZXQoQXBvbGxvKS5nZXRDbGllbnQoKSxcbiAgfSk7XG5cbiAgcmV0dXJuIG1hbmFnZXI7XG59XG4iXSwibmFtZXMiOlsidHNsaWJfMS5fX2V4dGVuZHMiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUlBLElBQWEsYUFBYSxHQUFHLElBQUksY0FBYyxDQUM3QyxhQUFhLENBQ2Q7O0FBQ0QsSUFBYSxXQUFXLEdBQUcsSUFBSSxjQUFjLENBQzNDLGtCQUFrQixDQUNuQjs7QUFDRCxJQUFhLFdBQVcsR0FBRyxJQUFJLGNBQWMsQ0FBbUIsYUFBYSxDQUFDOztBQUU5RSxJQUFhLElBQUksR0FBRyxRQUFROztBQUM1QixJQUFhLGlCQUFpQixHQUFHLGdCQUFnQjs7QUFDakQsSUFBYSxjQUFjLEdBQUcsa0JBQWtCOzs7Ozs7Ozs7QUNSaEQ7Ozs7SUFBeUNBLDJCQUFhO0lBQXREOztLQUF5RDtJQUFELGNBQUM7Q0FBekQsQ0FBeUMsVUFBVSxHQUFNOztJQUdyQkEsa0NBQWU7SUFEbkQ7O0tBS0M7Ozs7SUFIQyxvQ0FBVzs7O0lBQVg7UUFDRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7S0FDakI7O2dCQUpGLFVBQVU7O0lBS1gscUJBQUM7Q0FBQSxDQUptQyxPQUFPLEdBSTFDOztJQUdpQ0EsZ0NBQXVCO0lBQ3ZEO2VBQ0Usa0JBQU0sRUFBQyxJQUFJLEVBQUUsSUFBSSxFQUFDLENBQUM7S0FDcEI7Ozs7O0lBRUQsMkJBQUk7Ozs7SUFBSixVQUFLLE1BQWM7UUFDakIsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLEVBQUU7WUFDakMsTUFBTSxJQUFJLFNBQVMsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1NBQ2hEO2FBQU0sSUFBSSxPQUFPLE1BQU0s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