Skip to content

Instantly share code, notes, and snippets.

@xnohat
Created October 5, 2023 04:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xnohat/b7aa5035278478871697b7ad6255efb2 to your computer and use it in GitHub Desktop.
Save xnohat/b7aa5035278478871697b7ad6255efb2 to your computer and use it in GitHub Desktop.
sync wrapper for IndexedDB to same localStorage API, working extractly same as localStorage, just replace localStorage to idbStorage
class IndexedDBStorage {
constructor(dbName = 'localStorageDB', storeName = 'localStorageStore') {
this.dbName = dbName;
this.storeName = storeName;
this._init();
this.cache = {};
}
_init() {
const request = window.indexedDB.open(this.dbName, 1);
request.onerror = (event) => console.error('Error opening indexedDB');
request.onsuccess = async (event) => {
this.db = event.target.result;
await this._populateCache();
this._syncCache();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore(this.storeName);
};
}
async _populateCache() {
const store = this._getStore();
return new Promise((resolve, reject) => {
const request = store.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
this.cache[cursor.key] = cursor.value;
cursor.continue();
} else {
resolve(); // Finished populating the cache
}
};
request.onerror = (event) => reject('Error populating cache');
});
}
async _syncCache() {
for (const key in this.cache) {
await this._asyncSetItem(key, this.cache[key]);
}
}
async _asyncSetItem(key, value) {
const store = this._getStore('readwrite');
return new Promise((resolve, reject) => {
const request = store.put(value, key);
request.onsuccess = () => resolve();
request.onerror = (event) => reject('Error storing value');
});
}
_getStore(mode = 'readonly') {
const transaction = this.db.transaction([this.storeName], mode);
return transaction.objectStore(this.storeName);
}
setItem(key, value) {
this.cache[key] = value;
this._asyncSetItem(key, value).catch(console.error);
}
getItem(key) {
if (this.cache[key]) {
return this.cache[key];
}
// Fetch from indexedDB and store in cache (in the background)
this._asyncGetItem(key).then(value => {
this.cache[key] = value;
});
return null; // or some default value
}
async _asyncGetItem(key) {
const store = this._getStore();
return new Promise((resolve, reject) => {
const request = store.get(key);
request.onsuccess = (event) => resolve(event.target.result);
request.onerror = (event) => reject('Error retrieving value');
});
}
removeItem(key) {
delete this.cache[key];
this._asyncRemoveItem(key).catch(console.error);
}
async _asyncRemoveItem(key) {
const store = this._getStore('readwrite');
return new Promise((resolve, reject) => {
const request = store.delete(key);
request.onsuccess = () => resolve();
request.onerror = (event) => reject('Error removing value');
});
}
clear() {
this.cache = {};
this._asyncClear().catch(console.error);
}
async _asyncClear() {
const store = this._getStore('readwrite');
return new Promise((resolve, reject) => {
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = (event) => reject('Error clearing store');
});
}
}
const idbStorage = new Proxy(new IndexedDBStorage(), {
get: (target, prop) => {
if (typeof target[prop] === 'function') {
return target[prop].bind(target);
}
return target.getItem(prop);
},
set: (target, prop, value) => {
target.setItem(prop, value);
return true;
}
});
window.idbStorage = idbStorage;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment