@vielzeug/vault 2.1.0 → 2.3.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.
Files changed (87) hide show
  1. package/README.md +0 -19
  2. package/dist/_dev.cjs +1 -1
  3. package/dist/_dev.cjs.map +1 -1
  4. package/dist/_dev.js +2 -3
  5. package/dist/_dev.js.map +1 -1
  6. package/dist/adapter-core.cjs +1 -1
  7. package/dist/adapter-core.cjs.map +1 -1
  8. package/dist/adapter-core.d.ts.map +1 -1
  9. package/dist/adapter-core.js +2 -2
  10. package/dist/adapter-core.js.map +1 -1
  11. package/dist/adapters/indexeddb.cjs +1 -1
  12. package/dist/adapters/indexeddb.cjs.map +1 -1
  13. package/dist/adapters/indexeddb.d.ts +46 -2
  14. package/dist/adapters/indexeddb.d.ts.map +1 -1
  15. package/dist/adapters/indexeddb.js +76 -56
  16. package/dist/adapters/indexeddb.js.map +1 -1
  17. package/dist/adapters/memory.cjs.map +1 -1
  18. package/dist/adapters/memory.d.ts.map +1 -1
  19. package/dist/adapters/memory.js.map +1 -1
  20. package/dist/adapters/sqlite.cjs +1 -1
  21. package/dist/adapters/sqlite.cjs.map +1 -1
  22. package/dist/adapters/sqlite.d.ts +2 -2
  23. package/dist/adapters/sqlite.d.ts.map +1 -1
  24. package/dist/adapters/sqlite.js +1 -1
  25. package/dist/adapters/sqlite.js.map +1 -1
  26. package/dist/adapters/webstorage.cjs +1 -1
  27. package/dist/adapters/webstorage.cjs.map +1 -1
  28. package/dist/adapters/webstorage.d.ts +1 -1
  29. package/dist/adapters/webstorage.d.ts.map +1 -1
  30. package/dist/adapters/webstorage.js +1 -1
  31. package/dist/adapters/webstorage.js.map +1 -1
  32. package/dist/errors.cjs +1 -1
  33. package/dist/errors.cjs.map +1 -1
  34. package/dist/errors.d.ts +0 -1
  35. package/dist/errors.d.ts.map +1 -1
  36. package/dist/errors.js +1 -4
  37. package/dist/errors.js.map +1 -1
  38. package/dist/index.cjs +1 -1
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +3 -3
  42. package/dist/indexeddb.cjs +1 -1
  43. package/dist/indexeddb.d.ts +3 -4
  44. package/dist/indexeddb.d.ts.map +1 -1
  45. package/dist/indexeddb.js +1 -2
  46. package/dist/internal.cjs.map +1 -1
  47. package/dist/internal.d.ts.map +1 -1
  48. package/dist/internal.js.map +1 -1
  49. package/dist/prune.cjs +1 -1
  50. package/dist/prune.cjs.map +1 -1
  51. package/dist/prune.d.ts +8 -19
  52. package/dist/prune.d.ts.map +1 -1
  53. package/dist/prune.js +9 -13
  54. package/dist/prune.js.map +1 -1
  55. package/dist/query.cjs +1 -1
  56. package/dist/query.cjs.map +1 -1
  57. package/dist/query.d.ts +5 -11
  58. package/dist/query.d.ts.map +1 -1
  59. package/dist/query.js +1 -4
  60. package/dist/query.js.map +1 -1
  61. package/dist/sqlite.d.ts +1 -1
  62. package/dist/sqlite.d.ts.map +1 -1
  63. package/dist/ttl.cjs +1 -1
  64. package/dist/ttl.cjs.map +1 -1
  65. package/dist/ttl.d.ts +8 -19
  66. package/dist/ttl.d.ts.map +1 -1
  67. package/dist/ttl.js +5 -5
  68. package/dist/ttl.js.map +1 -1
  69. package/dist/types.cjs +1 -1
  70. package/dist/types.cjs.map +1 -1
  71. package/dist/types.d.ts +28 -40
  72. package/dist/types.d.ts.map +1 -1
  73. package/dist/types.js +14 -19
  74. package/dist/types.js.map +1 -1
  75. package/dist/vault.cjs +1 -1
  76. package/dist/vault.cjs.map +1 -1
  77. package/dist/vault.iife.js +1 -1
  78. package/dist/vault.iife.js.map +1 -1
  79. package/dist/vault.js +1 -1
  80. package/dist/vault.js.map +1 -1
  81. package/package.json +44 -39
  82. package/dist/migration.cjs +0 -2
  83. package/dist/migration.cjs.map +0 -1
  84. package/dist/migration.d.ts +0 -37
  85. package/dist/migration.d.ts.map +0 -1
  86. package/dist/migration.js +0 -25
  87. package/dist/migration.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.js","names":[],"sources":["../../src/adapters/sqlite.ts"],"sourcesContent":["import type {\n AnySchema,\n BaseAdapterOptions,\n IterableVaultStore,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n} from '../types';\n\nimport {\n assertBatchTables,\n buildAdapterOps,\n buildTxContext,\n type BatchImpl,\n type StorageBackend,\n} from '../adapter-core';\nimport { VaultDisposedError, VaultError } from '../errors';\nimport { encodeVaultKey, getRecordKey } from '../internal';\nimport { isExpired } from '../ttl';\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 interface SQLiteVaultStore<S extends AnySchema> extends TransactionalVaultStore<S>, IterableVaultStore<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))\n throw new VaultError(`SQLite serialization failed at ${path}: circular references are not supported`);\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);\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 (VaultError.is(error)) 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): 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 live = getRecord(table, key) !== undefined;\n const columns = toKeyColumns(key);\n\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ? AND key_tag = ?`, [\n name,\n table,\n columns.encoded,\n ]);\n\n return live;\n },\n async deleteMany(table, keys) {\n let deleted = 0;\n\n for (const key of keys) {\n if (await core.delete(table, key)) deleted += 1;\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 getByKeyRange(table, range) {\n if (range.type === 'eq')\n return getAllLive<RecordOf<S, typeof table>>(database, name, table, ' AND key_tag = ?', [\n toKeyColumns(range.value as KeyOf<S, typeof table>).encoded,\n ]);\n\n if (range.type === 'between') {\n if (\n typeof range.lower !== typeof range.upper ||\n (typeof range.lower !== 'number' && typeof range.lower !== 'string')\n ) {\n return getAllLive<RecordOf<S, typeof table>>(database, name, table);\n }\n\n const column = typeof range.lower === 'number' ? 'key_number' : 'key_string';\n\n return getAllLive<RecordOf<S, typeof table>>(\n database,\n name,\n table,\n ` AND key_kind = ? AND ${column} >= ? AND ${column} <= ?`,\n [typeof range.lower === 'number' ? 'number' : 'string', range.lower, range.upper as string | number],\n );\n }\n\n return getAllLive<RecordOf<S, typeof table>>(\n database,\n name,\n table,\n ' AND key_kind = ? AND substr(key_string, 1, length(?)) = ?',\n ['string', range.prefix, range.prefix],\n );\n },\n async getMany(table, keys) {\n return keys.map((key) => getRecord(table, key));\n },\n async getRawCount(table) {\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 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 before = await core.getRawCount!(table);\n\n deleteExpired(database, name, table);\n\n return before - (await core.getRawCount!(table));\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 for (const value of values) {\n await core.put(table, value, ttl);\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, logger, name, onMetrics, 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 logger,\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 onMetrics,\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);\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 IterableVaultStore,\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 interface SQLiteVaultStore<S extends AnySchema> extends TransactionalVaultStore<S>, IterableVaultStore<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))\n throw new VaultError(`SQLite serialization failed at ${path}: circular references are not supported`);\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);\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): 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 live = getRecord(table, key) !== undefined;\n const columns = toKeyColumns(key);\n\n run(database, `DELETE FROM ${RECORDS_TABLE} WHERE namespace = ? AND table_name = ? AND key_tag = ?`, [\n name,\n table,\n columns.encoded,\n ]);\n\n return live;\n },\n async deleteMany(table, keys) {\n let deleted = 0;\n\n for (const key of keys) {\n if (await core.delete(table, key)) deleted += 1;\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 getByKeyRange(table, range) {\n if (range.type === 'eq')\n return getAllLive<RecordOf<S, typeof table>>(database, name, table, ' AND key_tag = ?', [\n toKeyColumns(range.value as KeyOf<S, typeof table>).encoded,\n ]);\n\n if (range.type === 'between') {\n if (\n typeof range.lower !== typeof range.upper ||\n (typeof range.lower !== 'number' && typeof range.lower !== 'string')\n ) {\n return getAllLive<RecordOf<S, typeof table>>(database, name, table);\n }\n\n const column = typeof range.lower === 'number' ? 'key_number' : 'key_string';\n\n return getAllLive<RecordOf<S, typeof table>>(\n database,\n name,\n table,\n ` AND key_kind = ? AND ${column} >= ? AND ${column} <= ?`,\n [typeof range.lower === 'number' ? 'number' : 'string', range.lower, range.upper as string | number],\n );\n }\n\n return getAllLive<RecordOf<S, typeof table>>(\n database,\n name,\n table,\n ' AND key_kind = ? AND substr(key_string, 1, length(?)) = ?',\n ['string', range.prefix, range.prefix],\n );\n },\n async getMany(table, keys) {\n return keys.map((key) => getRecord(table, key));\n },\n async getRawCount(table) {\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 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 before = await core.getRawCount!(table);\n\n deleteExpired(database, name, table);\n\n return before - (await core.getRawCount!(table));\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 for (const value of values) {\n await core.put(table, value, ttl);\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, logger, name, onMetrics, 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 logger,\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 onMetrics,\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);\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,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("../ttl.cjs"),n=require("../internal.cjs"),r=require("../adapter-core.cjs");var i=new Set([`QuotaExceededError`,`NS_ERROR_DOM_QUOTA_REACHED`]);function a(a){let{getStorage:o,logger:s,name:c,onMetrics:l,onQuotaExceeded:u,schema:d,storageLabel:f,validators:p}=a,m;try{m=o()}catch(t){throw new e.VaultError(`${f} is not available in this environment (private browsing or sandboxed iframe?)`,{cause:t})}let h=()=>m,g=new Map(Object.keys(d).map(e=>[e,n.encodeStorageTablePrefix(c,e)])),_=t=>{let n=g.get(t);if(!n)throw new e.VaultError(`table "${t}" not in schema`);return n},v=(t,n,r)=>{try{h().setItem(n,JSON.stringify(r))}catch(n){if(n instanceof DOMException&&i.has(n.name)){let r=new e.VaultQuotaError(`${f} quota exceeded while writing record`,{cause:n});if(u?.(t,r)===`ignore`)return;throw r}throw n}},y=new Set;(()=>{let e=n.encodeDbPrefix(c);for(let t=0;t<m.length;t++){let n=m.key(t);n!==null&&n.startsWith(e)&&y.add(n)}})();let b=e=>{let n=h().getItem(e);if(n)try{let e=t.parseStored(JSON.parse(n));return!e||t.isExpired(e.expiresAt)?void 0:e.value}catch{return}},x=e=>{h().removeItem(e),y.delete(e)};return r.buildAdapterOps(d,{async clear(e){let t=h(),n=_(e),r=[];for(let e of y)e.startsWith(n)&&r.push(e);for(let e of r)t.removeItem(e),y.delete(e)},async count(e){let t=_(e),n=[],r=0;for(let e of y)e.startsWith(t)&&(b(e)===void 0?n.push(e):r+=1);for(let e of n)x(e);return r},async delete(e,t){let r=n.encodeStorageKey(c,e,t);return b(r)===void 0?(y.has(r)&&x(r),!1):(x(r),!0)},async deleteMany(e,t){let r=0;for(let i of t){let t=n.encodeStorageKey(c,e,i);b(t)===void 0?y.has(t)&&x(t):(x(t),r+=1)}return r},async get(e,t){let r=n.encodeStorageKey(c,e,t),i=b(r);return i===void 0&&y.has(r)&&x(r),i},async getAll(e){let t=[],n=[],r=_(e);for(let e of y){if(!e.startsWith(r))continue;let i=b(e);if(i===void 0){n.push(e);continue}t.push(i)}for(let e of n)x(e);return t},async getAllKeys(e){let t=_(e),n=[],r=[];for(let i of y){if(!i.startsWith(t))continue;let a=b(i);if(a===void 0){r.push(i);continue}n.push(a[d[e].key])}for(let e of r)x(e);return n},async getRawCount(e){let t=_(e),n=0;for(let e of y)e.startsWith(t)&&(n+=1);return n},async has(e,t){let r=n.encodeStorageKey(c,e,t),i=b(r);return i===void 0&&y.has(r)&&x(r),i!==void 0},async pruneExpiredInTable(e){let n=_(e),r=[];for(let e of y){if(!e.startsWith(n))continue;let i=h().getItem(e);if(i===null){r.push(e);continue}try{let n=t.parseStored(JSON.parse(i));(!n||t.isExpired(n.expiresAt))&&r.push(e)}catch{r.push(e)}}for(let e of r)x(e);return r.length},async put(e,t,r){let i=n.encodeStorageKey(c,e,n.getRecordKey(d,e,t)),a=r===void 0?void 0:Date.now()+r;v(e,i,a===void 0?{value:t}:{expiresAt:a,value:t}),y.add(i)},async putAll(e,t,r){let i=r===void 0?void 0:Date.now()+r;for(let r of t){let t=n.encodeStorageKey(c,e,n.getRecordKey(d,e,r));v(e,t,i===void 0?{value:r}:{expiresAt:i,value:r}),y.add(t)}}},{logger:s,onCrossTabMessage(e){if(typeof window>`u`||typeof window.addEventListener!=`function`)return;let t=t=>{if(t.storageArea&&t.storageArea!==m)return;if(t.key===null){y.clear();for(let t of Object.keys(d))e(t);return}let r=n.decodeStorageTableFromKey(c,t.key);r&&Object.hasOwn(d,r)&&(t.newValue===null?y.delete(t.key):y.add(t.key),e(r))};return window.addEventListener(`storage`,t),()=>window.removeEventListener(`storage`,t)},onMetrics:l,schema:d,validators:p})}function o(e){return a({...e,getStorage:()=>typeof window<`u`?window.localStorage:localStorage,storageLabel:`localStorage`})}function s(e){return a({...e,getStorage:()=>typeof window<`u`?window.sessionStorage:sessionStorage,storageLabel:`sessionStorage`})}exports.createLocalStorage=o,exports.createSessionStorage=s;
1
+ const e=require("../errors.cjs"),t=require("../ttl.cjs"),n=require("../internal.cjs"),r=require("../adapter-core.cjs");var i=new Set([`QuotaExceededError`,`NS_ERROR_DOM_QUOTA_REACHED`]);function a(a){let{getStorage:o,logger:s,name:c,onMetrics:l,onQuotaExceeded:u,schema:d,storageLabel:f,validators:p}=a,m;try{m=o()}catch(t){throw new e.VaultError(`${f} is not available in this environment (private browsing or sandboxed iframe?)`,{cause:t})}let h=()=>m,g=new Map(Object.keys(d).map(e=>[e,n.encodeStorageTablePrefix(c,e)])),_=t=>{let n=g.get(t);if(!n)throw new e.VaultError(`table "${t}" not in schema`);return n},v=(t,n,r)=>{try{h().setItem(n,JSON.stringify(r))}catch(n){if(n instanceof DOMException&&i.has(n.name)){let r=new e.VaultQuotaError(`${f} quota exceeded while writing record`,{cause:n});if(u?.(t,r)===`ignore`)return;throw r}throw n}},y=new Set;(()=>{let e=n.encodeDbPrefix(c);for(let t=0;t<m.length;t++){let n=m.key(t);n?.startsWith(e)&&y.add(n)}})();let b=e=>{let n=h().getItem(e);if(n)try{let e=t.parseStored(JSON.parse(n));return!e||t.isExpired(e.expiresAt)?void 0:e.value}catch{return}},x=e=>{h().removeItem(e),y.delete(e)};return r.buildAdapterOps(d,{async clear(e){let t=h(),n=_(e),r=[];for(let e of y)e.startsWith(n)&&r.push(e);for(let e of r)t.removeItem(e),y.delete(e)},async count(e){let t=_(e),n=[],r=0;for(let e of y)e.startsWith(t)&&(b(e)===void 0?n.push(e):r+=1);for(let e of n)x(e);return r},async delete(e,t){let r=n.encodeStorageKey(c,e,t);return b(r)===void 0?(y.has(r)&&x(r),!1):(x(r),!0)},async deleteMany(e,t){let r=0;for(let i of t){let t=n.encodeStorageKey(c,e,i);b(t)===void 0?y.has(t)&&x(t):(x(t),r+=1)}return r},async get(e,t){let r=n.encodeStorageKey(c,e,t),i=b(r);return i===void 0&&y.has(r)&&x(r),i},async getAll(e){let t=[],n=[],r=_(e);for(let e of y){if(!e.startsWith(r))continue;let i=b(e);if(i===void 0){n.push(e);continue}t.push(i)}for(let e of n)x(e);return t},async getAllKeys(e){let t=_(e),n=[],r=[];for(let i of y){if(!i.startsWith(t))continue;let a=b(i);if(a===void 0){r.push(i);continue}n.push(a[d[e].key])}for(let e of r)x(e);return n},async getRawCount(e){let t=_(e),n=0;for(let e of y)e.startsWith(t)&&(n+=1);return n},async has(e,t){let r=n.encodeStorageKey(c,e,t),i=b(r);return i===void 0&&y.has(r)&&x(r),i!==void 0},async pruneExpiredInTable(e){let n=_(e),r=[];for(let e of y){if(!e.startsWith(n))continue;let i=h().getItem(e);if(i===null){r.push(e);continue}try{let n=t.parseStored(JSON.parse(i));(!n||t.isExpired(n.expiresAt))&&r.push(e)}catch{r.push(e)}}for(let e of r)x(e);return r.length},async put(e,t,r){let i=n.encodeStorageKey(c,e,n.getRecordKey(d,e,t)),a=r===void 0?void 0:Date.now()+r;v(e,i,a===void 0?{value:t}:{expiresAt:a,value:t}),y.add(i)},async putAll(e,t,r){let i=r===void 0?void 0:Date.now()+r;for(let r of t){let t=n.encodeStorageKey(c,e,n.getRecordKey(d,e,r));v(e,t,i===void 0?{value:r}:{expiresAt:i,value:r}),y.add(t)}}},{logger:s,onCrossTabMessage(e){if(typeof window>`u`||typeof window.addEventListener!=`function`)return;let t=t=>{if(t.storageArea&&t.storageArea!==m)return;if(t.key===null){y.clear();for(let t of Object.keys(d))e(t);return}let r=n.decodeStorageTableFromKey(c,t.key);r&&Object.hasOwn(d,r)&&(t.newValue===null?y.delete(t.key):y.add(t.key),e(r))};return window.addEventListener(`storage`,t),()=>window.removeEventListener(`storage`,t)},onMetrics:l,schema:d,validators:p})}function o(e){return a({...e,getStorage:()=>typeof window<`u`?window.localStorage:localStorage,storageLabel:`localStorage`})}function s(e){return a({...e,getStorage:()=>typeof window<`u`?window.sessionStorage:sessionStorage,storageLabel:`sessionStorage`})}exports.createLocalStorage=o,exports.createSessionStorage=s;
2
2
  //# sourceMappingURL=webstorage.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"webstorage.cjs","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, TtlMs, VaultStore } from '../types';\n\nimport { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key !== null && key.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: TtlMs): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: TtlMs): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"webstorage.cjs","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\nimport type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, VaultStore } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import type { AnySchema, BaseAdapterOptions, VaultStore } from '../types';
2
1
  import { VaultQuotaError } from '../errors';
2
+ import type { AnySchema, BaseAdapterOptions, VaultStore } from '../types';
3
3
  type WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {
4
4
  name: string;
5
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"webstorage.d.ts","sourceRoot":"","sources":["../../src/adapters/webstorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAA0B,UAAU,EAAE,MAAM,UAAU,CAAC;AAGlG,OAAO,EAAc,eAAe,EAAE,MAAM,WAAW,CAAC;AAaxD,KAAK,iBAAiB,CAAC,CAAC,SAAS,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG;IACpE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,KAAK,QAAQ,GAAG,OAAO,CAAC;CAClF,CAAC;AA2VF,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAMpG;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAMtG"}
1
+ {"version":3,"file":"webstorage.d.ts","sourceRoot":"","sources":["../../src/adapters/webstorage.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,eAAe,EAAE,MAAM,WAAW,CAAC;AASxD,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAmB,UAAU,EAAE,MAAM,UAAU,CAAC;AAK3F,KAAK,iBAAiB,CAAC,CAAC,SAAS,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG;IACpE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,KAAK,QAAQ,GAAG,OAAO,CAAC;CAClF,CAAC;AA2VF,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAMpG;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAMtG"}
@@ -31,7 +31,7 @@ function d(d) {
31
31
  let e = a(m);
32
32
  for (let t = 0; t < b.length; t++) {
33
33
  let n = b.key(t);
34
- n !== null && n.startsWith(e) && T.add(n);
34
+ n?.startsWith(e) && T.add(n);
35
35
  }
36
36
  })();
37
37
  let E = (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"webstorage.js","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, TtlMs, VaultStore } from '../types';\n\nimport { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key !== null && key.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: TtlMs): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: TtlMs): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"webstorage.js","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\nimport type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, VaultStore } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{constructor(e=`adapter is disposed`,t){super(e,t)}},n=class extends e{},r=class extends e{},i=class extends e{};exports.VaultDisposedError=t,exports.VaultError=e,exports.VaultMigrationError=i,exports.VaultQuotaError=r,exports.VaultScopeError=n;
1
+ var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{constructor(e=`adapter is disposed`,t){super(e,t)}},n=class extends e{},r=class extends e{},i=class extends e{};exports.VaultDisposedError=t,exports.VaultError=e,exports.VaultMigrationError=i,exports.VaultQuotaError=r,exports.VaultScopeError=n;
2
2
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.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 static is(err: unknown): err is VaultError {\n return err instanceof VaultError;\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"],"mappings":"AAIA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KAEvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,OAAO,GAAG,EAAiC,CACzC,OAAO,aAAe,CACxB,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"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.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"],"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"}
package/dist/errors.d.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  */
5
5
  export declare class VaultError extends Error {
6
6
  constructor(message: string, opts?: ErrorOptions);
7
- static is(err: unknown): err is VaultError;
8
7
  }
9
8
  /** Thrown when an operation is attempted on a disposed adapter or observer hub. */
10
9
  export declare class VaultDisposedError extends VaultError {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;IAOhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU;CAG3C;AAED,mFAAmF;AACnF,qBAAa,kBAAmB,SAAQ,UAAU;gBACpC,OAAO,SAAwB,EAAE,IAAI,CAAC,EAAE,YAAY;CAGjE;AAED,mFAAmF;AACnF,qBAAa,eAAgB,SAAQ,UAAU;CAAG;AAElD,gEAAgE;AAChE,qBAAa,eAAgB,SAAQ,UAAU;CAAG;AAElD,4EAA4E;AAC5E,qBAAa,mBAAoB,SAAQ,UAAU;CAAG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAMjD;AAED,mFAAmF;AACnF,qBAAa,kBAAmB,SAAQ,UAAU;gBACpC,OAAO,SAAwB,EAAE,IAAI,CAAC,EAAE,YAAY;CAGjE;AAED,mFAAmF;AACnF,qBAAa,eAAgB,SAAQ,UAAU;CAAG;AAElD,gEAAgE;AAChE,qBAAa,eAAgB,SAAQ,UAAU;CAAG;AAElD,4EAA4E;AAC5E,qBAAa,mBAAoB,SAAQ,UAAU;CAAG"}
package/dist/errors.js CHANGED
@@ -1,11 +1,8 @@
1
1
  //#region src/errors.ts
2
- var e = class e extends Error {
2
+ var e = class extends Error {
3
3
  constructor(e, t) {
4
4
  super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
5
5
  }
6
- static is(t) {
7
- return t instanceof e;
8
- }
9
6
  }, t = class extends e {
10
7
  constructor(e = "adapter is disposed", t) {
11
8
  super(e, t);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.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 static is(err: unknown): err is VaultError {\n return err instanceof VaultError;\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"],"mappings":";AAIA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAIhD,AAHA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MAEvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAiC;EACzC,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAwC,EAAW;CACjD,YAAY,IAAU,uBAAuB,GAAqB;EAChE,MAAM,GAAS,CAAI;CACrB;AACF,GAGa,IAAb,cAAqC,EAAW,CAAC,GAGpC,IAAb,cAAqC,EAAW,CAAC,GAGpC,IAAb,cAAyC,EAAW,CAAC"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.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"],"mappings":";AAIA,IAAa,IAAb,cAAgC,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAIhD,AAHA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MAEvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAGa,IAAb,cAAwC,EAAW;CACjD,YAAY,IAAU,uBAAuB,GAAqB;EAChE,MAAM,GAAS,CAAI;CACrB;AACF,GAGa,IAAb,cAAqC,EAAW,CAAC,GAGpC,IAAb,cAAqC,EAAW,CAAC,GAGpC,IAAb,cAAyC,EAAW,CAAC"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./prune.cjs"),n=require("./ttl.cjs"),r=require("./types.cjs");exports.VaultDisposedError=e.VaultDisposedError,exports.VaultError=e.VaultError,exports.VaultMigrationError=e.VaultMigrationError,exports.VaultQuotaError=e.VaultQuotaError,exports.VaultScopeError=e.VaultScopeError,exports.isExpired=n.isExpired,exports.scheduleExpiredPrune=t.scheduleExpiredPrune,exports.table=r.table,exports.ttl=n.ttl;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./ttl.cjs"),n=require("./prune.cjs"),r=require("./types.cjs");exports.VaultDisposedError=e.VaultDisposedError,exports.VaultError=e.VaultError,exports.VaultMigrationError=e.VaultMigrationError,exports.VaultQuotaError=e.VaultQuotaError,exports.VaultScopeError=e.VaultScopeError,exports.isExpired=t.isExpired,exports.scheduleExpiredPrune=n.scheduleExpiredPrune,exports.table=r.table,exports.ttl=t.ttl;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, VaultScopeError } from './errors';
2
- export type { QueryBuilder } from './query';
3
2
  export { scheduleExpiredPrune } from './prune';
3
+ export type { QueryBuilder } from './query';
4
4
  export { isExpired, ttl } from './ttl';
5
+ export type { AnySchema, BaseAdapterOptions, DebugInfo, DebugStats, IterableVaultStore, KeyOf, MetricsEvent, Observer, RecordOf, RecordValidator, SchemaEntry, TableValidators, TransactionalVaultStore, Unsubscribe, VaultKey, VaultLogger, VaultStore, } from './types';
5
6
  export { table } from './types';
6
- export type { AnySchema, BaseAdapterOptions, DebugInfo, DebugStats, IterableVaultStore, KeyOf, MetricsEvent, Observer, RecordOf, RecordValidator, SchemaEntry, TableBuilder, TableValidators, TransactionalVaultStore, TtlMs, Unsubscribe, VaultKey, VaultLogger, VaultStore, } from './types';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACjH,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,WAAW,EACX,YAAY,EACZ,eAAe,EACf,uBAAuB,EACvB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACjH,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,WAAW,EACX,eAAe,EACf,uBAAuB,EACvB,WAAW,EACX,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { VaultDisposedError as e, VaultError as t, VaultMigrationError as n, VaultQuotaError as r, VaultScopeError as i } from "./errors.js";
2
- import { scheduleExpiredPrune as a } from "./prune.js";
3
- import { isExpired as o, ttl as s } from "./ttl.js";
2
+ import { isExpired as a, ttl as o } from "./ttl.js";
3
+ import { scheduleExpiredPrune as s } from "./prune.js";
4
4
  import { table as c } from "./types.js";
5
- export { e as VaultDisposedError, t as VaultError, n as VaultMigrationError, r as VaultQuotaError, i as VaultScopeError, o as isExpired, a as scheduleExpiredPrune, c as table, s as ttl };
5
+ export { e as VaultDisposedError, t as VaultError, n as VaultMigrationError, r as VaultQuotaError, i as VaultScopeError, a as isExpired, s as scheduleExpiredPrune, c as table, o as ttl };
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/indexeddb.cjs"),t=require("./migration.cjs");exports.createIndexedDB=e.createIndexedDB,exports.defineMigration=t.defineMigration;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/indexeddb.cjs");exports.createIndexedDB=e.createIndexedDB,exports.defineMigration=e.defineMigration;
@@ -1,5 +1,4 @@
1
- export { createIndexedDB } from './adapters/indexeddb';
2
- export { defineMigration } from './migration';
3
- export type { MigrationStep } from './migration';
4
- export type { IndexedDbVaultStore, MigrationContext, MigrationFn, TransactionContext } from './types';
1
+ export type { IndexedDbVaultStore, MigrationContext, MigrationFn, MigrationStep, } from './adapters/indexeddb';
2
+ export { createIndexedDB, defineMigration } from './adapters/indexeddb';
3
+ export type { TransactionContext } from './types';
5
4
  //# sourceMappingURL=indexeddb.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
package/dist/indexeddb.js CHANGED
@@ -1,3 +1,2 @@
1
- import { createIndexedDB as e } from "./adapters/indexeddb.js";
2
- import { defineMigration as t } from "./migration.js";
1
+ import { createIndexedDB as e, defineMigration as t } from "./adapters/indexeddb.js";
3
2
  export { e as createIndexedDB, t as defineMigration };
@@ -1 +1 @@
1
- {"version":3,"file":"internal.cjs","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import type { AnySchema, KeyOf, RecordOf, VaultKey } from './types';\n\nimport { error as logError } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\nconst SEP = '\\x00';\n\n/**\n * Type-tagged keys make `1` and `'1'` distinct in string-keyed adapters while\n * keeping the same portable primary-key contract as IndexedDB.\n */\nexport function encodeVaultKey(key: VaultKey): string {\n if (typeof key === 'number') {\n if (!Number.isFinite(key)) throw new VaultError(`primary key must be a finite number, received ${String(key)}`);\n\n return `n:${String(key)}`;\n }\n\n return `s:${encodeURIComponent(key)}`;\n}\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: VaultKey): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeVaultKey(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\n/** `observe()` is Vault's single reactivity primitive: a snapshot now, then on mutations. */\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) onError(error);\n else logError('observer notification failed', error);\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch(reportObserverError);\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) throw new VaultDisposedError('observer hub is disposed');\n\n if (signal?.aborted) return () => {};\n\n const wrapped = listener as ObserverListener<unknown>;\n const listeners = observers.get(table) ?? new Set<ObserverListener<unknown>>();\n\n observers.set(table, listeners);\n listeners.add(wrapped);\n\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(table);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(table);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n return {\n dispose: () => {\n disposed = true;\n observers.clear();\n },\n notify,\n observe,\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (typeof keyValue !== 'number' && typeof keyValue !== 'string') {\n throw new VaultError(`record in table \"${String(table)}\" must have a string or finite-number key at \"${keyField}\"`);\n }\n\n if (typeof keyValue === 'number' && !Number.isFinite(keyValue)) {\n throw new VaultError(`record in table \"${String(table)}\" must have a finite-number key at \"${keyField}\"`);\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":"sDAOA,IAAM,EAAM,KAMZ,SAAgB,EAAe,EAAuB,CACpD,GAAI,OAAO,GAAQ,SAAU,CAC3B,GAAI,CAAC,OAAO,SAAS,CAAG,EAAG,MAAM,IAAI,EAAA,WAAW,iDAAiD,OAAO,CAAG,GAAG,EAE9G,MAAO,KAAK,OAAO,CAAG,GACxB,CAEA,MAAO,KAAK,mBAAmB,CAAG,GACpC,CAEA,SAAgB,EAAe,EAAwB,CACrD,MAAO,GAAG,mBAAmB,CAAM,IAAI,GACzC,CAEA,SAAgB,EAAiB,EAAgB,EAAe,EAAuB,CACrF,MAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,IAAM,EAAe,CAAG,GACnG,CAEA,SAAgB,EAAyB,EAAgB,EAAuB,CAC9E,MAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,GAC3E,CAEA,SAAgB,EAA0B,EAAgB,EAA+C,CACvG,GAAI,CAAC,EAAY,OAEjB,IAAM,EAAS,EAAe,CAAM,EAEpC,GAAI,CAAC,EAAW,WAAW,CAAM,EAAG,OAEpC,IAAM,EAAO,EAAW,MAAM,EAAO,MAAM,EACrC,EAAM,EAAK,QAAQ,CAAG,EAExB,OAAQ,GAEZ,GAAI,CACF,OAAO,mBAAmB,EAAK,MAAM,EAAG,CAAG,CAAC,CAC9C,MAAQ,CACN,MACF,CACF,CAGA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAAY,IAAI,IAClB,EAAW,GAET,EAAuB,GAAyB,CAChD,GAAS,EAAQ,CAAK,CAE5B,EAEM,EAAsC,GAAmB,CAC7D,GAAI,EAAU,OAEd,IAAM,EAAY,EAAU,IAAI,CAAK,EAEjC,CAAC,GAAa,EAAU,OAAS,GAErC,EAAY,CAAK,CAAC,CACf,KAAM,GAAY,CACjB,GAAI,EAAU,OAEd,IAAM,EAAU,EAAU,IAAI,CAAK,EAE/B,MAAC,GAAW,EAAQ,OAAS,GAEjC,IAAK,IAAM,KAAY,EACrB,GAAI,CACF,EAAS,CAAoB,CAC/B,OAAS,EAAO,CACd,EAAoB,CAAK,CAC3B,CAEJ,CAAC,CAAC,CACD,MAAM,CAAmB,CAC9B,EAkCA,MAAO,CACL,YAAe,CACb,EAAW,GACX,EAAU,MAAM,CAClB,EACA,SACA,SArCA,EACA,EACA,CAAE,YAAY,GAAM,UAA0D,CAAC,IAC9D,CACjB,GAAI,EAAU,MAAM,IAAI,EAAA,mBAAmB,0BAA0B,EAErE,GAAI,GAAQ,QAAS,UAAa,CAAC,EAEnC,IAAM,EAAU,EACV,EAAY,EAAU,IAAI,CAAK,GAAK,IAAI,IAE9C,EAAU,IAAI,EAAO,CAAS,EAC9B,EAAU,IAAI,CAAO,EAEjB,GAAW,EAAO,CAAK,EAE3B,IAAM,MAAmB,CACvB,IAAM,EAAU,EAAU,IAAI,CAAK,EAE9B,IAEL,EAAQ,OAAO,CAAO,EAElB,EAAQ,OAAS,GAAG,EAAU,OAAO,CAAK,EAChD,EAIA,OAFA,GAAQ,iBAAiB,QAAS,EAAM,CAAE,KAAM,EAAK,CAAC,EAE/C,CACT,CASA,CACF,CAEA,SAAgB,EACd,EACA,EACA,EACa,CACb,IAAM,EAAW,OAAO,EAAO,EAAM,CAAC,GAAG,EACnC,EAAY,EAAkC,GAEpD,GAAI,OAAO,GAAa,UAAY,OAAO,GAAa,SACtD,MAAM,IAAI,EAAA,WAAW,oBAAoB,OAAO,CAAK,EAAE,gDAAgD,EAAS,EAAE,EAGpH,GAAI,OAAO,GAAa,UAAY,CAAC,OAAO,SAAS,CAAQ,EAC3D,MAAM,IAAI,EAAA,WAAW,oBAAoB,OAAO,CAAK,EAAE,sCAAsC,EAAS,EAAE,EAG1G,OAAO,CACT"}
1
+ {"version":3,"file":"internal.cjs","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\nimport type { AnySchema, KeyOf, RecordOf, VaultKey } from './types';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\nconst SEP = '\\x00';\n\n/**\n * Type-tagged keys make `1` and `'1'` distinct in string-keyed adapters while\n * keeping the same portable primary-key contract as IndexedDB.\n */\nexport function encodeVaultKey(key: VaultKey): string {\n if (typeof key === 'number') {\n if (!Number.isFinite(key)) throw new VaultError(`primary key must be a finite number, received ${String(key)}`);\n\n return `n:${String(key)}`;\n }\n\n return `s:${encodeURIComponent(key)}`;\n}\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: VaultKey): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeVaultKey(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\n/** `observe()` is Vault's single reactivity primitive: a snapshot now, then on mutations. */\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) onError(error);\n else logError('observer notification failed', error);\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch(reportObserverError);\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) throw new VaultDisposedError('observer hub is disposed');\n\n if (signal?.aborted) return () => {};\n\n const wrapped = listener as ObserverListener<unknown>;\n const listeners = observers.get(table) ?? new Set<ObserverListener<unknown>>();\n\n observers.set(table, listeners);\n listeners.add(wrapped);\n\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(table);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(table);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n return {\n dispose: () => {\n disposed = true;\n observers.clear();\n },\n notify,\n observe,\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (typeof keyValue !== 'number' && typeof keyValue !== 'string') {\n throw new VaultError(`record in table \"${String(table)}\" must have a string or finite-number key at \"${keyField}\"`);\n }\n\n if (typeof keyValue === 'number' && !Number.isFinite(keyValue)) {\n throw new VaultError(`record in table \"${String(table)}\" must have a finite-number key at \"${keyField}\"`);\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":"sDAMA,IAAM,EAAM,KAMZ,SAAgB,EAAe,EAAuB,CACpD,GAAI,OAAO,GAAQ,SAAU,CAC3B,GAAI,CAAC,OAAO,SAAS,CAAG,EAAG,MAAM,IAAI,EAAA,WAAW,iDAAiD,OAAO,CAAG,GAAG,EAE9G,MAAO,KAAK,OAAO,CAAG,GACxB,CAEA,MAAO,KAAK,mBAAmB,CAAG,GACpC,CAEA,SAAgB,EAAe,EAAwB,CACrD,MAAO,GAAG,mBAAmB,CAAM,IAAI,GACzC,CAEA,SAAgB,EAAiB,EAAgB,EAAe,EAAuB,CACrF,MAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,IAAM,EAAe,CAAG,GACnG,CAEA,SAAgB,EAAyB,EAAgB,EAAuB,CAC9E,MAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,GAC3E,CAEA,SAAgB,EAA0B,EAAgB,EAA+C,CACvG,GAAI,CAAC,EAAY,OAEjB,IAAM,EAAS,EAAe,CAAM,EAEpC,GAAI,CAAC,EAAW,WAAW,CAAM,EAAG,OAEpC,IAAM,EAAO,EAAW,MAAM,EAAO,MAAM,EACrC,EAAM,EAAK,QAAQ,CAAG,EAExB,OAAQ,GAEZ,GAAI,CACF,OAAO,mBAAmB,EAAK,MAAM,EAAG,CAAG,CAAC,CAC9C,MAAQ,CACN,MACF,CACF,CAGA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAAY,IAAI,IAClB,EAAW,GAET,EAAuB,GAAyB,CAChD,GAAS,EAAQ,CAAK,CAE5B,EAEM,EAAsC,GAAmB,CAC7D,GAAI,EAAU,OAEd,IAAM,EAAY,EAAU,IAAI,CAAK,EAEjC,CAAC,GAAa,EAAU,OAAS,GAErC,EAAY,CAAK,CAAC,CACf,KAAM,GAAY,CACjB,GAAI,EAAU,OAEd,IAAM,EAAU,EAAU,IAAI,CAAK,EAE/B,MAAC,GAAW,EAAQ,OAAS,GAEjC,IAAK,IAAM,KAAY,EACrB,GAAI,CACF,EAAS,CAAoB,CAC/B,OAAS,EAAO,CACd,EAAoB,CAAK,CAC3B,CAEJ,CAAC,CAAC,CACD,MAAM,CAAmB,CAC9B,EAkCA,MAAO,CACL,YAAe,CACb,EAAW,GACX,EAAU,MAAM,CAClB,EACA,SACA,SArCA,EACA,EACA,CAAE,YAAY,GAAM,UAA0D,CAAC,IAC9D,CACjB,GAAI,EAAU,MAAM,IAAI,EAAA,mBAAmB,0BAA0B,EAErE,GAAI,GAAQ,QAAS,UAAa,CAAC,EAEnC,IAAM,EAAU,EACV,EAAY,EAAU,IAAI,CAAK,GAAK,IAAI,IAE9C,EAAU,IAAI,EAAO,CAAS,EAC9B,EAAU,IAAI,CAAO,EAEjB,GAAW,EAAO,CAAK,EAE3B,IAAM,MAAmB,CACvB,IAAM,EAAU,EAAU,IAAI,CAAK,EAE9B,IAEL,EAAQ,OAAO,CAAO,EAElB,EAAQ,OAAS,GAAG,EAAU,OAAO,CAAK,EAChD,EAIA,OAFA,GAAQ,iBAAiB,QAAS,EAAM,CAAE,KAAM,EAAK,CAAC,EAE/C,CACT,CASA,CACF,CAEA,SAAgB,EACd,EACA,EACA,EACa,CACb,IAAM,EAAW,OAAO,EAAO,EAAM,CAAC,GAAG,EACnC,EAAY,EAAkC,GAEpD,GAAI,OAAO,GAAa,UAAY,OAAO,GAAa,SACtD,MAAM,IAAI,EAAA,WAAW,oBAAoB,OAAO,CAAK,EAAE,gDAAgD,EAAS,EAAE,EAGpH,GAAI,OAAO,GAAa,UAAY,CAAC,OAAO,SAAS,CAAQ,EAC3D,MAAM,IAAI,EAAA,WAAW,oBAAoB,OAAO,CAAK,EAAE,sCAAsC,EAAS,EAAE,EAG1G,OAAO,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AASpE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAQpD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,MAAM,CAErF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAiBvG;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,SAAS,EACnD,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;;aAUlB,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,KAAG,IAAI;cA0B1C,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAClC,CAAC,YACE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,0BACf;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAC1E,CAAC,MAAM,IAAI,CAAC;EAoChB;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,MAAM,CAAC,EACjE,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GACpB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAab"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAMpE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAQpD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,MAAM,CAErF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAiBvG;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,SAAS,EACnD,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;;aAUlB,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,KAAG,IAAI;cA0B1C,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAClC,CAAC,YACE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,0BACf;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAC1E,CAAC,MAAM,IAAI,CAAC;EAoChB;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,MAAM,CAAC,EACjE,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GACpB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAab"}
@@ -1 +1 @@
1
- {"version":3,"file":"internal.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import type { AnySchema, KeyOf, RecordOf, VaultKey } from './types';\n\nimport { error as logError } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\nconst SEP = '\\x00';\n\n/**\n * Type-tagged keys make `1` and `'1'` distinct in string-keyed adapters while\n * keeping the same portable primary-key contract as IndexedDB.\n */\nexport function encodeVaultKey(key: VaultKey): string {\n if (typeof key === 'number') {\n if (!Number.isFinite(key)) throw new VaultError(`primary key must be a finite number, received ${String(key)}`);\n\n return `n:${String(key)}`;\n }\n\n return `s:${encodeURIComponent(key)}`;\n}\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: VaultKey): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeVaultKey(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\n/** `observe()` is Vault's single reactivity primitive: a snapshot now, then on mutations. */\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) onError(error);\n else logError('observer notification failed', error);\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch(reportObserverError);\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) throw new VaultDisposedError('observer hub is disposed');\n\n if (signal?.aborted) return () => {};\n\n const wrapped = listener as ObserverListener<unknown>;\n const listeners = observers.get(table) ?? new Set<ObserverListener<unknown>>();\n\n observers.set(table, listeners);\n listeners.add(wrapped);\n\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(table);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(table);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n return {\n dispose: () => {\n disposed = true;\n observers.clear();\n },\n notify,\n observe,\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (typeof keyValue !== 'number' && typeof keyValue !== 'string') {\n throw new VaultError(`record in table \"${String(table)}\" must have a string or finite-number key at \"${keyField}\"`);\n }\n\n if (typeof keyValue === 'number' && !Number.isFinite(keyValue)) {\n throw new VaultError(`record in table \"${String(table)}\" must have a finite-number key at \"${keyField}\"`);\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":";;;AAOA,IAAM,IAAM;AAMZ,SAAgB,EAAe,GAAuB;CACpD,IAAI,OAAO,KAAQ,UAAU;EAC3B,IAAI,CAAC,OAAO,SAAS,CAAG,GAAG,MAAM,IAAI,EAAW,iDAAiD,OAAO,CAAG,GAAG;EAE9G,OAAO,KAAK,OAAO,CAAG;CACxB;CAEA,OAAO,KAAK,mBAAmB,CAAG;AACpC;AAEA,SAAgB,EAAe,GAAwB;CACrD,OAAO,GAAG,mBAAmB,CAAM,IAAI;AACzC;AAEA,SAAgB,EAAiB,GAAgB,GAAe,GAAuB;CACrF,OAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,IAAM,EAAe,CAAG;AACnG;AAEA,SAAgB,EAAyB,GAAgB,GAAuB;CAC9E,OAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI;AAC3E;AAEA,SAAgB,EAA0B,GAAgB,GAA+C;CACvG,IAAI,CAAC,GAAY;CAEjB,IAAM,IAAS,EAAe,CAAM;CAEpC,IAAI,CAAC,EAAW,WAAW,CAAM,GAAG;CAEpC,IAAM,IAAO,EAAW,MAAM,EAAO,MAAM,GACrC,IAAM,EAAK,QAAQ,CAAG;CAExB,UAAQ,IAEZ,IAAI;EACF,OAAO,mBAAmB,EAAK,MAAM,GAAG,CAAG,CAAC;CAC9C,QAAQ;EACN;CACF;AACF;AAGA,SAAgB,EACd,GACA,GACA;CACA,IAAM,oBAAY,IAAI,IAA4C,GAC9D,IAAW,IAET,KAAuB,MAAyB;EACpD,AAAI,KAAS,EAAQ,CAAK;CAE5B,GAEM,KAAsC,MAAmB;EAC7D,IAAI,GAAU;EAEd,IAAM,IAAY,EAAU,IAAI,CAAK;EAEjC,CAAC,KAAa,EAAU,SAAS,KAErC,EAAY,CAAK,CAAC,CACf,MAAM,MAAY;GACjB,IAAI,GAAU;GAEd,IAAM,IAAU,EAAU,IAAI,CAAK;GAE/B,OAAC,KAAW,EAAQ,SAAS,IAEjC,KAAK,IAAM,KAAY,GACrB,IAAI;IACF,EAAS,CAAoB;GAC/B,SAAS,GAAO;IACd,EAAoB,CAAK;GAC3B;EAEJ,CAAC,CAAC,CACD,MAAM,CAAmB;CAC9B;CAkCA,OAAO;EACL,eAAe;GAEb,AADA,IAAW,IACX,EAAU,MAAM;EAClB;EACA;EACA,UArCA,GACA,GACA,EAAE,eAAY,IAAM,cAA0D,CAAC,MAC9D;GACjB,IAAI,GAAU,MAAM,IAAI,EAAmB,0BAA0B;GAErE,IAAI,GAAQ,SAAS,aAAa,CAAC;GAEnC,IAAM,IAAU,GACV,IAAY,EAAU,IAAI,CAAK,qBAAK,IAAI,IAA+B;GAK7E,AAHA,EAAU,IAAI,GAAO,CAAS,GAC9B,EAAU,IAAI,CAAO,GAEjB,KAAW,EAAO,CAAK;GAE3B,IAAM,UAAmB;IACvB,IAAM,IAAU,EAAU,IAAI,CAAK;IAE9B,MAEL,EAAQ,OAAO,CAAO,GAElB,EAAQ,SAAS,KAAG,EAAU,OAAO,CAAK;GAChD;GAIA,OAFA,GAAQ,iBAAiB,SAAS,GAAM,EAAE,MAAM,GAAK,CAAC,GAE/C;EACT;CASA;AACF;AAEA,SAAgB,EACd,GACA,GACA,GACa;CACb,IAAM,IAAW,OAAO,EAAO,EAAM,CAAC,GAAG,GACnC,IAAY,EAAkC;CAEpD,IAAI,OAAO,KAAa,YAAY,OAAO,KAAa,UACtD,MAAM,IAAI,EAAW,oBAAoB,OAAO,CAAK,EAAE,gDAAgD,EAAS,EAAE;CAGpH,IAAI,OAAO,KAAa,YAAY,CAAC,OAAO,SAAS,CAAQ,GAC3D,MAAM,IAAI,EAAW,oBAAoB,OAAO,CAAK,EAAE,sCAAsC,EAAS,EAAE;CAG1G,OAAO;AACT"}
1
+ {"version":3,"file":"internal.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\nimport type { AnySchema, KeyOf, RecordOf, VaultKey } from './types';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\nconst SEP = '\\x00';\n\n/**\n * Type-tagged keys make `1` and `'1'` distinct in string-keyed adapters while\n * keeping the same portable primary-key contract as IndexedDB.\n */\nexport function encodeVaultKey(key: VaultKey): string {\n if (typeof key === 'number') {\n if (!Number.isFinite(key)) throw new VaultError(`primary key must be a finite number, received ${String(key)}`);\n\n return `n:${String(key)}`;\n }\n\n return `s:${encodeURIComponent(key)}`;\n}\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: VaultKey): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeVaultKey(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\n/** `observe()` is Vault's single reactivity primitive: a snapshot now, then on mutations. */\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) onError(error);\n else logError('observer notification failed', error);\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch(reportObserverError);\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) throw new VaultDisposedError('observer hub is disposed');\n\n if (signal?.aborted) return () => {};\n\n const wrapped = listener as ObserverListener<unknown>;\n const listeners = observers.get(table) ?? new Set<ObserverListener<unknown>>();\n\n observers.set(table, listeners);\n listeners.add(wrapped);\n\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(table);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(table);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n return {\n dispose: () => {\n disposed = true;\n observers.clear();\n },\n notify,\n observe,\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (typeof keyValue !== 'number' && typeof keyValue !== 'string') {\n throw new VaultError(`record in table \"${String(table)}\" must have a string or finite-number key at \"${keyField}\"`);\n }\n\n if (typeof keyValue === 'number' && !Number.isFinite(keyValue)) {\n throw new VaultError(`record in table \"${String(table)}\" must have a finite-number key at \"${keyField}\"`);\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":";;;AAMA,IAAM,IAAM;AAMZ,SAAgB,EAAe,GAAuB;CACpD,IAAI,OAAO,KAAQ,UAAU;EAC3B,IAAI,CAAC,OAAO,SAAS,CAAG,GAAG,MAAM,IAAI,EAAW,iDAAiD,OAAO,CAAG,GAAG;EAE9G,OAAO,KAAK,OAAO,CAAG;CACxB;CAEA,OAAO,KAAK,mBAAmB,CAAG;AACpC;AAEA,SAAgB,EAAe,GAAwB;CACrD,OAAO,GAAG,mBAAmB,CAAM,IAAI;AACzC;AAEA,SAAgB,EAAiB,GAAgB,GAAe,GAAuB;CACrF,OAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,IAAM,EAAe,CAAG;AACnG;AAEA,SAAgB,EAAyB,GAAgB,GAAuB;CAC9E,OAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI;AAC3E;AAEA,SAAgB,EAA0B,GAAgB,GAA+C;CACvG,IAAI,CAAC,GAAY;CAEjB,IAAM,IAAS,EAAe,CAAM;CAEpC,IAAI,CAAC,EAAW,WAAW,CAAM,GAAG;CAEpC,IAAM,IAAO,EAAW,MAAM,EAAO,MAAM,GACrC,IAAM,EAAK,QAAQ,CAAG;CAExB,UAAQ,IAEZ,IAAI;EACF,OAAO,mBAAmB,EAAK,MAAM,GAAG,CAAG,CAAC;CAC9C,QAAQ;EACN;CACF;AACF;AAGA,SAAgB,EACd,GACA,GACA;CACA,IAAM,oBAAY,IAAI,IAA4C,GAC9D,IAAW,IAET,KAAuB,MAAyB;EACpD,AAAI,KAAS,EAAQ,CAAK;CAE5B,GAEM,KAAsC,MAAmB;EAC7D,IAAI,GAAU;EAEd,IAAM,IAAY,EAAU,IAAI,CAAK;EAEjC,CAAC,KAAa,EAAU,SAAS,KAErC,EAAY,CAAK,CAAC,CACf,MAAM,MAAY;GACjB,IAAI,GAAU;GAEd,IAAM,IAAU,EAAU,IAAI,CAAK;GAE/B,OAAC,KAAW,EAAQ,SAAS,IAEjC,KAAK,IAAM,KAAY,GACrB,IAAI;IACF,EAAS,CAAoB;GAC/B,SAAS,GAAO;IACd,EAAoB,CAAK;GAC3B;EAEJ,CAAC,CAAC,CACD,MAAM,CAAmB;CAC9B;CAkCA,OAAO;EACL,eAAe;GAEb,AADA,IAAW,IACX,EAAU,MAAM;EAClB;EACA;EACA,UArCA,GACA,GACA,EAAE,eAAY,IAAM,cAA0D,CAAC,MAC9D;GACjB,IAAI,GAAU,MAAM,IAAI,EAAmB,0BAA0B;GAErE,IAAI,GAAQ,SAAS,aAAa,CAAC;GAEnC,IAAM,IAAU,GACV,IAAY,EAAU,IAAI,CAAK,qBAAK,IAAI,IAA+B;GAK7E,AAHA,EAAU,IAAI,GAAO,CAAS,GAC9B,EAAU,IAAI,CAAO,GAEjB,KAAW,EAAO,CAAK;GAE3B,IAAM,UAAmB;IACvB,IAAM,IAAU,EAAU,IAAI,CAAK;IAE9B,MAEL,EAAQ,OAAO,CAAO,GAElB,EAAQ,SAAS,KAAG,EAAU,OAAO,CAAK;GAChD;GAIA,OAFA,GAAQ,iBAAiB,SAAS,GAAM,EAAE,MAAM,GAAK,CAAC,GAE/C;EACT;CASA;AACF;AAEA,SAAgB,EACd,GACA,GACA,GACa;CACb,IAAM,IAAW,OAAO,EAAO,EAAM,CAAC,GAAG,GACnC,IAAY,EAAkC;CAEpD,IAAI,OAAO,KAAa,YAAY,OAAO,KAAa,UACtD,MAAM,IAAI,EAAW,oBAAoB,OAAO,CAAK,EAAE,gDAAgD,EAAS,EAAE;CAGpH,IAAI,OAAO,KAAa,YAAY,CAAC,OAAO,SAAS,CAAQ,GAC3D,MAAM,IAAI,EAAW,oBAAoB,OAAO,CAAK,EAAE,sCAAsC,EAAS,EAAE;CAG1G,OAAO;AACT"}