utils_userIndexedDB.mjs

/** 
### mapp.utils.userIndexedDB

The module exports method to store and retrieve objects from a userIndexedDB.

@module /utils/userIndexedDB
*/

let IDB;

/**
@function openDB
@description
The method is called from any transaction method to interact with the userIndexedDB.

A new database will be created when attempting to open a DB which does not exist.

A new store will be created when a new DB is upgraded [on creation].

@param {object} params 
@property {string} params.store
@property {string} params.user
@property {string} params.workspace
@returns {Promise} OpenDBPromise
*/
export async function openDB(params) {
  const OpenDBPromise = new Promise((resolve, reject) => {
    // will create a new database if db/version doesn't exist.
    const IDBRequest = indexedDB.open(`${params.user}|${params.workspace}`, 3);

    IDBRequest.onerror = (event) => {
      console.error(IDBRequest.error);
      resolve(IDBRequest);
    };

    IDBRequest.onsuccess = (event) => {
      if (!event.target.result.objectStoreNames.contains(params.store)) {
        console.warn(
          `UserIDB '${params.user}|${params.workspace}' doesn't contain "${params.store}" objectStore.`,
        );

        IDB = null;
        resolve();
      }

      IDB = event.target.result;
      resolve();
    };

    // will be called on database versioning.
    IDBRequest.onupgradeneeded = (event) => {
      // onsuccess method will be called after the object store is created.
      event.target.result.createObjectStore(params.store);
    };
  });

  await OpenDBPromise;

  return OpenDBPromise;
}

/**
@function deleteDB
*/
export async function deleteDB(params) {
  indexedDB.deleteDatabase(`${params.user}|${params.db}`);
}

/**
@function add
@param {Object} params 
@property {string} params.user 
@property {string} params.db 
@property {string} params.store 
@property {object} params.key 
@returns {Promise} addPromise
*/
export async function add(params) {
  if (!IDB) await openDB(params);

  const addPromise = new Promise((resolve, reject) => {
    const IDBTransaction = IDB.transaction([params.store], 'readwrite');

    const objectStore = IDBTransaction.objectStore(params.store);

    const IDBRequest = objectStore.add(params.obj);

    IDBRequest.onerror = (event) => {
      console.error(IDBRequest.error);
      resolve(IDBRequest);
    };

    IDBRequest.onsuccess = (event) => {
      resolve(event.target.result);
    };
  });

  await addPromise;

  return addPromise;
}

/**
@function get
@param {Object} params 
@property {string} params.user 
@property {string} params.workspace 
@property {string} params.store 
@property {object} params.name 
@returns {Promise} getPromise
*/
export async function get(params) {
  if (!IDB) await openDB(params);

  const getPromise = new Promise((resolve, reject) => {
    const IDBTransaction = IDB.transaction([params.store], 'readwrite');

    const objectStore = IDBTransaction.objectStore(params.store);

    const IDBRequest = objectStore.get(params.name);

    IDBRequest.onerror = (event) => {
      console.error(IDBRequest.error);
      reject(IDBRequest);
    };

    IDBRequest.onsuccess = (event) => {
      resolve(IDBRequest.result);
    };
  });

  await getPromise;

  return getPromise;
}

export async function list(params) {
  if (!IDB) await openDB(params);

  const getPromise = new Promise((resolve, reject) => {
    const IDBTransaction = IDB.transaction([params.store], 'readwrite');

    const objectStore = IDBTransaction.objectStore(params.store);

    const IDBRequest = objectStore.getAll();

    IDBRequest.onerror = (event) => {
      console.error(IDBRequest.error);
      reject(IDBRequest);
    };

    IDBRequest.onsuccess = (event) => {
      const locales = IDBRequest.result.map((locale) => ({
        key: locale.key,
        name: locale.name,
      }));
      resolve(locales);
    };
  });

  await getPromise;

  return getPromise;
}

/**
@function put
@param {Object} params 
@property {string} params.user 
@property {string} params.db 
@property {string} params.store 
@property {object} params.obj 
@returns {Promise} updatePromise
*/
export async function put(params) {
  if (!IDB) await openDB(params);

  const updatePromise = new Promise((resolve, reject) => {
    const IDBTransaction = IDB.transaction([params.store], 'readwrite');

    const objectStore = IDBTransaction.objectStore(params.store);

    const IDBRequest = objectStore.put(params.obj, params.name);

    IDBRequest.onerror = (event) => {
      console.error(IDBRequest.error);
      reject(IDBRequest);
    };

    IDBRequest.onsuccess = (event) => {
      resolve(IDBRequest.result);
    };
  });

  await updatePromise;

  return updatePromise;
}

export async function remove(params) {
  if (!IDB) await openDB(params);

  const removePromise = new Promise((resolve, reject) => {
    const IDBTransaction = IDB.transaction([params.store], 'readwrite');

    const objectStore = IDBTransaction.objectStore(params.store);

    const IDBRequest = objectStore.delete(params.name);

    IDBRequest.onerror = (event) => {
      console.error(IDBRequest.error);
      reject(IDBRequest);
    };

    IDBRequest.onsuccess = (event) => {
      resolve();
    };
  });

  await removePromise;

  return removePromise;
}