forked from swc-project/swc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
211 lines (211 loc) · 7.77 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "RequestHandler", {
enumerable: true,
get: ()=>RequestHandler
});
const _classPrivateFieldGet = require("@swc/helpers/lib/_class_private_field_get.js").default;
const _classPrivateFieldInit = require("@swc/helpers/lib/_class_private_field_init.js").default;
const _interopRequireDefault = require("@swc/helpers/lib/_interop_require_default.js").default;
const _nodeFetch = /*#__PURE__*/ _interopRequireDefault(require("node-fetch"));
const _abortSignal = require("./misc/AbortSignal");
const _errors = require("../../errors");
const _utils = require("../../utils");
const headers = [
"x-ratelimit-limit",
"x-ratelimit-remaining",
"x-ratelimit-reset-after",
"x-ratelimit-reset",
"via"
];
var /**
* Used for sequential requests.
* @type {AsyncQueue}
*/ _queue = /*#__PURE__*/ new WeakMap();
class RequestHandler {
/**
* Whether this handler is inactive or not.
* @return {boolean}
*/ get inactive() {
return !_classPrivateFieldGet(this, _queue).remaining && !this._limited;
}
/**
* Whether the rate-limit bucket is currently limited.
* @return {boolean}
* @private
*/ get _limited() {
return this.remaining <= 0 && Date.now() < this.reset;
}
/**
* The time until the rate-limit bucket resets.
* @private
*/ get _untilReset() {
return this.reset - Date.now();
}
/**
* Makes a route that can be used as a key.
* Taken from https://github.com/abalabahaha/eris/blob/master/lib/rest/RequestHandler.js#L46-L54.
* @param {string} method The method.
* @param {string} url The URL.
*
* @returns {string} The created route.
*/ static makeRoute(method, url) {
const route = url.replace(/\/([a-z-]+)\/(?:\d{17,19})/g, (match, p)=>[
"channels",
"guilds",
"webhooks"
].includes(p) ? match : `/${p}/:id`).replace(/\/reactions\/[^/]+/g, "/reactions/:id").replace(/\/webhooks\/(\d+)\/[\w-]{64,}/, "webhooks/$1/:token").replace(/\?.*$/, "");
let ending = ";";
if (method === "delete" && route.endsWith("/message/:id")) {
var _exec;
const id = (_exec = /\d{16,19}$/.exec(route)) === null || _exec === void 0 ? void 0 : _exec[0];
const snowflake = _utils.Snowflake.deconstruct(id);
if (Date.now() - snowflake.timestamp > 1000 * 60 * 60 * 24 * 14) {
ending += "deletes-old";
}
}
return route + ending;
}
/**
* Converts the response to usable data
* @param {Response} res
* @return {* | Promise<any>}
*/ static async parseResponse(res) {
var _res_headers_get;
if ((_res_headers_get = res.headers.get("Content-Type")) === null || _res_headers_get === void 0 ? void 0 : _res_headers_get.startsWith("application/json")) {
return await res.json();
}
return res.buffer();
}
/**
* Pushes a new request into the queue.
* @param {string} url The request URL.
* @param {Request} request The request data.
*
* @return {Promise<*>}
*/ async push(url, request) {
await _classPrivateFieldGet(this, _queue).wait();
try {
await this.rest.globalTimeout;
if (this._limited) {
/**
* @typedef {Object} RateLimitedData
* @property {number} limit
* @property {string} method
* @property {string} url
*
* Emitted whenever a routes rate-lim\it bucket was exceeded.
* @event RequestHandler#rate-limited
* @property {RateLimitedData} data The rate-limit data.
*/ this.rest.client.emit(_utils.ClientEvent.LIMITED, {
limit: this.limit,
method: request.method,
url
});
await (0, _utils.sleep)(this._untilReset);
}
return this._make(url, request);
} finally{
_classPrivateFieldGet(this, _queue).next();
}
}
/**
* Makes a new request to the provided url.
* @param url The request URL.
* @param {Request} request The request data.
* @param {number} [tries] The current try.
*
* @return {Promise<*>}
* @private
*/ async _make(url, request, tries = 0) {
const signal = new _abortSignal.AbortSignal();
const timeout = _utils.Timers.setTimeout(()=>signal.abort(), this.rest.options.timeout);
let res;
try {
res = await (0, _nodeFetch.default)(url, {
...request,
signal
});
} catch (e) {
if (e.name === "AbortError" && tries !== this.rest.options.retries) {
return this._make(url, options, tries++);
}
throw e;
} finally{
_utils.Timers.clearTimeout(timeout);
}
let _retry = 0;
if (res.headers) {
const [limit, remaining, reset, retry, cf] = getHeaders(res, headers), _reset = ~~reset * 1000 + Date.now() + this.rest.options.offset;
this.remaining = remaining ? ~~remaining : 1;
this.limit = limit ? ~~limit : Infinity;
this.reset = reset ? _reset : Date.now();
if (retry) {
_retry = ~~reset * (cf ? 1000 : 1 + this.rest.options.offset);
}
if (res.headers.get("X-RateLimit-Global")) {
this.rest.globalTimeout = (0, _utils.sleep)(_retry).then(()=>{
this.api.globalTimeout = null;
});
}
}
if (res.ok) {
return RequestHandler.parseResponse(res);
}
if (res.status === 429) {
this.rest.client.emit(_utils.ClientEvent.LIMITED, `Hit a 429 on route: ${this.id}, Retrying After: ${_retry}ms`);
await (0, _utils.sleep)(_retry);
return this._make(url, request, tries++);
}
if (res.status >= 500 && res.status < 600) {
if (tries !== this.rest.options.retries) {
return this._make(url, request, tries++);
}
throw new _errors.DiscordHTTPError(res.statusText, res.constructor.name, res.status, request.method, url);
}
if (res.status >= 400 && res.status < 500) {
const data = await RequestHandler.parseResponse(res);
throw new _errors.DiscordAPIError(data.message, data.code, res.status, request.method, url);
}
return null;
}
/**
* @param {Rest} rest The REST Manager.
* @param {string} id The ID of this request handler.
*/ constructor(rest, id){
_classPrivateFieldInit(this, _queue, {
writable: true,
value: new _utils.AsyncQueue()
});
/**
* The REST Manager.
* @type {Rest}
*/ this.rest = rest;
/**
* The ID of this request handler.
* @type {string}
*/ this.id = id;
/**
* Timestamp in which the rate-limit will reset.
* @type {number}
*/ this.reset = -1;
/**
* The remaining requests that can be made.
* @type {number}
*/ this.remaining = 1;
/**
* The total number of requests that can be made.
* @type {number}
*/ this.limit = Infinity;
}
}
/**
* Bulk fetch headers from a node-fetch response.
* @param {Response} res The request response.
* @param {string[]} headers The headers to fetch.
* @return {string[]} The header values.
*/ function getHeaders(res, headers) {
return headers.map((headerName)=>res.headers.get(headerName));
}