@isaiahiroko/ng-interceptors
Version:
Angular interceptors for token authorization, local and remote store synchronization and global notification
183 lines (175 loc) • 16.4 kB
JavaScript
import { Injectable } from '@angular/core';
import { AuthService } from '@isaiahiroko/ng-utils';
import { finalize, retryWhen, zip, mergeMap, catchError } from 'rxjs/operators';
import { range, timer, throwError } from 'rxjs';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class AuthInterceptor {
/**
* @param {?} auth
*/
constructor(auth) {
this.auth = auth;
}
/**
* @param {?} req
* @param {?} next
* @return {?}
*/
intercept(req, next) {
/** @type {?} */
const authReq = req.clone({
headers: req.headers.set('Authorization', this.auth.token()),
});
// send cloned request with header to the next handler.
return next.handle(authReq);
}
}
AuthInterceptor.decorators = [
{ type: Injectable },
];
/** @nocollapse */
AuthInterceptor.ctorParameters = () => [
{ type: AuthService }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class LoaderServiceContract {
/**
* @param {?=} type
* @return {?}
*/
start(type) { }
/**
* @return {?}
*/
stop() { }
/**
* @param {?} message
* @param {?=} title
* @return {?}
*/
notify(message, title) { }
/**
* @param {?} message
* @param {?=} title
* @return {?}
*/
toast(message, title) { }
}
class NotifInterceptor {
/**
* @param {?} notif
*/
constructor(notif) {
this.notif = notif;
}
/**
* @param {?} req
* @param {?} next
* @return {?}
*/
intercept(req, next) {
this.notif.start();
// extend server response observable with logging
return next.handle(req).pipe(
// things don't always work as planned
// if things go wrong, try again three times
// however, delay trial in an incremental manner (multipl of 1 second)
retryWhen((attempts) => {
return attempts.pipe(zip(range(1, 4)), mergeMap(([error, i]) => {
this.notif.start('query');
if (i > 2) {
return throwError(error);
}
return timer(i * 1000);
}));
}),
// catchError http errors and turn to observable of undefined
catchError((e) => {
/** @type {?} */
let title;
/** @type {?} */
let message;
this.notif.start('buffer');
if (e.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
title = 'Client Error';
message = e.error.message;
}
else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
title = 'Network Info';
message = `${e.statusText} (${e.status})`;
// message = e.message
}
if (e.status === 401) ;
console.error({ title, message });
// catche //re throw
// throw {title, message}
// return of({ e: {title, message} })
// return empty()
return throwError({ title, message });
}), finalize(() => {
this.notif.stop();
}));
}
}
NotifInterceptor.decorators = [
{ type: Injectable },
];
/** @nocollapse */
NotifInterceptor.ctorParameters = () => [
{ type: LoaderServiceContract }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class StoreSyncInterceptor {
/**
* @param {?} req
* @param {?} next
* @return {?}
*/
intercept(req, next) {
// continue if not cachable.
// if (!isCachable(req)) { return next.handle(req); }
{
return next.handle(req);
}
// cache-or-fetch
// const cachedResponse = this.cache.get(req);
// return cachedResponse ?
// of(cachedResponse) : sendRequest(req, next, this.cache);
}
}
StoreSyncInterceptor.decorators = [
{ type: Injectable },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/** @type {?} */
const INTERCEPTORS = [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
// { provide: HTTP_INTERCEPTORS, useClass: StoreSyncInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: NotifInterceptor, multi: true }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
export { INTERCEPTORS, StoreSyncInterceptor, AuthInterceptor, LoaderServiceContract, NotifInterceptor };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXNhaWFoaXJva28tbmctaW50ZXJjZXB0b3JzLmpzLm1hcCIsInNvdXJjZXMiOlsibmc6Ly9AaXNhaWFoaXJva28vbmctaW50ZXJjZXB0b3JzL2xpYi9hdXRoLmludGVyY2VwdG9yLnRzIiwibmc6Ly9AaXNhaWFoaXJva28vbmctaW50ZXJjZXB0b3JzL2xpYi9ub3RpZi5pbnRlcmNlcHRvci50cyIsIm5nOi8vQGlzYWlhaGlyb2tvL25nLWludGVyY2VwdG9ycy9saWIvc3RvcmUtc3luYy5pbnRlcmNlcHRvci50cyIsIm5nOi8vQGlzYWlhaGlyb2tvL25nLWludGVyY2VwdG9ycy9saWIvaW5kZXgudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXHJcbmltcG9ydCB7IEh0dHBJbnRlcmNlcHRvciwgSHR0cFJlcXVlc3QsIEh0dHBIYW5kbGVyLCBIdHRwRXZlbnQgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCdcclxuXHJcbmltcG9ydCB7IEF1dGhTZXJ2aWNlIH0gZnJvbSAnQGlzYWlhaGlyb2tvL25nLXV0aWxzJ1xyXG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XHJcblxyXG5ASW5qZWN0YWJsZSgpXHJcbmV4cG9ydCBjbGFzcyBBdXRoSW50ZXJjZXB0b3IgaW1wbGVtZW50cyBIdHRwSW50ZXJjZXB0b3Ige1xyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgYXV0aDogQXV0aFNlcnZpY2UpIHt9XHJcblxyXG4gIGludGVyY2VwdChyZXE6IEh0dHBSZXF1ZXN0PGFueT4sIG5leHQ6IEh0dHBIYW5kbGVyKTogT2JzZXJ2YWJsZTxIdHRwRXZlbnQ8YW55Pj4ge1xyXG4gICAgLy8gR2V0IHRoZSBhdXRoIHRva2VuIGZyb20gdGhlIHNlcnZpY2UuXHJcbiAgICAvLyBDbG9uZSB0aGUgcmVxdWVzdCBhbmQgcmVwbGFjZSB0aGUgb3JpZ2luYWwgaGVhZGVycyB3aXRoXHJcbiAgICAvLyBjbG9uZWQgaGVhZGVycywgdXBkYXRlZCB3aXRoIHRoZSBhdXRob3JpemF0aW9uLlxyXG4gICAgY29uc3QgYXV0aFJlcSA9IHJlcS5jbG9uZSh7XHJcbiAgICAgIGhlYWRlcnM6IHJlcS5oZWFkZXJzLnNldCgnQXV0aG9yaXphdGlvbicsIHRoaXMuYXV0aC50b2tlbigpKSxcclxuICAgIH0pXHJcblxyXG4gICAgLy8gc2VuZCBjbG9uZWQgcmVxdWVzdCB3aXRoIGhlYWRlciB0byB0aGUgbmV4dCBoYW5kbGVyLlxyXG4gICAgcmV0dXJuIG5leHQuaGFuZGxlKGF1dGhSZXEpXHJcbiAgfVxyXG59XHJcbiIsImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJ1xyXG5pbXBvcnQgeyBIdHRwUmVxdWVzdCwgSHR0cEhhbmRsZXIsIEh0dHBJbnRlcmNlcHRvciwgSHR0cEV2ZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnXHJcblxyXG5pbXBvcnQgeyBmaW5hbGl6ZSwgcmV0cnlXaGVuLCB6aXAsIG1lcmdlTWFwLCBjYXRjaEVycm9yIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnXHJcbmltcG9ydCB7IHJhbmdlLCAgdGltZXIsICB0aHJvd0Vycm9yLCBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcydcclxuXHJcbmV4cG9ydCBjbGFzcyBMb2FkZXJTZXJ2aWNlQ29udHJhY3Qge1xyXG4gIHN0YXJ0ICh0eXBlPzogc3RyaW5nKSB7fVxyXG4gIHN0b3AgKCkge31cclxuICBub3RpZnkgKG1lc3NhZ2U6IHN0cmluZywgdGl0bGU/OiBzdHJpbmcpIHt9XHJcbiAgdG9hc3QgKG1lc3NhZ2U6IHN0cmluZywgdGl0bGU/OiBzdHJpbmcpIHt9XHJcbn1cclxuXHJcbkBJbmplY3RhYmxlKClcclxuZXhwb3J0IGNsYXNzIE5vdGlmSW50ZXJjZXB0b3IgaW1wbGVtZW50cyBIdHRwSW50ZXJjZXB0b3Ige1xyXG4gIGNvbnN0cnVjdG9yIChcclxuICAgIHByaXZhdGUgbm90aWY6IExvYWRlclNlcnZpY2VDb250cmFjdCxcclxuICApIHt9XHJcblxyXG4gIGludGVyY2VwdCAocmVxOiBIdHRwUmVxdWVzdDxhbnk+LCBuZXh0OiBIdHRwSGFuZGxlcik6IE9ic2VydmFibGU8SHR0cEV2ZW50PGFueT4+IHtcclxuICAgIHRoaXMubm90aWYuc3RhcnQoKVxyXG5cclxuICAgIC8vIGV4dGVuZCBzZXJ2ZXIgcmVzcG9uc2Ugb2JzZXJ2YWJsZSB3aXRoIGxvZ2dpbmdcclxuICAgIHJldHVybiBuZXh0LmhhbmRsZShyZXEpLnBpcGUoXHJcbiAgICAgIC8vIHRoaW5ncyBkb24ndCBhbHdheXMgd29yayBhcyBwbGFubmVkXHJcbiAgICAgIC8vIGlmIHRoaW5ncyBnbyB3cm9uZywgdHJ5IGFnYWluIHRocmVlIHRpbWVzXHJcbiAgICAgIC8vIGhvd2V2ZXIsIGRlbGF5IHRyaWFsIGluIGFuIGluY3JlbWVudGFsIG1hbm5lciAobXVsdGlwbCBvZiAxIHNlY29uZClcclxuICAgICAgcmV0cnlXaGVuKChhdHRlbXB0cykgPT4ge1xyXG4gICAgICAgIHJldHVybiBhdHRlbXB0cy5waXBlKFxyXG4gICAgICAgICAgemlwKHJhbmdlKDEsIDQpKSxcclxuICAgICAgICAgIG1lcmdlTWFwKChbZXJyb3IsIGldKSA9PiB7XHJcbiAgICAgICAgICAgIHRoaXMubm90aWYuc3RhcnQoJ3F1ZXJ5JylcclxuICAgICAgICAgICAgaWYgKGkgPiAyKSB7XHJcbiAgICAgICAgICAgICAgcmV0dXJuIHRocm93RXJyb3IoZXJyb3IpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHRpbWVyKGkgKiAxMDAwKVxyXG4gICAgICAgICAgfSlcclxuICAgICAgICApXHJcbiAgICAgIH0pLFxyXG5cclxuICAgICAgLy8gY2F0Y2hFcnJvciBodHRwIGVycm9ycyBhbmQgdHVybiB0byBvYnNlcnZhYmxlIG9mIHVuZGVmaW5lZFxyXG4gICAgICBjYXRjaEVycm9yKChlKSA9PiB7XHJcbiAgICAgICAgbGV0IHRpdGxlLCBtZXNzYWdlXHJcblxyXG4gICAgICAgIHRoaXMubm90aWYuc3RhcnQoJ2J1ZmZlcicpXHJcblxyXG4gICAgICAgIGlmIChlLmVycm9yIGluc3RhbmNlb2YgRXJyb3JFdmVudCkge1xyXG4gICAgICAgICAgLy8gQSBjbGllbnQtc2lkZSBvciBuZXR3b3JrIGVycm9yIG9jY3VycmVkLiBIYW5kbGUgaXQgYWNjb3JkaW5nbHkuXHJcbiAgICAgICAgICB0aXRsZSA9ICdDbGllbnQgRXJyb3InXHJcbiAgICAgICAgICBtZXNzYWdlID0gZS5lcnJvci5tZXNzYWdlXHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIFRoZSBiYWNrZW5kIHJldHVybmVkIGFuIHVuc3VjY2Vzc2Z1bCByZXNwb25zZSBjb2RlLlxyXG4gICAgICAgICAgLy8gVGhlIHJlc3BvbnNlIGJvZHkgbWF5IGNvbnRhaW4gY2x1ZXMgYXMgdG8gd2hhdCB3ZW50IHdyb25nLFxyXG4gICAgICAgICAgdGl0bGUgPSAnTmV0d29yayBJbmZvJ1xyXG4gICAgICAgICAgbWVzc2FnZSA9IGAke2Uuc3RhdHVzVGV4dH0gKCR7ZS5zdGF0dXN9KWBcclxuICAgICAgICAgIC8vIG1lc3NhZ2UgPSBlLm1lc3NhZ2VcclxuICAgICAgICB9XHJcbiAgICAgICAgXHJcbiAgICAgICAgaWYoZS5zdGF0dXMgPT09IDQwMSl7XHJcbiAgICAgICAgICAvLyB0aGlzLm5vdGlmLm5vdGlmeSgnVGhlIHVzZXJuYW1lL3Bhc3N3b3JkIGlzIHdyb25nJywgJ1VuYXV0aG9yaXplZCcpXHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2V7XHJcbiAgICAgICAgICAvLyB0aGlzLm5vdGlmLnRvYXN0KG1lc3NhZ2UsIHRpdGxlKVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY29uc29sZS5lcnJvcih7IHRpdGxlLCBtZXNzYWdlIH0pO1xyXG4gICAgICAgIFxyXG4gICAgICAgIC8vIGNhdGNoZSAvL3JlIHRocm93XHJcbiAgICAgICAgLy8gdGhyb3cge3RpdGxlLCBtZXNzYWdlfVxyXG4gICAgICAgIC8vIHJldHVybiBvZih7IGU6IHt0aXRsZSwgbWVzc2FnZX0gfSlcclxuICAgICAgICAvLyByZXR1cm4gZW1wdHkoKVxyXG4gICAgICAgIHJldHVybiB0aHJvd0Vycm9yKHsgdGl0bGUsIG1lc3NhZ2V9KVxyXG4gICAgICB9KSxcclxuICAgICAgZmluYWxpemUoKCkgPT4ge1xyXG4gICAgICAgIHRoaXMubm90aWYuc3RvcCgpXHJcbiAgICAgIH0pLFxyXG4gICAgKVxyXG4gIH1cclxufVxyXG4iLCJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSdcclxuaW1wb3J0IHtcclxuICBIdHRwSW50ZXJjZXB0b3IsXHJcbiAgSHR0cFJlcXVlc3QsXHJcbiAgSHR0cEhhbmRsZXIsXHJcbiAgSHR0cEV2ZW50LFxyXG59IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJ1xyXG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XHJcblxyXG5ASW5qZWN0YWJsZSgpXHJcbmV4cG9ydCBjbGFzcyBTdG9yZVN5bmNJbnRlcmNlcHRvciBpbXBsZW1lbnRzIEh0dHBJbnRlcmNlcHRvciB7XHJcbiAgLy8gY29uc3RydWN0b3IocHJpdmF0ZSBjYWNoZTogUmVxdWVzdENhY2hlKSB7fVxyXG5cclxuICBpbnRlcmNlcHQocmVxOiBIdHRwUmVxdWVzdDxhbnk+LCBuZXh0OiBIdHRwSGFuZGxlcik6IE9ic2VydmFibGU8SHR0cEV2ZW50PGFueT4+ICB7XHJcbiAgICAvLyBjb250aW51ZSBpZiBub3QgY2FjaGFibGUuXHJcbiAgICAvLyBpZiAoIWlzQ2FjaGFibGUocmVxKSkgeyByZXR1cm4gbmV4dC5oYW5kbGUocmVxKTsgfVxyXG4gICAgaWYgKHRydWUpIHtcclxuICAgICAgcmV0dXJuIG5leHQuaGFuZGxlKHJlcSlcclxuICAgIH1cclxuXHJcbiAgICAvLyBjYWNoZS1vci1mZXRjaFxyXG4gICAgLy8gY29uc3QgY2FjaGVkUmVzcG9uc2UgPSB0aGlzLmNhY2hlLmdldChyZXEpO1xyXG4gICAgLy8gcmV0dXJuIGNhY2hlZFJlc3BvbnNlID9cclxuICAgIC8vICAgb2YoY2FjaGVkUmVzcG9uc2UpIDogc2VuZFJlcXVlc3QocmVxLCBuZXh0LCB0aGlzLmNhY2hlKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldCBzZXJ2ZXIgcmVzcG9uc2Ugb2JzZXJ2YWJsZSBieSBzZW5kaW5nIHJlcXVlc3QgdG8gYG5leHQoKWAuXHJcbiAgICogV2lsbCBhZGQgdGhlIHJlc3BvbnNlIHRvIHRoZSBjYWNoZSBvbiB0aGUgd2F5IG91dC5cclxuICAgKi9cclxuICAvLyAgc2VuZFJlcXVlc3QoXHJcbiAgLy8gICAgIHJlcTogSHR0cFJlcXVlc3Q8YW55PixcclxuICAvLyAgICAgbmV4dDogSHR0cEhhbmRsZXIsXHJcbiAgLy8gICAgIGNhY2hlOiBSZXF1ZXN0Q2FjaGUpOiBPYnNlcnZhYmxlPEh0dHBFdmVudDxhbnk+PiB7XHJcblxyXG4gIC8vICAgICAvLyBObyBoZWFkZXJzIGFsbG93ZWQgaW4gbnBtIHNlYXJjaCByZXF1ZXN0XHJcbiAgLy8gICAgIGNvbnN0IG5vSGVhZGVyUmVxID0gcmVxLmNsb25lKHsgaGVhZGVyczogbmV3IEh0dHBIZWFkZXJzKCkgfSk7XHJcblxyXG4gIC8vICAgICByZXR1cm4gbmV4dC5oYW5kbGUobm9IZWFkZXJSZXEpLnBpcGUoXHJcbiAgLy8gICAgICAgdGFwKGV2ZW50ID0+IHtcclxuICAvLyAgICAgICAgIC8vIFRoZXJlIG1heSBiZSBvdGhlciBldmVudHMgYmVzaWRlcyB0aGUgcmVzcG9uc2UuXHJcbiAgLy8gICAgICAgICBpZiAoZXZlbnQgaW5zdGFuY2VvZiBIdHRwUmVzcG9uc2UpIHtcclxuICAvLyAgICAgICAgICAgY2FjaGUucHV0KHJlcSwgZXZlbnQpOyAvLyBVcGRhdGUgdGhlIGNhY2hlLlxyXG4gIC8vICAgICAgICAgfVxyXG4gIC8vICAgICAgIH0pXHJcbiAgLy8gICAgICk7XHJcbiAgLy8gICB9XHJcblxyXG4gIC8vIGNhY2hlLXRoZW4tcmVmcmVzaFxyXG4gIC8vIGlmIChyZXEuaGVhZGVycy5nZXQoJ3gtcmVmcmVzaCcpKSB7XHJcbiAgLy8gICAgIGNvbnN0IHJlc3VsdHMkID0gc2VuZFJlcXVlc3QocmVxLCBuZXh0LCB0aGlzLmNhY2hlKTtcclxuICAvLyAgICAgcmV0dXJuIGNhY2hlZFJlc3BvbnNlID9cclxuICAvLyAgICAgICByZXN1bHRzJC5waXBlKCBzdGFydFdpdGgoY2FjaGVkUmVzcG9uc2UpICkgOlxyXG4gIC8vICAgICAgIHJlc3VsdHMkO1xyXG4gIC8vICAgfVxyXG59XHJcbiIsImltcG9ydCB7IFN0b3JlU3luY0ludGVyY2VwdG9yIH0gZnJvbSAnLi9zdG9yZS1zeW5jLmludGVyY2VwdG9yJ1xyXG5pbXBvcnQgeyBBdXRoSW50ZXJjZXB0b3IgfSBmcm9tICcuL2F1dGguaW50ZXJjZXB0b3InXHJcbmltcG9ydCB7IE5vdGlmSW50ZXJjZXB0b3IgfSBmcm9tICcuL25vdGlmLmludGVyY2VwdG9yJ1xyXG5pbXBvcnQgeyBIVFRQX0lOVEVSQ0VQVE9SUyB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJ1xyXG5cclxuZXhwb3J0IGNvbnN0IElOVEVSQ0VQVE9SUzogYW55W10gPSBbXHJcbiAgeyBwcm92aWRlOiBIVFRQX0lOVEVSQ0VQVE9SUywgdXNlQ2xhc3M6IEF1dGhJbnRlcmNlcHRvciwgbXVsdGk6IHRydWUgfSxcclxuICAvLyB7IHByb3ZpZGU6IEhUVFBfSU5URVJDRVBUT1JTLCB1c2VDbGFzczogU3RvcmVTeW5jSW50ZXJjZXB0b3IsIG11bHRpOiB0cnVlIH0sXHJcbiAgeyBwcm92aWRlOiBIVFRQX0lOVEVSQ0VQVE9SUywgdXNlQ2xhc3M6IE5vdGlmSW50ZXJjZXB0b3IsIG11bHRpOiB0cnVlIH1cclxuXVxyXG5cclxuZXhwb3J0ICogZnJvbSAnLi9zdG9yZS1zeW5jLmludGVyY2VwdG9yJ1xyXG5leHBvcnQgKiBmcm9tICcuL2F1dGguaW50ZXJjZXB0b3InXHJcbmV4cG9ydCAqIGZyb20gJy4vbm90aWYuaW50ZXJjZXB0b3InXHJcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUE7Ozs7SUFRRSxZQUFvQixJQUFpQjtRQUFqQixTQUFJLEdBQUosSUFBSSxDQUFhO0tBQUk7Ozs7OztJQUV6QyxTQUFTLENBQUMsR0FBcUIsRUFBRSxJQUFpQjs7UUFJaEQsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQztZQUN4QixPQUFPLEVBQUUsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7U0FDN0QsQ0FBQyxDQUFBOztRQUdGLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQTtLQUM1Qjs7O1lBZEYsVUFBVTs7OztZQUhGLFdBQVc7Ozs7Ozs7QUNIcEI7Ozs7O0lBT0UsS0FBSyxDQUFFLElBQWEsS0FBSTs7OztJQUN4QixJQUFJLE1BQU07Ozs7OztJQUNWLE1BQU0sQ0FBRSxPQUFlLEVBQUUsS0FBYyxLQUFJOzs7Ozs7SUFDM0MsS0FBSyxDQUFFLE9BQWUsRUFBRSxLQUFjLEtBQUk7Q0FDM0M7QUFHRDs7OztJQUNFLFlBQ1U7UUFBQSxVQUFLLEdBQUwsS0FBSztLQUNYOzs7Ozs7SUFFSixTQUFTLENBQUUsR0FBcUIsRUFBRSxJQUFpQjtRQUNqRCxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxDQUFBOztRQUdsQixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSTs7OztRQUkxQixTQUFTLENBQUMsQ0FBQyxRQUFRO1lBQ2pCLE9BQU8sUUFBUSxDQUFDLElBQUksQ0FDbEIsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFDaEIsUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUNsQixJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQTtnQkFDekIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFO29CQUNULE9BQU8sVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFBO2lCQUN6QjtnQkFDRCxPQUFPLEtBQUssQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUE7YUFDdkIsQ0FBQyxDQUNILENBQUE7U0FDRixDQUFDOztRQUdGLFVBQVUsQ0FBQyxDQUFDLENBQUM7O1lBQ1gsSUFBSSxLQUFLLENBQVM7O1lBQWxCLElBQVcsT0FBTyxDQUFBO1lBRWxCLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxDQUFBO1lBRTFCLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxVQUFVLEVBQUU7O2dCQUVqQyxLQUFLLEdBQUcsY0FBYyxDQUFBO2dCQUN0QixPQUFPLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUE7YUFDMUI7aUJBQU07OztnQkFHTCxLQUFLLEdBQUcsY0FBYyxDQUFBO2dCQUN0QixPQUFPLEdBQUcsR0FBRyxDQUFDLENBQUMsVUFBVSxLQUFLLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQTs7YUFFMUM7WUFFRCxJQUFHLENBQUMsQ0FBQyxNQUFNLEtBQUssR0FBRyxFQUFDLENBS25CO1lBRUQsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDOzs7OztZQU1sQyxPQUFPLFVBQVUsQ0FBQyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUMsQ0FBQyxDQUFBO1NBQ3JDLENBQUMsRUFDRixRQUFRLENBQUM7WUFDUCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFBO1NBQ2xCLENBQUMsQ0FDSCxDQUFBO0tBQ0Y7OztZQWhFRixVQUFVOzs7O1lBR1EscUJBQXFCOzs7Ozs7O0FDaEJ4Qzs7Ozs7O0lBYUUsU0FBUyxDQUFDLEdBQXFCLEVBQUUsSUFBaUI7OztRQUdoRCxBQUFVO1lBQ1IsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1NBQ3hCOzs7OztLQU1GOzs7WUFmRixVQUFVOzs7Ozs7O0FDUlg7QUFJQSxNQUFhLFlBQVksR0FBVTtJQUNqQyxFQUFFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxRQUFRLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUU7O0lBRXRFLEVBQUUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLFFBQVEsRUFBRSxnQkFBZ0IsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFO0NBQ3hFOzs7Ozs7Ozs7Ozs7OzsifQ==