234 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import deepmerge from 'deepmerge';
 | |
| import isPlainObject from 'is-plain-object';
 | |
| import urlTemplate from 'url-template';
 | |
| import getUserAgent from 'universal-user-agent';
 | |
| 
 | |
| function _slicedToArray(arr, i) {
 | |
|   return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
 | |
| }
 | |
| 
 | |
| function _arrayWithHoles(arr) {
 | |
|   if (Array.isArray(arr)) return arr;
 | |
| }
 | |
| 
 | |
| function _iterableToArrayLimit(arr, i) {
 | |
|   var _arr = [];
 | |
|   var _n = true;
 | |
|   var _d = false;
 | |
|   var _e = undefined;
 | |
| 
 | |
|   try {
 | |
|     for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
 | |
|       _arr.push(_s.value);
 | |
| 
 | |
|       if (i && _arr.length === i) break;
 | |
|     }
 | |
|   } catch (err) {
 | |
|     _d = true;
 | |
|     _e = err;
 | |
|   } finally {
 | |
|     try {
 | |
|       if (!_n && _i["return"] != null) _i["return"]();
 | |
|     } finally {
 | |
|       if (_d) throw _e;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return _arr;
 | |
| }
 | |
| 
 | |
| function _nonIterableRest() {
 | |
|   throw new TypeError("Invalid attempt to destructure non-iterable instance");
 | |
| }
 | |
| 
 | |
| function lowercaseKeys(object) {
 | |
|   if (!object) {
 | |
|     return {};
 | |
|   }
 | |
| 
 | |
|   return Object.keys(object).reduce((newObj, key) => {
 | |
|     newObj[key.toLowerCase()] = object[key];
 | |
|     return newObj;
 | |
|   }, {});
 | |
| }
 | |
| 
 | |
| function merge(defaults, route, options) {
 | |
|   if (typeof route === "string") {
 | |
|     let _route$split = route.split(" "),
 | |
|         _route$split2 = _slicedToArray(_route$split, 2),
 | |
|         method = _route$split2[0],
 | |
|         url = _route$split2[1];
 | |
| 
 | |
|     options = Object.assign(url ? {
 | |
|       method,
 | |
|       url
 | |
|     } : {
 | |
|       url: method
 | |
|     }, options);
 | |
|   } else {
 | |
|     options = route || {};
 | |
|   } // lowercase header names before merging with defaults to avoid duplicates
 | |
| 
 | |
| 
 | |
|   options.headers = lowercaseKeys(options.headers);
 | |
|   const mergedOptions = deepmerge.all([defaults, options].filter(Boolean), {
 | |
|     isMergeableObject: isPlainObject
 | |
|   }); // mediaType.previews arrays are merged, instead of overwritten
 | |
| 
 | |
|   if (defaults && defaults.mediaType.previews.length) {
 | |
|     mergedOptions.mediaType.previews = defaults.mediaType.previews.filter(preview => !mergedOptions.mediaType.previews.includes(preview)).concat(mergedOptions.mediaType.previews);
 | |
|   }
 | |
| 
 | |
|   mergedOptions.mediaType.previews = mergedOptions.mediaType.previews.map(preview => preview.replace(/-preview/, ""));
 | |
|   return mergedOptions;
 | |
| }
 | |
| 
 | |
| function addQueryParameters(url, parameters) {
 | |
|   const separator = /\?/.test(url) ? "&" : "?";
 | |
|   const names = Object.keys(parameters);
 | |
| 
 | |
|   if (names.length === 0) {
 | |
|     return url;
 | |
|   }
 | |
| 
 | |
|   return url + separator + names.map(name => {
 | |
|     if (name === "q") {
 | |
|       return "q=" + parameters.q.split("+").map(encodeURIComponent).join("+");
 | |
|     }
 | |
| 
 | |
|     return "".concat(name, "=").concat(encodeURIComponent(parameters[name]));
 | |
|   }).join("&");
 | |
| }
 | |
| 
 | |
| const urlVariableRegex = /\{[^}]+\}/g;
 | |
| 
 | |
| function removeNonChars(variableName) {
 | |
|   return variableName.replace(/^\W+|\W+$/g, "").split(/,/);
 | |
| }
 | |
| 
 | |
| function extractUrlVariableNames(url) {
 | |
|   const matches = url.match(urlVariableRegex);
 | |
| 
 | |
|   if (!matches) {
 | |
|     return [];
 | |
|   }
 | |
| 
 | |
|   return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []);
 | |
| }
 | |
| 
 | |
| function omit(object, keysToOmit) {
 | |
|   return Object.keys(object).filter(option => !keysToOmit.includes(option)).reduce((obj, key) => {
 | |
|     obj[key] = object[key];
 | |
|     return obj;
 | |
|   }, {});
 | |
| }
 | |
| 
 | |
| function parse(options) {
 | |
|   // https://fetch.spec.whatwg.org/#methods
 | |
|   let method = options.method.toUpperCase(); // replace :varname with {varname} to make it RFC 6570 compatible
 | |
| 
 | |
|   let url = options.url.replace(/:([a-z]\w+)/g, "{+$1}");
 | |
|   let headers = Object.assign({}, options.headers);
 | |
|   let body;
 | |
|   let parameters = omit(options, ["method", "baseUrl", "url", "headers", "request", "mediaType"]); // extract variable names from URL to calculate remaining variables later
 | |
| 
 | |
|   const urlVariableNames = extractUrlVariableNames(url);
 | |
|   url = urlTemplate.parse(url).expand(parameters);
 | |
| 
 | |
|   if (!/^http/.test(url)) {
 | |
|     url = options.baseUrl + url;
 | |
|   }
 | |
| 
 | |
|   const omittedParameters = Object.keys(options).filter(option => urlVariableNames.includes(option)).concat("baseUrl");
 | |
|   const remainingParameters = omit(parameters, omittedParameters);
 | |
|   const isBinaryRequset = /application\/octet-stream/i.test(headers.accept);
 | |
| 
 | |
|   if (!isBinaryRequset) {
 | |
|     if (options.mediaType.format) {
 | |
|       // e.g. application/vnd.github.v3+json => application/vnd.github.v3.raw
 | |
|       headers.accept = headers.accept.split(/,/).map(preview => preview.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, "application/vnd$1$2.".concat(options.mediaType.format))).join(",");
 | |
|     }
 | |
| 
 | |
|     if (options.mediaType.previews.length) {
 | |
|       const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || [];
 | |
|       headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map(preview => {
 | |
|         const format = options.mediaType.format ? ".".concat(options.mediaType.format) : "+json";
 | |
|         return "application/vnd.github.".concat(preview, "-preview").concat(format);
 | |
|       }).join(",");
 | |
|     }
 | |
|   } // for GET/HEAD requests, set URL query parameters from remaining parameters
 | |
|   // for PATCH/POST/PUT/DELETE requests, set request body from remaining parameters
 | |
| 
 | |
| 
 | |
|   if (["GET", "HEAD"].includes(method)) {
 | |
|     url = addQueryParameters(url, remainingParameters);
 | |
|   } else {
 | |
|     if ("data" in remainingParameters) {
 | |
|       body = remainingParameters.data;
 | |
|     } else {
 | |
|       if (Object.keys(remainingParameters).length) {
 | |
|         body = remainingParameters;
 | |
|       } else {
 | |
|         headers["content-length"] = 0;
 | |
|       }
 | |
|     }
 | |
|   } // default content-type for JSON if body is set
 | |
| 
 | |
| 
 | |
|   if (!headers["content-type"] && typeof body !== "undefined") {
 | |
|     headers["content-type"] = "application/json; charset=utf-8";
 | |
|   } // GitHub expects 'content-length: 0' header for PUT/PATCH requests without body.
 | |
|   // fetch does not allow to set `content-length` header, but we can set body to an empty string
 | |
| 
 | |
| 
 | |
|   if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") {
 | |
|     body = "";
 | |
|   } // Only return body/request keys if present
 | |
| 
 | |
| 
 | |
|   return Object.assign({
 | |
|     method,
 | |
|     url,
 | |
|     headers
 | |
|   }, typeof body !== "undefined" ? {
 | |
|     body
 | |
|   } : null, options.request ? {
 | |
|     request: options.request
 | |
|   } : null);
 | |
| }
 | |
| 
 | |
| function endpointWithDefaults(defaults, route, options) {
 | |
|   return parse(merge(defaults, route, options));
 | |
| }
 | |
| 
 | |
| function withDefaults(oldDefaults, newDefaults) {
 | |
|   const DEFAULTS = merge(oldDefaults, newDefaults);
 | |
|   const endpoint = endpointWithDefaults.bind(null, DEFAULTS);
 | |
|   return Object.assign(endpoint, {
 | |
|     DEFAULTS,
 | |
|     defaults: withDefaults.bind(null, DEFAULTS),
 | |
|     merge: merge.bind(null, DEFAULTS),
 | |
|     parse
 | |
|   });
 | |
| }
 | |
| 
 | |
| const VERSION = "0.0.0-development";
 | |
| 
 | |
| const userAgent = "octokit-endpoint.js/".concat(VERSION, " ").concat(getUserAgent());
 | |
| const DEFAULTS = {
 | |
|   method: "GET",
 | |
|   baseUrl: "https://api.github.com",
 | |
|   headers: {
 | |
|     accept: "application/vnd.github.v3+json",
 | |
|     "user-agent": userAgent
 | |
|   },
 | |
|   mediaType: {
 | |
|     format: "",
 | |
|     previews: []
 | |
|   }
 | |
| };
 | |
| 
 | |
| const endpoint = withDefaults(null, DEFAULTS);
 | |
| 
 | |
| export { endpoint };
 |