Skip to content

Commit

Permalink
feat: add support for using keyword on discord.js Client and `Web…
Browse files Browse the repository at this point in the history
…SocketManager` (#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>
  • Loading branch information
suneettipirneni and kodiakhq[bot] authored Feb 13, 2024
1 parent f48cb2a commit 543d617
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 3 deletions.
4 changes: 4 additions & 0 deletions packages/discord.js/src/client/BaseClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ class BaseClient extends EventEmitter {
toJSON(...props) {
return flatten(this, ...props);
}

async [Symbol.asyncDispose]() {
await this.destroy();
}
}

module.exports = BaseClient;
Expand Down
3 changes: 3 additions & 0 deletions packages/discord.js/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
'use strict';

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

polyfillDispose();

// "Root" classes (starting points)
exports.BaseClient = require('./client/BaseClient');
exports.Client = require('./client/Client');
Expand Down
3 changes: 2 additions & 1 deletion packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ export abstract class Base {
public valueOf(): string;
}

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

export type GuildCacheMessage<Cached extends CacheType> = CacheTypeReducer<
Expand Down
1 change: 1 addition & 0 deletions packages/util/src/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './range.js';
export * from './calculateShardId.js';
export * from './runtime.js';
export * from './userAgentAppendix.js';
export * from './polyfillDispose.js';
14 changes: 14 additions & 0 deletions packages/util/src/functions/polyfillDispose.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Polyfill for `Symbol.dispose` and `Symbol.asyncDispose` which is used as a part of
* {@link https://github.com/tc39/proposal-explicit-resource-management}. Node versions below 18.x
* don't have these symbols by default, so we need to polyfill them.
*/
export function polyfillDispose() {
// Polyfill for `Symbol.dispose` and `Symbol.asyncDispose` if not available.
// Taken from https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management

// @ts-expect-error This is a polyfill, so it's fine to write
Symbol.dispose ??= Symbol('Symbol.dispose');
// @ts-expect-error Same as above
Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose');
}
10 changes: 9 additions & 1 deletion packages/ws/src/ws/WebSocketManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { REST } from '@discordjs/rest';
import { range, type Awaitable } from '@discordjs/util';
import { polyfillDispose } from '@discordjs/util';
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
import {
Routes,
Expand All @@ -17,6 +18,9 @@ import type { IIdentifyThrottler } from '../throttling/IIdentifyThrottler.js';
import { DefaultWebSocketManagerOptions, type CompressionMethod, type Encoding } from '../utils/constants.js';
import type { WebSocketShardDestroyOptions, WebSocketShardEvents } from './WebSocketShard.js';

// We put this here because in index.ts WebSocketManager seems to be outputted before polyfillDispose() is called from tsup.
polyfillDispose();

/**
* Represents a range of shard ids
*/
Expand Down Expand Up @@ -199,7 +203,7 @@ export interface ManagerShardEventsMap {
];
}

export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> implements AsyncDisposable {
/**
* The options being used by this manager
*/
Expand Down Expand Up @@ -334,4 +338,8 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
public fetchStatus() {
return this.strategy.fetchStatus();
}

public async [Symbol.asyncDispose]() {
await this.destroy();
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

// Language and Environment
"experimentalDecorators": true,
"lib": ["ESNext"],
"lib": ["ESNext", "esnext.disposable"],
"target": "ESNext",
"useDefineForClassFields": true
},
Expand Down

0 comments on commit 543d617

Please sign in to comment.