@wolfstar/plugin-gateway 0.6.0 → 0.7.0-next-20260928135015

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -51,6 +51,113 @@ var EventGatewayListener = class extends Listener {
51
51
  }
52
52
  };
53
53
 
54
+ //#endregion
55
+ //#region src/errors/Messages.ts
56
+ /**
57
+ * The message of every {@link GatewayErrorCode}: a string, or a function formatting the arguments passed to the
58
+ * error's constructor.
59
+ */
60
+ const GatewayErrorMessages = {
61
+ ClientSessionStoreConflict: "sessionStore replaces gateway.retrieveSessionInfo and gateway.updateSessionInfo, pass one or the other",
62
+ ClientNotConstructed: "No GatewayClient has been constructed yet",
63
+ DispatchHandlerConflict: (event) => `Dispatch event "${event}" is registered in both DispatchHandlers and MultiDispatchHandlers`,
64
+ DispatchTimeout: (type, shardId, partition, timeout) => `Processing ${type} on shard ${shardId} (${partition ?? "barrier"}) took longer than ${timeout}ms`,
65
+ SessionStoreFailed: (operation, shardId) => `Cannot ${operation === "get" ? "read" : "write"} the session of shard ${shardId} in the session store`,
66
+ SessionStoreTimeout: (timeout) => `Timed out after ${timeout}ms`,
67
+ CacheAsynchronous: (entity) => `The ${entity} cache is asynchronous, use get instead of cached`,
68
+ CacheRelationsAsynchronous: (entity) => `The relations of the ${entity} cache are read from an asynchronous cache, use get instead of cached`,
69
+ CacheNotIterable: (entity) => `The ${entity} cache cannot enumerate its entries (it has no keys/values/entries)`,
70
+ CacheKeyUnresolvable: (entity, reason) => `Cannot key a ${entity} ${reason}`,
71
+ IdUnresolvable: "Cannot resolve an ID from a structure without one",
72
+ ChannelGuildUnknown: (channelId) => `Channel ${channelId} has no known guild`,
73
+ ChannelTypeChanged: (channelId, from, to) => `Channel ${channelId} changed type from ${from} to ${to}`,
74
+ ChannelLockPermissionsConflict: "Pass either lockPermissions or permissionOverwrites, not both",
75
+ GuildChannelOrphan: (channelId) => `Channel ${channelId} has no category to sync its permissions with`,
76
+ ThreadParentUnknown: (threadId) => `Thread ${threadId} has no known parent`,
77
+ ThreadOwnerUnknown: (threadId) => `Thread ${threadId} has no known owner`,
78
+ ThreadMemberIdsMissing: (action) => `A thread member without a thread or user ID cannot be ${action}`,
79
+ GuildIntegrationNotFound: (guildId, integrationId) => `The guild ${guildId} has no integration ${integrationId}`,
80
+ GuildMemberUserUnknown: "This member has no user data: its ID is unknown",
81
+ GuildMembersQueryConflict: "Cannot request members by both query and userIds",
82
+ GuildMembersUserIdsLimit: "Cannot request more than 100 members by their IDs",
83
+ MemberFetchNonceLength: "The nonce of a members request cannot exceed 32 bytes",
84
+ GuildMembersNoncePending: (nonce) => `A members request with the nonce "${nonce}" is pending already`,
85
+ GuildMembersTimeout: (guildId, nonce, timeout) => `Requesting the members of guild ${guildId} (${nonce}) took longer than ${timeout}ms`,
86
+ GuildMembersRateLimited: (guildId, nonce, retryAfter) => `Requesting the members of guild ${guildId} (${nonce}) is rate limited, retry after ${retryAfter}ms`,
87
+ PresenceNotFetchable: (guildId, userId) => `Presences cannot be fetched from the API, they are only received from the gateway (user ${userId} of guild ${guildId} is not cached); enable the presences cache to read them later`,
88
+ InviteGuildUnknown: (code) => `Invite ${code} has no known guild`,
89
+ EmojiEmpty: "Cannot resolve an empty string to an emoji",
90
+ EmojiType: "Cannot resolve an emoji without an ID nor a name",
91
+ NotGuildSticker: "Only guild stickers can be edited or deleted",
92
+ NotGuildSoundboardSound: "Default soundboard sounds cannot be changed",
93
+ MessageContentType: "The content of a message must be a string",
94
+ MessageNonceLength: "A message nonce must be at most 25 characters long",
95
+ MessageNonceType: "A message nonce must be an integer",
96
+ MessageForwardChannelMissing: "Forwarding a message by ID needs its channel",
97
+ MessageReferenceMissing: (messageId) => `Message ${messageId} references no message`,
98
+ MessagePollMissing: (messageId) => `Message ${messageId} has no poll`,
99
+ AttachmentDownloadFailed: (url, status) => `Could not download ${url}: ${status}`,
100
+ VoiceStateNotOwn: "Only the bot's own voice state can request to speak",
101
+ VoiceStateGuildUnknown: "This voice state does not belong to a guild",
102
+ WebhookTokenUnavailable: (webhookId) => `Webhook ${webhookId} has no token to post with`
103
+ };
104
+
105
+ //#endregion
106
+ //#region src/errors/GatewayError.ts
107
+ /**
108
+ * An {@link Error} carrying a {@link GatewayErrorCode}, like discord.js's `DiscordjsError`.
109
+ *
110
+ * @remarks
111
+ * Its `name` is `GatewayError [<code>]`, and its message is the code's entry in {@link GatewayErrorMessages}.
112
+ */
113
+ var GatewayError = class extends Error {
114
+ /**
115
+ * The code of the error.
116
+ */
117
+ code;
118
+ constructor(code, ...args) {
119
+ super(formatMessage(code, args));
120
+ this.code = code;
121
+ setName(this, new.target, code);
122
+ }
123
+ };
124
+ /**
125
+ * A {@link TypeError} carrying a {@link GatewayErrorCode}, like discord.js's `DiscordjsTypeError`.
126
+ */
127
+ var GatewayTypeError = class extends TypeError {
128
+ /**
129
+ * The code of the error.
130
+ */
131
+ code;
132
+ constructor(code, ...args) {
133
+ super(formatMessage(code, args));
134
+ this.code = code;
135
+ setName(this, new.target, code);
136
+ }
137
+ };
138
+ /**
139
+ * A {@link RangeError} carrying a {@link GatewayErrorCode}, like discord.js's `DiscordjsRangeError`.
140
+ */
141
+ var GatewayRangeError = class extends RangeError {
142
+ /**
143
+ * The code of the error.
144
+ */
145
+ code;
146
+ constructor(code, ...args) {
147
+ super(formatMessage(code, args));
148
+ this.code = code;
149
+ setName(this, new.target, code);
150
+ }
151
+ };
152
+ function formatMessage(code, args) {
153
+ const message = GatewayErrorMessages[code];
154
+ return typeof message === "function" ? message(...args) : message;
155
+ }
156
+ function setName(error, target, code) {
157
+ error.name = `${target.name} [${code}]`;
158
+ Error.captureStackTrace?.(error, target);
159
+ }
160
+
54
161
  //#endregion
55
162
  //#region src/util/channels.ts
56
163
  /**
@@ -60,7 +167,7 @@ var EventGatewayListener = class extends Listener {
60
167
  */
61
168
  function resolveId(value) {
62
169
  const id = typeof value === "string" ? value : value.id;
63
- if (!id) throw new TypeError("Cannot resolve an ID from a structure without one");
170
+ if (!id) throw new GatewayTypeError("IdUnresolvable");
64
171
  return id;
65
172
  }
66
173
  /**
@@ -210,7 +317,7 @@ function Mixin(target, mixins) {
210
317
  */
211
318
  function getGatewayClient() {
212
319
  const { client } = container;
213
- if (!client || !("gateway" in client)) throw new Error("No GatewayClient has been constructed yet");
320
+ if (!client || !("gateway" in client)) throw new GatewayError("ClientNotConstructed");
214
321
  return client;
215
322
  }
216
323
 
@@ -521,7 +628,7 @@ var BaseChannelMixin = class {
521
628
  */
522
629
  async fetch() {
523
630
  const data = await this.client.api.channels.get(this.id);
524
- if (data.type !== this.type) throw new TypeError(`Channel ${this.id} changed type from ${this.type} to ${data.type}`);
631
+ if (data.type !== this.type) throw new GatewayTypeError("ChannelTypeChanged", this.id, this.type, data.type);
525
632
  return this[kPatch](data);
526
633
  }
527
634
  };
@@ -833,7 +940,7 @@ async function computePermissionsIn(channel, target) {
833
940
  resolved = await client.channels.fetch(data.parent_id);
834
941
  data = resolved.toJSON();
835
942
  }
836
- if (!data.guild_id) throw new Error(`Channel ${resolved.id} has no known guild`);
943
+ if (!data.guild_id) throw new GatewayError("ChannelGuildUnknown", resolved.id);
837
944
  return computeTargetPermissions(data.guild_id, data.permission_overwrites ?? [], target);
838
945
  }
839
946
 
@@ -889,7 +996,7 @@ var ChannelPermissionMixin = class {
889
996
  */
890
997
  async setPosition(position, options = {}) {
891
998
  const { guild_id: guildId } = this[kData];
892
- if (!guildId) throw new Error(`Channel ${this.id} has no known guild`);
999
+ if (!guildId) throw new GatewayError("ChannelGuildUnknown", this.id);
893
1000
  const target = options.relative ? this.position + position : position;
894
1001
  await this.client.guilds.channels(guildId).setPositions([{
895
1002
  channel: this.id,
@@ -922,7 +1029,7 @@ var ChannelPermissionMixin = class {
922
1029
  * @param reason The reason for the audit log.
923
1030
  */
924
1031
  lockPermissions(reason) {
925
- if (!this[kData].parent_id) throw new Error(`Channel ${this.id} has no category to sync its permissions with`);
1032
+ if (!this[kData].parent_id) throw new GatewayError("GuildChannelOrphan", this.id);
926
1033
  return editChannel(this, {
927
1034
  lockPermissions: true,
928
1035
  reason
@@ -945,7 +1052,7 @@ var ChannelPermissionMixin = class {
945
1052
  */
946
1053
  async fetchPermissionsFor(target) {
947
1054
  const { guild_id: guildId, permission_overwrites: overwrites = [] } = this[kData];
948
- if (!guildId) throw new Error(`Channel ${this.id} has no known guild`);
1055
+ if (!guildId) throw new GatewayError("ChannelGuildUnknown", this.id);
949
1056
  return computeTargetPermissions(guildId, overwrites, target);
950
1057
  }
951
1058
  };
@@ -1002,7 +1109,7 @@ var ChannelThreadManager = class {
1002
1109
  * Fetches the active threads of the channel, and caches them.
1003
1110
  */
1004
1111
  async fetchActive() {
1005
- if (!this.guildId) throw new Error(`Channel ${this.channelId} has no known guild`);
1112
+ if (!this.guildId) throw new GatewayError("ChannelGuildUnknown", this.channelId);
1006
1113
  const { threads, members } = await this.client.threads.fetchActive(this.guildId);
1007
1114
  const own = threads.filter((thread) => thread.parentId === this.channelId);
1008
1115
  const ids = new Set(own.map((thread) => thread.id));
@@ -1125,7 +1232,7 @@ var GuildChannelMixin = class {
1125
1232
  */
1126
1233
  async clone(options = {}) {
1127
1234
  const { guildId } = this;
1128
- if (!guildId) throw new Error(`Channel ${this.id} has no known guild`);
1235
+ if (!guildId) throw new GatewayError("ChannelGuildUnknown", this.id);
1129
1236
  const data = this[kData];
1130
1237
  return this.client.guilds.channels(guildId).create({
1131
1238
  name: data.name ?? "",
@@ -1323,7 +1430,7 @@ var MessagePayload = class MessagePayload {
1323
1430
  const { content } = this.options;
1324
1431
  if (content === null) return this.isEdit ? "" : void 0;
1325
1432
  if (content === void 0) return void 0;
1326
- if (typeof content !== "string") throw new TypeError("The content of a message must be a string");
1433
+ if (typeof content !== "string") throw new GatewayTypeError("MessageContentType");
1327
1434
  return content;
1328
1435
  }
1329
1436
  /**
@@ -1380,8 +1487,8 @@ var MessagePayload = class MessagePayload {
1380
1487
  }
1381
1488
  resolveNonce(nonce) {
1382
1489
  if (nonce === void 0) return void 0;
1383
- if (typeof nonce === "string" && nonce.length > 25) throw new RangeError("A message nonce must be at most 25 characters long");
1384
- if (typeof nonce === "number" && !Number.isInteger(nonce)) throw new RangeError("A message nonce must be an integer");
1490
+ if (typeof nonce === "string" && nonce.length > 25) throw new GatewayRangeError("MessageNonceLength");
1491
+ if (typeof nonce === "number" && !Number.isInteger(nonce)) throw new GatewayRangeError("MessageNonceType");
1385
1492
  return nonce;
1386
1493
  }
1387
1494
  resolveList(values) {
@@ -1404,7 +1511,7 @@ var MessagePayload = class MessagePayload {
1404
1511
  if (forward) {
1405
1512
  const { message, channel, guild } = forward;
1406
1513
  const channelId = typeof message === "string" ? MessagePayload.id(channel) : message.channelId;
1407
- if (!channelId) throw new TypeError("Forwarding a message by ID needs its channel");
1514
+ if (!channelId) throw new GatewayTypeError("MessageForwardChannelMissing");
1408
1515
  return {
1409
1516
  type: MessageReferenceType.Forward,
1410
1517
  message_id: typeof message === "string" ? message : message.id,
@@ -1537,7 +1644,7 @@ async function resolveBuffer(attachment) {
1537
1644
  if (typeof attachment === "string") {
1538
1645
  if (/^https?:\/\//.test(attachment)) {
1539
1646
  const response = await fetch(attachment);
1540
- if (!response.ok) throw new Error(`Could not download ${attachment}: ${response.status}`);
1647
+ if (!response.ok) throw new GatewayError("AttachmentDownloadFailed", attachment, response.status);
1541
1648
  return {
1542
1649
  data: new Uint8Array(await response.arrayBuffer()),
1543
1650
  contentType: response.headers.get("content-type") ?? void 0
@@ -1815,7 +1922,7 @@ var ThreadChannelMixin = class {
1815
1922
  */
1816
1923
  fetchStarterMessage() {
1817
1924
  const { parent_id: parentId } = this[kData];
1818
- if (!parentId) throw new Error(`Thread ${this.id} has no known parent`);
1925
+ if (!parentId) throw new GatewayError("ThreadParentUnknown", this.id);
1819
1926
  return this.client.messages.fetch(parentId, this.id);
1820
1927
  }
1821
1928
  /**
@@ -1823,7 +1930,7 @@ var ThreadChannelMixin = class {
1823
1930
  */
1824
1931
  fetchOwner() {
1825
1932
  const { owner_id: ownerId } = this[kData];
1826
- if (!ownerId) throw new Error(`Thread ${this.id} has no known owner`);
1933
+ if (!ownerId) throw new GatewayError("ThreadOwnerUnknown", this.id);
1827
1934
  return this.client.threadMembers.fetch(this.id, ownerId);
1828
1935
  }
1829
1936
  };
@@ -2477,7 +2584,7 @@ var StageChannel = class extends StageChannel$1 {
2477
2584
  */
2478
2585
  createStageInstance(options) {
2479
2586
  const { guildId } = this;
2480
- if (!guildId) return Promise.reject(/* @__PURE__ */ new Error(`Channel ${this.id} has no known guild`));
2587
+ if (!guildId) return Promise.reject(new GatewayError("ChannelGuildUnknown", this.id));
2481
2588
  return this.client.guilds.stageInstances(guildId).create(this.id, options);
2482
2589
  }
2483
2590
  };
@@ -2771,11 +2878,11 @@ var CachedManager = class {
2771
2878
  cached(...args) {
2772
2879
  const { cache } = this;
2773
2880
  if (cache === void 0) return void 0;
2774
- if (cache.synchronous !== true) throw new TypeError(`The ${this.entity} cache is asynchronous, use get instead of cached`);
2881
+ if (cache.synchronous !== true) throw new GatewayTypeError("CacheAsynchronous", this.entity);
2775
2882
  const value = this._get(...args);
2776
2883
  if (isPromiseLike(value)) {
2777
2884
  value.catch(() => void 0);
2778
- throw new TypeError(`The relations of the ${this.entity} cache are read from an asynchronous cache, use get instead of cached`);
2885
+ throw new GatewayTypeError("CacheRelationsAsynchronous", this.entity);
2779
2886
  }
2780
2887
  return value;
2781
2888
  }
@@ -2934,7 +3041,7 @@ var CachedManager = class {
2934
3041
  iterableCache() {
2935
3042
  const { cache } = this;
2936
3043
  if (cache === void 0) return void 0;
2937
- if (!isIterableCache(cache)) throw new TypeError(`The ${this.entity} cache cannot enumerate its entries (it has no keys/values/entries)`);
3044
+ if (!isIterableCache(cache)) throw new GatewayTypeError("CacheNotIterable", this.entity);
2938
3045
  return cache;
2939
3046
  }
2940
3047
  /**
@@ -3118,7 +3225,7 @@ var ChannelManager = class extends CachedManager {
3118
3225
  * @param options The fields to edit, and the reason for the audit log.
3119
3226
  */
3120
3227
  async edit(channelId, options) {
3121
- if (options.lockPermissions && options.permissionOverwrites) throw new TypeError("Pass either lockPermissions or permissionOverwrites, not both");
3228
+ if (options.lockPermissions && options.permissionOverwrites) throw new GatewayTypeError("ChannelLockPermissionsConflict");
3122
3229
  const body = toChannelBody(options);
3123
3230
  if (options.lockPermissions) {
3124
3231
  const parentId = options.parent === void 0 ? overwriteHolder(await this.fetch(channelId)).parent_id ?? null : options.parent && resolveId(options.parent);
@@ -4112,13 +4219,13 @@ var ReactionEmoji = class extends Emoji {
4112
4219
  const { animated, name, id } = custom.groups;
4113
4220
  return `${animated ? "a:" : ""}${name}:${id}`;
4114
4221
  }
4115
- if (!decoded) throw new TypeError("Cannot resolve an empty string to an emoji");
4222
+ if (!decoded) throw new GatewayTypeError("EmojiEmpty");
4116
4223
  return encodeURIComponent(decoded);
4117
4224
  }
4118
4225
  const { id, name, animated } = emoji;
4119
4226
  if (id) return `${animated ? "a:" : ""}${name ?? "_"}:${id}`;
4120
4227
  if (name) return encodeURIComponent(name);
4121
- throw new TypeError("Cannot resolve an emoji without an ID nor a name");
4228
+ throw new GatewayTypeError("EmojiType");
4122
4229
  }
4123
4230
  /**
4124
4231
  * Resolves anything {@link EmojiIdentifierResolvable} to the ID and name pair of welcome screens and onboarding.
@@ -4753,7 +4860,7 @@ var GuildInvite = class extends BaseInvite {
4753
4860
  */
4754
4861
  async delete(reason) {
4755
4862
  const { guildId } = this;
4756
- if (!guildId) throw new Error(`Invite ${this.code} has no known guild`);
4863
+ if (!guildId) throw new GatewayError("InviteGuildUnknown", this.code);
4757
4864
  await this.client.guilds.invites(guildId).delete(this.code, reason);
4758
4865
  return this;
4759
4866
  }
@@ -4889,7 +4996,7 @@ var Sticker = class extends Sticker$1 {
4889
4996
  }
4890
4997
  requireGuildId() {
4891
4998
  const { guildId } = this;
4892
- if (!guildId) throw new Error("Only guild stickers can be edited or deleted");
4999
+ if (!guildId) throw new GatewayError("NotGuildSticker");
4893
5000
  return guildId;
4894
5001
  }
4895
5002
  };
@@ -4972,7 +5079,7 @@ var WebhookMixin = class {
4972
5079
  */
4973
5080
  requireToken() {
4974
5081
  const { token } = this;
4975
- if (!token) throw new Error(`Webhook ${this.id} has no token to post with`);
5082
+ if (!token) throw new GatewayError("WebhookTokenUnavailable", this.id);
4976
5083
  return token;
4977
5084
  }
4978
5085
  };
@@ -5949,7 +6056,7 @@ var GuildIntegrationManager = class extends CachedManager {
5949
6056
  }
5950
6057
  async fetchRaw(integrationId) {
5951
6058
  const integration = (await this.list()).find(({ id }) => id === integrationId);
5952
- if (!integration) throw new RangeError(`The guild ${this.guildId} has no integration ${integrationId}`);
6059
+ if (!integration) throw new GatewayRangeError("GuildIntegrationNotFound", this.guildId, integrationId);
5953
6060
  return integration;
5954
6061
  }
5955
6062
  async list() {
@@ -6165,7 +6272,7 @@ var SoundboardSound = class extends SoundboardSound$1 {
6165
6272
  }
6166
6273
  withGuild(action) {
6167
6274
  const { guildId } = this;
6168
- if (!guildId) return Promise.reject(/* @__PURE__ */ new Error("Default soundboard sounds cannot be changed"));
6275
+ if (!guildId) return Promise.reject(new GatewayError("NotGuildSoundboardSound"));
6169
6276
  return action(guildId);
6170
6277
  }
6171
6278
  };
@@ -7675,7 +7782,7 @@ var GuildMember = class extends Structure {
7675
7782
  }
7676
7783
  requireId() {
7677
7784
  const { id } = this;
7678
- if (id === null) throw new Error("This member has no user data: its ID is unknown");
7785
+ if (id === null) throw new GatewayError("GuildMemberUserUnknown");
7679
7786
  return id;
7680
7787
  }
7681
7788
  };
@@ -8084,7 +8191,7 @@ var VoiceState = class extends VoiceState$1 {
8084
8191
  */
8085
8192
  async edit(options) {
8086
8193
  const target = this.resolveTarget();
8087
- if (target !== "@me" && options.requestToSpeak !== void 0) throw new Error("Only the bot's own voice state can request to speak");
8194
+ if (target !== "@me" && options.requestToSpeak !== void 0) throw new GatewayError("VoiceStateNotOwn");
8088
8195
  const requestToSpeakTimestamp = options.requestToSpeak ? (/* @__PURE__ */ new Date()).toISOString() : options.requestToSpeak === false ? null : void 0;
8089
8196
  const body = {
8090
8197
  channel_id: this.channelId ?? void 0,
@@ -8112,7 +8219,7 @@ var VoiceState = class extends VoiceState$1 {
8112
8219
  }
8113
8220
  requireGuildId() {
8114
8221
  const { guildId } = this;
8115
- if (!guildId) throw new Error("This voice state does not belong to a guild");
8222
+ if (!guildId) throw new GatewayError("VoiceStateGuildUnknown");
8116
8223
  return guildId;
8117
8224
  }
8118
8225
  resolveTarget() {
@@ -8132,7 +8239,7 @@ Mixin(VoiceState, [StructureMixin]);
8132
8239
  * event order is preserved. The error only reports that the partition is stuck, usually on a slow or unreachable
8133
8240
  * cache.
8134
8241
  */
8135
- var DispatchTimeoutError = class extends Error {
8242
+ var DispatchTimeoutError = class extends GatewayError {
8136
8243
  /**
8137
8244
  * The type of the slow dispatch, e.g. `MESSAGE_CREATE`.
8138
8245
  */
@@ -8150,8 +8257,7 @@ var DispatchTimeoutError = class extends Error {
8150
8257
  */
8151
8258
  timeout;
8152
8259
  constructor(type, shardId, partition, timeout) {
8153
- super(`Processing ${type} on shard ${shardId} (${partition ?? "barrier"}) took longer than ${timeout}ms`);
8154
- this.name = "DispatchTimeoutError";
8260
+ super("DispatchTimeout", type, shardId, partition, timeout);
8155
8261
  this.type = type;
8156
8262
  this.shardId = shardId;
8157
8263
  this.partition = partition;
@@ -8164,7 +8270,7 @@ var DispatchTimeoutError = class extends Error {
8164
8270
  * @remarks
8165
8271
  * The request is dropped: chunks arriving later still update the cache and emit `guildMembersChunk`.
8166
8272
  */
8167
- var GuildMembersTimeoutError = class extends Error {
8273
+ var GuildMembersTimeoutError = class extends GatewayError {
8168
8274
  /**
8169
8275
  * The ID of the guild whose members were requested.
8170
8276
  */
@@ -8178,8 +8284,7 @@ var GuildMembersTimeoutError = class extends Error {
8178
8284
  */
8179
8285
  timeout;
8180
8286
  constructor(guildId, nonce, timeout) {
8181
- super(`Requesting the members of guild ${guildId} (${nonce}) took longer than ${timeout}ms`);
8182
- this.name = "GuildMembersTimeoutError";
8287
+ super("GuildMembersTimeout", guildId, nonce, timeout);
8183
8288
  this.guildId = guildId;
8184
8289
  this.nonce = nonce;
8185
8290
  this.timeout = timeout;
@@ -8188,7 +8293,7 @@ var GuildMembersTimeoutError = class extends Error {
8188
8293
  /**
8189
8294
  * Thrown by `GuildMemberManager#request` when Discord answers with `RATE_LIMITED` instead of the members' chunks.
8190
8295
  */
8191
- var GuildMembersRateLimitError = class extends Error {
8296
+ var GuildMembersRateLimitError = class extends GatewayError {
8192
8297
  /**
8193
8298
  * The ID of the guild whose members were requested.
8194
8299
  */
@@ -8202,8 +8307,7 @@ var GuildMembersRateLimitError = class extends Error {
8202
8307
  */
8203
8308
  retryAfter;
8204
8309
  constructor(guildId, nonce, retryAfter) {
8205
- super(`Requesting the members of guild ${guildId} (${nonce}) is rate limited, retry after ${retryAfter}ms`);
8206
- this.name = "GuildMembersRateLimitError";
8310
+ super("GuildMembersRateLimited", guildId, nonce, retryAfter);
8207
8311
  this.guildId = guildId;
8208
8312
  this.nonce = nonce;
8209
8313
  this.retryAfter = retryAfter;
@@ -8216,7 +8320,7 @@ var GuildMembersRateLimitError = class extends Error {
8216
8320
  * Neither failure stops the shard: a failed (or timed out) read identifies instead of resuming, and a failed write
8217
8321
  * only means the session may not be resumed after a restart. The store's error is the `cause`.
8218
8322
  */
8219
- var GatewaySessionStoreError = class extends Error {
8323
+ var GatewaySessionStoreError = class extends GatewayError {
8220
8324
  /**
8221
8325
  * Whether the store failed to read (`"get"`) or write (`"set"`) the session.
8222
8326
  */
@@ -8226,8 +8330,8 @@ var GatewaySessionStoreError = class extends Error {
8226
8330
  */
8227
8331
  shardId;
8228
8332
  constructor(operation, shardId, cause) {
8229
- super(`Cannot ${operation === "get" ? "read" : "write"} the session of shard ${shardId} in the session store`, { cause });
8230
- this.name = "GatewaySessionStoreError";
8333
+ super("SessionStoreFailed", operation, shardId);
8334
+ this.cause = cause;
8231
8335
  this.operation = operation;
8232
8336
  this.shardId = shardId;
8233
8337
  }
@@ -8247,7 +8351,7 @@ var GuildMemberManager = class extends CachedManager {
8247
8351
  return new GuildMember(data);
8248
8352
  }
8249
8353
  keyOf(data) {
8250
- if (!data.user) throw new TypeError("Cannot key a member without its user");
8354
+ if (!data.user) throw new GatewayTypeError("CacheKeyUnresolvable", "member", "without its user");
8251
8355
  return this.resolveKey(data.guild_id, data.user.id);
8252
8356
  }
8253
8357
  /**
@@ -8351,9 +8455,9 @@ var GuildMemberManager = class extends CachedManager {
8351
8455
  const { query, limit = 0, presences, time = 12e4 } = options;
8352
8456
  const userIds = options.userIds?.length ? options.userIds : void 0;
8353
8457
  const nonce = options.nonce ?? randomBytes(16).toString("hex");
8354
- if (query !== void 0 && userIds !== void 0) throw new TypeError("Cannot request members by both query and userIds");
8355
- if (userIds && userIds.length > 100) throw new RangeError("Cannot request more than 100 members by their IDs");
8356
- if (Buffer.byteLength(nonce) > 32) throw new RangeError("The nonce of a members request cannot exceed 32 bytes");
8458
+ if (query !== void 0 && userIds !== void 0) throw new GatewayTypeError("GuildMembersQueryConflict");
8459
+ if (userIds && userIds.length > 100) throw new GatewayRangeError("GuildMembersUserIdsLimit");
8460
+ if (Buffer.byteLength(nonce) > 32) throw new GatewayRangeError("MemberFetchNonceLength");
8357
8461
  const d = userIds ? {
8358
8462
  guild_id: guildId,
8359
8463
  user_ids: [...userIds],
@@ -8368,7 +8472,7 @@ var GuildMemberManager = class extends CachedManager {
8368
8472
  };
8369
8473
  const shardCount = BigInt(await this.client.gateway.getShardCount());
8370
8474
  const shardId = Number((BigInt(guildId) >> 22n) % shardCount);
8371
- if (this.#requests.has(nonce)) throw new Error(`A members request with the nonce "${nonce}" is pending already`);
8475
+ if (this.#requests.has(nonce)) throw new GatewayError("GuildMembersNoncePending", nonce);
8372
8476
  let request;
8373
8477
  const promise = new Promise((resolve, reject) => {
8374
8478
  request = {
@@ -9250,7 +9354,7 @@ var Poll = class extends Poll$1 {
9250
9354
  */
9251
9355
  async fetch() {
9252
9356
  const { poll } = (await this.client.messages.fetch(this.channelId, this.messageId, { force: true })).toJSON();
9253
- if (!poll) throw new Error(`Message ${this.messageId} has no poll`);
9357
+ if (!poll) throw new GatewayError("MessagePollMissing", this.messageId);
9254
9358
  return this[kPatch](poll);
9255
9359
  }
9256
9360
  get question() {
@@ -9564,7 +9668,7 @@ var Message = class Message extends Message$1 {
9564
9668
  */
9565
9669
  async fetchReference() {
9566
9670
  const reference = this.reference;
9567
- if (!reference?.message_id) throw new Error(`Message ${this.id} references no message`);
9671
+ if (!reference?.message_id) throw new GatewayError("MessageReferenceMissing", this.id);
9568
9672
  return this.client.messages.fetch(reference.channel_id ?? this.channelId, reference.message_id);
9569
9673
  }
9570
9674
  /**
@@ -10839,7 +10943,7 @@ var VoiceStateManager = class extends CachedManager {
10839
10943
  return new VoiceState(data);
10840
10944
  }
10841
10945
  keyOf(data) {
10842
- if (!data.guild_id) throw new TypeError("Cannot key a voice state outside of a guild");
10946
+ if (!data.guild_id) throw new GatewayTypeError("CacheKeyUnresolvable", "voice state", "outside of a guild");
10843
10947
  return this.resolveKey(data.guild_id, data.user_id);
10844
10948
  }
10845
10949
  resolveKey(guildId, userId) {
@@ -10928,7 +11032,7 @@ var PresenceManager = class extends CachedManager {
10928
11032
  return Promise.all(entries.filter(([key]) => key.startsWith(prefix)).map(([, raw]) => this.hydrate(raw)));
10929
11033
  }
10930
11034
  fetchRaw(guildId, userId) {
10931
- return Promise.reject(/* @__PURE__ */ new Error(`Presences cannot be fetched from the API, they are only received from the gateway (user ${userId} of guild ${guildId} is not cached); enable the presences cache to read them later`));
11035
+ return Promise.reject(new GatewayError("PresenceNotFetchable", guildId, userId));
10932
11036
  }
10933
11037
  };
10934
11038
 
@@ -11077,7 +11181,7 @@ var ThreadMember = class extends Structure {
11077
11181
  */
11078
11182
  async fetch() {
11079
11183
  const { threadId, id } = this;
11080
- if (!threadId || !id) throw new Error("A thread member without a thread or user ID cannot be fetched");
11184
+ if (!threadId || !id) throw new GatewayError("ThreadMemberIdsMissing", "fetched");
11081
11185
  const member = await this.client.threadMembers.fetch(threadId, id, { force: true });
11082
11186
  return this[kPatch](member.toJSON());
11083
11187
  }
@@ -11117,7 +11221,7 @@ var ThreadMember = class extends Structure {
11117
11221
  */
11118
11222
  async remove() {
11119
11223
  const { id, threadId } = this;
11120
- if (!id || !threadId) throw new Error("Cannot remove a thread member without its IDs");
11224
+ if (!id || !threadId) throw new GatewayError("ThreadMemberIdsMissing", "removed");
11121
11225
  await this.client.threadMembers.remove(threadId, id);
11122
11226
  return this;
11123
11227
  }
@@ -11136,7 +11240,7 @@ var ThreadMemberManager = class extends CachedManager {
11136
11240
  return new ThreadMember(data);
11137
11241
  }
11138
11242
  keyOf(data) {
11139
- if (!data.id || !data.user_id) throw new TypeError("Cannot key a thread member without its IDs");
11243
+ if (!data.id || !data.user_id) throw new GatewayTypeError("CacheKeyUnresolvable", "thread member", "without its IDs");
11140
11244
  return this.resolveKey(data.id, data.user_id);
11141
11245
  }
11142
11246
  resolveKey(threadId, userId) {
@@ -12479,7 +12583,7 @@ var ActionsManager = class {
12479
12583
  constructor(client) {
12480
12584
  for (const [event, handler] of Object.entries(DispatchHandlers)) if (handler) this.#actions.set(event, new DispatchAction(client, handler));
12481
12585
  for (const [event, handler] of Object.entries(MultiDispatchHandlers)) if (handler) {
12482
- if (this.#actions.has(event)) throw new Error(`Dispatch event "${event}" is registered in both DispatchHandlers and MultiDispatchHandlers`);
12586
+ if (this.#actions.has(event)) throw new GatewayError("DispatchHandlerConflict", event);
12483
12587
  this.#actions.set(event, new MultiDispatchAction(client, handler));
12484
12588
  }
12485
12589
  }
@@ -12642,7 +12746,7 @@ var GatewaySessionMirror = class {
12642
12746
  if (this.#timeout === null) return await read;
12643
12747
  const timeout = this.#timeout;
12644
12748
  return await Promise.race([read, new Promise((_, reject) => {
12645
- timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Timed out after ${timeout}ms`)), timeout);
12749
+ timer = setTimeout(() => reject(new GatewayError("SessionStoreTimeout", timeout)), timeout);
12646
12750
  })]);
12647
12751
  } catch (error) {
12648
12752
  this.#onError(new GatewaySessionStoreError("get", shardId, error));
@@ -13107,7 +13211,7 @@ var GatewayClient = class extends Client {
13107
13211
  }
13108
13212
  sessionCallbacks(options) {
13109
13213
  const { sessionStore, gateway } = options;
13110
- if (sessionStore && (gateway?.retrieveSessionInfo || gateway?.updateSessionInfo)) throw new TypeError("sessionStore replaces gateway.retrieveSessionInfo and gateway.updateSessionInfo, pass one or the other");
13214
+ if (sessionStore && (gateway?.retrieveSessionInfo || gateway?.updateSessionInfo)) throw new GatewayTypeError("ClientSessionStoreConflict");
13111
13215
  let retrieve = gateway?.retrieveSessionInfo ?? DefaultWebSocketManagerOptions.retrieveSessionInfo;
13112
13216
  let update = gateway?.updateSessionInfo ?? DefaultWebSocketManagerOptions.updateSessionInfo;
13113
13217
  if (sessionStore) {
@@ -13142,6 +13246,24 @@ function resolveCache(options) {
13142
13246
  return Object.keys(resolved).length === 0 ? void 0 : resolved;
13143
13247
  }
13144
13248
 
13249
+ //#endregion
13250
+ //#region src/errors/ErrorCodes.ts
13251
+ /**
13252
+ * Every {@link GatewayErrorCode}, keyed by itself, like discord.js's `DiscordjsErrorCodes`.
13253
+ *
13254
+ * @example
13255
+ * ```typescript
13256
+ * try {
13257
+ * await webhook.send("Awoo");
13258
+ * } catch (error) {
13259
+ * if (error instanceof GatewayError && error.code === GatewayErrorCodes.WebhookTokenUnavailable) {
13260
+ * // ...
13261
+ * }
13262
+ * }
13263
+ * ```
13264
+ */
13265
+ const GatewayErrorCodes = Object.freeze(Object.fromEntries(Object.keys(GatewayErrorMessages).map((code) => [code, code])));
13266
+
13145
13267
  //#endregion
13146
13268
  //#region src/util/decorators.ts
13147
13269
  /**
@@ -13180,5 +13302,5 @@ function RegisterAsGatewayListener(event, options = {}) {
13180
13302
  }
13181
13303
 
13182
13304
  //#endregion
13183
- export { Action, ActionsManager, Activity, ActivityFlagsBitField, AnnouncementChannel, AnnouncementThreadChannel, AnonymousGuild, AppliedTagsMixin, Attachment, AttachmentFlagsBitField, AutoModerationActionExecution, AutoModerationRule, AutoModerationRuleManager, BaseChannel, BaseChannelMixin, BaseInvite, CachedManager as BaseManager, CachedManager, BitField, CategoryChannel, Channel, ChannelFlagsBitField, ChannelManager, ChannelMessageManager, ChannelOwnerMixin, ChannelParentMixin, ChannelPermissionMixin, ChannelSlowmodeMixin, ChannelThreadManager, ChannelThreadsMixin, ChannelTopicMixin, ChannelWebhooksMixin, ClientUser, DMChannel, DMChannelMixin, DispatchAction, DispatchHandlers, DispatchQueue, DispatchTimeoutError, Embed, Emoji, EventGatewayListener, ForumChannel, GatewayClient, GatewayEvents, GatewaySessionStoreError, GroupDMChannel, GroupDMInvite, GroupDMMixin, Guild, GuildAuditLogsEntry, GuildBan, GuildBanManager, GuildChannelFields, GuildChannelManager, GuildChannelMixin, GuildEmoji, GuildEmojiManager, GuildEmojiRoleManager, GuildIntegrationManager, GuildInvite, GuildInviteManager, GuildManager, GuildMember, GuildMemberFlagsBitField, GuildMemberManager, GuildMemberRoleManager, GuildMembersRateLimitError, GuildMembersTimeoutError, GuildOnboarding, GuildOnboardingPrompt, GuildOnboardingPromptOption, GuildPreview, GuildScheduledEvent, GuildScheduledEventManager, GuildSoundboardSoundManager, GuildStickerManager, GuildTemplate, GuildTemplateManager, Integration, IntentsBitField, InviteGuild, MediaChannel, Message, MessageFlagsBitField, MessageManager, MessageMentions, MessagePayload, MessageReaction, Mixin, MultiDispatchAction, MultiDispatchHandlers, NumberFlagsBitField, Partials, PermissionOverwriteManager, PermissionOverwrites, PermissionsBitField, Poll, PollAnswer, Presence, PresenceManager, PrivateThreadChannel, PublicThreadChannel, ReactionEmoji, ReactionManager, ReactionUserManager, RegisterAsGatewayListener, RichPresenceAssets, Role, RoleFlagsBitField, RoleManager, SoundboardSound, StageChannel, StageInstance, StageInstanceManager, Sticker, StickerPack, Structure, StructureMixin, SystemChannelFlagsBitField, TextChannel, TextChannelMixin, ThreadChannelMemberManager, ThreadChannelMixin, ThreadManager, ThreadMember, ThreadMemberManager, ThreadOnlyChannelMixin, Typing, User, UserFlagsBitField, UserManager, VoiceChannel, VoiceChannelMixin, VoiceState, VoiceStateManager, Webhook, WebhookManager, WebhookMixin, WelcomeScreen, Widget, WidgetMember, bindClient, compareRolePositions, computeChannelPermissions, computeGuildPermissions, computePermissionsIn, computeTargetPermissions, createChannel, createInvite, dispatchPartition, getGatewayClient, initStructure, isDeepEqual, isThreadChannelType, kClient, kClone, kData, kPatch, kPatchRelations, kRelations, resolveId, resolveOverwriteOptions, snowflakeTimestamp, toAPIOverwrite, toChannelBody };
13305
+ export { Action, ActionsManager, Activity, ActivityFlagsBitField, AnnouncementChannel, AnnouncementThreadChannel, AnonymousGuild, AppliedTagsMixin, Attachment, AttachmentFlagsBitField, AutoModerationActionExecution, AutoModerationRule, AutoModerationRuleManager, BaseChannel, BaseChannelMixin, BaseInvite, CachedManager as BaseManager, CachedManager, BitField, CategoryChannel, Channel, ChannelFlagsBitField, ChannelManager, ChannelMessageManager, ChannelOwnerMixin, ChannelParentMixin, ChannelPermissionMixin, ChannelSlowmodeMixin, ChannelThreadManager, ChannelThreadsMixin, ChannelTopicMixin, ChannelWebhooksMixin, ClientUser, DMChannel, DMChannelMixin, DispatchAction, DispatchHandlers, DispatchQueue, DispatchTimeoutError, Embed, Emoji, EventGatewayListener, ForumChannel, GatewayClient, GatewayError, GatewayErrorCodes, GatewayErrorMessages, GatewayEvents, GatewayRangeError, GatewaySessionStoreError, GatewayTypeError, GroupDMChannel, GroupDMInvite, GroupDMMixin, Guild, GuildAuditLogsEntry, GuildBan, GuildBanManager, GuildChannelFields, GuildChannelManager, GuildChannelMixin, GuildEmoji, GuildEmojiManager, GuildEmojiRoleManager, GuildIntegrationManager, GuildInvite, GuildInviteManager, GuildManager, GuildMember, GuildMemberFlagsBitField, GuildMemberManager, GuildMemberRoleManager, GuildMembersRateLimitError, GuildMembersTimeoutError, GuildOnboarding, GuildOnboardingPrompt, GuildOnboardingPromptOption, GuildPreview, GuildScheduledEvent, GuildScheduledEventManager, GuildSoundboardSoundManager, GuildStickerManager, GuildTemplate, GuildTemplateManager, Integration, IntentsBitField, InviteGuild, MediaChannel, Message, MessageFlagsBitField, MessageManager, MessageMentions, MessagePayload, MessageReaction, Mixin, MultiDispatchAction, MultiDispatchHandlers, NumberFlagsBitField, Partials, PermissionOverwriteManager, PermissionOverwrites, PermissionsBitField, Poll, PollAnswer, Presence, PresenceManager, PrivateThreadChannel, PublicThreadChannel, ReactionEmoji, ReactionManager, ReactionUserManager, RegisterAsGatewayListener, RichPresenceAssets, Role, RoleFlagsBitField, RoleManager, SoundboardSound, StageChannel, StageInstance, StageInstanceManager, Sticker, StickerPack, Structure, StructureMixin, SystemChannelFlagsBitField, TextChannel, TextChannelMixin, ThreadChannelMemberManager, ThreadChannelMixin, ThreadManager, ThreadMember, ThreadMemberManager, ThreadOnlyChannelMixin, Typing, User, UserFlagsBitField, UserManager, VoiceChannel, VoiceChannelMixin, VoiceState, VoiceStateManager, Webhook, WebhookManager, WebhookMixin, WelcomeScreen, Widget, WidgetMember, bindClient, compareRolePositions, computeChannelPermissions, computeGuildPermissions, computePermissionsIn, computeTargetPermissions, createChannel, createInvite, dispatchPartition, getGatewayClient, initStructure, isDeepEqual, isThreadChannelType, kClient, kClone, kData, kPatch, kPatchRelations, kRelations, resolveId, resolveOverwriteOptions, snowflakeTimestamp, toAPIOverwrite, toChannelBody };
13184
13306
  //# sourceMappingURL=index.js.map