@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 40.3 kB
Source Map (JSON)
{"version":3,"file":"indexed-db.cjs","sources":["../../src/indexed-db.ts"],"sourcesContent":["/**\n * IndexedDB Collection for TanStack DB\n *\n * This module provides a factory function that creates IndexedDB-backed collections\n * compatible with TanStack DB's collection system.\n */\n\nimport { SchemaMustBeSynchronousError, SchemaValidationError } from './errors'\nimport { safeRandomUUID } from './utils/uuid'\nimport { withCollectionConfigFactory } from './client.js'\nimport {\n clear,\n executeTransaction,\n getAll,\n getByKey,\n openDatabase,\n} from './indexed-db-wrapper'\nimport { codedMessage, devBuild } from './error-message.js'\nimport type {\n BaseCollectionConfig,\n ChangeMessageOrDeleteKeyMessage,\n CollectionConfig,\n DeleteMutationFnParams,\n InsertMutationFnParams,\n OperationType,\n PendingMutation,\n SyncConfig,\n UpdateMutationFnParams,\n UtilsRecord,\n} from './types'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nexport class DatabaseRequiredError extends Error {\n constructor() {\n super(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `IndexedDB collection requires a \"db\" configuration option. ` +\n `Create a database instance using createIndexedDB() and pass it to the collection.`\n : codedMessage(175),\n )\n this.name = `DatabaseRequiredError`\n }\n}\n\n/**\n * Thrown when the specified object store doesn't exist in the database\n */\nexport class ObjectStoreNotFoundError extends Error {\n constructor(\n storeName: string,\n databaseName: string,\n availableStores: ReadonlyArray<string>,\n ) {\n super(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `Object store \"${storeName}\" not found in database \"${databaseName}\". ` +\n `Available stores: [${availableStores.join(', ')}]. ` +\n `Add \"${storeName}\" to the stores array when calling createIndexedDB().`\n : codedMessage(176, { storeName, databaseName, availableStores }),\n )\n this.name = 'ObjectStoreNotFoundError'\n }\n}\n\n/**\n * Thrown when the name (object store) configuration is missing\n */\nexport class NameRequiredError extends Error {\n constructor() {\n super(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `IndexedDB collection requires a \"name\" configuration option. ` +\n `This is the name of the object store within the database.`\n : codedMessage(177),\n )\n this.name = `NameRequiredError`\n }\n}\n\n/**\n * Thrown when the getKey function is missing\n */\nexport class GetKeyRequiredError extends Error {\n constructor() {\n super(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `IndexedDB collection requires a \"getKey\" configuration option. ` +\n `This function extracts the unique key from each item.`\n : codedMessage(178),\n )\n this.name = `GetKeyRequiredError`\n }\n}\n\nexport interface CreateIndexedDBOptions {\n /** Database name */\n name: string\n /** Schema version (increment when adding stores) */\n version: number\n /** Object store names to create */\n stores: ReadonlyArray<string>\n /** Custom IDBFactory for testing/mocking */\n idbFactory?: IDBFactory\n /** Reports a native blocker without settling the open request. */\n onBlocked?: (event: IDBVersionChangeEvent) => void\n}\n\n/**\n * A shared IndexedDB database instance.\n * Create with createIndexedDB() and pass to collections.\n */\nexport interface IndexedDBInstance {\n /** The underlying IDBDatabase connection */\n readonly db: IDBDatabase\n /** Database name */\n readonly name: string\n /** Database version */\n readonly version: number\n /** Requested object store names (frozen); omissions do not remove stores */\n readonly stores: ReadonlyArray<string>\n /** IDBFactory used to create this database (for testing) */\n readonly idbFactory?: IDBFactory\n /** Close the connection and mark its managed Collections as errored. */\n close: () => void\n}\n\ntype InferSchemaOutput<T> = T extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<T> extends object\n ? StandardSchemaV1.InferOutput<T>\n : Record<string, unknown>\n : Record<string, unknown>\n\n/**\n * Schema input type inference helper\n */\ntype InferSchemaInput<T> = T extends StandardSchemaV1\n ? StandardSchemaV1.InferInput<T> extends object\n ? StandardSchemaV1.InferInput<T>\n : Record<string, unknown>\n : Record<string, unknown>\n\n/**\n * Configuration options for creating an IndexedDB Collection\n */\nexport interface IndexedDBCollectionConfig<\n T extends object = object,\n TSchema extends StandardSchemaV1 = never,\n TKey extends string | number = string | number,\n> extends BaseCollectionConfig<T, TKey, TSchema> {\n /**\n * IndexedDB instance from createIndexedDB()\n * REQUIRED - must create database before collections\n */\n db: IndexedDBInstance\n\n /**\n * Name of the object store within the database\n * Must exist in the underlying database\n */\n name: string\n}\n\n/**\n * Version entry stored in the shared _versions object store\n * Key is a tuple: [name, itemKey]\n */\ninterface VersionEntry {\n versionKey: string // UUID for change detection\n}\n\n/**\n * Cross-tab message format via BroadcastChannel\n */\ninterface CrossTabMessage {\n type: 'data-changed' | 'database-cleared'\n database: string\n name: string // Object store name\n changedKeys: Array<string | number> // Keys that changed (for targeted loading)\n tabId: string // To avoid processing own messages\n}\n\n// BroadcastChannel is shared transport. Admit only this protocol before reading\n// its routing fields or issuing storage work. Invalid messages carry no writes.\nfunction isCrossTabMessage(value: unknown): value is CrossTabMessage {\n if (!value || typeof value !== 'object') return false\n const message = value as Record<string, unknown>\n return (\n (message.type === 'data-changed' || message.type === 'database-cleared') &&\n typeof message.database === 'string' &&\n typeof message.name === 'string' &&\n typeof message.tabId === 'string' &&\n Array.isArray(message.changedKeys) &&\n message.changedKeys.every(\n (key: unknown) =>\n typeof key === 'string' ||\n (typeof key === 'number' && !Number.isNaN(key)),\n )\n )\n}\n\n/**\n * Database information returned by getDatabaseInfo()\n */\nexport interface DatabaseInfo {\n name: string\n version: number\n objectStores: Array<string>\n /** Origin-wide storage usage in bytes, including other databases and caches. */\n estimatedSize?: number\n}\n\n/**\n * Utility functions exposed on collection.utils\n */\nexport interface IndexedDBCollectionUtils<\n TItem extends object = Record<string, unknown>,\n TInsertInput extends object = TItem,\n> extends UtilsRecord {\n /**\n * Removes all data from the object store\n * Does NOT delete the database itself\n */\n clearObjectStore: () => Promise<void>\n\n /**\n * Returns database information for debugging\n */\n getDatabaseInfo: () => Promise<DatabaseInfo>\n\n /**\n * Accepts mutations from a manual transaction and persists to IndexedDB\n */\n acceptMutations: (transaction: {\n mutations: Array<PendingMutation>\n }) => Promise<void>\n\n /**\n * Exports all data from the object store as an array\n * Useful for backup/debugging\n */\n exportData: () => Promise<Array<TItem>>\n\n /**\n * Validates input rows and atomically replaces the object store.\n * Failure preserves the previous rows and versions.\n */\n importData: (items: Array<TInsertInput>) => Promise<void>\n}\n\nconst VERSIONS_STORE_NAME = '_versions'\n// Connection closure is sticky, including Collections started after close.\n// Keep core's sync run alive: admitted writes and accepted sync transactions\n// still owe their confirmations, while new reads have no publication authority.\nconst connections = new WeakMap<\n IDBDatabase,\n {\n error?: Error\n listeners: Set<() => void>\n }\n>()\n\n/**\n * Creates or opens an IndexedDB database with the specified stores.\n * Call this once at app startup, then pass the instance to collections.\n * The connection closes on versionchange so another context can upgrade or\n * delete the database. Affected Collections enter error and retain their rows.\n * Recreate affected Collections with a new instance before\n * further persistence.\n *\n * All stores are created in a single upgrade transaction, avoiding\n * version race conditions when multiple collections share a database.\n *\n * @example\n * ```typescript\n * const db = await createIndexedDB({\n * name: 'myApp',\n * version: 1,\n * stores: ['todos', 'users', 'settings'],\n * })\n *\n * const todosCollection = createCollection(\n * indexedDBCollectionOptions({\n * db,\n * name: 'todos',\n * getKey: (item: { id: string }) => item.id,\n * })\n * )\n * ```\n */\nexport async function createIndexedDB(\n options: CreateIndexedDBOptions,\n): Promise<IndexedDBInstance> {\n const { name, version, stores, idbFactory, onBlocked } = options\n\n // Validate options\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime safety for JS consumers\n if (!stores || stores.length === 0) {\n throw new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? 'createIndexedDB requires at least one store in the stores array.'\n : codedMessage(193),\n )\n }\n\n const storeSet = new Set(stores)\n if (storeSet.size !== stores.length) {\n throw new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? 'createIndexedDB stores array contains duplicate store names.'\n : codedMessage(194),\n )\n }\n\n for (const storeName of stores) {\n if (storeName === VERSIONS_STORE_NAME) {\n throw new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? 'The \"_versions\" store is reserved for IndexedDB Collection metadata.'\n : codedMessage(195),\n )\n }\n if (!storeName || typeof storeName !== 'string') {\n throw new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? 'createIndexedDB stores array contains invalid store names. ' +\n 'Each store name must be a non-empty string.'\n : codedMessage(196),\n )\n }\n }\n\n const db = await openDatabase(\n name,\n version,\n (database) => {\n // Always create _versions store for cross-tab sync\n if (!database.objectStoreNames.contains(VERSIONS_STORE_NAME)) {\n database.createObjectStore(VERSIONS_STORE_NAME)\n }\n\n // Create each requested store\n for (const storeName of stores) {\n if (!database.objectStoreNames.contains(storeName)) {\n database.createObjectStore(storeName)\n }\n }\n },\n idbFactory,\n onBlocked,\n )\n\n const connection: { error?: Error; listeners: Set<() => void> } = {\n listeners: new Set(),\n }\n connections.set(db, connection)\n const close = () => {\n db.close()\n if (connection.error) return\n connection.error = new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `IndexedDB connection \"${name}\" closed. Recreate its Collections with a new database instance.`\n : codedMessage(197, { name }),\n )\n // Record closure before reentrant listeners can request more work. Core\n // reports user event-listener failures asynchronously and notifies siblings.\n for (const notify of [...connection.listeners]) notify()\n }\n db.addEventListener('versionchange', close)\n db.addEventListener('close', close)\n\n // Create frozen stores array for immutability\n const frozenStores = Object.freeze([...stores])\n\n return Object.freeze({\n db,\n name,\n version: db.version,\n stores: frozenStores,\n idbFactory,\n close,\n })\n}\n\n/**\n * Creates IndexedDB collection options for use with a standard Collection.\n * This provides persistent local storage with cross-tab synchronization.\n *\n * IMPORTANT: You must first create the database with createIndexedDB() and\n * pass the instance to this function. This ensures all stores are created\n * upfront in a single upgrade transaction.\n *\n * @example\n * // Step 1: Create database with all stores\n * const db = await createIndexedDB({\n * name: 'myApp',\n * version: 1,\n * stores: ['todos', 'users'],\n * })\n *\n * // Step 2: Create collections using the shared database\n * const todosCollection = createCollection(\n * indexedDBCollectionOptions({\n * db,\n * name: 'todos',\n * schema: todoSchema,\n * getKey: (item: { id: string }) => item.id,\n * })\n * )\n *\n * @example\n * // Without schema (explicit type)\n * const todosCollection = createCollection(\n * indexedDBCollectionOptions<Todo>({\n * db,\n * name: 'todos',\n * getKey: (item: { id: string }) => item.id,\n * })\n * )\n */\n\n// Overload for when schema is provided\nexport function indexedDBCollectionOptions<\n T extends StandardSchemaV1,\n TKey extends string | number = string | number,\n>(\n config: IndexedDBCollectionConfig<InferSchemaOutput<T>, T, TKey> & {\n schema: T\n },\n): CollectionConfig<\n InferSchemaOutput<T>,\n TKey,\n T,\n IndexedDBCollectionUtils<InferSchemaOutput<T>, InferSchemaInput<T>>\n> & {\n schema: T\n utils: IndexedDBCollectionUtils<InferSchemaOutput<T>, InferSchemaInput<T>>\n}\n\n// Overload for when no schema is provided\nexport function indexedDBCollectionOptions<\n T extends object,\n TKey extends string | number = string | number,\n>(\n config: IndexedDBCollectionConfig<T, never, TKey> & {\n schema?: never\n },\n): CollectionConfig<T, TKey, never, IndexedDBCollectionUtils<T>> & {\n schema?: never\n utils: IndexedDBCollectionUtils<T>\n}\n\nexport function indexedDBCollectionOptions(\n config: IndexedDBCollectionConfig<Record<string, unknown>, StandardSchemaV1>,\n): CollectionConfig<\n Record<string, unknown>,\n string | number,\n StandardSchemaV1,\n IndexedDBCollectionUtils\n> & {\n utils: IndexedDBCollectionUtils\n} {\n const {\n db: dbInstance,\n name,\n getKey,\n onInsert,\n onUpdate,\n onDelete,\n ...baseCollectionConfig\n } = config\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime safety for JS consumers\n if (!dbInstance) {\n throw new DatabaseRequiredError()\n }\n\n if (!name) {\n throw new NameRequiredError()\n }\n if (name === VERSIONS_STORE_NAME) {\n throw new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? 'The \"_versions\" store is reserved for IndexedDB Collection metadata.'\n : codedMessage(198),\n )\n }\n\n // Validate that the store exists in the database (sync check)\n for (const storeName of [name, VERSIONS_STORE_NAME]) {\n if (!dbInstance.db.objectStoreNames.contains(storeName)) {\n throw new ObjectStoreNotFoundError(\n storeName,\n dbInstance.name,\n Array.from(dbInstance.db.objectStoreNames),\n )\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!getKey) {\n throw new GetKeyRequiredError()\n }\n\n type Item = Record<string, unknown>\n type Mutation = {\n type: OperationType\n key: string | number\n modified: Item\n }\n type Sync = Parameters<SyncConfig<Item>['sync']>[0]\n const collectionId =\n baseCollectionConfig.id ??\n `indexed-db-collection:${dbInstance.name}:${name}`\n const connection = connections.get(dbInstance.db)\n const tabId = safeRandomUUID()\n const versionCache = new Map<string | number, string>()\n let collection: Sync['collection'] | undefined\n let acceptance: Promise<void> = Promise.resolve()\n let activeSync: Sync | undefined\n let broadcastChannel: BroadcastChannel | undefined\n\n function broadcastChange(\n changedKeys: Array<string | number>,\n type: CrossTabMessage['type'] = 'data-changed',\n ): void {\n let channel = broadcastChannel\n if (!channel) {\n try {\n channel = new BroadcastChannel(`tanstack-db:${dbInstance.name}`)\n } catch {\n return // Local persistence does not require BroadcastChannel.\n }\n }\n try {\n channel.postMessage({\n type,\n database: dbInstance.name,\n name,\n changedKeys,\n tabId,\n } satisfies CrossTabMessage)\n } finally {\n // Utility writes are legal before startup and after cleanup. Their\n // send-only channel owns no subscription and ends with the send.\n if (channel !== broadcastChannel) channel.close()\n }\n }\n\n // Jump to this store's compound-key prefix without ambient IDBKeyRange.\n // Stop after its last key; sibling rows are never visited. Request errors must\n // reject the surrounding transaction, including failures while deleting.\n function visitVersions(\n store: IDBObjectStore,\n visit: (cursor: IDBCursorWithValue) => void,\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = store.openCursor()\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n const cursor = request.result\n if (!cursor) {\n resolve()\n return\n }\n try {\n const [storeName] = cursor.key as [string, string | number]\n if (storeName < name) {\n cursor.continue([name])\n } else if (storeName === name) {\n visit(cursor)\n cursor.continue()\n } else {\n resolve()\n }\n } catch (error) {\n reject(error)\n }\n }\n })\n }\n\n // Stage cache changes until the ONE data-and-version transaction completes.\n // A failed request, including synchronous structured-clone failure, aborts\n // every mutation in this Collection's batch.\n async function persist(\n mutations: Array<Mutation>,\n replace = false,\n ): Promise<void> {\n const versions = new Map<string | number, string>()\n await executeTransaction(\n dbInstance.db,\n [name, VERSIONS_STORE_NAME],\n 'readwrite',\n async (_, stores) => {\n if (replace) {\n await clear(stores[name]!)\n await visitVersions(stores[VERSIONS_STORE_NAME]!, (cursor) => {\n cursor.delete()\n })\n }\n for (const mutation of mutations) {\n const { key } = mutation\n if (mutation.type === 'delete') {\n stores[name]!.delete(key)\n stores[VERSIONS_STORE_NAME]!.delete([name, key])\n } else {\n const versionKey = safeRandomUUID()\n stores[name]!.put(mutation.modified, key)\n stores[VERSIONS_STORE_NAME]!.put(\n {\n versionKey,\n } satisfies VersionEntry,\n [name, key],\n )\n versions.set(key, versionKey)\n }\n }\n },\n )\n if (replace) versionCache.clear()\n for (const mutation of mutations) {\n const version = versions.get(mutation.key)\n if (version === undefined) versionCache.delete(mutation.key)\n else versionCache.set(mutation.key, version)\n }\n }\n\n function confirm(mutations: Array<Mutation>, replace = false): void {\n if (!activeSync) return\n // Core queues ordinary confirmation until optimistic settlement.\n // Full-row update confirms put even if a peer inserted during the handler.\n // A replacement's truncate already supplies its own publication boundary.\n activeSync.begin()\n if (replace) activeSync.truncate({ markReady: !connection?.error })\n for (const mutation of mutations) {\n activeSync.write(\n mutation.type === 'delete'\n ? { type: 'delete', key: mutation.key }\n : { type: 'update', value: mutation.modified },\n )\n }\n activeSync.commit()\n }\n\n // Startup and replacement notifications read rows and versions from one\n // native snapshot, then publish only into the sync run that requested it.\n async function restore(params: Sync, replace: boolean): Promise<void> {\n const snapshot = await executeTransaction(\n dbInstance.db,\n [name, VERSIONS_STORE_NAME],\n 'readonly',\n async (_, stores) => {\n const items = await getAll<Item>(stores[name]!)\n const versions = new Map<string | number, string>()\n await visitVersions(stores[VERSIONS_STORE_NAME]!, (cursor) => {\n const [, key] = cursor.key as [string, string | number]\n versions.set(key, (cursor.value as VersionEntry).versionKey)\n })\n return { items, versions }\n },\n )\n if (activeSync !== params || connection?.error) return\n versionCache.clear()\n for (const [key, version] of snapshot.versions)\n versionCache.set(key, version)\n params.begin()\n if (replace) params.truncate()\n for (const item of snapshot.items)\n params.write({ type: 'insert', value: item })\n params.commit()\n if (!replace) params.markReady()\n }\n\n const internalSync: SyncConfig<Item>['sync'] = (params) => {\n const { begin, write, commit, markError } = params\n activeSync = params\n collection = params.collection\n let channel: BroadcastChannel | undefined\n const retire = () => {\n channel?.close()\n if (activeSync === params) {\n broadcastChannel = undefined\n markError(connection?.error)\n }\n }\n if (connection?.error) {\n retire()\n return\n }\n connection?.listeners.add(retire)\n\n try {\n channel = new BroadcastChannel(`tanstack-db:${dbInstance.name}`)\n broadcastChannel = channel\n channel.onmessage = async (event: MessageEvent<CrossTabMessage>) => {\n const message: unknown = event.data\n if (!isCrossTabMessage(message)) return\n if (\n activeSync !== params ||\n connection?.error ||\n message.tabId === tabId ||\n message.database !== dbInstance.name ||\n message.name !== name\n )\n return\n try {\n if (message.type === 'database-cleared') {\n // The notification describes an earlier replacement. Current\n // durable rows may also contain later accepted writes.\n await restore(params, true)\n return\n }\n if (!message.changedKeys.length) return\n const rows = await executeTransaction(\n dbInstance.db,\n [name, VERSIONS_STORE_NAME],\n 'readonly',\n async (_, stores) => {\n const result = []\n for (const key of message.changedKeys) {\n const [version, value] = await Promise.all([\n getByKey<VersionEntry>(stores[VERSIONS_STORE_NAME]!, [\n name,\n key,\n ]),\n getByKey<Item>(stores[name]!, key),\n ])\n result.push({ key, version, value })\n }\n return result\n },\n )\n if (activeSync !== params || connection?.error) return\n const changes: Array<ChangeMessageOrDeleteKeyMessage<Item>> = []\n for (const { key, version, value } of rows) {\n const cached = versionCache.get(key)\n if (value) {\n if (!version || cached !== version.versionKey) {\n changes.push({\n type: 'update',\n value,\n })\n if (version) versionCache.set(key, version.versionKey)\n else versionCache.delete(key)\n }\n } else {\n // Source deletion is independent of the optimistic public view.\n changes.push({ type: 'delete', key })\n versionCache.delete(key)\n }\n }\n if (changes.length) {\n begin()\n for (const change of changes) write(change)\n commit()\n }\n } catch (error) {\n if (activeSync === params) markError(error)\n }\n }\n } catch {\n // Persistence also works in environments without BroadcastChannel.\n }\n\n void restore(params, false).catch((error: unknown) => {\n if (activeSync === params) markError(error)\n })\n\n return {\n cleanup: () => {\n connection?.listeners.delete(retire)\n channel?.close()\n if (activeSync === params) {\n activeSync = undefined\n broadcastChannel = undefined\n versionCache.clear()\n }\n },\n }\n }\n\n const acceptMutations = async (transaction: {\n mutations: Array<PendingMutation<Item>>\n }): Promise<void> => {\n const mutations = transaction.mutations\n .filter((m) => m.collection === collection)\n .map((m) => ({\n type: m.type,\n key: getKey(m.modified),\n modified: m.modified,\n }))\n if (!mutations.length) return\n await persist(mutations)\n confirm(mutations)\n broadcastChange(mutations.map((m) => m.key))\n }\n\n // Start handlers immediately, but accept successful automatic writes in\n // invocation order. Observe rejection immediately even while a predecessor\n // is held. Each rejected decision releases its place without persisting.\n function persistInOrder(\n transaction: { mutations: Array<PendingMutation<Item>> },\n handler: () => unknown,\n ): Promise<unknown> {\n const result = acceptance.then(async () => {\n const outcome = await decision\n if (!outcome.accepted) throw outcome.error\n await acceptMutations(transaction)\n return outcome.value\n })\n acceptance = result.then(\n () => undefined,\n () => undefined,\n )\n // Reserve the position before application code can author another write.\n const decision = new Promise<unknown>((resolve) => resolve(handler())).then(\n (value) => ({ accepted: true as const, value }),\n (error: unknown) => ({ accepted: false as const, error }),\n )\n return result\n }\n\n const wrappedOnInsert = (params: InsertMutationFnParams<Item>) =>\n persistInOrder(params.transaction, () => onInsert?.(params))\n const wrappedOnUpdate = (params: UpdateMutationFnParams<Item>) =>\n persistInOrder(params.transaction, () => onUpdate?.(params))\n const wrappedOnDelete = (params: DeleteMutationFnParams<Item>) =>\n persistInOrder(params.transaction, () => onDelete?.(params))\n\n const clearObjectStore = async (): Promise<void> => {\n await persist([], true)\n confirm([], true)\n broadcastChange([], 'database-cleared')\n }\n\n const getDatabaseInfo = async (): Promise<DatabaseInfo> => {\n const db = dbInstance.db\n const info: DatabaseInfo = {\n name: db.name,\n version: db.version,\n objectStores: Array.from(db.objectStoreNames),\n }\n // Storage estimates are optional diagnostics, not a persistence prerequisite.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- optional browser API at runtime\n if (typeof navigator !== 'undefined' && navigator.storage?.estimate) {\n try {\n info.estimatedSize = (await navigator.storage.estimate()).usage\n } catch {}\n }\n return info\n }\n\n const exportData = (): Promise<Array<Item>> =>\n executeTransaction(dbInstance.db, name, 'readonly', (_, stores) =>\n getAll<Item>(stores[name]!),\n )\n\n const importData = async (items: Array<Item>): Promise<void> => {\n const keys = new Set<string | number>()\n const mutations = items.map((input) => {\n let item = input\n if (config.schema) {\n const result = config.schema['~standard'].validate(input)\n if (result instanceof Promise) throw new SchemaMustBeSynchronousError()\n if (result.issues) {\n throw new SchemaValidationError(\n 'insert',\n result.issues.map((issue) => ({\n message: issue.message,\n path: issue.path?.map(String),\n })),\n )\n }\n item = result.value as Item\n }\n const key = getKey(item)\n if (keys.has(key))\n throw new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `Duplicate imported key: ${key}`\n : codedMessage(199, { key }),\n )\n keys.add(key)\n return { type: 'insert' as const, key, modified: structuredClone(item) }\n })\n await persist(mutations, true)\n confirm(mutations, true)\n broadcastChange([], 'database-cleared')\n }\n\n const utils: IndexedDBCollectionUtils = {\n clearObjectStore,\n getDatabaseInfo,\n acceptMutations,\n exportData,\n importData,\n }\n const options = {\n ...baseCollectionConfig,\n id: collectionId,\n getKey,\n sync: { sync: internalSync, rowUpdateMode: 'full' as const },\n onInsert: wrappedOnInsert,\n onUpdate: wrappedOnUpdate,\n onDelete: wrappedOnDelete,\n utils,\n }\n return withCollectionConfigFactory(options, () =>\n (\n indexedDBCollectionOptions as (\n nextConfig: IndexedDBCollectionConfig<Item, StandardSchemaV1>,\n ) => typeof options\n )({ ...config, id: collectionId }),\n )\n}\n"],"names":["devBuild","codedMessage","openDatabase","safeRandomUUID","executeTransaction","clear","getAll","getByKey","SchemaMustBeSynchronousError","SchemaValidationError","withCollectionConfigFactory"],"mappings":"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"}