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
JavaScript
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=