use-idb-storage 0.0.2 → 0.0.4
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 +274 -0
- package/dist/index.d.ts +161 -22
- package/dist/index.js +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -4,3 +4,277 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
6
6
|

|
|
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
|
-
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
5
|
+
* Database and store configuration values.
|
|
4
6
|
*/
|
|
5
|
-
|
|
6
|
-
database
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
* //
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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
|
-
* //
|
|
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
|
|
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";
|
|
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.
|
|
3
|
+
"version": "0.0.4",
|
|
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",
|