use-idb-storage 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,280 @@
1
1
  # sohanemon/idb
2
2
 
3
- ![npm version](https://img.shields.io/npm/v/@sohanemon/idb)
4
- ![npm downloads](https://img.shields.io/npm/dm/@sohanemon/idb)
5
- ![License](https://img.shields.io/npm/l/@sohanemon/idb)
3
+ ![npm version](https://img.shields.io/npm/v/use-idb-storage)
4
+ ![npm downloads](https://img.shields.io/npm/dm/use-idb-storage)
5
+ ![License](https://img.shields.io/npm/l/use-idb-storage)
6
6
  ![Tests](https://github.com/sohanemon/idb/actions/workflows/test.yml/badge.svg)
7
+
8
+ A modern, developer-friendly IndexedDB library for React and vanilla JavaScript with both hook-based and class-based APIs.
9
+
10
+ ## Features
11
+
12
+ - 🚀 **Simple API**: Easy-to-use hooks and classes
13
+ - 🔄 **Reactive**: React hooks with automatic re-renders
14
+ - 🏪 **Multi-store**: Support for multiple object stores
15
+ - 📦 **Batch operations**: Efficient bulk operations
16
+ - 🔧 **TypeScript**: Full type safety
17
+ - 🎯 **Promise-based**: Modern async/await support
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install use-idb-storage
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ### Default Instance (Simplest)
28
+
29
+ ```typescript
30
+ import { idb } from 'use-idb-storage';
31
+
32
+ // Use default store immediately
33
+ await idb.store.set('user', { name: 'John' });
34
+ const user = await idb.store.get('user');
35
+
36
+ // Use custom store
37
+ const customStore = await idb.get('settings');
38
+ await customStore.set('theme', 'dark');
39
+ ```
40
+
41
+ ### React Hook (Recommended for React apps)
42
+
43
+ ```tsx
44
+ import { useIDBStorage, IDBConfig } from 'use-idb-storage';
45
+
46
+ function App() {
47
+ return (
48
+ <IDBConfig database="my-app" version={1} store="data">
49
+ <MyComponent />
50
+ </IDBConfig>
51
+ );
52
+ }
53
+
54
+ function MyComponent() {
55
+ const [user, setUser, removeUser] = useIDBStorage({
56
+ key: 'current-user',
57
+ defaultValue: { name: '', email: '' }
58
+ });
59
+
60
+ return (
61
+ <div>
62
+ <input
63
+ value={user.name}
64
+ onChange={e => setUser({ ...user, name: e.target.value })}
65
+ />
66
+ <button onClick={() => removeUser()}>Clear</button>
67
+ </div>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ### Class-based API (For advanced use cases)
73
+
74
+ ```tsx
75
+ import { IDBStorage } from 'use-idb-storage';
76
+
77
+ // Create storage instance
78
+ const db = new IDBStorage({
79
+ database: 'my-app',
80
+ version: 1,
81
+ store: 'data'
82
+ });
83
+
84
+ // Get a store
85
+ const store = await db.get('my-store');
86
+ // Or use default store
87
+ const defaultStore = await db.store; // Convenience getter
88
+
89
+ // Basic operations
90
+ await store.set('user', { name: 'John', age: 30 });
91
+ const user = await store.get('user');
92
+ await store.delete('user');
93
+
94
+ // Batch operations
95
+ await store.setMany([
96
+ ['key1', 'value1'],
97
+ ['key2', 'value2']
98
+ ]);
99
+ const values = await store.getMany(['key1', 'key2']);
100
+
101
+ // Advanced operations
102
+ await store.update('counter', (val) => (val || 0) + 1);
103
+ const allKeys = await store.keys();
104
+ await store.clear();
105
+ ```
106
+
107
+ ## API Reference
108
+
109
+ ### Default Instance
110
+
111
+ ```typescript
112
+ import { idb } from 'use-idb-storage';
113
+ ```
114
+
115
+ A pre-configured `IDBStorage` instance using global defaults. Perfect for quick usage without setup.
116
+
117
+ - `idb.store: Promise<IDBStore>` - Default store instance
118
+ - `idb.get(storeName: string): Promise<IDBStore>` - Get a specific store
119
+ - `idb.drop(storeName: string): Promise<void>` - Clear a store
120
+ - `idb.close(): void` - Close the database connection
121
+
122
+ ### IDBStorage Class
123
+
124
+ Main entry point for database operations.
125
+
126
+ #### Constructor
127
+
128
+ ```typescript
129
+ new IDBStorage(config: IDBConfigValues)
130
+ ```
131
+
132
+ #### Properties
133
+
134
+ - `store: Promise<IDBStore>` - Default store instance (convenience getter)
135
+
136
+ #### Methods
137
+
138
+ - `get(storeName: string): Promise<IDBStore>` - Get a store instance
139
+ - `getStore(): Promise<IDBStore>` - Get the default store instance
140
+ - `drop(storeName: string): Promise<void>` - Clear a store (IndexedDB doesn't support dropping stores after creation)
141
+ - `close(): void` - Close the database connection
142
+
143
+ ### IDBStore Class
144
+
145
+ Provides operations for a specific object store.
146
+
147
+ #### Methods
148
+
149
+ **Single Operations:**
150
+ - `get<T>(key: string): Promise<T | undefined>`
151
+ - `set<T>(key: string, value: T): Promise<void>`
152
+ - `delete(key: string): Promise<void>`
153
+
154
+ **Batch Operations:**
155
+ - `getMany<T>(keys: string[]): Promise<(T | undefined)[]>`
156
+ - `setMany<T>(entries: [string, T][]): Promise<void>`
157
+ - `deleteMany(keys: string[]): Promise<void>`
158
+
159
+ **Utility Operations:**
160
+ - `update<T>(key: string, updater: (value: T | undefined) => T): Promise<void>`
161
+ - `clear(): Promise<void>`
162
+ - `keys(): Promise<string[]>`
163
+ - `values<T>(): Promise<T[]>`
164
+ - `entries<T>(): Promise<[string, T][]>`
165
+
166
+ ### useIDBStorage Hook
167
+
168
+ React hook for reactive IndexedDB state management.
169
+
170
+ ```typescript
171
+ const [value, setValue, removeValue] = useIDBStorage(options);
172
+ ```
173
+
174
+ #### Parameters
175
+
176
+ ```typescript
177
+ interface IDBStorageOptions<T> {
178
+ key: string;
179
+ defaultValue: T;
180
+ database?: string;
181
+ version?: number;
182
+ store?: string;
183
+ }
184
+ ```
185
+
186
+ #### Returns
187
+
188
+ - `value: T` - Current stored value
189
+ - `setValue: (value: T | ((prev: T) => T)) => Promise<void>` - Update function
190
+ - `removeValue: () => Promise<void>` - Remove function
191
+
192
+ ### IDBConfig Provider
193
+
194
+ React context provider for default configuration.
195
+
196
+ ```tsx
197
+ <IDBConfig database="my-app" version={1} store="data">
198
+ <App />
199
+ </IDBConfig>
200
+ ```
201
+
202
+ ### Configuration
203
+
204
+ ```typescript
205
+ interface IDBConfigValues {
206
+ database: string; // IndexedDB database name
207
+ version?: number; // Database version (default: 1)
208
+ store: string; // Default object store name
209
+ }
210
+ ```
211
+
212
+ ## Advanced Usage
213
+
214
+ ### Custom Stores
215
+
216
+ ```typescript
217
+ const db = new IDBStorage({ database: 'my-app', store: 'default' });
218
+
219
+ // Different stores for different data types
220
+ const users = await db.get('users');
221
+ const settings = await db.get('settings');
222
+
223
+ await users.set('user-1', { name: 'John' });
224
+ await settings.set('theme', 'dark');
225
+ ```
226
+
227
+ ### Batch Operations
228
+
229
+ ```typescript
230
+ const store = await db.getStore();
231
+
232
+ // Set multiple values
233
+ await store.setMany([
234
+ ['user1', { name: 'Alice' }],
235
+ ['user2', { name: 'Bob' }],
236
+ ['user3', { name: 'Charlie' }]
237
+ ]);
238
+
239
+ // Get multiple values
240
+ const users = await store.getMany(['user1', 'user2']);
241
+ ```
242
+
243
+ ### Reactive Updates
244
+
245
+ ```typescript
246
+ const [counter, setCounter] = useIDBStorage({
247
+ key: 'clicks',
248
+ defaultValue: 0
249
+ });
250
+
251
+ // This will automatically persist to IndexedDB
252
+ const increment = () => setCounter(count => count + 1);
253
+ ```
254
+
255
+ ### Migration Strategy
256
+
257
+ For database migrations, increment the version number:
258
+
259
+ ```typescript
260
+ // Version 1
261
+ <IDBConfig database="my-app" version={1} store="data">
262
+ <App />
263
+ </IDBConfig>
264
+
265
+ // Version 2 (with new features)
266
+ <IDBConfig database="my-app" version={2} store="data">
267
+ <App />
268
+ </IDBConfig>
269
+ ```
270
+
271
+ ## Browser Support
272
+
273
+ - Chrome 24+
274
+ - Firefox 16+
275
+ - Safari 10+
276
+ - Edge 12+
277
+
278
+ ## License
279
+
280
+ ISC
package/dist/index.d.ts CHANGED
@@ -1,25 +1,124 @@
1
- //#region src/config.d.ts
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+
3
+ //#region src/types.d.ts
2
4
  /**
3
- * Configure global defaults for IDBStorage.
5
+ * Database and store configuration values.
4
6
  */
5
- declare function configureIDBStorage(config: {
6
- database?: string;
7
- store?: string;
8
- }): void;
9
- //#endregion
10
- //#region src/types.d.ts
7
+ interface IDBConfigValues {
8
+ /** The name of the IndexedDB database */
9
+ database: string;
10
+ /** The version of the IndexedDB database */
11
+ version: number;
12
+ /** The name of the object store */
13
+ store: string;
14
+ }
11
15
  /**
12
16
  * Configuration options for IDBStorage.
13
17
  */
14
- interface IDBStorageOptions<T extends Record<string, any>> {
18
+ interface IDBStorageOptions<T> extends Partial<IDBConfigValues> {
15
19
  /** The key for the stored value */
16
20
  key: string;
17
21
  /** The default value if no value is found in IndexedDB */
18
22
  defaultValue: T;
19
- /** The name of the IndexedDB database (optional, defaults to global config) */
20
- database?: string;
21
- /** The name of the object store (optional, defaults to global config) */
22
- store?: string;
23
+ }
24
+ /**
25
+ * Props for the IDBConfig component.
26
+ */
27
+ interface IDBConfigProps extends Partial<IDBConfigValues> {
28
+ /** The children to render */
29
+ children: React.ReactNode;
30
+ }
31
+ /**
32
+ * Return type of the useIDBStorage hook
33
+ */
34
+ type UseIDBStorageReturn<T> = [T, (value: T | ((prevState: T) => T)) => Promise<void>, () => Promise<void>];
35
+ //#endregion
36
+ //#region src/idb-storage.d.ts
37
+ /**
38
+ * IDBStore provides key-value operations for a specific IndexedDB object store.
39
+ * Inspired by idb-keyval but with store-specific operations and better error handling.
40
+ */
41
+ declare class IDBStore {
42
+ private db;
43
+ private storeName;
44
+ constructor(db: IDBDatabase, storeName: string);
45
+ /**
46
+ * Get a value by key
47
+ */
48
+ get<T>(key: string): Promise<T | undefined>;
49
+ /**
50
+ * Set a value for a key
51
+ */
52
+ set<T>(key: string, value: T): Promise<void>;
53
+ /**
54
+ * Delete a key-value pair
55
+ */
56
+ delete(key: string): Promise<void>;
57
+ /**
58
+ * Get multiple values by keys
59
+ */
60
+ getMany<T>(keys: string[]): Promise<(T | undefined)[]>;
61
+ /**
62
+ * Set multiple key-value pairs
63
+ */
64
+ setMany<T>(entries: [string, T][]): Promise<void>;
65
+ /**
66
+ * Delete multiple keys
67
+ */
68
+ deleteMany(keys: string[]): Promise<void>;
69
+ /**
70
+ * Update a value using a transformer function
71
+ */
72
+ update<T>(key: string, updater: (value: T | undefined) => T): Promise<void>;
73
+ /**
74
+ * Clear all key-value pairs in the store
75
+ */
76
+ clear(): Promise<void>;
77
+ /**
78
+ * Get all keys in the store
79
+ */
80
+ keys(): Promise<string[]>;
81
+ /**
82
+ * Get all values in the store
83
+ */
84
+ values<T>(): Promise<T[]>;
85
+ /**
86
+ * Get all entries in the store
87
+ */
88
+ entries<T>(): Promise<[string, T][]>;
89
+ }
90
+ /**
91
+ * IDBStorage provides access to IndexedDB with multiple stores.
92
+ * Main entry point for database operations.
93
+ */
94
+ declare class IDBStorage {
95
+ private config;
96
+ private db;
97
+ private dbPromise;
98
+ constructor(config?: Partial<IDBConfigValues>);
99
+ /**
100
+ * Get or create the database connection
101
+ */
102
+ private getDB;
103
+ /**
104
+ * Get a store instance by name
105
+ */
106
+ get(storeName: string): Promise<IDBStore>;
107
+ /**
108
+ * Get the default store instance
109
+ */
110
+ get store(): Promise<IDBStore>;
111
+ /**
112
+ * Drop/delete a specific store
113
+ * Note: IndexedDB doesn't support dropping stores after creation
114
+ * This would require a version upgrade with store deletion
115
+ * For now, we'll clear the store instead
116
+ */
117
+ drop(storeName: string): Promise<void>;
118
+ /**
119
+ * Close the database connection
120
+ */
121
+ close(): void;
23
122
  }
24
123
  //#endregion
25
124
  //#region src/hook.d.ts
@@ -31,17 +130,20 @@ interface IDBStorageOptions<T extends Record<string, any>> {
31
130
  *
32
131
  * @example
33
132
  * ```tsx
34
- * // Configure global defaults (optional)
35
- * configureIDBStorage({
36
- * database: 'myApp',
37
- * store: 'data'
38
- * });
133
+ * // Option 1: Using the provider (recommended)
134
+ * <IDBConfig database="myApp" store="data">
135
+ * <App />
136
+ * </IDBConfig>
137
+ *
138
+ * // Option 2: Global config
139
+ * configureIDBStorage({ database: 'myApp', version: 2, store: 'data' });
39
140
  *
40
141
  * const [userData, setUserData, removeUserData] = useIDBStorage({
41
142
  * key: 'currentUser',
42
143
  * defaultValue: { name: '', email: '' },
43
- * // database: 'myApp', // optional, uses global default
44
- * // store: 'users' // optional, uses global default
144
+ * // database: 'myApp', // optional, uses context or global default
145
+ * // version: 2, // optional, uses context or global default (increment for upgrades)
146
+ * // store: 'users' // optional, uses context or global default
45
147
  * });
46
148
  *
47
149
  * // Update data
@@ -50,7 +152,44 @@ interface IDBStorageOptions<T extends Record<string, any>> {
50
152
  * // Remove data
51
153
  * await removeUserData();
52
154
  * ```
155
+ *
156
+ * @note The version parameter is used for IndexedDB database versioning.
157
+ * When you increment the version, it triggers database upgrades. You cannot
158
+ * "downgrade" to a lower version once a database exists with a higher version.
159
+ */
160
+ declare function useIDBStorage<T>(options: IDBStorageOptions<T>): UseIDBStorageReturn<T>;
161
+ //#endregion
162
+ //#region src/idb-context.d.ts
163
+ /**
164
+ * Provider component to configure default IDBStorage settings.
165
+ * This is optional - if not used, the hook will fall back to global config.
166
+ *
167
+ * @param props - Configuration props containing database, store, and children
168
+ * @returns The provider component wrapping children
169
+ *
170
+ * @example
171
+ * ```tsx
172
+ * <IDBConfig database="myApp" store="data">
173
+ * <App />
174
+ * </IDBConfig>
175
+ * ```
53
176
  */
54
- declare function useIDBStorage<T extends Record<string, any>>(options: IDBStorageOptions<T>): [T, (value: T | ((prevState: T) => T)) => Promise<void>, () => Promise<void>];
177
+ declare function IDBConfig({
178
+ children,
179
+ ...conf
180
+ }: IDBConfigProps): react_jsx_runtime0.JSX.Element;
181
+ //#endregion
182
+ //#region src/config.d.ts
183
+ /**
184
+ * Configure global defaults for IDBStorage.
185
+ */
186
+ declare function configureIDBStorage(config: Partial<IDBConfigValues>): void;
187
+ /**
188
+ * Get the current global configuration.
189
+ */
190
+ declare function getGlobalConfig(): IDBConfigValues;
191
+ //#endregion
192
+ //#region src/index.d.ts
193
+ declare const idb: IDBStorage;
55
194
  //#endregion
56
- export { configureIDBStorage, useIDBStorage };
195
+ export { IDBConfig, type IDBConfigProps, type IDBConfigValues, IDBStorage, type IDBStorageOptions, IDBStore, type UseIDBStorageReturn, configureIDBStorage, getGlobalConfig, idb, useIDBStorage };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import*as e from"react";let t={database:`sohanemon-idb`,store:`default`};function n(e){t={...t,...e}}function r(){return{...t}}const i=(e,t,n=1)=>new Promise((r,i)=>{let a=indexedDB.open(e,n);a.onerror=()=>i(a.error),a.onsuccess=()=>r(a.result),a.onupgradeneeded=e=>{let n=e.target.result;n.objectStoreNames.contains(t)||n.createObjectStore(t)}});function a(e,t,n){return new Promise((r,i)=>{let a=e.transaction([t],`readonly`).objectStore(t).get(n);a.onerror=()=>i(a.error),a.onsuccess=()=>r(a.result)})}const o=(e,t,n,r)=>new Promise((i,a)=>{let o=e.transaction([t],`readwrite`).objectStore(t).put(r,n);o.onerror=()=>a(o.error),o.onsuccess=()=>i()}),s=(e,t,n)=>new Promise((r,i)=>{let a=e.transaction([t],`readwrite`).objectStore(t).delete(n);a.onerror=()=>i(a.error),a.onsuccess=()=>r()});function c(t){let{key:n,defaultValue:c,database:l=r().database,store:u=r().store}=t,[d,f]=e.useState(c),[p,m]=e.useState(null);return e.useEffect(()=>{let e=!0;return(async()=>{try{let t=await i(l,u);e&&m(t)}catch(e){console.error(`Failed to open IndexedDB:`,e)}})(),()=>{e=!1}},[l,u]),e.useEffect(()=>{p&&(async()=>{try{let e=await a(p,u,n);e!==void 0&&f(e)}catch(e){console.error(`Failed to load value from IndexedDB:`,e)}})()},[p,u,n]),[d,e.useCallback(async e=>{if(!p)return;let t=typeof e==`function`?e(d):e;f(t);try{await o(p,u,n,t)}catch(e){console.error(`Failed to save value to IndexedDB:`,e)}},[p,u,n,d]),e.useCallback(async()=>{if(p){f(c);try{await s(p,u,n)}catch(e){console.error(`Failed to remove value from IndexedDB:`,e)}}},[p,u,n,c])]}export{n as configureIDBStorage,c as useIDBStorage};
1
+ import*as e from"react";import{Fragment as t,jsx as n}from"react/jsx-runtime";const r=new Map;function i(){return typeof indexedDB<`u`}function a(e,t,n=1,a){if(!i())throw Error(`IndexedDB is not available in this environment`);let s=`${e}:${n}:${t}`;if(r.has(s))return r.get(s);let c=o(e,t,n,a);return r.set(s,c),c}function o(e,t,n,i){return new Promise((a,s)=>{let c=indexedDB.open(e,n);c.onerror=()=>{r.delete(`${e}:${n}:${t}`),s(c.error)},c.onupgradeneeded=e=>{let n=e.target.result;n.objectStoreNames.contains(t)||n.createObjectStore(t)},c.onsuccess=()=>{let s=c.result;if(!s.objectStoreNames.contains(t)){s.close(),r.delete(`${e}:${n}:${t}`),a(o(e,t,n+1,i));return}s.onversionchange=()=>{s.close();for(let[t]of r)t.startsWith(`${e}:`)&&r.delete(t);i?.()},a(s)}})}function s(e,t,n){return new Promise((r,i)=>{try{let a=e.transaction([t],`readonly`).objectStore(t).get(n);a.onerror=()=>i(a.error),a.onsuccess=()=>r(a.result)}catch(e){i(e)}})}function c(e,t,n,r){return new Promise((i,a)=>{try{let o=e.transaction([t],`readwrite`).objectStore(t).put(r,n);o.onerror=()=>a(o.error),o.onsuccess=()=>i()}catch(e){a(e)}})}function l(e,t,n){return new Promise((r,i)=>{try{let a=e.transaction([t],`readwrite`).objectStore(t).delete(n);a.onerror=()=>i(a.error),a.onsuccess=()=>r()}catch(e){i(e)}})}var u=class{db;storeName;constructor(e,t){this.db=e,this.storeName=t}async get(e){try{return await s(this.db,this.storeName,e)}catch(t){throw console.error(`Failed to get value for key "${e}":`,t),t}}async set(e,t){try{await c(this.db,this.storeName,e,t)}catch(t){throw console.error(`Failed to set value for key "${e}":`,t),t}}async delete(e){try{await l(this.db,this.storeName,e)}catch(t){throw console.error(`Failed to delete key "${e}":`,t),t}}async getMany(e){try{let t=e.map(e=>this.get(e));return await Promise.all(t)}catch(e){throw console.error(`Failed to get multiple values:`,e),e}}async setMany(e){try{let t=this.db.transaction([this.storeName],`readwrite`).objectStore(this.storeName),n=e.map(([e,n])=>new Promise((r,i)=>{let a=t.put(n,e);a.onerror=()=>i(a.error),a.onsuccess=()=>r()}));await Promise.all(n)}catch(e){throw console.error(`Failed to set multiple values:`,e),e}}async deleteMany(e){try{let t=this.db.transaction([this.storeName],`readwrite`).objectStore(this.storeName),n=e.map(e=>new Promise((n,r)=>{let i=t.delete(e);i.onerror=()=>r(i.error),i.onsuccess=()=>n()}));await Promise.all(n)}catch(e){throw console.error(`Failed to delete multiple keys:`,e),e}}async update(e,t){try{let n=t(await this.get(e));await this.set(e,n)}catch(t){throw console.error(`Failed to update value for key "${e}":`,t),t}}async clear(){try{let e=this.db.transaction([this.storeName],`readwrite`).objectStore(this.storeName).clear();await new Promise((t,n)=>{e.onerror=()=>n(e.error),e.onsuccess=()=>t()})}catch(e){throw console.error(`Failed to clear store:`,e),e}}async keys(){try{let e=this.db.transaction([this.storeName],`readonly`).objectStore(this.storeName).getAllKeys();return await new Promise((t,n)=>{e.onerror=()=>n(e.error),e.onsuccess=()=>t(Array.from(e.result))})}catch(e){throw console.error(`Failed to get keys:`,e),e}}async values(){try{let e=this.db.transaction([this.storeName],`readonly`).objectStore(this.storeName).getAll();return await new Promise((t,n)=>{e.onerror=()=>n(e.error),e.onsuccess=()=>t(e.result)})}catch(e){throw console.error(`Failed to get values:`,e),e}}async entries(){try{let e=this.db.transaction([this.storeName],`readonly`).objectStore(this.storeName).openCursor(),t=[];return new Promise((n,r)=>{e.onerror=()=>r(e.error),e.onsuccess=()=>{let r=e.result;r?(t.push([r.key,r.value]),r.continue()):n(t)}})}catch(e){throw console.error(`Failed to get entries:`,e),e}}},d=class{config;db=null;dbPromise=null;constructor(e){this.config={database:`sohanemon-idb`,version:1,store:`default`,...e},this.config.version=Math.max(1,Math.floor(this.config.version))}async getDB(){return this.db?this.db:this.dbPromise?this.dbPromise:(this.dbPromise=a(this.config.database,this.config.store,this.config.version,()=>{this.db=null,this.dbPromise=null}),this.db=await this.dbPromise,this.dbPromise=null,this.db)}async get(e){return new u(await this.getDB(),e)}get store(){return this.get(this.config.store)}async drop(e){await(await this.get(e)).clear()}close(){this.db&&=(this.db.close(),null)}};let f={database:`sohanemon-idb`,version:1,store:`default`};function p(e){f={...f,...e}}function m(){return{...f}}function h(t){let{key:n,defaultValue:r,...a}=t,o={...m(),...a};o.version=Math.max(1,Math.floor(o.version||1));let[s,c]=e.useState(r),[l,u]=e.useState(null),[f,p]=e.useState(!1),h=e.useRef(null),g=e.useRef(null),_=e.useRef([]);return e.useEffect(()=>{let e=!0;return(async()=>{try{if(!i()){console.warn(`IndexedDB is not available, using default values only`);return}u(null),h.current&&(h.current.close(),h.current=null,g.current=null);let t=new d(o),r=await t.get(o.store);if(!e)return;h.current=t,g.current=r;let a=await r.get(n);if(e&&a!==void 0&&c(a),e){p(!0);let e=_.current;_.current=[];for(let t of e)t().catch(e=>{console.error(`Failed to process pending update:`,e)})}}catch(t){e&&(u(t instanceof Error?t:Error(`Failed to load from IndexedDB`)),console.error(`Failed to initialize IDBStorage:`,t))}})(),()=>{e=!1,h.current&&(h.current.close(),h.current=null,g.current=null)}},[o.database,o.version,o.store,n]),[s,e.useCallback(async e=>{if(l){console.warn(`Cannot update value due to previous error:`,l);return}let t=typeof e==`function`?e(s):e;c(t);let r=async()=>{if(g.current)await g.current.set(n,t);else throw Error(`Store not initialized`)};if(f)try{await r()}catch(e){throw c(s),u(e instanceof Error?e:Error(`Failed to save to IndexedDB`)),console.error(`Failed to save value to IndexedDB:`,e),e}else _.current.push(r)},[s,n,l,f]),e.useCallback(async()=>{if(l){console.warn(`Cannot remove value due to previous error:`,l);return}c(r);let e=async()=>{if(g.current)await g.current.delete(n);else throw Error(`Store not initialized`)};if(f)try{await e()}catch(e){throw c(s),u(e instanceof Error?e:Error(`Failed to remove from IndexedDB`)),console.error(`Failed to remove value from IndexedDB:`,e),e}else _.current.push(e)},[s,n,r,l,f])]}function g({children:r,...i}){return e.useEffect(()=>{p(i)},[i.database,i.version,i.store]),n(t,{children:r})}const _=new d;export{g as IDBConfig,d as IDBStorage,u as IDBStore,p as configureIDBStorage,m as getGlobalConfig,_ as idb,h as useIDBStorage};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-idb-storage",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "author": "Sohan Emon <sohanemon@outlook.com>",
5
5
  "description": "",
6
6
  "type": "module",
@@ -19,6 +19,8 @@
19
19
  "test:list": "vitest list",
20
20
  "test:log": "vitest run --reporter verbose",
21
21
  "test:ui": "vitest --ui",
22
+ "build:playground": "vite build --config playground/vite.config.ts",
23
+ "playground": "tsdown && vite build --config playground/vite.config.ts && vite --config playground/vite.config.ts",
22
24
  "check": "biome check .",
23
25
  "fix": "biome check --diagnostic-level=error --write .",
24
26
  "lint": "biome lint .",
@@ -53,6 +55,7 @@
53
55
  "@types/react-dom": "^19.2.3",
54
56
  "@vitejs/plugin-react": "^5.1.1",
55
57
  "@vitest/ui": "^4.0.14",
58
+ "fake-indexeddb": "^6.2.5",
56
59
  "jsdom": "^27.2.0",
57
60
  "tsdown": "^0.16.4",
58
61
  "typescript": "^5.9.3",