mirror of
				https://github.com/therootcompany/request.js
				synced 2025-11-03 21:52:46 +00:00 
			
		
		
		
	feat: add fetch-based browser.js
This commit is contained in:
		
							parent
							
								
									961740ffa0
								
							
						
					
					
						commit
						2e233391b9
					
				
							
								
								
									
										198
									
								
								browser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								browser.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,198 @@
 | 
				
			|||||||
 | 
					'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) {
 | 
				
			||||||
 | 
					        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.<String, String>}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
@ -3,6 +3,9 @@
 | 
				
			|||||||
    "version": "1.8.3",
 | 
					    "version": "1.8.3",
 | 
				
			||||||
    "description": "A lightweight, zero-dependency drop-in replacement for request",
 | 
					    "description": "A lightweight, zero-dependency drop-in replacement for request",
 | 
				
			||||||
    "main": "index.js",
 | 
					    "main": "index.js",
 | 
				
			||||||
 | 
					    "browser": {
 | 
				
			||||||
 | 
					        "index.js": "browser.js"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "files": [
 | 
					    "files": [
 | 
				
			||||||
        "lib"
 | 
					        "lib"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user