Skip to content

Commit 7349a6e

Browse files
authored
refactor(client)!: remove BaseClient (#11274)
BREAKING CHANGE: `BaseClient` has been removed and its functionality has been merged into `Client`.
1 parent 87364c7 commit 7349a6e

File tree

4 files changed

+115
-159
lines changed

4 files changed

+115
-159
lines changed

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

Lines changed: 0 additions & 131 deletions
This file was deleted.

packages/discord.js/src/client/Client.js

Lines changed: 107 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
const process = require('node:process');
44
const { clearTimeout, setImmediate, setTimeout } = require('node:timers');
55
const { Collection } = require('@discordjs/collection');
6-
const { makeURLSearchParams } = require('@discordjs/rest');
6+
const { REST, RESTEvents, makeURLSearchParams } = require('@discordjs/rest');
77
const { WebSocketManager, WebSocketShardEvents, WebSocketShardStatus } = require('@discordjs/ws');
8+
const { AsyncEventEmitter } = require('@vladfrangu/async_event_emitter');
89
const { GatewayDispatchEvents, GatewayIntentBits, OAuth2Scopes, Routes } = require('discord-api-types/v10');
910
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
1011
const { ChannelManager } = require('../managers/ChannelManager.js');
@@ -28,7 +29,7 @@ const { Options } = require('../util/Options.js');
2829
const { PermissionsBitField } = require('../util/PermissionsBitField.js');
2930
const { Status } = require('../util/Status.js');
3031
const { Sweepers } = require('../util/Sweepers.js');
31-
const { BaseClient } = require('./BaseClient.js');
32+
const { flatten } = require('../util/Util.js');
3233
const { ActionsManager } = require('./actions/ActionsManager.js');
3334
const { ClientVoiceManager } = require('./voice/ClientVoiceManager.js');
3435
const { PacketHandlers } = require('./websocket/handlers/index.js');
@@ -47,24 +48,66 @@ const BeforeReadyWhitelist = [
4748
/**
4849
* The main hub for interacting with the Discord API, and the starting point for any bot.
4950
*
50-
* @extends {BaseClient}
51+
* @extends {AsyncEventEmitter}
5152
*/
52-
class Client extends BaseClient {
53+
class Client extends AsyncEventEmitter {
5354
/**
5455
* @param {ClientOptions} options Options for the client
5556
*/
5657
constructor(options) {
57-
super(options);
58+
super();
59+
60+
if (typeof options !== 'object' || options === null) {
61+
throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'options', 'object', true);
62+
}
63+
64+
const defaultOptions = Options.createDefault();
65+
/**
66+
* The options the client was instantiated with
67+
*
68+
* @type {ClientOptions}
69+
*/
70+
this.options = {
71+
...defaultOptions,
72+
...options,
73+
presence: {
74+
...defaultOptions.presence,
75+
...options.presence,
76+
},
77+
sweepers: {
78+
...defaultOptions.sweepers,
79+
...options.sweepers,
80+
},
81+
ws: {
82+
...defaultOptions.ws,
83+
...options.ws,
84+
},
85+
rest: {
86+
...defaultOptions.rest,
87+
...options.rest,
88+
userAgentAppendix: options.rest?.userAgentAppendix
89+
? `${Options.userAgentAppendix} ${options.rest.userAgentAppendix}`
90+
: Options.userAgentAppendix,
91+
},
92+
};
93+
94+
/**
95+
* The REST manager of the client
96+
*
97+
* @type {REST}
98+
*/
99+
this.rest = new REST(this.options.rest);
100+
101+
this.rest.on(RESTEvents.Debug, message => this.emit(Events.Debug, message));
58102

59103
const data = require('node:worker_threads').workerData ?? process.env;
60-
const defaults = Options.createDefault();
61104

62-
if (this.options.ws.shardIds === defaults.ws.shardIds && 'SHARDS' in data) {
105+
if (this.options.ws.shardIds === defaultOptions.ws.shardIds && 'SHARDS' in data) {
63106
const shards = JSON.parse(data.SHARDS);
64107
this.options.ws.shardIds = Array.isArray(shards) ? shards : [shards];
65108
}
66109

67-
if (this.options.ws.shardCount === defaults.ws.shardCount && 'SHARD_COUNT' in data) {
110+
if (this.options.ws.shardCount === defaultOptions.ws.shardCount && 'SHARD_COUNT' in data) {
68111
this.options.ws.shardCount = Number(data.SHARD_COUNT);
69112
}
70113

@@ -442,12 +485,56 @@ class Client extends BaseClient {
442485
}
443486

444487
/**
445-
* Logs out, terminates the connection to Discord, and destroys the client.
488+
* Options used for deleting a webhook.
489+
*
490+
* @typedef {Object} WebhookDeleteOptions
491+
* @property {string} [token] Token of the webhook
492+
* @property {string} [reason] The reason for deleting the webhook
493+
*/
494+
495+
/**
496+
* Deletes a webhook.
497+
*
498+
* @param {Snowflake} id The webhook's id
499+
* @param {WebhookDeleteOptions} [options] Options for deleting the webhook
500+
* @returns {Promise<void>}
501+
*/
502+
async deleteWebhook(id, { token, reason } = {}) {
503+
await this.rest.delete(Routes.webhook(id, token), { auth: !token, reason });
504+
}
505+
506+
/**
507+
* Increments max listeners by one, if they are not zero.
508+
*
509+
* @private
510+
*/
511+
incrementMaxListeners() {
512+
const maxListeners = this.getMaxListeners();
513+
if (maxListeners !== 0) {
514+
this.setMaxListeners(maxListeners + 1);
515+
}
516+
}
517+
518+
/**
519+
* Decrements max listeners by one, if they are not zero.
520+
*
521+
* @private
522+
*/
523+
decrementMaxListeners() {
524+
const maxListeners = this.getMaxListeners();
525+
if (maxListeners !== 0) {
526+
this.setMaxListeners(maxListeners - 1);
527+
}
528+
}
529+
530+
/**
531+
* Destroys all assets used by the client.
446532
*
447533
* @returns {Promise<void>}
448534
*/
449535
async destroy() {
450-
super.destroy();
536+
this.rest.clearHashSweeper();
537+
this.rest.clearHandlerSweeper();
451538

452539
this.sweepers.destroy();
453540
await this.ws.destroy();
@@ -701,10 +788,7 @@ class Client extends BaseClient {
701788
}
702789

703790
toJSON() {
704-
return super.toJSON({
705-
actions: false,
706-
presence: false,
707-
});
791+
return flatten(this, { actions: false, presence: false });
708792
}
709793

710794
/**
@@ -797,6 +881,10 @@ class Client extends BaseClient {
797881
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'jsonTransformer', 'a function');
798882
}
799883
}
884+
885+
async [Symbol.asyncDispose]() {
886+
await this.destroy();
887+
}
800888
}
801889

802890
exports.Client = Client;
@@ -846,6 +934,11 @@ exports.Client = Client;
846934
* @see {@link https://discord.js.org/docs/packages/collection/stable/Collection:Class}
847935
*/
848936

937+
/**
938+
* @external REST
939+
* @see {@link https://discord.js.org/docs/packages/rest/stable/REST:Class}
940+
*/
941+
849942
/**
850943
* @external ImageURLOptions
851944
* @see {@link https://discord.js.org/docs/packages/rest/stable/ImageURLOptions:Interface}

packages/discord.js/src/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const { __exportStar } = require('tslib');
44

55
// "Root" classes (starting points)
6-
exports.BaseClient = require('./client/BaseClient.js').BaseClient;
76
exports.Client = require('./client/Client.js').Client;
87
exports.Shard = require('./sharding/Shard.js').Shard;
98
exports.ShardClientUtil = require('./sharding/ShardClientUtil.js').ShardClientUtil;

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

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -492,18 +492,6 @@ export abstract class Base {
492492
public valueOf(): string;
493493
}
494494

495-
export class BaseClient<Events extends {}> extends AsyncEventEmitter<Events> implements AsyncDisposable {
496-
public constructor(options?: ClientOptions);
497-
private decrementMaxListeners(): void;
498-
private incrementMaxListeners(): void;
499-
500-
public options: ClientOptions;
501-
public rest: REST;
502-
public destroy(): void;
503-
public toJSON(...props: Record<string, boolean | string>[]): unknown;
504-
public [Symbol.asyncDispose](): Promise<void>;
505-
}
506-
507495
export type GuildCacheMessage<Cached extends CacheType> = CacheTypeReducer<
508496
Cached,
509497
Message<true>,
@@ -913,7 +901,10 @@ export type If<Value extends boolean, TrueResult, FalseResult = null> = Value ex
913901
? FalseResult
914902
: FalseResult | TrueResult;
915903

916-
export class Client<Ready extends boolean = boolean> extends BaseClient<ClientEventTypes> {
904+
export class Client<Ready extends boolean = boolean>
905+
extends AsyncEventEmitter<ClientEventTypes>
906+
implements AsyncDisposable
907+
{
917908
public constructor(options: ClientOptions);
918909
private readonly actions: unknown;
919910
private readonly expectedGuilds: Set<Snowflake>;
@@ -928,6 +919,8 @@ export class Client<Ready extends boolean = boolean> extends BaseClient<ClientEv
928919
private _triggerClientReady(): void;
929920
private _validateOptions(options: ClientOptions): void;
930921
private get _censoredToken(): string | null;
922+
private decrementMaxListeners(): void;
923+
private incrementMaxListeners(): void;
931924
// This a technique used to brand the ready state. Or else we'll get `never` errors on typeguard checks.
932925
private readonly _ready: Ready;
933926

@@ -939,6 +932,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient<ClientEv
939932
public get ping(): number | null;
940933
public get readyAt(): If<Ready, Date>;
941934
public readyTimestamp: If<Ready, number>;
935+
public rest: REST;
942936
public sweepers: Sweepers;
943937
public shard: ShardClientUtil | null;
944938
public status: Status;
@@ -969,6 +963,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient<ClientEv
969963
public login(token?: string): Promise<string>;
970964
public isReady(): this is Client<true>;
971965
public toJSON(): unknown;
966+
public [Symbol.asyncDispose](): Promise<void>;
972967
}
973968

974969
export interface StickerPackFetchOptions {

0 commit comments

Comments
 (0)