import { DB_VERSION_CURRENT } from './constants' import { addKnownInstance, deleteKnownInstance } from './knownInstances' import { migrations } from './migrations' import { clearAllCaches } from './cache' import lifecycle from 'page-lifecycle/dist/lifecycle.mjs' const openReqs = {} const databaseCache = {} function createDatabase (instanceName) { return new Promise((resolve, reject) => { let req = indexedDB.open(instanceName, DB_VERSION_CURRENT.version) openReqs[instanceName] = req req.onerror = reject req.onblocked = () => { console.error('idb blocked') } req.onupgradeneeded = (e) => { let db = req.result let tx = e.currentTarget.transaction let migrationsToDo = migrations.filter(({ version }) => e.oldVersion < version) function doNextMigration () { if (!migrationsToDo.length) { return } let { migration } = migrationsToDo.shift() migration(db, tx, doNextMigration) } doNextMigration() } req.onsuccess = () => resolve(req.result) }) } export async function getDatabase (instanceName) { if (!instanceName) { throw new Error('instanceName is undefined in getDatabase()') } if (!databaseCache[instanceName]) { databaseCache[instanceName] = await createDatabase(instanceName) await addKnownInstance(instanceName) } return databaseCache[instanceName] } export async function dbPromise (db, storeName, readOnlyOrReadWrite, cb) { return new Promise((resolve, reject) => { const tx = db.transaction(storeName, readOnlyOrReadWrite) let store = typeof storeName === 'string' ? tx.objectStore(storeName) : storeName.map(name => tx.objectStore(name)) let res cb(store, (result) => { res = result }) tx.oncomplete = () => resolve(res) tx.onerror = () => reject(tx.error) }) } export function deleteDatabase (instanceName) { return new Promise((resolve, reject) => { // close any open requests let openReq = openReqs[instanceName] if (openReq && openReq.result) { openReq.result.close() } delete openReqs[instanceName] delete databaseCache[instanceName] let req = indexedDB.deleteDatabase(instanceName) req.onsuccess = () => resolve() req.onerror = () => reject(req.error) req.onblocked = () => console.error(`database ${instanceName} blocked`) }).then(() => deleteKnownInstance(instanceName)) .then(() => clearAllCaches(instanceName)) } export function closeDatabase (instanceName) { // close any open requests let openReq = openReqs[instanceName] if (openReq && openReq.result) { openReq.result.close() } delete openReqs[instanceName] delete databaseCache[instanceName] clearAllCaches(instanceName) } if (process.browser) { lifecycle.addEventListener('statechange', event => { if (event.newState === 'frozen') { // page is frozen, close IDB connections Object.keys(openReqs).forEach(instanceName => { closeDatabase(instanceName) console.log('closed instance DBs') }) } }) }