UNPKG

@amitkhare/indexed-storage

Version:

A modern, localStorage-like API for IndexedDB with instance-based and static APIs, multi-store support, configurable databases, and enhanced capabilities

2 lines (1 loc) 14.5 kB
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).IndexedStorage={})}(this,(function(t){"use strict";class e{constructor(t={}){this.config={dbName:t.dbName||"IndexedStorage",version:t.version||1,storeName:t.storeName||"data",storeConfig:t.storeConfig||{keyPath:null,autoIncrement:!1},additionalStores:t.additionalStores||[],storeConfigs:t.storeConfigs||{},logging:t.logging||!1},this.db=null,this.isInitialized=!1,(t.dbName||t.version||t.storeName)&&this._initializeAsync()}async _initializeAsync(){try{await this.init()}catch(t){this.config.logging&&console.error("IndexedStorage auto-initialization failed:",t)}}async init(){return this.isInitialized&&this.db?this.db:new Promise(((t,e)=>{const o=indexedDB.open(this.config.dbName,this.config.version);o.onerror=()=>{const t=new Error(`Failed to open database: ${this.config.dbName}`);this.config.logging&&console.error("IndexedStorage init error:",t),e(t)},o.onsuccess=e=>{this.db=e.target.result,this.isInitialized=!0,this.config.logging&&console.log(`IndexedStorage initialized: ${this.config.dbName}`),t(this.db)},o.onupgradeneeded=t=>{const e=t.target.result;e.objectStoreNames.contains(this.config.storeName)||(e.createObjectStore(this.config.storeName,this.config.storeConfig),this.config.logging&&console.log(`Created store: ${this.config.storeName}`));for(const t of this.config.additionalStores)if(!e.objectStoreNames.contains(t)){const o=this.config.storeConfigs[t]||{keyPath:null,autoIncrement:!1};e.createObjectStore(t,o),this.config.logging&&console.log(`Created additional store: ${t}`)}}}))}async getItem(t,e=this.config.storeName){return await this.init(),new Promise(((o,n)=>{const r=this.db.transaction([e],"readonly").objectStore(e).get(t);r.onsuccess=()=>{const n=r.result;this.config.logging&&console.log(`Retrieved item: ${t} from ${e}`,n),o(n)},r.onerror=()=>{const e=new Error(`Failed to get item: ${t}`);this.config.logging&&console.error("getItem error:",e),n(e)}}))}async setItem(t,e,o=this.config.storeName){return await this.init(),new Promise(((n,r)=>{const a=this.db.transaction([o],"readwrite").objectStore(o).put(e,t);a.onsuccess=()=>{this.config.logging&&console.log(`Stored item: ${t} in ${o}`,e),n()},a.onerror=()=>{const e=new Error(`Failed to set item: ${t}`);this.config.logging&&console.error("setItem error:",e),r(e)}}))}async removeItem(t,e=this.config.storeName){return await this.init(),new Promise(((o,n)=>{const r=this.db.transaction([e],"readwrite").objectStore(e).delete(t);r.onsuccess=()=>{this.config.logging&&console.log(`Removed item: ${t} from ${e}`),o()},r.onerror=()=>{const e=new Error(`Failed to remove item: ${t}`);this.config.logging&&console.error("removeItem error:",e),n(e)}}))}async clear(t=this.config.storeName){return await this.init(),new Promise(((e,o)=>{const n=this.db.transaction([t],"readwrite").objectStore(t).clear();n.onsuccess=()=>{this.config.logging&&console.log(`Cleared store: ${t}`),e()},n.onerror=()=>{const e=new Error(`Failed to clear store: ${t}`);this.config.logging&&console.error("clear error:",e),o(e)}}))}async getAllKeys(t=this.config.storeName){return await this.init(),new Promise(((e,o)=>{const n=this.db.transaction([t],"readonly").objectStore(t).getAllKeys();n.onsuccess=()=>{const o=n.result;this.config.logging&&console.log(`Retrieved keys from ${t}:`,o),e(o)},n.onerror=()=>{const e=new Error(`Failed to get keys from store: ${t}`);this.config.logging&&console.error("getAllKeys error:",e),o(e)}}))}async getAllValues(t=this.config.storeName){return await this.init(),new Promise(((e,o)=>{const n=this.db.transaction([t],"readonly").objectStore(t).getAll();n.onsuccess=()=>{const o=n.result;this.config.logging&&console.log(`Retrieved values from ${t}:`,o),e(o)},n.onerror=()=>{const e=new Error(`Failed to get values from store: ${t}`);this.config.logging&&console.error("getAllValues error:",e),o(e)}}))}async createStore(t,e={keyPath:null,autoIncrement:!1}){return new Promise(((o,n)=>{this.db&&this.db.close();const r=this.config.version+1,a=indexedDB.open(this.config.dbName,r);a.onerror=()=>{const e=new Error(`Failed to create store: ${t}`);this.config.logging&&console.error("createStore error:",e),n(e)},a.onsuccess=e=>{this.db=e.target.result,this.config.version=r,this.config.logging&&console.log(`Store created: ${t}`),o()},a.onupgradeneeded=o=>{const n=o.target.result;n.objectStoreNames.contains(t)||n.createObjectStore(t,e)}}))}async deleteStore(t){return new Promise(((e,o)=>{this.db&&this.db.close();const n=this.config.version+1,r=indexedDB.open(this.config.dbName,n);r.onerror=()=>{const e=new Error(`Failed to delete store: ${t}`);this.config.logging&&console.error("deleteStore error:",e),o(e)},r.onsuccess=o=>{this.db=o.target.result,this.config.version=n,this.config.logging&&console.log(`Store deleted: ${t}`),e()},r.onupgradeneeded=e=>{const o=e.target.result;o.objectStoreNames.contains(t)&&o.deleteObjectStore(t)}}))}async getStoreInfo(t=this.config.storeName){await this.init();if(!this.db.objectStoreNames.contains(t))throw new Error(`Store '${t}' does not exist`);const e=this.db.transaction([t],"readonly").objectStore(t),o={name:t,keyPath:e.keyPath,autoIncrement:e.autoIncrement,indexNames:Array.from(e.indexNames)};return new Promise(((n,r)=>{const a=e.count();a.onsuccess=()=>{o.count=a.result,n(o)},a.onerror=()=>r(new Error(`Failed to get store info: ${t}`))}))}async listStores(){return await this.init(),Array.from(this.db.objectStoreNames)}close(){this.db&&(this.db.close(),this.db=null,this.isInitialized=!1,this.config.logging&&console.log(`Closed database: ${this.config.dbName}`))}getStore(t){const e=this;return{setItem:(o,n)=>e.setItem(o,n,t),getItem:o=>e.getItem(o,t),removeItem:o=>e.removeItem(o,t),clear:()=>e.clear(t),getAllKeys:()=>e.getAllKeys(t),getAllValues:()=>e.getAllValues(t),getInfo:()=>e.getStoreInfo(t)}}async getAllStoresData(){await this.init();const t=await this.listStores(),e={};for(const o of t)try{const t=await this.getAllValues(o),n=await this.getAllKeys(o);e[o]={};for(let r=0;r<n.length;r++)e[o][n[r]]=t[r]}catch(t){this.config.logging&&console.warn(`Failed to get data from store ${o}:`,t),e[o]=null}return e}async clearAllStores(){await this.init();const t=await this.listStores();for(const e of t)try{await this.clear(e),this.config.logging&&console.log(`Cleared store: ${e}`)}catch(t){this.config.logging&&console.error(`Failed to clear store ${e}:`,t)}}async getAvailableStores(){return this.listStores()}async setMultipleStores(t){await this.init();for(const[e,o]of Object.entries(t))for(const[t,n]of Object.entries(o))await this.setItem(t,n,e)}async getMultipleStores(t){await this.init();const e={};for(const[o,n]of Object.entries(t)){e[o]={};for(const t of n)e[o][t]=await this.getItem(t,o)}return e}static _defaultInstance=null;static _getDefaultInstance(){return e._defaultInstance||(e._defaultInstance=new e({dbName:"IndexedStorage",version:1,storeName:"data",logging:!1})),e._defaultInstance}static async getItem(t,o){return e._getDefaultInstance().getItem(t,o)}static async setItem(t,o,n){return e._getDefaultInstance().setItem(t,o,n)}static async removeItem(t,o){return e._getDefaultInstance().removeItem(t,o)}static async clear(t){return e._getDefaultInstance().clear(t)}static async getAllKeys(t){return e._getDefaultInstance().getAllKeys(t)}static async getAllValues(t){return e._getDefaultInstance().getAllValues(t)}static async createStore(t,o){return e._getDefaultInstance().createStore(t,o)}static async deleteStore(t){return e._getDefaultInstance().deleteStore(t)}static async getStoreInfo(t){return e._getDefaultInstance().getStoreInfo(t)}static async listStores(){return e._getDefaultInstance().listStores()}static async init(){return e._getDefaultInstance().init()}static close(){e._defaultInstance&&(e._defaultInstance.close(),e._defaultInstance=null)}static getStore(t){return e._getDefaultInstance().getStore(t)}static async getAllStoresData(){return e._getDefaultInstance().getAllStoresData()}static async clearAllStores(){return e._getDefaultInstance().clearAllStores()}static async getAvailableStores(){return e._getDefaultInstance().getAvailableStores()}static async setMultipleStores(t){return e._getDefaultInstance().setMultipleStores(t)}static async getMultipleStores(t){return e._getDefaultInstance().getMultipleStores(t)}}function o(t={}){const e=t.dbName||"LocalStorageDB",o=t.dbVersion||1,n=t.storeName||"localStates",r=t.additionalStores||[],a=[n,...r];function s(){return new Promise(((t,n)=>{const r=indexedDB.open(e,o);r.onerror=()=>{r.error,n(r.error)},r.onsuccess=()=>{t(r.result)},r.onupgradeneeded=t=>{const e=t.target.result;a.forEach((t=>{if(!e.objectStoreNames.contains(t)){const o=e.createObjectStore(t,{keyPath:"id"});o.createIndex("timestamp","timestamp",{unique:!1}),o.createIndex("version","version",{unique:!1})}}))}}))}async function i(t,e=n){try{const o=(await s()).transaction([e],"readonly"),n=o.objectStore(e).get(t);return new Promise(((t,e)=>{n.onsuccess=()=>{const e=n.result;t(e||null)},n.onerror=()=>e(n.error)}))}catch(t){return null}}async function c(t,e,o=n){try{const n=await s(),r=n.transaction([o],"readwrite").objectStore(o),a={id:t,...e,timestamp:e.timestamp||(new Date).toISOString(),version:e.version||"1.0"},i=r.put(a);return new Promise(((t,e)=>{i.onsuccess=()=>t(),i.onerror=()=>e(i.error)}))}catch(t){throw t}}async function l(t,e=n){try{const o=(await s()).transaction([e],"readwrite"),n=o.objectStore(e).delete(t);return new Promise(((t,e)=>{n.onsuccess=()=>t(),n.onerror=()=>e(n.error)}))}catch(t){throw t}}async function g(t=n){try{const e=(await s()).transaction([t],"readonly"),o=e.objectStore(t).getAll();return new Promise(((t,e)=>{o.onsuccess=()=>t(o.result||[]),o.onerror=()=>e(o.error)}))}catch(t){return[]}}async function u(t,e=n){try{return null!==await i(t,e)}catch(t){return!1}}async function d(t=n){try{const e=(await s()).transaction([t],"readwrite"),o=e.objectStore(t).clear();return new Promise(((t,e)=>{o.onsuccess=()=>{t()},o.onerror=()=>e(o.error)}))}catch(t){throw t}}async function f(t=n){try{const e=await g(t);let o=0;const n={};return e.forEach((t=>{const e=new Blob([JSON.stringify(t)]).size;o+=e,n[t.id]=e})),{totalSize:o,items:n}}catch(t){return{totalSize:0,items:{}}}}return{get:i,save:c,remove:l,getAll:g,exists:u,clear:d,openDB:s,calculateUsage:f,testAvailability:async function(){try{const t="indexeddb-test",e={test:!0,timestamp:(new Date).toISOString()};await c(t,e);const o=await i(t);return await l(t),null!==o&&!0===o.test}catch(t){return!1}},getAvailableStores:function(){return a},createStoreAPI:function(t){if(!a.includes(t))throw new Error(`Store '${t}' is not configured. Available stores: ${a.join(", ")}`);return{get:e=>i(e,t),save:(e,o)=>c(e,o,t),remove:e=>l(e,t),getAll:()=>g(t),exists:e=>u(e,t),clear:()=>d(t),calculateUsage:()=>f(t),storeName:t}},clearAllStores:async function(){try{for(const t of a)await d(t)}catch(t){throw t}},getAllStoresData:async function(){try{const t={};for(const e of a)t[e]=await g(e);return t}catch(t){return{}}},dbName:e,dbVersion:o,primaryStore:n,allStores:a}}"undefined"!=typeof module&&module.exports?module.exports=e:"undefined"!=typeof window&&(window.IndexedStorage=e),t.IndexedStorage=e,t.default=e,t.useIndexedDB=o,t.useStorageManager=function(t={}){const e=o(t);let n=null,r=null;const a={async getItem(t){const o=await e.get(t);return o||null},async setItem(t,o){const n={timestamp:(new Date).toISOString(),version:"1.0",...o};await e.save(t,n)},async removeItem(t){await e.remove(t)},hasItem:async t=>await e.exists(t),async clear(){await e.clear()},getAllKeys:async()=>(await e.getAll()).map((t=>t.id)),testAvailability:async()=>await e.testAvailability()};function s(t){if(0===t||null==t)return"0 Bytes";if(t<0)return"0 Bytes";const e=["Bytes","KB","MB","GB","TB"],o=Math.floor(Math.log(t)/Math.log(1024)),n=Math.min(o,e.length-1);return parseFloat((t/Math.pow(1024,n)).toFixed(2))+" "+e[n]}async function i(){return n||(r||(r=(async()=>{let t=null,e=null,o=!1;if("storage"in navigator&&"estimate"in navigator.storage)try{const n=await navigator.storage.estimate();t=n.quota,e=n.usage,o=!0,s(n.quota),s(n.usage)}catch(e){t=52428800,o=!1}else t=52428800,o=!1;s(t);const a={quota:t,usage:e,exactQuotaAvailable:o};return n=a,setTimeout((()=>{n=null,r=null}),3e5),a})(),r))}async function c(){return await e.calculateUsage()}async function l(t=null){try{const{totalSize:e,items:o}=await c();let n=0;t&&o[t]&&(n=o[t]);const{quota:r,usage:a,exactQuotaAvailable:l}=await i(),g=Math.round(e/r*100);return{totalSize:e,specificItemSize:n,quota:r,usage:l?a:e,exactQuotaAvailable:l,usagePercentage:g,apiUsagePercentage:l&&null!==a?Math.round(a/r*100):null,formattedTotalSize:s(e),formattedSpecificSize:s(n),formattedQuota:s(r),formattedUsage:s(l?a:e),items:Object.keys(o).map((t=>({key:t,size:o[t],formattedSize:s(o[t])})))}}catch(t){return null}}return{storage:a,getStorageEstimate:i,getStorageUsage:l,getStorageWarningLevel:async function(){const t=await l();if(!t)return"normal";const{usagePercentage:e,exactQuotaAvailable:o,apiUsagePercentage:n}=t,r=o&&null!==n?n:e;return r>=95?"critical":r>=80?"warning":"normal"},calculateUsage:c,formatBytes:s,testStorageAvailability:async function(){try{const t="storage-availability-test",e={test:!0,timestamp:(new Date).toISOString()};return await a.setItem(t,e),await a.removeItem(t),!0}catch(t){return!1}},migrateFromLocalStorage:async function(t){try{const e=localStorage.getItem(t);if(!e)return!1;if(await a.getItem(t))return!1;const o=JSON.parse(e);return await a.setItem(t,o),localStorage.removeItem(t),!0}catch(t){return!1}},createStoreManager:function(t){const o=e.createStoreAPI(t);return{storage:{async getItem(t){const e=await o.get(t);return e||null},async setItem(t,e){const n={timestamp:(new Date).toISOString(),version:"1.0",...e};await o.save(t,n)},async removeItem(t){await o.remove(t)},hasItem:async t=>await o.exists(t),async clear(){await o.clear()},getAllKeys:async()=>(await o.getAll()).map((t=>t.id)),testAvailability:async()=>await e.testAvailability()},storeName:t}},getAllStoresData:async function(){return await e.getAllStoresData()},clearAllStores:async function(){await e.clearAllStores()},getAvailableStores:function(){return e.getAvailableStores()},dbName:e.dbName,dbVersion:e.dbVersion,primaryStore:e.primaryStore,allStores:e.allStores}},Object.defineProperty(t,"__esModule",{value:!0})}));