@wolfstar/plugin-gateway 0.8.0 → 0.9.0-next-20261001183204

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,13 +1,14 @@
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, createCache, emojiKey, integrationKey, inviteKey, isIterableCache, memberKey, mergeValues, messageKey, presenceKey, roleKey, scheduledEventKey, soundboardSoundKey, stageInstanceKey, stickerKey, threadMemberKey, voiceStateKey } from "@wolfstar/plugin-cache";
4
+ import { MemoryEntityCache, applyGatewayDispatch, autoModerationRuleKey, banKey, createCache, emojiKey, integrationKey, inviteKey, isIterableCache, memberKey, messageKey, presenceKey, roleKey, scheduledEventKey, soundboardSoundKey, stageInstanceKey, stickerKey, threadMemberKey, voiceStateKey } from "@wolfstar/plugin-cache";
5
5
  import { ActivityType, ApplicationFlags, AuditLogEvent, ButtonStyle, ChannelType, ComponentType, FormattingPatterns, GatewayDispatchEvents, GatewayIntentBits, GatewayOpcodes, GuildFeature, GuildMemberFlags, GuildPremiumTier, GuildScheduledEventStatus, GuildSystemChannelFlags, GuildWidgetStyle, InviteType, MessageFlags, MessageReferenceType, MessageType, OverwriteType, PermissionFlagsBits, PresenceUpdateStatus, RoleFlags, RouteBases, Routes, SeparatorSpacingSize, StickerFormatType, UserFlags, WebhookType } from "discord-api-types/v10";
6
6
  import { ActionRowComponent, 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, ChannelSelectMenuComponent as ChannelSelectMenuComponent$1, Component as Component$1, ContainerComponent as ContainerComponent$1, DMChannel as DMChannel$1, Embed as Embed$1, Emoji as Emoji$1, FileComponent as FileComponent$1, FileUploadComponent as FileUploadComponent$1, ForumChannel as ForumChannel$1, GroupDMChannel as GroupDMChannel$1, InteractiveButtonComponent as InteractiveButtonComponent$1, Invite, LinkButtonComponent as LinkButtonComponent$1, MediaChannel as MediaChannel$1, MediaGalleryComponent as MediaGalleryComponent$1, MediaGalleryItem as MediaGalleryItem$1, MentionableSelectMenuComponent as MentionableSelectMenuComponent$1, Message as Message$1, MessageFlagsBitField as MessageFlagsBitField$1, PermissionsBitField, Poll as Poll$1, PollAnswer as PollAnswer$1, PremiumButtonComponent as PremiumButtonComponent$1, Presence as Presence$1, PrivateThreadChannel as PrivateThreadChannel$1, PublicThreadChannel as PublicThreadChannel$1, Reaction, RoleSelectMenuComponent as RoleSelectMenuComponent$1, SectionComponent as SectionComponent$1, SeparatorComponent as SeparatorComponent$1, SoundboardSound as SoundboardSound$1, StageChannel as StageChannel$1, StageInstance as StageInstance$1, Sticker as Sticker$1, StickerPack as StickerPack$1, StringSelectMenuComponent as StringSelectMenuComponent$1, Structure as Structure$1, TextChannel as TextChannel$1, TextDisplayComponent as TextDisplayComponent$1, TextInputComponent as TextInputComponent$1, ThumbnailComponent as ThumbnailComponent$1, UnfurledMediaItem as UnfurledMediaItem$1, User as User$1, UserSelectMenuComponent as UserSelectMenuComponent$1, VoiceChannel as VoiceChannel$1, VoiceState as VoiceState$1, Webhook as Webhook$1 } from "@discordjs/structures";
7
7
  import { CDN, DiscordAPIError } from "@discordjs/rest";
8
8
  import { basename as basename$1, parse, resolve } from "node:path";
9
9
  import { readFile, stat } from "node:fs/promises";
10
10
  import { randomBytes } from "node:crypto";
11
+ import { Collection } from "@discordjs/collection";
11
12
 
12
13
  //#region src/EventGatewayListener.ts
13
14
  /**
@@ -59,13 +60,14 @@ var EventGatewayListener = class extends Listener {
59
60
  */
60
61
  const GatewayErrorMessages = {
61
62
  ClientSessionStoreConflict: "sessionStore replaces gateway.retrieveSessionInfo and gateway.updateSessionInfo, pass one or the other",
63
+ ClientCacheConflict: "cacheConstructor and cacheOptions cannot be combined with cache or makeCache, pass one or the other",
62
64
  ClientNotConstructed: "No GatewayClient has been constructed yet",
63
65
  DispatchHandlerConflict: (event) => `Dispatch event "${event}" is registered in both DispatchHandlers and MultiDispatchHandlers`,
64
66
  DispatchTimeout: (type, shardId, partition, timeout) => `Processing ${type} on shard ${shardId} (${partition ?? "barrier"}) took longer than ${timeout}ms`,
65
67
  SessionStoreFailed: (operation, shardId) => `Cannot ${operation === "get" ? "read" : "write"} the session of shard ${shardId} in the session store`,
66
68
  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
+ CacheAsynchronous: (entity) => `The ${entity} cache is asynchronous, await cache.get instead`,
70
+ CacheConstructorAsynchronous: (entity) => `The ${entity} structures resolve relations from an asynchronous cache, which a cache of instances cannot await`,
69
71
  CacheNotIterable: (entity) => `The ${entity} cache cannot enumerate its entries (it has no keys/values/entries)`,
70
72
  CacheKeyUnresolvable: (entity, reason) => `Cannot key a ${entity} ${reason}`,
71
73
  IdUnresolvable: "Cannot resolve an ID from a structure without one",
@@ -645,6 +647,21 @@ function whenAll(values, callback) {
645
647
  return values.some(isPromiseLike) ? Promise.all(values).then(callback) : callback(values);
646
648
  }
647
649
  /**
650
+ * Resolves the relations of a structure again from its current data, and assigns them to that same structure: what
651
+ * keeps a long-lived instance from holding the relations of the day it was built. Synchronous when `hydrate` is.
652
+ *
653
+ * @param value The structure.
654
+ * @param hydrate Builds a structure of the same data, with its relations.
655
+ * @returns The structure it was given.
656
+ * @internal
657
+ */
658
+ function refreshRelations(value, hydrate) {
659
+ return whenAll([hydrate(value.toJSON())], ([fresh]) => {
660
+ value[kRelations] = fresh[kRelations];
661
+ return value;
662
+ });
663
+ }
664
+ /**
648
665
  * Picks the cached structures of some IDs, in their order, skipping the ones that are not cached: the equivalent of
649
666
  * the collections discord.js fills from its caches, e.g. `MessageMentions#channels`.
650
667
  *
@@ -676,6 +693,17 @@ function whenCachedMap(ids, get) {
676
693
  return map;
677
694
  });
678
695
  }
696
+ /**
697
+ * Reads what a {@link Cache} holds under a key as is: for a cache of instances (a `Map`, e.g. `CollectionCache`), the
698
+ * instance itself, without re-resolving its relations as `get` does. Other caches are read with `get`.
699
+ *
700
+ * @param cache The cache.
701
+ * @param key The key of the entry.
702
+ * @internal
703
+ */
704
+ function peekCache(cache, key) {
705
+ return cache instanceof Map ? Map.prototype.get.call(cache, key) : cache.get(key);
706
+ }
679
707
 
680
708
  //#endregion
681
709
  //#region src/util/Colors.ts
@@ -1019,16 +1047,16 @@ function computePositions(id, position, relative, sorted) {
1019
1047
  */
1020
1048
  function transformResolved({ client, guildId }, { users, members, roles, channels }) {
1021
1049
  return whenAll([
1022
- users && resolveEach(users, (id) => client.users._get(id), (raw) => client.users._resolveData(raw)),
1023
- guildId && members ? resolveEach(members, (id) => client.members._get(guildId, id), (raw) => client.members._resolveData({
1050
+ users && resolveEach(users, (id) => client.users.cache.get(id), (raw) => client.users._resolveData(raw)),
1051
+ guildId && members ? resolveEach(members, (id) => client.members.cache.get(client.members.resolveKey(guildId, id)), (raw) => client.members._resolveData({
1024
1052
  ...raw,
1025
1053
  guild_id: guildId
1026
1054
  }), (raw) => raw.user.id) : void 0,
1027
- guildId && roles ? resolveEach(roles, (id) => client.roles._get(guildId, id), (raw) => client.roles._resolveData({
1055
+ guildId && roles ? resolveEach(roles, (id) => client.roles.cache.get(client.roles.resolveKey(guildId, id)), (raw) => client.roles._resolveData({
1028
1056
  ...raw,
1029
1057
  guild_id: guildId
1030
1058
  })) : void 0,
1031
- channels && resolveEach(channels, (id) => client.channels._get(id), (raw) => client.channels._resolveData(raw))
1059
+ channels && resolveEach(channels, (id) => client.channels.cache.get(id), (raw) => client.channels._resolveData(raw))
1032
1060
  ], ([resolvedUsers, resolvedMembers, resolvedRoles, resolvedChannels]) => {
1033
1061
  const result = {};
1034
1062
  if (resolvedUsers) result.users = resolvedUsers;
@@ -2132,13 +2160,27 @@ var PermissionOverwrites = class extends Structure {
2132
2160
  }
2133
2161
  };
2134
2162
 
2163
+ //#endregion
2164
+ //#region src/managers/BaseManager.ts
2165
+ /**
2166
+ * Manages the API methods of a data model, like discord.js's `BaseManager`.
2167
+ */
2168
+ var BaseManager = class {
2169
+ /**
2170
+ * The client that instantiated this manager.
2171
+ */
2172
+ client;
2173
+ constructor(client) {
2174
+ this.client = client;
2175
+ }
2176
+ };
2177
+
2135
2178
  //#endregion
2136
2179
  //#region src/managers/PermissionOverwriteManager.ts
2137
2180
  /**
2138
2181
  * Manages the permission overwrites of one channel, as they were in the channel's payload.
2139
2182
  */
2140
- var PermissionOverwriteManager = class {
2141
- client;
2183
+ var PermissionOverwriteManager = class extends BaseManager {
2142
2184
  channelId;
2143
2185
  guildId;
2144
2186
  #overwrites;
@@ -2151,7 +2193,7 @@ var PermissionOverwriteManager = class {
2151
2193
  * @param channel The channel, which the overwrites refer to.
2152
2194
  */
2153
2195
  constructor(client, channelId, guildId, overwrites, channel = null) {
2154
- this.client = client;
2196
+ super(client);
2155
2197
  this.channelId = channelId;
2156
2198
  this.guildId = guildId;
2157
2199
  this.#overwrites = overwrites;
@@ -2246,16 +2288,13 @@ var PermissionOverwriteManager = class {
2246
2288
  async guessType(target, id) {
2247
2289
  if (typeof target !== "string") return "hoist" in target ? OverwriteType.Role : OverwriteType.Member;
2248
2290
  if (id === this.guildId || !this.guildId) return OverwriteType.Role;
2249
- if (this.client.roles.cache) return await this.client.roles.get(this.guildId, id) ? OverwriteType.Role : OverwriteType.Member;
2291
+ if (this.client.cache?.roles) return await this.client.roles.cache.get(this.client.roles.resolveKey(this.guildId, id)) ? OverwriteType.Role : OverwriteType.Member;
2250
2292
  return (await this.client.roles.fetchAll(this.guildId)).some((role) => role.id === id) ? OverwriteType.Role : OverwriteType.Member;
2251
2293
  }
2252
2294
  async patchCached(update) {
2253
- const cache = this.client.cache?.channels;
2254
- const cached = await cache?.get(this.channelId);
2255
- if (!cached || !("permission_overwrites" in cached)) return;
2256
- await cache.set(this.channelId, {
2257
- ...cached,
2258
- permission_overwrites: update(cached.permission_overwrites ?? [])
2295
+ await this.client.channels._patchCached(this.channelId, (cached) => {
2296
+ const data = cached.toJSON();
2297
+ return "permission_overwrites" in data ? { permission_overwrites: update(data.permission_overwrites ?? []) } : void 0;
2259
2298
  });
2260
2299
  }
2261
2300
  };
@@ -2497,12 +2536,11 @@ var ChannelSlowmodeMixin = class {
2497
2536
  /**
2498
2537
  * Manages the threads of one channel: `client.threads`, with the channel's ID filled in.
2499
2538
  */
2500
- var ChannelThreadManager = class {
2501
- client;
2539
+ var ChannelThreadManager = class extends BaseManager {
2502
2540
  channelId;
2503
2541
  guildId;
2504
2542
  constructor(client, channelId, guildId) {
2505
- this.client = client;
2543
+ super(client);
2506
2544
  this.channelId = channelId;
2507
2545
  this.guildId = guildId;
2508
2546
  }
@@ -2677,18 +2715,17 @@ var GuildChannelMixin = class {
2677
2715
  /**
2678
2716
  * Manages the messages of one channel: `client.messages`, with the channel's ID filled in.
2679
2717
  */
2680
- var ChannelMessageManager = class {
2681
- client;
2718
+ var ChannelMessageManager = class extends BaseManager {
2682
2719
  channelId;
2683
2720
  constructor(client, channelId) {
2684
- this.client = client;
2721
+ super(client);
2685
2722
  this.channelId = channelId;
2686
2723
  }
2687
2724
  /**
2688
2725
  * Gets a message of the channel from the cache.
2689
2726
  */
2690
- get(messageId) {
2691
- return this.client.messages.get(this.channelId, messageId);
2727
+ async get(messageId) {
2728
+ return this.client.messages.cache.get(this.client.messages.resolveKey(this.channelId, messageId));
2692
2729
  }
2693
2730
  /**
2694
2731
  * Gets a message of the channel from the cache, falling back to the API.
@@ -2872,18 +2909,17 @@ var ChannelOwnerMixin = class {
2872
2909
  /**
2873
2910
  * Manages the members of one thread: `client.threadMembers`, with the thread's ID filled in.
2874
2911
  */
2875
- var ThreadChannelMemberManager = class {
2876
- client;
2912
+ var ThreadChannelMemberManager = class extends BaseManager {
2877
2913
  threadId;
2878
2914
  constructor(client, threadId) {
2879
- this.client = client;
2915
+ super(client);
2880
2916
  this.threadId = threadId;
2881
2917
  }
2882
2918
  /**
2883
2919
  * Gets a member of the thread from the cache.
2884
2920
  */
2885
- get(userId) {
2886
- return this.client.threadMembers.get(this.threadId, userId);
2921
+ async get(userId) {
2922
+ return this.client.threadMembers.cache.get(this.client.threadMembers.resolveKey(this.threadId, userId));
2887
2923
  }
2888
2924
  /**
2889
2925
  * Gets a member of the thread from the cache, falling back to the API.
@@ -3687,187 +3723,222 @@ var StageInstance = class extends StageInstance$1 {
3687
3723
  Mixin(StageInstance, [StructureMixin]);
3688
3724
 
3689
3725
  //#endregion
3690
- //#region src/managers/CachedManager.ts
3726
+ //#region src/managers/DataManager.ts
3691
3727
  /**
3692
- * The base class of every manager, the `BaseManager` of the discord.js RFC #11426: it reads raw data from one of the
3693
- * client's entity caches, falls back to the REST API when asked to, and wraps the result in a
3694
- * {@link StructureMixin | structure} with {@link CachedManager.construct}.
3695
- *
3696
- * @remarks
3697
- * The cache only ever holds raw API data, building structures is always the manager's job. Without a store for its
3698
- * entity (see the client's `makeCache`), `get` always resolves to `undefined` and `fetch` always hits the API.
3699
- *
3700
- * A failing store (e.g. Redis being unreachable) emits `cacheError` and, with the client's default
3701
- * `cacheErrors: "miss"`, is treated as a cache miss.
3702
- *
3703
- * Every method is asynchronous since the cache can be Redis, except {@link CachedManager.cached}, which reads a
3704
- * synchronous cache (`createInMemoryCache`) without awaiting it.
3705
- *
3706
- * Like discord.js's `CachedManager`, every payload coming from the API goes through {@link CachedManager._add}, which
3707
- * patches the cached entry and builds the structure. Structures are built by {@link CachedManager.hydrate}, which
3708
- * resolves their relations (a message's author, a member's user, ...) from the cache, so they carry the latest known
3709
- * data rather than the copy embedded in the payload.
3728
+ * Manages the API methods of a data model along with a collection of instances, like discord.js's `DataManager`.
3710
3729
  *
3711
- * @typeParam Name The name of the entity cache this manager reads from.
3712
- * @typeParam Value The structure this manager builds.
3730
+ * @typeParam Value The structure this manager holds.
3713
3731
  * @typeParam Args The arguments identifying an entity, e.g. `[id]` or `[guildId, userId]`.
3714
3732
  */
3715
- var CachedManager = class {
3716
- /**
3717
- * The client this manager belongs to.
3718
- */
3719
- client;
3733
+ var DataManager = class extends BaseManager {
3720
3734
  /**
3721
- * The name of the entity cache this manager reads from.
3722
- */
3723
- entity;
3724
- constructor(client, entity) {
3725
- this.client = client;
3726
- this.entity = entity;
3727
- }
3728
- /**
3729
- * The entity cache this manager reads from, or `undefined` when the client has no cache.
3735
+ * Resolves a structure or a cache key to a structure.
3736
+ *
3737
+ * @param value A structure, returned as is, or the cache key of an entity (its ID, for managers keyed by ID).
3738
+ * @returns The structure, or `null` if the key is not cached.
3730
3739
  */
3731
- get cache() {
3732
- return this.client.cache?.[this.entity];
3740
+ resolve(value) {
3741
+ if (typeof value !== "string") return value ?? null;
3742
+ return whenAll([this.cache.get(value)], ([cached]) => cached ?? null);
3733
3743
  }
3734
3744
  /**
3735
- * Gets an entity from the cache.
3745
+ * Resolves a structure or an ID to an ID.
3736
3746
  *
3737
- * @param args The arguments identifying the entity.
3738
- * @returns The entity, or `undefined` if it is not cached.
3747
+ * @param value A structure, or an ID.
3748
+ * @returns The ID, or `null` if the value is neither.
3739
3749
  */
3740
- async get(...args) {
3741
- return this._get(...args);
3750
+ resolveId(value) {
3751
+ if (typeof value === "string") return value;
3752
+ const id = value?.id;
3753
+ return typeof id === "string" ? id : null;
3742
3754
  }
3755
+ };
3756
+
3757
+ //#endregion
3758
+ //#region src/managers/CachedManager.ts
3759
+ /**
3760
+ * Manages the API methods of a data model with a mutable cache of instances: the `CachedManager` of the discord.js
3761
+ * RFC #11426.
3762
+ *
3763
+ * @remarks
3764
+ * Reads go through {@link CachedManager.cache}: `client.users.cache.get(id)`. The cache is built by the client's
3765
+ * `cacheConstructor` (`CollectionCache` by default) and shared by every manager of the same entity.
3766
+ *
3767
+ * Caches keyed by more than an ID take the key built by `resolveKey`:
3768
+ * `client.members.cache.get(client.members.resolveKey(guildId, userId))`.
3769
+ *
3770
+ * @typeParam Name The name of the entity this manager holds.
3771
+ * @typeParam Value The structure this manager builds.
3772
+ * @typeParam Args The arguments identifying an entity, e.g. `[id]` or `[guildId, userId]`.
3773
+ */
3774
+ var CachedManager = class extends DataManager {
3743
3775
  /**
3744
- * Gets an entity from a synchronous cache, without awaiting it: the synchronous counterpart of
3745
- * {@link CachedManager.get}, for hot paths such as message filters.
3746
- *
3747
- * @remarks
3748
- * It builds the same structure as `get`, relations included, so the entity caches they are read from must be
3749
- * synchronous too, which they all are with `createInMemoryCache`. `manager.cache?.synchronous` tells whether it can
3750
- * be called.
3751
- *
3752
- * An asynchronous cache throws rather than returning `undefined`: it cannot tell whether the entity is cached, and
3753
- * reporting a miss would silently skip whatever the caller does with cached entities, e.g. a filter.
3776
+ * The cache of this manager's entity.
3754
3777
  *
3755
3778
  * @example
3756
3779
  * ```typescript
3757
- * const member = client.members.cached(guildId, userId);
3780
+ * const user = await client.users.cache.get(userId);
3758
3781
  * ```
3759
- *
3760
- * @param args The arguments identifying the entity.
3761
- * @returns The entity, or `undefined` if it is not cached or the client has no cache.
3762
- * @throws {TypeError} When the entity cache, or one of the caches its relations are read from, is asynchronous.
3763
3782
  */
3764
- cached(...args) {
3765
- const { cache } = this;
3766
- if (cache === void 0) return void 0;
3767
- if (cache.synchronous !== true) throw new GatewayTypeError("CacheAsynchronous", this.entity);
3768
- const value = this._get(...args);
3769
- if (isPromiseLike(value)) {
3770
- value.catch(() => void 0);
3771
- throw new GatewayTypeError("CacheRelationsAsynchronous", this.entity);
3772
- }
3773
- return value;
3783
+ cache;
3784
+ /**
3785
+ * The name of the entity this manager holds.
3786
+ */
3787
+ name;
3788
+ constructor(client, name) {
3789
+ super(client);
3790
+ this.name = name;
3791
+ this.cache = this.createCache();
3774
3792
  }
3775
3793
  /**
3776
- * Gets an entity from the cache, synchronously when every cache it reads is: the {@link Awaitable} counterpart of
3777
- * {@link CachedManager.get}, which both `get` and {@link CachedManager.cached} rely on.
3778
- *
3779
- * @param args The arguments identifying the entity.
3780
- * @internal
3794
+ * Gets {@link CachedManager.cache} from the client. Managers whose entity spans several caches override it.
3781
3795
  */
3782
- _get(...args) {
3783
- return this.getByKey(this.resolveKey(...args));
3796
+ createCache() {
3797
+ return this.client.CacheConstructor((data) => this.createStructure(data), this.name);
3784
3798
  }
3785
3799
  /**
3786
- * Resolves a structure or a cache key to a structure, like discord.js's `DataManager#resolve`.
3787
- *
3788
- * @param value A structure, returned as is, or the cache key of an entity (its ID, for managers keyed by ID).
3789
- * @returns The structure, or `null` if the key is not cached.
3800
+ * The raw store of this manager's entity, or `undefined` when it is not cached.
3790
3801
  */
3791
- async resolve(value) {
3792
- if (typeof value !== "string") return value;
3793
- return await this.getByKey(value) ?? null;
3802
+ get rawStore() {
3803
+ return this.client.cache?.[this.name];
3794
3804
  }
3795
3805
  /**
3796
- * Adds an API payload to the cache, patching the cached entry with it, and builds its structure. The counterpart of
3797
- * discord.js's `CachedManager#_add`, asynchronous since the cache can be remote.
3806
+ * Adds an API payload to the cache and returns its structure, as in the RFC.
3798
3807
  *
3799
3808
  * @remarks
3800
- * Like discord.js's `_patch`, the payload is shallowly merged into the cached entry, so the fields a partial payload
3801
- * lacks keep their cached value. With `cache` set to `false`, the merged entry is built but not written.
3809
+ * The payload is written with `cache.add`: a cached entry is patched and returned, and with a cache of instances
3810
+ * (`CollectionCache`) that is the very instance the application may already hold. An entity that is not cached is
3811
+ * built and stored.
3802
3812
  *
3803
- * The merge is a read followed by a write, not an atomic operation: on a shared cache, a write landing in between
3804
- * (another process, or a dispatch outside the guild's queue) is overwritten. `@wolfstar/plugin-cache` merges partial
3805
- * dispatches the same way, and the fields at stake are refreshed by the next payload of the entity.
3813
+ * With `cache` set to `false`, a patched clone is returned (or the entity is built) and the cache is left
3814
+ * untouched. The same goes for an entry the `filter` of the entity's policy rejects, whose cached entry is deleted
3815
+ * on top of that: a rejected update must not leave the outdated entry behind.
3806
3816
  *
3807
3817
  * @param data The raw data.
3808
- * @param cache Whether to write the merged entry to the cache.
3809
- * @param options The cache key, when it cannot be derived from the data.
3818
+ * @param cache Whether to write to the cache.
3819
+ * @param options The cache key, when it cannot be derived from the data, and the extras of `createStructure`.
3810
3820
  * @internal
3811
3821
  */
3812
- async _add(data, cache = true, { key = this.keyOf(data) } = {}) {
3813
- const store = this.cache;
3814
- if (store === void 0) return this.hydrate(data);
3815
- if (!cache) {
3816
- const existing = await this.guard("get", key, () => store.get(key), void 0);
3817
- return this.hydrate(mergeValues(existing, data));
3822
+ async _add(data, cache = true, { id, extras = [] } = {}) {
3823
+ const derived = id === void 0 ? this.keyOf(data) : this.derivedKey(data);
3824
+ const key = id ?? derived;
3825
+ if (!cache) return this.detached(await this.cache.get(key), data, extras);
3826
+ if (this.client.cacheFilter(this.name)) {
3827
+ const existing = await this.cache.get(key);
3828
+ if (!this.accepts(key, existing, data)) {
3829
+ if (existing) await this.cache.delete(key);
3830
+ return this.detached(existing, data, extras);
3831
+ }
3832
+ }
3833
+ if (key === derived && extras.length === 0) return this.cache.add(data);
3834
+ const existing = await this.cache.get(key);
3835
+ if (existing) {
3836
+ existing[kPatch](data);
3837
+ await this.cache.set(key, existing);
3838
+ return this.resolveRelations(existing, extras);
3818
3839
  }
3819
- const { added } = await this.guard("upsert", key, () => store.upsert(key, data), { added: data });
3820
- return this.hydrate(added);
3840
+ const entry = await this._build(data, extras);
3841
+ await this.cache.set(key, entry);
3842
+ return entry;
3821
3843
  }
3822
3844
  /**
3823
- * Gets the cached structure of the entity raw data describes, or builds one from the data when it is not cached.
3824
- * Used to resolve the relations of other structures, e.g. a message's author.
3845
+ * Patches the cached entry of a key, if any, with the fields an endpoint answered without the entity itself.
3825
3846
  *
3826
- * @param data The raw data.
3847
+ * @remarks
3848
+ * Like every write of the managers, it follows the `filter` of the entity's policy: an entry whose patched data is
3849
+ * rejected is deleted from the cache.
3850
+ *
3851
+ * @param key The cache key of the entry.
3852
+ * @param patch The fields to patch, or a function computing them from the cached entry, `undefined` to leave it.
3853
+ * @internal
3827
3854
  */
3828
- async resolveData(data) {
3829
- return this._resolveData(data);
3855
+ async _patchCached(key, patch) {
3856
+ const cached = await this.cache.get(key);
3857
+ if (!cached) return;
3858
+ const data = typeof patch === "function" ? patch(cached) : patch;
3859
+ if (data === void 0) return;
3860
+ if (!this.accepts(key, cached, data)) {
3861
+ await this.cache.delete(key);
3862
+ return;
3863
+ }
3864
+ cached[kPatch](data);
3865
+ await this.cache.set(key, cached);
3866
+ }
3867
+ accepts(key, existing, data) {
3868
+ const filter = this.client.cacheFilter(this.name);
3869
+ if (!filter) return true;
3870
+ const cached = existing?.toJSON();
3871
+ return filter(cached ? {
3872
+ ...cached,
3873
+ ...data
3874
+ } : data, key);
3875
+ }
3876
+ detached(existing, data, extras) {
3877
+ return existing ? this.resolveRelations(existing[kClone](data), extras) : this._build(data, extras);
3878
+ }
3879
+ derivedKey(data) {
3880
+ try {
3881
+ return this.keyOf(data);
3882
+ } catch {
3883
+ return;
3884
+ }
3885
+ }
3886
+ resolveRelations(value, extras) {
3887
+ return refreshRelations(value, (data) => this._build(data, extras));
3830
3888
  }
3831
3889
  /**
3832
- * The {@link Awaitable} counterpart of {@link CachedManager.resolveData}, synchronous when every cache it reads is.
3890
+ * Gets the cached structure of the entity raw data describes, or builds one from the data when it is not cached.
3891
+ * Used to resolve the relations of other structures, e.g. a message's author. Synchronous when the cache is.
3833
3892
  *
3834
3893
  * @param data The raw data.
3835
3894
  * @internal
3836
3895
  */
3837
3896
  _resolveData(data) {
3838
- return whenAll([this.getByKey(this.keyOf(data))], ([cached]) => cached ?? this.build(data));
3897
+ return whenAll([this.cache.get(this.keyOf(data))], ([cached]) => cached ?? this._build(data));
3839
3898
  }
3840
3899
  /**
3841
3900
  * Gets a guild from the cache, to resolve the `guild` of a structure. Synchronous when the guild cache is.
3842
3901
  *
3843
- * @param guildId The ID of the guild, if the structure belongs to one.
3844
3902
  * @remarks
3845
3903
  * The guild does not resolve its own channel relations, see `GuildManager._getShallow`.
3846
3904
  *
3905
+ * @param guildId The ID of the guild, if the structure belongs to one.
3847
3906
  * @returns The guild, or `null` when there is no ID or the guild is not cached.
3848
3907
  */
3849
3908
  cachedGuild(guildId) {
3850
3909
  return guildId ? whenAll([this.client.guilds._getShallow(guildId)], ([guild]) => guild ?? null) : null;
3851
3910
  }
3852
3911
  /**
3853
- * Builds the structure of raw data, resolving its relations from the cache. Without relations to resolve, the same
3854
- * as {@link CachedManager.construct}.
3912
+ * Builds the structure of raw data, resolving its relations from the cache; synchronous when every cache the
3913
+ * relations are read from is. Without relations to resolve, the same as `createStructure`.
3855
3914
  *
3856
3915
  * @param data The raw data.
3916
+ * @param extras The extra arguments passed to {@link CachedManager._add}.
3917
+ * @internal
3857
3918
  */
3858
- async hydrate(data) {
3859
- return this.build(data);
3919
+ _hydrate(data, ...extras) {
3920
+ return this.createStructure(data, ...extras);
3860
3921
  }
3861
3922
  /**
3862
- * The {@link Awaitable} counterpart of {@link CachedManager.hydrate}, synchronous when every cache the relations
3863
- * are read from is. Managers resolving relations override this one, so `hydrate` and
3864
- * {@link CachedManager.cached} build the same structures.
3923
+ * Wraps raw data in this manager's structure, bound to this manager's client, without resolving its relations: the
3924
+ * creator handed to this entity's cache, always synchronous.
3865
3925
  *
3866
3926
  * @param data The raw data.
3927
+ * @param extras The extra arguments passed to {@link CachedManager._add}.
3867
3928
  * @internal
3868
3929
  */
3869
- _hydrate(data) {
3870
- return this.construct(data);
3930
+ _construct(data, ...extras) {
3931
+ return bindClient(this.createStructure(data, ...extras), this.client);
3932
+ }
3933
+ /**
3934
+ * Builds the structure of raw data with {@link CachedManager._hydrate}, bound to this manager's client.
3935
+ *
3936
+ * @param data The raw data.
3937
+ * @param extras The extra arguments passed to {@link CachedManager._add}.
3938
+ * @internal
3939
+ */
3940
+ _build(data, extras = []) {
3941
+ return whenAll([this._hydrate(data, ...extras)], ([value]) => bindClient(value, this.client));
3871
3942
  }
3872
3943
  /**
3873
3944
  * Gets an entity from the cache, fetching it from the API (and caching it) on a cache miss.
@@ -3884,12 +3955,13 @@ var CachedManager = class {
3884
3955
  async fetch(...args) {
3885
3956
  const ids = args.slice(0, this.resolveKey.length);
3886
3957
  const { force = false, cache = true } = args[this.resolveKey.length] ?? {};
3958
+ const id = this.resolveKey(...ids);
3887
3959
  if (!force) {
3888
- const cached = await this.get(...ids);
3960
+ const cached = await this.cache.get(id);
3889
3961
  if (cached) return cached;
3890
3962
  }
3891
3963
  const raw = await this.fetchRaw(...ids);
3892
- return this._add(raw, cache, { key: this.resolveKey(...ids) });
3964
+ return this._add(raw, cache, { id });
3893
3965
  }
3894
3966
  /**
3895
3967
  * Fetches an entity from the API, bypassing and then updating the cache. Same as `fetch(...args, { force: true })`.
@@ -3900,78 +3972,23 @@ var CachedManager = class {
3900
3972
  return this.fetch(...args, { force: true });
3901
3973
  }
3902
3974
  /**
3903
- * Wraps raw data in this manager's structure.
3904
- *
3905
- * @param data The raw data.
3906
- * @deprecated Use {@link CachedManager.construct}.
3907
- */
3908
- createStructure(data) {
3909
- return this.construct(data);
3910
- }
3911
- /**
3912
- * Writes raw data to the cache.
3913
- *
3914
- * @param key The cache key of the entity.
3915
- * @param raw The raw data of the entity.
3916
- */
3917
- async storeRaw(key, raw) {
3918
- const { cache } = this;
3919
- if (cache) await this.guard("set", key, () => cache.set(key, raw), void 0);
3920
- }
3921
- /**
3922
- * Gets this manager's store when it can enumerate its entries, for the `listCached` methods.
3975
+ * Gets this entity's raw store when it can enumerate its entries, for the `listCached` methods.
3923
3976
  *
3924
3977
  * @returns The store, or `undefined` when this entity is not cached.
3925
3978
  * @throws {TypeError} When the store cannot enumerate its entries.
3926
3979
  */
3927
3980
  iterableCache() {
3928
- const { cache } = this;
3929
- if (cache === void 0) return void 0;
3930
- if (!isIterableCache(cache)) throw new GatewayTypeError("CacheNotIterable", this.entity);
3931
- return cache;
3981
+ const store = this.rawStore;
3982
+ if (store === void 0) return void 0;
3983
+ if (!isIterableCache(store)) throw new GatewayTypeError("CacheNotIterable", this.name);
3984
+ return store;
3932
3985
  }
3933
3986
  /**
3934
- * Gets an entity from the cache by its key, synchronously when every cache it reads is.
3935
- *
3936
- * @param key The cache key of the entity.
3937
- */
3938
- getByKey(key) {
3939
- const { cache } = this;
3940
- return whenAll([cache ? this.guard("get", key, () => cache.get(key), void 0) : void 0], ([raw]) => raw === void 0 ? void 0 : this.build(raw));
3941
- }
3942
- /**
3943
- * Runs a cache operation, reporting its failure through `cacheError`. With the client's `cacheErrors: "miss"`, a
3944
- * failure resolves to `fallback`, otherwise it is rethrown. Synchronous when the operation is.
3945
- *
3946
- * @param operation The operation, for `cacheError`.
3947
- * @param key The key of the entry, for `cacheError`.
3948
- * @param run Runs the operation.
3949
- * @param fallback What a failure resolves to under `cacheErrors: "miss"`.
3987
+ * Runs a raw store operation, reporting its failure through `cacheError`. With the client's `cacheErrors: "miss"`,
3988
+ * a failure resolves to `fallback`, otherwise it is rethrown. Synchronous when the operation is.
3950
3989
  */
3951
3990
  guard(operation, key, run, fallback) {
3952
- const fail = (error) => {
3953
- this.client.emit("cacheError", error, {
3954
- entity: this.entity,
3955
- key,
3956
- operation
3957
- });
3958
- if (this.client.cacheErrors === "throw") throw error;
3959
- return fallback;
3960
- };
3961
- try {
3962
- const result = run();
3963
- return isPromiseLike(result) ? result.catch(fail) : result;
3964
- } catch (error) {
3965
- return fail(error);
3966
- }
3967
- }
3968
- /**
3969
- * Builds the structure of raw data with {@link CachedManager._hydrate}, bound to this manager's client.
3970
- *
3971
- * @param data The raw data.
3972
- */
3973
- build(data) {
3974
- return whenAll([this._hydrate(data)], ([value]) => bindClient(value, this.client));
3991
+ return this.client.guardCache(this.name, operation, key, run, fallback);
3975
3992
  }
3976
3993
  };
3977
3994
 
@@ -4007,17 +4024,59 @@ function channelGuildId(data) {
4007
4024
  return "guild_id" in data ? data.guild_id ?? void 0 : void 0;
4008
4025
  }
4009
4026
  /**
4027
+ * The cache of {@link ChannelManager}: the channel cache, falling back to the thread cache.
4028
+ */
4029
+ var ChannelCache = class {
4030
+ #channels;
4031
+ #threads;
4032
+ constructor(channels, threads) {
4033
+ this.#channels = channels;
4034
+ this.#threads = threads;
4035
+ }
4036
+ get synchronous() {
4037
+ return this.#channels.synchronous && this.#threads().synchronous;
4038
+ }
4039
+ get construct() {
4040
+ return this.#channels.construct;
4041
+ }
4042
+ add(data, overwrite = false) {
4043
+ return data.type !== void 0 && isThreadChannelType(data.type) ? this.#threads().add(data, overwrite) : this.#channels.add(data, overwrite);
4044
+ }
4045
+ set(key, value) {
4046
+ const target = isThreadChannelType(value.type) ? this.#threads() : this.#channels;
4047
+ return whenAll([target.set(key, value)], () => this);
4048
+ }
4049
+ get(key) {
4050
+ return whenAll([this.#channels.get(key)], ([channel]) => channel ?? this.#threads().get(key));
4051
+ }
4052
+ has(key) {
4053
+ return whenAll([this.#channels.has(key)], ([has]) => has || this.#threads().has(key));
4054
+ }
4055
+ delete(key) {
4056
+ return whenAll([this.#channels.delete(key)], ([deleted]) => deleted || this.#threads().delete(key));
4057
+ }
4058
+ getSize() {
4059
+ return whenAll([this.#channels.getSize(), this.#threads().getSize()], ([a, b]) => a + b);
4060
+ }
4061
+ clear() {
4062
+ return whenAll([this.#channels.clear(), this.#threads().clear()], () => void 0);
4063
+ }
4064
+ };
4065
+ /**
4010
4066
  * Manages the channels known to the client, threads included.
4011
4067
  *
4012
4068
  * @remarks
4013
- * Threads live in their own entity cache, managed by `client.threads`, which {@link ChannelManager.get} falls back
4069
+ * Threads live in their own entity cache, managed by `client.threads`, which {@link ChannelManager.cache} falls back
4014
4070
  * to, so a thread ID resolves like any other channel ID.
4015
4071
  */
4016
4072
  var ChannelManager = class extends CachedManager {
4017
4073
  constructor(client) {
4018
4074
  super(client, "channels");
4019
4075
  }
4020
- construct(data) {
4076
+ createCache() {
4077
+ return new ChannelCache(super.createCache(), () => this.client.threads.cache);
4078
+ }
4079
+ createStructure(data) {
4021
4080
  return createChannel(data);
4022
4081
  }
4023
4082
  keyOf(data) {
@@ -4044,11 +4103,13 @@ var ChannelManager = class extends CachedManager {
4044
4103
  const recipient = data.type === ChannelType.DM ? data.recipients?.[0] : void 0;
4045
4104
  const guildId = channelGuildId(data) ?? guild?.id;
4046
4105
  const thread = isThreadChannelType(data.type);
4106
+ const stageKey = data.type === ChannelType.GuildStageVoice && guildId ? stageInstanceKey(guildId, data.id) : null;
4107
+ const meKey = thread ? threadMemberKey(data.id, client.user?.id ?? client.id) : null;
4047
4108
  return whenAll([
4048
- parentId && depth < MaxParentDepth ? this.cache?.get(parentId) : void 0,
4109
+ parentId && depth < MaxParentDepth ? this.readRaw(parentId) : void 0,
4049
4110
  recipient ? client.users._resolveData(recipient) : void 0,
4050
- data.type === ChannelType.GuildStageVoice && guildId ? client.cache?.stageInstances?.get(stageInstanceKey(guildId, data.id)) : void 0,
4051
- thread ? client.cache?.threadMembers?.get(threadMemberKey(data.id, client.user?.id ?? client.id)) : void 0
4111
+ stageKey ? client.guardCache("stageInstances", "get", stageKey, () => client.cache?.stageInstances?.get(stageKey), void 0) : void 0,
4112
+ meKey ? client.guardCache("threadMembers", "get", meKey, () => client.cache?.threadMembers?.get(meKey), void 0) : void 0
4052
4113
  ], ([parentData, resolvedRecipient, stageData, me]) => whenAll([parentData ? this._hydrateInGuild(parentData, guild, depth + 1) : null], ([parent]) => {
4053
4114
  const relations = {
4054
4115
  guild,
@@ -4072,7 +4133,10 @@ var ChannelManager = class extends CachedManager {
4072
4133
  * @internal
4073
4134
  */
4074
4135
  _getInGuild(channelId, guild) {
4075
- return whenAll([this.cache?.get(channelId)], ([data]) => data ? this._hydrateInGuild(data, guild) : null);
4136
+ return whenAll([this.readRaw(channelId)], ([data]) => data ? this._hydrateInGuild(data, guild) : null);
4137
+ }
4138
+ readRaw(channelId) {
4139
+ return this.guard("get", channelId, () => this.rawStore?.get(channelId), void 0);
4076
4140
  }
4077
4141
  resolveKey(channelId) {
4078
4142
  return channelId;
@@ -4086,25 +4150,6 @@ var ChannelManager = class extends CachedManager {
4086
4150
  return isThreadChannelType(data.type) ? this.client.threads._add(data, cache, options) : super._add(data, cache, options);
4087
4151
  }
4088
4152
  /**
4089
- * Gets a channel from the cache, looking it up in the thread cache as well, which `get` and `cached` rely on.
4090
- *
4091
- * @param channelId The ID of the channel.
4092
- * @internal
4093
- */
4094
- _get(channelId) {
4095
- return whenAll([super._get(channelId)], ([channel]) => channel ?? this.client.threads._get(channelId));
4096
- }
4097
- /**
4098
- * Writes a channel to the cache, threads to the thread cache.
4099
- *
4100
- * @param channelId The ID of the channel.
4101
- * @param raw The raw channel.
4102
- */
4103
- async storeRaw(channelId, raw) {
4104
- if (isThreadChannelType(raw.type)) await this.client.cache?.threads?.set(channelId, raw);
4105
- else await this.cache?.set(channelId, raw);
4106
- }
4107
- /**
4108
4153
  * Edits a channel.
4109
4154
  *
4110
4155
  * @param channelId The ID of the channel.
@@ -4391,9 +4436,13 @@ var Guild = class extends AnonymousGuild {
4391
4436
  const resolved = this[kRelations][name];
4392
4437
  if (resolved !== void 0) return resolved;
4393
4438
  const channelId = this[kData][GuildChannelFields[name]];
4394
- if (!channelId || this.client.cache?.channels?.synchronous !== true) return null;
4439
+ const { cache } = this.client.channels;
4440
+ if (!channelId || !cache.synchronous) return null;
4395
4441
  try {
4396
- return this.client.channels.cached(channelId) ?? null;
4442
+ const channel = cache.get(channelId);
4443
+ if (!isPromiseLike(channel)) return channel ?? null;
4444
+ channel.catch(() => void 0);
4445
+ return null;
4397
4446
  } catch {
4398
4447
  return null;
4399
4448
  }
@@ -6125,8 +6174,12 @@ function changesReduce(changes, initial = {}) {
6125
6174
  }
6126
6175
  return data;
6127
6176
  }
6128
- async function read(value) {
6129
- return value.catch(() => void 0);
6177
+ async function read(manager, ...args) {
6178
+ try {
6179
+ return await manager.cache.get(manager.resolveKey(...args));
6180
+ } catch {
6181
+ return;
6182
+ }
6130
6183
  }
6131
6184
  /**
6132
6185
  * Resolves the target of an audit log entry from the cache, like discord.js's `GuildAuditLogsEntry#target`.
@@ -6144,19 +6197,19 @@ async function resolveAuditLogTarget(client, entry, entities = {}) {
6144
6197
  const { guilds } = client;
6145
6198
  const built = (value) => bindClient(value, client);
6146
6199
  if (type === "Unknown") return changesReduce(changes, { id: targetId });
6147
- const user = async (userId) => entities.users?.get(userId) ?? await read(client.users.get(userId)) ?? null;
6200
+ const user = async (userId) => entities.users?.get(userId) ?? await read(client.users, userId) ?? null;
6148
6201
  if (type === "User") return targetId ? user(targetId) : null;
6149
6202
  if (!targetId && type !== "Invite") return null;
6150
6203
  const id = targetId;
6151
6204
  switch (type) {
6152
- case "Guild": return await read(guilds.get(id)) ?? { id };
6205
+ case "Guild": return await read(guilds, id) ?? { id };
6153
6206
  case "Channel":
6154
- case "Thread": return await read(client.channels.get(id)) ?? changesReduce(changes, { id });
6155
- case "Role": return await read(client.roles.get(guildId, id)) ?? { id };
6207
+ case "Thread": return await read(client.channels, id) ?? changesReduce(changes, { id });
6208
+ case "Role": return await read(client.roles, guildId, id) ?? { id };
6156
6209
  case "Invite": {
6157
6210
  const { id: _id, ...changed } = changesReduce(changes);
6158
6211
  const code = changed.code;
6159
- return (code ? await read(guilds.invites(guildId).get(code)) : void 0) ?? built(new GuildInvite({
6212
+ return (code ? await read(guilds.invites(guildId), code) : void 0) ?? built(new GuildInvite({
6160
6213
  ...changed,
6161
6214
  code: code ?? "",
6162
6215
  guild_id: guildId
@@ -6166,28 +6219,28 @@ async function resolveAuditLogTarget(client, entry, entities = {}) {
6166
6219
  id,
6167
6220
  guild_id: guildId
6168
6221
  })));
6169
- case "Emoji": return await read(guilds.emojis(guildId).get(id)) ?? { id };
6170
- case "Message": return entry.action_type === AuditLogEvent.MessageBulkDelete ? await read(client.channels.get(id)) ?? { id } : user(id);
6171
- case "Integration": return entities.integrations?.get(id) ?? await read(guilds.integrations(guildId).get(id)) ?? built(new Integration(changesReduce(changes, {
6222
+ case "Emoji": return await read(guilds.emojis(guildId), id) ?? { id };
6223
+ case "Message": return entry.action_type === AuditLogEvent.MessageBulkDelete ? await read(client.channels, id) ?? { id } : user(id);
6224
+ case "Integration": return entities.integrations?.get(id) ?? await read(guilds.integrations(guildId), id) ?? built(new Integration(changesReduce(changes, {
6172
6225
  id,
6173
6226
  guild_id: guildId
6174
6227
  })));
6175
6228
  case "StageInstance": {
6176
6229
  const channelId = options?.channel_id;
6177
- return (channelId ? await read(guilds.stageInstances(guildId).get(channelId)) : void 0) ?? built(new StageInstance(changesReduce(changes, {
6230
+ return (channelId ? await read(guilds.stageInstances(guildId), channelId) : void 0) ?? built(new StageInstance(changesReduce(changes, {
6178
6231
  id,
6179
6232
  channel_id: channelId,
6180
6233
  guild_id: guildId
6181
6234
  })));
6182
6235
  }
6183
- case "Sticker": return await read(guilds.stickers(guildId).get(id)) ?? built(new Sticker(changesReduce(changes, { id })));
6184
- case "GuildScheduledEvent": return await read(guilds.scheduledEvents(guildId).get(id)) ?? built(new GuildScheduledEvent(changesReduce(changes, {
6236
+ case "Sticker": return await read(guilds.stickers(guildId), id) ?? built(new Sticker(changesReduce(changes, { id })));
6237
+ case "GuildScheduledEvent": return await read(guilds.scheduledEvents(guildId), id) ?? built(new GuildScheduledEvent(changesReduce(changes, {
6185
6238
  id,
6186
6239
  guild_id: guildId
6187
6240
  })));
6188
6241
  case "ApplicationCommand": return entities.applicationCommands?.get(id) ?? { id };
6189
- case "SoundboardSound": return await read(guilds.soundboardSounds(guildId).get(id)) ?? { id };
6190
- case "AutoModeration": return await read(guilds.autoModerationRules(guildId).get(id)) ?? built(new AutoModerationRule(changesReduce(changes, {
6242
+ case "SoundboardSound": return await read(guilds.soundboardSounds(guildId), id) ?? { id };
6243
+ case "AutoModeration": return await read(guilds.autoModerationRules(guildId), id) ?? built(new AutoModerationRule(changesReduce(changes, {
6191
6244
  id,
6192
6245
  guild_id: guildId
6193
6246
  })));
@@ -6201,13 +6254,12 @@ async function resolveAuditLogTarget(client, entry, entities = {}) {
6201
6254
  /**
6202
6255
  * Manages the roles allowed to use one custom emoji. When it has none, everyone can use it.
6203
6256
  */
6204
- var GuildEmojiRoleManager = class {
6205
- client;
6257
+ var GuildEmojiRoleManager = class extends BaseManager {
6206
6258
  guildId;
6207
6259
  emojiId;
6208
6260
  #roleIds;
6209
6261
  constructor(client, guildId, emojiId, roleIds) {
6210
- this.client = client;
6262
+ super(client);
6211
6263
  this.guildId = guildId;
6212
6264
  this.emojiId = emojiId;
6213
6265
  this.#roleIds = roleIds;
@@ -6612,7 +6664,7 @@ var AutoModerationRuleManager = class extends CachedManager {
6612
6664
  super(client, "autoModerationRules");
6613
6665
  this.guildId = guildId;
6614
6666
  }
6615
- construct(data) {
6667
+ createStructure(data) {
6616
6668
  return new AutoModerationRule(data);
6617
6669
  }
6618
6670
  keyOf(data) {
@@ -6665,7 +6717,7 @@ var AutoModerationRuleManager = class extends CachedManager {
6665
6717
  */
6666
6718
  async delete(ruleId, reason) {
6667
6719
  await this.client.api.guilds.deleteAutoModerationRule(this.guildId, ruleId, { reason });
6668
- await this.cache?.delete(this.resolveKey(ruleId));
6720
+ await this.cache.delete(this.resolveKey(ruleId));
6669
6721
  }
6670
6722
  async fetchRaw(ruleId) {
6671
6723
  return this.client.api.guilds.getAutoModerationRule(this.guildId, ruleId);
@@ -6744,7 +6796,7 @@ var GuildBanManager = class extends CachedManager {
6744
6796
  super(client, "bans");
6745
6797
  this.guildId = guildId;
6746
6798
  }
6747
- construct(data) {
6799
+ createStructure(data) {
6748
6800
  return new GuildBan(data);
6749
6801
  }
6750
6802
  keyOf(data) {
@@ -6795,7 +6847,7 @@ var GuildBanManager = class extends CachedManager {
6795
6847
  async remove(user, reason) {
6796
6848
  const userId = resolveId(user);
6797
6849
  await this.client.members.unban(this.guildId, userId, reason);
6798
- await this.cache?.delete(this.resolveKey(userId));
6850
+ await this.cache.delete(this.resolveKey(userId));
6799
6851
  }
6800
6852
  /**
6801
6853
  * Bans up to 200 users at once.
@@ -6823,11 +6875,10 @@ var GuildBanManager = class extends CachedManager {
6823
6875
  /**
6824
6876
  * Manages the channels of one guild. Channels are cached by `client.channels`, which this manager writes to.
6825
6877
  */
6826
- var GuildChannelManager = class {
6827
- client;
6878
+ var GuildChannelManager = class extends BaseManager {
6828
6879
  guildId;
6829
6880
  constructor(client, guildId) {
6830
- this.client = client;
6881
+ super(client);
6831
6882
  this.guildId = guildId;
6832
6883
  }
6833
6884
  /**
@@ -6905,7 +6956,7 @@ var GuildChannelManager = class {
6905
6956
  channel: entry.id,
6906
6957
  position: entry.position
6907
6958
  })), options.reason);
6908
- return await this.client.channels.get(id);
6959
+ return await this.client.channels.cache.get(id);
6909
6960
  }
6910
6961
  /**
6911
6962
  * Fetches the channels a channel is sorted with, itself included, and caches them.
@@ -6949,7 +7000,7 @@ var GuildIntegrationManager = class extends CachedManager {
6949
7000
  super(client, "integrations");
6950
7001
  this.guildId = guildId;
6951
7002
  }
6952
- construct(data) {
7003
+ createStructure(data) {
6953
7004
  return new Integration(data);
6954
7005
  }
6955
7006
  keyOf(data) {
@@ -6971,7 +7022,7 @@ var GuildIntegrationManager = class extends CachedManager {
6971
7022
  return whenAll([
6972
7023
  data.user ? this.client.users._resolveData(data.user) : null,
6973
7024
  this.cachedGuild(data.guild_id),
6974
- data.role_id ? this.client.roles._get(data.guild_id, data.role_id) : void 0
7025
+ data.role_id ? this.client.roles.cache.get(this.client.roles.resolveKey(data.guild_id, data.role_id)) : void 0
6975
7026
  ], ([user, guild, role]) => new Integration(data, {
6976
7027
  user,
6977
7028
  guild,
@@ -6993,7 +7044,7 @@ var GuildIntegrationManager = class extends CachedManager {
6993
7044
  */
6994
7045
  async delete(integrationId, reason) {
6995
7046
  await this.client.api.guilds.deleteIntegration(this.guildId, integrationId, { reason });
6996
- await this.cache?.delete(this.resolveKey(integrationId));
7047
+ await this.cache.delete(this.resolveKey(integrationId));
6997
7048
  }
6998
7049
  async fetchRaw(integrationId) {
6999
7050
  const integration = (await this.list()).find(({ id }) => id === integrationId);
@@ -7019,7 +7070,7 @@ var GuildScheduledEventManager = class extends CachedManager {
7019
7070
  super(client, "scheduledEvents");
7020
7071
  this.guildId = guildId;
7021
7072
  }
7022
- construct(data) {
7073
+ createStructure(data) {
7023
7074
  return new GuildScheduledEvent(data);
7024
7075
  }
7025
7076
  keyOf(data) {
@@ -7039,9 +7090,9 @@ var GuildScheduledEventManager = class extends CachedManager {
7039
7090
  }
7040
7091
  _hydrate(data) {
7041
7092
  return whenAll([
7042
- data.creator ? this.client.users._resolveData(data.creator) : data.creator_id ? this.client.users._get(data.creator_id) : void 0,
7093
+ data.creator ? this.client.users._resolveData(data.creator) : data.creator_id ? this.client.users.cache.get(data.creator_id) : void 0,
7043
7094
  this.cachedGuild(data.guild_id),
7044
- data.channel_id ? this.client.channels._get(data.channel_id) : void 0
7095
+ data.channel_id ? this.client.channels.cache.get(data.channel_id) : void 0
7045
7096
  ], ([creator, guild, channel]) => new GuildScheduledEvent(data, {
7046
7097
  creator: creator ?? null,
7047
7098
  guild,
@@ -7084,7 +7135,7 @@ var GuildScheduledEventManager = class extends CachedManager {
7084
7135
  */
7085
7136
  async delete(eventId, reason) {
7086
7137
  await this.client.api.guilds.deleteScheduledEvent(this.guildId, eventId, { reason });
7087
- await this.cache?.delete(this.resolveKey(eventId));
7138
+ await this.cache.delete(this.resolveKey(eventId));
7088
7139
  }
7089
7140
  /**
7090
7141
  * Fetches the users who subscribed to an event, and caches them.
@@ -7230,11 +7281,11 @@ var GuildSoundboardSoundManager = class extends CachedManager {
7230
7281
  super(client, "soundboardSounds");
7231
7282
  this.guildId = guildId;
7232
7283
  }
7233
- construct(data) {
7284
+ createStructure(data) {
7234
7285
  return new SoundboardSound(data);
7235
7286
  }
7236
7287
  keyOf(data) {
7237
- return this.resolveKey(data.sound_id);
7288
+ return soundboardSoundKey(data.guild_id ?? this.guildId, data.sound_id);
7238
7289
  }
7239
7290
  resolveKey(soundId) {
7240
7291
  return soundboardSoundKey(this.guildId, soundId);
@@ -7252,7 +7303,7 @@ var GuildSoundboardSoundManager = class extends CachedManager {
7252
7303
  return whenAll([
7253
7304
  data.user ? this.client.users._resolveData(data.user) : null,
7254
7305
  this.cachedGuild(data.guild_id),
7255
- data.emoji_id && data.guild_id ? this.client.guilds.emojis(data.guild_id)._get(data.emoji_id) : void 0
7306
+ data.emoji_id && data.guild_id ? this.client.guilds.emojis(data.guild_id).cache.get(emojiKey(data.guild_id, data.emoji_id)) : void 0
7256
7307
  ], ([user, guild, emoji]) => new SoundboardSound(data, {
7257
7308
  user,
7258
7309
  guild,
@@ -7298,7 +7349,7 @@ var GuildSoundboardSoundManager = class extends CachedManager {
7298
7349
  */
7299
7350
  async delete(soundId, reason) {
7300
7351
  await this.client.api.guilds.deleteSoundboardSound(this.guildId, soundId, { reason });
7301
- await this.cache?.delete(this.resolveKey(soundId));
7352
+ await this.cache.delete(this.resolveKey(soundId));
7302
7353
  }
7303
7354
  async fetchRaw(soundId) {
7304
7355
  const sound = await this.client.api.guilds.getSoundboardSound(this.guildId, soundId);
@@ -7340,7 +7391,7 @@ var StageInstanceManager = class extends CachedManager {
7340
7391
  super(client, "stageInstances");
7341
7392
  this.guildId = guildId;
7342
7393
  }
7343
- construct(data) {
7394
+ createStructure(data) {
7344
7395
  return new StageInstance(data);
7345
7396
  }
7346
7397
  keyOf(data) {
@@ -7352,8 +7403,8 @@ var StageInstanceManager = class extends CachedManager {
7352
7403
  _hydrate(data) {
7353
7404
  return whenAll([
7354
7405
  this.cachedGuild(data.guild_id),
7355
- this.client.channels._get(data.channel_id),
7356
- data.guild_scheduled_event_id ? this.client.guilds.scheduledEvents(data.guild_id)._get(data.guild_scheduled_event_id) : void 0
7406
+ this.client.channels.cache.get(data.channel_id),
7407
+ data.guild_scheduled_event_id ? this.client.guilds.scheduledEvents(data.guild_id).cache.get(scheduledEventKey(data.guild_id, data.guild_scheduled_event_id)) : void 0
7357
7408
  ], ([guild, channel, guildScheduledEvent]) => new StageInstance(data, {
7358
7409
  guild,
7359
7410
  channel: channel ?? null,
@@ -7399,7 +7450,7 @@ var StageInstanceManager = class extends CachedManager {
7399
7450
  */
7400
7451
  async delete(channelId, reason) {
7401
7452
  await this.client.api.stageInstances.delete(channelId, { reason });
7402
- await this.cache?.delete(this.resolveKey(channelId));
7453
+ await this.cache.delete(this.resolveKey(channelId));
7403
7454
  }
7404
7455
  async fetchRaw(channelId) {
7405
7456
  return this.client.api.stageInstances.get(channelId);
@@ -7420,7 +7471,7 @@ var GuildEmojiManager = class extends CachedManager {
7420
7471
  super(client, "emojis");
7421
7472
  this.guildId = guildId;
7422
7473
  }
7423
- construct(data) {
7474
+ createStructure(data) {
7424
7475
  return new GuildEmoji(data);
7425
7476
  }
7426
7477
  keyOf(data) {
@@ -7487,7 +7538,7 @@ var GuildEmojiManager = class extends CachedManager {
7487
7538
  */
7488
7539
  async delete(emojiId, reason) {
7489
7540
  await this.client.api.guilds.deleteEmoji(this.guildId, emojiId, { reason });
7490
- await this.cache?.delete(this.resolveKey(emojiId));
7541
+ await this.cache.delete(this.resolveKey(emojiId));
7491
7542
  }
7492
7543
  /**
7493
7544
  * Fetches the user who uploaded an emoji, which the API only returns with `ManageGuildExpressions`.
@@ -7511,9 +7562,9 @@ var GuildEmojiManager = class extends CachedManager {
7511
7562
  const cache = this.iterableCache();
7512
7563
  if (!cache) return [];
7513
7564
  const prefix = `${this.guildId}:`;
7514
- const keys = (await cache.keys()).filter((key) => key.startsWith(prefix));
7515
- const values = await Promise.all(keys.map((key) => cache.get(key)));
7516
- return Promise.all(values.filter((value) => value !== void 0).map((value) => this.hydrate(value)));
7565
+ const keys = await this.guard("keys", null, () => cache.keys(), []);
7566
+ const values = await Promise.all(keys.filter((key) => key.startsWith(prefix)).map((key) => this.guard("get", key, () => cache.get(key), void 0)));
7567
+ return Promise.all(values.filter((value) => value !== void 0).map((value) => this._build(value)));
7517
7568
  }
7518
7569
  async fetchRaw(emojiId) {
7519
7570
  return {
@@ -7543,11 +7594,11 @@ var GuildInviteManager = class extends CachedManager {
7543
7594
  super(client, "invites");
7544
7595
  this.guildId = guildId;
7545
7596
  }
7546
- construct(data) {
7597
+ createStructure(data) {
7547
7598
  return new GuildInvite(data);
7548
7599
  }
7549
7600
  keyOf(data) {
7550
- return this.resolveKey(data.code);
7601
+ return inviteKey(data.guild_id ?? this.guildId, resolveInviteCode(data.code));
7551
7602
  }
7552
7603
  /**
7553
7604
  * Adds an invite to the cache, and its inviter and target user to `client.users`.
@@ -7564,8 +7615,8 @@ var GuildInviteManager = class extends CachedManager {
7564
7615
  return whenAll([
7565
7616
  data.inviter ? users._resolveData(data.inviter) : void 0,
7566
7617
  data.target_user ? users._resolveData(data.target_user) : void 0,
7567
- this.cachedGuild(this.guildId),
7568
- data.channel_id ? this.client.channels._get(data.channel_id) : void 0
7618
+ this.cachedGuild(data.guild_id ?? this.guildId),
7619
+ data.channel_id ? this.client.channels.cache.get(data.channel_id) : void 0
7569
7620
  ], ([inviter, targetUser, guild, channel]) => new GuildInvite(data, {
7570
7621
  inviter,
7571
7622
  targetUser,
@@ -7619,7 +7670,7 @@ var GuildInviteManager = class extends CachedManager {
7619
7670
  */
7620
7671
  async delete(code, reason) {
7621
7672
  await this.client.api.invites.delete(resolveInviteCode(code), { reason });
7622
- await this.cache?.delete(this.resolveKey(code));
7673
+ await this.cache.delete(this.resolveKey(code));
7623
7674
  }
7624
7675
  async fetchRaw(code) {
7625
7676
  const invite = await this.client.api.invites.get(resolveInviteCode(code), { with_counts: true });
@@ -7651,7 +7702,7 @@ var GuildStickerManager = class extends CachedManager {
7651
7702
  super(client, "stickers");
7652
7703
  this.guildId = guildId;
7653
7704
  }
7654
- construct(data) {
7705
+ createStructure(data) {
7655
7706
  return new Sticker(data);
7656
7707
  }
7657
7708
  keyOf(data) {
@@ -7719,7 +7770,7 @@ var GuildStickerManager = class extends CachedManager {
7719
7770
  */
7720
7771
  async delete(stickerId, reason) {
7721
7772
  await this.client.api.guilds.deleteSticker(this.guildId, stickerId, { reason });
7722
- await this.cache?.delete(this.resolveKey(stickerId));
7773
+ await this.cache.delete(this.resolveKey(stickerId));
7723
7774
  }
7724
7775
  /**
7725
7776
  * Fetches the user who uploaded a sticker, which the API only returns with `ManageGuildExpressions`.
@@ -7743,9 +7794,9 @@ var GuildStickerManager = class extends CachedManager {
7743
7794
  const cache = this.iterableCache();
7744
7795
  if (!cache) return [];
7745
7796
  const prefix = `${this.guildId}:`;
7746
- const keys = (await cache.keys()).filter((key) => key.startsWith(prefix));
7747
- const values = await Promise.all(keys.map((key) => cache.get(key)));
7748
- return Promise.all(values.filter((value) => value !== void 0).map((value) => this.hydrate(value)));
7797
+ const keys = await this.guard("keys", null, () => cache.keys(), []);
7798
+ const values = await Promise.all(keys.filter((key) => key.startsWith(prefix)).map((key) => this.guard("get", key, () => cache.get(key), void 0)));
7799
+ return Promise.all(values.filter((value) => value !== void 0).map((value) => this._build(value)));
7749
7800
  }
7750
7801
  async fetchRaw(stickerId) {
7751
7802
  return {
@@ -7770,7 +7821,7 @@ var GuildManager = class extends CachedManager {
7770
7821
  constructor(client) {
7771
7822
  super(client, "guilds");
7772
7823
  }
7773
- construct(data) {
7824
+ createStructure(data) {
7774
7825
  return new Guild(data);
7775
7826
  }
7776
7827
  keyOf(data) {
@@ -7802,7 +7853,8 @@ var GuildManager = class extends CachedManager {
7802
7853
  * @internal
7803
7854
  */
7804
7855
  _getShallow(guildId) {
7805
- return whenAll([this.cache?.get(guildId)], ([raw]) => raw === void 0 ? void 0 : bindClient(this.construct(raw), this.client));
7856
+ const read = this.guard("get", guildId, () => this.rawStore?.get(guildId), void 0);
7857
+ return whenAll([read], ([raw]) => raw === void 0 ? void 0 : bindClient(this.createStructure(raw), this.client));
7806
7858
  }
7807
7859
  resolveKey(guildId) {
7808
7860
  return guildId;
@@ -7895,9 +7947,7 @@ var GuildManager = class extends CachedManager {
7895
7947
  channel_id: options.channel === void 0 ? void 0 : options.channel && resolveId(options.channel)
7896
7948
  };
7897
7949
  const settings = await this.client.api.guilds.editWidgetSettings(guildId, body, { reason: options.reason });
7898
- const cached = await this.cache?.get(guildId);
7899
- if (cached) await this.cache.set(guildId, {
7900
- ...cached,
7950
+ await this._patchCached(guildId, {
7901
7951
  widget_enabled: settings.enabled,
7902
7952
  widget_channel_id: settings.channel_id
7903
7953
  });
@@ -7987,9 +8037,9 @@ var GuildManager = class extends CachedManager {
7987
8037
  const integrations = this.integrations(guildId);
7988
8038
  const [webhooks, autoModerationRules, threads, pageIntegrations] = await Promise.all([
7989
8039
  Promise.all(log.webhooks.map((webhook) => this.client.webhooks.hydrate(webhook))),
7990
- Promise.all(log.auto_moderation_rules.map((rule) => rules.hydrate(rule))),
7991
- Promise.all(log.threads.map((thread) => this.client.threads.hydrate(thread))),
7992
- Promise.all(log.integrations.map((integration) => integrations.hydrate({
8040
+ Promise.all(log.auto_moderation_rules.map((rule) => rules._build(rule))),
8041
+ Promise.all(log.threads.map((thread) => this.client.threads._build(thread))),
8042
+ Promise.all(log.integrations.map((integration) => integrations._build({
7993
8043
  ...integration,
7994
8044
  guild_id: guildId
7995
8045
  })))
@@ -8161,11 +8211,7 @@ var GuildManager = class extends CachedManager {
8161
8211
  dms_disabled_until: toISO(options.dmsDisabledUntil)
8162
8212
  };
8163
8213
  const incidents = await this.client.api.guilds.editIncidentActions(guildId, body);
8164
- const cached = await this.cache?.get(guildId);
8165
- if (cached) await this.cache.set(guildId, {
8166
- ...cached,
8167
- incidents_data: incidents
8168
- });
8214
+ await this._patchCached(guildId, { incidents_data: incidents });
8169
8215
  return transformAPIIncidentsData(incidents);
8170
8216
  }
8171
8217
  async fetchRaw(guildId) {
@@ -8176,10 +8222,11 @@ var GuildManager = class extends CachedManager {
8176
8222
  }
8177
8223
  async welcomeScreen(guildId, screen) {
8178
8224
  const channels = screen.welcome_channels;
8179
- const [guild, cachedChannels, emojis] = await Promise.all([
8225
+ const emojis = this.emojis(guildId);
8226
+ const [guild, cachedChannels, cachedEmojis] = await Promise.all([
8180
8227
  this.cachedGuild(guildId),
8181
- cachedMap(channels.map((channel) => channel.channel_id), (id) => this.client.channels.get(id)),
8182
- cachedMap(channels.flatMap((channel) => channel.emoji_id ? [channel.emoji_id] : []), (id) => this.emojis(guildId).get(id))
8228
+ cachedMap(channels.map((channel) => channel.channel_id), (id) => this.client.channels.cache.get(id)),
8229
+ cachedMap(channels.flatMap((channel) => channel.emoji_id ? [channel.emoji_id] : []), (id) => emojis.cache.get(emojis.resolveKey(id)))
8183
8230
  ]);
8184
8231
  return bindClient(new WelcomeScreen({
8185
8232
  ...screen,
@@ -8187,22 +8234,23 @@ var GuildManager = class extends CachedManager {
8187
8234
  }, {
8188
8235
  guild,
8189
8236
  channels: cachedChannels,
8190
- emojis
8237
+ emojis: cachedEmojis
8191
8238
  }), this.client);
8192
8239
  }
8193
8240
  async onboarding(guildId, onboarding) {
8194
8241
  const options = onboarding.prompts.flatMap((prompt) => prompt.options);
8195
- const [guild, channels, roles, emojis] = await Promise.all([
8242
+ const emojis = this.emojis(guildId);
8243
+ const [guild, channels, roles, cachedEmojis] = await Promise.all([
8196
8244
  this.cachedGuild(guildId),
8197
- cachedMap([...onboarding.default_channel_ids, ...options.flatMap((option) => option.channel_ids)], (id) => this.client.channels.get(id)),
8198
- cachedMap(options.flatMap((option) => option.role_ids), (id) => this.client.roles.get(guildId, id)),
8199
- cachedMap(options.flatMap((option) => option.emoji?.id ? [option.emoji.id] : []), (id) => this.emojis(guildId).get(id))
8245
+ cachedMap([...onboarding.default_channel_ids, ...options.flatMap((option) => option.channel_ids)], (id) => this.client.channels.cache.get(id)),
8246
+ cachedMap(options.flatMap((option) => option.role_ids), (id) => this.client.roles.cache.get(this.client.roles.resolveKey(guildId, id))),
8247
+ cachedMap(options.flatMap((option) => option.emoji?.id ? [option.emoji.id] : []), (id) => emojis.cache.get(emojis.resolveKey(id)))
8200
8248
  ]);
8201
8249
  return bindClient(new GuildOnboarding(onboarding, {
8202
8250
  guild,
8203
8251
  channels,
8204
8252
  roles,
8205
- emojis
8253
+ emojis: cachedEmojis
8206
8254
  }), this.client);
8207
8255
  }
8208
8256
  async forget(guildId) {
@@ -8218,7 +8266,13 @@ var GuildManager = class extends CachedManager {
8218
8266
  };
8219
8267
  async function cachedMap(ids, get) {
8220
8268
  const unique = [...new Set(ids)];
8221
- const values = await Promise.all(unique.map((id) => get(id).catch(() => void 0)));
8269
+ const values = await Promise.all(unique.map(async (id) => {
8270
+ try {
8271
+ return await get(id);
8272
+ } catch {
8273
+ return;
8274
+ }
8275
+ }));
8222
8276
  const map = /* @__PURE__ */ new Map();
8223
8277
  for (const [index, value] of values.entries()) if (value) map.set(unique[index], value);
8224
8278
  return map;
@@ -8241,8 +8295,7 @@ function toISO(value) {
8241
8295
  * discord.js's synchronous `member.roles.cache`, `highest`, `color`, ... become the `fetch*` methods here: the roles
8242
8296
  * are read from the (possibly asynchronous) cache, falling back to the API.
8243
8297
  */
8244
- var GuildMemberRoleManager = class {
8245
- client;
8298
+ var GuildMemberRoleManager = class extends BaseManager {
8246
8299
  guildId;
8247
8300
  userId;
8248
8301
  #roleIds;
@@ -8253,7 +8306,7 @@ var GuildMemberRoleManager = class {
8253
8306
  * @param roleIds The IDs of the member's roles, as known to the member structure.
8254
8307
  */
8255
8308
  constructor(client, guildId, userId, roleIds) {
8256
- this.client = client;
8309
+ super(client);
8257
8310
  this.guildId = guildId;
8258
8311
  this.userId = userId;
8259
8312
  this.#roleIds = roleIds;
@@ -8269,7 +8322,7 @@ var GuildMemberRoleManager = class {
8269
8322
  */
8270
8323
  async fetch() {
8271
8324
  const ids = [...this.#roleIds, this.guildId];
8272
- let roles = (await Promise.all(ids.map((id) => this.client.roles.get(this.guildId, id)))).filter((role) => role !== void 0);
8325
+ let roles = (await Promise.all(ids.map((id) => this.client.roles.cache.get(this.client.roles.resolveKey(this.guildId, id))))).filter((role) => role !== void 0);
8273
8326
  if (roles.length !== ids.length) {
8274
8327
  const wanted = new Set(ids);
8275
8328
  roles = (await this.client.roles.fetchAll(this.guildId)).filter((role) => wanted.has(role.id));
@@ -8599,7 +8652,7 @@ var GuildMember = class extends Structure {
8599
8652
  * @returns The presence, or `null` when it is not cached (the bot needs the `GuildPresences` intent).
8600
8653
  */
8601
8654
  async fetchPresence() {
8602
- return await this.client.presences.get(this.guildId, this.requireId()) ?? null;
8655
+ return await this.client.presences.cache.get(this.client.presences.resolveKey(this.guildId, this.requireId())) ?? null;
8603
8656
  }
8604
8657
  /**
8605
8658
  * Fetches the color the member's name is displayed in, `0` when none of their roles has one.
@@ -9303,7 +9356,7 @@ var GuildMemberManager = class extends CachedManager {
9303
9356
  constructor(client) {
9304
9357
  super(client, "members");
9305
9358
  }
9306
- construct(data) {
9359
+ createStructure(data) {
9307
9360
  return new GuildMember(data);
9308
9361
  }
9309
9362
  keyOf(data) {
@@ -9334,15 +9387,15 @@ var GuildMemberManager = class extends CachedManager {
9334
9387
  return whenAll([
9335
9388
  data.user ? client.users._resolveData(data.user) : void 0,
9336
9389
  this.cachedGuild(data.guild_id),
9337
- key ? client.cache?.voiceStates?.get(key) : void 0,
9338
- key ? client.cache?.presences?.get(key) : void 0
9390
+ key ? this.readRelation("voiceStates", key) : void 0,
9391
+ key ? this.readRelation("presences", key) : void 0
9339
9392
  ], ([user, guild, voiceData, presenceData]) => {
9340
9393
  const relations = {
9341
9394
  user,
9342
9395
  guild
9343
9396
  };
9344
9397
  const member = bindClient(new GuildMember(data, relations), client);
9345
- return whenAll([voiceData?.channel_id ? client.channels._get(voiceData.channel_id) : void 0], ([channel]) => {
9398
+ return whenAll([voiceData?.channel_id ? client.channels.cache.get(voiceData.channel_id) : void 0], ([channel]) => {
9346
9399
  if (key && client.cache?.voiceStates) relations.voice = voiceData ? bindClient(new VoiceState(voiceData, {
9347
9400
  member,
9348
9401
  guild,
@@ -9357,6 +9410,10 @@ var GuildMemberManager = class extends CachedManager {
9357
9410
  });
9358
9411
  });
9359
9412
  }
9413
+ readRelation(name, key) {
9414
+ const store = this.client.cache?.[name];
9415
+ return this.client.guardCache(name, "get", key, () => store?.get(key), void 0);
9416
+ }
9360
9417
  resolveKey(guildId, userId) {
9361
9418
  return memberKey(guildId, userId);
9362
9419
  }
@@ -9550,7 +9607,7 @@ var GuildMemberManager = class extends CachedManager {
9550
9607
  */
9551
9608
  async kick(guildId, userId, reason) {
9552
9609
  await this.client.api.guilds.removeMember(guildId, userId, { reason });
9553
- await this.cache?.delete(this.resolveKey(guildId, userId));
9610
+ await this.cache.delete(this.resolveKey(guildId, userId));
9554
9611
  }
9555
9612
  /**
9556
9613
  * Bans a user, member or not.
@@ -9562,7 +9619,7 @@ var GuildMemberManager = class extends CachedManager {
9562
9619
  async ban(guildId, userId, options = {}) {
9563
9620
  const body = { delete_message_seconds: options.deleteMessageSeconds };
9564
9621
  await this.client.api.guilds.banUser(guildId, userId, body, { reason: options.reason });
9565
- await this.cache?.delete(this.resolveKey(guildId, userId));
9622
+ await this.cache.delete(this.resolveKey(guildId, userId));
9566
9623
  }
9567
9624
  /**
9568
9625
  * Lifts a user's ban.
@@ -9588,7 +9645,7 @@ var GuildMemberManager = class extends CachedManager {
9588
9645
  delete_message_seconds: options.deleteMessageSeconds
9589
9646
  };
9590
9647
  const result = await this.client.api.guilds.bulkBanUsers(guildId, body, { reason: options.reason });
9591
- await Promise.all(result.banned_users.map((userId) => this.cache?.delete(this.resolveKey(guildId, userId))));
9648
+ await Promise.all(result.banned_users.map((userId) => this.cache.delete(this.resolveKey(guildId, userId))));
9592
9649
  return {
9593
9650
  bannedUsers: result.banned_users,
9594
9651
  failedUsers: result.failed_users
@@ -9643,24 +9700,19 @@ var GuildMemberManager = class extends CachedManager {
9643
9700
  guild_id: guildId
9644
9701
  };
9645
9702
  }
9646
- store(guildId, member) {
9703
+ async store(guildId, member) {
9647
9704
  const raw = {
9648
9705
  ...member,
9649
9706
  guild_id: guildId
9650
9707
  };
9651
- return raw.user ? this._add(raw) : this.hydrate(raw);
9708
+ return raw.user ? this._add(raw) : this._build(raw);
9652
9709
  }
9653
9710
  settle(nonce, request) {
9654
9711
  if (request.timer) clearTimeout(request.timer);
9655
9712
  this.#requests.delete(nonce);
9656
9713
  }
9657
9714
  async updateCachedRoles(guildId, userId, update) {
9658
- const key = this.resolveKey(guildId, userId);
9659
- const cached = await this.cache?.get(key);
9660
- if (cached) await this.cache.set(key, {
9661
- ...cached,
9662
- roles: update(cached.roles)
9663
- });
9715
+ await this._patchCached(this.resolveKey(guildId, userId), (cached) => ({ roles: update(cached.roleIds) }));
9664
9716
  }
9665
9717
  };
9666
9718
 
@@ -9669,8 +9721,7 @@ var GuildMemberManager = class extends CachedManager {
9669
9721
  /**
9670
9722
  * Manages the users who reacted to a message with one emoji.
9671
9723
  */
9672
- var ReactionUserManager = class {
9673
- client;
9724
+ var ReactionUserManager = class extends BaseManager {
9674
9725
  channelId;
9675
9726
  messageId;
9676
9727
  /**
@@ -9678,7 +9729,7 @@ var ReactionUserManager = class {
9678
9729
  */
9679
9730
  emoji;
9680
9731
  constructor(client, channelId, messageId, emoji) {
9681
- this.client = client;
9732
+ super(client);
9682
9733
  this.channelId = channelId;
9683
9734
  this.messageId = messageId;
9684
9735
  this.emoji = emoji;
@@ -9842,8 +9893,7 @@ Mixin(MessageReaction, [StructureMixin]);
9842
9893
  /**
9843
9894
  * Manages the reactions of one message, as they were in the message's payload.
9844
9895
  */
9845
- var ReactionManager = class {
9846
- client;
9896
+ var ReactionManager = class extends BaseManager {
9847
9897
  channelId;
9848
9898
  messageId;
9849
9899
  #reactions;
@@ -9858,7 +9908,7 @@ var ReactionManager = class {
9858
9908
  * @param emojis The cached custom emojis of the reactions, by ID.
9859
9909
  */
9860
9910
  constructor(client, channelId, messageId, reactions, message = null, emojis) {
9861
- this.client = client;
9911
+ super(client);
9862
9912
  this.channelId = channelId;
9863
9913
  this.messageId = messageId;
9864
9914
  this.#reactions = reactions;
@@ -11383,7 +11433,7 @@ var Message = class Message extends Message$1 {
11383
11433
  const resolved = this[kRelations].thread;
11384
11434
  if (resolved) return resolved;
11385
11435
  const { thread } = this[kData];
11386
- return thread ? this.client.threads.construct(thread) : null;
11436
+ return thread ? this.client.threads.cache.construct(thread) : null;
11387
11437
  }
11388
11438
  /**
11389
11439
  * The content with user, role, and channel mentions replaced by names, and `@everyone`/`@here` defused, like
@@ -11621,7 +11671,7 @@ var MessageManager = class extends CachedManager {
11621
11671
  constructor(client) {
11622
11672
  super(client, "messages");
11623
11673
  }
11624
- construct(data) {
11674
+ createStructure(data) {
11625
11675
  return new Message(data);
11626
11676
  }
11627
11677
  keyOf(data) {
@@ -11646,15 +11696,15 @@ var MessageManager = class extends CachedManager {
11646
11696
  _hydrate(data) {
11647
11697
  const { author, member, guild_id: guildId } = data;
11648
11698
  return whenAll([
11649
- data.webhook_id ? this.client.users.construct(author) : this.client.users._resolveData(author),
11699
+ data.webhook_id ? this.client.users.cache.construct(author) : this.client.users._resolveData(author),
11650
11700
  member && guildId ? this.client.members._resolveData({
11651
11701
  ...member,
11652
11702
  user: author,
11653
11703
  guild_id: guildId
11654
11704
  }) : null,
11655
11705
  this.cachedGuild(guildId),
11656
- this.client.channels._get(data.channel_id),
11657
- data.thread !== void 0 || ((data.flags ?? 0) & MessageFlags.HasThread) !== 0 ? this.client.threads._get(data.id) : void 0,
11706
+ this.client.channels.cache.get(data.channel_id),
11707
+ data.thread !== void 0 || ((data.flags ?? 0) & MessageFlags.HasThread) !== 0 ? this.client.threads.cache.get(data.id) : void 0,
11658
11708
  this.resolveMentions(data),
11659
11709
  guildId ? this.resolveEmojis(guildId, data) : void 0
11660
11710
  ], ([resolvedAuthor, resolvedMember, guild, channel, thread, mentions, emojis]) => new Message(data, {
@@ -11688,7 +11738,7 @@ var MessageManager = class extends CachedManager {
11688
11738
  resolveEmojis(guildId, data) {
11689
11739
  const ids = [...(data.reactions ?? []).map((reaction) => reaction.emoji.id), ...(data.poll?.answers ?? []).map((answer) => answer.poll_media.emoji?.id)].filter((id) => Boolean(id));
11690
11740
  const emojis = this.client.guilds.emojis(guildId);
11691
- return whenCachedMap(ids, (id) => emojis._get(id));
11741
+ return whenCachedMap(ids, (id) => emojis.cache.get(emojis.resolveKey(id)));
11692
11742
  }
11693
11743
  resolveKey(channelId, messageId) {
11694
11744
  return messageKey(channelId, messageId);
@@ -11759,7 +11809,7 @@ var MessageManager = class extends CachedManager {
11759
11809
  */
11760
11810
  async delete(channelId, messageId, reason) {
11761
11811
  await this.client.api.channels.deleteMessage(channelId, messageId, { reason });
11762
- await this.cache?.delete(this.resolveKey(channelId, messageId));
11812
+ await this.cache.delete(this.resolveKey(channelId, messageId));
11763
11813
  }
11764
11814
  /**
11765
11815
  * Deletes up to 100 messages at once, skipping the ones older than 14 days that Discord refuses.
@@ -11779,7 +11829,7 @@ var MessageManager = class extends CachedManager {
11779
11829
  if (ids.length === 1) await this.delete(channelId, ids[0]);
11780
11830
  else {
11781
11831
  await this.client.api.channels.bulkDeleteMessages(channelId, ids);
11782
- await Promise.all(ids.map((id) => this.cache?.delete(this.resolveKey(channelId, id))));
11832
+ await Promise.all(ids.map((id) => this.cache.delete(this.resolveKey(channelId, id))));
11783
11833
  }
11784
11834
  return ids;
11785
11835
  }
@@ -11811,7 +11861,7 @@ var MessageManager = class extends CachedManager {
11811
11861
  */
11812
11862
  async pin(channelId, messageId, reason) {
11813
11863
  await this.client.api.channels.pinMessage(channelId, messageId, { reason });
11814
- await this.patchCached(channelId, messageId, { pinned: true });
11864
+ await this._patchCached(this.resolveKey(channelId, messageId), { pinned: true });
11815
11865
  }
11816
11866
  /**
11817
11867
  * Unpins a message.
@@ -11822,7 +11872,7 @@ var MessageManager = class extends CachedManager {
11822
11872
  */
11823
11873
  async unpin(channelId, messageId, reason) {
11824
11874
  await this.client.api.channels.unpinMessage(channelId, messageId, { reason });
11825
- await this.patchCached(channelId, messageId, { pinned: false });
11875
+ await this._patchCached(this.resolveKey(channelId, messageId), { pinned: false });
11826
11876
  }
11827
11877
  /**
11828
11878
  * Publishes a message of an announcement channel to the channels following it.
@@ -11862,7 +11912,7 @@ var MessageManager = class extends CachedManager {
11862
11912
  */
11863
11913
  async removeAllReactions(channelId, messageId) {
11864
11914
  await this.client.api.channels.deleteAllMessageReactions(channelId, messageId);
11865
- await this.patchCached(channelId, messageId, { reactions: [] });
11915
+ await this._patchCached(this.resolveKey(channelId, messageId), { reactions: [] });
11866
11916
  }
11867
11917
  /**
11868
11918
  * Starts a thread from a message.
@@ -11908,14 +11958,6 @@ var MessageManager = class extends CachedManager {
11908
11958
  store(message) {
11909
11959
  return this._add(message);
11910
11960
  }
11911
- async patchCached(channelId, messageId, patch) {
11912
- const key = this.resolveKey(channelId, messageId);
11913
- const cached = await this.cache?.get(key);
11914
- if (cached) await this.cache.set(key, {
11915
- ...cached,
11916
- ...patch
11917
- });
11918
- }
11919
11961
  };
11920
11962
 
11921
11963
  //#endregion
@@ -12150,7 +12192,7 @@ var RoleManager = class extends CachedManager {
12150
12192
  constructor(client) {
12151
12193
  super(client, "roles");
12152
12194
  }
12153
- construct(data) {
12195
+ createStructure(data) {
12154
12196
  return new Role(data);
12155
12197
  }
12156
12198
  keyOf(data) {
@@ -12213,7 +12255,7 @@ var RoleManager = class extends CachedManager {
12213
12255
  */
12214
12256
  async delete(guildId, roleId, reason) {
12215
12257
  await this.client.api.guilds.deleteRole(guildId, roleId, { reason });
12216
- await this.cache?.delete(this.resolveKey(guildId, roleId));
12258
+ await this.cache.delete(this.resolveKey(guildId, roleId));
12217
12259
  }
12218
12260
  /**
12219
12261
  * Moves a role among the roles of its guild, like discord.js's `RoleManager#setPosition`.
@@ -12333,10 +12375,9 @@ async function toRoleBody(options) {
12333
12375
  * Methods taking a webhook's token call the API with it rather than with the bot's authorization, so they work for
12334
12376
  * webhooks of other applications too.
12335
12377
  */
12336
- var WebhookManager = class {
12337
- client;
12378
+ var WebhookManager = class extends BaseManager {
12338
12379
  constructor(client) {
12339
- this.client = client;
12380
+ super(client);
12340
12381
  }
12341
12382
  /**
12342
12383
  * Fetches a webhook.
@@ -12480,11 +12521,11 @@ var WebhookManager = class {
12480
12521
  */
12481
12522
  async hydrate(data) {
12482
12523
  const [guild, channel, sourceGuild, sourceChannel, owner] = await Promise.all([
12483
- data.guild_id ? this.client.guilds.get(data.guild_id) : void 0,
12484
- data.channel_id ? this.client.channels.get(data.channel_id) : void 0,
12485
- data.source_guild ? this.client.guilds.get(data.source_guild.id) : void 0,
12486
- data.source_channel ? this.client.channels.get(data.source_channel.id) : void 0,
12487
- data.user ? this.client.users.resolveData(data.user) : void 0
12524
+ data.guild_id ? this.client.guilds.cache.get(data.guild_id) : void 0,
12525
+ data.channel_id ? this.client.channels.cache.get(data.channel_id) : void 0,
12526
+ data.source_guild ? this.client.guilds.cache.get(data.source_guild.id) : void 0,
12527
+ data.source_channel ? this.client.channels.cache.get(data.source_channel.id) : void 0,
12528
+ data.user ? this.client.users._resolveData(data.user) : void 0
12488
12529
  ]);
12489
12530
  return bindClient(new Webhook(data, {
12490
12531
  guild: guild ?? null,
@@ -12622,10 +12663,9 @@ var GuildTemplate = class extends Structure {
12622
12663
  /**
12623
12664
  * Manages guild templates. Discord does not send them over the gateway, so they are never cached.
12624
12665
  */
12625
- var GuildTemplateManager = class {
12626
- client;
12666
+ var GuildTemplateManager = class extends BaseManager {
12627
12667
  constructor(client) {
12628
- this.client = client;
12668
+ super(client);
12629
12669
  }
12630
12670
  /**
12631
12671
  * Fetches a template by its code.
@@ -12708,7 +12748,7 @@ var GuildTemplateManager = class {
12708
12748
  }
12709
12749
  async build(template) {
12710
12750
  const creator = await this.client.users._add(template.creator);
12711
- const guild = await this.client.guilds.get(template.source_guild_id) ?? null;
12751
+ const guild = await this.client.guilds.cache.get(template.source_guild_id) ?? null;
12712
12752
  return new GuildTemplate(template, {
12713
12753
  guild,
12714
12754
  creator
@@ -12729,7 +12769,7 @@ var VoiceStateManager = class extends CachedManager {
12729
12769
  constructor(client) {
12730
12770
  super(client, "voiceStates");
12731
12771
  }
12732
- construct(data) {
12772
+ createStructure(data) {
12733
12773
  return new VoiceState(data);
12734
12774
  }
12735
12775
  keyOf(data) {
@@ -12745,9 +12785,9 @@ var VoiceStateManager = class extends CachedManager {
12745
12785
  member?.user && guildId ? this.client.members._resolveData({
12746
12786
  ...member,
12747
12787
  guild_id: guildId
12748
- }) : guildId ? this.client.members._get(guildId, userId) : null,
12788
+ }) : guildId ? this.client.members.cache.get(this.client.members.resolveKey(guildId, userId)) : null,
12749
12789
  this.cachedGuild(guildId),
12750
- data.channel_id ? this.client.channels._get(data.channel_id) : void 0
12790
+ data.channel_id ? this.client.channels.cache.get(data.channel_id) : void 0
12751
12791
  ], ([resolvedMember, guild, channel]) => new VoiceState(data, {
12752
12792
  member: resolvedMember ?? null,
12753
12793
  guild,
@@ -12763,8 +12803,9 @@ var VoiceStateManager = class extends CachedManager {
12763
12803
  */
12764
12804
  async listCached(guildId) {
12765
12805
  const prefix = `${guildId}:`;
12766
- const entries = await this.iterableCache()?.entries() ?? [];
12767
- return Promise.all(entries.filter(([key]) => key.startsWith(prefix)).map(([, raw]) => this.hydrate(raw)));
12806
+ const cache = this.iterableCache();
12807
+ const entries = cache ? await this.guard("entries", null, () => cache.entries(), []) : [];
12808
+ return Promise.all(entries.filter(([key]) => key.startsWith(prefix)).map(([, raw]) => this._build(raw)));
12768
12809
  }
12769
12810
  async fetchRaw(guildId, userId) {
12770
12811
  const client = this.client;
@@ -12783,13 +12824,13 @@ var VoiceStateManager = class extends CachedManager {
12783
12824
  *
12784
12825
  * @remarks
12785
12826
  * Presences only come from the gateway, with the `GuildPresences` intent: there is no API to fetch them, so `fetch`
12786
- * rejects on a cache miss. Use `get`, which resolves to `undefined` instead.
12827
+ * rejects on a cache miss. Use `cache.get`, which answers `undefined` instead.
12787
12828
  */
12788
12829
  var PresenceManager = class extends CachedManager {
12789
12830
  constructor(client) {
12790
12831
  super(client, "presences");
12791
12832
  }
12792
- construct(data) {
12833
+ createStructure(data) {
12793
12834
  return new Presence(data);
12794
12835
  }
12795
12836
  keyOf(data) {
@@ -12800,8 +12841,8 @@ var PresenceManager = class extends CachedManager {
12800
12841
  }
12801
12842
  _hydrate(data) {
12802
12843
  return whenAll([
12803
- this.client.users._get(data.user.id),
12804
- this.client.members._get(data.guild_id, data.user.id),
12844
+ this.client.users.cache.get(data.user.id),
12845
+ this.client.members.cache.get(this.client.members.resolveKey(data.guild_id, data.user.id)),
12805
12846
  this.cachedGuild(data.guild_id)
12806
12847
  ], ([user, member, guild]) => new Presence(data, {
12807
12848
  user: user ?? null,
@@ -12818,8 +12859,9 @@ var PresenceManager = class extends CachedManager {
12818
12859
  */
12819
12860
  async listCached(guildId) {
12820
12861
  const prefix = `${guildId}:`;
12821
- const entries = await this.iterableCache()?.entries() ?? [];
12822
- return Promise.all(entries.filter(([key]) => key.startsWith(prefix)).map(([, raw]) => this.hydrate(raw)));
12862
+ const cache = this.iterableCache();
12863
+ const entries = cache ? await this.guard("entries", null, () => cache.entries(), []) : [];
12864
+ return Promise.all(entries.filter(([key]) => key.startsWith(prefix)).map(([, raw]) => this._build(raw)));
12823
12865
  }
12824
12866
  fetchRaw(guildId, userId) {
12825
12867
  return Promise.reject(new GatewayError("PresenceNotFetchable", guildId, userId));
@@ -12835,7 +12877,7 @@ var ThreadManager = class extends CachedManager {
12835
12877
  constructor(client) {
12836
12878
  super(client, "threads");
12837
12879
  }
12838
- construct(data) {
12880
+ createStructure(data) {
12839
12881
  return createChannel(data);
12840
12882
  }
12841
12883
  keyOf(data) {
@@ -13026,7 +13068,7 @@ var ThreadMemberManager = class extends CachedManager {
13026
13068
  constructor(client) {
13027
13069
  super(client, "threadMembers");
13028
13070
  }
13029
- construct(data) {
13071
+ createStructure(data) {
13030
13072
  return new ThreadMember(data);
13031
13073
  }
13032
13074
  keyOf(data) {
@@ -13055,9 +13097,9 @@ var ThreadMemberManager = class extends CachedManager {
13055
13097
  member?.user && guildId ? this.client.members._resolveData({
13056
13098
  ...member,
13057
13099
  guild_id: guildId
13058
- }) : guildId && userId ? this.client.members._get(guildId, userId) : null,
13059
- data.id ? this.client.threads._get(data.id) : void 0,
13060
- userId ? this.client.users._get(userId) : void 0
13100
+ }) : guildId && userId ? this.client.members.cache.get(this.client.members.resolveKey(guildId, userId)) : null,
13101
+ data.id ? this.client.threads.cache.get(data.id) : void 0,
13102
+ userId ? this.client.users.cache.get(userId) : void 0
13061
13103
  ], ([guildMember, thread, user]) => new ThreadMember(data, {
13062
13104
  guildMember: guildMember ?? null,
13063
13105
  thread: thread ?? null,
@@ -13113,7 +13155,7 @@ var ThreadMemberManager = class extends CachedManager {
13113
13155
  if (userId === "@me") await this.client.api.threads.leave(threadId);
13114
13156
  else await this.client.api.threads.removeMember(threadId, userId);
13115
13157
  const cachedId = userId === "@me" ? this.client.user?.id ?? this.client.id : userId;
13116
- await this.cache?.delete(this.resolveKey(threadId, cachedId));
13158
+ await this.cache.delete(this.resolveKey(threadId, cachedId));
13117
13159
  }
13118
13160
  async fetchRaw(threadId, userId) {
13119
13161
  const member = await this.client.api.threads.getMember(threadId, userId);
@@ -13127,7 +13169,7 @@ var ThreadMemberManager = class extends CachedManager {
13127
13169
  } : member;
13128
13170
  }
13129
13171
  async guildIdOf(threadId) {
13130
- return ((await this.client.threads.get(threadId))?.toJSON())?.guild_id;
13172
+ return ((await this.client.threads.cache.get(threadId))?.toJSON())?.guild_id;
13131
13173
  }
13132
13174
  };
13133
13175
 
@@ -13140,7 +13182,7 @@ var UserManager = class extends CachedManager {
13140
13182
  constructor(client) {
13141
13183
  super(client, "users");
13142
13184
  }
13143
- construct(data) {
13185
+ createStructure(data) {
13144
13186
  return new User(data);
13145
13187
  }
13146
13188
  keyOf(data) {
@@ -13171,7 +13213,7 @@ var UserManager = class extends CachedManager {
13171
13213
  async deleteDM(userId) {
13172
13214
  const channel = await this.createDM(userId);
13173
13215
  await this.client.api.channels.delete(channel.id);
13174
- await this.client.cache?.channels?.delete(channel.id);
13216
+ await this.client.channels.cache.delete(channel.id);
13175
13217
  return channel;
13176
13218
  }
13177
13219
  /**
@@ -13747,6 +13789,15 @@ const Partials = {
13747
13789
 
13748
13790
  //#endregion
13749
13791
  //#region src/util/dispatch.ts
13792
+ async function previousOf(manager, ...args) {
13793
+ const key = manager.resolveKey(...args);
13794
+ const held = await peekCache(manager.cache, key);
13795
+ if (held === void 0) return void 0;
13796
+ return (Object.keys(held[kRelations]).length === 0 ? await manager.cache.get(key) : held)?.[kClone]();
13797
+ }
13798
+ function cachedOf(manager, ...args) {
13799
+ return manager.cache.get(manager.resolveKey(...args));
13800
+ }
13750
13801
  /**
13751
13802
  * The table mapping every supported dispatch to its event.
13752
13803
  *
@@ -13765,30 +13816,30 @@ const DispatchHandlers = {
13765
13816
  },
13766
13817
  [GatewayDispatchEvents.GuildCreate]: {
13767
13818
  event: "guildCreate",
13768
- build: async (client, data) => [await cachedOrUndefined(client.guilds.get(data.id)) ?? await client.guilds.hydrate(data)]
13819
+ build: async (client, data) => [await cachedOrUndefined(() => client.guilds.cache.get(data.id)) ?? await client.guilds._build(data)]
13769
13820
  },
13770
13821
  [GatewayDispatchEvents.GuildUpdate]: {
13771
13822
  event: "guildUpdate",
13772
- before: (client, data) => client.guilds.get(data.id),
13773
- build: async (client, data, previous) => [previous ?? null, await client.guilds.hydrate(data)]
13823
+ before: (client, data) => previousOf(client.guilds, data.id),
13824
+ build: async (client, data, previous) => [previous ?? null, await client.guilds._build(data)]
13774
13825
  },
13775
13826
  [GatewayDispatchEvents.GuildDelete]: {
13776
13827
  event: "guildDelete",
13777
- before: (client, data) => client.guilds.get(data.id),
13828
+ before: (client, data) => previousOf(client.guilds, data.id),
13778
13829
  build: (_client, data, previous) => [previous ?? null, data]
13779
13830
  },
13780
13831
  [GatewayDispatchEvents.ChannelCreate]: {
13781
13832
  event: "channelCreate",
13782
- build: async (client, data) => [await client.channels.hydrate(data)]
13833
+ build: async (client, data) => [await client.channels._build(data)]
13783
13834
  },
13784
13835
  [GatewayDispatchEvents.ChannelUpdate]: {
13785
13836
  event: "channelUpdate",
13786
- before: (client, data) => client.channels.get(data.id),
13787
- build: async (client, data, previous) => [previous ?? null, await client.channels.hydrate(data)]
13837
+ before: (client, data) => previousOf(client.channels, data.id),
13838
+ build: async (client, data, previous) => [previous ?? null, await client.channels._build(data)]
13788
13839
  },
13789
13840
  [GatewayDispatchEvents.ChannelDelete]: {
13790
13841
  event: "channelDelete",
13791
- build: async (client, data) => [await client.channels.hydrate(data)]
13842
+ build: async (client, data) => [await client.channels._build(data)]
13792
13843
  },
13793
13844
  [GatewayDispatchEvents.ChannelPinsUpdate]: {
13794
13845
  event: "channelPinsUpdate",
@@ -13800,23 +13851,23 @@ const DispatchHandlers = {
13800
13851
  },
13801
13852
  [GatewayDispatchEvents.ThreadCreate]: {
13802
13853
  event: "threadCreate",
13803
- build: async (client, data) => [await client.threads.hydrate(data)]
13854
+ build: async (client, data) => [await client.threads._build(data)]
13804
13855
  },
13805
13856
  [GatewayDispatchEvents.ThreadUpdate]: {
13806
13857
  event: "threadUpdate",
13807
- before: (client, data) => client.threads.get(data.id),
13808
- build: async (client, data, previous) => [previous ?? null, await client.threads.hydrate(data)]
13858
+ before: (client, data) => previousOf(client.threads, data.id),
13859
+ build: async (client, data, previous) => [previous ?? null, await client.threads._build(data)]
13809
13860
  },
13810
13861
  [GatewayDispatchEvents.ThreadDelete]: {
13811
13862
  event: "threadDelete",
13812
- before: (client, data) => client.threads.get(data.id),
13863
+ before: (client, data) => previousOf(client.threads, data.id),
13813
13864
  build: (_client, data, previous) => [previous ?? null, data]
13814
13865
  },
13815
13866
  [GatewayDispatchEvents.ThreadListSync]: {
13816
13867
  event: "threadListSync",
13817
13868
  build: async (client, data) => [
13818
- await Promise.all(data.threads.map((thread) => client.threads.hydrate(thread))),
13819
- await Promise.all(data.members.map((member) => client.threadMembers.hydrate({
13869
+ await Promise.all(data.threads.map((thread) => client.threads._build(thread))),
13870
+ await Promise.all(data.members.map((member) => client.threadMembers._build({
13820
13871
  ...member,
13821
13872
  guild_id: data.guild_id
13822
13873
  }))),
@@ -13825,43 +13876,43 @@ const DispatchHandlers = {
13825
13876
  },
13826
13877
  [GatewayDispatchEvents.ThreadMemberUpdate]: {
13827
13878
  event: "threadMemberUpdate",
13828
- before: (client, data) => data.id && data.user_id ? client.threadMembers.get(data.id, data.user_id) : Promise.resolve(void 0),
13829
- build: async (client, data, previous) => [previous ?? (data.id && data.user_id ? partialThreadMember(client, data.id, data.user_id, data.guild_id) : null), await client.threadMembers.hydrate(data)]
13879
+ before: (client, data) => data.id && data.user_id ? previousOf(client.threadMembers, data.id, data.user_id) : Promise.resolve(void 0),
13880
+ build: async (client, data, previous) => [previous ?? (data.id && data.user_id ? partialThreadMember(client, data.id, data.user_id, data.guild_id) : null), await client.threadMembers._build(data)]
13830
13881
  },
13831
13882
  [GatewayDispatchEvents.ThreadMembersUpdate]: {
13832
13883
  event: "threadMembersUpdate",
13833
13884
  before: async (client, data) => {
13834
- return (await Promise.all((data.removed_member_ids ?? []).map((userId) => client.threadMembers.get(data.id, userId)))).filter((member) => member !== void 0);
13885
+ return (await Promise.all((data.removed_member_ids ?? []).map((userId) => previousOf(client.threadMembers, data.id, userId)))).filter((member) => member !== void 0);
13835
13886
  },
13836
13887
  build: async (client, data, previous) => [
13837
- await Promise.all((data.added_members ?? []).map((member) => client.threadMembers.hydrate({
13888
+ await Promise.all((data.added_members ?? []).map((member) => client.threadMembers._build({
13838
13889
  ...member,
13839
13890
  id: data.id,
13840
13891
  guild_id: data.guild_id
13841
13892
  }))),
13842
13893
  withPartials(previous ?? [], data.removed_member_ids ?? [], (userId) => partialThreadMember(client, data.id, userId, data.guild_id)),
13843
- await cachedOrUndefined(client.threads.get(data.id)) ?? null,
13894
+ await cachedOrUndefined(() => client.threads.cache.get(data.id)) ?? null,
13844
13895
  data
13845
13896
  ]
13846
13897
  },
13847
13898
  [GatewayDispatchEvents.MessageCreate]: {
13848
13899
  event: "messageCreate",
13849
- build: async (client, data) => [await client.messages.hydrate(data)]
13900
+ build: async (client, data) => [await client.messages._build(data)]
13850
13901
  },
13851
13902
  [GatewayDispatchEvents.MessageUpdate]: {
13852
13903
  event: "messageUpdate",
13853
- before: (client, data) => client.messages.get(data.channel_id, data.id),
13854
- build: async (client, data, previous) => [previous ?? partialMessage(client, data.channel_id, data.id, data.guild_id), await client.messages.hydrate(data)]
13904
+ before: (client, data) => previousOf(client.messages, data.channel_id, data.id),
13905
+ build: async (client, data, previous) => [previous ?? partialMessage(client, data.channel_id, data.id, data.guild_id), await client.messages._build(data)]
13855
13906
  },
13856
13907
  [GatewayDispatchEvents.MessageDelete]: {
13857
13908
  event: "messageDelete",
13858
- before: (client, data) => client.messages.get(data.channel_id, data.id),
13909
+ before: (client, data) => previousOf(client.messages, data.channel_id, data.id),
13859
13910
  build: (client, data, previous) => [previous ?? partialMessage(client, data.channel_id, data.id, data.guild_id), data]
13860
13911
  },
13861
13912
  [GatewayDispatchEvents.MessageDeleteBulk]: {
13862
13913
  event: "messageDeleteBulk",
13863
13914
  before: async (client, data) => {
13864
- return (await Promise.all(data.ids.map((id) => client.messages.get(data.channel_id, id)))).filter((message) => message !== void 0);
13915
+ return (await Promise.all(data.ids.map((id) => previousOf(client.messages, data.channel_id, id)))).filter((message) => message !== void 0);
13865
13916
  },
13866
13917
  build: (client, data, previous) => [withPartials(previous ?? [], data.ids, (id) => partialMessage(client, data.channel_id, id, data.guild_id)), data]
13867
13918
  },
@@ -13869,7 +13920,7 @@ const DispatchHandlers = {
13869
13920
  event: "messageReactionAdd",
13870
13921
  build: async (client, data) => [
13871
13922
  await reactionOf(client, data),
13872
- await cachedOrUndefined(client.users.get(data.user_id)) ?? partialUser(client, data.user_id),
13923
+ await cachedOrUndefined(() => client.users.cache.get(data.user_id)) ?? partialUser(client, data.user_id),
13873
13924
  {
13874
13925
  userId: data.user_id,
13875
13926
  type: data.type,
@@ -13881,7 +13932,7 @@ const DispatchHandlers = {
13881
13932
  event: "messageReactionRemove",
13882
13933
  build: async (client, data) => [
13883
13934
  await reactionOf(client, data),
13884
- await cachedOrUndefined(client.users.get(data.user_id)) ?? partialUser(client, data.user_id),
13935
+ await cachedOrUndefined(() => client.users.cache.get(data.user_id)) ?? partialUser(client, data.user_id),
13885
13936
  {
13886
13937
  userId: data.user_id,
13887
13938
  type: data.type,
@@ -13891,16 +13942,16 @@ const DispatchHandlers = {
13891
13942
  },
13892
13943
  [GatewayDispatchEvents.MessageReactionRemoveAll]: {
13893
13944
  event: "messageReactionRemoveAll",
13894
- before: async (client, data) => (await client.messages.get(data.channel_id, data.message_id))?.reactions.cache ?? [],
13945
+ before: async (client, data) => (await previousOf(client.messages, data.channel_id, data.message_id))?.reactions.cache ?? [],
13895
13946
  build: async (client, data, previous) => [
13896
- await cachedOrUndefined(client.messages.get(data.channel_id, data.message_id)) ?? partialMessage(client, data.channel_id, data.message_id, data.guild_id),
13947
+ await cachedOrUndefined(() => cachedOf(client.messages, data.channel_id, data.message_id)) ?? partialMessage(client, data.channel_id, data.message_id, data.guild_id),
13897
13948
  previous ?? [],
13898
13949
  data
13899
13950
  ]
13900
13951
  },
13901
13952
  [GatewayDispatchEvents.MessageReactionRemoveEmoji]: {
13902
13953
  event: "messageReactionRemoveEmoji",
13903
- before: async (client, data) => (await client.messages.get(data.channel_id, data.message_id))?.reactions.resolve(data.emoji) ?? void 0,
13954
+ before: async (client, data) => (await previousOf(client.messages, data.channel_id, data.message_id))?.reactions.resolve(data.emoji) ?? void 0,
13904
13955
  build: async (client, data, previous) => [previous ?? await partialReaction(client, data)]
13905
13956
  },
13906
13957
  [GatewayDispatchEvents.MessagePollVoteAdd]: {
@@ -13913,25 +13964,25 @@ const DispatchHandlers = {
13913
13964
  },
13914
13965
  [GatewayDispatchEvents.GuildMemberAdd]: {
13915
13966
  event: "guildMemberAdd",
13916
- build: async (client, data) => [await client.members.hydrate(data)]
13967
+ build: async (client, data) => [await client.members._build(data)]
13917
13968
  },
13918
13969
  [GatewayDispatchEvents.GuildMemberUpdate]: {
13919
13970
  event: "guildMemberUpdate",
13920
- before: (client, data) => client.members.get(data.guild_id, data.user.id),
13921
- build: async (client, data, previous) => [previous ?? partialMember(client, data.guild_id, { id: data.user.id }), await cachedOrUndefined(client.members.get(data.guild_id, data.user.id)) ?? await client.members.hydrate(data)]
13971
+ before: (client, data) => previousOf(client.members, data.guild_id, data.user.id),
13972
+ build: async (client, data, previous) => [previous ?? partialMember(client, data.guild_id, { id: data.user.id }), await cachedOrUndefined(() => cachedOf(client.members, data.guild_id, data.user.id)) ?? await client.members._build(data)]
13922
13973
  },
13923
13974
  [GatewayDispatchEvents.GuildMemberRemove]: {
13924
13975
  event: "guildMemberRemove",
13925
- before: (client, data) => client.members.get(data.guild_id, data.user.id),
13976
+ before: (client, data) => previousOf(client.members, data.guild_id, data.user.id),
13926
13977
  build: (client, data, previous) => [previous ?? partialMember(client, data.guild_id, data.user), data]
13927
13978
  },
13928
13979
  [GatewayDispatchEvents.GuildMembersChunk]: {
13929
13980
  event: "guildMembersChunk",
13930
13981
  build: async (client, data) => {
13931
- const [members, guild] = await Promise.all([Promise.all(data.members.map((member) => client.members.hydrate({
13982
+ const [members, guild] = await Promise.all([Promise.all(data.members.map((member) => client.members._build({
13932
13983
  ...member,
13933
13984
  guild_id: data.guild_id
13934
- }))), cachedOrUndefined(client.guilds.get(data.guild_id))]);
13985
+ }))), cachedOrUndefined(() => client.guilds.cache.get(data.guild_id))]);
13935
13986
  client.members.handleChunk(members, data);
13936
13987
  return [
13937
13988
  members,
@@ -13942,31 +13993,31 @@ const DispatchHandlers = {
13942
13993
  },
13943
13994
  [GatewayDispatchEvents.GuildRoleCreate]: {
13944
13995
  event: "guildRoleCreate",
13945
- build: async (client, data) => [await client.roles.hydrate({
13996
+ build: async (client, data) => [await client.roles._build({
13946
13997
  ...data.role,
13947
13998
  guild_id: data.guild_id
13948
13999
  })]
13949
14000
  },
13950
14001
  [GatewayDispatchEvents.GuildRoleUpdate]: {
13951
14002
  event: "guildRoleUpdate",
13952
- before: (client, data) => client.roles.get(data.guild_id, data.role.id),
13953
- build: async (client, data, previous) => [previous ?? null, await client.roles.hydrate({
14003
+ before: (client, data) => previousOf(client.roles, data.guild_id, data.role.id),
14004
+ build: async (client, data, previous) => [previous ?? null, await client.roles._build({
13954
14005
  ...data.role,
13955
14006
  guild_id: data.guild_id
13956
14007
  })]
13957
14008
  },
13958
14009
  [GatewayDispatchEvents.GuildRoleDelete]: {
13959
14010
  event: "guildRoleDelete",
13960
- before: (client, data) => client.roles.get(data.guild_id, data.role_id),
14011
+ before: (client, data) => previousOf(client.roles, data.guild_id, data.role_id),
13961
14012
  build: (_client, data, previous) => [previous ?? null, data]
13962
14013
  },
13963
14014
  [GatewayDispatchEvents.InviteCreate]: {
13964
14015
  event: "inviteCreate",
13965
- build: async (client, data) => [data.guild_id ? await client.guilds.invites(data.guild_id).hydrate(data) : bindClient(new GuildInvite(data, { channel: await cachedOrUndefined(client.channels.get(data.channel_id)) ?? null }), client)]
14016
+ build: async (client, data) => [data.guild_id ? await client.guilds.invites(data.guild_id)._build(data) : bindClient(new GuildInvite(data, { channel: await cachedOrUndefined(() => client.channels.cache.get(data.channel_id)) ?? null }), client)]
13966
14017
  },
13967
14018
  [GatewayDispatchEvents.InviteDelete]: {
13968
14019
  event: "inviteDelete",
13969
- before: async (client, data) => data.guild_id ? client.guilds.invites(data.guild_id).get(data.code) : void 0,
14020
+ before: async (client, data) => data.guild_id ? previousOf(client.guilds.invites(data.guild_id), data.code) : void 0,
13970
14021
  build: (_client, data, previous) => [previous ?? null, data]
13971
14022
  },
13972
14023
  [GatewayDispatchEvents.TypingStart]: {
@@ -13975,99 +14026,100 @@ const DispatchHandlers = {
13975
14026
  },
13976
14027
  [GatewayDispatchEvents.VoiceStateUpdate]: {
13977
14028
  event: "voiceStateUpdate",
13978
- before: async (client, data) => data.guild_id ? client.voiceStates.get(data.guild_id, data.user_id) : void 0,
13979
- build: async (client, data, previous) => [previous ?? null, await client.voiceStates.hydrate(data)]
14029
+ before: async (client, data) => data.guild_id ? previousOf(client.voiceStates, data.guild_id, data.user_id) : void 0,
14030
+ build: async (client, data, previous) => [previous ?? null, await client.voiceStates._build(data)]
13980
14031
  },
13981
14032
  [GatewayDispatchEvents.PresenceUpdate]: {
13982
14033
  event: "presenceUpdate",
13983
- before: (client, data) => client.presences.get(data.guild_id, data.user.id),
13984
- build: async (client, data, previous) => [previous ?? null, await cachedOrUndefined(client.presences.get(data.guild_id, data.user.id)) ?? await client.presences.hydrate(data)]
14034
+ before: (client, data) => previousOf(client.presences, data.guild_id, data.user.id),
14035
+ build: async (client, data, previous) => [previous ?? null, await cachedOrUndefined(() => cachedOf(client.presences, data.guild_id, data.user.id)) ?? await client.presences._build(data)]
13985
14036
  },
13986
14037
  [GatewayDispatchEvents.GuildScheduledEventCreate]: {
13987
14038
  event: "guildScheduledEventCreate",
13988
- build: async (client, data) => [await client.guilds.scheduledEvents(data.guild_id).hydrate(data)]
14039
+ build: async (client, data) => [await client.guilds.scheduledEvents(data.guild_id)._build(data)]
13989
14040
  },
13990
14041
  [GatewayDispatchEvents.GuildScheduledEventUpdate]: {
13991
14042
  event: "guildScheduledEventUpdate",
13992
- before: (client, data) => client.guilds.scheduledEvents(data.guild_id).get(data.id),
13993
- build: async (client, data, previous) => [previous ?? partialScheduledEvent(client, data.guild_id, data.id), await client.guilds.scheduledEvents(data.guild_id).hydrate(data)]
14043
+ before: (client, data) => previousOf(client.guilds.scheduledEvents(data.guild_id), data.id),
14044
+ build: async (client, data, previous) => [previous ?? partialScheduledEvent(client, data.guild_id, data.id), await client.guilds.scheduledEvents(data.guild_id)._build(data)]
13994
14045
  },
13995
14046
  [GatewayDispatchEvents.GuildScheduledEventDelete]: {
13996
14047
  event: "guildScheduledEventDelete",
13997
- build: async (client, data) => [await client.guilds.scheduledEvents(data.guild_id).hydrate(data)]
14048
+ build: async (client, data) => [await client.guilds.scheduledEvents(data.guild_id)._build(data)]
13998
14049
  },
13999
14050
  [GatewayDispatchEvents.GuildScheduledEventUserAdd]: {
14000
14051
  event: "guildScheduledEventUserAdd",
14001
14052
  build: async (client, data) => [
14002
- await cachedOrUndefined(client.guilds.scheduledEvents(data.guild_id).get(data.guild_scheduled_event_id)) ?? partialScheduledEvent(client, data.guild_id, data.guild_scheduled_event_id),
14003
- await cachedOrUndefined(client.users.get(data.user_id)) ?? partialUser(client, data.user_id),
14053
+ await cachedOrUndefined(() => cachedOf(client.guilds.scheduledEvents(data.guild_id), data.guild_scheduled_event_id)) ?? partialScheduledEvent(client, data.guild_id, data.guild_scheduled_event_id),
14054
+ await cachedOrUndefined(() => client.users.cache.get(data.user_id)) ?? partialUser(client, data.user_id),
14004
14055
  data
14005
14056
  ]
14006
14057
  },
14007
14058
  [GatewayDispatchEvents.GuildScheduledEventUserRemove]: {
14008
14059
  event: "guildScheduledEventUserRemove",
14009
14060
  build: async (client, data) => [
14010
- await cachedOrUndefined(client.guilds.scheduledEvents(data.guild_id).get(data.guild_scheduled_event_id)) ?? partialScheduledEvent(client, data.guild_id, data.guild_scheduled_event_id),
14011
- await cachedOrUndefined(client.users.get(data.user_id)) ?? partialUser(client, data.user_id),
14061
+ await cachedOrUndefined(() => cachedOf(client.guilds.scheduledEvents(data.guild_id), data.guild_scheduled_event_id)) ?? partialScheduledEvent(client, data.guild_id, data.guild_scheduled_event_id),
14062
+ await cachedOrUndefined(() => client.users.cache.get(data.user_id)) ?? partialUser(client, data.user_id),
14012
14063
  data
14013
14064
  ]
14014
14065
  },
14015
14066
  [GatewayDispatchEvents.StageInstanceCreate]: {
14016
14067
  event: "stageInstanceCreate",
14017
- build: async (client, data) => [await client.guilds.stageInstances(data.guild_id).hydrate(data)]
14068
+ build: async (client, data) => [await client.guilds.stageInstances(data.guild_id)._build(data)]
14018
14069
  },
14019
14070
  [GatewayDispatchEvents.StageInstanceUpdate]: {
14020
14071
  event: "stageInstanceUpdate",
14021
- before: (client, data) => client.guilds.stageInstances(data.guild_id).get(data.channel_id),
14022
- build: async (client, data, previous) => [previous ?? null, await client.guilds.stageInstances(data.guild_id).hydrate(data)]
14072
+ before: (client, data) => previousOf(client.guilds.stageInstances(data.guild_id), data.channel_id),
14073
+ build: async (client, data, previous) => [previous ?? null, await client.guilds.stageInstances(data.guild_id)._build(data)]
14023
14074
  },
14024
14075
  [GatewayDispatchEvents.StageInstanceDelete]: {
14025
14076
  event: "stageInstanceDelete",
14026
- build: async (client, data) => [await client.guilds.stageInstances(data.guild_id).hydrate(data)]
14077
+ build: async (client, data) => [await client.guilds.stageInstances(data.guild_id)._build(data)]
14027
14078
  },
14028
14079
  [GatewayDispatchEvents.GuildSoundboardSoundCreate]: {
14029
14080
  event: "guildSoundboardSoundCreate",
14030
- build: async (client, data) => [await client.guilds.soundboardSounds(data.guild_id).hydrate(data)]
14081
+ build: async (client, data) => [await client.guilds.soundboardSounds(data.guild_id)._build(data)]
14031
14082
  },
14032
14083
  [GatewayDispatchEvents.GuildSoundboardSoundUpdate]: {
14033
14084
  event: "guildSoundboardSoundUpdate",
14034
- before: (client, data) => client.guilds.soundboardSounds(data.guild_id).get(data.sound_id),
14035
- build: async (client, data, previous) => [previous ?? partialSoundboardSound(client, data.guild_id, data.sound_id), await client.guilds.soundboardSounds(data.guild_id).hydrate(data)]
14085
+ before: (client, data) => previousOf(client.guilds.soundboardSounds(data.guild_id), data.sound_id),
14086
+ build: async (client, data, previous) => [previous ?? partialSoundboardSound(client, data.guild_id, data.sound_id), await client.guilds.soundboardSounds(data.guild_id)._build(data)]
14036
14087
  },
14037
14088
  [GatewayDispatchEvents.GuildSoundboardSoundDelete]: {
14038
14089
  event: "guildSoundboardSoundDelete",
14039
- before: (client, data) => client.guilds.soundboardSounds(data.guild_id).get(data.sound_id),
14090
+ before: (client, data) => previousOf(client.guilds.soundboardSounds(data.guild_id), data.sound_id),
14040
14091
  build: (client, data, previous) => [previous ?? partialSoundboardSound(client, data.guild_id, data.sound_id), data]
14041
14092
  },
14042
14093
  [GatewayDispatchEvents.GuildSoundboardSoundsUpdate]: {
14043
14094
  event: "guildSoundboardSoundsUpdate",
14044
14095
  build: async (client, data) => {
14045
14096
  const sounds = client.guilds.soundboardSounds(data.guild_id);
14046
- return [await Promise.all(data.soundboard_sounds.map((sound) => sounds.hydrate(sound))), data.guild_id];
14097
+ return [await Promise.all(data.soundboard_sounds.map((sound) => sounds._build(sound))), data.guild_id];
14047
14098
  }
14048
14099
  },
14049
14100
  [GatewayDispatchEvents.SoundboardSounds]: {
14050
14101
  event: "soundboardSounds",
14051
14102
  build: async (client, data) => {
14052
14103
  const sounds = client.guilds.soundboardSounds(data.guild_id);
14053
- return [await Promise.all(data.soundboard_sounds.map((sound) => sounds.hydrate(sound))), data.guild_id];
14104
+ return [await Promise.all(data.soundboard_sounds.map((sound) => sounds._build(sound))), data.guild_id];
14054
14105
  }
14055
14106
  },
14056
14107
  [GatewayDispatchEvents.GuildBanAdd]: {
14057
14108
  event: "guildBanAdd",
14058
- build: async (client, data) => [await client.guilds.bans(data.guild_id).hydrate(data)]
14109
+ build: async (client, data) => [await client.guilds.bans(data.guild_id)._build(data)]
14059
14110
  },
14060
14111
  [GatewayDispatchEvents.GuildBanRemove]: {
14061
14112
  event: "guildBanRemove",
14062
- before: (client, data) => client.guilds.bans(data.guild_id).get(data.user.id),
14063
- build: async (client, data, previous) => [previous ?? await client.guilds.bans(data.guild_id).hydrate(data)]
14113
+ before: (client, data) => previousOf(client.guilds.bans(data.guild_id), data.user.id),
14114
+ build: async (client, data, previous) => [previous ?? await client.guilds.bans(data.guild_id)._build(data)]
14064
14115
  },
14065
14116
  [GatewayDispatchEvents.GuildAuditLogEntryCreate]: {
14066
14117
  event: "guildAuditLogEntryCreate",
14067
14118
  build: async (client, data) => {
14119
+ const { user_id: userId } = data;
14068
14120
  const [executor, guild, target] = await Promise.all([
14069
- data.user_id ? cachedOrUndefined(client.users.get(data.user_id)) : void 0,
14070
- cachedOrUndefined(client.guilds.get(data.guild_id)),
14121
+ userId ? cachedOrUndefined(() => client.users.cache.get(userId)) : void 0,
14122
+ cachedOrUndefined(() => client.guilds.cache.get(data.guild_id)),
14071
14123
  resolveAuditLogTarget(client, data)
14072
14124
  ]);
14073
14125
  return [bindClient(new GuildAuditLogsEntry(data, {
@@ -14079,44 +14131,45 @@ const DispatchHandlers = {
14079
14131
  },
14080
14132
  [GatewayDispatchEvents.AutoModerationRuleCreate]: {
14081
14133
  event: "autoModerationRuleCreate",
14082
- build: async (client, data) => [await client.guilds.autoModerationRules(data.guild_id).hydrate(data)]
14134
+ build: async (client, data) => [await client.guilds.autoModerationRules(data.guild_id)._build(data)]
14083
14135
  },
14084
14136
  [GatewayDispatchEvents.AutoModerationRuleUpdate]: {
14085
14137
  event: "autoModerationRuleUpdate",
14086
- before: (client, data) => client.guilds.autoModerationRules(data.guild_id).get(data.id),
14087
- build: async (client, data, previous) => [previous ?? null, await client.guilds.autoModerationRules(data.guild_id).hydrate(data)]
14138
+ before: (client, data) => previousOf(client.guilds.autoModerationRules(data.guild_id), data.id),
14139
+ build: async (client, data, previous) => [previous ?? null, await client.guilds.autoModerationRules(data.guild_id)._build(data)]
14088
14140
  },
14089
14141
  [GatewayDispatchEvents.AutoModerationRuleDelete]: {
14090
14142
  event: "autoModerationRuleDelete",
14091
- build: async (client, data) => [await client.guilds.autoModerationRules(data.guild_id).hydrate(data)]
14143
+ build: async (client, data) => [await client.guilds.autoModerationRules(data.guild_id)._build(data)]
14092
14144
  },
14093
14145
  [GatewayDispatchEvents.GuildIntegrationsUpdate]: {
14094
14146
  event: "guildIntegrationsUpdate",
14095
- build: async (client, data) => [await cachedOrUndefined(client.guilds.get(data.guild_id)) ?? null, data]
14147
+ build: async (client, data) => [await cachedOrUndefined(() => client.guilds.cache.get(data.guild_id)) ?? null, data]
14096
14148
  },
14097
14149
  [GatewayDispatchEvents.IntegrationCreate]: {
14098
14150
  event: "integrationCreate",
14099
- build: async (client, data) => [await client.guilds.integrations(data.guild_id).hydrate(data)]
14151
+ build: async (client, data) => [await client.guilds.integrations(data.guild_id)._build(data)]
14100
14152
  },
14101
14153
  [GatewayDispatchEvents.IntegrationUpdate]: {
14102
14154
  event: "integrationUpdate",
14103
- before: (client, data) => client.guilds.integrations(data.guild_id).get(data.id),
14104
- build: async (client, data, previous) => [previous ?? null, await client.guilds.integrations(data.guild_id).hydrate(data)]
14155
+ before: (client, data) => previousOf(client.guilds.integrations(data.guild_id), data.id),
14156
+ build: async (client, data, previous) => [previous ?? null, await client.guilds.integrations(data.guild_id)._build(data)]
14105
14157
  },
14106
14158
  [GatewayDispatchEvents.IntegrationDelete]: {
14107
14159
  event: "integrationDelete",
14108
- before: (client, data) => client.guilds.integrations(data.guild_id).get(data.id),
14160
+ before: (client, data) => previousOf(client.guilds.integrations(data.guild_id), data.id),
14109
14161
  build: (_client, data, previous) => [previous ?? null, data]
14110
14162
  },
14111
14163
  [GatewayDispatchEvents.AutoModerationActionExecution]: {
14112
14164
  event: "autoModerationActionExecution",
14113
14165
  build: async (client, data) => {
14166
+ const { channel_id: channelId } = data;
14114
14167
  const [user, guild, member, channel, autoModerationRule] = await Promise.all([
14115
- cachedOrUndefined(client.users.get(data.user_id)),
14116
- cachedOrUndefined(client.guilds.get(data.guild_id)),
14117
- cachedOrUndefined(client.members.get(data.guild_id, data.user_id)),
14118
- data.channel_id ? cachedOrUndefined(client.channels.get(data.channel_id)) : void 0,
14119
- cachedOrUndefined(client.guilds.autoModerationRules(data.guild_id).get(data.rule_id))
14168
+ cachedOrUndefined(() => client.users.cache.get(data.user_id)),
14169
+ cachedOrUndefined(() => client.guilds.cache.get(data.guild_id)),
14170
+ cachedOrUndefined(() => cachedOf(client.members, data.guild_id, data.user_id)),
14171
+ channelId ? cachedOrUndefined(() => client.channels.cache.get(channelId)) : void 0,
14172
+ cachedOrUndefined(() => cachedOf(client.guilds.autoModerationRules(data.guild_id), data.rule_id))
14120
14173
  ]);
14121
14174
  return [bindClient(new AutoModerationActionExecution(data, {
14122
14175
  user: user ?? null,
@@ -14133,13 +14186,13 @@ const DispatchHandlers = {
14133
14186
  },
14134
14187
  [GatewayDispatchEvents.UserUpdate]: {
14135
14188
  event: "userUpdate",
14136
- before: (client, data) => client.users.get(data.id),
14189
+ before: (client, data) => previousOf(client.users, data.id),
14137
14190
  build: async (client, data, previous) => {
14138
14191
  if (client.user?.id === data.id) {
14139
14192
  client.user[kPatch](data);
14140
14193
  return [previous ?? partialUser(client, data.id), client.user];
14141
14194
  }
14142
- return [previous ?? partialUser(client, data.id), await client.users.hydrate(data)];
14195
+ return [previous ?? partialUser(client, data.id), await client.users._build(data)];
14143
14196
  }
14144
14197
  }
14145
14198
  };
@@ -14155,7 +14208,7 @@ const MultiDispatchHandlers = {
14155
14208
  before: (client, data) => cachedList(client.cache?.emojis, () => client.guilds.emojis(data.guild_id).listCached()),
14156
14209
  emit: async (client, data, previous) => {
14157
14210
  const emojis = client.guilds.emojis(data.guild_id);
14158
- const current = await Promise.all(data.emojis.map((emoji) => emojis.hydrate({
14211
+ const current = await Promise.all(data.emojis.map((emoji) => emojis._build({
14159
14212
  ...emoji,
14160
14213
  guild_id: data.guild_id
14161
14214
  })));
@@ -14172,7 +14225,7 @@ const MultiDispatchHandlers = {
14172
14225
  before: (client, data) => cachedList(client.cache?.stickers, () => client.guilds.stickers(data.guild_id).listCached()),
14173
14226
  emit: async (client, data, previous) => {
14174
14227
  const stickers = client.guilds.stickers(data.guild_id);
14175
- const current = await Promise.all(data.stickers.map((sticker) => stickers.hydrate({
14228
+ const current = await Promise.all(data.stickers.map((sticker) => stickers._build({
14176
14229
  ...sticker,
14177
14230
  guild_id: data.guild_id
14178
14231
  })));
@@ -14187,7 +14240,8 @@ const MultiDispatchHandlers = {
14187
14240
  }
14188
14241
  };
14189
14242
  async function cachedList(store, list) {
14190
- return store && isIterableCache(store) ? list() : void 0;
14243
+ if (!store || !isIterableCache(store)) return void 0;
14244
+ return (await list()).map((value) => value[kClone]());
14191
14245
  }
14192
14246
  function diff(previous, current, create, update, remove) {
14193
14247
  const before = new Map(previous.map((value) => [value.id, value]));
@@ -14206,13 +14260,14 @@ function diff(previous, current, create, update, remove) {
14206
14260
  return events;
14207
14261
  }
14208
14262
  async function reactionOf(client, data) {
14209
- const message = await cachedOrUndefined(client.messages.get(data.channel_id, data.message_id));
14263
+ const message = await cachedOrUndefined(() => cachedOf(client.messages, data.channel_id, data.message_id));
14210
14264
  if (!message) return partialReaction(client, data);
14211
14265
  return message.reactions.resolve(data.emoji) ?? partialReaction(client, data, true, message);
14212
14266
  }
14213
14267
  async function partialReaction(client, data, emptied = false, message = partialMessage(client, data.channel_id, data.message_id, data.guild_id)) {
14214
14268
  const { guild_id: guildId, emoji } = data;
14215
- const cachedEmoji = guildId && emoji.id ? await cachedOrUndefined(client.guilds.emojis(guildId).get(emoji.id)) : void 0;
14269
+ const emojiId = emoji.id;
14270
+ const cachedEmoji = guildId && emojiId ? await cachedOrUndefined(() => cachedOf(client.guilds.emojis(guildId), emojiId)) : void 0;
14216
14271
  return bindClient(new MessageReaction({
14217
14272
  channel_id: data.channel_id,
14218
14273
  message_id: data.message_id,
@@ -14233,7 +14288,7 @@ async function partialReaction(client, data, emptied = false, message = partialM
14233
14288
  }), client);
14234
14289
  }
14235
14290
  async function pollAnswerOf(client, data) {
14236
- const message = await cachedOrUndefined(client.messages.get(data.channel_id, data.message_id));
14291
+ const message = await cachedOrUndefined(() => cachedOf(client.messages, data.channel_id, data.message_id));
14237
14292
  return message?.poll?.answers.find((answer) => answer.id === data.answer_id) ?? bindClient(new PollAnswer({
14238
14293
  answer_id: data.answer_id,
14239
14294
  poll_media: {},
@@ -14244,13 +14299,13 @@ async function pollAnswerOf(client, data) {
14244
14299
  async function typingOf(client, data) {
14245
14300
  const { guild_id: guildId, member, user_id: userId } = data;
14246
14301
  const [channel, user, guild, resolvedMember] = await Promise.all([
14247
- cachedOrUndefined(client.channels.get(data.channel_id)),
14248
- cachedOrUndefined(member?.user ? client.users.resolveData(member.user) : client.users.get(userId)),
14249
- guildId ? cachedOrUndefined(Promise.resolve(client.guilds._getShallow(guildId))) : void 0,
14250
- guildId ? cachedOrUndefined(member?.user ? client.members.resolveData({
14302
+ cachedOrUndefined(() => client.channels.cache.get(data.channel_id)),
14303
+ cachedOrUndefined(() => member?.user ? client.users._resolveData(member.user) : client.users.cache.get(userId)),
14304
+ guildId ? cachedOrUndefined(() => client.guilds._getShallow(guildId)) : void 0,
14305
+ guildId ? cachedOrUndefined(() => member?.user ? client.members._resolveData({
14251
14306
  ...member,
14252
14307
  guild_id: guildId
14253
- }) : client.members.get(guildId, userId)) : void 0
14308
+ }) : cachedOf(client.members, guildId, userId)) : void 0
14254
14309
  ]);
14255
14310
  return bindClient(new Typing(data, {
14256
14311
  channel: channel ?? (guildId ? null : partialDMChannel(client, data.channel_id, userId)),
@@ -14326,8 +14381,16 @@ function partialDMChannel(client, channelId, recipientId) {
14326
14381
  recipients: [{ id: recipientId }]
14327
14382
  }), client);
14328
14383
  }
14329
- function cachedOrUndefined(read) {
14330
- return read.catch(() => void 0);
14384
+ /**
14385
+ * Resolves a cache read, or `undefined` when the cache cannot be read, so an event can still be built from its
14386
+ * payload. The failure itself was already reported while applying the dispatch.
14387
+ */
14388
+ async function cachedOrUndefined(read) {
14389
+ try {
14390
+ return await read();
14391
+ } catch {
14392
+ return;
14393
+ }
14331
14394
  }
14332
14395
 
14333
14396
  //#endregion
@@ -14562,11 +14625,338 @@ var GatewaySessionMirror = class {
14562
14625
  }
14563
14626
  };
14564
14627
 
14628
+ //#endregion
14629
+ //#region src/util/CollectionCache.ts
14630
+ /**
14631
+ * The default {@link Cache}: an in-memory `Collection` of structure instances, like discord.js's caches.
14632
+ *
14633
+ * @remarks
14634
+ * `add` patches the cached instance in place, so a reference kept by the application sees later updates.
14635
+ *
14636
+ * Every method is synchronous. Being a `Collection`, it also has `find`, `filter`, `map`, ... which return plain
14637
+ * collections. Instances reached by iterating carry the relations of their last `get`.
14638
+ */
14639
+ var CollectionCache = class extends Collection {
14640
+ static get [Symbol.species]() {
14641
+ return Collection;
14642
+ }
14643
+ synchronous = true;
14644
+ construct;
14645
+ /**
14646
+ * The name of the entity this cache holds.
14647
+ */
14648
+ name;
14649
+ #keyOf;
14650
+ #refresh;
14651
+ #maxSize;
14652
+ constructor(creator, name, options = {}) {
14653
+ super();
14654
+ this.construct = creator;
14655
+ this.name = name;
14656
+ this.#keyOf = options.keyOf ?? ((data) => data.id);
14657
+ this.#refresh = options.refresh;
14658
+ this.#maxSize = options.maxSize ?? Infinity;
14659
+ }
14660
+ add(data, overwrite = false) {
14661
+ const key = this.#keyOf(data);
14662
+ const existing = overwrite ? void 0 : super.get(key);
14663
+ if (existing !== void 0) {
14664
+ existing[kPatch](data);
14665
+ return this.#refresh ? this.#refresh(existing) : existing;
14666
+ }
14667
+ const value = this.construct(data);
14668
+ this.set(key, value);
14669
+ return this.#refresh ? this.#refresh(value) : value;
14670
+ }
14671
+ get(key) {
14672
+ const value = super.get(key);
14673
+ return value !== void 0 && this.#refresh ? this.#refresh(value) : value;
14674
+ }
14675
+ set(key, value) {
14676
+ if (this.#maxSize <= 0) return this;
14677
+ if (this.size >= this.#maxSize && !super.has(key)) {
14678
+ const oldest = this.keys().next();
14679
+ if (!oldest.done) super.delete(oldest.value);
14680
+ }
14681
+ return super.set(key, value);
14682
+ }
14683
+ getSize() {
14684
+ return this.size;
14685
+ }
14686
+ };
14687
+
14688
+ //#endregion
14689
+ //#region src/util/EntityStoreCache.ts
14690
+ const unguarded = (_operation, _key, run) => run();
14691
+ /**
14692
+ * A {@link Cache} over a raw `@wolfstar/plugin-cache` store (in memory or Redis): the store holds raw API data, and a
14693
+ * structure is built on every read.
14694
+ *
14695
+ * @remarks
14696
+ * Unlike `CollectionCache`, two reads of the same key return two structures: do not rely on object identity.
14697
+ */
14698
+ var EntityStoreCache = class {
14699
+ construct;
14700
+ /**
14701
+ * The name of the entity this cache holds.
14702
+ */
14703
+ name;
14704
+ /**
14705
+ * The raw store.
14706
+ */
14707
+ store;
14708
+ #keyOf;
14709
+ #hydrate;
14710
+ #guard;
14711
+ #synchronous;
14712
+ constructor(creator, name, options) {
14713
+ this.construct = creator;
14714
+ this.name = name;
14715
+ this.store = options.store;
14716
+ this.#keyOf = options.keyOf;
14717
+ this.#hydrate = options.hydrate ?? creator;
14718
+ this.#guard = options.guard ?? unguarded;
14719
+ this.#synchronous = options.synchronous;
14720
+ }
14721
+ get synchronous() {
14722
+ return this.#synchronous ? this.#synchronous() : this.store.synchronous === true;
14723
+ }
14724
+ add(data, overwrite = false) {
14725
+ const raw = data;
14726
+ const key = this.#keyOf(data);
14727
+ return whenAll([this.#guard("upsert", key, () => this.store.upsert(key, data, { overwrite }), { added: raw })], ([{ added }]) => this.#hydrate(added));
14728
+ }
14729
+ clear() {
14730
+ return this.#guard("clear", null, () => this.store.clear(), void 0);
14731
+ }
14732
+ delete(key) {
14733
+ return this.#guard("delete", key, () => this.store.delete(key), false);
14734
+ }
14735
+ get(key) {
14736
+ return whenAll([this.#guard("get", key, () => this.store.get(key), void 0)], ([raw]) => raw === void 0 ? void 0 : this.#hydrate(raw));
14737
+ }
14738
+ getSize() {
14739
+ return this.#guard("getSize", null, () => this.store.getSize(), 0);
14740
+ }
14741
+ has(key) {
14742
+ return this.#guard("has", key, () => this.store.has(key), false);
14743
+ }
14744
+ set(key, value) {
14745
+ const raw = value.toJSON();
14746
+ return whenAll([this.#guard("set", key, () => this.store.set(key, raw), void 0)], () => this);
14747
+ }
14748
+ };
14749
+
14750
+ //#endregion
14751
+ //#region src/util/entityManagers.ts
14752
+ /**
14753
+ * The entities of `@wolfstar/plugin-cache` a manager builds structures for.
14754
+ *
14755
+ * @internal
14756
+ */
14757
+ const ManagedEntityNames = [
14758
+ "autoModerationRules",
14759
+ "bans",
14760
+ "channels",
14761
+ "emojis",
14762
+ "guilds",
14763
+ "integrations",
14764
+ "invites",
14765
+ "members",
14766
+ "messages",
14767
+ "presences",
14768
+ "roles",
14769
+ "scheduledEvents",
14770
+ "soundboardSounds",
14771
+ "stageInstances",
14772
+ "stickers",
14773
+ "threadMembers",
14774
+ "threads",
14775
+ "users",
14776
+ "voiceStates"
14777
+ ];
14778
+ function guildIdOf(data) {
14779
+ return data.guild_id ?? "";
14780
+ }
14781
+ const managers = {
14782
+ autoModerationRules: (client, data) => client.guilds.autoModerationRules(guildIdOf(data)),
14783
+ bans: (client, data) => client.guilds.bans(guildIdOf(data)),
14784
+ channels: (client) => client.channels,
14785
+ emojis: (client, data) => client.guilds.emojis(guildIdOf(data)),
14786
+ guilds: (client) => client.guilds,
14787
+ integrations: (client, data) => client.guilds.integrations(guildIdOf(data)),
14788
+ invites: (client, data) => client.guilds.invites(guildIdOf(data)),
14789
+ members: (client) => client.members,
14790
+ messages: (client) => client.messages,
14791
+ presences: (client) => client.presences,
14792
+ roles: (client) => client.roles,
14793
+ scheduledEvents: (client, data) => client.guilds.scheduledEvents(guildIdOf(data)),
14794
+ soundboardSounds: (client, data) => client.guilds.soundboardSounds(guildIdOf(data)),
14795
+ stageInstances: (client, data) => client.guilds.stageInstances(guildIdOf(data)),
14796
+ stickers: (client, data) => client.guilds.stickers(guildIdOf(data)),
14797
+ threadMembers: (client) => client.threadMembers,
14798
+ threads: (client) => client.threads,
14799
+ users: (client) => client.users,
14800
+ voiceStates: (client) => client.voiceStates
14801
+ };
14802
+ /**
14803
+ * Gets the manager building the structures of an entity. Guild-scoped managers are created for the guild the data
14804
+ * belongs to.
14805
+ *
14806
+ * @param client The client.
14807
+ * @param name The name of the entity.
14808
+ * @param data The raw data the manager is needed for.
14809
+ * @internal
14810
+ */
14811
+ function managerOf(client, name, data) {
14812
+ return managers[name](client, data);
14813
+ }
14814
+
14815
+ //#endregion
14816
+ //#region src/util/NullCache.ts
14817
+ /**
14818
+ * The {@link Cache} of an entity that is not cached: always empty, writes are dropped.
14819
+ */
14820
+ var NullCache = class {
14821
+ construct;
14822
+ /**
14823
+ * The name of the entity this cache stands for.
14824
+ */
14825
+ name;
14826
+ #hydrate;
14827
+ #synchronous;
14828
+ constructor(creator, name, options = {}) {
14829
+ this.construct = creator;
14830
+ this.name = name;
14831
+ this.#hydrate = options.hydrate ?? creator;
14832
+ this.#synchronous = options.synchronous;
14833
+ }
14834
+ get synchronous() {
14835
+ return this.#synchronous?.() ?? true;
14836
+ }
14837
+ add(data) {
14838
+ return this.#hydrate(data);
14839
+ }
14840
+ clear() {}
14841
+ delete() {
14842
+ return false;
14843
+ }
14844
+ get() {}
14845
+ getSize() {
14846
+ return 0;
14847
+ }
14848
+ has() {
14849
+ return false;
14850
+ }
14851
+ set() {
14852
+ return this;
14853
+ }
14854
+ };
14855
+
14856
+ //#endregion
14857
+ //#region src/util/StructureStoreAdapter.ts
14858
+ function toRaw(value) {
14859
+ return { ...value.toJSON() };
14860
+ }
14861
+ /**
14862
+ * A raw `@wolfstar/plugin-cache` store over a structure {@link Cache}, so that what writes raw data (the gateway
14863
+ * dispatches, their cascades, the policies) works against a cache of structures.
14864
+ *
14865
+ * @remarks
14866
+ * Writes patch the cached instance in place. Time-to-live options are ignored: structure caches have none.
14867
+ *
14868
+ * @internal
14869
+ */
14870
+ var StructureStoreAdapter = class {
14871
+ cache;
14872
+ constructor(cache) {
14873
+ this.cache = cache;
14874
+ }
14875
+ get synchronous() {
14876
+ return this.cache.synchronous;
14877
+ }
14878
+ read(key) {
14879
+ return peekCache(this.cache, key);
14880
+ }
14881
+ get(key) {
14882
+ return whenAll([this.read(key)], ([value]) => value === void 0 ? void 0 : toRaw(value));
14883
+ }
14884
+ /**
14885
+ * Writes an entry. Unlike a raw store's `set`, it merges the value into a cached instance rather than replacing it:
14886
+ * the keys absent from the new value persist.
14887
+ */
14888
+ set(key, value) {
14889
+ return whenAll([this.read(key)], ([existing]) => {
14890
+ const entry = existing === void 0 ? this.cache.construct(value) : existing;
14891
+ if (existing !== void 0) existing[kPatch](value);
14892
+ return whenAll([this.cache.set(key, entry)], () => void 0);
14893
+ });
14894
+ }
14895
+ /**
14896
+ * Merges data into an entry, creating it when it is missing.
14897
+ *
14898
+ * @remarks
14899
+ * A cached instance is always patched in place, with or without `overwrite`: replacing it would leave the
14900
+ * application holding a stale instance. Since a structure's patch merges, an `overwrite` on a structure cache
14901
+ * behaves like {@link StructureStoreAdapter.set}: the keys absent from the new data keep their cached value,
14902
+ * where a raw store would drop them.
14903
+ */
14904
+ upsert(key, data, _options) {
14905
+ return whenAll([this.read(key)], ([existing]) => {
14906
+ if (existing === void 0) {
14907
+ const value = this.cache.construct(data);
14908
+ return whenAll([this.cache.set(key, value)], () => ({
14909
+ existing: void 0,
14910
+ added: toRaw(value)
14911
+ }));
14912
+ }
14913
+ const before = toRaw(existing);
14914
+ existing[kPatch](data);
14915
+ return whenAll([this.cache.set(key, existing)], () => ({
14916
+ existing: before,
14917
+ added: toRaw(existing)
14918
+ }));
14919
+ });
14920
+ }
14921
+ has(key) {
14922
+ return this.cache.has(key);
14923
+ }
14924
+ delete(key) {
14925
+ return this.cache.delete(key);
14926
+ }
14927
+ clear() {
14928
+ return this.cache.clear();
14929
+ }
14930
+ getSize() {
14931
+ return this.cache.getSize();
14932
+ }
14933
+ };
14934
+ var IterableStructureStoreAdapter = class extends StructureStoreAdapter {
14935
+ keys() {
14936
+ return [...this.cache.keys()];
14937
+ }
14938
+ values() {
14939
+ return [...this.cache.values()].map((value) => toRaw(value));
14940
+ }
14941
+ entries() {
14942
+ return [...this.cache.entries()].map(([key, value]) => [key, toRaw(value)]);
14943
+ }
14944
+ };
14945
+ /**
14946
+ * Creates the raw store of a structure cache, enumerable when the cache is a `Map` (e.g. `CollectionCache`).
14947
+ *
14948
+ * @param cache The structure cache.
14949
+ * @internal
14950
+ */
14951
+ function createStructureStoreAdapter(cache) {
14952
+ return cache instanceof Map ? new IterableStructureStoreAdapter(cache) : new StructureStoreAdapter(cache);
14953
+ }
14954
+
14565
14955
  //#endregion
14566
14956
  //#region src/GatewayClient.ts
14567
14957
  /**
14568
14958
  * A {@link Client} that, on top of serving HTTP interactions, connects to the Discord gateway, writes every dispatch
14569
- * into an optional {@link Cache}, and emits {@link GatewayEventMap} events carrying structures.
14959
+ * into its cache, and emits {@link GatewayEventMap} events carrying structures.
14570
14960
  *
14571
14961
  * @example
14572
14962
  * ```typescript
@@ -14586,7 +14976,8 @@ var GatewaySessionMirror = class {
14586
14976
  */
14587
14977
  var GatewayClient = class extends Client {
14588
14978
  /**
14589
- * The cache every dispatch is written into, if any.
14979
+ * The raw cache every dispatch is written into, if any: the `@wolfstar/plugin-cache` cache passed as
14980
+ * {@link GatewayClientOptions.cache}, or a raw view of the managers' caches of instances.
14590
14981
  */
14591
14982
  cache;
14592
14983
  /**
@@ -14656,6 +15047,12 @@ var GatewayClient = class extends Client {
14656
15047
  */
14657
15048
  clientReadyTimestamp = null;
14658
15049
  #queue = new DispatchQueue();
15050
+ #caches = /* @__PURE__ */ new Map();
15051
+ #cacheConstructor;
15052
+ #cacheOptions;
15053
+ #stores;
15054
+ #storesSynchronous;
15055
+ #policies;
14659
15056
  #shardCount;
14660
15057
  #intents;
14661
15058
  #token;
@@ -14668,7 +15065,14 @@ var GatewayClient = class extends Client {
14668
15065
  super(options);
14669
15066
  container.gatewayClient = this;
14670
15067
  this.rest = container.rest;
14671
- this.cache = resolveCache(options);
15068
+ const storeBacked = Boolean(options.cache || options.makeCache);
15069
+ if ((options.cacheConstructor || options.cacheOptions) && storeBacked) throw new GatewayTypeError("ClientCacheConflict");
15070
+ this.#cacheOptions = options.cacheOptions ?? {};
15071
+ const stores = resolveCache(options);
15072
+ this.#stores = stores;
15073
+ this.#storesSynchronous = Object.values(stores ?? {}).every((store) => store.synchronous === true);
15074
+ this.#cacheConstructor = options.cache === null || storeBacked ? null : options.cacheConstructor ?? CollectionCache;
15075
+ this.#policies = this.#cacheConstructor ? options.policies : void 0;
14672
15076
  this.cacheErrors = options.cacheErrors ?? "miss";
14673
15077
  this.cacheFailure = options.cacheFailure ?? "skip";
14674
15078
  this.dispatchTimeout = options.dispatchTimeout === void 0 ? 3e4 : options.dispatchTimeout;
@@ -14689,6 +15093,7 @@ var GatewayClient = class extends Client {
14689
15093
  this.templates = new GuildTemplateManager(this);
14690
15094
  this.voiceStates = new VoiceStateManager(this);
14691
15095
  this.presences = new PresenceManager(this);
15096
+ this.cache = this.#cacheConstructor ? this.#createStructureStores(options.policies) : stores;
14692
15097
  this.gateway = new WebSocketManager({
14693
15098
  ...options.gateway,
14694
15099
  ...this.sessionCallbacks(options),
@@ -14776,6 +15181,96 @@ var GatewayClient = class extends Client {
14776
15181
  return outcome.value;
14777
15182
  }
14778
15183
  /**
15184
+ * Gets the cache of an entity, building it on first use: the `CacheConstructor` of the discord.js RFC #11426.
15185
+ * Every manager of the same entity shares the same cache.
15186
+ *
15187
+ * @param creator Builds a structure from raw data, for entities the client does not know how to build.
15188
+ * @param name The name of the entity.
15189
+ */
15190
+ CacheConstructor(creator, name) {
15191
+ let cache = this.#caches.get(name);
15192
+ if (cache === void 0) {
15193
+ cache = this.#createCache(creator, name);
15194
+ this.#caches.set(name, cache);
15195
+ }
15196
+ return cache;
15197
+ }
15198
+ /**
15199
+ * Runs a raw store operation, reporting its failure through `cacheError`, see
15200
+ * {@link GatewayClientOptions.cacheErrors}. Synchronous when the operation is.
15201
+ *
15202
+ * @internal
15203
+ */
15204
+ guardCache(entity, operation, key, run, fallback) {
15205
+ const fail = (error) => {
15206
+ this.emit("cacheError", error, {
15207
+ entity,
15208
+ key,
15209
+ operation
15210
+ });
15211
+ if (this.cacheErrors === "throw") throw error;
15212
+ return fallback;
15213
+ };
15214
+ try {
15215
+ const result = run();
15216
+ return isPromiseLike(result) ? result.catch(fail) : result;
15217
+ } catch (error) {
15218
+ return fail(error);
15219
+ }
15220
+ }
15221
+ /**
15222
+ * Gets the `filter` of an entity's policy the managers must apply themselves before writing to its cache, see
15223
+ * {@link GatewayClientOptions.policies}.
15224
+ *
15225
+ * @param name The name of the entity.
15226
+ * @returns The filter, or `undefined` when there is none or when the entity's store applies it already.
15227
+ * @internal
15228
+ */
15229
+ cacheFilter(name) {
15230
+ const policy = this.#policies?.[name];
15231
+ return policy?.filter ? (value, key) => policy.filter(value, key) : void 0;
15232
+ }
15233
+ #createCache(creator, name) {
15234
+ const managed = ManagedEntityNames.includes(name);
15235
+ const construct = managed ? (data) => managerOf(this, name, data)._construct(data) : creator;
15236
+ const hydrate = managed ? (data) => managerOf(this, name, data)._build(data) : creator;
15237
+ const keyOf = managed ? (data) => managerOf(this, name, data).keyOf(data) : (data) => data.id;
15238
+ if (this.#cacheConstructor) {
15239
+ const refresh = (value) => {
15240
+ const refreshed = refreshRelations(value, hydrate);
15241
+ if (isPromiseLike(refreshed)) {
15242
+ refreshed.catch(() => void 0);
15243
+ throw new GatewayTypeError("CacheConstructorAsynchronous", name);
15244
+ }
15245
+ return refreshed;
15246
+ };
15247
+ return new this.#cacheConstructor(construct, name, {
15248
+ keyOf,
15249
+ refresh,
15250
+ ...this.#cacheOptions[name]
15251
+ });
15252
+ }
15253
+ const store = this.#stores?.[name];
15254
+ const synchronous = () => this.#storesSynchronous;
15255
+ if (store === void 0) return new NullCache(construct, name, {
15256
+ hydrate,
15257
+ synchronous
15258
+ });
15259
+ return new EntityStoreCache(construct, name, {
15260
+ store,
15261
+ keyOf,
15262
+ hydrate,
15263
+ synchronous,
15264
+ guard: (operation, key, run, fallback) => this.guardCache(name, operation, key, run, fallback)
15265
+ });
15266
+ }
15267
+ #createStructureStores(policies) {
15268
+ return createCache({
15269
+ policies,
15270
+ makeCache: (name) => ManagedEntityNames.includes(name) ? createStructureStoreAdapter(this.CacheConstructor(() => void 0, name)) : new MemoryEntityCache(this.#cacheOptions[name]?.maxSize ?? Infinity)
15271
+ });
15272
+ }
15273
+ /**
14779
15274
  * Resolves once every dispatch received so far has been processed.
14780
15275
  */
14781
15276
  async idle() {
@@ -14824,10 +15319,10 @@ var GatewayClient = class extends Client {
14824
15319
  guild_scheduled_event_id: options.guildScheduledEventId
14825
15320
  });
14826
15321
  const [guild, channel, inviter, targetUser] = await Promise.all([
14827
- invite.guild ? this.guilds.get(invite.guild.id) : void 0,
14828
- invite.channel ? this.channels.get(invite.channel.id) : void 0,
14829
- invite.inviter ? this.users.resolveData(invite.inviter) : void 0,
14830
- invite.target_user ? this.users.resolveData(invite.target_user) : void 0
15322
+ invite.guild ? this.guilds.cache.get(invite.guild.id) : void 0,
15323
+ invite.channel ? this.channels.cache.get(invite.channel.id) : void 0,
15324
+ invite.inviter ? this.users._resolveData(invite.inviter) : void 0,
15325
+ invite.target_user ? this.users._resolveData(invite.target_user) : void 0
14831
15326
  ]);
14832
15327
  return bindClient(createInvite(invite, {
14833
15328
  guild: guild ?? null,
@@ -14971,8 +15466,9 @@ var GatewayClient = class extends Client {
14971
15466
  }
14972
15467
  }
14973
15468
  async dropUnlistedGuilds(data, shardId) {
14974
- const guilds = this.cache?.guilds;
14975
- if (!guilds || !isIterableCache(guilds)) return;
15469
+ const { cache } = this;
15470
+ const guilds = cache?.guilds;
15471
+ if (!cache || !guilds || !isIterableCache(guilds)) return;
14976
15472
  const cached = await guilds.keys();
14977
15473
  if (cached.length === 0) return;
14978
15474
  const count = data.shard?.[1] ?? this.#shardCount ?? await this.gateway.getShardCount();
@@ -14980,8 +15476,8 @@ var GatewayClient = class extends Client {
14980
15476
  const shardCount = BigInt(count);
14981
15477
  for (const id of cached) {
14982
15478
  if (listed.has(id) || Number((BigInt(id) >> 22n) % shardCount) !== shardId) continue;
14983
- const guild = await this.guilds.get(id) ?? null;
14984
- await applyGatewayDispatch(this.cache, {
15479
+ const guild = await this.guilds.cache.get(id) ?? null;
15480
+ await applyGatewayDispatch(cache, {
14985
15481
  op: GatewayOpcodes.Dispatch,
14986
15482
  s: 0,
14987
15483
  t: GatewayDispatchEvents.GuildDelete,
@@ -15027,8 +15523,8 @@ var GatewayClient = class extends Client {
15027
15523
  }
15028
15524
  };
15029
15525
  /**
15030
- * Builds the cache of a client out of its options: the stores of `makeCache` (or `cache`), wrapped by `policies`.
15031
- * `undefined` when no entity kind is cached.
15526
+ * Builds the raw stores of a client out of its options: the stores of `makeCache` (or `cache`), wrapped by
15527
+ * `policies`. `undefined` when no entity kind is stored, which `cache: null` asks for.
15032
15528
  */
15033
15529
  function resolveCache(options) {
15034
15530
  const { makeCache, cache, policies } = options;
@@ -15097,5 +15593,5 @@ function RegisterAsGatewayListener(event, options = {}) {
15097
15593
  }
15098
15594
 
15099
15595
  //#endregion
15100
- export { Action, ActionRow, ActionsManager, Activity, ActivityFlagsBitField, AnnouncementChannel, AnnouncementThreadChannel, AnonymousGuild, ApplicationFlagsBitField, AppliedTagsMixin, Attachment, AttachmentFlagsBitField, AutoModerationActionExecution, AutoModerationRule, AutoModerationRuleManager, BaseChannel, BaseChannelMixin, BaseInvite, CachedManager as BaseManager, CachedManager, BitField, CategoryChannel, Channel, ChannelFlagsBitField, ChannelManager, ChannelMessageManager, ChannelOwnerMixin, ChannelParentMixin, ChannelPermissionMixin, ChannelSelectMenuComponent, ChannelSlowmodeMixin, ChannelThreadManager, ChannelThreadsMixin, ChannelTopicMixin, ChannelWebhooksMixin, CheckboxComponent, CheckboxGroupComponent, ClientUser, Colors, Component, ComponentMixin, ContainerComponent, DMChannel, DMChannelMixin, DispatchAction, DispatchHandlers, DispatchQueue, DispatchTimeoutError, Embed, Emoji, EventGatewayListener, FileComponent, FileUploadComponent, 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, GuildTemplatesPattern, Integration, IntentsBitField, InteractiveButtonComponent, InviteGuild, InvitesPattern, LabelComponent, LinkButtonComponent, MediaChannel, MediaGalleryComponent, MediaGalleryItem, MentionableSelectMenuComponent, Message, MessageFlagsBitField, MessageManager, MessageMentions, MessagePayload, MessageReaction, Mixin, MultiDispatchAction, MultiDispatchHandlers, NumberFlagsBitField, Partials, PermissionOverwriteManager, PermissionOverwrites, PermissionsBitField, Poll, PollAnswer, PremiumButtonComponent, Presence, PresenceManager, PrivateThreadChannel, PublicThreadChannel, RadioGroupComponent, ReactionEmoji, ReactionManager, ReactionUserManager, RegisterAsGatewayListener, RichPresenceAssets, Role, RoleFlagsBitField, RoleManager, RoleSelectMenuComponent, SectionComponent, SeparatorComponent, SoundboardSound, StageChannel, StageInstance, StageInstanceManager, Sticker, StickerPack, StringSelectMenuComponent, Structure, StructureMixin, SystemChannelFlagsBitField, TextChannel, TextChannelMixin, TextDisplayComponent, TextInputComponent, ThreadChannelMemberManager, ThreadChannelMixin, ThreadManager, ThreadMember, ThreadMemberManager, ThreadOnlyChannelMixin, ThumbnailComponent, Typing, UnfurledMediaItem, User, UserFlagsBitField, UserManager, UserSelectMenuComponent, VoiceChannel, VoiceChannelMixin, VoiceState, VoiceStateManager, Webhook, WebhookManager, WebhookMixin, WelcomeScreen, Widget, WidgetMember, basename, bindClient, cleanCodeBlockContent, cleanContent, compareRolePositions, computeChannelPermissions, computeGuildPermissions, computePermissionsIn, computePositions, computeTargetPermissions, createChannel, createComponent, createInvite, discordSort, dispatchPartition, findComponentByCustomId, findName, flatten, getGatewayClient, getSortableGroupTypes, initStructure, isDeepEqual, isThreadChannelType, kClient, kClone, kData, kPatch, kPatchRelations, kRelations, makeError, makePlainError, moveElementInArray, parseEmoji, parseWebhookURL, resolveBase64, resolveCode, resolveColor, resolveFile, resolveGuildTemplateCode, resolveId, resolveImage, resolveImageOption, resolveInviteCode, resolveOverwriteOptions, resolvePartialEmoji, resolveSKUId, snakeCase, snowflakeTimestamp, toAPIOverwrite, toChannelBody, toSnakeCase, transformAPIAuditLogChange, transformAPIAutoModerationAction, transformAPIAutoModerationRuleTriggerMetadata, transformAPIAvatarDecorationData, transformAPIChannelMention, transformAPIEmbedAsset, transformAPIEmbedAuthor, transformAPIEmbedFooter, transformAPIGuildDefaultReaction, transformAPIGuildForumTag, transformAPIGuildScheduledEventEntityMetadata, transformAPIGuildScheduledEventRecurrenceRule, transformAPIIncidentsData, transformAPIMessageActivity, transformAPIMessageCall, transformAPIMessageInteractionMetadata, transformAPIMessageReference, transformAPIRoleSubscriptionData, transformAPIRoleTags, transformAPIUserPrimaryGuild, transformAutoModerationAction, transformAutoModerationRuleTriggerMetadata, transformCollectibles, transformGuildDefaultReaction, transformGuildForumTag, transformGuildScheduledEventRecurrenceRule, transformResolved, verifyString };
15596
+ export { Action, ActionRow, ActionsManager, Activity, ActivityFlagsBitField, AnnouncementChannel, AnnouncementThreadChannel, AnonymousGuild, ApplicationFlagsBitField, AppliedTagsMixin, Attachment, AttachmentFlagsBitField, AutoModerationActionExecution, AutoModerationRule, AutoModerationRuleManager, BaseChannel, BaseChannelMixin, BaseInvite, BaseManager, BitField, CachedManager, CategoryChannel, Channel, ChannelFlagsBitField, ChannelManager, ChannelMessageManager, ChannelOwnerMixin, ChannelParentMixin, ChannelPermissionMixin, ChannelSelectMenuComponent, ChannelSlowmodeMixin, ChannelThreadManager, ChannelThreadsMixin, ChannelTopicMixin, ChannelWebhooksMixin, CheckboxComponent, CheckboxGroupComponent, ClientUser, CollectionCache, Colors, Component, ComponentMixin, ContainerComponent, DMChannel, DMChannelMixin, DataManager, DispatchAction, DispatchHandlers, DispatchQueue, DispatchTimeoutError, Embed, Emoji, EntityStoreCache, EventGatewayListener, FileComponent, FileUploadComponent, 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, GuildTemplatesPattern, Integration, IntentsBitField, InteractiveButtonComponent, InviteGuild, InvitesPattern, LabelComponent, LinkButtonComponent, MediaChannel, MediaGalleryComponent, MediaGalleryItem, MentionableSelectMenuComponent, Message, MessageFlagsBitField, MessageManager, MessageMentions, MessagePayload, MessageReaction, Mixin, MultiDispatchAction, MultiDispatchHandlers, NullCache, NumberFlagsBitField, Partials, PermissionOverwriteManager, PermissionOverwrites, PermissionsBitField, Poll, PollAnswer, PremiumButtonComponent, Presence, PresenceManager, PrivateThreadChannel, PublicThreadChannel, RadioGroupComponent, ReactionEmoji, ReactionManager, ReactionUserManager, RegisterAsGatewayListener, RichPresenceAssets, Role, RoleFlagsBitField, RoleManager, RoleSelectMenuComponent, SectionComponent, SeparatorComponent, SoundboardSound, StageChannel, StageInstance, StageInstanceManager, Sticker, StickerPack, StringSelectMenuComponent, Structure, StructureMixin, SystemChannelFlagsBitField, TextChannel, TextChannelMixin, TextDisplayComponent, TextInputComponent, ThreadChannelMemberManager, ThreadChannelMixin, ThreadManager, ThreadMember, ThreadMemberManager, ThreadOnlyChannelMixin, ThumbnailComponent, Typing, UnfurledMediaItem, User, UserFlagsBitField, UserManager, UserSelectMenuComponent, VoiceChannel, VoiceChannelMixin, VoiceState, VoiceStateManager, Webhook, WebhookManager, WebhookMixin, WelcomeScreen, Widget, WidgetMember, basename, bindClient, cleanCodeBlockContent, cleanContent, compareRolePositions, computeChannelPermissions, computeGuildPermissions, computePermissionsIn, computePositions, computeTargetPermissions, createChannel, createComponent, createInvite, discordSort, dispatchPartition, findComponentByCustomId, findName, flatten, getGatewayClient, getSortableGroupTypes, initStructure, isDeepEqual, isThreadChannelType, kClient, kClone, kData, kPatch, kPatchRelations, kRelations, makeError, makePlainError, moveElementInArray, parseEmoji, parseWebhookURL, resolveBase64, resolveCode, resolveColor, resolveFile, resolveGuildTemplateCode, resolveId, resolveImage, resolveImageOption, resolveInviteCode, resolveOverwriteOptions, resolvePartialEmoji, resolveSKUId, snakeCase, snowflakeTimestamp, toAPIOverwrite, toChannelBody, toSnakeCase, transformAPIAuditLogChange, transformAPIAutoModerationAction, transformAPIAutoModerationRuleTriggerMetadata, transformAPIAvatarDecorationData, transformAPIChannelMention, transformAPIEmbedAsset, transformAPIEmbedAuthor, transformAPIEmbedFooter, transformAPIGuildDefaultReaction, transformAPIGuildForumTag, transformAPIGuildScheduledEventEntityMetadata, transformAPIGuildScheduledEventRecurrenceRule, transformAPIIncidentsData, transformAPIMessageActivity, transformAPIMessageCall, transformAPIMessageInteractionMetadata, transformAPIMessageReference, transformAPIRoleSubscriptionData, transformAPIRoleTags, transformAPIUserPrimaryGuild, transformAutoModerationAction, transformAutoModerationRuleTriggerMetadata, transformCollectibles, transformGuildDefaultReaction, transformGuildForumTag, transformGuildScheduledEventRecurrenceRule, transformResolved, verifyString };
15101
15597
  //# sourceMappingURL=index.js.map