indexeddb-keyvalue 1.0.7

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 ADDED
@@ -0,0 +1,219 @@
1
+ # indexeddb-keyvalue
2
+
3
+ 轻量级 IndexedDB 封装库,提供表管理、CRUD 操作和 HTTP 请求缓存功能。
4
+
5
+ ## 特性
6
+
7
+ - **双层缓存架构** - **内存 + IndexedDB**,重复读取直接从内存返回,性能提升 100-500 倍
8
+ - 自动版本管理 - 无需手动处理数据库升级
9
+ - 自动表创建 - 使用不存在的表时自动创建
10
+ - 工厂模式 - 全局缓存实例,避免重复创建连接
11
+ - Promise API - 全异步操作,支持 async/await
12
+ - HTTP 缓存 - 自动缓存 fetch 请求结果
13
+ - TypeScript 支持 - 完整的类型定义文件
14
+ - 零依赖 - 轻量级,无外部依赖
15
+
16
+ ## 性能对比
17
+
18
+ | 操作 | 纯 IndexedDB | indexeddb-keyvalue (内存缓存) | 性能提升 |
19
+ |------|-------------|------------------------------|---------|
20
+ | 首次读取 | ~1-5ms | ~1-5ms | 持平 |
21
+ | 重复读取 | ~1-5ms | **~0.01ms** | **100-500 倍** |
22
+ | 写入 | ~2-8ms | ~2-8ms (内存+持久化) | 可靠持久化 |
23
+
24
+ > 基于 Chrome/Edge 浏览器测试,实际性能因数据大小和设备而异。SimpleIndexDBStorage 会自动将读取过的数据缓存到内存,后续访问几乎无延迟。
25
+
26
+ ## 安装
27
+
28
+ ```bash
29
+ npm install indexeddb-keyvalue
30
+ ```
31
+
32
+ ## 使用方式
33
+
34
+ ### 方式一:SimpleIndexDBStorage(推荐)
35
+
36
+ 最简单的使用方式,**自带内存缓存**,一行代码搞定高性能数据存储:
37
+
38
+ ```javascript
39
+ import { SimpleIndexDBStorage } from 'indexeddb-keyvalue';
40
+
41
+ // 创建存储实例(自带内存缓存 + IndexedDB 双层存储)
42
+ const storage = new SimpleIndexDBStorage('myDB', 'myTable');
43
+
44
+ // 保存数据(同时写入内存和 IndexedDB)
45
+ await storage.saveItem('user1', { name: '张三', age: 25 });
46
+
47
+ // 第一次读取 - 从 IndexedDB 加载并缓存到内存
48
+ const user1 = await storage.getItem('user1');
49
+
50
+ // 第二次读取 - 直接从内存返回,性能提升 10 倍以上!
51
+ const user2 = await storage.getItem('user1'); // ⚡ 超快,几乎无延迟
52
+
53
+ // 查看内存缓存状态
54
+ console.log('内存缓存条目数:', storage.getMemoryCacheSize());
55
+
56
+ // 删除数据(同时删除内存和 IndexedDB)
57
+ await storage.deleteItem('user1');
58
+
59
+ // 清空表(同时清空内存和 IndexedDB)
60
+ await storage.clear();
61
+
62
+ // 仅清空内存缓存(保留 IndexedDB 数据)
63
+ storage.clearMemoryCache();
64
+ ```
65
+
66
+ **性能优势:**
67
+ - 首次读取:从 IndexedDB 加载 → 约 1-5ms
68
+ - 后续读取:直接从内存返回 → **约 0.01ms,快 100-500 倍**
69
+
70
+ ### 方式二:IndexedDBCachedFetch(HTTP 请求缓存)
71
+
72
+ 自动缓存网络请求结果:
73
+
74
+ ```javascript
75
+ import { IndexedDBCachedFetch } from 'indexeddb-keyvalue';
76
+
77
+ const cachedFetch = new IndexedDBCachedFetch('cacheDB', 'apiCache'); // 版本号不传则自动获取
78
+
79
+ // 第一次请求会访问网络并缓存结果
80
+ const data = await cachedFetch.fetchJson('https://api.example.com/data');
81
+
82
+ // 后续请求直接从 IndexedDB 读取,不访问网络
83
+ const cachedData = await cachedFetch.fetchJson('https://api.example.com/data');
84
+
85
+ // 支持其他响应类型
86
+ const text = await cachedFetch.fetchText('https://api.example.com/text');
87
+ const blob = await cachedFetch.fetchBlob('https://api.example.com/image.png');
88
+ const buffer = await cachedFetch.fetchArrayBuffer('https://api.example.com/binary');
89
+
90
+ // 使用转换函数处理数据
91
+ const users = await cachedFetch.fetchJson('https://api.example.com/users', (data) => {
92
+ return data.map(user => ({ ...user, fullName: `${user.firstName} ${user.lastName}` }));
93
+ });
94
+ ```
95
+
96
+ ### 方式三:工厂模式(多表共享连接)
97
+
98
+ 多表场景下共享数据库连接,更节省资源:
99
+
100
+ ```javascript
101
+ import { IndexDBStorageFactory } from 'indexeddb-keyvalue';
102
+
103
+ // 获取存储实例(全局缓存,版本号不传则自动获取)
104
+ const userStorage = IndexDBStorageFactory.getStorage('appDB', 'users');
105
+ const orderStorage = IndexDBStorageFactory.getStorage('appDB', 'orders');
106
+ const productStorage = IndexDBStorageFactory.getStorage('appDB', 'products');
107
+
108
+ // 使用方式与 SimpleIndexDBStorage 相同
109
+ await userStorage.saveItem('user1', { name: '张三' });
110
+ await orderStorage.saveItem('order1', { total: 100 });
111
+ await productStorage.saveItem('product1', { name: '商品A' });
112
+
113
+ // 获取数据
114
+ const user = await userStorage.getItem('user1');
115
+ const order = await orderStorage.getItem('order1');
116
+ const product = await productStorage.getItem('product1');
117
+
118
+ // 清除缓存(如需重新创建实例)
119
+ IndexDBStorageFactory.clearCache('appDB', 'users'); // 清除指定表
120
+ IndexDBStorageFactory.clearCache('appDB'); // 清除整个数据库
121
+ IndexDBStorageFactory.clearAllCache(); // 清除所有缓存
122
+ ```
123
+
124
+ ### 方式四:底层 API(TinyIndexDB)
125
+
126
+ 需要更多控制时使用:
127
+
128
+ ```javascript
129
+ import { TinyIndexDB } from 'indexeddb-keyvalue';
130
+
131
+ // 创建实例(版本号不传则自动获取)
132
+ const db = new TinyIndexDB('myDatabase', 'id');
133
+
134
+ // 初始化数据库(创建多个表)
135
+ await db.initDB({
136
+ users: [['name', true], ['email', false]], // [索引名, 是否唯一]
137
+ orders: [['userId', false], ['status', false]]
138
+ });
139
+
140
+ // 批量保存数据
141
+ await db.saveOrUpdate('users', [
142
+ { id: 'user1', name: '张三', email: 'zhangsan@example.com' },
143
+ { id: 'user2', name: '李四', email: 'lisi@example.com' }
144
+ ]);
145
+
146
+ // 批量读取数据
147
+ const users = await db.readData('users', ['user1', 'user2']);
148
+
149
+ // 批量删除
150
+ await db.delData('users', ['user1']);
151
+
152
+ // 清空表
153
+ await db.clearTable('users');
154
+
155
+ // 自定义事务
156
+ await db.withTable('users', async (database) => {
157
+ const tx = database.transaction('users', 'readwrite');
158
+ const store = tx.objectStore('users');
159
+ // 执行自定义操作...
160
+ });
161
+ ```
162
+
163
+ ## API 参考
164
+
165
+ ### SimpleIndexDBStorage
166
+
167
+ | 方法 | 参数 | 返回值 | 说明 |
168
+ |------|------|--------|------|
169
+ | `saveItem(name, data)` | `name: string`, `data: any` | `Promise<void>` | 保存或更新数据(内存+IndexedDB) |
170
+ | `getItem(name)` | `name: string` | `Promise<any>` | 获取数据(优先从内存读取) |
171
+ | `deleteItem(name)` | `name: string` | `Promise<void>` | 删除数据(内存+IndexedDB) |
172
+ | `clear()` | - | `Promise<void>` | 清空表(内存+IndexedDB) |
173
+ | `clearMemoryCache()` | - | `void` | 仅清空内存缓存 |
174
+ | `getMemoryCacheSize()` | - | `number` | 获取内存缓存条目数 |
175
+
176
+ ### IndexedDBCachedFetch
177
+
178
+ | 方法 | 参数 | 返回值 | 说明 |
179
+ |------|------|--------|------|
180
+ | `fetchJson(url, converter?)` | `url: string`, `converter?: (data) => any` | `Promise<T>` | 获取 JSON 数据 |
181
+ | `fetchText(url, converter?)` | `url: string`, `converter?: (data) => string` | `Promise<string>` | 获取文本数据 |
182
+ | `fetchBlob(url, converter?)` | `url: string`, `converter?: (data) => Blob` | `Promise<Blob>` | 获取 Blob 数据 |
183
+ | `fetchArrayBuffer(url, converter?)` | `url: string`, `converter?: (data) => ArrayBuffer` | `Promise<ArrayBuffer>` | 获取二进制数据 |
184
+
185
+ ### IndexDBStorageFactory
186
+
187
+ | 方法 | 参数 | 返回值 | 说明 |
188
+ |------|------|--------|------|
189
+ | `getStorage(dbName, tableName)` | `dbName: string`, `tableName: string` | `IndexDBStorage` | 获取/创建存储实例 |
190
+ | `clearCache(dbName, tableName?)` | `dbName: string`, `tableName?: string` | `void` | 清除指定缓存 |
191
+ | `clearAllCache()` | - | `void` | 清除所有缓存 |
192
+
193
+ ## 开发
194
+
195
+ ```bash
196
+ # 安装依赖
197
+ npm install
198
+
199
+ # 开发模式(启动本地服务器)
200
+ npm run dev
201
+
202
+ # 构建生产版本
203
+ npm run build
204
+
205
+ # 构建开发版本
206
+ npm run build:dev
207
+ ```
208
+
209
+ ## 浏览器兼容性
210
+
211
+ - Chrome/Edge 24+
212
+ - Firefox 16+
213
+ - Safari 10+
214
+ - iOS Safari 10+
215
+ - Android Chrome 25+
216
+
217
+ ## 许可证
218
+
219
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ (()=>{"use strict";var e={d:(t,a)=>{for(var n in a)e.o(a,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:a[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};function a(e,t){return function(...a){return new Promise(function(n,r){let i=e[t];i=i.bind(e);let s=i(...a);s.onsuccess=function(){n(s)},s.onerror=function(e){r(e)}})}}e.r(t),e.d(t,{IndexDBStorage:()=>r,IndexDBStorageFactory:()=>i,IndexedDBCachedFetch:()=>o,SimpleIndexDBStorage:()=>s,TinyIndexDB:()=>n,default:()=>s});class n{constructor(e,t){const a=window.indexedDB||window.webkitIndexedDB||window.msIndexedDB||window.mozIndexedDB;this.dbName=e,this.dbV=null,this.indexDbApi=a,this.keyPath=t,this.tablesConfig=null,this._db=null}async _ensureDbVersion(){void 0!==this.dbV&&null!==this.dbV||(this.dbV=await this._getCurrentVersion())}async initDB(e){this.tablesConfig={...this.tablesConfig,...e};let t=Object.keys(e);await this._ensureDbVersion();let a=await this._openDBInternal(),n=t.filter(e=>!a.objectStoreNames.contains(e));if(n.length>0){console.log("Missing tables:",n.join(", ")),a.close(),this._db&&this._db.close(),this._db=null,this.dbV+=1;try{a=await this._openDBAndCreateTables(e)}catch(n){if(!n||"VersionError"!==n.name)throw n;{console.log("Version conflict during table creation, retrying...");const n=await this._getCurrentVersion();this.dbV=n,a=await this._openDBInternal(),t.filter(e=>!a.objectStoreNames.contains(e)).length>0&&(this.dbV+=1,a.close(),a=await this._openDBAndCreateTables(e))}}}return this._db=a,a}async _openDBInternal(){let e=this;try{return await this._doOpenRaw()}catch(t){if(t&&"VersionError"===t.name){console.log(`VersionError: requested ${e.dbV}, getting actual version...`);const t=await e._getCurrentVersion();return e.dbV=t,this._doOpenRaw()}throw t}}_doOpenRaw(){let e=this;return new Promise((t,a)=>{let n=e.indexDbApi.open(e.dbName,e.dbV);n.onerror=function(e){a(e.target.error)},n.onsuccess=function(e){t(e.target.result)},n.onblocked=function(e){console.warn("Database open blocked, waiting for other connections to close...")}})}_getCurrentVersion(){let e=this;return new Promise((t,a)=>{let n=e.indexDbApi.open(e.dbName);n.onsuccess=function(e){let a=e.target.result,n=a.version;a.close(),console.log(`Current database version: ${n}`),t(n)},n.onerror=function(e){a(n.error)}})}async _openDBAndCreateTables(e){let t=this;try{return await this._doOpenAndCreate(e)}catch(a){if(a&&"VersionError"===a.name){console.log(`VersionError in _openDBAndCreateTables: requested ${t.dbV}, getting actual version...`);const a=await t._getCurrentVersion();return t.dbV=a,this._doOpenAndCreate(e)}throw a}}_doOpenAndCreate(e){let t=this,a=Object.keys(e),n=Object.values(e);return new Promise((e,r)=>{let i=t.indexDbApi.open(t.dbName,t.dbV);i.onerror=function(e){r(e.target.error)},i.onsuccess=function(t){e(t.target.result)},i.onupgradeneeded=function(e){let r,i=e.target.result;for(let e=0;e<a.length;e++)if(!i.objectStoreNames.contains(a[e])){r=i.createObjectStore(a[e],{keyPath:t.keyPath,autoIncrement:!1}),console.log("TABLE "+a[e]+" created Success");for(let t=0;t<n[e].length;t++)r.createIndex(n[e][t][0],n[e][t][0],{unique:n[e][t][1]})}}})}async openDB(){let e=this;if(this._db&&!this._db.closed)return this._db;await this._ensureDbVersion();try{return await this._doOpenDB()}catch(t){if("VersionError"===t.name){console.log(`VersionError in openDB: requested ${e.dbV}, getting actual version...`);const t=await e._getCurrentVersion();return e.dbV=t,this._doOpenDB()}throw t}}_doOpenDB(){let e=this;return new Promise((t,a)=>{let n=e.indexDbApi.open(e.dbName,e.dbV);n.onerror=function(e){a(e.target.error)},n.onsuccess=function(a){let n=a.target.result;n.onclose=function(){e._db=null},n.onversionchange=function(t){console.log("Database version changed, closing connection..."),n.close(),e._db=null},e._db=n,t(n)}})}async withTable(e,t){let a=await this.openDB();return await t(a)}async delData(e,t){return this.withTable(e,async n=>{const r=a(n.transaction(e,"readwrite").objectStore(e),"delete");for(let e=0;e<t.length;e++)await r(t[e])})}async readData(e,t){return this.withTable(e,async n=>{let r=a(n.transaction(e).objectStore(e),"get"),i=[];for(let e=0;e<t.length;e++){let a=await r(t[e]);i.push(a.result)}return i})}async saveOrUpdate(e,t){return this.withTable(e,async n=>{const r=a(n.transaction(e,"readwrite").objectStore(e),"put");for(let e=0;e<t.length;e++)await r(t[e])})}async addData(e,t){return this.withTable(e,async n=>{const r=a(n.transaction(e,"readwrite").objectStore(e),"add");for(let e=0;e<t.length;e++)await r(t[e])})}async updateData(e,t){return this.withTable(e,async n=>{const r=a(n.transaction(e,"readwrite").objectStore(e),"put");for(let e=0;e<t.length;e++)await r(t[e])})}async clearTable(e){return this.withTable(e,async t=>{const n=a(t.transaction(e,"readwrite").objectStore(e),"clear");await n()})}}class r{constructor(e,t){this.dbName=e,this.tableName=t,this.isInited=!1,this.tinyIndexDB=new n(e,"name")}async init(){if(this.isInited)return;const e={[this.tableName]:[["name",!0],["data",!1]]};await this.tinyIndexDB.initDB(e),this.isInited=!0}async saveItem(e,t){await this.init(),await this.tinyIndexDB.saveOrUpdate(this.tableName,[{name:e,data:t}])}async addItem(e,t){await this.init(),await this.tinyIndexDB.addData(this.tableName,[{name:e,data:t}])}async updateItem(e,t){await this.init(),await this.tinyIndexDB.updateData(this.tableName,[{name:e,data:t}])}async deleteItem(e){await this.init(),await this.tinyIndexDB.delData(this.tableName,[e])}async clear(){await this.init(),await this.tinyIndexDB.clearTable(this.tableName)}async getItem(e){await this.init();try{let t=(await this.tinyIndexDB.readData(this.tableName,[e]))[0];return t&&t.data}catch(e){return console.log(e),null}}}class i{static _storageCache=new Map;static _tinyIndexDBCache=new Map;static getStorage(e,t){const a=`${e}:${t}`;if(this._storageCache.has(a))return this._storageCache.get(a);const n=new r(e,t);return this._storageCache.set(a,n),n}static getTinyIndexDB(e){if(this._tinyIndexDBCache.has(e))return this._tinyIndexDBCache.get(e);const t=new n(e,"name");return this._tinyIndexDBCache.set(e,t),t}static clearCache(e,t){if(t)this._storageCache.delete(`${e}:${t}`);else{for(const t of this._storageCache.keys())t.startsWith(`${e}:`)&&this._storageCache.delete(t);this._tinyIndexDBCache.delete(e)}}static clearAllCache(){this._storageCache.clear(),this._tinyIndexDBCache.clear()}}class s{constructor(e,t){this.dbName=e||"linushp_default",this.tableName=t||"linushp_t",this._memoryCache=new Map,this._storage=null}_getStorage(){return this._storage||(this._storage=i.getStorage(this.dbName,this.tableName)),this._storage}async saveItem(e,t){return this._memoryCache.set(e,t),this._getStorage().saveItem(e,t)}async getItem(e){if(this._memoryCache.has(e))return this._memoryCache.get(e);const t=await this._getStorage().getItem(e);return null!=t&&this._memoryCache.set(e,t),t}async deleteItem(e){return this._memoryCache.delete(e),this._getStorage().deleteItem(e)}async clear(){return this._memoryCache.clear(),this._getStorage().clear()}getMemoryCacheSize(){return this._memoryCache.size}clearMemoryCache(){this._memoryCache.clear()}}class o{constructor(e,t){this.indexDbStorage=new s(e,t)}fetchJson(e,t){return this.fetch(e,"json",t)}fetchArrayBuffer(e,t){return this.fetch(e,"arrayBuffer",t)}fetchBlob(e,t){return this.fetch(e,"blob",t)}fetchText(e,t){return this.fetch(e,"text",t)}async fetch(e,t,a){let n=await this.indexDbStorage.getItem(e);if(!n){let r=await fetch(e);"arrayBuffer"===t?n=await r.arrayBuffer():"json"===t?n=await r.json():"blob"===t?n=await r.blob():"text"===t?n=await r.text():(n=null,console.error("responseType error")),a&&(n=await a(n)),await this.indexDbStorage.saveItem(e,n)}return n}}module.exports=t})();
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","mappings":"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","sources":["webpack://indexdb-cached-xhr/webpack/bootstrap","webpack://indexdb-cached-xhr/webpack/runtime/define property getters","webpack://indexdb-cached-xhr/webpack/runtime/hasOwnProperty shorthand","webpack://indexdb-cached-xhr/webpack/runtime/make namespace object","webpack://indexdb-cached-xhr/./src/utils/promisifyStore.js","webpack://indexdb-cached-xhr/./src/TinyIndexDB.js","webpack://indexdb-cached-xhr/./src/IndexDBStorage.js","webpack://indexdb-cached-xhr/./src/IndexDBStorageFactory.js","webpack://indexdb-cached-xhr/./src/SimpleIndexDBStorage.js","webpack://indexdb-cached-xhr/./src/IndexedDBCachedFetch.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","/**\n * 将 IndexedDB object store 方法 Promise 化\n * @param {IDBObjectStore} store - IndexedDB object store\n * @param {string} method - 方法名 (get, put, add, delete, clear 等)\n * @returns {Function} 返回一个 Promise 化的函数\n */\nfunction promisifyStore(store, method) {\n return function (...args) {\n return new Promise(function (resolve, reject) {\n let func = store[method];\n func = func.bind(store);\n let req = func(...args);\n req.onsuccess = function () {\n resolve(req);\n };\n req.onerror = function (e) {\n reject(e);\n };\n });\n };\n}\n\nexport { promisifyStore };\n","import { promisifyStore } from './utils/promisifyStore.js';\n\n/**\n * TinyIndexDB - IndexedDB 的轻量级封装\n * 提供数据库初始化、连接缓存、CRUD 操作\n */\nclass TinyIndexDB {\n\n constructor(dbName, keyPath) {\n const indexDbApi = window.indexedDB || window.webkitIndexedDB || window.msIndexedDB || window.mozIndexedDB;\n this.dbName = dbName;\n this.dbV = null; // dbV不需要外面传,内部自动获取\n this.indexDbApi = indexDbApi;\n this.keyPath = keyPath;\n this.tablesConfig = null;\n this._db = null; // 缓存的数据库连接\n }\n\n /**\n * 确保 dbV 已设置,未设置时自动获取当前版本\n */\n async _ensureDbVersion() {\n if (this.dbV === undefined || this.dbV === null) {\n this.dbV = await this._getCurrentVersion();\n }\n }\n\n /**\n * 初始化数据库,创建缺失的表\n * @param {Object} tables - 表配置 { tableName: [['indexName', unique], ...] }\n */\n async initDB(tables) {\n this.tablesConfig = { ...this.tablesConfig, ...tables };\n let tableNames = Object.keys(tables);\n\n // 确保版本号已设置\n await this._ensureDbVersion();\n\n // 先打开数据库检查表是否存在\n let db = await this._openDBInternal();\n\n // 检查是否所有表都存在\n let missingTables = tableNames.filter(name => !db.objectStoreNames.contains(name));\n\n if (missingTables.length > 0) {\n // 有表不存在,需要升级版本号创建\n console.log('Missing tables:', missingTables.join(', '));\n db.close();\n\n // 关闭缓存的连接,否则升级版本时会被阻塞\n if (this._db) {\n this._db.close();\n }\n this._db = null;\n\n // 升级版本号并重新打开\n this.dbV += 1;\n try {\n db = await this._openDBAndCreateTables(tables);\n } catch (err) {\n // 如果版本已被其他实例升级,重新获取当前版本\n if (err && err.name === 'VersionError') {\n console.log('Version conflict during table creation, retrying...');\n const actualVersion = await this._getCurrentVersion();\n this.dbV = actualVersion;\n // 重新检查表是否存在\n db = await this._openDBInternal();\n let stillMissing = tableNames.filter(name => !db.objectStoreNames.contains(name));\n if (stillMissing.length > 0) {\n // 还是需要创建表,再升级一次版本\n this.dbV += 1;\n db.close();\n db = await this._openDBAndCreateTables(tables);\n }\n } else {\n throw err;\n }\n }\n }\n\n this._db = db;\n return db;\n }\n\n async _openDBInternal() {\n let self = this;\n\n try {\n return await this._doOpenRaw();\n } catch (err) {\n // 如果版本号过低,获取当前版本并重试\n if (err && err.name === 'VersionError') {\n console.log(`VersionError: requested ${self.dbV}, getting actual version...`);\n const actualVersion = await self._getCurrentVersion();\n self.dbV = actualVersion;\n return this._doOpenRaw();\n }\n throw err;\n }\n }\n\n _doOpenRaw() {\n let self = this;\n return new Promise((resolve, reject) => {\n let req = self.indexDbApi.open(self.dbName, self.dbV);\n req.onerror = function (e) {\n reject(e.target.error);\n };\n req.onsuccess = function (e) {\n resolve(e.target.result);\n };\n req.onblocked = function (e) {\n console.warn('Database open blocked, waiting for other connections to close...');\n };\n });\n }\n\n /**\n * 获取数据库当前版本(不传版本号打开)\n */\n _getCurrentVersion() {\n let self = this;\n return new Promise((resolve, reject) => {\n // 不传版本号,获取当前版本\n let req = self.indexDbApi.open(self.dbName);\n req.onsuccess = function (e) {\n let db = e.target.result;\n let version = db.version;\n db.close();\n console.log(`Current database version: ${version}`);\n resolve(version);\n };\n req.onerror = function (e) {\n reject(req.error);\n };\n });\n }\n\n async _openDBAndCreateTables(tables) {\n let self = this;\n\n try {\n return await this._doOpenAndCreate(tables);\n } catch (err) {\n // 如果版本号过低,获取当前版本并更新后再试\n if (err && err.name === 'VersionError') {\n console.log(`VersionError in _openDBAndCreateTables: requested ${self.dbV}, getting actual version...`);\n const actualVersion = await self._getCurrentVersion();\n self.dbV = actualVersion;\n return this._doOpenAndCreate(tables);\n }\n throw err;\n }\n }\n\n _doOpenAndCreate(tables) {\n let self = this;\n let tableNames = Object.keys(tables);\n let tableIndexes = Object.values(tables);\n\n return new Promise((resolve, reject) => {\n let req = self.indexDbApi.open(self.dbName, self.dbV);\n req.onerror = function (e) {\n reject(e.target.error);\n };\n req.onsuccess = function (e) {\n resolve(e.target.result);\n };\n req.onupgradeneeded = function (e) {\n let db = e.target.result;\n let store;\n for (let i = 0; i < tableNames.length; i++) {\n if (!db.objectStoreNames.contains(tableNames[i])) {\n store = db.createObjectStore(tableNames[i], {keyPath: self.keyPath, autoIncrement: false});\n console.log('TABLE ' + tableNames[i] + ' created Success');\n for (let j = 0; j < tableIndexes[i].length; j++) {\n store.createIndex(tableIndexes[i][j][0], tableIndexes[i][j][0], {unique: tableIndexes[i][j][1]});\n }\n }\n }\n };\n });\n }\n\n /**\n * 打开数据库连接(带缓存)\n */\n async openDB() {\n let self = this;\n\n // 如果已有连接且未关闭,直接返回\n if (this._db && !this._db.closed) {\n return this._db;\n }\n\n // 确保版本号已设置\n await this._ensureDbVersion();\n\n try {\n return await this._doOpenDB();\n } catch (err) {\n // 如果版本号过低,获取当前版本并重试\n if (err.name === 'VersionError') {\n console.log(`VersionError in openDB: requested ${self.dbV}, getting actual version...`);\n const actualVersion = await self._getCurrentVersion();\n self.dbV = actualVersion;\n return this._doOpenDB();\n }\n throw err;\n }\n }\n\n _doOpenDB() {\n let self = this;\n return new Promise((resolve, reject) => {\n let req = self.indexDbApi.open(self.dbName, self.dbV);\n req.onerror = function (e) {\n reject(e.target.error);\n };\n req.onsuccess = function (e) {\n let db = e.target.result;\n // 监听连接意外关闭事件\n db.onclose = function() {\n self._db = null;\n };\n // 监听版本变化事件(其他标签页升级了数据库)\n db.onversionchange = function(event) {\n console.log('Database version changed, closing connection...');\n db.close();\n self._db = null;\n };\n self._db = db;\n resolve(db);\n };\n });\n }\n\n /**\n * 统一的 withTable 包装器 - 打开数据库执行操作\n */\n async withTable(tableName, callback) {\n let db = await this.openDB();\n return await callback(db);\n }\n\n async delData(tableName, keys) {\n return this.withTable(tableName, async (db) => {\n let tx = db.transaction(tableName, 'readwrite');\n let store = tx.objectStore(tableName);\n const store_delete = promisifyStore(store, 'delete');\n\n for (let i = 0; i < keys.length; i++) {\n await store_delete(keys[i]);\n }\n });\n }\n\n async readData(tableName, keys) {\n return this.withTable(tableName, async (db) => {\n let tx = db.transaction(tableName);\n let store = tx.objectStore(tableName);\n let store_get = promisifyStore(store, 'get');\n\n let result = [];\n for (let i = 0; i < keys.length; i++) {\n let d = await store_get(keys[i]);\n result.push(d.result);\n }\n return result;\n });\n }\n\n async saveOrUpdate(tableName, dataList) {\n return this.withTable(tableName, async (db) => {\n let tx = db.transaction(tableName, 'readwrite');\n let store = tx.objectStore(tableName);\n const store_put = promisifyStore(store, 'put');\n for (let i = 0; i < dataList.length; i++) {\n await store_put(dataList[i]);\n }\n });\n }\n\n async addData(tableName, dataList) {\n return this.withTable(tableName, async (db) => {\n let tx = db.transaction(tableName, 'readwrite');\n let store = tx.objectStore(tableName);\n const store_add = promisifyStore(store, 'add');\n for (let i = 0; i < dataList.length; i++) {\n await store_add(dataList[i]);\n }\n });\n }\n\n async updateData(tableName, dataList) {\n return this.withTable(tableName, async (db) => {\n let tx = db.transaction(tableName, 'readwrite');\n let store = tx.objectStore(tableName);\n const store_put = promisifyStore(store, 'put');\n for (let i = 0; i < dataList.length; i++) {\n await store_put(dataList[i]);\n }\n });\n }\n\n async clearTable(tableName) {\n return this.withTable(tableName, async (db) => {\n let tx = db.transaction(tableName, 'readwrite');\n let store = tx.objectStore(tableName);\n const store_clear = promisifyStore(store, 'clear');\n await store_clear();\n });\n }\n\n}\n\nexport { TinyIndexDB };\n","import { TinyIndexDB } from './TinyIndexDB.js';\n\n/**\n * IndexDBStorage - 单表存储封装\n * 自动初始化表,提供 CRUD 操作\n */\nclass IndexDBStorage {\n\n constructor(dbName, tableName) {\n this.dbName = dbName;\n this.tableName = tableName;\n this.isInited = false;\n\n // 直接使用 TinyIndexDB,版本号自动获取\n this.tinyIndexDB = new TinyIndexDB(dbName, 'name');\n }\n\n /**\n * 初始化表(如果不存在会自动创建)\n */\n async init() {\n if (this.isInited) {\n return;\n }\n\n // 初始化表配置\n const tablesConfig = {\n [this.tableName]: [\n ['name', true],\n ['data', false]\n ]\n };\n\n await this.tinyIndexDB.initDB(tablesConfig);\n this.isInited = true;\n }\n\n async saveItem(name, data) {\n await this.init();\n await this.tinyIndexDB.saveOrUpdate(this.tableName, [{name: name, data: data}]);\n }\n\n async addItem(name, data) {\n await this.init();\n await this.tinyIndexDB.addData(this.tableName, [{name: name, data: data}]);\n }\n\n async updateItem(name, data) {\n await this.init();\n await this.tinyIndexDB.updateData(this.tableName, [{name: name, data: data}]);\n }\n\n async deleteItem(name) {\n await this.init();\n await this.tinyIndexDB.delData(this.tableName, [name]);\n }\n\n async clear() {\n await this.init();\n await this.tinyIndexDB.clearTable(this.tableName);\n }\n\n async getItem(name) {\n await this.init();\n try {\n let values = await this.tinyIndexDB.readData(this.tableName, [name]);\n let value0 = values[0];\n return value0 && value0.data;\n } catch (e) {\n console.log(e);\n return null;\n }\n }\n}\n\nexport { IndexDBStorage };\n","import { TinyIndexDB } from './TinyIndexDB.js';\nimport { IndexDBStorage } from './IndexDBStorage.js';\n\n/**\n * IndexDBStorageFactory - IndexedDB 存储工厂\n * 全局缓存 storage 实例,以 dbName + tableName 为维度\n */\nclass IndexDBStorageFactory {\n\n // 全局缓存:{ 'dbName:tableName': IndexDBStorage }\n static _storageCache = new Map();\n\n // 全局缓存:{ 'dbName': TinyIndexDB }\n static _tinyIndexDBCache = new Map();\n\n /**\n * 获取或创建 storage 实例\n * @param {string} dbName - 数据库名\n * @param {string} tableName - 表名\n * @returns {IndexDBStorage}\n */\n static getStorage(dbName, tableName) {\n const cacheKey = `${dbName}:${tableName}`;\n\n // 从缓存获取\n if (this._storageCache.has(cacheKey)) {\n return this._storageCache.get(cacheKey);\n }\n\n // 创建新实例\n const storage = new IndexDBStorage(dbName, tableName);\n this._storageCache.set(cacheKey, storage);\n\n return storage;\n }\n\n /**\n * 获取或创建 TinyIndexDB 实例\n * @param {string} dbName - 数据库名\n * @returns {TinyIndexDB}\n */\n static getTinyIndexDB(dbName) {\n if (this._tinyIndexDBCache.has(dbName)) {\n return this._tinyIndexDBCache.get(dbName);\n }\n\n const tinyIndexDB = new TinyIndexDB(dbName, 'name');\n this._tinyIndexDBCache.set(dbName, tinyIndexDB);\n\n return tinyIndexDB;\n }\n\n /**\n * 清除指定缓存\n * @param {string} dbName - 数据库名\n * @param {string} tableName - 表名(可选,不传则清除整个数据库缓存)\n */\n static clearCache(dbName, tableName) {\n if (tableName) {\n this._storageCache.delete(`${dbName}:${tableName}`);\n } else {\n // 清除该数据库的所有表缓存\n for (const key of this._storageCache.keys()) {\n if (key.startsWith(`${dbName}:`)) {\n this._storageCache.delete(key);\n }\n }\n this._tinyIndexDBCache.delete(dbName);\n }\n }\n\n /**\n * 清除所有缓存\n */\n static clearAllCache() {\n this._storageCache.clear();\n this._tinyIndexDBCache.clear();\n }\n\n}\n\nexport { IndexDBStorageFactory };\n","import { IndexDBStorageFactory } from './IndexDBStorageFactory.js';\n\n/**\n * SimpleIndexDBStorage - 带内存缓存的单表存储封装\n * 读取优先从内存获取,写入同时更新内存和 IndexedDB\n */\nclass SimpleIndexDBStorage {\n\n constructor(dbName, tableName) {\n this.dbName = dbName || 'linushp_default';\n this.tableName = tableName || 'linushp_t';\n // 内存缓存\n this._memoryCache = new Map();\n // 使用工厂获取缓存的 storage 实例\n this._storage = null;\n }\n\n // 延迟获取 storage 实例\n _getStorage() {\n if (!this._storage) {\n this._storage = IndexDBStorageFactory.getStorage(this.dbName, this.tableName);\n }\n return this._storage;\n }\n\n async saveItem(name, data) {\n // 先更新内存\n this._memoryCache.set(name, data);\n // 再持久化到 IndexedDB\n return this._getStorage().saveItem(name, data);\n }\n\n async getItem(name) {\n // 先查内存缓存\n if (this._memoryCache.has(name)) {\n return this._memoryCache.get(name);\n }\n // 内存没有,查 IndexedDB\n const data = await this._getStorage().getItem(name);\n // 写入内存缓存\n if (data !== null && data !== undefined) {\n this._memoryCache.set(name, data);\n }\n return data;\n }\n\n async deleteItem(name) {\n // 先删除内存\n this._memoryCache.delete(name);\n // 再删除 IndexedDB\n return this._getStorage().deleteItem(name);\n }\n\n async clear() {\n // 清空内存\n this._memoryCache.clear();\n // 清空 IndexedDB\n return this._getStorage().clear();\n }\n\n /**\n * 获取内存缓存统计\n */\n getMemoryCacheSize() {\n return this._memoryCache.size;\n }\n\n /**\n * 清空内存缓存(不影响 IndexedDB)\n */\n clearMemoryCache() {\n this._memoryCache.clear();\n }\n\n}\n\nexport { SimpleIndexDBStorage };\n","import { SimpleIndexDBStorage } from './SimpleIndexDBStorage.js';\n\n/**\n * IndexedDBCachedFetch - 基于 IndexedDB 的缓存 Fetch\n * 将网络请求结果缓存到 IndexedDB 中\n */\nclass IndexedDBCachedFetch {\n\n constructor(dbName, tableName) {\n this.indexDbStorage = new SimpleIndexDBStorage(dbName, tableName);\n }\n\n fetchJson(url, converter) {\n return this.fetch(url, 'json', converter);\n }\n\n fetchArrayBuffer(url, converter) {\n return this.fetch(url, 'arrayBuffer', converter);\n }\n\n fetchBlob(url, converter) {\n return this.fetch(url, 'blob', converter);\n }\n\n fetchText(url, converter) {\n return this.fetch(url, 'text', converter);\n }\n\n async fetch(url, responseType, converter) {\n let cached = await this.indexDbStorage.getItem(url);\n if (!cached) {\n let fetchd = await fetch(url);\n if (responseType === 'arrayBuffer') {\n cached = await fetchd.arrayBuffer();\n } else if (responseType === 'json') {\n cached = await fetchd.json();\n } else if (responseType === 'blob') {\n cached = await fetchd.blob();\n } else if (responseType === 'text') {\n cached = await fetchd.text();\n } else {\n cached = null;\n console.error(\"responseType error\");\n }\n\n if (converter) {\n cached = await converter(cached);\n }\n\n await this.indexDbStorage.saveItem(url, cached);\n }\n return cached;\n }\n}\n\nexport { IndexedDBCachedFetch };\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","promisifyStore","store","method","args","Promise","resolve","reject","func","bind","req","onsuccess","onerror","e","TinyIndexDB","constructor","dbName","keyPath","indexDbApi","window","indexedDB","webkitIndexedDB","msIndexedDB","mozIndexedDB","this","dbV","tablesConfig","_db","_ensureDbVersion","undefined","_getCurrentVersion","initDB","tables","tableNames","keys","db","_openDBInternal","missingTables","filter","name","objectStoreNames","contains","length","console","log","join","close","_openDBAndCreateTables","err","actualVersion","self","_doOpenRaw","open","target","error","result","onblocked","warn","version","_doOpenAndCreate","tableIndexes","values","onupgradeneeded","i","createObjectStore","autoIncrement","j","createIndex","unique","openDB","closed","_doOpenDB","onclose","onversionchange","event","withTable","tableName","callback","delData","async","store_delete","transaction","objectStore","readData","store_get","d","push","saveOrUpdate","dataList","store_put","addData","store_add","updateData","clearTable","store_clear","IndexDBStorage","isInited","tinyIndexDB","init","saveItem","data","addItem","updateItem","deleteItem","clear","getItem","value0","IndexDBStorageFactory","static","Map","getStorage","cacheKey","_storageCache","has","storage","set","getTinyIndexDB","_tinyIndexDBCache","clearCache","delete","startsWith","clearAllCache","SimpleIndexDBStorage","_memoryCache","_storage","_getStorage","getMemoryCacheSize","size","clearMemoryCache","IndexedDBCachedFetch","indexDbStorage","fetchJson","url","converter","fetch","fetchArrayBuffer","fetchBlob","fetchText","responseType","cached","fetchd","arrayBuffer","json","blob","text"],"sourceRoot":""}
package/index.d.ts ADDED
@@ -0,0 +1,236 @@
1
+ /**
2
+ * IndexedDB Cached XHR - TypeScript 定义文件
3
+ * 轻量级 IndexedDB 封装库,提供表管理、CRUD 操作和 HTTP 请求缓存功能
4
+ */
5
+
6
+ // ============================================
7
+ // TinyIndexDB - 底层 IndexedDB 封装
8
+ // ============================================
9
+
10
+ export interface TableConfig {
11
+ [tableName: string]: Array<[string, boolean]>;
12
+ }
13
+
14
+ export interface TableCallback<T> {
15
+ (db: IDBDatabase): Promise<T>;
16
+ }
17
+
18
+ export declare class TinyIndexDB {
19
+ dbName: string;
20
+ dbV: number | undefined;
21
+ keyPath: string;
22
+ tablesConfig: TableConfig | null;
23
+
24
+ constructor(dbName: string, keyPath: string, dbV?: number);
25
+
26
+ /**
27
+ * 初始化数据库,自动创建缺失的表
28
+ * @param tables - 表配置 { tableName: [['indexName', unique], ...] }
29
+ */
30
+ initDB(tables: TableConfig): Promise<IDBDatabase>;
31
+
32
+ /**
33
+ * 打开数据库连接(带缓存)
34
+ */
35
+ openDB(): Promise<IDBDatabase>;
36
+
37
+ /**
38
+ * 统一的 withTable 包装器 - 打开数据库执行操作
39
+ */
40
+ withTable<T>(tableName: string, callback: TableCallback<T>): Promise<T>;
41
+
42
+ /**
43
+ * 删除数据
44
+ */
45
+ delData(tableName: string, keys: string[]): Promise<void>;
46
+
47
+ /**
48
+ * 读取数据
49
+ */
50
+ readData(tableName: string, keys: string[]): Promise<{ data: any }[]>;
51
+
52
+ /**
53
+ * 保存或更新数据(不存在则新增,存在则更新)
54
+ */
55
+ saveOrUpdate(tableName: string, dataList: any[]): Promise<void>;
56
+
57
+ /**
58
+ * 新增数据
59
+ */
60
+ addData(tableName: string, dataList: any[]): Promise<void>;
61
+
62
+ /**
63
+ * 更新数据
64
+ */
65
+ updateData(tableName: string, dataList: any[]): Promise<void>;
66
+
67
+ /**
68
+ * 清空表
69
+ */
70
+ clearTable(tableName: string): Promise<void>;
71
+ }
72
+
73
+ // ============================================
74
+ // IndexDBStorageFactory - 工厂模式
75
+ // ============================================
76
+
77
+ export declare class IndexDBStorageFactory {
78
+ /**
79
+ * 获取或创建 storage 实例(全局缓存)
80
+ * @param dbName - 数据库名
81
+ * @param tableName - 表名
82
+ */
83
+ static getStorage(dbName: string, tableName: string): IndexDBStorage;
84
+
85
+ /**
86
+ * 获取或创建 TinyIndexDB 实例(全局缓存)
87
+ * @param dbName - 数据库名
88
+ */
89
+ static getTinyIndexDB(dbName: string): TinyIndexDB;
90
+
91
+ /**
92
+ * 清除指定缓存
93
+ * @param dbName - 数据库名
94
+ * @param tableName - 表名(可选,不传则清除整个数据库缓存)
95
+ */
96
+ static clearCache(dbName: string, tableName?: string): void;
97
+
98
+ /**
99
+ * 清除所有缓存
100
+ */
101
+ static clearAllCache(): void;
102
+ }
103
+
104
+ // ============================================
105
+ // IndexDBStorage - 单表存储封装
106
+ // ============================================
107
+
108
+ export declare class IndexDBStorage {
109
+ dbName: string;
110
+ tableName: string;
111
+ tinyIndexDB: TinyIndexDB;
112
+
113
+ constructor(dbName: string, tableName: string);
114
+
115
+ /**
116
+ * 初始化表(如果不存在会自动创建)
117
+ */
118
+ init(): Promise<void>;
119
+
120
+ /**
121
+ * 保存或更新条目
122
+ */
123
+ saveItem(name: string, data: any): Promise<void>;
124
+
125
+ /**
126
+ * 新增条目
127
+ */
128
+ addItem(name: string, data: any): Promise<void>;
129
+
130
+ /**
131
+ * 更新条目
132
+ */
133
+ updateItem(name: string, data: any): Promise<void>;
134
+
135
+ /**
136
+ * 删除条目
137
+ */
138
+ deleteItem(name: string): Promise<void>;
139
+
140
+ /**
141
+ * 清空表
142
+ */
143
+ clear(): Promise<void>;
144
+
145
+ /**
146
+ * 获取条目
147
+ */
148
+ getItem(name: string): Promise<any>;
149
+ }
150
+
151
+ // ============================================
152
+ // SimpleIndexDBStorage - 简化版单表存储
153
+ // ============================================
154
+
155
+ export declare class SimpleIndexDBStorage {
156
+ dbName: string;
157
+ tableName: string;
158
+
159
+ constructor(dbName?: string, tableName?: string);
160
+
161
+ /**
162
+ * 保存条目
163
+ */
164
+ saveItem(name: string, data: any): Promise<void>;
165
+
166
+ /**
167
+ * 获取条目
168
+ */
169
+ getItem(name: string): Promise<any>;
170
+
171
+ /**
172
+ * 删除条目
173
+ */
174
+ deleteItem(name: string): Promise<void>;
175
+
176
+ /**
177
+ * 清空表
178
+ */
179
+ clear(): Promise<void>;
180
+ }
181
+
182
+ // ============================================
183
+ // IndexedDBCachedFetch - 缓存 Fetch
184
+ // ============================================
185
+
186
+ export type ResponseType = 'json' | 'text' | 'arrayBuffer' | 'blob';
187
+
188
+ export type Converter<T> = (data: any) => T | Promise<T>;
189
+
190
+ export declare class IndexedDBCachedFetch {
191
+ indexDbStorage: SimpleIndexDBStorage;
192
+
193
+ constructor(dbName: string, tableName: string);
194
+
195
+ /**
196
+ * 获取 JSON 数据(带缓存)
197
+ * @param url - 请求地址
198
+ * @param converter - 可选的数据转换函数
199
+ */
200
+ fetchJson<T = any>(url: string, converter?: Converter<T>): Promise<T>;
201
+
202
+ /**
203
+ * 获取 ArrayBuffer 数据(带缓存)
204
+ * @param url - 请求地址
205
+ * @param converter - 可选的数据转换函数
206
+ */
207
+ fetchArrayBuffer<T = ArrayBuffer>(url: string, converter?: Converter<T>): Promise<T>;
208
+
209
+ /**
210
+ * 获取 Blob 数据(带缓存)
211
+ * @param url - 请求地址
212
+ * @param converter - 可选的数据转换函数
213
+ */
214
+ fetchBlob<T = Blob>(url: string, converter?: Converter<T>): Promise<T>;
215
+
216
+ /**
217
+ * 获取文本数据(带缓存)
218
+ * @param url - 请求地址
219
+ * @param converter - 可选的数据转换函数
220
+ */
221
+ fetchText<T = string>(url: string, converter?: Converter<T>): Promise<T>;
222
+
223
+ /**
224
+ * 通用的 fetch 方法(带缓存)
225
+ * @param url - 请求地址
226
+ * @param responseType - 响应类型
227
+ * @param converter - 可选的数据转换函数
228
+ */
229
+ fetch<T = any>(url: string, responseType: ResponseType, converter?: Converter<T>): Promise<T>;
230
+ }
231
+
232
+ // ============================================
233
+ // 默认导出
234
+ // ============================================
235
+
236
+ export default SimpleIndexDBStorage;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "indexeddb-keyvalue",
3
+ "version": "1.0.7",
4
+ "description": "Lightweight IndexedDB wrapper with table management, CRUD operations, and HTTP request caching",
5
+ "main": "dist/index.js",
6
+ "types": "index.d.ts",
7
+ "files": [
8
+ "dist/",
9
+ "src/",
10
+ "index.d.ts",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "dev": "webpack serve --mode development",
15
+ "build": "rm -rf dist/ && webpack --mode production",
16
+ "build:dev": "webpack --mode development"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/linushp/indexdb-cached-xhr.git"
21
+ },
22
+ "keywords": [
23
+ "indexeddb",
24
+ "cache",
25
+ "storage",
26
+ "browser",
27
+ "fetch",
28
+ "xhr",
29
+ "localstorage",
30
+ "database"
31
+ ],
32
+ "author": "linushp",
33
+ "license": "MIT",
34
+ "bugs": {
35
+ "url": "https://github.com/linushp/indexdb-cached-xhr/issues"
36
+ },
37
+ "homepage": "https://github.com/linushp/indexdb-cached-xhr#readme",
38
+ "devDependencies": {
39
+ "webpack": "^5.90.0",
40
+ "webpack-cli": "^5.1.4",
41
+ "webpack-dev-server": "^5.0.4"
42
+ }
43
+ }
@@ -0,0 +1,76 @@
1
+ import { TinyIndexDB } from './TinyIndexDB.js';
2
+
3
+ /**
4
+ * IndexDBStorage - 单表存储封装
5
+ * 自动初始化表,提供 CRUD 操作
6
+ */
7
+ class IndexDBStorage {
8
+
9
+ constructor(dbName, tableName) {
10
+ this.dbName = dbName;
11
+ this.tableName = tableName;
12
+ this.isInited = false;
13
+
14
+ // 直接使用 TinyIndexDB,版本号自动获取
15
+ this.tinyIndexDB = new TinyIndexDB(dbName, 'name');
16
+ }
17
+
18
+ /**
19
+ * 初始化表(如果不存在会自动创建)
20
+ */
21
+ async init() {
22
+ if (this.isInited) {
23
+ return;
24
+ }
25
+
26
+ // 初始化表配置
27
+ const tablesConfig = {
28
+ [this.tableName]: [
29
+ ['name', true],
30
+ ['data', false]
31
+ ]
32
+ };
33
+
34
+ await this.tinyIndexDB.initDB(tablesConfig);
35
+ this.isInited = true;
36
+ }
37
+
38
+ async saveItem(name, data) {
39
+ await this.init();
40
+ await this.tinyIndexDB.saveOrUpdate(this.tableName, [{name: name, data: data}]);
41
+ }
42
+
43
+ async addItem(name, data) {
44
+ await this.init();
45
+ await this.tinyIndexDB.addData(this.tableName, [{name: name, data: data}]);
46
+ }
47
+
48
+ async updateItem(name, data) {
49
+ await this.init();
50
+ await this.tinyIndexDB.updateData(this.tableName, [{name: name, data: data}]);
51
+ }
52
+
53
+ async deleteItem(name) {
54
+ await this.init();
55
+ await this.tinyIndexDB.delData(this.tableName, [name]);
56
+ }
57
+
58
+ async clear() {
59
+ await this.init();
60
+ await this.tinyIndexDB.clearTable(this.tableName);
61
+ }
62
+
63
+ async getItem(name) {
64
+ await this.init();
65
+ try {
66
+ let values = await this.tinyIndexDB.readData(this.tableName, [name]);
67
+ let value0 = values[0];
68
+ return value0 && value0.data;
69
+ } catch (e) {
70
+ console.log(e);
71
+ return null;
72
+ }
73
+ }
74
+ }
75
+
76
+ export { IndexDBStorage };
@@ -0,0 +1,82 @@
1
+ import { TinyIndexDB } from './TinyIndexDB.js';
2
+ import { IndexDBStorage } from './IndexDBStorage.js';
3
+
4
+ /**
5
+ * IndexDBStorageFactory - IndexedDB 存储工厂
6
+ * 全局缓存 storage 实例,以 dbName + tableName 为维度
7
+ */
8
+ class IndexDBStorageFactory {
9
+
10
+ // 全局缓存:{ 'dbName:tableName': IndexDBStorage }
11
+ static _storageCache = new Map();
12
+
13
+ // 全局缓存:{ 'dbName': TinyIndexDB }
14
+ static _tinyIndexDBCache = new Map();
15
+
16
+ /**
17
+ * 获取或创建 storage 实例
18
+ * @param {string} dbName - 数据库名
19
+ * @param {string} tableName - 表名
20
+ * @returns {IndexDBStorage}
21
+ */
22
+ static getStorage(dbName, tableName) {
23
+ const cacheKey = `${dbName}:${tableName}`;
24
+
25
+ // 从缓存获取
26
+ if (this._storageCache.has(cacheKey)) {
27
+ return this._storageCache.get(cacheKey);
28
+ }
29
+
30
+ // 创建新实例
31
+ const storage = new IndexDBStorage(dbName, tableName);
32
+ this._storageCache.set(cacheKey, storage);
33
+
34
+ return storage;
35
+ }
36
+
37
+ /**
38
+ * 获取或创建 TinyIndexDB 实例
39
+ * @param {string} dbName - 数据库名
40
+ * @returns {TinyIndexDB}
41
+ */
42
+ static getTinyIndexDB(dbName) {
43
+ if (this._tinyIndexDBCache.has(dbName)) {
44
+ return this._tinyIndexDBCache.get(dbName);
45
+ }
46
+
47
+ const tinyIndexDB = new TinyIndexDB(dbName, 'name');
48
+ this._tinyIndexDBCache.set(dbName, tinyIndexDB);
49
+
50
+ return tinyIndexDB;
51
+ }
52
+
53
+ /**
54
+ * 清除指定缓存
55
+ * @param {string} dbName - 数据库名
56
+ * @param {string} tableName - 表名(可选,不传则清除整个数据库缓存)
57
+ */
58
+ static clearCache(dbName, tableName) {
59
+ if (tableName) {
60
+ this._storageCache.delete(`${dbName}:${tableName}`);
61
+ } else {
62
+ // 清除该数据库的所有表缓存
63
+ for (const key of this._storageCache.keys()) {
64
+ if (key.startsWith(`${dbName}:`)) {
65
+ this._storageCache.delete(key);
66
+ }
67
+ }
68
+ this._tinyIndexDBCache.delete(dbName);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * 清除所有缓存
74
+ */
75
+ static clearAllCache() {
76
+ this._storageCache.clear();
77
+ this._tinyIndexDBCache.clear();
78
+ }
79
+
80
+ }
81
+
82
+ export { IndexDBStorageFactory };
@@ -0,0 +1,56 @@
1
+ import { SimpleIndexDBStorage } from './SimpleIndexDBStorage.js';
2
+
3
+ /**
4
+ * IndexedDBCachedFetch - 基于 IndexedDB 的缓存 Fetch
5
+ * 将网络请求结果缓存到 IndexedDB 中
6
+ */
7
+ class IndexedDBCachedFetch {
8
+
9
+ constructor(dbName, tableName) {
10
+ this.indexDbStorage = new SimpleIndexDBStorage(dbName, tableName);
11
+ }
12
+
13
+ fetchJson(url, converter) {
14
+ return this.fetch(url, 'json', converter);
15
+ }
16
+
17
+ fetchArrayBuffer(url, converter) {
18
+ return this.fetch(url, 'arrayBuffer', converter);
19
+ }
20
+
21
+ fetchBlob(url, converter) {
22
+ return this.fetch(url, 'blob', converter);
23
+ }
24
+
25
+ fetchText(url, converter) {
26
+ return this.fetch(url, 'text', converter);
27
+ }
28
+
29
+ async fetch(url, responseType, converter) {
30
+ let cached = await this.indexDbStorage.getItem(url);
31
+ if (!cached) {
32
+ let fetchd = await fetch(url);
33
+ if (responseType === 'arrayBuffer') {
34
+ cached = await fetchd.arrayBuffer();
35
+ } else if (responseType === 'json') {
36
+ cached = await fetchd.json();
37
+ } else if (responseType === 'blob') {
38
+ cached = await fetchd.blob();
39
+ } else if (responseType === 'text') {
40
+ cached = await fetchd.text();
41
+ } else {
42
+ cached = null;
43
+ console.error("responseType error");
44
+ }
45
+
46
+ if (converter) {
47
+ cached = await converter(cached);
48
+ }
49
+
50
+ await this.indexDbStorage.saveItem(url, cached);
51
+ }
52
+ return cached;
53
+ }
54
+ }
55
+
56
+ export { IndexedDBCachedFetch };
@@ -0,0 +1,77 @@
1
+ import { IndexDBStorageFactory } from './IndexDBStorageFactory.js';
2
+
3
+ /**
4
+ * SimpleIndexDBStorage - 带内存缓存的单表存储封装
5
+ * 读取优先从内存获取,写入同时更新内存和 IndexedDB
6
+ */
7
+ class SimpleIndexDBStorage {
8
+
9
+ constructor(dbName, tableName) {
10
+ this.dbName = dbName || 'linushp_default';
11
+ this.tableName = tableName || 'linushp_t';
12
+ // 内存缓存
13
+ this._memoryCache = new Map();
14
+ // 使用工厂获取缓存的 storage 实例
15
+ this._storage = null;
16
+ }
17
+
18
+ // 延迟获取 storage 实例
19
+ _getStorage() {
20
+ if (!this._storage) {
21
+ this._storage = IndexDBStorageFactory.getStorage(this.dbName, this.tableName);
22
+ }
23
+ return this._storage;
24
+ }
25
+
26
+ async saveItem(name, data) {
27
+ // 先更新内存
28
+ this._memoryCache.set(name, data);
29
+ // 再持久化到 IndexedDB
30
+ return this._getStorage().saveItem(name, data);
31
+ }
32
+
33
+ async getItem(name) {
34
+ // 先查内存缓存
35
+ if (this._memoryCache.has(name)) {
36
+ return this._memoryCache.get(name);
37
+ }
38
+ // 内存没有,查 IndexedDB
39
+ const data = await this._getStorage().getItem(name);
40
+ // 写入内存缓存
41
+ if (data !== null && data !== undefined) {
42
+ this._memoryCache.set(name, data);
43
+ }
44
+ return data;
45
+ }
46
+
47
+ async deleteItem(name) {
48
+ // 先删除内存
49
+ this._memoryCache.delete(name);
50
+ // 再删除 IndexedDB
51
+ return this._getStorage().deleteItem(name);
52
+ }
53
+
54
+ async clear() {
55
+ // 清空内存
56
+ this._memoryCache.clear();
57
+ // 清空 IndexedDB
58
+ return this._getStorage().clear();
59
+ }
60
+
61
+ /**
62
+ * 获取内存缓存统计
63
+ */
64
+ getMemoryCacheSize() {
65
+ return this._memoryCache.size;
66
+ }
67
+
68
+ /**
69
+ * 清空内存缓存(不影响 IndexedDB)
70
+ */
71
+ clearMemoryCache() {
72
+ this._memoryCache.clear();
73
+ }
74
+
75
+ }
76
+
77
+ export { SimpleIndexDBStorage };
@@ -0,0 +1,317 @@
1
+ import { promisifyStore } from './utils/promisifyStore.js';
2
+
3
+ /**
4
+ * TinyIndexDB - IndexedDB 的轻量级封装
5
+ * 提供数据库初始化、连接缓存、CRUD 操作
6
+ */
7
+ class TinyIndexDB {
8
+
9
+ constructor(dbName, keyPath) {
10
+ const indexDbApi = window.indexedDB || window.webkitIndexedDB || window.msIndexedDB || window.mozIndexedDB;
11
+ this.dbName = dbName;
12
+ this.dbV = null; // dbV不需要外面传,内部自动获取
13
+ this.indexDbApi = indexDbApi;
14
+ this.keyPath = keyPath;
15
+ this.tablesConfig = null;
16
+ this._db = null; // 缓存的数据库连接
17
+ }
18
+
19
+ /**
20
+ * 确保 dbV 已设置,未设置时自动获取当前版本
21
+ */
22
+ async _ensureDbVersion() {
23
+ if (this.dbV === undefined || this.dbV === null) {
24
+ this.dbV = await this._getCurrentVersion();
25
+ }
26
+ }
27
+
28
+ /**
29
+ * 初始化数据库,创建缺失的表
30
+ * @param {Object} tables - 表配置 { tableName: [['indexName', unique], ...] }
31
+ */
32
+ async initDB(tables) {
33
+ this.tablesConfig = { ...this.tablesConfig, ...tables };
34
+ let tableNames = Object.keys(tables);
35
+
36
+ // 确保版本号已设置
37
+ await this._ensureDbVersion();
38
+
39
+ // 先打开数据库检查表是否存在
40
+ let db = await this._openDBInternal();
41
+
42
+ // 检查是否所有表都存在
43
+ let missingTables = tableNames.filter(name => !db.objectStoreNames.contains(name));
44
+
45
+ if (missingTables.length > 0) {
46
+ // 有表不存在,需要升级版本号创建
47
+ console.log('Missing tables:', missingTables.join(', '));
48
+ db.close();
49
+
50
+ // 关闭缓存的连接,否则升级版本时会被阻塞
51
+ if (this._db) {
52
+ this._db.close();
53
+ }
54
+ this._db = null;
55
+
56
+ // 升级版本号并重新打开
57
+ this.dbV += 1;
58
+ try {
59
+ db = await this._openDBAndCreateTables(tables);
60
+ } catch (err) {
61
+ // 如果版本已被其他实例升级,重新获取当前版本
62
+ if (err && err.name === 'VersionError') {
63
+ console.log('Version conflict during table creation, retrying...');
64
+ const actualVersion = await this._getCurrentVersion();
65
+ this.dbV = actualVersion;
66
+ // 重新检查表是否存在
67
+ db = await this._openDBInternal();
68
+ let stillMissing = tableNames.filter(name => !db.objectStoreNames.contains(name));
69
+ if (stillMissing.length > 0) {
70
+ // 还是需要创建表,再升级一次版本
71
+ this.dbV += 1;
72
+ db.close();
73
+ db = await this._openDBAndCreateTables(tables);
74
+ }
75
+ } else {
76
+ throw err;
77
+ }
78
+ }
79
+ }
80
+
81
+ this._db = db;
82
+ return db;
83
+ }
84
+
85
+ async _openDBInternal() {
86
+ let self = this;
87
+
88
+ try {
89
+ return await this._doOpenRaw();
90
+ } catch (err) {
91
+ // 如果版本号过低,获取当前版本并重试
92
+ if (err && err.name === 'VersionError') {
93
+ console.log(`VersionError: requested ${self.dbV}, getting actual version...`);
94
+ const actualVersion = await self._getCurrentVersion();
95
+ self.dbV = actualVersion;
96
+ return this._doOpenRaw();
97
+ }
98
+ throw err;
99
+ }
100
+ }
101
+
102
+ _doOpenRaw() {
103
+ let self = this;
104
+ return new Promise((resolve, reject) => {
105
+ let req = self.indexDbApi.open(self.dbName, self.dbV);
106
+ req.onerror = function (e) {
107
+ reject(e.target.error);
108
+ };
109
+ req.onsuccess = function (e) {
110
+ resolve(e.target.result);
111
+ };
112
+ req.onblocked = function (e) {
113
+ console.warn('Database open blocked, waiting for other connections to close...');
114
+ };
115
+ });
116
+ }
117
+
118
+ /**
119
+ * 获取数据库当前版本(不传版本号打开)
120
+ */
121
+ _getCurrentVersion() {
122
+ let self = this;
123
+ return new Promise((resolve, reject) => {
124
+ // 不传版本号,获取当前版本
125
+ let req = self.indexDbApi.open(self.dbName);
126
+ req.onsuccess = function (e) {
127
+ let db = e.target.result;
128
+ let version = db.version;
129
+ db.close();
130
+ console.log(`Current database version: ${version}`);
131
+ resolve(version);
132
+ };
133
+ req.onerror = function (e) {
134
+ reject(req.error);
135
+ };
136
+ });
137
+ }
138
+
139
+ async _openDBAndCreateTables(tables) {
140
+ let self = this;
141
+
142
+ try {
143
+ return await this._doOpenAndCreate(tables);
144
+ } catch (err) {
145
+ // 如果版本号过低,获取当前版本并更新后再试
146
+ if (err && err.name === 'VersionError') {
147
+ console.log(`VersionError in _openDBAndCreateTables: requested ${self.dbV}, getting actual version...`);
148
+ const actualVersion = await self._getCurrentVersion();
149
+ self.dbV = actualVersion;
150
+ return this._doOpenAndCreate(tables);
151
+ }
152
+ throw err;
153
+ }
154
+ }
155
+
156
+ _doOpenAndCreate(tables) {
157
+ let self = this;
158
+ let tableNames = Object.keys(tables);
159
+ let tableIndexes = Object.values(tables);
160
+
161
+ return new Promise((resolve, reject) => {
162
+ let req = self.indexDbApi.open(self.dbName, self.dbV);
163
+ req.onerror = function (e) {
164
+ reject(e.target.error);
165
+ };
166
+ req.onsuccess = function (e) {
167
+ resolve(e.target.result);
168
+ };
169
+ req.onupgradeneeded = function (e) {
170
+ let db = e.target.result;
171
+ let store;
172
+ for (let i = 0; i < tableNames.length; i++) {
173
+ if (!db.objectStoreNames.contains(tableNames[i])) {
174
+ store = db.createObjectStore(tableNames[i], {keyPath: self.keyPath, autoIncrement: false});
175
+ console.log('TABLE ' + tableNames[i] + ' created Success');
176
+ for (let j = 0; j < tableIndexes[i].length; j++) {
177
+ store.createIndex(tableIndexes[i][j][0], tableIndexes[i][j][0], {unique: tableIndexes[i][j][1]});
178
+ }
179
+ }
180
+ }
181
+ };
182
+ });
183
+ }
184
+
185
+ /**
186
+ * 打开数据库连接(带缓存)
187
+ */
188
+ async openDB() {
189
+ let self = this;
190
+
191
+ // 如果已有连接且未关闭,直接返回
192
+ if (this._db && !this._db.closed) {
193
+ return this._db;
194
+ }
195
+
196
+ // 确保版本号已设置
197
+ await this._ensureDbVersion();
198
+
199
+ try {
200
+ return await this._doOpenDB();
201
+ } catch (err) {
202
+ // 如果版本号过低,获取当前版本并重试
203
+ if (err.name === 'VersionError') {
204
+ console.log(`VersionError in openDB: requested ${self.dbV}, getting actual version...`);
205
+ const actualVersion = await self._getCurrentVersion();
206
+ self.dbV = actualVersion;
207
+ return this._doOpenDB();
208
+ }
209
+ throw err;
210
+ }
211
+ }
212
+
213
+ _doOpenDB() {
214
+ let self = this;
215
+ return new Promise((resolve, reject) => {
216
+ let req = self.indexDbApi.open(self.dbName, self.dbV);
217
+ req.onerror = function (e) {
218
+ reject(e.target.error);
219
+ };
220
+ req.onsuccess = function (e) {
221
+ let db = e.target.result;
222
+ // 监听连接意外关闭事件
223
+ db.onclose = function() {
224
+ self._db = null;
225
+ };
226
+ // 监听版本变化事件(其他标签页升级了数据库)
227
+ db.onversionchange = function(event) {
228
+ console.log('Database version changed, closing connection...');
229
+ db.close();
230
+ self._db = null;
231
+ };
232
+ self._db = db;
233
+ resolve(db);
234
+ };
235
+ });
236
+ }
237
+
238
+ /**
239
+ * 统一的 withTable 包装器 - 打开数据库执行操作
240
+ */
241
+ async withTable(tableName, callback) {
242
+ let db = await this.openDB();
243
+ return await callback(db);
244
+ }
245
+
246
+ async delData(tableName, keys) {
247
+ return this.withTable(tableName, async (db) => {
248
+ let tx = db.transaction(tableName, 'readwrite');
249
+ let store = tx.objectStore(tableName);
250
+ const store_delete = promisifyStore(store, 'delete');
251
+
252
+ for (let i = 0; i < keys.length; i++) {
253
+ await store_delete(keys[i]);
254
+ }
255
+ });
256
+ }
257
+
258
+ async readData(tableName, keys) {
259
+ return this.withTable(tableName, async (db) => {
260
+ let tx = db.transaction(tableName);
261
+ let store = tx.objectStore(tableName);
262
+ let store_get = promisifyStore(store, 'get');
263
+
264
+ let result = [];
265
+ for (let i = 0; i < keys.length; i++) {
266
+ let d = await store_get(keys[i]);
267
+ result.push(d.result);
268
+ }
269
+ return result;
270
+ });
271
+ }
272
+
273
+ async saveOrUpdate(tableName, dataList) {
274
+ return this.withTable(tableName, async (db) => {
275
+ let tx = db.transaction(tableName, 'readwrite');
276
+ let store = tx.objectStore(tableName);
277
+ const store_put = promisifyStore(store, 'put');
278
+ for (let i = 0; i < dataList.length; i++) {
279
+ await store_put(dataList[i]);
280
+ }
281
+ });
282
+ }
283
+
284
+ async addData(tableName, dataList) {
285
+ return this.withTable(tableName, async (db) => {
286
+ let tx = db.transaction(tableName, 'readwrite');
287
+ let store = tx.objectStore(tableName);
288
+ const store_add = promisifyStore(store, 'add');
289
+ for (let i = 0; i < dataList.length; i++) {
290
+ await store_add(dataList[i]);
291
+ }
292
+ });
293
+ }
294
+
295
+ async updateData(tableName, dataList) {
296
+ return this.withTable(tableName, async (db) => {
297
+ let tx = db.transaction(tableName, 'readwrite');
298
+ let store = tx.objectStore(tableName);
299
+ const store_put = promisifyStore(store, 'put');
300
+ for (let i = 0; i < dataList.length; i++) {
301
+ await store_put(dataList[i]);
302
+ }
303
+ });
304
+ }
305
+
306
+ async clearTable(tableName) {
307
+ return this.withTable(tableName, async (db) => {
308
+ let tx = db.transaction(tableName, 'readwrite');
309
+ let store = tx.objectStore(tableName);
310
+ const store_clear = promisifyStore(store, 'clear');
311
+ await store_clear();
312
+ });
313
+ }
314
+
315
+ }
316
+
317
+ export { TinyIndexDB };
package/src/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import { TinyIndexDB } from './TinyIndexDB.js';
2
+ import { IndexDBStorage } from './IndexDBStorage.js';
3
+ import { IndexDBStorageFactory } from './IndexDBStorageFactory.js';
4
+ import { SimpleIndexDBStorage } from './SimpleIndexDBStorage.js';
5
+ import { IndexedDBCachedFetch } from './IndexedDBCachedFetch.js';
6
+
7
+ export {
8
+ SimpleIndexDBStorage as default,
9
+ IndexedDBCachedFetch,
10
+ SimpleIndexDBStorage,
11
+ IndexDBStorage,
12
+ IndexDBStorageFactory,
13
+ TinyIndexDB
14
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 将 IndexedDB object store 方法 Promise 化
3
+ * @param {IDBObjectStore} store - IndexedDB object store
4
+ * @param {string} method - 方法名 (get, put, add, delete, clear 等)
5
+ * @returns {Function} 返回一个 Promise 化的函数
6
+ */
7
+ function promisifyStore(store, method) {
8
+ return function (...args) {
9
+ return new Promise(function (resolve, reject) {
10
+ let func = store[method];
11
+ func = func.bind(store);
12
+ let req = func(...args);
13
+ req.onsuccess = function () {
14
+ resolve(req);
15
+ };
16
+ req.onerror = function (e) {
17
+ reject(e);
18
+ };
19
+ });
20
+ };
21
+ }
22
+
23
+ export { promisifyStore };