UNPKG

seitu

Version:
130 lines (93 loc) • 3.38 kB
# createIndexedDbStorage Defines a multi-key reactive key/value store for `createIndexedDb({ stores })`. Returns a definition only; the handle (async under the hood, sync `get()` via in-memory cache) lives under `db.stores.<name>`. ```ts import { createIndexedDb, createIndexedDbStorage } from 'seitu/web' import * as z from 'zod' const db = createIndexedDb({ name: 'app', stores: { session: createIndexedDbStorage({ schemas: { token: z.string().nullable(), settings: z.object({ theme: z.enum(['light', 'dark']) }), }, defaultValues: { token: null, settings: { theme: 'light' } }, }), }, }) const { session } = db.stores session.get() await db.ready await session.set({ token: 'abc' }) await session.clear() ``` ## Key differences from WebStorage - `set()` and `clear()` return `Promise<void>` (never rejects). - No `ready` on the handle: `db.ready` resolves after initial hydration. - Cross-tab sync via `BroadcastChannel` (while subscribed). - Factory returns a definition, not a handle. `createIndexedDb` owns the connection, creates the store, builds the handle. ## Options | Option | Type | Description | |--------|------|-------------| | `schemas` | `Record<string, StandardSchema>` | Validators per key | | `defaultValues` | matching record | Default values per key | | `keyTransform?` | `(key) => string` | Remap logical key to IDB key | | `onValidationError?` | `(props) => void \| value` | Handle invalid stored data | ## Interface ```ts interface IndexedDbStorage<O> extends Subscribable<O>, Readable<O>, Writable<Partial<O>, O>, Clearable { set: (value: Partial<O> | ((prev: O) => Partial<O>)) => Promise<void> clear: () => Promise<void> hydrate: () => Promise<O> db: IndexedDb storeName: string '~': { getDefaultValue; getSchema; transformKey } } ``` Each key is one record in the store (out-of-line key = transformed key name). ## Common Mistakes ### [HIGH] Using the definition as the handle Wrong: ```ts const s = createIndexedDbStorage({ schemas, defaultValues }) createIndexedDb({ name: 'app', stores: { settings: s } }) s.get() // definition has no get ``` Correct: ```ts const db = createIndexedDb({ name: 'app', stores: { settings: createIndexedDbStorage({ schemas, defaultValues }) }, }) db.stores.settings.get() ``` The factory only describes the store. Nothing persists, reads, or subscribes without `createIndexedDb`. ### [HIGH] Treating set as synchronous Wrong: ```ts await storage.set(data) expect(storage.get()).toEqual(persistedFromDb) ``` Correct: ```ts storage.set(data) storage.subscribe(next => { /* react when cache updates */ }) ``` IndexedDB writes are async; get() returns cached value immediately. ### [MEDIUM] Using in Node without fake-indexeddb Wrong: ```ts createIndexedDbStorage({ ... }) // in vitest without polyfill ``` Correct: ```ts import 'fake-indexeddb/auto' ``` IndexedDB is browser-only; tests need fake-indexeddb polyfill. ### [MEDIUM] Using storage for lists Storage is for a handful of settings-like values. Rows go in [`create-indexed-db-table`](create-indexed-db-table.md). ## See also - [`create-indexed-db`](create-indexed-db.md) — the database that owns the connection. - [`create-web-storage`](create-web-storage.md) — same shape for localStorage/sessionStorage. ## Source `src/web/indexed-db-storage/index.ts`