import { deleteDB, openDB } from 'idb'
import { OpenDBCallbacks, IDBPDatabase, DBSchema, StoreNames } from 'idb/build/esm/entry'
import {
IDB,
IDBOptions,
GetAllParams,
GetItemParams,
AddItemParams,
PutItemParams,
GetItemResult,
CreateTxParams,
GetItemsResult,
ClearStoreParams,
GetFromIndexParams,
CreateMultiTxParams,
GetAllFromIndexParams,
} from './@types'
const VERSION_ERROR = 'less than the existing version'
const INDEX_DB_ERROR = 'A mutation operation was attempted on a database that did not allow mutations.'
const IDB_VERSION = 9
// TODO method for migration, remove indexed
class IndexedDB implements IDB {
public dbName: string
public dbExists: boolean
public isBlocked: boolean
public db: IDBPDatabase
public options: OpenDBCallbacks
public constructor({ stores, dbName }: IDBOptions) {
this.dbExists = false
this.isBlocked = false
this.options = {
upgrade(db: IDBPDatabase) {
Object.values(db.objectStoreNames).forEach((value) => {
db.deleteObjectStore(value)
})
stores.forEach(({ name, keyPath, indexes }) => {
const store = db.createObjectStore(name as StoreNames, {
keyPath,
autoIncrement: true,
})
if (Array.isArray(indexes)) {
indexes.forEach(({ name, unique = false }) => {
store.createIndex(name, String(name), { unique })
})
}
})
},
}
this.dbName = dbName
}
public async initDB() {
try {
if (this.dbExists) {
return
}
this.db = await openDB(this.dbName, IDB_VERSION, this.options) // version (optional): Schema version, or undefined to open the current version.
this.onEventHandler()
this.dbExists = true
} catch (err) {
// need for private mode firefox browser
if (err.message.includes(INDEX_DB_ERROR)) {
this.isBlocked = true
return
}
if (err.message.includes(VERSION_ERROR)) {
await this.removeExist()
}
console.error(`initDB has error: ${err.message}`)
}
}
public async createTransactions
({ storeName, data, mode = 'readwrite' }: CreateTxParams) {
try {
const tx = this.db.transaction(storeName, mode)
const storedItem = tx.objectStore(storeName)
if (storedItem.add) {
await storedItem.add(data)
await tx.done
}
} catch (err) {
throw new Error(`Method createTransactions has error: ${err.message}`)
}
}
public createMultipleTransactions
({
storeName,
data,
index,
mode = 'readwrite',
}: CreateMultiTxParams ({ storeName, data, key }: AddItemParams ({ storeName, data }: PutItemParams) {
try {
const tx = this.db.transaction(storeName, mode)
data.forEach((item) => {
if (item && tx.store && tx.store.put) {
tx.store.put({ ...item, ...index })
}
})
} catch (err) {
throw new Error(`Method createMultipleTransactions has error: ${err.message}`)
}
}
public async getFromIndex(params: GetFromIndexParams): Promise): Promise) {
try {
const tx = this.db.transaction(storeName, 'readwrite')
const isExist = await tx.objectStore(storeName).get(key)
if (!isExist) {
await tx.objectStore(storeName).add(data)
}
} catch (err) {
throw new Error(`Method addItem has error: ${err.message}`)
}
}
public async putItem) {
try {
if (this.isBlocked) {
return
}
const tx = this.db.transaction(storeName, 'readwrite')
await tx.objectStore(storeName).put(data)
} catch (err) {
throw new Error(`Method putItem has error: ${err.message}`)
}
}
public async getAll({ storeName }: GetAllParams): Promise) {
try {
const tx = this.db.transaction(storeName, mode)
const storedItem = tx.objectStore(storeName)
if (storedItem.clear) {
await storedItem.clear()
}
} catch (err) {
throw new Error(`Method clearStore has error: ${err.message}`)
}
}
public async getAllFromIndex(params: GetAllFromIndexParams): Promise) {
try {
const value = await this.db.getFromIndex(storeName, indexName, key)
return value
} catch (err) {
throw new Error(`Method getFromIndexHandler has error: ${err.message}`)
}
}
private async getAllFromIndexHandler({ storeName, indexName, key, count }: GetAllFromIndexParams): GetItemsResult {
try {
const value = await this.db.getAllFromIndex(storeName, indexName, key, count)
return value
} catch (err) {
throw new Error(`Method getAllFromIndex has error: ${err.message}`)
}
}
}
export { IndexedDB }