@sovereignbase/offline-kv-store
v1.0.2
Published
Namespace-scoped key-value storage on top of IndexedDB with explicit error codes.
Maintainers
Readme
offline-kv-store
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.
Compatibility
- Runtimes: modern browsers and runtimes that expose global
indexedDB - Module format: ESM and CJS.
- Required globals / APIs:
indexedDB,DOMException. - TypeScript: bundled types.
Goals
- 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.
Installation
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-storeUsage
import { 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()Namespaces
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: '[email protected]',
verified: true,
})
await drafts.put('welcome', {
title: 'Hello',
body: 'Draft content goes here.',
updatedAt: new Date().toISOString(),
})Direct database lifecycle
import { destroyDB, resolveDB } from '@sovereignbase/offline-kv-store'
const db = await resolveDB()
console.log(db.name) // "offline-kv-store"
await destroyDB()Runtime Behavior
Browsers / IndexedDB runtimes
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 and errors
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
Tests
- 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
Benchmarks
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.
License
Apache-2.0
