feat: fetch gateway information without requiring rest in ws (#10651)

* feat: overridable initial gateway URL

* chore: discussion changes

* chore: requested change

* chore: other changes

* Update packages/ws/src/ws/WebSocketManager.ts

* style: run ESLint

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Vlad Frangu
2025-01-13 12:23:41 +02:00
committed by GitHub
parent 1e29bb4049
commit 5f7d335290
5 changed files with 114 additions and 27 deletions

View File

@@ -68,7 +68,7 @@ export const DefaultWebSocketManagerOptions = {
handshakeTimeout: 30_000,
helloTimeout: 60_000,
readyTimeout: 15_000,
} as const satisfies Omit<OptionalWebSocketManagerOptions, 'token'>;
} as const satisfies Omit<OptionalWebSocketManagerOptions, 'fetchGatewayInformation' | 'token'>;
export const ImportantGatewayOpcodes = new Set([
GatewayOpcodes.Heartbeat,

View File

@@ -63,10 +63,6 @@ export interface RequiredWebSocketManagerOptions {
* The intents to request
*/
intents: GatewayIntentBits | 0;
/**
* The REST instance to use for fetching gateway information
*/
rest: REST;
}
/**
@@ -103,6 +99,21 @@ export interface OptionalWebSocketManagerOptions {
* @defaultValue `'json'`
*/
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
*/
@@ -127,6 +138,12 @@ export interface OptionalWebSocketManagerOptions {
* How long to wait for a shard's READY packet before giving up
*/
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
*
@@ -257,8 +274,24 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
}
public constructor(options: CreateWebSocketManagerOptions) {
if (!options.rest && !options.fetchGatewayInformation) {
throw new RangeError('Either a REST instance or a fetchGatewayInformation function must be provided');
}
super();
this.options = { ...DefaultWebSocketManagerOptions, ...options };
this.options = {
...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,
};
this.strategy = this.options.buildStrategy(this);
this.#token = options.token ?? null;
}
@@ -277,7 +310,7 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
}
}
const data = (await this.options.rest.get(Routes.gatewayBot())) as RESTGetAPIGatewayBotResult;
const data = await this.options.fetchGatewayInformation();
// For single sharded bots session_start_limit.reset_after will be 0, use 5 seconds as a minimum expiration time
this.gatewayInformation = { data, expiresAt: Date.now() + (data.session_start_limit.reset_after || 5_000) };