UNPKG

@first-line/firstline-angular

Version:

Firstline SDK for Angular Single Page Applications (SPA)

96 lines 13.7 kB
import { Inject, Injectable } from '@angular/core'; import { BehaviorSubject, defer, merge, of, ReplaySubject, Subject, } from 'rxjs'; import { concatMap, distinctUntilChanged, filter, mergeMap, scan, shareReplay, switchMap, } from 'rxjs/operators'; import { ClientService } from './client'; import * as i0 from "@angular/core"; import * as i1 from "./client"; /** * Tracks the Authentication State for the SDK */ export class AuthState { constructor(client) { this.client = client; this.isLoadingSubject$ = new BehaviorSubject(true); this.refresh$ = new Subject(); this.accessToken$ = new ReplaySubject(1); this.errorSubject$ = new ReplaySubject(1); /** * Emits boolean values indicating the loading state of the SDK. */ this.isLoading$ = this.isLoadingSubject$.asObservable(); /** * Trigger used to pull User information from the Client. * Triggers when the access token has changed. */ this.accessTokenTrigger$ = this.accessToken$.pipe(scan((acc, current) => ({ previous: acc.current, current, }), { current: null, previous: null }), filter(({ previous, current }) => previous !== current)); /** * Trigger used to pull User information from the Client. * Triggers when an event occurs that needs to retrigger the User Profile information. * Events: Login, Access Token change and Logout */ this.isAuthenticatedTrigger$ = this.isLoading$.pipe(filter((loading) => !loading), distinctUntilChanged(), switchMap(() => // To track the value of isAuthenticated over time, we need to merge: // - the current value // - the value whenever the access token changes. (this should always be true of there is an access token // but it is safer to pass this through this.client.isAuthenticated() nevertheless) // - the value whenever refreshState$ emits merge(defer(() => this.client.isAuthenticated()), this.accessTokenTrigger$.pipe(mergeMap(() => this.client.isAuthenticated())), this.refresh$.pipe(mergeMap(() => this.client.isAuthenticated()))))); /** * Emits boolean values indicating the authentication state of the user. If `true`, it means a user has authenticated. * This depends on the value of `isLoading$`, so there is no need to manually check the loading state of the SDK. */ this.isAuthenticated$ = this.isAuthenticatedTrigger$.pipe(distinctUntilChanged(), shareReplay(1)); /** * Emits details about the authenticated user, or null if not authenticated. */ this.user$ = this.isAuthenticatedTrigger$.pipe(concatMap((authenticated) => authenticated ? this.client.getUser() : of(null)), distinctUntilChanged()); /** * Emits errors that occur. */ this.error$ = this.errorSubject$.asObservable(); } /** * Update the isLoading state using the provided value * * @param isLoading The new value for isLoading */ setIsLoading(isLoading) { this.isLoadingSubject$.next(isLoading); } /** * Refresh the state to ensure the `isAuthenticated` and `user$` * reflect the most up-to-date values from Client. */ refresh() { this.refresh$.next(); } /** * Update the access token, doing so will also refresh the state. * * @param accessToken The new Access Token */ setAccessToken(accessToken) { this.accessToken$.next(accessToken); } /** * Emits the error in the `error$` observable. * * @param error The new error */ setError(error) { this.errorSubject$.next(error); } } AuthState.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: AuthState, deps: [{ token: ClientService }], target: i0.ɵɵFactoryTarget.Injectable }); AuthState.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: AuthState, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: AuthState, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: i1.Client, decorators: [{ type: Inject, args: [ClientService] }] }]; } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3N0YXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFDTCxlQUFlLEVBQ2YsS0FBSyxFQUNMLEtBQUssRUFDTCxFQUFFLEVBQ0YsYUFBYSxFQUNiLE9BQU8sR0FDUixNQUFNLE1BQU0sQ0FBQztBQUNkLE9BQU8sRUFDTCxTQUFTLEVBQ1Qsb0JBQW9CLEVBQ3BCLE1BQU0sRUFDTixRQUFRLEVBQ1IsSUFBSSxFQUNKLFdBQVcsRUFDWCxTQUFTLEdBQ1YsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQVUsYUFBYSxFQUFFLE1BQU0sVUFBVSxDQUFDOzs7QUFFakQ7O0dBRUc7QUFFSCxNQUFNLE9BQU8sU0FBUztJQTZFcEIsWUFBMkMsTUFBYztRQUFkLFdBQU0sR0FBTixNQUFNLENBQVE7UUE1RWpELHNCQUFpQixHQUFHLElBQUksZUFBZSxDQUFVLElBQUksQ0FBQyxDQUFDO1FBQ3ZELGFBQVEsR0FBRyxJQUFJLE9BQU8sRUFBUSxDQUFDO1FBQy9CLGlCQUFZLEdBQUcsSUFBSSxhQUFhLENBQVMsQ0FBQyxDQUFDLENBQUM7UUFDNUMsa0JBQWEsR0FBRyxJQUFJLGFBQWEsQ0FBUSxDQUFDLENBQUMsQ0FBQztRQUVwRDs7V0FFRztRQUNhLGVBQVUsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsWUFBWSxFQUFFLENBQUM7UUFFbkU7OztXQUdHO1FBQ0ssd0JBQW1CLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQ2xELElBQUksQ0FDRixDQUNFLEdBQXdELEVBQ3hELE9BQXNCLEVBQ3RCLEVBQUUsQ0FBQyxDQUFDO1lBQ0osUUFBUSxFQUFFLEdBQUcsQ0FBQyxPQUFPO1lBQ3JCLE9BQU87U0FDUixDQUFDLEVBQ0YsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FDbEMsRUFDRCxNQUFNLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLENBQUMsUUFBUSxLQUFLLE9BQU8sQ0FBQyxDQUN4RCxDQUFDO1FBRUY7Ozs7V0FJRztRQUNjLDRCQUF1QixHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUM3RCxNQUFNLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLEVBQzdCLG9CQUFvQixFQUFFLEVBQ3RCLFNBQVMsQ0FBQyxHQUFHLEVBQUU7UUFDYixxRUFBcUU7UUFDckUsdUJBQXVCO1FBQ3ZCLDBHQUEwRztRQUMxRyxzRkFBc0Y7UUFDdEYsNENBQTRDO1FBQzVDLEtBQUssQ0FDSCxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLEVBQUUsQ0FBQyxFQUMxQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUMzQixRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLEVBQUUsQ0FBQyxDQUM5QyxFQUNELElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLGVBQWUsRUFBRSxDQUFDLENBQUMsQ0FDbEUsQ0FDRixDQUNGLENBQUM7UUFFRjs7O1dBR0c7UUFDTSxxQkFBZ0IsR0FBRyxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxDQUMzRCxvQkFBb0IsRUFBRSxFQUN0QixXQUFXLENBQUMsQ0FBQyxDQUFDLENBQ2YsQ0FBQztRQUVGOztXQUVHO1FBQ00sVUFBSyxHQUFHLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQ2hELFNBQVMsQ0FBQyxDQUFDLGFBQWEsRUFBRSxFQUFFLENBQzFCLGFBQWEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQSxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUNoRCxFQUNELG9CQUFvQixFQUFFLENBQ3ZCLENBQUM7UUFFRjs7V0FFRztRQUNhLFdBQU0sR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFlBQVksRUFBRSxDQUFDO0lBRUMsQ0FBQztJQUU3RDs7OztPQUlHO0lBQ0ksWUFBWSxDQUFDLFNBQWtCO1FBQ3BDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVEOzs7T0FHRztJQUNJLE9BQU87UUFDWixJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksY0FBYyxDQUFDLFdBQW1CO1FBQ3ZDLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksUUFBUSxDQUFDLEtBQVU7UUFDeEIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDakMsQ0FBQzs7c0dBaEhVLFNBQVMsa0JBNkVBLGFBQWE7MEdBN0V0QixTQUFTLGNBREksTUFBTTsyRkFDbkIsU0FBUztrQkFEckIsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUU7OzBCQThFbkIsTUFBTTsyQkFBQyxhQUFhIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0LCBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7XHJcbiAgQmVoYXZpb3JTdWJqZWN0LFxyXG4gIGRlZmVyLFxyXG4gIG1lcmdlLFxyXG4gIG9mLFxyXG4gIFJlcGxheVN1YmplY3QsXHJcbiAgU3ViamVjdCxcclxufSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHtcclxuICBjb25jYXRNYXAsXHJcbiAgZGlzdGluY3RVbnRpbENoYW5nZWQsXHJcbiAgZmlsdGVyLFxyXG4gIG1lcmdlTWFwLFxyXG4gIHNjYW4sXHJcbiAgc2hhcmVSZXBsYXksXHJcbiAgc3dpdGNoTWFwLFxyXG59IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcclxuXHJcbmltcG9ydCB7IENsaWVudCwgQ2xpZW50U2VydmljZSB9IGZyb20gJy4vY2xpZW50JztcclxuXHJcbi8qKlxyXG4gKiBUcmFja3MgdGhlIEF1dGhlbnRpY2F0aW9uIFN0YXRlIGZvciB0aGUgU0RLXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxyXG5leHBvcnQgY2xhc3MgQXV0aFN0YXRlIHtcclxuICBwcml2YXRlIGlzTG9hZGluZ1N1YmplY3QkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPih0cnVlKTtcclxuICBwcml2YXRlIHJlZnJlc2gkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcclxuICBwcml2YXRlIGFjY2Vzc1Rva2VuJCA9IG5ldyBSZXBsYXlTdWJqZWN0PHN0cmluZz4oMSk7XHJcbiAgcHJpdmF0ZSBlcnJvclN1YmplY3QkID0gbmV3IFJlcGxheVN1YmplY3Q8RXJyb3I+KDEpO1xyXG5cclxuICAvKipcclxuICAgKiBFbWl0cyBib29sZWFuIHZhbHVlcyBpbmRpY2F0aW5nIHRoZSBsb2FkaW5nIHN0YXRlIG9mIHRoZSBTREsuXHJcbiAgICovXHJcbiAgcHVibGljIHJlYWRvbmx5IGlzTG9hZGluZyQgPSB0aGlzLmlzTG9hZGluZ1N1YmplY3QkLmFzT2JzZXJ2YWJsZSgpO1xyXG5cclxuICAvKipcclxuICAgKiBUcmlnZ2VyIHVzZWQgdG8gcHVsbCBVc2VyIGluZm9ybWF0aW9uIGZyb20gdGhlIENsaWVudC5cclxuICAgKiBUcmlnZ2VycyB3aGVuIHRoZSBhY2Nlc3MgdG9rZW4gaGFzIGNoYW5nZWQuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBhY2Nlc3NUb2tlblRyaWdnZXIkID0gdGhpcy5hY2Nlc3NUb2tlbiQucGlwZShcclxuICAgIHNjYW4oXHJcbiAgICAgIChcclxuICAgICAgICBhY2M6IHsgY3VycmVudDogc3RyaW5nIHwgbnVsbDsgcHJldmlvdXM6IHN0cmluZyB8IG51bGwgfSxcclxuICAgICAgICBjdXJyZW50OiBzdHJpbmcgfCBudWxsXHJcbiAgICAgICkgPT4gKHtcclxuICAgICAgICBwcmV2aW91czogYWNjLmN1cnJlbnQsXHJcbiAgICAgICAgY3VycmVudCxcclxuICAgICAgfSksXHJcbiAgICAgIHsgY3VycmVudDogbnVsbCwgcHJldmlvdXM6IG51bGwgfVxyXG4gICAgKSxcclxuICAgIGZpbHRlcigoeyBwcmV2aW91cywgY3VycmVudCB9KSA9PiBwcmV2aW91cyAhPT0gY3VycmVudClcclxuICApO1xyXG5cclxuICAvKipcclxuICAgKiBUcmlnZ2VyIHVzZWQgdG8gcHVsbCBVc2VyIGluZm9ybWF0aW9uIGZyb20gdGhlIENsaWVudC5cclxuICAgKiBUcmlnZ2VycyB3aGVuIGFuIGV2ZW50IG9jY3VycyB0aGF0IG5lZWRzIHRvIHJldHJpZ2dlciB0aGUgVXNlciBQcm9maWxlIGluZm9ybWF0aW9uLlxyXG4gICAqIEV2ZW50czogTG9naW4sIEFjY2VzcyBUb2tlbiBjaGFuZ2UgYW5kIExvZ291dFxyXG4gICAqL1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgaXNBdXRoZW50aWNhdGVkVHJpZ2dlciQgPSB0aGlzLmlzTG9hZGluZyQucGlwZShcclxuICAgIGZpbHRlcigobG9hZGluZykgPT4gIWxvYWRpbmcpLFxyXG4gICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSxcclxuICAgIHN3aXRjaE1hcCgoKSA9PlxyXG4gICAgICAvLyBUbyB0cmFjayB0aGUgdmFsdWUgb2YgaXNBdXRoZW50aWNhdGVkIG92ZXIgdGltZSwgd2UgbmVlZCB0byBtZXJnZTpcclxuICAgICAgLy8gIC0gdGhlIGN1cnJlbnQgdmFsdWVcclxuICAgICAgLy8gIC0gdGhlIHZhbHVlIHdoZW5ldmVyIHRoZSBhY2Nlc3MgdG9rZW4gY2hhbmdlcy4gKHRoaXMgc2hvdWxkIGFsd2F5cyBiZSB0cnVlIG9mIHRoZXJlIGlzIGFuIGFjY2VzcyB0b2tlblxyXG4gICAgICAvLyAgICBidXQgaXQgaXMgc2FmZXIgdG8gcGFzcyB0aGlzIHRocm91Z2ggdGhpcy5jbGllbnQuaXNBdXRoZW50aWNhdGVkKCkgbmV2ZXJ0aGVsZXNzKVxyXG4gICAgICAvLyAgLSB0aGUgdmFsdWUgd2hlbmV2ZXIgcmVmcmVzaFN0YXRlJCBlbWl0c1xyXG4gICAgICBtZXJnZShcclxuICAgICAgICBkZWZlcigoKSA9PiB0aGlzLmNsaWVudC5pc0F1dGhlbnRpY2F0ZWQoKSksXHJcbiAgICAgICAgdGhpcy5hY2Nlc3NUb2tlblRyaWdnZXIkLnBpcGUoXHJcbiAgICAgICAgICBtZXJnZU1hcCgoKSA9PiB0aGlzLmNsaWVudC5pc0F1dGhlbnRpY2F0ZWQoKSlcclxuICAgICAgICApLFxyXG4gICAgICAgIHRoaXMucmVmcmVzaCQucGlwZShtZXJnZU1hcCgoKSA9PiB0aGlzLmNsaWVudC5pc0F1dGhlbnRpY2F0ZWQoKSkpXHJcbiAgICAgIClcclxuICAgIClcclxuICApO1xyXG5cclxuICAvKipcclxuICAgKiBFbWl0cyBib29sZWFuIHZhbHVlcyBpbmRpY2F0aW5nIHRoZSBhdXRoZW50aWNhdGlvbiBzdGF0ZSBvZiB0aGUgdXNlci4gSWYgYHRydWVgLCBpdCBtZWFucyBhIHVzZXIgaGFzIGF1dGhlbnRpY2F0ZWQuXHJcbiAgICogVGhpcyBkZXBlbmRzIG9uIHRoZSB2YWx1ZSBvZiBgaXNMb2FkaW5nJGAsIHNvIHRoZXJlIGlzIG5vIG5lZWQgdG8gbWFudWFsbHkgY2hlY2sgdGhlIGxvYWRpbmcgc3RhdGUgb2YgdGhlIFNESy5cclxuICAgKi9cclxuICByZWFkb25seSBpc0F1dGhlbnRpY2F0ZWQkID0gdGhpcy5pc0F1dGhlbnRpY2F0ZWRUcmlnZ2VyJC5waXBlKFxyXG4gICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSxcclxuICAgIHNoYXJlUmVwbGF5KDEpXHJcbiAgKTtcclxuXHJcbiAgLyoqXHJcbiAgICogRW1pdHMgZGV0YWlscyBhYm91dCB0aGUgYXV0aGVudGljYXRlZCB1c2VyLCBvciBudWxsIGlmIG5vdCBhdXRoZW50aWNhdGVkLlxyXG4gICAqL1xyXG4gIHJlYWRvbmx5IHVzZXIkID0gdGhpcy5pc0F1dGhlbnRpY2F0ZWRUcmlnZ2VyJC5waXBlKFxyXG4gICAgY29uY2F0TWFwKChhdXRoZW50aWNhdGVkKSA9PlxyXG4gICAgICBhdXRoZW50aWNhdGVkID8gdGhpcy5jbGllbnQuZ2V0VXNlcigpOiBvZihudWxsKVxyXG4gICAgKSxcclxuICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKClcclxuICApO1xyXG5cclxuICAvKipcclxuICAgKiBFbWl0cyBlcnJvcnMgdGhhdCBvY2N1ci5cclxuICAgKi9cclxuICBwdWJsaWMgcmVhZG9ubHkgZXJyb3IkID0gdGhpcy5lcnJvclN1YmplY3QkLmFzT2JzZXJ2YWJsZSgpO1xyXG5cclxuICBjb25zdHJ1Y3RvcihASW5qZWN0KENsaWVudFNlcnZpY2UpIHByaXZhdGUgY2xpZW50OiBDbGllbnQpIHt9XHJcblxyXG4gIC8qKlxyXG4gICAqIFVwZGF0ZSB0aGUgaXNMb2FkaW5nIHN0YXRlIHVzaW5nIHRoZSBwcm92aWRlZCB2YWx1ZVxyXG4gICAqXHJcbiAgICogQHBhcmFtIGlzTG9hZGluZyBUaGUgbmV3IHZhbHVlIGZvciBpc0xvYWRpbmdcclxuICAgKi9cclxuICBwdWJsaWMgc2V0SXNMb2FkaW5nKGlzTG9hZGluZzogYm9vbGVhbik6IHZvaWQge1xyXG4gICAgdGhpcy5pc0xvYWRpbmdTdWJqZWN0JC5uZXh0KGlzTG9hZGluZyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZWZyZXNoIHRoZSBzdGF0ZSB0byBlbnN1cmUgdGhlIGBpc0F1dGhlbnRpY2F0ZWRgIGFuZCBgdXNlciRgXHJcbiAgICogcmVmbGVjdCB0aGUgbW9zdCB1cC10by1kYXRlIHZhbHVlcyBmcm9tICBDbGllbnQuXHJcbiAgICovXHJcbiAgcHVibGljIHJlZnJlc2goKTogdm9pZCB7XHJcbiAgICB0aGlzLnJlZnJlc2gkLm5leHQoKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFVwZGF0ZSB0aGUgYWNjZXNzIHRva2VuLCBkb2luZyBzbyB3aWxsIGFsc28gcmVmcmVzaCB0aGUgc3RhdGUuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gYWNjZXNzVG9rZW4gVGhlIG5ldyBBY2Nlc3MgVG9rZW5cclxuICAgKi9cclxuICBwdWJsaWMgc2V0QWNjZXNzVG9rZW4oYWNjZXNzVG9rZW46IHN0cmluZyk6IHZvaWQge1xyXG4gICAgdGhpcy5hY2Nlc3NUb2tlbiQubmV4dChhY2Nlc3NUb2tlbik7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBFbWl0cyB0aGUgZXJyb3IgaW4gdGhlIGBlcnJvciRgIG9ic2VydmFibGUuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZXJyb3IgVGhlIG5ldyBlcnJvclxyXG4gICAqL1xyXG4gIHB1YmxpYyBzZXRFcnJvcihlcnJvcjogYW55KTogdm9pZCB7XHJcbiAgICB0aGlzLmVycm9yU3ViamVjdCQubmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcbiJdfQ==