ngx-indexed-db
Version:
Angular wrapper to IndexedDB database.
1 lines • 75.8 kB
Source Map (JSON)
{"version":3,"file":"ngx-indexed-db.mjs","sources":["../../../projects/ngx-indexed-db/src/lib/ngx-indexed-db.meta.ts","../../../projects/ngx-indexed-db/src/utils/index.ts","../../../projects/ngx-indexed-db/src/lib/ngx-indexed-db.ts","../../../projects/ngx-indexed-db/src/lib/decorators.ts","../../../projects/ngx-indexed-db/src/lib/ngx-indexed-db.service.ts","../../../projects/ngx-indexed-db/src/ssr/server-indexed-db.ts","../../../projects/ngx-indexed-db/src/ssr/index.ts","../../../projects/ngx-indexed-db/src/lib/provide-indexed-db.ts","../../../projects/ngx-indexed-db/src/lib/ngxindexeddb.module.ts","../../../projects/ngx-indexed-db/src/ngx-indexed-db.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport interface DBConfig {\n name: string;\n version?: number;\n objectStoresMeta: ObjectStoreMeta[];\n migrationFactory?: () => { [key: number]: (db: IDBDatabase, transaction: IDBTransaction) => void };\n isDefault?: boolean;\n}\n\nexport interface ObjectStoreMeta {\n store: string;\n storeConfig: { keyPath: string | string[]; autoIncrement: boolean; [key: string]: any };\n storeSchema: ObjectStoreSchema[];\n}\n\nexport interface ObjectStoreSchema {\n name: string;\n keypath: string | string[];\n options: { unique: boolean; [key: string]: any };\n}\n\nexport interface IndexDetails {\n indexName: string;\n order: string;\n}\n\nexport interface RequestEvent<T> extends Event {\n target: RequestEventTarget<T>;\n}\n\nexport interface RequestEventTarget<T> extends EventTarget {\n result: T | T[];\n}\n\nexport enum DBMode {\n readonly = 'readonly',\n readwrite = 'readwrite',\n}\n\nexport type WithID = { id: number };\n\nexport type IndexKey<P extends IDBValidKey, K extends IDBValidKey> = {\n readonly primaryKey: P;\n readonly key: K;\n};\n\ntype Modify<T, R> = Omit<T, keyof R> & R;\n\nexport type NgxIDBCursor<P extends IDBValidKey, K extends IDBValidKey, V = any> = Modify<\n IDBCursor,\n { key: K; primaryKey: P; update(value: V): IDBRequest<IDBValidKey> }\n>;\n\nexport type NgxIDBCursorWithValue<\n V = any,\n P extends IDBValidKey = IDBValidKey,\n K extends IDBValidKey = IDBValidKey\n> = NgxIDBCursor<P, K, V> & { value: V };\n\nexport const CONFIG_TOKEN = new InjectionToken<Record<string, DBConfig>>(null);\nexport const INDEXED_DB = new InjectionToken<IDBFactory>('Indexed DB');\n/**\n * Token used to inject the indexed db implementation on the server\n */\nexport const SERVER_INDEXED_DB = new InjectionToken<IDBFactory>('Server Indexed DB');\n","export interface Options {\n storeName: string;\n dbMode: IDBTransactionMode;\n error: (e: Event) => any;\n complete?: (e: Event) => any;\n abort?: any;\n}\n\nexport function validateStoreName(db: IDBDatabase, storeName: string): boolean {\n return db.objectStoreNames.contains(storeName);\n}\n\nexport function validateBeforeTransaction(db: IDBDatabase, storeName: string, reject: (message: string) => void): void {\n if (!db) {\n reject('You need to use the openDatabase function to create a database before you query it!');\n return; // Stop further execution\n }\n if (!validateStoreName(db, storeName)) {\n reject(`objectStore does not exists: ${storeName}`);\n }\n}\n\nexport function createTransaction(db: IDBDatabase, options: Options): IDBTransaction {\n const trans: IDBTransaction = db.transaction(options.storeName, options.dbMode);\n trans.onerror = options.error;\n trans.onabort = options.abort;\n return trans;\n}\n\nexport function optionsGenerator(\n type: any,\n storeName: any,\n reject: (reason?: any) => void,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n resolve?: (e: any) => void\n): Options {\n return {\n storeName,\n dbMode: type,\n error: (e: Event) => {\n reject(e);\n },\n abort: (e: Event) => {\n reject(e);\n },\n };\n}\n","import { ObjectStoreMeta } from './ngx-indexed-db.meta';\nimport { Observable } from 'rxjs';\n\nexport const openedDatabases: IDBDatabase[] = [];\n\nexport function openDatabase(\n indexedDB: IDBFactory,\n dbName: string,\n version?: number,\n upgradeCallback?: (a: Event, b: IDBDatabase) => void\n): Promise<IDBDatabase> {\n return new Promise<IDBDatabase>((resolve, reject) => {\n if (!indexedDB) {\n reject('IndexedDB not available');\n }\n const request = indexedDB.open(dbName, version);\n let db: IDBDatabase;\n request.onsuccess = () => {\n db = request.result;\n openedDatabases.push(db);\n resolve(db);\n };\n request.onerror = () => {\n reject(`IndexedDB error: ${request.error}`);\n };\n if (typeof upgradeCallback === 'function') {\n request.onupgradeneeded = (event: Event) => {\n upgradeCallback(event, db);\n };\n }\n });\n}\n\nexport async function CreateObjectStore(\n indexedDB: IDBFactory,\n dbName: string,\n version: number,\n storeSchemas: ObjectStoreMeta[],\n migrationFactory?: () => { [key: number]: (db: IDBDatabase, transaction: IDBTransaction) => void }\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (!indexedDB) {\n return;\n }\n const request: IDBOpenDBRequest = indexedDB.open(dbName, version);\n request.onupgradeneeded = async (event: IDBVersionChangeEvent) => {\n const database: IDBDatabase = (event.target as any).result;\n\n const storeCreationPromises = storeSchemas.map(async (storeSchema) => {\n if (!database.objectStoreNames.contains(storeSchema.store)) {\n const objectStore = database.createObjectStore(storeSchema.store, storeSchema.storeConfig);\n for (const schema of storeSchema.storeSchema) {\n objectStore.createIndex(schema.name, schema.keypath, schema.options);\n }\n }\n });\n\n await Promise.all(storeCreationPromises);\n\n const storeMigrations = migrationFactory && migrationFactory();\n if (storeMigrations) {\n const migrationKeys = Object.keys(storeMigrations)\n .map((k) => parseInt(k, 10))\n .filter((v) => v > event.oldVersion)\n .sort((a, b) => a - b);\n\n for (const v of migrationKeys) {\n storeMigrations[v](database, request.transaction);\n }\n }\n\n database.close();\n resolve();\n };\n\n request.onsuccess = (e: any) => {\n e.target.result.close();\n resolve();\n };\n\n request.onerror = (error: Event) => {\n reject(error);\n };\n });\n}\n\nexport function DeleteObjectStore(dbName: string, version: number, storeName: string): Observable<void> {\n if (!dbName || !version || !storeName) {\n throw Error('Params: \"dbName\", \"version\", \"storeName\" are mandatory.');\n }\n\n return new Observable((obs) => {\n try {\n const newVersion = version + 1;\n const request: IDBOpenDBRequest = indexedDB.open(dbName, newVersion);\n request.onupgradeneeded = (event: IDBVersionChangeEvent) => {\n const database: IDBDatabase = (event.target as any).result;\n\n database.deleteObjectStore(storeName);\n database.close();\n console.log('onupgradeneeded');\n obs.next();\n obs.complete();\n };\n\n request.onerror = (e) => obs.error(e);\n } catch (error) {\n obs.error(error);\n }\n });\n}\n\nexport function closeDatabase(db: IDBDatabase): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (!db) {\n reject(new Error('No database to close'));\n return;\n }\n\n try {\n db.close();\n resolve();\n } catch (error) {\n reject(`Error closing database: ${error}`);\n }\n });\n}\n","import { Observable } from 'rxjs';\nimport { finalize } from 'rxjs/operators';\nimport { closeDatabase, openedDatabases } from './ngx-indexed-db';\n\nexport function CloseDbConnection(): MethodDecorator {\n return function (_target: object, _propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor {\n const originalMethod = descriptor.value;\n descriptor.value = function (...args: unknown[]) {\n const result = originalMethod.apply(this, args);\n\n if (result instanceof Observable) {\n return result.pipe(\n finalize(async () => {\n const promises = openedDatabases.map(async (db: IDBDatabase) => {\n await closeDatabase(db);\n });\n await Promise.all(promises);\n openedDatabases.length = 0;\n })\n );\n }\n return result;\n };\n return descriptor;\n };\n}\n","import { Inject, Injectable, isDevMode } from '@angular/core';\nimport { Observable, combineLatest, from } from 'rxjs';\nimport { take } from 'rxjs/operators';\nimport { createTransaction, optionsGenerator, validateBeforeTransaction } from '../utils';\nimport { CloseDbConnection } from './decorators';\nimport { CreateObjectStore, DeleteObjectStore, openDatabase } from './ngx-indexed-db';\nimport {\n CONFIG_TOKEN,\n DBConfig,\n DBMode,\n INDEXED_DB,\n IndexKey,\n NgxIDBCursor,\n NgxIDBCursorWithValue,\n ObjectStoreMeta,\n RequestEvent,\n WithID,\n} from './ngx-indexed-db.meta';\n\n@Injectable()\nexport class NgxIndexedDBService {\n private defaultDatabaseName?: string = null;\n private selectedDb: string;\n\n constructor(\n @Inject(CONFIG_TOKEN) private dbConfigs: Record<string, DBConfig>,\n @Inject(INDEXED_DB) private indexedDB: IDBFactory\n ) {\n Object.values(this.dbConfigs).forEach((dbConfig, _, ref) => this.instanciateConfig(dbConfig, ref.length === 1));\n }\n\n private async instanciateConfig(dbConfig: DBConfig, isOnlyConfig: boolean): Promise<void> {\n if (!dbConfig.name) {\n throw new Error('NgxIndexedDB: Please, provide the dbName in the configuration');\n }\n // if (!dbConfig.version) {\n // throw new Error('NgxIndexedDB: Please, provide the db version in the configuration');\n // }\n if ((dbConfig.isDefault ?? false) && this.defaultDatabaseName) {\n // A default DB is already configured, throw an error\n throw new Error('NgxIndexedDB: Only one database can be set as default');\n }\n if (((dbConfig.isDefault ?? false) && !this.defaultDatabaseName) || isOnlyConfig) {\n this.defaultDatabaseName = dbConfig.name;\n this.selectedDb = dbConfig.name;\n }\n\n await CreateObjectStore(\n this.indexedDB,\n dbConfig.name,\n dbConfig.version,\n dbConfig.objectStoresMeta,\n dbConfig.migrationFactory\n );\n\n openDatabase(this.indexedDB, dbConfig.name).then((db) => {\n if (db.version !== dbConfig.version) {\n if (isDevMode()) {\n console.warn(`\n Your DB Config doesn't match the most recent version of the DB with name ${dbConfig.name}, please update it\n DB current version: ${db.version};\n Your configuration: ${dbConfig.version};\n `);\n console.warn(`Using latest version ${db.version}`);\n }\n this.dbConfigs[dbConfig.name].version = db.version;\n }\n\n db.close();\n });\n }\n\n private get dbConfig(): DBConfig {\n return this.dbConfigs[this.selectedDb];\n }\n\n /**\n * The function return the current version of database\n *\n * @Return the current version of database as number\n */\n @CloseDbConnection()\n getDatabaseVersion(): Observable<number | string> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db: IDBDatabase) => {\n obs.next(db.version);\n obs.complete();\n })\n .catch((err) => obs.error(`error during get version of database => ${err} `));\n });\n }\n\n /**\n * Selects a database for the current context.\n * @param {string} [databaseName=undefined] Database name to select.\n */\n public selectDb(databaseName?: string): void {\n databaseName = databaseName ?? this.defaultDatabaseName;\n if (!databaseName) {\n // Name is still null, it means that there is no default database set\n // and the database name was not specified while calling a method\n throw new Error(`No database name specified and no default database set.`);\n }\n if (!Object.keys(this.dbConfigs).includes(databaseName)) {\n throw new Error(`NgxIndexedDB: Database ${databaseName} is not initialized.`);\n }\n\n this.selectedDb = databaseName;\n }\n\n /**\n * Allows to create a new object store ad-hoc\n * @param storeName The name of the store to be created\n * @param migrationFactory The migration factory if exists\n */\n async createObjectStore(\n storeSchema: ObjectStoreMeta,\n migrationFactory?: () => { [key: number]: (db: IDBDatabase, transaction: IDBTransaction) => void }\n ): Promise<void> {\n const storeSchemas: ObjectStoreMeta[] = [storeSchema];\n await CreateObjectStore(\n this.indexedDB,\n this.dbConfig.name,\n ++this.dbConfig.version,\n storeSchemas,\n migrationFactory\n );\n }\n\n /**\n * Create dynamic store if not already without incrementing version\n * For Dynamic store\n * @param storeName The name of the store to create\n */\n async createDynamicObjectStore(\n storeSchema: ObjectStoreMeta,\n migrationFactory?: () => { [key: number]: (db: IDBDatabase, transaction: IDBTransaction) => void }\n ): Promise<void> {\n const storeSchemas: ObjectStoreMeta[] = [storeSchema];\n await CreateObjectStore(this.indexedDB, this.dbConfig.name, this.dbConfig.version, storeSchemas, migrationFactory);\n }\n\n /**\n * Adds new entry in the store and returns its key\n * @param storeName The name of the store to add the item\n * @param value The entry to be added\n * @param key The optional key for the entry\n */\n @CloseDbConnection()\n add<T>(storeName: string, value: T, key?: any): Observable<T & WithID> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db: IDBDatabase) => {\n const transaction = createTransaction(\n db,\n optionsGenerator(DBMode.readwrite, storeName, (e) => obs.error(e))\n );\n const objectStore = transaction.objectStore(storeName);\n const hasKey = Boolean(key);\n const request: IDBRequest<IDBValidKey> = hasKey ? objectStore.add(value, key) : objectStore.add(value);\n\n request.onsuccess = async (evt: Event) => {\n const result: any = (evt.target as IDBOpenDBRequest).result;\n const getRequest: IDBRequest = objectStore.get(result) as IDBRequest<T>;\n getRequest.onsuccess = (event: Event) => {\n obs.next((event.target as IDBRequest<T & WithID>).result);\n obs.complete();\n };\n\n getRequest.onerror = (event: Event) => {\n obs.error(event);\n };\n };\n\n request.onerror = (event: Event) => {\n obs.error(event);\n };\n })\n .catch((error) => obs.error(error));\n });\n }\n\n /**\n * Adds new entries in the store and returns its key\n * @param storeName The name of the store to add the item\n * @param values The entries to be added containing optional key attribute\n */\n @CloseDbConnection()\n bulkAdd<T>(storeName: string, values: Array<T & { key?: any }>): Observable<number[]> {\n const promises = new Promise<number[]>((resolve, reject) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db: IDBDatabase) => {\n const transaction = createTransaction(db, optionsGenerator(DBMode.readwrite, storeName, resolve, reject));\n const objectStore = transaction.objectStore(storeName);\n\n const results = values.map((value) => {\n return new Promise<number>((resolve1) => {\n const key = value.key;\n delete value.key;\n\n const hasKey = Boolean(key);\n const request: IDBRequest<IDBValidKey> = hasKey ? objectStore.add(value, key) : objectStore.add(value);\n\n request.onsuccess = (evt: Event) => {\n const result = (evt.target as IDBOpenDBRequest).result;\n resolve1((result as unknown) as number);\n };\n });\n });\n\n resolve(Promise.all(results));\n })\n .catch((reason) => reject(reason));\n });\n\n return from(promises);\n }\n\n /**\n * Delete entries in the store and returns current entries in the store\n * @param storeName The name of the store to add the item\n * @param keys The keys to be deleted\n */\n @CloseDbConnection()\n bulkDelete(storeName: string, keys: IDBValidKey[]): Observable<number[]> {\n const promises = keys.map((key) => {\n return new Promise<number>((resolve, reject) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db: IDBDatabase) => {\n const transaction = createTransaction(db, optionsGenerator(DBMode.readwrite, storeName, reject, resolve));\n const objectStore = transaction.objectStore(storeName);\n objectStore.delete(key);\n\n transaction.oncomplete = () => {\n this.getAll(storeName)\n .pipe(take(1))\n .subscribe((newValues) => {\n resolve(newValues as any);\n });\n };\n })\n .catch((reason) => reject(reason));\n });\n });\n return from(Promise.all(promises));\n }\n\n /**\n * Returns entry by key.\n * @param storeName The name of the store to query\n * @param key The entry key\n */\n @CloseDbConnection()\n getByKey<T>(storeName: string, key: IDBValidKey): Observable<T> {\n return new Observable<T>((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db: IDBDatabase) => {\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const request = objectStore.get(key) as IDBRequest<T>;\n request.onsuccess = (event: Event) => {\n obs.next((event.target as IDBRequest<T>).result);\n obs.complete();\n };\n request.onerror = (event: Event) => {\n obs.error(event);\n };\n })\n .catch((error) => obs.error(error));\n });\n }\n\n /**\n * Retrieve multiple entries in the store\n * @param storeName The name of the store to retrieve the items\n * @param keys The ids entries to be retrieve\n */\n @CloseDbConnection()\n bulkGet<T>(storeName: string, keys: Array<IDBValidKey>): Observable<T[]> {\n const observables = keys.map((key) => this.getByKey<T>(storeName, key));\n\n return new Observable((obs) => {\n combineLatest(observables).subscribe((values) => {\n obs.next(values);\n obs.complete();\n });\n });\n }\n\n /**\n * Returns entry by id.\n * @param storeName The name of the store to query\n * @param id The entry id\n */\n @CloseDbConnection()\n getByID<T>(storeName: string, id: string | number): Observable<T> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db: IDBDatabase) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error, obs.next));\n const objectStore = transaction.objectStore(storeName);\n const request: IDBRequest = objectStore.get(id) as IDBRequest<T>;\n request.onsuccess = (event: Event) => {\n obs.next((event.target as IDBRequest<T>).result);\n obs.complete();\n };\n })\n .catch((error) => obs.error(error));\n });\n }\n\n /**\n * Returns entry by index.\n * @param storeName The name of the store to query\n * @param indexName The index name to filter\n * @param key The entry key.\n */\n @CloseDbConnection()\n getByIndex<T>(storeName: string, indexName: string, key: IDBValidKey): Observable<T> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const index = objectStore.index(indexName);\n const request = index.get(key) as IDBRequest<T>;\n request.onsuccess = (event: Event) => {\n obs.next((event.target as IDBRequest<T>).result);\n obs.complete();\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Return all elements from one store\n * @param storeName The name of the store to select the items\n */\n @CloseDbConnection()\n getAll<T>(storeName: string): Observable<T[]> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error, obs.next));\n const objectStore = transaction.objectStore(storeName);\n\n const request: IDBRequest = objectStore.getAll();\n\n request.onerror = (evt: Event) => {\n obs.error(evt);\n };\n\n request.onsuccess = ({ target: { result: ResultAll } }: RequestEvent<T>) => {\n obs.next(ResultAll as T[]);\n obs.complete();\n };\n })\n .catch((error) => obs.error(error));\n });\n }\n\n /**\n * Adds or updates a record in store with the given value and key. Return all items present in the store\n * @param storeName The name of the store to update\n * @param value The new value for the entry\n */\n @CloseDbConnection()\n update<T>(storeName: string, value: T): Observable<T> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(\n db,\n optionsGenerator(DBMode.readwrite, storeName, (e) => obs.error(e))\n );\n const objectStore = transaction.objectStore(storeName);\n\n const request: IDBRequest<IDBValidKey> = objectStore.put(value);\n\n request.onsuccess = async (evt: Event) => {\n const result: any = (evt.target as IDBOpenDBRequest).result;\n\n const getRequest: IDBRequest = objectStore.get(result) as IDBRequest<T>;\n getRequest.onsuccess = (event: Event) => {\n obs.next((event.target as IDBRequest<T & WithID>).result);\n obs.complete();\n };\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Adds or updates a record in store with the given value and key. Return all items present in the store\n * @param storeName The name of the store to update\n * @param items The values to update in the DB\n *\n * @Return The return value is an Observable with the primary key of the object that was last in given array\n *\n * @error If the call to bulkPut fails the transaction will be aborted and previously inserted entities will be deleted\n */\n @CloseDbConnection()\n public bulkPut<T>(storeName: string, items: T[]): Observable<IDBValidKey> {\n let transaction: IDBTransaction;\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n transaction = createTransaction(\n db,\n optionsGenerator(DBMode.readwrite, storeName, (e) => obs.error(e))\n );\n const objectStore = transaction.objectStore(storeName);\n\n items.forEach((item, index: number) => {\n const request = objectStore.put(item);\n\n if (index === items.length - 1) {\n request.onsuccess = (evt: Event) => {\n transaction.commit();\n obs.next((evt.target as IDBRequest<IDBValidKey>).result);\n obs.complete();\n };\n }\n\n request.onerror = (evt: Event) => {\n transaction.abort();\n obs.error(evt);\n };\n });\n })\n .catch((reason) => {\n transaction?.abort();\n obs.error(reason);\n });\n });\n }\n\n /**\n * Returns all items from the store after delete.\n * @param storeName The name of the store to have the entry deleted\n * @param query The key or key range criteria to apply\n */\n @CloseDbConnection()\n delete<T>(storeName: string, query: IDBValidKey | IDBKeyRange): Observable<T[]> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(\n db,\n optionsGenerator(DBMode.readwrite, storeName, (e) => obs.error(e))\n );\n const objectStore = transaction.objectStore(storeName);\n objectStore.delete(query);\n\n transaction.onerror = (e) => obs.error(e);\n transaction.oncomplete = () => {\n this.getAll<T>(storeName)\n .pipe(take(1))\n .subscribe({\n next: (newValues) => {\n obs.next(newValues);\n },\n error: (e) => obs.error(e),\n complete: () => obs.complete(),\n });\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Returns after a successful delete.\n * @param storeName The name of the store to have the entry deleted\n * @param query The key or key range criteria to apply\n */\n @CloseDbConnection()\n deleteByKey(storeName: string, query: IDBValidKey | IDBKeyRange): Observable<void> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(\n db,\n optionsGenerator(DBMode.readwrite, storeName, (e) => obs.error(e))\n );\n const objectStore = transaction.objectStore(storeName);\n objectStore.delete(query);\n\n transaction.onerror = (e) => obs.error(e);\n transaction.oncomplete = () => {\n obs.next();\n obs.complete();\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Delete all items by an index.\n * @param storeName The name of the store to query\n * @param indexName The index name to filter\n * @param query The key or key range criteria to apply\n * @param direction A string telling the cursor which direction to travel.\n */\n @CloseDbConnection()\n deleteAllByIndex<T>(\n storeName: string,\n indexName: string,\n query?: IDBValidKey | IDBKeyRange | null,\n direction?: IDBCursorDirection\n ): Observable<void> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readwrite, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const index = objectStore.index(indexName);\n const request = index.openCursor(query, direction);\n\n request.onerror = (e) => obs.error(e);\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<NgxIDBCursorWithValue<T>>).result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n } else {\n obs.next();\n obs.complete();\n }\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Clear the data in the objectStore.\n * @param storeName The name of the store to have the entries deleted\n */\n @CloseDbConnection()\n clear(storeName: string): Observable<void> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(\n db,\n optionsGenerator(DBMode.readwrite, storeName, (e) => obs.error(e))\n );\n const objectStore = transaction.objectStore(storeName);\n objectStore.clear();\n\n transaction.onerror = (e) => obs.error(e);\n transaction.oncomplete = () => {\n obs.next();\n obs.complete();\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Delete database.\n */\n @CloseDbConnection()\n deleteDatabase(): Observable<void> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then(async (db) => {\n db.close();\n const deleteDBRequest = this.indexedDB.deleteDatabase(this.dbConfig.name);\n deleteDBRequest.onsuccess = () => {\n obs.next();\n obs.complete();\n };\n deleteDBRequest.onerror = (error) => obs.error(error);\n deleteDBRequest.onblocked = () => {\n console.warn(\n 'Delete blocked: Ensure all tabs, instances, or connections are closed. Database name:',\n this.dbConfig.name\n );\n obs.error(new Error(\"Unable to delete database because it's blocked\"));\n };\n })\n .catch((error) => obs.error(error));\n });\n }\n\n /**\n * Returns the open cursor\n * If no matching data are present, the observable is completed immediately.\n * @param options The options to open the cursor\n * @param options.storeName The name of the store to have the entries deleted\n * @param options.query The key or key range criteria to apply\n * @param options.direction A string telling the cursor which direction to travel\n * @param options.mode The transaction mode.\n */\n @CloseDbConnection()\n openCursor<V = any, P extends IDBValidKey = IDBValidKey, K extends IDBValidKey = IDBValidKey>(options: {\n storeName: string;\n query?: IDBValidKey | IDBKeyRange | null;\n direction?: IDBCursorDirection;\n mode: DBMode;\n }): Observable<NgxIDBCursorWithValue<V, P, K>> {\n const { storeName, query, direction, mode = DBMode.readonly } = options;\n\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(mode, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const request = objectStore.openCursor(query, direction);\n\n transaction.oncomplete = () => obs.complete();\n request.onerror = (e) => obs.error(e);\n request.onsuccess = (event: Event) => {\n const cursor = (event.target as IDBRequest<NgxIDBCursorWithValue<V, P, K>>).result;\n if (cursor) {\n obs.next(cursor);\n }\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Open a cursor by index filter\n * If no matching data are present, the observable is completed immediately.\n * @param options The options to open the cursor\n * @param options.storeName The name of the store to query\n * @param options.indexName The index name to filter\n * @param options.query The key or key range criteria to apply\n * @param options.direction A string telling the cursor which direction to travel\n * @param options.mode The transaction mode.\n */\n @CloseDbConnection()\n openCursorByIndex<V, P extends IDBValidKey = IDBValidKey, K extends IDBValidKey = IDBValidKey>(options: {\n storeName: string;\n indexName: string;\n query?: IDBValidKey | IDBKeyRange | null;\n direction?: IDBCursorDirection;\n mode?: DBMode;\n }): Observable<NgxIDBCursorWithValue<V, P, K>> {\n const { storeName, indexName, query, direction, mode = DBMode.readonly } = options;\n\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(mode, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const index = objectStore.index(indexName);\n const request = index.openCursor(query, direction);\n\n transaction.oncomplete = () => obs.complete();\n request.onerror = (e) => obs.error(e);\n request.onsuccess = (event: Event) => {\n const cursor = (event.target as IDBRequest<NgxIDBCursorWithValue<V, P, K>>).result;\n if (cursor) {\n obs.next(cursor);\n }\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Returns all items by an index.\n * @param storeName The name of the store to query\n * @param indexName The index name to filter\n * @param query The key or key range criteria to apply\n * @param direction A string telling the cursor which direction to travel.\n */\n @CloseDbConnection()\n getAllByIndex<T>(\n storeName: string,\n indexName: string,\n query?: IDBValidKey | IDBKeyRange | null,\n direction?: IDBCursorDirection\n ): Observable<T[]> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const index = objectStore.index(indexName);\n const request = index.openCursor(query, direction);\n\n const data: T[] = [];\n request.onerror = (e) => obs.error(e);\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<NgxIDBCursorWithValue<T>>).result;\n if (cursor) {\n data.push(cursor.value);\n cursor.continue();\n } else {\n obs.next(data);\n obs.complete();\n }\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Returns all primary keys by an index.\n * @param storeName The name of the store to query\n * @param query The key or key range criteria to apply\n * @param direction A string telling the cursor which direction to travel.\n */\n @CloseDbConnection()\n getAllKeysByIndex<P extends IDBValidKey = IDBValidKey, K extends IDBValidKey = IDBValidKey>(\n storeName: string,\n indexName: string,\n query?: IDBValidKey | IDBKeyRange | null,\n direction?: IDBCursorDirection\n ): Observable<IndexKey<P, K>[]> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const index = objectStore.index(indexName);\n\n const data: IndexKey<P, K>[] = [];\n const request = index.openKeyCursor(query, direction);\n request.onerror = (e) => obs.error(e);\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<NgxIDBCursor<P, K>>).result;\n if (cursor) {\n const { primaryKey, key } = cursor;\n data.push({\n primaryKey,\n key,\n });\n cursor.continue();\n } else {\n obs.next(data);\n obs.complete();\n }\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Returns the number of rows in a store.\n * @param storeName The name of the store to query\n * @param query The key or key range criteria to apply.\n */\n @CloseDbConnection()\n count(storeName: string, query?: IDBValidKey | IDBKeyRange): Observable<number> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const request = objectStore.count(query);\n request.onerror = (e) => obs.error(e);\n request.onsuccess = (e) => {\n obs.next((e.target as IDBRequest<number>).result);\n obs.complete();\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Returns the number of records within a key range.\n * @param storeName The name of the store to query\n * @param indexName The index name to filter\n * @param query The key or key range criteria to apply.\n */\n @CloseDbConnection()\n countByIndex(storeName: string, indexName: string, query?: IDBValidKey | IDBKeyRange): Observable<number> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n validateBeforeTransaction(db, storeName, (e) => obs.error(e));\n const transaction = createTransaction(db, optionsGenerator(DBMode.readonly, storeName, obs.error));\n const objectStore = transaction.objectStore(storeName);\n const index = objectStore.index(indexName);\n const request: IDBRequest = index.count(query);\n request.onerror = (e) => obs.error(e);\n request.onsuccess = (e) => {\n obs.next((e.target as IDBRequest<number>).result);\n obs.complete();\n };\n })\n .catch((reason) => obs.error(reason));\n });\n }\n\n /**\n * Delete the store by name.\n * @param storeName The name of the store to query\n */\n deleteObjectStore(storeName: string): Observable<void> {\n return DeleteObjectStore(this.dbConfig.name, ++this.dbConfig.version, storeName);\n }\n\n /**\n * Get all object store names.\n */\n @CloseDbConnection()\n getAllObjectStoreNames(): Observable<string[]> {\n return new Observable((obs) => {\n openDatabase(this.indexedDB, this.dbConfig.name, this.dbConfig.version)\n .then((db) => {\n obs.next(Array.from(db.objectStoreNames));\n obs.complete();\n })\n .catch((reason) => obs.error(reason));\n });\n }\n}\n","/**\n *\n * A class that implements the IDBFactory interface, but only for the server.\n * All methods return a mocked value.\n *\n */\nexport class ServerIndexedDB implements IDBFactory {\n cmp(): number {\n return 0;\n }\n databases(): Promise<IDBDatabaseInfo[]> {\n return Promise.resolve([]);\n }\n\n deleteDatabase(): IDBOpenDBRequest {\n return {\n onupgradeneeded: null,\n onblocked: null,\n onerror: null,\n onsuccess: null,\n error: null,\n } as IDBOpenDBRequest;\n }\n\n open(): IDBOpenDBRequest {\n return {\n onupgradeneeded: null,\n onblocked: null,\n onerror: null,\n onsuccess: null,\n error: null,\n } as IDBOpenDBRequest;\n }\n}\n","import { assertInInjectionContext, inject, PLATFORM_ID, DOCUMENT } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { SERVER_INDEXED_DB } from '../lib/ngx-indexed-db.meta';\nimport { ServerIndexedDB } from './server-indexed-db';\n\n/**\n * Factory function for creating an instance of IDBFactory.\n *\n * It determines the platform using the {@link PLATFORM_ID} to decide whether to use the\n * browser's IndexedDB or a server-side implementation.\n *\n * @returns {IDBFactory} IDBFactory.\n */\nexport function indexedDbFactory(): IDBFactory {\n assertInInjectionContext(indexedDbFactory);\n const platformId = inject(PLATFORM_ID);\n const serverIndexedDB = inject(SERVER_INDEXED_DB, { optional: true }) ?? new ServerIndexedDB();\n return isPlatformBrowser(platformId) ? inject(DOCUMENT).defaultView.indexedDB : serverIndexedDB;\n}\n","import { makeEnvironmentProviders, Provider } from '@angular/core';\nimport { DBConfig, CONFIG_TOKEN, INDEXED_DB } from './ngx-indexed-db.meta';\nimport { NgxIndexedDBService } from './ngx-indexed-db.service';\nimport { indexedDbFactory } from '../ssr';\n\nexport const provideIndexedDb = (...dbConfigs: DBConfig[]) => {\n return makeEnvironmentProviders([..._provideIndexedDb(...dbConfigs)]);\n};\n\nexport const _provideIndexedDb = (...dbConfigs: DBConfig[]): Provider[] => {\n const configs = dbConfigs.reduce<Record<string, DBConfig>>((acc, curr) => {\n acc[curr.name] = curr;\n return acc;\n }, {});\n return [\n NgxIndexedDBService,\n { provide: CONFIG_TOKEN, useValue: configs },\n { provide: INDEXED_DB, useFactory: indexedDbFactory },\n ];\n};\n","import { NgModule, ModuleWithProviders } from '@angular/core';\nimport { DBConfig } from './ngx-indexed-db.meta';\nimport { _provideIndexedDb } from './provide-indexed-db';\n\n@NgModule()\nexport class NgxIndexedDBModule {\n static forRoot(...dbConfigs: DBConfig[]): ModuleWithProviders<NgxIndexedDBModule> {\n return {\n ngModule: NgxIndexedDBModule,\n providers: [..._provideIndexedDb(...dbConfigs)],\n };\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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