@wolfstar/plugin-gateway 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -303,6 +303,44 @@ the copy embedded in its payload.
303
303
  Swapping `createInMemoryCache()` for `createRedisCache({ redis })` changes nothing else, see
304
304
  [`@wolfstar/plugin-cache`](../plugin-cache).
305
305
 
306
+ ### Zero caching and cache control
307
+
308
+ Every feature works without a cache, following the
309
+ [discord.js RFC #11426](https://github.com/discordjs/discord.js/issues/11426): leave `cache` out
310
+ and every `fetch` hits the API, every event is still emitted, and the previous state of update and
311
+ delete events is `null` (or a partial, see [Partials](#partials)). Each entity kind can be cached,
312
+ or not, on its own:
313
+
314
+ ```ts
315
+ import { MemoryEntityCache } from "@wolfstar/plugin-cache";
316
+
317
+ const client = new GatewayClient({
318
+ intents,
319
+ // Called once per entity kind: `null` not to cache it.
320
+ makeCache: (entity) =>
321
+ ["guilds", "channels", "roles"].includes(entity) ? new MemoryEntityCache() : null,
322
+ // Entry by entry, for dispatches and managers alike.
323
+ policies: { users: { filter: (user) => !user.bot }, messages: { ttl: () => 3_600_000 } },
324
+ // A failing store (e.g. Redis down) is a cache miss, reported through `cacheError`.
325
+ cacheErrors: "miss",
326
+ });
327
+ ```
328
+
329
+ `makeCache` takes precedence over `cache`, and `policies` apply to either. What needs a store:
330
+
331
+ | Without the store of… | What happens |
332
+ | ---------------------------- | -------------------------------------------------------------------------- |
333
+ | any entity | `get` resolves to `undefined`, `fetch` hits the API, previous state `null` |
334
+ | `emojis` / `stickers` | only `guildEmojisUpdate` / `guildStickersUpdate`, no granular diff events |
335
+ | `guilds` (able to enumerate) | guilds left while offline are not reconciled on `READY` |
336
+ | `presences` | `presences.fetch` rejects: presences only come from the gateway |
337
+ | `roles` | overwrite types and member roles are read from one `GET /guilds/:id/roles` |
338
+ | `threadMembers` | `thread.joined` is `null` unless the payload carries the bot's member |
339
+ | a relation's entity | the relation getter (`message.guild`, `member.voice`, ...) returns `null` |
340
+
341
+ `listCached` resolves to `[]` without a store, and throws a `TypeError` for a store that cannot
342
+ enumerate its entries. With `cacheErrors: "throw"`, a failing store rejects instead of missing.
343
+
306
344
  ### Synchronous reads
307
345
 
308
346
  Every manager method above is asynchronous since the cache can be Redis, which makes a hot path
@@ -318,7 +356,7 @@ client.on("messageCreate", (message) => {
318
356
  });
319
357
  ```
320
358
 
321
- It returns `undefined` on a miss, or when the client has no cache, like `get`. An asynchronous cache
359
+ It returns `undefined` on a miss, or when the entity is not cached, like `get`. An asynchronous cache
322
360
  cannot tell a miss apart without awaiting it, so there `cached` throws a `TypeError` rather than
323
361
  silently returning `undefined`; the same goes for a custom cache mixing both kinds, when a relation
324
362
  lives in an asynchronous entity cache. `manager.cache?.synchronous` tells which path to take:
@@ -1,7 +1,7 @@
1
1
  import { Client, ClientOptions, Listener } from "@wolfstar/http-framework";
2
2
  import { OptionalWebSocketManagerOptions, ShardRange, WebSocketManager } from "@discordjs/ws";
3
3
  import { API, Client as Client$1 } from "@discordjs/core";
4
- import { Awaitable, Cache, CacheEntityName, CacheEntityTypes, EntityCache, GatewaySessionStore } from "@wolfstar/plugin-cache";
4
+ import { Awaitable, Cache, CacheEntityName, CacheEntityTypes, CacheFactory, CachePolicies, EntityCache, GatewaySessionStore, IterableEntityCache } from "@wolfstar/plugin-cache";
5
5
  import { APIAllowedMentions, APIApplication, APIApplicationCommand, APIAttachment, APIAuditLogChange, APIAutoModerationAction, APIAutoModerationRule, APIAutoModerationRuleTriggerMetadata, APIAvatarDecorationData, APIChannel, APIChannelMention, APICollectibles, APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage, APIEmbedProvider, APIEmbedThumbnail, APIEmbedVideo, APIEmoji, APIExtendedInvite, APIGuild, APIGuildForumDefaultReactionEmoji, APIGuildForumTag, APIGuildMember, APIGuildOnboarding, APIGuildOnboardingPrompt, APIGuildOnboardingPromptOption, APIGuildPreview, APIGuildScheduledEvent, APIGuildScheduledEventEntityMetadata, APIGuildScheduledEventRecurrenceRule, APIGuildWelcomeScreen, APIGuildWidget, APIGuildWidgetMember, APIIncidentsData, APIInviteChannel, APIInviteGuild, APIMessage, APIMessageTopLevelComponent, APIOverwrite, APIPartialEmoji, APIPoll, APIPollAnswer, APIReaction, APIRole, APIRoleTags, APISoundboardSound, APIStageInstance, APISticker, APIStickerPack, APITemplate, APIThreadChannel, APIThreadMember, APIUser, APIUserPrimaryGuild, APIVoiceRegion, APIWebhook, ActivityFlags, ActivityType, AllowedMentionsTypes, AttachmentFlags, AuditLogEvent, AutoModerationRuleEventType, AutoModerationRuleKeywordPresetType, AutoModerationRuleTriggerType, ChannelFlags, ChannelType, ForumLayoutType, GatewayActivity, GatewayActivityAssets, GatewayActivityUpdateData, GatewayAutoModerationActionExecutionDispatchData, GatewayChannelPinsUpdateDispatchData, GatewayDispatchEvents, GatewayDispatchPayload, GatewayGuildDeleteDispatchData, GatewayGuildIntegrationsUpdateDispatchData, GatewayGuildMemberRemoveDispatchData, GatewayGuildMembersChunkDispatchData, GatewayGuildRoleDeleteDispatchData, GatewayGuildScheduledEventUserAddDispatchData, GatewayGuildScheduledEventUserRemoveDispatchData, GatewayGuildSoundboardSoundDeleteDispatchData, GatewayIntegrationDeleteDispatchData, GatewayIntentBits, GatewayInviteCreateDispatchData, GatewayInviteDeleteDispatchData, GatewayMessageDeleteBulkDispatchData, GatewayMessageDeleteDispatchData, GatewayMessageReactionRemoveAllDispatchData, GatewayPresenceClientStatus, GatewayPresenceUpdateData, GatewayRateLimitedDispatchData, GatewayReadyDispatchData, GatewayThreadDeleteDispatchData, GatewayThreadListSync, GatewayThreadMembersUpdateDispatchData, GatewayTypingStartDispatchData, GatewayVoiceServerUpdateDispatchData, GatewayWebhooksUpdateDispatchData, GuildDefaultMessageNotifications, GuildExplicitContentFilter, GuildMemberFlags, GuildOnboardingMode, GuildOnboardingPromptType, GuildPremiumTier, GuildScheduledEventEntityType, GuildScheduledEventPrivacyLevel, GuildScheduledEventStatus, GuildSystemChannelFlags, GuildVerificationLevel, GuildWidgetStyle, InviteTargetType, InviteType, Locale, MessageFlags, OverwriteType, PermissionFlagsBits, PollLayoutType, PresenceUpdateReceiveStatus, PresenceUpdateStatus, RESTAPIPoll, RESTGetAPIGuildVanityUrlResult, RESTPatchAPIChannelJSONBody, RESTPatchAPIGuildJSONBody, RESTPostAPIChannelMessageJSONBody, RESTPostAPIGuildsJSONBody, ReactionType, RoleFlags, Snowflake, SortOrderType, StageInstancePrivacyLevel, StickerFormatType, ThreadAutoArchiveDuration, ThreadChannelType, UserFlags, VideoQualityMode } from "discord-api-types/v10";
6
6
  import { APIActualInvite, 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, BitFieldResolvable, BitFieldResolvable as BitFieldResolvable$1, CategoryChannel as CategoryChannel$1, Channel as Channel$1, ChannelFlagsBitField as ChannelFlagsBitField$1, DMChannel as DMChannel$1, Embed as Embed$1, Emoji as Emoji$1, ForumChannel as ForumChannel$1, GroupDMChannel as GroupDMChannel$1, Invite, MediaChannel as MediaChannel$1, Message as Message$1, MessageFlagsBitField as MessageFlagsBitField$1, Partialize, PermissionsBitField, Poll as Poll$1, PollAnswer as PollAnswer$1, Presence as Presence$1, PrivateThreadChannel as PrivateThreadChannel$1, PublicThreadChannel as PublicThreadChannel$1, Reaction, SoundboardSound as SoundboardSound$1, StageChannel as StageChannel$1, StageInstance as StageInstance$1, Sticker as Sticker$1, StickerPack as StickerPack$1, Structure as Structure$1, TextChannel as TextChannel$1, User as User$1, VoiceChannel as VoiceChannel$1, VoiceState as VoiceState$1, Webhook as Webhook$1 } from "@discordjs/structures";
7
7
  import { BaseImageURLOptions, EmojiURLOptions, ImageURLOptions, REST, RawFile } from "@discordjs/rest";
@@ -390,12 +390,16 @@ export interface AddOptions {
390
390
  key?: string;
391
391
  }
392
392
  /**
393
- * The base class of every manager: it reads raw data from one of the client's entity caches, falls back to the REST
394
- * API when asked to, and wraps the result in a {@link StructureMixin | structure}.
393
+ * The base class of every manager, the `BaseManager` of the discord.js RFC #11426: it reads raw data from one of the
394
+ * client's entity caches, falls back to the REST API when asked to, and wraps the result in a
395
+ * {@link StructureMixin | structure} with {@link CachedManager.construct}.
395
396
  *
396
397
  * @remarks
397
- * The cache only ever holds raw API data, building structures is always the manager's job. Without a cache, `get`
398
- * always resolves to `undefined` and `fetch` always hits the API.
398
+ * The cache only ever holds raw API data, building structures is always the manager's job. Without a store for its
399
+ * entity (see the client's `makeCache`), `get` always resolves to `undefined` and `fetch` always hits the API.
400
+ *
401
+ * A failing store (e.g. Redis being unreachable) emits `cacheError` and, with the client's default
402
+ * `cacheErrors: "miss"`, is treated as a cache miss.
399
403
  *
400
404
  * Every method is asynchronous since the cache can be Redis, except {@link CachedManager.cached}, which reads a
401
405
  * synchronous cache (`createInMemoryCache`) without awaiting it.
@@ -511,7 +515,7 @@ export declare abstract class CachedManager<Name extends CacheEntityName, Value
511
515
  protected cachedGuild(guildId: string | null | undefined): Awaitable<Guild | null>;
512
516
  /**
513
517
  * Builds the structure of raw data, resolving its relations from the cache. Without relations to resolve, the same
514
- * as {@link CachedManager.createStructure}.
518
+ * as {@link CachedManager.construct}.
515
519
  *
516
520
  * @param data The raw data.
517
521
  */
@@ -544,12 +548,20 @@ export declare abstract class CachedManager<Name extends CacheEntityName, Value
544
548
  * @param args The arguments identifying the entity.
545
549
  */
546
550
  refresh(...args: Args): Promise<Value>;
551
+ /**
552
+ * Wraps raw data in this manager's structure: the RFC's `StructureCreator`, which lives on the manager since the
553
+ * cache only holds raw data.
554
+ *
555
+ * @param data The raw data.
556
+ */
557
+ abstract construct(data: CacheEntityTypes[Name]): Value;
547
558
  /**
548
559
  * Wraps raw data in this manager's structure.
549
560
  *
550
561
  * @param data The raw data.
562
+ * @deprecated Use {@link CachedManager.construct}.
551
563
  */
552
- abstract createStructure(data: CacheEntityTypes[Name]): Value;
564
+ createStructure(data: CacheEntityTypes[Name]): Value;
553
565
  /**
554
566
  * Gets the cache key of raw data.
555
567
  *
@@ -575,12 +587,29 @@ export declare abstract class CachedManager<Name extends CacheEntityName, Value
575
587
  * @param raw The raw data of the entity.
576
588
  */
577
589
  protected storeRaw(key: string, raw: CacheEntityTypes[Name]): Promise<void>;
590
+ /**
591
+ * Gets this manager's store when it can enumerate its entries, for the `listCached` methods.
592
+ *
593
+ * @returns The store, or `undefined` when this entity is not cached.
594
+ * @throws {TypeError} When the store cannot enumerate its entries.
595
+ */
596
+ protected iterableCache(): IterableEntityCache<CacheEntityTypes[Name]> | undefined;
578
597
  /**
579
598
  * Gets an entity from the cache by its key, synchronously when every cache it reads is.
580
599
  *
581
600
  * @param key The cache key of the entity.
582
601
  */
583
602
  protected getByKey(key: string): Awaitable<Value | undefined>;
603
+ /**
604
+ * Runs a cache operation, reporting its failure through `cacheError`. With the client's `cacheErrors: "miss"`, a
605
+ * failure resolves to `fallback`, otherwise it is rethrown. Synchronous when the operation is.
606
+ *
607
+ * @param operation The operation, for `cacheError`.
608
+ * @param key The key of the entry, for `cacheError`.
609
+ * @param run Runs the operation.
610
+ * @param fallback What a failure resolves to under `cacheErrors: "miss"`.
611
+ */
612
+ protected guard<T>(operation: CacheErrorContext["operation"], key: string | null, run: () => Awaitable<T>, fallback: T): Awaitable<T>;
584
613
  /**
585
614
  * Builds the structure of raw data with {@link CachedManager._hydrate}, bound to this manager's client.
586
615
  *
@@ -621,7 +650,7 @@ export interface AutoModerationRuleCreateOptions extends AutoModerationRuleEditO
621
650
  export declare class AutoModerationRuleManager extends CachedManager<"autoModerationRules", AutoModerationRule, [ruleId: string]> {
622
651
  readonly guildId: string;
623
652
  constructor(client: GatewayClient, guildId: string);
624
- createStructure(data: CacheEntityTypes["autoModerationRules"]): AutoModerationRule;
653
+ construct(data: CacheEntityTypes["autoModerationRules"]): AutoModerationRule;
625
654
  keyOf(data: CacheEntityTypes["autoModerationRules"]): string;
626
655
  resolveKey(ruleId: string): string;
627
656
  _hydrate(data: CacheEntityTypes["autoModerationRules"]): Awaitable<AutoModerationRule>;
@@ -1463,7 +1492,7 @@ export interface GuildMembersRequestOptions {
1463
1492
  export declare class GuildMemberManager extends CachedManager<"members", GuildMember, [guildId: string, userId: string]> {
1464
1493
  #private;
1465
1494
  constructor(client: GatewayClient);
1466
- createStructure(data: CacheEntityTypes["members"]): GuildMember;
1495
+ construct(data: CacheEntityTypes["members"]): GuildMember;
1467
1496
  keyOf(data: CacheEntityTypes["members"]): string;
1468
1497
  /**
1469
1498
  * Adds a member to the cache, and its user to `client.users`.
@@ -1679,7 +1708,7 @@ export interface RolePosition {
1679
1708
  */
1680
1709
  export declare class RoleManager extends CachedManager<"roles", Role, [guildId: string, roleId: string]> {
1681
1710
  constructor(client: GatewayClient);
1682
- createStructure(data: CacheEntityTypes["roles"]): Role;
1711
+ construct(data: CacheEntityTypes["roles"]): Role;
1683
1712
  keyOf(data: CacheEntityTypes["roles"]): string;
1684
1713
  _hydrate(data: CacheEntityTypes["roles"]): Awaitable<Role>;
1685
1714
  resolveKey(guildId: string, roleId: string): string;
@@ -2629,7 +2658,7 @@ export interface ThreadMemberListOptions {
2629
2658
  */
2630
2659
  export declare class ThreadMemberManager extends CachedManager<"threadMembers", ThreadMember, [threadId: string, userId: string]> {
2631
2660
  constructor(client: GatewayClient);
2632
- createStructure(data: CacheEntityTypes["threadMembers"]): ThreadMember;
2661
+ construct(data: CacheEntityTypes["threadMembers"]): ThreadMember;
2633
2662
  keyOf(data: CacheEntityTypes["threadMembers"]): string;
2634
2663
  resolveKey(threadId: string, userId: string): string;
2635
2664
  /**
@@ -2736,9 +2765,11 @@ export declare class ThreadChannelMixin<Type extends ChannelType = ChannelType>
2736
2765
  get invitable(): boolean | null;
2737
2766
  /**
2738
2767
  * Whether the bot is a member of the thread, like discord.js's `ThreadChannel#joined`: from the thread member cache
2739
- * when the thread was built by a manager, else whether the payload carries the bot's thread member.
2768
+ * when the thread was built by a manager, else `true` when the payload carries the bot's thread member.
2769
+ *
2770
+ * @returns `null` when it cannot be told: no thread member cache, and no thread member in the payload.
2740
2771
  */
2741
- get joined(): boolean;
2772
+ get joined(): boolean | null;
2742
2773
  /**
2743
2774
  * The members of the thread.
2744
2775
  */
@@ -2888,7 +2919,7 @@ export interface FetchedThreads {
2888
2919
  */
2889
2920
  export declare class ThreadManager extends CachedManager<"threads", AnyThreadChannel, [threadId: string]> {
2890
2921
  constructor(client: GatewayClient);
2891
- createStructure(data: CacheEntityTypes["threads"]): AnyThreadChannel;
2922
+ construct(data: CacheEntityTypes["threads"]): AnyThreadChannel;
2892
2923
  keyOf(data: CacheEntityTypes["threads"]): string;
2893
2924
  _hydrate(data: CacheEntityTypes["threads"]): Awaitable<AnyThreadChannel>;
2894
2925
  resolveKey(threadId: string): string;
@@ -2953,7 +2984,7 @@ export interface PinnedMessage {
2953
2984
  */
2954
2985
  export declare class MessageManager extends CachedManager<"messages", Message, [channelId: string, messageId: string]> {
2955
2986
  constructor(client: GatewayClient);
2956
- createStructure(data: CacheEntityTypes["messages"]): Message;
2987
+ construct(data: CacheEntityTypes["messages"]): Message;
2957
2988
  keyOf(data: CacheEntityTypes["messages"]): string;
2958
2989
  /**
2959
2990
  * Adds a message to the cache, its author to `client.users` (unless a webhook sent it), and its member to
@@ -3140,7 +3171,7 @@ export declare class GuildEmojiManager extends CachedManager<"emojis", GuildEmoj
3140
3171
  */
3141
3172
  readonly guildId: string;
3142
3173
  constructor(client: GatewayClient, guildId: string);
3143
- createStructure(data: CacheEntityTypes["emojis"]): GuildEmoji;
3174
+ construct(data: CacheEntityTypes["emojis"]): GuildEmoji;
3144
3175
  keyOf(data: CacheEntityTypes["emojis"]): string;
3145
3176
  /**
3146
3177
  * Adds an emoji to the cache, and its uploader to `client.users`.
@@ -3186,6 +3217,9 @@ export declare class GuildEmojiManager extends CachedManager<"emojis", GuildEmoj
3186
3217
  * @remarks
3187
3218
  * It enumerates the whole entity cache, which a Redis store answers from its index: prefer `fetchAll` when the
3188
3219
  * cache may be incomplete.
3220
+ *
3221
+ * @returns The cached entries, `[]` when this entity is not cached.
3222
+ * @throws {TypeError} When the store cannot enumerate its entries.
3189
3223
  */
3190
3224
  listCached(): Promise<GuildEmoji[]>;
3191
3225
  protected fetchRaw(emojiId: string): Promise<{
@@ -4373,7 +4407,7 @@ export interface GuildBanListOptions {
4373
4407
  export declare class GuildBanManager extends CachedManager<"bans", GuildBan, [userId: string]> {
4374
4408
  readonly guildId: string;
4375
4409
  constructor(client: GatewayClient, guildId: string);
4376
- createStructure(data: CacheEntityTypes["bans"]): GuildBan;
4410
+ construct(data: CacheEntityTypes["bans"]): GuildBan;
4377
4411
  keyOf(data: CacheEntityTypes["bans"]): string;
4378
4412
  resolveKey(userId: string): string;
4379
4413
  /**
@@ -4580,7 +4614,7 @@ export interface GuildScheduledEventSubscriber {
4580
4614
  export declare class GuildScheduledEventManager extends CachedManager<"scheduledEvents", GuildScheduledEvent, [eventId: string]> {
4581
4615
  readonly guildId: string;
4582
4616
  constructor(client: GatewayClient, guildId: string);
4583
- createStructure(data: CacheEntityTypes["scheduledEvents"]): GuildScheduledEvent;
4617
+ construct(data: CacheEntityTypes["scheduledEvents"]): GuildScheduledEvent;
4584
4618
  keyOf(data: CacheEntityTypes["scheduledEvents"]): string;
4585
4619
  resolveKey(eventId: string): string;
4586
4620
  /**
@@ -4711,7 +4745,7 @@ export interface SoundboardSoundCreateOptions extends SoundboardSoundEditOptions
4711
4745
  export declare class GuildSoundboardSoundManager extends CachedManager<"soundboardSounds", SoundboardSound, [soundId: string]> {
4712
4746
  readonly guildId: string;
4713
4747
  constructor(client: GatewayClient, guildId: string);
4714
- createStructure(data: CacheEntityTypes["soundboardSounds"]): SoundboardSound;
4748
+ construct(data: CacheEntityTypes["soundboardSounds"]): SoundboardSound;
4715
4749
  keyOf(data: CacheEntityTypes["soundboardSounds"]): string;
4716
4750
  resolveKey(soundId: string): string;
4717
4751
  /**
@@ -4829,7 +4863,7 @@ export interface StageInstanceCreateOptions extends StageInstanceEditOptions {
4829
4863
  export declare class StageInstanceManager extends CachedManager<"stageInstances", StageInstance, [channelId: string]> {
4830
4864
  readonly guildId: string;
4831
4865
  constructor(client: GatewayClient, guildId: string);
4832
- createStructure(data: CacheEntityTypes["stageInstances"]): StageInstance;
4866
+ construct(data: CacheEntityTypes["stageInstances"]): StageInstance;
4833
4867
  keyOf(data: CacheEntityTypes["stageInstances"]): string;
4834
4868
  resolveKey(channelId: string): string;
4835
4869
  _hydrate(data: CacheEntityTypes["stageInstances"]): Awaitable<StageInstance>;
@@ -4998,7 +5032,7 @@ export declare class Integration extends Structure<CacheEntityTypes["integration
4998
5032
  export declare class GuildIntegrationManager extends CachedManager<"integrations", Integration, [integrationId: string]> {
4999
5033
  readonly guildId: string;
5000
5034
  constructor(client: GatewayClient, guildId: string);
5001
- createStructure(data: CacheEntityTypes["integrations"]): Integration;
5035
+ construct(data: CacheEntityTypes["integrations"]): Integration;
5002
5036
  keyOf(data: CacheEntityTypes["integrations"]): string;
5003
5037
  resolveKey(integrationId: string): string;
5004
5038
  /**
@@ -5122,7 +5156,7 @@ export declare class GuildStickerManager extends CachedManager<"stickers", Stick
5122
5156
  */
5123
5157
  readonly guildId: string;
5124
5158
  constructor(client: GatewayClient, guildId: string);
5125
- createStructure(data: CacheEntityTypes["stickers"]): Sticker;
5159
+ construct(data: CacheEntityTypes["stickers"]): Sticker;
5126
5160
  keyOf(data: CacheEntityTypes["stickers"]): string;
5127
5161
  /**
5128
5162
  * Adds a sticker to the cache, and its uploader to `client.users`.
@@ -5168,6 +5202,9 @@ export declare class GuildStickerManager extends CachedManager<"stickers", Stick
5168
5202
  * @remarks
5169
5203
  * It enumerates the whole entity cache, which a Redis store answers from its index: prefer `fetchAll` when the
5170
5204
  * cache may be incomplete.
5205
+ *
5206
+ * @returns The cached entries, `[]` when this entity is not cached.
5207
+ * @throws {TypeError} When the store cannot enumerate its entries.
5171
5208
  */
5172
5209
  listCached(): Promise<Sticker[]>;
5173
5210
  protected fetchRaw(stickerId: string): Promise<{
@@ -5968,7 +6005,7 @@ export declare class GuildInviteManager extends CachedManager<"invites", GuildIn
5968
6005
  */
5969
6006
  readonly guildId: string;
5970
6007
  constructor(client: GatewayClient, guildId: string);
5971
- createStructure(data: CacheEntityTypes["invites"]): GuildInvite;
6008
+ construct(data: CacheEntityTypes["invites"]): GuildInvite;
5972
6009
  keyOf(data: CacheEntityTypes["invites"]): string;
5973
6010
  /**
5974
6011
  * Adds an invite to the cache, and its inviter and target user to `client.users`.
@@ -6126,7 +6163,7 @@ export interface GuildOnboardingEditOptions {
6126
6163
  */
6127
6164
  export declare class GuildManager extends CachedManager<"guilds", Guild, [guildId: string]> {
6128
6165
  constructor(client: GatewayClient);
6129
- createStructure(data: CacheEntityTypes["guilds"]): Guild;
6166
+ construct(data: CacheEntityTypes["guilds"]): Guild;
6130
6167
  keyOf(data: CacheEntityTypes["guilds"]): string;
6131
6168
  /**
6132
6169
  * Builds a guild, resolving its AFK, system, widget, rules, public updates, and safety alerts channels from the
@@ -7461,7 +7498,7 @@ export declare function createChannel(data: CacheEntityTypes["channels"], relati
7461
7498
  */
7462
7499
  export declare class ChannelManager extends CachedManager<"channels", AnyChannel, [channelId: string]> {
7463
7500
  constructor(client: GatewayClient);
7464
- createStructure(data: CacheEntityTypes["channels"]): AnyChannel;
7501
+ construct(data: CacheEntityTypes["channels"]): AnyChannel;
7465
7502
  keyOf(data: CacheEntityTypes["channels"]): string;
7466
7503
  _hydrate(data: CacheEntityTypes["channels"]): Awaitable<AnyChannel>;
7467
7504
  /**
@@ -7539,7 +7576,7 @@ export declare class ChannelManager extends CachedManager<"channels", AnyChannel
7539
7576
  */
7540
7577
  export declare class VoiceStateManager extends CachedManager<"voiceStates", VoiceState, [guildId: string, userId: string]> {
7541
7578
  constructor(client: GatewayClient);
7542
- createStructure(data: CacheEntityTypes["voiceStates"]): VoiceState;
7579
+ construct(data: CacheEntityTypes["voiceStates"]): VoiceState;
7543
7580
  keyOf(data: CacheEntityTypes["voiceStates"]): string;
7544
7581
  resolveKey(guildId: string, userId: string): string;
7545
7582
  _hydrate(data: CacheEntityTypes["voiceStates"]): Awaitable<VoiceState>;
@@ -7547,6 +7584,8 @@ export declare class VoiceStateManager extends CachedManager<"voiceStates", Voic
7547
7584
  * Lists the cached voice states of a guild.
7548
7585
  *
7549
7586
  * @param guildId The ID of the guild.
7587
+ * @returns The cached entries, `[]` when this entity is not cached.
7588
+ * @throws {TypeError} When the store cannot enumerate its entries.
7550
7589
  */
7551
7590
  listCached(guildId: string): Promise<VoiceState[]>;
7552
7591
  protected fetchRaw(guildId: string, userId: string): Promise<{
@@ -7576,7 +7615,7 @@ export declare class VoiceStateManager extends CachedManager<"voiceStates", Voic
7576
7615
  */
7577
7616
  export declare class PresenceManager extends CachedManager<"presences", Presence, [guildId: string, userId: string]> {
7578
7617
  constructor(client: GatewayClient);
7579
- createStructure(data: CacheEntityTypes["presences"]): Presence;
7618
+ construct(data: CacheEntityTypes["presences"]): Presence;
7580
7619
  keyOf(data: CacheEntityTypes["presences"]): string;
7581
7620
  resolveKey(guildId: string, userId: string): string;
7582
7621
  _hydrate(data: CacheEntityTypes["presences"]): Awaitable<Presence>;
@@ -7584,6 +7623,8 @@ export declare class PresenceManager extends CachedManager<"presences", Presence
7584
7623
  * Lists the cached presences of a guild.
7585
7624
  *
7586
7625
  * @param guildId The ID of the guild.
7626
+ * @returns The cached entries, `[]` when this entity is not cached.
7627
+ * @throws {TypeError} When the store cannot enumerate its entries.
7587
7628
  */
7588
7629
  listCached(guildId: string): Promise<Presence[]>;
7589
7630
  protected fetchRaw(guildId: string, userId: string): Promise<CacheEntityTypes["presences"]>;
@@ -7595,7 +7636,7 @@ export declare class PresenceManager extends CachedManager<"presences", Presence
7595
7636
  */
7596
7637
  export declare class UserManager extends CachedManager<"users", User, [userId: string]> {
7597
7638
  constructor(client: GatewayClient);
7598
- createStructure(data: CacheEntityTypes["users"]): User;
7639
+ construct(data: CacheEntityTypes["users"]): User;
7599
7640
  keyOf(data: CacheEntityTypes["users"]): string;
7600
7641
  resolveKey(userId: string): string;
7601
7642
  /**
@@ -7762,7 +7803,8 @@ export declare const DispatchHandlers: { [Type in GatewayDispatchEvents]?: AnyDi
7762
7803
  * The table of the dispatches that turn into several events, diffed against the cache.
7763
7804
  *
7764
7805
  * @remarks
7765
- * Without a cache there is nothing to diff against, so these dispatches only reach `raw`.
7806
+ * Their aggregate event (e.g. `guildEmojisUpdate`) is always emitted. Without a cache there is nothing to diff
7807
+ * against, so the granular events are not.
7766
7808
  */
7767
7809
  export declare const MultiDispatchHandlers: { [Type in GatewayDispatchEvents]?: MultiDispatchHandler<Type>; };
7768
7810
  //#endregion
@@ -7914,9 +7956,58 @@ export interface GatewayClientOptions extends ClientOptions, GatewayClientMessag
7914
7956
  * The cache to write every dispatch into, see `@wolfstar/plugin-cache`. Without one, the managers' `get` always
7915
7957
  * resolve to `undefined`, update events receive `null` as their previous state, and `fetch` always hits the API.
7916
7958
  *
7959
+ * @remarks
7960
+ * Every entity cache is optional: an entity kind the cache does not hold is simply not cached, see
7961
+ * `createInMemoryCache`'s `entities` option.
7962
+ *
7917
7963
  * @default undefined
7918
7964
  */
7919
7965
  cache?: Cache;
7966
+ /**
7967
+ * Creates the store of each entity kind, `null` or `undefined` not to cache it: the `CacheConstructor` of the
7968
+ * discord.js RFC #11426. Called once per entity kind when the client is constructed, and takes precedence over
7969
+ * {@link GatewayClientOptions.cache}.
7970
+ *
7971
+ * @example
7972
+ * ```typescript
7973
+ * import { MemoryEntityCache } from '@wolfstar/plugin-cache';
7974
+ *
7975
+ * // Cache guilds, channels, and roles only.
7976
+ * const client = new GatewayClient({
7977
+ * intents,
7978
+ * makeCache: (entity) => (['guilds', 'channels', 'roles'].includes(entity) ? new MemoryEntityCache() : null),
7979
+ * });
7980
+ * ```
7981
+ *
7982
+ * @default undefined
7983
+ */
7984
+ makeCache?: CacheFactory;
7985
+ /**
7986
+ * The policies deciding which entries get cached, and for how long, per entity kind, see `withPolicy`. They apply to
7987
+ * every write, from dispatches as well as from the managers.
7988
+ *
7989
+ * @example
7990
+ * ```typescript
7991
+ * // Do not cache bots, and forget messages after an hour.
7992
+ * policies: { users: { filter: (user) => !user.bot }, messages: { ttl: () => 3_600_000 } }
7993
+ * ```
7994
+ *
7995
+ * @default undefined
7996
+ */
7997
+ policies?: CachePolicies;
7998
+ /**
7999
+ * What the managers do when a cache read or write fails, e.g. while Redis is unreachable. A `cacheError` event is
8000
+ * emitted either way.
8001
+ *
8002
+ * - `"miss"`: treat it as a cache miss, falling back to the API or to the data at hand.
8003
+ * - `"throw"`: reject with the error.
8004
+ *
8005
+ * @remarks
8006
+ * Dispatches follow {@link GatewayClientOptions.cacheFailure} instead.
8007
+ *
8008
+ * @default "miss"
8009
+ */
8010
+ cacheErrors?: "miss" | "throw";
7920
8011
  /**
7921
8012
  * Additional options for the underlying `@discordjs/ws` `WebSocketManager`, e.g. `compression` or
7922
8013
  * `initialPresence`.
@@ -8073,6 +8164,10 @@ export declare class GatewayClient extends Client {
8073
8164
  * What happens to a dispatch whose cache read or write fails, see {@link GatewayClientOptions.cacheFailure}.
8074
8165
  */
8075
8166
  readonly cacheFailure: "skip" | "emitUncached";
8167
+ /**
8168
+ * What the managers do when a cache read or write fails, see {@link GatewayClientOptions.cacheErrors}.
8169
+ */
8170
+ readonly cacheErrors: "miss" | "throw";
8076
8171
  /**
8077
8172
  * See {@link GatewayClientOptions.dispatchTimeout}.
8078
8173
  */
@@ -8199,6 +8294,8 @@ export declare class GatewayClient extends Client {
8199
8294
  * They are the guilds the bot left while it was disconnected, or while the process was down with a persistent
8200
8295
  * cache. Discord does not replay those removals on a new session, so the cache would otherwise keep them forever.
8201
8296
  *
8297
+ * It needs a guilds cache able to enumerate its entries, and is skipped without one.
8298
+ *
8202
8299
  * It is best effort: any failure (cache unreachable, unknown shard count) is reported through `error` and stops the
8203
8300
  * reconciliation, keeping the remaining guilds, but never fails `READY` itself.
8204
8301
  *
@@ -8335,6 +8432,23 @@ export declare class Typing extends Structure<GatewayTypingStartDispatchData> {
8335
8432
  }
8336
8433
  //#endregion
8337
8434
  //#region src/util/events.d.ts
8435
+ /**
8436
+ * Where a cache failure reported by the `cacheError` event happened.
8437
+ */
8438
+ export interface CacheErrorContext {
8439
+ /**
8440
+ * The entity cache that failed.
8441
+ */
8442
+ entity: CacheEntityName;
8443
+ /**
8444
+ * The key of the entry, `null` for operations spanning the whole cache.
8445
+ */
8446
+ key: string | null;
8447
+ /**
8448
+ * The operation that failed.
8449
+ */
8450
+ operation: "get" | "set" | "upsert" | "delete";
8451
+ }
8338
8452
  /**
8339
8453
  * What a reaction event says besides the reaction and the user.
8340
8454
  */
@@ -8397,6 +8511,11 @@ export interface GatewayEventMap {
8397
8511
  * Emitted when a shard runs into an error.
8398
8512
  */
8399
8513
  shardError: [error: Error, shardId: number];
8514
+ /**
8515
+ * Emitted when a manager's cache read or write fails, e.g. while Redis is unreachable. With the default
8516
+ * `cacheErrors: "miss"`, the manager then carries on as if the entry was not cached.
8517
+ */
8518
+ cacheError: [error: unknown, context: CacheErrorContext];
8400
8519
  guildCreate: [guild: Guild];
8401
8520
  guildUpdate: [oldGuild: Guild | null, newGuild: Guild];
8402
8521
  /**
@@ -8470,12 +8589,21 @@ export interface GatewayEventMap {
8470
8589
  guildRoleDelete: [role: Role | null, data: GatewayGuildRoleDeleteDispatchData];
8471
8590
  userUpdate: [oldUser: User | null, newUser: User];
8472
8591
  /**
8473
- * Emitted for each emoji a `GUILD_EMOJIS_UPDATE` adds, compared with the cache. Without a cache, the emoji events
8474
- * are not emitted: listen to `raw` instead.
8592
+ * Emitted for every `GUILD_EMOJIS_UPDATE`, with every emoji the guild now has. Always emitted, cache or not: the
8593
+ * granular `emojiCreate`, `emojiUpdate`, and `emojiDelete` need the previous emojis, so the emojis cache.
8594
+ */
8595
+ guildEmojisUpdate: [guildId: string, emojis: GuildEmoji[]];
8596
+ /**
8597
+ * Emitted for each emoji a `GUILD_EMOJIS_UPDATE` adds, compared with the cache. Without an emojis cache (able to
8598
+ * enumerate its entries), the emoji events are not emitted: listen to `guildEmojisUpdate` instead.
8475
8599
  */
8476
8600
  emojiCreate: [emoji: GuildEmoji];
8477
8601
  emojiUpdate: [oldEmoji: GuildEmoji, newEmoji: GuildEmoji];
8478
8602
  emojiDelete: [emoji: GuildEmoji];
8603
+ /**
8604
+ * Emitted for every `GUILD_STICKERS_UPDATE`, with every sticker the guild now has, like `guildEmojisUpdate`.
8605
+ */
8606
+ guildStickersUpdate: [guildId: string, stickers: Sticker[]];
8479
8607
  /**
8480
8608
  * Emitted for each sticker a `GUILD_STICKERS_UPDATE` adds, compared with the cache, like the emoji events.
8481
8609
  */
@@ -8577,6 +8705,7 @@ export declare enum GatewayEvents {
8577
8705
  ShardResume = "shardResume",
8578
8706
  ShardClose = "shardClose",
8579
8707
  ShardError = "shardError",
8708
+ CacheError = "cacheError",
8580
8709
  GuildCreate = "guildCreate",
8581
8710
  GuildUpdate = "guildUpdate",
8582
8711
  GuildDelete = "guildDelete",
@@ -8609,9 +8738,11 @@ export declare enum GatewayEvents {
8609
8738
  GuildRoleUpdate = "guildRoleUpdate",
8610
8739
  GuildRoleDelete = "guildRoleDelete",
8611
8740
  UserUpdate = "userUpdate",
8741
+ GuildEmojisUpdate = "guildEmojisUpdate",
8612
8742
  EmojiCreate = "emojiCreate",
8613
8743
  EmojiUpdate = "emojiUpdate",
8614
8744
  EmojiDelete = "emojiDelete",
8745
+ GuildStickersUpdate = "guildStickersUpdate",
8615
8746
  StickerCreate = "stickerCreate",
8616
8747
  StickerUpdate = "stickerUpdate",
8617
8748
  StickerDelete = "stickerDelete",
@@ -8968,5 +9099,5 @@ export declare function computeTargetPermissions(guildId: Snowflake, overwrites:
8968
9099
  */
8969
9100
  export declare function computePermissionsIn(channel: AnyChannel | Snowflake, target: GuildMember | Role | Snowflake): Promise<PermissionsBitField>;
8970
9101
  //#endregion
8971
- export { type AttachmentPayload, type AttachmentResolvable, type BaseMessageOptions, BitField, type BitFieldResolvable, type BufferResolvable, type ForwardOptions, type GatewayClientMessageDefaults, type JSONEncodable, type MessageCreateOptions, type MessageEditOptions, type MessageMentionOptions, type MessagePayloadContext, type MessagePayloadOptions, type MessagePayloadResolvable, type MessageTarget, PermissionsBitField, type PollCreateData, type RawAPIType, type ReplyOptions, type ResolvedMessagePayload, type StructureCreator, type WebhookMessageCreateOptions, type WebhookMessageEditOptions, type WebhookMessageOptions, type WebhookThreadOptions, type isPromiseLike, type pickCached, type whenAll, type whenCachedMap };
9102
+ export { type AttachmentPayload, type AttachmentResolvable, CachedManager as BaseManager, type BaseMessageOptions, BitField, type BitFieldResolvable, type BufferResolvable, type ForwardOptions, type GatewayClientMessageDefaults, type JSONEncodable, type MessageCreateOptions, type MessageEditOptions, type MessageMentionOptions, type MessagePayloadContext, type MessagePayloadOptions, type MessagePayloadResolvable, type MessageTarget, PermissionsBitField, type PollCreateData, type RawAPIType, type ReplyOptions, type ResolvedMessagePayload, type StructureCreator, type WebhookMessageCreateOptions, type WebhookMessageEditOptions, type WebhookMessageOptions, type WebhookThreadOptions, type isPromiseLike, type pickCached, type whenAll, type whenCachedMap };
8972
9103
  //# sourceMappingURL=index.d.ts.map