index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  4. var isPlainObject = _interopDefault(require('is-plain-object'));
  5. var universalUserAgent = require('universal-user-agent');
  6. function lowercaseKeys(object) {
  7. if (!object) {
  8. return {};
  9. }
  10. return Object.keys(object).reduce((newObj, key) => {
  11. newObj[key.toLowerCase()] = object[key];
  12. return newObj;
  13. }, {});
  14. }
  15. function mergeDeep(defaults, options) {
  16. const result = Object.assign({}, defaults);
  17. Object.keys(options).forEach(key => {
  18. if (isPlainObject(options[key])) {
  19. if (!(key in defaults)) Object.assign(result, {
  20. [key]: options[key]
  21. });else result[key] = mergeDeep(defaults[key], options[key]);
  22. } else {
  23. Object.assign(result, {
  24. [key]: options[key]
  25. });
  26. }
  27. });
  28. return result;
  29. }
  30. function merge(defaults, route, options) {
  31. if (typeof route === "string") {
  32. let [method, url] = route.split(" ");
  33. options = Object.assign(url ? {
  34. method,
  35. url
  36. } : {
  37. url: method
  38. }, options);
  39. } else {
  40. options = Object.assign({}, route);
  41. } // lowercase header names before merging with defaults to avoid duplicates
  42. options.headers = lowercaseKeys(options.headers);
  43. const mergedOptions = mergeDeep(defaults || {}, options); // mediaType.previews arrays are merged, instead of overwritten
  44. if (defaults && defaults.mediaType.previews.length) {
  45. mergedOptions.mediaType.previews = defaults.mediaType.previews.filter(preview => !mergedOptions.mediaType.previews.includes(preview)).concat(mergedOptions.mediaType.previews);
  46. }
  47. mergedOptions.mediaType.previews = mergedOptions.mediaType.previews.map(preview => preview.replace(/-preview/, ""));
  48. return mergedOptions;
  49. }
  50. function addQueryParameters(url, parameters) {
  51. const separator = /\?/.test(url) ? "&" : "?";
  52. const names = Object.keys(parameters);
  53. if (names.length === 0) {
  54. return url;
  55. }
  56. return url + separator + names.map(name => {
  57. if (name === "q") {
  58. return "q=" + parameters.q.split("+").map(encodeURIComponent).join("+");
  59. }
  60. return `${name}=${encodeURIComponent(parameters[name])}`;
  61. }).join("&");
  62. }
  63. const urlVariableRegex = /\{[^}]+\}/g;
  64. function removeNonChars(variableName) {
  65. return variableName.replace(/^\W+|\W+$/g, "").split(/,/);
  66. }
  67. function extractUrlVariableNames(url) {
  68. const matches = url.match(urlVariableRegex);
  69. if (!matches) {
  70. return [];
  71. }
  72. return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []);
  73. }
  74. function omit(object, keysToOmit) {
  75. return Object.keys(object).filter(option => !keysToOmit.includes(option)).reduce((obj, key) => {
  76. obj[key] = object[key];
  77. return obj;
  78. }, {});
  79. }
  80. // Based on https://github.com/bramstein/url-template, licensed under BSD
  81. // TODO: create separate package.
  82. //
  83. // Copyright (c) 2012-2014, Bram Stein
  84. // All rights reserved.
  85. // Redistribution and use in source and binary forms, with or without
  86. // modification, are permitted provided that the following conditions
  87. // are met:
  88. // 1. Redistributions of source code must retain the above copyright
  89. // notice, this list of conditions and the following disclaimer.
  90. // 2. Redistributions in binary form must reproduce the above copyright
  91. // notice, this list of conditions and the following disclaimer in the
  92. // documentation and/or other materials provided with the distribution.
  93. // 3. The name of the author may not be used to endorse or promote products
  94. // derived from this software without specific prior written permission.
  95. // THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
  96. // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  97. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  98. // EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  99. // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  100. // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  101. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  102. // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  103. // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  104. // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  105. /* istanbul ignore file */
  106. function encodeReserved(str) {
  107. return str.split(/(%[0-9A-Fa-f]{2})/g).map(function (part) {
  108. if (!/%[0-9A-Fa-f]/.test(part)) {
  109. part = encodeURI(part).replace(/%5B/g, "[").replace(/%5D/g, "]");
  110. }
  111. return part;
  112. }).join("");
  113. }
  114. function encodeUnreserved(str) {
  115. return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
  116. return "%" + c.charCodeAt(0).toString(16).toUpperCase();
  117. });
  118. }
  119. function encodeValue(operator, value, key) {
  120. value = operator === "+" || operator === "#" ? encodeReserved(value) : encodeUnreserved(value);
  121. if (key) {
  122. return encodeUnreserved(key) + "=" + value;
  123. } else {
  124. return value;
  125. }
  126. }
  127. function isDefined(value) {
  128. return value !== undefined && value !== null;
  129. }
  130. function isKeyOperator(operator) {
  131. return operator === ";" || operator === "&" || operator === "?";
  132. }
  133. function getValues(context, operator, key, modifier) {
  134. var value = context[key],
  135. result = [];
  136. if (isDefined(value) && value !== "") {
  137. if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
  138. value = value.toString();
  139. if (modifier && modifier !== "*") {
  140. value = value.substring(0, parseInt(modifier, 10));
  141. }
  142. result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : ""));
  143. } else {
  144. if (modifier === "*") {
  145. if (Array.isArray(value)) {
  146. value.filter(isDefined).forEach(function (value) {
  147. result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : ""));
  148. });
  149. } else {
  150. Object.keys(value).forEach(function (k) {
  151. if (isDefined(value[k])) {
  152. result.push(encodeValue(operator, value[k], k));
  153. }
  154. });
  155. }
  156. } else {
  157. const tmp = [];
  158. if (Array.isArray(value)) {
  159. value.filter(isDefined).forEach(function (value) {
  160. tmp.push(encodeValue(operator, value));
  161. });
  162. } else {
  163. Object.keys(value).forEach(function (k) {
  164. if (isDefined(value[k])) {
  165. tmp.push(encodeUnreserved(k));
  166. tmp.push(encodeValue(operator, value[k].toString()));
  167. }
  168. });
  169. }
  170. if (isKeyOperator(operator)) {
  171. result.push(encodeUnreserved(key) + "=" + tmp.join(","));
  172. } else if (tmp.length !== 0) {
  173. result.push(tmp.join(","));
  174. }
  175. }
  176. }
  177. } else {
  178. if (operator === ";") {
  179. if (isDefined(value)) {
  180. result.push(encodeUnreserved(key));
  181. }
  182. } else if (value === "" && (operator === "&" || operator === "?")) {
  183. result.push(encodeUnreserved(key) + "=");
  184. } else if (value === "") {
  185. result.push("");
  186. }
  187. }
  188. return result;
  189. }
  190. function parseUrl(template) {
  191. return {
  192. expand: expand.bind(null, template)
  193. };
  194. }
  195. function expand(template, context) {
  196. var operators = ["+", "#", ".", "/", ";", "?", "&"];
  197. return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) {
  198. if (expression) {
  199. let operator = "";
  200. const values = [];
  201. if (operators.indexOf(expression.charAt(0)) !== -1) {
  202. operator = expression.charAt(0);
  203. expression = expression.substr(1);
  204. }
  205. expression.split(/,/g).forEach(function (variable) {
  206. var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable);
  207. values.push(getValues(context, operator, tmp[1], tmp[2] || tmp[3]));
  208. });
  209. if (operator && operator !== "+") {
  210. var separator = ",";
  211. if (operator === "?") {
  212. separator = "&";
  213. } else if (operator !== "#") {
  214. separator = operator;
  215. }
  216. return (values.length !== 0 ? operator : "") + values.join(separator);
  217. } else {
  218. return values.join(",");
  219. }
  220. } else {
  221. return encodeReserved(literal);
  222. }
  223. });
  224. }
  225. function parse(options) {
  226. // https://fetch.spec.whatwg.org/#methods
  227. let method = options.method.toUpperCase(); // replace :varname with {varname} to make it RFC 6570 compatible
  228. let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{+$1}");
  229. let headers = Object.assign({}, options.headers);
  230. let body;
  231. let parameters = omit(options, ["method", "baseUrl", "url", "headers", "request", "mediaType"]); // extract variable names from URL to calculate remaining variables later
  232. const urlVariableNames = extractUrlVariableNames(url);
  233. url = parseUrl(url).expand(parameters);
  234. if (!/^http/.test(url)) {
  235. url = options.baseUrl + url;
  236. }
  237. const omittedParameters = Object.keys(options).filter(option => urlVariableNames.includes(option)).concat("baseUrl");
  238. const remainingParameters = omit(parameters, omittedParameters);
  239. const isBinaryRequset = /application\/octet-stream/i.test(headers.accept);
  240. if (!isBinaryRequset) {
  241. if (options.mediaType.format) {
  242. // e.g. application/vnd.github.v3+json => application/vnd.github.v3.raw
  243. headers.accept = headers.accept.split(/,/).map(preview => preview.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, `application/vnd$1$2.${options.mediaType.format}`)).join(",");
  244. }
  245. if (options.mediaType.previews.length) {
  246. const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || [];
  247. headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map(preview => {
  248. const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json";
  249. return `application/vnd.github.${preview}-preview${format}`;
  250. }).join(",");
  251. }
  252. } // for GET/HEAD requests, set URL query parameters from remaining parameters
  253. // for PATCH/POST/PUT/DELETE requests, set request body from remaining parameters
  254. if (["GET", "HEAD"].includes(method)) {
  255. url = addQueryParameters(url, remainingParameters);
  256. } else {
  257. if ("data" in remainingParameters) {
  258. body = remainingParameters.data;
  259. } else {
  260. if (Object.keys(remainingParameters).length) {
  261. body = remainingParameters;
  262. } else {
  263. headers["content-length"] = 0;
  264. }
  265. }
  266. } // default content-type for JSON if body is set
  267. if (!headers["content-type"] && typeof body !== "undefined") {
  268. headers["content-type"] = "application/json; charset=utf-8";
  269. } // GitHub expects 'content-length: 0' header for PUT/PATCH requests without body.
  270. // fetch does not allow to set `content-length` header, but we can set body to an empty string
  271. if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") {
  272. body = "";
  273. } // Only return body/request keys if present
  274. return Object.assign({
  275. method,
  276. url,
  277. headers
  278. }, typeof body !== "undefined" ? {
  279. body
  280. } : null, options.request ? {
  281. request: options.request
  282. } : null);
  283. }
  284. function endpointWithDefaults(defaults, route, options) {
  285. return parse(merge(defaults, route, options));
  286. }
  287. function withDefaults(oldDefaults, newDefaults) {
  288. const DEFAULTS = merge(oldDefaults, newDefaults);
  289. const endpoint = endpointWithDefaults.bind(null, DEFAULTS);
  290. return Object.assign(endpoint, {
  291. DEFAULTS,
  292. defaults: withDefaults.bind(null, DEFAULTS),
  293. merge: merge.bind(null, DEFAULTS),
  294. parse
  295. });
  296. }
  297. const VERSION = "6.0.1";
  298. const userAgent = `octokit-endpoint.js/${VERSION} ${universalUserAgent.getUserAgent()}`; // DEFAULTS has all properties set that EndpointOptions has, except url.
  299. // So we use RequestParameters and add method as additional required property.
  300. const DEFAULTS = {
  301. method: "GET",
  302. baseUrl: "https://api.github.com",
  303. headers: {
  304. accept: "application/vnd.github.v3+json",
  305. "user-agent": userAgent
  306. },
  307. mediaType: {
  308. format: "",
  309. previews: []
  310. }
  311. };
  312. const endpoint = withDefaults(null, DEFAULTS);
  313. exports.endpoint = endpoint;
  314. //# sourceMappingURL=index.js.map