UNPKG

angular-indexed-db-helper

Version:

A powerful and flexible Angular library for managing client-side storage using IndexedDB with support for cache expiration and CRUD operations.

267 lines 37.4 kB
import { Injectable, Inject } from '@angular/core'; // Injection tokens for dbName and storeName (these will be injected dynamically) import { DB_NAME, STORE_NAME, CACHED_TIME } from './indexed-db.tokens'; import { DataToStore } from './indexed-db.model'; // Import the tokens import * as i0 from "@angular/core"; export class IndexedDbHandler { dbName; storeName; cachedTime; dbInitialized; constructor(dbName, storeName, cachedTime) { this.dbName = dbName; this.storeName = storeName; this.cachedTime = cachedTime; this.dbInitialized = this.initDB(); } /** * Ensures that the database is fully set up before performing any operations. * @returns A promise that resolves when the IndexedDB is initialized. */ async whenInitialized() { return this.dbInitialized; } /** * Initializes the IndexedDB and creates the object store if it doesn't exist. * @returns A promise that resolves to the IndexedDB instance. */ initDB() { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName); request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains(this.storeName)) { db.createObjectStore(this.storeName, { keyPath: 'id' }); console.info(`Object store '${this.storeName}' created.`); } }; request.onsuccess = (event) => { const db = event.target.result; resolve(db); }; request.onerror = (event) => { console.error('Error opening IndexedDB:', event); reject(event.target.error); }; }); } /** * Saves data to the IndexedDB. * @param key The key used to store the data. * @param value The data to be saved. * @param cacheTime Optional parameter to specify the cache time for the data. * @returns A promise that resolves when the data is saved. */ async saveData(key, value, cacheTime = null) { await this.whenInitialized(); // Ensure DB is initialized const db = await this.whenInitialized(); const transaction = db.transaction(this.storeName, 'readwrite'); const store = transaction.objectStore(this.storeName); // Create a new instance of the DataToStore class const dataToStore = new DataToStore(key, value); dataToStore.cachedTime = cacheTime; // Set the cacheTime (null if not provided) return new Promise((resolve, reject) => { const request = store.put(dataToStore); // Save the object request.onsuccess = () => { resolve(); }; request.onerror = (event) => { reject(`Error saving data: ${event}`); }; }); } /** * Retrieves data from the IndexedDB by key. * @param key The key of the data to retrieve. * @returns A promise that resolves to the data or null if not found or expired. */ async getData(key) { await this.whenInitialized(); // Ensure DB is initialized const db = await this.whenInitialized(); const transaction = db.transaction(this.storeName, 'readonly'); const store = transaction.objectStore(this.storeName); return new Promise((resolve, reject) => { const request = store.get(key); // Retrieve data by key request.onsuccess = async () => { if (request.result === undefined) { resolve(null); // Return null if no data found } else { // Check expiration based on cachedTime or CACHED_TIME const cachedTime = request.result.cachedTime; let isExpired = false; if (cachedTime === null) { // If cachedTime is null, use the default CACHED_TIME for expiration check isExpired = await this.checkIfExpired(request.result.savedTime, this.cachedTime); } else { // If cachedTime exists, check against it isExpired = await this.checkIfExpired(request.result.savedTime, cachedTime); } if (isExpired) { resolve(null); // Return null if data is expired } else { resolve(request.result.value); // Return the value if not expired } } }; request.onerror = (event) => { reject(`Error retrieving data: ${event}`); }; }); } /** * Removes data from the IndexedDB by key. * @param key The key of the data to remove. * @returns A promise that resolves when the data is removed. */ async removeData(key) { await this.whenInitialized(); // Ensure DB is initialized const db = await this.whenInitialized(); const transaction = db.transaction(this.storeName, 'readwrite'); const store = transaction.objectStore(this.storeName); return new Promise((resolve, reject) => { const request = store.delete(key); // Delete data by key request.onsuccess = () => { resolve(); // Resolve when the data is removed }; request.onerror = (event) => { reject(`Error removing data: ${event}`); }; }); } /** * Updates data in the IndexedDB by key. * @param key The key of the data to update. * @param value The new data to update. * @returns A promise that resolves when the data is updated. */ async updateData(key, value) { await this.whenInitialized(); // Ensure DB is initialized const db = await this.whenInitialized(); const transaction = db.transaction(this.storeName, 'readwrite'); const store = transaction.objectStore(this.storeName); // Create a new instance of the DataToStore class const dataToStore = new DataToStore(key, value); return new Promise((resolve, reject) => { const request = store.put(dataToStore); // Save the object request.onsuccess = () => { resolve(); }; request.onerror = (event) => { reject(`Error saving data: ${event}`); }; }); } /** * Retrieves all data from the IndexedDB store. * Filters out expired data based on the cache time or default expiration time. * @returns A promise that resolves to an array of all valid data items. */ async getAll() { await this.whenInitialized(); // Ensure DB is initialized const db = await this.whenInitialized(); const transaction = db.transaction(this.storeName, 'readonly'); const store = transaction.objectStore(this.storeName); return new Promise((resolve, reject) => { const getRequest = store.getAll(); // Get all records from the store // Handle success getRequest.onsuccess = async () => { const result = getRequest.result; if (result === undefined || result.length === 0) { resolve([]); // Return an empty array if no data found } else { // Filter out expired items using cachedTime or CACHED_TIME const filteredResults = await Promise.all(result.map(async (item) => { let isExpired = false; const cachedTime = item.cachedTime; if (cachedTime === null) { // If cachedTime is null, compare against CACHED_TIME isExpired = await this.checkIfExpired(item.savedTime, this.cachedTime); } else { // Compare against cachedTime isExpired = await this.checkIfExpired(item.savedTime, cachedTime); } return isExpired ? null : item.value; // Exclude expired items })); // Filter out null values (expired data) resolve(filteredResults.filter(item => item !== null)); } }; // Handle error getRequest.onerror = (event) => { reject(`Error retrieving all data from IndexedDB: ${event}`); }; }); } /** * Clears all data in the IndexedDB store. * @returns A promise that resolves when the store is cleared. */ async clearStore() { await this.whenInitialized(); // Ensure DB is initialized const db = await this.whenInitialized(); const transaction = db.transaction(this.storeName, 'readwrite'); // Open a transaction in 'readwrite' mode const store = transaction.objectStore(this.storeName); return new Promise((resolve, reject) => { const request = store.clear(); // Clear all records in the store request.onsuccess = () => { resolve(); // Resolve if the store is cleared successfully }; request.onerror = (event) => { reject(`Error clearing the store: ${event}`); // Reject if there's an error }; }); } /** * Getter for the cached time value. * @returns The current cached time value. */ getCachedTime() { return this.cachedTime; } /** * Setter for the cached time value. * @param newCachedTime The new cached time value to set. */ setCachedTime(newCachedTime) { this.cachedTime = newCachedTime; // Update the cachedTime value } /** * Checks if the data has expired based on the saved time and comparison time. * @param savedTime The saved time of the data. * @param comparisonTimeInMs The comparison time in milliseconds. * @returns A promise that resolves to a boolean indicating if the data is expired. */ async checkIfExpired(savedTime, comparisonTimeInMs) { if (comparisonTimeInMs === 0) { return false; // If comparisonTime is 0, data never expires } const currentTime = new Date().getTime(); // Get current time in milliseconds const savedTimeStamp = new Date(savedTime).getTime(); // Convert savedTime to milliseconds const difference = currentTime - savedTimeStamp; // Calculate time difference // Check if the time difference exceeds the comparisonTime (in milliseconds) return difference > comparisonTimeInMs; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IndexedDbHandler, deps: [{ token: DB_NAME }, { token: STORE_NAME }, { token: CACHED_TIME }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IndexedDbHandler, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IndexedDbHandler, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [DB_NAME] }] }, { type: undefined, decorators: [{ type: Inject, args: [STORE_NAME] }] }, { type: undefined, decorators: [{ type: Inject, args: [CACHED_TIME] }] }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXhlZC1kYi5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvaW5kZXhlZC1kYi9zcmMvbGliL2luZGV4ZWQtZGIuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUMsVUFBVSxFQUFFLE1BQU0sRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUVqRCxpRkFBaUY7QUFDakYsT0FBTyxFQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFDLE1BQU0scUJBQXFCLENBQUM7QUFDckUsT0FBTyxFQUFDLFdBQVcsRUFBQyxNQUFNLG9CQUFvQixDQUFDLENBQUMsb0JBQW9COztBQUtwRSxNQUFNLE9BQU8sZ0JBQWdCO0lBQ25CLE1BQU0sQ0FBUztJQUNmLFNBQVMsQ0FBUztJQUNsQixVQUFVLENBQVM7SUFDbkIsYUFBYSxDQUF1QjtJQUU1QyxZQUNtQixNQUFjLEVBQ1gsU0FBaUIsRUFDaEIsVUFBa0I7UUFFdkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7UUFDM0IsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFDN0IsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDckMsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQyxlQUFlO1FBQzFCLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQztJQUM1QixDQUFDO0lBRUQ7OztPQUdHO0lBQ0ssTUFBTTtRQUNaLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDckMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFNUMsT0FBTyxDQUFDLGVBQWUsR0FBRyxDQUFDLEtBQTRCLEVBQUUsRUFB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QkFBMEI7UUFDeEUsSUFBSSxrQkFBa0IsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM3QixPQUFPLEtBQUssQ0FBQyxDQUFFLDZDQUE2QztRQUM5RCxDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFFLG1DQUFtQztRQUM5RSxNQUFNLGNBQWMsR0FBRyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFFLG9DQUFvQztRQUUzRixNQUFNLFVBQVUsR0FBRyxXQUFXLEdBQUcsY0FBYyxDQUFDLENBQUUsNEJBQTRCO1FBRTlFLDRFQUE0RTtRQUM1RSxPQUFPLFVBQVUsR0FBRyxrQkFBa0IsQ0FBQztJQUN6QyxDQUFDO3dHQS9SVSxnQkFBZ0Isa0JBT2pCLE9BQU8sYUFDUCxVQUFVLGFBQ1YsV0FBVzs0R0FUVixnQkFBZ0IsY0FGZixNQUFNOzs0RkFFUCxnQkFBZ0I7a0JBSDVCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25COzswQkFRSSxNQUFNOzJCQUFDLE9BQU87OzBCQUNkLE1BQU07MkJBQUMsVUFBVTs7MEJBQ2pCLE1BQU07MkJBQUMsV0FBVyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7SW5qZWN0YWJsZSwgSW5qZWN0fSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuXHJcbi8vIEluamVjdGlvbiB0b2tlbnMgZm9yIGRiTmFtZSBhbmQgc3RvcmVOYW1lICh0aGVzZSB3aWxsIGJlIGluamVjdGVkIGR5bmFtaWNhbGx5KVxyXG5pbXBvcnQge0RCX05BTUUsIFNUT1JFX05BTUUsIENBQ0hFRF9USU1FfSBmcm9tICcuL2luZGV4ZWQtZGIudG9rZW5zJztcclxuaW1wb3J0IHtEYXRhVG9TdG9yZX0gZnJvbSAnLi9pbmRleGVkLWRiLm1vZGVsJzsgLy8gSW1wb3J0IHRoZSB0b2tlbnNcclxuXHJcbkBJbmplY3RhYmxlKHtcclxuICBwcm92aWRlZEluOiAncm9vdCcsXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBJbmRleGVkRGJIYW5kbGVyIHtcclxuICBwcml2YXRlIGRiTmFtZTogc3RyaW5nO1xyXG4gIHByaXZhdGUgc3RvcmVOYW1lOiBzdHJpbmc7XHJcbiAgcHJpdmF0ZSBjYWNoZWRUaW1lOiBudW1iZXI7XHJcbiAgcHJpdmF0ZSBkYkluaXRpYWxpemVkOiBQcm9taXNlPElEQkRhdGFiYXNlPjtcclxuXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBASW5qZWN0KERCX05BTUUpIGRiTmFtZTogc3RyaW5nLFxyXG4gICAgQEluamVjdChTVE9SRV9OQU1FKSBzdG9yZU5hbWU6IHN0cmluZyxcclxuICAgIEBJbmplY3QoQ0FDSEVEX1RJTUUpIGNhY2hlZFRpbWU6IG51bWJlclxyXG4gICkge1xyXG4gICAgdGhpcy5kYk5hbWUgPSBkYk5hbWU7XHJcbiAgICB0aGlzLnN0b3JlTmFtZSA9IHN0b3JlTmFtZTtcclxuICAgIHRoaXMuY2FjaGVkVGltZSA9IGNhY2hlZFRpbWU7XHJcbiAgICB0aGlzLmRiSW5pdGlhbGl6ZWQgPSB0aGlzLmluaXREQigpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogRW5zdXJlcyB0aGF0IHRoZSBkYXRhYmFzZSBpcyBmdWxseSBzZXQgdXAgYmVmb3JlIHBlcmZvcm1pbmcgYW55IG9wZXJhdGlvbnMuXHJcbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgSW5kZXhlZERCIGlzIGluaXRpYWxpemVkLlxyXG4gICAqL1xyXG4gIHB1YmxpYyBhc3luYyB3aGVuSW5pdGlhbGl6ZWQoKTogUHJvbWlzZTxJREJEYXRhYmFzZT4ge1xyXG4gICAgcmV0dXJuIHRoaXMuZGJJbml0aWFsaXplZDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEluaXRpYWxpemVzIHRoZSBJbmRleGVkREIgYW5kIGNyZWF0ZXMgdGhlIG9iamVjdCBzdG9yZSBpZiBpdCBkb2Vzbid0IGV4aXN0LlxyXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBJbmRleGVkREIgaW5zdGFuY2UuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBpbml0REIoKTogUHJvbWlzZTxJREJEYXRhYmFzZT4ge1xyXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4ZWREQi5vcGVuKHRoaXMuZGJOYW1lKTtcclxuXHJcbiAgICAgIHJlcXVlc3Qub251cGdyYWRlbmVlZGVkID0gKGV2ZW50OiBJREJWZXJzaW9uQ2hhbmdlRXZlbnQpID0+IHtcclxuICAgICAgICBjb25zdCBkYiA9IChldmVudC50YXJnZXQgYXMgSURCT3BlbkRCUmVxdWVzdCkucmVzdWx0O1xyXG4gICAgICAgIGlmICghZGIub2JqZWN0U3RvcmVOYW1lcy5jb250YWlucyh0aGlzLnN0b3JlTmFtZSkpIHtcclxuICAgICAgICAgIGRiLmNyZWF0ZU9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lLCB7a2V5UGF0aDogJ2lkJ30pO1xyXG4gICAgICAgICAgY29uc29sZS5pbmZvKGBPYmplY3Qgc3RvcmUgJyR7dGhpcy5zdG9yZU5hbWV9JyBjcmVhdGVkLmApO1xyXG4gICAgICAgIH1cclxuICAgICAgfTtcclxuXHJcbiAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKGV2ZW50OiBFdmVudCkgPT4ge1xyXG4gICAgICAgIGNvbnN0IGRiID0gKGV2ZW50LnRhcmdldCBhcyBJREJPcGVuREJSZXF1ZXN0KS5yZXN1bHQ7XHJcbiAgICAgICAgcmVzb2x2ZShkYik7XHJcbiAgICAgIH07XHJcblxyXG4gICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoZXZlbnQ6IEV2ZW50KSA9PiB7XHJcbiAgICAgICAgY29uc29sZS5lcnJvcignRXJyb3Igb3BlbmluZyBJbmRleGVkREI6JywgZXZlbnQpO1xyXG4gICAgICAgIHJlamVjdCgoZXZlbnQudGFyZ2V0IGFzIElEQk9wZW5EQlJlcXVlc3QpLmVycm9yKTtcclxuICAgICAgfTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2F2ZXMgZGF0YSB0byB0aGUgSW5kZXhlZERCLlxyXG4gICAqIEBwYXJhbSBrZXkgVGhlIGtleSB1c2VkIHRvIHN0b3JlIHRoZSBkYXRhLlxyXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgZGF0YSB0byBiZSBzYXZlZC5cclxuICAgKiBAcGFyYW0gY2FjaGVUaW1lIE9wdGlvbmFsIHBhcmFtZXRlciB0byBzcGVjaWZ5IHRoZSBjYWNoZSB0aW1lIGZvciB0aGUgZGF0YS5cclxuICAgKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBkYXRhIGlzIHNhdmVkLlxyXG4gICAqL1xyXG4gIHB1YmxpYyBhc3luYyBzYXZlRGF0YShrZXk6IHN0cmluZywgdmFsdWU6IGFueSwgY2FjaGVUaW1lOiBudW1iZXIgfCBudWxsID0gbnVsbCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgYXdhaXQgdGhpcy53aGVuSW5pdGlhbGl6ZWQoKTsgLy8gRW5zdXJlIERCIGlzIGluaXRpYWxpemVkXHJcbiAgICBjb25zdCBkYiA9IGF3YWl0IHRoaXMud2hlbkluaXRpYWxpemVkKCk7XHJcbiAgICBjb25zdCB0cmFuc2FjdGlvbiA9IGRiLnRyYW5zYWN0aW9uKHRoaXMuc3RvcmVOYW1lLCAncmVhZHdyaXRlJyk7XHJcbiAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSBuZXcgaW5zdGFuY2Ugb2YgdGhlIERhdGFUb1N0b3JlIGNsYXNzXHJcbiAgICBjb25zdCBkYXRhVG9TdG9yZSA9IG5ldyBEYXRhVG9TdG9yZShrZXksIHZhbHVlKTtcclxuICAgIGRhdGFUb1N0b3JlLmNhY2hlZFRpbWUgPSBjYWNoZVRpbWU7ICAvLyBTZXQgdGhlIGNhY2hlVGltZSAobnVsbCBpZiBub3QgcHJvdmlkZWQpXHJcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICBjb25zdCByZXF1ZXN0ID0gc3RvcmUucHV0KGRhdGFUb1N0b3JlKTsgLy8gU2F2ZSB0aGUgb2JqZWN0XHJcblxyXG4gICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcclxuICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgIH07XHJcblxyXG4gICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoZXZlbnQpID0+IHtcclxuICAgICAgICByZWplY3QoYEVycm9yIHNhdmluZyBkYXRhOiAke2V2ZW50fWApO1xyXG4gICAgICB9O1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXRyaWV2ZXMgZGF0YSBmcm9tIHRoZSBJbmRleGVkREIgYnkga2V5LlxyXG4gICAqIEBwYXJhbSBrZXkgVGhlIGtleSBvZiB0aGUgZGF0YSB0byByZXRyaWV2ZS5cclxuICAgKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZGF0YSBvciBudWxsIGlmIG5vdCBmb3VuZCBvciBleHBpcmVkLlxyXG4gICAqL1xyXG4gIHB1YmxpYyBhc3luYyBnZXREYXRhKGtleTogc3RyaW5nKTogUHJvbWlzZTxhbnk+IHtcclxuICAgIGF3YWl0IHRoaXMud2hlbkluaXRpYWxpemVkKCk7IC8vIEVuc3VyZSBEQiBpcyBpbml0aWFsaXplZFxyXG4gICAgY29uc3QgZGIgPSBhd2FpdCB0aGlzLndoZW5Jbml0aWFsaXplZCgpO1xyXG4gICAgY29uc3QgdHJhbnNhY3Rpb24gPSBkYi50cmFuc2FjdGlvbih0aGlzLnN0b3JlTmFtZSwgJ3JlYWRvbmx5Jyk7XHJcbiAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcclxuXHJcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICBjb25zdCByZXF1ZXN0ID0gc3RvcmUuZ2V0KGtleSk7IC8vIFJldHJpZXZlIGRhdGEgYnkga2V5XHJcblxyXG4gICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9IGFzeW5jICgpID0+IHtcclxuICAgICAgICBpZiAocmVxdWVzdC5yZXN1bHQgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgcmVzb2x2ZShudWxsKTsgLy8gUmV0dXJuIG51bGwgaWYgbm8gZGF0YSBmb3VuZFxyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAvLyBDaGVjayBleHBpcmF0aW9uIGJhc2VkIG9uIGNhY2hlZFRpbWUgb3IgQ0FDSEVEX1RJTUVcclxuICAgICAgICAgIGNvbnN0IGNhY2hlZFRpbWUgPSByZXF1ZXN0LnJlc3VsdC5jYWNoZWRUaW1lO1xyXG4gICAgICAgICAgbGV0IGlzRXhwaXJlZCA9IGZhbHNlO1xyXG5cclxuICAgICAgICAgIGlmIChjYWNoZWRUaW1lID09PSBudWxsKSB7XHJcbiAgICAgICAgICAgIC8vIElmIGNhY2hlZFRpbWUgaXMgbnVsbCwgdXNlIHRoZSBkZWZhdWx0IENBQ0hFRF9USU1FIGZvciBleHBpcmF0aW9uIGNoZWNrXHJcbiAgICAgICAgICAgIGlzRXhwaXJlZCA9IGF3YWl0IHRoaXMuY2hlY2tJZkV4cGlyZWQocmVxdWVzdC5yZXN1bHQuc2F2ZWRUaW1lLCB0aGlzLmNhY2hlZFRpbWUpO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gSWYgY2FjaGVkVGltZSBleGlzdHMsIGNoZWNrIGFnYWluc3QgaXRcclxuICAgICAgICAgICAgaXNFeHBpcmVkID0gYXdhaXQgdGhpcy5jaGVja0lmRXhwaXJlZChyZXF1ZXN0LnJlc3VsdC5zYXZlZFRpbWUsIGNhY2hlZFRpbWUpO1xyXG4gICAgICAgICAgfVxyXG5cclxuICAgICAgICAgIGlmIChpc0V4cGlyZWQpIHtcclxuICAgICAgICAgICAgcmVzb2x2ZShudWxsKTsgIC8vIFJldHVybiBudWxsIGlmIGRhdGEgaXMgZXhwaXJlZFxyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgcmVzb2x2ZShyZXF1ZXN0LnJlc3VsdC52YWx1ZSk7IC8vIFJldHVybiB0aGUgdmFsdWUgaWYgbm90IGV4cGlyZWRcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH07XHJcblxyXG4gICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoZXZlbnQpID0+IHtcclxuICAgICAgICByZWplY3QoYEVycm9yIHJldHJpZXZpbmcgZGF0YTogJHtldmVudH1gKTtcclxuICAgICAgfTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIFJlbW92ZXMgZGF0YSBmcm9tIHRoZSBJbmRleGVkREIgYnkga2V5LlxyXG4gICAqIEBwYXJhbSBrZXkgVGhlIGtleSBvZiB0aGUgZGF0YSB0byByZW1vdmUuXHJcbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgZGF0YSBpcyByZW1vdmVkLlxyXG4gICAqL1xyXG4gIHB1YmxpYyBhc3luYyByZW1vdmVEYXRhKGtleTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBhd2FpdCB0aGlzLndoZW5Jbml0aWFsaXplZCgpOyAvLyBFbnN1cmUgREIgaXMgaW5pdGlhbGl6ZWRcclxuICAgIGNvbnN0IGRiID0gYXdhaXQgdGhpcy53aGVuSW5pdGlhbGl6ZWQoKTtcclxuICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gZGIudHJhbnNhY3Rpb24odGhpcy5zdG9yZU5hbWUsICdyZWFkd3JpdGUnKTtcclxuICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xyXG5cclxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgIGNvbnN0IHJlcXVlc3QgPSBzdG9yZS5kZWxldGUoa2V5KTsgLy8gRGVsZXRlIGRhdGEgYnkga2V5XHJcblxyXG4gICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcclxuICAgICAgICByZXNvbHZlKCk7IC8vIFJlc29sdmUgd2hlbiB0aGUgZGF0YSBpcyByZW1vdmVkXHJcbiAgICAgIH07XHJcblxyXG4gICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoZXZlbnQpID0+IHtcclxuICAgICAgICByZWplY3QoYEVycm9yIHJlbW92aW5nIGRhdGE6ICR7ZXZlbnR9YCk7XHJcbiAgICAgIH07XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG5cclxuICAvKipcclxuICAgKiBVcGRhdGVzIGRhdGEgaW4gdGhlIEluZGV4ZWREQiBieSBrZXkuXHJcbiAgICogQHBhcmFtIGtleSBUaGUga2V5IG9mIHRoZSBkYXRhIHRvIHVwZGF0ZS5cclxuICAgKiBAcGFyYW0gdmFsdWUgVGhlIG5ldyBkYXRhIHRvIHVwZGF0ZS5cclxuICAgKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBkYXRhIGlzIHVwZGF0ZWQuXHJcbiAgICovXHJcbiAgcHVibGljIGFzeW5jIHVwZGF0ZURhdGEoa2V5OiBzdHJpbmcsIHZhbHVlOiBhbnkpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIGF3YWl0IHRoaXMud2hlbkluaXRpYWxpemVkKCk7IC8vIEVuc3VyZSBEQiBpcyBpbml0aWFsaXplZFxyXG4gICAgY29uc3QgZGIgPSBhd2FpdCB0aGlzLndoZW5Jbml0aWFsaXplZCgpO1xyXG4gICAgY29uc3QgdHJhbnNhY3Rpb24gPSBkYi50cmFuc2FjdGlvbih0aGlzLnN0b3JlTmFtZSwgJ3JlYWR3cml0ZScpO1xyXG4gICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgbmV3IGluc3RhbmNlIG9mIHRoZSBEYXRhVG9TdG9yZSBjbGFzc1xyXG4gICAgY29uc3QgZGF0YVRvU3RvcmUgPSBuZXcgRGF0YVRvU3RvcmUoa2V5LCB2YWx1ZSk7XHJcblxyXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgY29uc3QgcmVxdWVzdCA9IHN0b3JlLnB1dChkYXRhVG9TdG9yZSk7IC8vIFNhdmUgdGhlIG9iamVjdFxyXG5cclxuICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XHJcbiAgICAgICAgcmVzb2x2ZSgpO1xyXG4gICAgICB9O1xyXG5cclxuICAgICAgcmVxdWVzdC5vbmVycm9yID0gKGV2ZW50KSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGBFcnJvciBzYXZpbmcgZGF0YTogJHtldmVudH1gKTtcclxuICAgICAgfTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmV0cmlldmVzIGFsbCBkYXRhIGZyb20gdGhlIEluZGV4ZWREQiBzdG9yZS5cclxuICAgKiBGaWx0ZXJzIG91dCBleHBpcmVkIGRhdGEgYmFzZWQgb24gdGhlIGNhY2hlIHRpbWUgb3IgZGVmYXVsdCBleHBpcmF0aW9uIHRpbWUuXHJcbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXkgb2YgYWxsIHZhbGlkIGRhdGEgaXRlbXMuXHJcbiAgICovXHJcbiAgcHVibGljIGFzeW5jIGdldEFsbCgpOiBQcm9taXNlPGFueVtdPiB7XHJcbiAgICBhd2FpdCB0aGlzLndoZW5Jbml0aWFsaXplZCgpOyAvLyBFbnN1cmUgREIgaXMgaW5pdGlhbGl6ZWRcclxuXHJcbiAgICBjb25zdCBkYiA9IGF3YWl0IHRoaXMud2hlbkluaXRpYWxpemVkKCk7XHJcbiAgICBjb25zdCB0cmFuc2FjdGlvbiA9IGRiLnRyYW5zYWN0aW9uKHRoaXMuc3RvcmVOYW1lLCAncmVhZG9ubHknKTtcclxuICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xyXG5cclxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgIGNvbnN0IGdldFJlcXVlc3QgPSBzdG9yZS5nZXRBbGwoKTsgLy8gR2V0IGFsbCByZWNvcmRzIGZyb20gdGhlIHN0b3JlXHJcblxyXG4gICAgICAvLyBIYW5kbGUgc3VjY2Vzc1xyXG4gICAgICBnZXRSZXF1ZXN0Lm9uc3VjY2VzcyA9IGFzeW5jICgpID0+IHtcclxuICAgICAgICBjb25zdCByZXN1bHQgPSBnZXRSZXF1ZXN0LnJlc3VsdDtcclxuICAgICAgICBpZiAocmVzdWx0ID09PSB1bmRlZmluZWQgfHwgcmVzdWx0Lmxlbmd0aCA9PT0gMCkge1xyXG4gICAgICAgICAgcmVzb2x2ZShbXSk7IC8vIFJldHVybiBhbiBlbXB0eSBhcnJheSBpZiBubyBkYXRhIGZvdW5kXHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIEZpbHRlciBvdXQgZXhwaXJlZCBpdGVtcyB1c2luZyBjYWNoZWRUaW1lIG9yIENBQ0hFRF9USU1FXHJcbiAgICAgICAgICBjb25zdCBmaWx0ZXJlZFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChyZXN1bHQubWFwKGFzeW5jIChpdGVtKSA9PiB7XHJcbiAgICAgICAgICAgIGxldCBpc0V4cGlyZWQgPSBmYWxzZTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IGNhY2hlZFRpbWUgPSBpdGVtLmNhY2hlZFRpbWU7XHJcbiAgICAgICAgICAgIGlmIChjYWNoZWRUaW1lID09PSBudWxsKSB7XHJcbiAgICAgICAgICAgICAgLy8gSWYgY2FjaGVkVGltZSBpcyBudWxsLCBjb21wYXJlIGFnYWluc3QgQ0FDSEVEX1RJTUVcclxuICAgICAgICAgICAgICBpc0V4cGlyZWQgPSBhd2FpdCB0aGlzLmNoZWNrSWZFeHBpcmVkKGl0ZW0uc2F2ZWRUaW1lLCB0aGlzLmNhY2hlZFRpbWUpO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgIC8vIENvbXBhcmUgYWdhaW5zdCBjYWNoZWRUaW1lXHJcbiAgICAgICAgICAgICAgaXNFeHBpcmVkID0gYXdhaXQgdGhpcy5jaGVja0lmRXhwaXJlZChpdGVtLnNhdmVkVGltZSwgY2FjaGVkVGltZSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHJldHVybiBpc0V4cGlyZWQgPyBudWxsIDogaXRlbS52YWx1ZTsgLy8gRXhjbHVkZSBleHBpcmVkIGl0ZW1zXHJcbiAgICAgICAgICB9KSk7XHJcblxyXG4gICAgICAgICAgLy8gRmlsdGVyIG91dCBudWxsIHZhbHVlcyAoZXhwaXJlZCBkYXRhKVxyXG4gICAgICAgICAgcmVzb2x2ZShmaWx0ZXJlZFJlc3VsdHMuZmlsdGVyKGl0ZW0gPT4gaXRlbSAhPT0gbnVsbCkpO1xyXG4gICAgICAgIH1cclxuICAgICAgfTtcclxuXHJcbiAgICAgIC8vIEhhbmRsZSBlcnJvclxyXG4gICAgICBnZXRSZXF1ZXN0Lm9uZXJyb3IgPSAoZXZlbnQpID0+IHtcclxuICAgICAgICByZWplY3QoYEVycm9yIHJldHJpZXZpbmcgYWxsIGRhdGEgZnJvbSBJbmRleGVkREI6ICR7ZXZlbnR9YCk7XHJcbiAgICAgIH07XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENsZWFycyBhbGwgZGF0YSBpbiB0aGUgSW5kZXhlZERCIHN0b3JlLlxyXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHN0b3JlIGlzIGNsZWFyZWQuXHJcbiAgICovXHJcbiAgcHVibGljIGFzeW5jIGNsZWFyU3RvcmUoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBhd2FpdCB0aGlzLndoZW5Jbml0aWFsaXplZCgpOyAvLyBFbnN1cmUgREIgaXMgaW5pdGlhbGl6ZWRcclxuICAgIGNvbnN0IGRiID0gYXdhaXQgdGhpcy53aGVuSW5pdGlhbGl6ZWQoKTtcclxuICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gZGIudHJhbnNhY3Rpb24odGhpcy5zdG9yZU5hbWUsICdyZWFkd3JpdGUnKTsgLy8gT3BlbiBhIHRyYW5zYWN0aW9uIGluICdyZWFkd3JpdGUnIG1vZGVcclxuICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xyXG5cclxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgIGNvbnN0IHJlcXVlc3QgPSBzdG9yZS5jbGVhcigpOyAvLyBDbGVhciBhbGwgcmVjb3JkcyBpbiB0aGUgc3RvcmVcclxuXHJcbiAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xyXG4gICAgICAgIHJlc29sdmUoKTsgLy8gUmVzb2x2ZSBpZiB0aGUgc3RvcmUgaXMgY2xlYXJlZCBzdWNjZXNzZnVsbHlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHJlcXVlc3Qub25lcnJvciA9IChldmVudCkgPT4ge1xyXG4gICAgICAgIHJlamVjdChgRXJyb3IgY2xlYXJpbmcgdGhlIHN0b3JlOiAke2V2ZW50fWApOyAvLyBSZWplY3QgaWYgdGhlcmUncyBhbiBlcnJvclxyXG4gICAgICB9O1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogR2V0dGVyIGZvciB0aGUgY2FjaGVkIHRpbWUgdmFsdWUuXHJcbiAgICogQHJldHVybnMgVGhlIGN1cnJlbnQgY2FjaGVkIHRpbWUgdmFsdWUuXHJcbiAgICovXHJcbiAgcHVibGljIGdldENhY2hlZFRpbWUoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLmNhY2hlZFRpbWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXR0ZXIgZm9yIHRoZSBjYWNoZWQgdGltZSB2YWx1ZS5cclxuICAgKiBAcGFyYW0gbmV3Q2FjaGVkVGltZSBUaGUgbmV3IGNhY2hlZCB0aW1lIHZhbHVlIHRvIHNldC5cclxuICAgKi9cclxuICBwdWJsaWMgc2V0Q2FjaGVkVGltZShuZXdDYWNoZWRUaW1lOiBudW1iZXIpOiB2b2lkIHtcclxuICAgIHRoaXMuY2FjaGVkVGltZSA9IG5ld0NhY2hlZFRpbWU7IC8vIFVwZGF0ZSB0aGUgY2FjaGVkVGltZSB2YWx1ZVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogQ2hlY2tzIGlmIHRoZSBkYXRhIGhhcyBleHBpcmVkIGJhc2VkIG9uIHRoZSBzYXZlZCB0aW1lIGFuZCBjb21wYXJpc29uIHRpbWUuXHJcbiAgICogQHBhcmFtIHNhdmVkVGltZSBUaGUgc2F2ZWQgdGltZSBvZiB0aGUgZGF0YS5cclxuICAgKiBAcGFyYW0gY29tcGFyaXNvblRpbWVJbk1zIFRoZSBjb21wYXJpc29uIHRpbWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgYm9vbGVhbiBpbmRpY2F0aW5nIGlmIHRoZSBkYXRhIGlzIGV4cGlyZWQuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBhc3luYyBjaGVja0lmRXhwaXJlZChzYXZlZFRpbWU6IHN0cmluZywgY29tcGFyaXNvblRpbWVJbk1zOiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcclxuICAgIGlmIChjb21wYXJpc29uVGltZUluTXMgPT09IDApIHtcclxuICAgICAgcmV0dXJuIGZhbHNlOyAgLy8gSWYgY29tcGFyaXNvblRpbWUgaXMgMCwgZGF0YSBuZXZlciBleHBpcmVzXHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgY3VycmVudFRpbWUgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTsgIC8vIEdldCBjdXJyZW50IHRpbWUgaW4gbWlsbGlzZWNvbmRzXHJcbiAgICBjb25zdCBzYXZlZFRpbWVTdGFtcCA9IG5ldyBEYXRlKHNhdmVkVGltZSkuZ2V0VGltZSgpOyAgLy8gQ29udmVydCBzYXZlZFRpbWUgdG8gbWlsbGlzZWNvbmRzXHJcblxyXG4gICAgY29uc3QgZGlmZmVyZW5jZSA9IGN1cnJlbnRUaW1lIC0gc2F2ZWRUaW1lU3RhbXA7ICAvLyBDYWxjdWxhdGUgdGltZSBkaWZmZXJlbmNlXHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlIHRpbWUgZGlmZmVyZW5jZSBleGNlZWRzIHRoZSBjb21wYXJpc29uVGltZSAoaW4gbWlsbGlzZWNvbmRzKVxyXG4gICAgcmV0dXJuIGRpZmZlcmVuY2UgPiBjb21wYXJpc29uVGltZUluTXM7XHJcbiAgfVxyXG5cclxufVxyXG4iXX0=