Skip to content

Instantly share code, notes, and snippets.

@xnohat
Created October 5, 2023 04:50
Show Gist options
  • 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;
@kezenwa
Copy link

kezenwa commented Jul 1, 2024

yes have seen that. Equally TESTED to ensure _init() ain't called on every GET and SET operation and was glad it isn't.

Good Work Sir. Currently further extending it serioulsy as I needed much more features BUT wouldn't have bee able to do any of such without the foundation you gave me.

Thanks once more

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment