UNPKG

query-params-store

Version:

[![Build Status](https://travis-ci.org/IliaIdakiev/query-param-store.svg?branch=master)](https://travis-ci.org/IliaIdakiev/query-param-store)

570 lines 111 kB
import { Injectable, Inject } from '@angular/core'; import { NavigationStart, ActivationEnd, RoutesRecognized, NavigationCancel, NavigationEnd, ResolveStart } from '@angular/router'; import { ReplaySubject, Observable, of as observableOf, of, defer } from 'rxjs'; import { map, filter, tap, distinctUntilChanged, withLatestFrom, first, pairwise, publishReplay, switchMap, mapTo } from 'rxjs/operators'; import { decompressFromEncodedURIComponent } from 'lz-string'; import { QPS_CONFIG } from './tokens'; import { compress } from './utils'; import * as i0 from "@angular/core"; import * as i1 from "@angular/router"; const NOT_PRESENT = Symbol('NOT_PRESENT'); function booleanTypeConverter(val) { if (['true', 1, '1', true].includes(val)) { return true; } if (['false', 0, '0', false].includes(val)) { return false; } return Boolean(val); } export class QueryParamsStore { constructor(router, config) { this.router = router; this.snapshot = new ReplaySubject(2); this.isInDebugMode = false; this.useCompression = false; this.compressionKey = null; this.snapshot.next(null); this.isInDebugMode = !!config && !!config.debug; this.useCompression = !!config && !!config.useCompression; this.compressionKey = !!config && config.compressionKey || null; } getConvertBinaryBooleanResult(value, stateConfigLength) { value = +value; if (isNaN(value)) { return { isConvertedCorrectly: false }; } const binaryBooleanStringArray = value.toString(2).split(''); const isOverflowing = binaryBooleanStringArray.length > stateConfigLength; let binaryBooleanResult = binaryBooleanStringArray.slice(0, stateConfigLength).map((val) => `${val === '1'}`).reverse(); if (binaryBooleanResult.length < stateConfigLength) { binaryBooleanResult = binaryBooleanResult.concat(new Array(stateConfigLength - binaryBooleanResult.length).fill('false')); } const booleanResult = binaryBooleanResult.map(v => this.getConvertValueResult(v, Boolean)); return { binaryBooleanResult: booleanResult, isConvertedCorrectly: true, isOverflowing }; } getConvertValueResult(value, typeConvertor) { const isTypeConverterBoolean = typeConvertor === Boolean || typeConvertor === booleanTypeConverter; const isBoolean = ['true', 'false'].includes(value) && isTypeConverterBoolean; const convertedValue = typeConvertor === String && typeof value === 'string' ? value : typeConvertor(value === 'true' ? 1 : value === 'false' ? 0 : value); const isValidNumber = typeConvertor === Number && !Number.isNaN(convertedValue); const isValidString = typeConvertor === String && typeof convertedValue === 'string'; const isValidBoolean = isTypeConverterBoolean && typeof convertedValue === 'boolean'; const hasValidConversion = isValidNumber || isValidString || isValidBoolean; return { hasValidConversion, convertedValue, invalidRawConversion: (isTypeConverterBoolean && !isBoolean || typeConvertor === Number && isNaN(typeConvertor(value))) }; } getTypeConverter(stateConfig) { if (stateConfig.typeConvertor) { return stateConfig.typeConvertor === Boolean ? booleanTypeConverter : stateConfig.typeConvertor; } const stateConfigValueType = typeof (stateConfig.value ? stateConfig.value : stateConfig); if (stateConfigValueType === 'number') { return Number; } if (stateConfigValueType === 'boolean') { return booleanTypeConverter; } return String; } parseStateConfig(config) { return Object.entries(config).reduce((acc, [key, configForKey]) => { if (configForKey === null || Array.isArray(configForKey)) { // remove invalid configuration if (this.isInDebugMode) { console.warn(`Query Params Store: Invalid configuration for parameter key: ${key}! (nulls or arrays are not supported)`); } return acc; } const typeConvertor = this.getTypeConverter(configForKey); const isObjectConfig = typeof configForKey === 'object'; if (!isObjectConfig) { // the case when the property is just a value (page: 1 or filter: 'something') acc[key] = { value: configForKey, originalValue: configForKey, typeConvertor, multi: false }; return acc; } if (!configForKey.hasOwnProperty('value')) { // missing the value property => remove invalid configuration if (this.isInDebugMode) { console.warn(`Query Params Store: Invalid configuration for parameter key ${key}! (value is missing)`); } return acc; } let value = configForKey.value; const originalValue = value; if (!['number', 'string', 'boolean'].includes(typeof value) && value !== null) { // value is not in the supported type => remove invalid configuration if (this.isInDebugMode) { // tslint:disable-next-line:max-line-length console.warn(`Query Params Store: Invalid value configuration for parameter key ${key}! (value can be number, string, boolean or null)`); } return acc; } const useCompression = configForKey.useCompression || false; const isMultiValueConfig = configForKey.multi || false; const multiCount = isMultiValueConfig ? configForKey.count : null; const isBinaryBoolean = this.isBinaryBoolean({ value, typeConvertor, multi: isMultiValueConfig }); if (isBinaryBoolean) { const configForKeyLength = !configForKey.hasOwnProperty('length') ? undefined : +configForKey.length; value = value.toString(2).split('').slice(0, configForKeyLength).map((val) => `${val === '1'}`).reverse(); if (value.length < (configForKeyLength || 0)) { value = value.concat(new Array(configForKeyLength - value.length).fill('false')); } } else if (isMultiValueConfig && value !== null) { if (typeof value !== 'string' && !isBinaryBoolean) { // value should be a string and if we need multiple values we should use the given separator // tslint:disable-next-line:max-line-length if (this.isInDebugMode) { console.warn(`Query Params Store: Invalid configuration for parameter key ${key}! (value should be a string)`); } return acc; } value = isBinaryBoolean ? value : value === '' ? [] : value.split(configForKey.separator || ';'); const difference = (!multiCount || isBinaryBoolean) ? 0 : +multiCount - value.length; if (difference > 0) { for (let i = 0; i < difference; i++) { value.push(typeConvertor === String ? '' : typeConvertor === Number ? 0 : false); } } } if (Array.isArray(value)) { value = value.map(typeConvertor); } acc[key] = { ...configForKey, typeConvertor, originalValue, value, multi: isMultiValueConfig, multiCount, useCompression, separator: configForKey.separator || ';' }; return acc; }, {}); } isBinaryBoolean(cfg) { return cfg.multi && typeof cfg.value === 'number' && (cfg.typeConvertor === Boolean || cfg.typeConvertor === booleanTypeConverter); } getSnapshotData(snapshot) { const defaultStoreConfig = { stateConfig: {}, noQueryParams: false, removeUnknown: false }; let storeConfig = snapshot.data && snapshot.data['storeConfig'] || defaultStoreConfig; const inherit = storeConfig.inherit || false; if (inherit) { // iterate over all route data configs and merge the store configs (priority is from the bottom up) storeConfig = snapshot.pathFromRoot.slice(0, -1).reduceRight((acc, curr) => { const currentData = curr.data; if (!currentData || !currentData['storeConfig']) { return acc; } const { stateConfig: accumulatedStateConfig, ...accumulatedStoreConfigOptions } = acc; const { stateConfig: currentStateConfig, ...currentStoreConfigOptions } = currentData['storeConfig']; if (this.isInDebugMode) { const accumulatedStateKeys = Object.keys(accumulatedStateConfig || {}); const currentStateKeys = Object.keys(currentStateConfig || {}); const accumulatedStoreKeys = Object.keys(accumulatedStoreConfigOptions); const currentStoreKeys = Object.keys(currentStoreConfigOptions); const storeKeysIntersection = accumulatedStoreKeys.filter(x => currentStoreKeys.includes(x)); const stateKeysIntersection = accumulatedStateKeys.filter(x => currentStateKeys.includes(x)); // tslint:disable-next-line:max-line-length if (storeKeysIntersection.length > 0) { console.warn(`Query Params Store: parameter keys ${storeKeysIntersection} were overridden`); } // tslint:disable-next-line:max-line-length if (stateKeysIntersection.length > 0) { console.warn(`Query Params Store: parameter keys ${stateKeysIntersection} were overridden`); } } return { ...currentStoreConfigOptions, ...accumulatedStoreConfigOptions, stateConfig: { ...currentStateConfig, ...accumulatedStateConfig } }; }, storeConfig); } const noQueryParams = storeConfig.noQueryParams || false; let snapshotQueryParams = snapshot.queryParams; if (this.useCompression) { const key = this.compressionKey || 'q'; const compressedString = key === '#' ? snapshot.fragment : snapshotQueryParams[key]; if (compressedString) { const decompressedString = decompressFromEncodedURIComponent(compressedString); try { snapshotQueryParams = JSON.parse(decompressedString); } catch { if (this.isInDebugMode) { console.warn(`Query Params Store: Can't decompress provided value!`); } snapshotQueryParams = {}; } } } const stateConfig = this.parseStateConfig(storeConfig.stateConfig || {}); const removeUnknown = storeConfig.removeUnknown || false; const caseSensitive = storeConfig.hasOwnProperty('caseSensitive') ? storeConfig.caseSensitive : true; const allQueryParamsNames = Array.from(new Set(Object.keys(snapshotQueryParams).concat(Object.keys(stateConfig)))); const allQueryParams = allQueryParamsNames.reduce((acc, queryParamName) => { acc[queryParamName] = snapshotQueryParams[queryParamName] || NOT_PRESENT; return acc; }, {}); return { noQueryParams, storeConfig, removeUnknown, caseSensitive, allQueryParams, stateConfig }; } constructStore(snpsht) { const stream$ = (snpsht ? of(null, snpsht) : this.snapshot).pipe(pairwise(), filter(val => !this.redirectUrl || !!val[1]), switchMap(s => snpsht ? [s] : this.router.events.pipe( // emit the store if we are in the resolve phase (the guard checks passed) or catch the case // if we are just changing a query paramter in an already resolved route (watch for the NavigationEnd) first(e => e instanceof ResolveStart || e instanceof NavigationEnd), mapTo(s))), map(([prevSnapshot, snapshot]) => { if (!snapshot?.data) { return {}; } const { stateConfig, noQueryParams, caseSensitive, removeUnknown, allQueryParams } = this.getSnapshotData(snapshot); if (noQueryParams && Object.keys(snapshot.queryParams).length !== 0) { this.router.navigateByUrl(this.url); return null; } const queryParamsResult = Object.entries(allQueryParams).reduce((queryParams, [paramKey, paramValue]) => { // if we have caseSensitive: false we have to skip the NOT_PRESENT set when allQueryParams is generated if (paramValue === NOT_PRESENT && queryParams[paramKey]) { return queryParams; } let stateConfigForParam = stateConfig[paramKey]; paramValue = paramValue === NOT_PRESENT ? NOT_PRESENT : decodeURIComponent(paramValue); if (!stateConfigForParam && caseSensitive === false) { const stateConfigKeys = Object.keys(stateConfig); paramKey = stateConfigKeys.find(objKey => paramKey.toLowerCase() === objKey.toLowerCase()); stateConfigForParam = stateConfig[paramKey]; } if (removeUnknown && !stateConfigForParam) { if (this.isInDebugMode) { console.warn(`Query Params Store: removing parameter key: ${paramKey} because it's unknown!`); } return { ...queryParams, [paramKey]: { storeValue: null, urlValue: null } }; } else if (!stateConfigForParam) { return { ...queryParams, [paramKey]: { urlValue: paramValue, storeValue: paramValue } }; } const hasAllowedValuesProperty = stateConfigForParam.hasOwnProperty('allowedValues'); const isAllowedValuesAnArray = Array.isArray(stateConfigForParam.allowedValues); const allowedValues = hasAllowedValuesProperty && isAllowedValuesAnArray ? stateConfigForParam.allowedValues : null; if (hasAllowedValuesProperty && !isAllowedValuesAnArray && this.isInDebugMode) { console.warn(`Query Params Store: ignoring allowed values for parameter key: ${paramKey} since it's not an array!`); } const typeConvertor = stateConfigForParam.typeConvertor; // const value = paramValue !== NOT_PRESENT ? +paramValue : NaN; const isBinaryBoolean = this.isBinaryBoolean({ value: stateConfigForParam.originalValue, multi: stateConfigForParam.multi, typeConvertor }); const stateConfigLength = stateConfigForParam.length; const paramConfigValue = stateConfigForParam.hasOwnProperty('value') ? stateConfigForParam.value : stateConfigForParam; if (paramValue === NOT_PRESENT) { return { ...queryParams, [paramKey]: { urlValue: NOT_PRESENT, storeValue: paramConfigValue } }; } // Binary Boolean Configuration Case if (isBinaryBoolean) { if (this.isInDebugMode && allowedValues) { console.warn(`Query Params Store: Allowed values can't be used with binary boolean values!`); } const removeInvalid = stateConfigForParam.removeInvalid; const isFalseAsString = paramValue === 'false'; const isTrueAsString = paramValue === 'true'; const isBooleanAsString = isFalseAsString || isTrueAsString; paramValue = isFalseAsString ? 0 : isTrueAsString ? 1 : paramValue; const binaryBooleanConversionResult = this.getConvertBinaryBooleanResult(paramValue, stateConfigLength); const { binaryBooleanResult, isConvertedCorrectly, isOverflowing } = binaryBooleanConversionResult; if (!isConvertedCorrectly || (isOverflowing && removeInvalid)) { if (this.isInDebugMode) { if (isOverflowing) { // tslint:disable-next-line:max-line-length console.warn(`Query Params Store: removing parameter key: ${paramKey} since ${paramValue} converted to boolean ${binaryBooleanResult.map(r => r.convertedValue).join(',')} is longer than the configured length of ${stateConfigLength} and the property removeInvalid is set to true!`); } else { // tslint:disable-next-line:max-line-length console.warn(`Query Params Store: Config value ${paramValue} for parameter key ${paramKey} can't be converted correctly so its replaced with the one from the configuration!`); } } const defaultValue = { storeValue: paramConfigValue, urlValue: null }; return { ...queryParams, [paramKey]: defaultValue }; } return { ...queryParams, [paramKey]: { storeValue: binaryBooleanResult?.map(r => r.convertedValue), urlValue: paramValue, isNewUrlValue: isBooleanAsString } }; } // const isMultiToMultiParamTransition = !!(stateConfigForParam.multi && prevSnapshot && prevSnapshot.data // && prevSnapshot.data.storeConfig && prevSnapshot.data.storeConfig.stateConfig && // prevSnapshot.data.storeConfig.stateConfig[paramKey] && prevSnapshot.data.storeConfig.stateConfig[paramKey].multi); // Multi Value Configuration Case if (stateConfigForParam.multi) { // const isSingleToMultiParamTransition = !!(prevSnapshot && prevSnapshot.data // && prevSnapshot.data.storeConfig && prevSnapshot.data.storeConfig.stateConfig && // prevSnapshot.data.storeConfig.stateConfig[paramKey] && !prevSnapshot.data.storeConfig.stateConfig[paramKey].multi); let conversionResults = paramValue.split(stateConfigForParam.separator) .map((v) => this.getConvertValueResult(v, typeConvertor)); const multiCount = typeof stateConfigForParam.multiCount === 'number' ? stateConfigForParam.multiCount : paramConfigValue && typeof paramConfigValue.length === 'number' ? paramConfigValue.length : conversionResults.length || 0; const difference = isBinaryBoolean ? 0 : +multiCount - conversionResults.length; if (difference > 0) { for (let i = 0; i < difference; i++) { // if we have difference in the lengths of the value of the parameter then we either use the default value // provided in the config for the selected index or just use a default one const conversionResultsIndex = conversionResults.length + i; const hasDefaultValueArrayIndex = paramConfigValue.length > conversionResultsIndex; conversionResults.push({ hasValidConversion: true, convertedValue: hasDefaultValueArrayIndex ? paramConfigValue[conversionResultsIndex] : typeConvertor === String ? '' : typeConvertor === Number ? 0 : false, invalidRawConversion: false }); } } else if (difference < 0) { conversionResults = conversionResults.slice(0, difference); } const checkResult = conversionResults.map(v => { if (!v.hasValidConversion) { return 'invalid'; } if (allowedValues && !allowedValues.includes(v.convertedValue)) { return 'not-allowed'; } return null; }); const hasInvalidValue = checkResult.includes('invalid'); const hasNotAllowedValue = checkResult.includes('not-allowed'); if (hasInvalidValue || hasNotAllowedValue) { if (this.isInDebugMode) { if (hasInvalidValue) { // tslint:disable-next-line:max-line-length console.warn(`Query Params Store: parameter key ${paramKey} with value: ${paramValue} can't be converted correctly!`); } else { // tslint:disable-next-line:max-line-length console.warn(`Query Params Store: parameter key ${paramKey} with value: ${paramValue} is not found in the allowed values ${allowedValues}!`); } } const defaultValue = { storeValue: paramConfigValue, urlValue: null }; return { ...queryParams, [paramKey]: defaultValue }; } const invalidURLValue = !!conversionResults.find(r => r.invalidRawConversion); const storeValue = conversionResults.map(r => r.convertedValue); // here we also provide the difference so if it's > 0 we can correctly redirect bellow return { ...queryParams, [paramKey]: { urlValue: paramValue, storeValue, difference, invalidURLValue } }; } // Default (string, number, boolean) Value Configuration Case { // when using strings hasValidConversion can be true because '10;10' is a valid string so we need to know if we are having // a multi to single transition in order to be able to redirect the the correct url const isMultiToSingleParamTransition = !!(!stateConfigForParam.multi && prevSnapshot && prevSnapshot.data && prevSnapshot.data['storeConfig'] && prevSnapshot.data['storeConfig'].stateConfig && prevSnapshot.data['storeConfig'].stateConfig[paramKey] && prevSnapshot.data['storeConfig'].stateConfig[paramKey].multi); let isValidMultiToSingle = false; let invalidURLValue = false; let conversionResult = this.getConvertValueResult(paramValue, typeConvertor); if ((!conversionResult.hasValidConversion && prevSnapshot && prevSnapshot.data) || isMultiToSingleParamTransition) { const { stateConfig: prevStateConfig, allQueryParams: allPrevQueryParams } = this.getSnapshotData(prevSnapshot); const prevStateConfigForKey = prevStateConfig[paramKey]; if (prevStateConfigForKey && prevStateConfigForKey.multi) { paramValue = allPrevQueryParams[paramKey] ? allPrevQueryParams[paramKey].split(prevStateConfig[paramKey].separator)[0] : prevStateConfig[paramKey].value[0]; conversionResult = this.getConvertValueResult(paramValue, typeConvertor); isValidMultiToSingle = conversionResult.hasValidConversion; invalidURLValue = conversionResult.invalidRawConversion; } } const isAllowed = allowedValues ? allowedValues.includes(conversionResult.convertedValue) : true; if (!conversionResult.hasValidConversion || !isAllowed) { if (this.isInDebugMode) { if (!isAllowed) { // tslint:disable-next-line:max-line-length console.warn(`Query Params Store: parameter key: ${paramKey} with value: ${paramValue} is not found in the allowed values ${allowedValues}!`); } else { console.warn(`Query Params Store: parameter key: ${paramKey} with value: ${paramValue} can't be converted correctly!`); } } const defaultValue = { storeValue: paramConfigValue, urlValue: null }; return { ...queryParams, [paramKey]: defaultValue }; } const storeValue = conversionResult.convertedValue; // if isValidMultiToSingle is true then we need to set isNewUrlValue to true so we can change the URL bellow return { ...queryParams, [paramKey]: { urlValue: paramValue, storeValue, isNewUrlValue: isValidMultiToSingle, invalidURLValue } }; } }, {}); // Redirect if needed and send the new store { const mustChangeUrl = !!Object.values(queryParamsResult).find((v) => v.urlValue === null || v.isNewUrlValue || v.difference || v.invalidURLValue); if (mustChangeUrl) { const queryParamsTupleArray = Object.entries(queryParamsResult).reduce((acc, [key, { urlValue, difference, storeValue, isNewUrlValue, invalidURLValue }]) => { if (isNewUrlValue || invalidURLValue || ![null, NOT_PRESENT].includes(urlValue)) { const currentConfig = stateConfig[key]; if (!currentConfig) { return acc; } const separator = currentConfig.separator; if (invalidURLValue) { urlValue = currentConfig.multi ? storeValue.join(separator) : storeValue; difference = 0; } if (typeof difference === 'number' && difference !== 0) { const currentUrlValueArray = urlValue.split(separator); if (difference > 0) { for (let i = 0; i < difference; i++) { urlValue = `${urlValue}${separator}${storeValue[currentUrlValueArray.length + i]}`; } } else { urlValue = `${currentUrlValueArray.slice(0, difference).join(separator)}`; } } return acc.concat([[key, urlValue]]); } return acc; }, []); let redirectUrl = null; if (this.useCompression) { const queryParamsObject = queryParamsTupleArray.reduce((acc, [key, value]) => { acc[key] = value; return acc; }, {}); const compressedQueryParams = compress(queryParamsObject); const compressionKey = this.compressionKey || 'q'; const anchorCompressionKey = compressionKey === '#'; redirectUrl = `${anchorCompressionKey ? this.url.split('#')[0] : this.url}${queryParamsTupleArray.length > 0 ? anchorCompressionKey ? `#${compressedQueryParams}` : `?${compressionKey}=${compressedQueryParams}` : ''}`; } else { const queryParamsArray = queryParamsTupleArray.reduce((acc, [key, value]) => acc.concat(`${key}=${value}`), []); redirectUrl = `${this.url}${queryParamsTupleArray.length > 0 ? '?' + queryParamsArray.join('&') : ''}`; } this.router.navigateByUrl(redirectUrl); this.redirectUrl = redirectUrl; return undefined; } const store = Object.entries(queryParamsResult).reduce((acc, [key, data]) => { acc[key] = data.storeValue; return acc; }, {}); return store; } })).pipe(filter(val => !!val)); return stream$; } _constructHandler() { if (this.subscription) { return; } this.subscription = this.router.events.pipe(filter(event => !(event instanceof ActivationEnd)), tap(event => { if (event instanceof NavigationStart) { this.prevFullUrl = this.fullUrl || ''; this.fullUrl = event.url; this.url = decodeURIComponent(/[^?]+/.exec(event.url)[0]); } else if (event instanceof RoutesRecognized) { this.url = decodeURIComponent(/[^?]+/.exec(event.urlAfterRedirects)[0]); } else if (event instanceof NavigationCancel) { this.fullUrl = this.prevFullUrl; } else if (event instanceof NavigationEnd) { this.redirectUrl = null; } }), filter(event => !!event.snapshot), map(event => { let currSnapshot = event.snapshot; while (currSnapshot && currSnapshot.children.length !== 0) { currSnapshot = currSnapshot.children.find((childSnapshot) => { return this.url === '/' && childSnapshot.url.length === 0 || !!childSnapshot.url.find((segment) => this.url.includes(segment.path)); }); } return currSnapshot; }), filter(val => !!val), distinctUntilChanged()).subscribe(this.snapshot); this.store = this.constructStore().pipe(publishReplay(1)); this.store.connect(); } select(selector, disableDistinctUntilChanged = false, compare) { return defer(() => { const fn = typeof selector === 'function' ? selector : (state) => state[selector]; let select = this.store.pipe(map(fn)); if (!disableDistinctUntilChanged) { select = select.pipe(distinctUntilChanged(compare)); } return select; }); } first(selector, predicate, defaultValue) { selector = selector || (s => s); return this.select(selector).pipe(first(predicate, defaultValue)); } _match(snapshot, allowedValues, navigateTo, isDeactivate = false) { if (!(allowedValues instanceof Observable)) { allowedValues = observableOf(allowedValues); } if (navigateTo === this.fullUrl) { throw new Error('Navigating to the same route will result into infinite loop!'); } return this.constructStore(snapshot).pipe(withLatestFrom(allowedValues), map(([queryParams, allowedValuesObj]) => { let successfulMatch = true; let redirectQueryParams = {}; for (const [name, value] of Object.entries(queryParams)) { const config = allowedValuesObj[name]; if (!config) { continue; } const { match, default: defaultValue } = config; const isCurrentMatch = (Array.isArray(match) ? match.includes(value) : value === match); if (!isCurrentMatch && defaultValue) { redirectQueryParams = { ...redirectQueryParams, [name]: defaultValue }; } successfulMatch = successfulMatch && isCurrentMatch; } if (!successfulMatch && typeof navigateTo === 'string') { if (navigateTo !== this.prevFullUrl) { this.router.navigateByUrl(navigateTo); } } return successfulMatch; })); } canActivate(allowedValues, currentSnapshot) { return this._match(currentSnapshot, allowedValues, this.prevFullUrl).pipe(first()); } canDeactivate(allowedValues, currentSnapshot, routerSnapshot) { return this._match(currentSnapshot, allowedValues, routerSnapshot.url, true).pipe(first()); } ngOnDestroy() { this.subscription.unsubscribe(); } } QueryParamsStore.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: QueryParamsStore, deps: [{ token: i1.Router }, { token: QPS_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); QueryParamsStore.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: QueryParamsStore }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: QueryParamsStore, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.Router }, { type: undefined, decorators: [{ type: Inject, args: [QPS_CONFIG] }] }]; } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVlcnktcGFyYW1zLXN0b3JlLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9xdWVyeS1wYXJhbXMtc3RvcmUvc3JjL2xpYi9xdWVyeS1wYXJhbXMtc3RvcmUuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFhLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM5RCxPQUFPLEVBRUwsZUFBZSxFQUNmLGFBQWEsRUFFYixnQkFBZ0IsRUFFaEIsZ0JBQWdCLEVBQ2hCLGFBQWEsRUFDYixZQUFZLEVBQ2IsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QixPQUFPLEVBQUUsYUFBYSxFQUFnQixVQUFVLEVBQUUsRUFBRSxJQUFJLFlBQVksRUFBRSxFQUFFLEVBQXlCLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNySCxPQUFPLEVBQ0wsR0FBRyxFQUNILE1BQU0sRUFDTixHQUFHLEVBQ0gsb0JBQW9CLEVBQ3BCLGNBQWMsRUFDZCxLQUFLLEVBQ0wsUUFBUSxFQUNSLGFBQWEsRUFDYixTQUFTLEVBQ1QsS0FBSyxFQUNOLE1BQU0sZ0JBQWdCLENBQUM7QUFReEIsT0FBTyxFQUFFLGlDQUFpQyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzlELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDdEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFNBQVMsQ0FBQzs7O0FBR25DLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQztBQUUxQyxTQUFTLG9CQUFvQixDQUFDLEdBQVE7SUFDcEMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRTtRQUFFLE9BQU8sSUFBSSxDQUFDO0tBQUU7SUFDMUQsSUFBSSxDQUFDLE9BQU8sRUFBRSxD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