index.mjs 17.4 KB
var ne=Object.defineProperty;var ae=(n,e,t)=>e in n?ne(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var oe=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports);var l=(n,e,t)=>(ae(n,typeof e!="symbol"?e+"":e,t),t),Q=(n,e,t)=>{if(!e.has(n))throw TypeError("Cannot "+t)};var u=(n,e,t)=>(Q(n,e,"read from private field"),t?t.call(n):e.get(n)),L=(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)},g=(n,e,t,s)=>(Q(n,e,"write to private field"),s?s.call(n,t):e.set(n,t),t);var Z=oe((we,le)=>{le.exports={name:"@discordjs/rest",version:"0.3.0",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"},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":"^0.4.0","@sapphire/async-queue":"^1.1.9","@sapphire/snowflake":"^3.0.1","discord-api-types":"^0.26.1","form-data":"^4.0.0","node-fetch":"^2.6.5",tslib:"^2.3.1"},devDependencies:{"@babel/core":"^7.16.12","@babel/plugin-proposal-decorators":"^7.16.7","@babel/preset-env":"^7.16.11","@babel/preset-typescript":"^7.16.7","@discordjs/ts-docgen":"^0.3.4","@types/jest":"^27.4.0","@types/node-fetch":"^2.5.10","@typescript-eslint/eslint-plugin":"^5.10.0","@typescript-eslint/parser":"^5.10.0","babel-plugin-const-enum":"^1.2.0","babel-plugin-transform-typescript-metadata":"^0.3.2",eslint:"^8.7.0","eslint-config-marine":"^9.3.2","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0",jest:"^27.4.7",nock:"^13.2.2",prettier:"^2.5.1",tsup:"^5.11.11",typedoc:"^0.22.11",typescript:"^4.5.5"},engines:{node:">=16.9.0"},publishConfig:{access:"public"}}});import{APIVersion as ue}from"discord-api-types/v9";var V=Z(),Y=`DiscordBot (${V.homepage}, ${V.version})`,x={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:ue,hashSweepInterval:144e5,hashLifetime:864e5,handlerSweepInterval:36e5},j=(m=>(m.Debug="restDebug",m.InvalidRequestWarning="invalidRequestWarning",m.RateLimited="rateLimited",m.Request="request",m.Response="response",m.HashSweep="hashSweep",m.HandlerSweep="handlerSweep",m))(j||{}),ee=["webp","png","jpg","jpeg","gif"],te=["png","json"],_=[16,32,64,128,256,512,1024,2048,4096];var N=class{constructor(e=x.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}`)}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:te,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)}dynamicMakeURL(e,t,{forceStatic:s=!1,...i}={}){return this.makeURL(e,!s&&t.startsWith("a_")?{...i,extension:"gif"}:i)}makeURL(e,{allowedExtensions:t=ee,extension:s="webp",size:i}={}){if(s=String(s).toLowerCase(),!t.includes(s))throw new RangeError(`Invalid extension provided: ${s}
Must be one of: ${t.join(", ")}`);if(i&&!_.includes(i))throw new RangeError(`Invalid size provided: ${i}
Must be one of: ${_.join(", ")}`);let r=new URL(`${this.base}${e}.${s}`);return i&&r.searchParams.set("size",String(i)),r.toString()}};function me(n){return Reflect.has(n,"_errors")}function pe(n){return typeof Reflect.get(n,"message")=="string"}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(`
`)),e.message&&t?`${e.message}
${t}`:e.message||t||"Unknown Error"):e.error_description??"No Description"}static*flattenDiscordError(e,t=""){if(pe(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(me(i))for(let a of i._errors)yield*this.flattenDiscordError(a,r);else yield*this.flattenDiscordError(i,r)}}};var B=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}}};var A=class extends Error{constructor({timeToReset:e,limit:t,method:s,hash:i,url:r,route:a,majorParameter:m,global:E}){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=E}get name(){return`${A.name}[${this.route}]`}};import U from"@discordjs/collection";import he from"form-data";import{DiscordSnowflake as de}from"@sapphire/snowflake";import{EventEmitter as ge}from"node:events";import{Agent as fe}from"node:https";import{Agent as Re}from"node:http";import{setTimeout as W}from"node:timers/promises";import{AsyncQueue as se}from"@sapphire/async-queue";import ce from"node-fetch";function M(n){return n.headers.get("Content-Type")?.startsWith("application/json")?n.json():n.buffer()}function H(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!1}var T=0,P=null;var w,c,b,k,C=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);L(this,w,new se);L(this,c,null);L(this,b,null);L(this,k,!1);this.id=`${t}:${s}`}get inactive(){return u(this,w).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 W(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 A(e)}async queueRequest(e,t,s,i){let r=u(this,w),a=0;if(u(this,c)&&H(e.bucketRoute,i.body,s.method)&&(r=u(this,c),a=1),await r.wait(),a===0)if(u(this,c)&&H(e.bucketRoute,i.body,s.method)){r=u(this,c);let m=r.wait();u(this,w).shift(),await m}else u(this,b)&&await u(this,b).promise;try{return await this.runRequest(e,t,s,i)}finally{r.shift(),u(this,k)&&(g(this,k,!1),u(this,c)?.shift()),u(this,c)?.remaining===0&&(u(this,b)?.resolve(),g(this,c,null))}}async runRequest(e,t,s,i,r=0){for(;this.limited;){let p=this.globalLimited,v,h,S;p?(v=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):(v=this.limit,h=this.timeToReset,S=W(h));let O={timeToReset:h,limit:v,method:s.method??"get",hash:this.hash,url:t,route:e.bucketRoute,majorParameter:this.majorParameter,global:p};this.manager.emit("rateLimited",O),await this.onRateLimit(O),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,E=setTimeout(()=>m.abort(),this.manager.options.timeout).unref(),o;try{o=await ce(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(E)}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 d=0,y=o.headers.get("X-RateLimit-Limit"),X=o.headers.get("X-RateLimit-Remaining"),z=o.headers.get("X-RateLimit-Reset-After"),$=o.headers.get("X-RateLimit-Bucket"),J=o.headers.get("Retry-After");if(this.limit=y?Number(y):1/0,this.remaining=X?Number(X):1,this.reset=z?Number(z)*1e3+Date.now()+this.manager.options.offset:Date.now(),J&&(d=Number(J)*1e3+this.manager.options.offset),$&&$!==this.hash)this.debug(["Received bucket hash update",`  Old Hash  : ${this.hash}`,`  New Hash  : ${$}`].join(`
`)),this.manager.hashes.set(`${a}:${e.bucketRoute}`,{value:$,lastAccess:Date.now()});else if($){let p=this.manager.hashes.get(`${a}:${e.bucketRoute}`);p&&(p.lastAccess=Date.now())}let q=null;if(d>0&&(o.headers.get("X-RateLimit-Global")?(this.manager.globalRemaining=0,this.manager.globalReset=Date.now()+d):this.localLimited||(q=d)),(o.status===401||o.status===403||o.status===429)&&((!P||P<Date.now())&&(P=Date.now()+1e3*60*10,T=0),T++,this.manager.options.invalidRequestWarningInterval>0&&T%this.manager.options.invalidRequestWarningInterval===0&&this.manager.emit("invalidRequestWarning",{count:T,remainingTime:P-Date.now()})),o.ok)return M(o);if(o.status===429){let p=this.globalLimited,v,h;if(p?(v=this.manager.options.globalRequestsPerSecond,h=this.manager.globalReset+this.manager.options.offset-Date.now()):(v=this.limit,h=this.timeToReset),await this.onRateLimit({timeToReset:h,limit:v,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          : ${v}`,`  Retry After    : ${d}ms`,`  Sublimit       : ${q?`${q}ms`:"None"}`].join(`
`)),q){let S=!u(this,c);S&&(g(this,c,new se),u(this,c).wait(),u(this,w).shift()),u(this,b)?.resolve(),g(this,b,null),await W(q,void 0,{ref:!1});let O,ie=new Promise(re=>O=re);g(this,b,{promise:ie,resolve:O}),S&&(await u(this,w).wait(),g(this,k,!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 B(o.statusText,o.constructor.name,o.status,a,t,i)}else{if(o.status>=400&&o.status<500){o.status===401&&this.manager.setToken(null);let p=await M(o);throw new D(p,"code"in p?p.code:p.error,o.status,a,t,i)}return null}}};w=new WeakMap,c=new WeakMap,b=new WeakMap,k=new WeakMap;var K=(r=>(r.Delete="delete",r.Get="get",r.Patch="patch",r.Post="post",r.Put="put",r))(K||{}),I,G=class extends ge{constructor(e){super();l(this,"globalRemaining");l(this,"globalDelay",null);l(this,"globalReset",-1);l(this,"hashes",new U);l(this,"handlers",new U);L(this,I,null);l(this,"hashTimer");l(this,"handlerTimer");l(this,"agent",null);l(this,"options");this.options={...x,...e},this.options.offset=Math.max(0,this.options.offset),this.globalRemaining=this.options.globalRequestsPerSecond,this.setupSweepers()}setupSweepers(){let e=t=>{if(t>144e5)throw new Error("Cannot set an interval greater than 4 hours")};this.options.hashSweepInterval!==0&&this.options.hashSweepInterval!==1/0&&(e(this.options.hashSweepInterval),this.hashTimer=setInterval(()=>{let t=new U,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 U;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 g(this,I,e),this}async queueRequest(e){let t=G.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})}createHandler(e,t){let s=new C(this,e,t);return this.handlers.set(s.id,s),s}resolveRequest(e){let{options:t}=this;this.agent??=t.api.startsWith("https")?new fe({...t.agent,keepAlive:!0}):new Re({...t.agent,keepAlive:!0});let s="";if(e.query){let o=e.query.toString();o!==""&&(s=`?${o}`)}let i={...this.options.headers,"User-Agent":`${Y} ${t.userAgentAppendix}`.trim()};if(e.auth!==!1){if(!u(this,I))throw new Error("Expected token to be set for this request, but none was present");i.Authorization=`${e.authPrefix??"Bot"} ${u(this,I)}`}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 he;for(let[d,y]of e.files.entries())o.append(y.key??`files[${d}]`,y.fileData,y.fileName);if(e.body!=null)if(e.appendToFormData)for(let[d,y]of Object.entries(e.body))o.append(d,y);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 E={agent:this.agent,body:a,headers:{...e.headers??{},...m,...i},method:e.method};return{url:r,fetchOptions:E}}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],E=de.deconstruct(m);Date.now()-Number(E.timestamp)>1e3*60*60*24*14&&(a+="/Delete Old Message")}return{majorParameter:i,bucketRoute:r+a,original:e}}},F=G;I=new WeakMap;import{EventEmitter as be}from"node:events";var ye=class extends be{constructor(e={}){super();l(this,"cdn");l(this,"requestManager");this.cdn=new N(e.cdn??x.cdn),this.requestManager=new F(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)}};export{ee as ALLOWED_EXTENSIONS,_ as ALLOWED_SIZES,te as ALLOWED_STICKER_EXTENSIONS,N as CDN,x as DefaultRestOptions,Y as DefaultUserAgent,D as DiscordAPIError,B as HTTPError,ye as REST,j as RESTEvents,A as RateLimitError,F as RequestManager,K as RequestMethod};
//# sourceMappingURL=index.mjs.map