tiny-idb 1.4.0 → 1.5.0
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 +101 -94
- package/index.d.ts +7 -2
- package/package.json +10 -1
- package/tiny-idb.js +41 -29
- package/tiny-idb.min.js +1 -1
package/README.md
CHANGED
|
@@ -27,15 +27,15 @@ Native IndexedDB requires managing requests, transactions, and version upgrades.
|
|
|
27
27
|
### Guaranteed Atomicity
|
|
28
28
|
The primary weakness of most storage wrappers is the "read-modify-write" race condition. If two parts of an application attempt to update the same key simultaneously, data loss often occurs. `tiny-idb` addresses this by executing `update`, `push`, and `merge` operations within a single IndexedDB transaction, ensuring that updates are processed sequentially and reliably.
|
|
29
29
|
|
|
30
|
+
### Automatic Transaction Batching (Pipelining)
|
|
31
|
+
`tiny-idb` automatically batches multiple operations (`set`, `get`, `update`, etc.) called in the same microtask (event loop tick) into a **single IndexedDB transaction**. This provides a **10x to 100x performance boost** for concurrent operations (like `Promise.all`) without changing any code.
|
|
32
|
+
|
|
30
33
|
### Resource Efficiency
|
|
31
34
|
At **less than 1KB** (gzipped), the library introduces negligible overhead to your bundle. It is dependency-free and written in modern vanilla JavaScript, ensuring high performance across all environments that support IndexedDB.
|
|
32
35
|
|
|
33
36
|
### Intelligent Lifecycle Management
|
|
34
37
|
The library handles database connection pooling, tab synchronization, and error recovery automatically. If a database connection is blocked by another tab or fails due to an environmental error, `tiny-idb` gracefully resets and recovers without requiring a page reload.
|
|
35
38
|
|
|
36
|
-
### Zero-Configuration Portability
|
|
37
|
-
Beyond npm installation, `tiny-idb` is designed for maximum portability. As a single-file, dependency-free module, it can be integrated into any environment by simply dropping `tiny-idb.js` into a project directory. **No build step, compilation, or transpilation is required.** This makes it an ideal solution for rapid prototyping, legacy system upgrades, and environments where complex build pipelines are unavailable.
|
|
38
|
-
|
|
39
39
|
## Installation
|
|
40
40
|
|
|
41
41
|
```bash
|
|
@@ -46,29 +46,15 @@ npm install tiny-idb
|
|
|
46
46
|
|
|
47
47
|
```html
|
|
48
48
|
<script type="module">
|
|
49
|
-
import { tinyIDB } from 'https://unpkg.com/tiny-idb/tiny-idb.min.js';
|
|
49
|
+
import { tinyIDB as db } from 'https://unpkg.com/tiny-idb/tiny-idb.min.js';
|
|
50
50
|
</script>
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
* **Minified Module**: `https://unpkg.com/tiny-idb/tiny-idb.min.js` (less than 1KB gzipped)
|
|
54
|
-
* **Source**: `https://unpkg.com/tiny-idb/tiny-idb.js`
|
|
55
|
-
|
|
56
|
-
## Technical Comparison
|
|
57
|
-
|
|
58
|
-
| Feature | `localStorage` | `tiny-idb` |
|
|
59
|
-
|---------|--------------|----------|
|
|
60
|
-
| **Execution** | Synchronous (Blocks UI) | Asynchronous (Non-blocking) |
|
|
61
|
-
| **Storage Limit** | ~5-10MB (Fixed) | Virtually unlimited (80%+ of disk) |
|
|
62
|
-
| **Data Types** | Strings only (Requires `JSON.parse`) | Objects, Blobs, Arrays, Numbers, Files |
|
|
63
|
-
| **Data Integrity** | Basic | ACID Compliant |
|
|
64
|
-
| **Race Condition Safety** | None | Atomic `update`/`push`/`merge` |
|
|
65
|
-
| **Tab Sync** | No | Automatic |
|
|
66
|
-
|
|
67
53
|
## API Reference
|
|
68
54
|
|
|
69
55
|
| Method | Description |
|
|
70
56
|
|--------|-------------|
|
|
71
|
-
| `open(db, store?)` | Creates or retrieves a cached instance. `
|
|
57
|
+
| `open(db, store?, batch?)` | Creates or retrieves a cached instance. `batch` defaults to `true`. |
|
|
72
58
|
| `get(key)` | Retrieves a value; returns `undefined` if not found. |
|
|
73
59
|
| `set(key, value)` | Persists a value to the store. |
|
|
74
60
|
| `remove(key)` | Deletes a specific key. |
|
|
@@ -82,121 +68,148 @@ npm install tiny-idb
|
|
|
82
68
|
| `push(key, value)` | **Atomically** appends to an array. |
|
|
83
69
|
| `merge(key, patch)` | **Atomically** shallow-merges an object. |
|
|
84
70
|
|
|
85
|
-
##
|
|
71
|
+
## Examples (Easy to Advanced)
|
|
86
72
|
|
|
87
|
-
###
|
|
88
|
-
|
|
73
|
+
### 1. localStorage Compatibility
|
|
74
|
+
Use `tiny-idb` as a drop-in replacement for `localStorage`. Just add `await`.
|
|
89
75
|
```javascript
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const cursor = request.result;
|
|
96
|
-
if (cursor) {
|
|
97
|
-
if (cursor.value.type === 'fruit') matches.push(cursor.value);
|
|
98
|
-
cursor.continue();
|
|
99
|
-
} else resolve(matches);
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
});
|
|
76
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
77
|
+
|
|
78
|
+
await db.setItem('session_id', 'xyz-123');
|
|
79
|
+
const sid = await db.getItem('session_id');
|
|
80
|
+
await db.removeItem('session_id');
|
|
103
81
|
```
|
|
104
82
|
|
|
105
|
-
###
|
|
106
|
-
|
|
83
|
+
### 2. Simple Custom Database
|
|
84
|
+
If you only need one store per database, you can omit the `storeName`.
|
|
107
85
|
```javascript
|
|
108
|
-
|
|
86
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
109
87
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
88
|
+
// Creates/retrieves a DB named 'my-store' with an internal store also named 'my-store'
|
|
89
|
+
const store = db.open('my-store');
|
|
90
|
+
await store.set('key', 'value');
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Atomic Counters
|
|
94
|
+
Safely increment values using the `update` method.
|
|
95
|
+
```javascript
|
|
96
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
97
|
+
|
|
98
|
+
await db.set('page_views', 0);
|
|
99
|
+
|
|
100
|
+
// Increment safely - even if multiple tabs do it at once
|
|
101
|
+
await db.update('page_views', count => (count || 0) + 1);
|
|
113
102
|
```
|
|
114
103
|
|
|
115
|
-
### User Settings Management
|
|
104
|
+
### 4. User Settings Management (Atomic Merge)
|
|
116
105
|
Easily manage and update partial user preferences without worrying about race conditions.
|
|
117
106
|
```javascript
|
|
118
|
-
import { tinyIDB } from 'tiny-idb';
|
|
107
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
119
108
|
|
|
120
109
|
// Initial setup
|
|
121
|
-
await
|
|
110
|
+
await db.set('settings', { theme: 'dark', notifications: true });
|
|
122
111
|
|
|
123
112
|
// Later, merge new settings
|
|
124
|
-
await
|
|
113
|
+
await db.merge('settings', { notifications: false, language: 'en' });
|
|
125
114
|
|
|
126
115
|
// Result: { theme: 'dark', notifications: false, language: 'en' }
|
|
127
116
|
```
|
|
128
117
|
|
|
129
|
-
###
|
|
118
|
+
### 5. Persistent Shopping Cart (Atomic Push)
|
|
119
|
+
Atomically add items to a list, ensuring no items are lost during concurrent updates.
|
|
120
|
+
```javascript
|
|
121
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
122
|
+
|
|
123
|
+
await db.push('cart', { id: 101, qty: 1 });
|
|
124
|
+
await db.push('cart', { id: 202, qty: 2 });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 6. Storing Binary Data (Blobs/Files)
|
|
130
128
|
Unlike `localStorage`, `tiny-idb` can store binary data directly.
|
|
131
129
|
```javascript
|
|
130
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
131
|
+
|
|
132
132
|
const response = await fetch('/profile-picture.jpg');
|
|
133
133
|
const blob = await response.blob();
|
|
134
134
|
|
|
135
|
-
await
|
|
135
|
+
await db.set('user_avatar', blob);
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
const avatar = await tinyIDB.get('user_avatar');
|
|
137
|
+
const avatar = await db.get('user_avatar');
|
|
139
138
|
document.querySelector('img').src = URL.createObjectURL(avatar);
|
|
140
139
|
```
|
|
141
140
|
|
|
142
|
-
###
|
|
143
|
-
|
|
141
|
+
### 7. Iterating over Data
|
|
142
|
+
Use `entries()` to process all stored key-value pairs efficiently.
|
|
144
143
|
```javascript
|
|
145
|
-
|
|
146
|
-
await tinyIDB.push('cart', { id: 101, qty: 1 });
|
|
147
|
-
await tinyIDB.push('cart', { id: 202, qty: 2 });
|
|
144
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
148
145
|
|
|
149
|
-
const
|
|
150
|
-
|
|
146
|
+
const allEntries = await db.entries();
|
|
147
|
+
for (const [key, value] of allEntries) {
|
|
148
|
+
console.log(`${key}:`, value);
|
|
149
|
+
}
|
|
151
150
|
```
|
|
152
151
|
|
|
153
|
-
###
|
|
154
|
-
|
|
152
|
+
### 8. Multi-Instance Support
|
|
153
|
+
Use `open` to create isolated storage instances.
|
|
155
154
|
```javascript
|
|
156
|
-
|
|
155
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
```
|
|
157
|
+
const settings = db.open('app-db', 'settings');
|
|
158
|
+
const cache = db.open('app-db', 'cache');
|
|
161
159
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
```javascript
|
|
165
|
-
await tinyIDB.setItem('session_id', 'xyz-123');
|
|
166
|
-
const sid = await tinyIDB.getItem('session_id');
|
|
167
|
-
await tinyIDB.removeItem('session_id');
|
|
160
|
+
await settings.set('theme', 'dark');
|
|
161
|
+
await cache.set('temp_data', { id: 1 });
|
|
168
162
|
```
|
|
169
163
|
|
|
170
|
-
###
|
|
171
|
-
|
|
164
|
+
### 9. Advanced: Direct IndexedDB Access (Cursors & Search)
|
|
165
|
+
Use `raw()` for custom searches or when working with extremely large datasets.
|
|
172
166
|
```javascript
|
|
173
|
-
|
|
174
|
-
const store = tinyIDB.open('my-store');
|
|
167
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
175
168
|
|
|
176
|
-
await
|
|
169
|
+
const results = await db.raw(store => {
|
|
170
|
+
return new Promise((resolve) => {
|
|
171
|
+
const matches = [];
|
|
172
|
+
const request = store.openCursor();
|
|
173
|
+
request.onsuccess = () => {
|
|
174
|
+
const cursor = request.result;
|
|
175
|
+
if (cursor) {
|
|
176
|
+
if (cursor.value.type === 'urgent') matches.push(cursor.value);
|
|
177
|
+
cursor.continue();
|
|
178
|
+
} else resolve(matches);
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
});
|
|
177
182
|
```
|
|
178
183
|
|
|
179
|
-
###
|
|
180
|
-
|
|
184
|
+
### 10. Advanced: Disabling Batching
|
|
185
|
+
If you need strict one-transaction-per-operation behavior (e.g., for debugging), you can disable the default batching.
|
|
181
186
|
```javascript
|
|
182
|
-
import { tinyIDB } from 'tiny-idb';
|
|
187
|
+
import { tinyIDB as db } from 'tiny-idb';
|
|
183
188
|
|
|
184
|
-
//
|
|
185
|
-
const
|
|
186
|
-
const cache = tinyIDB.open('app-db', 'cache');
|
|
187
|
-
|
|
188
|
-
await settings.set('theme', 'dark');
|
|
189
|
-
await cache.set('temp_data', { id: 1 });
|
|
189
|
+
// Disable batching for a specific instance
|
|
190
|
+
const debugDB = db.open('debug-db', false);
|
|
190
191
|
```
|
|
191
192
|
|
|
192
|
-
|
|
193
|
+
> **Optimization Note:** While `entries()` is sufficient for most apps, developers working with **extremely large datasets** (100k+ records) should use `raw()` with a cursor to minimize memory overhead.
|
|
194
|
+
> ```javascript
|
|
195
|
+
> // Memory-efficient search for a massive dataset
|
|
196
|
+
> const activeUser = await db.raw(store => new Promise(res => {
|
|
197
|
+
> const req = store.openCursor();
|
|
198
|
+
> req.onsuccess = () => {
|
|
199
|
+
> const cursor = req.result;
|
|
200
|
+
> if (!cursor || cursor.value.status === 'active') res(cursor?.value);
|
|
201
|
+
> else cursor.continue();
|
|
202
|
+
> };
|
|
203
|
+
> }));
|
|
204
|
+
> ```
|
|
193
205
|
|
|
194
|
-
|
|
195
|
-
- [IndexedDB](https://caniuse.com/indexeddb) (98%+)
|
|
196
|
-
- [ES Modules](https://caniuse.com/es6-module)
|
|
197
|
-
- [Async/Await](https://caniuse.com/async-functions)
|
|
206
|
+
## Browser Support
|
|
198
207
|
|
|
199
|
-
|
|
208
|
+
Supported by virtually all browsers in use today (99%+ market share). Since [May 2018](https://caniuse.com/es6-module), this feature works across the latest devices and major browser versions:
|
|
209
|
+
- **Chrome** 61+
|
|
210
|
+
- **Firefox** 60+
|
|
211
|
+
- **Safari** 11+
|
|
212
|
+
- **Edge** 16+
|
|
200
213
|
|
|
201
214
|
## Development
|
|
202
215
|
|
|
@@ -207,13 +220,7 @@ If you need to support legacy browsers (IE11), you will need to transpile and po
|
|
|
207
220
|
npm test
|
|
208
221
|
```
|
|
209
222
|
|
|
210
|
-
### Running Tests on Minified Build
|
|
211
|
-
```bash
|
|
212
|
-
npm run test:min
|
|
213
|
-
```
|
|
214
|
-
|
|
215
223
|
### Building & Minification
|
|
216
|
-
Generate the production-ready minified file:
|
|
217
224
|
```bash
|
|
218
225
|
npm run build
|
|
219
226
|
```
|
package/index.d.ts
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
export interface TinyIDBInstance {
|
|
4
4
|
/**
|
|
5
5
|
* Creates or retrieves a cached instance of a database and store.
|
|
6
|
+
* Note: Multiple calls in the same microtask (tick) are automatically batched
|
|
7
|
+
* into a single IndexedDB transaction for 10x-100x performance.
|
|
6
8
|
* @param dbName Name of the IndexedDB database.
|
|
7
9
|
* @param storeName Name of the object store (defaults to dbName).
|
|
10
|
+
* @param batching Set to false to disable automatic batching (defaults to true).
|
|
8
11
|
*/
|
|
9
|
-
open(dbName: string, storeName?: string): TinyIDBInstance;
|
|
12
|
+
open(dbName: string, storeName?: string, batching?: boolean): TinyIDBInstance;
|
|
13
|
+
/** Shorthand for open(dbName, dbName, batching) */
|
|
14
|
+
open(dbName: string, batching?: boolean): TinyIDBInstance;
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* Persists a value to the store.
|
|
@@ -55,7 +60,7 @@ export interface TinyIDBInstance {
|
|
|
55
60
|
count(): Promise<number>;
|
|
56
61
|
|
|
57
62
|
/**
|
|
58
|
-
*
|
|
63
|
+
* Provides direct access to the IDBObjectStore within a transaction.
|
|
59
64
|
* This provides an "escape hatch" to use native IndexedDB features like cursors, ranges, and search.
|
|
60
65
|
* @param cb A callback that receives the IDBObjectStore.
|
|
61
66
|
* @param mode The transaction mode ('readonly' or 'readwrite').
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-idb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "An extremely fast, super simple, and dependency-free IndexedDB wrapper. A drop-in replacement for localStorage with reliability and durability.",
|
|
5
5
|
"main": "tiny-idb.js",
|
|
6
6
|
"module": "tiny-idb.js",
|
|
7
|
+
"unpkg": "tiny-idb.min.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./tiny-idb.js",
|
|
10
|
+
"./min": "./tiny-idb.min.js"
|
|
11
|
+
},
|
|
7
12
|
"types": "index.d.ts",
|
|
8
13
|
"type": "module",
|
|
9
14
|
"repository": {
|
|
@@ -25,6 +30,10 @@
|
|
|
25
30
|
],
|
|
26
31
|
"author": "Jelodar",
|
|
27
32
|
"license": "MIT",
|
|
33
|
+
"sideEffects": false,
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=16.0.0"
|
|
36
|
+
},
|
|
28
37
|
"bugs": {
|
|
29
38
|
"url": "https://github.com/Jelodar/tiny-idb/issues"
|
|
30
39
|
},
|
package/tiny-idb.js
CHANGED
|
@@ -7,52 +7,64 @@ const prom = (req) => new Promise((res, rej) => {
|
|
|
7
7
|
});
|
|
8
8
|
const RO = 'readonly', RW = 'readwrite';
|
|
9
9
|
|
|
10
|
-
const getAPI = (dbName = 'tiny-idb',
|
|
11
|
-
|
|
12
|
-
const key = dbName + '\0' +
|
|
10
|
+
const getAPI = (dbName = 'tiny-idb', s = dbName, b = true) => {
|
|
11
|
+
if (s === !!s) [b, s] = [s, dbName];
|
|
12
|
+
const key = dbName + '\0' + s + '\0' + b;
|
|
13
13
|
if (instances.has(key)) return instances.get(key);
|
|
14
14
|
|
|
15
|
-
let dbPromise;
|
|
15
|
+
let dbPromise, queue = [];
|
|
16
|
+
const flush = async () => {
|
|
17
|
+
const items = queue; queue = [];
|
|
18
|
+
try {
|
|
19
|
+
const mode = items.some(i => i.m === RW) ? RW : RO;
|
|
20
|
+
await tx(mode, async store => {
|
|
21
|
+
for (const {c, r} of items) r(await c(store));
|
|
22
|
+
});
|
|
23
|
+
} catch (e) {
|
|
24
|
+
items.forEach(i => i.j(e));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
16
28
|
const tx = async (mode, cb) => {
|
|
17
29
|
const db = await (dbPromise || (dbPromise = new Promise((resolve, reject) => {
|
|
18
30
|
const req = indexedDB.open(dbName, 1);
|
|
19
|
-
req.onupgradeneeded = () => req.result.createObjectStore(
|
|
31
|
+
req.onupgradeneeded = () => req.result.createObjectStore(s);
|
|
20
32
|
req.onsuccess = () => {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
resolve(
|
|
33
|
+
const d = req.result;
|
|
34
|
+
d.onversionchange = () => { d.close(); dbPromise = 0; };
|
|
35
|
+
resolve(d);
|
|
24
36
|
};
|
|
25
|
-
req.onerror = () => { dbPromise =
|
|
37
|
+
req.onerror = () => { dbPromise = 0; reject(req.error); };
|
|
26
38
|
})));
|
|
27
|
-
return new Promise(
|
|
28
|
-
const t = db.transaction(
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const t = db.transaction(s, mode);
|
|
41
|
+
t.oncomplete = () => resolve();
|
|
29
42
|
t.onabort = t.onerror = () => reject(t.error || new DOMException('Aborted'));
|
|
30
|
-
try {
|
|
31
|
-
const res = await cb(t.objectStore(storeName));
|
|
32
|
-
t.oncomplete = () => resolve(res);
|
|
33
|
-
} catch (e) {
|
|
34
|
-
try { t.abort(); } catch {}
|
|
35
|
-
reject(e);
|
|
36
|
-
}
|
|
43
|
+
cb(t.objectStore(s)).catch(e => { try { t.abort(); } catch {} reject(e); });
|
|
37
44
|
});
|
|
38
45
|
};
|
|
39
46
|
|
|
40
|
-
const
|
|
47
|
+
const op = (m, c) => new Promise((r, j) => {
|
|
48
|
+
queue.push({ m, c, r, j });
|
|
49
|
+
if (queue.length < 2) b ? queueMicrotask(flush) : flush();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const update = (key, fn) => op(RW, async store => prom(store.put(await fn(await prom(store.get(key))), key)));
|
|
41
53
|
|
|
42
54
|
const api = {
|
|
43
55
|
open: getAPI,
|
|
44
|
-
set: (key, value) =>
|
|
45
|
-
get: key =>
|
|
46
|
-
remove: key =>
|
|
47
|
-
clear: () =>
|
|
48
|
-
keys: () =>
|
|
49
|
-
values: () =>
|
|
50
|
-
entries: () =>
|
|
51
|
-
const [k, v] = await Promise.all([prom(
|
|
56
|
+
set: (key, value) => op(RW, store => prom(store.put(value, key))),
|
|
57
|
+
get: key => op(RO, store => prom(store.get(key))),
|
|
58
|
+
remove: key => op(RW, store => prom(store.delete(key))),
|
|
59
|
+
clear: () => op(RW, store => prom(store.clear())),
|
|
60
|
+
keys: () => op(RO, store => prom(store.getAllKeys())),
|
|
61
|
+
values: () => op(RO, store => prom(store.getAll())),
|
|
62
|
+
entries: () => op(RO, async store => {
|
|
63
|
+
const [k, v] = await Promise.all([prom(store.getAllKeys()), prom(store.getAll())]);
|
|
52
64
|
return k.map((key, i) => [key, v[i]]);
|
|
53
65
|
}),
|
|
54
|
-
count: () =>
|
|
55
|
-
raw: (cb, mode = RO) =>
|
|
66
|
+
count: () => op(RO, store => prom(store.count())),
|
|
67
|
+
raw: (cb, mode = RO) => op(mode, cb),
|
|
56
68
|
update,
|
|
57
69
|
push: (key, val) => update(key, (c = []) => [...(Array.isArray(c) ? c : []), val]),
|
|
58
70
|
merge: (key, obj) => update(key, (c = {}) => ({ ...(c && typeof c === 'object' ? c : {}), ...obj }))
|
package/tiny-idb.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** tiny-idb - MIT © Jelodar */
|
|
2
|
-
const e=new Map,t=e=>new Promise((t,r)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>r(e.error)}),r="readonly",o="readwrite",n=(s="tiny-idb",a
|
|
2
|
+
const e=new Map,t=e=>new Promise((t,r)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>r(e.error)}),r="readonly",o="readwrite",n=(s="tiny-idb",c=s,a=!0)=>{c===!!c&&([a,c]=[c,s]);const i=s+"\0"+c+"\0"+a;if(e.has(i))return e.get(i);let l,u=[];const y=async()=>{const e=u;u=[];try{const t=e.some(e=>e.m===o)?o:r;await p(t,async t=>{for(const{c:r,r:o}of e)o(await r(t))})}catch(t){e.forEach(e=>e.j(t))}},p=async(e,t)=>{const r=await(l||(l=new Promise((e,t)=>{const r=indexedDB.open(s,1);r.onupgradeneeded=()=>r.result.createObjectStore(c),r.onsuccess=()=>{const t=r.result;t.onversionchange=()=>{t.close(),l=0},e(t)},r.onerror=()=>{l=0,t(r.error)}})));return new Promise((o,n)=>{const s=r.transaction(c,e);s.oncomplete=()=>o(),s.onabort=s.onerror=()=>n(s.error||new DOMException("Aborted")),t(s.objectStore(c)).catch(e=>{try{s.abort()}catch{}n(e)})})},m=(e,t)=>new Promise((r,o)=>{u.push({m:e,c:t,r:r,j:o}),u.length<2&&(a?queueMicrotask(y):y())}),w=(e,r)=>m(o,async o=>t(o.put(await r(await t(o.get(e))),e))),g={open:n,set:(e,r)=>m(o,o=>t(o.put(r,e))),get:e=>m(r,r=>t(r.get(e))),remove:e=>m(o,r=>t(r.delete(e))),clear:()=>m(o,e=>t(e.clear())),keys:()=>m(r,e=>t(e.getAllKeys())),values:()=>m(r,e=>t(e.getAll())),entries:()=>m(r,async e=>{const[r,o]=await Promise.all([t(e.getAllKeys()),t(e.getAll())]);return r.map((e,t)=>[e,o[t]])}),count:()=>m(r,e=>t(e.count())),raw:(e,t=r)=>m(t,e),update:w,push:(e,t)=>w(e,(e=[])=>[...Array.isArray(e)?e:[],t]),merge:(e,t)=>w(e,(e={})=>({...e&&"object"==typeof e?e:{},...t}))};return["get","set","remove"].forEach(e=>g[e+"Item"]=g[e]),e.set(i,g),g};export const tinyIDB=n();export default tinyIDB;
|