@discordjs/rest 0.4.0-dev.1648166871.cedd053 → 0.4.0-dev.1649030897-5748dbe

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,882 @@
1
- var Z=Object.defineProperty;var le=(n,e,t)=>e in n?Z(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var d=(n,e)=>Z(n,"name",{value:e,configurable:!0});var ue=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports);var l=(n,e,t)=>(le(n,typeof e!="symbol"?e+"":e,t),t),V=(n,e,t)=>{if(!e.has(n))throw TypeError("Cannot "+t)};var u=(n,e,t)=>(V(n,e,"read from private field"),t?t.call(n):e.get(n)),x=(n,e,t)=>{if(e.has(n))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(n):e.set(n,t)},f=(n,e,t,s)=>(V(n,e,"write to private field"),s?s.call(n,t):e.set(n,t),t);var Y=ue((Ee,me)=>{me.exports={name:"@discordjs/rest",version:"0.4.0-dev",description:"The REST API for discord.js",scripts:{build:"tsup && tsc --emitDeclarationOnly --incremental",test:"jest --pass-with-no-tests --collect-coverage",lint:"prettier --check . && eslint src __tests__ --ext mjs,js,ts",format:"prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix",docs:"typedoc --json docs/typedoc-out.json src/index.ts && node scripts/docs.mjs",prepublishOnly:"yarn build && yarn lint && yarn test",changelog:"git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/rest/*'"},main:"./dist/index.js",module:"./dist/index.mjs",typings:"./dist/index.d.ts",exports:{import:"./dist/index.mjs",require:"./dist/index.js",types:"./dist/index.d.ts"},directories:{lib:"src",test:"__tests__"},files:["dist"],contributors:["Crawl <icrawltogo@gmail.com>","Amish Shah <amishshah.2k@gmail.com>","SpaceEEC <spaceeec@yahoo.com>","Vlad Frangu <kingdgrizzle@gmail.com>","Antonio Roman <kyradiscord@gmail.com>"],license:"Apache-2.0",keywords:["discord","api","rest","discordapp","discordjs"],repository:{type:"git",url:"git+https://github.com/discordjs/discord.js.git"},bugs:{url:"https://github.com/discordjs/discord.js/issues"},homepage:"https://discord.js.org",dependencies:{"@discordjs/collection":"workspace:^","@sapphire/async-queue":"^1.2.0","@sapphire/snowflake":"^3.1.0","@types/node-fetch":"^2.5.12","discord-api-types":"^0.29.0","form-data":"^4.0.0","node-fetch":"^2.6.7",tslib:"^2.3.1"},devDependencies:{"@babel/core":"^7.17.2","@babel/plugin-proposal-decorators":"^7.17.2","@babel/preset-env":"^7.16.11","@babel/preset-typescript":"^7.16.7","@discordjs/ts-docgen":"^0.3.4","@types/jest":"^27.4.0","@typescript-eslint/eslint-plugin":"^5.11.0","@typescript-eslint/parser":"^5.11.0","babel-plugin-const-enum":"^1.2.0","babel-plugin-transform-typescript-metadata":"^0.3.2",eslint:"^8.9.0","eslint-config-marine":"^9.3.2","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0",jest:"^27.5.1",nock:"^13.2.4",prettier:"^2.5.1",tsup:"^5.11.13",typedoc:"^0.22.11",typescript:"^4.5.5"},engines:{node:">=16.9.0"},publishConfig:{access:"public"}}});import{APIVersion as pe}from"discord-api-types/v10";var ee=Y(),te=`DiscordBot (${ee.homepage}, ${ee.version})`,k={agent:{},api:"https://discord.com/api",cdn:"https://cdn.discordapp.com",headers:{},invalidRequestWarningInterval:0,globalRequestsPerSecond:50,offset:50,rejectOnRateLimit:null,retries:3,timeout:15e3,userAgentAppendix:`Node.js ${process.version}`,version:pe,hashSweepInterval:144e5,hashLifetime:864e5,handlerSweepInterval:36e5},T=(m=>(m.Debug="restDebug",m.InvalidRequestWarning="invalidRequestWarning",m.RateLimited="rateLimited",m.Request="request",m.Response="response",m.HashSweep="hashSweep",m.HandlerSweep="handlerSweep",m))(T||{}),se=["webp","png","jpg","jpeg","gif"],ie=["png","json"],N=[16,32,64,128,256,512,1024,2048,4096];var P=class{constructor(e=k.cdn){this.base=e}appAsset(e,t,s){return this.makeURL(`/app-assets/${e}/${t}`,s)}appIcon(e,t,s){return this.makeURL(`/app-icons/${e}/${t}`,s)}avatar(e,t,s){return this.dynamicMakeURL(`/avatars/${e}/${t}`,t,s)}banner(e,t,s){return this.dynamicMakeURL(`/banners/${e}/${t}`,t,s)}channelIcon(e,t,s){return this.makeURL(`/channel-icons/${e}/${t}`,s)}defaultAvatar(e){return this.makeURL(`/embed/avatars/${e}`,{extension:"png"})}discoverySplash(e,t,s){return this.makeURL(`/discovery-splashes/${e}/${t}`,s)}emoji(e,t){return this.makeURL(`/emojis/${e}`,{extension:t})}guildMemberAvatar(e,t,s,i){return this.dynamicMakeURL(`/guilds/${e}/users/${t}/avatars/${s}`,s,i)}icon(e,t,s){return this.dynamicMakeURL(`/icons/${e}/${t}`,t,s)}roleIcon(e,t,s){return this.makeURL(`/role-icons/${e}/${t}`,s)}splash(e,t,s){return this.makeURL(`/splashes/${e}/${t}`,s)}sticker(e,t){return this.makeURL(`/stickers/${e}`,{allowedExtensions:ie,extension:t??"png"})}stickerPackBanner(e,t){return this.makeURL(`/app-assets/710982414301790216/store/${e}`,t)}teamIcon(e,t,s){return this.makeURL(`/team-icons/${e}/${t}`,s)}guildScheduledEventCover(e,t,s){return this.makeURL(`/guild-events/${e}/${t}`,s)}dynamicMakeURL(e,t,{forceStatic:s=!1,...i}={}){return this.makeURL(e,!s&&t.startsWith("a_")?{...i,extension:"gif"}:i)}makeURL(e,{allowedExtensions:t=se,extension:s="webp",size:i}={}){if(s=String(s).toLowerCase(),!t.includes(s))throw new RangeError(`Invalid extension provided: ${s}
2
- Must be one of: ${t.join(", ")}`);if(i&&!N.includes(i))throw new RangeError(`Invalid size provided: ${i}
3
- Must be one of: ${N.join(", ")}`);let r=new URL(`${this.base}${e}.${s}`);return i&&r.searchParams.set("size",String(i)),r.toString()}};d(P,"CDN");function de(n){return Reflect.has(n,"_errors")}d(de,"isErrorGroupWrapper");function ce(n){return typeof Reflect.get(n,"message")=="string"}d(ce,"isErrorResponse");var D=class extends Error{constructor(e,t,s,i,r,a){super(D.getMessage(e));this.rawError=e;this.code=t;this.status=s;this.method=i;this.url=r;l(this,"requestBody");this.requestBody={files:a.files,json:a.body}}get name(){return`${D.name}[${this.code}]`}static getMessage(e){let t="";return"code"in e?(e.errors&&(t=[...this.flattenDiscordError(e.errors)].join(`
4
- `)),e.message&&t?`${e.message}
5
- ${t}`:e.message||t||"Unknown Error"):e.error_description??"No Description"}static*flattenDiscordError(e,t=""){if(ce(e))return yield`${t.length?`${t}[${e.code}]`:`${e.code}`}: ${e.message}`.trim();for(let[s,i]of Object.entries(e)){let r=s.startsWith("_")?t:t?Number.isNaN(Number(s))?`${t}.${s}`:`${t}[${s}]`:s;if(typeof i=="string")yield i;else if(de(i))for(let a of i._errors)yield*this.flattenDiscordError(a,r);else yield*this.flattenDiscordError(i,r)}}};d(D,"DiscordAPIError");var U=class extends Error{constructor(e,t,s,i,r,a){super(e);this.name=t;this.status=s;this.method=i;this.url=r;l(this,"requestBody");this.requestBody={files:a.files,json:a.body}}};d(U,"HTTPError");var I=class extends Error{constructor({timeToReset:e,limit:t,method:s,hash:i,url:r,route:a,majorParameter:m,global:L}){super();l(this,"timeToReset");l(this,"limit");l(this,"method");l(this,"hash");l(this,"url");l(this,"route");l(this,"majorParameter");l(this,"global");this.timeToReset=e,this.limit=t,this.method=s,this.hash=i,this.url=r,this.route=a,this.majorParameter=m,this.global=L}get name(){return`${I.name}[${this.route}]`}};d(I,"RateLimitError");import M from"@discordjs/collection";import ge from"form-data";import{DiscordSnowflake as fe}from"@sapphire/snowflake";import{EventEmitter as Re}from"node:events";import{Agent as be}from"node:https";import{Agent as ye}from"node:http";import{setTimeout as G}from"node:timers/promises";import{AsyncQueue as re}from"@sapphire/async-queue";import he from"node-fetch";function W(n){return n.headers.get("Content-Type")?.startsWith("application/json")?n.json():n.arrayBuffer()}d(W,"parseResponse");function C(n,e,t){if(n==="/channels/:id"){if(typeof e!="object"||e===null||t!=="patch")return!1;let s=e;return["name","topic"].some(i=>Reflect.has(s,i))}return!0}d(C,"hasSublimit");var H=0,B=null;var E,c,y,$,_=class{constructor(e,t,s){this.manager=e;this.hash=t;this.majorParameter=s;l(this,"id");l(this,"reset",-1);l(this,"remaining",1);l(this,"limit",1/0);x(this,E,new re);x(this,c,null);x(this,y,null);x(this,$,!1);this.id=`${t}:${s}`}get inactive(){return u(this,E).remaining===0&&(u(this,c)===null||u(this,c).remaining===0)&&!this.limited}get globalLimited(){return this.manager.globalRemaining<=0&&Date.now()<this.manager.globalReset}get localLimited(){return this.remaining<=0&&Date.now()<this.reset}get limited(){return this.globalLimited||this.localLimited}get timeToReset(){return this.reset+this.manager.options.offset-Date.now()}debug(e){this.manager.emit("restDebug",`[REST ${this.id}] ${e}`)}async globalDelayFor(e){await G(e,void 0,{ref:!1}),this.manager.globalDelay=null}async onRateLimit(e){let{options:t}=this.manager;if(!t.rejectOnRateLimit)return;if(typeof t.rejectOnRateLimit=="function"?await t.rejectOnRateLimit(e):t.rejectOnRateLimit.some(i=>e.route.startsWith(i.toLowerCase())))throw new I(e)}async queueRequest(e,t,s,i){let r=u(this,E),a=0;if(u(this,c)&&C(e.bucketRoute,i.body,s.method)&&(r=u(this,c),a=1),await r.wait(),a===0)if(u(this,c)&&C(e.bucketRoute,i.body,s.method)){r=u(this,c);let m=r.wait();u(this,E).shift(),await m}else u(this,y)&&await u(this,y).promise;try{return await this.runRequest(e,t,s,i)}finally{r.shift(),u(this,$)&&(f(this,$,!1),u(this,c)?.shift()),u(this,c)?.remaining===0&&(u(this,y)?.resolve(),f(this,c,null))}}async runRequest(e,t,s,i,r=0){for(;this.limited;){let p=this.globalLimited,w,h,S;p?(w=this.manager.options.globalRequestsPerSecond,h=this.manager.globalReset+this.manager.options.offset-Date.now(),this.manager.globalDelay||(this.manager.globalDelay=this.globalDelayFor(h)),S=this.manager.globalDelay):(w=this.limit,h=this.timeToReset,S=G(h));let j={timeToReset:h,limit:w,method:s.method??"get",hash:this.hash,url:t,route:e.bucketRoute,majorParameter:this.majorParameter,global:p};this.manager.emit("rateLimited",j),await this.onRateLimit(j),p?this.debug(`Global rate limit hit, blocking all requests for ${h}ms`):this.debug(`Waiting ${h}ms for rate limit to pass`),await S}(!this.manager.globalReset||this.manager.globalReset<Date.now())&&(this.manager.globalReset=Date.now()+1e3,this.manager.globalRemaining=this.manager.options.globalRequestsPerSecond),this.manager.globalRemaining--;let a=s.method??"get";this.manager.listenerCount("request")&&this.manager.emit("request",{method:a,path:e.original,route:e.bucketRoute,options:s,data:i,retries:r});let m=new AbortController,L=setTimeout(()=>m.abort(),this.manager.options.timeout).unref(),o;try{o=await he(t,{...s,signal:m.signal})}catch(p){if(p instanceof Error&&p.name==="AbortError"&&r!==this.manager.options.retries)return await this.runRequest(e,t,s,i,++r);throw p}finally{clearTimeout(L)}this.manager.listenerCount("response")&&this.manager.emit("response",{method:a,path:e.original,route:e.bucketRoute,options:s,data:i,retries:r},o.clone());let g=0,v=o.headers.get("X-RateLimit-Limit"),z=o.headers.get("X-RateLimit-Remaining"),J=o.headers.get("X-RateLimit-Reset-After"),O=o.headers.get("X-RateLimit-Bucket"),Q=o.headers.get("Retry-After");if(this.limit=v?Number(v):1/0,this.remaining=z?Number(z):1,this.reset=J?Number(J)*1e3+Date.now()+this.manager.options.offset:Date.now(),Q&&(g=Number(Q)*1e3+this.manager.options.offset),O&&O!==this.hash)this.debug(["Received bucket hash update",` Old Hash : ${this.hash}`,` New Hash : ${O}`].join(`
6
- `)),this.manager.hashes.set(`${a}:${e.bucketRoute}`,{value:O,lastAccess:Date.now()});else if(O){let p=this.manager.hashes.get(`${a}:${e.bucketRoute}`);p&&(p.lastAccess=Date.now())}let A=null;if(g>0&&(o.headers.get("X-RateLimit-Global")?(this.manager.globalRemaining=0,this.manager.globalReset=Date.now()+g):this.localLimited||(A=g)),(o.status===401||o.status===403||o.status===429)&&((!B||B<Date.now())&&(B=Date.now()+1e3*60*10,H=0),H++,this.manager.options.invalidRequestWarningInterval>0&&H%this.manager.options.invalidRequestWarningInterval===0&&this.manager.emit("invalidRequestWarning",{count:H,remainingTime:B-Date.now()})),o.ok)return W(o);if(o.status===429){let p=this.globalLimited,w,h;if(p?(w=this.manager.options.globalRequestsPerSecond,h=this.manager.globalReset+this.manager.options.offset-Date.now()):(w=this.limit,h=this.timeToReset),await this.onRateLimit({timeToReset:h,limit:w,method:a,hash:this.hash,url:t,route:e.bucketRoute,majorParameter:this.majorParameter,global:p}),this.debug(["Encountered unexpected 429 rate limit",` Global : ${p.toString()}`,` Method : ${a}`,` URL : ${t}`,` Bucket : ${e.bucketRoute}`,` Major parameter: ${e.majorParameter}`,` Hash : ${this.hash}`,` Limit : ${w}`,` Retry After : ${g}ms`,` Sublimit : ${A?`${A}ms`:"None"}`].join(`
7
- `)),A){let S=!u(this,c);S&&(f(this,c,new re),u(this,c).wait(),u(this,E).shift()),u(this,y)?.resolve(),f(this,y,null),await G(A,void 0,{ref:!1});let j,ae=new Promise(oe=>j=oe);f(this,y,{promise:ae,resolve:j}),S&&(await u(this,E).wait(),f(this,$,!0))}return this.runRequest(e,t,s,i,r)}else if(o.status>=500&&o.status<600){if(r!==this.manager.options.retries)return this.runRequest(e,t,s,i,++r);throw new U(o.statusText,o.constructor.name,o.status,a,t,i)}else{if(o.status>=400&&o.status<500){o.status===401&&i.auth&&this.manager.setToken(null);let p=await W(o);throw new D(p,"code"in p?p.code:p.error,o.status,a,t,i)}return null}}};d(_,"SequentialHandler"),E=new WeakMap,c=new WeakMap,y=new WeakMap,$=new WeakMap;var F=(r=>(r.Delete="delete",r.Get="get",r.Patch="patch",r.Post="post",r.Put="put",r))(F||{}),q,X=class extends Re{constructor(e){super();l(this,"globalRemaining");l(this,"globalDelay",null);l(this,"globalReset",-1);l(this,"hashes",new M);l(this,"handlers",new M);x(this,q,null);l(this,"hashTimer");l(this,"handlerTimer");l(this,"agent",null);l(this,"options");this.options={...k,...e},this.options.offset=Math.max(0,this.options.offset),this.globalRemaining=this.options.globalRequestsPerSecond,this.setupSweepers()}setupSweepers(){let e=d(t=>{if(t>144e5)throw new Error("Cannot set an interval greater than 4 hours")},"validateMaxInterval");this.options.hashSweepInterval!==0&&this.options.hashSweepInterval!==1/0&&(e(this.options.hashSweepInterval),this.hashTimer=setInterval(()=>{let t=new M,s=Date.now();this.hashes.sweep((i,r)=>{if(i.lastAccess===-1)return!1;let a=Math.floor(s-i.lastAccess)>this.options.hashLifetime;return a&&t.set(r,i),this.emit("restDebug",`Hash ${i.value} for ${r} swept due to lifetime being exceeded`),a}),this.emit("hashSweep",t)},this.options.hashSweepInterval).unref()),this.options.handlerSweepInterval!==0&&this.options.handlerSweepInterval!==1/0&&(e(this.options.handlerSweepInterval),this.handlerTimer=setInterval(()=>{let t=new M;this.handlers.sweep((s,i)=>{let{inactive:r}=s;return r&&t.set(i,s),this.emit("restDebug",`Handler ${s.id} for ${i} swept due to being inactive`),r}),this.emit("handlerSweep",t)},this.options.handlerSweepInterval).unref())}setToken(e){return f(this,q,e),this}async queueRequest(e){let t=X.generateRouteData(e.fullRoute,e.method),s=this.hashes.get(`${e.method}:${t.bucketRoute}`)??{value:`Global(${e.method}:${t.bucketRoute})`,lastAccess:-1},i=this.handlers.get(`${s.value}:${t.majorParameter}`)??this.createHandler(s.value,t.majorParameter),{url:r,fetchOptions:a}=this.resolveRequest(e);return i.queueRequest(t,r,a,{body:e.body,files:e.files,auth:e.auth!==!1})}createHandler(e,t){let s=new _(this,e,t);return this.handlers.set(s.id,s),s}resolveRequest(e){let{options:t}=this;this.agent??=t.api.startsWith("https")?new be({...t.agent,keepAlive:!0}):new ye({...t.agent,keepAlive:!0});let s="";if(e.query){let o=e.query.toString();o!==""&&(s=`?${o}`)}let i={...this.options.headers,"User-Agent":`${te} ${t.userAgentAppendix}`.trim()};if(e.auth!==!1){if(!u(this,q))throw new Error("Expected token to be set for this request, but none was present");i.Authorization=`${e.authPrefix??"Bot"} ${u(this,q)}`}e.reason?.length&&(i["X-Audit-Log-Reason"]=encodeURIComponent(e.reason));let r=`${t.api}${e.versioned===!1?"":`/v${t.version}`}${e.fullRoute}${s}`,a,m={};if(e.files?.length){let o=new ge;for(let[g,v]of e.files.entries())o.append(v.key??`files[${g}]`,v.data,v.name);if(e.body!=null)if(e.appendToFormData)for(let[g,v]of Object.entries(e.body))o.append(g,v);else o.append("payload_json",JSON.stringify(e.body));a=o,m=o.getHeaders()}else e.body!=null&&(e.passThroughBody?a=e.body:(a=JSON.stringify(e.body),m={"Content-Type":"application/json"}));let L={agent:this.agent,body:a,headers:{...e.headers??{},...m,...i},method:e.method};return{url:r,fetchOptions:L}}clearHashSweeper(){clearInterval(this.hashTimer)}clearHandlerSweeper(){clearInterval(this.handlerTimer)}static generateRouteData(e,t){let i=/^\/(?:channels|guilds|webhooks)\/(\d{16,19})/.exec(e)?.[1]??"global",r=e.replace(/\d{16,19}/g,":id").replace(/\/reactions\/(.*)/,"/reactions/:reaction"),a="";if(t==="delete"&&r==="/channels/:id/messages/:id"){let m=/\d{16,19}$/.exec(e)[0],L=fe.timestampFrom(m);Date.now()-L>1e3*60*60*24*14&&(a+="/Delete Old Message")}return{majorParameter:i,bucketRoute:r+a,original:e}}},K=X;d(K,"RequestManager"),q=new WeakMap;import{EventEmitter as ve}from"node:events";var ne=class extends ve{constructor(e={}){super();l(this,"cdn");l(this,"requestManager");this.cdn=new P(e.cdn??k.cdn),this.requestManager=new K(e).on("restDebug",this.emit.bind(this,"restDebug")).on("rateLimited",this.emit.bind(this,"rateLimited")).on("invalidRequestWarning",this.emit.bind(this,"invalidRequestWarning")).on("hashSweep",this.emit.bind(this,"hashSweep")),this.on("newListener",(t,s)=>{(t==="request"||t==="response")&&this.requestManager.on(t,s)}),this.on("removeListener",(t,s)=>{(t==="request"||t==="response")&&this.requestManager.off(t,s)})}setToken(e){return this.requestManager.setToken(e),this}get(e,t={}){return this.request({...t,fullRoute:e,method:"get"})}delete(e,t={}){return this.request({...t,fullRoute:e,method:"delete"})}post(e,t={}){return this.request({...t,fullRoute:e,method:"post"})}put(e,t={}){return this.request({...t,fullRoute:e,method:"put"})}patch(e,t={}){return this.request({...t,fullRoute:e,method:"patch"})}request(e){return this.requestManager.queueRequest(e)}};d(ne,"REST");export{se as ALLOWED_EXTENSIONS,N as ALLOWED_SIZES,ie as ALLOWED_STICKER_EXTENSIONS,P as CDN,k as DefaultRestOptions,te as DefaultUserAgent,D as DiscordAPIError,U as HTTPError,ne as REST,T as RESTEvents,I as RateLimitError,K as RequestManager,F as RequestMethod};
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+ var __commonJS = (cb, mod) => function __require() {
6
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
7
+ };
8
+ var __publicField = (obj, key, value) => {
9
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
10
+ return value;
11
+ };
12
+ var __accessCheck = (obj, member, msg) => {
13
+ if (!member.has(obj))
14
+ throw TypeError("Cannot " + msg);
15
+ };
16
+ var __privateGet = (obj, member, getter) => {
17
+ __accessCheck(obj, member, "read from private field");
18
+ return getter ? getter.call(obj) : member.get(obj);
19
+ };
20
+ var __privateAdd = (obj, member, value) => {
21
+ if (member.has(obj))
22
+ throw TypeError("Cannot add the same private member more than once");
23
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
24
+ };
25
+ var __privateSet = (obj, member, value, setter) => {
26
+ __accessCheck(obj, member, "write to private field");
27
+ setter ? setter.call(obj, value) : member.set(obj, value);
28
+ return value;
29
+ };
30
+
31
+ // package.json
32
+ var require_package = __commonJS({
33
+ "package.json"(exports, module) {
34
+ module.exports = {
35
+ name: "@discordjs/rest",
36
+ version: "0.4.0-dev",
37
+ description: "The REST API for discord.js",
38
+ scripts: {
39
+ build: "tsup && tsc --emitDeclarationOnly --incremental",
40
+ test: "jest --pass-with-no-tests --collect-coverage",
41
+ lint: "prettier --check . && eslint src __tests__ --ext mjs,js,ts",
42
+ format: "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix",
43
+ docs: "typedoc --json docs/typedoc-out.json src/index.ts && node scripts/docs.mjs",
44
+ prepublishOnly: "yarn build && yarn lint && yarn test",
45
+ changelog: "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/rest/*'"
46
+ },
47
+ main: "./dist/index.js",
48
+ module: "./dist/index.mjs",
49
+ typings: "./dist/index.d.ts",
50
+ exports: {
51
+ import: "./dist/index.mjs",
52
+ require: "./dist/index.js",
53
+ types: "./dist/index.d.ts"
54
+ },
55
+ directories: {
56
+ lib: "src",
57
+ test: "__tests__"
58
+ },
59
+ files: [
60
+ "dist"
61
+ ],
62
+ contributors: [
63
+ "Crawl <icrawltogo@gmail.com>",
64
+ "Amish Shah <amishshah.2k@gmail.com>",
65
+ "SpaceEEC <spaceeec@yahoo.com>",
66
+ "Vlad Frangu <kingdgrizzle@gmail.com>",
67
+ "Antonio Roman <kyradiscord@gmail.com>"
68
+ ],
69
+ license: "Apache-2.0",
70
+ keywords: [
71
+ "discord",
72
+ "api",
73
+ "rest",
74
+ "discordapp",
75
+ "discordjs"
76
+ ],
77
+ repository: {
78
+ type: "git",
79
+ url: "git+https://github.com/discordjs/discord.js.git"
80
+ },
81
+ bugs: {
82
+ url: "https://github.com/discordjs/discord.js/issues"
83
+ },
84
+ homepage: "https://discord.js.org",
85
+ dependencies: {
86
+ "@discordjs/collection": "workspace:^",
87
+ "@sapphire/async-queue": "^1.2.0",
88
+ "@sapphire/snowflake": "^3.1.0",
89
+ "@types/node-fetch": "^2.5.12",
90
+ "discord-api-types": "^0.29.0",
91
+ "form-data": "^4.0.0",
92
+ "node-fetch": "^2.6.7",
93
+ tslib: "^2.3.1"
94
+ },
95
+ devDependencies: {
96
+ "@babel/core": "^7.17.2",
97
+ "@babel/plugin-proposal-decorators": "^7.17.2",
98
+ "@babel/preset-env": "^7.16.11",
99
+ "@babel/preset-typescript": "^7.16.7",
100
+ "@discordjs/ts-docgen": "^0.3.4",
101
+ "@types/jest": "^27.4.0",
102
+ "@typescript-eslint/eslint-plugin": "^5.11.0",
103
+ "@typescript-eslint/parser": "^5.11.0",
104
+ "babel-plugin-const-enum": "^1.2.0",
105
+ "babel-plugin-transform-typescript-metadata": "^0.3.2",
106
+ eslint: "^8.9.0",
107
+ "eslint-config-marine": "^9.3.2",
108
+ "eslint-config-prettier": "^8.3.0",
109
+ "eslint-plugin-prettier": "^4.0.0",
110
+ jest: "^27.5.1",
111
+ nock: "^13.2.4",
112
+ prettier: "^2.5.1",
113
+ tsup: "^5.11.13",
114
+ typedoc: "^0.22.11",
115
+ typescript: "^4.5.5"
116
+ },
117
+ engines: {
118
+ node: ">=16.9.0"
119
+ },
120
+ publishConfig: {
121
+ access: "public"
122
+ }
123
+ };
124
+ }
125
+ });
126
+
127
+ // src/lib/utils/constants.ts
128
+ import { APIVersion } from "discord-api-types/v10";
129
+ var Package = require_package();
130
+ var DefaultUserAgent = `DiscordBot (${Package.homepage}, ${Package.version})`;
131
+ var DefaultRestOptions = {
132
+ agent: {},
133
+ api: "https://discord.com/api",
134
+ cdn: "https://cdn.discordapp.com",
135
+ headers: {},
136
+ invalidRequestWarningInterval: 0,
137
+ globalRequestsPerSecond: 50,
138
+ offset: 50,
139
+ rejectOnRateLimit: null,
140
+ retries: 3,
141
+ timeout: 15e3,
142
+ userAgentAppendix: `Node.js ${process.version}`,
143
+ version: APIVersion,
144
+ hashSweepInterval: 144e5,
145
+ hashLifetime: 864e5,
146
+ handlerSweepInterval: 36e5
147
+ };
148
+ var RESTEvents = /* @__PURE__ */ ((RESTEvents2) => {
149
+ RESTEvents2["Debug"] = "restDebug";
150
+ RESTEvents2["InvalidRequestWarning"] = "invalidRequestWarning";
151
+ RESTEvents2["RateLimited"] = "rateLimited";
152
+ RESTEvents2["Request"] = "request";
153
+ RESTEvents2["Response"] = "response";
154
+ RESTEvents2["HashSweep"] = "hashSweep";
155
+ RESTEvents2["HandlerSweep"] = "handlerSweep";
156
+ return RESTEvents2;
157
+ })(RESTEvents || {});
158
+ var ALLOWED_EXTENSIONS = ["webp", "png", "jpg", "jpeg", "gif"];
159
+ var ALLOWED_STICKER_EXTENSIONS = ["png", "json"];
160
+ var ALLOWED_SIZES = [16, 32, 64, 128, 256, 512, 1024, 2048, 4096];
161
+
162
+ // src/lib/CDN.ts
163
+ var CDN = class {
164
+ constructor(base = DefaultRestOptions.cdn) {
165
+ this.base = base;
166
+ }
167
+ appAsset(clientId, assetHash, options) {
168
+ return this.makeURL(`/app-assets/${clientId}/${assetHash}`, options);
169
+ }
170
+ appIcon(clientId, iconHash, options) {
171
+ return this.makeURL(`/app-icons/${clientId}/${iconHash}`, options);
172
+ }
173
+ avatar(id, avatarHash, options) {
174
+ return this.dynamicMakeURL(`/avatars/${id}/${avatarHash}`, avatarHash, options);
175
+ }
176
+ banner(id, bannerHash, options) {
177
+ return this.dynamicMakeURL(`/banners/${id}/${bannerHash}`, bannerHash, options);
178
+ }
179
+ channelIcon(channelId, iconHash, options) {
180
+ return this.makeURL(`/channel-icons/${channelId}/${iconHash}`, options);
181
+ }
182
+ defaultAvatar(discriminator) {
183
+ return this.makeURL(`/embed/avatars/${discriminator}`, { extension: "png" });
184
+ }
185
+ discoverySplash(guildId, splashHash, options) {
186
+ return this.makeURL(`/discovery-splashes/${guildId}/${splashHash}`, options);
187
+ }
188
+ emoji(emojiId, extension) {
189
+ return this.makeURL(`/emojis/${emojiId}`, { extension });
190
+ }
191
+ guildMemberAvatar(guildId, userId, avatarHash, options) {
192
+ return this.dynamicMakeURL(`/guilds/${guildId}/users/${userId}/avatars/${avatarHash}`, avatarHash, options);
193
+ }
194
+ icon(id, iconHash, options) {
195
+ return this.dynamicMakeURL(`/icons/${id}/${iconHash}`, iconHash, options);
196
+ }
197
+ roleIcon(roleId, roleIconHash, options) {
198
+ return this.makeURL(`/role-icons/${roleId}/${roleIconHash}`, options);
199
+ }
200
+ splash(guildId, splashHash, options) {
201
+ return this.makeURL(`/splashes/${guildId}/${splashHash}`, options);
202
+ }
203
+ sticker(stickerId, extension) {
204
+ return this.makeURL(`/stickers/${stickerId}`, {
205
+ allowedExtensions: ALLOWED_STICKER_EXTENSIONS,
206
+ extension: extension ?? "png"
207
+ });
208
+ }
209
+ stickerPackBanner(bannerId, options) {
210
+ return this.makeURL(`/app-assets/710982414301790216/store/${bannerId}`, options);
211
+ }
212
+ teamIcon(teamId, iconHash, options) {
213
+ return this.makeURL(`/team-icons/${teamId}/${iconHash}`, options);
214
+ }
215
+ guildScheduledEventCover(scheduledEventId, coverHash, options) {
216
+ return this.makeURL(`/guild-events/${scheduledEventId}/${coverHash}`, options);
217
+ }
218
+ dynamicMakeURL(route, hash, { forceStatic = false, ...options } = {}) {
219
+ return this.makeURL(route, !forceStatic && hash.startsWith("a_") ? { ...options, extension: "gif" } : options);
220
+ }
221
+ makeURL(route, { allowedExtensions = ALLOWED_EXTENSIONS, extension = "webp", size } = {}) {
222
+ extension = String(extension).toLowerCase();
223
+ if (!allowedExtensions.includes(extension)) {
224
+ throw new RangeError(`Invalid extension provided: ${extension}
225
+ Must be one of: ${allowedExtensions.join(", ")}`);
226
+ }
227
+ if (size && !ALLOWED_SIZES.includes(size)) {
228
+ throw new RangeError(`Invalid size provided: ${size}
229
+ Must be one of: ${ALLOWED_SIZES.join(", ")}`);
230
+ }
231
+ const url = new URL(`${this.base}${route}.${extension}`);
232
+ if (size) {
233
+ url.searchParams.set("size", String(size));
234
+ }
235
+ return url.toString();
236
+ }
237
+ };
238
+ __name(CDN, "CDN");
239
+
240
+ // src/lib/errors/DiscordAPIError.ts
241
+ function isErrorGroupWrapper(error) {
242
+ return Reflect.has(error, "_errors");
243
+ }
244
+ __name(isErrorGroupWrapper, "isErrorGroupWrapper");
245
+ function isErrorResponse(error) {
246
+ return typeof Reflect.get(error, "message") === "string";
247
+ }
248
+ __name(isErrorResponse, "isErrorResponse");
249
+ var DiscordAPIError = class extends Error {
250
+ constructor(rawError, code, status, method, url, bodyData) {
251
+ super(DiscordAPIError.getMessage(rawError));
252
+ this.rawError = rawError;
253
+ this.code = code;
254
+ this.status = status;
255
+ this.method = method;
256
+ this.url = url;
257
+ __publicField(this, "requestBody");
258
+ this.requestBody = { files: bodyData.files, json: bodyData.body };
259
+ }
260
+ get name() {
261
+ return `${DiscordAPIError.name}[${this.code}]`;
262
+ }
263
+ static getMessage(error) {
264
+ let flattened = "";
265
+ if ("code" in error) {
266
+ if (error.errors) {
267
+ flattened = [...this.flattenDiscordError(error.errors)].join("\n");
268
+ }
269
+ return error.message && flattened ? `${error.message}
270
+ ${flattened}` : error.message || flattened || "Unknown Error";
271
+ }
272
+ return error.error_description ?? "No Description";
273
+ }
274
+ static *flattenDiscordError(obj, key = "") {
275
+ if (isErrorResponse(obj)) {
276
+ return yield `${key.length ? `${key}[${obj.code}]` : `${obj.code}`}: ${obj.message}`.trim();
277
+ }
278
+ for (const [k, v] of Object.entries(obj)) {
279
+ const nextKey = k.startsWith("_") ? key : key ? Number.isNaN(Number(k)) ? `${key}.${k}` : `${key}[${k}]` : k;
280
+ if (typeof v === "string") {
281
+ yield v;
282
+ } else if (isErrorGroupWrapper(v)) {
283
+ for (const error of v._errors) {
284
+ yield* this.flattenDiscordError(error, nextKey);
285
+ }
286
+ } else {
287
+ yield* this.flattenDiscordError(v, nextKey);
288
+ }
289
+ }
290
+ }
291
+ };
292
+ __name(DiscordAPIError, "DiscordAPIError");
293
+
294
+ // src/lib/errors/HTTPError.ts
295
+ var HTTPError = class extends Error {
296
+ constructor(message, name, status, method, url, bodyData) {
297
+ super(message);
298
+ this.name = name;
299
+ this.status = status;
300
+ this.method = method;
301
+ this.url = url;
302
+ __publicField(this, "requestBody");
303
+ this.requestBody = { files: bodyData.files, json: bodyData.body };
304
+ }
305
+ };
306
+ __name(HTTPError, "HTTPError");
307
+
308
+ // src/lib/errors/RateLimitError.ts
309
+ var RateLimitError = class extends Error {
310
+ constructor({ timeToReset, limit, method, hash, url, route, majorParameter, global }) {
311
+ super();
312
+ __publicField(this, "timeToReset");
313
+ __publicField(this, "limit");
314
+ __publicField(this, "method");
315
+ __publicField(this, "hash");
316
+ __publicField(this, "url");
317
+ __publicField(this, "route");
318
+ __publicField(this, "majorParameter");
319
+ __publicField(this, "global");
320
+ this.timeToReset = timeToReset;
321
+ this.limit = limit;
322
+ this.method = method;
323
+ this.hash = hash;
324
+ this.url = url;
325
+ this.route = route;
326
+ this.majorParameter = majorParameter;
327
+ this.global = global;
328
+ }
329
+ get name() {
330
+ return `${RateLimitError.name}[${this.route}]`;
331
+ }
332
+ };
333
+ __name(RateLimitError, "RateLimitError");
334
+
335
+ // src/lib/RequestManager.ts
336
+ import Collection from "@discordjs/collection";
337
+ import FormData from "form-data";
338
+ import { DiscordSnowflake } from "@sapphire/snowflake";
339
+ import { EventEmitter } from "node:events";
340
+ import { Agent as httpsAgent } from "node:https";
341
+ import { Agent as httpAgent } from "node:http";
342
+
343
+ // src/lib/handlers/SequentialHandler.ts
344
+ import { setTimeout as sleep } from "node:timers/promises";
345
+ import { AsyncQueue } from "@sapphire/async-queue";
346
+ import fetch from "node-fetch";
347
+
348
+ // src/lib/utils/utils.ts
349
+ function parseResponse(res) {
350
+ if (res.headers.get("Content-Type")?.startsWith("application/json")) {
351
+ return res.json();
352
+ }
353
+ return res.arrayBuffer();
354
+ }
355
+ __name(parseResponse, "parseResponse");
356
+ function hasSublimit(bucketRoute, body, method) {
357
+ if (bucketRoute === "/channels/:id") {
358
+ if (typeof body !== "object" || body === null)
359
+ return false;
360
+ if (method !== "patch" /* Patch */)
361
+ return false;
362
+ const castedBody = body;
363
+ return ["name", "topic"].some((key) => Reflect.has(castedBody, key));
364
+ }
365
+ return true;
366
+ }
367
+ __name(hasSublimit, "hasSublimit");
368
+
369
+ // src/lib/handlers/SequentialHandler.ts
370
+ var invalidCount = 0;
371
+ var invalidCountResetTime = null;
372
+ var _asyncQueue, _sublimitedQueue, _sublimitPromise, _shiftSublimit;
373
+ var SequentialHandler = class {
374
+ constructor(manager, hash, majorParameter) {
375
+ this.manager = manager;
376
+ this.hash = hash;
377
+ this.majorParameter = majorParameter;
378
+ __publicField(this, "id");
379
+ __publicField(this, "reset", -1);
380
+ __publicField(this, "remaining", 1);
381
+ __publicField(this, "limit", Infinity);
382
+ __privateAdd(this, _asyncQueue, new AsyncQueue());
383
+ __privateAdd(this, _sublimitedQueue, null);
384
+ __privateAdd(this, _sublimitPromise, null);
385
+ __privateAdd(this, _shiftSublimit, false);
386
+ this.id = `${hash}:${majorParameter}`;
387
+ }
388
+ get inactive() {
389
+ return __privateGet(this, _asyncQueue).remaining === 0 && (__privateGet(this, _sublimitedQueue) === null || __privateGet(this, _sublimitedQueue).remaining === 0) && !this.limited;
390
+ }
391
+ get globalLimited() {
392
+ return this.manager.globalRemaining <= 0 && Date.now() < this.manager.globalReset;
393
+ }
394
+ get localLimited() {
395
+ return this.remaining <= 0 && Date.now() < this.reset;
396
+ }
397
+ get limited() {
398
+ return this.globalLimited || this.localLimited;
399
+ }
400
+ get timeToReset() {
401
+ return this.reset + this.manager.options.offset - Date.now();
402
+ }
403
+ debug(message) {
404
+ this.manager.emit("restDebug" /* Debug */, `[REST ${this.id}] ${message}`);
405
+ }
406
+ async globalDelayFor(time) {
407
+ await sleep(time, void 0, { ref: false });
408
+ this.manager.globalDelay = null;
409
+ }
410
+ async onRateLimit(rateLimitData) {
411
+ const { options } = this.manager;
412
+ if (!options.rejectOnRateLimit)
413
+ return;
414
+ const shouldThrow = typeof options.rejectOnRateLimit === "function" ? await options.rejectOnRateLimit(rateLimitData) : options.rejectOnRateLimit.some((route) => rateLimitData.route.startsWith(route.toLowerCase()));
415
+ if (shouldThrow) {
416
+ throw new RateLimitError(rateLimitData);
417
+ }
418
+ }
419
+ async queueRequest(routeId, url, options, requestData) {
420
+ let queue = __privateGet(this, _asyncQueue);
421
+ let queueType = 0 /* Standard */;
422
+ if (__privateGet(this, _sublimitedQueue) && hasSublimit(routeId.bucketRoute, requestData.body, options.method)) {
423
+ queue = __privateGet(this, _sublimitedQueue);
424
+ queueType = 1 /* Sublimit */;
425
+ }
426
+ await queue.wait();
427
+ if (queueType === 0 /* Standard */) {
428
+ if (__privateGet(this, _sublimitedQueue) && hasSublimit(routeId.bucketRoute, requestData.body, options.method)) {
429
+ queue = __privateGet(this, _sublimitedQueue);
430
+ const wait = queue.wait();
431
+ __privateGet(this, _asyncQueue).shift();
432
+ await wait;
433
+ } else if (__privateGet(this, _sublimitPromise)) {
434
+ await __privateGet(this, _sublimitPromise).promise;
435
+ }
436
+ }
437
+ try {
438
+ return await this.runRequest(routeId, url, options, requestData);
439
+ } finally {
440
+ queue.shift();
441
+ if (__privateGet(this, _shiftSublimit)) {
442
+ __privateSet(this, _shiftSublimit, false);
443
+ __privateGet(this, _sublimitedQueue)?.shift();
444
+ }
445
+ if (__privateGet(this, _sublimitedQueue)?.remaining === 0) {
446
+ __privateGet(this, _sublimitPromise)?.resolve();
447
+ __privateSet(this, _sublimitedQueue, null);
448
+ }
449
+ }
450
+ }
451
+ async runRequest(routeId, url, options, requestData, retries = 0) {
452
+ while (this.limited) {
453
+ const isGlobal = this.globalLimited;
454
+ let limit2;
455
+ let timeout2;
456
+ let delay;
457
+ if (isGlobal) {
458
+ limit2 = this.manager.options.globalRequestsPerSecond;
459
+ timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
460
+ if (!this.manager.globalDelay) {
461
+ this.manager.globalDelay = this.globalDelayFor(timeout2);
462
+ }
463
+ delay = this.manager.globalDelay;
464
+ } else {
465
+ limit2 = this.limit;
466
+ timeout2 = this.timeToReset;
467
+ delay = sleep(timeout2);
468
+ }
469
+ const rateLimitData = {
470
+ timeToReset: timeout2,
471
+ limit: limit2,
472
+ method: options.method ?? "get",
473
+ hash: this.hash,
474
+ url,
475
+ route: routeId.bucketRoute,
476
+ majorParameter: this.majorParameter,
477
+ global: isGlobal
478
+ };
479
+ this.manager.emit("rateLimited" /* RateLimited */, rateLimitData);
480
+ await this.onRateLimit(rateLimitData);
481
+ if (isGlobal) {
482
+ this.debug(`Global rate limit hit, blocking all requests for ${timeout2}ms`);
483
+ } else {
484
+ this.debug(`Waiting ${timeout2}ms for rate limit to pass`);
485
+ }
486
+ await delay;
487
+ }
488
+ if (!this.manager.globalReset || this.manager.globalReset < Date.now()) {
489
+ this.manager.globalReset = Date.now() + 1e3;
490
+ this.manager.globalRemaining = this.manager.options.globalRequestsPerSecond;
491
+ }
492
+ this.manager.globalRemaining--;
493
+ const method = options.method ?? "get";
494
+ if (this.manager.listenerCount("request" /* Request */)) {
495
+ this.manager.emit("request" /* Request */, {
496
+ method,
497
+ path: routeId.original,
498
+ route: routeId.bucketRoute,
499
+ options,
500
+ data: requestData,
501
+ retries
502
+ });
503
+ }
504
+ const controller = new AbortController();
505
+ const timeout = setTimeout(() => controller.abort(), this.manager.options.timeout).unref();
506
+ let res;
507
+ try {
508
+ res = await fetch(url, { ...options, signal: controller.signal });
509
+ } catch (error) {
510
+ if (error instanceof Error && error.name === "AbortError" && retries !== this.manager.options.retries) {
511
+ return await this.runRequest(routeId, url, options, requestData, ++retries);
512
+ }
513
+ throw error;
514
+ } finally {
515
+ clearTimeout(timeout);
516
+ }
517
+ if (this.manager.listenerCount("response" /* Response */)) {
518
+ this.manager.emit("response" /* Response */, {
519
+ method,
520
+ path: routeId.original,
521
+ route: routeId.bucketRoute,
522
+ options,
523
+ data: requestData,
524
+ retries
525
+ }, res.clone());
526
+ }
527
+ let retryAfter = 0;
528
+ const limit = res.headers.get("X-RateLimit-Limit");
529
+ const remaining = res.headers.get("X-RateLimit-Remaining");
530
+ const reset = res.headers.get("X-RateLimit-Reset-After");
531
+ const hash = res.headers.get("X-RateLimit-Bucket");
532
+ const retry = res.headers.get("Retry-After");
533
+ this.limit = limit ? Number(limit) : Infinity;
534
+ this.remaining = remaining ? Number(remaining) : 1;
535
+ this.reset = reset ? Number(reset) * 1e3 + Date.now() + this.manager.options.offset : Date.now();
536
+ if (retry)
537
+ retryAfter = Number(retry) * 1e3 + this.manager.options.offset;
538
+ if (hash && hash !== this.hash) {
539
+ this.debug(["Received bucket hash update", ` Old Hash : ${this.hash}`, ` New Hash : ${hash}`].join("\n"));
540
+ this.manager.hashes.set(`${method}:${routeId.bucketRoute}`, { value: hash, lastAccess: Date.now() });
541
+ } else if (hash) {
542
+ const hashData = this.manager.hashes.get(`${method}:${routeId.bucketRoute}`);
543
+ if (hashData) {
544
+ hashData.lastAccess = Date.now();
545
+ }
546
+ }
547
+ let sublimitTimeout = null;
548
+ if (retryAfter > 0) {
549
+ if (res.headers.get("X-RateLimit-Global")) {
550
+ this.manager.globalRemaining = 0;
551
+ this.manager.globalReset = Date.now() + retryAfter;
552
+ } else if (!this.localLimited) {
553
+ sublimitTimeout = retryAfter;
554
+ }
555
+ }
556
+ if (res.status === 401 || res.status === 403 || res.status === 429) {
557
+ if (!invalidCountResetTime || invalidCountResetTime < Date.now()) {
558
+ invalidCountResetTime = Date.now() + 1e3 * 60 * 10;
559
+ invalidCount = 0;
560
+ }
561
+ invalidCount++;
562
+ const emitInvalid = this.manager.options.invalidRequestWarningInterval > 0 && invalidCount % this.manager.options.invalidRequestWarningInterval === 0;
563
+ if (emitInvalid) {
564
+ this.manager.emit("invalidRequestWarning" /* InvalidRequestWarning */, {
565
+ count: invalidCount,
566
+ remainingTime: invalidCountResetTime - Date.now()
567
+ });
568
+ }
569
+ }
570
+ if (res.ok) {
571
+ return parseResponse(res);
572
+ } else if (res.status === 429) {
573
+ const isGlobal = this.globalLimited;
574
+ let limit2;
575
+ let timeout2;
576
+ if (isGlobal) {
577
+ limit2 = this.manager.options.globalRequestsPerSecond;
578
+ timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
579
+ } else {
580
+ limit2 = this.limit;
581
+ timeout2 = this.timeToReset;
582
+ }
583
+ await this.onRateLimit({
584
+ timeToReset: timeout2,
585
+ limit: limit2,
586
+ method,
587
+ hash: this.hash,
588
+ url,
589
+ route: routeId.bucketRoute,
590
+ majorParameter: this.majorParameter,
591
+ global: isGlobal
592
+ });
593
+ this.debug([
594
+ "Encountered unexpected 429 rate limit",
595
+ ` Global : ${isGlobal.toString()}`,
596
+ ` Method : ${method}`,
597
+ ` URL : ${url}`,
598
+ ` Bucket : ${routeId.bucketRoute}`,
599
+ ` Major parameter: ${routeId.majorParameter}`,
600
+ ` Hash : ${this.hash}`,
601
+ ` Limit : ${limit2}`,
602
+ ` Retry After : ${retryAfter}ms`,
603
+ ` Sublimit : ${sublimitTimeout ? `${sublimitTimeout}ms` : "None"}`
604
+ ].join("\n"));
605
+ if (sublimitTimeout) {
606
+ const firstSublimit = !__privateGet(this, _sublimitedQueue);
607
+ if (firstSublimit) {
608
+ __privateSet(this, _sublimitedQueue, new AsyncQueue());
609
+ void __privateGet(this, _sublimitedQueue).wait();
610
+ __privateGet(this, _asyncQueue).shift();
611
+ }
612
+ __privateGet(this, _sublimitPromise)?.resolve();
613
+ __privateSet(this, _sublimitPromise, null);
614
+ await sleep(sublimitTimeout, void 0, { ref: false });
615
+ let resolve;
616
+ const promise = new Promise((res2) => resolve = res2);
617
+ __privateSet(this, _sublimitPromise, { promise, resolve });
618
+ if (firstSublimit) {
619
+ await __privateGet(this, _asyncQueue).wait();
620
+ __privateSet(this, _shiftSublimit, true);
621
+ }
622
+ }
623
+ return this.runRequest(routeId, url, options, requestData, retries);
624
+ } else if (res.status >= 500 && res.status < 600) {
625
+ if (retries !== this.manager.options.retries) {
626
+ return this.runRequest(routeId, url, options, requestData, ++retries);
627
+ }
628
+ throw new HTTPError(res.statusText, res.constructor.name, res.status, method, url, requestData);
629
+ } else {
630
+ if (res.status >= 400 && res.status < 500) {
631
+ if (res.status === 401 && requestData.auth) {
632
+ this.manager.setToken(null);
633
+ }
634
+ const data = await parseResponse(res);
635
+ throw new DiscordAPIError(data, "code" in data ? data.code : data.error, res.status, method, url, requestData);
636
+ }
637
+ return null;
638
+ }
639
+ }
640
+ };
641
+ __name(SequentialHandler, "SequentialHandler");
642
+ _asyncQueue = new WeakMap();
643
+ _sublimitedQueue = new WeakMap();
644
+ _sublimitPromise = new WeakMap();
645
+ _shiftSublimit = new WeakMap();
646
+
647
+ // src/lib/RequestManager.ts
648
+ var RequestMethod = /* @__PURE__ */ ((RequestMethod2) => {
649
+ RequestMethod2["Delete"] = "delete";
650
+ RequestMethod2["Get"] = "get";
651
+ RequestMethod2["Patch"] = "patch";
652
+ RequestMethod2["Post"] = "post";
653
+ RequestMethod2["Put"] = "put";
654
+ return RequestMethod2;
655
+ })(RequestMethod || {});
656
+ var _token;
657
+ var _RequestManager = class extends EventEmitter {
658
+ constructor(options) {
659
+ super();
660
+ __publicField(this, "globalRemaining");
661
+ __publicField(this, "globalDelay", null);
662
+ __publicField(this, "globalReset", -1);
663
+ __publicField(this, "hashes", new Collection());
664
+ __publicField(this, "handlers", new Collection());
665
+ __privateAdd(this, _token, null);
666
+ __publicField(this, "hashTimer");
667
+ __publicField(this, "handlerTimer");
668
+ __publicField(this, "agent", null);
669
+ __publicField(this, "options");
670
+ this.options = { ...DefaultRestOptions, ...options };
671
+ this.options.offset = Math.max(0, this.options.offset);
672
+ this.globalRemaining = this.options.globalRequestsPerSecond;
673
+ this.setupSweepers();
674
+ }
675
+ setupSweepers() {
676
+ const validateMaxInterval = /* @__PURE__ */ __name((interval) => {
677
+ if (interval > 144e5) {
678
+ throw new Error("Cannot set an interval greater than 4 hours");
679
+ }
680
+ }, "validateMaxInterval");
681
+ if (this.options.hashSweepInterval !== 0 && this.options.hashSweepInterval !== Infinity) {
682
+ validateMaxInterval(this.options.hashSweepInterval);
683
+ this.hashTimer = setInterval(() => {
684
+ const sweptHashes = new Collection();
685
+ const currentDate = Date.now();
686
+ this.hashes.sweep((v, k) => {
687
+ if (v.lastAccess === -1)
688
+ return false;
689
+ const shouldSweep = Math.floor(currentDate - v.lastAccess) > this.options.hashLifetime;
690
+ if (shouldSweep) {
691
+ sweptHashes.set(k, v);
692
+ }
693
+ this.emit("restDebug" /* Debug */, `Hash ${v.value} for ${k} swept due to lifetime being exceeded`);
694
+ return shouldSweep;
695
+ });
696
+ this.emit("hashSweep" /* HashSweep */, sweptHashes);
697
+ }, this.options.hashSweepInterval).unref();
698
+ }
699
+ if (this.options.handlerSweepInterval !== 0 && this.options.handlerSweepInterval !== Infinity) {
700
+ validateMaxInterval(this.options.handlerSweepInterval);
701
+ this.handlerTimer = setInterval(() => {
702
+ const sweptHandlers = new Collection();
703
+ this.handlers.sweep((v, k) => {
704
+ const { inactive } = v;
705
+ if (inactive) {
706
+ sweptHandlers.set(k, v);
707
+ }
708
+ this.emit("restDebug" /* Debug */, `Handler ${v.id} for ${k} swept due to being inactive`);
709
+ return inactive;
710
+ });
711
+ this.emit("handlerSweep" /* HandlerSweep */, sweptHandlers);
712
+ }, this.options.handlerSweepInterval).unref();
713
+ }
714
+ }
715
+ setToken(token) {
716
+ __privateSet(this, _token, token);
717
+ return this;
718
+ }
719
+ async queueRequest(request) {
720
+ const routeId = _RequestManager.generateRouteData(request.fullRoute, request.method);
721
+ const hash = this.hashes.get(`${request.method}:${routeId.bucketRoute}`) ?? {
722
+ value: `Global(${request.method}:${routeId.bucketRoute})`,
723
+ lastAccess: -1
724
+ };
725
+ const handler = this.handlers.get(`${hash.value}:${routeId.majorParameter}`) ?? this.createHandler(hash.value, routeId.majorParameter);
726
+ const { url, fetchOptions } = this.resolveRequest(request);
727
+ return handler.queueRequest(routeId, url, fetchOptions, {
728
+ body: request.body,
729
+ files: request.files,
730
+ auth: request.auth !== false
731
+ });
732
+ }
733
+ createHandler(hash, majorParameter) {
734
+ const queue = new SequentialHandler(this, hash, majorParameter);
735
+ this.handlers.set(queue.id, queue);
736
+ return queue;
737
+ }
738
+ resolveRequest(request) {
739
+ const { options } = this;
740
+ this.agent ??= options.api.startsWith("https") ? new httpsAgent({ ...options.agent, keepAlive: true }) : new httpAgent({ ...options.agent, keepAlive: true });
741
+ let query = "";
742
+ if (request.query) {
743
+ const resolvedQuery = request.query.toString();
744
+ if (resolvedQuery !== "") {
745
+ query = `?${resolvedQuery}`;
746
+ }
747
+ }
748
+ const headers = {
749
+ ...this.options.headers,
750
+ "User-Agent": `${DefaultUserAgent} ${options.userAgentAppendix}`.trim()
751
+ };
752
+ if (request.auth !== false) {
753
+ if (!__privateGet(this, _token)) {
754
+ throw new Error("Expected token to be set for this request, but none was present");
755
+ }
756
+ headers.Authorization = `${request.authPrefix ?? "Bot"} ${__privateGet(this, _token)}`;
757
+ }
758
+ if (request.reason?.length) {
759
+ headers["X-Audit-Log-Reason"] = encodeURIComponent(request.reason);
760
+ }
761
+ const url = `${options.api}${request.versioned === false ? "" : `/v${options.version}`}${request.fullRoute}${query}`;
762
+ let finalBody;
763
+ let additionalHeaders = {};
764
+ if (request.files?.length) {
765
+ const formData = new FormData();
766
+ for (const [index, file] of request.files.entries()) {
767
+ formData.append(file.key ?? `files[${index}]`, file.data, file.name);
768
+ }
769
+ if (request.body != null) {
770
+ if (request.appendToFormData) {
771
+ for (const [key, value] of Object.entries(request.body)) {
772
+ formData.append(key, value);
773
+ }
774
+ } else {
775
+ formData.append("payload_json", JSON.stringify(request.body));
776
+ }
777
+ }
778
+ finalBody = formData;
779
+ additionalHeaders = formData.getHeaders();
780
+ } else if (request.body != null) {
781
+ if (request.passThroughBody) {
782
+ finalBody = request.body;
783
+ } else {
784
+ finalBody = JSON.stringify(request.body);
785
+ additionalHeaders = { "Content-Type": "application/json" };
786
+ }
787
+ }
788
+ const fetchOptions = {
789
+ agent: this.agent,
790
+ body: finalBody,
791
+ headers: { ...request.headers ?? {}, ...additionalHeaders, ...headers },
792
+ method: request.method
793
+ };
794
+ return { url, fetchOptions };
795
+ }
796
+ clearHashSweeper() {
797
+ clearInterval(this.hashTimer);
798
+ }
799
+ clearHandlerSweeper() {
800
+ clearInterval(this.handlerTimer);
801
+ }
802
+ static generateRouteData(endpoint, method) {
803
+ const majorIdMatch = /^\/(?:channels|guilds|webhooks)\/(\d{16,19})/.exec(endpoint);
804
+ const majorId = majorIdMatch?.[1] ?? "global";
805
+ const baseRoute = endpoint.replace(/\d{16,19}/g, ":id").replace(/\/reactions\/(.*)/, "/reactions/:reaction");
806
+ let exceptions = "";
807
+ if (method === "delete" /* Delete */ && baseRoute === "/channels/:id/messages/:id") {
808
+ const id = /\d{16,19}$/.exec(endpoint)[0];
809
+ const timestamp = DiscordSnowflake.timestampFrom(id);
810
+ if (Date.now() - timestamp > 1e3 * 60 * 60 * 24 * 14) {
811
+ exceptions += "/Delete Old Message";
812
+ }
813
+ }
814
+ return {
815
+ majorParameter: majorId,
816
+ bucketRoute: baseRoute + exceptions,
817
+ original: endpoint
818
+ };
819
+ }
820
+ };
821
+ var RequestManager = _RequestManager;
822
+ __name(RequestManager, "RequestManager");
823
+ _token = new WeakMap();
824
+
825
+ // src/lib/REST.ts
826
+ import { EventEmitter as EventEmitter2 } from "node:events";
827
+ var REST = class extends EventEmitter2 {
828
+ constructor(options = {}) {
829
+ super();
830
+ __publicField(this, "cdn");
831
+ __publicField(this, "requestManager");
832
+ this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn);
833
+ this.requestManager = new RequestManager(options).on("restDebug" /* Debug */, this.emit.bind(this, "restDebug" /* Debug */)).on("rateLimited" /* RateLimited */, this.emit.bind(this, "rateLimited" /* RateLimited */)).on("invalidRequestWarning" /* InvalidRequestWarning */, this.emit.bind(this, "invalidRequestWarning" /* InvalidRequestWarning */)).on("hashSweep" /* HashSweep */, this.emit.bind(this, "hashSweep" /* HashSweep */));
834
+ this.on("newListener", (name, listener) => {
835
+ if (name === "request" /* Request */ || name === "response" /* Response */)
836
+ this.requestManager.on(name, listener);
837
+ });
838
+ this.on("removeListener", (name, listener) => {
839
+ if (name === "request" /* Request */ || name === "response" /* Response */)
840
+ this.requestManager.off(name, listener);
841
+ });
842
+ }
843
+ setToken(token) {
844
+ this.requestManager.setToken(token);
845
+ return this;
846
+ }
847
+ get(fullRoute, options = {}) {
848
+ return this.request({ ...options, fullRoute, method: "get" /* Get */ });
849
+ }
850
+ delete(fullRoute, options = {}) {
851
+ return this.request({ ...options, fullRoute, method: "delete" /* Delete */ });
852
+ }
853
+ post(fullRoute, options = {}) {
854
+ return this.request({ ...options, fullRoute, method: "post" /* Post */ });
855
+ }
856
+ put(fullRoute, options = {}) {
857
+ return this.request({ ...options, fullRoute, method: "put" /* Put */ });
858
+ }
859
+ patch(fullRoute, options = {}) {
860
+ return this.request({ ...options, fullRoute, method: "patch" /* Patch */ });
861
+ }
862
+ request(options) {
863
+ return this.requestManager.queueRequest(options);
864
+ }
865
+ };
866
+ __name(REST, "REST");
867
+ export {
868
+ ALLOWED_EXTENSIONS,
869
+ ALLOWED_SIZES,
870
+ ALLOWED_STICKER_EXTENSIONS,
871
+ CDN,
872
+ DefaultRestOptions,
873
+ DefaultUserAgent,
874
+ DiscordAPIError,
875
+ HTTPError,
876
+ REST,
877
+ RESTEvents,
878
+ RateLimitError,
879
+ RequestManager,
880
+ RequestMethod
881
+ };
8
882
  //# sourceMappingURL=index.mjs.map