@wolfstar/plugin-gateway 0.5.0 → 0.6.0

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
@@ -1,7 +1,7 @@
1
1
  import { Client, Listener, container } from "@wolfstar/http-framework";
2
2
  import { CloseCodes, DefaultWebSocketManagerOptions, WebSocketManager, WebSocketShardEvents, WebSocketShardStatus } from "@discordjs/ws";
3
3
  import { Client as Client$1 } from "@discordjs/core";
4
- import { applyGatewayDispatch, autoModerationRuleKey, banKey, emojiKey, integrationKey, inviteKey, memberKey, messageKey, presenceKey, roleKey, scheduledEventKey, soundboardSoundKey, stageInstanceKey, stickerKey, threadMemberKey, voiceStateKey } from "@wolfstar/plugin-cache";
4
+ import { applyGatewayDispatch, autoModerationRuleKey, banKey, createCache, emojiKey, integrationKey, inviteKey, isIterableCache, memberKey, mergeValues, messageKey, presenceKey, roleKey, scheduledEventKey, soundboardSoundKey, stageInstanceKey, stickerKey, threadMemberKey, voiceStateKey } from "@wolfstar/plugin-cache";
5
5
  import { ActivityType, AuditLogEvent, ChannelType, FormattingPatterns, GatewayDispatchEvents, GatewayIntentBits, GatewayOpcodes, GuildFeature, GuildMemberFlags, GuildPremiumTier, GuildScheduledEventStatus, GuildSystemChannelFlags, GuildWidgetStyle, InviteType, MessageFlags, MessageReferenceType, MessageType, OverwriteType, PermissionFlagsBits, PresenceUpdateStatus, RoleFlags, RouteBases, Routes, StickerFormatType, UserFlags, WebhookType } from "discord-api-types/v10";
6
6
  import { Activity as Activity$1, ActivityFlagsBitField as ActivityFlagsBitField$1, AnnouncementChannel as AnnouncementChannel$1, AnnouncementThreadChannel as AnnouncementThreadChannel$1, Attachment as Attachment$1, AttachmentFlagsBitField as AttachmentFlagsBitField$1, AutoModerationRule as AutoModerationRule$1, BitField, BitField as BitField$1, CategoryChannel as CategoryChannel$1, Channel as Channel$1, ChannelFlagsBitField as ChannelFlagsBitField$1, DMChannel as DMChannel$1, Embed as Embed$1, Emoji as Emoji$1, ForumChannel as ForumChannel$1, GroupDMChannel as GroupDMChannel$1, Invite, MediaChannel as MediaChannel$1, Message as Message$1, MessageFlagsBitField as MessageFlagsBitField$1, PermissionsBitField, Poll as Poll$1, PollAnswer as PollAnswer$1, Presence as Presence$1, PrivateThreadChannel as PrivateThreadChannel$1, PublicThreadChannel as PublicThreadChannel$1, Reaction, SoundboardSound as SoundboardSound$1, StageChannel as StageChannel$1, StageInstance as StageInstance$1, Sticker as Sticker$1, StickerPack as StickerPack$1, Structure as Structure$1, TextChannel as TextChannel$1, User as User$1, VoiceChannel as VoiceChannel$1, VoiceState as VoiceState$1, Webhook as Webhook$1 } from "@discordjs/structures";
7
7
  import { readFile } from "node:fs/promises";
@@ -725,8 +725,9 @@ var PermissionOverwriteManager = class {
725
725
  }
726
726
  async guessType(target, id) {
727
727
  if (typeof target !== "string") return "hoist" in target ? OverwriteType.Role : OverwriteType.Member;
728
- if (id === this.guildId) return OverwriteType.Role;
729
- return (this.guildId ? await this.client.roles.get(this.guildId, id) : void 0) ? OverwriteType.Role : OverwriteType.Member;
728
+ if (id === this.guildId || !this.guildId) return OverwriteType.Role;
729
+ if (this.client.roles.cache) return await this.client.roles.get(this.guildId, id) ? OverwriteType.Role : OverwriteType.Member;
730
+ return (await this.client.roles.fetchAll(this.guildId)).some((role) => role.id === id) ? OverwriteType.Role : OverwriteType.Member;
730
731
  }
731
732
  async patchCached(update) {
732
733
  const cache = this.client.cache?.channels;
@@ -1755,10 +1756,12 @@ var ThreadChannelMixin = class {
1755
1756
  }
1756
1757
  /**
1757
1758
  * Whether the bot is a member of the thread, like discord.js's `ThreadChannel#joined`: from the thread member cache
1758
- * when the thread was built by a manager, else whether the payload carries the bot's thread member.
1759
+ * when the thread was built by a manager, else `true` when the payload carries the bot's thread member.
1760
+ *
1761
+ * @returns `null` when it cannot be told: no thread member cache, and no thread member in the payload.
1759
1762
  */
1760
1763
  get joined() {
1761
- return this[kRelations].joined ?? this[kData].member !== void 0;
1764
+ return this[kRelations].joined ?? (this[kData].member === void 0 ? null : true);
1762
1765
  }
1763
1766
  /**
1764
1767
  * The members of the thread.
@@ -2693,12 +2696,16 @@ function whenCachedMap(ids, get) {
2693
2696
  //#endregion
2694
2697
  //#region src/managers/CachedManager.ts
2695
2698
  /**
2696
- * The base class of every manager: it reads raw data from one of the client's entity caches, falls back to the REST
2697
- * API when asked to, and wraps the result in a {@link StructureMixin | structure}.
2699
+ * The base class of every manager, the `BaseManager` of the discord.js RFC #11426: it reads raw data from one of the
2700
+ * client's entity caches, falls back to the REST API when asked to, and wraps the result in a
2701
+ * {@link StructureMixin | structure} with {@link CachedManager.construct}.
2698
2702
  *
2699
2703
  * @remarks
2700
- * The cache only ever holds raw API data, building structures is always the manager's job. Without a cache, `get`
2701
- * always resolves to `undefined` and `fetch` always hits the API.
2704
+ * The cache only ever holds raw API data, building structures is always the manager's job. Without a store for its
2705
+ * entity (see the client's `makeCache`), `get` always resolves to `undefined` and `fetch` always hits the API.
2706
+ *
2707
+ * A failing store (e.g. Redis being unreachable) emits `cacheError` and, with the client's default
2708
+ * `cacheErrors: "miss"`, is treated as a cache miss.
2702
2709
  *
2703
2710
  * Every method is asynchronous since the cache can be Redis, except {@link CachedManager.cached}, which reads a
2704
2711
  * synchronous cache (`createInMemoryCache`) without awaiting it.
@@ -2810,13 +2817,14 @@ var CachedManager = class {
2810
2817
  * @internal
2811
2818
  */
2812
2819
  async _add(data, cache = true, { key = this.keyOf(data) } = {}) {
2813
- const existing = await this.cache?.get(key);
2814
- const merged = existing ? {
2815
- ...existing,
2816
- ...data
2817
- } : data;
2818
- if (cache) await this.storeRaw(key, merged);
2819
- return this.hydrate(merged);
2820
+ const store = this.cache;
2821
+ if (store === void 0) return this.hydrate(data);
2822
+ if (!cache) {
2823
+ const existing = await this.guard("get", key, () => store.get(key), void 0);
2824
+ return this.hydrate(mergeValues(existing, data));
2825
+ }
2826
+ const { added } = await this.guard("upsert", key, () => store.upsert(key, data), { added: data });
2827
+ return this.hydrate(added);
2820
2828
  }
2821
2829
  /**
2822
2830
  * Gets the cached structure of the entity raw data describes, or builds one from the data when it is not cached.
@@ -2850,7 +2858,7 @@ var CachedManager = class {
2850
2858
  }
2851
2859
  /**
2852
2860
  * Builds the structure of raw data, resolving its relations from the cache. Without relations to resolve, the same
2853
- * as {@link CachedManager.createStructure}.
2861
+ * as {@link CachedManager.construct}.
2854
2862
  *
2855
2863
  * @param data The raw data.
2856
2864
  */
@@ -2866,7 +2874,7 @@ var CachedManager = class {
2866
2874
  * @internal
2867
2875
  */
2868
2876
  _hydrate(data) {
2869
- return this.createStructure(data);
2877
+ return this.construct(data);
2870
2878
  }
2871
2879
  /**
2872
2880
  * Gets an entity from the cache, fetching it from the API (and caching it) on a cache miss.
@@ -2899,13 +2907,35 @@ var CachedManager = class {
2899
2907
  return this.fetch(...args, { force: true });
2900
2908
  }
2901
2909
  /**
2910
+ * Wraps raw data in this manager's structure.
2911
+ *
2912
+ * @param data The raw data.
2913
+ * @deprecated Use {@link CachedManager.construct}.
2914
+ */
2915
+ createStructure(data) {
2916
+ return this.construct(data);
2917
+ }
2918
+ /**
2902
2919
  * Writes raw data to the cache.
2903
2920
  *
2904
2921
  * @param key The cache key of the entity.
2905
2922
  * @param raw The raw data of the entity.
2906
2923
  */
2907
2924
  async storeRaw(key, raw) {
2908
- await this.cache?.set(key, raw);
2925
+ const { cache } = this;
2926
+ if (cache) await this.guard("set", key, () => cache.set(key, raw), void 0);
2927
+ }
2928
+ /**
2929
+ * Gets this manager's store when it can enumerate its entries, for the `listCached` methods.
2930
+ *
2931
+ * @returns The store, or `undefined` when this entity is not cached.
2932
+ * @throws {TypeError} When the store cannot enumerate its entries.
2933
+ */
2934
+ iterableCache() {
2935
+ const { cache } = this;
2936
+ 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)`);
2938
+ return cache;
2909
2939
  }
2910
2940
  /**
2911
2941
  * Gets an entity from the cache by its key, synchronously when every cache it reads is.
@@ -2913,7 +2943,34 @@ var CachedManager = class {
2913
2943
  * @param key The cache key of the entity.
2914
2944
  */
2915
2945
  getByKey(key) {
2916
- return whenAll([this.cache?.get(key)], ([raw]) => raw === void 0 ? void 0 : this.build(raw));
2946
+ const { cache } = this;
2947
+ return whenAll([cache ? this.guard("get", key, () => cache.get(key), void 0) : void 0], ([raw]) => raw === void 0 ? void 0 : this.build(raw));
2948
+ }
2949
+ /**
2950
+ * Runs a cache operation, reporting its failure through `cacheError`. With the client's `cacheErrors: "miss"`, a
2951
+ * failure resolves to `fallback`, otherwise it is rethrown. Synchronous when the operation is.
2952
+ *
2953
+ * @param operation The operation, for `cacheError`.
2954
+ * @param key The key of the entry, for `cacheError`.
2955
+ * @param run Runs the operation.
2956
+ * @param fallback What a failure resolves to under `cacheErrors: "miss"`.
2957
+ */
2958
+ guard(operation, key, run, fallback) {
2959
+ const fail = (error) => {
2960
+ this.client.emit("cacheError", error, {
2961
+ entity: this.entity,
2962
+ key,
2963
+ operation
2964
+ });
2965
+ if (this.client.cacheErrors === "throw") throw error;
2966
+ return fallback;
2967
+ };
2968
+ try {
2969
+ const result = run();
2970
+ return isPromiseLike(result) ? result.catch(fail) : result;
2971
+ } catch (error) {
2972
+ return fail(error);
2973
+ }
2917
2974
  }
2918
2975
  /**
2919
2976
  * Builds the structure of raw data with {@link CachedManager._hydrate}, bound to this manager's client.
@@ -2967,7 +3024,7 @@ var ChannelManager = class extends CachedManager {
2967
3024
  constructor(client) {
2968
3025
  super(client, "channels");
2969
3026
  }
2970
- createStructure(data) {
3027
+ construct(data) {
2971
3028
  return createChannel(data);
2972
3029
  }
2973
3030
  keyOf(data) {
@@ -2997,15 +3054,15 @@ var ChannelManager = class extends CachedManager {
2997
3054
  return whenAll([
2998
3055
  parentId && depth < MaxParentDepth ? this.cache?.get(parentId) : void 0,
2999
3056
  recipient ? client.users._resolveData(recipient) : void 0,
3000
- data.type === ChannelType.GuildStageVoice && guildId ? client.cache?.stageInstances.get(stageInstanceKey(guildId, data.id)) : void 0,
3001
- thread ? client.cache?.threadMembers.get(threadMemberKey(data.id, client.user?.id ?? client.id)) : void 0
3057
+ data.type === ChannelType.GuildStageVoice && guildId ? client.cache?.stageInstances?.get(stageInstanceKey(guildId, data.id)) : void 0,
3058
+ thread ? client.cache?.threadMembers?.get(threadMemberKey(data.id, client.user?.id ?? client.id)) : void 0
3002
3059
  ], ([parentData, resolvedRecipient, stageData, me]) => whenAll([parentData ? this._hydrateInGuild(parentData, guild, depth + 1) : null], ([parent]) => {
3003
3060
  const relations = {
3004
3061
  guild,
3005
3062
  parent
3006
3063
  };
3007
3064
  if (recipient) relations.recipient = resolvedRecipient ?? null;
3008
- if (thread && client.cache) relations.joined = me !== void 0;
3065
+ if (thread && client.cache?.threadMembers) relations.joined = me !== void 0;
3009
3066
  const channel = bindClient(createChannel(data, relations), client);
3010
3067
  if (data.type === ChannelType.GuildStageVoice) relations.stageInstance = stageData ? bindClient(new StageInstance(stageData, {
3011
3068
  guild,
@@ -3051,7 +3108,7 @@ var ChannelManager = class extends CachedManager {
3051
3108
  * @param raw The raw channel.
3052
3109
  */
3053
3110
  async storeRaw(channelId, raw) {
3054
- if (isThreadChannelType(raw.type)) await this.client.cache?.threads.set(channelId, raw);
3111
+ if (isThreadChannelType(raw.type)) await this.client.cache?.threads?.set(channelId, raw);
3055
3112
  else await this.cache?.set(channelId, raw);
3056
3113
  }
3057
3114
  /**
@@ -3341,7 +3398,7 @@ var Guild = class extends AnonymousGuild {
3341
3398
  const resolved = this[kRelations][name];
3342
3399
  if (resolved !== void 0) return resolved;
3343
3400
  const channelId = this[kData][GuildChannelFields[name]];
3344
- if (!channelId || this.client.cache?.channels.synchronous !== true) return null;
3401
+ if (!channelId || this.client.cache?.channels?.synchronous !== true) return null;
3345
3402
  try {
3346
3403
  return this.client.channels.cached(channelId) ?? null;
3347
3404
  } catch {
@@ -5552,7 +5609,7 @@ var AutoModerationRuleManager = class extends CachedManager {
5552
5609
  super(client, "autoModerationRules");
5553
5610
  this.guildId = guildId;
5554
5611
  }
5555
- createStructure(data) {
5612
+ construct(data) {
5556
5613
  return new AutoModerationRule(data);
5557
5614
  }
5558
5615
  keyOf(data) {
@@ -5684,7 +5741,7 @@ var GuildBanManager = class extends CachedManager {
5684
5741
  super(client, "bans");
5685
5742
  this.guildId = guildId;
5686
5743
  }
5687
- createStructure(data) {
5744
+ construct(data) {
5688
5745
  return new GuildBan(data);
5689
5746
  }
5690
5747
  keyOf(data) {
@@ -5844,7 +5901,7 @@ var GuildIntegrationManager = class extends CachedManager {
5844
5901
  super(client, "integrations");
5845
5902
  this.guildId = guildId;
5846
5903
  }
5847
- createStructure(data) {
5904
+ construct(data) {
5848
5905
  return new Integration(data);
5849
5906
  }
5850
5907
  keyOf(data) {
@@ -5914,7 +5971,7 @@ var GuildScheduledEventManager = class extends CachedManager {
5914
5971
  super(client, "scheduledEvents");
5915
5972
  this.guildId = guildId;
5916
5973
  }
5917
- createStructure(data) {
5974
+ construct(data) {
5918
5975
  return new GuildScheduledEvent(data);
5919
5976
  }
5920
5977
  keyOf(data) {
@@ -6125,7 +6182,7 @@ var GuildSoundboardSoundManager = class extends CachedManager {
6125
6182
  super(client, "soundboardSounds");
6126
6183
  this.guildId = guildId;
6127
6184
  }
6128
- createStructure(data) {
6185
+ construct(data) {
6129
6186
  return new SoundboardSound(data);
6130
6187
  }
6131
6188
  keyOf(data) {
@@ -6228,7 +6285,7 @@ var StageInstanceManager = class extends CachedManager {
6228
6285
  super(client, "stageInstances");
6229
6286
  this.guildId = guildId;
6230
6287
  }
6231
- createStructure(data) {
6288
+ construct(data) {
6232
6289
  return new StageInstance(data);
6233
6290
  }
6234
6291
  keyOf(data) {
@@ -6308,7 +6365,7 @@ var GuildEmojiManager = class extends CachedManager {
6308
6365
  super(client, "emojis");
6309
6366
  this.guildId = guildId;
6310
6367
  }
6311
- createStructure(data) {
6368
+ construct(data) {
6312
6369
  return new GuildEmoji(data);
6313
6370
  }
6314
6371
  keyOf(data) {
@@ -6391,9 +6448,12 @@ var GuildEmojiManager = class extends CachedManager {
6391
6448
  * @remarks
6392
6449
  * It enumerates the whole entity cache, which a Redis store answers from its index: prefer `fetchAll` when the
6393
6450
  * cache may be incomplete.
6451
+ *
6452
+ * @returns The cached entries, `[]` when this entity is not cached.
6453
+ * @throws {TypeError} When the store cannot enumerate its entries.
6394
6454
  */
6395
6455
  async listCached() {
6396
- const cache = this.cache;
6456
+ const cache = this.iterableCache();
6397
6457
  if (!cache) return [];
6398
6458
  const prefix = `${this.guildId}:`;
6399
6459
  const keys = (await cache.keys()).filter((key) => key.startsWith(prefix));
@@ -6428,7 +6488,7 @@ var GuildInviteManager = class extends CachedManager {
6428
6488
  super(client, "invites");
6429
6489
  this.guildId = guildId;
6430
6490
  }
6431
- createStructure(data) {
6491
+ construct(data) {
6432
6492
  return new GuildInvite(data);
6433
6493
  }
6434
6494
  keyOf(data) {
@@ -6536,7 +6596,7 @@ var GuildStickerManager = class extends CachedManager {
6536
6596
  super(client, "stickers");
6537
6597
  this.guildId = guildId;
6538
6598
  }
6539
- createStructure(data) {
6599
+ construct(data) {
6540
6600
  return new Sticker(data);
6541
6601
  }
6542
6602
  keyOf(data) {
@@ -6620,9 +6680,12 @@ var GuildStickerManager = class extends CachedManager {
6620
6680
  * @remarks
6621
6681
  * It enumerates the whole entity cache, which a Redis store answers from its index: prefer `fetchAll` when the
6622
6682
  * cache may be incomplete.
6683
+ *
6684
+ * @returns The cached entries, `[]` when this entity is not cached.
6685
+ * @throws {TypeError} When the store cannot enumerate its entries.
6623
6686
  */
6624
6687
  async listCached() {
6625
- const cache = this.cache;
6688
+ const cache = this.iterableCache();
6626
6689
  if (!cache) return [];
6627
6690
  const prefix = `${this.guildId}:`;
6628
6691
  const keys = (await cache.keys()).filter((key) => key.startsWith(prefix));
@@ -6652,7 +6715,7 @@ var GuildManager = class extends CachedManager {
6652
6715
  constructor(client) {
6653
6716
  super(client, "guilds");
6654
6717
  }
6655
- createStructure(data) {
6718
+ construct(data) {
6656
6719
  return new Guild(data);
6657
6720
  }
6658
6721
  keyOf(data) {
@@ -6684,7 +6747,7 @@ var GuildManager = class extends CachedManager {
6684
6747
  * @internal
6685
6748
  */
6686
6749
  _getShallow(guildId) {
6687
- return whenAll([this.cache?.get(guildId)], ([raw]) => raw === void 0 ? void 0 : bindClient(this.createStructure(raw), this.client));
6750
+ return whenAll([this.cache?.get(guildId)], ([raw]) => raw === void 0 ? void 0 : bindClient(this.construct(raw), this.client));
6688
6751
  }
6689
6752
  resolveKey(guildId) {
6690
6753
  return guildId;
@@ -7151,7 +7214,12 @@ var GuildMemberRoleManager = class {
7151
7214
  */
7152
7215
  async fetch() {
7153
7216
  const ids = [...this.#roleIds, this.guildId];
7154
- return (await Promise.all(ids.map((id) => this.client.roles.fetch(this.guildId, id)))).toSorted((a, b) => b.comparePositionTo(a));
7217
+ let roles = (await Promise.all(ids.map((id) => this.client.roles.get(this.guildId, id)))).filter((role) => role !== void 0);
7218
+ if (roles.length !== ids.length) {
7219
+ const wanted = new Set(ids);
7220
+ roles = (await this.client.roles.fetchAll(this.guildId)).filter((role) => wanted.has(role.id));
7221
+ }
7222
+ return roles.toSorted((a, b) => b.comparePositionTo(a));
7155
7223
  }
7156
7224
  /**
7157
7225
  * Fetches the member's highest role, `@everyone` when they have no other.
@@ -8175,7 +8243,7 @@ var GuildMemberManager = class extends CachedManager {
8175
8243
  constructor(client) {
8176
8244
  super(client, "members");
8177
8245
  }
8178
- createStructure(data) {
8246
+ construct(data) {
8179
8247
  return new GuildMember(data);
8180
8248
  }
8181
8249
  keyOf(data) {
@@ -8206,8 +8274,8 @@ var GuildMemberManager = class extends CachedManager {
8206
8274
  return whenAll([
8207
8275
  data.user ? client.users._resolveData(data.user) : void 0,
8208
8276
  this.cachedGuild(data.guild_id),
8209
- key ? client.cache?.voiceStates.get(key) : void 0,
8210
- key ? client.cache?.presences.get(key) : void 0
8277
+ key ? client.cache?.voiceStates?.get(key) : void 0,
8278
+ key ? client.cache?.presences?.get(key) : void 0
8211
8279
  ], ([user, guild, voiceData, presenceData]) => {
8212
8280
  const relations = {
8213
8281
  user,
@@ -8215,18 +8283,16 @@ var GuildMemberManager = class extends CachedManager {
8215
8283
  };
8216
8284
  const member = bindClient(new GuildMember(data, relations), client);
8217
8285
  return whenAll([voiceData?.channel_id ? client.channels._get(voiceData.channel_id) : void 0], ([channel]) => {
8218
- if (key && client.cache) {
8219
- relations.voice = voiceData ? bindClient(new VoiceState(voiceData, {
8220
- member,
8221
- guild,
8222
- channel: channel ?? null
8223
- }), client) : null;
8224
- relations.presence = presenceData ? bindClient(new Presence(presenceData, {
8225
- user: user ?? null,
8226
- member,
8227
- guild
8228
- }), client) : null;
8229
- }
8286
+ if (key && client.cache?.voiceStates) relations.voice = voiceData ? bindClient(new VoiceState(voiceData, {
8287
+ member,
8288
+ guild,
8289
+ channel: channel ?? null
8290
+ }), client) : null;
8291
+ if (key && client.cache?.presences) relations.presence = presenceData ? bindClient(new Presence(presenceData, {
8292
+ user: user ?? null,
8293
+ member,
8294
+ guild
8295
+ }), client) : null;
8230
8296
  return member;
8231
8297
  });
8232
8298
  });
@@ -9430,7 +9496,7 @@ var Message = class Message extends Message$1 {
9430
9496
  const resolved = this[kRelations].thread;
9431
9497
  if (resolved) return resolved;
9432
9498
  const { thread } = this[kData];
9433
- return thread ? this.client.threads.createStructure(thread) : null;
9499
+ return thread ? this.client.threads.construct(thread) : null;
9434
9500
  }
9435
9501
  /**
9436
9502
  * The content with user, role, and channel mentions replaced by names, and `@everyone`/`@here` defused, like
@@ -9668,7 +9734,7 @@ var MessageManager = class extends CachedManager {
9668
9734
  constructor(client) {
9669
9735
  super(client, "messages");
9670
9736
  }
9671
- createStructure(data) {
9737
+ construct(data) {
9672
9738
  return new Message(data);
9673
9739
  }
9674
9740
  keyOf(data) {
@@ -9693,7 +9759,7 @@ var MessageManager = class extends CachedManager {
9693
9759
  _hydrate(data) {
9694
9760
  const { author, member, guild_id: guildId } = data;
9695
9761
  return whenAll([
9696
- data.webhook_id ? this.client.users.createStructure(author) : this.client.users._resolveData(author),
9762
+ data.webhook_id ? this.client.users.construct(author) : this.client.users._resolveData(author),
9697
9763
  member && guildId ? this.client.members._resolveData({
9698
9764
  ...member,
9699
9765
  user: author,
@@ -10201,7 +10267,7 @@ var RoleManager = class extends CachedManager {
10201
10267
  constructor(client) {
10202
10268
  super(client, "roles");
10203
10269
  }
10204
- createStructure(data) {
10270
+ construct(data) {
10205
10271
  return new Role(data);
10206
10272
  }
10207
10273
  keyOf(data) {
@@ -10769,7 +10835,7 @@ var VoiceStateManager = class extends CachedManager {
10769
10835
  constructor(client) {
10770
10836
  super(client, "voiceStates");
10771
10837
  }
10772
- createStructure(data) {
10838
+ construct(data) {
10773
10839
  return new VoiceState(data);
10774
10840
  }
10775
10841
  keyOf(data) {
@@ -10798,10 +10864,12 @@ var VoiceStateManager = class extends CachedManager {
10798
10864
  * Lists the cached voice states of a guild.
10799
10865
  *
10800
10866
  * @param guildId The ID of the guild.
10867
+ * @returns The cached entries, `[]` when this entity is not cached.
10868
+ * @throws {TypeError} When the store cannot enumerate its entries.
10801
10869
  */
10802
10870
  async listCached(guildId) {
10803
10871
  const prefix = `${guildId}:`;
10804
- const entries = await this.cache?.entries() ?? [];
10872
+ const entries = await this.iterableCache()?.entries() ?? [];
10805
10873
  return Promise.all(entries.filter(([key]) => key.startsWith(prefix)).map(([, raw]) => this.hydrate(raw)));
10806
10874
  }
10807
10875
  async fetchRaw(guildId, userId) {
@@ -10827,7 +10895,7 @@ var PresenceManager = class extends CachedManager {
10827
10895
  constructor(client) {
10828
10896
  super(client, "presences");
10829
10897
  }
10830
- createStructure(data) {
10898
+ construct(data) {
10831
10899
  return new Presence(data);
10832
10900
  }
10833
10901
  keyOf(data) {
@@ -10851,14 +10919,16 @@ var PresenceManager = class extends CachedManager {
10851
10919
  * Lists the cached presences of a guild.
10852
10920
  *
10853
10921
  * @param guildId The ID of the guild.
10922
+ * @returns The cached entries, `[]` when this entity is not cached.
10923
+ * @throws {TypeError} When the store cannot enumerate its entries.
10854
10924
  */
10855
10925
  async listCached(guildId) {
10856
10926
  const prefix = `${guildId}:`;
10857
- const entries = await this.cache?.entries() ?? [];
10927
+ const entries = await this.iterableCache()?.entries() ?? [];
10858
10928
  return Promise.all(entries.filter(([key]) => key.startsWith(prefix)).map(([, raw]) => this.hydrate(raw)));
10859
10929
  }
10860
10930
  fetchRaw(guildId, userId) {
10861
- return Promise.reject(/* @__PURE__ */ new Error(`Presences cannot be fetched from the API (user ${userId} of guild ${guildId} is not cached)`));
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`));
10862
10932
  }
10863
10933
  };
10864
10934
 
@@ -10871,7 +10941,7 @@ var ThreadManager = class extends CachedManager {
10871
10941
  constructor(client) {
10872
10942
  super(client, "threads");
10873
10943
  }
10874
- createStructure(data) {
10944
+ construct(data) {
10875
10945
  return createChannel(data);
10876
10946
  }
10877
10947
  keyOf(data) {
@@ -11062,7 +11132,7 @@ var ThreadMemberManager = class extends CachedManager {
11062
11132
  constructor(client) {
11063
11133
  super(client, "threadMembers");
11064
11134
  }
11065
- createStructure(data) {
11135
+ construct(data) {
11066
11136
  return new ThreadMember(data);
11067
11137
  }
11068
11138
  keyOf(data) {
@@ -11176,7 +11246,7 @@ var UserManager = class extends CachedManager {
11176
11246
  constructor(client) {
11177
11247
  super(client, "users");
11178
11248
  }
11179
- createStructure(data) {
11249
+ construct(data) {
11180
11250
  return new User(data);
11181
11251
  }
11182
11252
  keyOf(data) {
@@ -11207,7 +11277,7 @@ var UserManager = class extends CachedManager {
11207
11277
  async deleteDM(userId) {
11208
11278
  const channel = await this.createDM(userId);
11209
11279
  await this.client.api.channels.delete(channel.id);
11210
- await this.client.cache?.channels.delete(channel.id);
11280
+ await this.client.cache?.channels?.delete(channel.id);
11211
11281
  return channel;
11212
11282
  }
11213
11283
  /**
@@ -11673,6 +11743,7 @@ let GatewayEvents = /* @__PURE__ */ function(GatewayEvents) {
11673
11743
  GatewayEvents["ShardResume"] = "shardResume";
11674
11744
  GatewayEvents["ShardClose"] = "shardClose";
11675
11745
  GatewayEvents["ShardError"] = "shardError";
11746
+ GatewayEvents["CacheError"] = "cacheError";
11676
11747
  GatewayEvents["GuildCreate"] = "guildCreate";
11677
11748
  GatewayEvents["GuildUpdate"] = "guildUpdate";
11678
11749
  GatewayEvents["GuildDelete"] = "guildDelete";
@@ -11705,9 +11776,11 @@ let GatewayEvents = /* @__PURE__ */ function(GatewayEvents) {
11705
11776
  GatewayEvents["GuildRoleUpdate"] = "guildRoleUpdate";
11706
11777
  GatewayEvents["GuildRoleDelete"] = "guildRoleDelete";
11707
11778
  GatewayEvents["UserUpdate"] = "userUpdate";
11779
+ GatewayEvents["GuildEmojisUpdate"] = "guildEmojisUpdate";
11708
11780
  GatewayEvents["EmojiCreate"] = "emojiCreate";
11709
11781
  GatewayEvents["EmojiUpdate"] = "emojiUpdate";
11710
11782
  GatewayEvents["EmojiDelete"] = "emojiDelete";
11783
+ GatewayEvents["GuildStickersUpdate"] = "guildStickersUpdate";
11711
11784
  GatewayEvents["StickerCreate"] = "stickerCreate";
11712
11785
  GatewayEvents["StickerUpdate"] = "stickerUpdate";
11713
11786
  GatewayEvents["StickerDelete"] = "stickerDelete";
@@ -12174,32 +12247,48 @@ const DispatchHandlers = {
12174
12247
  * The table of the dispatches that turn into several events, diffed against the cache.
12175
12248
  *
12176
12249
  * @remarks
12177
- * Without a cache there is nothing to diff against, so these dispatches only reach `raw`.
12250
+ * Their aggregate event (e.g. `guildEmojisUpdate`) is always emitted. Without a cache there is nothing to diff
12251
+ * against, so the granular events are not.
12178
12252
  */
12179
12253
  const MultiDispatchHandlers = {
12180
12254
  [GatewayDispatchEvents.GuildEmojisUpdate]: {
12181
- before: (client, data) => client.guilds.emojis(data.guild_id).listCached(),
12255
+ before: (client, data) => cachedList(client.cache?.emojis, () => client.guilds.emojis(data.guild_id).listCached()),
12182
12256
  emit: async (client, data, previous) => {
12183
- if (!client.cache || !previous) return [];
12184
12257
  const emojis = client.guilds.emojis(data.guild_id);
12185
- return diff(previous, await Promise.all(data.emojis.map((emoji) => emojis.hydrate({
12258
+ const current = await Promise.all(data.emojis.map((emoji) => emojis.hydrate({
12186
12259
  ...emoji,
12187
12260
  guild_id: data.guild_id
12188
- }))), "emojiCreate", "emojiUpdate", "emojiDelete");
12261
+ })));
12262
+ const events = [[
12263
+ "guildEmojisUpdate",
12264
+ data.guild_id,
12265
+ current
12266
+ ]];
12267
+ if (!previous) return events;
12268
+ return events.concat(diff(previous, current, "emojiCreate", "emojiUpdate", "emojiDelete"));
12189
12269
  }
12190
12270
  },
12191
12271
  [GatewayDispatchEvents.GuildStickersUpdate]: {
12192
- before: (client, data) => client.guilds.stickers(data.guild_id).listCached(),
12272
+ before: (client, data) => cachedList(client.cache?.stickers, () => client.guilds.stickers(data.guild_id).listCached()),
12193
12273
  emit: async (client, data, previous) => {
12194
- if (!client.cache || !previous) return [];
12195
12274
  const stickers = client.guilds.stickers(data.guild_id);
12196
- return diff(previous, await Promise.all(data.stickers.map((sticker) => stickers.hydrate({
12275
+ const current = await Promise.all(data.stickers.map((sticker) => stickers.hydrate({
12197
12276
  ...sticker,
12198
12277
  guild_id: data.guild_id
12199
- }))), "stickerCreate", "stickerUpdate", "stickerDelete");
12278
+ })));
12279
+ const events = [[
12280
+ "guildStickersUpdate",
12281
+ data.guild_id,
12282
+ current
12283
+ ]];
12284
+ if (!previous) return events;
12285
+ return events.concat(diff(previous, current, "stickerCreate", "stickerUpdate", "stickerDelete"));
12200
12286
  }
12201
12287
  }
12202
12288
  };
12289
+ async function cachedList(store, list) {
12290
+ return store && isIterableCache(store) ? list() : void 0;
12291
+ }
12203
12292
  function diff(previous, current, create, update, remove) {
12204
12293
  const before = new Map(previous.map((value) => [value.id, value]));
12205
12294
  const events = [];
@@ -12646,6 +12735,10 @@ var GatewayClient = class extends Client {
12646
12735
  */
12647
12736
  cacheFailure;
12648
12737
  /**
12738
+ * What the managers do when a cache read or write fails, see {@link GatewayClientOptions.cacheErrors}.
12739
+ */
12740
+ cacheErrors;
12741
+ /**
12649
12742
  * See {@link GatewayClientOptions.dispatchTimeout}.
12650
12743
  */
12651
12744
  dispatchTimeout;
@@ -12675,7 +12768,8 @@ var GatewayClient = class extends Client {
12675
12768
  super(options);
12676
12769
  container.gatewayClient = this;
12677
12770
  this.rest = container.rest;
12678
- this.cache = options.cache;
12771
+ this.cache = resolveCache(options);
12772
+ this.cacheErrors = options.cacheErrors ?? "miss";
12679
12773
  this.cacheFailure = options.cacheFailure ?? "skip";
12680
12774
  this.dispatchTimeout = options.dispatchTimeout === void 0 ? 3e4 : options.dispatchTimeout;
12681
12775
  this.partials = Object.freeze([...options.partials ?? []]);
@@ -12962,6 +13056,8 @@ var GatewayClient = class extends Client {
12962
13056
  * They are the guilds the bot left while it was disconnected, or while the process was down with a persistent
12963
13057
  * cache. Discord does not replay those removals on a new session, so the cache would otherwise keep them forever.
12964
13058
  *
13059
+ * It needs a guilds cache able to enumerate its entries, and is skipped without one.
13060
+ *
12965
13061
  * It is best effort: any failure (cache unreachable, unknown shard count) is reported through `error` and stops the
12966
13062
  * reconciliation, keeping the remaining guilds, but never fails `READY` itself.
12967
13063
  *
@@ -12976,8 +13072,9 @@ var GatewayClient = class extends Client {
12976
13072
  }
12977
13073
  }
12978
13074
  async dropUnlistedGuilds(data, shardId) {
12979
- if (!this.cache) return;
12980
- const cached = await this.cache.guilds.keys();
13075
+ const guilds = this.cache?.guilds;
13076
+ if (!guilds || !isIterableCache(guilds)) return;
13077
+ const cached = await guilds.keys();
12981
13078
  if (cached.length === 0) return;
12982
13079
  const count = data.shard?.[1] ?? this.#shardCount ?? await this.gateway.getShardCount();
12983
13080
  const listed = new Set(data.guilds.map((guild) => guild.id));
@@ -13030,6 +13127,20 @@ var GatewayClient = class extends Client {
13030
13127
  else this.logger.error(`[Gateway] [Shard ${shardId}] Failed to process ${type}:`, error);
13031
13128
  }
13032
13129
  };
13130
+ /**
13131
+ * Builds the cache of a client out of its options: the stores of `makeCache` (or `cache`), wrapped by `policies`.
13132
+ * `undefined` when no entity kind is cached.
13133
+ */
13134
+ function resolveCache(options) {
13135
+ const { makeCache, cache, policies } = options;
13136
+ const source = makeCache ?? (cache ? (entity) => cache[entity] : void 0);
13137
+ if (!source) return void 0;
13138
+ const resolved = source === makeCache || policies ? createCache({
13139
+ makeCache: source,
13140
+ policies
13141
+ }) : cache;
13142
+ return Object.keys(resolved).length === 0 ? void 0 : resolved;
13143
+ }
13033
13144
 
13034
13145
  //#endregion
13035
13146
  //#region src/util/decorators.ts
@@ -13069,5 +13180,5 @@ function RegisterAsGatewayListener(event, options = {}) {
13069
13180
  }
13070
13181
 
13071
13182
  //#endregion
13072
- export { Action, ActionsManager, Activity, ActivityFlagsBitField, AnnouncementChannel, AnnouncementThreadChannel, AnonymousGuild, AppliedTagsMixin, Attachment, AttachmentFlagsBitField, AutoModerationActionExecution, AutoModerationRule, AutoModerationRuleManager, BaseChannel, BaseChannelMixin, BaseInvite, BitField, CachedManager, 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 };
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 };
13073
13184
  //# sourceMappingURL=index.js.map