Refactor IndexedDB

This commit is contained in:
Thomas Weber committed 2021-07-10 18:22:18 -05:00
1 parent 494ed7bc05
commit e67f6e5807
2 files changed
+92 -77

No files matched your search

+11 -77
View File
@@ -1,90 +1,23 @@
import largeAssets from './large-assets';
import {buildId} from './lib/build-id';
import Database from './lib/idb';
// We can't trust the HTTP cache to reliably cache these large assets
const DATABASE_NAME = 'p4-large-assets';
const DATABASE_VERSION = 1;
const STORE_NAME = 'assets';
let _db;
const db = new Database(DATABASE_NAME, DATABASE_VERSION, STORE_NAME);
const getAssetId = (asset) => {
return `${asset.src}-${asset.type}-${asset.sha256 || buildId}`;
};
// https://github.com/jakearchibald/safari-14-idb-fix/blob/582bbdc7230891113bfb5743391550cbf29d21f2/src/index.ts
const idbReady = () => {
const isSafari =
!navigator.userAgentData &&
/Safari\//.test(navigator.userAgent) &&
!/Chrom(e|ium)\//.test(navigator.userAgent);
// No point putting other browsers or older versions of Safari through this mess.
if (!isSafari || !indexedDB.databases) return Promise.resolve();
let intervalId;
return new Promise((resolve) => {
const tryIdb = () => indexedDB.databases().finally(resolve);
intervalId = setInterval(tryIdb, 100);
tryIdb();
}).finally(() => clearInterval(intervalId));
};
const openDB = async () => {
if (_db) {
return _db;
}
if (!window.indexedDB) {
throw new Error('indexedDB is not supported');
}
await idbReady();
return new Promise((resolve, reject) => {
const request = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
request.onupgradeneeded = e => {
const db = e.target.result;
db.createObjectStore(STORE_NAME, {
keyPath: 'id'
});
};
request.onsuccess = (e) => {
_db = e.target.result;
removeExtraneous()
.then(() => {
resolve(_db);
})
.catch((err) => {
reject(err);
});
};
request.onerror = (e) => {
reject(new Error(`IDB Error ${e.target.error}`));
};
});
};
const createTransaction = async (readwrite) => {
const db = await openDB();
const transaction = db.transaction(STORE_NAME, readwrite);
const store = transaction.objectStore(STORE_NAME);
return {
db,
transaction,
store
};
};
const setTransactionErrorHandler = (transaction, reject) => {
transaction.onerror = () => {
reject(new Error(`Transaction error: ${transaction.error}`))
};
};
const removeExtraneous = async () => {
const {transaction, store} = await createTransaction('readwrite');
const {transaction, store} = await db.createTransaction('readwrite');
return new Promise((resolve, reject) => {
setTransactionErrorHandler(transaction, reject);
Database.setTransactionErrorHandler(transaction, reject);
const allValidAssetIds = Object.values(largeAssets).map(getAssetId);
const request = store.openCursor();
request.onsuccess = e => {
@@ -101,11 +34,12 @@ const removeExtraneous = async () => {
};
});
};
db.onopen = removeExtraneous;
const get = async (asset) => {
const {transaction, store} = await createTransaction('readonly');
const {transaction, store} = await db.createTransaction('readonly');
return new Promise((resolve, reject) => {
setTransactionErrorHandler(transaction, reject);
Database.setTransactionErrorHandler(transaction, reject);
const assetId = getAssetId(asset);
const request = store.get(assetId);
request.onsuccess = (e) => {
@@ -120,9 +54,9 @@ const get = async (asset) => {
};
const set = async (asset, content) => {
const {transaction, store} = await createTransaction('readwrite');
const {transaction, store} = await db.createTransaction('readwrite');
return new Promise((resolve, reject) => {
setTransactionErrorHandler(transaction, reject);
Database.setTransactionErrorHandler(transaction, reject);
const assetId = getAssetId(asset);
const request = store.put({
id: assetId,
@@ -135,9 +69,9 @@ const set = async (asset, content) => {
};
const resetAll = async () => {
const {transaction, store} = await createTransaction('readwrite');
const {transaction, store} = await db.createTransaction('readwrite');
return new Promise((resolve, reject) => {
setTransactionErrorHandler(transaction, reject);
Database.setTransactionErrorHandler(transaction, reject);
const request = store.openCursor();
request.onsuccess = e => {
const cursor = e.target.result;
+81
View File
@@ -0,0 +1,81 @@
// https://github.com/jakearchibald/safari-14-idb-fix/blob/582bbdc7230891113bfb5743391550cbf29d21f2/src/index.ts
const idbReady = () => {
const isSafari =
!navigator.userAgentData &&
/Safari\//.test(navigator.userAgent) &&
!/Chrom(e|ium)\//.test(navigator.userAgent);
// No point putting other browsers or older versions of Safari through this mess.
if (!isSafari || !indexedDB.databases) return Promise.resolve();
let intervalId;
return new Promise((resolve) => {
const tryIdb = () => indexedDB.databases().finally(resolve);
intervalId = setInterval(tryIdb, 100);
tryIdb();
}).finally(() => clearInterval(intervalId));
};
class Database {
constructor (name, version, storeName) {
this.name = name;
this.version = version;
this.storeName = storeName;
this.db = null;
}
async open () {
if (this.db) {
return this.db;
}
if (!window.indexedDB) {
throw new Error('indexedDB is not supported');
}
await idbReady();
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.name, this.version);
request.onupgradeneeded = (e) => {
const db = e.target.result;
db.createObjectStore(this.storeName, {
keyPath: 'id'
});
};
request.onsuccess = (e) => {
this.db = e.target.result;
this.onopen()
.then(() => {
resolve(this.db);
})
.catch((err) => {
reject(err);
});
};
request.onerror = (e) => {
reject(new Error(`IDB Error ${e.target.error}`));
};
});
}
async createTransaction (readwrite) {
const db = await this.open();
const transaction = db.transaction(this.storeName, readwrite);
const store = transaction.objectStore(this.storeName);
return {
db,
transaction,
store
};
}
onopen () {
return Promise.resolve();
}
}
Database.setTransactionErrorHandler = (transaction, reject) => {
transaction.onerror = () => {
reject(new Error(`Transaction error: ${transaction.error}`))
};
};
export default Database;