@vielzeug/vault 3.0.3 → 26.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,4 +40,4 @@ stop();
40
40
 
41
41
  ## License
42
42
 
43
- MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
43
+ MIT © [Helmuth Saatkamp](https://github.com/helmuthdu): part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.cjs","names":[],"sources":["../../src/adapters/indexeddb.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n assertBatchTables,\n batchScopeGuard,\n buildDocumentStore,\n buildOperations,\n type StorageBackend,\n} from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DocumentVaultStore, DurableStoreOptions, RecordOf, TransactionContext } from '../types';\n\n/** IndexedDB-only migration context supplied to `MigrationFn` during `onupgradeneeded`. */\nexport type MigrationContext = {\n db: IDBDatabase;\n newVersion: number | null;\n oldVersion: number;\n tx: IDBTransaction;\n};\n\n/**\n * Synchronous IndexedDB schema upgrade callback, invoked from `onupgradeneeded`.\n * Tables and schema-declared indexes are created automatically; use the hook\n * only for work the schema cannot express, such as deleting a removed object\n * store or transforming records written by an older format.\n */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\nfunction idbReq<R>(request: IDBRequest<R>): Promise<R> {\n return new Promise<R>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB request failed'));\n });\n}\n\nfunction wrapTxError(scope: string, message: string, cause: unknown): VaultError {\n const causeMessage = cause instanceof Error && cause.message ? `: ${cause.message}` : '';\n\n return new VaultError(`${message} on \"${scope}\"${causeMessage}`, { cause });\n}\n\nfunction runIdbTx<T>(tx: IDBTransaction, scope: string, work: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let result: T | undefined;\n let callbackError: unknown;\n\n Promise.resolve()\n .then(work)\n .then((value) => {\n result = value;\n })\n .catch((error) => {\n callbackError = error;\n\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n });\n\n const rejectWithCallbackError = (fallbackCause: unknown, message = 'transaction failed'): void => {\n if (callbackError instanceof Error) {\n reject(callbackError);\n } else {\n reject(wrapTxError(scope, message, callbackError ?? fallbackCause));\n }\n };\n\n tx.oncomplete = () => {\n if (callbackError) {\n rejectWithCallbackError(undefined);\n\n return;\n }\n\n resolve(result as T);\n };\n tx.onerror = () => reject(wrapTxError(scope, 'transaction error', tx.error));\n tx.onabort = () => rejectWithCallbackError(tx.error, 'transaction aborted');\n });\n}\n\nasync function getAllFromStore<T extends object>(\n store: IDBIndex | IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n query?: IDBValidKey | IDBKeyRange,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll(query));\n const records: T[] = [];\n\n for (const raw of rawRecords) {\n const value = decode(raw);\n\n if (value !== undefined) records.push(value);\n }\n\n return records;\n}\n\nasync function storeGet<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<T | undefined> {\n const raw = await idbReq<unknown>(store.get(key));\n\n if (raw == null) return undefined;\n\n return decode(raw);\n}\n\nasync function storeDelete<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n const live = (await storeGet<T>(store, key, decode)) !== undefined;\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: readonly IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n const results = await Promise.all(keys.map((key) => storeDelete(store, key, decode)));\n return results.filter(Boolean).length;\n}\n\nfunction pruneExpiredInStore(store: IDBObjectStore): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n let deleted = 0;\n const request = store.openCursor();\n\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB cursor failed during prune'));\n request.onsuccess = () => {\n const cursor = request.result;\n\n if (!cursor) {\n resolve(deleted);\n\n return;\n }\n\n const stored = parseStored(cursor.value as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n cursor.delete();\n deleted += 1;\n }\n\n cursor.continue();\n };\n });\n}\n\n/**\n * Cursor state machine — a single discriminated union replaces five boolean/nullable variables.\n * Transitions: idle → waiting (next() before cursor fires) | buffered (cursor fires first) | done | error\n */\ntype CursorState<T> =\n | { type: 'idle' }\n | { reject: (e: unknown) => void; resolve: (r: IteratorResult<T>) => void; type: 'waiting' }\n | { results: IteratorResult<T>[]; type: 'buffered' }\n | { error: unknown; type: 'error' }\n | { type: 'done' };\n\n/**\n * F1: True cursor-based iteration for IndexedDB.\n * Yields live records one-by-one using an IDB cursor, avoiding materializing the full table.\n * This is memory-efficient for large tables — the cursor walks the store incrementally.\n *\n * Design: the cursor is opened *synchronously* in [Symbol.asyncIterator]() so that event\n * handlers are wired immediately (no queueMicrotask races). The cursor is advanced *eagerly*\n * before yielding — this keeps the IDB readonly transaction alive between consumer awaits,\n * because IDB auto-commits when there are no pending requests.\n */\nfunction iterateStoreWithCursor<T extends object>(\n store: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const cursorRequest = store.openCursor();\n let state: CursorState<T> = { type: 'idle' };\n\n const deliver = (result: IteratorResult<T>): void => {\n if (state.type === 'waiting') {\n const { resolve } = state;\n\n state = result.done ? { type: 'done' } : { type: 'idle' };\n resolve(result);\n } else if (state.type === 'buffered') {\n state.results.push(result);\n } else {\n state = { results: [result], type: 'buffered' };\n }\n };\n\n cursorRequest.onerror = () => {\n const err = cursorRequest.error ?? new VaultError('IndexedDB cursor iteration failed');\n\n if (state.type === 'waiting') {\n const { reject } = state;\n\n state = { type: 'done' };\n reject(err);\n } else {\n state = { error: err, type: 'error' };\n }\n };\n\n cursorRequest.onsuccess = () => {\n if (state.type === 'done') return;\n const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n let value: T | undefined;\n try {\n value = decode(cursor.value as unknown);\n } catch (error) {\n if (state.type === 'waiting') {\n const { reject } = state;\n state = { type: 'done' };\n reject(error);\n } else {\n state = { error, type: 'error' };\n }\n return;\n }\n\n // Advance eagerly BEFORE yielding to keep the IDB transaction alive.\n cursor.continue();\n\n if (value !== undefined) deliver({ done: false, value });\n };\n\n return {\n next(): Promise<IteratorResult<T>> {\n if (state.type === 'error') {\n const { error } = state;\n\n state = { type: 'done' };\n\n return Promise.reject(error);\n }\n\n if (state.type === 'buffered') {\n const [result, ...results] = state.results;\n\n state = result.done\n ? { type: 'done' }\n : results.length > 0\n ? { results, type: 'buffered' }\n : { type: 'idle' };\n\n return Promise.resolve(result);\n }\n\n if (state.type === 'done') return Promise.resolve({ done: true, value: undefined });\n\n return new Promise<IteratorResult<T>>((resolve, reject) => {\n state = { reject, resolve, type: 'waiting' };\n });\n },\n\n return(value?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.resolve({ done: true, value: undefined });\n\n state = { type: 'done' };\n\n return Promise.resolve({ done: true, value });\n },\n\n throw(err?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.reject(err);\n\n state = { type: 'done' };\n\n return Promise.reject(err);\n },\n };\n },\n };\n}\n\nexport interface IndexedDbVaultStore<S extends AnySchema> extends DocumentVaultStore<S> {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]>;\n}\n\ntype IndexedDbOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n migrate?: MigrationFn;\n name: string;\n /** Schema version. Must be a positive integer. Increment when adding tables or changing the schema, then provide `migrate`. Defaults to 1. */\n version?: number;\n};\n\nexport function createIndexedDB<S extends AnySchema>(options: IndexedDbOptions<S>): IndexedDbVaultStore<S> {\n const { migrate, name, schema, codecs, version = 1 } = options;\n\n if (!codecs) {\n throw new VaultError('createIndexedDB: codecs are required for durable persistence');\n }\n\n if (!Number.isInteger(version) || version < 1) {\n throw new VaultError(`createIndexedDB: version must be a positive integer, got ${String(version)}`);\n }\n\n // Fixed envelopes keep IndexedDB records and `value.<field>` indexes portable across adapters.\n // Table-aware codec encode/decode is applied in the backend methods below.\n\n const channel = typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel(`vault:${name}`) : undefined;\n\n let db: IDBDatabase | null = null;\n let connectPromise: Promise<void> | null = null;\n let disposed = false;\n\n const createObjectStores = (target: IDBDatabase, tx: IDBTransaction): void => {\n for (const [tableName, entry] of Object.entries(schema)) {\n let store: IDBObjectStore;\n\n if (!target.objectStoreNames.contains(tableName)) {\n store = target.createObjectStore(tableName);\n } else {\n store = tx.objectStore(tableName);\n }\n\n // F5: Create secondary indexes for fields declared via .index() on the table() builder.\n // Stored format is { value: T, expiresAt?: number } so IDB keyPath is `value.<field>`.\n const indexes = (entry as { indexes?: readonly string[] }).indexes ?? [];\n\n for (const field of indexes) {\n if (!store.indexNames.contains(field)) {\n store.createIndex(field, `value.${field}`);\n }\n }\n }\n };\n\n const connect = async (): Promise<void> => {\n if (!connectPromise) {\n connectPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(name, version);\n\n request.onupgradeneeded = (event) => {\n const target = request.result;\n const tx = request.transaction!;\n\n if (migrate) {\n try {\n migrate({\n db: target,\n newVersion: (event as IDBVersionChangeEvent).newVersion ?? null,\n oldVersion: event.oldVersion,\n tx,\n });\n } catch (error) {\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n\n reject(new VaultMigrationError(`migration failed for \"${name}\"`, { cause: error }));\n\n return;\n }\n }\n\n createObjectStores(target, tx);\n };\n\n request.onsuccess = () => {\n if (disposed) {\n request.result.close();\n resolve();\n\n return;\n }\n\n const connection = request.result;\n\n connection.onversionchange = () => {\n connection.close();\n db = null;\n connectPromise = null;\n };\n\n db = connection;\n resolve();\n };\n request.onerror = () => {\n connectPromise = null;\n reject(new VaultError(`failed to open \"${name}\"`, { cause: request.error }));\n };\n });\n }\n\n return connectPromise;\n };\n\n const withStore = async <T>(\n table: keyof S,\n mode: 'readonly' | 'readwrite',\n fn: (store: IDBObjectStore) => Promise<T>,\n ): Promise<T> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const tableName = String(table);\n const tx = db.transaction(tableName, mode);\n\n return runIdbTx(tx, `${name}/${tableName}`, () => fn(tx.objectStore(tableName)));\n };\n\n const requireDb = async (): Promise<IDBDatabase> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return db;\n };\n\n const publish = <K extends keyof S>(table: K): void => {\n channel?.postMessage({ table: String(table) });\n };\n\n // Table-aware decode: unwrap envelope + apply codec.decode\n const decodeWithCodec = <T extends object>(table: keyof S, raw: unknown): T | undefined => {\n const stored = parseStored<unknown>(raw);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: err });\n }\n };\n\n // Table-aware encode: apply codec.encode + wrap in envelope\n const encodeWithCodec = <T>(table: keyof S, value: T, ttl?: number): StoredRecord<unknown> => {\n const record = value as RecordOf<S, typeof table>;\n const encoded = encodeRecord(codecs[table], record);\n\n for (const field of schema[table].indexes ?? []) {\n const indexedValue = (record as Record<string, unknown>)[field];\n if (indexedValue === undefined) continue;\n\n if (\n typeof encoded !== 'object' ||\n encoded === null ||\n !Object.hasOwn(encoded, field) ||\n !Object.is((encoded as Record<string, unknown>)[field], indexedValue)\n ) {\n throw new VaultError(`codec output for table \"${String(table)}\" must preserve indexed field \"${field}\"`);\n }\n }\n\n return ttl === undefined ? { value: encoded } : { expiresAt: Date.now() + ttl, value: encoded };\n };\n\n const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n deleteMany: (table, keys) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, typeof table>>(s, keys.map(encodeVaultKey), decode);\n }),\n\n async dispose(): Promise<void> {\n disposed = true;\n channel?.close();\n\n // F7: Wait for any in-progress connect before closing the DB to avoid\n // \"database connection is closing\" errors on in-flight requests.\n if (connectPromise) await connectPromise.catch(() => {});\n\n db?.close();\n db = null;\n connectPromise = null;\n },\n\n get: (table, key) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n getAll: (table) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n }),\n\n getMany: (table, keys) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)));\n }),\n\n iterate: (table) => {\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, typeof table>>> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db || disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const idb = db;\n const tx = idb.transaction(String(table), 'readonly');\n const store = tx.objectStore(String(table));\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n store,\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n };\n\n let inner: AsyncIterator<RecordOf<S, typeof table>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, typeof table>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, typeof table>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.return?.(value) ?? Promise.resolve({ done: true, value });\n\n return Promise.resolve({ done: true, value });\n },\n throw(err?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n\n async pruneAllExpired() {\n const idb = await requireDb();\n const tableNames = Object.keys(schema);\n const tx = idb.transaction(tableNames, 'readwrite');\n const results = await runIdbTx(tx, `${name}/pruneAll`, () =>\n Promise.all(tableNames.map(async (t) => [t, await pruneExpiredInStore(tx.objectStore(t))] as const)),\n );\n\n return Object.fromEntries(results);\n },\n\n pruneExpiredInTable: (table) => withStore(table, 'readwrite', (s) => pruneExpiredInStore(s)),\n\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return withStore(table, 'readwrite', (s) => idbReq(s.put(encoded, key)).then(() => undefined));\n },\n\n putAll(table, values, ttl) {\n const encodedValues = values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n\n return { encoded: encodeWithCodec(table, v, ttl), key };\n });\n\n return withStore(table, 'readwrite', (s) =>\n Promise.all(encodedValues.map(({ encoded, key }) => idbReq(s.put(encoded, key)))).then(() => undefined),\n );\n },\n };\n\n const idbBatch = async <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n notifyMutation: (table: K) => void,\n validateFn: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n ): Promise<R> => {\n assertBatchTables(tables);\n\n const idb = await requireDb();\n const idbTx = idb.transaction([...tables] as string[], 'readwrite');\n const dirtyTables = new Set<K>();\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n const batchCore: StorageBackend<S, K> = {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n delete: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode);\n },\n deleteMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode);\n },\n get: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode);\n },\n getAll: (table) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode);\n },\n getMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n const store = storeOf(table);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(store, encodeVaultKey(key), decode)));\n },\n iterate(table) {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n storeOf(table),\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n },\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return idbReq(storeOf(table).put(encoded, key)).then(() => undefined);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n const encoded = encodeWithCodec(table, v, ttl);\n\n return idbReq(storeOf(table).put(encoded, key));\n }),\n ).then(() => undefined);\n },\n };\n\n let contextActive = true;\n const tx = buildOperations<S, K>(\n schema,\n batchCore,\n (t) => dirtyTables.add(t),\n validateFn,\n batchScopeGuard(tables, () => contextActive),\n );\n\n try {\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) notifyMutation(table);\n\n return result;\n } finally {\n contextActive = false;\n }\n };\n\n const adapter = buildDocumentStore(schema, core, {\n codecs,\n createBatch: (deps) => (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate),\n onCrossTabMessage(notify) {\n if (!channel) {\n return undefined;\n }\n\n channel.onmessage = (event: MessageEvent<{ table?: string }>) => {\n const tableName = event.data?.table;\n\n if (!tableName || !Object.hasOwn(schema, tableName)) return;\n\n notify(tableName as keyof S & string);\n };\n\n return () => {\n channel.onmessage = null;\n };\n },\n onMutation: publish,\n schema,\n });\n\n return Object.assign(adapter, {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]> {\n return withStore(table, 'readonly', (store) => {\n if (!store.indexNames.contains(field)) throw new VaultError(`Table \"${table}\" has no index \"${field}\"`);\n\n const decode = (raw: unknown): RecordOf<S, K> | undefined => decodeWithCodec<RecordOf<S, K>>(table, raw);\n return getAllFromStore(store.index(field), decode, value as IDBValidKey);\n });\n },\n });\n}\n"],"mappings":"iJA+BA,SAAS,OAAU,EAAoC,CACrD,OAAO,IAAI,SAAY,EAAS,IAAW,CACzC,EAAQ,cAAkB,EAAQ,EAAQ,MAAM,EAChD,EAAQ,YAAgB,EAAO,EAAQ,OAAS,IAAI,EAAA,WAAW,0BAA0B,CAAC,CAC5F,CAAC,CACH,CAEA,SAAS,YAAY,EAAe,EAAiB,EAA4B,CAC/E,IAAM,EAAe,aAAiB,OAAS,EAAM,QAAU,KAAK,EAAM,UAAY,GAEtF,OAAO,IAAI,EAAA,WAAW,GAAG,EAAQ,OAAO,EAAM,GAAG,IAAgB,CAAE,OAAM,CAAC,CAC5E,CAEA,SAAS,SAAY,EAAoB,EAAe,EAAoC,CAC1F,OAAO,IAAI,SAAY,EAAS,IAAW,CACzC,IAAI,EACA,EAEJ,QAAQ,QAAQ,CAAC,CACd,KAAK,CAAI,CAAC,CACV,KAAM,GAAU,CACf,EAAS,CACX,CAAC,CAAC,CACD,MAAO,GAAU,CAChB,EAAgB,EAEhB,GAAI,CACF,EAAG,MAAM,CACX,MAAQ,CAER,CACF,CAAC,EAEH,IAAM,yBAA2B,EAAwB,EAAU,uBAA+B,CAC5F,aAAyB,MAC3B,EAAO,CAAa,EAEpB,EAAO,YAAY,EAAO,EAAS,GAAiB,CAAa,CAAC,CAEtE,EAEA,EAAG,eAAmB,CACpB,GAAI,EAAe,CACjB,wBAAwB,IAAA,EAAS,EAEjC,MACF,CAEA,EAAQ,CAAW,CACrB,EACA,EAAG,YAAgB,EAAO,YAAY,EAAO,oBAAqB,EAAG,KAAK,CAAC,EAC3E,EAAG,YAAgB,wBAAwB,EAAG,MAAO,qBAAqB,CAC5E,CAAC,CACH,CAEA,eAAe,gBACb,EACA,EACA,EACc,CACd,IAAM,EAAa,MAAM,OAAkB,EAAM,OAAO,CAAK,CAAC,EACxD,EAAe,CAAC,EAEtB,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,CAAG,EAEpB,IAAU,IAAA,IAAW,EAAQ,KAAK,CAAK,CAC7C,CAEA,OAAO,CACT,CAEA,eAAe,SACb,EACA,EACA,EACwB,CACxB,IAAM,EAAM,MAAM,OAAgB,EAAM,IAAI,CAAG,CAAC,EAE5C,MAAO,KAEX,OAAO,EAAO,CAAG,CACnB,CAEA,eAAe,YACb,EACA,EACA,EACkB,CAClB,IAAM,EAAQ,MAAM,SAAY,EAAO,EAAK,CAAM,IAAO,IAAA,GAIzD,OAFA,MAAM,OAAO,EAAM,OAAO,CAAG,CAAC,EAEvB,CACT,CAEA,eAAe,gBACb,EACA,EACA,EACiB,CAEjB,OAAO,MADe,QAAQ,IAAI,EAAK,IAAK,GAAQ,YAAY,EAAO,EAAK,CAAM,CAAC,CAAC,EAAA,CACrE,OAAO,OAAO,CAAC,CAAC,MACjC,CAEA,SAAS,oBAAoB,EAAwC,CACnE,OAAO,IAAI,SAAiB,EAAS,IAAW,CAC9C,IAAI,EAAU,EACR,EAAU,EAAM,WAAW,EAEjC,EAAQ,YAAgB,EAAO,EAAQ,OAAS,IAAI,EAAA,WAAW,sCAAsC,CAAC,EACtG,EAAQ,cAAkB,CACxB,IAAM,EAAS,EAAQ,OAEvB,GAAI,CAAC,EAAQ,CACX,EAAQ,CAAO,EAEf,MACF,CAEA,IAAM,EAAS,EAAA,YAAY,EAAO,KAAgB,GAE9C,CAAC,GAAU,EAAA,UAAU,EAAO,SAAS,KACvC,EAAO,OAAO,EACd,GAAW,GAGb,EAAO,SAAS,CAClB,CACF,CAAC,CACH,CAuBA,SAAS,uBACP,EACA,EACkB,CAClB,MAAO,CACL,CAAC,OAAO,gBAAmC,CACzC,IAAM,EAAgB,EAAM,WAAW,EACnC,EAAwB,CAAE,KAAM,MAAO,EAErC,QAAW,GAAoC,CACnD,GAAI,EAAM,OAAS,UAAW,CAC5B,GAAM,CAAE,WAAY,EAEpB,EAAQ,EAAO,KAAO,CAAE,KAAM,MAAO,EAAI,CAAE,KAAM,MAAO,EACxD,EAAQ,CAAM,CAChB,MAAW,EAAM,OAAS,WACxB,EAAM,QAAQ,KAAK,CAAM,EAEzB,EAAQ,CAAE,QAAS,CAAC,CAAM,EAAG,KAAM,UAAW,CAElD,EA6CA,MA3CA,GAAc,YAAgB,CAC5B,IAAM,EAAM,EAAc,OAAS,IAAI,EAAA,WAAW,mCAAmC,EAErF,GAAI,EAAM,OAAS,UAAW,CAC5B,GAAM,CAAE,UAAW,EAEnB,EAAQ,CAAE,KAAM,MAAO,EACvB,EAAO,CAAG,CACZ,KACE,GAAQ,CAAE,MAAO,EAAK,KAAM,OAAQ,CAExC,EAEA,EAAc,cAAkB,CAC9B,GAAI,EAAM,OAAS,OAAQ,OAC3B,IAAM,EAAS,EAAc,OAE7B,GAAI,CAAC,EAAQ,CACX,QAAQ,CAAE,KAAM,GAAM,MAAO,IAAA,EAAU,CAAC,EAExC,MACF,CAEA,IAAI,EACJ,GAAI,CACF,EAAQ,EAAO,EAAO,KAAgB,CACxC,OAAS,EAAO,CACd,GAAI,EAAM,OAAS,UAAW,CAC5B,GAAM,CAAE,UAAW,EACnB,EAAQ,CAAE,KAAM,MAAO,EACvB,EAAO,CAAK,CACd,KACE,GAAQ,CAAE,QAAO,KAAM,OAAQ,EAEjC,MACF,CAGA,EAAO,SAAS,EAEZ,IAAU,IAAA,IAAW,QAAQ,CAAE,KAAM,GAAO,OAAM,CAAC,CACzD,EAEO,CACL,MAAmC,CACjC,GAAI,EAAM,OAAS,QAAS,CAC1B,GAAM,CAAE,SAAU,EAIlB,MAFA,GAAQ,CAAE,KAAM,MAAO,EAEhB,QAAQ,OAAO,CAAK,CAC7B,CAEA,GAAI,EAAM,OAAS,WAAY,CAC7B,GAAM,CAAC,EAAQ,GAAG,GAAW,EAAM,QAQnC,MANA,GAAQ,EAAO,KACX,CAAE,KAAM,MAAO,EACf,EAAQ,OAAS,EACf,CAAE,UAAS,KAAM,UAAW,EAC5B,CAAE,KAAM,MAAO,EAEd,QAAQ,QAAQ,CAAM,CAC/B,CAIA,OAFI,EAAM,OAAS,OAAe,QAAQ,QAAQ,CAAE,KAAM,GAAM,MAAO,IAAA,EAAU,CAAC,EAE3E,IAAI,SAA4B,EAAS,IAAW,CACzD,EAAQ,CAAE,SAAQ,UAAS,KAAM,SAAU,CAC7C,CAAC,CACH,EAEA,OAAO,EAA6C,CAKlD,OAJI,EAAM,OAAS,WAAW,EAAM,QAAQ,CAAE,KAAM,GAAM,MAAO,IAAA,EAAU,CAAC,EAE5E,EAAQ,CAAE,KAAM,MAAO,EAEhB,QAAQ,QAAQ,CAAE,KAAM,GAAM,OAAM,CAAC,CAC9C,EAEA,MAAM,EAA2C,CAK/C,OAJI,EAAM,OAAS,WAAW,EAAM,OAAO,CAAG,EAE9C,EAAQ,CAAE,KAAM,MAAO,EAEhB,QAAQ,OAAO,CAAG,CAC3B,CACF,CACF,CACF,CACF,CAiBA,SAAgB,gBAAqC,EAAsD,CACzG,GAAM,CAAE,UAAS,OAAM,SAAQ,SAAQ,UAAU,GAAM,EAEvD,GAAI,CAAC,EACH,MAAM,IAAI,EAAA,WAAW,8DAA8D,EAGrF,GAAI,CAAC,OAAO,UAAU,CAAO,GAAK,EAAU,EAC1C,MAAM,IAAI,EAAA,WAAW,4DAA4D,OAAO,CAAO,GAAG,EAMpG,IAAM,EAAU,OAAO,iBAAqB,IAAc,IAAI,iBAAiB,SAAS,GAAM,EAAI,IAAA,GAE9F,EAAyB,KACzB,EAAuC,KACvC,EAAW,GAET,oBAAsB,EAAqB,IAA6B,CAC5E,IAAK,GAAM,CAAC,EAAW,KAAU,OAAO,QAAQ,CAAM,EAAG,CACvD,IAAI,EAEJ,AACE,EADG,EAAO,iBAAiB,SAAS,CAAS,EAGrC,EAAG,YAAY,CAAS,EAFxB,EAAO,kBAAkB,CAAS,EAO5C,IAAM,EAAW,EAA0C,SAAW,CAAC,EAEvE,IAAK,IAAM,KAAS,EACb,EAAM,WAAW,SAAS,CAAK,GAClC,EAAM,YAAY,EAAO,SAAS,GAAO,CAG/C,CACF,EAEM,QAAU,UACd,AACE,IAAiB,IAAI,SAAS,EAAS,IAAW,CAChD,IAAM,EAAU,UAAU,KAAK,EAAM,CAAO,EAE5C,EAAQ,gBAAmB,GAAU,CACnC,IAAM,EAAS,EAAQ,OACjB,EAAK,EAAQ,YAEnB,GAAI,EACF,GAAI,CACF,EAAQ,CACN,GAAI,EACJ,WAAa,EAAgC,YAAc,KAC3D,WAAY,EAAM,WAClB,IACF,CAAC,CACH,OAAS,EAAO,CACd,GAAI,CACF,EAAG,MAAM,CACX,MAAQ,CAER,CAEA,EAAO,IAAI,EAAA,oBAAoB,yBAAyB,EAAK,GAAI,CAAE,MAAO,CAAM,CAAC,CAAC,EAElF,MACF,CAGF,mBAAmB,EAAQ,CAAE,CAC/B,EAEA,EAAQ,cAAkB,CACxB,GAAI,EAAU,CACZ,EAAQ,OAAO,MAAM,EACrB,EAAQ,EAER,MACF,CAEA,IAAM,EAAa,EAAQ,OAE3B,EAAW,oBAAwB,CACjC,EAAW,MAAM,EACjB,EAAK,KACL,EAAiB,IACnB,EAEA,EAAK,EACL,EAAQ,CACV,EACA,EAAQ,YAAgB,CACtB,EAAiB,KACjB,EAAO,IAAI,EAAA,WAAW,mBAAmB,EAAK,GAAI,CAAE,MAAO,EAAQ,KAAM,CAAC,CAAC,CAC7E,CACF,CAAC,EAGI,GAGH,UAAY,MAChB,EACA,EACA,IACe,CAKf,GAJI,IAEC,GAAI,MAAM,QAAQ,EAEnB,CAAC,GAAI,MAAM,IAAI,EAAA,mBAAmB,IAAI,EAAK,cAAc,EAE7D,IAAM,EAAY,OAAO,CAAK,EACxB,EAAK,EAAG,YAAY,EAAW,CAAI,EAEzC,OAAO,SAAS,EAAI,GAAG,EAAK,GAAG,QAAmB,EAAG,EAAG,YAAY,CAAS,CAAC,CAAC,CACjF,EAEM,UAAY,SAAkC,CAKlD,GAJI,IAEC,GAAI,MAAM,QAAQ,EAEnB,CAAC,GAAI,MAAM,IAAI,EAAA,mBAAmB,IAAI,EAAK,cAAc,EAE7D,OAAO,CACT,EAEM,QAA8B,GAAmB,CACrD,GAAS,YAAY,CAAE,MAAO,OAAO,CAAK,CAAE,CAAC,CAC/C,EAGM,iBAAqC,EAAgB,IAAgC,CACzF,IAAM,EAAS,EAAA,YAAqB,CAAG,EAEnC,GAAC,GAAU,GAAA,UAAU,EAAO,SAAS,EAEzC,GAAI,CACF,OAAO,EAAA,aAAa,EAAO,GAAQ,EAAO,KAAK,CACjD,OAAS,EAAK,CACZ,MAAM,IAAI,EAAA,WAAW,gCAAgC,OAAO,CAAK,EAAE,GAAI,CAAE,MAAO,CAAI,CAAC,CACvF,CACF,EAGM,iBAAsB,EAAgB,EAAU,IAAwC,CAC5F,IAAM,EAAS,EACT,EAAU,EAAA,aAAa,EAAO,GAAQ,CAAM,EAElD,IAAK,IAAM,KAAS,EAAO,EAAM,CAAC,SAAW,CAAC,EAAG,CAC/C,IAAM,EAAgB,EAAmC,GACrD,OAAiB,IAAA,KAGnB,OAAO,GAAY,WACnB,GACA,CAAC,OAAO,OAAO,EAAS,CAAK,GAC7B,CAAC,OAAO,GAAI,EAAoC,GAAQ,CAAY,GAEpE,MAAM,IAAI,EAAA,WAAW,2BAA2B,OAAO,CAAK,EAAE,iCAAiC,EAAM,EAAE,CAE3G,CAEA,OAAO,IAAQ,IAAA,GAAY,CAAE,MAAO,CAAQ,EAAI,CAAE,UAAW,KAAK,IAAI,EAAI,EAAK,MAAO,CAAQ,CAChG,EAEM,EAA0B,CAC9B,MAAQ,GAAU,UAAU,EAAO,YAAc,GAAM,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,SAAW,IAAA,EAAS,CAAC,EAE9F,QAAS,EAAO,IACd,UAAU,EAAO,YAAc,GAAM,CACnC,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,YAAuC,EAAG,EAAA,eAAe,CAAG,EAAG,MAAM,CAC9E,CAAC,EAEH,YAAa,EAAO,IAClB,UAAU,EAAO,YAAc,GAAM,CACnC,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAC/F,OAAO,gBAA2C,EAAG,EAAK,IAAI,EAAA,cAAc,EAAG,MAAM,CACvF,CAAC,EAEH,MAAM,SAAyB,CAC7B,EAAW,GACX,GAAS,MAAM,EAIX,GAAgB,MAAM,EAAe,UAAY,CAAC,CAAC,EAEvD,GAAI,MAAM,EACV,EAAK,KACL,EAAiB,IACnB,EAEA,KAAM,EAAO,IACX,UAAU,EAAO,WAAa,GAAM,CAClC,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,SAAoC,EAAG,EAAA,eAAe,CAAG,EAAG,MAAM,CAC3E,CAAC,EAEH,OAAS,GACP,UAAU,EAAO,WAAa,GAAM,CAClC,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,gBAA2C,EAAG,MAAM,CAC7D,CAAC,EAEH,SAAU,EAAO,IACf,UAAU,EAAO,WAAa,GAAM,CAClC,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAC/F,OAAO,QAAQ,IAAI,EAAK,IAAK,GAAQ,SAAoC,EAAG,EAAA,eAAe,CAAG,EAAG,MAAM,CAAC,CAAC,CAC3G,CAAC,EAEH,QAAU,GAAU,CAElB,IAAM,YAAc,SAA+D,CAKjF,GAJI,IAEC,GAAI,MAAM,QAAQ,EAEnB,CAAC,GAAM,GAAU,MAAM,IAAI,EAAA,mBAAmB,IAAI,EAAK,cAAc,EAIzE,IAAM,EADK,EAAI,YAAY,OAAO,CAAK,EAAG,UAC5B,CAAA,CAAG,YAAY,OAAO,CAAK,CAAC,EACpC,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,uBACL,EACA,MACF,CACF,EAEI,EAEE,cACJ,YAAY,CAAC,CAAC,KAAM,IAClB,EAAQ,EAAS,OAAO,cAAc,CAAC,EAEhC,EACR,EAEH,MAAO,CACL,CAAC,OAAO,gBAA2D,CACjE,MAAO,CACL,MAA2D,CAGzD,OAFI,EAAc,EAAM,KAAK,EAEtB,UAAU,CAAC,CAAC,KAAM,GAAO,EAAG,KAAK,CAAC,CAC3C,EACA,OAAO,EAAqE,CAG1E,OAFI,EAAc,EAAM,SAAS,CAAK,GAAK,QAAQ,QAAQ,CAAE,KAAM,GAAM,OAAM,CAAC,EAEzE,QAAQ,QAAQ,CAAE,KAAM,GAAM,OAAM,CAAC,CAC9C,EACA,MAAM,EAAmE,CAGvE,OAFI,EAAc,EAAM,QAAQ,CAAG,GAAK,QAAQ,OAAO,CAAG,EAEnD,QAAQ,OAAO,CAAG,CAC3B,CACF,CACF,CACF,CACF,EAEA,MAAM,iBAAkB,CACtB,IAAM,EAAM,MAAM,UAAU,EACtB,EAAa,OAAO,KAAK,CAAM,EAC/B,EAAK,EAAI,YAAY,EAAY,WAAW,EAC5C,EAAU,MAAM,SAAS,EAAI,GAAG,EAAK,eACzC,QAAQ,IAAI,EAAW,IAAI,KAAO,IAAM,CAAC,EAAG,MAAM,oBAAoB,EAAG,YAAY,CAAC,CAAC,CAAC,CAAU,CAAC,CACrG,EAEA,OAAO,OAAO,YAAY,CAAO,CACnC,EAEA,oBAAsB,GAAU,UAAU,EAAO,YAAc,GAAM,oBAAoB,CAAC,CAAC,EAE3F,IAAI,EAAO,EAAO,EAAK,CACrB,IAAM,EAAM,EAAA,eAAe,EAAA,aAAa,EAAQ,EAAO,CAAK,CAAC,EACvD,EAAU,gBAAgB,EAAO,EAAO,CAAG,EAEjD,OAAO,UAAU,EAAO,YAAc,GAAM,OAAO,EAAE,IAAI,EAAS,CAAG,CAAC,CAAC,CAAC,SAAW,IAAA,EAAS,CAAC,CAC/F,EAEA,OAAO,EAAO,EAAQ,EAAK,CACzB,IAAM,EAAgB,EAAO,IAAK,GAAM,CACtC,IAAM,EAAM,EAAA,eAAe,EAAA,aAAa,EAAQ,EAAO,CAAC,CAAC,EAEzD,MAAO,CAAE,QAAS,gBAAgB,EAAO,EAAG,CAAG,EAAG,KAAI,CACxD,CAAC,EAED,OAAO,UAAU,EAAO,YAAc,GACpC,QAAQ,IAAI,EAAc,KAAK,CAAE,UAAS,SAAU,OAAO,EAAE,IAAI,EAAS,CAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAW,IAAA,EAAS,CACxG,CACF,CACF,EAEM,SAAW,MACf,EACA,EACA,EACA,IACe,CACf,EAAA,kBAAkB,CAAM,EAGxB,IAAM,GAAQ,MADI,UAAU,EAAA,CACV,YAAY,CAAC,GAAG,CAAM,EAAe,WAAW,EAC5D,EAAc,IAAI,IAClB,QAAW,GAA6B,EAAM,YAAY,CAAK,EAE/D,EAAkC,CACtC,MAAO,KAAO,IAAU,CACtB,MAAM,OAAO,QAAQ,CAAK,CAAC,CAAC,MAAM,CAAC,CACrC,EACA,QAAS,EAAO,IAAQ,CACtB,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,YAA4B,QAAQ,CAAK,EAAG,EAAA,eAAe,CAAG,EAAG,MAAM,CAChF,EACA,YAAa,EAAO,IAAS,CAC3B,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAC/F,OAAO,gBAAgC,QAAQ,CAAK,EAAG,EAAK,IAAI,EAAA,cAAc,EAAG,MAAM,CACzF,EACA,KAAM,EAAO,IAAQ,CACnB,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,SAAoC,QAAQ,CAAK,EAAG,EAAA,eAAe,CAAG,EAAG,MAAM,CACxF,EACA,OAAS,GAAU,CACjB,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,gBAA2C,QAAQ,CAAK,EAAG,MAAM,CAC1E,EACA,SAAU,EAAO,IAAS,CACxB,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EACzF,EAAQ,QAAQ,CAAK,EAC3B,OAAO,QAAQ,IAAI,EAAK,IAAK,GAAQ,SAAoC,EAAO,EAAA,eAAe,CAAG,EAAG,MAAM,CAAC,CAAC,CAC/G,EACA,QAAQ,EAAO,CACb,IAAM,OAA4B,GAAgC,gBAAmB,EAAO,CAAG,EAE/F,OAAO,uBACL,QAAQ,CAAK,EACb,MACF,CACF,EACA,oBAAsB,GAAU,oBAAoB,QAAQ,CAAK,CAAC,EAClE,IAAI,EAAO,EAAO,EAAK,CACrB,IAAM,EAAM,EAAA,eAAe,EAAA,aAAa,EAAQ,EAAO,CAAK,CAAC,EACvD,EAAU,gBAAgB,EAAO,EAAO,CAAG,EAEjD,OAAO,OAAO,QAAQ,CAAK,CAAC,CAAC,IAAI,EAAS,CAAG,CAAC,CAAC,CAAC,SAAW,IAAA,EAAS,CACtE,EACA,OAAO,EAAO,EAAQ,EAAK,CACzB,OAAO,QAAQ,IACb,EAAO,IAAK,GAAM,CAChB,IAAM,EAAM,EAAA,eAAe,EAAA,aAAa,EAAQ,EAAO,CAAC,CAAC,EACnD,EAAU,gBAAgB,EAAO,EAAG,CAAG,EAE7C,OAAO,OAAO,QAAQ,CAAK,CAAC,CAAC,IAAI,EAAS,CAAG,CAAC,CAChD,CAAC,CACH,CAAC,CAAC,SAAW,IAAA,EAAS,CACxB,CACF,EAEI,EAAgB,GACd,EAAK,EAAA,gBACT,EACA,EACC,GAAM,EAAY,IAAI,CAAC,EACxB,EACA,EAAA,gBAAgB,MAAc,CAAa,CAC7C,EAEA,GAAI,CACF,IAAM,EAAS,MAAM,SAAS,EAAO,MAAY,EAAG,CAAE,CAAC,EAEvD,IAAK,IAAM,KAAS,EAAa,EAAe,CAAK,EAErD,OAAO,CACT,QAAU,CACR,EAAgB,EAClB,CACF,EAEM,EAAU,EAAA,mBAAmB,EAAQ,EAAM,CAC/C,SACA,YAAc,IAAU,EAAQ,IAAO,SAAS,EAAQ,EAAI,EAAK,eAAgB,EAAK,QAAQ,EAC9F,kBAAkB,EAAQ,CACnB,KAYL,MARA,GAAQ,UAAa,GAA4C,CAC/D,IAAM,EAAY,EAAM,MAAM,MAEzB,GAAc,OAAO,OAAO,EAAQ,CAAS,GAElD,EAAO,CAA6B,CACtC,MAEa,CACX,EAAQ,UAAY,IACtB,CACF,EACA,WAAY,QACZ,QACF,CAAC,EAED,OAAO,OAAO,OAAO,EAAS,CAC5B,cACE,EACA,EACA,EAC2B,CAC3B,OAAO,UAAU,EAAO,WAAa,GAAU,CAC7C,GAAI,CAAC,EAAM,WAAW,SAAS,CAAK,EAAG,MAAM,IAAI,EAAA,WAAW,UAAU,EAAM,kBAAkB,EAAM,EAAE,EAEtG,IAAM,OAAU,GAA6C,gBAAgC,EAAO,CAAG,EACvG,OAAO,gBAAgB,EAAM,MAAM,CAAK,EAAG,OAAQ,CAAoB,CACzE,CAAC,CACH,CACF,CAAC,CACH"}
1
+ {"version":3,"file":"indexeddb.cjs","names":[],"sources":["../../src/adapters/indexeddb.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n assertBatchTables,\n batchScopeGuard,\n buildDocumentStore,\n buildOperations,\n type StorageBackend,\n} from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DocumentVaultStore, DurableStoreOptions, RecordOf, TransactionContext } from '../types';\n\n/** IndexedDB-only migration context supplied to `MigrationFn` during `onupgradeneeded`. */\nexport type MigrationContext = {\n db: IDBDatabase;\n newVersion: number | null;\n oldVersion: number;\n tx: IDBTransaction;\n};\n\n/**\n * Synchronous IndexedDB schema upgrade callback, invoked from `onupgradeneeded`.\n * Tables and schema-declared indexes are created automatically; use the hook\n * only for work the schema cannot express, such as deleting a removed object\n * store or transforming records written by an older format.\n */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\nfunction idbReq<R>(request: IDBRequest<R>): Promise<R> {\n return new Promise<R>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB request failed'));\n });\n}\n\nfunction wrapTxError(scope: string, message: string, cause: unknown): VaultError {\n const causeMessage = cause instanceof Error && cause.message ? `: ${cause.message}` : '';\n\n return new VaultError(`${message} on \"${scope}\"${causeMessage}`, { cause });\n}\n\nfunction runIdbTx<T>(tx: IDBTransaction, scope: string, work: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let result: T | undefined;\n let callbackError: unknown;\n\n Promise.resolve()\n .then(work)\n .then((value) => {\n result = value;\n })\n .catch((error) => {\n callbackError = error;\n\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n });\n\n const rejectWithCallbackError = (fallbackCause: unknown, message = 'transaction failed'): void => {\n if (callbackError instanceof Error) {\n reject(callbackError);\n } else {\n reject(wrapTxError(scope, message, callbackError ?? fallbackCause));\n }\n };\n\n tx.oncomplete = () => {\n if (callbackError) {\n rejectWithCallbackError(undefined);\n\n return;\n }\n\n resolve(result as T);\n };\n tx.onerror = () => reject(wrapTxError(scope, 'transaction error', tx.error));\n tx.onabort = () => rejectWithCallbackError(tx.error, 'transaction aborted');\n });\n}\n\nasync function getAllFromStore<T extends object>(\n store: IDBIndex | IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n query?: IDBValidKey | IDBKeyRange,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll(query));\n const records: T[] = [];\n\n for (const raw of rawRecords) {\n const value = decode(raw);\n\n if (value !== undefined) records.push(value);\n }\n\n return records;\n}\n\nasync function storeGet<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<T | undefined> {\n const raw = await idbReq<unknown>(store.get(key));\n\n if (raw == null) return undefined;\n\n return decode(raw);\n}\n\nasync function storeDelete<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n const live = (await storeGet<T>(store, key, decode)) !== undefined;\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: readonly IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n const results = await Promise.all(keys.map((key) => storeDelete(store, key, decode)));\n return results.filter(Boolean).length;\n}\n\nfunction pruneExpiredInStore(store: IDBObjectStore): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n let deleted = 0;\n const request = store.openCursor();\n\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB cursor failed during prune'));\n request.onsuccess = () => {\n const cursor = request.result;\n\n if (!cursor) {\n resolve(deleted);\n\n return;\n }\n\n const stored = parseStored(cursor.value as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n cursor.delete();\n deleted += 1;\n }\n\n cursor.continue();\n };\n });\n}\n\n/**\n * Cursor state machine: a single discriminated union replaces five boolean/nullable variables.\n * Transitions: idle → waiting (next() before cursor fires) | buffered (cursor fires first) | done | error\n */\ntype CursorState<T> =\n | { type: 'idle' }\n | { reject: (e: unknown) => void; resolve: (r: IteratorResult<T>) => void; type: 'waiting' }\n | { results: IteratorResult<T>[]; type: 'buffered' }\n | { error: unknown; type: 'error' }\n | { type: 'done' };\n\n/**\n * F1: True cursor-based iteration for IndexedDB.\n * Yields live records one-by-one using an IDB cursor, avoiding materializing the full table.\n * This is memory-efficient for large tables: the cursor walks the store incrementally.\n *\n * Design: the cursor is opened *synchronously* in [Symbol.asyncIterator]() so that event\n * handlers are wired immediately (no queueMicrotask races). The cursor is advanced *eagerly*\n * before yielding: this keeps the IDB readonly transaction alive between consumer awaits,\n * because IDB auto-commits when there are no pending requests.\n */\nfunction iterateStoreWithCursor<T extends object>(\n store: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const cursorRequest = store.openCursor();\n let state: CursorState<T> = { type: 'idle' };\n\n const deliver = (result: IteratorResult<T>): void => {\n if (state.type === 'waiting') {\n const { resolve } = state;\n\n state = result.done ? { type: 'done' } : { type: 'idle' };\n resolve(result);\n } else if (state.type === 'buffered') {\n state.results.push(result);\n } else {\n state = { results: [result], type: 'buffered' };\n }\n };\n\n cursorRequest.onerror = () => {\n const err = cursorRequest.error ?? new VaultError('IndexedDB cursor iteration failed');\n\n if (state.type === 'waiting') {\n const { reject } = state;\n\n state = { type: 'done' };\n reject(err);\n } else {\n state = { error: err, type: 'error' };\n }\n };\n\n cursorRequest.onsuccess = () => {\n if (state.type === 'done') return;\n const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n let value: T | undefined;\n try {\n value = decode(cursor.value as unknown);\n } catch (error) {\n if (state.type === 'waiting') {\n const { reject } = state;\n state = { type: 'done' };\n reject(error);\n } else {\n state = { error, type: 'error' };\n }\n return;\n }\n\n // Advance eagerly BEFORE yielding to keep the IDB transaction alive.\n cursor.continue();\n\n if (value !== undefined) deliver({ done: false, value });\n };\n\n return {\n next(): Promise<IteratorResult<T>> {\n if (state.type === 'error') {\n const { error } = state;\n\n state = { type: 'done' };\n\n return Promise.reject(error);\n }\n\n if (state.type === 'buffered') {\n const [result, ...results] = state.results;\n\n state = result.done\n ? { type: 'done' }\n : results.length > 0\n ? { results, type: 'buffered' }\n : { type: 'idle' };\n\n return Promise.resolve(result);\n }\n\n if (state.type === 'done') return Promise.resolve({ done: true, value: undefined });\n\n return new Promise<IteratorResult<T>>((resolve, reject) => {\n state = { reject, resolve, type: 'waiting' };\n });\n },\n\n return(value?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.resolve({ done: true, value: undefined });\n\n state = { type: 'done' };\n\n return Promise.resolve({ done: true, value });\n },\n\n throw(err?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.reject(err);\n\n state = { type: 'done' };\n\n return Promise.reject(err);\n },\n };\n },\n };\n}\n\nexport interface IndexedDbVaultStore<S extends AnySchema> extends DocumentVaultStore<S> {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]>;\n}\n\ntype IndexedDbOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n migrate?: MigrationFn;\n name: string;\n /** Schema version. Must be a positive integer. Increment when adding tables or changing the schema, then provide `migrate`. Defaults to 1. */\n version?: number;\n};\n\nexport function createIndexedDB<S extends AnySchema>(options: IndexedDbOptions<S>): IndexedDbVaultStore<S> {\n const { migrate, name, schema, codecs, version = 1 } = options;\n\n if (!codecs) {\n throw new VaultError('createIndexedDB: codecs are required for durable persistence');\n }\n\n if (!Number.isInteger(version) || version < 1) {\n throw new VaultError(`createIndexedDB: version must be a positive integer, got ${String(version)}`);\n }\n\n // Fixed envelopes keep IndexedDB records and `value.<field>` indexes portable across adapters.\n // Table-aware codec encode/decode is applied in the backend methods below.\n\n const channel = typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel(`vault:${name}`) : undefined;\n\n let db: IDBDatabase | null = null;\n let connectPromise: Promise<void> | null = null;\n let disposed = false;\n\n const createObjectStores = (target: IDBDatabase, tx: IDBTransaction): void => {\n for (const [tableName, entry] of Object.entries(schema)) {\n let store: IDBObjectStore;\n\n if (!target.objectStoreNames.contains(tableName)) {\n store = target.createObjectStore(tableName);\n } else {\n store = tx.objectStore(tableName);\n }\n\n // F5: Create secondary indexes for fields declared via .index() on the table() builder.\n // Stored format is { value: T, expiresAt?: number } so IDB keyPath is `value.<field>`.\n const indexes = (entry as { indexes?: readonly string[] }).indexes ?? [];\n\n for (const field of indexes) {\n if (!store.indexNames.contains(field)) {\n store.createIndex(field, `value.${field}`);\n }\n }\n }\n };\n\n const connect = async (): Promise<void> => {\n if (!connectPromise) {\n connectPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(name, version);\n\n request.onupgradeneeded = (event) => {\n const target = request.result;\n const tx = request.transaction!;\n\n if (migrate) {\n try {\n migrate({\n db: target,\n newVersion: (event as IDBVersionChangeEvent).newVersion ?? null,\n oldVersion: event.oldVersion,\n tx,\n });\n } catch (error) {\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n\n reject(new VaultMigrationError(`migration failed for \"${name}\"`, { cause: error }));\n\n return;\n }\n }\n\n createObjectStores(target, tx);\n };\n\n request.onsuccess = () => {\n if (disposed) {\n request.result.close();\n resolve();\n\n return;\n }\n\n const connection = request.result;\n\n connection.onversionchange = () => {\n connection.close();\n db = null;\n connectPromise = null;\n };\n\n db = connection;\n resolve();\n };\n request.onerror = () => {\n connectPromise = null;\n reject(new VaultError(`failed to open \"${name}\"`, { cause: request.error }));\n };\n });\n }\n\n return connectPromise;\n };\n\n const withStore = async <T>(\n table: keyof S,\n mode: 'readonly' | 'readwrite',\n fn: (store: IDBObjectStore) => Promise<T>,\n ): Promise<T> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const tableName = String(table);\n const tx = db.transaction(tableName, mode);\n\n return runIdbTx(tx, `${name}/${tableName}`, () => fn(tx.objectStore(tableName)));\n };\n\n const requireDb = async (): Promise<IDBDatabase> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return db;\n };\n\n const publish = <K extends keyof S>(table: K): void => {\n channel?.postMessage({ table: String(table) });\n };\n\n // Table-aware decode: unwrap envelope + apply codec.decode\n const decodeWithCodec = <T extends object>(table: keyof S, raw: unknown): T | undefined => {\n const stored = parseStored<unknown>(raw);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: err });\n }\n };\n\n // Table-aware encode: apply codec.encode + wrap in envelope\n const encodeWithCodec = <T>(table: keyof S, value: T, ttl?: number): StoredRecord<unknown> => {\n const record = value as RecordOf<S, typeof table>;\n const encoded = encodeRecord(codecs[table], record);\n\n for (const field of schema[table].indexes ?? []) {\n const indexedValue = (record as Record<string, unknown>)[field];\n if (indexedValue === undefined) continue;\n\n if (\n typeof encoded !== 'object' ||\n encoded === null ||\n !Object.hasOwn(encoded, field) ||\n !Object.is((encoded as Record<string, unknown>)[field], indexedValue)\n ) {\n throw new VaultError(`codec output for table \"${String(table)}\" must preserve indexed field \"${field}\"`);\n }\n }\n\n return ttl === undefined ? { value: encoded } : { expiresAt: Date.now() + ttl, value: encoded };\n };\n\n const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n deleteMany: (table, keys) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, typeof table>>(s, keys.map(encodeVaultKey), decode);\n }),\n\n async dispose(): Promise<void> {\n disposed = true;\n channel?.close();\n\n // F7: Wait for any in-progress connect before closing the DB to avoid\n // \"database connection is closing\" errors on in-flight requests.\n if (connectPromise) await connectPromise.catch(() => {});\n\n db?.close();\n db = null;\n connectPromise = null;\n },\n\n get: (table, key) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n getAll: (table) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n }),\n\n getMany: (table, keys) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)));\n }),\n\n iterate: (table) => {\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, typeof table>>> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db || disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const idb = db;\n const tx = idb.transaction(String(table), 'readonly');\n const store = tx.objectStore(String(table));\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n store,\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n };\n\n let inner: AsyncIterator<RecordOf<S, typeof table>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, typeof table>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, typeof table>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.return?.(value) ?? Promise.resolve({ done: true, value });\n\n return Promise.resolve({ done: true, value });\n },\n throw(err?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n\n async pruneAllExpired() {\n const idb = await requireDb();\n const tableNames = Object.keys(schema);\n const tx = idb.transaction(tableNames, 'readwrite');\n const results = await runIdbTx(tx, `${name}/pruneAll`, () =>\n Promise.all(tableNames.map(async (t) => [t, await pruneExpiredInStore(tx.objectStore(t))] as const)),\n );\n\n return Object.fromEntries(results);\n },\n\n pruneExpiredInTable: (table) => withStore(table, 'readwrite', (s) => pruneExpiredInStore(s)),\n\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return withStore(table, 'readwrite', (s) => idbReq(s.put(encoded, key)).then(() => undefined));\n },\n\n putAll(table, values, ttl) {\n const encodedValues = values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n\n return { encoded: encodeWithCodec(table, v, ttl), key };\n });\n\n return withStore(table, 'readwrite', (s) =>\n Promise.all(encodedValues.map(({ encoded, key }) => idbReq(s.put(encoded, key)))).then(() => undefined),\n );\n },\n };\n\n const idbBatch = async <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n notifyMutation: (table: K) => void,\n validateFn: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n ): Promise<R> => {\n assertBatchTables(tables);\n\n const idb = await requireDb();\n const idbTx = idb.transaction([...tables] as string[], 'readwrite');\n const dirtyTables = new Set<K>();\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n const batchCore: StorageBackend<S, K> = {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n delete: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode);\n },\n deleteMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode);\n },\n get: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode);\n },\n getAll: (table) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode);\n },\n getMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n const store = storeOf(table);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(store, encodeVaultKey(key), decode)));\n },\n iterate(table) {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n storeOf(table),\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n },\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return idbReq(storeOf(table).put(encoded, key)).then(() => undefined);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n const encoded = encodeWithCodec(table, v, ttl);\n\n return idbReq(storeOf(table).put(encoded, key));\n }),\n ).then(() => undefined);\n },\n };\n\n let contextActive = true;\n const tx = buildOperations<S, K>(\n schema,\n batchCore,\n (t) => dirtyTables.add(t),\n validateFn,\n batchScopeGuard(tables, () => contextActive),\n );\n\n try {\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) notifyMutation(table);\n\n return result;\n } finally {\n contextActive = false;\n }\n };\n\n const adapter = buildDocumentStore(schema, core, {\n codecs,\n createBatch: (deps) => (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate),\n onCrossTabMessage(notify) {\n if (!channel) {\n return undefined;\n }\n\n channel.onmessage = (event: MessageEvent<{ table?: string }>) => {\n const tableName = event.data?.table;\n\n if (!tableName || !Object.hasOwn(schema, tableName)) return;\n\n notify(tableName as keyof S & string);\n };\n\n return () => {\n channel.onmessage = null;\n };\n },\n onMutation: publish,\n schema,\n });\n\n return Object.assign(adapter, {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]> {\n return withStore(table, 'readonly', (store) => {\n if (!store.indexNames.contains(field)) throw new VaultError(`Table \"${table}\" has no index \"${field}\"`);\n\n const decode = (raw: unknown): RecordOf<S, K> | undefined => decodeWithCodec<RecordOf<S, K>>(table, raw);\n return getAllFromStore(store.index(field), decode, value as IDBValidKey);\n });\n },\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.js","names":[],"sources":["../../src/adapters/indexeddb.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n assertBatchTables,\n batchScopeGuard,\n buildDocumentStore,\n buildOperations,\n type StorageBackend,\n} from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DocumentVaultStore, DurableStoreOptions, RecordOf, TransactionContext } from '../types';\n\n/** IndexedDB-only migration context supplied to `MigrationFn` during `onupgradeneeded`. */\nexport type MigrationContext = {\n db: IDBDatabase;\n newVersion: number | null;\n oldVersion: number;\n tx: IDBTransaction;\n};\n\n/**\n * Synchronous IndexedDB schema upgrade callback, invoked from `onupgradeneeded`.\n * Tables and schema-declared indexes are created automatically; use the hook\n * only for work the schema cannot express, such as deleting a removed object\n * store or transforming records written by an older format.\n */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\nfunction idbReq<R>(request: IDBRequest<R>): Promise<R> {\n return new Promise<R>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB request failed'));\n });\n}\n\nfunction wrapTxError(scope: string, message: string, cause: unknown): VaultError {\n const causeMessage = cause instanceof Error && cause.message ? `: ${cause.message}` : '';\n\n return new VaultError(`${message} on \"${scope}\"${causeMessage}`, { cause });\n}\n\nfunction runIdbTx<T>(tx: IDBTransaction, scope: string, work: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let result: T | undefined;\n let callbackError: unknown;\n\n Promise.resolve()\n .then(work)\n .then((value) => {\n result = value;\n })\n .catch((error) => {\n callbackError = error;\n\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n });\n\n const rejectWithCallbackError = (fallbackCause: unknown, message = 'transaction failed'): void => {\n if (callbackError instanceof Error) {\n reject(callbackError);\n } else {\n reject(wrapTxError(scope, message, callbackError ?? fallbackCause));\n }\n };\n\n tx.oncomplete = () => {\n if (callbackError) {\n rejectWithCallbackError(undefined);\n\n return;\n }\n\n resolve(result as T);\n };\n tx.onerror = () => reject(wrapTxError(scope, 'transaction error', tx.error));\n tx.onabort = () => rejectWithCallbackError(tx.error, 'transaction aborted');\n });\n}\n\nasync function getAllFromStore<T extends object>(\n store: IDBIndex | IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n query?: IDBValidKey | IDBKeyRange,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll(query));\n const records: T[] = [];\n\n for (const raw of rawRecords) {\n const value = decode(raw);\n\n if (value !== undefined) records.push(value);\n }\n\n return records;\n}\n\nasync function storeGet<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<T | undefined> {\n const raw = await idbReq<unknown>(store.get(key));\n\n if (raw == null) return undefined;\n\n return decode(raw);\n}\n\nasync function storeDelete<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n const live = (await storeGet<T>(store, key, decode)) !== undefined;\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: readonly IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n const results = await Promise.all(keys.map((key) => storeDelete(store, key, decode)));\n return results.filter(Boolean).length;\n}\n\nfunction pruneExpiredInStore(store: IDBObjectStore): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n let deleted = 0;\n const request = store.openCursor();\n\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB cursor failed during prune'));\n request.onsuccess = () => {\n const cursor = request.result;\n\n if (!cursor) {\n resolve(deleted);\n\n return;\n }\n\n const stored = parseStored(cursor.value as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n cursor.delete();\n deleted += 1;\n }\n\n cursor.continue();\n };\n });\n}\n\n/**\n * Cursor state machine — a single discriminated union replaces five boolean/nullable variables.\n * Transitions: idle → waiting (next() before cursor fires) | buffered (cursor fires first) | done | error\n */\ntype CursorState<T> =\n | { type: 'idle' }\n | { reject: (e: unknown) => void; resolve: (r: IteratorResult<T>) => void; type: 'waiting' }\n | { results: IteratorResult<T>[]; type: 'buffered' }\n | { error: unknown; type: 'error' }\n | { type: 'done' };\n\n/**\n * F1: True cursor-based iteration for IndexedDB.\n * Yields live records one-by-one using an IDB cursor, avoiding materializing the full table.\n * This is memory-efficient for large tables — the cursor walks the store incrementally.\n *\n * Design: the cursor is opened *synchronously* in [Symbol.asyncIterator]() so that event\n * handlers are wired immediately (no queueMicrotask races). The cursor is advanced *eagerly*\n * before yielding — this keeps the IDB readonly transaction alive between consumer awaits,\n * because IDB auto-commits when there are no pending requests.\n */\nfunction iterateStoreWithCursor<T extends object>(\n store: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const cursorRequest = store.openCursor();\n let state: CursorState<T> = { type: 'idle' };\n\n const deliver = (result: IteratorResult<T>): void => {\n if (state.type === 'waiting') {\n const { resolve } = state;\n\n state = result.done ? { type: 'done' } : { type: 'idle' };\n resolve(result);\n } else if (state.type === 'buffered') {\n state.results.push(result);\n } else {\n state = { results: [result], type: 'buffered' };\n }\n };\n\n cursorRequest.onerror = () => {\n const err = cursorRequest.error ?? new VaultError('IndexedDB cursor iteration failed');\n\n if (state.type === 'waiting') {\n const { reject } = state;\n\n state = { type: 'done' };\n reject(err);\n } else {\n state = { error: err, type: 'error' };\n }\n };\n\n cursorRequest.onsuccess = () => {\n if (state.type === 'done') return;\n const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n let value: T | undefined;\n try {\n value = decode(cursor.value as unknown);\n } catch (error) {\n if (state.type === 'waiting') {\n const { reject } = state;\n state = { type: 'done' };\n reject(error);\n } else {\n state = { error, type: 'error' };\n }\n return;\n }\n\n // Advance eagerly BEFORE yielding to keep the IDB transaction alive.\n cursor.continue();\n\n if (value !== undefined) deliver({ done: false, value });\n };\n\n return {\n next(): Promise<IteratorResult<T>> {\n if (state.type === 'error') {\n const { error } = state;\n\n state = { type: 'done' };\n\n return Promise.reject(error);\n }\n\n if (state.type === 'buffered') {\n const [result, ...results] = state.results;\n\n state = result.done\n ? { type: 'done' }\n : results.length > 0\n ? { results, type: 'buffered' }\n : { type: 'idle' };\n\n return Promise.resolve(result);\n }\n\n if (state.type === 'done') return Promise.resolve({ done: true, value: undefined });\n\n return new Promise<IteratorResult<T>>((resolve, reject) => {\n state = { reject, resolve, type: 'waiting' };\n });\n },\n\n return(value?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.resolve({ done: true, value: undefined });\n\n state = { type: 'done' };\n\n return Promise.resolve({ done: true, value });\n },\n\n throw(err?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.reject(err);\n\n state = { type: 'done' };\n\n return Promise.reject(err);\n },\n };\n },\n };\n}\n\nexport interface IndexedDbVaultStore<S extends AnySchema> extends DocumentVaultStore<S> {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]>;\n}\n\ntype IndexedDbOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n migrate?: MigrationFn;\n name: string;\n /** Schema version. Must be a positive integer. Increment when adding tables or changing the schema, then provide `migrate`. Defaults to 1. */\n version?: number;\n};\n\nexport function createIndexedDB<S extends AnySchema>(options: IndexedDbOptions<S>): IndexedDbVaultStore<S> {\n const { migrate, name, schema, codecs, version = 1 } = options;\n\n if (!codecs) {\n throw new VaultError('createIndexedDB: codecs are required for durable persistence');\n }\n\n if (!Number.isInteger(version) || version < 1) {\n throw new VaultError(`createIndexedDB: version must be a positive integer, got ${String(version)}`);\n }\n\n // Fixed envelopes keep IndexedDB records and `value.<field>` indexes portable across adapters.\n // Table-aware codec encode/decode is applied in the backend methods below.\n\n const channel = typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel(`vault:${name}`) : undefined;\n\n let db: IDBDatabase | null = null;\n let connectPromise: Promise<void> | null = null;\n let disposed = false;\n\n const createObjectStores = (target: IDBDatabase, tx: IDBTransaction): void => {\n for (const [tableName, entry] of Object.entries(schema)) {\n let store: IDBObjectStore;\n\n if (!target.objectStoreNames.contains(tableName)) {\n store = target.createObjectStore(tableName);\n } else {\n store = tx.objectStore(tableName);\n }\n\n // F5: Create secondary indexes for fields declared via .index() on the table() builder.\n // Stored format is { value: T, expiresAt?: number } so IDB keyPath is `value.<field>`.\n const indexes = (entry as { indexes?: readonly string[] }).indexes ?? [];\n\n for (const field of indexes) {\n if (!store.indexNames.contains(field)) {\n store.createIndex(field, `value.${field}`);\n }\n }\n }\n };\n\n const connect = async (): Promise<void> => {\n if (!connectPromise) {\n connectPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(name, version);\n\n request.onupgradeneeded = (event) => {\n const target = request.result;\n const tx = request.transaction!;\n\n if (migrate) {\n try {\n migrate({\n db: target,\n newVersion: (event as IDBVersionChangeEvent).newVersion ?? null,\n oldVersion: event.oldVersion,\n tx,\n });\n } catch (error) {\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n\n reject(new VaultMigrationError(`migration failed for \"${name}\"`, { cause: error }));\n\n return;\n }\n }\n\n createObjectStores(target, tx);\n };\n\n request.onsuccess = () => {\n if (disposed) {\n request.result.close();\n resolve();\n\n return;\n }\n\n const connection = request.result;\n\n connection.onversionchange = () => {\n connection.close();\n db = null;\n connectPromise = null;\n };\n\n db = connection;\n resolve();\n };\n request.onerror = () => {\n connectPromise = null;\n reject(new VaultError(`failed to open \"${name}\"`, { cause: request.error }));\n };\n });\n }\n\n return connectPromise;\n };\n\n const withStore = async <T>(\n table: keyof S,\n mode: 'readonly' | 'readwrite',\n fn: (store: IDBObjectStore) => Promise<T>,\n ): Promise<T> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const tableName = String(table);\n const tx = db.transaction(tableName, mode);\n\n return runIdbTx(tx, `${name}/${tableName}`, () => fn(tx.objectStore(tableName)));\n };\n\n const requireDb = async (): Promise<IDBDatabase> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return db;\n };\n\n const publish = <K extends keyof S>(table: K): void => {\n channel?.postMessage({ table: String(table) });\n };\n\n // Table-aware decode: unwrap envelope + apply codec.decode\n const decodeWithCodec = <T extends object>(table: keyof S, raw: unknown): T | undefined => {\n const stored = parseStored<unknown>(raw);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: err });\n }\n };\n\n // Table-aware encode: apply codec.encode + wrap in envelope\n const encodeWithCodec = <T>(table: keyof S, value: T, ttl?: number): StoredRecord<unknown> => {\n const record = value as RecordOf<S, typeof table>;\n const encoded = encodeRecord(codecs[table], record);\n\n for (const field of schema[table].indexes ?? []) {\n const indexedValue = (record as Record<string, unknown>)[field];\n if (indexedValue === undefined) continue;\n\n if (\n typeof encoded !== 'object' ||\n encoded === null ||\n !Object.hasOwn(encoded, field) ||\n !Object.is((encoded as Record<string, unknown>)[field], indexedValue)\n ) {\n throw new VaultError(`codec output for table \"${String(table)}\" must preserve indexed field \"${field}\"`);\n }\n }\n\n return ttl === undefined ? { value: encoded } : { expiresAt: Date.now() + ttl, value: encoded };\n };\n\n const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n deleteMany: (table, keys) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, typeof table>>(s, keys.map(encodeVaultKey), decode);\n }),\n\n async dispose(): Promise<void> {\n disposed = true;\n channel?.close();\n\n // F7: Wait for any in-progress connect before closing the DB to avoid\n // \"database connection is closing\" errors on in-flight requests.\n if (connectPromise) await connectPromise.catch(() => {});\n\n db?.close();\n db = null;\n connectPromise = null;\n },\n\n get: (table, key) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n getAll: (table) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n }),\n\n getMany: (table, keys) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)));\n }),\n\n iterate: (table) => {\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, typeof table>>> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db || disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const idb = db;\n const tx = idb.transaction(String(table), 'readonly');\n const store = tx.objectStore(String(table));\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n store,\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n };\n\n let inner: AsyncIterator<RecordOf<S, typeof table>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, typeof table>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, typeof table>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.return?.(value) ?? Promise.resolve({ done: true, value });\n\n return Promise.resolve({ done: true, value });\n },\n throw(err?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n\n async pruneAllExpired() {\n const idb = await requireDb();\n const tableNames = Object.keys(schema);\n const tx = idb.transaction(tableNames, 'readwrite');\n const results = await runIdbTx(tx, `${name}/pruneAll`, () =>\n Promise.all(tableNames.map(async (t) => [t, await pruneExpiredInStore(tx.objectStore(t))] as const)),\n );\n\n return Object.fromEntries(results);\n },\n\n pruneExpiredInTable: (table) => withStore(table, 'readwrite', (s) => pruneExpiredInStore(s)),\n\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return withStore(table, 'readwrite', (s) => idbReq(s.put(encoded, key)).then(() => undefined));\n },\n\n putAll(table, values, ttl) {\n const encodedValues = values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n\n return { encoded: encodeWithCodec(table, v, ttl), key };\n });\n\n return withStore(table, 'readwrite', (s) =>\n Promise.all(encodedValues.map(({ encoded, key }) => idbReq(s.put(encoded, key)))).then(() => undefined),\n );\n },\n };\n\n const idbBatch = async <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n notifyMutation: (table: K) => void,\n validateFn: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n ): Promise<R> => {\n assertBatchTables(tables);\n\n const idb = await requireDb();\n const idbTx = idb.transaction([...tables] as string[], 'readwrite');\n const dirtyTables = new Set<K>();\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n const batchCore: StorageBackend<S, K> = {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n delete: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode);\n },\n deleteMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode);\n },\n get: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode);\n },\n getAll: (table) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode);\n },\n getMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n const store = storeOf(table);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(store, encodeVaultKey(key), decode)));\n },\n iterate(table) {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n storeOf(table),\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n },\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return idbReq(storeOf(table).put(encoded, key)).then(() => undefined);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n const encoded = encodeWithCodec(table, v, ttl);\n\n return idbReq(storeOf(table).put(encoded, key));\n }),\n ).then(() => undefined);\n },\n };\n\n let contextActive = true;\n const tx = buildOperations<S, K>(\n schema,\n batchCore,\n (t) => dirtyTables.add(t),\n validateFn,\n batchScopeGuard(tables, () => contextActive),\n );\n\n try {\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) notifyMutation(table);\n\n return result;\n } finally {\n contextActive = false;\n }\n };\n\n const adapter = buildDocumentStore(schema, core, {\n codecs,\n createBatch: (deps) => (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate),\n onCrossTabMessage(notify) {\n if (!channel) {\n return undefined;\n }\n\n channel.onmessage = (event: MessageEvent<{ table?: string }>) => {\n const tableName = event.data?.table;\n\n if (!tableName || !Object.hasOwn(schema, tableName)) return;\n\n notify(tableName as keyof S & string);\n };\n\n return () => {\n channel.onmessage = null;\n };\n },\n onMutation: publish,\n schema,\n });\n\n return Object.assign(adapter, {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]> {\n return withStore(table, 'readonly', (store) => {\n if (!store.indexNames.contains(field)) throw new VaultError(`Table \"${table}\" has no index \"${field}\"`);\n\n const decode = (raw: unknown): RecordOf<S, K> | undefined => decodeWithCodec<RecordOf<S, K>>(table, raw);\n return getAllFromStore(store.index(field), decode, value as IDBValidKey);\n });\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"indexeddb.js","names":[],"sources":["../../src/adapters/indexeddb.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n assertBatchTables,\n batchScopeGuard,\n buildDocumentStore,\n buildOperations,\n type StorageBackend,\n} from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DocumentVaultStore, DurableStoreOptions, RecordOf, TransactionContext } from '../types';\n\n/** IndexedDB-only migration context supplied to `MigrationFn` during `onupgradeneeded`. */\nexport type MigrationContext = {\n db: IDBDatabase;\n newVersion: number | null;\n oldVersion: number;\n tx: IDBTransaction;\n};\n\n/**\n * Synchronous IndexedDB schema upgrade callback, invoked from `onupgradeneeded`.\n * Tables and schema-declared indexes are created automatically; use the hook\n * only for work the schema cannot express, such as deleting a removed object\n * store or transforming records written by an older format.\n */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\nfunction idbReq<R>(request: IDBRequest<R>): Promise<R> {\n return new Promise<R>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB request failed'));\n });\n}\n\nfunction wrapTxError(scope: string, message: string, cause: unknown): VaultError {\n const causeMessage = cause instanceof Error && cause.message ? `: ${cause.message}` : '';\n\n return new VaultError(`${message} on \"${scope}\"${causeMessage}`, { cause });\n}\n\nfunction runIdbTx<T>(tx: IDBTransaction, scope: string, work: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let result: T | undefined;\n let callbackError: unknown;\n\n Promise.resolve()\n .then(work)\n .then((value) => {\n result = value;\n })\n .catch((error) => {\n callbackError = error;\n\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n });\n\n const rejectWithCallbackError = (fallbackCause: unknown, message = 'transaction failed'): void => {\n if (callbackError instanceof Error) {\n reject(callbackError);\n } else {\n reject(wrapTxError(scope, message, callbackError ?? fallbackCause));\n }\n };\n\n tx.oncomplete = () => {\n if (callbackError) {\n rejectWithCallbackError(undefined);\n\n return;\n }\n\n resolve(result as T);\n };\n tx.onerror = () => reject(wrapTxError(scope, 'transaction error', tx.error));\n tx.onabort = () => rejectWithCallbackError(tx.error, 'transaction aborted');\n });\n}\n\nasync function getAllFromStore<T extends object>(\n store: IDBIndex | IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n query?: IDBValidKey | IDBKeyRange,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll(query));\n const records: T[] = [];\n\n for (const raw of rawRecords) {\n const value = decode(raw);\n\n if (value !== undefined) records.push(value);\n }\n\n return records;\n}\n\nasync function storeGet<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<T | undefined> {\n const raw = await idbReq<unknown>(store.get(key));\n\n if (raw == null) return undefined;\n\n return decode(raw);\n}\n\nasync function storeDelete<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n const live = (await storeGet<T>(store, key, decode)) !== undefined;\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: readonly IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n const results = await Promise.all(keys.map((key) => storeDelete(store, key, decode)));\n return results.filter(Boolean).length;\n}\n\nfunction pruneExpiredInStore(store: IDBObjectStore): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n let deleted = 0;\n const request = store.openCursor();\n\n request.onerror = () => reject(request.error ?? new VaultError('IndexedDB cursor failed during prune'));\n request.onsuccess = () => {\n const cursor = request.result;\n\n if (!cursor) {\n resolve(deleted);\n\n return;\n }\n\n const stored = parseStored(cursor.value as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n cursor.delete();\n deleted += 1;\n }\n\n cursor.continue();\n };\n });\n}\n\n/**\n * Cursor state machine: a single discriminated union replaces five boolean/nullable variables.\n * Transitions: idle → waiting (next() before cursor fires) | buffered (cursor fires first) | done | error\n */\ntype CursorState<T> =\n | { type: 'idle' }\n | { reject: (e: unknown) => void; resolve: (r: IteratorResult<T>) => void; type: 'waiting' }\n | { results: IteratorResult<T>[]; type: 'buffered' }\n | { error: unknown; type: 'error' }\n | { type: 'done' };\n\n/**\n * F1: True cursor-based iteration for IndexedDB.\n * Yields live records one-by-one using an IDB cursor, avoiding materializing the full table.\n * This is memory-efficient for large tables: the cursor walks the store incrementally.\n *\n * Design: the cursor is opened *synchronously* in [Symbol.asyncIterator]() so that event\n * handlers are wired immediately (no queueMicrotask races). The cursor is advanced *eagerly*\n * before yielding: this keeps the IDB readonly transaction alive between consumer awaits,\n * because IDB auto-commits when there are no pending requests.\n */\nfunction iterateStoreWithCursor<T extends object>(\n store: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const cursorRequest = store.openCursor();\n let state: CursorState<T> = { type: 'idle' };\n\n const deliver = (result: IteratorResult<T>): void => {\n if (state.type === 'waiting') {\n const { resolve } = state;\n\n state = result.done ? { type: 'done' } : { type: 'idle' };\n resolve(result);\n } else if (state.type === 'buffered') {\n state.results.push(result);\n } else {\n state = { results: [result], type: 'buffered' };\n }\n };\n\n cursorRequest.onerror = () => {\n const err = cursorRequest.error ?? new VaultError('IndexedDB cursor iteration failed');\n\n if (state.type === 'waiting') {\n const { reject } = state;\n\n state = { type: 'done' };\n reject(err);\n } else {\n state = { error: err, type: 'error' };\n }\n };\n\n cursorRequest.onsuccess = () => {\n if (state.type === 'done') return;\n const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n let value: T | undefined;\n try {\n value = decode(cursor.value as unknown);\n } catch (error) {\n if (state.type === 'waiting') {\n const { reject } = state;\n state = { type: 'done' };\n reject(error);\n } else {\n state = { error, type: 'error' };\n }\n return;\n }\n\n // Advance eagerly BEFORE yielding to keep the IDB transaction alive.\n cursor.continue();\n\n if (value !== undefined) deliver({ done: false, value });\n };\n\n return {\n next(): Promise<IteratorResult<T>> {\n if (state.type === 'error') {\n const { error } = state;\n\n state = { type: 'done' };\n\n return Promise.reject(error);\n }\n\n if (state.type === 'buffered') {\n const [result, ...results] = state.results;\n\n state = result.done\n ? { type: 'done' }\n : results.length > 0\n ? { results, type: 'buffered' }\n : { type: 'idle' };\n\n return Promise.resolve(result);\n }\n\n if (state.type === 'done') return Promise.resolve({ done: true, value: undefined });\n\n return new Promise<IteratorResult<T>>((resolve, reject) => {\n state = { reject, resolve, type: 'waiting' };\n });\n },\n\n return(value?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.resolve({ done: true, value: undefined });\n\n state = { type: 'done' };\n\n return Promise.resolve({ done: true, value });\n },\n\n throw(err?: unknown): Promise<IteratorResult<T>> {\n if (state.type === 'waiting') state.reject(err);\n\n state = { type: 'done' };\n\n return Promise.reject(err);\n },\n };\n },\n };\n}\n\nexport interface IndexedDbVaultStore<S extends AnySchema> extends DocumentVaultStore<S> {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]>;\n}\n\ntype IndexedDbOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n migrate?: MigrationFn;\n name: string;\n /** Schema version. Must be a positive integer. Increment when adding tables or changing the schema, then provide `migrate`. Defaults to 1. */\n version?: number;\n};\n\nexport function createIndexedDB<S extends AnySchema>(options: IndexedDbOptions<S>): IndexedDbVaultStore<S> {\n const { migrate, name, schema, codecs, version = 1 } = options;\n\n if (!codecs) {\n throw new VaultError('createIndexedDB: codecs are required for durable persistence');\n }\n\n if (!Number.isInteger(version) || version < 1) {\n throw new VaultError(`createIndexedDB: version must be a positive integer, got ${String(version)}`);\n }\n\n // Fixed envelopes keep IndexedDB records and `value.<field>` indexes portable across adapters.\n // Table-aware codec encode/decode is applied in the backend methods below.\n\n const channel = typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel(`vault:${name}`) : undefined;\n\n let db: IDBDatabase | null = null;\n let connectPromise: Promise<void> | null = null;\n let disposed = false;\n\n const createObjectStores = (target: IDBDatabase, tx: IDBTransaction): void => {\n for (const [tableName, entry] of Object.entries(schema)) {\n let store: IDBObjectStore;\n\n if (!target.objectStoreNames.contains(tableName)) {\n store = target.createObjectStore(tableName);\n } else {\n store = tx.objectStore(tableName);\n }\n\n // F5: Create secondary indexes for fields declared via .index() on the table() builder.\n // Stored format is { value: T, expiresAt?: number } so IDB keyPath is `value.<field>`.\n const indexes = (entry as { indexes?: readonly string[] }).indexes ?? [];\n\n for (const field of indexes) {\n if (!store.indexNames.contains(field)) {\n store.createIndex(field, `value.${field}`);\n }\n }\n }\n };\n\n const connect = async (): Promise<void> => {\n if (!connectPromise) {\n connectPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(name, version);\n\n request.onupgradeneeded = (event) => {\n const target = request.result;\n const tx = request.transaction!;\n\n if (migrate) {\n try {\n migrate({\n db: target,\n newVersion: (event as IDBVersionChangeEvent).newVersion ?? null,\n oldVersion: event.oldVersion,\n tx,\n });\n } catch (error) {\n try {\n tx.abort();\n } catch {\n /* ignore */\n }\n\n reject(new VaultMigrationError(`migration failed for \"${name}\"`, { cause: error }));\n\n return;\n }\n }\n\n createObjectStores(target, tx);\n };\n\n request.onsuccess = () => {\n if (disposed) {\n request.result.close();\n resolve();\n\n return;\n }\n\n const connection = request.result;\n\n connection.onversionchange = () => {\n connection.close();\n db = null;\n connectPromise = null;\n };\n\n db = connection;\n resolve();\n };\n request.onerror = () => {\n connectPromise = null;\n reject(new VaultError(`failed to open \"${name}\"`, { cause: request.error }));\n };\n });\n }\n\n return connectPromise;\n };\n\n const withStore = async <T>(\n table: keyof S,\n mode: 'readonly' | 'readwrite',\n fn: (store: IDBObjectStore) => Promise<T>,\n ): Promise<T> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const tableName = String(table);\n const tx = db.transaction(tableName, mode);\n\n return runIdbTx(tx, `${name}/${tableName}`, () => fn(tx.objectStore(tableName)));\n };\n\n const requireDb = async (): Promise<IDBDatabase> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return db;\n };\n\n const publish = <K extends keyof S>(table: K): void => {\n channel?.postMessage({ table: String(table) });\n };\n\n // Table-aware decode: unwrap envelope + apply codec.decode\n const decodeWithCodec = <T extends object>(table: keyof S, raw: unknown): T | undefined => {\n const stored = parseStored<unknown>(raw);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: err });\n }\n };\n\n // Table-aware encode: apply codec.encode + wrap in envelope\n const encodeWithCodec = <T>(table: keyof S, value: T, ttl?: number): StoredRecord<unknown> => {\n const record = value as RecordOf<S, typeof table>;\n const encoded = encodeRecord(codecs[table], record);\n\n for (const field of schema[table].indexes ?? []) {\n const indexedValue = (record as Record<string, unknown>)[field];\n if (indexedValue === undefined) continue;\n\n if (\n typeof encoded !== 'object' ||\n encoded === null ||\n !Object.hasOwn(encoded, field) ||\n !Object.is((encoded as Record<string, unknown>)[field], indexedValue)\n ) {\n throw new VaultError(`codec output for table \"${String(table)}\" must preserve indexed field \"${field}\"`);\n }\n }\n\n return ttl === undefined ? { value: encoded } : { expiresAt: Date.now() + ttl, value: encoded };\n };\n\n const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n deleteMany: (table, keys) =>\n withStore(table, 'readwrite', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, typeof table>>(s, keys.map(encodeVaultKey), decode);\n }),\n\n async dispose(): Promise<void> {\n disposed = true;\n channel?.close();\n\n // F7: Wait for any in-progress connect before closing the DB to avoid\n // \"database connection is closing\" errors on in-flight requests.\n if (connectPromise) await connectPromise.catch(() => {});\n\n db?.close();\n db = null;\n connectPromise = null;\n },\n\n get: (table, key) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode);\n }),\n\n getAll: (table) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n }),\n\n getMany: (table, keys) =>\n withStore(table, 'readonly', (s) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)));\n }),\n\n iterate: (table) => {\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, typeof table>>> => {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n if (!db) await connect();\n\n if (!db || disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n const idb = db;\n const tx = idb.transaction(String(table), 'readonly');\n const store = tx.objectStore(String(table));\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n store,\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n };\n\n let inner: AsyncIterator<RecordOf<S, typeof table>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, typeof table>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, typeof table>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.return?.(value) ?? Promise.resolve({ done: true, value });\n\n return Promise.resolve({ done: true, value });\n },\n throw(err?: unknown): Promise<IteratorResult<RecordOf<S, typeof table>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n\n async pruneAllExpired() {\n const idb = await requireDb();\n const tableNames = Object.keys(schema);\n const tx = idb.transaction(tableNames, 'readwrite');\n const results = await runIdbTx(tx, `${name}/pruneAll`, () =>\n Promise.all(tableNames.map(async (t) => [t, await pruneExpiredInStore(tx.objectStore(t))] as const)),\n );\n\n return Object.fromEntries(results);\n },\n\n pruneExpiredInTable: (table) => withStore(table, 'readwrite', (s) => pruneExpiredInStore(s)),\n\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return withStore(table, 'readwrite', (s) => idbReq(s.put(encoded, key)).then(() => undefined));\n },\n\n putAll(table, values, ttl) {\n const encodedValues = values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n\n return { encoded: encodeWithCodec(table, v, ttl), key };\n });\n\n return withStore(table, 'readwrite', (s) =>\n Promise.all(encodedValues.map(({ encoded, key }) => idbReq(s.put(encoded, key)))).then(() => undefined),\n );\n },\n };\n\n const idbBatch = async <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n notifyMutation: (table: K) => void,\n validateFn: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n ): Promise<R> => {\n assertBatchTables(tables);\n\n const idb = await requireDb();\n const idbTx = idb.transaction([...tables] as string[], 'readwrite');\n const dirtyTables = new Set<K>();\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n const batchCore: StorageBackend<S, K> = {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n delete: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode);\n },\n deleteMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n return storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode);\n },\n get: (table, key) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode);\n },\n getAll: (table) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode);\n },\n getMany: (table, keys) => {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n const store = storeOf(table);\n return Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(store, encodeVaultKey(key), decode)));\n },\n iterate(table) {\n const decode = <T extends object>(raw: unknown): T | undefined => decodeWithCodec<T>(table, raw);\n\n return iterateStoreWithCursor<RecordOf<S, typeof table>>(\n storeOf(table),\n decode as (raw: unknown) => RecordOf<S, typeof table> | undefined,\n );\n },\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n const key = encodeVaultKey(getRecordKey(schema, table, value));\n const encoded = encodeWithCodec(table, value, ttl);\n\n return idbReq(storeOf(table).put(encoded, key)).then(() => undefined);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => {\n const key = encodeVaultKey(getRecordKey(schema, table, v));\n const encoded = encodeWithCodec(table, v, ttl);\n\n return idbReq(storeOf(table).put(encoded, key));\n }),\n ).then(() => undefined);\n },\n };\n\n let contextActive = true;\n const tx = buildOperations<S, K>(\n schema,\n batchCore,\n (t) => dirtyTables.add(t),\n validateFn,\n batchScopeGuard(tables, () => contextActive),\n );\n\n try {\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) notifyMutation(table);\n\n return result;\n } finally {\n contextActive = false;\n }\n };\n\n const adapter = buildDocumentStore(schema, core, {\n codecs,\n createBatch: (deps) => (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate),\n onCrossTabMessage(notify) {\n if (!channel) {\n return undefined;\n }\n\n channel.onmessage = (event: MessageEvent<{ table?: string }>) => {\n const tableName = event.data?.table;\n\n if (!tableName || !Object.hasOwn(schema, tableName)) return;\n\n notify(tableName as keyof S & string);\n };\n\n return () => {\n channel.onmessage = null;\n };\n },\n onMutation: publish,\n schema,\n });\n\n return Object.assign(adapter, {\n getAllByIndex<K extends keyof S & string, Field extends keyof RecordOf<S, K> & string>(\n table: K,\n field: Field,\n value: RecordOf<S, K>[Field],\n ): Promise<RecordOf<S, K>[]> {\n return withStore(table, 'readonly', (store) => {\n if (!store.indexNames.contains(field)) throw new VaultError(`Table \"${table}\" has no index \"${field}\"`);\n\n const decode = (raw: unknown): RecordOf<S, K> | undefined => decodeWithCodec<RecordOf<S, K>>(table, raw);\n return getAllFromStore(store.index(field), decode, value as IDBValidKey);\n });\n },\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"webstorage.cjs","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildVaultStore, type StorageBackend } from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DurableStoreOptions, KeyOf, KeyValueVaultStore, RecordOf } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n name: string;\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): KeyValueVaultStore<S> {\n const { getStorage, name, schema, storageLabel, codecs } = options;\n\n if (!codecs) {\n throw new VaultError(`${storageLabel} requires codecs for durable persistence`);\n }\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n throw new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, { cause: error });\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of storage keys owned by this namespace.\n // Table-wide operations resynchronize it so same-window adapters stay consistent.\n const ownedKeys = new Set<string>();\n\n const syncOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n ownedKeys.clear();\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n syncOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the decoded live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n */\n const parseEntry = <T extends object>(table: keyof S, storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n let stored: StoredRecord<unknown> | undefined;\n try {\n stored = parseStored<unknown>(JSON.parse(raw) as unknown);\n } catch {\n return undefined;\n }\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (error) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: error });\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n syncOwnedKeys();\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n syncOwnedKeys();\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n syncOwnedKeys();\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildVaultStore(schema, core, {\n codecs,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n schema,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"webstorage.cjs","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildVaultStore, type StorageBackend } from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DurableStoreOptions, KeyOf, KeyValueVaultStore, RecordOf } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n name: string;\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): KeyValueVaultStore<S> {\n const { getStorage, name, schema, storageLabel, codecs } = options;\n\n if (!codecs) {\n throw new VaultError(`${storageLabel} requires codecs for durable persistence`);\n }\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n throw new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, { cause: error });\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of storage keys owned by this namespace.\n // Table-wide operations resynchronize it so same-window adapters stay consistent.\n const ownedKeys = new Set<string>();\n\n const syncOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n ownedKeys.clear();\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n syncOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the decoded live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n */\n const parseEntry = <T extends object>(table: keyof S, storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n let stored: StoredRecord<unknown> | undefined;\n try {\n stored = parseStored<unknown>(JSON.parse(raw) as unknown);\n } catch {\n return undefined;\n }\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (error) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: error });\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n syncOwnedKeys();\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n syncOwnedKeys();\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n syncOwnedKeys();\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildVaultStore(schema, core, {\n codecs,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab: all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n schema,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"webstorage.js","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildVaultStore, type StorageBackend } from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DurableStoreOptions, KeyOf, KeyValueVaultStore, RecordOf } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n name: string;\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): KeyValueVaultStore<S> {\n const { getStorage, name, schema, storageLabel, codecs } = options;\n\n if (!codecs) {\n throw new VaultError(`${storageLabel} requires codecs for durable persistence`);\n }\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n throw new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, { cause: error });\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of storage keys owned by this namespace.\n // Table-wide operations resynchronize it so same-window adapters stay consistent.\n const ownedKeys = new Set<string>();\n\n const syncOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n ownedKeys.clear();\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n syncOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the decoded live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n */\n const parseEntry = <T extends object>(table: keyof S, storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n let stored: StoredRecord<unknown> | undefined;\n try {\n stored = parseStored<unknown>(JSON.parse(raw) as unknown);\n } catch {\n return undefined;\n }\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (error) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: error });\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n syncOwnedKeys();\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n syncOwnedKeys();\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n syncOwnedKeys();\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildVaultStore(schema, core, {\n codecs,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n schema,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"webstorage.js","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildVaultStore, type StorageBackend } from '../adapter-core';\nimport { decodeRecord, encodeRecord } from '../codec';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type { AnySchema, DurableStoreOptions, KeyOf, KeyValueVaultStore, RecordOf } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = DurableStoreOptions<S> & {\n name: string;\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): KeyValueVaultStore<S> {\n const { getStorage, name, schema, storageLabel, codecs } = options;\n\n if (!codecs) {\n throw new VaultError(`${storageLabel} requires codecs for durable persistence`);\n }\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n throw new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, { cause: error });\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of storage keys owned by this namespace.\n // Table-wide operations resynchronize it so same-window adapters stay consistent.\n const ownedKeys = new Set<string>();\n\n const syncOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n ownedKeys.clear();\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n syncOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the decoded live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n */\n const parseEntry = <T extends object>(table: keyof S, storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n let stored: StoredRecord<unknown> | undefined;\n try {\n stored = parseStored<unknown>(JSON.parse(raw) as unknown);\n } catch {\n return undefined;\n }\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n try {\n return decodeRecord(codecs[table], stored.value) as unknown as T;\n } catch (error) {\n throw new VaultError(`validation failed for table \"${String(table)}\"`, { cause: error });\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n syncOwnedKeys();\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n syncOwnedKeys();\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(table, storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n syncOwnedKeys();\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const encoded = encodeRecord(codecs[table], value);\n\n writeItem(storageKey, expiresAt === undefined ? { value: encoded } : { expiresAt, value: encoded });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildVaultStore(schema, core, {\n codecs,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab: all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n schema,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): KeyValueVaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\nexport type RecordOf<S extends AnySchema, K extends keyof S> =\n S[K] extends SchemaEntry<infer R, infer _Key> ? R : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = Extract<\n S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never,\n VaultKey\n>;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"uDA0CA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,EAAA,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,EAAA,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
1
+ {"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\n/** Distributes over the table key so a union of tables resolves to the union of its records\n * (and of its keys) instead of collapsing to `never`: callers that dispatch on a set of\n * tables keep the store's correlation without per-table casts. */\nexport type RecordOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? S[K] extends SchemaEntry<infer R, infer _Key>\n ? R\n : never\n : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey>\n : never;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"uDA+CA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,EAAA,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,EAAA,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
package/dist/types.d.ts CHANGED
@@ -12,8 +12,11 @@ export type AnySchema = Record<string, {
12
12
  indexes?: readonly string[];
13
13
  key: string;
14
14
  }>;
15
- export type RecordOf<S extends AnySchema, K extends keyof S> = S[K] extends SchemaEntry<infer R, infer _Key> ? R : never;
16
- export type KeyOf<S extends AnySchema, K extends keyof S> = Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey>;
15
+ /** Distributes over the table key so a union of tables resolves to the union of its records
16
+ * (and of its keys) instead of collapsing to `never`: callers that dispatch on a set of
17
+ * tables keep the store's correlation without per-table casts. */
18
+ export type RecordOf<S extends AnySchema, K extends keyof S> = K extends unknown ? S[K] extends SchemaEntry<infer R, infer _Key> ? R : never : never;
19
+ export type KeyOf<S extends AnySchema, K extends keyof S> = K extends unknown ? Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey> : never;
17
20
  /** A codec validates and decodes persisted values crossing a trust boundary. */
18
21
  export type RecordCodec<T> = {
19
22
  decode(value: unknown): T;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\nexport type RecordOf<S extends AnySchema, K extends keyof S> =\n S[K] extends SchemaEntry<infer R, infer _Key> ? R : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = Extract<\n S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never,\n VaultKey\n>;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"yFA0CA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,EAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,EAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\n/** Distributes over the table key so a union of tables resolves to the union of its records\n * (and of its keys) instead of collapsing to `never`: callers that dispatch on a set of\n * tables keep the store's correlation without per-table casts. */\nexport type RecordOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? S[K] extends SchemaEntry<infer R, infer _Key>\n ? R\n : never\n : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey>\n : never;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"yFA+CA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,EAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,EAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"vault.cjs","names":[],"sources":["../src/errors.ts","../src/ttl.ts","../src/types.ts"],"sourcesContent":["/**\n * Base class for all vault errors. Catch with `instanceof VaultError` to\n * handle any vault-originated error regardless of its specific subtype.\n */\nexport class VaultError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n // Ensures `instanceof` works correctly when transpiled to ES5.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when an operation is attempted on a disposed adapter or observer hub. */\nexport class VaultDisposedError extends VaultError {\n constructor(message = 'adapter is disposed', opts?: ErrorOptions) {\n super(message, opts);\n }\n}\n\n/** Thrown when a `batch()` callback accesses a table not declared in the scope. */\nexport class VaultScopeError extends VaultError {}\n\n/** Thrown when a WebStorage write exceeds the storage quota. */\nexport class VaultQuotaError extends VaultError {}\n\n/** Thrown when an IndexedDB `onupgradeneeded` migration callback throws. */\nexport class VaultMigrationError extends VaultError {}\n","import { VaultError } from './errors';\n\n/** Duration helpers that produce finite, positive millisecond values. */\nexport const ttl = {\n days: (n: number) => assertPositiveFinite(n * 86_400_000, 'ttl.days'),\n hours: (n: number) => assertPositiveFinite(n * 3_600_000, 'ttl.hours'),\n minutes: (n: number) => assertPositiveFinite(n * 60_000, 'ttl.minutes'),\n ms: (n: number) => assertPositiveFinite(n, 'ttl.ms'),\n seconds: (n: number) => assertPositiveFinite(n * 1000, 'ttl.seconds'),\n} as const;\n\n/** Fixed storage envelope shared by every adapter, so data and indexes stay portable. */\nexport type StoredRecord<T> = { expiresAt?: number; value: T };\n\nexport function isExpired(expiresAt: number | undefined): boolean {\n return expiresAt !== undefined && Date.now() >= expiresAt;\n}\n\n/** Throws when `ttlMs` is not a finite, positive number. Returns it unchanged otherwise. */\nexport function assertPositiveFinite(ttlMs: number, source: string): number {\n if (!Number.isFinite(ttlMs) || ttlMs <= 0) {\n throw new VaultError(`${source} expected a finite positive number, received ${String(ttlMs)}`);\n }\n\n return ttlMs;\n}\n\nexport function parseStored<T>(raw: unknown): StoredRecord<T> | undefined {\n if (typeof raw !== 'object' || raw === null || !('value' in raw)) return undefined;\n\n const record = raw as { expiresAt?: unknown; value: unknown };\n\n if (record.expiresAt !== undefined && (typeof record.expiresAt !== 'number' || !Number.isFinite(record.expiresAt))) {\n return undefined;\n }\n\n return record as StoredRecord<T>;\n}\n","/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\nexport type RecordOf<S extends AnySchema, K extends keyof S> =\n S[K] extends SchemaEntry<infer R, infer _Key> ? R : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = Extract<\n S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never,\n VaultKey\n>;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"mEAIA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,gBAAb,cAAqC,UAAW,CAAC,EAGpC,gBAAb,cAAqC,UAAW,CAAC,EAGpC,oBAAb,cAAyC,UAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,qBAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,qBAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,qBAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,qBAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,qBAAqB,EAAI,IAAM,aAAa,CACtE,EAUA,SAAgB,qBAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,WAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCiBA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
1
+ {"version":3,"file":"vault.cjs","names":[],"sources":["../src/errors.ts","../src/ttl.ts","../src/types.ts"],"sourcesContent":["/**\n * Base class for all vault errors. Catch with `instanceof VaultError` to\n * handle any vault-originated error regardless of its specific subtype.\n */\nexport class VaultError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n // Ensures `instanceof` works correctly when transpiled to ES5.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when an operation is attempted on a disposed adapter or observer hub. */\nexport class VaultDisposedError extends VaultError {\n constructor(message = 'adapter is disposed', opts?: ErrorOptions) {\n super(message, opts);\n }\n}\n\n/** Thrown when a `batch()` callback accesses a table not declared in the scope. */\nexport class VaultScopeError extends VaultError {}\n\n/** Thrown when a WebStorage write exceeds the storage quota. */\nexport class VaultQuotaError extends VaultError {}\n\n/** Thrown when an IndexedDB `onupgradeneeded` migration callback throws. */\nexport class VaultMigrationError extends VaultError {}\n","import { VaultError } from './errors';\n\n/** Duration helpers that produce finite, positive millisecond values. */\nexport const ttl = {\n days: (n: number) => assertPositiveFinite(n * 86_400_000, 'ttl.days'),\n hours: (n: number) => assertPositiveFinite(n * 3_600_000, 'ttl.hours'),\n minutes: (n: number) => assertPositiveFinite(n * 60_000, 'ttl.minutes'),\n ms: (n: number) => assertPositiveFinite(n, 'ttl.ms'),\n seconds: (n: number) => assertPositiveFinite(n * 1000, 'ttl.seconds'),\n} as const;\n\n/** Fixed storage envelope shared by every adapter, so data and indexes stay portable. */\nexport type StoredRecord<T> = { expiresAt?: number; value: T };\n\nexport function isExpired(expiresAt: number | undefined): boolean {\n return expiresAt !== undefined && Date.now() >= expiresAt;\n}\n\n/** Throws when `ttlMs` is not a finite, positive number. Returns it unchanged otherwise. */\nexport function assertPositiveFinite(ttlMs: number, source: string): number {\n if (!Number.isFinite(ttlMs) || ttlMs <= 0) {\n throw new VaultError(`${source} expected a finite positive number, received ${String(ttlMs)}`);\n }\n\n return ttlMs;\n}\n\nexport function parseStored<T>(raw: unknown): StoredRecord<T> | undefined {\n if (typeof raw !== 'object' || raw === null || !('value' in raw)) return undefined;\n\n const record = raw as { expiresAt?: unknown; value: unknown };\n\n if (record.expiresAt !== undefined && (typeof record.expiresAt !== 'number' || !Number.isFinite(record.expiresAt))) {\n return undefined;\n }\n\n return record as StoredRecord<T>;\n}\n","/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\n/** Distributes over the table key so a union of tables resolves to the union of its records\n * (and of its keys) instead of collapsing to `never`: callers that dispatch on a set of\n * tables keep the store's correlation without per-table casts. */\nexport type RecordOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? S[K] extends SchemaEntry<infer R, infer _Key>\n ? R\n : never\n : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey>\n : never;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"mEAIA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,gBAAb,cAAqC,UAAW,CAAC,EAGpC,gBAAb,cAAqC,UAAW,CAAC,EAGpC,oBAAb,cAAyC,UAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,qBAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,qBAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,qBAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,qBAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,qBAAqB,EAAI,IAAM,aAAa,CACtE,EAUA,SAAgB,qBAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,WAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCsBA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"vault.iife.js","names":[],"sources":["../src/errors.ts","../src/ttl.ts","../src/types.ts"],"sourcesContent":["/**\n * Base class for all vault errors. Catch with `instanceof VaultError` to\n * handle any vault-originated error regardless of its specific subtype.\n */\nexport class VaultError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n // Ensures `instanceof` works correctly when transpiled to ES5.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when an operation is attempted on a disposed adapter or observer hub. */\nexport class VaultDisposedError extends VaultError {\n constructor(message = 'adapter is disposed', opts?: ErrorOptions) {\n super(message, opts);\n }\n}\n\n/** Thrown when a `batch()` callback accesses a table not declared in the scope. */\nexport class VaultScopeError extends VaultError {}\n\n/** Thrown when a WebStorage write exceeds the storage quota. */\nexport class VaultQuotaError extends VaultError {}\n\n/** Thrown when an IndexedDB `onupgradeneeded` migration callback throws. */\nexport class VaultMigrationError extends VaultError {}\n","import { VaultError } from './errors';\n\n/** Duration helpers that produce finite, positive millisecond values. */\nexport const ttl = {\n days: (n: number) => assertPositiveFinite(n * 86_400_000, 'ttl.days'),\n hours: (n: number) => assertPositiveFinite(n * 3_600_000, 'ttl.hours'),\n minutes: (n: number) => assertPositiveFinite(n * 60_000, 'ttl.minutes'),\n ms: (n: number) => assertPositiveFinite(n, 'ttl.ms'),\n seconds: (n: number) => assertPositiveFinite(n * 1000, 'ttl.seconds'),\n} as const;\n\n/** Fixed storage envelope shared by every adapter, so data and indexes stay portable. */\nexport type StoredRecord<T> = { expiresAt?: number; value: T };\n\nexport function isExpired(expiresAt: number | undefined): boolean {\n return expiresAt !== undefined && Date.now() >= expiresAt;\n}\n\n/** Throws when `ttlMs` is not a finite, positive number. Returns it unchanged otherwise. */\nexport function assertPositiveFinite(ttlMs: number, source: string): number {\n if (!Number.isFinite(ttlMs) || ttlMs <= 0) {\n throw new VaultError(`${source} expected a finite positive number, received ${String(ttlMs)}`);\n }\n\n return ttlMs;\n}\n\nexport function parseStored<T>(raw: unknown): StoredRecord<T> | undefined {\n if (typeof raw !== 'object' || raw === null || !('value' in raw)) return undefined;\n\n const record = raw as { expiresAt?: unknown; value: unknown };\n\n if (record.expiresAt !== undefined && (typeof record.expiresAt !== 'number' || !Number.isFinite(record.expiresAt))) {\n return undefined;\n }\n\n return record as StoredRecord<T>;\n}\n","/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\nexport type RecordOf<S extends AnySchema, K extends keyof S> =\n S[K] extends SchemaEntry<infer R, infer _Key> ? R : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = Extract<\n S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never,\n VaultKey\n>;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"oFAIA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,gBAAb,cAAqC,UAAW,CAAC,EAGpC,gBAAb,cAAqC,UAAW,CAAC,EAGpC,oBAAb,cAAyC,UAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,qBAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,qBAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,qBAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,qBAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,qBAAqB,EAAI,IAAM,aAAa,CACtE,EAUA,SAAgB,qBAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,WAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCiBA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
1
+ {"version":3,"file":"vault.iife.js","names":[],"sources":["../src/errors.ts","../src/ttl.ts","../src/types.ts"],"sourcesContent":["/**\n * Base class for all vault errors. Catch with `instanceof VaultError` to\n * handle any vault-originated error regardless of its specific subtype.\n */\nexport class VaultError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n // Ensures `instanceof` works correctly when transpiled to ES5.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when an operation is attempted on a disposed adapter or observer hub. */\nexport class VaultDisposedError extends VaultError {\n constructor(message = 'adapter is disposed', opts?: ErrorOptions) {\n super(message, opts);\n }\n}\n\n/** Thrown when a `batch()` callback accesses a table not declared in the scope. */\nexport class VaultScopeError extends VaultError {}\n\n/** Thrown when a WebStorage write exceeds the storage quota. */\nexport class VaultQuotaError extends VaultError {}\n\n/** Thrown when an IndexedDB `onupgradeneeded` migration callback throws. */\nexport class VaultMigrationError extends VaultError {}\n","import { VaultError } from './errors';\n\n/** Duration helpers that produce finite, positive millisecond values. */\nexport const ttl = {\n days: (n: number) => assertPositiveFinite(n * 86_400_000, 'ttl.days'),\n hours: (n: number) => assertPositiveFinite(n * 3_600_000, 'ttl.hours'),\n minutes: (n: number) => assertPositiveFinite(n * 60_000, 'ttl.minutes'),\n ms: (n: number) => assertPositiveFinite(n, 'ttl.ms'),\n seconds: (n: number) => assertPositiveFinite(n * 1000, 'ttl.seconds'),\n} as const;\n\n/** Fixed storage envelope shared by every adapter, so data and indexes stay portable. */\nexport type StoredRecord<T> = { expiresAt?: number; value: T };\n\nexport function isExpired(expiresAt: number | undefined): boolean {\n return expiresAt !== undefined && Date.now() >= expiresAt;\n}\n\n/** Throws when `ttlMs` is not a finite, positive number. Returns it unchanged otherwise. */\nexport function assertPositiveFinite(ttlMs: number, source: string): number {\n if (!Number.isFinite(ttlMs) || ttlMs <= 0) {\n throw new VaultError(`${source} expected a finite positive number, received ${String(ttlMs)}`);\n }\n\n return ttlMs;\n}\n\nexport function parseStored<T>(raw: unknown): StoredRecord<T> | undefined {\n if (typeof raw !== 'object' || raw === null || !('value' in raw)) return undefined;\n\n const record = raw as { expiresAt?: unknown; value: unknown };\n\n if (record.expiresAt !== undefined && (typeof record.expiresAt !== 'number' || !Number.isFinite(record.expiresAt))) {\n return undefined;\n }\n\n return record as StoredRecord<T>;\n}\n","/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\n/** Distributes over the table key so a union of tables resolves to the union of its records\n * (and of its keys) instead of collapsing to `never`: callers that dispatch on a set of\n * tables keep the store's correlation without per-table casts. */\nexport type RecordOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? S[K] extends SchemaEntry<infer R, infer _Key>\n ? R\n : never\n : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey>\n : never;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"oFAIA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,gBAAb,cAAqC,UAAW,CAAC,EAGpC,gBAAb,cAAqC,UAAW,CAAC,EAGpC,oBAAb,cAAyC,UAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,qBAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,qBAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,qBAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,qBAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,qBAAqB,EAAI,IAAM,aAAa,CACtE,EAUA,SAAgB,qBAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,WAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCsBA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
package/dist/vault.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vault.js","names":[],"sources":["../src/errors.ts","../src/ttl.ts","../src/types.ts"],"sourcesContent":["/**\n * Base class for all vault errors. Catch with `instanceof VaultError` to\n * handle any vault-originated error regardless of its specific subtype.\n */\nexport class VaultError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n // Ensures `instanceof` works correctly when transpiled to ES5.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when an operation is attempted on a disposed adapter or observer hub. */\nexport class VaultDisposedError extends VaultError {\n constructor(message = 'adapter is disposed', opts?: ErrorOptions) {\n super(message, opts);\n }\n}\n\n/** Thrown when a `batch()` callback accesses a table not declared in the scope. */\nexport class VaultScopeError extends VaultError {}\n\n/** Thrown when a WebStorage write exceeds the storage quota. */\nexport class VaultQuotaError extends VaultError {}\n\n/** Thrown when an IndexedDB `onupgradeneeded` migration callback throws. */\nexport class VaultMigrationError extends VaultError {}\n","import { VaultError } from './errors';\n\n/** Duration helpers that produce finite, positive millisecond values. */\nexport const ttl = {\n days: (n: number) => assertPositiveFinite(n * 86_400_000, 'ttl.days'),\n hours: (n: number) => assertPositiveFinite(n * 3_600_000, 'ttl.hours'),\n minutes: (n: number) => assertPositiveFinite(n * 60_000, 'ttl.minutes'),\n ms: (n: number) => assertPositiveFinite(n, 'ttl.ms'),\n seconds: (n: number) => assertPositiveFinite(n * 1000, 'ttl.seconds'),\n} as const;\n\n/** Fixed storage envelope shared by every adapter, so data and indexes stay portable. */\nexport type StoredRecord<T> = { expiresAt?: number; value: T };\n\nexport function isExpired(expiresAt: number | undefined): boolean {\n return expiresAt !== undefined && Date.now() >= expiresAt;\n}\n\n/** Throws when `ttlMs` is not a finite, positive number. Returns it unchanged otherwise. */\nexport function assertPositiveFinite(ttlMs: number, source: string): number {\n if (!Number.isFinite(ttlMs) || ttlMs <= 0) {\n throw new VaultError(`${source} expected a finite positive number, received ${String(ttlMs)}`);\n }\n\n return ttlMs;\n}\n\nexport function parseStored<T>(raw: unknown): StoredRecord<T> | undefined {\n if (typeof raw !== 'object' || raw === null || !('value' in raw)) return undefined;\n\n const record = raw as { expiresAt?: unknown; value: unknown };\n\n if (record.expiresAt !== undefined && (typeof record.expiresAt !== 'number' || !Number.isFinite(record.expiresAt))) {\n return undefined;\n }\n\n return record as StoredRecord<T>;\n}\n","/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\nexport type RecordOf<S extends AnySchema, K extends keyof S> =\n S[K] extends SchemaEntry<infer R, infer _Key> ? R : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = Extract<\n S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never,\n VaultKey\n>;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"AAIA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,gBAAb,cAAqC,UAAW,CAAC,EAGpC,gBAAb,cAAqC,UAAW,CAAC,EAGpC,oBAAb,cAAyC,UAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,qBAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,qBAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,qBAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,qBAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,qBAAqB,EAAI,IAAM,aAAa,CACtE,EAUA,SAAgB,qBAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,WAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCiBA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
1
+ {"version":3,"file":"vault.js","names":[],"sources":["../src/errors.ts","../src/ttl.ts","../src/types.ts"],"sourcesContent":["/**\n * Base class for all vault errors. Catch with `instanceof VaultError` to\n * handle any vault-originated error regardless of its specific subtype.\n */\nexport class VaultError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n // Ensures `instanceof` works correctly when transpiled to ES5.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when an operation is attempted on a disposed adapter or observer hub. */\nexport class VaultDisposedError extends VaultError {\n constructor(message = 'adapter is disposed', opts?: ErrorOptions) {\n super(message, opts);\n }\n}\n\n/** Thrown when a `batch()` callback accesses a table not declared in the scope. */\nexport class VaultScopeError extends VaultError {}\n\n/** Thrown when a WebStorage write exceeds the storage quota. */\nexport class VaultQuotaError extends VaultError {}\n\n/** Thrown when an IndexedDB `onupgradeneeded` migration callback throws. */\nexport class VaultMigrationError extends VaultError {}\n","import { VaultError } from './errors';\n\n/** Duration helpers that produce finite, positive millisecond values. */\nexport const ttl = {\n days: (n: number) => assertPositiveFinite(n * 86_400_000, 'ttl.days'),\n hours: (n: number) => assertPositiveFinite(n * 3_600_000, 'ttl.hours'),\n minutes: (n: number) => assertPositiveFinite(n * 60_000, 'ttl.minutes'),\n ms: (n: number) => assertPositiveFinite(n, 'ttl.ms'),\n seconds: (n: number) => assertPositiveFinite(n * 1000, 'ttl.seconds'),\n} as const;\n\n/** Fixed storage envelope shared by every adapter, so data and indexes stay portable. */\nexport type StoredRecord<T> = { expiresAt?: number; value: T };\n\nexport function isExpired(expiresAt: number | undefined): boolean {\n return expiresAt !== undefined && Date.now() >= expiresAt;\n}\n\n/** Throws when `ttlMs` is not a finite, positive number. Returns it unchanged otherwise. */\nexport function assertPositiveFinite(ttlMs: number, source: string): number {\n if (!Number.isFinite(ttlMs) || ttlMs <= 0) {\n throw new VaultError(`${source} expected a finite positive number, received ${String(ttlMs)}`);\n }\n\n return ttlMs;\n}\n\nexport function parseStored<T>(raw: unknown): StoredRecord<T> | undefined {\n if (typeof raw !== 'object' || raw === null || !('value' in raw)) return undefined;\n\n const record = raw as { expiresAt?: unknown; value: unknown };\n\n if (record.expiresAt !== undefined && (typeof record.expiresAt !== 'number' || !Number.isFinite(record.expiresAt))) {\n return undefined;\n }\n\n return record as StoredRecord<T>;\n}\n","/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport { assertPositiveFinite } from './ttl';\n\n/** Portable primary-key values. Vault preserves their type and encodes them distinctly at rest. */\nexport type VaultKey = number | string;\n\n/** A typed table definition whose primary-key field must hold a portable Vault key. */\nexport type SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> = {\n defaultTtl?: number;\n /** IndexedDB creates these as `value.<field>` indexes; other stores filter in memory. */\n indexes?: readonly (keyof T & string)[];\n key: Key;\n};\n\nexport type AnySchema = Record<string, { defaultTtl?: number; indexes?: readonly string[]; key: string }>;\n\n/** Distributes over the table key so a union of tables resolves to the union of its records\n * (and of its keys) instead of collapsing to `never`: callers that dispatch on a set of\n * tables keep the store's correlation without per-table casts. */\nexport type RecordOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? S[K] extends SchemaEntry<infer R, infer _Key>\n ? R\n : never\n : never;\nexport type KeyOf<S extends AnySchema, K extends keyof S> = K extends unknown\n ? Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey>\n : never;\n\n/** A codec validates and decodes persisted values crossing a trust boundary. */\nexport type RecordCodec<T> = {\n decode(value: unknown): T;\n encode(value: T): unknown;\n};\n\nexport type RecordParser<T> = {\n parse(value: unknown): T;\n};\n\nexport type CodecInput<T> = RecordCodec<T> | RecordParser<T>;\n\nexport type TableCodecs<S extends AnySchema> = {\n [K in keyof S]: CodecInput<RecordOf<S, K>>;\n};\n\n/** A validator-only codec: encodes values as-is, validates on decode. */\nexport function validatorCodec<T>(validator: RecordParser<T>): RecordCodec<T> {\n return {\n decode: (value) => validator.parse(value),\n encode: (value) => value,\n };\n}\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options for stores that do not require durable codecs. */\nexport type MemoryStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Optional per-table codecs. Memory values never cross a trust boundary, so codecs may be omitted. */\n codecs?: TableCodecs<S>;\n};\n\n/** Shared factory options for durable stores whose values cross a trust boundary. */\nexport type DurableStoreOptions<S extends AnySchema> = {\n schema: S;\n /** Required per-table codecs. Persisted values are decoded through these before entering typed code. */\n codecs: TableCodecs<S>;\n};\n\nexport type VaultConveniences<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n count<T extends K>(table: T): Promise<number>;\n deleteMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n getMany<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n isEmpty<T extends K>(table: T): Promise<boolean>;\n keys<T extends K>(table: T, filter?: (record: RecordOf<S, T>) => boolean): Promise<KeyOf<S, T>[]>;\n update<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n changes: Partial<RecordOf<S, T>>,\n ttl?: number,\n ): Promise<RecordOf<S, T> | undefined>;\n upsert<T extends K>(\n table: T,\n key: KeyOf<S, T>,\n update: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Methods available within an IndexedDB or SQLite transaction. */\nexport type TransactionContext<S extends AnySchema, K extends keyof S & string = keyof S & string> = VaultConveniences<\n S,\n K\n> & {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n iterate<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<RecordOf<S, T>>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** Key-value store for Memory and Web Storage: core CRUD, observation, TTL, and pruning. */\nexport interface KeyValueVaultStore<S extends AnySchema> extends VaultConveniences<S> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n observe<K extends keyof S & string>(\n table: K,\n listener: Observer<RecordOf<S, K>>,\n options?: { immediate?: boolean; signal?: AbortSignal },\n ): Unsubscribe;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;\n putAll<K extends keyof S & string>(table: K, values: readonly RecordOf<S, K>[], ttl?: number): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Document store for IndexedDB and SQLite: adds atomic transactions and lazy iteration. */\nexport interface DocumentVaultStore<S extends AnySchema> extends KeyValueVaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n\ntype VaultKeyField<T extends object> = {\n [K in keyof T & string]: T[K] extends VaultKey ? K : never;\n}[keyof T & string];\n\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends VaultKeyField<T> = VaultKeyField<T>>(\n key: Key,\n options: { defaultTtl?: number; indexes?: readonly (keyof T & string)[] } = {},\n): SchemaEntry<T, Key> {\n const { defaultTtl, indexes } = options;\n\n if (defaultTtl !== undefined) assertPositiveFinite(defaultTtl, 'table: defaultTtl');\n\n if (indexes) {\n const seen = new Set<string>();\n\n for (const field of indexes) {\n if (seen.has(field)) {\n throw new VaultError(`table: index \"${field}\" is already registered`);\n }\n\n seen.add(field);\n }\n }\n\n return { defaultTtl, indexes, key };\n}\n"],"mappings":"AAIA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,gBAAb,cAAqC,UAAW,CAAC,EAGpC,gBAAb,cAAqC,UAAW,CAAC,EAGpC,oBAAb,cAAyC,UAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,qBAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,qBAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,qBAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,qBAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,qBAAqB,EAAI,IAAM,aAAa,CACtE,EAUA,SAAgB,qBAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,WAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCsBA,SAAgB,eAAkB,EAA4C,CAC5E,MAAO,CACL,OAAS,GAAU,EAAU,MAAM,CAAK,EACxC,OAAS,GAAU,CACrB,CACF,CAwFA,SAAgB,MACd,EACA,EAA4E,CAAC,EACxD,CACrB,GAAM,CAAE,aAAY,WAAY,EAIhC,GAFI,IAAe,IAAA,IAAW,qBAAqB,EAAY,mBAAmB,EAE9E,EAAS,CACX,IAAM,EAAO,IAAI,IAEjB,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,EAAK,IAAI,CAAK,EAChB,MAAM,IAAI,WAAW,iBAAiB,EAAM,wBAAwB,EAGtE,EAAK,IAAI,CAAK,CAChB,CACF,CAEA,MAAO,CAAE,aAAY,UAAS,KAAI,CACpC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/vault",
3
- "version": "3.0.3",
3
+ "version": "26.10.1",
4
4
  "description": "Adapter-free typed storage core with focused browser and SQLite subpaths",
5
5
  "repository": {
6
6
  "type": "git",
@@ -81,14 +81,15 @@
81
81
  "lint": "biome ci src",
82
82
  "prepublishOnly": "pnpm --silent run build",
83
83
  "preview": "vite preview",
84
- "test": "vitest",
84
+ "test": "pnpm test:types && vitest",
85
+ "test:types": "tsc -p tsconfig.types.json",
85
86
  "test:sqlite:bun": "bun test ./runtime-smoke/bun-sqlite.ts",
86
87
  "test:sqlite:deno": "deno test --allow-read --allow-env --allow-ffi runtime-smoke/deno-sqlite.ts",
87
88
  "verify:exports": "node verify-exports.mjs"
88
89
  },
89
90
  "devDependencies": {
90
91
  "@types/node": "^26.6.2",
91
- "@vielzeug/spell": "3.1.1",
92
+ "@vielzeug/spell": "26.10.1",
92
93
  "fake-indexeddb": "^6.2.5",
93
94
  "typescript": "^6.0.3",
94
95
  "vite": "^8.3.0",