tiny-idb 1.2.1 → 1.4.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 CHANGED
@@ -10,6 +10,15 @@ It is designed for developers who require the reliability and capacity of Indexe
10
10
  [![License](https://img.shields.io/npm/l/tiny-idb.svg)](LICENSE)
11
11
  [![Size](https://img.shields.io/bundlephobia/minzip/tiny-idb)](https://bundlephobia.com/package/tiny-idb)
12
12
 
13
+ ## Why tiny-idb?
14
+
15
+ There are many IndexedDB wrappers, but `tiny-idb` is built with a specific philosophy: **Smallest possible footprint without sacrificing data integrity.**
16
+
17
+ - **Smaller than most icons**: At less than 1KB (gzipped), it's lighter than a 16x16 PNG.
18
+ - **Atomic Operations**: Built-in `update`, `push`, and `merge` are ACID-compliant and race-condition safe. No more partial updates or data loss.
19
+ - **Zero dependencies**: Built on pure vanilla JS. No external bloat.
20
+ - **Drop-in localStorage replacement**: Use the same `getItem`, `setItem`, `removeItem` calls, but with `await`.
21
+
13
22
  ## Core Advantages
14
23
 
15
24
  ### Architectural Simplicity
@@ -46,13 +55,14 @@ npm install tiny-idb
46
55
 
47
56
  ## Technical Comparison
48
57
 
49
- | Feature | localStorage | tiny-idb |
58
+ | Feature | `localStorage` | `tiny-idb` |
50
59
  |---------|--------------|----------|
51
60
  | **Execution** | Synchronous (Blocks UI) | Asynchronous (Non-blocking) |
52
- | **Storage Limit** | ~5-10MB | Virtually unlimited (until disk is full) |
53
- | **Data Types** | Strings only | Objects, Blobs, Arrays, Numbers |
61
+ | **Storage Limit** | ~5-10MB (Fixed) | Virtually unlimited (80%+ of disk) |
62
+ | **Data Types** | Strings only (Requires `JSON.parse`) | Objects, Blobs, Arrays, Numbers, Files |
54
63
  | **Data Integrity** | Basic | ACID Compliant |
55
64
  | **Race Condition Safety** | None | Atomic `update`/`push`/`merge` |
65
+ | **Tab Sync** | No | Automatic |
56
66
 
57
67
  ## API Reference
58
68
 
@@ -65,13 +75,43 @@ npm install tiny-idb
65
75
  | `clear()` | Removes all data from the store. |
66
76
  | `keys()` | Returns an array of all keys. |
67
77
  | `values()` | Returns an array of all values. |
78
+ | `entries()` | Returns an array of `[key, value]` pairs. |
68
79
  | `count()` | Returns the total number of entries. |
69
- | `update(key, fn)` | Performs an atomic read-modify-write operation. |
70
- | `push(key, value)` | Atomically appends a value to an array. |
71
- | `merge(key, patch)` | Atomically shallow-merges an object. |
80
+ | `raw(cb, mode?)` | Provides direct access to the `IDBObjectStore`. |
81
+ | `update(key, fn)` | Performs an **atomic** read-modify-write. |
82
+ | `push(key, value)` | **Atomically** appends to an array. |
83
+ | `merge(key, patch)` | **Atomically** shallow-merges an object. |
72
84
 
73
85
  ## Example Use Cases
74
86
 
87
+ ### Direct IndexedDB Access (Cursors & Search)
88
+ For large datasets where loading everything via `entries()` is inefficient, use `raw()` to perform cursor-based searches or filtered queries.
89
+ ```javascript
90
+ const fruits = await tinyIDB.raw(store => {
91
+ return new Promise((resolve) => {
92
+ const matches = [];
93
+ const request = store.openCursor();
94
+ request.onsuccess = () => {
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
+ });
103
+ ```
104
+
105
+ ### Iterating over Data
106
+ Use `entries()` to easily process all stored key-value pairs.
107
+ ```javascript
108
+ const allEntries = await tinyIDB.entries();
109
+
110
+ for (const [key, value] of allEntries) {
111
+ console.log(`${key}:`, value);
112
+ }
113
+ ```
114
+
75
115
  ### User Settings Management
76
116
  Easily manage and update partial user preferences without worrying about race conditions.
77
117
  ```javascript
@@ -86,6 +126,19 @@ await tinyIDB.merge('settings', { notifications: false, language: 'en' });
86
126
  // Result: { theme: 'dark', notifications: false, language: 'en' }
87
127
  ```
88
128
 
129
+ ### Storing Binary Data (Blobs/Files)
130
+ Unlike `localStorage`, `tiny-idb` can store binary data directly.
131
+ ```javascript
132
+ const response = await fetch('/profile-picture.jpg');
133
+ const blob = await response.blob();
134
+
135
+ await tinyIDB.set('user_avatar', blob);
136
+
137
+ // Later...
138
+ const avatar = await tinyIDB.get('user_avatar');
139
+ document.querySelector('img').src = URL.createObjectURL(avatar);
140
+ ```
141
+
89
142
  ### Persistent Shopping Cart
90
143
  Atomically add items to a list, ensuring no items are lost during concurrent updates.
91
144
  ```javascript
@@ -102,7 +155,7 @@ Safely increment values using the `update` method.
102
155
  ```javascript
103
156
  await tinyIDB.set('page_views', 0);
104
157
 
105
- // Increment safely
158
+ // Increment safely - even if multiple tabs do it at once
106
159
  await tinyIDB.update('page_views', count => (count || 0) + 1);
107
160
  ```
108
161
 
@@ -136,6 +189,15 @@ await settings.set('theme', 'dark');
136
189
  await cache.set('temp_data', { id: 1 });
137
190
  ```
138
191
 
192
+ ## Browser Support
193
+
194
+ `tiny-idb` targets modern browsers that support:
195
+ - [IndexedDB](https://caniuse.com/indexeddb) (98%+)
196
+ - [ES Modules](https://caniuse.com/es6-module)
197
+ - [Async/Await](https://caniuse.com/async-functions)
198
+
199
+ If you need to support legacy browsers (IE11), you will need to transpile and polyfill.
200
+
139
201
  ## Development
140
202
 
141
203
  `tiny-idb` is written in pure vanilla JavaScript. No compilation is required for development.
@@ -150,10 +212,10 @@ npm test
150
212
  npm run test:min
151
213
  ```
152
214
 
153
- ### Minification
215
+ ### Building & Minification
154
216
  Generate the production-ready minified file:
155
217
  ```bash
156
- npm run minify
218
+ npm run build
157
219
  ```
158
220
 
159
221
  ## License
package/index.d.ts ADDED
@@ -0,0 +1,86 @@
1
+ /** tiny-idb - MIT © Jelodar */
2
+
3
+ export interface TinyIDBInstance {
4
+ /**
5
+ * Creates or retrieves a cached instance of a database and store.
6
+ * @param dbName Name of the IndexedDB database.
7
+ * @param storeName Name of the object store (defaults to dbName).
8
+ */
9
+ open(dbName: string, storeName?: string): TinyIDBInstance;
10
+
11
+ /**
12
+ * Persists a value to the store.
13
+ */
14
+ set(key: any, value: any): Promise<void>;
15
+ /** Alias for set() */
16
+ setItem(key: any, value: any): Promise<void>;
17
+
18
+ /**
19
+ * Retrieves a value; returns undefined if not found.
20
+ */
21
+ get<T = any>(key: any): Promise<T | undefined>;
22
+ /** Alias for get() */
23
+ getItem<T = any>(key: any): Promise<T | undefined>;
24
+
25
+ /**
26
+ * Deletes a specific key.
27
+ */
28
+ remove(key: any): Promise<void>;
29
+ /** Alias for remove() */
30
+ removeItem(key: any): Promise<void>;
31
+
32
+ /**
33
+ * Removes all data from the store.
34
+ */
35
+ clear(): Promise<void>;
36
+
37
+ /**
38
+ * Returns an array of all keys.
39
+ */
40
+ keys(): Promise<any[]>;
41
+
42
+ /**
43
+ * Returns an array of all values.
44
+ */
45
+ values<T = any>(): Promise<T[]>;
46
+
47
+ /**
48
+ * Returns an array of [key, value] pairs.
49
+ */
50
+ entries<K = any, V = any>(): Promise<[K, V][]>;
51
+
52
+ /**
53
+ * Returns the total number of entries.
54
+ */
55
+ count(): Promise<number>;
56
+
57
+ /**
58
+ * Direct access to the IDBObjectStore within a transaction.
59
+ * This provides an "escape hatch" to use native IndexedDB features like cursors, ranges, and search.
60
+ * @param cb A callback that receives the IDBObjectStore.
61
+ * @param mode The transaction mode ('readonly' or 'readwrite').
62
+ */
63
+ raw<T = any>(cb: (store: IDBObjectStore) => T | Promise<T>, mode?: IDBTransactionMode): Promise<T>;
64
+
65
+ /**
66
+ * Performs an atomic read-modify-write operation within a single transaction.
67
+ * Note: The function 'fn' must be synchronous or microtask-only (no await on fetch/setTimeout)
68
+ * to prevent the IndexedDB transaction from auto-committing.
69
+ * @param key The key to update.
70
+ * @param fn A function that receives the current value and returns the new value.
71
+ */
72
+ update<T = any>(key: any, fn: (currentValue: T | undefined) => T | Promise<T>): Promise<void>;
73
+
74
+ /**
75
+ * Atomically appends a value to an array. Initializes as [value] if key doesn't exist.
76
+ */
77
+ push(key: any, value: any): Promise<void>;
78
+
79
+ /**
80
+ * Atomically shallow-merges an object. Initializes as patch if key doesn't exist.
81
+ */
82
+ merge(key: any, patch: object): Promise<void>;
83
+ }
84
+
85
+ export const tinyIDB: TinyIDBInstance;
86
+ export default tinyIDB;
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "tiny-idb",
3
- "version": "1.2.1",
3
+ "version": "1.4.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
+ "types": "index.d.ts",
7
8
  "type": "module",
8
9
  "repository": {
9
10
  "type": "git",
@@ -19,7 +20,8 @@
19
20
  "browser",
20
21
  "persistent",
21
22
  "nosql",
22
- "key-value"
23
+ "key-value",
24
+ "typescript"
23
25
  ],
24
26
  "author": "Jelodar",
25
27
  "license": "MIT",
@@ -30,6 +32,7 @@
30
32
  "files": [
31
33
  "tiny-idb.js",
32
34
  "tiny-idb.min.js",
35
+ "index.d.ts",
33
36
  "LICENSE",
34
37
  "README.md"
35
38
  ],
@@ -40,6 +43,7 @@
40
43
  "scripts": {
41
44
  "test": "node test.js",
42
45
  "test:min": "TEST_MIN=1 node test.js",
46
+ "build": "npm run minify",
43
47
  "minify": "terser tiny-idb.js -o tiny-idb.min.js -c toplevel,unsafe -m toplevel --comments '/tiny-idb/'"
44
48
  }
45
49
  }
package/tiny-idb.js CHANGED
@@ -47,7 +47,12 @@ const getAPI = (dbName = 'tiny-idb', storeName = undefined) => {
47
47
  clear: () => tx(RW, s => prom(s.clear())),
48
48
  keys: () => tx(RO, s => prom(s.getAllKeys())),
49
49
  values: () => tx(RO, s => prom(s.getAll())),
50
+ entries: () => tx(RO, async s => {
51
+ const [k, v] = await Promise.all([prom(s.getAllKeys()), prom(s.getAll())]);
52
+ return k.map((key, i) => [key, v[i]]);
53
+ }),
50
54
  count: () => tx(RO, s => prom(s.count())),
55
+ raw: (cb, mode = RO) => tx(mode, cb),
51
56
  update,
52
57
  push: (key, val) => update(key, (c = []) => [...(Array.isArray(c) ? c : []), val]),
53
58
  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",c=void 0)=>{const a=s+"\0"+(c=c||s);if(e.has(a))return e.get(a);let l;const i=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=null},e(t)},r.onerror=()=>{l=null,t(r.error)}})));return new Promise(async(o,n)=>{const s=r.transaction(c,e);s.onabort=s.onerror=()=>n(s.error||new DOMException("Aborted"));try{const e=await t(s.objectStore(c));s.oncomplete=()=>o(e)}catch(e){try{s.abort()}catch{}n(e)}})},u=(e,r)=>i(o,async o=>t(o.put(await r(await t(o.get(e))),e))),y={open:n,set:(e,r)=>i(o,o=>t(o.put(r,e))),get:e=>i(r,r=>t(r.get(e))),remove:e=>i(o,r=>t(r.delete(e))),clear:()=>i(o,e=>t(e.clear())),keys:()=>i(r,e=>t(e.getAllKeys())),values:()=>i(r,e=>t(e.getAll())),count:()=>i(r,e=>t(e.count())),update:u,push:(e,t)=>u(e,(e=[])=>[...Array.isArray(e)?e:[],t]),merge:(e,t)=>u(e,(e={})=>({...e&&"object"==typeof e?e:{},...t}))};return["get","set","remove"].forEach(e=>y[e+"Item"]=y[e]),e.set(a,y),y};export const tinyIDB=n();export default tinyIDB;
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=void 0)=>{const c=s+"\0"+(a=a||s);if(e.has(c))return e.get(c);let l;const i=async(e,t)=>{const r=await(l||(l=new Promise((e,t)=>{const r=indexedDB.open(s,1);r.onupgradeneeded=()=>r.result.createObjectStore(a),r.onsuccess=()=>{const t=r.result;t.onversionchange=()=>{t.close(),l=null},e(t)},r.onerror=()=>{l=null,t(r.error)}})));return new Promise(async(o,n)=>{const s=r.transaction(a,e);s.onabort=s.onerror=()=>n(s.error||new DOMException("Aborted"));try{const e=await t(s.objectStore(a));s.oncomplete=()=>o(e)}catch(e){try{s.abort()}catch{}n(e)}})},u=(e,r)=>i(o,async o=>t(o.put(await r(await t(o.get(e))),e))),y={open:n,set:(e,r)=>i(o,o=>t(o.put(r,e))),get:e=>i(r,r=>t(r.get(e))),remove:e=>i(o,r=>t(r.delete(e))),clear:()=>i(o,e=>t(e.clear())),keys:()=>i(r,e=>t(e.getAllKeys())),values:()=>i(r,e=>t(e.getAll())),entries:()=>i(r,async e=>{const[r,o]=await Promise.all([t(e.getAllKeys()),t(e.getAll())]);return r.map((e,t)=>[e,o[t]])}),count:()=>i(r,e=>t(e.count())),raw:(e,t=r)=>i(t,e),update:u,push:(e,t)=>u(e,(e=[])=>[...Array.isArray(e)?e:[],t]),merge:(e,t)=>u(e,(e={})=>({...e&&"object"==typeof e?e:{},...t}))};return["get","set","remove"].forEach(e=>y[e+"Item"]=y[e]),e.set(c,y),y};export const tinyIDB=n();export default tinyIDB;