@ovencord/rest 2.5.8 → 2.6.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@ovencord/rest",
4
- "version": "2.5.8",
4
+ "version": "2.6.0",
5
5
  "description": "The REST API for discord.js",
6
6
  "scripts": {
7
7
  "test": "bun test",
@@ -36,8 +36,8 @@
36
36
  "homepage": "https://ovencord.dev",
37
37
  "funding": "https://github.com/ovencord/ovencord?sponsor",
38
38
  "dependencies": {
39
- "@ovencord/collection": "^2.1.2",
40
- "@ovencord/util": "^1.1.4",
39
+ "@ovencord/collection": "^2.1.4",
40
+ "@ovencord/util": "^1.1.8",
41
41
  "discord-api-types": "^0.38.40"
42
42
  },
43
43
  "devDependencies": {},
package/src/index.ts CHANGED
@@ -5,4 +5,3 @@ import { makeRequest } from './strategies/bunRequest.js';
5
5
  setDefaultStrategy(makeRequest);
6
6
 
7
7
  export * from './shared.js';
8
-
package/src/lib/CDN.ts CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  import { CDNRoutes } from 'discord-api-types/v10';
3
2
  import {
4
3
  ALLOWED_EXTENSIONS,
package/src/lib/REST.ts CHANGED
@@ -1,33 +1,31 @@
1
1
  import { Collection } from '@ovencord/collection';
2
2
  import { DiscordSnowflake } from '@ovencord/util';
3
- import { AsyncEventEmitter } from './utils/AsyncEventEmitter.js';
4
- import { uuidv5 as uuidV5 } from './utils/utils.js';
5
3
  import { CDN } from './CDN.js';
6
4
  import { BurstHandler } from './handlers/BurstHandler.js';
7
5
  import { SequentialHandler } from './handlers/SequentialHandler.js';
8
6
  import type { IHandler } from './interfaces/Handler.js';
7
+ import { AsyncEventEmitter } from './utils/AsyncEventEmitter.js';
9
8
  import {
10
9
  AUTH_UUID_NAMESPACE,
11
10
  BurstHandlerMajorIdKey,
12
11
  DefaultRestOptions,
13
12
  DefaultUserAgent,
14
-
15
13
  RESTEvents,
16
14
  } from './utils/constants.js';
17
- import { RequestMethod } from './utils/types.js';
18
15
  import type {
19
- RESTOptions,
20
- ResponseLike,
21
- RestEvents,
16
+ AuthData,
22
17
  HashData,
23
18
  InternalRequest,
24
- RouteLike,
19
+ RESTOptions,
20
+ RequestData,
25
21
  RequestHeaders,
22
+ ResponseLike,
23
+ RestEvents,
26
24
  RouteData,
27
- RequestData,
28
- AuthData,
25
+ RouteLike,
29
26
  } from './utils/types.js';
30
- import { isBufferLike, parseResponse } from './utils/utils.js';
27
+ import { RequestMethod } from './utils/types.js';
28
+ import { isBufferLike, parseResponse, uuidv5 as uuidV5 } from './utils/utils.js';
31
29
 
32
30
  /**
33
31
  * Represents the class that manages handlers for endpoints
@@ -380,15 +378,12 @@ export class REST extends AsyncEventEmitter<RestEvents> {
380
378
 
381
379
  // Set the final body to the form data
382
380
  finalBody = formData;
383
-
384
381
  } else if (request.body != null) {
385
382
  if (request.passThroughBody) {
386
383
  finalBody = request.body as any;
387
384
  } else {
388
385
  // Stringify the JSON data
389
- finalBody = JSON.stringify(request.body, (_, value) =>
390
- typeof value === 'bigint' ? value.toString() : value,
391
- );
386
+ finalBody = JSON.stringify(request.body, (_, value) => (typeof value === 'bigint' ? value.toString() : value));
392
387
  // Set the additional headers to specify the content-type
393
388
  additionalHeaders = { 'Content-Type': 'application/json' };
394
389
  }
@@ -459,10 +454,12 @@ export class REST extends AsyncEventEmitter<RestEvents> {
459
454
  // Hard-Code Old Message Deletion Exception (2 week+ old messages are a different bucket)
460
455
  // https://github.com/discord/discord-api-docs/issues/1295
461
456
  if (method === RequestMethod.Delete && baseRoute === '/channels/:id/messages/:id') {
462
- const id = /\d{17,19}$/.exec(endpoint)![0]!;
463
- const timestamp = DiscordSnowflake.timestampFrom(id);
464
- if (Date.now() - timestamp > 1_000 * 60 * 60 * 24 * 14) {
465
- exceptions += '/Delete Old Message';
457
+ const id = /\d{17,19}$/.exec(endpoint)?.[0];
458
+ if (id) {
459
+ const timestamp = DiscordSnowflake.timestampFrom(id);
460
+ if (Date.now() - timestamp > 1_000 * 60 * 60 * 24 * 14) {
461
+ exceptions += '/Delete Old Message';
462
+ }
466
463
  }
467
464
  }
468
465
 
@@ -77,7 +77,7 @@ export class DiscordAPIError extends Error {
77
77
  let flattened = '';
78
78
  if ('code' in error) {
79
79
  if (error.errors) {
80
- flattened = [...this.flattenDiscordError(error.errors)].join('\n');
80
+ flattened = [...DiscordAPIError.flattenDiscordError(error.errors)].join('\n');
81
81
  }
82
82
 
83
83
  return error.message && flattened
@@ -106,10 +106,10 @@ export class DiscordAPIError extends Error {
106
106
  yield val;
107
107
  } else if (isErrorGroupWrapper(val)) {
108
108
  for (const error of val._errors) {
109
- yield* this.flattenDiscordError(error, nextKey);
109
+ yield* DiscordAPIError.flattenDiscordError(error, nextKey);
110
110
  }
111
111
  } else {
112
- yield* this.flattenDiscordError(val, nextKey);
112
+ yield* DiscordAPIError.flattenDiscordError(val, nextKey);
113
113
  }
114
114
  }
115
115
  }
@@ -1,8 +1,7 @@
1
-
2
- import type { REST } from '../REST.js';
3
1
  import type { IHandler } from '../interfaces/Handler.js';
2
+ import type { REST } from '../REST.js';
4
3
  import { RESTEvents } from '../utils/constants.js';
5
- import type { ResponseLike, HandlerRequestData, RouteData, RateLimitData } from '../utils/types.js';
4
+ import type { HandlerRequestData, RateLimitData, ResponseLike, RouteData } from '../utils/types.js';
6
5
  import { normalizeRateLimitOffset, onRateLimit, sleep } from '../utils/utils.js';
7
6
  import { handleErrors, incrementInvalidCount, makeNetworkRequest } from './Shared.js';
8
7
 
@@ -1,12 +1,12 @@
1
- import { AsyncQueue } from '../utils/AsyncQueue.js';
2
- import type { REST } from '../REST.js';
3
1
  import type { IHandler } from '../interfaces/Handler.js';
2
+ import type { REST } from '../REST.js';
3
+ import { AsyncQueue } from '../utils/AsyncQueue.js';
4
4
  import { RESTEvents } from '../utils/constants.js';
5
- import type { RateLimitData, ResponseLike, HandlerRequestData, RouteData } from '../utils/types.js';
5
+ import type { HandlerRequestData, RateLimitData, ResponseLike, RouteData } from '../utils/types.js';
6
6
  import { hasSublimit, normalizeRateLimitOffset, onRateLimit, sleep } from '../utils/utils.js';
7
7
  import { handleErrors, incrementInvalidCount, makeNetworkRequest } from './Shared.js';
8
8
 
9
- const enum QueueType {
9
+ enum QueueType {
10
10
  Standard,
11
11
  Sublimit,
12
12
  }
@@ -1,11 +1,9 @@
1
-
2
-
3
- import type { REST } from '../REST.js';
4
1
  import type { DiscordErrorData, OAuthErrorData } from '../errors/DiscordAPIError.js';
5
2
  import { DiscordAPIError } from '../errors/DiscordAPIError.js';
6
3
  import { HTTPError } from '../errors/HTTPError.js';
4
+ import type { REST } from '../REST.js';
7
5
  import { RESTEvents } from '../utils/constants.js';
8
- import type { ResponseLike, HandlerRequestData, RouteData } from '../utils/types.js';
6
+ import type { HandlerRequestData, ResponseLike, RouteData } from '../utils/types.js';
9
7
  import { normalizeRetryBackoff, normalizeTimeout, parseResponse, shouldRetry, sleep } from '../utils/utils.js';
10
8
 
11
9
  let authFalseWarningEmitted = false;
@@ -1,5 +1,4 @@
1
-
2
- import type { HandlerRequestData, RouteData, ResponseLike } from '../utils/types.js';
1
+ import type { HandlerRequestData, ResponseLike, RouteData } from '../utils/types.js';
3
2
 
4
3
  export interface IHandler {
5
4
  /**
@@ -1,2 +1 @@
1
1
  export { AsyncEventEmitter } from '@ovencord/util';
2
-
@@ -17,7 +17,7 @@ export class AsyncQueue {
17
17
  * @param options.signal - An optional abort signal
18
18
  */
19
19
  public wait(options?: { signal?: AbortSignal }): Promise<void> {
20
- const next = this.#promises.length ? this.#promises.at(-1)!.promise : Promise.resolve();
20
+ const next = this.#promises.length ? this.#promises.at(-1)?.promise : Promise.resolve();
21
21
 
22
22
  let resolve!: () => void;
23
23
  const promise = new Promise<void>((res) => {
@@ -26,32 +26,32 @@ export class AsyncQueue {
26
26
 
27
27
  this.#promises.push({ promise, resolve });
28
28
 
29
- // If no signal, just return the promise we wait on
30
- if (!options?.signal) {
31
- return next;
32
- }
29
+ // If no signal, just return the promise we wait on
30
+ if (!options?.signal) {
31
+ return next;
32
+ }
33
33
 
34
- return new Promise((res, rej) => {
35
- if (options.signal!.aborted) {
36
- // Bridge immediately: when next resolves, we resolve our token
37
- next.then(() => resolve());
38
- rej(new Error('AbortError')); // TODO: Use DOMException or standard AbortError if available
39
- return;
40
- }
34
+ return new Promise((res, rej) => {
35
+ if (options.signal?.aborted) {
36
+ // Bridge immediately: when next resolves, we resolve our token
37
+ next.then(() => resolve());
38
+ rej(new Error('AbortError')); // TODO: Use DOMException or standard AbortError if available
39
+ return;
40
+ }
41
41
 
42
- const abortHandler = () => {
43
- // Bridge: when next resolves, we resolve our token
44
- next.then(() => resolve());
45
- rej(new Error('AbortError'));
46
- };
42
+ const abortHandler = () => {
43
+ // Bridge: when next resolves, we resolve our token
44
+ next.then(() => resolve());
45
+ rej(new Error('AbortError'));
46
+ };
47
47
 
48
- options.signal!.addEventListener('abort', abortHandler, { once: true });
48
+ options.signal?.addEventListener('abort', abortHandler, { once: true });
49
49
 
50
- next.then(() => {
51
- options.signal!.removeEventListener('abort', abortHandler);
52
- res();
53
- });
54
- });
50
+ next.then(() => {
51
+ options.signal?.removeEventListener('abort', abortHandler);
52
+ res();
53
+ });
54
+ });
55
55
  }
56
56
 
57
57
  /**
@@ -15,7 +15,7 @@ export const DefaultUserAgent =
15
15
  export const DefaultUserAgentAppendix = getUserAgentAppendix();
16
16
 
17
17
  export const DefaultRestOptions = {
18
- // @ts-ignore
18
+ // @ts-expect-error
19
19
  agent: null,
20
20
  api: 'https://discord.com/api',
21
21
  authPrefix: 'Bot',
@@ -24,7 +24,7 @@ export const DefaultRestOptions = {
24
24
  invalidRequestWarningInterval: 0,
25
25
  globalRequestsPerSecond: 50,
26
26
  offset: 50,
27
- // @ts-ignore
27
+ // @ts-expect-error
28
28
  rejectOnRateLimit: null,
29
29
  retries: 3,
30
30
  retryBackoff: 0,
@@ -61,7 +61,6 @@ export const ALLOWED_SIZES: readonly number[] = [
61
61
  export type ImageExtension = (typeof ALLOWED_EXTENSIONS)[number];
62
62
  export type StickerExtension = (typeof ALLOWED_STICKER_EXTENSIONS)[number];
63
63
 
64
-
65
64
  export const BurstHandlerMajorIdKey = 'burst';
66
65
 
67
66
  export const AUTH_UUID_NAMESPACE = 'acc82a4c-f887-417b-a69c-f74096ff7e59';
@@ -260,10 +260,11 @@ export interface APIRequest {
260
260
  route: string;
261
261
  }
262
262
 
263
- export interface ResponseLike extends Pick<
264
- globalThis.Response,
265
- 'arrayBuffer' | 'bodyUsed' | 'headers' | 'json' | 'ok' | 'status' | 'statusText' | 'text'
266
- > {
263
+ export interface ResponseLike
264
+ extends Pick<
265
+ globalThis.Response,
266
+ 'arrayBuffer' | 'bodyUsed' | 'headers' | 'json' | 'ok' | 'status' | 'statusText' | 'text'
267
+ > {
267
268
  body: ReadableStream | null;
268
269
  }
269
270
 
@@ -1,8 +1,6 @@
1
-
2
1
  import type { RESTPatchAPIChannelJSONBody, Snowflake } from 'discord-api-types/v10';
3
- import type { REST } from '../REST.js';
4
2
  import { RateLimitError } from '../errors/RateLimitError.js';
5
- import { RequestMethod } from './types.js';
3
+ import type { REST } from '../REST.js';
6
4
  import type {
7
5
  GetRateLimitOffsetFunction,
8
6
  GetRetryBackoffFunction,
@@ -10,6 +8,7 @@ import type {
10
8
  RateLimitData,
11
9
  ResponseLike,
12
10
  } from './types.js';
11
+ import { RequestMethod } from './types.js';
13
12
 
14
13
  function serializeSearchParam(value: unknown): string | null {
15
14
  switch (typeof value) {
@@ -203,44 +202,44 @@ export function normalizeTimeout(timeout: GetTimeoutFunction | number, route: st
203
202
  * @param namespace - The namespace UUID
204
203
  */
205
204
  export function uuidv5(value: string | Uint8Array, namespace: string): string {
206
- // 1. Verify namespace is a valid UUID
207
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(namespace)) {
208
- throw new TypeError('Invalid namespace UUID');
209
- }
210
-
211
- // 2. Parse namespace UUID into bytes
212
- const namespaceBytes = new Uint8Array(16);
213
- const hex = namespace.replace(/-/g, '');
214
- for (let i = 0; i < 16; i++) {
215
- namespaceBytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
216
- }
217
-
218
- // 3. Convert value to bytes if string
219
- const valueBytes = typeof value === 'string' ? new TextEncoder().encode(value) : value;
220
-
221
- // 4. Concatenate namespace and value
222
- const data = new Uint8Array(namespaceBytes.length + valueBytes.length);
223
- data.set(namespaceBytes);
224
- data.set(valueBytes, namespaceBytes.length);
225
-
226
- // 5. Hash with SHA-1
227
- const buffer = new Bun.CryptoHasher('sha1').update(data).digest();
228
- const hash = new Uint8Array(buffer);
229
-
230
- // 6. Set version to 5 (0101)
231
- hash[6] = (hash[6]! & 0x0f) | 0x50;
232
-
233
- // 7. Set variant to RFC 4122 (10xx)
234
- hash[8] = (hash[8]! & 0x3f) | 0x80;
235
-
236
- // 8. Convert to hex string with dashes
237
- const hexHash = Array.from(hash, (byte) => byte.toString(16).padStart(2, '0'));
238
-
239
- return [
240
- hexHash.slice(0, 4).join(''),
241
- hexHash.slice(4, 6).join(''),
242
- hexHash.slice(6, 8).join(''),
243
- hexHash.slice(8, 10).join(''),
244
- hexHash.slice(10, 16).join('')
245
- ].join('-');
205
+ // 1. Verify namespace is a valid UUID
206
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(namespace)) {
207
+ throw new TypeError('Invalid namespace UUID');
208
+ }
209
+
210
+ // 2. Parse namespace UUID into bytes
211
+ const namespaceBytes = new Uint8Array(16);
212
+ const hex = namespace.replace(/-/g, '');
213
+ for (let i = 0; i < 16; i++) {
214
+ namespaceBytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
215
+ }
216
+
217
+ // 3. Convert value to bytes if string
218
+ const valueBytes = typeof value === 'string' ? new TextEncoder().encode(value) : value;
219
+
220
+ // 4. Concatenate namespace and value
221
+ const data = new Uint8Array(namespaceBytes.length + valueBytes.length);
222
+ data.set(namespaceBytes);
223
+ data.set(valueBytes, namespaceBytes.length);
224
+
225
+ // 5. Hash with SHA-1
226
+ const buffer = new Bun.CryptoHasher('sha1').update(data).digest();
227
+ const hash = new Uint8Array(buffer);
228
+
229
+ // 6. Set version to 5 (0101)
230
+ hash[6] = (hash[6]! & 0x0f) | 0x50;
231
+
232
+ // 7. Set variant to RFC 4122 (10xx)
233
+ hash[8] = (hash[8]! & 0x3f) | 0x80;
234
+
235
+ // 8. Convert to hex string with dashes
236
+ const hexHash = Array.from(hash, (byte) => byte.toString(16).padStart(2, '0'));
237
+
238
+ return [
239
+ hexHash.slice(0, 4).join(''),
240
+ hexHash.slice(4, 6).join(''),
241
+ hexHash.slice(6, 8).join(''),
242
+ hexHash.slice(8, 10).join(''),
243
+ hexHash.slice(10, 16).join(''),
244
+ ].join('-');
246
245
  }