@vielzeug/vault 2.4.0 → 2.5.0
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/dist/adapters/indexeddb.cjs.map +1 -1
- package/dist/adapters/indexeddb.d.ts +1 -1
- package/dist/adapters/indexeddb.d.ts.map +1 -1
- package/dist/adapters/indexeddb.js.map +1 -1
- package/dist/adapters/sqlite.cjs.map +1 -1
- package/dist/adapters/sqlite.d.ts +1 -3
- package/dist/adapters/sqlite.d.ts.map +1 -1
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/indexeddb.d.ts +1 -1
- package/dist/indexeddb.d.ts.map +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/sqlite.d.ts.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault.cjs.map +1 -1
- package/dist/vault.iife.js.map +1 -1
- package/dist/vault.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.cjs","names":[],"sources":["../../src/adapters/indexeddb.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n assertBatchTables,\n type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n withIndexedDbTransactions,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} from '../types';\n\nexport type { TransactionalVaultStore as IndexedDbVaultStore };\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/** Synchronous IndexedDB schema upgrade callback. */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\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: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll());\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 storeHas<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n return (await storeGet<T>(store, key, decode)) !== undefined;\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 storeHas<T>(store, key, decode);\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n if (keys.length === 0) return 0;\n\n const results = await Promise.all(keys.map((k) => storeDelete<T>(store, k, decode)));\n\n return results.filter(Boolean).length;\n}\n\nasync function storePutAt<T>(\n store: IDBObjectStore,\n key: IDBValidKey,\n value: T,\n encode: (v: T, ttl?: number) => unknown,\n ttl?: number,\n): Promise<void> {\n await idbReq(store.put(encode(value, ttl), key));\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 | { result: 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 {\n state = { 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 const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n const value = decode(cursor.value as unknown);\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 } = state;\n\n state = result.done ? { type: 'done' } : { 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\n/**\n * R3: Extracted IDB batch core — builds a StorageBackend that operates within\n * an existing IDBTransaction, shared by all tables in the batch.\n * Eliminates the duplicated `txCore` block that was previously inlined in `idbBatch`.\n */\nfunction buildIdbBatchCore<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n idbTx: IDBTransaction,\n decode: <T extends object>(raw: unknown) => T | undefined,\n encode: <T>(value: T, ttl?: number) => unknown,\n): StorageBackend<S, K> {\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n return {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n count: async (table) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n // This matches the top-level core.count() behaviour in the IDB adapter.\n const all = await idbReq<unknown[]>(storeOf(table).getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n },\n delete: (table, key) => storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode),\n deleteMany: (table, keys) => storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode),\n get: (table, key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n getAll: (table) => getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode),\n getMany: (table, keys) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode))),\n has: (table, key) => storeHas<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n return storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, value)), value, encode, ttl);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).then(() => undefined);\n },\n };\n}\n\ntype IndexedDbOptions<S extends AnySchema> = BaseAdapterOptions<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>): TransactionalVaultStore<S> {\n const { migrate, name, schema, validators, version = 1 } = options;\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 const decode = <T extends object>(raw: unknown): T | undefined => {\n const stored = parseStored<T>(raw);\n\n return !stored || isExpired(stored.expiresAt) ? undefined : stored.value;\n };\n\n const encode = <T>(value: T, ttl?: number): StoredRecord<T> => {\n return ttl === undefined ? { value } : { expiresAt: Date.now() + ttl, value };\n };\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 const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n count: (table) =>\n withStore(table, 'readonly', async (s) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n const all = await idbReq<unknown[]>(s.getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n }),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n deleteMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve(0)\n : withStore(table, 'readwrite', (s) =>\n 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) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n getAll: (table) => withStore(table, 'readonly', (s) => getAllFromStore<RecordOf<S, typeof table>>(s, decode)),\n\n // Ad-hoc per-put TTLs can be attached regardless of schema-level defaultTtl (same caveat as\n // count()), so we cannot take the O(1) store.getAllKeys() shortcut unconditionally — every\n // record must be decoded to exclude TTL-expired entries correctly.\n getAllKeys: (table) =>\n withStore(table, 'readonly', async (s) => {\n const records = await getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }),\n\n getMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve([])\n : withStore(table, 'readonly', (s) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode))),\n ),\n\n has: (table, key) =>\n withStore(table, 'readonly', (s) => storeHas<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\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\n return withStore(table, 'readwrite', (s) => storePutAt(s, key, value, encode, ttl));\n },\n\n putAll(table, values, ttl) {\n return withStore(table, 'readwrite', (s) =>\n Promise.all(\n values.map((v) => storePutAt(s, encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).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\n const txCore = buildIdbBatchCore<S, K>(schema, idbTx, decode, encode);\n const scope = new Set<string>(tables);\n const tx = buildTxContext<S, K>(schema, txCore, (t) => dirtyTables.add(t), validateFn, scope);\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) {\n notifyMutation(table);\n }\n\n return result;\n };\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, core, {\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 onTransactions: (deps) => {\n batch = (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate);\n },\n schema,\n validators,\n });\n\n /**\n * F1: Attach cursor-based `iterate()` on top of the adapter.\n * Opens a dedicated readonly transaction per call and streams records via IDB cursor —\n * genuinely memory-efficient for large tables unlike the getAll()-then-yield pattern.\n */\n const store = {\n ...adapter,\n get disposalSignal(): AbortSignal {\n return adapter.disposalSignal;\n },\n // Spread copies getters as static values; re-expose live disposal state.\n get disposed(): boolean {\n return adapter.disposed;\n },\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n // We need to ensure the DB is connected before opening the transaction.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, K>>> => {\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\n return iterateStoreWithCursor<RecordOf<S, K>>(store, decode as (raw: unknown) => RecordOf<S, K> | undefined);\n };\n\n let inner: AsyncIterator<RecordOf<S, K>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, K>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, K>>> {\n // Sync check avoids an extra Promise allocation on every iteration after the first.\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\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, K>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n };\n\n if (!batch) throw new VaultError('IndexedDB transaction capability was not initialized');\n\n return withIndexedDbTransactions(store, batch);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"indexeddb.cjs","names":[],"sources":["../../src/adapters/indexeddb.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n assertBatchTables,\n type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n withIndexedDbTransactions,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} 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/** Synchronous IndexedDB schema upgrade callback. */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\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: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll());\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 storeHas<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n return (await storeGet<T>(store, key, decode)) !== undefined;\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 storeHas<T>(store, key, decode);\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n if (keys.length === 0) return 0;\n\n const results = await Promise.all(keys.map((k) => storeDelete<T>(store, k, decode)));\n\n return results.filter(Boolean).length;\n}\n\nasync function storePutAt<T>(\n store: IDBObjectStore,\n key: IDBValidKey,\n value: T,\n encode: (v: T, ttl?: number) => unknown,\n ttl?: number,\n): Promise<void> {\n await idbReq(store.put(encode(value, ttl), key));\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 | { result: 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 {\n state = { 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 const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n const value = decode(cursor.value as unknown);\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 } = state;\n\n state = result.done ? { type: 'done' } : { 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\n/**\n * R3: Extracted IDB batch core — builds a StorageBackend that operates within\n * an existing IDBTransaction, shared by all tables in the batch.\n * Eliminates the duplicated `txCore` block that was previously inlined in `idbBatch`.\n */\nfunction buildIdbBatchCore<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n idbTx: IDBTransaction,\n decode: <T extends object>(raw: unknown) => T | undefined,\n encode: <T>(value: T, ttl?: number) => unknown,\n): StorageBackend<S, K> {\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n return {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n count: async (table) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n // This matches the top-level core.count() behaviour in the IDB adapter.\n const all = await idbReq<unknown[]>(storeOf(table).getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n },\n delete: (table, key) => storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode),\n deleteMany: (table, keys) => storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode),\n get: (table, key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n getAll: (table) => getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode),\n getMany: (table, keys) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode))),\n has: (table, key) => storeHas<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n return storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, value)), value, encode, ttl);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).then(() => undefined);\n },\n };\n}\n\ntype IndexedDbOptions<S extends AnySchema> = BaseAdapterOptions<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>): TransactionalVaultStore<S> {\n const { migrate, name, schema, validators, version = 1 } = options;\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 const decode = <T extends object>(raw: unknown): T | undefined => {\n const stored = parseStored<T>(raw);\n\n return !stored || isExpired(stored.expiresAt) ? undefined : stored.value;\n };\n\n const encode = <T>(value: T, ttl?: number): StoredRecord<T> => {\n return ttl === undefined ? { value } : { expiresAt: Date.now() + ttl, value };\n };\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 const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n count: (table) =>\n withStore(table, 'readonly', async (s) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n const all = await idbReq<unknown[]>(s.getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n }),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n deleteMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve(0)\n : withStore(table, 'readwrite', (s) =>\n 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) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n getAll: (table) => withStore(table, 'readonly', (s) => getAllFromStore<RecordOf<S, typeof table>>(s, decode)),\n\n // Ad-hoc per-put TTLs can be attached regardless of schema-level defaultTtl (same caveat as\n // count()), so we cannot take the O(1) store.getAllKeys() shortcut unconditionally — every\n // record must be decoded to exclude TTL-expired entries correctly.\n getAllKeys: (table) =>\n withStore(table, 'readonly', async (s) => {\n const records = await getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }),\n\n getMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve([])\n : withStore(table, 'readonly', (s) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode))),\n ),\n\n has: (table, key) =>\n withStore(table, 'readonly', (s) => storeHas<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\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\n return withStore(table, 'readwrite', (s) => storePutAt(s, key, value, encode, ttl));\n },\n\n putAll(table, values, ttl) {\n return withStore(table, 'readwrite', (s) =>\n Promise.all(\n values.map((v) => storePutAt(s, encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).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\n const txCore = buildIdbBatchCore<S, K>(schema, idbTx, decode, encode);\n const scope = new Set<string>(tables);\n const tx = buildTxContext<S, K>(schema, txCore, (t) => dirtyTables.add(t), validateFn, scope);\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) {\n notifyMutation(table);\n }\n\n return result;\n };\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, core, {\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 onTransactions: (deps) => {\n batch = (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate);\n },\n schema,\n validators,\n });\n\n /**\n * F1: Attach cursor-based `iterate()` on top of the adapter.\n * Opens a dedicated readonly transaction per call and streams records via IDB cursor —\n * genuinely memory-efficient for large tables unlike the getAll()-then-yield pattern.\n */\n const store = {\n ...adapter,\n get disposalSignal(): AbortSignal {\n return adapter.disposalSignal;\n },\n // Spread copies getters as static values; re-expose live disposal state.\n get disposed(): boolean {\n return adapter.disposed;\n },\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n // We need to ensure the DB is connected before opening the transaction.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, K>>> => {\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\n return iterateStoreWithCursor<RecordOf<S, K>>(store, decode as (raw: unknown) => RecordOf<S, K> | undefined);\n };\n\n let inner: AsyncIterator<RecordOf<S, K>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, K>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, K>>> {\n // Sync check avoids an extra Promise allocation on every iteration after the first.\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\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, K>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n };\n\n if (!batch) throw new VaultError('IndexedDB transaction capability was not initialized');\n\n return withIndexedDbTransactions(store, batch);\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AnySchema, BaseAdapterOptions, TransactionalVaultStore } from '../types';
|
|
2
|
-
export type { TransactionalVaultStore as IndexedDbVaultStore };
|
|
3
2
|
/** IndexedDB-only migration context supplied to `MigrationFn` during `onupgradeneeded`. */
|
|
4
3
|
export type MigrationContext = {
|
|
5
4
|
db: IDBDatabase;
|
|
@@ -51,4 +50,5 @@ type IndexedDbOptions<S extends AnySchema> = BaseAdapterOptions<S> & {
|
|
|
51
50
|
version?: number;
|
|
52
51
|
};
|
|
53
52
|
export declare function createIndexedDB<S extends AnySchema>(options: IndexedDbOptions<S>): TransactionalVaultStore<S>;
|
|
53
|
+
export {};
|
|
54
54
|
//# sourceMappingURL=indexeddb.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../../src/adapters/indexeddb.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAGlB,uBAAuB,EAExB,MAAM,UAAU,CAAC;AAElB,
|
|
1
|
+
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../../src/adapters/indexeddb.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAGlB,uBAAuB,EAExB,MAAM,UAAU,CAAC;AAElB,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,WAAW,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,cAAc,CAAC;CACpB,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAClD;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,WAAW,CAsCnE;AA2TD,KAAK,gBAAgB,CAAC,CAAC,SAAS,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG;IACnE,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,8IAA8I;IAC9I,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,uBAAuB,CAAC,CAAC,CAAC,CA4V7G"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.js","names":[],"sources":["../../src/adapters/indexeddb.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n assertBatchTables,\n type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n withIndexedDbTransactions,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} from '../types';\n\nexport type { TransactionalVaultStore as IndexedDbVaultStore };\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/** Synchronous IndexedDB schema upgrade callback. */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\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: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll());\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 storeHas<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n return (await storeGet<T>(store, key, decode)) !== undefined;\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 storeHas<T>(store, key, decode);\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n if (keys.length === 0) return 0;\n\n const results = await Promise.all(keys.map((k) => storeDelete<T>(store, k, decode)));\n\n return results.filter(Boolean).length;\n}\n\nasync function storePutAt<T>(\n store: IDBObjectStore,\n key: IDBValidKey,\n value: T,\n encode: (v: T, ttl?: number) => unknown,\n ttl?: number,\n): Promise<void> {\n await idbReq(store.put(encode(value, ttl), key));\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 | { result: 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 {\n state = { 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 const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n const value = decode(cursor.value as unknown);\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 } = state;\n\n state = result.done ? { type: 'done' } : { 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\n/**\n * R3: Extracted IDB batch core — builds a StorageBackend that operates within\n * an existing IDBTransaction, shared by all tables in the batch.\n * Eliminates the duplicated `txCore` block that was previously inlined in `idbBatch`.\n */\nfunction buildIdbBatchCore<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n idbTx: IDBTransaction,\n decode: <T extends object>(raw: unknown) => T | undefined,\n encode: <T>(value: T, ttl?: number) => unknown,\n): StorageBackend<S, K> {\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n return {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n count: async (table) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n // This matches the top-level core.count() behaviour in the IDB adapter.\n const all = await idbReq<unknown[]>(storeOf(table).getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n },\n delete: (table, key) => storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode),\n deleteMany: (table, keys) => storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode),\n get: (table, key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n getAll: (table) => getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode),\n getMany: (table, keys) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode))),\n has: (table, key) => storeHas<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n return storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, value)), value, encode, ttl);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).then(() => undefined);\n },\n };\n}\n\ntype IndexedDbOptions<S extends AnySchema> = BaseAdapterOptions<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>): TransactionalVaultStore<S> {\n const { migrate, name, schema, validators, version = 1 } = options;\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 const decode = <T extends object>(raw: unknown): T | undefined => {\n const stored = parseStored<T>(raw);\n\n return !stored || isExpired(stored.expiresAt) ? undefined : stored.value;\n };\n\n const encode = <T>(value: T, ttl?: number): StoredRecord<T> => {\n return ttl === undefined ? { value } : { expiresAt: Date.now() + ttl, value };\n };\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 const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n count: (table) =>\n withStore(table, 'readonly', async (s) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n const all = await idbReq<unknown[]>(s.getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n }),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n deleteMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve(0)\n : withStore(table, 'readwrite', (s) =>\n 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) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n getAll: (table) => withStore(table, 'readonly', (s) => getAllFromStore<RecordOf<S, typeof table>>(s, decode)),\n\n // Ad-hoc per-put TTLs can be attached regardless of schema-level defaultTtl (same caveat as\n // count()), so we cannot take the O(1) store.getAllKeys() shortcut unconditionally — every\n // record must be decoded to exclude TTL-expired entries correctly.\n getAllKeys: (table) =>\n withStore(table, 'readonly', async (s) => {\n const records = await getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }),\n\n getMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve([])\n : withStore(table, 'readonly', (s) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode))),\n ),\n\n has: (table, key) =>\n withStore(table, 'readonly', (s) => storeHas<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\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\n return withStore(table, 'readwrite', (s) => storePutAt(s, key, value, encode, ttl));\n },\n\n putAll(table, values, ttl) {\n return withStore(table, 'readwrite', (s) =>\n Promise.all(\n values.map((v) => storePutAt(s, encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).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\n const txCore = buildIdbBatchCore<S, K>(schema, idbTx, decode, encode);\n const scope = new Set<string>(tables);\n const tx = buildTxContext<S, K>(schema, txCore, (t) => dirtyTables.add(t), validateFn, scope);\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) {\n notifyMutation(table);\n }\n\n return result;\n };\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, core, {\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 onTransactions: (deps) => {\n batch = (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate);\n },\n schema,\n validators,\n });\n\n /**\n * F1: Attach cursor-based `iterate()` on top of the adapter.\n * Opens a dedicated readonly transaction per call and streams records via IDB cursor —\n * genuinely memory-efficient for large tables unlike the getAll()-then-yield pattern.\n */\n const store = {\n ...adapter,\n get disposalSignal(): AbortSignal {\n return adapter.disposalSignal;\n },\n // Spread copies getters as static values; re-expose live disposal state.\n get disposed(): boolean {\n return adapter.disposed;\n },\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n // We need to ensure the DB is connected before opening the transaction.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, K>>> => {\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\n return iterateStoreWithCursor<RecordOf<S, K>>(store, decode as (raw: unknown) => RecordOf<S, K> | undefined);\n };\n\n let inner: AsyncIterator<RecordOf<S, K>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, K>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, K>>> {\n // Sync check avoids an extra Promise allocation on every iteration after the first.\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\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, K>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n };\n\n if (!batch) throw new VaultError('IndexedDB transaction capability was not initialized');\n\n return withIndexedDbTransactions(store, batch);\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 type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n withIndexedDbTransactions,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError, VaultMigrationError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired, parseStored, type StoredRecord } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} 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/** Synchronous IndexedDB schema upgrade callback. */\nexport type MigrationFn = (ctx: MigrationContext) => void;\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\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: IDBObjectStore,\n decode: (raw: unknown) => T | undefined,\n): Promise<T[]> {\n const rawRecords = await idbReq<unknown[]>(store.getAll());\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 storeHas<T extends object>(\n store: IDBObjectStore,\n key: IDBValidKey,\n decode: (raw: unknown) => T | undefined,\n): Promise<boolean> {\n return (await storeGet<T>(store, key, decode)) !== undefined;\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 storeHas<T>(store, key, decode);\n\n await idbReq(store.delete(key));\n\n return live;\n}\n\nasync function storeDeleteMany<T extends object>(\n store: IDBObjectStore,\n keys: IDBValidKey[],\n decode: (raw: unknown) => T | undefined,\n): Promise<number> {\n if (keys.length === 0) return 0;\n\n const results = await Promise.all(keys.map((k) => storeDelete<T>(store, k, decode)));\n\n return results.filter(Boolean).length;\n}\n\nasync function storePutAt<T>(\n store: IDBObjectStore,\n key: IDBValidKey,\n value: T,\n encode: (v: T, ttl?: number) => unknown,\n ttl?: number,\n): Promise<void> {\n await idbReq(store.put(encode(value, ttl), key));\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 | { result: 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 {\n state = { 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 const cursor = cursorRequest.result;\n\n if (!cursor) {\n deliver({ done: true, value: undefined });\n\n return;\n }\n\n const value = decode(cursor.value as unknown);\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 } = state;\n\n state = result.done ? { type: 'done' } : { 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\n/**\n * R3: Extracted IDB batch core — builds a StorageBackend that operates within\n * an existing IDBTransaction, shared by all tables in the batch.\n * Eliminates the duplicated `txCore` block that was previously inlined in `idbBatch`.\n */\nfunction buildIdbBatchCore<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n idbTx: IDBTransaction,\n decode: <T extends object>(raw: unknown) => T | undefined,\n encode: <T>(value: T, ttl?: number) => unknown,\n): StorageBackend<S, K> {\n const storeOf = (table: K): IDBObjectStore => idbTx.objectStore(table);\n\n return {\n clear: async (table) => {\n await idbReq(storeOf(table).clear());\n },\n count: async (table) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n // This matches the top-level core.count() behaviour in the IDB adapter.\n const all = await idbReq<unknown[]>(storeOf(table).getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n },\n delete: (table, key) => storeDelete<RecordOf<S, K>>(storeOf(table), encodeVaultKey(key), decode),\n deleteMany: (table, keys) => storeDeleteMany<RecordOf<S, K>>(storeOf(table), keys.map(encodeVaultKey), decode),\n get: (table, key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n getAll: (table) => getAllFromStore<RecordOf<S, typeof table>>(storeOf(table), decode),\n getMany: (table, keys) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode))),\n has: (table, key) => storeHas<RecordOf<S, typeof table>>(storeOf(table), encodeVaultKey(key), decode),\n pruneExpiredInTable: (table) => pruneExpiredInStore(storeOf(table)),\n put(table, value, ttl) {\n return storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, value)), value, encode, ttl);\n },\n putAll(table, values, ttl) {\n return Promise.all(\n values.map((v) => storePutAt(storeOf(table), encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).then(() => undefined);\n },\n };\n}\n\ntype IndexedDbOptions<S extends AnySchema> = BaseAdapterOptions<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>): TransactionalVaultStore<S> {\n const { migrate, name, schema, validators, version = 1 } = options;\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 const decode = <T extends object>(raw: unknown): T | undefined => {\n const stored = parseStored<T>(raw);\n\n return !stored || isExpired(stored.expiresAt) ? undefined : stored.value;\n };\n\n const encode = <T>(value: T, ttl?: number): StoredRecord<T> => {\n return ttl === undefined ? { value } : { expiresAt: Date.now() + ttl, value };\n };\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 const core: StorageBackend<S> = {\n clear: (table) => withStore(table, 'readwrite', (s) => idbReq(s.clear()).then(() => undefined)),\n\n count: (table) =>\n withStore(table, 'readonly', async (s) => {\n // Must inspect each stored record to exclude TTL-expired entries.\n // Individual put() calls can attach a TTL even when the schema has no defaultTtl,\n // so schema[table].defaultTtl being absent does not guarantee a clean count.\n const all = await idbReq<unknown[]>(s.getAll());\n\n return all.filter((r) => decode(r) !== undefined).length;\n }),\n\n delete: (table, key) =>\n withStore(table, 'readwrite', (s) => storeDelete<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n deleteMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve(0)\n : withStore(table, 'readwrite', (s) =>\n 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) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\n\n getAll: (table) => withStore(table, 'readonly', (s) => getAllFromStore<RecordOf<S, typeof table>>(s, decode)),\n\n // Ad-hoc per-put TTLs can be attached regardless of schema-level defaultTtl (same caveat as\n // count()), so we cannot take the O(1) store.getAllKeys() shortcut unconditionally — every\n // record must be decoded to exclude TTL-expired entries correctly.\n getAllKeys: (table) =>\n withStore(table, 'readonly', async (s) => {\n const records = await getAllFromStore<RecordOf<S, typeof table>>(s, decode);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }),\n\n getMany: (table, keys) =>\n keys.length === 0\n ? Promise.resolve([])\n : withStore(table, 'readonly', (s) =>\n Promise.all(keys.map((key) => storeGet<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode))),\n ),\n\n has: (table, key) =>\n withStore(table, 'readonly', (s) => storeHas<RecordOf<S, typeof table>>(s, encodeVaultKey(key), decode)),\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\n return withStore(table, 'readwrite', (s) => storePutAt(s, key, value, encode, ttl));\n },\n\n putAll(table, values, ttl) {\n return withStore(table, 'readwrite', (s) =>\n Promise.all(\n values.map((v) => storePutAt(s, encodeVaultKey(getRecordKey(schema, table, v)), v, encode, ttl)),\n ).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\n const txCore = buildIdbBatchCore<S, K>(schema, idbTx, decode, encode);\n const scope = new Set<string>(tables);\n const tx = buildTxContext<S, K>(schema, txCore, (t) => dirtyTables.add(t), validateFn, scope);\n const result = await runIdbTx(idbTx, name, () => fn(tx));\n\n for (const table of dirtyTables) {\n notifyMutation(table);\n }\n\n return result;\n };\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, core, {\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 onTransactions: (deps) => {\n batch = (tables, fn) => idbBatch(tables, fn, deps.notifyMutation, deps.validate);\n },\n schema,\n validators,\n });\n\n /**\n * F1: Attach cursor-based `iterate()` on top of the adapter.\n * Opens a dedicated readonly transaction per call and streams records via IDB cursor —\n * genuinely memory-efficient for large tables unlike the getAll()-then-yield pattern.\n */\n const store = {\n ...adapter,\n get disposalSignal(): AbortSignal {\n return adapter.disposalSignal;\n },\n // Spread copies getters as static values; re-expose live disposal state.\n get disposed(): boolean {\n return adapter.disposed;\n },\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n // Each call opens a fresh transaction so iteration doesn't hold locks across awaits.\n // We need to ensure the DB is connected before opening the transaction.\n const getIterable = async (): Promise<AsyncIterable<RecordOf<S, K>>> => {\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\n return iterateStoreWithCursor<RecordOf<S, K>>(store, decode as (raw: unknown) => RecordOf<S, K> | undefined);\n };\n\n let inner: AsyncIterator<RecordOf<S, K>> | undefined;\n\n const initInner = (): Promise<AsyncIterator<RecordOf<S, K>>> =>\n getIterable().then((iterable) => {\n inner = iterable[Symbol.asyncIterator]();\n\n return inner;\n });\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n return {\n next(): Promise<IteratorResult<RecordOf<S, K>>> {\n // Sync check avoids an extra Promise allocation on every iteration after the first.\n if (inner) return inner.next();\n\n return initInner().then((it) => it.next());\n },\n return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\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, K>>> {\n if (inner) return inner.throw?.(err) ?? Promise.reject(err);\n\n return Promise.reject(err);\n },\n };\n },\n };\n },\n };\n\n if (!batch) throw new VaultError('IndexedDB transaction capability was not initialized');\n\n return withIndexedDbTransactions(store, batch);\n}\n"],"mappings":";;;;;AA0DA,SAAgB,EAAgB,GAAqC;CACnE,QAAQ,EAAE,OAAI,YAAS;EACrB,KAAK,IAAM,KAAQ,GACjB,QAAQ,EAAK,MAAb;GACE,KAAK,YAAY;IACf,IAAM,IAAQ,EAAG,YAAY,EAAK,KAAK;IAGvC,AAAK,EAAM,WAAW,SAAS,EAAK,KAAK,KACvC,EAAM,YAAY,EAAK,OAAO,SAAS,EAAK,OAAO;IAGrD;GACF;GACA,KAAK;IACH,AAAK,EAAG,iBAAiB,SAAS,EAAK,IAAI,KACzC,EAAG,kBAAkB,EAAK,IAAI;IAGhC;GACF,KAAK,eAAe;IAClB,IAAM,IAAQ,EAAG,YAAY,EAAK,KAAK;IAEvC,AAAI,EAAM,WAAW,SAAS,EAAK,KAAK,KACtC,EAAM,YAAY,EAAK,KAAK;IAG9B;GACF;GACA,KAAK,eACH,AAAI,EAAG,iBAAiB,SAAS,EAAK,IAAI,KACxC,EAAG,kBAAkB,EAAK,IAAI;EAIpC;CAEJ;AACF;AAEA,SAAS,EAAU,GAAoC;CACrD,OAAO,IAAI,SAAY,GAAS,MAAW;EAEzC,AADA,EAAQ,kBAAkB,EAAQ,EAAQ,MAAM,GAChD,EAAQ,gBAAgB,EAAO,EAAQ,SAAS,IAAI,EAAW,0BAA0B,CAAC;CAC5F,CAAC;AACH;AAEA,SAAS,EAAY,GAAe,GAAiB,GAA4B;CAC/E,IAAM,IAAe,aAAiB,SAAS,EAAM,UAAU,KAAK,EAAM,YAAY;CAEtF,OAAO,IAAI,EAAW,GAAG,EAAQ,OAAO,EAAM,GAAG,KAAgB,EAAE,SAAM,CAAC;AAC5E;AAEA,SAAS,EAAY,GAAoB,GAAe,GAAoC;CAC1F,OAAO,IAAI,SAAY,GAAS,MAAW;EACzC,IAAI,GACA;EAEJ,QAAQ,QAAQ,CAAC,CACd,KAAK,CAAI,CAAC,CACV,MAAM,MAAU;GACf,IAAS;EACX,CAAC,CAAC,CACD,OAAO,MAAU;GAChB,IAAgB;GAEhB,IAAI;IACF,EAAG,MAAM;GACX,QAAQ,CAER;EACF,CAAC;EAEH,IAAM,KAA2B,GAAwB,IAAU,yBAA+B;GAChG,AAAI,aAAyB,QAC3B,EAAO,CAAa,IAEpB,EAAO,EAAY,GAAO,GAAS,KAAiB,CAAa,CAAC;EAEtE;EAYA,AAVA,EAAG,mBAAmB;GACpB,IAAI,GAAe;IACjB,EAAwB,KAAA,CAAS;IAEjC;GACF;GAEA,EAAQ,CAAW;EACrB,GACA,EAAG,gBAAgB,EAAO,EAAY,GAAO,qBAAqB,EAAG,KAAK,CAAC,GAC3E,EAAG,gBAAgB,EAAwB,EAAG,OAAO,qBAAqB;CAC5E,CAAC;AACH;AAEA,eAAe,EACb,GACA,GACc;CACd,IAAM,IAAa,MAAM,EAAkB,EAAM,OAAO,CAAC,GACnD,IAAe,CAAC;CAEtB,KAAK,IAAM,KAAO,GAAY;EAC5B,IAAM,IAAQ,EAAO,CAAG;EAExB,AAAI,MAAU,KAAA,KAAW,EAAQ,KAAK,CAAK;CAC7C;CAEA,OAAO;AACT;AAEA,eAAe,EACb,GACA,GACA,GACwB;CACxB,IAAM,IAAM,MAAM,EAAgB,EAAM,IAAI,CAAG,CAAC;CAE5C,SAAO,MAEX,OAAO,EAAO,CAAG;AACnB;AAEA,eAAe,EACb,GACA,GACA,GACkB;CAClB,OAAQ,MAAM,EAAY,GAAO,GAAK,CAAM,MAAO,KAAA;AACrD;AAEA,eAAe,EACb,GACA,GACA,GACkB;CAClB,IAAM,IAAO,MAAM,EAAY,GAAO,GAAK,CAAM;CAIjD,OAFA,MAAM,EAAO,EAAM,OAAO,CAAG,CAAC,GAEvB;AACT;AAEA,eAAe,EACb,GACA,GACA,GACiB;CAKjB,OAJI,EAAK,WAAW,IAAU,KAIvB,MAFe,QAAQ,IAAI,EAAK,KAAK,MAAM,EAAe,GAAO,GAAG,CAAM,CAAC,CAAC,EAAA,CAEpE,OAAO,OAAO,CAAC,CAAC;AACjC;AAEA,eAAe,EACb,GACA,GACA,GACA,GACA,GACe;CACf,MAAM,EAAO,EAAM,IAAI,EAAO,GAAO,CAAG,GAAG,CAAG,CAAC;AACjD;AAEA,SAAS,EAAoB,GAAwC;CACnE,OAAO,IAAI,SAAiB,GAAS,MAAW;EAC9C,IAAI,IAAU,GACR,IAAU,EAAM,WAAW;EAGjC,AADA,EAAQ,gBAAgB,EAAO,EAAQ,SAAS,IAAI,EAAW,sCAAsC,CAAC,GACtG,EAAQ,kBAAkB;GACxB,IAAM,IAAS,EAAQ;GAEvB,IAAI,CAAC,GAAQ;IACX,EAAQ,CAAO;IAEf;GACF;GAEA,IAAM,IAAS,EAAY,EAAO,KAAgB;GAOlD,CALI,CAAC,KAAU,EAAU,EAAO,SAAS,OACvC,EAAO,OAAO,GACd,KAAW,IAGb,EAAO,SAAS;EAClB;CACF,CAAC;AACH;AAuBA,SAAS,EACP,GACA,GACkB;CAClB,OAAO,EACL,CAAC,OAAO,iBAAmC;EACzC,IAAM,IAAgB,EAAM,WAAW,GACnC,IAAwB,EAAE,MAAM,OAAO,GAErC,KAAW,MAAoC;GACnD,IAAI,EAAM,SAAS,WAAW;IAC5B,IAAM,EAAE,eAAY;IAGpB,AADA,IAAQ,EAAO,OAAO,EAAE,MAAM,OAAO,IAAI,EAAE,MAAM,OAAO,GACxD,EAAQ,CAAM;GAChB,OACE,IAAQ;IAAE;IAAQ,MAAM;GAAW;EAEvC;EAgCA,OA9BA,EAAc,gBAAgB;GAC5B,IAAM,IAAM,EAAc,SAAS,IAAI,EAAW,mCAAmC;GAErF,IAAI,EAAM,SAAS,WAAW;IAC5B,IAAM,EAAE,cAAW;IAGnB,AADA,IAAQ,EAAE,MAAM,OAAO,GACvB,EAAO,CAAG;GACZ,OACE,IAAQ;IAAE,OAAO;IAAK,MAAM;GAAQ;EAExC,GAEA,EAAc,kBAAkB;GAC9B,IAAM,IAAS,EAAc;GAE7B,IAAI,CAAC,GAAQ;IACX,EAAQ;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU,CAAC;IAExC;GACF;GAEA,IAAM,IAAQ,EAAO,EAAO,KAAgB;GAK5C,AAFA,EAAO,SAAS,GAEZ,MAAU,KAAA,KAAW,EAAQ;IAAE,MAAM;IAAO;GAAM,CAAC;EACzD,GAEO;GACL,OAAmC;IACjC,IAAI,EAAM,SAAS,SAAS;KAC1B,IAAM,EAAE,aAAU;KAIlB,OAFA,IAAQ,EAAE,MAAM,OAAO,GAEhB,QAAQ,OAAO,CAAK;IAC7B;IAEA,IAAI,EAAM,SAAS,YAAY;KAC7B,IAAM,EAAE,cAAW;KAInB,OAFA,IAAQ,EAAO,OAAO,EAAE,MAAM,OAAO,IAAI,EAAE,MAAM,OAAO,GAEjD,QAAQ,QAAQ,CAAM;IAC/B;IAIA,OAFI,EAAM,SAAS,SAAe,QAAQ,QAAQ;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU,CAAC,IAE3E,IAAI,SAA4B,GAAS,MAAW;KACzD,IAAQ;MAAE;MAAQ;MAAS,MAAM;KAAU;IAC7C,CAAC;GACH;GAEA,OAAO,GAA6C;IAKlD,OAJI,EAAM,SAAS,aAAW,EAAM,QAAQ;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU,CAAC,GAE5E,IAAQ,EAAE,MAAM,OAAO,GAEhB,QAAQ,QAAQ;KAAE,MAAM;KAAM;IAAM,CAAC;GAC9C;GAEA,MAAM,GAA2C;IAK/C,OAJI,EAAM,SAAS,aAAW,EAAM,OAAO,CAAG,GAE9C,IAAQ,EAAE,MAAM,OAAO,GAEhB,QAAQ,OAAO,CAAG;GAC3B;EACF;CACF,EACF;AACF;AAOA,SAAS,EACP,GACA,GACA,GACA,GACsB;CACtB,IAAM,KAAW,MAA6B,EAAM,YAAY,CAAK;CAErE,OAAO;EACL,OAAO,OAAO,MAAU;GACtB,MAAM,EAAO,EAAQ,CAAK,CAAC,CAAC,MAAM,CAAC;EACrC;EACA,OAAO,OAAO,OAOL,MAFW,EAAkB,EAAQ,CAAK,CAAC,CAAC,OAAO,CAAC,EAAA,CAEhD,QAAQ,MAAM,EAAO,CAAC,MAAM,KAAA,CAAS,CAAC,CAAC;EAEpD,SAAS,GAAO,MAAQ,EAA4B,EAAQ,CAAK,GAAG,EAAe,CAAG,GAAG,CAAM;EAC/F,aAAa,GAAO,MAAS,EAAgC,EAAQ,CAAK,GAAG,EAAK,IAAI,CAAc,GAAG,CAAM;EAC7G,MAAM,GAAO,MAAQ,EAAoC,EAAQ,CAAK,GAAG,EAAe,CAAG,GAAG,CAAM;EACpG,SAAS,MAAU,EAA2C,EAAQ,CAAK,GAAG,CAAM;EACpF,UAAU,GAAO,MACf,QAAQ,IAAI,EAAK,KAAK,MAAQ,EAAoC,EAAQ,CAAK,GAAG,EAAe,CAAG,GAAG,CAAM,CAAC,CAAC;EACjH,MAAM,GAAO,MAAQ,EAAoC,EAAQ,CAAK,GAAG,EAAe,CAAG,GAAG,CAAM;EACpG,sBAAsB,MAAU,EAAoB,EAAQ,CAAK,CAAC;EAClE,IAAI,GAAO,GAAO,GAAK;GACrB,OAAO,EAAW,EAAQ,CAAK,GAAG,EAAe,EAAa,GAAQ,GAAO,CAAK,CAAC,GAAG,GAAO,GAAQ,CAAG;EAC1G;EACA,OAAO,GAAO,GAAQ,GAAK;GACzB,OAAO,QAAQ,IACb,EAAO,KAAK,MAAM,EAAW,EAAQ,CAAK,GAAG,EAAe,EAAa,GAAQ,GAAO,CAAC,CAAC,GAAG,GAAG,GAAQ,CAAG,CAAC,CAC9G,CAAC,CAAC,WAAW,KAAA,CAAS;EACxB;CACF;AACF;AASA,SAAgB,EAAqC,GAA0D;CAC7G,IAAM,EAAE,YAAS,SAAM,WAAQ,eAAY,aAAU,MAAM;CAE3D,IAAI,CAAC,OAAO,UAAU,CAAO,KAAK,IAAU,GAC1C,MAAM,IAAI,EAAW,4DAA4D,OAAO,CAAO,GAAG;CAIpG,IAAM,KAA4B,MAAgC;EAChE,IAAM,IAAS,EAAe,CAAG;EAEjC,OAAO,CAAC,KAAU,EAAU,EAAO,SAAS,IAAI,KAAA,IAAY,EAAO;CACrE,GAEM,KAAa,GAAU,MACpB,MAAQ,KAAA,IAAY,EAAE,SAAM,IAAI;EAAE,WAAW,KAAK,IAAI,IAAI;EAAK;CAAM,GAGxE,IAAU,OAAO,mBAAqB,MAAc,IAAI,iBAAiB,SAAS,GAAM,IAAI,KAAA,GAE9F,IAAyB,MACzB,IAAuC,MACvC,IAAW,IAET,KAAsB,GAAqB,MAA6B;EAC5E,KAAK,IAAM,CAAC,GAAW,MAAU,OAAO,QAAQ,CAAM,GAAG;GACvD,IAAI;GAEJ,AACE,IADG,EAAO,iBAAiB,SAAS,CAAS,IAGrC,EAAG,YAAY,CAAS,IAFxB,EAAO,kBAAkB,CAAS;GAO5C,IAAM,IAAW,EAA0C,WAAW,CAAC;GAEvE,KAAK,IAAM,KAAS,GAClB,AAAK,EAAM,WAAW,SAAS,CAAK,KAClC,EAAM,YAAY,GAAO,SAAS,GAAO;EAG/C;CACF,GAEM,IAAU,aACd,AACE,MAAiB,IAAI,SAAS,GAAS,MAAW;EAChD,IAAM,IAAU,UAAU,KAAK,GAAM,CAAO;EAiD5C,AA/CA,EAAQ,mBAAmB,MAAU;GACnC,IAAM,IAAS,EAAQ,QACjB,IAAK,EAAQ;GAEnB,IAAI,GACF,IAAI;IACF,EAAQ;KACN,IAAI;KACJ,YAAa,EAAgC,cAAc;KAC3D,YAAY,EAAM;KAClB;IACF,CAAC;GACH,SAAS,GAAO;IACd,IAAI;KACF,EAAG,MAAM;IACX,QAAQ,CAER;IAEA,EAAO,IAAI,EAAoB,yBAAyB,EAAK,IAAI,EAAE,OAAO,EAAM,CAAC,CAAC;IAElF;GACF;GAGF,EAAmB,GAAQ,CAAE;EAC/B,GAEA,EAAQ,kBAAkB;GACxB,IAAI,GAAU;IAEZ,AADA,EAAQ,OAAO,MAAM,GACrB,EAAQ;IAER;GACF;GAEA,IAAM,IAAa,EAAQ;GAS3B,AAPA,EAAW,wBAAwB;IAGjC,AAFA,EAAW,MAAM,GACjB,IAAK,MACL,IAAiB;GACnB,GAEA,IAAK,GACL,EAAQ;EACV,GACA,EAAQ,gBAAgB;GAEtB,AADA,IAAiB,MACjB,EAAO,IAAI,EAAW,mBAAmB,EAAK,IAAI,EAAE,OAAO,EAAQ,MAAM,CAAC,CAAC;EAC7E;CACF,CAAC,GAGI,IAGH,IAAY,OAChB,GACA,GACA,MACe;EAKf,IAJI,MAEC,KAAI,MAAM,EAAQ,GAEnB,CAAC,IAAI,MAAM,IAAI,EAAmB,IAAI,EAAK,cAAc;EAE7D,IAAM,IAAY,OAAO,CAAK,GACxB,IAAK,EAAG,YAAY,GAAW,CAAI;EAEzC,OAAO,EAAS,GAAI,GAAG,EAAK,GAAG,WAAmB,EAAG,EAAG,YAAY,CAAS,CAAC,CAAC;CACjF,GAEM,IAAY,YAAkC;EAKlD,IAJI,MAEC,KAAI,MAAM,EAAQ,GAEnB,CAAC,IAAI,MAAM,IAAI,EAAmB,IAAI,EAAK,cAAc;EAE7D,OAAO;CACT,GAEM,KAA8B,MAAmB;EACrD,GAAS,YAAY,EAAE,OAAO,OAAO,CAAK,EAAE,CAAC;CAC/C,GAEM,IAA0B;EAC9B,QAAQ,MAAU,EAAU,GAAO,cAAc,MAAM,EAAO,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,KAAA,CAAS,CAAC;EAE9F,QAAQ,MACN,EAAU,GAAO,YAAY,OAAO,OAM3B,MAFW,EAAkB,EAAE,OAAO,CAAC,EAAA,CAEnC,QAAQ,MAAM,EAAO,CAAC,MAAM,KAAA,CAAS,CAAC,CAAC,MACnD;EAEH,SAAS,GAAO,MACd,EAAU,GAAO,cAAc,MAAM,EAAuC,GAAG,EAAe,CAAG,GAAG,CAAM,CAAC;EAE7G,aAAa,GAAO,MAClB,EAAK,WAAW,IACZ,QAAQ,QAAQ,CAAC,IACjB,EAAU,GAAO,cAAc,MAC7B,EAA2C,GAAG,EAAK,IAAI,CAAc,GAAG,CAAM,CAChF;EAEN,MAAM,UAAyB;GAU7B,AATA,IAAW,IACX,GAAS,MAAM,GAIX,KAAgB,MAAM,EAAe,YAAY,CAAC,CAAC,GAEvD,GAAI,MAAM,GACV,IAAK,MACL,IAAiB;EACnB;EAEA,MAAM,GAAO,MACX,EAAU,GAAO,aAAa,MAAM,EAAoC,GAAG,EAAe,CAAG,GAAG,CAAM,CAAC;EAEzG,SAAS,MAAU,EAAU,GAAO,aAAa,MAAM,EAA2C,GAAG,CAAM,CAAC;EAK5G,aAAa,MACX,EAAU,GAAO,YAAY,OAAO,MAAM;GACxC,IAAM,IAAU,MAAM,EAA2C,GAAG,CAAM,GACpE,IAAW,EAAO,EAAM,CAAC;GAE/B,OAAO,EAAQ,KAAK,MAAO,EAA8B,EAAmC;EAC9F,CAAC;EAEH,UAAU,GAAO,MACf,EAAK,WAAW,IACZ,QAAQ,QAAQ,CAAC,CAAC,IAClB,EAAU,GAAO,aAAa,MAC5B,QAAQ,IAAI,EAAK,KAAK,MAAQ,EAAoC,GAAG,EAAe,CAAG,GAAG,CAAM,CAAC,CAAC,CACpG;EAEN,MAAM,GAAO,MACX,EAAU,GAAO,aAAa,MAAM,EAAoC,GAAG,EAAe,CAAG,GAAG,CAAM,CAAC;EAEzG,MAAM,kBAAkB;GACtB,IAAM,IAAM,MAAM,EAAU,GACtB,IAAa,OAAO,KAAK,CAAM,GAC/B,IAAK,EAAI,YAAY,GAAY,WAAW,GAC5C,IAAU,MAAM,EAAS,GAAI,GAAG,EAAK,kBACzC,QAAQ,IAAI,EAAW,IAAI,OAAO,MAAM,CAAC,GAAG,MAAM,EAAoB,EAAG,YAAY,CAAC,CAAC,CAAC,CAAU,CAAC,CACrG;GAEA,OAAO,OAAO,YAAY,CAAO;EACnC;EAEA,sBAAsB,MAAU,EAAU,GAAO,cAAc,MAAM,EAAoB,CAAC,CAAC;EAE3F,IAAI,GAAO,GAAO,GAAK;GACrB,IAAM,IAAM,EAAe,EAAa,GAAQ,GAAO,CAAK,CAAC;GAE7D,OAAO,EAAU,GAAO,cAAc,MAAM,EAAW,GAAG,GAAK,GAAO,GAAQ,CAAG,CAAC;EACpF;EAEA,OAAO,GAAO,GAAQ,GAAK;GACzB,OAAO,EAAU,GAAO,cAAc,MACpC,QAAQ,IACN,EAAO,KAAK,MAAM,EAAW,GAAG,EAAe,EAAa,GAAQ,GAAO,CAAC,CAAC,GAAG,GAAG,GAAQ,CAAG,CAAC,CACjG,CAAC,CAAC,WAAW,KAAA,CAAS,CACxB;EACF;CACF,GAEM,IAAW,OACf,GACA,GACA,GACA,MACe;EACf,EAAkB,CAAM;EAGxB,IAAM,KAAQ,MADI,EAAU,EAAA,CACV,YAAY,CAAC,GAAG,CAAM,GAAe,WAAW,GAC5D,oBAAc,IAAI,IAAO,GAEzB,IAAS,EAAwB,GAAQ,GAAO,GAAQ,CAAM,GAC9D,IAAQ,IAAI,IAAY,CAAM,GAC9B,IAAK,EAAqB,GAAQ,IAAS,MAAM,EAAY,IAAI,CAAC,GAAG,GAAY,CAAK,GACtF,IAAS,MAAM,EAAS,GAAO,SAAY,EAAG,CAAE,CAAC;EAEvD,KAAK,IAAM,KAAS,GAClB,EAAe,CAAK;EAGtB,OAAO;CACT,GAEI,GACE,IAAU,EAAgB,GAAQ,GAAM;EAC5C,kBAAkB,GAAQ;GACnB,OAYL,OARA,EAAQ,aAAa,MAA4C;IAC/D,IAAM,IAAY,EAAM,MAAM;IAE1B,CAAC,KAAa,CAAC,OAAO,OAAO,GAAQ,CAAS,KAElD,EAAO,CAA6B;GACtC,SAEa;IACX,EAAQ,YAAY;GACtB;EACF;EACA,YAAY;EACZ,iBAAiB,MAAS;GACxB,KAAS,GAAQ,MAAO,EAAS,GAAQ,GAAI,EAAK,gBAAgB,EAAK,QAAQ;EACjF;EACA;EACA;CACF,CAAC,GAOK,IAAQ;EACZ,GAAG;EACH,IAAI,iBAA8B;GAChC,OAAO,EAAQ;EACjB;EAEA,IAAI,WAAoB;GACtB,OAAO,EAAQ;EACjB;EACA,QAAoC,GAAyC;GAC3E,IAAI,GAAU,MAAM,IAAI,EAAmB,IAAI,EAAK,cAAc;GAIlE,IAAM,IAAc,YAAoD;IAGtE,IAFK,KAAI,MAAM,EAAQ,GAEnB,CAAC,KAAM,GAAU,MAAM,IAAI,EAAmB,IAAI,EAAK,cAAc;IAMzE,OAAO,EAHI,EAAI,YAAY,OAAO,CAAK,GAAG,UAC5B,CAAA,CAAG,YAAY,OAAO,CAAK,CAEK,GAAO,CAAsD;GAC7G,GAEI,GAEE,UACJ,EAAY,CAAC,CAAC,MAAM,OAClB,IAAQ,EAAS,OAAO,cAAc,CAAC,GAEhC,EACR;GAEH,OAAO,EACL,CAAC,OAAO,iBAAgD;IACtD,OAAO;KACL,OAAgD;MAI9C,OAFI,IAAc,EAAM,KAAK,IAEtB,EAAU,CAAC,CAAC,MAAM,MAAO,EAAG,KAAK,CAAC;KAC3C;KACA,OAAO,GAA0D;MAG/D,OAFI,IAAc,EAAM,SAAS,CAAK,KAAK,QAAQ,QAAQ;OAAE,MAAM;OAAM;MAAM,CAAC,IAEzE,QAAQ,QAAQ;OAAE,MAAM;OAAM;MAAM,CAAC;KAC9C;KACA,MAAM,GAAwD;MAG5D,OAFI,IAAc,EAAM,QAAQ,CAAG,KAAK,QAAQ,OAAO,CAAG,IAEnD,QAAQ,OAAO,CAAG;KAC3B;IACF;GACF,EACF;EACF;CACF;CAEA,IAAI,CAAC,GAAO,MAAM,IAAI,EAAW,sDAAsD;CAEvF,OAAO,EAA0B,GAAO,CAAK;AAC/C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.cjs","names":[],"sources":["../../src/adapters/sqlite.ts"],"sourcesContent":["import {\n assertBatchTables,\n type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} from '../types';\n\nexport type { TransactionContext };\n\nexport type SQLiteParameter = null | number | string;\ntype SQLiteRow = Record<string, unknown>;\n\n/**\n * A synchronous SQLite statement with positional parameter binding.\n *\n * Adapters for drivers whose native statement API differs can implement this\n * structural protocol without adding a runtime dependency to Vault.\n */\nexport interface SQLiteStatement {\n all(...parameters: SQLiteParameter[]): readonly SQLiteRow[];\n finalize?(): void;\n get(...parameters: SQLiteParameter[]): SQLiteRow | undefined;\n run(...parameters: SQLiteParameter[]): unknown;\n}\n\n/**\n * A runtime-neutral synchronous SQLite connection.\n *\n * Node's `DatabaseSync`, Bun's `Database`, and Deno's `@db/sqlite` `Database`\n * satisfy this protocol directly.\n */\nexport interface SQLiteDatabase {\n close?(): void;\n exec(sql: string): void;\n prepare(sql: string): SQLiteStatement;\n}\n\nexport type SQLiteVaultOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n /** Closes the caller-provided connection during store disposal when true. */\n closeOnDispose?: boolean;\n database: SQLiteDatabase;\n /** Namespace that isolates this store's records in the shared connection. */\n name: string;\n};\n\n/** SQLite provides atomic batches and lazy keyset-paginated iteration. */\nexport type SQLiteVaultStore<S extends AnySchema> = TransactionalVaultStore<S>;\n\ntype ConnectionState = {\n batchActive: boolean;\n executor: ConnectionExecutor;\n initialized: Promise<void>;\n listeners: Set<ConnectionListener>;\n};\ntype ConnectionListener = (name: string, table: string) => void;\ntype KeyColumns = { encoded: string; kind: 'number' | 'string'; number: number | null; string: string | null };\ntype StoredRow = { expiresAt: number | undefined; json: string; rowId: number };\n\nconst connectionStates = new WeakMap<SQLiteDatabase, ConnectionState>();\nconst RECORDS_TABLE = '\"__vielzeug_vault_records\"';\nconst METADATA_TABLE = '\"__vielzeug_vault_metadata\"';\nconst STORAGE_FORMAT_VERSION = 1;\nconst ITERATION_PAGE_SIZE = 100;\n\nclass ConnectionExecutor {\n private tail: Promise<void> = Promise.resolve();\n\n async acquire(): Promise<() => void> {\n let release: (() => void) | undefined;\n const previous = this.tail;\n\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n await previous;\n\n return () => release?.();\n }\n\n async run<T>(work: () => T | Promise<T>): Promise<T> {\n const release = await this.acquire();\n\n try {\n return await work();\n } finally {\n release();\n }\n }\n}\n\nfunction getConnectionState(database: SQLiteDatabase): ConnectionState {\n const current = connectionStates.get(database);\n\n if (current) return current;\n\n const executor = new ConnectionExecutor();\n const state: ConnectionState = {\n batchActive: false,\n executor,\n initialized: executor.run(() => initializeDatabase(database)),\n listeners: new Set(),\n };\n\n connectionStates.set(database, state);\n\n return state;\n}\n\nfunction initializeDatabase(database: SQLiteDatabase): void {\n database.exec(\n `\n CREATE TABLE IF NOT EXISTS ${METADATA_TABLE} (\n namespace TEXT PRIMARY KEY,\n format_version INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${RECORDS_TABLE} (\n namespace TEXT NOT NULL,\n table_name TEXT NOT NULL,\n key_tag TEXT NOT NULL,\n key_kind TEXT NOT NULL,\n key_number REAL,\n key_string TEXT,\n value_json TEXT NOT NULL,\n expires_at INTEGER,\n PRIMARY KEY (namespace, table_name, key_tag)\n );\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_expiration\"\n ON ${RECORDS_TABLE} (namespace, table_name, expires_at);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_number_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_number);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_string_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_string);\n `,\n );\n}\n\nfunction initializeNamespace(database: SQLiteDatabase, name: string): void {\n const row = get(database, `SELECT format_version FROM ${METADATA_TABLE} WHERE namespace = ?`, [name]);\n\n if (row === undefined) {\n run(database, `INSERT INTO ${METADATA_TABLE} (namespace, format_version) VALUES (?, ?)`, [\n name,\n STORAGE_FORMAT_VERSION,\n ]);\n\n return;\n }\n\n if (row.format_version !== STORAGE_FORMAT_VERSION) {\n throw new VaultError(`SQLite storage format for \"${name}\" is not supported`);\n }\n}\n\nfunction assertName(name: string): void {\n if (name.length === 0) throw new VaultError('createSQLite: name must not be empty');\n}\n\nfunction assertJsonValue(value: unknown, seen: Set<object>, path: string): void {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return;\n\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return;\n\n throw new VaultError(`SQLite serialization failed at ${path}: numbers must be finite`);\n }\n\n if (typeof value !== 'object') {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a JSON-compatible value`);\n }\n\n if (seen.has(value as object)) {\n throw new VaultError(`SQLite serialization failed at ${path}: circular references are not supported`);\n }\n\n if (Array.isArray(value)) {\n seen.add(value);\n\n for (let index = 0; index < value.length; index += 1) {\n assertJsonValue(value[index], seen, `${path}[${String(index)}]`);\n }\n\n seen.delete(value);\n\n return;\n }\n\n const prototype = Object.getPrototypeOf(value);\n\n if (prototype !== null && prototype !== Object.prototype) {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a plain object`);\n }\n\n seen.add(value as object);\n\n for (const [key, nested] of Object.entries(value)) {\n assertJsonValue(nested, seen, `${path}.${key}`);\n }\n\n seen.delete(value);\n}\n\nfunction encodeJson(value: object): string {\n assertJsonValue(value, new Set(), 'record');\n\n return JSON.stringify(value);\n}\n\nfunction decodeJson(json: string): object {\n try {\n const value: unknown = JSON.parse(json);\n\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new VaultError('stored record is not a JSON object');\n }\n\n return value;\n } catch (error) {\n if (error instanceof VaultError) throw error;\n\n throw new VaultError('stored record contains invalid JSON', { cause: error });\n }\n}\n\nfunction toKeyColumns(key: number | string): KeyColumns {\n const encoded = encodeVaultKey(key);\n\n return typeof key === 'number'\n ? { encoded, kind: 'number', number: key, string: null }\n : { encoded, kind: 'string', number: null, string: key };\n}\n\nfunction getStoredRow(row: SQLiteRow): StoredRow {\n const json = row.value_json;\n const rawExpiresAt = row.expires_at;\n const rowId = row.row_id;\n\n if (typeof json !== 'string' || typeof rowId !== 'number' || !Number.isInteger(rowId)) {\n throw new VaultError('SQLite storage contains a malformed record');\n }\n\n if (rawExpiresAt !== null && (typeof rawExpiresAt !== 'number' || !Number.isFinite(rawExpiresAt))) {\n throw new VaultError('SQLite storage contains a malformed expiration timestamp');\n }\n\n const expiresAt = typeof rawExpiresAt === 'number' ? rawExpiresAt : undefined;\n\n return { expiresAt, json, rowId };\n}\n\nfunction withStatement<T>(database: SQLiteDatabase, sql: string, work: (statement: SQLiteStatement) => T): T {\n const statement = database.prepare(sql);\n\n try {\n return work(statement);\n } finally {\n statement.finalize?.();\n }\n}\n\nfunction run(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): unknown {\n return withStatement(database, sql, (statement) => statement.run(...parameters));\n}\n\nfunction get(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): SQLiteRow | undefined {\n return withStatement(database, sql, (statement) => statement.get(...parameters));\n}\n\nfunction all(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): readonly SQLiteRow[] {\n return withStatement(database, sql, (statement) => statement.all(...parameters));\n}\n\nfunction deleteExpired(database: SQLiteDatabase, name: string, table: string): void {\n run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND expires_at IS NOT NULL AND expires_at <= ?`,\n [name, table, Date.now()],\n );\n}\n\nfunction decodeLiveRecord<T extends object>(database: SQLiteDatabase, row: SQLiteRow): T | undefined {\n const stored = getStoredRow(row);\n\n if (isExpired(stored.expiresAt)) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE rowid = ?`, [stored.rowId]);\n\n return undefined;\n }\n\n return decodeJson(stored.json) as T;\n}\n\nfunction getAllLive<T extends object>(\n database: SQLiteDatabase,\n name: string,\n table: string,\n filterSql = '',\n filterParameters: SQLiteParameter[] = [],\n): T[] {\n deleteExpired(database, name, table);\n\n const records = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ?${filterSql}\n ORDER BY rowid`,\n [name, table, ...filterParameters],\n );\n\n return records.flatMap((row) => {\n const record = decodeLiveRecord<T>(database, row);\n\n return record === undefined ? [] : [record];\n });\n}\n\nfunction createDirectCore<S extends AnySchema, K extends keyof S & string>(\n database: SQLiteDatabase,\n name: string,\n schema: S,\n inTransaction = false,\n): StorageBackend<S, K> {\n const getRecord = <T extends K>(table: T, key: KeyOf<S, T>): RecordOf<S, T> | undefined => {\n const columns = toKeyColumns(key);\n const row = get(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?`,\n [name, table, columns.encoded],\n );\n\n return row === undefined ? undefined : decodeLiveRecord<RecordOf<S, T>>(database, row);\n };\n\n const core: StorageBackend<S, K> = {\n async clear(table) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`, [name, table]);\n },\n async count(table) {\n deleteExpired(database, name, table);\n\n const row = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const count = row?.count;\n\n if (typeof count !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return count;\n },\n async delete(table, key) {\n const columns = toKeyColumns(key);\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, columns.encoded, Date.now()],\n ) as { changes?: number } | undefined;\n\n return (result?.changes ?? 0) > 0;\n },\n async deleteMany(table, keys) {\n if (keys.length === 0) return 0;\n\n let deleted = 0;\n // 3 fixed params: namespace, table_name, expires_at check.\n const SQLITE_PARAM_LIMIT = 999;\n const MAX_KEYS_PER_CHUNK = SQLITE_PARAM_LIMIT - 3;\n const encodedKeys = keys.map((k) => toKeyColumns(k).encoded);\n\n for (let i = 0; i < encodedKeys.length; i += MAX_KEYS_PER_CHUNK) {\n const chunk = encodedKeys.slice(i, i + MAX_KEYS_PER_CHUNK);\n const placeholders = chunk.map(() => '?').join(', ');\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag IN (${placeholders})\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, ...chunk, Date.now()],\n ) as { changes?: number } | undefined;\n\n deleted += result?.changes ?? 0;\n }\n\n return deleted;\n },\n async get(table, key) {\n return getRecord(table, key);\n },\n async getAll(table) {\n return getAllLive<RecordOf<S, typeof table>>(database, name, table);\n },\n async getAllKeys(table) {\n return (await core.getAll(table)).map((record) => getRecordKey(schema, table, record));\n },\n async getMany(table, keys) {\n return keys.map((key) => getRecord(table, key));\n },\n async has(table, key) {\n return getRecord(table, key) !== undefined;\n },\n async pruneAllExpired() {\n const results: Record<string, number> = {};\n\n for (const table of Object.keys(schema)) {\n results[table] = await core.pruneExpiredInTable(table as K);\n }\n\n return results;\n },\n async pruneExpiredInTable(table) {\n const beforeRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const before = beforeRow?.count;\n\n if (typeof before !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n deleteExpired(database, name, table);\n\n const afterRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const after = afterRow?.count;\n\n if (typeof after !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return before - after;\n },\n async put(table, value, ttl) {\n const key = getRecordKey(schema, table, value);\n const columns = toKeyColumns(key);\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [name, table, columns.encoded, columns.kind, columns.number, columns.string, encodeJson(value), expiresAt],\n );\n },\n async putAll(table, values, ttl) {\n if (values.length === 0) return;\n\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n const encodedJsonValues = values.map((v) => encodeJson(v));\n const columnsList = values.map((v) => toKeyColumns(getRecordKey(schema, table, v)));\n\n const writeAll = () => {\n for (let i = 0; i < values.length; i++) {\n const columns = columnsList[i];\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [\n name,\n table,\n columns.encoded,\n columns.kind,\n columns.number,\n columns.string,\n encodedJsonValues[i],\n expiresAt,\n ],\n );\n }\n };\n\n if (inTransaction) {\n writeAll();\n return;\n }\n\n database.exec('BEGIN');\n try {\n writeAll();\n database.exec('COMMIT');\n } catch (error) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite putAll rollback failed', { cause: rollbackError });\n }\n throw error;\n }\n },\n };\n\n return core;\n}\n\n/**\n * Creates a SQLite-backed Vault store. The connection is caller-owned unless\n * `closeOnDispose` is explicitly enabled.\n */\nexport function createSQLite<S extends AnySchema>(options: SQLiteVaultOptions<S>): SQLiteVaultStore<S> {\n const { closeOnDispose = false, database, name, schema, validators } = options;\n\n assertName(name);\n\n const state = getConnectionState(database);\n const namespaceReady = state.executor.run(async () => {\n await state.initialized;\n initializeNamespace(database, name);\n });\n let ownListener: ConnectionListener | undefined;\n\n const directCore = createDirectCore(database, name, schema);\n const withConnection = <T>(work: () => Promise<T>): Promise<T> =>\n state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n return work();\n });\n const guardedCore = Object.fromEntries(\n Object.entries(directCore).map(([method, implementation]) => [\n method,\n (...arguments_: unknown[]) => {\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return withConnection(() => (implementation as (...args: unknown[]) => Promise<unknown>)(...arguments_));\n },\n ]),\n ) as StorageBackend<S>;\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, guardedCore, {\n onCrossTabMessage(notify) {\n const listener: ConnectionListener = (eventName, table) => {\n if (eventName === name && Object.hasOwn(schema, table)) notify(table as keyof S & string);\n };\n\n ownListener = listener;\n state.listeners.add(listener);\n\n return () => {\n state.listeners.delete(listener);\n\n if (ownListener === listener) ownListener = undefined;\n };\n },\n onMutation(table) {\n for (const listener of state.listeners) {\n if (listener !== ownListener) listener(name, table);\n }\n },\n onTransactions: (deps) => {\n batch = async (tables, fn) => {\n assertBatchTables(tables);\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n const dirtyTables = new Set<keyof S & string>();\n const txCore = createDirectCore<S, keyof S & string>(database, name, schema, true);\n const tx = buildTxContext(\n schema,\n txCore,\n (table) => dirtyTables.add(table),\n deps.validate,\n new Set<string>(tables),\n );\n let transactionStarted = false;\n let committed = false;\n\n state.batchActive = true;\n\n try {\n database.exec('BEGIN IMMEDIATE');\n transactionStarted = true;\n\n const result = await fn(tx);\n\n database.exec('COMMIT');\n committed = true;\n\n for (const table of dirtyTables) {\n deps.notifyMutation(table);\n }\n\n return result;\n } catch (error) {\n if (transactionStarted && !committed) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite batch rollback failed', { cause: rollbackError });\n }\n }\n\n throw error;\n } finally {\n state.batchActive = false;\n }\n });\n };\n },\n schema,\n validators,\n });\n\n if (!batch) throw new VaultError('SQLite transaction capability was not initialized');\n\n const store: SQLiteVaultStore<S> = {\n ...adapter,\n batch,\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (adapter.disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n let completed = false;\n let lastRowId = 0;\n let lease: (() => void) | undefined;\n let rows: readonly SQLiteRow[] = [];\n let index = 0;\n\n const release = (): void => {\n lease?.();\n lease = undefined;\n };\n const loadNextPage = (): void => {\n rows = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND rowid > ?\n ORDER BY rowid\n LIMIT ?`,\n [name, table, lastRowId, ITERATION_PAGE_SIZE],\n );\n index = 0;\n };\n\n return {\n async next(): Promise<IteratorResult<RecordOf<S, K>>> {\n if (completed) return { done: true, value: undefined };\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n try {\n if (!lease) {\n lease = await state.executor.acquire();\n await state.initialized;\n await namespaceReady;\n }\n\n while (true) {\n if (index >= rows.length) {\n loadNextPage();\n\n if (rows.length === 0) {\n completed = true;\n release();\n\n return { done: true, value: undefined };\n }\n }\n\n const row = rows[index++];\n const stored = getStoredRow(row);\n\n lastRowId = stored.rowId;\n\n const value = decodeLiveRecord<RecordOf<S, K>>(database, row);\n\n if (value !== undefined) return { done: false, value };\n }\n } catch (error) {\n completed = true;\n release();\n\n throw error;\n }\n },\n async return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n return { done: true, value: value as RecordOf<S, K> };\n },\n async throw(error?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n throw error;\n },\n };\n },\n };\n },\n };\n\n if (closeOnDispose) {\n const dispose = store.dispose.bind(store);\n let closePromise: Promise<void> | undefined;\n\n store.dispose = async (): Promise<void> => {\n await dispose();\n closePromise ??= state.executor.run(() => database.close?.());\n await closePromise;\n };\n store[Symbol.asyncDispose] = async (): Promise<void> => {\n await store.dispose();\n };\n }\n\n return store;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqlite.cjs","names":[],"sources":["../../src/adapters/sqlite.ts"],"sourcesContent":["import {\n assertBatchTables,\n type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} from '../types';\n\nexport type { TransactionContext };\n\nexport type SQLiteParameter = null | number | string;\ntype SQLiteRow = Record<string, unknown>;\n\n/**\n * A synchronous SQLite statement with positional parameter binding.\n *\n * Adapters for drivers whose native statement API differs can implement this\n * structural protocol without adding a runtime dependency to Vault.\n */\nexport interface SQLiteStatement {\n all(...parameters: SQLiteParameter[]): readonly SQLiteRow[];\n finalize?(): void;\n get(...parameters: SQLiteParameter[]): SQLiteRow | undefined;\n run(...parameters: SQLiteParameter[]): unknown;\n}\n\n/**\n * A runtime-neutral synchronous SQLite connection.\n *\n * Node's `DatabaseSync`, Bun's `Database`, and Deno's `@db/sqlite` `Database`\n * satisfy this protocol directly.\n */\nexport interface SQLiteDatabase {\n close?(): void;\n exec(sql: string): void;\n prepare(sql: string): SQLiteStatement;\n}\n\nexport type SQLiteVaultOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n /** Closes the caller-provided connection during store disposal when true. */\n closeOnDispose?: boolean;\n database: SQLiteDatabase;\n /** Namespace that isolates this store's records in the shared connection. */\n name: string;\n};\n\ntype ConnectionState = {\n batchActive: boolean;\n executor: ConnectionExecutor;\n initialized: Promise<void>;\n listeners: Set<ConnectionListener>;\n};\ntype ConnectionListener = (name: string, table: string) => void;\ntype KeyColumns = { encoded: string; kind: 'number' | 'string'; number: number | null; string: string | null };\ntype StoredRow = { expiresAt: number | undefined; json: string; rowId: number };\n\nconst connectionStates = new WeakMap<SQLiteDatabase, ConnectionState>();\nconst RECORDS_TABLE = '\"__vielzeug_vault_records\"';\nconst METADATA_TABLE = '\"__vielzeug_vault_metadata\"';\nconst STORAGE_FORMAT_VERSION = 1;\nconst ITERATION_PAGE_SIZE = 100;\n\nclass ConnectionExecutor {\n private tail: Promise<void> = Promise.resolve();\n\n async acquire(): Promise<() => void> {\n let release: (() => void) | undefined;\n const previous = this.tail;\n\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n await previous;\n\n return () => release?.();\n }\n\n async run<T>(work: () => T | Promise<T>): Promise<T> {\n const release = await this.acquire();\n\n try {\n return await work();\n } finally {\n release();\n }\n }\n}\n\nfunction getConnectionState(database: SQLiteDatabase): ConnectionState {\n const current = connectionStates.get(database);\n\n if (current) return current;\n\n const executor = new ConnectionExecutor();\n const state: ConnectionState = {\n batchActive: false,\n executor,\n initialized: executor.run(() => initializeDatabase(database)),\n listeners: new Set(),\n };\n\n connectionStates.set(database, state);\n\n return state;\n}\n\nfunction initializeDatabase(database: SQLiteDatabase): void {\n database.exec(\n `\n CREATE TABLE IF NOT EXISTS ${METADATA_TABLE} (\n namespace TEXT PRIMARY KEY,\n format_version INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${RECORDS_TABLE} (\n namespace TEXT NOT NULL,\n table_name TEXT NOT NULL,\n key_tag TEXT NOT NULL,\n key_kind TEXT NOT NULL,\n key_number REAL,\n key_string TEXT,\n value_json TEXT NOT NULL,\n expires_at INTEGER,\n PRIMARY KEY (namespace, table_name, key_tag)\n );\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_expiration\"\n ON ${RECORDS_TABLE} (namespace, table_name, expires_at);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_number_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_number);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_string_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_string);\n `,\n );\n}\n\nfunction initializeNamespace(database: SQLiteDatabase, name: string): void {\n const row = get(database, `SELECT format_version FROM ${METADATA_TABLE} WHERE namespace = ?`, [name]);\n\n if (row === undefined) {\n run(database, `INSERT INTO ${METADATA_TABLE} (namespace, format_version) VALUES (?, ?)`, [\n name,\n STORAGE_FORMAT_VERSION,\n ]);\n\n return;\n }\n\n if (row.format_version !== STORAGE_FORMAT_VERSION) {\n throw new VaultError(`SQLite storage format for \"${name}\" is not supported`);\n }\n}\n\nfunction assertName(name: string): void {\n if (name.length === 0) throw new VaultError('createSQLite: name must not be empty');\n}\n\nfunction assertJsonValue(value: unknown, seen: Set<object>, path: string): void {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return;\n\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return;\n\n throw new VaultError(`SQLite serialization failed at ${path}: numbers must be finite`);\n }\n\n if (typeof value !== 'object') {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a JSON-compatible value`);\n }\n\n if (seen.has(value as object)) {\n throw new VaultError(`SQLite serialization failed at ${path}: circular references are not supported`);\n }\n\n if (Array.isArray(value)) {\n seen.add(value);\n\n for (let index = 0; index < value.length; index += 1) {\n assertJsonValue(value[index], seen, `${path}[${String(index)}]`);\n }\n\n seen.delete(value);\n\n return;\n }\n\n const prototype = Object.getPrototypeOf(value);\n\n if (prototype !== null && prototype !== Object.prototype) {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a plain object`);\n }\n\n seen.add(value as object);\n\n for (const [key, nested] of Object.entries(value)) {\n assertJsonValue(nested, seen, `${path}.${key}`);\n }\n\n seen.delete(value);\n}\n\nfunction encodeJson(value: object): string {\n assertJsonValue(value, new Set(), 'record');\n\n return JSON.stringify(value);\n}\n\nfunction decodeJson(json: string): object {\n try {\n const value: unknown = JSON.parse(json);\n\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new VaultError('stored record is not a JSON object');\n }\n\n return value;\n } catch (error) {\n if (error instanceof VaultError) throw error;\n\n throw new VaultError('stored record contains invalid JSON', { cause: error });\n }\n}\n\nfunction toKeyColumns(key: number | string): KeyColumns {\n const encoded = encodeVaultKey(key);\n\n return typeof key === 'number'\n ? { encoded, kind: 'number', number: key, string: null }\n : { encoded, kind: 'string', number: null, string: key };\n}\n\nfunction getStoredRow(row: SQLiteRow): StoredRow {\n const json = row.value_json;\n const rawExpiresAt = row.expires_at;\n const rowId = row.row_id;\n\n if (typeof json !== 'string' || typeof rowId !== 'number' || !Number.isInteger(rowId)) {\n throw new VaultError('SQLite storage contains a malformed record');\n }\n\n if (rawExpiresAt !== null && (typeof rawExpiresAt !== 'number' || !Number.isFinite(rawExpiresAt))) {\n throw new VaultError('SQLite storage contains a malformed expiration timestamp');\n }\n\n const expiresAt = typeof rawExpiresAt === 'number' ? rawExpiresAt : undefined;\n\n return { expiresAt, json, rowId };\n}\n\nfunction withStatement<T>(database: SQLiteDatabase, sql: string, work: (statement: SQLiteStatement) => T): T {\n const statement = database.prepare(sql);\n\n try {\n return work(statement);\n } finally {\n statement.finalize?.();\n }\n}\n\nfunction run(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): unknown {\n return withStatement(database, sql, (statement) => statement.run(...parameters));\n}\n\nfunction get(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): SQLiteRow | undefined {\n return withStatement(database, sql, (statement) => statement.get(...parameters));\n}\n\nfunction all(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): readonly SQLiteRow[] {\n return withStatement(database, sql, (statement) => statement.all(...parameters));\n}\n\nfunction deleteExpired(database: SQLiteDatabase, name: string, table: string): void {\n run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND expires_at IS NOT NULL AND expires_at <= ?`,\n [name, table, Date.now()],\n );\n}\n\nfunction decodeLiveRecord<T extends object>(database: SQLiteDatabase, row: SQLiteRow): T | undefined {\n const stored = getStoredRow(row);\n\n if (isExpired(stored.expiresAt)) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE rowid = ?`, [stored.rowId]);\n\n return undefined;\n }\n\n return decodeJson(stored.json) as T;\n}\n\nfunction getAllLive<T extends object>(\n database: SQLiteDatabase,\n name: string,\n table: string,\n filterSql = '',\n filterParameters: SQLiteParameter[] = [],\n): T[] {\n deleteExpired(database, name, table);\n\n const records = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ?${filterSql}\n ORDER BY rowid`,\n [name, table, ...filterParameters],\n );\n\n return records.flatMap((row) => {\n const record = decodeLiveRecord<T>(database, row);\n\n return record === undefined ? [] : [record];\n });\n}\n\nfunction createDirectCore<S extends AnySchema, K extends keyof S & string>(\n database: SQLiteDatabase,\n name: string,\n schema: S,\n inTransaction = false,\n): StorageBackend<S, K> {\n const getRecord = <T extends K>(table: T, key: KeyOf<S, T>): RecordOf<S, T> | undefined => {\n const columns = toKeyColumns(key);\n const row = get(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?`,\n [name, table, columns.encoded],\n );\n\n return row === undefined ? undefined : decodeLiveRecord<RecordOf<S, T>>(database, row);\n };\n\n const core: StorageBackend<S, K> = {\n async clear(table) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`, [name, table]);\n },\n async count(table) {\n deleteExpired(database, name, table);\n\n const row = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const count = row?.count;\n\n if (typeof count !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return count;\n },\n async delete(table, key) {\n const columns = toKeyColumns(key);\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, columns.encoded, Date.now()],\n ) as { changes?: number } | undefined;\n\n return (result?.changes ?? 0) > 0;\n },\n async deleteMany(table, keys) {\n if (keys.length === 0) return 0;\n\n let deleted = 0;\n // 3 fixed params: namespace, table_name, expires_at check.\n const SQLITE_PARAM_LIMIT = 999;\n const MAX_KEYS_PER_CHUNK = SQLITE_PARAM_LIMIT - 3;\n const encodedKeys = keys.map((k) => toKeyColumns(k).encoded);\n\n for (let i = 0; i < encodedKeys.length; i += MAX_KEYS_PER_CHUNK) {\n const chunk = encodedKeys.slice(i, i + MAX_KEYS_PER_CHUNK);\n const placeholders = chunk.map(() => '?').join(', ');\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag IN (${placeholders})\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, ...chunk, Date.now()],\n ) as { changes?: number } | undefined;\n\n deleted += result?.changes ?? 0;\n }\n\n return deleted;\n },\n async get(table, key) {\n return getRecord(table, key);\n },\n async getAll(table) {\n return getAllLive<RecordOf<S, typeof table>>(database, name, table);\n },\n async getAllKeys(table) {\n return (await core.getAll(table)).map((record) => getRecordKey(schema, table, record));\n },\n async getMany(table, keys) {\n return keys.map((key) => getRecord(table, key));\n },\n async has(table, key) {\n return getRecord(table, key) !== undefined;\n },\n async pruneAllExpired() {\n const results: Record<string, number> = {};\n\n for (const table of Object.keys(schema)) {\n results[table] = await core.pruneExpiredInTable(table as K);\n }\n\n return results;\n },\n async pruneExpiredInTable(table) {\n const beforeRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const before = beforeRow?.count;\n\n if (typeof before !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n deleteExpired(database, name, table);\n\n const afterRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const after = afterRow?.count;\n\n if (typeof after !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return before - after;\n },\n async put(table, value, ttl) {\n const key = getRecordKey(schema, table, value);\n const columns = toKeyColumns(key);\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [name, table, columns.encoded, columns.kind, columns.number, columns.string, encodeJson(value), expiresAt],\n );\n },\n async putAll(table, values, ttl) {\n if (values.length === 0) return;\n\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n const encodedJsonValues = values.map((v) => encodeJson(v));\n const columnsList = values.map((v) => toKeyColumns(getRecordKey(schema, table, v)));\n\n const writeAll = () => {\n for (let i = 0; i < values.length; i++) {\n const columns = columnsList[i];\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [\n name,\n table,\n columns.encoded,\n columns.kind,\n columns.number,\n columns.string,\n encodedJsonValues[i],\n expiresAt,\n ],\n );\n }\n };\n\n if (inTransaction) {\n writeAll();\n return;\n }\n\n database.exec('BEGIN');\n try {\n writeAll();\n database.exec('COMMIT');\n } catch (error) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite putAll rollback failed', { cause: rollbackError });\n }\n throw error;\n }\n },\n };\n\n return core;\n}\n\n/**\n * Creates a SQLite-backed Vault store. The connection is caller-owned unless\n * `closeOnDispose` is explicitly enabled.\n */\nexport function createSQLite<S extends AnySchema>(options: SQLiteVaultOptions<S>): TransactionalVaultStore<S> {\n const { closeOnDispose = false, database, name, schema, validators } = options;\n\n assertName(name);\n\n const state = getConnectionState(database);\n const namespaceReady = state.executor.run(async () => {\n await state.initialized;\n initializeNamespace(database, name);\n });\n let ownListener: ConnectionListener | undefined;\n\n const directCore = createDirectCore(database, name, schema);\n const withConnection = <T>(work: () => Promise<T>): Promise<T> =>\n state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n return work();\n });\n const guardedCore = Object.fromEntries(\n Object.entries(directCore).map(([method, implementation]) => [\n method,\n (...arguments_: unknown[]) => {\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return withConnection(() => (implementation as (...args: unknown[]) => Promise<unknown>)(...arguments_));\n },\n ]),\n ) as StorageBackend<S>;\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, guardedCore, {\n onCrossTabMessage(notify) {\n const listener: ConnectionListener = (eventName, table) => {\n if (eventName === name && Object.hasOwn(schema, table)) notify(table as keyof S & string);\n };\n\n ownListener = listener;\n state.listeners.add(listener);\n\n return () => {\n state.listeners.delete(listener);\n\n if (ownListener === listener) ownListener = undefined;\n };\n },\n onMutation(table) {\n for (const listener of state.listeners) {\n if (listener !== ownListener) listener(name, table);\n }\n },\n onTransactions: (deps) => {\n batch = async (tables, fn) => {\n assertBatchTables(tables);\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n const dirtyTables = new Set<keyof S & string>();\n const txCore = createDirectCore<S, keyof S & string>(database, name, schema, true);\n const tx = buildTxContext(\n schema,\n txCore,\n (table) => dirtyTables.add(table),\n deps.validate,\n new Set<string>(tables),\n );\n let transactionStarted = false;\n let committed = false;\n\n state.batchActive = true;\n\n try {\n database.exec('BEGIN IMMEDIATE');\n transactionStarted = true;\n\n const result = await fn(tx);\n\n database.exec('COMMIT');\n committed = true;\n\n for (const table of dirtyTables) {\n deps.notifyMutation(table);\n }\n\n return result;\n } catch (error) {\n if (transactionStarted && !committed) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite batch rollback failed', { cause: rollbackError });\n }\n }\n\n throw error;\n } finally {\n state.batchActive = false;\n }\n });\n };\n },\n schema,\n validators,\n });\n\n if (!batch) throw new VaultError('SQLite transaction capability was not initialized');\n\n const store: TransactionalVaultStore<S> = {\n ...adapter,\n batch,\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (adapter.disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n let completed = false;\n let lastRowId = 0;\n let lease: (() => void) | undefined;\n let rows: readonly SQLiteRow[] = [];\n let index = 0;\n\n const release = (): void => {\n lease?.();\n lease = undefined;\n };\n const loadNextPage = (): void => {\n rows = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND rowid > ?\n ORDER BY rowid\n LIMIT ?`,\n [name, table, lastRowId, ITERATION_PAGE_SIZE],\n );\n index = 0;\n };\n\n return {\n async next(): Promise<IteratorResult<RecordOf<S, K>>> {\n if (completed) return { done: true, value: undefined };\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n try {\n if (!lease) {\n lease = await state.executor.acquire();\n await state.initialized;\n await namespaceReady;\n }\n\n while (true) {\n if (index >= rows.length) {\n loadNextPage();\n\n if (rows.length === 0) {\n completed = true;\n release();\n\n return { done: true, value: undefined };\n }\n }\n\n const row = rows[index++];\n const stored = getStoredRow(row);\n\n lastRowId = stored.rowId;\n\n const value = decodeLiveRecord<RecordOf<S, K>>(database, row);\n\n if (value !== undefined) return { done: false, value };\n }\n } catch (error) {\n completed = true;\n release();\n\n throw error;\n }\n },\n async return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n return { done: true, value: value as RecordOf<S, K> };\n },\n async throw(error?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n throw error;\n },\n };\n },\n };\n },\n };\n\n if (closeOnDispose) {\n const dispose = store.dispose.bind(store);\n let closePromise: Promise<void> | undefined;\n\n store.dispose = async (): Promise<void> => {\n await dispose();\n closePromise ??= state.executor.run(() => database.close?.());\n await closePromise;\n };\n store[Symbol.asyncDispose] = async (): Promise<void> => {\n await store.dispose();\n };\n }\n\n return store;\n}\n"],"mappings":"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"}
|
|
@@ -32,11 +32,9 @@ export type SQLiteVaultOptions<S extends AnySchema> = BaseAdapterOptions<S> & {
|
|
|
32
32
|
/** Namespace that isolates this store's records in the shared connection. */
|
|
33
33
|
name: string;
|
|
34
34
|
};
|
|
35
|
-
/** SQLite provides atomic batches and lazy keyset-paginated iteration. */
|
|
36
|
-
export type SQLiteVaultStore<S extends AnySchema> = TransactionalVaultStore<S>;
|
|
37
35
|
/**
|
|
38
36
|
* Creates a SQLite-backed Vault store. The connection is caller-owned unless
|
|
39
37
|
* `closeOnDispose` is explicitly enabled.
|
|
40
38
|
*/
|
|
41
|
-
export declare function createSQLite<S extends AnySchema>(options: SQLiteVaultOptions<S>):
|
|
39
|
+
export declare function createSQLite<S extends AnySchema>(options: SQLiteVaultOptions<S>): TransactionalVaultStore<S>;
|
|
42
40
|
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAGlB,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAElB,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AACrD,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,UAAU,EAAE,eAAe,EAAE,GAAG,SAAS,SAAS,EAAE,CAAC;IAC5D,QAAQ,CAAC,IAAI,IAAI,CAAC;IAClB,GAAG,CAAC,GAAG,UAAU,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,SAAS,CAAC;IAC7D,GAAG,CAAC,GAAG,UAAU,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;CAChD;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;CACvC;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAC5E,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,cAAc,CAAC;IACzB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAGlB,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAElB,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AACrD,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,UAAU,EAAE,eAAe,EAAE,GAAG,SAAS,SAAS,EAAE,CAAC;IAC5D,QAAQ,CAAC,IAAI,IAAI,CAAC;IAClB,GAAG,CAAC,GAAG,UAAU,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,SAAS,CAAC;IAC7D,GAAG,CAAC,GAAG,UAAU,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;CAChD;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;CACvC;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAC5E,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,cAAc,CAAC;IACzB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAqdF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAqO5G"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/adapters/sqlite.ts"],"sourcesContent":["import {\n assertBatchTables,\n type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} from '../types';\n\nexport type { TransactionContext };\n\nexport type SQLiteParameter = null | number | string;\ntype SQLiteRow = Record<string, unknown>;\n\n/**\n * A synchronous SQLite statement with positional parameter binding.\n *\n * Adapters for drivers whose native statement API differs can implement this\n * structural protocol without adding a runtime dependency to Vault.\n */\nexport interface SQLiteStatement {\n all(...parameters: SQLiteParameter[]): readonly SQLiteRow[];\n finalize?(): void;\n get(...parameters: SQLiteParameter[]): SQLiteRow | undefined;\n run(...parameters: SQLiteParameter[]): unknown;\n}\n\n/**\n * A runtime-neutral synchronous SQLite connection.\n *\n * Node's `DatabaseSync`, Bun's `Database`, and Deno's `@db/sqlite` `Database`\n * satisfy this protocol directly.\n */\nexport interface SQLiteDatabase {\n close?(): void;\n exec(sql: string): void;\n prepare(sql: string): SQLiteStatement;\n}\n\nexport type SQLiteVaultOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n /** Closes the caller-provided connection during store disposal when true. */\n closeOnDispose?: boolean;\n database: SQLiteDatabase;\n /** Namespace that isolates this store's records in the shared connection. */\n name: string;\n};\n\n/** SQLite provides atomic batches and lazy keyset-paginated iteration. */\nexport type SQLiteVaultStore<S extends AnySchema> = TransactionalVaultStore<S>;\n\ntype ConnectionState = {\n batchActive: boolean;\n executor: ConnectionExecutor;\n initialized: Promise<void>;\n listeners: Set<ConnectionListener>;\n};\ntype ConnectionListener = (name: string, table: string) => void;\ntype KeyColumns = { encoded: string; kind: 'number' | 'string'; number: number | null; string: string | null };\ntype StoredRow = { expiresAt: number | undefined; json: string; rowId: number };\n\nconst connectionStates = new WeakMap<SQLiteDatabase, ConnectionState>();\nconst RECORDS_TABLE = '\"__vielzeug_vault_records\"';\nconst METADATA_TABLE = '\"__vielzeug_vault_metadata\"';\nconst STORAGE_FORMAT_VERSION = 1;\nconst ITERATION_PAGE_SIZE = 100;\n\nclass ConnectionExecutor {\n private tail: Promise<void> = Promise.resolve();\n\n async acquire(): Promise<() => void> {\n let release: (() => void) | undefined;\n const previous = this.tail;\n\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n await previous;\n\n return () => release?.();\n }\n\n async run<T>(work: () => T | Promise<T>): Promise<T> {\n const release = await this.acquire();\n\n try {\n return await work();\n } finally {\n release();\n }\n }\n}\n\nfunction getConnectionState(database: SQLiteDatabase): ConnectionState {\n const current = connectionStates.get(database);\n\n if (current) return current;\n\n const executor = new ConnectionExecutor();\n const state: ConnectionState = {\n batchActive: false,\n executor,\n initialized: executor.run(() => initializeDatabase(database)),\n listeners: new Set(),\n };\n\n connectionStates.set(database, state);\n\n return state;\n}\n\nfunction initializeDatabase(database: SQLiteDatabase): void {\n database.exec(\n `\n CREATE TABLE IF NOT EXISTS ${METADATA_TABLE} (\n namespace TEXT PRIMARY KEY,\n format_version INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${RECORDS_TABLE} (\n namespace TEXT NOT NULL,\n table_name TEXT NOT NULL,\n key_tag TEXT NOT NULL,\n key_kind TEXT NOT NULL,\n key_number REAL,\n key_string TEXT,\n value_json TEXT NOT NULL,\n expires_at INTEGER,\n PRIMARY KEY (namespace, table_name, key_tag)\n );\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_expiration\"\n ON ${RECORDS_TABLE} (namespace, table_name, expires_at);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_number_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_number);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_string_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_string);\n `,\n );\n}\n\nfunction initializeNamespace(database: SQLiteDatabase, name: string): void {\n const row = get(database, `SELECT format_version FROM ${METADATA_TABLE} WHERE namespace = ?`, [name]);\n\n if (row === undefined) {\n run(database, `INSERT INTO ${METADATA_TABLE} (namespace, format_version) VALUES (?, ?)`, [\n name,\n STORAGE_FORMAT_VERSION,\n ]);\n\n return;\n }\n\n if (row.format_version !== STORAGE_FORMAT_VERSION) {\n throw new VaultError(`SQLite storage format for \"${name}\" is not supported`);\n }\n}\n\nfunction assertName(name: string): void {\n if (name.length === 0) throw new VaultError('createSQLite: name must not be empty');\n}\n\nfunction assertJsonValue(value: unknown, seen: Set<object>, path: string): void {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return;\n\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return;\n\n throw new VaultError(`SQLite serialization failed at ${path}: numbers must be finite`);\n }\n\n if (typeof value !== 'object') {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a JSON-compatible value`);\n }\n\n if (seen.has(value as object)) {\n throw new VaultError(`SQLite serialization failed at ${path}: circular references are not supported`);\n }\n\n if (Array.isArray(value)) {\n seen.add(value);\n\n for (let index = 0; index < value.length; index += 1) {\n assertJsonValue(value[index], seen, `${path}[${String(index)}]`);\n }\n\n seen.delete(value);\n\n return;\n }\n\n const prototype = Object.getPrototypeOf(value);\n\n if (prototype !== null && prototype !== Object.prototype) {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a plain object`);\n }\n\n seen.add(value as object);\n\n for (const [key, nested] of Object.entries(value)) {\n assertJsonValue(nested, seen, `${path}.${key}`);\n }\n\n seen.delete(value);\n}\n\nfunction encodeJson(value: object): string {\n assertJsonValue(value, new Set(), 'record');\n\n return JSON.stringify(value);\n}\n\nfunction decodeJson(json: string): object {\n try {\n const value: unknown = JSON.parse(json);\n\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new VaultError('stored record is not a JSON object');\n }\n\n return value;\n } catch (error) {\n if (error instanceof VaultError) throw error;\n\n throw new VaultError('stored record contains invalid JSON', { cause: error });\n }\n}\n\nfunction toKeyColumns(key: number | string): KeyColumns {\n const encoded = encodeVaultKey(key);\n\n return typeof key === 'number'\n ? { encoded, kind: 'number', number: key, string: null }\n : { encoded, kind: 'string', number: null, string: key };\n}\n\nfunction getStoredRow(row: SQLiteRow): StoredRow {\n const json = row.value_json;\n const rawExpiresAt = row.expires_at;\n const rowId = row.row_id;\n\n if (typeof json !== 'string' || typeof rowId !== 'number' || !Number.isInteger(rowId)) {\n throw new VaultError('SQLite storage contains a malformed record');\n }\n\n if (rawExpiresAt !== null && (typeof rawExpiresAt !== 'number' || !Number.isFinite(rawExpiresAt))) {\n throw new VaultError('SQLite storage contains a malformed expiration timestamp');\n }\n\n const expiresAt = typeof rawExpiresAt === 'number' ? rawExpiresAt : undefined;\n\n return { expiresAt, json, rowId };\n}\n\nfunction withStatement<T>(database: SQLiteDatabase, sql: string, work: (statement: SQLiteStatement) => T): T {\n const statement = database.prepare(sql);\n\n try {\n return work(statement);\n } finally {\n statement.finalize?.();\n }\n}\n\nfunction run(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): unknown {\n return withStatement(database, sql, (statement) => statement.run(...parameters));\n}\n\nfunction get(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): SQLiteRow | undefined {\n return withStatement(database, sql, (statement) => statement.get(...parameters));\n}\n\nfunction all(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): readonly SQLiteRow[] {\n return withStatement(database, sql, (statement) => statement.all(...parameters));\n}\n\nfunction deleteExpired(database: SQLiteDatabase, name: string, table: string): void {\n run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND expires_at IS NOT NULL AND expires_at <= ?`,\n [name, table, Date.now()],\n );\n}\n\nfunction decodeLiveRecord<T extends object>(database: SQLiteDatabase, row: SQLiteRow): T | undefined {\n const stored = getStoredRow(row);\n\n if (isExpired(stored.expiresAt)) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE rowid = ?`, [stored.rowId]);\n\n return undefined;\n }\n\n return decodeJson(stored.json) as T;\n}\n\nfunction getAllLive<T extends object>(\n database: SQLiteDatabase,\n name: string,\n table: string,\n filterSql = '',\n filterParameters: SQLiteParameter[] = [],\n): T[] {\n deleteExpired(database, name, table);\n\n const records = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ?${filterSql}\n ORDER BY rowid`,\n [name, table, ...filterParameters],\n );\n\n return records.flatMap((row) => {\n const record = decodeLiveRecord<T>(database, row);\n\n return record === undefined ? [] : [record];\n });\n}\n\nfunction createDirectCore<S extends AnySchema, K extends keyof S & string>(\n database: SQLiteDatabase,\n name: string,\n schema: S,\n inTransaction = false,\n): StorageBackend<S, K> {\n const getRecord = <T extends K>(table: T, key: KeyOf<S, T>): RecordOf<S, T> | undefined => {\n const columns = toKeyColumns(key);\n const row = get(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?`,\n [name, table, columns.encoded],\n );\n\n return row === undefined ? undefined : decodeLiveRecord<RecordOf<S, T>>(database, row);\n };\n\n const core: StorageBackend<S, K> = {\n async clear(table) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`, [name, table]);\n },\n async count(table) {\n deleteExpired(database, name, table);\n\n const row = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const count = row?.count;\n\n if (typeof count !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return count;\n },\n async delete(table, key) {\n const columns = toKeyColumns(key);\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, columns.encoded, Date.now()],\n ) as { changes?: number } | undefined;\n\n return (result?.changes ?? 0) > 0;\n },\n async deleteMany(table, keys) {\n if (keys.length === 0) return 0;\n\n let deleted = 0;\n // 3 fixed params: namespace, table_name, expires_at check.\n const SQLITE_PARAM_LIMIT = 999;\n const MAX_KEYS_PER_CHUNK = SQLITE_PARAM_LIMIT - 3;\n const encodedKeys = keys.map((k) => toKeyColumns(k).encoded);\n\n for (let i = 0; i < encodedKeys.length; i += MAX_KEYS_PER_CHUNK) {\n const chunk = encodedKeys.slice(i, i + MAX_KEYS_PER_CHUNK);\n const placeholders = chunk.map(() => '?').join(', ');\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag IN (${placeholders})\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, ...chunk, Date.now()],\n ) as { changes?: number } | undefined;\n\n deleted += result?.changes ?? 0;\n }\n\n return deleted;\n },\n async get(table, key) {\n return getRecord(table, key);\n },\n async getAll(table) {\n return getAllLive<RecordOf<S, typeof table>>(database, name, table);\n },\n async getAllKeys(table) {\n return (await core.getAll(table)).map((record) => getRecordKey(schema, table, record));\n },\n async getMany(table, keys) {\n return keys.map((key) => getRecord(table, key));\n },\n async has(table, key) {\n return getRecord(table, key) !== undefined;\n },\n async pruneAllExpired() {\n const results: Record<string, number> = {};\n\n for (const table of Object.keys(schema)) {\n results[table] = await core.pruneExpiredInTable(table as K);\n }\n\n return results;\n },\n async pruneExpiredInTable(table) {\n const beforeRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const before = beforeRow?.count;\n\n if (typeof before !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n deleteExpired(database, name, table);\n\n const afterRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const after = afterRow?.count;\n\n if (typeof after !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return before - after;\n },\n async put(table, value, ttl) {\n const key = getRecordKey(schema, table, value);\n const columns = toKeyColumns(key);\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [name, table, columns.encoded, columns.kind, columns.number, columns.string, encodeJson(value), expiresAt],\n );\n },\n async putAll(table, values, ttl) {\n if (values.length === 0) return;\n\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n const encodedJsonValues = values.map((v) => encodeJson(v));\n const columnsList = values.map((v) => toKeyColumns(getRecordKey(schema, table, v)));\n\n const writeAll = () => {\n for (let i = 0; i < values.length; i++) {\n const columns = columnsList[i];\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [\n name,\n table,\n columns.encoded,\n columns.kind,\n columns.number,\n columns.string,\n encodedJsonValues[i],\n expiresAt,\n ],\n );\n }\n };\n\n if (inTransaction) {\n writeAll();\n return;\n }\n\n database.exec('BEGIN');\n try {\n writeAll();\n database.exec('COMMIT');\n } catch (error) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite putAll rollback failed', { cause: rollbackError });\n }\n throw error;\n }\n },\n };\n\n return core;\n}\n\n/**\n * Creates a SQLite-backed Vault store. The connection is caller-owned unless\n * `closeOnDispose` is explicitly enabled.\n */\nexport function createSQLite<S extends AnySchema>(options: SQLiteVaultOptions<S>): SQLiteVaultStore<S> {\n const { closeOnDispose = false, database, name, schema, validators } = options;\n\n assertName(name);\n\n const state = getConnectionState(database);\n const namespaceReady = state.executor.run(async () => {\n await state.initialized;\n initializeNamespace(database, name);\n });\n let ownListener: ConnectionListener | undefined;\n\n const directCore = createDirectCore(database, name, schema);\n const withConnection = <T>(work: () => Promise<T>): Promise<T> =>\n state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n return work();\n });\n const guardedCore = Object.fromEntries(\n Object.entries(directCore).map(([method, implementation]) => [\n method,\n (...arguments_: unknown[]) => {\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return withConnection(() => (implementation as (...args: unknown[]) => Promise<unknown>)(...arguments_));\n },\n ]),\n ) as StorageBackend<S>;\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, guardedCore, {\n onCrossTabMessage(notify) {\n const listener: ConnectionListener = (eventName, table) => {\n if (eventName === name && Object.hasOwn(schema, table)) notify(table as keyof S & string);\n };\n\n ownListener = listener;\n state.listeners.add(listener);\n\n return () => {\n state.listeners.delete(listener);\n\n if (ownListener === listener) ownListener = undefined;\n };\n },\n onMutation(table) {\n for (const listener of state.listeners) {\n if (listener !== ownListener) listener(name, table);\n }\n },\n onTransactions: (deps) => {\n batch = async (tables, fn) => {\n assertBatchTables(tables);\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n const dirtyTables = new Set<keyof S & string>();\n const txCore = createDirectCore<S, keyof S & string>(database, name, schema, true);\n const tx = buildTxContext(\n schema,\n txCore,\n (table) => dirtyTables.add(table),\n deps.validate,\n new Set<string>(tables),\n );\n let transactionStarted = false;\n let committed = false;\n\n state.batchActive = true;\n\n try {\n database.exec('BEGIN IMMEDIATE');\n transactionStarted = true;\n\n const result = await fn(tx);\n\n database.exec('COMMIT');\n committed = true;\n\n for (const table of dirtyTables) {\n deps.notifyMutation(table);\n }\n\n return result;\n } catch (error) {\n if (transactionStarted && !committed) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite batch rollback failed', { cause: rollbackError });\n }\n }\n\n throw error;\n } finally {\n state.batchActive = false;\n }\n });\n };\n },\n schema,\n validators,\n });\n\n if (!batch) throw new VaultError('SQLite transaction capability was not initialized');\n\n const store: SQLiteVaultStore<S> = {\n ...adapter,\n batch,\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (adapter.disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n let completed = false;\n let lastRowId = 0;\n let lease: (() => void) | undefined;\n let rows: readonly SQLiteRow[] = [];\n let index = 0;\n\n const release = (): void => {\n lease?.();\n lease = undefined;\n };\n const loadNextPage = (): void => {\n rows = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND rowid > ?\n ORDER BY rowid\n LIMIT ?`,\n [name, table, lastRowId, ITERATION_PAGE_SIZE],\n );\n index = 0;\n };\n\n return {\n async next(): Promise<IteratorResult<RecordOf<S, K>>> {\n if (completed) return { done: true, value: undefined };\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n try {\n if (!lease) {\n lease = await state.executor.acquire();\n await state.initialized;\n await namespaceReady;\n }\n\n while (true) {\n if (index >= rows.length) {\n loadNextPage();\n\n if (rows.length === 0) {\n completed = true;\n release();\n\n return { done: true, value: undefined };\n }\n }\n\n const row = rows[index++];\n const stored = getStoredRow(row);\n\n lastRowId = stored.rowId;\n\n const value = decodeLiveRecord<RecordOf<S, K>>(database, row);\n\n if (value !== undefined) return { done: false, value };\n }\n } catch (error) {\n completed = true;\n release();\n\n throw error;\n }\n },\n async return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n return { done: true, value: value as RecordOf<S, K> };\n },\n async throw(error?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n throw error;\n },\n };\n },\n };\n },\n };\n\n if (closeOnDispose) {\n const dispose = store.dispose.bind(store);\n let closePromise: Promise<void> | undefined;\n\n store.dispose = async (): Promise<void> => {\n await dispose();\n closePromise ??= state.executor.run(() => database.close?.());\n await closePromise;\n };\n store[Symbol.asyncDispose] = async (): Promise<void> => {\n await store.dispose();\n };\n }\n\n return store;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/adapters/sqlite.ts"],"sourcesContent":["import {\n assertBatchTables,\n type BatchImpl,\n buildAdapterOps,\n buildTxContext,\n type StorageBackend,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired } from '../ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n} from '../types';\n\nexport type { TransactionContext };\n\nexport type SQLiteParameter = null | number | string;\ntype SQLiteRow = Record<string, unknown>;\n\n/**\n * A synchronous SQLite statement with positional parameter binding.\n *\n * Adapters for drivers whose native statement API differs can implement this\n * structural protocol without adding a runtime dependency to Vault.\n */\nexport interface SQLiteStatement {\n all(...parameters: SQLiteParameter[]): readonly SQLiteRow[];\n finalize?(): void;\n get(...parameters: SQLiteParameter[]): SQLiteRow | undefined;\n run(...parameters: SQLiteParameter[]): unknown;\n}\n\n/**\n * A runtime-neutral synchronous SQLite connection.\n *\n * Node's `DatabaseSync`, Bun's `Database`, and Deno's `@db/sqlite` `Database`\n * satisfy this protocol directly.\n */\nexport interface SQLiteDatabase {\n close?(): void;\n exec(sql: string): void;\n prepare(sql: string): SQLiteStatement;\n}\n\nexport type SQLiteVaultOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n /** Closes the caller-provided connection during store disposal when true. */\n closeOnDispose?: boolean;\n database: SQLiteDatabase;\n /** Namespace that isolates this store's records in the shared connection. */\n name: string;\n};\n\ntype ConnectionState = {\n batchActive: boolean;\n executor: ConnectionExecutor;\n initialized: Promise<void>;\n listeners: Set<ConnectionListener>;\n};\ntype ConnectionListener = (name: string, table: string) => void;\ntype KeyColumns = { encoded: string; kind: 'number' | 'string'; number: number | null; string: string | null };\ntype StoredRow = { expiresAt: number | undefined; json: string; rowId: number };\n\nconst connectionStates = new WeakMap<SQLiteDatabase, ConnectionState>();\nconst RECORDS_TABLE = '\"__vielzeug_vault_records\"';\nconst METADATA_TABLE = '\"__vielzeug_vault_metadata\"';\nconst STORAGE_FORMAT_VERSION = 1;\nconst ITERATION_PAGE_SIZE = 100;\n\nclass ConnectionExecutor {\n private tail: Promise<void> = Promise.resolve();\n\n async acquire(): Promise<() => void> {\n let release: (() => void) | undefined;\n const previous = this.tail;\n\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n await previous;\n\n return () => release?.();\n }\n\n async run<T>(work: () => T | Promise<T>): Promise<T> {\n const release = await this.acquire();\n\n try {\n return await work();\n } finally {\n release();\n }\n }\n}\n\nfunction getConnectionState(database: SQLiteDatabase): ConnectionState {\n const current = connectionStates.get(database);\n\n if (current) return current;\n\n const executor = new ConnectionExecutor();\n const state: ConnectionState = {\n batchActive: false,\n executor,\n initialized: executor.run(() => initializeDatabase(database)),\n listeners: new Set(),\n };\n\n connectionStates.set(database, state);\n\n return state;\n}\n\nfunction initializeDatabase(database: SQLiteDatabase): void {\n database.exec(\n `\n CREATE TABLE IF NOT EXISTS ${METADATA_TABLE} (\n namespace TEXT PRIMARY KEY,\n format_version INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${RECORDS_TABLE} (\n namespace TEXT NOT NULL,\n table_name TEXT NOT NULL,\n key_tag TEXT NOT NULL,\n key_kind TEXT NOT NULL,\n key_number REAL,\n key_string TEXT,\n value_json TEXT NOT NULL,\n expires_at INTEGER,\n PRIMARY KEY (namespace, table_name, key_tag)\n );\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_expiration\"\n ON ${RECORDS_TABLE} (namespace, table_name, expires_at);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_number_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_number);\n CREATE INDEX IF NOT EXISTS \"__vielzeug_vault_records_string_key\"\n ON ${RECORDS_TABLE} (namespace, table_name, key_kind, key_string);\n `,\n );\n}\n\nfunction initializeNamespace(database: SQLiteDatabase, name: string): void {\n const row = get(database, `SELECT format_version FROM ${METADATA_TABLE} WHERE namespace = ?`, [name]);\n\n if (row === undefined) {\n run(database, `INSERT INTO ${METADATA_TABLE} (namespace, format_version) VALUES (?, ?)`, [\n name,\n STORAGE_FORMAT_VERSION,\n ]);\n\n return;\n }\n\n if (row.format_version !== STORAGE_FORMAT_VERSION) {\n throw new VaultError(`SQLite storage format for \"${name}\" is not supported`);\n }\n}\n\nfunction assertName(name: string): void {\n if (name.length === 0) throw new VaultError('createSQLite: name must not be empty');\n}\n\nfunction assertJsonValue(value: unknown, seen: Set<object>, path: string): void {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return;\n\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return;\n\n throw new VaultError(`SQLite serialization failed at ${path}: numbers must be finite`);\n }\n\n if (typeof value !== 'object') {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a JSON-compatible value`);\n }\n\n if (seen.has(value as object)) {\n throw new VaultError(`SQLite serialization failed at ${path}: circular references are not supported`);\n }\n\n if (Array.isArray(value)) {\n seen.add(value);\n\n for (let index = 0; index < value.length; index += 1) {\n assertJsonValue(value[index], seen, `${path}[${String(index)}]`);\n }\n\n seen.delete(value);\n\n return;\n }\n\n const prototype = Object.getPrototypeOf(value);\n\n if (prototype !== null && prototype !== Object.prototype) {\n throw new VaultError(`SQLite serialization failed at ${path}: expected a plain object`);\n }\n\n seen.add(value as object);\n\n for (const [key, nested] of Object.entries(value)) {\n assertJsonValue(nested, seen, `${path}.${key}`);\n }\n\n seen.delete(value);\n}\n\nfunction encodeJson(value: object): string {\n assertJsonValue(value, new Set(), 'record');\n\n return JSON.stringify(value);\n}\n\nfunction decodeJson(json: string): object {\n try {\n const value: unknown = JSON.parse(json);\n\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new VaultError('stored record is not a JSON object');\n }\n\n return value;\n } catch (error) {\n if (error instanceof VaultError) throw error;\n\n throw new VaultError('stored record contains invalid JSON', { cause: error });\n }\n}\n\nfunction toKeyColumns(key: number | string): KeyColumns {\n const encoded = encodeVaultKey(key);\n\n return typeof key === 'number'\n ? { encoded, kind: 'number', number: key, string: null }\n : { encoded, kind: 'string', number: null, string: key };\n}\n\nfunction getStoredRow(row: SQLiteRow): StoredRow {\n const json = row.value_json;\n const rawExpiresAt = row.expires_at;\n const rowId = row.row_id;\n\n if (typeof json !== 'string' || typeof rowId !== 'number' || !Number.isInteger(rowId)) {\n throw new VaultError('SQLite storage contains a malformed record');\n }\n\n if (rawExpiresAt !== null && (typeof rawExpiresAt !== 'number' || !Number.isFinite(rawExpiresAt))) {\n throw new VaultError('SQLite storage contains a malformed expiration timestamp');\n }\n\n const expiresAt = typeof rawExpiresAt === 'number' ? rawExpiresAt : undefined;\n\n return { expiresAt, json, rowId };\n}\n\nfunction withStatement<T>(database: SQLiteDatabase, sql: string, work: (statement: SQLiteStatement) => T): T {\n const statement = database.prepare(sql);\n\n try {\n return work(statement);\n } finally {\n statement.finalize?.();\n }\n}\n\nfunction run(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): unknown {\n return withStatement(database, sql, (statement) => statement.run(...parameters));\n}\n\nfunction get(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): SQLiteRow | undefined {\n return withStatement(database, sql, (statement) => statement.get(...parameters));\n}\n\nfunction all(database: SQLiteDatabase, sql: string, parameters: SQLiteParameter[] = []): readonly SQLiteRow[] {\n return withStatement(database, sql, (statement) => statement.all(...parameters));\n}\n\nfunction deleteExpired(database: SQLiteDatabase, name: string, table: string): void {\n run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND expires_at IS NOT NULL AND expires_at <= ?`,\n [name, table, Date.now()],\n );\n}\n\nfunction decodeLiveRecord<T extends object>(database: SQLiteDatabase, row: SQLiteRow): T | undefined {\n const stored = getStoredRow(row);\n\n if (isExpired(stored.expiresAt)) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE rowid = ?`, [stored.rowId]);\n\n return undefined;\n }\n\n return decodeJson(stored.json) as T;\n}\n\nfunction getAllLive<T extends object>(\n database: SQLiteDatabase,\n name: string,\n table: string,\n filterSql = '',\n filterParameters: SQLiteParameter[] = [],\n): T[] {\n deleteExpired(database, name, table);\n\n const records = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ?${filterSql}\n ORDER BY rowid`,\n [name, table, ...filterParameters],\n );\n\n return records.flatMap((row) => {\n const record = decodeLiveRecord<T>(database, row);\n\n return record === undefined ? [] : [record];\n });\n}\n\nfunction createDirectCore<S extends AnySchema, K extends keyof S & string>(\n database: SQLiteDatabase,\n name: string,\n schema: S,\n inTransaction = false,\n): StorageBackend<S, K> {\n const getRecord = <T extends K>(table: T, key: KeyOf<S, T>): RecordOf<S, T> | undefined => {\n const columns = toKeyColumns(key);\n const row = get(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?`,\n [name, table, columns.encoded],\n );\n\n return row === undefined ? undefined : decodeLiveRecord<RecordOf<S, T>>(database, row);\n };\n\n const core: StorageBackend<S, K> = {\n async clear(table) {\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`, [name, table]);\n },\n async count(table) {\n deleteExpired(database, name, table);\n\n const row = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const count = row?.count;\n\n if (typeof count !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return count;\n },\n async delete(table, key) {\n const columns = toKeyColumns(key);\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag = ?\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, columns.encoded, Date.now()],\n ) as { changes?: number } | undefined;\n\n return (result?.changes ?? 0) > 0;\n },\n async deleteMany(table, keys) {\n if (keys.length === 0) return 0;\n\n let deleted = 0;\n // 3 fixed params: namespace, table_name, expires_at check.\n const SQLITE_PARAM_LIMIT = 999;\n const MAX_KEYS_PER_CHUNK = SQLITE_PARAM_LIMIT - 3;\n const encodedKeys = keys.map((k) => toKeyColumns(k).encoded);\n\n for (let i = 0; i < encodedKeys.length; i += MAX_KEYS_PER_CHUNK) {\n const chunk = encodedKeys.slice(i, i + MAX_KEYS_PER_CHUNK);\n const placeholders = chunk.map(() => '?').join(', ');\n const result = run(\n database,\n `DELETE FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND key_tag IN (${placeholders})\n AND (expires_at IS NULL OR expires_at > ?)`,\n [name, table, ...chunk, Date.now()],\n ) as { changes?: number } | undefined;\n\n deleted += result?.changes ?? 0;\n }\n\n return deleted;\n },\n async get(table, key) {\n return getRecord(table, key);\n },\n async getAll(table) {\n return getAllLive<RecordOf<S, typeof table>>(database, name, table);\n },\n async getAllKeys(table) {\n return (await core.getAll(table)).map((record) => getRecordKey(schema, table, record));\n },\n async getMany(table, keys) {\n return keys.map((key) => getRecord(table, key));\n },\n async has(table, key) {\n return getRecord(table, key) !== undefined;\n },\n async pruneAllExpired() {\n const results: Record<string, number> = {};\n\n for (const table of Object.keys(schema)) {\n results[table] = await core.pruneExpiredInTable(table as K);\n }\n\n return results;\n },\n async pruneExpiredInTable(table) {\n const beforeRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const before = beforeRow?.count;\n\n if (typeof before !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n deleteExpired(database, name, table);\n\n const afterRow = get(\n database,\n `SELECT COUNT(*) AS count FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ?`,\n [name, table],\n );\n const after = afterRow?.count;\n\n if (typeof after !== 'number') throw new VaultError('SQLite storage returned an invalid count');\n\n return before - after;\n },\n async put(table, value, ttl) {\n const key = getRecordKey(schema, table, value);\n const columns = toKeyColumns(key);\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [name, table, columns.encoded, columns.kind, columns.number, columns.string, encodeJson(value), expiresAt],\n );\n },\n async putAll(table, values, ttl) {\n if (values.length === 0) return;\n\n const expiresAt = ttl === undefined ? null : Date.now() + ttl;\n const encodedJsonValues = values.map((v) => encodeJson(v));\n const columnsList = values.map((v) => toKeyColumns(getRecordKey(schema, table, v)));\n\n const writeAll = () => {\n for (let i = 0; i < values.length; i++) {\n const columns = columnsList[i];\n run(\n database,\n `INSERT INTO ${RECORDS_TABLE}\n (namespace, table_name, key_tag, key_kind, key_number, key_string, value_json, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(namespace, table_name, key_tag) DO UPDATE SET\n key_kind = excluded.key_kind,\n key_number = excluded.key_number,\n key_string = excluded.key_string,\n value_json = excluded.value_json,\n expires_at = excluded.expires_at`,\n [\n name,\n table,\n columns.encoded,\n columns.kind,\n columns.number,\n columns.string,\n encodedJsonValues[i],\n expiresAt,\n ],\n );\n }\n };\n\n if (inTransaction) {\n writeAll();\n return;\n }\n\n database.exec('BEGIN');\n try {\n writeAll();\n database.exec('COMMIT');\n } catch (error) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite putAll rollback failed', { cause: rollbackError });\n }\n throw error;\n }\n },\n };\n\n return core;\n}\n\n/**\n * Creates a SQLite-backed Vault store. The connection is caller-owned unless\n * `closeOnDispose` is explicitly enabled.\n */\nexport function createSQLite<S extends AnySchema>(options: SQLiteVaultOptions<S>): TransactionalVaultStore<S> {\n const { closeOnDispose = false, database, name, schema, validators } = options;\n\n assertName(name);\n\n const state = getConnectionState(database);\n const namespaceReady = state.executor.run(async () => {\n await state.initialized;\n initializeNamespace(database, name);\n });\n let ownListener: ConnectionListener | undefined;\n\n const directCore = createDirectCore(database, name, schema);\n const withConnection = <T>(work: () => Promise<T>): Promise<T> =>\n state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n return work();\n });\n const guardedCore = Object.fromEntries(\n Object.entries(directCore).map(([method, implementation]) => [\n method,\n (...arguments_: unknown[]) => {\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return withConnection(() => (implementation as (...args: unknown[]) => Promise<unknown>)(...arguments_));\n },\n ]),\n ) as StorageBackend<S>;\n\n let batch: BatchImpl<S> | undefined;\n const adapter = buildAdapterOps(schema, guardedCore, {\n onCrossTabMessage(notify) {\n const listener: ConnectionListener = (eventName, table) => {\n if (eventName === name && Object.hasOwn(schema, table)) notify(table as keyof S & string);\n };\n\n ownListener = listener;\n state.listeners.add(listener);\n\n return () => {\n state.listeners.delete(listener);\n\n if (ownListener === listener) ownListener = undefined;\n };\n },\n onMutation(table) {\n for (const listener of state.listeners) {\n if (listener !== ownListener) listener(name, table);\n }\n },\n onTransactions: (deps) => {\n batch = async (tables, fn) => {\n assertBatchTables(tables);\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n return state.executor.run(async () => {\n await state.initialized;\n await namespaceReady;\n\n const dirtyTables = new Set<keyof S & string>();\n const txCore = createDirectCore<S, keyof S & string>(database, name, schema, true);\n const tx = buildTxContext(\n schema,\n txCore,\n (table) => dirtyTables.add(table),\n deps.validate,\n new Set<string>(tables),\n );\n let transactionStarted = false;\n let committed = false;\n\n state.batchActive = true;\n\n try {\n database.exec('BEGIN IMMEDIATE');\n transactionStarted = true;\n\n const result = await fn(tx);\n\n database.exec('COMMIT');\n committed = true;\n\n for (const table of dirtyTables) {\n deps.notifyMutation(table);\n }\n\n return result;\n } catch (error) {\n if (transactionStarted && !committed) {\n try {\n database.exec('ROLLBACK');\n } catch (rollbackError) {\n throw new VaultError('SQLite batch rollback failed', { cause: rollbackError });\n }\n }\n\n throw error;\n } finally {\n state.batchActive = false;\n }\n });\n };\n },\n schema,\n validators,\n });\n\n if (!batch) throw new VaultError('SQLite transaction capability was not initialized');\n\n const store: TransactionalVaultStore<S> = {\n ...adapter,\n batch,\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (adapter.disposed) throw new VaultDisposedError(`\"${name}\" is disposed`);\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<RecordOf<S, K>> {\n let completed = false;\n let lastRowId = 0;\n let lease: (() => void) | undefined;\n let rows: readonly SQLiteRow[] = [];\n let index = 0;\n\n const release = (): void => {\n lease?.();\n lease = undefined;\n };\n const loadNextPage = (): void => {\n rows = all(\n database,\n `SELECT rowid AS row_id, expires_at, value_json\n FROM ${RECORDS_TABLE}\n WHERE namespace = ? AND table_name = ? AND rowid > ?\n ORDER BY rowid\n LIMIT ?`,\n [name, table, lastRowId, ITERATION_PAGE_SIZE],\n );\n index = 0;\n };\n\n return {\n async next(): Promise<IteratorResult<RecordOf<S, K>>> {\n if (completed) return { done: true, value: undefined };\n\n if (state.batchActive) {\n throw new VaultError(\n 'cannot call a SQLite store sharing this connection from batch(); use the transaction context instead',\n );\n }\n\n try {\n if (!lease) {\n lease = await state.executor.acquire();\n await state.initialized;\n await namespaceReady;\n }\n\n while (true) {\n if (index >= rows.length) {\n loadNextPage();\n\n if (rows.length === 0) {\n completed = true;\n release();\n\n return { done: true, value: undefined };\n }\n }\n\n const row = rows[index++];\n const stored = getStoredRow(row);\n\n lastRowId = stored.rowId;\n\n const value = decodeLiveRecord<RecordOf<S, K>>(database, row);\n\n if (value !== undefined) return { done: false, value };\n }\n } catch (error) {\n completed = true;\n release();\n\n throw error;\n }\n },\n async return(value?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n return { done: true, value: value as RecordOf<S, K> };\n },\n async throw(error?: unknown): Promise<IteratorResult<RecordOf<S, K>>> {\n completed = true;\n release();\n\n throw error;\n },\n };\n },\n };\n },\n };\n\n if (closeOnDispose) {\n const dispose = store.dispose.bind(store);\n let closePromise: Promise<void> | undefined;\n\n store.dispose = async (): Promise<void> => {\n await dispose();\n closePromise ??= state.executor.run(() => database.close?.());\n await closePromise;\n };\n store[Symbol.asyncDispose] = async (): Promise<void> => {\n await store.dispose();\n };\n }\n\n return store;\n}\n"],"mappings":"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"}
|
package/dist/indexeddb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { MigrationContext, MigrationFn, MigrationStep } from './adapters/indexeddb';
|
|
2
2
|
export { createIndexedDB, defineMigration } from './adapters/indexeddb';
|
|
3
3
|
export type { TransactionContext } from './types';
|
|
4
4
|
//# sourceMappingURL=indexeddb.d.ts.map
|
package/dist/indexeddb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA,YAAY,
|
|
1
|
+
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/sqlite.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { SQLiteDatabase, SQLiteParameter, SQLiteStatement, SQLiteVaultOptions,
|
|
1
|
+
export type { SQLiteDatabase, SQLiteParameter, SQLiteStatement, SQLiteVaultOptions, TransactionContext, } from './adapters/sqlite';
|
|
2
2
|
export { createSQLite } from './adapters/sqlite';
|
|
3
3
|
//# sourceMappingURL=sqlite.d.ts.map
|
package/dist/sqlite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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 }
|
|
1
|
+
{"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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":"uDAyHA,SAAgB,EACd,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.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 type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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 }
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { VaultError } from './errors';\nimport type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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":";;;AAyHA,SAAgB,EACd,GACA,IAA4E,CAAC,GACxD;CACrB,IAAM,EAAE,eAAY,eAAY;CAIhC,IAFI,MAAe,KAAA,KAAW,EAAqB,GAAY,mBAAmB,GAE9E,GAAS;EACX,IAAM,oBAAO,IAAI,IAAY;EAE7B,KAAK,IAAM,KAAS,GAAS;GAC3B,IAAI,EAAK,IAAI,CAAK,GAChB,MAAM,IAAI,EAAW,iBAAiB,EAAM,wBAAwB;GAGtE,EAAK,IAAI,CAAK;EAChB;CACF;CAEA,OAAO;EAAE;EAAY;EAAS;CAAI;AACpC"}
|
package/dist/vault.cjs.map
CHANGED
|
@@ -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 type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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 } as unknown as SchemaEntry<T, Key>;\n}\n"],"mappings":"mEAIA,IAAa,EAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAyC,CAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,EAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,EAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,EAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,EAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,EAAqB,EAAI,IAAM,aAAa,CACtE,EAKA,SAAgB,EAAU,EAAwC,CAChE,OAAO,IAAc,IAAA,IAAa,KAAK,IAAI,GAAK,CAClD,CAGA,SAAgB,EAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,EAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCgGA,SAAgB,EACd,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":"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 type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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,EAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAyC,CAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,EAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,EAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,EAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,EAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,EAAqB,EAAI,IAAM,aAAa,CACtE,EAKA,SAAgB,EAAU,EAAwC,CAChE,OAAO,IAAc,IAAA,IAAa,KAAK,IAAI,GAAK,CAClD,CAGA,SAAgB,EAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,EAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCgGA,SAAgB,EACd,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"}
|
package/dist/vault.iife.js.map
CHANGED
|
@@ -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 type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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 } as unknown as SchemaEntry<T, Key>;\n}\n"],"mappings":"oFAIA,IAAa,EAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAyC,CAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,EAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,EAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,EAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,EAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,EAAqB,EAAI,IAAM,aAAa,CACtE,EAKA,SAAgB,EAAU,EAAwC,CAChE,OAAO,IAAc,IAAA,IAAa,KAAK,IAAI,GAAK,CAClD,CAGA,SAAgB,EAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,EAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCgGA,SAAgB,EACd,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":"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 type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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,EAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAyC,CAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,EAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,EAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,EAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,EAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,EAAqB,EAAI,IAAM,aAAa,CACtE,EAKA,SAAgB,EAAU,EAAwC,CAChE,OAAO,IAAc,IAAA,IAAa,KAAK,IAAI,GAAK,CAClD,CAGA,SAAgB,EAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,EAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCgGA,SAAgB,EACd,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"}
|
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 type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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 } as unknown as SchemaEntry<T, Key>;\n}\n"],"mappings":"AAIA,IAAa,EAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAyC,CAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,EAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,EAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,EAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,EAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,EAAqB,EAAI,IAAM,aAAa,CACtE,EAKA,SAAgB,EAAU,EAAwC,CAChE,OAAO,IAAc,IAAA,IAAa,KAAK,IAAI,GAAK,CAClD,CAGA,SAAgB,EAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,EAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCgGA,SAAgB,EACd,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":"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 type { QueryBuilder } from './query';\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\nexport type RecordValidator<T> = {\n parse(value: unknown): T;\n};\n\nexport type TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\nexport type Observer<T> = (records: T[]) => void;\nexport type Unsubscribe = () => void;\n\n/** Shared factory options. Values always use Vault's fixed `{ value, expiresAt? }` envelope. */\nexport type BaseAdapterOptions<S extends AnySchema> = {\n schema: S;\n validators?: TableValidators<S>;\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> = {\n clear<T extends K>(table: T): Promise<void>;\n count<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<number>;\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 getMany<T extends K>(table: T, keys: 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 put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: number): Promise<void>;\n query<T extends K>(table: T): QueryBuilder<RecordOf<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 fn: (existing: RecordOf<S, T> | undefined) => RecordOf<S, T>,\n ttl?: number,\n ): Promise<RecordOf<S, T>>;\n};\n\n/** Portable API returned by memory and Web Storage factories. */\nexport interface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\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 getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<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<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Atomic, scoped transactions and lazy iteration supplied by storage engines that support them. */\nexport interface TransactionalVaultStore<S extends AnySchema> extends VaultStore<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\n/** Define a typed table whose primary-key field holds a portable Vault key. */\nexport function table<T extends object, Key extends keyof T & string = keyof T & string>(\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,EAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAU,sBAAuB,EAAqB,CAChE,MAAM,EAAS,CAAI,CACrB,CACF,EAGa,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAyC,CAAW,CAAC,ECxBxC,EAAM,CACjB,KAAO,GAAc,EAAqB,EAAI,MAAY,UAAU,EACpE,MAAQ,GAAc,EAAqB,EAAI,KAAW,WAAW,EACrE,QAAU,GAAc,EAAqB,EAAI,IAAQ,aAAa,EACtE,GAAK,GAAc,EAAqB,EAAG,QAAQ,EACnD,QAAU,GAAc,EAAqB,EAAI,IAAM,aAAa,CACtE,EAKA,SAAgB,EAAU,EAAwC,CAChE,OAAO,IAAc,IAAA,IAAa,KAAK,IAAI,GAAK,CAClD,CAGA,SAAgB,EAAqB,EAAe,EAAwB,CAC1E,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EACtC,MAAM,IAAI,EAAW,GAAG,EAAO,+CAA+C,OAAO,CAAK,GAAG,EAG/F,OAAO,CACT,CCgGA,SAAgB,EACd,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"}
|