1 |
|
2 | import { getUserAgent } from "universal-user-agent";
|
3 |
|
4 |
|
5 | var VERSION = "0.0.0-development";
|
6 |
|
7 |
|
8 | var userAgent = `octokit-endpoint.js/${VERSION} ${getUserAgent()}`;
|
9 | var DEFAULTS = {
|
10 | method: "GET",
|
11 | baseUrl: "https://api.github.com",
|
12 | headers: {
|
13 | accept: "application/vnd.github.v3+json",
|
14 | "user-agent": userAgent
|
15 | },
|
16 | mediaType: {
|
17 | format: ""
|
18 | }
|
19 | };
|
20 |
|
21 |
|
22 | function lowercaseKeys(object) {
|
23 | if (!object) {
|
24 | return {};
|
25 | }
|
26 | return Object.keys(object).reduce((newObj, key) => {
|
27 | newObj[key.toLowerCase()] = object[key];
|
28 | return newObj;
|
29 | }, {});
|
30 | }
|
31 |
|
32 |
|
33 | function isPlainObject(value) {
|
34 | if (typeof value !== "object" || value === null)
|
35 | return false;
|
36 | if (Object.prototype.toString.call(value) !== "[object Object]")
|
37 | return false;
|
38 | const proto = Object.getPrototypeOf(value);
|
39 | if (proto === null)
|
40 | return true;
|
41 | const Ctor = Object.prototype.hasOwnProperty.call(proto, "constructor") && proto.constructor;
|
42 | return typeof Ctor === "function" && Ctor instanceof Ctor && Function.prototype.call(Ctor) === Function.prototype.call(value);
|
43 | }
|
44 |
|
45 |
|
46 | function mergeDeep(defaults, options) {
|
47 | const result = Object.assign({}, defaults);
|
48 | Object.keys(options).forEach((key) => {
|
49 | if (isPlainObject(options[key])) {
|
50 | if (!(key in defaults))
|
51 | Object.assign(result, { [key]: options[key] });
|
52 | else
|
53 | result[key] = mergeDeep(defaults[key], options[key]);
|
54 | } else {
|
55 | Object.assign(result, { [key]: options[key] });
|
56 | }
|
57 | });
|
58 | return result;
|
59 | }
|
60 |
|
61 |
|
62 | function removeUndefinedProperties(obj) {
|
63 | for (const key in obj) {
|
64 | if (obj[key] === void 0) {
|
65 | delete obj[key];
|
66 | }
|
67 | }
|
68 | return obj;
|
69 | }
|
70 |
|
71 |
|
72 | function merge(defaults, route, options) {
|
73 | if (typeof route === "string") {
|
74 | let [method, url] = route.split(" ");
|
75 | options = Object.assign(url ? { method, url } : { url: method }, options);
|
76 | } else {
|
77 | options = Object.assign({}, route);
|
78 | }
|
79 | options.headers = lowercaseKeys(options.headers);
|
80 | removeUndefinedProperties(options);
|
81 | removeUndefinedProperties(options.headers);
|
82 | const mergedOptions = mergeDeep(defaults || {}, options);
|
83 | if (options.url === "/graphql") {
|
84 | if (defaults && defaults.mediaType.previews?.length) {
|
85 | mergedOptions.mediaType.previews = defaults.mediaType.previews.filter(
|
86 | (preview) => !mergedOptions.mediaType.previews.includes(preview)
|
87 | ).concat(mergedOptions.mediaType.previews);
|
88 | }
|
89 | mergedOptions.mediaType.previews = (mergedOptions.mediaType.previews || []).map((preview) => preview.replace(/-preview/, ""));
|
90 | }
|
91 | return mergedOptions;
|
92 | }
|
93 |
|
94 |
|
95 | function addQueryParameters(url, parameters) {
|
96 | const separator = /\?/.test(url) ? "&" : "?";
|
97 | const names = Object.keys(parameters);
|
98 | if (names.length === 0) {
|
99 | return url;
|
100 | }
|
101 | return url + separator + names.map((name) => {
|
102 | if (name === "q") {
|
103 | return "q=" + parameters.q.split("+").map(encodeURIComponent).join("+");
|
104 | }
|
105 | return `${name}=${encodeURIComponent(parameters[name])}`;
|
106 | }).join("&");
|
107 | }
|
108 |
|
109 |
|
110 | var urlVariableRegex = /\{[^}]+\}/g;
|
111 | function removeNonChars(variableName) {
|
112 | return variableName.replace(/^\W+|\W+$/g, "").split(/,/);
|
113 | }
|
114 | function extractUrlVariableNames(url) {
|
115 | const matches = url.match(urlVariableRegex);
|
116 | if (!matches) {
|
117 | return [];
|
118 | }
|
119 | return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []);
|
120 | }
|
121 |
|
122 |
|
123 | function omit(object, keysToOmit) {
|
124 | const result = { __proto__: null };
|
125 | for (const key of Object.keys(object)) {
|
126 | if (keysToOmit.indexOf(key) === -1) {
|
127 | result[key] = object[key];
|
128 | }
|
129 | }
|
130 | return result;
|
131 | }
|
132 |
|
133 |
|
134 | function encodeReserved(str) {
|
135 | return str.split(/(%[0-9A-Fa-f]{2})/g).map(function(part) {
|
136 | if (!/%[0-9A-Fa-f]/.test(part)) {
|
137 | part = encodeURI(part).replace(/%5B/g, "[").replace(/%5D/g, "]");
|
138 | }
|
139 | return part;
|
140 | }).join("");
|
141 | }
|
142 | function encodeUnreserved(str) {
|
143 | return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
|
144 | return "%" + c.charCodeAt(0).toString(16).toUpperCase();
|
145 | });
|
146 | }
|
147 | function encodeValue(operator, value, key) {
|
148 | value = operator === "+" || operator === "#" ? encodeReserved(value) : encodeUnreserved(value);
|
149 | if (key) {
|
150 | return encodeUnreserved(key) + "=" + value;
|
151 | } else {
|
152 | return value;
|
153 | }
|
154 | }
|
155 | function isDefined(value) {
|
156 | return value !== void 0 && value !== null;
|
157 | }
|
158 | function isKeyOperator(operator) {
|
159 | return operator === ";" || operator === "&" || operator === "?";
|
160 | }
|
161 | function getValues(context, operator, key, modifier) {
|
162 | var value = context[key], result = [];
|
163 | if (isDefined(value) && value !== "") {
|
164 | if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
165 | value = value.toString();
|
166 | if (modifier && modifier !== "*") {
|
167 | value = value.substring(0, parseInt(modifier, 10));
|
168 | }
|
169 | result.push(
|
170 | encodeValue(operator, value, isKeyOperator(operator) ? key : "")
|
171 | );
|
172 | } else {
|
173 | if (modifier === "*") {
|
174 | if (Array.isArray(value)) {
|
175 | value.filter(isDefined).forEach(function(value2) {
|
176 | result.push(
|
177 | encodeValue(operator, value2, isKeyOperator(operator) ? key : "")
|
178 | );
|
179 | });
|
180 | } else {
|
181 | Object.keys(value).forEach(function(k) {
|
182 | if (isDefined(value[k])) {
|
183 | result.push(encodeValue(operator, value[k], k));
|
184 | }
|
185 | });
|
186 | }
|
187 | } else {
|
188 | const tmp = [];
|
189 | if (Array.isArray(value)) {
|
190 | value.filter(isDefined).forEach(function(value2) {
|
191 | tmp.push(encodeValue(operator, value2));
|
192 | });
|
193 | } else {
|
194 | Object.keys(value).forEach(function(k) {
|
195 | if (isDefined(value[k])) {
|
196 | tmp.push(encodeUnreserved(k));
|
197 | tmp.push(encodeValue(operator, value[k].toString()));
|
198 | }
|
199 | });
|
200 | }
|
201 | if (isKeyOperator(operator)) {
|
202 | result.push(encodeUnreserved(key) + "=" + tmp.join(","));
|
203 | } else if (tmp.length !== 0) {
|
204 | result.push(tmp.join(","));
|
205 | }
|
206 | }
|
207 | }
|
208 | } else {
|
209 | if (operator === ";") {
|
210 | if (isDefined(value)) {
|
211 | result.push(encodeUnreserved(key));
|
212 | }
|
213 | } else if (value === "" && (operator === "&" || operator === "?")) {
|
214 | result.push(encodeUnreserved(key) + "=");
|
215 | } else if (value === "") {
|
216 | result.push("");
|
217 | }
|
218 | }
|
219 | return result;
|
220 | }
|
221 | function parseUrl(template) {
|
222 | return {
|
223 | expand: expand.bind(null, template)
|
224 | };
|
225 | }
|
226 | function expand(template, context) {
|
227 | var operators = ["+", "#", ".", "/", ";", "?", "&"];
|
228 | template = template.replace(
|
229 | /\{([^\{\}]+)\}|([^\{\}]+)/g,
|
230 | function(_, expression, literal) {
|
231 | if (expression) {
|
232 | let operator = "";
|
233 | const values = [];
|
234 | if (operators.indexOf(expression.charAt(0)) !== -1) {
|
235 | operator = expression.charAt(0);
|
236 | expression = expression.substr(1);
|
237 | }
|
238 | expression.split(/,/g).forEach(function(variable) {
|
239 | var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable);
|
240 | values.push(getValues(context, operator, tmp[1], tmp[2] || tmp[3]));
|
241 | });
|
242 | if (operator && operator !== "+") {
|
243 | var separator = ",";
|
244 | if (operator === "?") {
|
245 | separator = "&";
|
246 | } else if (operator !== "#") {
|
247 | separator = operator;
|
248 | }
|
249 | return (values.length !== 0 ? operator : "") + values.join(separator);
|
250 | } else {
|
251 | return values.join(",");
|
252 | }
|
253 | } else {
|
254 | return encodeReserved(literal);
|
255 | }
|
256 | }
|
257 | );
|
258 | if (template === "/") {
|
259 | return template;
|
260 | } else {
|
261 | return template.replace(/\/$/, "");
|
262 | }
|
263 | }
|
264 |
|
265 |
|
266 | function parse(options) {
|
267 | let method = options.method.toUpperCase();
|
268 | let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}");
|
269 | let headers = Object.assign({}, options.headers);
|
270 | let body;
|
271 | let parameters = omit(options, [
|
272 | "method",
|
273 | "baseUrl",
|
274 | "url",
|
275 | "headers",
|
276 | "request",
|
277 | "mediaType"
|
278 | ]);
|
279 | const urlVariableNames = extractUrlVariableNames(url);
|
280 | url = parseUrl(url).expand(parameters);
|
281 | if (!/^http/.test(url)) {
|
282 | url = options.baseUrl + url;
|
283 | }
|
284 | const omittedParameters = Object.keys(options).filter((option) => urlVariableNames.includes(option)).concat("baseUrl");
|
285 | const remainingParameters = omit(parameters, omittedParameters);
|
286 | const isBinaryRequest = /application\/octet-stream/i.test(headers.accept);
|
287 | if (!isBinaryRequest) {
|
288 | if (options.mediaType.format) {
|
289 | headers.accept = headers.accept.split(/,/).map(
|
290 | (format) => format.replace(
|
291 | /application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/,
|
292 | `application/vnd$1$2.${options.mediaType.format}`
|
293 | )
|
294 | ).join(",");
|
295 | }
|
296 | if (url.endsWith("/graphql")) {
|
297 | if (options.mediaType.previews?.length) {
|
298 | const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || [];
|
299 | headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map((preview) => {
|
300 | const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json";
|
301 | return `application/vnd.github.${preview}-preview${format}`;
|
302 | }).join(",");
|
303 | }
|
304 | }
|
305 | }
|
306 | if (["GET", "HEAD"].includes(method)) {
|
307 | url = addQueryParameters(url, remainingParameters);
|
308 | } else {
|
309 | if ("data" in remainingParameters) {
|
310 | body = remainingParameters.data;
|
311 | } else {
|
312 | if (Object.keys(remainingParameters).length) {
|
313 | body = remainingParameters;
|
314 | }
|
315 | }
|
316 | }
|
317 | if (!headers["content-type"] && typeof body !== "undefined") {
|
318 | headers["content-type"] = "application/json; charset=utf-8";
|
319 | }
|
320 | if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") {
|
321 | body = "";
|
322 | }
|
323 | return Object.assign(
|
324 | { method, url, headers },
|
325 | typeof body !== "undefined" ? { body } : null,
|
326 | options.request ? { request: options.request } : null
|
327 | );
|
328 | }
|
329 |
|
330 |
|
331 | function endpointWithDefaults(defaults, route, options) {
|
332 | return parse(merge(defaults, route, options));
|
333 | }
|
334 |
|
335 |
|
336 | function withDefaults(oldDefaults, newDefaults) {
|
337 | const DEFAULTS2 = merge(oldDefaults, newDefaults);
|
338 | const endpoint2 = endpointWithDefaults.bind(null, DEFAULTS2);
|
339 | return Object.assign(endpoint2, {
|
340 | DEFAULTS: DEFAULTS2,
|
341 | defaults: withDefaults.bind(null, DEFAULTS2),
|
342 | merge: merge.bind(null, DEFAULTS2),
|
343 | parse
|
344 | });
|
345 | }
|
346 |
|
347 |
|
348 | var endpoint = withDefaults(null, DEFAULTS);
|
349 | export {
|
350 | endpoint
|
351 | };
|