refactor(WebSocketManager)!: remove deprecated rest option (#10998)

BREAKING CHANGE: The `rest` option in the `WebSocketManager` constructor has been removed. Pass a `fetchGatewayInformation` function instead.
This commit is contained in:
Almeida
2025-07-27 11:49:29 +01:00
committed by GitHub
parent 2e9bfba5f4
commit 08a61ca8e3
9 changed files with 98 additions and 281 deletions

View File

@@ -37,9 +37,16 @@ pnpm add @discordjs/core
These examples use [ES modules](https://nodejs.org/api/esm.html#enabling). These examples use [ES modules](https://nodejs.org/api/esm.html#enabling).
```ts ```ts
import {
Client,
GatewayDispatchEvents,
GatewayIntentBits,
InteractionType,
MessageFlags,
type RESTGetAPIGatewayBotResult,
} from '@discordjs/core';
import { REST } from '@discordjs/rest'; import { REST } from '@discordjs/rest';
import { WebSocketManager } from '@discordjs/ws'; import { WebSocketManager } from '@discordjs/ws';
import { GatewayDispatchEvents, GatewayIntentBits, InteractionType, MessageFlags, Client } from '@discordjs/core';
// Create REST and WebSocket managers directly // Create REST and WebSocket managers directly
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN); const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
@@ -47,7 +54,7 @@ const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
const gateway = new WebSocketManager({ const gateway = new WebSocketManager({
token: process.env.DISCORD_TOKEN, token: process.env.DISCORD_TOKEN,
intents: GatewayIntentBits.GuildMessages | GatewayIntentBits.MessageContent, intents: GatewayIntentBits.GuildMessages | GatewayIntentBits.MessageContent,
rest, fetchGatewayInformation: () => rest.get('/gateway/bot') as Promise<RESTGetAPIGatewayBotResult>,
}); });
// Create a client to emit relevant events. // Create a client to emit relevant events.

View File

@@ -163,7 +163,7 @@ class Client extends BaseClient {
const wsOptions = { const wsOptions = {
...this.options.ws, ...this.options.ws,
intents: this.options.intents.bitfield, intents: this.options.intents.bitfield,
rest: this.rest, fetchGatewayInformation: () => this.rest.get(Routes.gatewayBot()),
// Explicitly nulled to always be set using `setToken` in `login` // Explicitly nulled to always be set using `setToken` in `login`
token: null, token: null,
}; };

View File

@@ -0,0 +1,12 @@
import type { RESTGetAPIGatewayBotResult } from 'discord-api-types/v10';
export const mockGatewayInformation: RESTGetAPIGatewayBotResult = {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
};

View File

@@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/consistent-type-imports */ /* eslint-disable @typescript-eslint/consistent-type-imports */
// @ts-nocheck // @ts-nocheck
import { REST } from '@discordjs/rest';
import { MockAgent, type Interceptable } from 'undici';
import { beforeEach, test, vi, expect } from 'vitest'; import { beforeEach, test, vi, expect } from 'vitest';
import { import {
managerToFetchingStrategyOptions, managerToFetchingStrategyOptions,
@@ -12,15 +10,7 @@ import {
type WorkerReceivePayload, type WorkerReceivePayload,
type WorkerSendPayload, type WorkerSendPayload,
} from '../../src/index.js'; } from '../../src/index.js';
import { mockGatewayInformation } from '../gateway.mock.js';
let mockAgent: MockAgent;
let mockPool: Interceptable;
beforeEach(() => {
mockAgent = new MockAgent();
mockAgent.disableNetConnect();
mockPool = mockAgent.get('https://discord.com');
});
const session = { const session = {
shardId: 0, shardId: 0,
@@ -52,32 +42,13 @@ vi.mock('node:worker_threads', async () => {
}); });
test('session info', async () => { test('session info', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token'); const manager = new WebSocketManager({
const manager = new WebSocketManager({ token: 'A-Very-Fake-Token', intents: 0, rest }); token: 'A-Very-Fake-Token',
intents: 0,
mockPool async fetchGatewayInformation() {
.intercept({ return mockGatewayInformation;
path: '/api/v10/gateway/bot', },
method: 'GET', });
})
.reply(() => ({
data: {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
},
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
const strategy = new WorkerContextFetchingStrategy(await managerToFetchingStrategyOptions(manager)); const strategy = new WorkerContextFetchingStrategy(await managerToFetchingStrategyOptions(manager));

View File

@@ -1,10 +1,8 @@
/* eslint-disable id-length */ /* eslint-disable id-length */
import { setImmediate } from 'node:timers'; import { setImmediate } from 'node:timers';
import { REST } from '@discordjs/rest'; import type { GatewayDispatchPayload, GatewaySendPayload } from 'discord-api-types/v10';
import type { RESTGetAPIGatewayBotResult, GatewayDispatchPayload, GatewaySendPayload } from 'discord-api-types/v10'; import { GatewayDispatchEvents, GatewayOpcodes } from 'discord-api-types/v10';
import { GatewayDispatchEvents, GatewayOpcodes, Routes } from 'discord-api-types/v10'; import { test, vi, expect, afterEach } from 'vitest';
import { MockAgent, type Interceptable } from 'undici';
import { beforeEach, test, vi, expect, afterEach } from 'vitest';
import { import {
WebSocketManager, WebSocketManager,
WorkerSendPayloadOp, WorkerSendPayloadOp,
@@ -15,9 +13,7 @@ import {
type WorkerSendPayload, type WorkerSendPayload,
type SessionInfo, type SessionInfo,
} from '../../src/index.js'; } from '../../src/index.js';
import { mockGatewayInformation } from '../gateway.mock.js';
let mockAgent: MockAgent;
let mockPool: Interceptable;
const mockConstructor = vi.fn(); const mockConstructor = vi.fn();
const mockSend = vi.fn(); const mockSend = vi.fn();
@@ -135,12 +131,6 @@ vi.mock('node:worker_threads', async () => {
}; };
}); });
beforeEach(() => {
mockAgent = new MockAgent();
mockAgent.disableNetConnect();
mockPool = mockAgent.get('https://discord.com');
});
afterEach(() => { afterEach(() => {
mockConstructor.mockClear(); mockConstructor.mockClear();
mockSend.mockClear(); mockSend.mockClear();
@@ -148,15 +138,13 @@ afterEach(() => {
}); });
test('spawn, connect, send a message, session info, and destroy', async () => { test('spawn, connect, send a message, session info, and destroy', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const mockRetrieveSessionInfo = vi.fn(); const mockRetrieveSessionInfo = vi.fn();
const mockUpdateSessionInfo = vi.fn(); const mockUpdateSessionInfo = vi.fn();
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
intents: 0, intents: 0,
async fetchGatewayInformation() { async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>; return mockGatewayInformation;
}, },
shardIds: [0, 1], shardIds: [0, 1],
retrieveSessionInfo: mockRetrieveSessionInfo, retrieveSessionInfo: mockRetrieveSessionInfo,
@@ -166,30 +154,6 @@ test('spawn, connect, send a message, session info, and destroy', async () => {
const managerEmitSpy = vi.spyOn(manager, 'emit'); const managerEmitSpy = vi.spyOn(manager, 'emit');
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(() => ({
data: {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
},
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
await manager.connect(); await manager.connect();
expect(mockConstructor).toHaveBeenCalledWith( expect(mockConstructor).toHaveBeenCalledWith(
expect.stringContaining('defaultWorker.js'), expect.stringContaining('defaultWorker.js'),

View File

@@ -1,107 +1,55 @@
import { REST } from '@discordjs/rest'; import type { GatewaySendPayload } from 'discord-api-types/v10';
import type { RESTGetAPIGatewayBotResult, APIGatewayBotInfo, GatewaySendPayload } from 'discord-api-types/v10'; import { GatewayOpcodes } from 'discord-api-types/v10';
import { GatewayOpcodes, Routes } from 'discord-api-types/v10'; import { describe, expect, test, vi } from 'vitest';
import { MockAgent, type Interceptable } from 'undici';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { WebSocketManager, type IShardingStrategy } from '../../src/index.js'; import { WebSocketManager, type IShardingStrategy } from '../../src/index.js';
import { mockGatewayInformation } from '../gateway.mock.js';
vi.useFakeTimers(); vi.useFakeTimers();
let mockAgent: MockAgent;
let mockPool: Interceptable;
beforeEach(() => {
mockAgent = new MockAgent();
mockAgent.disableNetConnect();
mockPool = mockAgent.get('https://discord.com');
});
const NOW = vi.fn().mockReturnValue(Date.now()); const NOW = vi.fn().mockReturnValue(Date.now());
global.Date.now = NOW; global.Date.now = NOW;
test('fetch gateway information', async () => { test('fetch gateway information', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token'); const fetchGatewayInformation = vi.fn(async () => mockGatewayInformation);
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
intents: 0, intents: 0,
async fetchGatewayInformation() { fetchGatewayInformation,
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
}); });
const data: APIGatewayBotInfo = {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
};
const fetch = vi.fn(() => ({
data,
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
const initial = await manager.fetchGatewayInformation(); const initial = await manager.fetchGatewayInformation();
expect(initial).toEqual(data); expect(initial).toEqual(mockGatewayInformation);
expect(fetch).toHaveBeenCalledOnce(); expect(fetchGatewayInformation).toHaveBeenCalledOnce();
fetch.mockClear(); fetchGatewayInformation.mockClear();
const cached = await manager.fetchGatewayInformation(); const cached = await manager.fetchGatewayInformation();
expect(cached).toEqual(data); expect(cached).toEqual(mockGatewayInformation);
expect(fetch).not.toHaveBeenCalled(); expect(fetchGatewayInformation).not.toHaveBeenCalled();
fetch.mockClear(); fetchGatewayInformation.mockClear();
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
const forced = await manager.fetchGatewayInformation(true); const forced = await manager.fetchGatewayInformation(true);
expect(forced).toEqual(data); expect(forced).toEqual(mockGatewayInformation);
expect(fetch).toHaveBeenCalledOnce(); expect(fetchGatewayInformation).toHaveBeenCalledOnce();
fetch.mockClear(); fetchGatewayInformation.mockClear();
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
NOW.mockReturnValue(Number.POSITIVE_INFINITY); NOW.mockReturnValue(Number.POSITIVE_INFINITY);
const cacheExpired = await manager.fetchGatewayInformation(); const cacheExpired = await manager.fetchGatewayInformation();
expect(cacheExpired).toEqual(data); expect(cacheExpired).toEqual(mockGatewayInformation);
expect(fetch).toHaveBeenCalledOnce(); expect(fetchGatewayInformation).toHaveBeenCalledOnce();
}); });
describe('get shard count', () => { describe('get shard count', () => {
test('with shard count', async () => { test('with shard count', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
intents: 0, intents: 0,
shardCount: 2, shardCount: 2,
async fetchGatewayInformation() { async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>; return mockGatewayInformation;
}, },
}); });
@@ -109,14 +57,13 @@ describe('get shard count', () => {
}); });
test('with shard ids array', async () => { test('with shard ids array', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = [5, 9]; const shardIds = [5, 9];
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
intents: 0, intents: 0,
shardIds, shardIds,
async fetchGatewayInformation() { async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>; return mockGatewayInformation;
}, },
}); });
@@ -124,14 +71,13 @@ describe('get shard count', () => {
}); });
test('with shard id range', async () => { test('with shard id range', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = { start: 5, end: 9 }; const shardIds = { start: 5, end: 9 };
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
intents: 0, intents: 0,
shardIds, shardIds,
async fetchGatewayInformation() { async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>; return mockGatewayInformation;
}, },
}); });
@@ -140,62 +86,26 @@ describe('get shard count', () => {
}); });
test('update shard count', async () => { test('update shard count', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token'); const fetchGatewayInformation = vi.fn(async () => mockGatewayInformation);
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
intents: 0, intents: 0,
shardCount: 2, shardCount: 2,
async fetchGatewayInformation() { fetchGatewayInformation,
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
}); });
const data: APIGatewayBotInfo = {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
};
const fetch = vi.fn(() => ({
data,
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
expect(await manager.getShardCount()).toBe(2); expect(await manager.getShardCount()).toBe(2);
expect(fetch).not.toHaveBeenCalled(); expect(fetchGatewayInformation).not.toHaveBeenCalled();
fetch.mockClear(); fetchGatewayInformation.mockClear();
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
await manager.updateShardCount(3); await manager.updateShardCount(3);
expect(await manager.getShardCount()).toBe(3); expect(await manager.getShardCount()).toBe(3);
expect(fetch).toHaveBeenCalled(); expect(fetchGatewayInformation).toHaveBeenCalled();
}); });
test('it handles passing in both shardIds and shardCount', async () => { test('it handles passing in both shardIds and shardCount', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = { start: 2, end: 3 }; const shardIds = { start: 2, end: 3 };
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
@@ -203,7 +113,7 @@ test('it handles passing in both shardIds and shardCount', async () => {
shardIds, shardIds,
shardCount: 4, shardCount: 4,
async fetchGatewayInformation() { async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>; return mockGatewayInformation;
}, },
}); });
@@ -226,44 +136,18 @@ test('strategies', async () => {
const strategy = new MockStrategy(); const strategy = new MockStrategy();
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = [0, 1, 2]; const shardIds = [0, 1, 2];
const manager = new WebSocketManager({ const manager = new WebSocketManager({
token: 'A-Very-Fake-Token', token: 'A-Very-Fake-Token',
intents: 0, intents: 0,
rest,
shardIds, shardIds,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
buildStrategy: () => strategy, buildStrategy: () => strategy,
}); });
const data: APIGatewayBotInfo = {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
};
const fetch = vi.fn(() => ({
data,
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
await manager.connect(); await manager.connect();
expect(strategy.spawn).toHaveBeenCalledWith(shardIds); expect(strategy.spawn).toHaveBeenCalledWith(shardIds);
expect(strategy.connect).toHaveBeenCalled(); expect(strategy.connect).toHaveBeenCalled();

View File

@@ -74,7 +74,6 @@
"funding": "https://github.com/discordjs/discord.js?sponsor", "funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": { "dependencies": {
"@discordjs/collection": "workspace:^", "@discordjs/collection": "workspace:^",
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^", "@discordjs/util": "workspace:^",
"@sapphire/async-queue": "^1.5.5", "@sapphire/async-queue": "^1.5.5",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
@@ -100,7 +99,6 @@
"tsup": "^8.5.0", "tsup": "^8.5.0",
"turbo": "^2.5.4", "turbo": "^2.5.4",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"undici": "7.11.0",
"vitest": "^3.2.4", "vitest": "^3.2.4",
"zlib-sync": "^0.1.10" "zlib-sync": "^0.1.10"
}, },

View File

@@ -1,17 +1,15 @@
import type { Collection } from '@discordjs/collection'; import type { Collection } from '@discordjs/collection';
import type { REST } from '@discordjs/rest';
import { range, type Awaitable } from '@discordjs/util'; import { range, type Awaitable } from '@discordjs/util';
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter'; import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
import { import type {
Routes, APIGatewayBotInfo,
type APIGatewayBotInfo, GatewayIdentifyProperties,
type GatewayIdentifyProperties, GatewayPresenceUpdateData,
type GatewayPresenceUpdateData, RESTGetAPIGatewayBotResult,
type RESTGetAPIGatewayBotResult, GatewayIntentBits,
type GatewayIntentBits, GatewaySendPayload,
type GatewaySendPayload, GatewayDispatchPayload,
type GatewayDispatchPayload, GatewayReadyDispatchData,
type GatewayReadyDispatchData,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import type { IShardingStrategy } from '../strategies/sharding/IShardingStrategy.js'; import type { IShardingStrategy } from '../strategies/sharding/IShardingStrategy.js';
import type { IIdentifyThrottler } from '../throttling/IIdentifyThrottler.js'; import type { IIdentifyThrottler } from '../throttling/IIdentifyThrottler.js';
@@ -56,6 +54,22 @@ export interface SessionInfo {
* Required options for the WebSocketManager * Required options for the WebSocketManager
*/ */
export interface RequiredWebSocketManagerOptions { export interface RequiredWebSocketManagerOptions {
/**
* Function for retrieving the information returned by the `/gateway/bot` endpoint.
* We recommend using a REST client that respects Discord's rate limits, such as `@discordjs/rest`.
*
* @example
* ```ts
* const rest = new REST().setToken(process.env.DISCORD_TOKEN);
* const manager = new WebSocketManager({
* token: process.env.DISCORD_TOKEN,
* fetchGatewayInformation() {
* return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
* },
* });
* ```
*/
fetchGatewayInformation(): Awaitable<RESTGetAPIGatewayBotResult>;
/** /**
* The intents to request * The intents to request
*/ */
@@ -75,10 +89,13 @@ export interface OptionalWebSocketManagerOptions {
* *
* @example * @example
* ```ts * ```ts
* const rest = new REST().setToken(process.env.DISCORD_TOKEN);
* const manager = new WebSocketManager({ * const manager = new WebSocketManager({
* token: process.env.DISCORD_TOKEN, * token: process.env.DISCORD_TOKEN,
* intents: 0, // for no intents * intents: 0, // for no intents
* rest, * fetchGatewayInformation() {
* return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
* },
* buildStrategy: (manager) => new WorkerShardingStrategy(manager, { shardsPerWorker: 2 }), * buildStrategy: (manager) => new WorkerShardingStrategy(manager, { shardsPerWorker: 2 }),
* }); * });
* ``` * ```
@@ -96,21 +113,6 @@ export interface OptionalWebSocketManagerOptions {
* @defaultValue `'json'` * @defaultValue `'json'`
*/ */
encoding: Encoding; encoding: Encoding;
/**
* Fetches the initial gateway URL used to connect to Discord. When missing, this will default to the gateway URL
* that Discord returns from the `/gateway/bot` route.
*
* @example
* ```ts
* const manager = new WebSocketManager({
* token: process.env.DISCORD_TOKEN,
* fetchGatewayInformation() {
* return rest.get(Routes.gatewayBot());
* },
* })
* ```
*/
fetchGatewayInformation(): Awaitable<RESTGetAPIGatewayBotResult>;
/** /**
* How long to wait for a shard to connect before giving up * How long to wait for a shard to connect before giving up
*/ */
@@ -135,12 +137,6 @@ export interface OptionalWebSocketManagerOptions {
* How long to wait for a shard's READY packet before giving up * How long to wait for a shard's READY packet before giving up
*/ */
readyTimeout: number | null; readyTimeout: number | null;
/**
* The REST instance to use for fetching gateway information
*
* @deprecated Providing a REST instance is deprecated. Provide the `fetchGatewayInformation` function instead.
*/
rest?: REST;
/** /**
* Function used to retrieve session information (and attempt to resume) for a given shard * Function used to retrieve session information (and attempt to resume) for a given shard
* *
@@ -271,22 +267,13 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
} }
public constructor(options: CreateWebSocketManagerOptions) { public constructor(options: CreateWebSocketManagerOptions) {
if (!options.rest && !options.fetchGatewayInformation) { if (typeof options.fetchGatewayInformation !== 'function') {
throw new RangeError('Either a REST instance or a fetchGatewayInformation function must be provided'); throw new TypeError('fetchGatewayInformation is required');
} }
super(); super();
this.options = { this.options = {
...DefaultWebSocketManagerOptions, ...DefaultWebSocketManagerOptions,
fetchGatewayInformation:
options.fetchGatewayInformation ??
(async () => {
if (!options.rest) {
throw new RangeError('A REST instance must be provided if no fetchGatewayInformation function is provided');
}
return options.rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
}),
...options, ...options,
}; };
this.strategy = this.options.buildStrategy(this); this.strategy = this.options.buildStrategy(this);

6
pnpm-lock.yaml generated
View File

@@ -1861,9 +1861,6 @@ importers:
'@discordjs/collection': '@discordjs/collection':
specifier: workspace:^ specifier: workspace:^
version: link:../collection version: link:../collection
'@discordjs/rest':
specifier: workspace:^
version: link:../rest
'@discordjs/util': '@discordjs/util':
specifier: workspace:^ specifier: workspace:^
version: link:../util version: link:../util
@@ -1934,9 +1931,6 @@ importers:
typescript: typescript:
specifier: ~5.8.3 specifier: ~5.8.3
version: 5.8.3 version: 5.8.3
undici:
specifier: 7.11.0
version: 7.11.0
vitest: vitest:
specifier: ^3.2.4 specifier: ^3.2.4
version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@22.16.3)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@22.16.3)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)