Skip to content

Commit 543d617

Browse files
feat: add support for using keyword on discord.js Client and WebSocketManager (#10063)
* feat: add support for `using` keyword on client * fix: use async dispose * feat: add support for web socket manager disposing * fix: use interface for client --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent f48cb2a commit 543d617

File tree

7 files changed

+34
-3
lines changed

7 files changed

+34
-3
lines changed

packages/discord.js/src/client/BaseClient.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ class BaseClient extends EventEmitter {
106106
toJSON(...props) {
107107
return flatten(this, ...props);
108108
}
109+
110+
async [Symbol.asyncDispose]() {
111+
await this.destroy();
112+
}
109113
}
110114

111115
module.exports = BaseClient;

packages/discord.js/src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
'use strict';
22

3+
const { polyfillDispose } = require('@discordjs/util');
34
const { __exportStar } = require('tslib');
45

6+
polyfillDispose();
7+
58
// "Root" classes (starting points)
69
exports.BaseClient = require('./client/BaseClient');
710
exports.Client = require('./client/Client');

packages/discord.js/typings/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ export abstract class Base {
517517
public valueOf(): string;
518518
}
519519

520-
export class BaseClient extends EventEmitter {
520+
export class BaseClient extends EventEmitter implements AsyncDisposable {
521521
public constructor(options?: ClientOptions | WebhookClientOptions);
522522
private decrementMaxListeners(): void;
523523
private incrementMaxListeners(): void;
@@ -526,6 +526,7 @@ export class BaseClient extends EventEmitter {
526526
public rest: REST;
527527
public destroy(): void;
528528
public toJSON(...props: Record<string, boolean | string>[]): unknown;
529+
public [Symbol.asyncDispose](): Promise<void>;
529530
}
530531

531532
export type GuildCacheMessage<Cached extends CacheType> = CacheTypeReducer<

packages/util/src/functions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './range.js';
33
export * from './calculateShardId.js';
44
export * from './runtime.js';
55
export * from './userAgentAppendix.js';
6+
export * from './polyfillDispose.js';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Polyfill for `Symbol.dispose` and `Symbol.asyncDispose` which is used as a part of
3+
* {@link https://github.com/tc39/proposal-explicit-resource-management}. Node versions below 18.x
4+
* don't have these symbols by default, so we need to polyfill them.
5+
*/
6+
export function polyfillDispose() {
7+
// Polyfill for `Symbol.dispose` and `Symbol.asyncDispose` if not available.
8+
// Taken from https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management
9+
10+
// @ts-expect-error This is a polyfill, so it's fine to write
11+
Symbol.dispose ??= Symbol('Symbol.dispose');
12+
// @ts-expect-error Same as above
13+
Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose');
14+
}

packages/ws/src/ws/WebSocketManager.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { REST } from '@discordjs/rest';
22
import { range, type Awaitable } from '@discordjs/util';
3+
import { polyfillDispose } from '@discordjs/util';
34
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
45
import {
56
Routes,
@@ -17,6 +18,9 @@ import type { IIdentifyThrottler } from '../throttling/IIdentifyThrottler.js';
1718
import { DefaultWebSocketManagerOptions, type CompressionMethod, type Encoding } from '../utils/constants.js';
1819
import type { WebSocketShardDestroyOptions, WebSocketShardEvents } from './WebSocketShard.js';
1920

21+
// We put this here because in index.ts WebSocketManager seems to be outputted before polyfillDispose() is called from tsup.
22+
polyfillDispose();
23+
2024
/**
2125
* Represents a range of shard ids
2226
*/
@@ -199,7 +203,7 @@ export interface ManagerShardEventsMap {
199203
];
200204
}
201205

202-
export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
206+
export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> implements AsyncDisposable {
203207
/**
204208
* The options being used by this manager
205209
*/
@@ -334,4 +338,8 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
334338
public fetchStatus() {
335339
return this.strategy.fetchStatus();
336340
}
341+
342+
public async [Symbol.asyncDispose]() {
343+
await this.destroy();
344+
}
337345
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
// Language and Environment
4444
"experimentalDecorators": true,
45-
"lib": ["ESNext"],
45+
"lib": ["ESNext", "esnext.disposable"],
4646
"target": "ESNext",
4747
"useDefineForClassFields": true
4848
},

0 commit comments

Comments
 (0)