Skip to content
vault logoVaultStorage
Typed browser storage and opt-in driver-neutral SQLite with portable keys, TTL, observation, and transactions.
Version
v3.0.0
Size
0.8 KB gzip
Dependencies
Zero dependencies
BrowserNode ≥22Deno
tablettlisExpiredvalidatorCodeccreateMemory View all 10 exports

Why Vault?

Vault gives browser and SQLite persistence one typed schema while keeping backend guarantees explicit. Use KeyValueVaultStore for portable CRUD and observation; choose IndexedDB or the opt-in SQLite subpath when you need atomic transactions or lazy iteration. Durable adapters require codecs so invalid stored data never enters typed code.

ts
// Before
localStorage.setItem('theme', JSON.stringify({ value: 'dark' }));
const theme = JSON.parse(localStorage.getItem('theme') ?? '{}').value;

// After
await store.put('preferences', { id: 'theme', value: 'dark' });
const theme = await store.get('preferences', 'theme');
FeatureVaultRaw Web StorageDexie
Bundle size0.8 KBBrowser built-inExtra dependency
Runtime dependencies
Typed schema and keysApplication-defined
Portable key-value store APIIndexedDB only
Explicit atomic transactionsIndexedDB + SQLite capability
Required durable codecs
Driver-neutral SQLiteOpt-in subpath

Use Vault when you need typed browser persistence or application-owned SQLite with honest storage capabilities and required durable codecs.

Consider raw Web Storage when you only persist one or two unstructured values. Consider Dexie when you need a broader IndexedDB ecosystem.

Installation

sh
pnpm add @vielzeug/vault
sh
npm install @vielzeug/vault
sh
yarn add @vielzeug/vault

Quick Start

Define a schema, create a key-value store with codecs, and dispose it with its owner.

ts
import { s } from '@vielzeug/spell';
import { table } from '@vielzeug/vault';
import { createLocalStorage } from '@vielzeug/vault/local-storage';

const PreferenceSchema = s.object({ id: s.string(), theme: s.union('dark', 'light') });
const store = createLocalStorage({
  name: 'app-v2',
  schema: { preferences: table<{ id: string; theme: 'dark' | 'light' }>('id') },
  codecs: { preferences: PreferenceSchema },
});

try {
  await store.put('preferences', { id: 'theme', theme: 'dark' });
  console.log(await store.get('preferences', 'theme'));
} finally {
  await store.dispose();
}

Features

  • table() defines typed records with portable string or number keys.
  • Spell and other parser schemas work directly as codecs; validatorCodec() creates an explicit identity-encoding codec.
  • /memory returns a portable KeyValueVaultStore — codecs optional (in-memory, no trust boundary).
  • /local-storage and /session-storage return KeyValueVaultStore — codecs required.
  • observe() emits current and changed table snapshots.
  • ttl creates validated expiration durations.
  • /indexeddb and /sqlite return DocumentVaultStore with batch() and iterate() — codecs required.
  • /indexeddb also exports defineMigration() for schema upgrades.
  • pruneExpired() removes stale TTL entries on demand.
  • Common helpers are bound to stores and transaction contexts; standalone forms remain exported.
  • query() provides typed filtering, ordering, pagination, counting, first-match, and deletion.

Documentation

See Also

  • Forge saves and restores form drafts through Vault stores.
  • Ripple owns application state that can persist through Vault.
  • Courier can populate persistent cache data.