text: const axios = require("axios");
  const FormData = require('form-data')
  const fs = require('fs')
  
  const METHOD_GET = 'GET'
  const METHOD_POST = 'POST'
  
  /**
   * @param {Object} param0
   * @param {string} param0.method HTTP Method
   * @param {{ baseURL: string; timeout: number; headers: { [name: string]: string } }} param0.instanceConfig
   * @param {string} param0.data Request Body as string, default {}
   * @param {string} param0.files Map of Request Files (name: absolute path) as JSON String, default: {}
   * @param {{ username: string; password: string }|undefined} param0.auth Optional HTTP Basic Auth
   * @param {*} param0.actions 
   * @param {number[]} param0.ignoredCodes Prevent Action to fail if the API response with one of this StatusCodes
   * @param {boolean} param0.preventFailureOnNoResponse Prevent Action to fail if the API respond without Response
   * @param {boolean} param0.escapeData Escape unescaped JSON content in data
   *
   * @returns {void}
   */
  const request = async({ method, instanceConfig, data, files, auth, actions, ignoredCodes, preventFailureOnNoResponse, escapeData }) => {
    try {
      if (escapeData) {
        data = data.replace(/"[^"]*"/g, (match) => { 
          return match.replace(/[\n\r]\s*/g, "\\n");
        }); 
      }
  
      if (method === METHOD_GET) {
        data = undefined;
      }
  
      if (files && files !== '{}') {
        filesJson = convertToJSON(files)
        dataJson = convertToJSON(data)
  
        if (Object.keys(filesJson).length > 0) {
          try {
            data = convertToFormData(dataJson, filesJson)
            instanceConfig = await updateConfig(instanceConfig, data, actions)
          } catch(error) {
            actions.setFailed({ message: `Unable to convert Data and Files into FormData: ${error.message}`, data: dataJson, files: filesJson })
            return
          }
        }
      }
  
      const requestData = {
        auth,
        method,
        data,
        maxContentLength: Infinity,
        maxBodyLength: Infinity
      }
  
      actions.debug('Instance Configuration: ' + JSON.stringify(instanceConfig))
      
      const instance = axios.create(instanceConfig);
  
      actions.debug('Request Data: ' + JSON.stringify(requestData))
  
      const response = await instance.request(requestData)
  
      actions.setOutput('response', JSON.stringify(response.data))
    } catch (error) {
      if (error.toJSON) {
        actions.setOutput('requestError', JSON.stringify(error.toJSON()));
      }
  
      if (error.response && ignoredCodes.includes(error.response.status)) {
        actions.warning(JSON.stringify({ code: error.response.status, message: error.response.data }))
      } else if (error.response) {
        actions.setFailed(JSON.stringify({ code: error.response.status, message: error.response.data }))
      } else if (error.request && !preventFailureOnNoResponse) {
        actions.setFailed(JSON.stringify({ error: "no response received" }));
      } else if (error.request && preventFailureOnNoResponse) {
        actions.warning(JSON.stringify(error));
      } else {
        actions.setFailed(JSON.stringify({ message: error.message, data }));
      }
    }
  }
  
  /**
   * @param {string} value
   *
   * @returns {Object}
   */
  const convertToJSON = (value) => {
    try {
      return JSON.parse(value)
    } catch(e) {
      return {}
    }
  }
  
  /**
   * @param {Object} data
   * @param {Object} files
   *
   * @returns {FormData}
   */
  const convertToFormData = (data, files) => {
    formData = new FormData()
  
    for (const [key, value] of Object.entries(data)) {
      formData.append(key, value)
    }
  
    for (const [key, value] of Object.entries(files)) {
      formData.append(key, fs.createReadStream(value))
    }
  
    return formData
  }
  
  /**
   * @param {{ baseURL: string; timeout: number; headers: { [name: string]: string } }} instanceConfig
   * @param {FormData} formData
   * @param {*} actions
   *
   * @returns {{ baseURL: string; timeout: number; headers: { [name: string]: string } }}
   */
  const updateConfig = async (instanceConfig, formData, actions) => {
    try {
      const formHeaders = formData.getHeaders()
      const contentType = formHeaders['content-type']
  
      delete formHeaders['content-type']
  
      return { 
        ...instanceConfig, 
        headers: { 
          ...instanceConfig.headers, 
          ...formHeaders,
          'Content-Length': await contentLength(formData),
          'Content-Type': contentType
        }
      }
    } catch(error) {
      actions.setFailed({ message: `Unable to read Content-Length: ${error.message}`, data, files })
    }
  }
  
  /**
   * @param {FormData} formData
   *
   * @returns {Promise<number>}
   */
  const contentLength = (formData) => new Promise((resolve, reject) => {
    formData.getLength((err, length) => {
      if (err) {
        reject (err)
        return
      }
  
      resolve(length)
    })
  })
  
  module.exports = {
    request,
    METHOD_POST,
    METHOD_GET,
  }
  