@ngx-pwa/local-storage
Version:
Efficient local storage module for Angular: simple API based on native localStorage API, but internally stored via the asynchronous IndexedDB API for performance, and wrapped in RxJS observables to be homogeneous with other Angular modules.
294 lines • 39.6 kB
JavaScript
import { Injectable, Inject } from '@angular/core';
import { throwError, of, ReplaySubject } from 'rxjs';
import { mergeMap, catchError, tap } from 'rxjs/operators';
import { JSONValidator } from '../validation/json-validator';
import { IndexedDBDatabase } from '../databases/indexeddb-database';
import { LocalStorageDatabase } from '../databases/localstorage-database';
import { MemoryDatabase } from '../databases/memory-database';
import { LocalDatabase } from '../databases/local-database';
import { IDB_BROKEN_ERROR } from '../databases/exceptions';
import { LS_PREFIX } from '../tokens';
import { ValidationError } from './exceptions';
import * as i0 from "@angular/core";
import * as i1 from "../databases/local-database";
import * as i2 from "../validation/json-validator";
import * as i3 from "../tokens";
export class StorageMap {
/**
* Constructor params are provided by Angular (but can also be passed manually in tests)
* @param database Storage to use
* @param jsonValidator Validator service
* @param LSPrefix Prefix for `localStorage` keys to avoid collision for multiple apps on the same subdomain or for interoperability
*/
constructor(database, jsonValidator = new JSONValidator(), LSPrefix = '') {
this.database = database;
this.jsonValidator = jsonValidator;
this.LSPrefix = LSPrefix;
this.notifiers = new Map();
}
/**
* **Number of items** in storage, wrapped in an `Observable`.
*
* @example
* this.storageMap.size.subscribe((size) => {
* console.log(size);
* });
*/
get size() {
return this.database.size
/* Catch if `indexedDb` is broken */
.pipe(this.catchIDBBroken(() => this.database.size));
}
/**
* Tells you which storage engine is used. *Only useful for interoperability.*
* Note that due to some browsers issues in some special contexts
* (Firefox private mode and Safari cross-origin iframes),
* **this information may be wrong at initialization,**
* as the storage could fallback from `indexedDB` to `localStorage`
* only after a first read or write operation.
* @returns Storage engine used
*
* @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/INTEROPERABILITY.md}
*
* @example
* if (this.storageMap.backingEngine === 'indexedDB') {}
*/
get backingEngine() {
if (this.database instanceof IndexedDBDatabase) {
return 'indexedDB';
}
else if (this.database instanceof LocalStorageDatabase) {
return 'localStorage';
}
else if (this.database instanceof MemoryDatabase) {
return 'memory';
}
else {
return 'unknown';
}
}
/**
* Info about `indexedDB` database. *Only useful for interoperability.*
* @returns `indexedDB` database name, store name and database version.
* **Values will be empty if the storage is not `indexedDB`,**
* **so it should be used after an engine check**.
*
* @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/INTEROPERABILITY.md}
*
* @example
* if (this.storageMap.backingEngine === 'indexedDB') {
* const { database, store, version } = this.storageMap.backingStore;
* }
*/
get backingStore() {
return (this.database instanceof IndexedDBDatabase) ?
this.database.backingStore :
{ database: '', store: '', version: 0 };
}
/**
* Info about `localStorage` fallback storage. *Only useful for interoperability.*
* @returns `localStorage` prefix.
* **Values will be empty if the storage is not `localStorage`,**
* **so it should be used after an engine check**.
*
* @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/INTEROPERABILITY.md}
*
* @example
* if (this.storageMap.backingEngine === 'localStorage') {
* const { prefix } = this.storageMap.fallbackBackingStore;
* }
*/
get fallbackBackingStore() {
return (this.database instanceof LocalStorageDatabase) ?
{ prefix: this.database.prefix } :
{ prefix: '' };
}
get(key, schema) {
/* Get the data in storage */
return this.database.get(key).pipe(
/* Check if `indexedDb` is broken */
this.catchIDBBroken(() => this.database.get(key)), mergeMap((data) => {
/* No need to validate if the data is empty */
if ((data === undefined) || (data === null)) {
return of(undefined);
}
else if (schema) {
/* Validate data against a JSON schema if provided */
if (!this.jsonValidator.validate(data, schema)) {
return throwError(new ValidationError());
}
/* Data have been checked, so it's OK to cast */
return of(data);
}
/* Cast to unknown as the data wasn't checked */
return of(data);
}));
}
/**
* Set an item in storage.
* Note that setting `null` or `undefined` will remove the item to avoid some browsers issues.
* @param key The item's key
* @param data The item's value
* @param schema Optional JSON schema to validate the data
* @returns A RxJS `Observable` to wait the end of the operation
*
* @example
* this.storageMap.set('key', 'value').subscribe(() => {});
*/
set(key, data, schema) {
/* Storing `undefined` or `null` is useless and can cause issues in `indexedDb` in some browsers,
* so removing item instead for all storages to have a consistent API */
if ((data === undefined) || (data === null)) {
return this.delete(key);
}
/* Validate data against a JSON schema if provided */
if (schema && !this.jsonValidator.validate(data, schema)) {
return throwError(new ValidationError());
}
return this.database.set(key, data).pipe(
/* Catch if `indexedDb` is broken */
this.catchIDBBroken(() => this.database.set(key, data)),
/* Notify watchers (must be last because it should only happen if the operation succeeds) */
tap(() => { this.notify(key, data); }));
}
/**
* Delete an item in storage
* @param key The item's key
* @returns A RxJS `Observable` to wait the end of the operation
*
* @example
* this.storageMap.delete('key').subscribe(() => {});
*/
delete(key) {
return this.database.delete(key).pipe(
/* Catch if `indexedDb` is broken */
this.catchIDBBroken(() => this.database.delete(key)),
/* Notify watchers (must be last because it should only happen if the operation succeeds) */
tap(() => { this.notify(key, undefined); }));
}
/**
* Delete all items in storage
* @returns A RxJS `Observable` to wait the end of the operation
*
* @example
* this.storageMap.clear().subscribe(() => {});
*/
clear() {
return this.database.clear().pipe(
/* Catch if `indexedDb` is broken */
this.catchIDBBroken(() => this.database.clear()),
/* Notify watchers (must be last because it should only happen if the operation succeeds) */
tap(() => {
for (const key of this.notifiers.keys()) {
this.notify(key, undefined);
}
}));
}
/**
* Get all keys stored in storage. Note **this is an *iterating* `Observable`**:
* * if there is no key, the `next` callback will not be invoked,
* * if you need to wait the whole operation to end, be sure to act in the `complete` callback,
* as this `Observable` can emit several values and so will invoke the `next` callback several times.
* @returns A list of the keys wrapped in a RxJS `Observable`
*
* @example
* this.storageMap.keys().subscribe({
* next: (key) => { console.log(key); },
* complete: () => { console.log('Done'); },
* });
*/
keys() {
return this.database.keys()
/* Catch if `indexedDb` is broken */
.pipe(this.catchIDBBroken(() => this.database.keys()));
}
/**
* Tells if a key exists in storage
* @returns A RxJS `Observable` telling if the key exists
*
* @example
* this.storageMap.has('key').subscribe((hasKey) => {
* if (hasKey) {}
* });
*/
has(key) {
return this.database.has(key)
/* Catch if `indexedDb` is broken */
.pipe(this.catchIDBBroken(() => this.database.has(key)));
}
watch(key, schema) {
/* Check if there is already a notifier and cast according to schema */
let notifier = this.notifiers.get(key);
if (!notifier) {
/* Create a notifier and cast according to schema */
notifier = new ReplaySubject(1);
/* Memorize the notifier */
this.notifiers.set(key, notifier);
/* Get the current item value */
this.get(key, schema).subscribe({
next: (result) => notifier.next(result),
error: (error) => notifier.error(error),
});
}
/* Only the public API of the `Observable` should be returned */
return notifier.asObservable();
}
/**
* Notify when a value changes
* @param key The item's key
* @param data The new value
*/
notify(key, value) {
const notifier = this.notifiers.get(key);
if (notifier) {
notifier.next(value);
}
}
/**
* RxJS operator to catch if `indexedDB` is broken
* @param operationCallback Callback with the operation to redo
*/
catchIDBBroken(operationCallback) {
return catchError((error) => {
/* Check if `indexedDB` is broken based on error message (the specific error class seems to be lost in the process) */
if ((error !== undefined) && (error !== null) && (error.message === IDB_BROKEN_ERROR)) {
/* When storage is fully disabled in browser (via the "Block all cookies" option),
* just trying to check `localStorage` variable causes a security exception.
* Prevents https://github.com/cyrilletuzi/angular-async-local-storage/issues/118
*/
try {
if ('getItem' in localStorage) {
/* Fallback to `localStorage` if available */
this.database = new LocalStorageDatabase(this.LSPrefix);
}
else {
/* Fallback to memory storage otherwise */
this.database = new MemoryDatabase();
}
}
catch (_a) {
/* Fallback to memory storage otherwise */
this.database = new MemoryDatabase();
}
/* Redo the operation */
return operationCallback();
}
else {
/* Otherwise, rethrow the error */
return throwError(error);
}
});
}
}
StorageMap.ɵprov = i0.ɵɵdefineInjectable({ factory: function StorageMap_Factory() { return new StorageMap(i0.ɵɵinject(i1.LocalDatabase), i0.ɵɵinject(i2.JSONValidator), i0.ɵɵinject(i3.LS_PREFIX)); }, token: StorageMap, providedIn: "root" });
StorageMap.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
StorageMap.ctorParameters = () => [
{ type: LocalDatabase },
{ type: JSONValidator },
{ type: undefined, decorators: [{ type: Inject, args: [LS_PREFIX,] }] }
];
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZS1tYXAuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1wd2EvbG9jYWwtc3RvcmFnZS9zcmMvbGliL3N0b3JhZ2VzL3N0b3JhZ2UtbWFwLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUFjLFVBQVUsRUFBRSxFQUFFLEVBQW9CLGFBQWEsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNuRixPQUFPLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQU0zRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDcEUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDMUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQzlELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxjQUFjLENBQUM7Ozs7O0FBSy9DLE1BQU0sT0FBTyxVQUFVO0lBSXJCOzs7OztPQUtHO0lBQ0gsWUFDWSxRQUF1QixFQUN2QixnQkFBK0IsSUFBSSxhQUFhLEVBQUUsRUFDL0IsV0FBVyxFQUFFO1FBRmhDLGFBQVEsR0FBUixRQUFRLENBQWU7UUFDdkIsa0JBQWEsR0FBYixhQUFhLENBQXFDO1FBQy9CLGFBQVEsR0FBUixRQUFRLENBQUs7UUFYbEMsY0FBUyxH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O2lCQUVGO2dCQUFDLFdBQU07b0JBRU4sMENBQTBDO29CQUMxQyxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksY0FBYyxFQUFFLENBQUM7aUJBRXRDO2dCQUVELHdCQUF3QjtnQkFDeEIsT0FBTyxpQkFBaUIsRUFBRSxDQUFDO2FBRTVCO2lCQUFNO2dCQUVMLGtDQUFrQztnQkFDbEMsT0FBTyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUM7YUFFMUI7UUFFSCxDQUFDLENBQUMsQ0FBQztJQUVMLENBQUM7Ozs7WUFyWkYsVUFBVSxTQUFDO2dCQUNWLFVBQVUsRUFBRSxNQUFNO2FBQ25COzs7WUFQUSxhQUFhO1lBSmIsYUFBYTs0Q0F5QmpCLE1BQU0sU0FBQyxTQUFTIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgSW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCB0aHJvd0Vycm9yLCBvZiwgT3BlcmF0b3JGdW5jdGlvbiwgUmVwbGF5U3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgbWVyZ2VNYXAsIGNhdGNoRXJyb3IsIHRhcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHtcbiAgSlNPTlNjaGVtYSwgSlNPTlNjaGVtYUJvb2xlYW4sIEpTT05TY2hlbWFJbnRlZ2VyLFxuICBKU09OU2NoZW1hTnVtYmVyLCBKU09OU2NoZW1hU3RyaW5nLCBKU09OU2NoZW1hQXJyYXlPZixcbn0gZnJvbSAnLi4vdmFsaWRhdGlvbi9qc29uLXNjaGVtYSc7XG5pbXBvcnQgeyBKU09OVmFsaWRhdG9yIH0gZnJvbSAnLi4vdmFsaWRhdGlvbi9qc29uLXZhbGlkYXRvcic7XG5pbXBvcnQgeyBJbmRleGVkREJEYXRhYmFzZSB9IGZyb20gJy4uL2RhdGFiYXNlcy9pbmRleGVkZGItZGF0YWJhc2UnO1xuaW1wb3J0IHsgTG9jYWxTdG9yYWdlRGF0YWJhc2UgfSBmcm9tICcuLi9kYXRhYmFzZXMvbG9jYWxzdG9yYWdlLWRhdGFiYXNlJztcbmltcG9ydCB7IE1lbW9yeURhdGFiYXNlIH0gZnJvbSAnLi4vZGF0YWJhc2VzL21lbW9yeS1kYXRhYmFzZSc7XG5pbXBvcnQgeyBMb2NhbERhdGFiYXNlIH0gZnJvbSAnLi4vZGF0YWJhc2VzL2xvY2FsLWRhdGFiYXNlJztcbmltcG9ydCB7IElEQl9CUk9LRU5fRVJST1IgfSBmcm9tICcuLi9kYXRhYmFzZXMvZXhjZXB0aW9ucyc7XG5pbXBvcnQgeyBMU19QUkVGSVggfSBmcm9tICcuLi90b2tlbnMnO1xuaW1wb3J0IHsgVmFsaWRhdGlvbkVycm9yIH0gZnJvbSAnLi9leGNlcHRpb25zJztcblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgU3RvcmFnZU1hcCB7XG5cbiAgcHJvdGVjdGVkIG5vdGlmaWVycyA9IG5ldyBNYXA8c3RyaW5nLCBSZXBsYXlTdWJqZWN0PHVua25vd24+PigpO1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RvciBwYXJhbXMgYXJlIHByb3ZpZGVkIGJ5IEFuZ3VsYXIgKGJ1dCBjYW4gYWxzbyBiZSBwYXNzZWQgbWFudWFsbHkgaW4gdGVzdHMpXG4gICAqIEBwYXJhbSBkYXRhYmFzZSBTdG9yYWdlIHRvIHVzZVxuICAgKiBAcGFyYW0ganNvblZhbGlkYXRvciBWYWxpZGF0b3Igc2VydmljZVxuICAgKiBAcGFyYW0gTFNQcmVmaXggUHJlZml4IGZvciBgbG9jYWxTdG9yYWdlYCBrZXlzIHRvIGF2b2lkIGNvbGxpc2lvbiBmb3IgbXVsdGlwbGUgYXBwcyBvbiB0aGUgc2FtZSBzdWJkb21haW4gb3IgZm9yIGludGVyb3BlcmFiaWxpdHlcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByb3RlY3RlZCBkYXRhYmFzZTogTG9jYWxEYXRhYmFzZSxcbiAgICBwcm90ZWN0ZWQganNvblZhbGlkYXRvcjogSlNPTlZhbGlkYXRvciA9IG5ldyBKU09OVmFsaWRhdG9yKCksXG4gICAgQEluamVjdChMU19QUkVGSVgpIHByb3RlY3RlZCBMU1ByZWZpeCA9ICcnLFxuICApIHt9XG5cbiAgLyoqXG4gICAqICoqTnVtYmVyIG9mIGl0ZW1zKiogaW4gc3RvcmFnZSwgd3JhcHBlZCBpbiBhbiBgT2JzZXJ2YWJsZWAuXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIHRoaXMuc3RvcmFnZU1hcC5zaXplLnN1YnNjcmliZSgoc2l6ZSkgPT4ge1xuICAgKiAgIGNvbnNvbGUubG9nKHNpemUpO1xuICAgKiB9KTtcbiAgICovXG4gIGdldCBzaXplKCk6IE9ic2VydmFibGU8bnVtYmVyPiB7XG5cbiAgICByZXR1cm4gdGhpcy5kYXRhYmFzZS5zaXplXG4gICAgICAvKiBDYXRjaCBpZiBgaW5kZXhlZERiYCBpcyBicm9rZW4gKi9cbiAgICAgIC5waXBlKHRoaXMuY2F0Y2hJREJCcm9rZW4oKCkgPT4gdGhpcy5kYXRhYmFzZS5zaXplKSk7XG5cbiAgfVxuXG4gIC8qKlxuICAgKiBUZWxscyB5b3Ugd2hpY2ggc3RvcmFnZSBlbmdpbmUgaXMgdXNlZC4gKk9ubHkgdXNlZnVsIGZvciBpbnRlcm9wZXJhYmlsaXR5LipcbiAgICogTm90ZSB0aGF0IGR1ZSB0byBzb21lIGJyb3dzZXJzIGlzc3VlcyBpbiBzb21lIHNwZWNpYWwgY29udGV4dHNcbiAgICogKEZpcmVmb3ggcHJpdmF0ZSBtb2RlIGFuZCBTYWZhcmkgY3Jvc3Mtb3JpZ2luIGlmcmFtZXMpLFxuICAgKiAqKnRoaXMgaW5mb3JtYXRpb24gbWF5IGJlIHdyb25nIGF0IGluaXRpYWxpemF0aW9uLCoqXG4gICAqIGFzIHRoZSBzdG9yYWdlIGNvdWxkIGZhbGxiYWNrIGZyb20gYGluZGV4ZWREQmAgdG8gYGxvY2FsU3RvcmFnZWBcbiAgICogb25seSBhZnRlciBhIGZpcnN0IHJlYWQgb3Igd3JpdGUgb3BlcmF0aW9uLlxuICAgKiBAcmV0dXJucyBTdG9yYWdlIGVuZ2luZSB1c2VkXG4gICAqXG4gICAqIEBzZWUge0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS9jeXJpbGxldHV6aS9hbmd1bGFyLWFzeW5jLWxvY2FsLXN0b3JhZ2UvYmxvYi9tYXN0ZXIvZG9jcy9JTlRFUk9QRVJBQklMSVRZLm1kfVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBpZiAodGhpcy5zdG9yYWdlTWFwLmJhY2tpbmdFbmdpbmUgPT09ICdpbmRleGVkREInKSB7fVxuICAgKi9cbiAgZ2V0IGJhY2tpbmdFbmdpbmUoKTogJ2luZGV4ZWREQicgfCAnbG9jYWxTdG9yYWdlJyB8wqAnbWVtb3J5JyB8ICd1bmtub3duJyB7XG5cbiAgICBpZiAodGhpcy5kYXRhYmFzZSBpbnN0YW5jZW9mIEluZGV4ZWREQkRhdGFiYXNlKSB7XG5cbiAgICAgIHJldHVybiAnaW5kZXhlZERCJztcblxuICAgIH0gZWxzZSBpZiAodGhpcy5kYXRhYmFzZSBpbnN0YW5jZW9mIExvY2FsU3RvcmFnZURhdGFiYXNlKSB7XG5cbiAgICAgIHJldHVybiAnbG9jYWxTdG9yYWdlJztcblxuICAgIH0gZWxzZSBpZiAodGhpcy5kYXRhYmFzZSBpbnN0YW5jZW9mIE1lbW9yeURhdGFiYXNlKSB7XG5cbiAgICAgIHJldHVybiAnbWVtb3J5JztcblxuICAgIH0gZWxzZSB7XG5cbiAgICAgIHJldHVybiAndW5rbm93bic7XG5cbiAgICB9XG5cbiAgfVxuXG4gIC8qKlxuICAgKiBJbmZvIGFib3V0IGBpbmRleGVkREJgIGRhdGFiYXNlLiAqT25seSB1c2VmdWwgZm9yIGludGVyb3BlcmFiaWxpdHkuKlxuICAgKiBAcmV0dXJucyBgaW5kZXhlZERCYCBkYXRhYmFzZSBuYW1lLCBzdG9yZSBuYW1lIGFuZCBkYXRhYmFzZSB2ZXJzaW9uLlxuICAgKiAqKlZhbHVlcyB3aWxsIGJlIGVtcHR5IGlmIHRoZSBzdG9yYWdlIGlzIG5vdCBgaW5kZXhlZERCYCwqKlxuICAgKiAqKnNvIGl0IHNob3VsZCBiZSB1c2VkIGFmdGVyIGFuIGVuZ2luZSBjaGVjayoqLlxuICAgKlxuICAgKiBAc2VlIHtAbGluayBodHRwczovL2dpdGh1Yi5jb20vY3lyaWxsZXR1emkvYW5ndWxhci1hc3luYy1sb2NhbC1zdG9yYWdlL2Jsb2IvbWFzdGVyL2RvY3MvSU5URVJPUEVSQUJJTElUWS5tZH1cbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogaWYgKHRoaXMuc3RvcmFnZU1hcC5iYWNraW5nRW5naW5lID09PSAnaW5kZXhlZERCJykge1xuICAgKiAgIGNvbnN0IHsgZGF0YWJhc2UsIHN0b3JlLCB2ZXJzaW9uIH0gPSB0aGlzLnN0b3JhZ2VNYXAuYmFja2luZ1N0b3JlO1xuICAgKiB9XG4gICAqL1xuICBnZXQgYmFja2luZ1N0b3JlKCk6IHsgZGF0YWJhc2U6IHN0cmluZywgc3RvcmU6IHN0cmluZywgdmVyc2lvbjogbnVtYmVyIH0ge1xuXG4gICAgcmV0dXJuICh0aGlzLmRhdGFiYXNlIGluc3RhbmNlb2YgSW5kZXhlZERCRGF0YWJhc2UpID9cbiAgICAgIHRoaXMuZGF0YWJhc2UuYmFja2luZ1N0b3JlIDpcbiAgICAgIHsgZGF0YWJhc2U6ICcnLCBzdG9yZTogJycsIHZlcnNpb246IDAgfTtcblxuICB9XG5cbiAgLyoqXG4gICAqIEluZm8gYWJvdXQgYGxvY2FsU3RvcmFnZWAgZmFsbGJhY2sgc3RvcmFnZS4gKk9ubHkgdXNlZnVsIGZvciBpbnRlcm9wZXJhYmlsaXR5LipcbiAgICogQHJldHVybnMgYGxvY2FsU3RvcmFnZWAgcHJlZml4LlxuICAgKiAqKlZhbHVlcyB3aWxsIGJlIGVtcHR5IGlmIHRoZSBzdG9yYWdlIGlzIG5vdCBgbG9jYWxTdG9yYWdlYCwqKlxuICAgKiAqKnNvIGl0IHNob3VsZCBiZSB1c2VkIGFmdGVyIGFuIGVuZ2luZSBjaGVjayoqLlxuICAgKlxuICAgKiBAc2VlIHtAbGluayBodHRwczovL2dpdGh1Yi5jb20vY3lyaWxsZXR1emkvYW5ndWxhci1hc3luYy1sb2NhbC1zdG9yYWdlL2Jsb2IvbWFzdGVyL2RvY3MvSU5URVJPUEVSQUJJTElUWS5tZH1cbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogaWYgKHRoaXMuc3RvcmFnZU1hcC5iYWNraW5nRW5naW5lID09PSAnbG9jYWxTdG9yYWdlJykge1xuICAgKiAgIGNvbnN0IHsgcHJlZml4IH0gPSB0aGlzLnN0b3JhZ2VNYXAuZmFsbGJhY2tCYWNraW5nU3RvcmU7XG4gICAqIH1cbiAgICovXG4gIGdldCBmYWxsYmFja0JhY2tpbmdTdG9yZSgpOiB7IHByZWZpeDogc3RyaW5nIH0ge1xuXG4gICAgcmV0dXJuICh0aGlzLmRhdGFiYXNlIGluc3RhbmNlb2YgTG9jYWxTdG9yYWdlRGF0YWJhc2UpID9cbiAgICAgIHsgcHJlZml4OiB0aGlzLmRhdGFiYXNlLnByZWZpeCB9IDpcbiAgICAgIHsgcHJlZml4OiAnJyB9O1xuXG4gIH1cblxuICAvKipcbiAgICogR2V0IGFuIGl0ZW0gdmFsdWUgaW4gc3RvcmFnZS5cbiAgICogVGhlIHNpZ25hdHVyZSBoYXMgbWFueSBvdmVybG9hZHMgZHVlIHRvIHZhbGlkYXRpb24sICoqcGxlYXNlIHJlZmVyIHRvIHRoZSBkb2N1bWVudGF0aW9uLioqXG4gICAqIEBzZWUge0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS9jeXJpbGxldHV6aS9hbmd1bGFyLWFzeW5jLWxvY2FsLXN0b3JhZ2UvYmxvYi9tYXN0ZXIvZG9jcy9WQUxJREFUSU9OLm1kfVxuICAgKiBAcGFyYW0ga2V5IFRoZSBpdGVtJ3Mga2V5XG4gICAqIEBwYXJhbSBzY2hlbWEgT3B0aW9uYWwgSlNPTiBzY2hlbWEgdG8gdmFsaWRhdGUgdGhlIGRhdGFcbiAgICogQHJldHVybnMgVGhlIGl0ZW0ncyB2YWx1ZSBpZiB0aGUga2V5IGV4aXN0cywgYHVuZGVmaW5lZGAgb3RoZXJ3aXNlLCB3cmFwcGVkIGluIGEgUnhKUyBgT2JzZXJ2YWJsZWBcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogdGhpcy5zdG9yYWdlTWFwLmdldCgna2V5JywgeyB0eXBlOiAnc3RyaW5nJyB9KS5zdWJzY3JpYmUoKHJlc3VsdCkgPT4ge1xuICAgKiAgIHJlc3VsdDsgLy8gc3RyaW5nIG9yIHVuZGVmaW5lZFxuICAgKiB9KTtcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogaW50ZXJmYWNlIFVzZXIge1xuICAgKiAgIGZpcnN0TmFtZTogc3RyaW5nO1xuICAgKiAgIGxhc3ROYW1lPzogc3RyaW5nO1xuICAgKiB9XG4gICAqXG4gICAqIGNvbnN0IHNjaGVtYSA9IHtcbiAgICogICB0eXBlOiAnb2JqZWN0JyxcbiAgICogICBwcm9wZXJ0aWVzOiB7XG4gICAqICAgICBmaXJzdE5hbWU6IHsgdHlwZTogJ3N0cmluZycgfSxcbiAgICogICAgIGxhc3ROYW1lOiB7IHR5cGU6ICdzdHJpbmcnIH0sXG4gICAqICAgfSxcbiAgICogICByZXF1aXJlZDogWydmaXJzdE5hbWUnXVxuICAgKiB9O1xuICAgKlxuICAgKiB0aGlzLnN0b3JhZ2VNYXAuZ2V0PFVzZXI+KCd1c2VyJywgc2NoZW1hKS5zdWJzY3JpYmUoKHVzZXIpID0+IHtcbiAgICogICBpZiAodXNlcikge1xuICAgKiAgICAgdXNlci5maXJzdE5hbWU7XG4gICAqICAgfVxuICAgKiB9KTtcbiAgICovXG4gIGdldDxUID0gc3RyaW5nPihrZXk6IHN0cmluZywgc2NoZW1hOiBKU09OU2NoZW1hU3RyaW5nKTogT2JzZXJ2YWJsZTxzdHJpbmcgfCB1bmRlZmluZWQ+O1xuICBnZXQ8VCA9IG51bWJlcj4oa2V5OiBzdHJpbmcsIHNjaGVtYTogSlNPTlNjaGVtYUludGVnZXLCoHwgSlNPTlNjaGVtYU51bWJlcik6IE9ic2VydmFibGU8bnVtYmVyIHwgdW5kZWZpbmVkPjtcbiAgZ2V0PFQgPSBib29sZWFuPihrZXk6IHN0cmluZywgc2NoZW1hOiBKU09OU2NoZW1hQm9vbGVhbik6IE9ic2VydmFibGU8Ym9vbGVhbiB8IHVuZGVmaW5lZD47XG4gIGdldDxUID0gc3RyaW5nW10+KGtleTogc3RyaW5nLCBzY2hlbWE6IEpTT05TY2hlbWFBcnJheU9mPEpTT05TY2hlbWFTdHJpbmc+KTogT2JzZXJ2YWJsZTxzdHJpbmdbXSB8IHVuZGVmaW5lZD47XG4gIGdldDxUID0gbnVtYmVyW10+KGtleTogc3RyaW5nLCBzY2hlbWE6IEpTT05TY2hlbWFBcnJheU9mPEpTT05TY2hlbWFJbnRlZ2VywqB8IEpTT05TY2hlbWFOdW1iZXI+KTogT2JzZXJ2YWJsZTxudW1iZXJbXSB8IHVuZGVmaW5lZD47XG4gIGdldDxUID0gYm9vbGVhbltdPihrZXk6IHN0cmluZywgc2NoZW1hOiBKU09OU2NoZW1hQXJyYXlPZjxKU09OU2NoZW1hQm9vbGVhbj4pOiBPYnNlcnZhYmxlPGJvb2xlYW5bXSB8IHVuZGVmaW5lZD47XG4gIGdldDxUID0gdW5rbm93bj4oa2V5OiBzdHJpbmcsIHNjaGVtYTogSlNPTlNjaGVtYSk6IE9ic2VydmFibGU8VCB8IHVuZGVmaW5lZD47XG4gIGdldDxUID0gdW5rbm93bj4oa2V5OiBzdHJpbmcsIHNjaGVtYT86IEpTT05TY2hlbWEpOiBPYnNlcnZhYmxlPHVua25vd24+O1xuICBnZXQ8VCA9IHVua25vd24+KGtleTogc3RyaW5nLCBzY2hlbWE/OiBKU09OU2NoZW1hKTogT2JzZXJ2YWJsZTx1bmtub3duPiB7XG5cbiAgICAvKiBHZXQgdGhlIGRhdGEgaW4gc3RvcmFnZSAqL1xuICAgIHJldHVybiB0aGlzLmRhdGFiYXNlLmdldDxUPihrZXkpLnBpcGUoXG4gICAgICAvKiBDaGVjayBpZiBgaW5kZXhlZERiYCBpcyBicm9rZW4gKi9cbiAgICAgIHRoaXMuY2F0Y2hJREJCcm9rZW4oKCkgPT4gdGhpcy5kYXRhYmFzZS5nZXQ8VD4oa2V5KSksXG4gICAgICBtZXJnZU1hcCgoZGF0YSkgPT4ge1xuXG4gICAgICAgIC8qIE5vIG5lZWQgdG8gdmFsaWRhdGUgaWYgdGhlIGRhdGEgaXMgZW1wdHkgKi9cbiAgICAgICAgaWYgKChkYXRhID09PSB1bmRlZmluZWQpIHx8IChkYXRhID09PSBudWxsKSkge1xuXG4gICAgICAgICAgcmV0dXJuIG9mKHVuZGVmaW5lZCk7XG5cbiAgICAgICAgfSBlbHNlIGlmIChzY2hlbWEpIHtcblxuICAgICAgICAgIC8qIFZhbGlkYXRlIGRhdGEgYWdhaW5zdCBhIEpTT04gc2NoZW1hIGlmIHByb3ZpZGVkICovXG4gICAgICAgICAgaWYgKCF0aGlzLmpzb25WYWxpZGF0b3IudmFsaWRhdGUoZGF0YSwgc2NoZW1hKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRocm93RXJyb3IobmV3IFZhbGlkYXRpb25FcnJvcigpKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvKiBEYXRhIGhhdmUgYmVlbiBjaGVja2VkLCBzbyBpdCdzIE9LIHRvIGNhc3QgKi9cbiAgICAgICAgICByZXR1cm4gb2YoZGF0YSBhcyBUIHwgdW5kZWZpbmVkKTtcblxuICAgICAgICB9XG5cbiAgICAgICAgLyogQ2FzdCB0byB1bmtub3duIGFzIHRoZSBkYXRhIHdhc24ndCBjaGVja2VkICovXG4gICAgICAgIHJldHVybiBvZihkYXRhIGFzIHVua25vd24pO1xuXG4gICAgICB9KSxcbiAgICApO1xuXG4gIH1cblxuICAvKipcbiAgICogU2V0IGFuIGl0ZW0gaW4gc3RvcmFnZS5cbiAgICogTm90ZSB0aGF0IHNldHRpbmcgYG51bGxgIG9yIGB1bmRlZmluZWRgIHdpbGwgcmVtb3ZlIHRoZSBpdGVtIHRvIGF2b2lkIHNvbWUgYnJvd3NlcnMgaXNzdWVzLlxuICAgKiBAcGFyYW0ga2V5IFRoZSBpdGVtJ3Mga2V5XG4gICAqIEBwYXJhbSBkYXRhIFRoZSBpdGVtJ3MgdmFsdWVcbiAgICogQHBhcmFtIHNjaGVtYSBPcHRpb25hbCBKU09OIHNjaGVtYSB0byB2YWxpZGF0ZSB0aGUgZGF0YVxuICAgKiBAcmV0dXJucyBBIFJ4SlMgYE9ic2VydmFibGVgIHRvIHdhaXQgdGhlIGVuZCBvZiB0aGUgb3BlcmF0aW9uXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIHRoaXMuc3RvcmFnZU1hcC5zZXQoJ2tleScsICd2YWx1ZScpLnN1YnNjcmliZSgoKSA9PiB7fSk7XG4gICAqL1xuICBzZXQoa2V5OiBzdHJpbmcsIGRhdGE6IHVua25vd24sIHNjaGVtYT86IEpTT05TY2hlbWEpOiBPYnNlcnZhYmxlPHVuZGVmaW5lZD4ge1xuXG4gICAgLyogU3RvcmluZyBgdW5kZWZpbmVkYCBvciBgbnVsbGAgaXMgdXNlbGVzcyBhbmQgY2FuIGNhdXNlIGlzc3VlcyBpbiBgaW5kZXhlZERiYCBpbiBzb21lIGJyb3dzZXJzLFxuICAgICAqIHNvIHJlbW92aW5nIGl0ZW0gaW5zdGVhZCBmb3IgYWxsIHN0b3JhZ2VzIHRvIGhhdmUgYSBjb25zaXN0ZW50IEFQSSAqL1xuICAgIGlmICgoZGF0YSA9PT0gdW5kZWZpbmVkKSB8fCAoZGF0YSA9PT0gbnVsbCkpIHtcbiAgICAgIHJldHVybiB0aGlzLmRlbGV0ZShrZXkpO1xuICAgIH1cblxuICAgIC8qIFZhbGlkYXRlIGRhdGEgYWdhaW5zdCBhIEpTT04gc2NoZW1hIGlmIHByb3ZpZGVkICovXG4gICAgaWYgKHNjaGVtYSAmJiAhdGhpcy5qc29uVmFsaWRhdG9yLnZhbGlkYXRlKGRhdGEsIHNjaGVtYSkpIHtcbiAgICAgIHJldHVybiB0aHJvd0Vycm9yKG5ldyBWYWxpZGF0aW9uRXJyb3IoKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZGF0YWJhc2Uuc2V0KGtleSwgZGF0YSkucGlwZShcbiAgICAgIC8qIENhdGNoIGlmIGBpbmRleGVkRGJgIGlzIGJyb2tlbiAqL1xuICAgICAgdGhpcy5jYXRjaElEQkJyb2tlbigoKSA9PiB0aGlzLmRhdGFiYXNlLnNldChrZXksIGRhdGEpKSxcbiAgICAgIC8qIE5vdGlmeSB3YXRjaGVycyAobXVzdCBiZSBsYXN0IGJlY2F1c2UgaXQgc2hvdWxkIG9ubHkgaGFwcGVuIGlmIHRoZSBvcGVyYXRpb24gc3VjY2VlZHMpICovXG4gICAgICB0YXAoKCkgPT4geyB0aGlzLm5vdGlmeShrZXksIGRhdGEpOyB9KSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIERlbGV0ZSBhbiBpdGVtIGluIHN0b3JhZ2VcbiAgICogQHBhcmFtIGtleSBUaGUgaXRlbSdzIGtleVxuICAgKiBAcmV0dXJucyBBIFJ4SlMgYE9ic2VydmFibGVgIHRvIHdhaXQgdGhlIGVuZCBvZiB0aGUgb3BlcmF0aW9uXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIHRoaXMuc3RvcmFnZU1hcC5kZWxldGUoJ2tleScpLnN1YnNjcmliZSgoKSA9PiB7fSk7XG4gICAqL1xuICBkZWxldGUoa2V5OiBzdHJpbmcpOiBPYnNlcnZhYmxlPHVuZGVmaW5lZD4ge1xuXG4gICAgcmV0dXJuIHRoaXMuZGF0YWJhc2UuZGVsZXRlKGtleSkucGlwZShcbiAgICAgIC8qIENhdGNoIGlmIGBpbmRleGVkRGJgIGlzIGJyb2tlbiAqL1xuICAgICAgdGhpcy5jYXRjaElEQkJyb2tlbigoKSA9PiB0aGlzLmRhdGFiYXNlLmRlbGV0ZShrZXkpKSxcbiAgICAgIC8qIE5vdGlmeSB3YXRjaGVycyAobXVzdCBiZSBsYXN0IGJlY2F1c2UgaXQgc2hvdWxkIG9ubHkgaGFwcGVuIGlmIHRoZSBvcGVyYXRpb24gc3VjY2VlZHMpICovXG4gICAgICB0YXAoKCkgPT4geyB0aGlzLm5vdGlmeShrZXksIHVuZGVmaW5lZCk7IH0pLFxuICAgICk7XG5cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWxldGUgYWxsIGl0ZW1zIGluIHN0b3JhZ2VcbiAgICogQHJldHVybnMgQSBSeEpTIGBPYnNlcnZhYmxlYCB0byB3YWl0IHRoZSBlbmQgb2YgdGhlIG9wZXJhdGlvblxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiB0aGlzLnN0b3JhZ2VNYXAuY2xlYXIoKS5zdWJzY3JpYmUoKCkgPT4ge30pO1xuICAgKi9cbiAgY2xlYXIoKTogT2JzZXJ2YWJsZTx1bmRlZmluZWQ+IHtcblxuICAgIHJldHVybiB0aGlzLmRhdGFiYXNlLmNsZWFyKCkucGlwZShcbiAgICAgIC8qIENhdGNoIGlmIGBpbmRleGVkRGJgIGlzIGJyb2tlbiAqL1xuICAgICAgdGhpcy5jYXRjaElEQkJyb2tlbigoKSA9PiB0aGlzLmRhdGFiYXNlLmNsZWFyKCkpLFxuICAgICAgLyogTm90aWZ5IHdhdGNoZXJzIChtdXN0IGJlIGxhc3QgYmVjYXVzZSBpdCBzaG91bGQgb25seSBoYXBwZW4gaWYgdGhlIG9wZXJhdGlvbiBzdWNjZWVkcykgKi9cbiAgICAgIHRhcCgoKSA9PiB7XG4gICAgICAgIGZvciAoY29uc3Qga2V5IG9mIHRoaXMubm90aWZpZXJzLmtleXMoKSkge1xuICAgICAgICAgIHRoaXMubm90aWZ5KGtleSwgdW5kZWZpbmVkKTtcbiAgICAgICAgfVxuICAgICAgfSksXG4gICAgKTtcblxuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbGwga2V5cyBzdG9yZWQgaW4gc3RvcmFnZS4gTm90ZSAqKnRoaXMgaXMgYW4gKml0ZXJhdGluZyogYE9ic2VydmFibGVgKio6XG4gICAqICogaWYgdGhlcmUgaXMgbm8ga2V5LCB0aGUgYG5leHRgIGNhbGxiYWNrIHdpbGwgbm90IGJlIGludm9rZWQsXG4gICAqICogaWYgeW91IG5lZWQgdG8gd2FpdCB0aGUgd2hvbGUgb3BlcmF0aW9uIHRvIGVuZCwgYmUgc3VyZSB0byBhY3QgaW4gdGhlIGBjb21wbGV0ZWAgY2FsbGJhY2ssXG4gICAqIGFzIHRoaXMgYE9ic2VydmFibGVgIGNhbiBlbWl0IHNldmVyYWwgdmFsdWVzIGFuZCBzbyB3aWxsIGludm9rZSB0aGUgYG5leHRgIGNhbGxiYWNrIHNldmVyYWwgdGltZXMuXG4gICAqIEByZXR1cm5zIEEgbGlzdCBvZiB0aGUga2V5cyB3cmFwcGVkIGluIGEgUnhKUyBgT2JzZXJ2YWJsZWBcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogdGhpcy5zdG9yYWdlTWFwLmtleXMoKS5zdWJzY3JpYmUoe1xuICAgKiAgIG5leHQ6IChrZXkpID0+IHsgY29uc29sZS5sb2coa2V5KTsgfSxcbiAgICogICBjb21wbGV0ZTogKCkgPT4geyBjb25zb2xlLmxvZygnRG9uZScpOyB9LFxuICAgKiB9KTtcbiAgICovXG4gIGtleXMoKTogT2JzZXJ2YWJsZTxzdHJpbmc+IHtcblxuICAgIHJldHVybiB0aGlzLmRhdGFiYXNlLmtleXMoKVxuICAgICAgLyogQ2F0Y2ggaWYgYGluZGV4ZWREYmAgaXMgYnJva2VuICovXG4gICAgICAucGlwZSh0aGlzLmNhdGNoSURCQnJva2VuKCgpID0+IHRoaXMuZGF0YWJhc2Uua2V5cygpKSk7XG5cbiAgfVxuXG4gIC8qKlxuICAgKiBUZWxscyBpZiBhIGtleSBleGlzdHMgaW4gc3RvcmFnZVxuICAgKiBAcmV0dXJucyBBIFJ4SlMgYE9ic2VydmFibGVgIHRlbGxpbmcgaWYgdGhlIGtleSBleGlzdHNcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogdGhpcy5zdG9yYWdlTWFwLmhhcygna2V5Jykuc3Vic2NyaWJlKChoYXNLZXkpID0+IHtcbiAgICogICBpZiAoaGFzS2V5KSB7fVxuICAgKiB9KTtcbiAgICovXG4gIGhhcyhrZXk6IHN0cmluZyk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuXG4gICAgcmV0dXJuIHRoaXMuZGF0YWJhc2UuaGFzKGtleSlcbiAgICAgIC8qIENhdGNoIGlmIGBpbmRleGVkRGJgIGlzIGJyb2tlbiAqL1xuICAgICAgLnBpcGUodGhpcy5jYXRjaElEQkJyb2tlbigoKSA9PiB0aGlzLmRhdGFiYXNlLmhhcyhrZXkpKSk7XG5cbiAgfVxuXG4gIC8qKlxuICAgKiBXYXRjaCBhbiBpdGVtIHZhbHVlIGluIHN0b3JhZ2UuXG4gICAqICoqTm90ZSBvbmx5IGNoYW5nZXMgZG9uZSB2aWEgdGhpcyBsaWIgd2lsbCBiZSB3YXRjaGVkKiosIGV4dGVybmFsIGNoYW5nZXMgaW4gc3RvcmFnZSBjYW4ndCBiZSBkZXRlY3RlZC5cbiAgICogVGhlIHNpZ25hdHVyZSBoYXMgbWFueSBvdmVybG9hZHMgZHVlIHRvIHZhbGlkYXRpb24sICoqcGxlYXNlIHJlZmVyIHRvIHRoZSBkb2N1bWVudGF0aW9uLioqXG4gICAqIEBzZWUgaHR0cHM6Ly9naXRodWIuY29tL2N5cmlsbGV0dXppL2FuZ3VsYXItYXN5bmMtbG9jYWwtc3RvcmFnZS9ibG9iL21hc3Rlci9kb2NzL1ZBTElEQVRJT04ubWRcbiAgICogQHBhcmFtIGtleSBUaGUgaXRlbSdzIGtleSB0byB3YXRjaFxuICAgKiBAcGFyYW0gc2NoZW1hIE9wdGlvbmFsIEpTT04gc2NoZW1hIHRvIHZhbGlkYXRlIHRoZSBpbml0aWFsIHZhbHVlXG4gICAqIEByZXR1cm5zIEFuIGluZmluaXRlIGBPYnNlcnZhYmxlYCBnaXZpbmcgdGhlIGN1cnJlbnQgdmFsdWVcbiAgICovXG4gIHdhdGNoPFQgPSBzdHJpbmc+KGtleTogc3RyaW5nLCBzY2hlbWE6IEpTT05TY2hlbWFTdHJpbmcpOiBPYnNlcnZhYmxlPHN0cmluZyB8IHVuZGVmaW5lZD47XG4gIHdhdGNoPFQgPSBudW1iZXI+KGtleTogc3RyaW5nLCBzY2hlbWE6IEpTT05TY2hlbWFJbnRlZ2VyIHwgSlNPTlNjaGVtYU51bWJlcik6IE9ic2VydmFibGU8bnVtYmVyIHwgdW5kZWZpbmVkPjtcbiAgd2F0Y2g8VCA9IGJvb2xlYW4+KGtleTogc3RyaW5nLCBzY2hlbWE6IEpTT05TY2hlbWFCb29sZWFuKTogT2JzZXJ2YWJsZTxib29sZWFuIHwgdW5kZWZpbmVkPjtcbiAgd2F0Y2g8VCA9IHN0cmluZ1tdPihrZXk6IHN0cmluZywgc2NoZW1hOiBKU09OU2NoZW1hQXJyYXlPZjxKU09OU2NoZW1hU3RyaW5nPik6IE9ic2VydmFibGU8c3RyaW5nW10gfCB1bmRlZmluZWQ+O1xuICB3YXRjaDxUID0gbnVtYmVyW10+KGtleTogc3RyaW5nLCBzY2hlbWE6IEpTT05TY2hlbWFBcnJheU9mPEpTT05TY2hlbWFJbnRlZ2VyIHwgSlNPTlNjaGVtYU51bWJlcj4pOiBPYnNlcnZhYmxlPG51bWJlcltdIHwgdW5kZWZpbmVkPjtcbiAgd2F0Y2g8VCA9IGJvb2xlYW5bXT4oa2V5OiBzdHJpbmcsIHNjaGVtYTogSlNPTlNjaGVtYUFycmF5T2Y8SlNPTlNjaGVtYUJvb2xlYW4+KTogT2JzZXJ2YWJsZTxib29sZWFuW10gfCB1bmRlZmluZWQ+O1xuICB3YXRjaDxUID0gdW5rbm93bj4oa2V5OiBzdHJpbmcsIHNjaGVtYTogSlNPTlNjaGVtYSk6IE9ic2VydmFibGU8VCB8IHVuZGVmaW5lZD47XG4gIHdhdGNoPFQgPSB1bmtub3duPihrZXk6IHN0cmluZywgc2NoZW1hPzogSlNPTlNjaGVtYSk6IE9ic2VydmFibGU8dW5rbm93bj47XG4gIHdhdGNoPFQgPSB1bmtub3duPihrZXk6IHN0cmluZywgc2NoZW1hPzogSlNPTlNjaGVtYSk6IE9ic2VydmFibGU8dW5rbm93bj4ge1xuXG4gICAgLyogQ2hlY2sgaWYgdGhlcmUgaXMgYWxyZWFkeSBhIG5vdGlmaWVyIGFuZCBjYXN0IGFjY29yZGluZyB0byBzY2hlbWEgKi9cbiAgICBsZXQgbm90aWZpZXIgPSB0aGlzLm5vdGlmaWVycy5nZXQoa2V5KSBhcyBSZXBsYXlTdWJqZWN0PHR5cGVvZiBzY2hlbWEgZXh0ZW5kcyBKU09OU2NoZW1hID8gKFQgfCB1bmRlZmluZWQpIDogdW5rbm93bj47XG5cbiAgICBpZiAoIW5vdGlmaWVyKSB7XG5cbiAgICAgIC8qIENyZWF0ZSBhIG5vdGlmaWVyIGFuZCBjYXN0IGFjY29yZGluZyB0byBzY2hlbWEgKi9cbiAgICAgIG5vdGlmaWVyID0gbmV3IFJlcGxheVN1YmplY3Q8dHlwZW9mIHNjaGVtYSBleHRlbmRzIEpTT05TY2hlbWEgPyAoVCB8IHVuZGVmaW5lZCkgOiB1bmtub3duPigxKTtcblxuICAgICAgLyogTWVtb3JpemUgdGhlIG5vdGlmaWVyICovXG4gICAgICB0aGlzLm5vdGlmaWVycy5zZXQoa2V5LCBub3RpZmllcik7XG5cbiAgICAgIC8qIEdldCB0aGUgY3VycmVudCBpdGVtIHZhbHVlICovXG4gICAgICB0aGlzLmdldDxUPihrZXksIHNjaGVtYSkuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKHJlc3VsdCkgPT4gbm90aWZpZXIubmV4dChyZXN1bHQpLFxuICAgICAgICBlcnJvcjogKGVycm9yKSA9PiBub3RpZmllci5lcnJvcihlcnJvciksXG4gICAgICB9KTtcblxuICAgIH1cblxuICAgIC8qIE9ubHkgdGhlIHB1YmxpYyBBUEkgb2YgdGhlIGBPYnNlcnZhYmxlYCBzaG91bGQgYmUgcmV0dXJuZWQgKi9cbiAgICByZXR1cm4gbm90aWZpZXIuYXNPYnNlcnZhYmxlKCk7XG5cbiAgfVxuXG4gIC8qKlxuICAgKiBOb3RpZnkgd2hlbiBhIHZhbHVlIGNoYW5nZXNcbiAgICogQHBhcmFtIGtleSBUaGUgaXRlbSdzIGtleVxuICAgKiBAcGFyYW0gZGF0YSBUaGUgbmV3IHZhbHVlXG4gICAqL1xuICBwcm90ZWN0ZWQgbm90aWZ5KGtleTogc3RyaW5nLCB2YWx1ZTogdW5rbm93bik6IHZvaWQge1xuXG4gICAgY29uc3Qgbm90aWZpZXIgPSB0aGlzLm5vdGlmaWVycy5nZXQoa2V5KTtcblxuICAgIGlmIChub3RpZmllcikge1xuICAgICAgbm90aWZpZXIubmV4dCh2YWx1ZSk7XG4gICAgfVxuXG4gIH1cblxuICAvKipcbiAgICogUnhKUyBvcGVyYXRvciB0byBjYXRjaCBpZiBgaW5kZXhlZERCYCBpcyBicm9rZW5cbiAgICogQHBhcmFtIG9wZXJhdGlvbkNhbGxiYWNrIENhbGxiYWNrIHdpdGggdGhlIG9wZXJhdGlvbiB0byByZWRvXG4gICAqL1xuICBwcm90ZWN0ZWQgY2F0Y2hJREJCcm9rZW48VD4ob3BlcmF0aW9uQ2FsbGJhY2s6ICgpID0+IE9ic2VydmFibGU8VD4pOiBPcGVyYXRvckZ1bmN0aW9uPFQsIFQ+IHtcblxuICAgIHJldHVybiBjYXRjaEVycm9yKChlcnJvcikgPT4ge1xuXG4gICAgICAvKiBDaGVjayBpZiBgaW5kZXhlZERCYCBpcyBicm9rZW4gYmFzZWQgb24gZXJyb3IgbWVzc2FnZSAodGhlIHNwZWNpZmljIGVycm9yIGNsYXNzIHNlZW1zIHRvIGJlIGxvc3QgaW4gdGhlIHByb2Nlc3MpICovXG4gICAgICBpZiAoKGVycm9yICE9PSB1bmRlZmluZWQpICYmIChlcnJvciAhPT0gbnVsbCkgJiYgKGVycm9yLm1lc3NhZ2UgPT09IElEQl9CUk9LRU5fRVJST1IpKSB7XG5cbiAgICAgICAgLyogV2hlbiBzdG9yYWdlIGlzIGZ1bGx5IGRpc2FibGVkIGluIGJyb3dzZXIgKHZpYSB0aGUgXCJCbG9jayBhbGwgY29va2llc1wiIG9wdGlvbiksXG4gICAgICAgICAqIGp1c3QgdHJ5aW5nIHRvIGNoZWNrIGBsb2NhbFN0b3JhZ2VgIHZhcmlhYmxlIGNhdXNlcyBhIHNlY3VyaXR5IGV4Y2VwdGlvbi5cbiAgICAgICAgICogUHJldmVudHMgaHR0cHM6Ly9naXRodWIuY29tL2N5cmlsbGV0dXppL2FuZ3VsYXItYXN5bmMtbG9jYWwtc3RvcmFnZS9pc3N1ZXMvMTE4XG4gICAgICAgICAqL1xuICAgICAgICB0cnkge1xuXG4gICAgICAgICAgaWYgKCdnZXRJdGVtJyBpbiBsb2NhbFN0b3JhZ2UpIHtcblxuICAgICAgICAgICAgLyogRmFsbGJhY2sgdG8gYGxvY2FsU3RvcmFnZWAgaWYgYXZhaWxhYmxlICovXG4gICAgICAgICAgICB0aGlzLmRhdGFiYXNlID0gbmV3IExvY2FsU3RvcmFnZURhdGFiYXNlKHRoaXMuTFNQcmVmaXgpO1xuXG4gICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgLyogRmFsbGJhY2sgdG8gbWVtb3J5IHN0b3JhZ2Ugb3RoZXJ3aXNlICovXG4gICAgICAgICAgICB0aGlzLmRhdGFiYXNlID0gbmV3IE1lbW9yeURhdGFiYXNlKCk7XG5cbiAgICAgICAgICB9XG5cbiAgICAgICAgfSBjYXRjaCB7XG5cbiAgICAgICAgICAvKiBGYWxsYmFjayB0byBtZW1vcnkgc3RvcmFnZSBvdGhlcndpc2UgKi9cbiAgICAgICAgICB0aGlzLmRhdGFiYXNlID0gbmV3IE1lbW9yeURhdGFiYXNlKCk7XG5cbiAgICAgICAgfVxuXG4gICAgICAgIC8qIFJlZG8gdGhlIG9wZXJhdGlvbiAqL1xuICAgICAgICByZXR1cm4gb3BlcmF0aW9uQ2FsbGJhY2soKTtcblxuICAgICAgfSBlbHNlIHtcblxuICAgICAgICAvKiBPdGhlcndpc2UsIHJldGhyb3cgdGhlIGVycm9yICovXG4gICAgICAgIHJldHVybiB0aHJvd0Vycm9yKGVycm9yKTtcblxuICAgICAgfVxuXG4gICAgfSk7XG5cbiAgfVxuXG59XG4iXX0=