// public/data.worker.js const DB_NAME = "4nk"; const DB_VERSION = 1; const EMPTY32BYTES = String("").padStart(64, "0"); // ============================================ // SERVICE WORKER LIFECYCLE // ============================================ self.addEventListener("install", (event) => { event.waitUntil(self.skipWaiting()); }); self.addEventListener("activate", (event) => { event.waitUntil(self.clients.claim()); }); // ============================================ // INDEXEDDB DIRECT ACCESS (READ-ONLY) // ============================================ /** * Ouvre une connexion à la BDD directement depuis le Service Worker */ function openDB() { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(request.result); }); } /** * Récupère un objet spécifique (équivalent à GET_OBJECT) */ function getObject(db, storeName, key) { return new Promise((resolve, reject) => { const transaction = db.transaction(storeName, "readonly"); const store = transaction.objectStore(storeName); const request = store.get(key); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(request.result); }); } /** * Récupère plusieurs objets d'un coup (équivalent à GET_MULTIPLE_OBJECTS) * Optimisé pour utiliser une seule transaction. */ function getMultipleObjects(db, storeName, keys) { return new Promise((resolve, reject) => { const transaction = db.transaction(storeName, "readonly"); const store = transaction.objectStore(storeName); const results = []; let completed = 0; if (keys.length === 0) resolve([]); keys.forEach((key) => { const request = store.get(key); request.onsuccess = () => { if (request.result) results.push(request.result); completed++; if (completed === keys.length) resolve(results); }; request.onerror = () => { console.warn(`[SW] Erreur lecture clé ${key}`); completed++; if (completed === keys.length) resolve(results); }; }); }); } // ============================================ // SCAN LOGIC // ============================================ async function scanMissingData(processesToScan) { console.log("[Service Worker] 🚀 Scanning with DIRECT DB ACCESS..."); let db; try { db = await openDB(); } catch (e) { console.error("[SW] Impossible d'ouvrir la BDD:", e); return { toDownload: [], diffsToCreate: [] }; } // 1. Récupération directe des processus const myProcesses = await getMultipleObjects( db, "processes", processesToScan ); let toDownload = new Set(); let diffsToCreate = []; if (myProcesses && myProcesses.length !== 0) { for (const process of myProcesses) { if (!process || !process.states) continue; const firstState = process.states[0]; // Sécurisation : on vérifie que firstState existe if (!firstState) continue; const processId = firstState.commited_in; for (const state of process.states) { if (state.state_id === EMPTY32BYTES) continue; for (const [field, hash] of Object.entries(state.pcd_commitment)) { // On ignore les données publiques ou les rôles if ( (state.public_data && state.public_data[field] !== undefined) || field === "roles" ) continue; // 2. Vérification directe dans 'data' const existingData = await getObject(db, "data", hash); if (!existingData) { toDownload.add(hash); // 3. Vérification directe dans 'diffs' const existingDiff = await getObject(db, "diffs", hash); if (!existingDiff) { diffsToCreate.push({ process_id: processId, state_id: state.state_id, value_commitment: hash, roles: state.roles, field: field, description: null, previous_value: null, new_value: null, notify_user: false, need_validation: false, validation_status: "None", }); } } else { // Si on a trouvé la donnée, on est sûr de ne pas avoir besoin de la télécharger if (toDownload.has(hash)) { toDownload.delete(hash); } } } } } } // On ferme la connexion BDD pour libérer les ressources db.close(); console.log("[Service Worker] Scan complete:", { toDownload: toDownload.size, diffsToCreate: diffsToCreate.length, }); return { toDownload: Array.from(toDownload), diffsToCreate: diffsToCreate, }; } // ============================================ // MESSAGE HANDLER // ============================================ self.addEventListener("message", async (event) => { const data = event.data; if (data.type === "SCAN") { try { const myProcessesId = data.payload; if (myProcessesId && myProcessesId.length !== 0) { // Appel direct de la nouvelle fonction optimisée const scanResult = await scanMissingData(myProcessesId); if (scanResult.toDownload.length !== 0) { event.source.postMessage({ type: "TO_DOWNLOAD", data: scanResult.toDownload, }); } if (scanResult.diffsToCreate.length > 0) { event.source.postMessage({ type: "DIFFS_TO_CREATE", data: scanResult.diffsToCreate, }); } } } catch (error) { console.error("[Service Worker] Scan error:", error); // On évite de spammer l'UI avec des erreurs internes du worker } } });