@discordeno/rest 19.0.0-next.f8afb94 → 19.0.0-next.f98bb9b

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.
@@ -21,7 +21,7 @@ import { delay, logger } from '@discordeno/utils';
21
21
  processing: false,
22
22
  waiting: [],
23
23
  requestsAllowed: function() {
24
- if (bucket.resetAt !== undefined && Date.now() > bucket.resetAt) {
24
+ if (bucket.resetAt !== undefined && Date.now() >= bucket.resetAt) {
25
25
  bucket.invalidRequests = 0;
26
26
  bucket.resetAt = Date.now() + bucket.interval;
27
27
  }
@@ -45,14 +45,13 @@ import { delay, logger } from '@discordeno/utils';
45
45
  },
46
46
  processWaiting: async function() {
47
47
  // If already processing, that loop will handle all waiting requests.
48
- if (bucket.processing) {
49
- return;
50
- }
48
+ if (bucket.processing) return;
51
49
  // Mark as processing so other loops don't start
52
50
  bucket.processing = true;
53
51
  while(bucket.waiting.length > 0){
54
- logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length} remaining.`);
55
- if (bucket.resetAt !== undefined && !bucket.isRequestAllowed()) {
52
+ logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length} pending requests to be made. ${JSON.stringify(bucket)}`);
53
+ if (!bucket.isRequestAllowed() && bucket.resetAt !== undefined) {
54
+ logger.warn(`[InvalidBucket] processing waiting queue is now paused until more requests are available. ${bucket.waiting.length} pending requests. ${JSON.stringify(bucket)}`);
56
55
  await delay(bucket.resetAt - Date.now());
57
56
  }
58
57
  bucket.activeRequests += 1;
@@ -74,6 +73,7 @@ import { delay, logger } from '@discordeno/utils';
74
73
  bucket.resetAt = Date.now() + bucket.interval;
75
74
  }
76
75
  bucket.invalidRequests += 1;
76
+ logger.warn(`[InvalidBucket] an invalid request was made. Increasing invalidRequests count to ${bucket.invalidRequests}`);
77
77
  }
78
78
  };
79
79
  return bucket;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/invalidBucket.ts"],"sourcesContent":["import { delay, logger } from '@discordeno/utils'\n\n/**\n * A invalid request bucket is used in a similar manner as a leaky bucket but a invalid request bucket can be refilled as needed.\n * It's purpose is to make sure the bot does not hit the limit to getting a 1 hr ban.\n *\n * @param options The options used to configure this bucket.\n * @returns RefillingBucket\n */\nexport function createInvalidRequestBucket(options: InvalidRequestBucketOptions): InvalidRequestBucket {\n const bucket: InvalidRequestBucket = {\n invalidRequests: options.current ?? 0,\n max: options.max ?? 10000,\n interval: options.interval ?? 600_000, // 10 minutes\n resetAt: options.resetAt,\n safety: options.safety ?? 1,\n errorStatuses: options.errorStatuses ?? [401, 403, 429],\n activeRequests: options.requested ?? 0,\n processing: false,\n\n waiting: [],\n\n requestsAllowed: function () {\n if (bucket.resetAt !== undefined && Date.now() > bucket.resetAt) {\n bucket.invalidRequests = 0\n bucket.resetAt = Date.now() + bucket.interval\n }\n\n return bucket.max - bucket.invalidRequests - bucket.activeRequests - bucket.safety\n },\n\n isRequestAllowed: function () {\n return bucket.requestsAllowed() > 0\n },\n\n waitUntilRequestAvailable: async function () {\n // eslint-disable-next-line no-async-promise-executor\n return await new Promise(async (resolve) => {\n // If whatever amount of requests is left is more than the safety margin, allow the request\n if (bucket.isRequestAllowed()) {\n bucket.activeRequests += 1\n resolve()\n } else {\n bucket.waiting.push(resolve)\n await bucket.processWaiting()\n }\n })\n },\n\n processWaiting: async function () {\n // If already processing, that loop will handle all waiting requests.\n if (bucket.processing) {\n return\n }\n\n // Mark as processing so other loops don't start\n bucket.processing = true\n\n while (bucket.waiting.length > 0) {\n logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length} remaining.`)\n\n if (bucket.resetAt !== undefined && !bucket.isRequestAllowed()) {\n await delay(bucket.resetAt - Date.now())\n }\n\n bucket.activeRequests += 1\n // Resolve the next item in the queue\n bucket.waiting.shift()?.()\n }\n\n // Mark as false so next pending request can be triggered by new loop.\n bucket.processing = false\n },\n\n handleCompletedRequest: function (code, sharedScope) {\n // Since request is complete, we can remove one from requested.\n bucket.activeRequests -= 1\n // Since it is as a valid request, we don't need to do anything\n if (!bucket.errorStatuses.includes(code)) return\n // Shared scope is not considered invalid\n if (code === 429 && sharedScope) return\n\n // INVALID REQUEST WAS MADE\n if (bucket.resetAt === undefined) {\n bucket.resetAt = Date.now() + bucket.interval\n }\n\n bucket.invalidRequests += 1\n },\n }\n\n return bucket\n}\n\nexport interface InvalidRequestBucketOptions {\n /** current invalid amount */\n current?: number\n /** max invalid requests allowed until ban. Defaults to 10,000 */\n max?: number\n /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */\n interval?: number\n /** When the timeout for the bucket has started at. */\n resetAt?: number\n /** how safe to be from max. Defaults to 1 */\n safety?: number\n /** The request statuses that count as an invalid request. */\n errorStatuses?: number[]\n /** The amount of requests that were requested from this bucket. */\n requested?: number\n}\n\nexport interface InvalidRequestBucket {\n /** current invalid amount */\n invalidRequests: number\n /** max invalid requests allowed until ban. Defaults to 10,000 */\n max: number\n /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */\n interval: number\n /** When the timeout for this bucket has started at. */\n resetAt: number | undefined\n /** how safe to be from max. Defaults to 1 */\n safety: number\n /** The request statuses that count as an invalid request. */\n errorStatuses: number[]\n /** The amount of requests that were requested from this bucket. */\n activeRequests: number\n /** The requests that are currently pending. */\n waiting: Array<(value: void | PromiseLike<void>) => void>\n /** Whether or not the waiting queue is already processing. */\n processing: boolean\n\n /** Gives the number of requests that are currently allowed. */\n requestsAllowed: () => number\n /** Checks if a request is allowed at this time. */\n isRequestAllowed: () => boolean\n /** Waits until a request is available */\n waitUntilRequestAvailable: () => Promise<void>\n /** Begins processing the waiting queue of requests. */\n processWaiting: () => Promise<void>\n /** Handler for whenever a request is validated. This should update the requested values or trigger any other necessary stuff. */\n handleCompletedRequest: (code: number, sharedScope: boolean) => void\n}\n"],"names":["delay","logger","createInvalidRequestBucket","options","bucket","invalidRequests","current","max","interval","resetAt","safety","errorStatuses","activeRequests","requested","processing","waiting","requestsAllowed","undefined","Date","now","isRequestAllowed","waitUntilRequestAvailable","Promise","resolve","push","processWaiting","length","info","shift","handleCompletedRequest","code","sharedScope","includes"],"mappings":"AAAA,SAASA,KAAK,EAAEC,MAAM,QAAQ,oBAAmB;AAEjD;;;;;;CAMC,GACD,OAAO,SAASC,2BAA2BC,OAAoC,EAAwB;IACrG,MAAMC,SAA+B;QACnCC,iBAAiBF,QAAQG,OAAO,IAAI;QACpCC,KAAKJ,QAAQI,GAAG,IAAI;QACpBC,UAAUL,QAAQK,QAAQ,IAAI;QAC9BC,SAASN,QAAQM,OAAO;QACxBC,QAAQP,QAAQO,MAAM,IAAI;QAC1BC,eAAeR,QAAQQ,aAAa,IAAI;YAAC;YAAK;YAAK;SAAI;QACvDC,gBAAgBT,QAAQU,SAAS,IAAI;QACrCC,YAAY,KAAK;QAEjBC,SAAS,EAAE;QAEXC,iBAAiB,WAAY;YAC3B,IAAIZ,OAAOK,OAAO,KAAKQ,aAAaC,KAAKC,GAAG,KAAKf,OAAOK,OAAO,EAAE;gBAC/DL,OAAOC,eAAe,GAAG;gBACzBD,OAAOK,OAAO,GAAGS,KAAKC,GAAG,KAAKf,OAAOI,QAAQ;YAC/C,CAAC;YAED,OAAOJ,OAAOG,GAAG,GAAGH,OAAOC,eAAe,GAAGD,OAAOQ,cAAc,GAAGR,OAAOM,MAAM;QACpF;QAEAU,kBAAkB,WAAY;YAC5B,OAAOhB,OAAOY,eAAe,KAAK;QACpC;QAEAK,2BAA2B,iBAAkB;YAC3C,qDAAqD;YACrD,OAAO,MAAM,IAAIC,QAAQ,OAAOC,UAAY;gBAC1C,2FAA2F;gBAC3F,IAAInB,OAAOgB,gBAAgB,IAAI;oBAC7BhB,OAAOQ,cAAc,IAAI;oBACzBW;gBACF,OAAO;oBACLnB,OAAOW,OAAO,CAACS,IAAI,CAACD;oBACpB,MAAMnB,OAAOqB,cAAc;gBAC7B,CAAC;YACH;QACF;QAEAA,gBAAgB,iBAAkB;YAChC,qEAAqE;YACrE,IAAIrB,OAAOU,UAAU,EAAE;gBACrB;YACF,CAAC;YAED,gDAAgD;YAChDV,OAAOU,UAAU,GAAG,IAAI;YAExB,MAAOV,OAAOW,OAAO,CAACW,MAAM,GAAG,EAAG;gBAChCzB,OAAO0B,IAAI,CAAC,CAAC,6DAA6D,EAAEvB,OAAOW,OAAO,CAACW,MAAM,CAAC,WAAW,CAAC;gBAE9G,IAAItB,OAAOK,OAAO,KAAKQ,aAAa,CAACb,OAAOgB,gBAAgB,IAAI;oBAC9D,MAAMpB,MAAMI,OAAOK,OAAO,GAAGS,KAAKC,GAAG;gBACvC,CAAC;gBAEDf,OAAOQ,cAAc,IAAI;gBACzB,qCAAqC;gBACrCR,OAAOW,OAAO,CAACa,KAAK;YACtB;YAEA,sEAAsE;YACtExB,OAAOU,UAAU,GAAG,KAAK;QAC3B;QAEAe,wBAAwB,SAAUC,IAAI,EAAEC,WAAW,EAAE;YACnD,+DAA+D;YAC/D3B,OAAOQ,cAAc,IAAI;YACzB,+DAA+D;YAC/D,IAAI,CAACR,OAAOO,aAAa,CAACqB,QAAQ,CAACF,OAAO;YAC1C,yCAAyC;YACzC,IAAIA,SAAS,OAAOC,aAAa;YAEjC,2BAA2B;YAC3B,IAAI3B,OAAOK,OAAO,KAAKQ,WAAW;gBAChCb,OAAOK,OAAO,GAAGS,KAAKC,GAAG,KAAKf,OAAOI,QAAQ;YAC/C,CAAC;YAEDJ,OAAOC,eAAe,IAAI;QAC5B;IACF;IAEA,OAAOD;AACT,CAAC"}
1
+ {"version":3,"sources":["../src/invalidBucket.ts"],"sourcesContent":["import { delay, logger } from '@discordeno/utils'\n\n/**\n * A invalid request bucket is used in a similar manner as a leaky bucket but a invalid request bucket can be refilled as needed.\n * It's purpose is to make sure the bot does not hit the limit to getting a 1 hr ban.\n *\n * @param options The options used to configure this bucket.\n * @returns RefillingBucket\n */\nexport function createInvalidRequestBucket(options: InvalidRequestBucketOptions): InvalidRequestBucket {\n const bucket: InvalidRequestBucket = {\n invalidRequests: options.current ?? 0,\n max: options.max ?? 10000,\n interval: options.interval ?? 600_000, // 10 minutes\n resetAt: options.resetAt,\n safety: options.safety ?? 1,\n errorStatuses: options.errorStatuses ?? [401, 403, 429],\n activeRequests: options.requested ?? 0,\n processing: false,\n\n waiting: [],\n\n requestsAllowed: function () {\n if (bucket.resetAt !== undefined && Date.now() >= bucket.resetAt) {\n bucket.invalidRequests = 0\n bucket.resetAt = Date.now() + bucket.interval\n }\n\n return bucket.max - bucket.invalidRequests - bucket.activeRequests - bucket.safety\n },\n\n isRequestAllowed: function () {\n return bucket.requestsAllowed() > 0\n },\n\n waitUntilRequestAvailable: async function () {\n // eslint-disable-next-line no-async-promise-executor\n return await new Promise(async (resolve) => {\n // If whatever amount of requests is left is more than the safety margin, allow the request\n if (bucket.isRequestAllowed()) {\n bucket.activeRequests += 1\n resolve()\n } else {\n bucket.waiting.push(resolve)\n await bucket.processWaiting()\n }\n })\n },\n\n processWaiting: async function () {\n // If already processing, that loop will handle all waiting requests.\n if (bucket.processing) return\n\n // Mark as processing so other loops don't start\n bucket.processing = true\n\n while (bucket.waiting.length > 0) {\n logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length} pending requests to be made. ${JSON.stringify(bucket)}`)\n\n if (!bucket.isRequestAllowed() && bucket.resetAt !== undefined) {\n logger.warn(`[InvalidBucket] processing waiting queue is now paused until more requests are available. ${bucket.waiting.length} pending requests. ${JSON.stringify(bucket)}`)\n await delay(bucket.resetAt - Date.now())\n }\n\n bucket.activeRequests += 1\n // Resolve the next item in the queue\n bucket.waiting.shift()?.()\n }\n\n // Mark as false so next pending request can be triggered by new loop.\n bucket.processing = false\n },\n\n handleCompletedRequest: function (code, sharedScope) {\n // Since request is complete, we can remove one from requested.\n bucket.activeRequests -= 1\n // Since it is as a valid request, we don't need to do anything\n if (!bucket.errorStatuses.includes(code)) return\n // Shared scope is not considered invalid\n if (code === 429 && sharedScope) return\n\n // INVALID REQUEST WAS MADE\n if (bucket.resetAt === undefined) {\n bucket.resetAt = Date.now() + bucket.interval\n }\n\n bucket.invalidRequests += 1\n logger.warn(`[InvalidBucket] an invalid request was made. Increasing invalidRequests count to ${bucket.invalidRequests}`)\n },\n }\n\n return bucket\n}\n\nexport interface InvalidRequestBucketOptions {\n /** current invalid amount */\n current?: number\n /** max invalid requests allowed until ban. Defaults to 10,000 */\n max?: number\n /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */\n interval?: number\n /** When the timeout for the bucket has started at. */\n resetAt?: number\n /** how safe to be from max. Defaults to 1 */\n safety?: number\n /** The request statuses that count as an invalid request. */\n errorStatuses?: number[]\n /** The amount of requests that were requested from this bucket. */\n requested?: number\n}\n\nexport interface InvalidRequestBucket {\n /** current invalid amount */\n invalidRequests: number\n /** max invalid requests allowed until ban. Defaults to 10,000 */\n max: number\n /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */\n interval: number\n /** When the timeout for this bucket has started at. */\n resetAt: number | undefined\n /** how safe to be from max. Defaults to 1 */\n safety: number\n /** The request statuses that count as an invalid request. */\n errorStatuses: number[]\n /** The amount of requests that were requested from this bucket. */\n activeRequests: number\n /** The requests that are currently pending. */\n waiting: Array<(value: void | PromiseLike<void>) => void>\n /** Whether or not the waiting queue is already processing. */\n processing: boolean\n\n /** Gives the number of requests that are currently allowed. */\n requestsAllowed: () => number\n /** Checks if a request is allowed at this time. */\n isRequestAllowed: () => boolean\n /** Waits until a request is available */\n waitUntilRequestAvailable: () => Promise<void>\n /** Begins processing the waiting queue of requests. */\n processWaiting: () => Promise<void>\n /** Handler for whenever a request is validated. This should update the requested values or trigger any other necessary stuff. */\n handleCompletedRequest: (code: number, sharedScope: boolean) => void\n}\n"],"names":["delay","logger","createInvalidRequestBucket","options","bucket","invalidRequests","current","max","interval","resetAt","safety","errorStatuses","activeRequests","requested","processing","waiting","requestsAllowed","undefined","Date","now","isRequestAllowed","waitUntilRequestAvailable","Promise","resolve","push","processWaiting","length","info","JSON","stringify","warn","shift","handleCompletedRequest","code","sharedScope","includes"],"mappings":"AAAA,SAASA,KAAK,EAAEC,MAAM,QAAQ,oBAAmB;AAEjD;;;;;;CAMC,GACD,OAAO,SAASC,2BAA2BC,OAAoC,EAAwB;IACrG,MAAMC,SAA+B;QACnCC,iBAAiBF,QAAQG,OAAO,IAAI;QACpCC,KAAKJ,QAAQI,GAAG,IAAI;QACpBC,UAAUL,QAAQK,QAAQ,IAAI;QAC9BC,SAASN,QAAQM,OAAO;QACxBC,QAAQP,QAAQO,MAAM,IAAI;QAC1BC,eAAeR,QAAQQ,aAAa,IAAI;YAAC;YAAK;YAAK;SAAI;QACvDC,gBAAgBT,QAAQU,SAAS,IAAI;QACrCC,YAAY,KAAK;QAEjBC,SAAS,EAAE;QAEXC,iBAAiB,WAAY;YAC3B,IAAIZ,OAAOK,OAAO,KAAKQ,aAAaC,KAAKC,GAAG,MAAMf,OAAOK,OAAO,EAAE;gBAChEL,OAAOC,eAAe,GAAG;gBACzBD,OAAOK,OAAO,GAAGS,KAAKC,GAAG,KAAKf,OAAOI,QAAQ;YAC/C,CAAC;YAED,OAAOJ,OAAOG,GAAG,GAAGH,OAAOC,eAAe,GAAGD,OAAOQ,cAAc,GAAGR,OAAOM,MAAM;QACpF;QAEAU,kBAAkB,WAAY;YAC5B,OAAOhB,OAAOY,eAAe,KAAK;QACpC;QAEAK,2BAA2B,iBAAkB;YAC3C,qDAAqD;YACrD,OAAO,MAAM,IAAIC,QAAQ,OAAOC,UAAY;gBAC1C,2FAA2F;gBAC3F,IAAInB,OAAOgB,gBAAgB,IAAI;oBAC7BhB,OAAOQ,cAAc,IAAI;oBACzBW;gBACF,OAAO;oBACLnB,OAAOW,OAAO,CAACS,IAAI,CAACD;oBACpB,MAAMnB,OAAOqB,cAAc;gBAC7B,CAAC;YACH;QACF;QAEAA,gBAAgB,iBAAkB;YAChC,qEAAqE;YACrE,IAAIrB,OAAOU,UAAU,EAAE;YAEvB,gDAAgD;YAChDV,OAAOU,UAAU,GAAG,IAAI;YAExB,MAAOV,OAAOW,OAAO,CAACW,MAAM,GAAG,EAAG;gBAChCzB,OAAO0B,IAAI,CAAC,CAAC,6DAA6D,EAAEvB,OAAOW,OAAO,CAACW,MAAM,CAAC,8BAA8B,EAAEE,KAAKC,SAAS,CAACzB,QAAQ,CAAC;gBAE1J,IAAI,CAACA,OAAOgB,gBAAgB,MAAMhB,OAAOK,OAAO,KAAKQ,WAAW;oBAC9DhB,OAAO6B,IAAI,CAAC,CAAC,0FAA0F,EAAE1B,OAAOW,OAAO,CAACW,MAAM,CAAC,mBAAmB,EAAEE,KAAKC,SAAS,CAACzB,QAAQ,CAAC;oBAC5K,MAAMJ,MAAMI,OAAOK,OAAO,GAAGS,KAAKC,GAAG;gBACvC,CAAC;gBAEDf,OAAOQ,cAAc,IAAI;gBACzB,qCAAqC;gBACrCR,OAAOW,OAAO,CAACgB,KAAK;YACtB;YAEA,sEAAsE;YACtE3B,OAAOU,UAAU,GAAG,KAAK;QAC3B;QAEAkB,wBAAwB,SAAUC,IAAI,EAAEC,WAAW,EAAE;YACnD,+DAA+D;YAC/D9B,OAAOQ,cAAc,IAAI;YACzB,+DAA+D;YAC/D,IAAI,CAACR,OAAOO,aAAa,CAACwB,QAAQ,CAACF,OAAO;YAC1C,yCAAyC;YACzC,IAAIA,SAAS,OAAOC,aAAa;YAEjC,2BAA2B;YAC3B,IAAI9B,OAAOK,OAAO,KAAKQ,WAAW;gBAChCb,OAAOK,OAAO,GAAGS,KAAKC,GAAG,KAAKf,OAAOI,QAAQ;YAC/C,CAAC;YAEDJ,OAAOC,eAAe,IAAI;YAC1BJ,OAAO6B,IAAI,CAAC,CAAC,iFAAiF,EAAE1B,OAAOC,eAAe,CAAC,CAAC;QAC1H;IACF;IAEA,OAAOD;AACT,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAkDA,OAAO,KAAK,EAA4B,wBAAwB,EAAE,WAAW,EAAsB,MAAM,YAAY,CAAA;AAMrH,eAAO,MAAM,mBAAmB,KAAK,CAAA;AACrC,eAAO,MAAM,eAAe,4BAA4B,CAAA;AAExD,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,2BAA2B,0BAA0B,CAAA;AAClE,eAAO,MAAM,6BAA6B,4BAA4B,CAAA;AACtE,eAAO,MAAM,wBAAwB,uBAAuB,CAAA;AAC5D,eAAO,MAAM,wBAAwB,uBAAuB,CAAA;AAC5D,eAAO,MAAM,uBAAuB,sBAAsB,CAAA;AAC1D,eAAO,MAAM,uBAAuB,sBAAsB,CAAA;AAE1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,WAAW,CAknChF"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAA4B,wBAAwB,EAAsB,WAAW,EAAsB,MAAM,YAAY,CAAA;AAKzI,eAAO,MAAM,mBAAmB,KAAK,CAAA;AACrC,eAAO,MAAM,eAAe,4BAA4B,CAAA;AAExD,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,2BAA2B,0BAA0B,CAAA;AAClE,eAAO,MAAM,6BAA6B,4BAA4B,CAAA;AACtE,eAAO,MAAM,wBAAwB,uBAAuB,CAAA;AAC5D,eAAO,MAAM,wBAAwB,uBAAuB,CAAA;AAC5D,eAAO,MAAM,uBAAuB,sBAAsB,CAAA;AAC1D,eAAO,MAAM,uBAAuB,sBAAsB,CAAA;AAE1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,WAAW,CAq3ChF"}
package/dist/manager.js CHANGED
@@ -1,8 +1,8 @@
1
- /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable no-const-assign */ import { InteractionResponseTypes } from '@discordeno/types';
1
+ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable no-const-assign */ import { Buffer } from 'node:buffer';
2
2
  import { calculateBits, camelize, camelToSnakeCase, delay, getBotIdFromToken, logger, processReactionString, urlToBase64 } from '@discordeno/utils';
3
- import fetch from 'node-fetch';
4
3
  import { createInvalidRequestBucket } from './invalidBucket.js';
5
4
  import { Queue } from './queue.js';
5
+ import { InteractionResponseTypes } from '@discordeno/types';
6
6
  import { createRoutes } from './routes.js';
7
7
  // TODO: make dynamic based on package.json file
8
8
  const version = '19.0.0-alpha.1';
@@ -36,8 +36,14 @@ export function createRestManager(options) {
36
36
  token: options.token,
37
37
  version: options.version ?? DISCORD_API_VERSION,
38
38
  routes: createRoutes(),
39
- checkRateLimits (url) {
40
- const ratelimited = rest.rateLimitedPaths.get(url);
39
+ createBaseHeaders () {
40
+ return {
41
+ 'user-agent': `DiscordBot (https://github.com/discordeno/discordeno, v${version})`
42
+ };
43
+ },
44
+ checkRateLimits (url, headers) {
45
+ const authHeader = headers?.authorization ?? '';
46
+ const ratelimited = rest.rateLimitedPaths.get(`${authHeader}${url}`);
41
47
  const global = rest.rateLimitedPaths.get('global');
42
48
  const now = Date.now();
43
49
  if (ratelimited && now < ratelimited.resetTimestamp) {
@@ -56,20 +62,27 @@ export function createRestManager(options) {
56
62
  }
57
63
  const newObj = {};
58
64
  for (const key of Object.keys(obj)){
59
- // Keys that dont require snake casing
60
- if ([
61
- 'permissions',
62
- 'allow',
63
- 'deny'
64
- ].includes(key)) {
65
- newObj[key] = calculateBits(obj[key]);
66
- continue;
65
+ const value = obj[key];
66
+ // Some falsy values should be allowed like null or 0
67
+ if (value !== undefined) {
68
+ switch(key){
69
+ case 'permissions':
70
+ case 'allow':
71
+ case 'deny':
72
+ newObj[key] = typeof value === 'string' ? value : calculateBits(value);
73
+ continue;
74
+ case 'defaultMemberPermissions':
75
+ newObj.default_member_permissions = typeof value === 'string' ? value : calculateBits(value);
76
+ continue;
77
+ case 'nameLocalizations':
78
+ newObj.name_localizations = value;
79
+ continue;
80
+ case 'descriptionLocalizations':
81
+ newObj.description_localizations = value;
82
+ continue;
83
+ }
67
84
  }
68
- if (key === 'defaultMemberPermissions') {
69
- newObj.default_member_permissions = calculateBits(obj[key]);
70
- continue;
71
- }
72
- newObj[camelToSnakeCase(key)] = rest.changeToDiscordFormat(obj[key]);
85
+ newObj[camelToSnakeCase(key)] = rest.changeToDiscordFormat(value);
73
86
  }
74
87
  return newObj;
75
88
  }
@@ -77,10 +90,8 @@ export function createRestManager(options) {
77
90
  return obj;
78
91
  },
79
92
  createRequestBody (method, options) {
80
- const headers = {
81
- 'user-agent': `DiscordBot (https://github.com/discordeno/discordeno, v${version})`
82
- };
83
- if (options?.unauthorized !== false) headers.authorization = `Bot ${rest.token}`;
93
+ const headers = this.createBaseHeaders();
94
+ if (options?.unauthorized !== true) headers.authorization = `Bot ${rest.token}`;
84
95
  // IF A REASON IS PROVIDED ENCODE IT IN HEADERS
85
96
  if (options?.reason !== undefined) {
86
97
  headers[AUDIT_LOG_REASON_HEADER] = encodeURIComponent(options?.reason);
@@ -94,12 +105,21 @@ export function createRestManager(options) {
94
105
  for(let i = 0; i < options.files.length; ++i){
95
106
  form.append(`file${i}`, options.files[i].blob, options.files[i].name);
96
107
  }
97
- form.append('payload_json', JSON.stringify({
108
+ // Have to use changeToDiscordFormat or else JSON.stringify may throw an error for the presence of BigInt(s) in the json
109
+ form.append('payload_json', JSON.stringify(rest.changeToDiscordFormat({
98
110
  ...options.body,
99
111
  files: undefined
100
- }));
112
+ })));
113
+ // No need to set the `content-type` header since `fetch` does that automatically for us when we use a `FormData` object.
101
114
  body = form;
102
- // No need to set the `content-type` header since `fetch` does that automatically for us when we use a `FormData` object.
115
+ } else if (options?.body && options.headers && options.headers['content-type'] === 'application/x-www-form-urlencoded') {
116
+ // OAuth2 body handling
117
+ const formBody = [];
118
+ const discordBody = rest.changeToDiscordFormat(options.body);
119
+ for(const prop in discordBody){
120
+ formBody.push(`${encodeURIComponent(prop)}=${encodeURIComponent(discordBody[prop])}`);
121
+ }
122
+ body = formBody.join('&');
103
123
  } else if (options?.body !== undefined) {
104
124
  if (options.body instanceof FormData) {
105
125
  body = options.body;
@@ -146,7 +166,7 @@ export function createRestManager(options) {
146
166
  }, 1000);
147
167
  }
148
168
  },
149
- /** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */ processHeaders (url, headers) {
169
+ /** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */ processHeaders (url, headers, requestAuthorization) {
150
170
  let rateLimited = false;
151
171
  // GET ALL NECESSARY HEADERS
152
172
  const remaining = headers.get(RATE_LIMIT_REMAINING_HEADER);
@@ -156,7 +176,7 @@ export function createRestManager(options) {
156
176
  // undefined override null needed for typings
157
177
  const bucketId = headers.get(RATE_LIMIT_BUCKET_HEADER) ?? undefined;
158
178
  const limit = headers.get(RATE_LIMIT_LIMIT_HEADER);
159
- rest.queues.get(url)?.handleCompletedRequest({
179
+ rest.queues.get(`${requestAuthorization}${url}`)?.handleCompletedRequest({
160
180
  remaining: remaining ? Number(remaining) : undefined,
161
181
  interval: retryAfter ? Number(retryAfter) * 1000 : undefined,
162
182
  max: limit ? Number(limit) : undefined
@@ -165,14 +185,14 @@ export function createRestManager(options) {
165
185
  if (remaining === '0') {
166
186
  rateLimited = true;
167
187
  // SAVE THE URL AS LIMITED, IMPORTANT FOR NEW REQUESTS BY USER WITHOUT BUCKET
168
- rest.rateLimitedPaths.set(url, {
188
+ rest.rateLimitedPaths.set(`${requestAuthorization}${url}`, {
169
189
  url,
170
190
  resetTimestamp: reset,
171
191
  bucketId
172
192
  });
173
193
  // SAVE THE BUCKET AS LIMITED SINCE DIFFERENT URLS MAY SHARE A BUCKET
174
194
  if (bucketId) {
175
- rest.rateLimitedPaths.set(bucketId, {
195
+ rest.rateLimitedPaths.set(`${requestAuthorization}${bucketId}`, {
176
196
  url,
177
197
  resetTimestamp: reset,
178
198
  bucketId
@@ -181,8 +201,8 @@ export function createRestManager(options) {
181
201
  }
182
202
  // IF THERE IS NO REMAINING GLOBAL LIMIT, MARK IT RATE LIMITED GLOBALLY
183
203
  if (global) {
184
- const retryAfter = headers.get('retry-after');
185
- const globalReset = Date.now() + Number(retryAfter) * 1000;
204
+ const retryAfter = Number(headers.get('retry-after')) * 1000;
205
+ const globalReset = Date.now() + retryAfter;
186
206
  // rest.debug(
187
207
  // `[REST = Globally Rate Limited] URL: ${url} | Global Rest: ${globalReset}`
188
208
  // )
@@ -190,14 +210,14 @@ export function createRestManager(options) {
190
210
  rateLimited = true;
191
211
  setTimeout(()=>{
192
212
  rest.globallyRateLimited = false;
193
- }, globalReset);
213
+ }, retryAfter);
194
214
  rest.rateLimitedPaths.set('global', {
195
215
  url: 'global',
196
216
  resetTimestamp: globalReset,
197
217
  bucketId
198
218
  });
199
219
  if (bucketId) {
200
- rest.rateLimitedPaths.set(bucketId, {
220
+ rest.rateLimitedPaths.set(`${requestAuthorization}${bucketId}`, {
201
221
  url: 'global',
202
222
  resetTimestamp: globalReset,
203
223
  bucketId
@@ -212,17 +232,33 @@ export function createRestManager(options) {
212
232
  async sendRequest (options) {
213
233
  const url = `${rest.baseUrl}/v${rest.version}${options.route}`;
214
234
  const payload = rest.createRequestBody(options.method, options.requestBodyOptions);
235
+ const loggingHeaders = {
236
+ ...payload.headers
237
+ };
238
+ const authenticationScheme = payload.headers.authorization?.split(' ')[0];
239
+ if (payload.headers.authorization) {
240
+ loggingHeaders.authorization = `${authenticationScheme} tokenhere`;
241
+ }
215
242
  logger.debug(`sending request to ${url}`, 'with payload:', {
216
243
  ...payload,
217
- headers: {
218
- ...payload.headers,
219
- authorization: 'Bot tokenhere'
220
- }
244
+ headers: loggingHeaders
245
+ });
246
+ const response = await fetch(url, payload).catch(async (error)=>{
247
+ logger.error(error);
248
+ // Mark request and completed
249
+ rest.invalidBucket.handleCompletedRequest(999, false);
250
+ options.reject({
251
+ ok: false,
252
+ status: 999,
253
+ error: 'Possible network or request shape issue occurred. If this is rare, its a network glitch. If it occurs a lot something is wrong.'
254
+ });
255
+ throw error;
221
256
  });
222
- const response = await fetch(url, payload);
223
257
  logger.debug(`request fetched from ${url} with status ${response.status} & ${response.statusText}`);
258
+ // Mark request and completed
259
+ rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get(RATE_LIMIT_SCOPE_HEADER) === 'shared');
224
260
  // Set the bucket id if it was available on the headers
225
- const bucketId = rest.processHeaders(rest.simplifyUrl(options.route, options.method), response.headers);
261
+ const bucketId = rest.processHeaders(rest.simplifyUrl(options.route, options.method), response.headers, authenticationScheme === 'Bearer' ? payload.headers.authorization : '');
226
262
  if (bucketId) options.bucketId = bucketId;
227
263
  if (response.status < 200 || response.status >= 400) {
228
264
  logger.debug(`Request to ${url} failed.`);
@@ -247,8 +283,6 @@ export function createRestManager(options) {
247
283
  return;
248
284
  }
249
285
  options.retryCount += 1;
250
- // Rate limited, add back to queue
251
- rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get(RATE_LIMIT_SCOPE_HEADER) === 'shared');
252
286
  const resetAfter = response.headers.get(RATE_LIMIT_RESET_AFTER_HEADER);
253
287
  if (resetAfter) await delay(Number(resetAfter) * 1000);
254
288
  // process the response to prevent mem leak
@@ -289,23 +323,30 @@ export function createRestManager(options) {
289
323
  await rest.sendRequest(request);
290
324
  return;
291
325
  }
292
- const queue = rest.queues.get(url);
326
+ const authHeader = request.requestBodyOptions?.headers?.authorization ?? '';
327
+ const queue = rest.queues.get(`${authHeader}${url}`);
293
328
  if (queue !== undefined) {
294
329
  queue.makeRequest(request);
295
330
  } else {
296
331
  // CREATES A NEW QUEUE
297
332
  const bucketQueue = new Queue(rest, {
298
333
  url,
299
- deleteQueueDelay: rest.deleteQueueDelay
334
+ deleteQueueDelay: rest.deleteQueueDelay,
335
+ authentication: authHeader
300
336
  });
301
337
  // Add request to queue
302
338
  bucketQueue.makeRequest(request);
303
339
  // Save queue
304
- rest.queues.set(url, bucketQueue);
340
+ rest.queues.set(`${authHeader}${url}`, bucketQueue);
305
341
  }
306
342
  },
307
343
  async makeRequest (method, route, options) {
308
344
  if (rest.isProxied) {
345
+ if (rest.authorization !== undefined) {
346
+ options ??= {};
347
+ options.headers ??= {};
348
+ options.headers.authorization = rest.authorization;
349
+ }
309
350
  const result = await fetch(`${rest.baseUrl}/v${rest.version}${route}`, rest.createRequestBody(method, options));
310
351
  if (!result.ok) {
311
352
  const err = await result.json().catch(()=>{});
@@ -372,6 +413,11 @@ export function createRestManager(options) {
372
413
  async addThreadMember (channelId, userId) {
373
414
  await rest.put(rest.routes.channels.threads.user(channelId, userId));
374
415
  },
416
+ async addDmRecipient (channelId, userId, body) {
417
+ await rest.put(rest.routes.channels.dmRecipient(channelId, userId), {
418
+ body
419
+ });
420
+ },
375
421
  async createAutomodRule (guildId, body, reason) {
376
422
  return await rest.post(rest.routes.guilds.automod.rules(guildId), {
377
423
  body,
@@ -390,20 +436,34 @@ export function createRestManager(options) {
390
436
  reason
391
437
  });
392
438
  },
393
- async createGlobalApplicationCommand (body) {
394
- return await rest.post(rest.routes.interactions.commands.commands(rest.applicationId), {
439
+ async createGlobalApplicationCommand (body, options) {
440
+ const restOptions = {
395
441
  body
396
- });
442
+ };
443
+ if (options?.bearerToken) {
444
+ restOptions.unauthorized = true;
445
+ restOptions.headers = {
446
+ authorization: `Bearer ${options.bearerToken}`
447
+ };
448
+ }
449
+ return await rest.post(rest.routes.interactions.commands.commands(rest.applicationId), restOptions);
397
450
  },
398
451
  async createGuild (body) {
399
452
  return await rest.post(rest.routes.guilds.all(), {
400
453
  body
401
454
  });
402
455
  },
403
- async createGuildApplicationCommand (body, guildId) {
404
- return await rest.post(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), {
456
+ async createGuildApplicationCommand (body, guildId, options) {
457
+ const restOptions = {
405
458
  body
406
- });
459
+ };
460
+ if (options?.bearerToken) {
461
+ restOptions.unauthorized = true;
462
+ restOptions.headers = {
463
+ authorization: `Bearer ${options.bearerToken}`
464
+ };
465
+ }
466
+ return await rest.post(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), restOptions);
407
467
  },
408
468
  async createGuildFromTemplate (templateCode, body) {
409
469
  if (body.icon) {
@@ -596,7 +656,7 @@ export function createRestManager(options) {
596
656
  },
597
657
  async editBotProfile (options) {
598
658
  const avatar = options?.botAvatarURL ? await urlToBase64(options?.botAvatarURL) : options?.botAvatarURL;
599
- return await rest.patch(rest.routes.userBot(), {
659
+ return await rest.patch(rest.routes.currentUser(), {
600
660
  body: {
601
661
  username: options.username?.trim(),
602
662
  avatar
@@ -670,7 +730,8 @@ export function createRestManager(options) {
670
730
  },
671
731
  async editMessage (channelId, messageId, body) {
672
732
  return await rest.patch(rest.routes.channels.message(channelId, messageId), {
673
- body
733
+ body,
734
+ files: body.files
674
735
  });
675
736
  },
676
737
  async editOriginalInteractionResponse (token, body) {
@@ -772,14 +833,62 @@ export function createRestManager(options) {
772
833
  async getActiveThreads (guildId) {
773
834
  return await rest.get(rest.routes.channels.threads.active(guildId));
774
835
  },
775
- async getApplicationCommandPermission (guildId, commandId) {
776
- return await rest.get(rest.routes.interactions.commands.permission(rest.applicationId, guildId, commandId));
777
- },
778
- async getApplicationCommandPermissions (guildId) {
779
- return await rest.get(rest.routes.interactions.commands.permissions(rest.applicationId, guildId));
836
+ async getApplicationCommandPermission (guildId, commandId, options) {
837
+ const restOptions = {};
838
+ if (options?.accessToken) {
839
+ restOptions.unauthorized = true;
840
+ restOptions.headers = {
841
+ authorization: `Bearer ${options.accessToken}`
842
+ };
843
+ }
844
+ return await rest.get(rest.routes.interactions.commands.permission(options?.applicationId ?? rest.applicationId, guildId, commandId), restOptions);
845
+ },
846
+ async getApplicationCommandPermissions (guildId, options) {
847
+ const restOptions = {};
848
+ if (options?.accessToken) {
849
+ restOptions.unauthorized = true;
850
+ restOptions.headers = {
851
+ authorization: `Bearer ${options.accessToken}`
852
+ };
853
+ }
854
+ return await rest.get(rest.routes.interactions.commands.permissions(options?.applicationId ?? rest.applicationId, guildId), restOptions);
780
855
  },
781
856
  async getApplicationInfo () {
782
- return await rest.get(rest.routes.oauth2Application());
857
+ return await rest.get(rest.routes.oauth2.application());
858
+ },
859
+ async getCurrentAuthenticationInfo (token) {
860
+ return await rest.get(rest.routes.oauth2.currentAuthorization(), {
861
+ headers: {
862
+ authorization: `Bearer ${token}`
863
+ },
864
+ unauthorized: true
865
+ });
866
+ },
867
+ async exchangeToken (clientId, clientSecret, body) {
868
+ const basicCredentials = Buffer.from(`${clientId}:${clientSecret}`);
869
+ const restOptions = {
870
+ body,
871
+ headers: {
872
+ 'content-type': 'application/x-www-form-urlencoded',
873
+ authorization: `Basic ${basicCredentials.toString('base64')}`
874
+ },
875
+ unauthorized: true
876
+ };
877
+ if (body.grantType === 'client_credentials') {
878
+ restOptions.body.scope = body.scope.join(' ');
879
+ }
880
+ return await rest.post(rest.routes.oauth2.tokenExchange(), restOptions);
881
+ },
882
+ async revokeToken (clientId, clientSecret, body) {
883
+ const basicCredentials = Buffer.from(`${clientId}:${clientSecret}`);
884
+ await rest.post(rest.routes.oauth2.tokenRevoke(), {
885
+ body,
886
+ headers: {
887
+ 'content-type': 'application/x-www-form-urlencoded',
888
+ authorization: `Basic ${basicCredentials.toString('base64')}`
889
+ },
890
+ unauthorized: true
891
+ });
783
892
  },
784
893
  async getAuditLog (guildId, options) {
785
894
  return await rest.get(rest.routes.guilds.auditlogs(guildId, options));
@@ -818,6 +927,11 @@ export function createRestManager(options) {
818
927
  }
819
928
  });
820
929
  },
930
+ async getGroupDmChannel (body) {
931
+ return await rest.post(rest.routes.channels.dm(), {
932
+ body
933
+ });
934
+ },
821
935
  async getEmoji (guildId, emojiId) {
822
936
  return await rest.get(rest.routes.guilds.emoji(guildId, emojiId));
823
937
  },
@@ -843,6 +957,14 @@ export function createRestManager(options) {
843
957
  }) {
844
958
  return await rest.get(rest.routes.guilds.guild(guildId, options.counts));
845
959
  },
960
+ async getGuilds (token, options) {
961
+ return await rest.get(rest.routes.guilds.userGuilds(options), {
962
+ headers: {
963
+ authorization: `Bearer ${token}`
964
+ },
965
+ unauthorized: true
966
+ });
967
+ },
846
968
  async getGuildApplicationCommand (commandId, guildId) {
847
969
  return await rest.get(rest.routes.interactions.commands.guilds.one(rest.applicationId, guildId, commandId));
848
970
  },
@@ -938,6 +1060,30 @@ export function createRestManager(options) {
938
1060
  async getUser (id) {
939
1061
  return await rest.get(rest.routes.user(id));
940
1062
  },
1063
+ async getCurrentUser (token) {
1064
+ return await rest.get(rest.routes.currentUser(), {
1065
+ headers: {
1066
+ authorization: `Bearer ${token}`
1067
+ },
1068
+ unauthorized: true
1069
+ });
1070
+ },
1071
+ async getUserConnections (token) {
1072
+ return await rest.get(rest.routes.oauth2.connections(), {
1073
+ headers: {
1074
+ authorization: `Bearer ${token}`
1075
+ },
1076
+ unauthorized: true
1077
+ });
1078
+ },
1079
+ async getUserApplicationRoleConnection (token, applicationId) {
1080
+ return await rest.get(rest.routes.oauth2.roleConnections(applicationId), {
1081
+ headers: {
1082
+ authorization: `Bearer ${token}`
1083
+ },
1084
+ unauthorized: true
1085
+ });
1086
+ },
941
1087
  async getVanityUrl (guildId) {
942
1088
  return await rest.get(rest.routes.guilds.vanity(guildId));
943
1089
  },
@@ -982,6 +1128,9 @@ export function createRestManager(options) {
982
1128
  async removeThreadMember (channelId, userId) {
983
1129
  await rest.delete(rest.routes.channels.threads.user(channelId, userId));
984
1130
  },
1131
+ async removeDmRecipient (channelId, userId) {
1132
+ await rest.delete(rest.routes.channels.dmRecipient(channelId, userId));
1133
+ },
985
1134
  async sendFollowupMessage (token, options) {
986
1135
  return await rest.post(rest.routes.webhooks.webhook(rest.applicationId, token), {
987
1136
  body: options,
@@ -1039,6 +1188,14 @@ export function createRestManager(options) {
1039
1188
  async getMember (guildId, userId) {
1040
1189
  return await rest.get(rest.routes.guilds.members.member(guildId, userId));
1041
1190
  },
1191
+ async getCurrentMember (guildId, token) {
1192
+ return await rest.get(rest.routes.guilds.members.currentMember(guildId), {
1193
+ headers: {
1194
+ authorization: `Bearer ${token}`
1195
+ },
1196
+ unauthorized: true
1197
+ });
1198
+ },
1042
1199
  async getMembers (guildId, options) {
1043
1200
  return await rest.get(rest.routes.guilds.members.members(guildId, options));
1044
1201
  },
@@ -1074,15 +1231,62 @@ export function createRestManager(options) {
1074
1231
  async triggerTypingIndicator (channelId) {
1075
1232
  await rest.post(rest.routes.channels.typing(channelId));
1076
1233
  },
1077
- async upsertGlobalApplicationCommands (body) {
1078
- return await rest.put(rest.routes.interactions.commands.commands(rest.applicationId), {
1234
+ async upsertGlobalApplicationCommands (body, options) {
1235
+ const restOptions = {
1079
1236
  body
1237
+ };
1238
+ if (options?.bearerToken) {
1239
+ restOptions.unauthorized = true;
1240
+ restOptions.headers = {
1241
+ authorization: `Bearer ${options.bearerToken}`
1242
+ };
1243
+ }
1244
+ return await rest.put(rest.routes.interactions.commands.commands(rest.applicationId), restOptions);
1245
+ },
1246
+ async upsertGuildApplicationCommands (guildId, body, options) {
1247
+ const restOptions = {
1248
+ body
1249
+ };
1250
+ if (options?.bearerToken) {
1251
+ restOptions.unauthorized = true;
1252
+ restOptions.headers = {
1253
+ authorization: `Bearer ${options.bearerToken}`
1254
+ };
1255
+ }
1256
+ return await rest.put(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), restOptions);
1257
+ },
1258
+ async editUserApplicationRoleConnection (token, applicationId, body) {
1259
+ return await rest.put(rest.routes.oauth2.roleConnections(applicationId), {
1260
+ body,
1261
+ headers: {
1262
+ authorization: `Bearer ${token}`
1263
+ },
1264
+ unauthorized: true
1080
1265
  });
1081
1266
  },
1082
- async upsertGuildApplicationCommands (guildId, body) {
1083
- return await rest.put(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), {
1267
+ async addGuildMember (guildId, userId, body) {
1268
+ return await rest.put(rest.routes.guilds.members.member(guildId, userId), {
1084
1269
  body
1085
1270
  });
1271
+ },
1272
+ preferSnakeCase (enabled) {
1273
+ const camelizer = enabled ? (x)=>x : camelize;
1274
+ rest.get = async (url, options)=>{
1275
+ return camelizer(await rest.makeRequest('GET', url, options));
1276
+ };
1277
+ rest.post = async (url, options)=>{
1278
+ return camelizer(await rest.makeRequest('POST', url, options));
1279
+ };
1280
+ rest.delete = async (url, options)=>{
1281
+ camelizer(await rest.makeRequest('DELETE', url, options));
1282
+ };
1283
+ rest.patch = async (url, options)=>{
1284
+ return camelizer(await rest.makeRequest('PATCH', url, options));
1285
+ };
1286
+ rest.put = async (url, options)=>{
1287
+ return camelizer(await rest.makeRequest('PUT', url, options));
1288
+ };
1289
+ return rest;
1086
1290
  }
1087
1291
  };
1088
1292
  return rest;