pinafore/src/routes/_database/databaseLifecycle.js

102 lines
3.0 KiB
JavaScript

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')
})
}
})
}