Small namespace-scoped key-value storage on top of IndexedDB. It is designed for local-first browser data where each namespace maps to its own object store and operations fail with explicit error codes instead of silent fallthrough.
- Runtimes: modern browsers and runtimes that expose global
indexedDB - Module format: ESM and CJS.
- Required globals / APIs:
indexedDB,DOMException. - TypeScript: bundled types.
- Simple
put/get/has/delete/clearAPI per namespace. - Explicit error surfaces with stable
codestrings. - Dynamic namespace creation backed by IndexedDB object stores.
- Small public surface:
KVStore,resolveDB, anddestroyDB.
npm install @sovereignbase/offline-kv-store
# or
pnpm add @sovereignbase/offline-kv-store
# or
yarn add @sovereignbase/offline-kv-store
# or
bun add @sovereignbase/offline-kv-store
# or
deno add jsr:@sovereignbase/offline-kv-store
# or
vlt install jsr:@sovereignbase/offline-kv-storeimport { KVStore } from '@sovereignbase/offline-kv-store'
const settings = new KVStore<string>('settings')
await settings.put('theme', 'dark')
const theme = await settings.get('theme')
console.log(theme) // "dark"
console.log(await settings.has('theme')) // true
await settings.delete('theme')
await settings.clear()Each KVStore instance is bound to one namespace:
import { KVStore } from '@sovereignbase/offline-kv-store'
const profiles = new KVStore<{
name: string
email: string
verified: boolean
}>('profiles')
const drafts = new KVStore<{
title: string
body: string
updatedAt: string
}>('drafts')
await profiles.put('alice', {
name: 'Alice',
email: 'alice@example.test',
verified: true,
})
await drafts.put('welcome', {
title: 'Hello',
body: 'Draft content goes here.',
updatedAt: new Date().toISOString(),
})import { destroyDB, resolveDB } from '@sovereignbase/offline-kv-store'
const db = await resolveDB()
console.log(db.name) // "offline-kv-store"
await destroyDB()Namespaces are created on demand. The first operation for a new namespace may trigger an IndexedDB version upgrade to create the backing object store.
Validation failures and runtime failures reject with errors named KVStoreError. The error object includes a code property such as:
NAME_WAS_INVALIDDATABASE_OPEN_FAILEDDATABASE_OPEN_BLOCKEDDATABASE_DELETION_FAILEDDATABASE_DELETION_BLOCKEDINDEXED_DB_TRANSACTION_FAILEDINDEXED_DB_TRANSACTION_ABORTED
- Suite: unit + integration in Node, browser E2E in Playwright.
- Browser matrix: Chromium, Firefox, WebKit, mobile Chrome, mobile Safari.
- Coverage:
c8at 100% statements / branches / functions / lines for the Node test pass. - Command:
npm run test
How it was run: npm run bench
Environment: Node v22.14.0 on win32 x64 using the repository benchmark harness.
| Operation | Workload | Time | Throughput |
|---|---|---|---|
| namespace provision + first put | 200 ops | 97.92 ms | 2,042.43 ops/s |
| put | 2000 ops | 293.87 ms | 6,805.64 ops/s |
| get | 2000 ops | 516.10 ms | 3,875.20 ops/s |
| has | 2000 ops | 565.26 ms | 3,538.22 ops/s |
| delete | 2000 ops | 823.03 ms | 2,430.05 ops/s |
| clear | 1 op | 3.58 ms | 279.30 ops/s |
Results vary by machine and runtime. These numbers come from the included Node benchmark harness, not from an in-browser benchmark.
Apache-2.0