@wolfstar/plugin-cache 0.2.1-next-20260926221022 → 0.3.0-next-20260926221500

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
@@ -132,6 +132,8 @@ propagate as the client throws them. `@wolfstar/plugin-gateway` surfaces both as
132
132
 
133
133
  ```ts
134
134
  interface EntityCache<Raw> {
135
+ // Optional: `true` when no method ever returns a promise.
136
+ readonly synchronous?: boolean;
135
137
  get(key: string): Awaitable<Raw | undefined>;
136
138
  set(key: string, value: Raw): Awaitable<void>;
137
139
  has(key: string): Awaitable<boolean>;
@@ -151,6 +153,12 @@ semantics identical between synchronous and asynchronous stores. A store impleme
151
153
  lets `applyGatewayDispatch` skip the scans of a `GUILD_DELETE`; without it, or when it resolves to
152
154
  `null`, the scans run as before.
153
155
 
156
+ Since an `Awaitable` cannot be told apart from a promise without calling the method, a store tells
157
+ its consumers it never returns one through `synchronous`: the `MemoryEntityCache`s of
158
+ `createInMemoryCache` set it to `true`, the `RedisEntityCache`s of `createRedisCache` to `false`
159
+ (compressed or not), and a store leaving it out is treated as asynchronous. It is what lets
160
+ `@wolfstar/plugin-gateway`'s `cached` accessors read the cache without awaiting it.
161
+
154
162
  ### Keys
155
163
 
156
164
  Entities are keyed by their ID, except the ones scoped to a guild or a channel, which use the
@@ -137,6 +137,15 @@ type CacheEntityName = keyof CacheEntityTypes;
137
137
  * semantics identical across backends.
138
138
  */
139
139
  interface EntityCache<Raw> {
140
+ /**
141
+ * Whether every method returns its result synchronously, never a promise. Optional: a cache without it is treated
142
+ * as asynchronous, which is always safe.
143
+ *
144
+ * @remarks
145
+ * An {@link Awaitable} cannot be told apart from a promise without calling the method, so this is how consumers
146
+ * know they can read the cache without awaiting it, e.g. `@wolfstar/plugin-gateway`'s `cached` accessors.
147
+ */
148
+ readonly synchronous?: boolean;
140
149
  get(key: string): Awaitable<Raw | undefined>;
141
150
  set(key: string, value: Raw): Awaitable<void>;
142
151
  has(key: string): Awaitable<boolean>;
@@ -271,6 +280,10 @@ export declare function attachCacheToGateway(gateway: GatewayDispatchSource, cac
271
280
  */
272
281
  export declare class MemoryEntityCache<Raw> implements EntityCache<Raw> {
273
282
  #private;
283
+ /**
284
+ * Always `true`: every method returns synchronously.
285
+ */
286
+ readonly synchronous = true;
274
287
  /**
275
288
  * The maximum amount of entries, `Infinity` for an unbounded cache.
276
289
  */
@@ -412,6 +425,10 @@ interface RedisEntityCacheOptions<Raw = unknown> {
412
425
  */
413
426
  export declare class RedisEntityCache<Raw> implements EntityCache<Raw> {
414
427
  #private;
428
+ /**
429
+ * Always `false`: every method returns a promise, compressed or not.
430
+ */
431
+ readonly synchronous = false;
415
432
  readonly prefix: string;
416
433
  readonly ttl: number | undefined;
417
434
  readonly compression: RedisCacheCompression;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/lib/keys.ts","../../src/lib/types.ts","../../src/lib/operations.ts","../../src/lib/gateway.ts","../../src/lib/memory.ts","../../src/lib/redis.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/lib/keys.ts","../../src/lib/types.ts","../../src/lib/operations.ts","../../src/lib/gateway.ts","../../src/lib/memory.ts","../../src/lib/redis.ts"],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -1220,6 +1220,10 @@ function attachCacheToGateway(gateway, cache, options = {}) {
1220
1220
  * An {@link EntityCache} backed by a `Map`, optionally bounded as a least-recently-used cache.
1221
1221
  */
1222
1222
  var MemoryEntityCache = class {
1223
+ /**
1224
+ * Always `true`: every method returns synchronously.
1225
+ */
1226
+ synchronous = true;
1223
1227
  /**
1224
1228
  * The maximum amount of entries, `Infinity` for an unbounded cache.
1225
1229
  */
@@ -1324,6 +1328,10 @@ const CompressionMarkers = {
1324
1328
  * keys of that guild's entries the same way, and `<prefix>:@guilds` lists the guilds having one, for `clear`.
1325
1329
  */
1326
1330
  var RedisEntityCache = class {
1331
+ /**
1332
+ * Always `false`: every method returns a promise, compressed or not.
1333
+ */
1334
+ synchronous = false;
1327
1335
  prefix;
1328
1336
  ttl;
1329
1337
  compression;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/lib/keys.ts","../../src/lib/reactions.ts","../../src/lib/operations.ts","../../src/lib/gateway.ts","../../src/lib/memory.ts","../../src/lib/redis.ts"],"sourcesContent":["import type { Snowflake } from \"discord-api-types/v10\";\n\n/**\n * Creates a stable key for a guild-scoped entity.\n */\nexport function guildScopedKey(guildId: Snowflake, id: Snowflake): string {\n return `${guildId}:${id}`;\n}\n\n/**\n * Creates a stable key for a channel message.\n */\nexport function messageKey(channelId: Snowflake, messageId: Snowflake): string {\n return `${channelId}:${messageId}`;\n}\n\n/**\n * Creates a stable key for a guild member.\n */\nexport function memberKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild presence.\n */\nexport function presenceKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild voice state.\n */\nexport function voiceStateKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild role.\n */\nexport function roleKey(guildId: Snowflake, roleId: Snowflake): string {\n return guildScopedKey(guildId, roleId);\n}\n\n/**\n * Creates a stable key for a guild emoji.\n */\nexport function emojiKey(guildId: Snowflake, emojiId: Snowflake): string {\n return guildScopedKey(guildId, emojiId);\n}\n\n/**\n * Creates a stable key for a guild sticker.\n */\nexport function stickerKey(guildId: Snowflake, stickerId: Snowflake): string {\n return guildScopedKey(guildId, stickerId);\n}\n\n/**\n * Creates a stable key for a guild scheduled event.\n */\nexport function scheduledEventKey(guildId: Snowflake, scheduledEventId: Snowflake): string {\n return guildScopedKey(guildId, scheduledEventId);\n}\n\n/**\n * Creates a stable key for a stage instance.\n */\nexport function stageInstanceKey(guildId: Snowflake, channelId: Snowflake): string {\n return guildScopedKey(guildId, channelId);\n}\n\n/**\n * Creates a stable key for a guild soundboard sound.\n */\nexport function soundboardSoundKey(guildId: Snowflake, soundId: Snowflake): string {\n return guildScopedKey(guildId, soundId);\n}\n\n/**\n * Creates a stable key for a guild auto moderation rule.\n */\nexport function autoModerationRuleKey(guildId: Snowflake, ruleId: Snowflake): string {\n return guildScopedKey(guildId, ruleId);\n}\n\n/**\n * Creates a stable key for a guild ban.\n */\nexport function banKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild integration.\n */\nexport function integrationKey(guildId: Snowflake, integrationId: Snowflake): string {\n return guildScopedKey(guildId, integrationId);\n}\n\n/**\n * Creates a stable key for an invite, using `@global` for invites that do not belong to a guild.\n */\nexport function inviteKey(guildId: Snowflake | null | undefined, code: string): string {\n return `${guildId ?? \"@global\"}:${code}`;\n}\n\n/**\n * Creates a stable key for a thread member.\n */\nexport function threadMemberKey(threadId: Snowflake, userId: Snowflake): string {\n return `${threadId}:${userId}`;\n}\n\n/**\n * Creates a stable key for application command permissions.\n */\nexport function applicationCommandPermissionsKey(\n applicationId: Snowflake,\n guildId: Snowflake,\n commandId: Snowflake,\n): string {\n return `${applicationId}:${guildId}:${commandId}`;\n}\n","import type {\n APIMessage,\n APIPartialEmoji,\n APIReaction,\n GatewayMessagePollVoteDispatchData,\n GatewayMessageReactionAddDispatchData,\n GatewayMessageReactionRemoveDispatchData,\n} from \"discord-api-types/v10\";\n\n// Reactions and poll votes only carry the voter's ID, so whether the bot is the voter needs the bot's own ID.\n\n/**\n * Whether two reaction emojis are the same: custom emojis match by ID, Unicode emojis by name.\n */\nfunction isSameEmoji(a: APIPartialEmoji, b: APIPartialEmoji): boolean {\n return a.id ? a.id === b.id : !b.id && a.name === b.name;\n}\n\n/**\n * Adds a `MESSAGE_REACTION_ADD` to a cached message's reactions.\n *\n * @param message The cached message.\n * @param data The dispatch data.\n * @param clientUserId The bot's user ID, to set `me` when the bot reacted.\n */\nexport function addReaction(\n message: APIMessage,\n data: GatewayMessageReactionAddDispatchData,\n clientUserId?: string,\n): APIMessage {\n const me = data.user_id === clientUserId;\n const kind = data.burst ? \"burst\" : \"normal\";\n const reactions = message.reactions ?? [];\n const existing = reactions.find((reaction) => isSameEmoji(reaction.emoji, data.emoji));\n\n if (!existing) {\n const reaction: APIReaction = {\n emoji: data.emoji,\n count: 1,\n count_details: { normal: data.burst ? 0 : 1, burst: data.burst ? 1 : 0 },\n me: me && !data.burst,\n me_burst: me && data.burst,\n burst_colors: data.burst_colors ?? [],\n };\n return { ...message, reactions: [...reactions, reaction] };\n }\n\n return {\n ...message,\n reactions: reactions.map((reaction) =>\n reaction === existing\n ? {\n ...reaction,\n count: reaction.count + 1,\n count_details: {\n ...reaction.count_details,\n [kind]: reaction.count_details[kind] + 1,\n },\n me: reaction.me || (me && !data.burst),\n me_burst: reaction.me_burst || (me && data.burst),\n burst_colors: data.burst\n ? (data.burst_colors ?? reaction.burst_colors)\n : reaction.burst_colors,\n }\n : reaction,\n ),\n };\n}\n\n/**\n * Removes a `MESSAGE_REACTION_REMOVE` from a cached message's reactions, dropping the reaction when its count reaches\n * zero.\n *\n * @param message The cached message.\n * @param data The dispatch data.\n * @param clientUserId The bot's user ID, to clear `me` when the bot's reaction was removed.\n */\nexport function removeReaction(\n message: APIMessage,\n data: GatewayMessageReactionRemoveDispatchData,\n clientUserId?: string,\n): APIMessage {\n const me = data.user_id === clientUserId;\n const kind = data.burst ? \"burst\" : \"normal\";\n const reactions = (message.reactions ?? []).flatMap((reaction) => {\n if (!isSameEmoji(reaction.emoji, data.emoji)) return [reaction];\n if (reaction.count <= 1) return [];\n return [\n {\n ...reaction,\n count: reaction.count - 1,\n count_details: {\n ...reaction.count_details,\n [kind]: Math.max(0, reaction.count_details[kind] - 1),\n },\n me: reaction.me && !(me && !data.burst),\n me_burst: reaction.me_burst && !(me && data.burst),\n },\n ];\n });\n return { ...message, reactions };\n}\n\n/**\n * Removes every reaction with one emoji from a cached message.\n *\n * @param message The cached message.\n * @param emoji The emoji.\n */\nexport function removeReactionEmoji(message: APIMessage, emoji: APIPartialEmoji): APIMessage {\n return {\n ...message,\n reactions: (message.reactions ?? []).filter((reaction) => !isSameEmoji(reaction.emoji, emoji)),\n };\n}\n\n/**\n * Counts a `MESSAGE_POLL_VOTE_ADD` or `MESSAGE_POLL_VOTE_REMOVE` in a cached message's poll results.\n *\n * @param message The cached message.\n * @param data The dispatch data.\n * @param delta `1` for a vote, `-1` for a removed vote.\n * @param clientUserId The bot's user ID, to set `me_voted` when the bot voted.\n */\nexport function countPollVote(\n message: APIMessage,\n data: GatewayMessagePollVoteDispatchData,\n delta: 1 | -1,\n clientUserId?: string,\n): APIMessage {\n const { poll } = message;\n if (!poll) return message;\n\n const me = data.user_id === clientUserId;\n const results = poll.results ?? { is_finalized: false, answer_counts: [] };\n const counts = results.answer_counts.some((count) => count.id === data.answer_id)\n ? results.answer_counts\n : [...results.answer_counts, { id: data.answer_id, count: 0, me_voted: false }];\n\n return {\n ...message,\n poll: {\n ...poll,\n results: {\n ...results,\n answer_counts: counts.map((count) =>\n count.id === data.answer_id\n ? {\n ...count,\n count: Math.max(0, count.count + delta),\n me_voted: me ? delta === 1 : count.me_voted,\n }\n : count,\n ),\n },\n },\n };\n}\n","import { GatewayDispatchEvents } from \"discord-api-types/v10\";\nimport type {\n APIEmoji,\n APIMessage,\n APIGuildMember,\n APIRole,\n APISoundboardSound,\n APISticker,\n GatewayDispatchPayload,\n GatewayGuildCreateDispatchData,\n GatewayGuildMembersChunkDispatchData,\n GatewayGuildSoundboardSoundsUpdateDispatchData,\n GatewaySoundboardSoundsDispatchData,\n GatewayThreadListSync,\n GatewayThreadMembersUpdateDispatchData,\n Snowflake,\n} from \"discord-api-types/v10\";\nimport {\n applicationCommandPermissionsKey,\n autoModerationRuleKey,\n banKey,\n emojiKey,\n guildScopedKey,\n integrationKey,\n inviteKey,\n memberKey,\n messageKey,\n presenceKey,\n roleKey,\n scheduledEventKey,\n soundboardSoundKey,\n stageInstanceKey,\n stickerKey,\n threadMemberKey,\n voiceStateKey,\n} from \"./keys.js\";\nimport { addReaction, countPollVote, removeReaction, removeReactionEmoji } from \"./reactions.js\";\nimport type { Cache, CacheEntityName, EntityCache } from \"./types.js\";\n\n// A record rather than an array so the compiler enforces that every entity cache is listed.\nconst cacheEntityNameRecord: Record<CacheEntityName, true> = {\n applicationCommandPermissions: true,\n auditLogEntries: true,\n autoModerationRules: true,\n bans: true,\n channels: true,\n emojis: true,\n entitlements: true,\n guilds: true,\n integrations: true,\n invites: true,\n members: true,\n messages: true,\n presences: true,\n roles: true,\n scheduledEvents: true,\n soundboardSounds: true,\n stageInstances: true,\n stickers: true,\n subscriptions: true,\n threadMembers: true,\n threads: true,\n users: true,\n voiceStates: true,\n};\n\n/**\n * The name of every entity cache held by a {@link Cache}.\n */\nexport const CacheEntityNames = Object.keys(cacheEntityNameRecord) as readonly CacheEntityName[];\n\n/**\n * The entity caches whose keys start with the ID of the guild the entity belongs to (`${guildId}:...`).\n *\n * @internal\n */\nexport const GuildKeyedCacheEntityNames = [\n \"auditLogEntries\",\n \"autoModerationRules\",\n \"bans\",\n \"emojis\",\n \"integrations\",\n \"members\",\n \"presences\",\n \"roles\",\n \"scheduledEvents\",\n \"soundboardSounds\",\n \"stageInstances\",\n \"stickers\",\n \"voiceStates\",\n] as const satisfies readonly CacheEntityName[];\n\n/**\n * The entity caches keyed by the entity's own ID, which only know their guild through the stored `guild_id`.\n *\n * @internal\n */\nexport const GuildFieldCacheEntityNames = [\n \"applicationCommandPermissions\",\n \"channels\",\n \"threads\",\n \"threadMembers\",\n \"messages\",\n \"invites\",\n] as const satisfies readonly CacheEntityName[];\n\n/**\n * A single mutation a gateway dispatch produces on a {@link Cache}.\n */\nexport type CacheOperation =\n | {\n type: \"upsert\";\n store: CacheEntityName;\n key: string;\n raw: unknown;\n /** Whether to shallow-merge `raw` onto the existing value, used for partial updates. */\n merge?: boolean;\n }\n | {\n type: \"update\";\n store: CacheEntityName;\n key: string;\n /** Computes the new value from the cached one. Nothing is written when the key is not cached. */\n update: (value: unknown) => unknown;\n }\n | { type: \"delete\"; store: CacheEntityName; key: string }\n | {\n type: \"deletePrefix\";\n store: CacheEntityName;\n prefix: string;\n /** The guild the deleted entries are exactly the entries of, see {@link EntityCache.deleteGuild}. */\n guildId?: Snowflake;\n }\n | {\n type: \"deleteWhere\";\n store: CacheEntityName;\n predicate: (value: unknown) => boolean;\n /** The guild the deleted entries are exactly the entries of, see {@link EntityCache.deleteGuild}. */\n guildId?: Snowflake;\n };\n\n/**\n * What {@link createCacheOperations} needs to know besides the dispatch.\n */\nexport interface CacheOperationContext {\n /**\n * The bot's user ID. Reactions and poll votes only carry the voter's ID, so without it the cached `me` and\n * `me_voted` flags are never set.\n */\n clientUserId?: string;\n}\n\n/**\n * Translates a gateway dispatch into the list of {@link CacheOperation}s it implies, including the cascades (e.g.\n * `CHANNEL_DELETE` also drops that channel's messages).\n *\n * @remarks\n * This is a pure function, it does not touch any cache. Use {@link applyGatewayDispatch} to apply them.\n * `INTERACTION_CREATE` is intentionally ignored: interactions are short-lived and never cached.\n *\n * @param payload The gateway dispatch payload.\n * @param context The bot's user ID, for the `me` flags of reactions and poll votes.\n */\nexport function createCacheOperations(\n payload: GatewayDispatchPayload,\n context: CacheOperationContext = {},\n): CacheOperation[] {\n const { clientUserId } = context;\n const operations: CacheOperation[] = [];\n\n switch (payload.t) {\n case GatewayDispatchEvents.ApplicationCommandPermissionsUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"applicationCommandPermissions\",\n key: applicationCommandPermissionsKey(data.application_id, data.guild_id, data.id),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.AutoModerationRuleCreate:\n case GatewayDispatchEvents.AutoModerationRuleUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"autoModerationRules\",\n key: autoModerationRuleKey(data.guild_id, data.id),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.AutoModerationRuleDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"autoModerationRules\",\n key: autoModerationRuleKey(data.guild_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.ChannelCreate:\n case GatewayDispatchEvents.ChannelUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"channels\",\n key: data.id,\n raw: data,\n merge: payload.t === GatewayDispatchEvents.ChannelUpdate,\n });\n break;\n }\n\n case GatewayDispatchEvents.ChannelDelete: {\n const data = payload.d;\n operations.push({ type: \"delete\", store: \"channels\", key: data.id });\n operations.push({ type: \"deletePrefix\", store: \"messages\", prefix: `${data.id}:` });\n break;\n }\n\n case GatewayDispatchEvents.EntitlementCreate:\n case GatewayDispatchEvents.EntitlementUpdate: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"entitlements\", key: data.id, raw: data });\n break;\n }\n\n case GatewayDispatchEvents.EntitlementDelete: {\n operations.push({ type: \"delete\", store: \"entitlements\", key: payload.d.id });\n break;\n }\n\n case GatewayDispatchEvents.GuildAuditLogEntryCreate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"auditLogEntries\",\n key: guildScopedKey(data.guild_id, data.id),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildBanAdd: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"bans\",\n key: banKey(data.guild_id, data.user.id),\n raw: data,\n });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildBanRemove: {\n const data = payload.d;\n operations.push({ type: \"delete\", store: \"bans\", key: banKey(data.guild_id, data.user.id) });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildCreate: {\n hydrateGuildCreate(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.GuildUpdate: {\n // Like `GUILD_CREATE`, the roles and emojis go to their own entity caches rather than the guild entry.\n const { roles, emojis, stickers, ...data } = payload.d;\n operations.push({ type: \"upsert\", store: \"guilds\", key: data.id, raw: data, merge: true });\n for (const role of roles ?? []) hydrateRole(operations, data.id, role);\n for (const emoji of emojis ?? []) hydrateEmoji(operations, data.id, emoji);\n for (const sticker of stickers ?? []) hydrateSticker(operations, data.id, sticker);\n break;\n }\n\n case GatewayDispatchEvents.GuildDelete: {\n const data = payload.d;\n // An `unavailable` guild is an outage, not a removal: keep its data around until it comes back.\n if (data.unavailable) {\n operations.push({ type: \"upsert\", store: \"guilds\", key: data.id, raw: data, merge: true });\n break;\n }\n\n operations.push({ type: \"delete\", store: \"guilds\", key: data.id });\n deleteGuildScopedResources(operations, data.id);\n break;\n }\n\n case GatewayDispatchEvents.GuildEmojisUpdate: {\n const data = payload.d;\n operations.push({\n type: \"deletePrefix\",\n store: \"emojis\",\n prefix: `${data.guild_id}:`,\n guildId: data.guild_id,\n });\n for (const emoji of data.emojis) {\n if (!emoji.id) continue;\n operations.push({\n type: \"upsert\",\n store: \"emojis\",\n key: emojiKey(data.guild_id, emoji.id),\n raw: withGuildId(emoji, data.guild_id),\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.GuildStickersUpdate: {\n const data = payload.d;\n operations.push({\n type: \"deletePrefix\",\n store: \"stickers\",\n prefix: `${data.guild_id}:`,\n guildId: data.guild_id,\n });\n for (const sticker of data.stickers) {\n operations.push({\n type: \"upsert\",\n store: \"stickers\",\n key: stickerKey(data.guild_id, sticker.id),\n raw: withGuildId(sticker, data.guild_id),\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.GuildMemberAdd:\n case GatewayDispatchEvents.GuildMemberUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, data.user.id),\n raw: data,\n merge: true,\n });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildMemberRemove: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"members\",\n key: memberKey(data.guild_id, data.user.id),\n });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildMembersChunk: {\n hydrateGuildMembersChunk(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.GuildRoleCreate:\n case GatewayDispatchEvents.GuildRoleUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"roles\",\n key: roleKey(data.guild_id, data.role.id),\n raw: withGuildId(data.role, data.guild_id),\n merge: payload.t === GatewayDispatchEvents.GuildRoleUpdate,\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildRoleDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"roles\",\n key: roleKey(data.guild_id, data.role_id),\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildScheduledEventCreate:\n case GatewayDispatchEvents.GuildScheduledEventUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"scheduledEvents\",\n key: scheduledEventKey(data.guild_id, data.id),\n raw: data,\n merge: true,\n });\n if (data.creator) {\n operations.push({\n type: \"upsert\",\n store: \"users\",\n key: data.creator.id,\n raw: data.creator,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.GuildScheduledEventDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"scheduledEvents\",\n key: scheduledEventKey(data.guild_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildSoundboardSoundCreate:\n case GatewayDispatchEvents.GuildSoundboardSoundUpdate: {\n const data = payload.d;\n if (!data.guild_id) break;\n operations.push({\n type: \"upsert\",\n store: \"soundboardSounds\",\n key: soundboardSoundKey(data.guild_id, data.sound_id),\n raw: data,\n merge: payload.t === GatewayDispatchEvents.GuildSoundboardSoundUpdate,\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildSoundboardSoundDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"soundboardSounds\",\n key: soundboardSoundKey(data.guild_id, data.sound_id),\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildSoundboardSoundsUpdate: {\n hydrateGuildSoundboardSoundsUpdate(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.SoundboardSounds: {\n hydrateSoundboardSounds(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.IntegrationCreate:\n case GatewayDispatchEvents.IntegrationUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"integrations\",\n key: integrationKey(data.guild_id, data.id),\n raw: data,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.IntegrationDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"integrations\",\n key: integrationKey(data.guild_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.InviteCreate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"invites\",\n key: inviteKey(data.guild_id, data.code),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.InviteDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"invites\",\n key: inviteKey(data.guild_id, data.code),\n });\n break;\n }\n\n case GatewayDispatchEvents.MessageCreate:\n case GatewayDispatchEvents.MessageUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"messages\",\n key: messageKey(data.channel_id, data.id),\n raw: data,\n merge: payload.t === GatewayDispatchEvents.MessageUpdate,\n });\n\n const author = \"author\" in data ? data.author : undefined;\n if (author) {\n operations.push({ type: \"upsert\", store: \"users\", key: author.id, raw: author });\n if (data.member && data.guild_id) {\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, author.id),\n raw: withGuildId(data.member, data.guild_id),\n merge: true,\n });\n }\n }\n break;\n }\n\n case GatewayDispatchEvents.MessageDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"messages\",\n key: messageKey(data.channel_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.MessageDeleteBulk: {\n const data = payload.d;\n for (const id of data.ids) {\n operations.push({\n type: \"delete\",\n store: \"messages\",\n key: messageKey(data.channel_id, id),\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionAdd: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n addReaction(message, data, clientUserId),\n ),\n );\n // Guild reactions carry the reacting member, like messages carry their author.\n const user = data.member?.user;\n if (user && data.guild_id) {\n operations.push({ type: \"upsert\", store: \"users\", key: user.id, raw: user });\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, user.id),\n raw: withGuildId(data.member!, data.guild_id),\n merge: true,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionRemove: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n removeReaction(message, data, clientUserId),\n ),\n );\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionRemoveAll: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) => ({\n ...message,\n reactions: [],\n })),\n );\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionRemoveEmoji: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n removeReactionEmoji(message, data.emoji),\n ),\n );\n break;\n }\n\n case GatewayDispatchEvents.MessagePollVoteAdd:\n case GatewayDispatchEvents.MessagePollVoteRemove: {\n const data = payload.d;\n const delta = payload.t === GatewayDispatchEvents.MessagePollVoteAdd ? 1 : -1;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n countPollVote(message, data, delta, clientUserId),\n ),\n );\n break;\n }\n\n case GatewayDispatchEvents.PresenceUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"presences\",\n key: presenceKey(data.guild_id, data.user.id),\n raw: data,\n merge: true,\n });\n operations.push({\n type: \"upsert\",\n store: \"users\",\n key: data.user.id,\n raw: data.user,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.Ready: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n for (const guild of data.guilds) {\n operations.push({\n type: \"upsert\",\n store: \"guilds\",\n key: guild.id,\n raw: guild,\n merge: true,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.StageInstanceCreate:\n case GatewayDispatchEvents.StageInstanceUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"stageInstances\",\n key: stageInstanceKey(data.guild_id, data.channel_id),\n raw: data,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.StageInstanceDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"stageInstances\",\n key: stageInstanceKey(data.guild_id, data.channel_id),\n });\n break;\n }\n\n case GatewayDispatchEvents.SubscriptionCreate:\n case GatewayDispatchEvents.SubscriptionUpdate: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"subscriptions\", key: data.id, raw: data });\n break;\n }\n\n case GatewayDispatchEvents.SubscriptionDelete: {\n operations.push({ type: \"delete\", store: \"subscriptions\", key: payload.d.id });\n break;\n }\n\n case GatewayDispatchEvents.ThreadCreate:\n case GatewayDispatchEvents.ThreadUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"threads\",\n key: data.id,\n raw: data,\n merge: payload.t === GatewayDispatchEvents.ThreadUpdate,\n });\n\n const member = \"member\" in data ? data.member : undefined;\n if (member?.user_id) {\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, member.user_id),\n // Thread members carry no guild ID of their own: without it, `GUILD_DELETE` could not sweep them.\n raw: data.guild_id ? withGuildId(member, data.guild_id) : member,\n merge: true,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.ThreadDelete: {\n const data = payload.d;\n operations.push({ type: \"delete\", store: \"threads\", key: data.id });\n operations.push({ type: \"deletePrefix\", store: \"threadMembers\", prefix: `${data.id}:` });\n operations.push({ type: \"deletePrefix\", store: \"messages\", prefix: `${data.id}:` });\n break;\n }\n\n case GatewayDispatchEvents.ThreadListSync: {\n hydrateThreadListSync(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.ThreadMemberUpdate: {\n const data = payload.d;\n if (!data.id || !data.user_id) break;\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, data.user_id),\n raw: data,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.ThreadMembersUpdate: {\n hydrateThreadMembersUpdate(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.UserUpdate: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"users\", key: data.id, raw: data, merge: true });\n break;\n }\n\n case GatewayDispatchEvents.VoiceStateUpdate: {\n const data = payload.d;\n if (!data.guild_id) break;\n\n const key = voiceStateKey(data.guild_id, data.user_id);\n if (data.channel_id) {\n operations.push({ type: \"upsert\", store: \"voiceStates\", key, raw: data, merge: true });\n } else {\n operations.push({ type: \"delete\", store: \"voiceStates\", key });\n }\n\n const user = data.member?.user;\n if (data.member && user) {\n operations.push({ type: \"upsert\", store: \"users\", key: user.id, raw: user });\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, user.id),\n raw: withGuildId(data.member, data.guild_id),\n merge: true,\n });\n }\n break;\n }\n\n default:\n break;\n }\n\n return operations;\n}\n\n/**\n * Applies a list of {@link CacheOperation}s to a {@link Cache}, sequentially and in order.\n *\n * @param cache The cache to mutate.\n * @param operations The operations to apply, usually created by {@link createCacheOperations}.\n */\nexport async function applyCacheOperations(\n cache: Cache,\n operations: readonly CacheOperation[],\n): Promise<void> {\n for (const operation of operations) {\n const store = cache[operation.store] as EntityCache<unknown>;\n\n switch (operation.type) {\n case \"upsert\": {\n const value = operation.merge\n ? mergeValues(await store.get(operation.key), operation.raw)\n : operation.raw;\n await store.set(operation.key, value);\n break;\n }\n case \"update\": {\n const existing = await store.get(operation.key);\n if (existing !== undefined) await store.set(operation.key, operation.update(existing));\n break;\n }\n case \"delete\":\n await store.delete(operation.key);\n break;\n case \"deletePrefix\":\n if (await deleteGuildThroughIndex(store, operation.guildId)) break;\n for (const key of await store.keys()) {\n if (key.startsWith(operation.prefix)) await store.delete(key);\n }\n break;\n case \"deleteWhere\":\n if (await deleteGuildThroughIndex(store, operation.guildId)) break;\n for (const [key, value] of await store.entries()) {\n if (operation.predicate(value)) await store.delete(key);\n }\n break;\n }\n }\n}\n\n/**\n * Deletes a guild's entries through the store's guild index, when it has one.\n *\n * @returns Whether the entries were deleted, `false` when the caller has to scan the store instead.\n */\nasync function deleteGuildThroughIndex(\n store: EntityCache<unknown>,\n guildId: Snowflake | undefined,\n): Promise<boolean> {\n if (guildId === undefined || store.deleteGuild === undefined) return false;\n return (await store.deleteGuild(guildId)) !== null;\n}\n\n/**\n * Writes a gateway dispatch into every relevant entity cache of a {@link Cache}.\n *\n * @param cache The cache to mutate.\n * @param payload The gateway dispatch payload.\n * @param context The bot's user ID, for the `me` flags of reactions and poll votes.\n */\nexport function applyGatewayDispatch(\n cache: Cache,\n payload: GatewayDispatchPayload,\n context?: CacheOperationContext,\n): Promise<void> {\n return applyCacheOperations(cache, createCacheOperations(payload, context));\n}\n\n/**\n * Shallow-merges `value` onto `existing` when both are plain objects, returning `value` otherwise.\n */\nexport function mergeValues<Value>(existing: Value | undefined, value: Value): Value {\n if (isObject(existing) && isObject(value)) return { ...existing, ...value };\n return value;\n}\n\nfunction hydrateGuildCreate(\n operations: CacheOperation[],\n guild: GatewayGuildCreateDispatchData,\n): void {\n // Unavailable guilds carry no data besides their ID, see `GuildDelete`.\n if (\"unavailable\" in guild && guild.unavailable) {\n operations.push({ type: \"upsert\", store: \"guilds\", key: guild.id, raw: guild, merge: true });\n return;\n }\n\n const {\n channels,\n threads,\n members,\n presences,\n voice_states: voiceStates,\n stage_instances: stageInstances,\n guild_scheduled_events: scheduledEvents,\n soundboard_sounds: soundboardSounds,\n roles,\n emojis,\n stickers,\n ...rest\n } = guild;\n // Collections are stored in their own entity caches, keeping the guild entry small.\n operations.push({ type: \"upsert\", store: \"guilds\", key: guild.id, raw: rest });\n\n for (const channel of channels ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"channels\",\n key: channel.id,\n raw: withGuildId(channel, guild.id),\n });\n }\n\n for (const thread of threads ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"threads\",\n key: thread.id,\n raw: withGuildId(thread, guild.id),\n });\n if (thread.member?.user_id) {\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(thread.id, thread.member.user_id),\n raw: withGuildId(thread.member, guild.id),\n });\n }\n }\n\n for (const member of members ?? []) hydrateMember(operations, guild.id, member);\n\n for (const presence of presences ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"presences\",\n key: presenceKey(guild.id, presence.user.id),\n raw: withGuildId(presence, guild.id),\n });\n }\n\n for (const role of roles ?? []) hydrateRole(operations, guild.id, role);\n for (const emoji of emojis ?? []) hydrateEmoji(operations, guild.id, emoji);\n for (const sticker of stickers ?? []) hydrateSticker(operations, guild.id, sticker);\n\n for (const voiceState of voiceStates ?? []) {\n if (!voiceState.channel_id) continue;\n operations.push({\n type: \"upsert\",\n store: \"voiceStates\",\n key: voiceStateKey(guild.id, voiceState.user_id),\n raw: withGuildId(voiceState, guild.id),\n });\n }\n\n for (const stageInstance of stageInstances ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"stageInstances\",\n key: stageInstanceKey(guild.id, stageInstance.channel_id),\n raw: stageInstance,\n });\n }\n\n for (const scheduledEvent of scheduledEvents ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"scheduledEvents\",\n key: scheduledEventKey(guild.id, scheduledEvent.id),\n raw: scheduledEvent,\n });\n }\n\n for (const sound of soundboardSounds ?? []) hydrateSoundboardSound(operations, guild.id, sound);\n}\n\nfunction hydrateGuildMembersChunk(\n operations: CacheOperation[],\n data: GatewayGuildMembersChunkDispatchData,\n): void {\n for (const member of data.members) hydrateMember(operations, data.guild_id, member);\n for (const presence of data.presences ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"presences\",\n key: presenceKey(data.guild_id, presence.user.id),\n raw: withGuildId(presence, data.guild_id),\n });\n }\n}\n\nfunction hydrateGuildSoundboardSoundsUpdate(\n operations: CacheOperation[],\n data: GatewayGuildSoundboardSoundsUpdateDispatchData,\n): void {\n operations.push({\n type: \"deletePrefix\",\n store: \"soundboardSounds\",\n prefix: `${data.guild_id}:`,\n guildId: data.guild_id,\n });\n for (const sound of data.soundboard_sounds)\n hydrateSoundboardSound(operations, data.guild_id, sound);\n}\n\nfunction hydrateSoundboardSounds(\n operations: CacheOperation[],\n data: GatewaySoundboardSoundsDispatchData,\n): void {\n for (const sound of data.soundboard_sounds)\n hydrateSoundboardSound(operations, data.guild_id, sound);\n}\n\nfunction hydrateThreadListSync(operations: CacheOperation[], data: GatewayThreadListSync): void {\n for (const thread of data.threads) {\n operations.push({\n type: \"upsert\",\n store: \"threads\",\n key: thread.id,\n raw: withGuildId(thread, data.guild_id),\n merge: true,\n });\n }\n\n for (const member of data.members) {\n if (!member.id || !member.user_id) continue;\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(member.id, member.user_id),\n raw: withGuildId(member, data.guild_id),\n merge: true,\n });\n }\n}\n\nfunction hydrateThreadMembersUpdate(\n operations: CacheOperation[],\n data: GatewayThreadMembersUpdateDispatchData,\n): void {\n for (const member of data.added_members ?? []) {\n if (!member.user_id) continue;\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, member.user_id),\n raw: withGuildId(member, data.guild_id),\n merge: true,\n });\n }\n\n for (const userId of data.removed_member_ids ?? []) {\n operations.push({\n type: \"delete\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, userId),\n });\n }\n}\n\nfunction hydrateMember(\n operations: CacheOperation[],\n guildId: Snowflake,\n member: APIGuildMember,\n): void {\n if (!member.user) return;\n\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(guildId, member.user.id),\n raw: withGuildId(member, guildId),\n merge: true,\n });\n operations.push({ type: \"upsert\", store: \"users\", key: member.user.id, raw: member.user });\n}\n\nfunction hydrateRole(operations: CacheOperation[], guildId: Snowflake, role: APIRole): void {\n operations.push({\n type: \"upsert\",\n store: \"roles\",\n key: roleKey(guildId, role.id),\n raw: withGuildId(role, guildId),\n });\n}\n\nfunction hydrateEmoji(operations: CacheOperation[], guildId: Snowflake, emoji: APIEmoji): void {\n if (!emoji.id) return;\n operations.push({\n type: \"upsert\",\n store: \"emojis\",\n key: emojiKey(guildId, emoji.id),\n raw: withGuildId(emoji, guildId),\n });\n}\n\nfunction hydrateSticker(\n operations: CacheOperation[],\n guildId: Snowflake,\n sticker: APISticker,\n): void {\n operations.push({\n type: \"upsert\",\n store: \"stickers\",\n key: stickerKey(guildId, sticker.id),\n raw: withGuildId(sticker, guildId),\n });\n}\n\nfunction hydrateSoundboardSound(\n operations: CacheOperation[],\n guildId: Snowflake,\n sound: APISoundboardSound,\n): void {\n operations.push({\n type: \"upsert\",\n store: \"soundboardSounds\",\n key: soundboardSoundKey(guildId, sound.sound_id),\n raw: withGuildId(sound, guildId),\n });\n}\n\nfunction deleteGuildScopedResources(operations: CacheOperation[], guildId: Snowflake): void {\n // Every operation carries the guild, so a store indexing its entries by guild skips the scans below.\n // Entities keyed by `${guildId}:...` can be dropped by prefix, which only reads the keys.\n for (const store of GuildKeyedCacheEntityNames) {\n operations.push({ type: \"deletePrefix\", store, prefix: `${guildId}:`, guildId });\n }\n\n // The rest are keyed by their own ID and need a scan over the stored `guild_id`.\n for (const store of GuildFieldCacheEntityNames) {\n operations.push({\n type: \"deleteWhere\",\n store,\n predicate: (value) => isObject(value) && value.guild_id === guildId,\n guildId,\n });\n }\n}\n\n// An `update` of a cached message; a message the cache does not hold stays uncached.\nfunction updateMessage(\n channelId: Snowflake,\n messageId: Snowflake,\n update: (message: APIMessage) => APIMessage,\n): CacheOperation {\n return {\n type: \"update\",\n store: \"messages\",\n key: messageKey(channelId, messageId),\n update: (value) => update(value as APIMessage),\n };\n}\n\nfunction withGuildId<Value extends object>(\n value: Value,\n guildId: Snowflake,\n): Value & { guild_id: Snowflake } {\n return { ...value, guild_id: guildId };\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import type { GatewayDispatchPayload } from \"discord-api-types/v10\";\nimport { applyGatewayDispatch, type CacheOperationContext } from \"./operations.js\";\nimport type { Cache } from \"./types.js\";\n\n/** A gateway that emits the dispatch event used by @discordjs/ws and @discordjs/core. */\nexport interface GatewayDispatchSource {\n on(\n event: \"dispatch\",\n listener: (payload: GatewayDispatchPayload, shardId: number) => void,\n ): unknown;\n off?(\n event: \"dispatch\",\n listener: (payload: GatewayDispatchPayload, shardId: number) => void,\n ): unknown;\n}\n\nexport interface CacheGatewayOptions extends CacheOperationContext {\n /** Receives cache write failures from the asynchronous gateway listener. */\n onError?: (error: unknown, payload: GatewayDispatchPayload, shardId: number) => void;\n}\n\n/**\n * Writes gateway dispatches into a cache. Call the returned function to stop listening.\n * Use this when a gateway has no GatewayClient managing its dispatch queue.\n */\nexport function attachCacheToGateway(\n gateway: GatewayDispatchSource,\n cache: Cache,\n options: CacheGatewayOptions = {},\n): () => void {\n const listener = (payload: GatewayDispatchPayload, shardId: number): void => {\n void applyGatewayDispatch(cache, payload, { clientUserId: options.clientUserId }).catch(\n (error) => {\n if (options.onError) options.onError(error, payload, shardId);\n else console.error(\"Failed to cache gateway dispatch\", error);\n },\n );\n };\n\n gateway.on(\"dispatch\", listener);\n return () => gateway.off?.(\"dispatch\", listener);\n}\n","import { CacheEntityNames } from \"./operations.js\";\nimport type { Cache, CacheEntityName, CacheEntityTypes, EntityCache } from \"./types.js\";\n\n/**\n * An {@link EntityCache} backed by a `Map`, optionally bounded as a least-recently-used cache.\n */\nexport class MemoryEntityCache<Raw> implements EntityCache<Raw> {\n /**\n * The maximum amount of entries, `Infinity` for an unbounded cache.\n */\n public readonly maxSize: number;\n\n readonly #items = new Map<string, Raw>();\n\n public constructor(maxSize = Infinity) {\n if (maxSize !== Infinity && (!Number.isInteger(maxSize) || maxSize < 0)) {\n throw new RangeError(\n `maxSize must be a non-negative integer or Infinity, received ${maxSize}`,\n );\n }\n\n this.maxSize = maxSize;\n }\n\n public get(key: string): Raw | undefined {\n const value = this.#items.get(key);\n if (value !== undefined && this.maxSize !== Infinity) {\n // Re-insert to mark the entry as the most recently used one.\n this.#items.delete(key);\n this.#items.set(key, value);\n }\n\n return value;\n }\n\n public set(key: string, value: Raw): void {\n if (this.maxSize === 0) return;\n\n this.#items.delete(key);\n this.#items.set(key, value);\n\n if (this.#items.size > this.maxSize) {\n // Maps iterate in insertion order, so the first key is the least recently used one.\n this.#items.delete(this.#items.keys().next().value!);\n }\n }\n\n public has(key: string): boolean {\n return this.#items.has(key);\n }\n\n public delete(key: string): boolean {\n return this.#items.delete(key);\n }\n\n public clear(): void {\n this.#items.clear();\n }\n\n public getSize(): number {\n return this.#items.size;\n }\n\n public keys(): string[] {\n return [...this.#items.keys()];\n }\n\n public values(): Raw[] {\n return [...this.#items.values()];\n }\n\n public entries(): [key: string, value: Raw][] {\n return [...this.#items.entries()];\n }\n}\n\n/**\n * A {@link Cache} whose entity caches are all {@link MemoryEntityCache}s.\n */\nexport type InMemoryCache = {\n readonly [Name in CacheEntityName]: MemoryEntityCache<CacheEntityTypes[Name]>;\n};\n\nexport interface InMemoryCacheOptions {\n /**\n * The maximum amount of entries kept per entity cache before evicting the least recently used ones, either for\n * every entity cache or per entity cache. Entity caches left out are unbounded.\n *\n * @default Infinity\n */\n maxSize?: number | Partial<Record<CacheEntityName, number>>;\n}\n\n/**\n * Creates a {@link Cache} that keeps everything in the process' memory.\n *\n * @example\n * ```typescript\n * import { createInMemoryCache } from '@wolfstar/plugin-cache';\n *\n * // Keep at most 1000 messages around, everything else is unbounded.\n * const cache = createInMemoryCache({ maxSize: { messages: 1_000 } });\n * ```\n *\n * @param options The options for the cache.\n */\nexport function createInMemoryCache(options: InMemoryCacheOptions = {}): InMemoryCache & Cache {\n const { maxSize } = options;\n const resolve = (name: CacheEntityName) =>\n typeof maxSize === \"number\" ? maxSize : (maxSize?.[name] ?? Infinity);\n\n return Object.freeze(\n Object.fromEntries(\n CacheEntityNames.map((name) => [name, new MemoryEntityCache(resolve(name))]),\n ) as InMemoryCache,\n );\n}\n","/// <reference types=\"node\" />\nimport { promisify } from \"node:util\";\nimport { brotliCompress, brotliDecompress, gunzip, gzip } from \"node:zlib\";\nimport {\n CacheEntityNames,\n GuildFieldCacheEntityNames,\n GuildKeyedCacheEntityNames,\n} from \"./operations.js\";\nimport type { Cache, CacheEntityName, CacheEntityTypes, EntityCache } from \"./types.js\";\n\nconst gzipAsync = promisify(gzip);\nconst gunzipAsync = promisify(gunzip);\nconst brotliCompressAsync = promisify(brotliCompress);\nconst brotliDecompressAsync = promisify(brotliDecompress);\n\n/**\n * The subset of the [`ioredis`](https://github.com/redis/ioredis) client API the Redis cache relies on. An `ioredis`\n * `Redis` or `Cluster` instance satisfies it, as can any other client exposing the same commands.\n */\nexport interface RedisClientLike {\n get(key: string): Promise<string | null>;\n mget(...keys: string[]): Promise<(string | null)[]>;\n set(key: string, value: string): Promise<unknown>;\n set(key: string, value: string, mode: \"PX\", milliseconds: number): Promise<unknown>;\n del(...keys: string[]): Promise<number>;\n exists(...keys: string[]): Promise<number>;\n zadd(key: string, ...scoreMembers: (string | number)[]): Promise<unknown>;\n zrem(key: string, ...members: string[]): Promise<number>;\n zrange(key: string, start: string, stop: string): Promise<string[]>;\n zcard(key: string): Promise<number>;\n zremrangebyscore(key: string, min: number | string, max: number | string): Promise<number>;\n multi(): RedisTransactionLike;\n}\n\n/**\n * The subset of an [`ioredis`](https://github.com/redis/ioredis) `MULTI` transaction the Redis cache relies on: every\n * queued command returns the transaction, and `exec` runs them atomically.\n */\nexport interface RedisTransactionLike {\n set(key: string, value: string): RedisTransactionLike;\n set(key: string, value: string, mode: \"PX\", milliseconds: number): RedisTransactionLike;\n del(...keys: string[]): RedisTransactionLike;\n zadd(key: string, ...scoreMembers: (string | number)[]): RedisTransactionLike;\n zrem(key: string, ...members: string[]): RedisTransactionLike;\n zremrangebyscore(key: string, min: number | string, max: number | string): RedisTransactionLike;\n /**\n * Optional: when available (`ioredis` has it), the guild indexes of a cache with a `ttl` expire once their guild\n * stops being written to. Without it, they are only pruned by the next write to their guild.\n */\n pexpire?(key: string, milliseconds: number): RedisTransactionLike;\n exec(): Promise<[error: Error | null, result: unknown][] | null>;\n}\n\n/**\n * Thrown when a value stored in Redis cannot be read back: invalid JSON, or compressed bytes that fail to decompress.\n *\n * @remarks\n * A missing value is not an error, `get` resolves to `undefined` for it. Redis connection errors are not wrapped\n * either, they propagate as the client throws them.\n */\nexport class CacheValueError extends Error {\n /**\n * The Redis key holding the unreadable value.\n */\n public readonly key: string;\n\n public constructor(key: string, cause: unknown) {\n super(`Cannot read the cached value at \"${key}\"`, { cause });\n this.name = \"CacheValueError\";\n this.key = key;\n }\n}\n\n/**\n * The algorithm used to compress values before writing them to Redis.\n */\nexport type RedisCacheCompression = \"gzip\" | \"brotli\" | \"none\";\n\nexport interface RedisEntityCacheOptions<Raw = unknown> {\n /**\n * The prefix of every Redis key owned by this entity cache.\n */\n prefix: string;\n /**\n * The time-to-live of every entry, in seconds. Entries never expire when omitted.\n */\n ttl?: number;\n /**\n * The compression algorithm to use.\n *\n * @default \"none\"\n */\n compression?: RedisCacheCompression;\n /**\n * The minimum size, in bytes, a serialized value must reach to be compressed. Small payloads rarely benefit from it.\n *\n * @default 1024\n */\n compressionThreshold?: number;\n /**\n * Resolves the guild an entry belongs to, indexing the entries by guild so {@link RedisEntityCache.deleteGuild}\n * does not have to scan the whole cache. Entries it resolves no guild for are not indexed. Without it, nothing is\n * indexed and `deleteGuild` resolves to `null`.\n *\n * On delete, it is first called without the value: when the key alone gives the guild away, the value is not\n * read back.\n */\n guildOf?: (key: string, value?: Raw) => string | undefined;\n}\n\n// The most keys a single `DEL`/`ZREM` of `deleteGuild` sends, so a large guild does not make one huge command.\nconst DeleteGuildChunkSize = 500;\n\n// Compressed values are stored as `<marker><base64>`. JSON can never start with either marker, so values written with\n// a different `compression` setting (e.g. before it was turned on) are still read back correctly.\nconst CompressionMarkers = { gzip: \"gz:\", brotli: \"br:\" } as const;\n\n/**\n * An {@link EntityCache} backed by Redis.\n *\n * @remarks\n * Every entry is stored as its own string key (`<prefix>:<key>`), and a sorted set (`<prefix>:@index`) tracks the\n * stored keys with their expiration time as score, which is what `keys`, `entries`, `getSize`, and `clear` read from.\n *\n * With {@link RedisEntityCacheOptions.guildOf}, one more sorted set per guild (`<prefix>:@guild:<guildId>`) tracks the\n * keys of that guild's entries the same way, and `<prefix>:@guilds` lists the guilds having one, for `clear`.\n */\nexport class RedisEntityCache<Raw> implements EntityCache<Raw> {\n public readonly prefix: string;\n public readonly ttl: number | undefined;\n public readonly compression: RedisCacheCompression;\n public readonly compressionThreshold: number;\n\n readonly #redis: RedisClientLike;\n readonly #guildOf: ((key: string, value?: Raw) => string | undefined) | undefined;\n\n public constructor(redis: RedisClientLike, options: RedisEntityCacheOptions<Raw>) {\n if (options.ttl !== undefined && !(options.ttl > 0)) {\n throw new RangeError(`ttl must be a positive amount of seconds, received ${options.ttl}`);\n }\n\n this.#redis = redis;\n this.prefix = options.prefix;\n this.ttl = options.ttl;\n this.compression = options.compression ?? \"none\";\n this.compressionThreshold = options.compressionThreshold ?? 1024;\n this.#guildOf = options.guildOf;\n }\n\n public async get(key: string): Promise<Raw | undefined> {\n const valueKey = this.valueKey(key);\n const value = await this.#redis.get(valueKey);\n return value === null ? undefined : this.deserialize(valueKey, value);\n }\n\n public async set(key: string, value: Raw): Promise<void> {\n const serialized = await this.serialize(value);\n const guildId = this.#guildOf?.(key, value);\n // The value and its index entries are written in one transaction, so neither can exist without the other.\n const transaction = this.#redis.multi();\n if (this.ttl === undefined) {\n transaction.set(this.valueKey(key), serialized).zadd(this.indexKey, \"+inf\", key);\n if (guildId !== undefined) {\n transaction\n .zadd(this.guildIndexKey(guildId), \"+inf\", key)\n .zadd(this.guildsKey, \"+inf\", guildId);\n }\n } else {\n const now = Date.now();\n const milliseconds = Math.round(this.ttl * 1000);\n transaction\n .set(this.valueKey(key), serialized, \"PX\", milliseconds)\n .zadd(this.indexKey, now + milliseconds, key)\n // Pruning on every write keeps the index bounded even when nothing ever enumerates it.\n .zremrangebyscore(this.indexKey, \"-inf\", now);\n if (guildId !== undefined) {\n const guildIndexKey = this.guildIndexKey(guildId);\n transaction\n .zadd(guildIndexKey, now + milliseconds, key)\n .zremrangebyscore(guildIndexKey, \"-inf\", now)\n .zadd(this.guildsKey, now + milliseconds, guildId)\n .zremrangebyscore(this.guildsKey, \"-inf\", now);\n // Every write pushes the expiration back, so the indexes outlive every entry they list.\n transaction.pexpire?.(guildIndexKey, milliseconds);\n transaction.pexpire?.(this.guildsKey, milliseconds);\n }\n }\n\n await execute(transaction);\n }\n\n public async has(key: string): Promise<boolean> {\n return (await this.#redis.exists(this.valueKey(key))) > 0;\n }\n\n public async delete(key: string): Promise<boolean> {\n const guildId = await this.readGuild(key);\n const transaction = this.#redis.multi().del(this.valueKey(key)).zrem(this.indexKey, key);\n if (guildId !== undefined) transaction.zrem(this.guildIndexKey(guildId), key);\n\n const [deleted] = await execute(transaction);\n return (deleted as number) > 0;\n }\n\n public async clear(): Promise<void> {\n const keys = await this.#redis.zrange(this.indexKey, \"0\", \"-1\");\n if (keys.length > 0) await this.#redis.del(...keys.map((key) => this.valueKey(key)));\n\n const guilds = this.#guildOf ? await this.#redis.zrange(this.guildsKey, \"0\", \"-1\") : [];\n await this.#redis.del(\n this.indexKey,\n this.guildsKey,\n ...guilds.map((guildId) => this.guildIndexKey(guildId)),\n );\n }\n\n /**\n * Deletes every entry of a guild, reading their keys from the guild's index rather than scanning the cache.\n *\n * @remarks\n * Only indexed entries are deleted: when `guildOf` is set on a cache already holding entries, the ones written\n * before are left behind until they expire or the cache is cleared.\n *\n * @param guildId The ID of the guild.\n * @returns The amount of deleted entries, or `null` without {@link RedisEntityCacheOptions.guildOf}.\n */\n public async deleteGuild(guildId: string): Promise<number | null> {\n if (this.#guildOf === undefined) return null;\n\n const guildIndexKey = this.guildIndexKey(guildId);\n const keys = await this.#redis.zrange(guildIndexKey, \"0\", \"-1\");\n let deleted = 0;\n for (let index = 0; index < keys.length; index += DeleteGuildChunkSize) {\n const chunk = keys.slice(index, index + DeleteGuildChunkSize);\n // Only the listed keys leave the guild index: entries written meanwhile stay indexed.\n const [count] = await execute(\n this.#redis\n .multi()\n .del(...chunk.map((key) => this.valueKey(key)))\n .zrem(this.indexKey, ...chunk)\n .zrem(guildIndexKey, ...chunk),\n );\n deleted += count as number;\n }\n\n await this.#redis.zrem(this.guildsKey, guildId);\n return deleted;\n }\n\n public async getSize(): Promise<number> {\n await this.prune();\n return this.#redis.zcard(this.indexKey);\n }\n\n public async keys(): Promise<string[]> {\n await this.prune();\n return this.#redis.zrange(this.indexKey, \"0\", \"-1\");\n }\n\n public async values(): Promise<Raw[]> {\n return (await this.entries()).map(([, value]) => value);\n }\n\n public async entries(): Promise<[key: string, value: Raw][]> {\n const keys = await this.keys();\n if (keys.length === 0) return [];\n\n const values = await this.#redis.mget(...keys.map((key) => this.valueKey(key)));\n const entries: [key: string, value: Raw][] = [];\n for (const [index, value] of values.entries()) {\n // The value may have been evicted by Redis between both reads.\n if (value !== null) {\n const key = keys[index]!;\n entries.push([key, await this.deserialize(this.valueKey(key), value)]);\n }\n }\n\n return entries;\n }\n\n /**\n * Gets the Redis key a value is stored at.\n * @param key The entity cache key.\n */\n public valueKey(key: string): string {\n return `${this.prefix}:${key}`;\n }\n\n /**\n * The Redis key of the sorted set indexing the stored keys.\n */\n public get indexKey(): string {\n return `${this.prefix}:@index`;\n }\n\n /**\n * Gets the Redis key of the sorted set indexing a guild's entries.\n * @param guildId The ID of the guild.\n */\n public guildIndexKey(guildId: string): string {\n return `${this.prefix}:@guild:${guildId}`;\n }\n\n /**\n * The Redis key of the sorted set listing the guilds having an index.\n */\n public get guildsKey(): string {\n return `${this.prefix}:@guilds`;\n }\n\n /**\n * Reads the guild of a stored entry, to remove the entry from its guild index.\n * @param key The entity cache key.\n */\n private async readGuild(key: string): Promise<string | undefined> {\n if (this.#guildOf === undefined) return undefined;\n\n // Most guild-scoped keys start with the guild ID, sparing a read (and a decompression) on every delete.\n const fromKey = this.#guildOf(key);\n if (fromKey !== undefined) return fromKey;\n\n let value: Raw | undefined;\n try {\n value = await this.get(key);\n } catch (error) {\n // An unreadable value is deleted all the same, it only stays listed in its guild index.\n if (error instanceof CacheValueError) return undefined;\n throw error;\n }\n\n return value === undefined ? undefined : this.#guildOf(key, value);\n }\n\n private async prune(): Promise<void> {\n if (this.ttl !== undefined)\n await this.#redis.zremrangebyscore(this.indexKey, \"-inf\", Date.now());\n }\n\n private async serialize(value: Raw): Promise<string> {\n const json = JSON.stringify(value);\n if (this.compression === \"none\" || Buffer.byteLength(json) < this.compressionThreshold) {\n return json;\n }\n\n const compressed =\n this.compression === \"gzip\" ? await gzipAsync(json) : await brotliCompressAsync(json);\n return `${CompressionMarkers[this.compression]}${compressed.toString(\"base64\")}`;\n }\n\n private async deserialize(valueKey: string, value: string): Promise<Raw> {\n try {\n if (value.startsWith(CompressionMarkers.gzip)) {\n return JSON.parse((await gunzipAsync(decode(value))).toString(\"utf8\")) as Raw;\n }\n\n if (value.startsWith(CompressionMarkers.brotli)) {\n return JSON.parse((await brotliDecompressAsync(decode(value))).toString(\"utf8\")) as Raw;\n }\n\n return JSON.parse(value) as Raw;\n } catch (error) {\n throw new CacheValueError(valueKey, error);\n }\n }\n}\n\n/**\n * Runs a transaction, throwing the first command error, and resolves to the command results.\n */\nasync function execute(transaction: RedisTransactionLike): Promise<unknown[]> {\n const results = await transaction.exec();\n if (results === null) throw new Error(\"The Redis transaction was aborted\");\n\n return results.map(([error, result]) => {\n if (error) throw error;\n return result;\n });\n}\n\nfunction decode(value: string): Buffer {\n return Buffer.from(value.slice(3), \"base64\");\n}\n\n/**\n * A {@link Cache} whose entity caches are all {@link RedisEntityCache}s.\n */\nexport type RedisCache = {\n readonly [Name in CacheEntityName]: RedisEntityCache<CacheEntityTypes[Name]>;\n};\n\nexport interface RedisCacheOptions {\n /**\n * The Redis client to use, e.g. an [`ioredis`](https://github.com/redis/ioredis) instance.\n */\n redis: RedisClientLike;\n /**\n * The prefix of every Redis key owned by the cache, which allows several caches to share a database.\n *\n * @default \"wolfstar:cache\"\n */\n prefix?: string;\n /**\n * The compression algorithm to use for values of at least {@link RedisCacheOptions.compressionThreshold} bytes.\n *\n * @default \"none\"\n */\n compression?: RedisCacheCompression;\n /**\n * The minimum size, in bytes, a serialized value must reach to be compressed.\n *\n * @default 1024\n */\n compressionThreshold?: number;\n /**\n * The time-to-live per entity cache, in seconds. Entity caches left out never expire.\n */\n ttl?: Partial<Record<CacheEntityName, number>>;\n /**\n * Whether to index the guild-scoped entity caches by guild, so a `GUILD_DELETE` drops a guild's entries without\n * scanning every entity cache. It costs one more sorted-set write per write, and a read before every delete.\n *\n * @remarks\n * Entries written while it was off are not indexed: turning it on for a populated cache leaves them behind on\n * `GUILD_DELETE`, until they expire or the cache is cleared.\n *\n * @default true\n */\n indexGuilds?: boolean;\n}\n\n/**\n * The guild of an entry keyed by its guild (`${guildId}:...`), matching what the `deletePrefix` scan drops.\n */\nfunction guildOfKey(key: string, value?: unknown): string | undefined {\n const separator = key.indexOf(\":\");\n return separator > 0 ? key.slice(0, separator) : guildOfField(key, value);\n}\n\n/**\n * The guild of an entry keyed by its own ID, matching what the `deleteWhere` scan over `guild_id` drops.\n */\nfunction guildOfField(_key: string, value?: unknown): string | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const guildId = (value as { guild_id?: unknown }).guild_id;\n return typeof guildId === \"string\" ? guildId : undefined;\n}\n\nconst GuildResolvers: Partial<\n Record<CacheEntityName, (key: string, value?: unknown) => string | undefined>\n> = Object.fromEntries([\n ...GuildKeyedCacheEntityNames.map((name) => [name, guildOfKey] as const),\n ...GuildFieldCacheEntityNames.map((name) => [name, guildOfField] as const),\n]);\n\n/**\n * The default prefix of every Redis key owned by a cache created with {@link createRedisCache}.\n */\nexport const DefaultRedisCachePrefix = \"wolfstar:cache\";\n\n/**\n * Creates a {@link Cache} stored in Redis, optionally compressing its values.\n *\n * @example\n * ```typescript\n * import { createRedisCache } from '@wolfstar/plugin-cache';\n * import { Redis } from 'ioredis';\n *\n * const cache = createRedisCache({\n * redis: new Redis(process.env.REDIS_URL!),\n * compression: 'gzip',\n * ttl: { guilds: 60 * 60, users: 30 * 60 },\n * });\n * ```\n *\n * @param options The options for the cache.\n */\nexport function createRedisCache(options: RedisCacheOptions): RedisCache & Cache {\n const {\n redis,\n prefix = DefaultRedisCachePrefix,\n compression,\n compressionThreshold,\n ttl,\n indexGuilds = true,\n } = options;\n\n return Object.freeze(\n Object.fromEntries(\n CacheEntityNames.map((name) => [\n name,\n new RedisEntityCache(redis, {\n prefix: `${prefix}:${name}`,\n ttl: ttl?.[name],\n compression,\n compressionThreshold,\n guildOf: indexGuilds ? GuildResolvers[name] : undefined,\n }),\n ]),\n ) as RedisCache,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/lib/keys.ts","../../src/lib/reactions.ts","../../src/lib/operations.ts","../../src/lib/gateway.ts","../../src/lib/memory.ts","../../src/lib/redis.ts"],"sourcesContent":["import type { Snowflake } from \"discord-api-types/v10\";\n\n/**\n * Creates a stable key for a guild-scoped entity.\n */\nexport function guildScopedKey(guildId: Snowflake, id: Snowflake): string {\n return `${guildId}:${id}`;\n}\n\n/**\n * Creates a stable key for a channel message.\n */\nexport function messageKey(channelId: Snowflake, messageId: Snowflake): string {\n return `${channelId}:${messageId}`;\n}\n\n/**\n * Creates a stable key for a guild member.\n */\nexport function memberKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild presence.\n */\nexport function presenceKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild voice state.\n */\nexport function voiceStateKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild role.\n */\nexport function roleKey(guildId: Snowflake, roleId: Snowflake): string {\n return guildScopedKey(guildId, roleId);\n}\n\n/**\n * Creates a stable key for a guild emoji.\n */\nexport function emojiKey(guildId: Snowflake, emojiId: Snowflake): string {\n return guildScopedKey(guildId, emojiId);\n}\n\n/**\n * Creates a stable key for a guild sticker.\n */\nexport function stickerKey(guildId: Snowflake, stickerId: Snowflake): string {\n return guildScopedKey(guildId, stickerId);\n}\n\n/**\n * Creates a stable key for a guild scheduled event.\n */\nexport function scheduledEventKey(guildId: Snowflake, scheduledEventId: Snowflake): string {\n return guildScopedKey(guildId, scheduledEventId);\n}\n\n/**\n * Creates a stable key for a stage instance.\n */\nexport function stageInstanceKey(guildId: Snowflake, channelId: Snowflake): string {\n return guildScopedKey(guildId, channelId);\n}\n\n/**\n * Creates a stable key for a guild soundboard sound.\n */\nexport function soundboardSoundKey(guildId: Snowflake, soundId: Snowflake): string {\n return guildScopedKey(guildId, soundId);\n}\n\n/**\n * Creates a stable key for a guild auto moderation rule.\n */\nexport function autoModerationRuleKey(guildId: Snowflake, ruleId: Snowflake): string {\n return guildScopedKey(guildId, ruleId);\n}\n\n/**\n * Creates a stable key for a guild ban.\n */\nexport function banKey(guildId: Snowflake, userId: Snowflake): string {\n return guildScopedKey(guildId, userId);\n}\n\n/**\n * Creates a stable key for a guild integration.\n */\nexport function integrationKey(guildId: Snowflake, integrationId: Snowflake): string {\n return guildScopedKey(guildId, integrationId);\n}\n\n/**\n * Creates a stable key for an invite, using `@global` for invites that do not belong to a guild.\n */\nexport function inviteKey(guildId: Snowflake | null | undefined, code: string): string {\n return `${guildId ?? \"@global\"}:${code}`;\n}\n\n/**\n * Creates a stable key for a thread member.\n */\nexport function threadMemberKey(threadId: Snowflake, userId: Snowflake): string {\n return `${threadId}:${userId}`;\n}\n\n/**\n * Creates a stable key for application command permissions.\n */\nexport function applicationCommandPermissionsKey(\n applicationId: Snowflake,\n guildId: Snowflake,\n commandId: Snowflake,\n): string {\n return `${applicationId}:${guildId}:${commandId}`;\n}\n","import type {\n APIMessage,\n APIPartialEmoji,\n APIReaction,\n GatewayMessagePollVoteDispatchData,\n GatewayMessageReactionAddDispatchData,\n GatewayMessageReactionRemoveDispatchData,\n} from \"discord-api-types/v10\";\n\n// Reactions and poll votes only carry the voter's ID, so whether the bot is the voter needs the bot's own ID.\n\n/**\n * Whether two reaction emojis are the same: custom emojis match by ID, Unicode emojis by name.\n */\nfunction isSameEmoji(a: APIPartialEmoji, b: APIPartialEmoji): boolean {\n return a.id ? a.id === b.id : !b.id && a.name === b.name;\n}\n\n/**\n * Adds a `MESSAGE_REACTION_ADD` to a cached message's reactions.\n *\n * @param message The cached message.\n * @param data The dispatch data.\n * @param clientUserId The bot's user ID, to set `me` when the bot reacted.\n */\nexport function addReaction(\n message: APIMessage,\n data: GatewayMessageReactionAddDispatchData,\n clientUserId?: string,\n): APIMessage {\n const me = data.user_id === clientUserId;\n const kind = data.burst ? \"burst\" : \"normal\";\n const reactions = message.reactions ?? [];\n const existing = reactions.find((reaction) => isSameEmoji(reaction.emoji, data.emoji));\n\n if (!existing) {\n const reaction: APIReaction = {\n emoji: data.emoji,\n count: 1,\n count_details: { normal: data.burst ? 0 : 1, burst: data.burst ? 1 : 0 },\n me: me && !data.burst,\n me_burst: me && data.burst,\n burst_colors: data.burst_colors ?? [],\n };\n return { ...message, reactions: [...reactions, reaction] };\n }\n\n return {\n ...message,\n reactions: reactions.map((reaction) =>\n reaction === existing\n ? {\n ...reaction,\n count: reaction.count + 1,\n count_details: {\n ...reaction.count_details,\n [kind]: reaction.count_details[kind] + 1,\n },\n me: reaction.me || (me && !data.burst),\n me_burst: reaction.me_burst || (me && data.burst),\n burst_colors: data.burst\n ? (data.burst_colors ?? reaction.burst_colors)\n : reaction.burst_colors,\n }\n : reaction,\n ),\n };\n}\n\n/**\n * Removes a `MESSAGE_REACTION_REMOVE` from a cached message's reactions, dropping the reaction when its count reaches\n * zero.\n *\n * @param message The cached message.\n * @param data The dispatch data.\n * @param clientUserId The bot's user ID, to clear `me` when the bot's reaction was removed.\n */\nexport function removeReaction(\n message: APIMessage,\n data: GatewayMessageReactionRemoveDispatchData,\n clientUserId?: string,\n): APIMessage {\n const me = data.user_id === clientUserId;\n const kind = data.burst ? \"burst\" : \"normal\";\n const reactions = (message.reactions ?? []).flatMap((reaction) => {\n if (!isSameEmoji(reaction.emoji, data.emoji)) return [reaction];\n if (reaction.count <= 1) return [];\n return [\n {\n ...reaction,\n count: reaction.count - 1,\n count_details: {\n ...reaction.count_details,\n [kind]: Math.max(0, reaction.count_details[kind] - 1),\n },\n me: reaction.me && !(me && !data.burst),\n me_burst: reaction.me_burst && !(me && data.burst),\n },\n ];\n });\n return { ...message, reactions };\n}\n\n/**\n * Removes every reaction with one emoji from a cached message.\n *\n * @param message The cached message.\n * @param emoji The emoji.\n */\nexport function removeReactionEmoji(message: APIMessage, emoji: APIPartialEmoji): APIMessage {\n return {\n ...message,\n reactions: (message.reactions ?? []).filter((reaction) => !isSameEmoji(reaction.emoji, emoji)),\n };\n}\n\n/**\n * Counts a `MESSAGE_POLL_VOTE_ADD` or `MESSAGE_POLL_VOTE_REMOVE` in a cached message's poll results.\n *\n * @param message The cached message.\n * @param data The dispatch data.\n * @param delta `1` for a vote, `-1` for a removed vote.\n * @param clientUserId The bot's user ID, to set `me_voted` when the bot voted.\n */\nexport function countPollVote(\n message: APIMessage,\n data: GatewayMessagePollVoteDispatchData,\n delta: 1 | -1,\n clientUserId?: string,\n): APIMessage {\n const { poll } = message;\n if (!poll) return message;\n\n const me = data.user_id === clientUserId;\n const results = poll.results ?? { is_finalized: false, answer_counts: [] };\n const counts = results.answer_counts.some((count) => count.id === data.answer_id)\n ? results.answer_counts\n : [...results.answer_counts, { id: data.answer_id, count: 0, me_voted: false }];\n\n return {\n ...message,\n poll: {\n ...poll,\n results: {\n ...results,\n answer_counts: counts.map((count) =>\n count.id === data.answer_id\n ? {\n ...count,\n count: Math.max(0, count.count + delta),\n me_voted: me ? delta === 1 : count.me_voted,\n }\n : count,\n ),\n },\n },\n };\n}\n","import { GatewayDispatchEvents } from \"discord-api-types/v10\";\nimport type {\n APIEmoji,\n APIMessage,\n APIGuildMember,\n APIRole,\n APISoundboardSound,\n APISticker,\n GatewayDispatchPayload,\n GatewayGuildCreateDispatchData,\n GatewayGuildMembersChunkDispatchData,\n GatewayGuildSoundboardSoundsUpdateDispatchData,\n GatewaySoundboardSoundsDispatchData,\n GatewayThreadListSync,\n GatewayThreadMembersUpdateDispatchData,\n Snowflake,\n} from \"discord-api-types/v10\";\nimport {\n applicationCommandPermissionsKey,\n autoModerationRuleKey,\n banKey,\n emojiKey,\n guildScopedKey,\n integrationKey,\n inviteKey,\n memberKey,\n messageKey,\n presenceKey,\n roleKey,\n scheduledEventKey,\n soundboardSoundKey,\n stageInstanceKey,\n stickerKey,\n threadMemberKey,\n voiceStateKey,\n} from \"./keys.js\";\nimport { addReaction, countPollVote, removeReaction, removeReactionEmoji } from \"./reactions.js\";\nimport type { Cache, CacheEntityName, EntityCache } from \"./types.js\";\n\n// A record rather than an array so the compiler enforces that every entity cache is listed.\nconst cacheEntityNameRecord: Record<CacheEntityName, true> = {\n applicationCommandPermissions: true,\n auditLogEntries: true,\n autoModerationRules: true,\n bans: true,\n channels: true,\n emojis: true,\n entitlements: true,\n guilds: true,\n integrations: true,\n invites: true,\n members: true,\n messages: true,\n presences: true,\n roles: true,\n scheduledEvents: true,\n soundboardSounds: true,\n stageInstances: true,\n stickers: true,\n subscriptions: true,\n threadMembers: true,\n threads: true,\n users: true,\n voiceStates: true,\n};\n\n/**\n * The name of every entity cache held by a {@link Cache}.\n */\nexport const CacheEntityNames = Object.keys(cacheEntityNameRecord) as readonly CacheEntityName[];\n\n/**\n * The entity caches whose keys start with the ID of the guild the entity belongs to (`${guildId}:...`).\n *\n * @internal\n */\nexport const GuildKeyedCacheEntityNames = [\n \"auditLogEntries\",\n \"autoModerationRules\",\n \"bans\",\n \"emojis\",\n \"integrations\",\n \"members\",\n \"presences\",\n \"roles\",\n \"scheduledEvents\",\n \"soundboardSounds\",\n \"stageInstances\",\n \"stickers\",\n \"voiceStates\",\n] as const satisfies readonly CacheEntityName[];\n\n/**\n * The entity caches keyed by the entity's own ID, which only know their guild through the stored `guild_id`.\n *\n * @internal\n */\nexport const GuildFieldCacheEntityNames = [\n \"applicationCommandPermissions\",\n \"channels\",\n \"threads\",\n \"threadMembers\",\n \"messages\",\n \"invites\",\n] as const satisfies readonly CacheEntityName[];\n\n/**\n * A single mutation a gateway dispatch produces on a {@link Cache}.\n */\nexport type CacheOperation =\n | {\n type: \"upsert\";\n store: CacheEntityName;\n key: string;\n raw: unknown;\n /** Whether to shallow-merge `raw` onto the existing value, used for partial updates. */\n merge?: boolean;\n }\n | {\n type: \"update\";\n store: CacheEntityName;\n key: string;\n /** Computes the new value from the cached one. Nothing is written when the key is not cached. */\n update: (value: unknown) => unknown;\n }\n | { type: \"delete\"; store: CacheEntityName; key: string }\n | {\n type: \"deletePrefix\";\n store: CacheEntityName;\n prefix: string;\n /** The guild the deleted entries are exactly the entries of, see {@link EntityCache.deleteGuild}. */\n guildId?: Snowflake;\n }\n | {\n type: \"deleteWhere\";\n store: CacheEntityName;\n predicate: (value: unknown) => boolean;\n /** The guild the deleted entries are exactly the entries of, see {@link EntityCache.deleteGuild}. */\n guildId?: Snowflake;\n };\n\n/**\n * What {@link createCacheOperations} needs to know besides the dispatch.\n */\nexport interface CacheOperationContext {\n /**\n * The bot's user ID. Reactions and poll votes only carry the voter's ID, so without it the cached `me` and\n * `me_voted` flags are never set.\n */\n clientUserId?: string;\n}\n\n/**\n * Translates a gateway dispatch into the list of {@link CacheOperation}s it implies, including the cascades (e.g.\n * `CHANNEL_DELETE` also drops that channel's messages).\n *\n * @remarks\n * This is a pure function, it does not touch any cache. Use {@link applyGatewayDispatch} to apply them.\n * `INTERACTION_CREATE` is intentionally ignored: interactions are short-lived and never cached.\n *\n * @param payload The gateway dispatch payload.\n * @param context The bot's user ID, for the `me` flags of reactions and poll votes.\n */\nexport function createCacheOperations(\n payload: GatewayDispatchPayload,\n context: CacheOperationContext = {},\n): CacheOperation[] {\n const { clientUserId } = context;\n const operations: CacheOperation[] = [];\n\n switch (payload.t) {\n case GatewayDispatchEvents.ApplicationCommandPermissionsUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"applicationCommandPermissions\",\n key: applicationCommandPermissionsKey(data.application_id, data.guild_id, data.id),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.AutoModerationRuleCreate:\n case GatewayDispatchEvents.AutoModerationRuleUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"autoModerationRules\",\n key: autoModerationRuleKey(data.guild_id, data.id),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.AutoModerationRuleDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"autoModerationRules\",\n key: autoModerationRuleKey(data.guild_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.ChannelCreate:\n case GatewayDispatchEvents.ChannelUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"channels\",\n key: data.id,\n raw: data,\n merge: payload.t === GatewayDispatchEvents.ChannelUpdate,\n });\n break;\n }\n\n case GatewayDispatchEvents.ChannelDelete: {\n const data = payload.d;\n operations.push({ type: \"delete\", store: \"channels\", key: data.id });\n operations.push({ type: \"deletePrefix\", store: \"messages\", prefix: `${data.id}:` });\n break;\n }\n\n case GatewayDispatchEvents.EntitlementCreate:\n case GatewayDispatchEvents.EntitlementUpdate: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"entitlements\", key: data.id, raw: data });\n break;\n }\n\n case GatewayDispatchEvents.EntitlementDelete: {\n operations.push({ type: \"delete\", store: \"entitlements\", key: payload.d.id });\n break;\n }\n\n case GatewayDispatchEvents.GuildAuditLogEntryCreate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"auditLogEntries\",\n key: guildScopedKey(data.guild_id, data.id),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildBanAdd: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"bans\",\n key: banKey(data.guild_id, data.user.id),\n raw: data,\n });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildBanRemove: {\n const data = payload.d;\n operations.push({ type: \"delete\", store: \"bans\", key: banKey(data.guild_id, data.user.id) });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildCreate: {\n hydrateGuildCreate(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.GuildUpdate: {\n // Like `GUILD_CREATE`, the roles and emojis go to their own entity caches rather than the guild entry.\n const { roles, emojis, stickers, ...data } = payload.d;\n operations.push({ type: \"upsert\", store: \"guilds\", key: data.id, raw: data, merge: true });\n for (const role of roles ?? []) hydrateRole(operations, data.id, role);\n for (const emoji of emojis ?? []) hydrateEmoji(operations, data.id, emoji);\n for (const sticker of stickers ?? []) hydrateSticker(operations, data.id, sticker);\n break;\n }\n\n case GatewayDispatchEvents.GuildDelete: {\n const data = payload.d;\n // An `unavailable` guild is an outage, not a removal: keep its data around until it comes back.\n if (data.unavailable) {\n operations.push({ type: \"upsert\", store: \"guilds\", key: data.id, raw: data, merge: true });\n break;\n }\n\n operations.push({ type: \"delete\", store: \"guilds\", key: data.id });\n deleteGuildScopedResources(operations, data.id);\n break;\n }\n\n case GatewayDispatchEvents.GuildEmojisUpdate: {\n const data = payload.d;\n operations.push({\n type: \"deletePrefix\",\n store: \"emojis\",\n prefix: `${data.guild_id}:`,\n guildId: data.guild_id,\n });\n for (const emoji of data.emojis) {\n if (!emoji.id) continue;\n operations.push({\n type: \"upsert\",\n store: \"emojis\",\n key: emojiKey(data.guild_id, emoji.id),\n raw: withGuildId(emoji, data.guild_id),\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.GuildStickersUpdate: {\n const data = payload.d;\n operations.push({\n type: \"deletePrefix\",\n store: \"stickers\",\n prefix: `${data.guild_id}:`,\n guildId: data.guild_id,\n });\n for (const sticker of data.stickers) {\n operations.push({\n type: \"upsert\",\n store: \"stickers\",\n key: stickerKey(data.guild_id, sticker.id),\n raw: withGuildId(sticker, data.guild_id),\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.GuildMemberAdd:\n case GatewayDispatchEvents.GuildMemberUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, data.user.id),\n raw: data,\n merge: true,\n });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildMemberRemove: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"members\",\n key: memberKey(data.guild_id, data.user.id),\n });\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n break;\n }\n\n case GatewayDispatchEvents.GuildMembersChunk: {\n hydrateGuildMembersChunk(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.GuildRoleCreate:\n case GatewayDispatchEvents.GuildRoleUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"roles\",\n key: roleKey(data.guild_id, data.role.id),\n raw: withGuildId(data.role, data.guild_id),\n merge: payload.t === GatewayDispatchEvents.GuildRoleUpdate,\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildRoleDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"roles\",\n key: roleKey(data.guild_id, data.role_id),\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildScheduledEventCreate:\n case GatewayDispatchEvents.GuildScheduledEventUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"scheduledEvents\",\n key: scheduledEventKey(data.guild_id, data.id),\n raw: data,\n merge: true,\n });\n if (data.creator) {\n operations.push({\n type: \"upsert\",\n store: \"users\",\n key: data.creator.id,\n raw: data.creator,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.GuildScheduledEventDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"scheduledEvents\",\n key: scheduledEventKey(data.guild_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildSoundboardSoundCreate:\n case GatewayDispatchEvents.GuildSoundboardSoundUpdate: {\n const data = payload.d;\n if (!data.guild_id) break;\n operations.push({\n type: \"upsert\",\n store: \"soundboardSounds\",\n key: soundboardSoundKey(data.guild_id, data.sound_id),\n raw: data,\n merge: payload.t === GatewayDispatchEvents.GuildSoundboardSoundUpdate,\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildSoundboardSoundDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"soundboardSounds\",\n key: soundboardSoundKey(data.guild_id, data.sound_id),\n });\n break;\n }\n\n case GatewayDispatchEvents.GuildSoundboardSoundsUpdate: {\n hydrateGuildSoundboardSoundsUpdate(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.SoundboardSounds: {\n hydrateSoundboardSounds(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.IntegrationCreate:\n case GatewayDispatchEvents.IntegrationUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"integrations\",\n key: integrationKey(data.guild_id, data.id),\n raw: data,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.IntegrationDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"integrations\",\n key: integrationKey(data.guild_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.InviteCreate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"invites\",\n key: inviteKey(data.guild_id, data.code),\n raw: data,\n });\n break;\n }\n\n case GatewayDispatchEvents.InviteDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"invites\",\n key: inviteKey(data.guild_id, data.code),\n });\n break;\n }\n\n case GatewayDispatchEvents.MessageCreate:\n case GatewayDispatchEvents.MessageUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"messages\",\n key: messageKey(data.channel_id, data.id),\n raw: data,\n merge: payload.t === GatewayDispatchEvents.MessageUpdate,\n });\n\n const author = \"author\" in data ? data.author : undefined;\n if (author) {\n operations.push({ type: \"upsert\", store: \"users\", key: author.id, raw: author });\n if (data.member && data.guild_id) {\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, author.id),\n raw: withGuildId(data.member, data.guild_id),\n merge: true,\n });\n }\n }\n break;\n }\n\n case GatewayDispatchEvents.MessageDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"messages\",\n key: messageKey(data.channel_id, data.id),\n });\n break;\n }\n\n case GatewayDispatchEvents.MessageDeleteBulk: {\n const data = payload.d;\n for (const id of data.ids) {\n operations.push({\n type: \"delete\",\n store: \"messages\",\n key: messageKey(data.channel_id, id),\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionAdd: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n addReaction(message, data, clientUserId),\n ),\n );\n // Guild reactions carry the reacting member, like messages carry their author.\n const user = data.member?.user;\n if (user && data.guild_id) {\n operations.push({ type: \"upsert\", store: \"users\", key: user.id, raw: user });\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, user.id),\n raw: withGuildId(data.member!, data.guild_id),\n merge: true,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionRemove: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n removeReaction(message, data, clientUserId),\n ),\n );\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionRemoveAll: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) => ({\n ...message,\n reactions: [],\n })),\n );\n break;\n }\n\n case GatewayDispatchEvents.MessageReactionRemoveEmoji: {\n const data = payload.d;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n removeReactionEmoji(message, data.emoji),\n ),\n );\n break;\n }\n\n case GatewayDispatchEvents.MessagePollVoteAdd:\n case GatewayDispatchEvents.MessagePollVoteRemove: {\n const data = payload.d;\n const delta = payload.t === GatewayDispatchEvents.MessagePollVoteAdd ? 1 : -1;\n operations.push(\n updateMessage(data.channel_id, data.message_id, (message) =>\n countPollVote(message, data, delta, clientUserId),\n ),\n );\n break;\n }\n\n case GatewayDispatchEvents.PresenceUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"presences\",\n key: presenceKey(data.guild_id, data.user.id),\n raw: data,\n merge: true,\n });\n operations.push({\n type: \"upsert\",\n store: \"users\",\n key: data.user.id,\n raw: data.user,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.Ready: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"users\", key: data.user.id, raw: data.user });\n for (const guild of data.guilds) {\n operations.push({\n type: \"upsert\",\n store: \"guilds\",\n key: guild.id,\n raw: guild,\n merge: true,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.StageInstanceCreate:\n case GatewayDispatchEvents.StageInstanceUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"stageInstances\",\n key: stageInstanceKey(data.guild_id, data.channel_id),\n raw: data,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.StageInstanceDelete: {\n const data = payload.d;\n operations.push({\n type: \"delete\",\n store: \"stageInstances\",\n key: stageInstanceKey(data.guild_id, data.channel_id),\n });\n break;\n }\n\n case GatewayDispatchEvents.SubscriptionCreate:\n case GatewayDispatchEvents.SubscriptionUpdate: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"subscriptions\", key: data.id, raw: data });\n break;\n }\n\n case GatewayDispatchEvents.SubscriptionDelete: {\n operations.push({ type: \"delete\", store: \"subscriptions\", key: payload.d.id });\n break;\n }\n\n case GatewayDispatchEvents.ThreadCreate:\n case GatewayDispatchEvents.ThreadUpdate: {\n const data = payload.d;\n operations.push({\n type: \"upsert\",\n store: \"threads\",\n key: data.id,\n raw: data,\n merge: payload.t === GatewayDispatchEvents.ThreadUpdate,\n });\n\n const member = \"member\" in data ? data.member : undefined;\n if (member?.user_id) {\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, member.user_id),\n // Thread members carry no guild ID of their own: without it, `GUILD_DELETE` could not sweep them.\n raw: data.guild_id ? withGuildId(member, data.guild_id) : member,\n merge: true,\n });\n }\n break;\n }\n\n case GatewayDispatchEvents.ThreadDelete: {\n const data = payload.d;\n operations.push({ type: \"delete\", store: \"threads\", key: data.id });\n operations.push({ type: \"deletePrefix\", store: \"threadMembers\", prefix: `${data.id}:` });\n operations.push({ type: \"deletePrefix\", store: \"messages\", prefix: `${data.id}:` });\n break;\n }\n\n case GatewayDispatchEvents.ThreadListSync: {\n hydrateThreadListSync(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.ThreadMemberUpdate: {\n const data = payload.d;\n if (!data.id || !data.user_id) break;\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, data.user_id),\n raw: data,\n merge: true,\n });\n break;\n }\n\n case GatewayDispatchEvents.ThreadMembersUpdate: {\n hydrateThreadMembersUpdate(operations, payload.d);\n break;\n }\n\n case GatewayDispatchEvents.UserUpdate: {\n const data = payload.d;\n operations.push({ type: \"upsert\", store: \"users\", key: data.id, raw: data, merge: true });\n break;\n }\n\n case GatewayDispatchEvents.VoiceStateUpdate: {\n const data = payload.d;\n if (!data.guild_id) break;\n\n const key = voiceStateKey(data.guild_id, data.user_id);\n if (data.channel_id) {\n operations.push({ type: \"upsert\", store: \"voiceStates\", key, raw: data, merge: true });\n } else {\n operations.push({ type: \"delete\", store: \"voiceStates\", key });\n }\n\n const user = data.member?.user;\n if (data.member && user) {\n operations.push({ type: \"upsert\", store: \"users\", key: user.id, raw: user });\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(data.guild_id, user.id),\n raw: withGuildId(data.member, data.guild_id),\n merge: true,\n });\n }\n break;\n }\n\n default:\n break;\n }\n\n return operations;\n}\n\n/**\n * Applies a list of {@link CacheOperation}s to a {@link Cache}, sequentially and in order.\n *\n * @param cache The cache to mutate.\n * @param operations The operations to apply, usually created by {@link createCacheOperations}.\n */\nexport async function applyCacheOperations(\n cache: Cache,\n operations: readonly CacheOperation[],\n): Promise<void> {\n for (const operation of operations) {\n const store = cache[operation.store] as EntityCache<unknown>;\n\n switch (operation.type) {\n case \"upsert\": {\n const value = operation.merge\n ? mergeValues(await store.get(operation.key), operation.raw)\n : operation.raw;\n await store.set(operation.key, value);\n break;\n }\n case \"update\": {\n const existing = await store.get(operation.key);\n if (existing !== undefined) await store.set(operation.key, operation.update(existing));\n break;\n }\n case \"delete\":\n await store.delete(operation.key);\n break;\n case \"deletePrefix\":\n if (await deleteGuildThroughIndex(store, operation.guildId)) break;\n for (const key of await store.keys()) {\n if (key.startsWith(operation.prefix)) await store.delete(key);\n }\n break;\n case \"deleteWhere\":\n if (await deleteGuildThroughIndex(store, operation.guildId)) break;\n for (const [key, value] of await store.entries()) {\n if (operation.predicate(value)) await store.delete(key);\n }\n break;\n }\n }\n}\n\n/**\n * Deletes a guild's entries through the store's guild index, when it has one.\n *\n * @returns Whether the entries were deleted, `false` when the caller has to scan the store instead.\n */\nasync function deleteGuildThroughIndex(\n store: EntityCache<unknown>,\n guildId: Snowflake | undefined,\n): Promise<boolean> {\n if (guildId === undefined || store.deleteGuild === undefined) return false;\n return (await store.deleteGuild(guildId)) !== null;\n}\n\n/**\n * Writes a gateway dispatch into every relevant entity cache of a {@link Cache}.\n *\n * @param cache The cache to mutate.\n * @param payload The gateway dispatch payload.\n * @param context The bot's user ID, for the `me` flags of reactions and poll votes.\n */\nexport function applyGatewayDispatch(\n cache: Cache,\n payload: GatewayDispatchPayload,\n context?: CacheOperationContext,\n): Promise<void> {\n return applyCacheOperations(cache, createCacheOperations(payload, context));\n}\n\n/**\n * Shallow-merges `value` onto `existing` when both are plain objects, returning `value` otherwise.\n */\nexport function mergeValues<Value>(existing: Value | undefined, value: Value): Value {\n if (isObject(existing) && isObject(value)) return { ...existing, ...value };\n return value;\n}\n\nfunction hydrateGuildCreate(\n operations: CacheOperation[],\n guild: GatewayGuildCreateDispatchData,\n): void {\n // Unavailable guilds carry no data besides their ID, see `GuildDelete`.\n if (\"unavailable\" in guild && guild.unavailable) {\n operations.push({ type: \"upsert\", store: \"guilds\", key: guild.id, raw: guild, merge: true });\n return;\n }\n\n const {\n channels,\n threads,\n members,\n presences,\n voice_states: voiceStates,\n stage_instances: stageInstances,\n guild_scheduled_events: scheduledEvents,\n soundboard_sounds: soundboardSounds,\n roles,\n emojis,\n stickers,\n ...rest\n } = guild;\n // Collections are stored in their own entity caches, keeping the guild entry small.\n operations.push({ type: \"upsert\", store: \"guilds\", key: guild.id, raw: rest });\n\n for (const channel of channels ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"channels\",\n key: channel.id,\n raw: withGuildId(channel, guild.id),\n });\n }\n\n for (const thread of threads ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"threads\",\n key: thread.id,\n raw: withGuildId(thread, guild.id),\n });\n if (thread.member?.user_id) {\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(thread.id, thread.member.user_id),\n raw: withGuildId(thread.member, guild.id),\n });\n }\n }\n\n for (const member of members ?? []) hydrateMember(operations, guild.id, member);\n\n for (const presence of presences ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"presences\",\n key: presenceKey(guild.id, presence.user.id),\n raw: withGuildId(presence, guild.id),\n });\n }\n\n for (const role of roles ?? []) hydrateRole(operations, guild.id, role);\n for (const emoji of emojis ?? []) hydrateEmoji(operations, guild.id, emoji);\n for (const sticker of stickers ?? []) hydrateSticker(operations, guild.id, sticker);\n\n for (const voiceState of voiceStates ?? []) {\n if (!voiceState.channel_id) continue;\n operations.push({\n type: \"upsert\",\n store: \"voiceStates\",\n key: voiceStateKey(guild.id, voiceState.user_id),\n raw: withGuildId(voiceState, guild.id),\n });\n }\n\n for (const stageInstance of stageInstances ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"stageInstances\",\n key: stageInstanceKey(guild.id, stageInstance.channel_id),\n raw: stageInstance,\n });\n }\n\n for (const scheduledEvent of scheduledEvents ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"scheduledEvents\",\n key: scheduledEventKey(guild.id, scheduledEvent.id),\n raw: scheduledEvent,\n });\n }\n\n for (const sound of soundboardSounds ?? []) hydrateSoundboardSound(operations, guild.id, sound);\n}\n\nfunction hydrateGuildMembersChunk(\n operations: CacheOperation[],\n data: GatewayGuildMembersChunkDispatchData,\n): void {\n for (const member of data.members) hydrateMember(operations, data.guild_id, member);\n for (const presence of data.presences ?? []) {\n operations.push({\n type: \"upsert\",\n store: \"presences\",\n key: presenceKey(data.guild_id, presence.user.id),\n raw: withGuildId(presence, data.guild_id),\n });\n }\n}\n\nfunction hydrateGuildSoundboardSoundsUpdate(\n operations: CacheOperation[],\n data: GatewayGuildSoundboardSoundsUpdateDispatchData,\n): void {\n operations.push({\n type: \"deletePrefix\",\n store: \"soundboardSounds\",\n prefix: `${data.guild_id}:`,\n guildId: data.guild_id,\n });\n for (const sound of data.soundboard_sounds)\n hydrateSoundboardSound(operations, data.guild_id, sound);\n}\n\nfunction hydrateSoundboardSounds(\n operations: CacheOperation[],\n data: GatewaySoundboardSoundsDispatchData,\n): void {\n for (const sound of data.soundboard_sounds)\n hydrateSoundboardSound(operations, data.guild_id, sound);\n}\n\nfunction hydrateThreadListSync(operations: CacheOperation[], data: GatewayThreadListSync): void {\n for (const thread of data.threads) {\n operations.push({\n type: \"upsert\",\n store: \"threads\",\n key: thread.id,\n raw: withGuildId(thread, data.guild_id),\n merge: true,\n });\n }\n\n for (const member of data.members) {\n if (!member.id || !member.user_id) continue;\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(member.id, member.user_id),\n raw: withGuildId(member, data.guild_id),\n merge: true,\n });\n }\n}\n\nfunction hydrateThreadMembersUpdate(\n operations: CacheOperation[],\n data: GatewayThreadMembersUpdateDispatchData,\n): void {\n for (const member of data.added_members ?? []) {\n if (!member.user_id) continue;\n operations.push({\n type: \"upsert\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, member.user_id),\n raw: withGuildId(member, data.guild_id),\n merge: true,\n });\n }\n\n for (const userId of data.removed_member_ids ?? []) {\n operations.push({\n type: \"delete\",\n store: \"threadMembers\",\n key: threadMemberKey(data.id, userId),\n });\n }\n}\n\nfunction hydrateMember(\n operations: CacheOperation[],\n guildId: Snowflake,\n member: APIGuildMember,\n): void {\n if (!member.user) return;\n\n operations.push({\n type: \"upsert\",\n store: \"members\",\n key: memberKey(guildId, member.user.id),\n raw: withGuildId(member, guildId),\n merge: true,\n });\n operations.push({ type: \"upsert\", store: \"users\", key: member.user.id, raw: member.user });\n}\n\nfunction hydrateRole(operations: CacheOperation[], guildId: Snowflake, role: APIRole): void {\n operations.push({\n type: \"upsert\",\n store: \"roles\",\n key: roleKey(guildId, role.id),\n raw: withGuildId(role, guildId),\n });\n}\n\nfunction hydrateEmoji(operations: CacheOperation[], guildId: Snowflake, emoji: APIEmoji): void {\n if (!emoji.id) return;\n operations.push({\n type: \"upsert\",\n store: \"emojis\",\n key: emojiKey(guildId, emoji.id),\n raw: withGuildId(emoji, guildId),\n });\n}\n\nfunction hydrateSticker(\n operations: CacheOperation[],\n guildId: Snowflake,\n sticker: APISticker,\n): void {\n operations.push({\n type: \"upsert\",\n store: \"stickers\",\n key: stickerKey(guildId, sticker.id),\n raw: withGuildId(sticker, guildId),\n });\n}\n\nfunction hydrateSoundboardSound(\n operations: CacheOperation[],\n guildId: Snowflake,\n sound: APISoundboardSound,\n): void {\n operations.push({\n type: \"upsert\",\n store: \"soundboardSounds\",\n key: soundboardSoundKey(guildId, sound.sound_id),\n raw: withGuildId(sound, guildId),\n });\n}\n\nfunction deleteGuildScopedResources(operations: CacheOperation[], guildId: Snowflake): void {\n // Every operation carries the guild, so a store indexing its entries by guild skips the scans below.\n // Entities keyed by `${guildId}:...` can be dropped by prefix, which only reads the keys.\n for (const store of GuildKeyedCacheEntityNames) {\n operations.push({ type: \"deletePrefix\", store, prefix: `${guildId}:`, guildId });\n }\n\n // The rest are keyed by their own ID and need a scan over the stored `guild_id`.\n for (const store of GuildFieldCacheEntityNames) {\n operations.push({\n type: \"deleteWhere\",\n store,\n predicate: (value) => isObject(value) && value.guild_id === guildId,\n guildId,\n });\n }\n}\n\n// An `update` of a cached message; a message the cache does not hold stays uncached.\nfunction updateMessage(\n channelId: Snowflake,\n messageId: Snowflake,\n update: (message: APIMessage) => APIMessage,\n): CacheOperation {\n return {\n type: \"update\",\n store: \"messages\",\n key: messageKey(channelId, messageId),\n update: (value) => update(value as APIMessage),\n };\n}\n\nfunction withGuildId<Value extends object>(\n value: Value,\n guildId: Snowflake,\n): Value & { guild_id: Snowflake } {\n return { ...value, guild_id: guildId };\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import type { GatewayDispatchPayload } from \"discord-api-types/v10\";\nimport { applyGatewayDispatch, type CacheOperationContext } from \"./operations.js\";\nimport type { Cache } from \"./types.js\";\n\n/** A gateway that emits the dispatch event used by @discordjs/ws and @discordjs/core. */\nexport interface GatewayDispatchSource {\n on(\n event: \"dispatch\",\n listener: (payload: GatewayDispatchPayload, shardId: number) => void,\n ): unknown;\n off?(\n event: \"dispatch\",\n listener: (payload: GatewayDispatchPayload, shardId: number) => void,\n ): unknown;\n}\n\nexport interface CacheGatewayOptions extends CacheOperationContext {\n /** Receives cache write failures from the asynchronous gateway listener. */\n onError?: (error: unknown, payload: GatewayDispatchPayload, shardId: number) => void;\n}\n\n/**\n * Writes gateway dispatches into a cache. Call the returned function to stop listening.\n * Use this when a gateway has no GatewayClient managing its dispatch queue.\n */\nexport function attachCacheToGateway(\n gateway: GatewayDispatchSource,\n cache: Cache,\n options: CacheGatewayOptions = {},\n): () => void {\n const listener = (payload: GatewayDispatchPayload, shardId: number): void => {\n void applyGatewayDispatch(cache, payload, { clientUserId: options.clientUserId }).catch(\n (error) => {\n if (options.onError) options.onError(error, payload, shardId);\n else console.error(\"Failed to cache gateway dispatch\", error);\n },\n );\n };\n\n gateway.on(\"dispatch\", listener);\n return () => gateway.off?.(\"dispatch\", listener);\n}\n","import { CacheEntityNames } from \"./operations.js\";\nimport type { Cache, CacheEntityName, CacheEntityTypes, EntityCache } from \"./types.js\";\n\n/**\n * An {@link EntityCache} backed by a `Map`, optionally bounded as a least-recently-used cache.\n */\nexport class MemoryEntityCache<Raw> implements EntityCache<Raw> {\n /**\n * Always `true`: every method returns synchronously.\n */\n public readonly synchronous = true;\n\n /**\n * The maximum amount of entries, `Infinity` for an unbounded cache.\n */\n public readonly maxSize: number;\n\n readonly #items = new Map<string, Raw>();\n\n public constructor(maxSize = Infinity) {\n if (maxSize !== Infinity && (!Number.isInteger(maxSize) || maxSize < 0)) {\n throw new RangeError(\n `maxSize must be a non-negative integer or Infinity, received ${maxSize}`,\n );\n }\n\n this.maxSize = maxSize;\n }\n\n public get(key: string): Raw | undefined {\n const value = this.#items.get(key);\n if (value !== undefined && this.maxSize !== Infinity) {\n // Re-insert to mark the entry as the most recently used one.\n this.#items.delete(key);\n this.#items.set(key, value);\n }\n\n return value;\n }\n\n public set(key: string, value: Raw): void {\n if (this.maxSize === 0) return;\n\n this.#items.delete(key);\n this.#items.set(key, value);\n\n if (this.#items.size > this.maxSize) {\n // Maps iterate in insertion order, so the first key is the least recently used one.\n this.#items.delete(this.#items.keys().next().value!);\n }\n }\n\n public has(key: string): boolean {\n return this.#items.has(key);\n }\n\n public delete(key: string): boolean {\n return this.#items.delete(key);\n }\n\n public clear(): void {\n this.#items.clear();\n }\n\n public getSize(): number {\n return this.#items.size;\n }\n\n public keys(): string[] {\n return [...this.#items.keys()];\n }\n\n public values(): Raw[] {\n return [...this.#items.values()];\n }\n\n public entries(): [key: string, value: Raw][] {\n return [...this.#items.entries()];\n }\n}\n\n/**\n * A {@link Cache} whose entity caches are all {@link MemoryEntityCache}s.\n */\nexport type InMemoryCache = {\n readonly [Name in CacheEntityName]: MemoryEntityCache<CacheEntityTypes[Name]>;\n};\n\nexport interface InMemoryCacheOptions {\n /**\n * The maximum amount of entries kept per entity cache before evicting the least recently used ones, either for\n * every entity cache or per entity cache. Entity caches left out are unbounded.\n *\n * @default Infinity\n */\n maxSize?: number | Partial<Record<CacheEntityName, number>>;\n}\n\n/**\n * Creates a {@link Cache} that keeps everything in the process' memory.\n *\n * @example\n * ```typescript\n * import { createInMemoryCache } from '@wolfstar/plugin-cache';\n *\n * // Keep at most 1000 messages around, everything else is unbounded.\n * const cache = createInMemoryCache({ maxSize: { messages: 1_000 } });\n * ```\n *\n * @param options The options for the cache.\n */\nexport function createInMemoryCache(options: InMemoryCacheOptions = {}): InMemoryCache & Cache {\n const { maxSize } = options;\n const resolve = (name: CacheEntityName) =>\n typeof maxSize === \"number\" ? maxSize : (maxSize?.[name] ?? Infinity);\n\n return Object.freeze(\n Object.fromEntries(\n CacheEntityNames.map((name) => [name, new MemoryEntityCache(resolve(name))]),\n ) as InMemoryCache,\n );\n}\n","/// <reference types=\"node\" />\nimport { promisify } from \"node:util\";\nimport { brotliCompress, brotliDecompress, gunzip, gzip } from \"node:zlib\";\nimport {\n CacheEntityNames,\n GuildFieldCacheEntityNames,\n GuildKeyedCacheEntityNames,\n} from \"./operations.js\";\nimport type { Cache, CacheEntityName, CacheEntityTypes, EntityCache } from \"./types.js\";\n\nconst gzipAsync = promisify(gzip);\nconst gunzipAsync = promisify(gunzip);\nconst brotliCompressAsync = promisify(brotliCompress);\nconst brotliDecompressAsync = promisify(brotliDecompress);\n\n/**\n * The subset of the [`ioredis`](https://github.com/redis/ioredis) client API the Redis cache relies on. An `ioredis`\n * `Redis` or `Cluster` instance satisfies it, as can any other client exposing the same commands.\n */\nexport interface RedisClientLike {\n get(key: string): Promise<string | null>;\n mget(...keys: string[]): Promise<(string | null)[]>;\n set(key: string, value: string): Promise<unknown>;\n set(key: string, value: string, mode: \"PX\", milliseconds: number): Promise<unknown>;\n del(...keys: string[]): Promise<number>;\n exists(...keys: string[]): Promise<number>;\n zadd(key: string, ...scoreMembers: (string | number)[]): Promise<unknown>;\n zrem(key: string, ...members: string[]): Promise<number>;\n zrange(key: string, start: string, stop: string): Promise<string[]>;\n zcard(key: string): Promise<number>;\n zremrangebyscore(key: string, min: number | string, max: number | string): Promise<number>;\n multi(): RedisTransactionLike;\n}\n\n/**\n * The subset of an [`ioredis`](https://github.com/redis/ioredis) `MULTI` transaction the Redis cache relies on: every\n * queued command returns the transaction, and `exec` runs them atomically.\n */\nexport interface RedisTransactionLike {\n set(key: string, value: string): RedisTransactionLike;\n set(key: string, value: string, mode: \"PX\", milliseconds: number): RedisTransactionLike;\n del(...keys: string[]): RedisTransactionLike;\n zadd(key: string, ...scoreMembers: (string | number)[]): RedisTransactionLike;\n zrem(key: string, ...members: string[]): RedisTransactionLike;\n zremrangebyscore(key: string, min: number | string, max: number | string): RedisTransactionLike;\n /**\n * Optional: when available (`ioredis` has it), the guild indexes of a cache with a `ttl` expire once their guild\n * stops being written to. Without it, they are only pruned by the next write to their guild.\n */\n pexpire?(key: string, milliseconds: number): RedisTransactionLike;\n exec(): Promise<[error: Error | null, result: unknown][] | null>;\n}\n\n/**\n * Thrown when a value stored in Redis cannot be read back: invalid JSON, or compressed bytes that fail to decompress.\n *\n * @remarks\n * A missing value is not an error, `get` resolves to `undefined` for it. Redis connection errors are not wrapped\n * either, they propagate as the client throws them.\n */\nexport class CacheValueError extends Error {\n /**\n * The Redis key holding the unreadable value.\n */\n public readonly key: string;\n\n public constructor(key: string, cause: unknown) {\n super(`Cannot read the cached value at \"${key}\"`, { cause });\n this.name = \"CacheValueError\";\n this.key = key;\n }\n}\n\n/**\n * The algorithm used to compress values before writing them to Redis.\n */\nexport type RedisCacheCompression = \"gzip\" | \"brotli\" | \"none\";\n\nexport interface RedisEntityCacheOptions<Raw = unknown> {\n /**\n * The prefix of every Redis key owned by this entity cache.\n */\n prefix: string;\n /**\n * The time-to-live of every entry, in seconds. Entries never expire when omitted.\n */\n ttl?: number;\n /**\n * The compression algorithm to use.\n *\n * @default \"none\"\n */\n compression?: RedisCacheCompression;\n /**\n * The minimum size, in bytes, a serialized value must reach to be compressed. Small payloads rarely benefit from it.\n *\n * @default 1024\n */\n compressionThreshold?: number;\n /**\n * Resolves the guild an entry belongs to, indexing the entries by guild so {@link RedisEntityCache.deleteGuild}\n * does not have to scan the whole cache. Entries it resolves no guild for are not indexed. Without it, nothing is\n * indexed and `deleteGuild` resolves to `null`.\n *\n * On delete, it is first called without the value: when the key alone gives the guild away, the value is not\n * read back.\n */\n guildOf?: (key: string, value?: Raw) => string | undefined;\n}\n\n// The most keys a single `DEL`/`ZREM` of `deleteGuild` sends, so a large guild does not make one huge command.\nconst DeleteGuildChunkSize = 500;\n\n// Compressed values are stored as `<marker><base64>`. JSON can never start with either marker, so values written with\n// a different `compression` setting (e.g. before it was turned on) are still read back correctly.\nconst CompressionMarkers = { gzip: \"gz:\", brotli: \"br:\" } as const;\n\n/**\n * An {@link EntityCache} backed by Redis.\n *\n * @remarks\n * Every entry is stored as its own string key (`<prefix>:<key>`), and a sorted set (`<prefix>:@index`) tracks the\n * stored keys with their expiration time as score, which is what `keys`, `entries`, `getSize`, and `clear` read from.\n *\n * With {@link RedisEntityCacheOptions.guildOf}, one more sorted set per guild (`<prefix>:@guild:<guildId>`) tracks the\n * keys of that guild's entries the same way, and `<prefix>:@guilds` lists the guilds having one, for `clear`.\n */\nexport class RedisEntityCache<Raw> implements EntityCache<Raw> {\n /**\n * Always `false`: every method returns a promise, compressed or not.\n */\n public readonly synchronous = false;\n\n public readonly prefix: string;\n public readonly ttl: number | undefined;\n public readonly compression: RedisCacheCompression;\n public readonly compressionThreshold: number;\n\n readonly #redis: RedisClientLike;\n readonly #guildOf: ((key: string, value?: Raw) => string | undefined) | undefined;\n\n public constructor(redis: RedisClientLike, options: RedisEntityCacheOptions<Raw>) {\n if (options.ttl !== undefined && !(options.ttl > 0)) {\n throw new RangeError(`ttl must be a positive amount of seconds, received ${options.ttl}`);\n }\n\n this.#redis = redis;\n this.prefix = options.prefix;\n this.ttl = options.ttl;\n this.compression = options.compression ?? \"none\";\n this.compressionThreshold = options.compressionThreshold ?? 1024;\n this.#guildOf = options.guildOf;\n }\n\n public async get(key: string): Promise<Raw | undefined> {\n const valueKey = this.valueKey(key);\n const value = await this.#redis.get(valueKey);\n return value === null ? undefined : this.deserialize(valueKey, value);\n }\n\n public async set(key: string, value: Raw): Promise<void> {\n const serialized = await this.serialize(value);\n const guildId = this.#guildOf?.(key, value);\n // The value and its index entries are written in one transaction, so neither can exist without the other.\n const transaction = this.#redis.multi();\n if (this.ttl === undefined) {\n transaction.set(this.valueKey(key), serialized).zadd(this.indexKey, \"+inf\", key);\n if (guildId !== undefined) {\n transaction\n .zadd(this.guildIndexKey(guildId), \"+inf\", key)\n .zadd(this.guildsKey, \"+inf\", guildId);\n }\n } else {\n const now = Date.now();\n const milliseconds = Math.round(this.ttl * 1000);\n transaction\n .set(this.valueKey(key), serialized, \"PX\", milliseconds)\n .zadd(this.indexKey, now + milliseconds, key)\n // Pruning on every write keeps the index bounded even when nothing ever enumerates it.\n .zremrangebyscore(this.indexKey, \"-inf\", now);\n if (guildId !== undefined) {\n const guildIndexKey = this.guildIndexKey(guildId);\n transaction\n .zadd(guildIndexKey, now + milliseconds, key)\n .zremrangebyscore(guildIndexKey, \"-inf\", now)\n .zadd(this.guildsKey, now + milliseconds, guildId)\n .zremrangebyscore(this.guildsKey, \"-inf\", now);\n // Every write pushes the expiration back, so the indexes outlive every entry they list.\n transaction.pexpire?.(guildIndexKey, milliseconds);\n transaction.pexpire?.(this.guildsKey, milliseconds);\n }\n }\n\n await execute(transaction);\n }\n\n public async has(key: string): Promise<boolean> {\n return (await this.#redis.exists(this.valueKey(key))) > 0;\n }\n\n public async delete(key: string): Promise<boolean> {\n const guildId = await this.readGuild(key);\n const transaction = this.#redis.multi().del(this.valueKey(key)).zrem(this.indexKey, key);\n if (guildId !== undefined) transaction.zrem(this.guildIndexKey(guildId), key);\n\n const [deleted] = await execute(transaction);\n return (deleted as number) > 0;\n }\n\n public async clear(): Promise<void> {\n const keys = await this.#redis.zrange(this.indexKey, \"0\", \"-1\");\n if (keys.length > 0) await this.#redis.del(...keys.map((key) => this.valueKey(key)));\n\n const guilds = this.#guildOf ? await this.#redis.zrange(this.guildsKey, \"0\", \"-1\") : [];\n await this.#redis.del(\n this.indexKey,\n this.guildsKey,\n ...guilds.map((guildId) => this.guildIndexKey(guildId)),\n );\n }\n\n /**\n * Deletes every entry of a guild, reading their keys from the guild's index rather than scanning the cache.\n *\n * @remarks\n * Only indexed entries are deleted: when `guildOf` is set on a cache already holding entries, the ones written\n * before are left behind until they expire or the cache is cleared.\n *\n * @param guildId The ID of the guild.\n * @returns The amount of deleted entries, or `null` without {@link RedisEntityCacheOptions.guildOf}.\n */\n public async deleteGuild(guildId: string): Promise<number | null> {\n if (this.#guildOf === undefined) return null;\n\n const guildIndexKey = this.guildIndexKey(guildId);\n const keys = await this.#redis.zrange(guildIndexKey, \"0\", \"-1\");\n let deleted = 0;\n for (let index = 0; index < keys.length; index += DeleteGuildChunkSize) {\n const chunk = keys.slice(index, index + DeleteGuildChunkSize);\n // Only the listed keys leave the guild index: entries written meanwhile stay indexed.\n const [count] = await execute(\n this.#redis\n .multi()\n .del(...chunk.map((key) => this.valueKey(key)))\n .zrem(this.indexKey, ...chunk)\n .zrem(guildIndexKey, ...chunk),\n );\n deleted += count as number;\n }\n\n await this.#redis.zrem(this.guildsKey, guildId);\n return deleted;\n }\n\n public async getSize(): Promise<number> {\n await this.prune();\n return this.#redis.zcard(this.indexKey);\n }\n\n public async keys(): Promise<string[]> {\n await this.prune();\n return this.#redis.zrange(this.indexKey, \"0\", \"-1\");\n }\n\n public async values(): Promise<Raw[]> {\n return (await this.entries()).map(([, value]) => value);\n }\n\n public async entries(): Promise<[key: string, value: Raw][]> {\n const keys = await this.keys();\n if (keys.length === 0) return [];\n\n const values = await this.#redis.mget(...keys.map((key) => this.valueKey(key)));\n const entries: [key: string, value: Raw][] = [];\n for (const [index, value] of values.entries()) {\n // The value may have been evicted by Redis between both reads.\n if (value !== null) {\n const key = keys[index]!;\n entries.push([key, await this.deserialize(this.valueKey(key), value)]);\n }\n }\n\n return entries;\n }\n\n /**\n * Gets the Redis key a value is stored at.\n * @param key The entity cache key.\n */\n public valueKey(key: string): string {\n return `${this.prefix}:${key}`;\n }\n\n /**\n * The Redis key of the sorted set indexing the stored keys.\n */\n public get indexKey(): string {\n return `${this.prefix}:@index`;\n }\n\n /**\n * Gets the Redis key of the sorted set indexing a guild's entries.\n * @param guildId The ID of the guild.\n */\n public guildIndexKey(guildId: string): string {\n return `${this.prefix}:@guild:${guildId}`;\n }\n\n /**\n * The Redis key of the sorted set listing the guilds having an index.\n */\n public get guildsKey(): string {\n return `${this.prefix}:@guilds`;\n }\n\n /**\n * Reads the guild of a stored entry, to remove the entry from its guild index.\n * @param key The entity cache key.\n */\n private async readGuild(key: string): Promise<string | undefined> {\n if (this.#guildOf === undefined) return undefined;\n\n // Most guild-scoped keys start with the guild ID, sparing a read (and a decompression) on every delete.\n const fromKey = this.#guildOf(key);\n if (fromKey !== undefined) return fromKey;\n\n let value: Raw | undefined;\n try {\n value = await this.get(key);\n } catch (error) {\n // An unreadable value is deleted all the same, it only stays listed in its guild index.\n if (error instanceof CacheValueError) return undefined;\n throw error;\n }\n\n return value === undefined ? undefined : this.#guildOf(key, value);\n }\n\n private async prune(): Promise<void> {\n if (this.ttl !== undefined)\n await this.#redis.zremrangebyscore(this.indexKey, \"-inf\", Date.now());\n }\n\n private async serialize(value: Raw): Promise<string> {\n const json = JSON.stringify(value);\n if (this.compression === \"none\" || Buffer.byteLength(json) < this.compressionThreshold) {\n return json;\n }\n\n const compressed =\n this.compression === \"gzip\" ? await gzipAsync(json) : await brotliCompressAsync(json);\n return `${CompressionMarkers[this.compression]}${compressed.toString(\"base64\")}`;\n }\n\n private async deserialize(valueKey: string, value: string): Promise<Raw> {\n try {\n if (value.startsWith(CompressionMarkers.gzip)) {\n return JSON.parse((await gunzipAsync(decode(value))).toString(\"utf8\")) as Raw;\n }\n\n if (value.startsWith(CompressionMarkers.brotli)) {\n return JSON.parse((await brotliDecompressAsync(decode(value))).toString(\"utf8\")) as Raw;\n }\n\n return JSON.parse(value) as Raw;\n } catch (error) {\n throw new CacheValueError(valueKey, error);\n }\n }\n}\n\n/**\n * Runs a transaction, throwing the first command error, and resolves to the command results.\n */\nasync function execute(transaction: RedisTransactionLike): Promise<unknown[]> {\n const results = await transaction.exec();\n if (results === null) throw new Error(\"The Redis transaction was aborted\");\n\n return results.map(([error, result]) => {\n if (error) throw error;\n return result;\n });\n}\n\nfunction decode(value: string): Buffer {\n return Buffer.from(value.slice(3), \"base64\");\n}\n\n/**\n * A {@link Cache} whose entity caches are all {@link RedisEntityCache}s.\n */\nexport type RedisCache = {\n readonly [Name in CacheEntityName]: RedisEntityCache<CacheEntityTypes[Name]>;\n};\n\nexport interface RedisCacheOptions {\n /**\n * The Redis client to use, e.g. an [`ioredis`](https://github.com/redis/ioredis) instance.\n */\n redis: RedisClientLike;\n /**\n * The prefix of every Redis key owned by the cache, which allows several caches to share a database.\n *\n * @default \"wolfstar:cache\"\n */\n prefix?: string;\n /**\n * The compression algorithm to use for values of at least {@link RedisCacheOptions.compressionThreshold} bytes.\n *\n * @default \"none\"\n */\n compression?: RedisCacheCompression;\n /**\n * The minimum size, in bytes, a serialized value must reach to be compressed.\n *\n * @default 1024\n */\n compressionThreshold?: number;\n /**\n * The time-to-live per entity cache, in seconds. Entity caches left out never expire.\n */\n ttl?: Partial<Record<CacheEntityName, number>>;\n /**\n * Whether to index the guild-scoped entity caches by guild, so a `GUILD_DELETE` drops a guild's entries without\n * scanning every entity cache. It costs one more sorted-set write per write, and a read before every delete.\n *\n * @remarks\n * Entries written while it was off are not indexed: turning it on for a populated cache leaves them behind on\n * `GUILD_DELETE`, until they expire or the cache is cleared.\n *\n * @default true\n */\n indexGuilds?: boolean;\n}\n\n/**\n * The guild of an entry keyed by its guild (`${guildId}:...`), matching what the `deletePrefix` scan drops.\n */\nfunction guildOfKey(key: string, value?: unknown): string | undefined {\n const separator = key.indexOf(\":\");\n return separator > 0 ? key.slice(0, separator) : guildOfField(key, value);\n}\n\n/**\n * The guild of an entry keyed by its own ID, matching what the `deleteWhere` scan over `guild_id` drops.\n */\nfunction guildOfField(_key: string, value?: unknown): string | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const guildId = (value as { guild_id?: unknown }).guild_id;\n return typeof guildId === \"string\" ? guildId : undefined;\n}\n\nconst GuildResolvers: Partial<\n Record<CacheEntityName, (key: string, value?: unknown) => string | undefined>\n> = Object.fromEntries([\n ...GuildKeyedCacheEntityNames.map((name) => [name, guildOfKey] as const),\n ...GuildFieldCacheEntityNames.map((name) => [name, guildOfField] as const),\n]);\n\n/**\n * The default prefix of every Redis key owned by a cache created with {@link createRedisCache}.\n */\nexport const DefaultRedisCachePrefix = \"wolfstar:cache\";\n\n/**\n * Creates a {@link Cache} stored in Redis, optionally compressing its values.\n *\n * @example\n * ```typescript\n * import { createRedisCache } from '@wolfstar/plugin-cache';\n * import { Redis } from 'ioredis';\n *\n * const cache = createRedisCache({\n * redis: new Redis(process.env.REDIS_URL!),\n * compression: 'gzip',\n * ttl: { guilds: 60 * 60, users: 30 * 60 },\n * });\n * ```\n *\n * @param options The options for the cache.\n */\nexport function createRedisCache(options: RedisCacheOptions): RedisCache & Cache {\n const {\n redis,\n prefix = DefaultRedisCachePrefix,\n compression,\n compressionThreshold,\n ttl,\n indexGuilds = true,\n } = options;\n\n return Object.freeze(\n Object.fromEntries(\n CacheEntityNames.map((name) => [\n name,\n new RedisEntityCache(redis, {\n prefix: `${prefix}:${name}`,\n ttl: ttl?.[name],\n compression,\n compressionThreshold,\n guildOf: indexGuilds ? GuildResolvers[name] : undefined,\n }),\n ]),\n ) as RedisCache,\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wolfstar/plugin-cache",
3
- "version": "0.2.1-next-20260926221022",
3
+ "version": "0.3.0-next-20260926221500",
4
4
  "description": "Pluggable, storage-agnostic Discord entity cache (in-memory and Redis, with optional compression) for @wolfstar/plugin-gateway",
5
5
  "keywords": [
6
6
  "cache",