utils_xhr.mjs

/**
## /utils/xhr

Export the default xhr method to mapp.utils{}.

@module /utils/xhr
*/

/**
@function xhr

@description
The params object/string for the xhr utility method is required.

The params are assumed to the request URL if provided as a string argument.

The request params and response are stored in a Map() if the cache flag is set in the params object argument.

The method is assumed to be 'POST' if a params.body is provided.

@param {Object} params The object containing the parameters.
@property {string} params.url The request URL.
@property {string} [params.method=GET] The request method.
@property {string} [params.responseType=json] The XHR responseType.
@property {Object} [params.requestHeader={'Content-Type': 'application/json'}] The XHR requestHeader.
@property {string} [params.body] A stringified request body for a 'POST' request.
@property {boolean} [params.resolveTarget] Whether the target instead of target.response should be resolved.
@property {boolean} [params.cache] Whether the response should be cached in a Map().

@returns {Promise} A promise that resolves with the XHR.
*/
const requestMap = new Map()

export function xhr(params) {

  return new Promise(resolve => {

    // Return if params are falsy.
    if (!params) {
      console.error(`xhr params are falsy.`)
      return;
    }

    // Set params as object with url from string.
    params = typeof params === 'string' ? { url: params } : params

    // A request url must be provided.
    if (!params.url) {
      console.error(`no xhr request url has been provided.`)
      return;
    };

    // Check whether a request with the same params has already been resolved.
    if (params.cache && requestMap.has(params)) return resolve(requestMap.get(params))

    // Assign 'GET' as default method if no body is provided.
    params.method ??= params.body ? 'POST' : 'GET'

    const xhr = new XMLHttpRequest()

    xhr.open(params.method, params.url)

    // Use requestHeader: null to prevent assignment of requestHeader.
    if (params.requestHeader !== null) {

      // Butter (spread) over requestHeader.
      const requestHeader = {
        'Content-Type': 'application/json',
        ...params.requestHeader
      }

      Object.entries(requestHeader).forEach(entry => xhr.setRequestHeader(...entry))
    }

    xhr.responseType = params.responseType || 'json'

    xhr.onload = e => {

      if (e.target.status >= 400) {
        resolve(new Error(e.target.status))
        return;
      }

      // Cache the response in the requestMap
      params.cache && requestMap.set(params, e.target.response)

      resolve(params.resolveTarget ? e.target : e.target.response)
    }

    xhr.onerror = (e) => resolve(new Error(e))

    xhr.send(params.body)
  })
}