export const DEFAULT_TIMEOUT = 20000 export const MEDIA_WRITE_TIMEOUT = 90000 // media uploads can take awhile export const WRITE_TIMEOUT = 45000 // allow more time if the user did a write action function fetchWithTimeout (url, fetchOptions, timeout) { return new Promise((resolve, reject) => { fetch(url, fetchOptions).then(resolve, reject) setTimeout(() => reject(new Error(`Timed out after ${timeout / 1000} seconds`)), timeout) }) } function makeFetchOptions (method, headers, options) { const res = { method, headers: Object.assign(headers || {}, { Accept: 'application/json' }) } if (options && options.signal) { res.signal = options.signal } return res } async function throwErrorIfInvalidResponse (response) { if (response.status >= 300) { const err = new Error('Request failed: ' + response.status) err.status = response.status throw err } const json = await response.json() if (response.status >= 200 && response.status < 300) { return { json, headers: response.headers } } if (json && json.error) { throw new Error(response.status + ': ' + json.error) } throw new Error('Request failed: ' + response.status) } async function _fetch (url, fetchOptions, options) { let response if (options && options.timeout) { response = await fetchWithTimeout(url, fetchOptions, options.timeout) } else { response = await fetch(url, fetchOptions) } return throwErrorIfInvalidResponse(response) } async function _putOrPostOrPatch (method, url, body, headers, options) { const fetchOptions = makeFetchOptions(method, headers, options) if (body) { if (body instanceof FormData || body instanceof URLSearchParams) { fetchOptions.body = body } else { fetchOptions.body = JSON.stringify(body) fetchOptions.headers['Content-Type'] = 'application/json' } } return _fetch(url, fetchOptions, options) } export async function put (url, body, headers, options) { return (await _putOrPostOrPatch('PUT', url, body, headers, options)).json } export async function post (url, body, headers, options) { return (await _putOrPostOrPatch('POST', url, body, headers, options)).json } export async function patch (url, body, headers, options) { return (await _putOrPostOrPatch('PATCH', url, body, headers, options)).json } export async function get (url, headers, options) { return (await _fetch(url, makeFetchOptions('GET', headers, options), options)).json } /** @returns {json, headers} */ export async function getWithHeaders (url, headers, options) { return _fetch(url, makeFetchOptions('GET', headers, options), options) } export async function del (url, headers, options) { return (await _fetch(url, makeFetchOptions('DELETE', headers, options), options)).json } export function paramsString (paramsObject) { let res = '' let count = -1 Object.keys(paramsObject).forEach(key => { const value = paramsObject[key] if (Array.isArray(value)) { // rails convention for encoding multiple values for (const item of value) { res += (++count > 0 ? '&' : '') + encodeURIComponent(key) + '[]=' + encodeURIComponent(item) } } else { res += (++count > 0 ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value) } }) return res }