@datorama/akita
Version:
State Management Tailored-Made for JS Applications
387 lines • 27.4 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { BehaviorSubject } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { assertStoreHasName } from './errors';
import { commit, isTransactionInProcess } from './transaction';
import { deepFreeze } from './deepFreeze';
import { configKey } from './storeConfig';
import { getAkitaConfig } from './config';
import { isPlainObject } from './isPlainObject';
import { isFunction } from './isFunction';
import { dispatchAdded, dispatchDeleted, dispatchUpdate } from './dispatchers';
import { __stores__ } from './stores';
import { resetCustomAction, setAction } from './actions';
import { isNotBrowser } from './root';
import { __DEV__, isDev } from './env';
/**
*
* Store for managing any type of data
*
* \@example
*
* export interface SessionState {
* token: string;
* userDetails: UserDetails
* }
*
* export function createInitialState(): SessionState {
* return {
* token: '',
* userDetails: null
* };
* }
*
* \@StoreConfig({ name: 'session' })
* export class SessionStore extends Store<SessionState> {
* constructor() {
* super(createInitialState());
* }
* }
* @template S
*/
export class Store {
/**
* @param {?} initialState
* @param {?=} options
*/
constructor(initialState, options = {}) {
this.options = options;
this.inTransaction = false;
this.cache = {
active: new BehaviorSubject(false),
ttl: null
};
this.onInit((/** @type {?} */ (initialState)));
}
/**
* Set the loading state
*
* \@example
*
* store.setLoading(true)
*
* @param {?=} loading
* @return {?}
*/
setLoading(loading = false) {
if (loading !== ((/** @type {?} */ (this._value()))).loading) {
isDev() && setAction('Set Loading');
this._setState((/**
* @param {?} state
* @return {?}
*/
state => ((/** @type {?} */ (Object.assign({}, state, { loading }))))));
}
}
/**
*
* Set whether the data is cached
*
* \@example
*
* store.setHasCache(true)
* store.setHasCache(false)
* store.setHasCache(true, { restartTTL: true })
*
* @param {?} hasCache
* @param {?=} options
* @return {?}
*/
setHasCache(hasCache, options = { restartTTL: false }) {
if (hasCache !== this.cache.active.value) {
this.cache.active.next(hasCache);
}
if (options.restartTTL) {
/** @type {?} */
const ttlConfig = this.cacheConfig && this.cacheConfig.ttl;
if (ttlConfig) {
if (this.cache.ttl !== null) {
clearTimeout(this.cache.ttl);
}
this.cache.ttl = (/** @type {?} */ (setTimeout((/**
* @return {?}
*/
() => this.setHasCache(false)), ttlConfig)));
}
}
}
/**
* Set the error state
*
* \@example
*
* store.setError({text: 'unable to load data' })
*
* @template T
* @param {?} error
* @return {?}
*/
setError(error) {
if (error !== ((/** @type {?} */ (this._value()))).error) {
isDev() && setAction('Set Error');
this._setState((/**
* @param {?} state
* @return {?}
*/
state => ((/** @type {?} */ (Object.assign({}, state, { error }))))));
}
}
// @internal
/**
* @template R
* @param {?} project
* @return {?}
*/
_select(project) {
return this.store.asObservable().pipe(map(project), distinctUntilChanged());
}
// @internal
/**
* @return {?}
*/
_value() {
return this.storeValue;
}
// @internal
/**
* @return {?}
*/
_cache() {
return this.cache.active;
}
// @internal
/**
* @return {?}
*/
get config() {
return this.constructor[configKey] || {};
}
// @internal
/**
* @return {?}
*/
get storeName() {
return ((/** @type {?} */ (this.config))).storeName || ((/** @type {?} */ (this.options))).storeName || this.options.name;
}
// @internal
/**
* @return {?}
*/
get deepFreeze() {
return this.config.deepFreezeFn || this.options.deepFreezeFn || deepFreeze;
}
// @internal
/**
* @return {?}
*/
get cacheConfig() {
return this.config.cache || this.options.cache;
}
// @internal
/**
* @return {?}
*/
get resettable() {
return this.config.resettable || this.options.resettable;
}
// @internal
/**
* @param {?} newStateFn
* @param {?=} _dispatchAction
* @return {?}
*/
_setState(newStateFn, _dispatchAction = true) {
this.storeValue = __DEV__ ? this.deepFreeze(newStateFn(this._value())) : newStateFn(this._value());
if (!this.store) {
this.store = new BehaviorSubject(this.storeValue);
return;
}
if (isTransactionInProcess()) {
this.handleTransaction();
return;
}
this.dispatch(this.storeValue, _dispatchAction);
}
/**
*
* Reset the current store back to the initial value
*
* \@example
*
* store.reset()
*
* @return {?}
*/
reset() {
if (this.isResettable()) {
isDev() && setAction('Reset');
this._setState((/**
* @return {?}
*/
() => Object.assign({}, this._initialState)));
this.setHasCache(false);
}
else {
isDev() && console.warn(`You need to enable the reset functionality`);
}
}
/**
* @param {?} stateOrCallback
* @return {?}
*/
update(stateOrCallback) {
isDev() && setAction('Update');
this._setState((/**
* @param {?} state
* @return {?}
*/
state => {
/** @type {?} */
const newState = isFunction(stateOrCallback) ? stateOrCallback(state) : stateOrCallback;
/** @type {?} */
const merged = this.akitaPreUpdate(state, (/** @type {?} */ (Object.assign({}, state, newState))));
return isPlainObject(state) ? merged : new ((/** @type {?} */ (state))).constructor(merged);
}));
}
/**
* @param {?} newOptions
* @return {?}
*/
updateStoreConfig(newOptions) {
this.options = Object.assign({}, this.options, newOptions);
}
// @internal
/**
* @param {?} _
* @param {?} nextState
* @return {?}
*/
akitaPreUpdate(_, nextState) {
return nextState;
}
/**
* @return {?}
*/
ngOnDestroy() {
this.destroy();
}
/**
*
* Destroy the store
*
* \@example
*
* store.destroy()
*
* @return {?}
*/
destroy() {
if (isNotBrowser)
return;
if (!((/** @type {?} */ (window))).hmrEnabled && this === __stores__[this.storeName]) {
delete __stores__[this.storeName];
dispatchDeleted(this.storeName);
this.setHasCache(false);
this.cache.active.complete();
}
}
/**
* @private
* @param {?} initialState
* @return {?}
*/
onInit(initialState) {
__stores__[this.storeName] = this;
this._setState((/**
* @return {?}
*/
() => initialState));
dispatchAdded(this.storeName);
if (this.isResettable()) {
this._initialState = initialState;
}
isDev() && assertStoreHasName(this.storeName, this.constructor.name);
}
/**
* @private
* @param {?} state
* @param {?=} _dispatchAction
* @return {?}
*/
dispatch(state, _dispatchAction = true) {
this.store.next(state);
if (_dispatchAction) {
dispatchUpdate(this.storeName);
resetCustomAction();
}
}
/**
* @private
* @return {?}
*/
watchTransaction() {
commit().subscribe((/**
* @return {?}
*/
() => {
this.inTransaction = false;
this.dispatch(this._value());
}));
}
/**
* @private
* @return {?}
*/
isResettable() {
if (this.resettable === false) {
return false;
}
return this.resettable || getAkitaConfig().resettable;
}
/**
* @private
* @return {?}
*/
handleTransaction() {
if (!this.inTransaction) {
this.watchTransaction();
this.inTransaction = true;
}
}
}
if (false) {
/**
* @type {?}
* @private
*/
Store.prototype.store;
/**
* @type {?}
* @private
*/
Store.prototype.storeValue;
/**
* @type {?}
* @private
*/
Store.prototype.inTransaction;
/**
* @type {?}
* @private
*/
Store.prototype._initialState;
/**
* @type {?}
* @protected
*/
Store.prototype.cache;
/**
* @type {?}
* @protected
*/
Store.prototype.options;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmUuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9AZGF0b3JhbWEvYWtpdGEvIiwic291cmNlcyI6WyJzcmMvc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQUNBLE9BQU8sRUFBRSxlQUFlLEVBQWMsTUFBTSxNQUFNLENBQUM7QUFDbkQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzNELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUM5QyxPQUFPLEVBQUUsTUFBTSxFQUFFLHNCQUFzQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQy9ELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDMUMsT0FBTyxFQUFFLFNBQVMsRUFBbUQsTUFBTSxlQUFlLENBQUM7QUFDM0YsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUMxQyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUMxQyxPQUFPLEVBQUUsYUFBYSxFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDL0UsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUN0QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDdEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxPQUFPLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTJCdkMsTUFBTSxPQUFPLEtBQUs7Ozs7O0lBVWhCLFlBQVksWUFBd0IsRUFBWSxVQUF1QyxFQUFFO1FBQXpDLFlBQU8sR0FBUCxPQUFPLENBQWtDO1FBUGpGLGtCQUFhLEdBQUcsS0FB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Uzs7O1FBQUMsR0FBRyxFQUFFO1lBQ3RCLElBQUksQ0FBQyxhQUFhLEdBQUcsS0FBSyxDQUFDO1lBQzNCLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDL0IsQ0FBQyxFQUFDLENBQUM7SUFDTCxDQUFDOzs7OztJQUVPLFlBQVk7UUFDbEIsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLEtBQUssRUFBRTtZQUM3QixPQUFPLEtBQUssQ0FBQztTQUNkO1FBQ0QsT0FBTyxJQUFJLENBQUMsVUFBVSxJQUFJLGNBQWMsRUFBRSxDQUFDLFVBQVUsQ0FBQztJQUN4RCxDQUFDOzs7OztJQUVPLGlCQUFpQjtRQUN2QixJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUN2QixJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztZQUN4QixJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQztTQUMzQjtJQUNILENBQUM7Q0FDRjs7Ozs7O0lBdlBDLHNCQUE0Qzs7Ozs7SUFDNUMsMkJBQXNCOzs7OztJQUN0Qiw4QkFBOEI7Ozs7O0lBQzlCLDhCQUF5Qjs7Ozs7SUFDekIsc0JBR0U7Ozs7O0lBRW9DLHdCQUFtRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFN0b3JlQ2FjaGUsIFVwZGF0ZVN0YXRlQ2FsbGJhY2sgfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGlzdGluY3RVbnRpbENoYW5nZWQsIG1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IGFzc2VydFN0b3JlSGFzTmFtZSB9IGZyb20gJy4vZXJyb3JzJztcbmltcG9ydCB7IGNvbW1pdCwgaXNUcmFuc2FjdGlvbkluUHJvY2VzcyB9IGZyb20gJy4vdHJhbnNhY3Rpb24nO1xuaW1wb3J0IHsgZGVlcEZyZWV6ZSB9IGZyb20gJy4vZGVlcEZyZWV6ZSc7XG5pbXBvcnQgeyBjb25maWdLZXksIFN0b3JlQ29uZmlnT3B0aW9ucywgVXBkYXRhYmxlU3RvcmVDb25maWdPcHRpb25zIH0gZnJvbSAnLi9zdG9yZUNvbmZpZyc7XG5pbXBvcnQgeyBnZXRBa2l0YUNvbmZpZyB9IGZyb20gJy4vY29uZmlnJztcbmltcG9ydCB7IGlzUGxhaW5PYmplY3QgfSBmcm9tICcuL2lzUGxhaW5PYmplY3QnO1xuaW1wb3J0IHsgaXNGdW5jdGlvbiB9IGZyb20gJy4vaXNGdW5jdGlvbic7XG5pbXBvcnQgeyBkaXNwYXRjaEFkZGVkLCBkaXNwYXRjaERlbGV0ZWQsIGRpc3BhdGNoVXBkYXRlIH0gZnJvbSAnLi9kaXNwYXRjaGVycyc7XG5pbXBvcnQgeyBfX3N0b3Jlc19fIH0gZnJvbSAnLi9zdG9yZXMnO1xuaW1wb3J0IHsgcmVzZXRDdXN0b21BY3Rpb24sIHNldEFjdGlvbiB9IGZyb20gJy4vYWN0aW9ucyc7XG5pbXBvcnQgeyBpc05vdEJyb3dzZXIgfSBmcm9tICcuL3Jvb3QnO1xuaW1wb3J0IHsgX19ERVZfXywgaXNEZXYgfSBmcm9tICcuL2Vudic7XG5cbi8qKlxuICpcbiAqIFN0b3JlIGZvciBtYW5hZ2luZyBhbnkgdHlwZSBvZiBkYXRhXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBleHBvcnQgaW50ZXJmYWNlIFNlc3Npb25TdGF0ZSB7XG4gKiAgIHRva2VuOiBzdHJpbmc7XG4gKiAgIHVzZXJEZXRhaWxzOiBVc2VyRGV0YWlsc1xuICogfVxuICpcbiAqIGV4cG9ydCBmdW5jdGlvbiBjcmVhdGVJbml0aWFsU3RhdGUoKTogU2Vzc2lvblN0YXRlIHtcbiAqICByZXR1cm4ge1xuICogICAgdG9rZW46ICcnLFxuICogICAgdXNlckRldGFpbHM6IG51bGxcbiAqICB9O1xuICogfVxuICpcbiAqIEBTdG9yZUNvbmZpZyh7IG5hbWU6ICdzZXNzaW9uJyB9KVxuICogZXhwb3J0IGNsYXNzIFNlc3Npb25TdG9yZSBleHRlbmRzIFN0b3JlPFNlc3Npb25TdGF0ZT4ge1xuICogICBjb25zdHJ1Y3RvcigpIHtcbiAqICAgIHN1cGVyKGNyZWF0ZUluaXRpYWxTdGF0ZSgpKTtcbiAqICAgfVxuICogfVxuICovXG5leHBvcnQgY2xhc3MgU3RvcmU8UyA9IGFueT4ge1xuICBwcml2YXRlIHN0b3JlOiBCZWhhdmlvclN1YmplY3Q8UmVhZG9ubHk8Uz4+O1xuICBwcml2YXRlIHN0b3JlVmFsdWU6IFM7XG4gIHByaXZhdGUgaW5UcmFuc2FjdGlvbiA9IGZhbHNlO1xuICBwcml2YXRlIF9pbml0aWFsU3RhdGU6IFM7XG4gIHByb3RlY3RlZCBjYWNoZTogU3RvcmVDYWNoZSA9IHtcbiAgICBhY3RpdmU6IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpLFxuICAgIHR0bDogbnVsbFxuICB9O1xuXG4gIGNvbnN0cnVjdG9yKGluaXRpYWxTdGF0ZTogUGFydGlhbDxTPiwgcHJvdGVjdGVkIG9wdGlvbnM6IFBhcnRpYWw8U3RvcmVDb25maWdPcHRpb25zPiA9IHt9KSB7XG4gICAgdGhpcy5vbkluaXQoaW5pdGlhbFN0YXRlIGFzIFMpO1xuICB9XG5cbiAgLyoqXG4gICAqICBTZXQgdGhlIGxvYWRpbmcgc3RhdGVcbiAgICpcbiAgICogIEBleGFtcGxlXG4gICAqXG4gICAqICBzdG9yZS5zZXRMb2FkaW5nKHRydWUpXG4gICAqXG4gICAqL1xuICBzZXRMb2FkaW5nKGxvYWRpbmcgPSBmYWxzZSkge1xuICAgIGlmIChsb2FkaW5nICE9PSAodGhpcy5fdmFsdWUoKSBhcyBTICYgeyBsb2FkaW5nOiBib29sZWFuIH0pLmxvYWRpbmcpIHtcbiAgICAgIGlzRGV2KCkgJiYgc2V0QWN0aW9uKCdTZXQgTG9hZGluZycpO1xuICAgICAgdGhpcy5fc2V0U3RhdGUoc3RhdGUgPT4gKHsgLi4uc3RhdGUsIGxvYWRpbmcgfSBhcyBTICYgeyBsb2FkaW5nOiBib29sZWFuIH0pKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICpcbiAgICogU2V0IHdoZXRoZXIgdGhlIGRhdGEgaXMgY2FjaGVkXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqXG4gICAqIHN0b3JlLnNldEhhc0NhY2hlKHRydWUpXG4gICAqIHN0b3JlLnNldEhhc0NhY2hlKGZhbHNlKVxuICAgKiBzdG9yZS5zZXRIYXNDYWNoZSh0cnVlLCB7IHJlc3RhcnRUVEw6IHRydWUgfSlcbiAgICpcbiAgICovXG4gIHNldEhhc0NhY2hlKGhhc0NhY2hlOiBib29sZWFuLCBvcHRpb25zOiB7IHJlc3RhcnRUVEw6IGJvb2xlYW4gfSA9IHsgcmVzdGFydFRUTDogZmFsc2UgfSkge1xuICAgIGlmIChoYXNDYWNoZSAhPT0gdGhpcy5jYWNoZS5hY3RpdmUudmFsdWUpIHtcbiAgICAgIHRoaXMuY2FjaGUuYWN0aXZlLm5leHQoaGFzQ2FjaGUpO1xuICAgIH1cblxuICAgIGlmIChvcHRpb25zLnJlc3RhcnRUVEwpIHtcbiAgICAgIGNvbnN0IHR0bENvbmZpZyA9IHRoaXMuY2FjaGVDb25maWcgJiYgdGhpcy5jYWNoZUNvbmZpZy50dGw7XG4gICAgICBpZiAodHRsQ29uZmlnKSB7XG4gICAgICAgIGlmICh0aGlzLmNhY2hlLnR0bCAhPT0gbnVsbCkge1xuICAgICAgICAgIGNsZWFyVGltZW91dCh0aGlzLmNhY2hlLnR0bCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5jYWNoZS50dGwgPSA8YW55PnNldFRpbWVvdXQoKCkgPT4gdGhpcy5zZXRIYXNDYWNoZShmYWxzZSksIHR0bENvbmZpZyk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqICBTZXQgdGhlIGVycm9yIHN0YXRlXG4gICAqXG4gICAqICBAZXhhbXBsZVxuICAgKlxuICAgKiAgc3RvcmUuc2V0RXJyb3Ioe3RleHQ6ICd1bmFibGUgdG8gbG9hZCBkYXRhJyB9KVxuICAgKlxuICAgKi9cbiAgc2V0RXJyb3I8VD4oZXJyb3I6IFQpIHtcbiAgICBpZiAoZXJyb3IgIT09ICh0aGlzLl92YWx1ZSgpIGFzIFMgJiB7IGVycm9yOiBhbnkgfSkuZXJyb3IpIHtcbiAgICAgIGlzRGV2KCkgJiYgc2V0QWN0aW9uKCdTZXQgRXJyb3InKTtcbiAgICAgIHRoaXMuX3NldFN0YXRlKHN0YXRlID0+ICh7IC4uLnN0YXRlLCBlcnJvciB9IGFzIFMgJiB7IGVycm9yOiBhbnkgfSkpO1xuICAgIH1cbiAgfVxuXG4gIC8vIEBpbnRlcm5hbFxuICBfc2VsZWN0PFI+KHByb2plY3Q6IChzdG9yZTogUykgPT4gUik6IE9ic2VydmFibGU8Uj4ge1xuICAgIHJldHVybiB0aGlzLnN0b3JlLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICBtYXAocHJvamVjdCksXG4gICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpXG4gICAgKTtcbiAgfVxuXG4gIC8vIEBpbnRlcm5hbFxuICBfdmFsdWUoKTogUyB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmVWYWx1ZTtcbiAgfVxuXG4gIC8vIEBpbnRlcm5hbFxuICBfY2FjaGUoKTogQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+IHtcbiAgICByZXR1cm4gdGhpcy5jYWNoZS5hY3RpdmU7XG4gIH1cblxuICAvLyBAaW50ZXJuYWxcbiAgZ2V0IGNvbmZpZygpOiBTdG9yZUNvbmZpZ09wdGlvbnMge1xuICAgIHJldHVybiB0aGlzLmNvbnN0cnVjdG9yW2NvbmZpZ0tleV0gfHwge307XG4gIH1cblxuICAvLyBAaW50ZXJuYWxcbiAgZ2V0IHN0b3JlTmFtZSgpIHtcbiAgICByZXR1cm4gKHRoaXMuY29uZmlnIGFzIFN0b3JlQ29uZmlnT3B0aW9ucyAmIHsgc3RvcmVOYW1lOiBzdHJpbmcgfSkuc3RvcmVOYW1lIHx8ICh0aGlzLm9wdGlvbnMgYXMgU3RvcmVDb25maWdPcHRpb25zICYgeyBzdG9yZU5hbWU6IHN0cmluZyB9KS5zdG9yZU5hbWUgfHwgdGhpcy5vcHRpb25zLm5hbWU7XG4gIH1cblxuICAvLyBAaW50ZXJuYWxcbiAgZ2V0IGRlZXBGcmVlemUoKSB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmRlZXBGcmVlemVGbiB8fCB0aGlzLm9wdGlvbnMuZGVlcEZyZWV6ZUZuIHx8IGRlZXBGcmVlemU7XG4gIH1cblxuICAvLyBAaW50ZXJuYWxcbiAgZ2V0IGNhY2hlQ29uZmlnKCkge1xuICAgIHJldHVybiB0aGlzLmNvbmZpZy5jYWNoZSB8fCB0aGlzLm9wdGlvbnMuY2FjaGU7XG4gIH1cblxuICAvLyBAaW50ZXJuYWxcbiAgZ2V0IHJlc2V0dGFibGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLnJlc2V0dGFibGUgfHwgdGhpcy5vcHRpb25zLnJlc2V0dGFibGU7XG4gIH1cblxuICAvLyBAaW50ZXJuYWxcbiAgX3NldFN0YXRlKG5ld1N0YXRlRm46IChzdGF0ZTogUmVhZG9ubHk8Uz4pID0+IFMsIF9kaXNwYXRjaEFjdGlvbiA9IHRydWUpIHtcbiAgICB0aGlzLnN0b3JlVmFsdWUgPSBfX0RFVl9fID8gdGhpcy5kZWVwRnJlZXplKG5ld1N0YXRlRm4odGhpcy5fdmFsdWUoKSkpIDogbmV3U3RhdGVGbih0aGlzLl92YWx1ZSgpKTtcblxuICAgIGlmICghdGhpcy5zdG9yZSkge1xuICAgICAgdGhpcy5zdG9yZSA9IG5ldyBCZWhhdmlvclN1YmplY3QodGhpcy5zdG9yZVZhbHVlKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoaXNUcmFuc2FjdGlvbkluUHJvY2VzcygpKSB7XG4gICAgICB0aGlzLmhhbmRsZVRyYW5zYWN0aW9uKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5kaXNwYXRjaCh0aGlzLnN0b3JlVmFsdWUsIF9kaXNwYXRjaEFjdGlvbik7XG4gIH1cblxuICAvKipcbiAgICpcbiAgICogUmVzZXQgdGhlIGN1cnJlbnQgc3RvcmUgYmFjayB0byB0aGUgaW5pdGlhbCB2YWx1ZVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiBzdG9yZS5yZXNldCgpXG4gICAqXG4gICAqL1xuICByZXNldCgpIHtcbiAgICBpZiAodGhpcy5pc1Jlc2V0dGFibGUoKSkge1xuICAgICAgaXNEZXYoKSAmJiBzZXRBY3Rpb24oJ1Jlc2V0Jyk7XG4gICAgICB0aGlzLl9zZXRTdGF0ZSgoKSA9PiBPYmplY3QuYXNzaWduKHt9LCB0aGlzLl9pbml0aWFsU3RhdGUpKTtcbiAgICAgIHRoaXMuc2V0SGFzQ2FjaGUoZmFsc2UpO1xuICAgIH0gZWxzZSB7XG4gICAgICBpc0RldigpICYmIGNvbnNvbGUud2FybihgWW91IG5lZWQgdG8gZW5hYmxlIHRoZSByZXNldCBmdW5jdGlvbmFsaXR5YCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqIFVwZGF0ZSB0aGUgc3RvcmUncyB2YWx1ZVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiB0aGlzLnN0b3JlLnVwZGF0ZShzdGF0ZSA9PiB7XG4gICAqICAgcmV0dXJuIHsuLi59XG4gICAqIH0pXG4gICAqL1xuICB1cGRhdGUoc3RhdGVDYWxsYmFjazogVXBkYXRlU3RhdGVDYWxsYmFjazxTPik7XG4gIC8qKlxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiAgdGhpcy5zdG9yZS51cGRhdGUoeyB0b2tlbjogdG9rZW4gfSlcbiAgICovXG4gIHVwZGF0ZShzdGF0ZTogUGFydGlhbDxTPik7XG4gIHVwZGF0ZShzdGF0ZU9yQ2FsbGJhY2s6IFBhcnRpYWw8Uz4gfCBVcGRhdGVTdGF0ZUNhbGxiYWNrPFM+KSB7XG4gICAgaXNEZXYoKSAmJiBzZXRBY3Rpb24oJ1VwZGF0ZScpO1xuXG4gICAgdGhpcy5fc2V0U3RhdGUoc3RhdGUgPT4ge1xuICAgICAgY29uc3QgbmV3U3RhdGUgPSBpc0Z1bmN0aW9uKHN0YXRlT3JDYWxsYmFjaykgPyBzdGF0ZU9yQ2FsbGJhY2soc3RhdGUpIDogc3RhdGVPckNhbGxiYWNrO1xuICAgICAgY29uc3QgbWVyZ2VkID0gdGhpcy5ha2l0YVByZVVwZGF0ZShzdGF0ZSwgeyAuLi5zdGF0ZSwgLi4ubmV3U3RhdGUgfSBhcyBTKTtcbiAgICAgIHJldHVybiBpc1BsYWluT2JqZWN0KHN0YXRlKSA/IG1lcmdlZCA6IG5ldyAoc3RhdGUgYXMgYW55KS5jb25zdHJ1Y3RvcihtZXJnZWQpO1xuICAgIH0pO1xuICB9XG5cbiAgdXBkYXRlU3RvcmVDb25maWcobmV3T3B0aW9uczogVXBkYXRhYmxlU3RvcmVDb25maWdPcHRpb25zKSB7XG4gICAgdGhpcy5vcHRpb25zID0geyAuLi50aGlzLm9wdGlvbnMsIC4uLm5ld09wdGlvbnMgfTtcbiAgfVxuXG4gIC8vIEBpbnRlcm5hbFxuICBha2l0YVByZVVwZGF0ZShfOiBSZWFkb25seTxTPiwgbmV4dFN0YXRlOiBSZWFkb25seTxTPik6IFMge1xuICAgIHJldHVybiBuZXh0U3RhdGU7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLmRlc3Ryb3koKTtcbiAgfVxuXG4gIC8qKlxuICAgKlxuICAgKiBEZXN0cm95IHRoZSBzdG9yZVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiBzdG9yZS5kZXN0cm95KClcbiAgICpcbiAgICovXG4gIGRlc3Ryb3koKSB7XG4gICAgaWYgKGlzTm90QnJvd3NlcikgcmV0dXJuO1xuICAgIGlmICghKHdpbmRvdyBhcyBhbnkpLmhtckVuYWJsZWQgJiYgdGhpcyA9PT0gX19zdG9yZXNfX1t0aGlzLnN0b3JlTmFtZV0pIHtcbiAgICAgIGRlbGV0ZSBfX3N0b3Jlc19fW3RoaXMuc3RvcmVOYW1lXTtcbiAgICAgIGRpc3BhdGNoRGVsZXRlZCh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICB0aGlzLnNldEhhc0NhY2hlKGZhbHNlKTtcbiAgICAgIHRoaXMuY2FjaGUuYWN0aXZlLmNvbXBsZXRlKCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBvbkluaXQoaW5pdGlhbFN0YXRlOiBTKSB7XG4gICAgX19zdG9yZXNfX1t0aGlzLnN0b3JlTmFtZV0gPSB0aGlzO1xuICAgIHRoaXMuX3NldFN0YXRlKCgpID0+IGluaXRpYWxTdGF0ZSk7XG4gICAgZGlzcGF0Y2hBZGRlZCh0aGlzLnN0b3JlTmFtZSk7XG4gICAgaWYgKHRoaXMuaXNSZXNldHRhYmxlKCkpIHtcbiAgICAgIHRoaXMuX2luaXRpYWxTdGF0ZSA9IGluaXRpYWxTdGF0ZTtcbiAgICB9XG4gICAgaXNEZXYoKSAmJiBhc3NlcnRTdG9yZUhhc05hbWUodGhpcy5zdG9yZU5hbWUsIHRoaXMuY29uc3RydWN0b3IubmFtZSk7XG4gIH1cblxuICBwcml2YXRlIGRpc3BhdGNoKHN0YXRlOiBTLCBfZGlzcGF0Y2hBY3Rpb24gPSB0cnVlKSB7XG4gICAgdGhpcy5zdG9yZS5uZXh0KHN0YXRlKTtcbiAgICBpZiAoX2Rpc3BhdGNoQWN0aW9uKSB7XG4gICAgICBkaXNwYXRjaFVwZGF0ZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICByZXNldEN1c3RvbUFjdGlvbigpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgd2F0Y2hUcmFuc2FjdGlvbigpIHtcbiAgICBjb21taXQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgdGhpcy5pblRyYW5zYWN0aW9uID0gZmFsc2U7XG4gICAgICB0aGlzLmRpc3BhdGNoKHRoaXMuX3ZhbHVlKCkpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBpc1Jlc2V0dGFibGUoKSB7XG4gICAgaWYgKHRoaXMucmVzZXR0YWJsZSA9PT0gZmFsc2UpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMucmVzZXR0YWJsZSB8fCBnZXRBa2l0YUNvbmZpZygpLnJlc2V0dGFibGU7XG4gIH1cblxuICBwcml2YXRlIGhhbmRsZVRyYW5zYWN0aW9uKCkge1xuICAgIGlmICghdGhpcy5pblRyYW5zYWN0aW9uKSB7XG4gICAgICB0aGlzLndhdGNoVHJhbnNhY3Rpb24oKTtcbiAgICAgIHRoaXMuaW5UcmFuc2FjdGlvbiA9IHRydWU7XG4gICAgfVxuICB9XG59XG4iXX0=