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

565 lines (436 loc) • 16.5 kB
# @amitkhare/indexed-storage A modern, localStorage-like API for IndexedDB that provides a simple and intuitive interface for client-side data storage with enhanced capabilities. ## ✨ Features - **localStorage-like API** - Familiar `setItem()`, `getItem()`, `removeItem()` interface - **Type Preservation** - Stores and retrieves exact values (primitives, objects, arrays) - **Async/Await Support** - Modern Promise-based API - **Configurable Database** - Customize database name, version, and store name - **Multi-Store Support** - Multiple object stores within the same database - **Multiple Database Instances** - Create separate databases for different purposes - **Store-Specific APIs** - Isolated access to individual stores - **Batch Operations** - Get/set/remove multiple items at once - **Optional Logging** - Configurable console logging for debugging (disabled by default) - **TypeScript Ready** - Complete TypeScript declarations included - **Environment-Aware** - Easy configuration for development, staging, and production ## šŸš€ Installation ```bash npm install @amitkhare/indexed-storage ``` ### Import IndexedStorage supports multiple import patterns for maximum flexibility: ```javascript // Default import import IndexedStorage from '@amitkhare/indexed-storage'; // Named import import { IndexedStorage } from '@amitkhare/indexed-storage'; // Import with utilities import IndexedStorage, { useStorageManager, useIndexedDB } from '@amitkhare/indexed-storage'; // Named import with utilities import { IndexedStorage, useStorageManager, useIndexedDB } from '@amitkhare/indexed-storage'; // TypeScript type imports import { IndexedStorage, type IndexedStorageConfig } from '@amitkhare/indexed-storage'; ``` ### Basic Usage ```javascript // Store data await IndexedStorage.setItem('username', 'john_doe'); await IndexedStorage.setItem('count', 42); await IndexedStorage.setItem('user', { name: 'John', age: 30 }); // Retrieve data const username = await IndexedStorage.getItem('username'); // 'john_doe' const count = await IndexedStorage.getItem('count'); // 42 const user = await IndexedStorage.getItem('user'); // { name: 'John', age: 30 } // Check if item exists if (await IndexedStorage.hasItem('username')) { console.log('Username is stored'); } // Remove item await IndexedStorage.removeItem('username'); // Clear all data await IndexedStorage.clear(); ``` ## šŸ”„ New Instance-based API **NEW:** Create multiple independent IndexedStorage instances with different configurations! ### Multiple Independent Instances ```javascript // Create separate storage instances for different purposes const appStorage = new IndexedStorage({ dbName: 'MyApp', storeName: 'settings', logging: true }); const userStorage = new IndexedStorage({ dbName: 'UserData', storeName: 'profiles' }); const cacheStorage = new IndexedStorage({ dbName: 'Cache', storeName: 'temp' }); // Each instance operates independently await appStorage.setItem('theme', 'dark'); await userStorage.setItem('currentUser', { id: 123, name: 'John' }); await cacheStorage.setItem('lastSync', Date.now()); ``` ### Instance Configuration Options ```javascript const storage = new IndexedStorage({ dbName: 'MyDatabase', // Database name (default: 'IndexedStorage') version: 1, // Database version (default: 1) storeName: 'myStore', // Default store name (default: 'data') storeConfig: { // Store configuration keyPath: 'id', // Key path for the store autoIncrement: true // Auto-increment keys }, logging: true // Enable logging (default: false) }); ``` ### Multi-Store within Single Database ```javascript // Method 1: Create stores during initialization const storage = new IndexedStorage({ dbName: 'ECommerceApp', storeName: 'products', // default store additionalStores: ['users', 'orders', 'cart'], // create these stores automatically storeConfigs: { users: { keyPath: 'id', autoIncrement: true }, orders: { keyPath: 'orderId', autoIncrement: false }, cart: { keyPath: null, autoIncrement: false } } }); // Method 2: Create stores dynamically const storage2 = new IndexedStorage({ dbName: 'MyApp', storeName: 'default' }); await storage2.createStore('users', { keyPath: 'id', autoIncrement: true }); await storage2.createStore('sessions', { keyPath: 'sessionId' }); // Store data in different stores await storage.setItem('prod1', { name: 'Laptop', price: 999 }, 'products'); await storage.setItem('user1', { name: 'John', email: 'john@example.com' }, 'users'); await storage.setItem('order1', { total: 999, items: ['prod1'] }, 'orders'); // Get store-specific API for easier operations const userStore = storage.getStore('users'); const orderStore = storage.getStore('orders'); await userStore.setItem('user2', { name: 'Jane', email: 'jane@example.com' }); await orderStore.setItem('order2', { total: 1500, items: ['prod1', 'prod2'] }); // Batch operations across multiple stores await storage.setMultipleStores({ products: { 'prod2': { name: 'Mouse', price: 29 } }, users: { 'user3': { name: 'Bob', email: 'bob@example.com' } } }); // Get data from multiple stores const multiData = await storage.getMultipleStores({ products: ['prod1', 'prod2'], users: ['user1', 'user2'] }); // List all stores const stores = await storage.listStores(); console.log(stores); // ['products', 'users', 'orders', 'cart'] // Get all data from all stores const allData = await storage.getAllStoresData(); console.log(allData); // { products: {...}, users: {...}, orders: {...}, cart: {...} } ``` ### Instance Methods **Core Operations:** - `setItem(key, value, storeName?)` - Store an item - `getItem(key, storeName?)` - Retrieve an item - `removeItem(key, storeName?)` - Remove an item - `clear(storeName?)` - Clear all items from a store **Batch Operations:** - `getAllKeys(storeName?)` - Get all keys - `getAllValues(storeName?)` - Get all values **Store Management:** - `createStore(name, config?)` - Create new store - `deleteStore(name)` - Delete a store - `listStores()` - List all stores - `getStoreInfo(name?)` - Get store information - `getAvailableStores()` - Get available stores (alias for listStores) **Multi-Store Operations:** - `getStore(storeName)` - Get store-specific API object - `getAllStoresData()` - Get data from all stores - `clearAllStores()` - Clear all stores in database - `setMultipleStores(storeData)` - Set data across multiple stores - `getMultipleStores(storeKeys)` - Get data from multiple stores **Database Management:** - `init()` - Initialize the database connection - `close()` - Close the database connection ### Backward Compatibility The original static API still works alongside the new instance API: ```javascript // Static API (legacy) - still works await IndexedStorage.setItem('key', 'value'); // Instance API (new) const storage = new IndexedStorage({ dbName: 'MyApp' }); await storage.setItem('key', 'value'); ``` šŸ“– **See [Instance API Guide](./docs/INSTANCE_API_GUIDE.md) for comprehensive examples and migration guide.** ## āš™ļø Configuration ### Enable/Disable Logging By default, IndexedStorage operates silently. You can enable logging for debugging: ```javascript // Enable logging for debugging (disabled by default) IndexedStorage.configure({ enableLogging: true }); // Disable logging IndexedStorage.configure({ enableLogging: false }); // Check current configuration const config = IndexedStorage.getConfig(); console.log(config); // { enableLogging: false } ``` ### šŸ—„ļø Configurable Database Settings IndexedStorage now supports configurable IndexedDB settings, allowing you to customize the database name, version, and store name for different applications and use cases. #### Basic Database Configuration ```javascript // Configure custom database settings IndexedStorage.configure({ enableLogging: true, indexedDB: { dbName: 'MyApplication', // Database name (default: 'LocalStorageDB') dbVersion: 2, // Database version (default: 1) storeName: 'appData' // Object store name (default: 'localStates') } }); // All subsequent operations use the custom configuration await IndexedStorage.setItem('user-data', { name: 'John' }); ``` #### Multiple Database Instances Create separate database instances for different purposes: ```javascript import { useIndexedDB, useStorageManager } from '@amitkhare/indexed-storage'; // User data database const userDB = useIndexedDB({ dbName: 'UserData', dbVersion: 1, storeName: 'users' }); // Application cache database const cacheDB = useIndexedDB({ dbName: 'AppCache', dbVersion: 1, storeName: 'cache' }); // Store data in different databases await userDB.save('profile', { name: 'John', email: 'john@example.com' }); await cacheDB.save('api-data', { response: {...}, timestamp: Date.now() }); ``` #### Multi-Store Configuration Use multiple object stores within the same database: ```javascript // Configure with multiple stores in the same database IndexedStorage.configure({ enableLogging: true, indexedDB: { dbName: 'MyApplication', dbVersion: 1, storeName: 'users', // Primary store additionalStores: ['cache', 'analytics', 'settings'] // Additional stores } }); // Get store-specific APIs const userStore = IndexedStorage.getStore('users'); const cacheStore = IndexedStorage.getStore('cache'); const analyticsStore = IndexedStorage.getStore('analytics'); // Use each store independently await userStore.setItem('profile', { name: 'John' }); await cacheStore.setItem('api-data', { response: 'cached' }); await analyticsStore.setItem('event-1', { action: 'click' }); ``` #### Environment-Specific Configuration ```javascript const isDevelopment = process.env.NODE_ENV === 'development'; IndexedStorage.configure({ enableLogging: isDevelopment, indexedDB: { dbName: isDevelopment ? 'MyApp_Development' : 'MyApp_Production', dbVersion: isDevelopment ? 1 : 2, storeName: 'appData' } }); ``` šŸ“– **See [Configurable Database Guide](./docs/CONFIGURABLE_DATABASE.md) for comprehensive examples and best practices.** šŸ“– **See [Multi-Store Usage Example](./examples/multi-store-usage.js) for comprehensive multi-store examples.** šŸ“– **See [Migration Guide](./docs/MIGRATION_GUIDE.md) for upgrading to multi-store functionality.** ## šŸ“š API Reference ### Core Methods #### `getItem(key)` Retrieves an item from storage. - **Returns:** The stored value or null if not found #### `setItem(key, value)` Stores an item in storage. - **Supports:** All JSON-serializable data types #### `removeItem(key)` Removes an item from storage. #### `hasItem(key)` Checks if an item exists in storage. - **Returns:** `true` if the key exists #### `clear()` Removes all items from storage. #### `getAllKeys()` Gets all keys from storage. - **Returns:** Array of all keys #### `length()` Gets the number of items in storage. ### Batch Operations #### `getMultiple(keys)` Retrieves multiple items at once. ```javascript const data = await IndexedStorage.getMultiple(['user', 'settings']); // Returns: { user: {...}, settings: {...} } ``` #### `setMultiple(items)` Stores multiple items at once. ```javascript await IndexedStorage.setMultiple({ 'user': { name: 'John' }, 'settings': { theme: 'dark' } }); ``` #### `removeMultiple(keys)` Removes multiple items at once. ```javascript await IndexedStorage.removeMultiple(['temp1', 'temp2']); ``` ### Multi-Store Operations #### `getStore(storeName)` Get a store-specific API for working with a particular store. ```javascript const cacheStore = IndexedStorage.getStore('cache'); await cacheStore.setItem('key', 'value'); const value = await cacheStore.getItem('key'); ``` #### `getAvailableStores()` Get list of all available stores in the database. ```javascript const stores = IndexedStorage.getAvailableStores(); console.log(stores); // ['users', 'cache', 'analytics', 'settings'] ``` #### `getAllStoresData()` Get all data from all stores at once. ```javascript const allData = await IndexedStorage.getAllStoresData(); // Returns: { users: [...], cache: [...], analytics: [...] } ``` #### `clearAllStores()` Clear all data from all stores. ```javascript await IndexedStorage.clearAllStores(); ``` ## šŸŽÆ Usage Examples ### Application State ```javascript // Save application state await IndexedStorage.setItem('app-state', { currentView: 'dashboard', sidebarCollapsed: true, activeProject: 'project-123' }); // Restore state on reload const appState = await IndexedStorage.getItem('app-state'); if (appState) { navigateToView(appState.currentView); setSidebarState(appState.sidebarCollapsed); } ``` ### User Preferences ```javascript // Save user preferences await IndexedStorage.setItem('user-preferences', { theme: 'dark', language: 'en', notifications: true }); // Load preferences on app start const prefs = await IndexedStorage.getItem('user-preferences'); if (prefs) { applyTheme(prefs.theme); setLanguage(prefs.language); } ``` ### Caching Data ```javascript // Cache API responses await IndexedStorage.setItem('api-cache-users', { data: usersData, timestamp: Date.now(), expires: Date.now() + (1000 * 60 * 60) // 1 hour }); // Check cache before API call const cachedUsers = await IndexedStorage.getItem('api-cache-users'); if (cachedUsers && Date.now() < cachedUsers.expires) { return cachedUsers.data; } ``` ### Multi-Store Application ```javascript // Configure multi-store database IndexedStorage.configure({ indexedDB: { dbName: 'TodoApp', dbVersion: 1, storeName: 'todos', additionalStores: ['cache', 'analytics', 'settings'] } }); // Use different stores for different purposes const todoStore = IndexedStorage.getStore('todos'); const cacheStore = IndexedStorage.getStore('cache'); const analyticsStore = IndexedStorage.getStore('analytics'); const settingsStore = IndexedStorage.getStore('settings'); // Store todos await todoStore.setItem('todo-1', { title: 'Buy groceries', completed: false, createdAt: new Date().toISOString() }); // Cache API response await cacheStore.setItem('/api/weather', { data: { temperature: 22, condition: 'sunny' }, timestamp: Date.now(), ttl: 300000 // 5 minutes }); // Track user events await analyticsStore.setItem('event-' + Date.now(), { action: 'todo-created', timestamp: Date.now(), userAgent: navigator.userAgent }); // Save app settings await settingsStore.setItem('app-config', { theme: 'dark', notifications: true, autoSave: true }); // Get data from all stores const allData = await IndexedStorage.getAllStoresData(); console.log('All stores data:', allData); ``` ## šŸ”§ Error Handling IndexedStorage includes comprehensive error handling: ```javascript try { await IndexedStorage.setItem('large-data', massiveObject); } catch (error) { console.error('Failed to store data:', error); // Handle storage quota exceeded, etc. } // Check availability before using if (await IndexedStorage.isAvailable()) { // Safe to proceed } else { // Fallback to localStorage or other storage } ``` ## šŸ”„ Migration from localStorage IndexedStorage is designed as a drop-in replacement for localStorage with async operations: ```javascript // localStorage (synchronous) localStorage.setItem('key', JSON.stringify(data)); const data = JSON.parse(localStorage.getItem('key') || '{}'); // IndexedStorage (asynchronous) await IndexedStorage.setItem('key', data); const data = await IndexedStorage.getItem('key'); ``` ## šŸ“„ License MIT License - see [LICENSE](LICENSE) file for details. ## šŸ¤ Contributing Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.