/** * @module lib/idb */ /* eslint-env browser */ import * as globals from './globals.js' /* * IDB Request to Promise transformer */ export const rtop = request => globals.createPromise((resolve, reject) => { request.onerror = event => reject(new Error(event.target.error)) request.onblocked = () => location.reload() request.onsuccess = event => resolve(event.target.result) }) /** * @param {string} name * @param {Function} initDB Called when the database is first created * @return {Promise} */ export const openDB = (name, initDB) => globals.createPromise((resolve, reject) => { let request = indexedDB.open(name) /** * @param {any} event */ request.onupgradeneeded = event => initDB(event.target.result) /** * @param {any} event */ request.onerror = event => reject(new Error(event.target.error)) request.onblocked = () => location.reload() /** * @param {any} event */ request.onsuccess = event => { const db = event.target.result db.onversionchange = () => { db.close() } addEventListener('unload', () => db.close()) resolve(db) } }) export const deleteDB = name => rtop(indexedDB.deleteDatabase(name)) export const createStores = (db, definitions) => definitions.forEach(d => db.createObjectStore.apply(db, d) ) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | Array } key * @return {Promise} */ export const get = (store, key) => rtop(store.get(key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | IDBKeyRange | Array } key */ export const del = (store, key) => rtop(store.delete(key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | boolean} item * @param {String | number | ArrayBuffer | Date | Array} [key] */ export const put = (store, item, key) => rtop(store.put(item, key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | boolean} item * @param {String | number | ArrayBuffer | Date | Array} [key] * @return {Promise} */ export const add = (store, item, key) => rtop(store.add(item, key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date} item * @return {Promise} */ export const addAutoKey = (store, item) => rtop(store.add(item)) /** * @param {IDBObjectStore} store * @param {IDBKeyRange} [range] */ export const getAll = (store, range) => rtop(store.getAll(range)) /** * @param {IDBObjectStore} store * @param {IDBKeyRange} [range] */ export const getAllKeys = (store, range) => rtop(store.getAllKeys(range)) /** * @typedef KeyValuePair * @type {Object} * @property {any} k key * @property {any} v Value */ /** * @param {IDBObjectStore} store * @param {IDBKeyRange} [range] * @return {Promise>} */ export const getAllKeysValues = (store, range) => globals.pall([getAllKeys(store, range), getAll(store, range)]).then(([ks, vs]) => ks.map((k, i) => ({ k, v: vs[i] }))) /** * Iterate on keys and values * @param {IDBObjectStore} store * @param {IDBKeyRange?} keyrange * @param {Function} f Return true in order to continue the cursor */ export const iterate = (store, keyrange, f) => globals.createPromise((resolve, reject) => { const request = store.openCursor(keyrange) request.onerror = reject /** * @param {any} event */ request.onsuccess = event => { const cursor = event.target.result if (cursor === null) { return resolve() } f(cursor.value, cursor.key) cursor.continue() } }) /** * Iterate on the keys (no values) * * @param {IDBObjectStore} store * @param {IDBKeyRange} keyrange * @param {function} f Call `idbcursor.continue()` to iterate further */ export const iterateKeys = (store, keyrange, f) => { /** * @param {any} event */ store.openKeyCursor(keyrange).onsuccess = event => f(event.target.result) } /** * Open store from transaction * @param {IDBTransaction} t * @param {String} store * @returns {IDBObjectStore} */ export const getStore = (t, store) => t.objectStore(store) export const createIDBKeyRangeBound = (lower, upper, lowerOpen, upperOpen) => IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen) export const createIDBKeyRangeUpperBound = (upper, upperOpen) => IDBKeyRange.upperBound(upper, upperOpen) export const createIDBKeyRangeLowerBound = (lower, lowerOpen) => IDBKeyRange.lowerBound(lower, lowerOpen)