From f74fcabec7bab66c44bb4e0f8c47794575807f7a Mon Sep 17 00:00:00 2001 From: omaroughriss Date: Thu, 27 Nov 2025 16:43:49 +0100 Subject: [PATCH] Delete store definition --- src/services/database.service.ts | 896 +++++++++++++++---------------- 1 file changed, 429 insertions(+), 467 deletions(-) diff --git a/src/services/database.service.ts b/src/services/database.service.ts index b689ee8..3344fde 100755 --- a/src/services/database.service.ts +++ b/src/services/database.service.ts @@ -1,467 +1,429 @@ -import Services from './service'; - -export class Database { - private static instance: Database; - private db: IDBDatabase | null = null; - private dbName: string = '4nk'; - private dbVersion: number = 1; - private serviceWorkerRegistration: ServiceWorkerRegistration | null = null; - private messageChannel: MessageChannel | null = null; - private messageChannelForGet: MessageChannel | null = null; - private serviceWorkerCheckIntervalId: number | null = null; - private storeDefinitions = { - AnkLabels: { - name: 'labels', - options: { keyPath: 'emoji' }, - indices: [], - }, - AnkWallet: { - name: 'wallet', - options: { keyPath: 'pre_id' }, - indices: [], - }, - AnkProcess: { - name: 'processes', - options: {}, - indices: [], - }, - AnkSharedSecrets: { - name: 'shared_secrets', - options: {}, - indices: [], - }, - AnkUnconfirmedSecrets: { - name: 'unconfirmed_secrets', - options: { autoIncrement: true }, - indices: [], - }, - AnkPendingDiffs: { - name: 'diffs', - options: { keyPath: 'value_commitment' }, - indices: [ - { name: 'byStateId', keyPath: 'state_id', options: { unique: false } }, - { name: 'byNeedValidation', keyPath: 'need_validation', options: { unique: false } }, - { name: 'byStatus', keyPath: 'validation_status', options: { unique: false } }, - ], - }, - AnkData: { - name: 'data', - options: {}, - indices: [], - }, - }; - - // Private constructor to prevent direct instantiation from outside - private constructor() {} - - // Method to access the singleton instance of Database - public static async getInstance(): Promise { - if (!Database.instance) { - Database.instance = new Database(); - await Database.instance.init(); - } - return Database.instance; - } - - // Initialize the database - private async init(): Promise { - return new Promise((resolve, reject) => { - const request = indexedDB.open(this.dbName, this.dbVersion); - - request.onupgradeneeded = () => { - const db = request.result; - - Object.values(this.storeDefinitions).forEach(({ name, options, indices }) => { - if (!db.objectStoreNames.contains(name)) { - let store = db.createObjectStore(name, options as IDBObjectStoreParameters); - - indices.forEach(({ name, keyPath, options }) => { - store.createIndex(name, keyPath, options); - }); - } - }); - }; - - request.onsuccess = async () => { - this.db = request.result; - resolve(); - }; - - request.onerror = () => { - console.error('Database error:', request.error); - reject(request.error); - }; - }); - } - - public async getDb(): Promise { - if (!this.db) { - await this.init(); - } - return this.db!; - } - - public getStoreList(): { [key: string]: string } { - const objectList: { [key: string]: string } = {}; - Object.keys(this.storeDefinitions).forEach((key) => { - objectList[key] = this.storeDefinitions[key as keyof typeof this.storeDefinitions].name; - }); - return objectList; - } - - public async registerServiceWorker(path: string) { - if (!('serviceWorker' in navigator)) return; - console.log('[Database] Initialisation du Service Worker sur :', path); - - try { - // 1. NETTOYAGE DES ANCIENS WORKERS (ZOMBIES) - const registrations = await navigator.serviceWorker.getRegistrations(); - - for (const registration of registrations) { - const scriptURL = registration.active?.scriptURL || registration.installing?.scriptURL || registration.waiting?.scriptURL; - const scope = registration.scope; - - // On détecte spécifiquement l'ancien dossier qui pose problème - // L'erreur mentionne : scope ('.../src/service-workers/') - if (scope.includes('/src/service-workers/') || (scriptURL && scriptURL.includes('/src/service-workers/'))) { - console.warn(`[Database] 🚨 ANCIEN Service Worker détecté (${scope}). Suppression immédiate...`); - await registration.unregister(); - // On continue la boucle, ne pas retourner ici, il faut installer le nouveau après - } - } - - // 2. INSTALLATION DU NOUVEAU WORKER (PROPRE) - // On vérifie s'il est déjà installé à la BONNE adresse - const existingValidWorker = registrations.find((r) => { - const url = r.active?.scriptURL || r.installing?.scriptURL || r.waiting?.scriptURL; - // On compare la fin de l'URL pour éviter les soucis http/https/localhost - return url && url.endsWith(path.replace(/^\//, '')); - }); - - if (!existingValidWorker) { - console.log('[Database] Enregistrement du nouveau Service Worker...'); - this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, { type: 'module', scope: '/' }); - } else { - console.log('[Database] Service Worker déjà actif et valide.'); - this.serviceWorkerRegistration = existingValidWorker; - await this.serviceWorkerRegistration.update(); - } - // Set up listeners - navigator.serviceWorker.addEventListener('message', async (event) => { - // console.log('Received message from service worker:', event.data); - await this.handleServiceWorkerMessage(event.data); - }); - - // Periodic check - if (this.serviceWorkerCheckIntervalId) clearInterval(this.serviceWorkerCheckIntervalId); - this.serviceWorkerCheckIntervalId = window.setInterval(async () => { - const activeWorker = this.serviceWorkerRegistration?.active || (await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration!)); - const service = await Services.getInstance(); - const payload = await service.getMyProcesses(); - if (payload && payload.length != 0) { - activeWorker?.postMessage({ type: 'SCAN', payload }); - } - }, 5000); - } catch (error) { - console.error('[Database] 💥 Erreur critique Service Worker:', error); - } - } - - // Helper function to wait for service worker activation - private async waitForServiceWorkerActivation(registration: ServiceWorkerRegistration): Promise { - return new Promise((resolve) => { - if (registration.active) { - resolve(registration.active); - } else { - const listener = () => { - if (registration.active) { - navigator.serviceWorker.removeEventListener('controllerchange', listener); - resolve(registration.active); - } - }; - navigator.serviceWorker.addEventListener('controllerchange', listener); - } - }); - } - - private async checkForUpdates() { - if (this.serviceWorkerRegistration) { - // Check for updates to the service worker - try { - await this.serviceWorkerRegistration.update(); - - // If there's a new worker waiting, activate it immediately - if (this.serviceWorkerRegistration.waiting) { - this.serviceWorkerRegistration.waiting.postMessage({ type: 'SKIP_WAITING' }); - } - } catch (error) { - console.error('Error checking for service worker updates:', error); - } - } - } - - private async handleServiceWorkerMessage(message: any) { - switch (message.type) { - case 'TO_DOWNLOAD': - await this.handleDownloadList(message.data); - break; - default: - console.warn('Unknown message type received from service worker:', message); - } - } - - private async handleDownloadList(downloadList: string[]): Promise { - // Download the missing data - let requestedStateId: string[] = []; - const service = await Services.getInstance(); - for (const hash of downloadList) { - const diff = await service.getDiffByValue(hash); - if (!diff) { - // This should never happen - console.warn(`Missing a diff for hash ${hash}`); - continue; - } - const processId = diff.process_id; - const stateId = diff.state_id; - const roles = diff.roles; - try { - const valueBytes = await service.fetchValueFromStorage(hash); - if (valueBytes) { - // Save data to db - const blob = new Blob([valueBytes], { type: 'application/octet-stream' }); - await service.saveBlobToDb(hash, blob); - document.dispatchEvent( - new CustomEvent('newDataReceived', { - detail: { - processId, - stateId, - hash, - }, - }), - ); - } else { - // We first request the data from managers - console.log('Request data from managers of the process'); - // get the diff from db - if (!requestedStateId.includes(stateId)) { - await service.requestDataFromPeers(processId, [stateId], [roles]); - requestedStateId.push(stateId); - } - } - } catch (e) { - console.error(e); - } - } - } - - private handleAddObjectResponse = async (event: MessageEvent) => { - const data = event.data; - console.log('Received response from service worker (ADD_OBJECT):', data); - const service = await Services.getInstance(); - if (data.type === 'NOTIFICATIONS') { - service.setNotifications(data.data); - } else if (data.type === 'TO_DOWNLOAD') { - console.log(`Received missing data ${data}`); - // Download the missing data - let requestedStateId: string[] = []; - for (const hash of data.data) { - try { - const valueBytes = await service.fetchValueFromStorage(hash); - if (valueBytes) { - // Save data to db - const blob = new Blob([valueBytes], { type: 'application/octet-stream' }); - await service.saveBlobToDb(hash, blob); - } else { - // We first request the data from managers - console.log('Request data from managers of the process'); - // get the diff from db - const diff = await service.getDiffByValue(hash); - if (diff === null) { - continue; - } - const processId = diff!.process_id; - const stateId = diff!.state_id; - const roles = diff!.roles; - if (!requestedStateId.includes(stateId)) { - await service.requestDataFromPeers(processId, [stateId], [roles]); - requestedStateId.push(stateId); - } - } - } catch (e) { - console.error(e); - } - } - } - }; - - private handleGetObjectResponse = (event: MessageEvent) => { - console.log('Received response from service worker (GET_OBJECT):', event.data); - }; - - public addObject(payload: { storeName: string; object: any; key: any }): Promise { - return new Promise(async (resolve, reject) => { - // Check if the service worker is active - if (!this.serviceWorkerRegistration) { - // console.warn('Service worker registration is not ready. Waiting...'); - this.serviceWorkerRegistration = await navigator.serviceWorker.ready; - } - - const activeWorker = await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration); - - // Create a message channel for communication - const messageChannel = new MessageChannel(); - - // Handle the response from the service worker - messageChannel.port1.onmessage = (event) => { - if (event.data.status === 'success') { - resolve(); - } else { - const error = event.data.message; - reject(new Error(error || 'Unknown error occurred while adding object')); - } - }; - - // Send the add object request to the service worker - try { - activeWorker?.postMessage( - { - type: 'ADD_OBJECT', - payload, - }, - [messageChannel.port2], - ); - } catch (error) { - reject(new Error(`Failed to send message to service worker: ${error}`)); - } - }); - } - - public batchWriting(payload: { storeName: string; objects: { key: any; object: any }[] }): Promise { - return new Promise(async (resolve, reject) => { - if (!this.serviceWorkerRegistration) { - this.serviceWorkerRegistration = await navigator.serviceWorker.ready; - } - - const activeWorker = await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration); - const messageChannel = new MessageChannel(); - - messageChannel.port1.onmessage = (event) => { - if (event.data.status === 'success') { - resolve(); - } else { - const error = event.data.message; - reject(new Error(error || 'Unknown error occurred while adding objects')); - } - }; - - try { - activeWorker?.postMessage( - { - type: 'BATCH_WRITING', - payload, - }, - [messageChannel.port2], - ); - } catch (error) { - reject(new Error(`Failed to send message to service worker: ${error}`)); - } - }); - } - - public async getObject(storeName: string, key: string): Promise { - const db = await this.getDb(); - const tx = db.transaction(storeName, 'readonly'); - const store = tx.objectStore(storeName); - const result = await new Promise((resolve, reject) => { - const getRequest = store.get(key); - getRequest.onsuccess = () => resolve(getRequest.result); - getRequest.onerror = () => reject(getRequest.error); - }); - return result ?? null; // Convert undefined to null - } - - public async dumpStore(storeName: string): Promise> { - const db = await this.getDb(); - const tx = db.transaction(storeName, 'readonly'); - const store = tx.objectStore(storeName); - - try { - return new Promise((resolve, reject) => { - const result: Record = {}; - const cursor = store.openCursor(); - - cursor.onsuccess = (event) => { - const request = event.target as IDBRequest; - const cursor = request.result; - if (cursor) { - result[cursor.key as string] = cursor.value; - cursor.continue(); - } else { - resolve(result); - } - }; - - cursor.onerror = () => { - reject(cursor.error); - }; - }); - } catch (error) { - console.error('Error fetching data from IndexedDB:', error); - throw error; - } - } - - public async deleteObject(storeName: string, key: string): Promise { - const db = await this.getDb(); - const tx = db.transaction(storeName, 'readwrite'); - const store = tx.objectStore(storeName); - try { - await new Promise((resolve, reject) => { - const getRequest = store.delete(key); - getRequest.onsuccess = () => resolve(getRequest.result); - getRequest.onerror = () => reject(getRequest.error); - }); - } catch (e) { - throw e; - } - } - - public async clearStore(storeName: string): Promise { - const db = await this.getDb(); - const tx = db.transaction(storeName, 'readwrite'); - const store = tx.objectStore(storeName); - try { - await new Promise((resolve, reject) => { - const clearRequest = store.clear(); - clearRequest.onsuccess = () => resolve(clearRequest.result); - clearRequest.onerror = () => reject(clearRequest.error); - }); - } catch (e) { - throw e; - } - } - - // Request a store by index - public async requestStoreByIndex(storeName: string, indexName: string, request: string): Promise { - const db = await this.getDb(); - const tx = db.transaction(storeName, 'readonly'); - const store = tx.objectStore(storeName); - const index = store.index(indexName); - - try { - return new Promise((resolve, reject) => { - const getAllRequest = index.getAll(request); - getAllRequest.onsuccess = () => { - const allItems = getAllRequest.result; - const filtered = allItems.filter((item) => item.state_id === request); - resolve(filtered); - }; - getAllRequest.onerror = () => reject(getAllRequest.error); - }); - } catch (e) { - throw e; - } - } -} - -export default Database; +import Services from './service'; + +export class Database { + private static instance: Database; + private serviceWorkerRegistration: ServiceWorkerRegistration | null = null; + private serviceWorkerCheckIntervalId: number | null = null; + private indexedDBWorker: Worker | null = null; + private messageIdCounter: number = 0; + private pendingMessages: Map void; reject: (error: any) => void }> = new Map(); + + // ============================================ + // INITIALIZATION & SINGLETON + // ============================================ + + private constructor() { + this.initIndexedDBWorker(); + this.initServiceWorker(); + } + + public static async getInstance(): Promise { + if (!Database.instance) { + Database.instance = new Database(); + await Database.instance.init(); + } + return Database.instance; + } + + // Initialize the database + private async init(): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.dbName, this.dbVersion); + + request.onupgradeneeded = () => { + const db = request.result; + + Object.values(this.storeDefinitions).forEach(({ name, options, indices }) => { + if (!db.objectStoreNames.contains(name)) { + let store = db.createObjectStore(name, options as IDBObjectStoreParameters); + + indices.forEach(({ name, keyPath, options }) => { + store.createIndex(name, keyPath, options); + }); + } + }); + }; + + request.onsuccess = async () => { + this.db = request.result; + resolve(); + }; + + request.onerror = () => { + console.error('Database error:', request.error); + reject(request.error); + }; + }); + } + + public async getDb(): Promise { + if (!this.db) { + await this.init(); + } + return this.db!; + } + + public getStoreList(): { [key: string]: string } { + const objectList: { [key: string]: string } = {}; + Object.keys(this.storeDefinitions).forEach((key) => { + objectList[key] = this.storeDefinitions[key as keyof typeof this.storeDefinitions].name; + }); + return objectList; + } + + public async registerServiceWorker(path: string) { + if (!('serviceWorker' in navigator)) return; + console.log('[Database] Initialisation du Service Worker sur :', path); + + try { + // 1. NETTOYAGE DES ANCIENS WORKERS (ZOMBIES) + const registrations = await navigator.serviceWorker.getRegistrations(); + + for (const registration of registrations) { + const scriptURL = registration.active?.scriptURL || registration.installing?.scriptURL || registration.waiting?.scriptURL; + const scope = registration.scope; + + // On détecte spécifiquement l'ancien dossier qui pose problème + // L'erreur mentionne : scope ('.../src/service-workers/') + if (scope.includes('/src/service-workers/') || (scriptURL && scriptURL.includes('/src/service-workers/'))) { + console.warn(`[Database] 🚨 ANCIEN Service Worker détecté (${scope}). Suppression immédiate...`); + await registration.unregister(); + // On continue la boucle, ne pas retourner ici, il faut installer le nouveau après + } + } + + // 2. INSTALLATION DU NOUVEAU WORKER (PROPRE) + // On vérifie s'il est déjà installé à la BONNE adresse + const existingValidWorker = registrations.find((r) => { + const url = r.active?.scriptURL || r.installing?.scriptURL || r.waiting?.scriptURL; + // On compare la fin de l'URL pour éviter les soucis http/https/localhost + return url && url.endsWith(path.replace(/^\//, '')); + }); + + if (!existingValidWorker) { + console.log('[Database] Enregistrement du nouveau Service Worker...'); + this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, { type: 'module', scope: '/' }); + } else { + console.log('[Database] Service Worker déjà actif et valide.'); + this.serviceWorkerRegistration = existingValidWorker; + await this.serviceWorkerRegistration.update(); + } + // Set up listeners + navigator.serviceWorker.addEventListener('message', async (event) => { + // console.log('Received message from service worker:', event.data); + await this.handleServiceWorkerMessage(event.data); + }); + + // Periodic check + if (this.serviceWorkerCheckIntervalId) clearInterval(this.serviceWorkerCheckIntervalId); + this.serviceWorkerCheckIntervalId = window.setInterval(async () => { + const activeWorker = this.serviceWorkerRegistration?.active || (await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration!)); + const service = await Services.getInstance(); + const payload = await service.getMyProcesses(); + if (payload && payload.length != 0) { + activeWorker?.postMessage({ type: 'SCAN', payload }); + } + }, 5000); + } catch (error) { + console.error('[Database] 💥 Erreur critique Service Worker:', error); + } + } + + // Helper function to wait for service worker activation + private async waitForServiceWorkerActivation(registration: ServiceWorkerRegistration): Promise { + return new Promise((resolve) => { + if (registration.active) { + resolve(registration.active); + } else { + const listener = () => { + if (registration.active) { + navigator.serviceWorker.removeEventListener('controllerchange', listener); + resolve(registration.active); + } + }; + navigator.serviceWorker.addEventListener('controllerchange', listener); + } + }); + } + + private async checkForUpdates() { + if (this.serviceWorkerRegistration) { + // Check for updates to the service worker + try { + await this.serviceWorkerRegistration.update(); + + // If there's a new worker waiting, activate it immediately + if (this.serviceWorkerRegistration.waiting) { + this.serviceWorkerRegistration.waiting.postMessage({ type: 'SKIP_WAITING' }); + } + } catch (error) { + console.error('Error checking for service worker updates:', error); + } + } + } + + private async handleServiceWorkerMessage(message: any) { + switch (message.type) { + case 'TO_DOWNLOAD': + await this.handleDownloadList(message.data); + break; + default: + console.warn('Unknown message type received from service worker:', message); + } + } + + private async handleDownloadList(downloadList: string[]): Promise { + // Download the missing data + let requestedStateId: string[] = []; + const service = await Services.getInstance(); + for (const hash of downloadList) { + const diff = await service.getDiffByValue(hash); + if (!diff) { + // This should never happen + console.warn(`Missing a diff for hash ${hash}`); + continue; + } + const processId = diff.process_id; + const stateId = diff.state_id; + const roles = diff.roles; + try { + const valueBytes = await service.fetchValueFromStorage(hash); + if (valueBytes) { + // Save data to db + const blob = new Blob([valueBytes], { type: 'application/octet-stream' }); + await service.saveBlobToDb(hash, blob); + document.dispatchEvent( + new CustomEvent('newDataReceived', { + detail: { + processId, + stateId, + hash, + }, + }), + ); + } else { + // We first request the data from managers + console.log('Request data from managers of the process'); + // get the diff from db + if (!requestedStateId.includes(stateId)) { + await service.requestDataFromPeers(processId, [stateId], [roles]); + requestedStateId.push(stateId); + } + } + } catch (e) { + console.error(e); + } + } + } + + private handleAddObjectResponse = async (event: MessageEvent) => { + const data = event.data; + console.log('Received response from service worker (ADD_OBJECT):', data); + const service = await Services.getInstance(); + if (data.type === 'NOTIFICATIONS') { + service.setNotifications(data.data); + } else if (data.type === 'TO_DOWNLOAD') { + console.log(`Received missing data ${data}`); + // Download the missing data + let requestedStateId: string[] = []; + for (const hash of data.data) { + try { + const valueBytes = await service.fetchValueFromStorage(hash); + if (valueBytes) { + // Save data to db + const blob = new Blob([valueBytes], { type: 'application/octet-stream' }); + await service.saveBlobToDb(hash, blob); + } else { + // We first request the data from managers + console.log('Request data from managers of the process'); + // get the diff from db + const diff = await service.getDiffByValue(hash); + if (diff === null) { + continue; + } + const processId = diff!.process_id; + const stateId = diff!.state_id; + const roles = diff!.roles; + if (!requestedStateId.includes(stateId)) { + await service.requestDataFromPeers(processId, [stateId], [roles]); + requestedStateId.push(stateId); + } + } + } catch (e) { + console.error(e); + } + } + } + }; + + private handleGetObjectResponse = (event: MessageEvent) => { + console.log('Received response from service worker (GET_OBJECT):', event.data); + }; + + public addObject(payload: { storeName: string; object: any; key: any }): Promise { + return new Promise(async (resolve, reject) => { + // Check if the service worker is active + if (!this.serviceWorkerRegistration) { + // console.warn('Service worker registration is not ready. Waiting...'); + this.serviceWorkerRegistration = await navigator.serviceWorker.ready; + } + + const activeWorker = await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration); + + // Create a message channel for communication + const messageChannel = new MessageChannel(); + + // Handle the response from the service worker + messageChannel.port1.onmessage = (event) => { + if (event.data.status === 'success') { + resolve(); + } else { + const error = event.data.message; + reject(new Error(error || 'Unknown error occurred while adding object')); + } + }; + + // Send the add object request to the service worker + try { + activeWorker?.postMessage( + { + type: 'ADD_OBJECT', + payload, + }, + [messageChannel.port2], + ); + } catch (error) { + reject(new Error(`Failed to send message to service worker: ${error}`)); + } + }); + } + + public batchWriting(payload: { storeName: string; objects: { key: any; object: any }[] }): Promise { + return new Promise(async (resolve, reject) => { + if (!this.serviceWorkerRegistration) { + this.serviceWorkerRegistration = await navigator.serviceWorker.ready; + } + + const activeWorker = await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration); + const messageChannel = new MessageChannel(); + + messageChannel.port1.onmessage = (event) => { + if (event.data.status === 'success') { + resolve(); + } else { + const error = event.data.message; + reject(new Error(error || 'Unknown error occurred while adding objects')); + } + }; + + try { + activeWorker?.postMessage( + { + type: 'BATCH_WRITING', + payload, + }, + [messageChannel.port2], + ); + } catch (error) { + reject(new Error(`Failed to send message to service worker: ${error}`)); + } + }); + } + + public async getObject(storeName: string, key: string): Promise { + const db = await this.getDb(); + const tx = db.transaction(storeName, 'readonly'); + const store = tx.objectStore(storeName); + const result = await new Promise((resolve, reject) => { + const getRequest = store.get(key); + getRequest.onsuccess = () => resolve(getRequest.result); + getRequest.onerror = () => reject(getRequest.error); + }); + return result ?? null; // Convert undefined to null + } + + public async dumpStore(storeName: string): Promise> { + const db = await this.getDb(); + const tx = db.transaction(storeName, 'readonly'); + const store = tx.objectStore(storeName); + + try { + return new Promise((resolve, reject) => { + const result: Record = {}; + const cursor = store.openCursor(); + + cursor.onsuccess = (event) => { + const request = event.target as IDBRequest; + const cursor = request.result; + if (cursor) { + result[cursor.key as string] = cursor.value; + cursor.continue(); + } else { + resolve(result); + } + }; + + cursor.onerror = () => { + reject(cursor.error); + }; + }); + } catch (error) { + console.error('Error fetching data from IndexedDB:', error); + throw error; + } + } + + public async deleteObject(storeName: string, key: string): Promise { + const db = await this.getDb(); + const tx = db.transaction(storeName, 'readwrite'); + const store = tx.objectStore(storeName); + try { + await new Promise((resolve, reject) => { + const getRequest = store.delete(key); + getRequest.onsuccess = () => resolve(getRequest.result); + getRequest.onerror = () => reject(getRequest.error); + }); + } catch (e) { + throw e; + } + } + + public async clearStore(storeName: string): Promise { + const db = await this.getDb(); + const tx = db.transaction(storeName, 'readwrite'); + const store = tx.objectStore(storeName); + try { + await new Promise((resolve, reject) => { + const clearRequest = store.clear(); + clearRequest.onsuccess = () => resolve(clearRequest.result); + clearRequest.onerror = () => reject(clearRequest.error); + }); + } catch (e) { + throw e; + } + } + + // Request a store by index + public async requestStoreByIndex(storeName: string, indexName: string, request: string): Promise { + const db = await this.getDb(); + const tx = db.transaction(storeName, 'readonly'); + const store = tx.objectStore(storeName); + const index = store.index(indexName); + + try { + return new Promise((resolve, reject) => { + const getAllRequest = index.getAll(request); + getAllRequest.onsuccess = () => { + const allItems = getAllRequest.result; + const filtered = allItems.filter((item) => item.state_id === request); + resolve(filtered); + }; + getAllRequest.onerror = () => reject(getAllRequest.error); + }); + } catch (e) { + throw e; + } + } +} + +export default Database;