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.
293 lines (286 loc) • 12.5 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Inject } from '@angular/core';
// Define InjectionTokens for dbName and storeName
const DB_NAME = new InjectionToken('DB_NAME');
const STORE_NAME = new InjectionToken('STORE_NAME');
const CACHED_TIME = new InjectionToken('CACHED_TIME'); // Add new token for cached time
class DataToStore {
id;
value;
savedTime;
cachedTime = null;
constructor(id, value) {
this.id = id;
this.value = value;
this.savedTime = new Date().toISOString(); // Generate the current timestamp
}
}
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]
}] }] });
/*
* Public API Surface of indexed-db
*/
/**
* Generated bundle index. Do not edit.
*/
export { CACHED_TIME, DB_NAME, IndexedDbHandler, STORE_NAME };
//# sourceMappingURL=angular-indexed-db-helper.mjs.map