'use strict'; // `fetch` will be available for node and browsers as a global //var fetch = window.fetch; // https://developer.mozilla.org/en-US/docs/Web/API/fetch // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch let _fetchDefaults = { method: 'GET', // *GET, POST, PATCH, PUT, DELETE, etc headers: {}, body: undefined, // String, ArrayBuffer, FormData, etc mode: 'cors', // no-cors, *cors, same-origin credentials: 'same-origin', // omit, *same-origin, include cache: 'default', // *default, no-store, reload, no-cache, force-cache, only-if-cached redirect: 'follow', // *follow, error, manual, referrer: undefined, referrerPolicy: 'no-referrer-when-downgrade', // no-referrer, *no-referrer-when-downgrade, same-origin, origin, strict-origin, origin-when-cross-origin, strict-origin-when-cross-origin, unsafe-url integrity: '', keepalive: false, signal: null // }; let _optionKeys = Object.keys(_fetchDefaults).concat([ //'encoding', // N/A //'stream', // TODO via getReader //'json' // handled manually //'form', // TODO //'auth' // handled manually //'formData', // TODO //'FormData', // TODO //'userAgent' // not allowed, non-standard for request.js ]); function setDefaults(_defs) { return async function request(opts) { if ('string' === typeof opts) { opts = { url: opts }; } let reqOpts = { headers: {} }; if ( opts.body || (opts.json && true !== opts.json) || opts.form || opts.formData ) { // TODO this is probably a deviation from request's API // need to check and probably eliminate it reqOpts.method = (reqOpts.method || 'POST').toUpperCase(); } else { reqOpts.method = (reqOpts.method || 'GET').toUpperCase(); } _optionKeys.forEach(function (key) { if (key in opts) { if ('undefined' !== typeof opts[key]) { reqOpts[key] = opts[key]; } } else if (key in _defs) { reqOpts[key] = _defs[key]; } }); if (opts.auth) { // if opts.uri specifies auth it will be parsed by url.parse and passed directly to the http module if ('string' !== typeof opts.auth) { let u = opts.auth.user || opts.auth.username || ''; let p = opts.auth.pass || opts.auth.password || ''; reqOpts.headers.Authorization = encodeBasicAuth(`${u}:${p}`); } else if ('string' === typeof opts.auth) { reqOpts.headers.Authorization = encodeBasicAuth(`${opts.auth}`); } // [request-compat] if (opts.auth.bearer) { // having a shortcut for base64 encoding makes sense, // but this? Eh, whatevs... reqOpts.header.Authorization = `Bearer ${opts.auth.bearer}`; } } let body; if (opts.json && true !== opts.json) { if (!opts.headers['content-type']) { opts.headers['content-type'] = 'application/json'; } body = JSON.stringify(opts.json); if (!opts.method) { opts.method = 'POST'; } } // The node version will send HTTP Auth by default, but not Cookies. // We don't have an equivalent option for `fetch`. Furthermore, // `fetch` caches HTTP Auth Basic across browser refreshes, // which is not analogous to the node behavior. // // "In the face of ambiguity, refuse the temptation to guess" // //if (!('credentials' in opts)) { // opts.credentials = 'include'; //} if (!('mode' in opts)) { reqOpts.mode = 'cors'; } if (!('body' in opts)) { if (body) { reqOpts.body = body; } } let resp = await fetch(opts.url, reqOpts); let result = { ok: resp.ok, headers: headersToObj(resp.headers), body: undefined, // swapped to match request.js statusCode: resp.status, status: resp.statusText, request: reqOpts, response: resp }; result.toJSON = function () { return { ok: result.ok, headers: result.headers, body: result.body, statusCode: result.statusCode, status: result.status }; }; // return early if there's no body if (!result.headers['content-type']) { return result; } // TODO blob, formData ? if (null === opts.encoding) { return await resp.arrayBuffer(); } if (!opts.json) { result.body = await resp.text(); } else { result.body = await resp.json().catch(async function () { return await resp.text(); }); } return result; }; } /** * @param {Iterable.<*>} rheaders * @returns {Object.} */ function headersToObj(rheaders) { /* Array.from(resp.headers.entries()).forEach(function (h) { headers[h[0]] = h[1]; }); */ let headerNames = Array.from(rheaders.keys()); let resHeaders = {}; headerNames.forEach(function (k) { resHeaders[k] = rheaders.get(k); }); return resHeaders; } /** * @param {String} utf8 * @returns {String} */ function encodeBasicAuth(utf8) { let b64 = unicodeToBase64(utf8); return `Basic ${b64}`; } /** * @param {String} utf8 * @returns {String} */ function unicodeToBase64(utf8) { let str = ''; let uint8 = new TextEncoder().encode(utf8); uint8.forEach(function (b) { str += String.fromCharCode(b); }); let b64 = btoa(str); return b64; } let defaultRequest = setDefaults({ mode: 'cors' }); exports.request = defaultRequest; exports.defaults = setDefaults; module.exports = defaultRequest; module.exports.defaults = setDefaults;