@discordjs/rest 1.7.0-dev.1679918672-b8b852e.0 → 1.7.0

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
@@ -8,7 +8,8 @@ import { URL } from "node:url";
8
8
  import process from "node:process";
9
9
  import { APIVersion } from "discord-api-types/v10";
10
10
  import { Agent } from "undici";
11
- var DefaultUserAgent = `DiscordBot (https://discord.js.org, 1.7.0-dev.1679918672-b8b852e.0)`;
11
+ var DefaultUserAgent = `DiscordBot (https://discord.js.org, 1.7.0)`;
12
+ var DefaultUserAgentAppendix = process.release?.name === "node" ? `Node.js/${process.version}` : "";
12
13
  var DefaultRestOptions = {
13
14
  get agent() {
14
15
  return new Agent({
@@ -27,7 +28,7 @@ var DefaultRestOptions = {
27
28
  rejectOnRateLimit: null,
28
29
  retries: 3,
29
30
  timeout: 15e3,
30
- userAgentAppendix: `Node.js ${process.version}`,
31
+ userAgentAppendix: DefaultUserAgentAppendix,
31
32
  version: APIVersion,
32
33
  hashSweepInterval: 144e5,
33
34
  // 4 Hours
@@ -52,6 +53,7 @@ var OverwrittenMimeTypes = {
52
53
  // https://github.com/discordjs/discord.js/issues/8557
53
54
  "image/apng": "image/png"
54
55
  };
56
+ var BurstHandlerMajorIdKey = "burst";
55
57
 
56
58
  // src/lib/CDN.ts
57
59
  var CDN = class {
@@ -387,11 +389,8 @@ import { lazy } from "@discordjs/util";
387
389
  import { DiscordSnowflake } from "@sapphire/snowflake";
388
390
  import { FormData as FormData2 } from "undici";
389
391
 
390
- // src/lib/handlers/SequentialHandler.ts
391
- import { setTimeout, clearTimeout } from "node:timers";
392
+ // src/lib/handlers/BurstHandler.ts
392
393
  import { setTimeout as sleep } from "node:timers/promises";
393
- import { AsyncQueue } from "@sapphire/async-queue";
394
- import { request } from "undici";
395
394
 
396
395
  // src/lib/utils/utils.ts
397
396
  import { Blob, Buffer as Buffer2 } from "node:buffer";
@@ -502,10 +501,199 @@ function shouldRetry(error) {
502
501
  return "code" in error && error.code === "ECONNRESET" || error.message.includes("ECONNRESET");
503
502
  }
504
503
  __name(shouldRetry, "shouldRetry");
504
+ async function onRateLimit(manager, rateLimitData) {
505
+ const { options } = manager;
506
+ if (!options.rejectOnRateLimit)
507
+ return;
508
+ const shouldThrow = typeof options.rejectOnRateLimit === "function" ? await options.rejectOnRateLimit(rateLimitData) : options.rejectOnRateLimit.some((route) => rateLimitData.route.startsWith(route.toLowerCase()));
509
+ if (shouldThrow) {
510
+ throw new RateLimitError(rateLimitData);
511
+ }
512
+ }
513
+ __name(onRateLimit, "onRateLimit");
505
514
 
506
- // src/lib/handlers/SequentialHandler.ts
515
+ // src/lib/handlers/Shared.ts
516
+ import { setTimeout, clearTimeout } from "node:timers";
517
+ import { request } from "undici";
507
518
  var invalidCount = 0;
508
519
  var invalidCountResetTime = null;
520
+ function incrementInvalidCount(manager) {
521
+ if (!invalidCountResetTime || invalidCountResetTime < Date.now()) {
522
+ invalidCountResetTime = Date.now() + 1e3 * 60 * 10;
523
+ invalidCount = 0;
524
+ }
525
+ invalidCount++;
526
+ const emitInvalid = manager.options.invalidRequestWarningInterval > 0 && invalidCount % manager.options.invalidRequestWarningInterval === 0;
527
+ if (emitInvalid) {
528
+ manager.emit("invalidRequestWarning" /* InvalidRequestWarning */, {
529
+ count: invalidCount,
530
+ remainingTime: invalidCountResetTime - Date.now()
531
+ });
532
+ }
533
+ }
534
+ __name(incrementInvalidCount, "incrementInvalidCount");
535
+ async function makeNetworkRequest(manager, routeId, url, options, requestData, retries) {
536
+ const controller = new AbortController();
537
+ const timeout = setTimeout(() => controller.abort(), manager.options.timeout).unref();
538
+ if (requestData.signal) {
539
+ const signal = requestData.signal;
540
+ if (signal.aborted)
541
+ controller.abort();
542
+ else
543
+ signal.addEventListener("abort", () => controller.abort());
544
+ }
545
+ let res;
546
+ try {
547
+ res = await request(url, { ...options, signal: controller.signal });
548
+ } catch (error) {
549
+ if (!(error instanceof Error))
550
+ throw error;
551
+ if (shouldRetry(error) && retries !== manager.options.retries) {
552
+ return null;
553
+ }
554
+ throw error;
555
+ } finally {
556
+ clearTimeout(timeout);
557
+ }
558
+ if (manager.listenerCount("response" /* Response */)) {
559
+ manager.emit(
560
+ "response" /* Response */,
561
+ {
562
+ method: options.method ?? "get",
563
+ path: routeId.original,
564
+ route: routeId.bucketRoute,
565
+ options,
566
+ data: requestData,
567
+ retries
568
+ },
569
+ { ...res }
570
+ );
571
+ }
572
+ return res;
573
+ }
574
+ __name(makeNetworkRequest, "makeNetworkRequest");
575
+ async function handleErrors(manager, res, method, url, requestData, retries) {
576
+ const status = res.statusCode;
577
+ if (status >= 500 && status < 600) {
578
+ if (retries !== manager.options.retries) {
579
+ return null;
580
+ }
581
+ throw new HTTPError(status, method, url, requestData);
582
+ } else {
583
+ if (status >= 400 && status < 500) {
584
+ if (status === 401 && requestData.auth) {
585
+ manager.setToken(null);
586
+ }
587
+ const data = await parseResponse(res);
588
+ throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData);
589
+ }
590
+ return res;
591
+ }
592
+ }
593
+ __name(handleErrors, "handleErrors");
594
+
595
+ // src/lib/handlers/BurstHandler.ts
596
+ var BurstHandler = class {
597
+ /**
598
+ * @param manager - The request manager
599
+ * @param hash - The hash that this RequestHandler handles
600
+ * @param majorParameter - The major parameter for this handler
601
+ */
602
+ constructor(manager, hash, majorParameter) {
603
+ this.manager = manager;
604
+ this.hash = hash;
605
+ this.majorParameter = majorParameter;
606
+ this.id = `${hash}:${majorParameter}`;
607
+ }
608
+ /**
609
+ * {@inheritdoc IHandler.id}
610
+ */
611
+ id;
612
+ /**
613
+ * {@inheritDoc IHandler.inactive}
614
+ */
615
+ inactive = false;
616
+ /**
617
+ * Emits a debug message
618
+ *
619
+ * @param message - The message to debug
620
+ */
621
+ debug(message) {
622
+ this.manager.emit("restDebug" /* Debug */, `[REST ${this.id}] ${message}`);
623
+ }
624
+ /**
625
+ * {@inheritDoc IHandler.queueRequest}
626
+ */
627
+ async queueRequest(routeId, url, options, requestData) {
628
+ return this.runRequest(routeId, url, options, requestData);
629
+ }
630
+ /**
631
+ * The method that actually makes the request to the API, and updates info about the bucket accordingly
632
+ *
633
+ * @param routeId - The generalized API route with literal ids for major parameters
634
+ * @param url - The fully resolved URL to make the request to
635
+ * @param options - The fetch options needed to make the request
636
+ * @param requestData - Extra data from the user's request needed for errors and additional processing
637
+ * @param retries - The number of retries this request has already attempted (recursion)
638
+ */
639
+ async runRequest(routeId, url, options, requestData, retries = 0) {
640
+ const method = options.method ?? "get";
641
+ const res = await makeNetworkRequest(this.manager, routeId, url, options, requestData, retries);
642
+ if (res === null) {
643
+ return this.runRequest(routeId, url, options, requestData, ++retries);
644
+ }
645
+ const status = res.statusCode;
646
+ let retryAfter = 0;
647
+ const retry = parseHeader(res.headers["retry-after"]);
648
+ if (retry)
649
+ retryAfter = Number(retry) * 1e3 + this.manager.options.offset;
650
+ if (status === 401 || status === 403 || status === 429) {
651
+ incrementInvalidCount(this.manager);
652
+ }
653
+ if (status >= 200 && status < 300) {
654
+ return res;
655
+ } else if (status === 429) {
656
+ const isGlobal = res.headers["x-ratelimit-global"] !== void 0;
657
+ await onRateLimit(this.manager, {
658
+ timeToReset: retryAfter,
659
+ limit: Number.POSITIVE_INFINITY,
660
+ method,
661
+ hash: this.hash,
662
+ url,
663
+ route: routeId.bucketRoute,
664
+ majorParameter: this.majorParameter,
665
+ global: isGlobal
666
+ });
667
+ this.debug(
668
+ [
669
+ "Encountered unexpected 429 rate limit",
670
+ ` Global : ${isGlobal}`,
671
+ ` Method : ${method}`,
672
+ ` URL : ${url}`,
673
+ ` Bucket : ${routeId.bucketRoute}`,
674
+ ` Major parameter: ${routeId.majorParameter}`,
675
+ ` Hash : ${this.hash}`,
676
+ ` Limit : ${Number.POSITIVE_INFINITY}`,
677
+ ` Retry After : ${retryAfter}ms`,
678
+ ` Sublimit : None`
679
+ ].join("\n")
680
+ );
681
+ await sleep(retryAfter);
682
+ return this.runRequest(routeId, url, options, requestData, retries);
683
+ } else {
684
+ const handled = await handleErrors(this.manager, res, method, url, requestData, retries);
685
+ if (handled === null) {
686
+ return this.runRequest(routeId, url, options, requestData, ++retries);
687
+ }
688
+ return handled;
689
+ }
690
+ }
691
+ };
692
+ __name(BurstHandler, "BurstHandler");
693
+
694
+ // src/lib/handlers/SequentialHandler.ts
695
+ import { setTimeout as sleep2 } from "node:timers/promises";
696
+ import { AsyncQueue } from "@sapphire/async-queue";
509
697
  var SequentialHandler = class {
510
698
  /**
511
699
  * @param manager - The request manager
@@ -594,21 +782,9 @@ var SequentialHandler = class {
594
782
  * @param time - The amount of time to delay all requests for
595
783
  */
596
784
  async globalDelayFor(time) {
597
- await sleep(time);
785
+ await sleep2(time);
598
786
  this.manager.globalDelay = null;
599
787
  }
600
- /*
601
- * Determines whether the request should be queued or whether a RateLimitError should be thrown
602
- */
603
- async onRateLimit(rateLimitData) {
604
- const { options } = this.manager;
605
- if (!options.rejectOnRateLimit)
606
- return;
607
- const shouldThrow = typeof options.rejectOnRateLimit === "function" ? await options.rejectOnRateLimit(rateLimitData) : options.rejectOnRateLimit.some((route) => rateLimitData.route.startsWith(route.toLowerCase()));
608
- if (shouldThrow) {
609
- throw new RateLimitError(rateLimitData);
610
- }
611
- }
612
788
  /**
613
789
  * {@inheritDoc IHandler.queueRequest}
614
790
  */
@@ -657,22 +833,22 @@ var SequentialHandler = class {
657
833
  while (this.limited) {
658
834
  const isGlobal = this.globalLimited;
659
835
  let limit2;
660
- let timeout2;
836
+ let timeout;
661
837
  let delay;
662
838
  if (isGlobal) {
663
839
  limit2 = this.manager.options.globalRequestsPerSecond;
664
- timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
840
+ timeout = this.manager.globalReset + this.manager.options.offset - Date.now();
665
841
  if (!this.manager.globalDelay) {
666
- this.manager.globalDelay = this.globalDelayFor(timeout2);
842
+ this.manager.globalDelay = this.globalDelayFor(timeout);
667
843
  }
668
844
  delay = this.manager.globalDelay;
669
845
  } else {
670
846
  limit2 = this.limit;
671
- timeout2 = this.timeToReset;
672
- delay = sleep(timeout2);
847
+ timeout = this.timeToReset;
848
+ delay = sleep2(timeout);
673
849
  }
674
850
  const rateLimitData = {
675
- timeToReset: timeout2,
851
+ timeToReset: timeout,
676
852
  limit: limit2,
677
853
  method: options.method ?? "get",
678
854
  hash: this.hash,
@@ -682,11 +858,11 @@ var SequentialHandler = class {
682
858
  global: isGlobal
683
859
  };
684
860
  this.manager.emit("rateLimited" /* RateLimited */, rateLimitData);
685
- await this.onRateLimit(rateLimitData);
861
+ await onRateLimit(this.manager, rateLimitData);
686
862
  if (isGlobal) {
687
- this.debug(`Global rate limit hit, blocking all requests for ${timeout2}ms`);
863
+ this.debug(`Global rate limit hit, blocking all requests for ${timeout}ms`);
688
864
  } else {
689
- this.debug(`Waiting ${timeout2}ms for rate limit to pass`);
865
+ this.debug(`Waiting ${timeout}ms for rate limit to pass`);
690
866
  }
691
867
  await delay;
692
868
  }
@@ -696,41 +872,9 @@ var SequentialHandler = class {
696
872
  }
697
873
  this.manager.globalRemaining--;
698
874
  const method = options.method ?? "get";
699
- const controller = new AbortController();
700
- const timeout = setTimeout(() => controller.abort(), this.manager.options.timeout).unref();
701
- if (requestData.signal) {
702
- const signal = requestData.signal;
703
- if (signal.aborted)
704
- controller.abort();
705
- else
706
- signal.addEventListener("abort", () => controller.abort());
707
- }
708
- let res;
709
- try {
710
- res = await request(url, { ...options, signal: controller.signal });
711
- } catch (error) {
712
- if (!(error instanceof Error))
713
- throw error;
714
- if (shouldRetry(error) && retries !== this.manager.options.retries) {
715
- return await this.runRequest(routeId, url, options, requestData, ++retries);
716
- }
717
- throw error;
718
- } finally {
719
- clearTimeout(timeout);
720
- }
721
- if (this.manager.listenerCount("response" /* Response */)) {
722
- this.manager.emit(
723
- "response" /* Response */,
724
- {
725
- method,
726
- path: routeId.original,
727
- route: routeId.bucketRoute,
728
- options,
729
- data: requestData,
730
- retries
731
- },
732
- { ...res }
733
- );
875
+ const res = await makeNetworkRequest(this.manager, routeId, url, options, requestData, retries);
876
+ if (res === null) {
877
+ return this.runRequest(routeId, url, options, requestData, ++retries);
734
878
  }
735
879
  const status = res.statusCode;
736
880
  let retryAfter = 0;
@@ -763,34 +907,23 @@ var SequentialHandler = class {
763
907
  }
764
908
  }
765
909
  if (status === 401 || status === 403 || status === 429) {
766
- if (!invalidCountResetTime || invalidCountResetTime < Date.now()) {
767
- invalidCountResetTime = Date.now() + 1e3 * 60 * 10;
768
- invalidCount = 0;
769
- }
770
- invalidCount++;
771
- const emitInvalid = this.manager.options.invalidRequestWarningInterval > 0 && invalidCount % this.manager.options.invalidRequestWarningInterval === 0;
772
- if (emitInvalid) {
773
- this.manager.emit("invalidRequestWarning" /* InvalidRequestWarning */, {
774
- count: invalidCount,
775
- remainingTime: invalidCountResetTime - Date.now()
776
- });
777
- }
910
+ incrementInvalidCount(this.manager);
778
911
  }
779
912
  if (status >= 200 && status < 300) {
780
913
  return res;
781
914
  } else if (status === 429) {
782
915
  const isGlobal = this.globalLimited;
783
916
  let limit2;
784
- let timeout2;
917
+ let timeout;
785
918
  if (isGlobal) {
786
919
  limit2 = this.manager.options.globalRequestsPerSecond;
787
- timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
920
+ timeout = this.manager.globalReset + this.manager.options.offset - Date.now();
788
921
  } else {
789
922
  limit2 = this.limit;
790
- timeout2 = this.timeToReset;
923
+ timeout = this.timeToReset;
791
924
  }
792
- await this.onRateLimit({
793
- timeToReset: timeout2,
925
+ await onRateLimit(this.manager, {
926
+ timeToReset: timeout,
794
927
  limit: limit2,
795
928
  method,
796
929
  hash: this.hash,
@@ -822,7 +955,7 @@ var SequentialHandler = class {
822
955
  }
823
956
  this.#sublimitPromise?.resolve();
824
957
  this.#sublimitPromise = null;
825
- await sleep(sublimitTimeout);
958
+ await sleep2(sublimitTimeout);
826
959
  let resolve;
827
960
  const promise = new Promise((res2) => resolve = res2);
828
961
  this.#sublimitPromise = { promise, resolve };
@@ -832,20 +965,12 @@ var SequentialHandler = class {
832
965
  }
833
966
  }
834
967
  return this.runRequest(routeId, url, options, requestData, retries);
835
- } else if (status >= 500 && status < 600) {
836
- if (retries !== this.manager.options.retries) {
837
- return this.runRequest(routeId, url, options, requestData, ++retries);
838
- }
839
- throw new HTTPError(status, method, url, requestData);
840
968
  } else {
841
- if (status >= 400 && status < 500) {
842
- if (status === 401 && requestData.auth) {
843
- this.manager.setToken(null);
844
- }
845
- const data = await parseResponse(res);
846
- throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData);
969
+ const handled = await handleErrors(this.manager, res, method, url, requestData, retries);
970
+ if (handled === null) {
971
+ return this.runRequest(routeId, url, options, requestData, ++retries);
847
972
  }
848
- return res;
973
+ return handled;
849
974
  }
850
975
  }
851
976
  };
@@ -986,7 +1111,7 @@ var RequestManager = class extends EventEmitter {
986
1111
  * @internal
987
1112
  */
988
1113
  createHandler(hash, majorParameter) {
989
- const queue = new SequentialHandler(this, hash, majorParameter);
1114
+ const queue = majorParameter === BurstHandlerMajorIdKey ? new BurstHandler(this, hash, majorParameter) : new SequentialHandler(this, hash, majorParameter);
990
1115
  this.handlers.set(queue.id, queue);
991
1116
  return queue;
992
1117
  }
@@ -1087,6 +1212,13 @@ var RequestManager = class extends EventEmitter {
1087
1212
  * @internal
1088
1213
  */
1089
1214
  static generateRouteData(endpoint, method) {
1215
+ if (endpoint.startsWith("/interactions/") && endpoint.endsWith("/callback")) {
1216
+ return {
1217
+ majorParameter: BurstHandlerMajorIdKey,
1218
+ bucketRoute: "/interactions/:id/:token/callback",
1219
+ original: endpoint
1220
+ };
1221
+ }
1090
1222
  const majorIdMatch = /^\/(?:channels|guilds|webhooks)\/(\d{17,19})/.exec(endpoint);
1091
1223
  const majorId = majorIdMatch?.[1] ?? "global";
1092
1224
  const baseRoute = endpoint.replaceAll(/\d{17,19}/g, ":id").replace(/\/reactions\/(.*)/, "/reactions/:reaction");
@@ -1215,14 +1347,16 @@ var REST = class extends EventEmitter2 {
1215
1347
  __name(REST, "REST");
1216
1348
 
1217
1349
  // src/index.ts
1218
- var version = "1.7.0-dev.1679918672-b8b852e.0";
1350
+ var version = "1.7.0";
1219
1351
  export {
1220
1352
  ALLOWED_EXTENSIONS,
1221
1353
  ALLOWED_SIZES,
1222
1354
  ALLOWED_STICKER_EXTENSIONS,
1355
+ BurstHandlerMajorIdKey,
1223
1356
  CDN,
1224
1357
  DefaultRestOptions,
1225
1358
  DefaultUserAgent,
1359
+ DefaultUserAgentAppendix,
1226
1360
  DiscordAPIError,
1227
1361
  HTTPError,
1228
1362
  OverwrittenMimeTypes,