npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

indexeddb-toolkit-package

v1.0.1

Published

A PostgreSQL-style IndexedDB toolkit with connection pooling, schema builder, transactions, and SQL-like query API

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<T>IDBError 错误码体系、完整类型推导
  • 双格式输出 — ESM + CJS dual package + UMD bundle

安装

npm install indexeddb-toolkit-package

浏览器直接使用:

<script src="dist/indexeddb-toolkit.umd.js"></script>
<script>
  const { createPool } = IDBToolkit
</script>

快速开始

import { createPool } from 'indexeddb-toolkit-package'

// 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: '[email protected]', 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(),它会自动确保数据库已初始化:

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)

const pool = createPool({
  database: 'myApp',       // 必填,数据库名
  version: 1,              // 默认 1
  maxConnections: 10,      // 默认 10
  schema: (builder) => {}, // 可选,表结构定义
})

Schema 定义

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')      // 删除表
}

查询构建器

// 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(手动连接管理)

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() // 必须归还连接
}

事务

// 通过 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()
}

文件存储

import { FileStorage } from 'indexeddb-toolkit-package'

// 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 导入导出

import { exportDatabase, importDatabase } from 'indexeddb-toolkit-package'

// 导出
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 字段:

import { IDBError, IDBErrorCode } from 'indexeddb-toolkit-package'

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<T>

interface IDBResult<T> {
  success: boolean
  data: T
  meta?: {
    count?: number
    duration?: number
    affectedRows?: number
  }
}

注意:错误通过 throw 抛出而非通过 success: false 返回。成功操作始终返回 success: true

日志级别

import { setLogLevel } from 'indexeddb-toolkit-package'
setLogLevel('debug') // 'debug' | 'info' | 'warn' | 'error'

交互测试

项目根目录包含 index.html 可交互测试页,在浏览器中直接打开即可测试所有 API。

npm run build:umd   # 构建 UMD bundle
# 然后用浏览器打开 index.html

构建

npm run build       # ESM + CJS + .d.ts 类型声明
npm run build:umd   # UMD bundle(供 <script> 标签使用)
npm run test        # 运行测试
npm run lint        # 代码检查

License

MIT