@discordjs/rest 0.5.0-dev.1651147752-679dcda → 0.5.0-dev.1652400670-aed687b

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.js CHANGED
@@ -105,11 +105,9 @@ var require_package = __commonJS({
105
105
  "@discordjs/collection": "workspace:^",
106
106
  "@sapphire/async-queue": "^1.3.1",
107
107
  "@sapphire/snowflake": "^3.2.1",
108
- "@types/node-fetch": "^2.6.1",
109
108
  "discord-api-types": "^0.29.0",
110
- "form-data": "^4.0.0",
111
- "node-fetch": "^2.6.7",
112
- tslib: "^2.3.1"
109
+ tslib: "^2.3.1",
110
+ undici: "^5.2.0"
113
111
  },
114
112
  devDependencies: {
115
113
  "@babel/core": "^7.17.9",
@@ -127,7 +125,6 @@ var require_package = __commonJS({
127
125
  "eslint-config-prettier": "^8.5.0",
128
126
  "eslint-plugin-import": "^2.26.0",
129
127
  jest: "^27.5.1",
130
- nock: "^13.2.4",
131
128
  prettier: "^2.6.2",
132
129
  tsup: "^5.12.5",
133
130
  typedoc: "^0.22.15",
@@ -165,10 +162,13 @@ module.exports = __toCommonJS(src_exports);
165
162
 
166
163
  // src/lib/utils/constants.ts
167
164
  var import_v10 = require("discord-api-types/v10");
165
+ var import_undici = require("undici");
168
166
  var Package = require_package();
169
167
  var DefaultUserAgent = `DiscordBot (${Package.homepage}, ${Package.version})`;
170
168
  var DefaultRestOptions = {
171
- agent: {},
169
+ get agent() {
170
+ return (0, import_undici.getGlobalDispatcher)();
171
+ },
172
172
  api: "https://discord.com/api",
173
173
  authPrefix: "Bot",
174
174
  cdn: "https://cdn.discordapp.com",
@@ -189,7 +189,6 @@ var RESTEvents = /* @__PURE__ */ ((RESTEvents2) => {
189
189
  RESTEvents2["Debug"] = "restDebug";
190
190
  RESTEvents2["InvalidRequestWarning"] = "invalidRequestWarning";
191
191
  RESTEvents2["RateLimited"] = "rateLimited";
192
- RESTEvents2["Request"] = "request";
193
192
  RESTEvents2["Response"] = "response";
194
193
  RESTEvents2["HashSweep"] = "hashSweep";
195
194
  RESTEvents2["HandlerSweep"] = "handlerSweep";
@@ -333,8 +332,8 @@ __name(DiscordAPIError, "DiscordAPIError");
333
332
 
334
333
  // src/lib/errors/HTTPError.ts
335
334
  var HTTPError = class extends Error {
336
- constructor(message, name, status, method, url, bodyData) {
337
- super(message);
335
+ constructor(name, status, method, url, bodyData) {
336
+ super();
338
337
  this.name = name;
339
338
  this.status = status;
340
339
  this.method = method;
@@ -373,19 +372,31 @@ var RateLimitError = class extends Error {
373
372
  __name(RateLimitError, "RateLimitError");
374
373
 
375
374
  // src/lib/RequestManager.ts
375
+ var import_node_buffer2 = require("buffer");
376
376
  var import_node_events = require("events");
377
- var import_node_http = require("http");
378
- var import_node_https = require("https");
379
377
  var import_collection = __toESM(require("@discordjs/collection"));
380
378
  var import_snowflake = require("@sapphire/snowflake");
381
- var import_form_data = __toESM(require("form-data"));
379
+ var import_undici4 = require("undici");
382
380
 
383
381
  // src/lib/handlers/SequentialHandler.ts
384
382
  var import_promises = require("timers/promises");
385
383
  var import_async_queue = require("@sapphire/async-queue");
386
- var import_node_fetch = __toESM(require("node-fetch"));
384
+ var import_undici3 = require("undici");
387
385
 
388
386
  // src/lib/utils/utils.ts
387
+ var import_node_buffer = require("buffer");
388
+ var import_node_url = require("url");
389
+ var import_node_util = require("util");
390
+ var import_undici2 = require("undici");
391
+ function parseHeader(header) {
392
+ if (header === void 0) {
393
+ return header;
394
+ } else if (typeof header === "string") {
395
+ return header;
396
+ }
397
+ return header.join(";");
398
+ }
399
+ __name(parseHeader, "parseHeader");
389
400
  function serializeSearchParam(value) {
390
401
  switch (typeof value) {
391
402
  case "string":
@@ -409,7 +420,7 @@ function serializeSearchParam(value) {
409
420
  }
410
421
  __name(serializeSearchParam, "serializeSearchParam");
411
422
  function makeURLSearchParams(options) {
412
- const params = new URLSearchParams();
423
+ const params = new import_node_url.URLSearchParams();
413
424
  if (!options)
414
425
  return params;
415
426
  for (const [key, value] of Object.entries(options)) {
@@ -421,17 +432,18 @@ function makeURLSearchParams(options) {
421
432
  }
422
433
  __name(makeURLSearchParams, "makeURLSearchParams");
423
434
  function parseResponse(res) {
424
- if (res.headers.get("Content-Type")?.startsWith("application/json")) {
425
- return res.json();
435
+ const header = parseHeader(res.headers["content-type"]);
436
+ if (header?.startsWith("application/json")) {
437
+ return res.body.json();
426
438
  }
427
- return res.arrayBuffer();
439
+ return res.body.arrayBuffer();
428
440
  }
429
441
  __name(parseResponse, "parseResponse");
430
442
  function hasSublimit(bucketRoute, body, method) {
431
443
  if (bucketRoute === "/channels/:id") {
432
444
  if (typeof body !== "object" || body === null)
433
445
  return false;
434
- if (method !== "patch" /* Patch */)
446
+ if (method !== "PATCH" /* Patch */)
435
447
  return false;
436
448
  const castedBody = body;
437
449
  return ["name", "topic"].some((key) => Reflect.has(castedBody, key));
@@ -439,6 +451,43 @@ function hasSublimit(bucketRoute, body, method) {
439
451
  return true;
440
452
  }
441
453
  __name(hasSublimit, "hasSublimit");
454
+ async function resolveBody(body) {
455
+ if (body == null) {
456
+ return null;
457
+ } else if (typeof body === "string") {
458
+ return body;
459
+ } else if (import_node_util.types.isUint8Array(body)) {
460
+ return body;
461
+ } else if (import_node_util.types.isArrayBuffer(body)) {
462
+ return new Uint8Array(body);
463
+ } else if (body instanceof import_node_url.URLSearchParams) {
464
+ return body.toString();
465
+ } else if (body instanceof DataView) {
466
+ return new Uint8Array(body.buffer);
467
+ } else if (body instanceof import_node_buffer.Blob) {
468
+ return new Uint8Array(await body.arrayBuffer());
469
+ } else if (body instanceof import_undici2.FormData) {
470
+ return body;
471
+ } else if (body[Symbol.iterator]) {
472
+ const chunks = [...body];
473
+ const length = chunks.reduce((a, b) => a + b.length, 0);
474
+ const uint8 = new Uint8Array(length);
475
+ let lengthUsed = 0;
476
+ return chunks.reduce((a, b) => {
477
+ a.set(b, lengthUsed);
478
+ lengthUsed += b.length;
479
+ return a;
480
+ }, uint8);
481
+ } else if (body[Symbol.asyncIterator]) {
482
+ const chunks = [];
483
+ for await (const chunk of body) {
484
+ chunks.push(chunk);
485
+ }
486
+ return Buffer.concat(chunks);
487
+ }
488
+ throw new TypeError(`Unable to resolve body.`);
489
+ }
490
+ __name(resolveBody, "resolveBody");
442
491
 
443
492
  // src/lib/handlers/SequentialHandler.ts
444
493
  var invalidCount = 0;
@@ -565,21 +614,11 @@ var SequentialHandler = class {
565
614
  }
566
615
  this.manager.globalRemaining--;
567
616
  const method = options.method ?? "get";
568
- if (this.manager.listenerCount("request" /* Request */)) {
569
- this.manager.emit("request" /* Request */, {
570
- method,
571
- path: routeId.original,
572
- route: routeId.bucketRoute,
573
- options,
574
- data: requestData,
575
- retries
576
- });
577
- }
578
617
  const controller = new AbortController();
579
618
  const timeout = setTimeout(() => controller.abort(), this.manager.options.timeout).unref();
580
619
  let res;
581
620
  try {
582
- res = await (0, import_node_fetch.default)(url, { ...options, signal: controller.signal });
621
+ res = await (0, import_undici3.request)(url, { ...options, signal: controller.signal });
583
622
  } catch (error) {
584
623
  if (error instanceof Error && error.name === "AbortError" && retries !== this.manager.options.retries) {
585
624
  return await this.runRequest(routeId, url, options, requestData, ++retries);
@@ -596,14 +635,15 @@ var SequentialHandler = class {
596
635
  options,
597
636
  data: requestData,
598
637
  retries
599
- }, res.clone());
638
+ }, { ...res });
600
639
  }
640
+ const status = res.statusCode;
601
641
  let retryAfter = 0;
602
- const limit = res.headers.get("X-RateLimit-Limit");
603
- const remaining = res.headers.get("X-RateLimit-Remaining");
604
- const reset = res.headers.get("X-RateLimit-Reset-After");
605
- const hash = res.headers.get("X-RateLimit-Bucket");
606
- const retry = res.headers.get("Retry-After");
642
+ const limit = parseHeader(res.headers["x-ratelimit-limit"]);
643
+ const remaining = parseHeader(res.headers["x-ratelimit-remaining"]);
644
+ const reset = parseHeader(res.headers["x-ratelimit-reset-after"]);
645
+ const hash = parseHeader(res.headers["x-ratelimit-bucket"]);
646
+ const retry = parseHeader(res.headers["retry-after"]);
607
647
  this.limit = limit ? Number(limit) : Infinity;
608
648
  this.remaining = remaining ? Number(remaining) : 1;
609
649
  this.reset = reset ? Number(reset) * 1e3 + Date.now() + this.manager.options.offset : Date.now();
@@ -620,14 +660,14 @@ var SequentialHandler = class {
620
660
  }
621
661
  let sublimitTimeout = null;
622
662
  if (retryAfter > 0) {
623
- if (res.headers.get("X-RateLimit-Global")) {
663
+ if (res.headers["x-ratelimit-global"] !== void 0) {
624
664
  this.manager.globalRemaining = 0;
625
665
  this.manager.globalReset = Date.now() + retryAfter;
626
666
  } else if (!this.localLimited) {
627
667
  sublimitTimeout = retryAfter;
628
668
  }
629
669
  }
630
- if (res.status === 401 || res.status === 403 || res.status === 429) {
670
+ if (status === 401 || status === 403 || status === 429) {
631
671
  if (!invalidCountResetTime || invalidCountResetTime < Date.now()) {
632
672
  invalidCountResetTime = Date.now() + 1e3 * 60 * 10;
633
673
  invalidCount = 0;
@@ -641,9 +681,9 @@ var SequentialHandler = class {
641
681
  });
642
682
  }
643
683
  }
644
- if (res.ok) {
684
+ if (status === 200) {
645
685
  return parseResponse(res);
646
- } else if (res.status === 429) {
686
+ } else if (status === 429) {
647
687
  const isGlobal = this.globalLimited;
648
688
  let limit2;
649
689
  let timeout2;
@@ -695,18 +735,18 @@ var SequentialHandler = class {
695
735
  }
696
736
  }
697
737
  return this.runRequest(routeId, url, options, requestData, retries);
698
- } else if (res.status >= 500 && res.status < 600) {
738
+ } else if (status >= 500 && status < 600) {
699
739
  if (retries !== this.manager.options.retries) {
700
740
  return this.runRequest(routeId, url, options, requestData, ++retries);
701
741
  }
702
- throw new HTTPError(res.statusText, res.constructor.name, res.status, method, url, requestData);
742
+ throw new HTTPError(res.constructor.name, status, method, url, requestData);
703
743
  } else {
704
- if (res.status >= 400 && res.status < 500) {
705
- if (res.status === 401 && requestData.auth) {
744
+ if (status >= 400 && status < 500) {
745
+ if (status === 401 && requestData.auth) {
706
746
  this.manager.setToken(null);
707
747
  }
708
748
  const data = await parseResponse(res);
709
- throw new DiscordAPIError(data, "code" in data ? data.code : data.error, res.status, method, url, requestData);
749
+ throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData);
710
750
  }
711
751
  return null;
712
752
  }
@@ -720,17 +760,18 @@ _shiftSublimit = new WeakMap();
720
760
 
721
761
  // src/lib/RequestManager.ts
722
762
  var RequestMethod = /* @__PURE__ */ ((RequestMethod2) => {
723
- RequestMethod2["Delete"] = "delete";
724
- RequestMethod2["Get"] = "get";
725
- RequestMethod2["Patch"] = "patch";
726
- RequestMethod2["Post"] = "post";
727
- RequestMethod2["Put"] = "put";
763
+ RequestMethod2["Delete"] = "DELETE";
764
+ RequestMethod2["Get"] = "GET";
765
+ RequestMethod2["Patch"] = "PATCH";
766
+ RequestMethod2["Post"] = "POST";
767
+ RequestMethod2["Put"] = "PUT";
728
768
  return RequestMethod2;
729
769
  })(RequestMethod || {});
730
770
  var _token;
731
771
  var _RequestManager = class extends import_node_events.EventEmitter {
732
772
  constructor(options) {
733
773
  super();
774
+ __publicField(this, "agent", null);
734
775
  __publicField(this, "globalRemaining");
735
776
  __publicField(this, "globalDelay", null);
736
777
  __publicField(this, "globalReset", -1);
@@ -739,11 +780,11 @@ var _RequestManager = class extends import_node_events.EventEmitter {
739
780
  __privateAdd(this, _token, null);
740
781
  __publicField(this, "hashTimer");
741
782
  __publicField(this, "handlerTimer");
742
- __publicField(this, "agent", null);
743
783
  __publicField(this, "options");
744
784
  this.options = { ...DefaultRestOptions, ...options };
745
785
  this.options.offset = Math.max(0, this.options.offset);
746
786
  this.globalRemaining = this.options.globalRequestsPerSecond;
787
+ this.agent = options.agent ?? null;
747
788
  this.setupSweepers();
748
789
  }
749
790
  setupSweepers() {
@@ -786,22 +827,26 @@ var _RequestManager = class extends import_node_events.EventEmitter {
786
827
  }, this.options.handlerSweepInterval).unref();
787
828
  }
788
829
  }
830
+ setAgent(agent) {
831
+ this.agent = agent;
832
+ return this;
833
+ }
789
834
  setToken(token) {
790
835
  __privateSet(this, _token, token);
791
836
  return this;
792
837
  }
793
- async queueRequest(request) {
794
- const routeId = _RequestManager.generateRouteData(request.fullRoute, request.method);
795
- const hash = this.hashes.get(`${request.method}:${routeId.bucketRoute}`) ?? {
796
- value: `Global(${request.method}:${routeId.bucketRoute})`,
838
+ async queueRequest(request2) {
839
+ const routeId = _RequestManager.generateRouteData(request2.fullRoute, request2.method);
840
+ const hash = this.hashes.get(`${request2.method}:${routeId.bucketRoute}`) ?? {
841
+ value: `Global(${request2.method}:${routeId.bucketRoute})`,
797
842
  lastAccess: -1
798
843
  };
799
844
  const handler = this.handlers.get(`${hash.value}:${routeId.majorParameter}`) ?? this.createHandler(hash.value, routeId.majorParameter);
800
- const { url, fetchOptions } = this.resolveRequest(request);
845
+ const { url, fetchOptions } = await this.resolveRequest(request2);
801
846
  return handler.queueRequest(routeId, url, fetchOptions, {
802
- body: request.body,
803
- files: request.files,
804
- auth: request.auth !== false
847
+ body: request2.body,
848
+ files: request2.files,
849
+ auth: request2.auth !== false
805
850
  });
806
851
  }
807
852
  createHandler(hash, majorParameter) {
@@ -809,12 +854,11 @@ var _RequestManager = class extends import_node_events.EventEmitter {
809
854
  this.handlers.set(queue.id, queue);
810
855
  return queue;
811
856
  }
812
- resolveRequest(request) {
857
+ async resolveRequest(request2) {
813
858
  const { options } = this;
814
- this.agent ??= options.api.startsWith("https") ? new import_node_https.Agent({ ...options.agent, keepAlive: true }) : new import_node_http.Agent({ ...options.agent, keepAlive: true });
815
859
  let query = "";
816
- if (request.query) {
817
- const resolvedQuery = request.query.toString();
860
+ if (request2.query) {
861
+ const resolvedQuery = request2.query.toString();
818
862
  if (resolvedQuery !== "") {
819
863
  query = `?${resolvedQuery}`;
820
864
  }
@@ -823,48 +867,55 @@ var _RequestManager = class extends import_node_events.EventEmitter {
823
867
  ...this.options.headers,
824
868
  "User-Agent": `${DefaultUserAgent} ${options.userAgentAppendix}`.trim()
825
869
  };
826
- if (request.auth !== false) {
870
+ if (request2.auth !== false) {
827
871
  if (!__privateGet(this, _token)) {
828
872
  throw new Error("Expected token to be set for this request, but none was present");
829
873
  }
830
- headers.Authorization = `${request.authPrefix ?? this.options.authPrefix} ${__privateGet(this, _token)}`;
874
+ headers.Authorization = `${request2.authPrefix ?? this.options.authPrefix} ${__privateGet(this, _token)}`;
831
875
  }
832
- if (request.reason?.length) {
833
- headers["X-Audit-Log-Reason"] = encodeURIComponent(request.reason);
876
+ if (request2.reason?.length) {
877
+ headers["X-Audit-Log-Reason"] = encodeURIComponent(request2.reason);
834
878
  }
835
- const url = `${options.api}${request.versioned === false ? "" : `/v${options.version}`}${request.fullRoute}${query}`;
879
+ const url = `${options.api}${request2.versioned === false ? "" : `/v${options.version}`}${request2.fullRoute}${query}`;
836
880
  let finalBody;
837
881
  let additionalHeaders = {};
838
- if (request.files?.length) {
839
- const formData = new import_form_data.default();
840
- for (const [index, file] of request.files.entries()) {
841
- formData.append(file.key ?? `files[${index}]`, file.data, file.name);
882
+ if (request2.files?.length) {
883
+ const formData = new import_undici4.FormData();
884
+ for (const [index, file] of request2.files.entries()) {
885
+ const fileKey = file.key ?? `files[${index}]`;
886
+ if (Buffer.isBuffer(file.data) || typeof file.data === "string") {
887
+ formData.append(fileKey, new import_node_buffer2.Blob([file.data]), file.name);
888
+ } else {
889
+ formData.append(fileKey, new import_node_buffer2.Blob([`${file.data}`]), file.name);
890
+ }
842
891
  }
843
- if (request.body != null) {
844
- if (request.appendToFormData) {
845
- for (const [key, value] of Object.entries(request.body)) {
892
+ if (request2.body != null) {
893
+ if (request2.appendToFormData) {
894
+ for (const [key, value] of Object.entries(request2.body)) {
846
895
  formData.append(key, value);
847
896
  }
848
897
  } else {
849
- formData.append("payload_json", JSON.stringify(request.body));
898
+ formData.append("payload_json", JSON.stringify(request2.body));
850
899
  }
851
900
  }
852
901
  finalBody = formData;
853
- additionalHeaders = formData.getHeaders();
854
- } else if (request.body != null) {
855
- if (request.passThroughBody) {
856
- finalBody = request.body;
902
+ } else if (request2.body != null) {
903
+ if (request2.passThroughBody) {
904
+ finalBody = request2.body;
857
905
  } else {
858
- finalBody = JSON.stringify(request.body);
906
+ finalBody = JSON.stringify(request2.body);
859
907
  additionalHeaders = { "Content-Type": "application/json" };
860
908
  }
861
909
  }
910
+ finalBody = await resolveBody(finalBody);
862
911
  const fetchOptions = {
863
- agent: this.agent,
864
- body: finalBody,
865
- headers: { ...request.headers ?? {}, ...additionalHeaders, ...headers },
866
- method: request.method
912
+ headers: { ...request2.headers ?? {}, ...additionalHeaders, ...headers },
913
+ method: request2.method.toUpperCase()
867
914
  };
915
+ if (finalBody !== void 0) {
916
+ fetchOptions.body = finalBody;
917
+ }
918
+ fetchOptions.dispatcher = request2.dispatcher ?? this.agent ?? void 0;
868
919
  return { url, fetchOptions };
869
920
  }
870
921
  clearHashSweeper() {
@@ -878,7 +929,7 @@ var _RequestManager = class extends import_node_events.EventEmitter {
878
929
  const majorId = majorIdMatch?.[1] ?? "global";
879
930
  const baseRoute = endpoint.replace(/\d{16,19}/g, ":id").replace(/\/reactions\/(.*)/, "/reactions/:reaction");
880
931
  let exceptions = "";
881
- if (method === "delete" /* Delete */ && baseRoute === "/channels/:id/messages/:id") {
932
+ if (method === "DELETE" /* Delete */ && baseRoute === "/channels/:id/messages/:id") {
882
933
  const id = /\d{16,19}$/.exec(endpoint)[0];
883
934
  const timestamp = import_snowflake.DiscordSnowflake.timestampFrom(id);
884
935
  if (Date.now() - timestamp > 1e3 * 60 * 60 * 24 * 14) {
@@ -906,32 +957,39 @@ var REST = class extends import_node_events2.EventEmitter {
906
957
  this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn);
907
958
  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 */));
908
959
  this.on("newListener", (name, listener) => {
909
- if (name === "request" /* Request */ || name === "response" /* Response */)
960
+ if (name === "response" /* Response */)
910
961
  this.requestManager.on(name, listener);
911
962
  });
912
963
  this.on("removeListener", (name, listener) => {
913
- if (name === "request" /* Request */ || name === "response" /* Response */)
964
+ if (name === "response" /* Response */)
914
965
  this.requestManager.off(name, listener);
915
966
  });
916
967
  }
968
+ getAgent() {
969
+ return this.requestManager.agent;
970
+ }
971
+ setAgent(agent) {
972
+ this.requestManager.setAgent(agent);
973
+ return this;
974
+ }
917
975
  setToken(token) {
918
976
  this.requestManager.setToken(token);
919
977
  return this;
920
978
  }
921
979
  get(fullRoute, options = {}) {
922
- return this.request({ ...options, fullRoute, method: "get" /* Get */ });
980
+ return this.request({ ...options, fullRoute, method: "GET" /* Get */ });
923
981
  }
924
982
  delete(fullRoute, options = {}) {
925
- return this.request({ ...options, fullRoute, method: "delete" /* Delete */ });
983
+ return this.request({ ...options, fullRoute, method: "DELETE" /* Delete */ });
926
984
  }
927
985
  post(fullRoute, options = {}) {
928
- return this.request({ ...options, fullRoute, method: "post" /* Post */ });
986
+ return this.request({ ...options, fullRoute, method: "POST" /* Post */ });
929
987
  }
930
988
  put(fullRoute, options = {}) {
931
- return this.request({ ...options, fullRoute, method: "put" /* Put */ });
989
+ return this.request({ ...options, fullRoute, method: "PUT" /* Put */ });
932
990
  }
933
991
  patch(fullRoute, options = {}) {
934
- return this.request({ ...options, fullRoute, method: "patch" /* Patch */ });
992
+ return this.request({ ...options, fullRoute, method: "PATCH" /* Patch */ });
935
993
  }
936
994
  request(options) {
937
995
  return this.requestManager.queueRequest(options);