# indexeddb-toolkit **Repository Path**: chen-jingsheng-123/indexeddb-toolkit ## Basic Information - **Project Name**: indexeddb-toolkit - **Description**: indexeddb-toolkit是一个用于浏览器的轻量级数据库操作库,使用 ts+promise 提供了对 IndexedDB 的封装和便捷的 API,用于简化数据库的创建、表的管理和数据的增删改查操作。它提供了一系列的方法来操作数据库和表,包括创建、删除、查询和更新数据等功能,来减少开发者的成本,API命名与主流数据库orm类似 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: v2 - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 13 - **Forks**: 0 - **Created**: 2023-12-03 - **Last Updated**: 2026-03-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # indexeddb-toolkit PostgreSQL 风格的 IndexedDB 工具库 — 连接池、Schema DDL、事务、SQL-like 查询。 ## 特性 - **连接池 (Pool/Client)** — `createPool()` 管理连接,自动复用,可配置最大连接数 - **Schema DDL 定义** — Knex 风格的表结构声明,支持索引、唯一约束、multiEntry - **SQL-like 链式查询** — `.select().where().orderBy().limit().offset()` 完整链式 API - **事务支持** — `pool.transaction(async tx => {...})` 自动 commit/rollback - **索引优化** — 查询自动利用 `IDBKeyRange` 走索引,避免全表扫描 - **冲突处理** — `onConflict('field', 'skip' | 'update')` 支持 upsert - **文件存储插件** — 自动建表、Blob URL 生命周期追踪、cursor 真分页 - **JSON 导入导出** — 完整数据库快照与恢复,支持冲突策略 - **强类型** — 泛型 `IDBResult`、`IDBError` 错误码体系、完整类型推导 - **双格式输出** — ESM + CJS dual package + UMD bundle ## 安装 ```bash npm install indexeddb-toolkit ``` 浏览器直接使用: ```html ``` ## 快速开始 ```typescript import { createPool } from 'indexeddb-toolkit' // 1. 创建连接池 + 定义 Schema const pool = createPool({ database: 'myApp', version: 1, schema: (s) => { s.createTable('users', (t) => { t.autoIncrement('id') t.string('name').index() t.string('email').unique() t.number('age').index() t.boolean('active') }) }, }) // 2. 初始化连接(首次使用前需要) await pool.connect().then(c => c.release()) // 3. 插入 await pool.query('users').insert({ name: 'Alice', email: 'alice@test.com', age: 25, active: true }).execute() // 4. 查询 const result = await pool.query('users') .select('name', 'age') .where('age', '>=', 18) .orderBy('name', 'asc') .limit(10) .execute() console.log(result.data) // [{ name: 'Alice', age: 25 }] // 5. 关闭 await pool.end() ``` ### asyncQuery — 自动初始化 如果不想手动 `connect()`,可以使用 `asyncQuery()`,它会自动确保数据库已初始化: ```typescript const pool = createPool({ database: 'myApp', version: 1, schema: ... }) // 无需先 connect(),asyncQuery 自动初始化 const qb = await pool.asyncQuery('users') const result = await qb.select().execute() ``` > **注意**:`pool.query()` 是同步方法,要求数据库已经初始化(即至少调用过一次 `connect()` 或 `asyncQuery()`)。如果在未初始化时调用会抛出 `CONNECTION_CLOSED` 错误。 ## API 参考 ### `createPool(config)` ```typescript const pool = createPool({ database: 'myApp', // 必填,数据库名 version: 1, // 默认 1 maxConnections: 10, // 默认 10 schema: (builder) => {}, // 可选,表结构定义 }) ``` ### Schema 定义 ```typescript schema: (s) => { s.createTable('tableName', (t) => { t.autoIncrement('id') // 自增主键 t.primaryKey('key') // 手动主键(无自增) t.string('name').index() // 字符串列 + 普通索引 t.string('email').unique() // 唯一索引 t.number('age') // 数字列 t.boolean('active') // 布尔列 t.date('createdAt') // 日期列 t.any('data') // 任意类型列 t.string('tags').multiEntry() // 多值索引(数组字段) }) s.dropTable('oldTable') // 删除表 } ``` ### 查询构建器 ```typescript // SELECT const all = await pool.query('users').select().execute() const filtered = await pool.query('users').select('name', 'age').where('age', '>', 18).execute() const first = await pool.query('users').select().where('id', '=', 1).first() const count = await pool.query('users').where('active', '=', true).count() // INSERT await pool.query('users').insert({ name: 'Alice', age: 25 }).execute() await pool.query('users').insert([item1, item2, item3]).execute() // INSERT 冲突处理 await pool.query('users').insert(data).onConflict('email', 'skip').execute() // 跳过 await pool.query('users').insert(data).onConflict('email', 'update').execute() // upsert // UPDATE await pool.query('users').where('id', '=', 1).update({ age: 26 }).execute() // DELETE await pool.query('users').where('id', '=', 1).delete() await pool.query('users').delete() // 清空 // WHERE 运算符: =, !=, >, >=, <, <=, between, in, notIn, like await pool.query('users').where('age', 'between', [20, 30]).execute() await pool.query('users').where('name', 'in', ['Alice', 'Bob']).execute() await pool.query('users').where('name', 'like', 'A%').execute() // 排序 + 分页 await pool.query('users').select().orderBy('age', 'desc').limit(10).offset(20).execute() ``` ### Client(手动连接管理) ```typescript const client = await pool.connect() try { await client.query('users').insert({ name: 'Alice' }).execute() const result = await client.query('users').select().execute() } finally { client.release() // 必须归还连接 } ``` ### 事务 ```typescript // 通过 pool await pool.transaction(async (tx) => { await tx.add('accounts', { name: 'Alice', balance: 100 }) const all = await tx.getAll('accounts') await tx.put('accounts', { ...all[0], balance: 150 }) // 正常结束自动 commit,抛异常自动 rollback }) // 通过 client const client = await pool.connect() try { await client.transaction('accounts', async (tx) => { await tx.add('accounts', { name: 'Bob', balance: 200 }) }) } finally { client.release() } ``` ### 文件存储 ```typescript import { FileStorage } from 'indexeddb-toolkit' // FileStorage 会自动创建所需的 object store(无需在 schema 中声明) const files = new FileStorage(pool, '_files') await files.save(fileObj, { metadata: { category: 'avatar' } }) const file = await files.get(fileId, { withData: true }) await files.download(fileId) await files.downloadAll({ format: 'zip', zipFilename: 'backup.zip' }) // Blob URL 生命周期管理 const result = await files.get(fileId, { asUrl: true }) files.revokeUrl(result.data.url) // 释放单个 URL files.revokeAllUrls() // 释放所有已追踪的 URL ``` ### JSON 导入导出 ```typescript import { exportDatabase, importDatabase } from 'indexeddb-toolkit' // 导出 const snapshot = await exportDatabase(pool, { tables: ['users', 'posts'] }) const json = JSON.stringify(snapshot.data) // 导入 await importDatabase(pool, json, { onConflict: 'skip' }) // 跳过冲突 await importDatabase(pool, json, { onConflict: 'update' }) // 覆盖冲突 ``` ### 错误处理 所有错误都是 `IDBError` 实例,带有类型化的 `code` 字段: ```typescript import { IDBError, IDBErrorCode } from 'indexeddb-toolkit' try { await pool.query('nonexistent').select().execute() } catch (err) { if (err instanceof IDBError) { console.log(err.code) // IDBErrorCode.TABLE_NOT_FOUND console.log(err.message) // 'Table "nonexistent" does not exist' console.log(err.detail) // 'nonexistent' } } ``` 错误码列表:`CONNECTION_FAILED`, `CONNECTION_BLOCKED`, `CONNECTION_CLOSED`, `TABLE_NOT_FOUND`, `TABLE_ALREADY_EXISTS`, `CONSTRAINT_VIOLATION`, `TRANSACTION_ABORTED`, `INVALID_QUERY`, `INVALID_ARGUMENT`, `POOL_EXHAUSTED`, `POOL_CLOSED`, `SCHEMA_ERROR`, `VERSION_CONFLICT`, `NOT_FOUND`, `TIMEOUT` 等。 ### 返回类型 所有操作返回 `IDBResult`: ```typescript interface IDBResult { success: boolean data: T meta?: { count?: number duration?: number affectedRows?: number } } ``` > 注意:错误通过 `throw` 抛出而非通过 `success: false` 返回。成功操作始终返回 `success: true`。 ### 日志级别 ```typescript import { setLogLevel } from 'indexeddb-toolkit' setLogLevel('debug') // 'debug' | 'info' | 'warn' | 'error' ``` ## 交互测试 项目根目录包含 `index.html` 可交互测试页,在浏览器中直接打开即可测试所有 API。 ```bash npm run build:umd # 构建 UMD bundle # 然后用浏览器打开 index.html ``` ## 构建 ```bash npm run build # ESM + CJS + .d.ts 类型声明 npm run build:umd # UMD bundle(供