tiny-idb 1.0.0 → 1.2.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
@@ -17,13 +17,13 @@ Native IndexedDB requires managing requests, transactions, and version upgrades.
17
17
  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.
18
18
 
19
19
  ### Resource Efficiency
20
- At only **596 bytes** (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.
20
+ 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.
21
21
 
22
22
  ### Intelligent Lifecycle Management
23
23
  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.
24
24
 
25
25
  ### Zero-Configuration Portability
26
- 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. This makes it an ideal solution for rapid prototyping, legacy system upgrades, and environments where complex build pipelines are unavailable.
26
+ 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.
27
27
 
28
28
  ## Installation
29
29
 
@@ -39,7 +39,7 @@ npm install tiny-idb
39
39
  </script>
40
40
  ```
41
41
 
42
- * **Minified Module**: `https://unpkg.com/tiny-idb/tiny-idb.min.js` (596 bytes gzipped)
42
+ * **Minified Module**: `https://unpkg.com/tiny-idb/tiny-idb.min.js` (less than 1KB gzipped)
43
43
  * **Source**: `https://unpkg.com/tiny-idb/tiny-idb.js`
44
44
 
45
45
  ## Technical Comparison
@@ -47,7 +47,7 @@ npm install tiny-idb
47
47
  | Feature | localStorage | tiny-idb |
48
48
  |---------|--------------|----------|
49
49
  | **Execution** | Synchronous (Blocks UI) | Asynchronous (Non-blocking) |
50
- | **Storage Limit** | ~5-10MB | Disk-limited |
50
+ | **Storage Limit** | ~5-10MB | Virtually unlimited (until disk is full) |
51
51
  | **Data Types** | Strings only | Objects, Blobs, Arrays, Numbers |
52
52
  | **Data Integrity** | Basic | ACID Compliant |
53
53
  | **Race Condition Safety** | None | Atomic `update`/`push`/`merge` |
@@ -56,6 +56,7 @@ npm install tiny-idb
56
56
 
57
57
  | Method | Description |
58
58
  |--------|-------------|
59
+ | `open(db, store?)` | Creates or retrieves a cached instance. `store` defaults to `db` name. |
59
60
  | `get(key)` | Retrieves a value; returns `undefined` if not found. |
60
61
  | `set(key, value)` | Persists a value to the store. |
61
62
  | `remove(key)` | Deletes a specific key. |
@@ -65,7 +66,93 @@ npm install tiny-idb
65
66
  | `count()` | Returns the total number of entries. |
66
67
  | `update(key, fn)` | Performs an atomic read-modify-write operation. |
67
68
  | `push(key, value)` | Atomically appends a value to an array. |
68
- | `merge(key, object)` | Atomically shallow-merges an object. |
69
+ | `merge(key, patch)` | Atomically shallow-merges an object. |
70
+
71
+ ## Example Use Cases
72
+
73
+ ### User Settings Management
74
+ Easily manage and update partial user preferences without worrying about race conditions.
75
+ ```javascript
76
+ import { tinyIDB } from 'tiny-idb';
77
+
78
+ // Initial setup
79
+ await tinyIDB.set('settings', { theme: 'dark', notifications: true });
80
+
81
+ // Later, merge new settings
82
+ await tinyIDB.merge('settings', { notifications: false, language: 'en' });
83
+
84
+ // Result: { theme: 'dark', notifications: false, language: 'en' }
85
+ ```
86
+
87
+ ### Persistent Shopping Cart
88
+ Atomically add items to a list, ensuring no items are lost during concurrent updates.
89
+ ```javascript
90
+ // Add items from different parts of the UI
91
+ await tinyIDB.push('cart', { id: 101, qty: 1 });
92
+ await tinyIDB.push('cart', { id: 202, qty: 2 });
93
+
94
+ const cart = await tinyIDB.get('cart');
95
+ console.log(`Items in cart: ${cart.length}`);
96
+ ```
97
+
98
+ ### Atomic Counters
99
+ Safely increment values using the `update` method.
100
+ ```javascript
101
+ await tinyIDB.set('page_views', 0);
102
+
103
+ // Increment safely
104
+ await tinyIDB.update('page_views', count => (count || 0) + 1);
105
+ ```
106
+
107
+ ### localStorage Compatibility
108
+ `tiny-idb` provides aliases for `get`, `set`, and `remove` to match the `localStorage` API.
109
+ ```javascript
110
+ await tinyIDB.setItem('session_id', 'xyz-123');
111
+ const sid = await tinyIDB.getItem('session_id');
112
+ await tinyIDB.removeItem('session_id');
113
+ ```
114
+
115
+ ### Simple Custom Database
116
+ If you only need one store per database, you can omit the `storeName`. It will automatically default to the same name as the database.
117
+ ```javascript
118
+ // Creates/retrieves a DB named 'my-store' with an internal store also named 'my-store'
119
+ const store = tinyIDB.open('my-store');
120
+
121
+ await store.set('key', 'value');
122
+ ```
123
+
124
+ ### Multi-Instance Support
125
+ Use `open` to create isolated storage instances. Instances are cached internally.
126
+ ```javascript
127
+ import { tinyIDB } from 'tiny-idb';
128
+
129
+ // Create isolated storage instances
130
+ const settings = tinyIDB.open('app-db', 'settings');
131
+ const cache = tinyIDB.open('app-db', 'cache');
132
+
133
+ await settings.set('theme', 'dark');
134
+ await cache.set('temp_data', { id: 1 });
135
+ ```
136
+
137
+ ## Development
138
+
139
+ `tiny-idb` is written in pure vanilla JavaScript. No compilation is required for development.
140
+
141
+ ### Running Tests
142
+ ```bash
143
+ npm test
144
+ ```
145
+
146
+ ### Running Tests on Minified Build
147
+ ```bash
148
+ npm run test:min
149
+ ```
150
+
151
+ ### Minification
152
+ Generate the production-ready minified file:
153
+ ```bash
154
+ npm run minify
155
+ ```
69
156
 
70
157
  ## License
71
158
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-idb",
3
- "version": "1.0.0",
3
+ "version": "1.2.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",
@@ -38,6 +38,8 @@
38
38
  "terser": "^5.46.1"
39
39
  },
40
40
  "scripts": {
41
- "test": "node test.js"
41
+ "test": "node test.js",
42
+ "test:min": "TEST_MIN=1 node test.js",
43
+ "minify": "terser tiny-idb.js -o tiny-idb.min.js -c toplevel,unsafe -m toplevel --comments false"
42
44
  }
43
45
  }
package/tiny-idb.js CHANGED
@@ -6,50 +6,66 @@
6
6
  * @license MIT
7
7
  */
8
8
 
9
- const DB_NAME = 'tiny-idb', STORE_NAME = 's', VERSION = 1;
10
- let dbPromise;
11
-
12
- const getDB = () => dbPromise || (dbPromise = new Promise((resolve, reject) => {
13
- const req = indexedDB.open(DB_NAME, VERSION);
14
- req.onupgradeneeded = e => e.target.result.createObjectStore(STORE_NAME);
15
- req.onsuccess = e => {
16
- const db = e.target.result;
17
- db.onversionchange = () => { db.close(); dbPromise = null; };
18
- resolve(db);
19
- };
20
- req.onerror = () => { dbPromise = null; reject(req.error); };
21
- }));
22
-
9
+ const instances = new Map();
23
10
  const prom = (req) => new Promise((res, rej) => {
24
11
  req.onsuccess = () => res(req.result);
25
12
  req.onerror = () => rej(req.error);
26
13
  });
14
+ const RO = 'readonly', RW = 'readwrite';
27
15
 
28
- const tx = async (mode, cb) => {
29
- const db = await getDB();
30
- return new Promise((resolve, reject) => {
31
- const t = db.transaction(STORE_NAME, mode);
32
- let res;
33
- try { res = cb(t.objectStore(STORE_NAME)); } catch (e) { t.abort(); return reject(e); }
34
- t.oncomplete = () => resolve(res);
35
- t.onerror = () => reject(t.error);
16
+ const getAPI = (dbName = 'tiny-idb', storeName = undefined) => {
17
+ storeName = storeName || dbName;
18
+ const key = dbName + '\0' + storeName;
19
+ if (instances.has(key)) return instances.get(key);
20
+
21
+ let dbPromise;
22
+ const tx = async (mode, cb) => {
23
+ const db = await (dbPromise || (dbPromise = new Promise((resolve, reject) => {
24
+ const req = indexedDB.open(dbName, 1);
25
+ req.onupgradeneeded = () => req.result.createObjectStore(storeName);
26
+ req.onsuccess = () => {
27
+ const db = req.result;
28
+ db.onversionchange = () => { db.close(); dbPromise = null; };
29
+ resolve(db);
30
+ };
31
+ req.onerror = () => { dbPromise = null; reject(req.error); };
32
+ })));
33
+ return new Promise(async (resolve, reject) => {
34
+ const t = db.transaction(storeName, mode);
35
+ t.onabort = t.onerror = () => reject(t.error || new DOMException('Aborted'));
36
+ try {
37
+ const res = await cb(t.objectStore(storeName));
38
+ t.oncomplete = () => resolve(res);
39
+ } catch (e) {
40
+ try { t.abort(); } catch {}
41
+ reject(e);
42
+ }
43
+ });
44
+ };
45
+
46
+ const update = async (key, fn) => tx(RW, async s => {
47
+ const n = await fn(await prom(s.get(key)));
48
+ return prom(s.put(n, key));
36
49
  });
37
- };
38
50
 
39
- export const tinyIDB = {
40
- set: (k, v) => tx('readwrite', s => prom(s.put(v, k))),
41
- get: k => tx('readonly', s => prom(s.get(k))),
42
- remove: k => tx('readwrite', s => prom(s.delete(k))),
43
- clear: () => tx('readwrite', s => prom(s.clear())),
44
- keys: () => tx('readonly', s => prom(s.getAllKeys())),
45
- values: () => tx('readonly', s => prom(s.getAll())),
46
- count: () => tx('readonly', s => prom(s.count())),
47
- update: async (k, fn) => tx('readwrite', async s => {
48
- const n = await fn(await prom(s.get(k)));
49
- return prom(s.put(n, k));
50
- }),
51
- push: (k, v) => tinyIDB.update(k, c => [...(Array.isArray(c) ? c : []), v]),
52
- merge: (k, p) => tinyIDB.update(k, c => ({ ...(c && typeof c === 'object' ? c : {}), ...p }))
51
+ const api = {
52
+ open: getAPI,
53
+ set: (key, value) => tx(RW, s => prom(s.put(value, key))),
54
+ get: key => tx(RO, s => prom(s.get(key))),
55
+ remove: k => tx(RW, s => prom(s.delete(k))),
56
+ clear: () => tx(RW, s => prom(s.clear())),
57
+ keys: () => tx(RO, s => prom(s.getAllKeys())),
58
+ values: () => tx(RO, s => prom(s.getAll())),
59
+ count: () => tx(RO, s => prom(s.count())),
60
+ update,
61
+ push: (key, value) => update(key, c => [...(Array.isArray(c) ? c : []), value]),
62
+ merge: (key, patch) => update(key, c => ({ ...(c && typeof c === 'object' ? c : {}), ...patch }))
63
+ };
64
+
65
+ ['get', 'set', 'remove'].forEach(m => api[m + 'Item'] = api[m]);
66
+ instances.set(key, api);
67
+ return api;
53
68
  };
54
69
 
70
+ export const tinyIDB = getAPI();
55
71
  export default tinyIDB;
package/tiny-idb.min.js CHANGED
@@ -1 +1 @@
1
- const DB_NAME="tiny-idb",STORE_NAME="s",VERSION=1;let dbPromise;const getDB=()=>dbPromise||(dbPromise=new Promise((e,r)=>{const t=indexedDB.open(DB_NAME,1);t.onupgradeneeded=e=>e.target.result.createObjectStore("s"),t.onsuccess=r=>{const t=r.target.result;t.onversionchange=()=>{t.close(),dbPromise=null},e(t)},t.onerror=()=>{dbPromise=null,r(t.error)}})),prom=e=>new Promise((r,t)=>{e.onsuccess=()=>r(e.result),e.onerror=()=>t(e.error)}),tx=async(e,r)=>{const t=await getDB();return new Promise((o,n)=>{const s=t.transaction("s",e);let a;try{a=r(s.objectStore("s"))}catch(e){return s.abort(),n(e)}s.oncomplete=()=>o(a),s.onerror=()=>n(s.error)})};export const tinyIDB={set:(e,r)=>tx("readwrite",t=>prom(t.put(r,e))),get:e=>tx("readonly",r=>prom(r.get(e))),remove:e=>tx("readwrite",r=>prom(r.delete(e))),clear:()=>tx("readwrite",e=>prom(e.clear())),keys:()=>tx("readonly",e=>prom(e.getAllKeys())),values:()=>tx("readonly",e=>prom(e.getAll())),count:()=>tx("readonly",e=>prom(e.count())),update:async(e,r)=>tx("readwrite",async t=>{const o=await r(await prom(t.get(e)));return prom(t.put(o,e))}),push:(e,r)=>tinyIDB.update(e,e=>[...Array.isArray(e)?e:[],r]),merge:(e,r)=>tinyIDB.update(e,e=>({...e&&"object"==typeof e?e:{},...r}))};export default tinyIDB;
1
+ 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 u=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)}})},i=async(e,r)=>u(o,async o=>{const n=await r(await t(o.get(e)));return t(o.put(n,e))}),y={open:n,set:(e,r)=>u(o,o=>t(o.put(r,e))),get:e=>u(r,r=>t(r.get(e))),remove:e=>u(o,r=>t(r.delete(e))),clear:()=>u(o,e=>t(e.clear())),keys:()=>u(r,e=>t(e.getAllKeys())),values:()=>u(r,e=>t(e.getAll())),count:()=>u(r,e=>t(e.count())),update:i,push:(e,t)=>i(e,e=>[...Array.isArray(e)?e:[],t]),merge:(e,t)=>i(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;