From 353b4a339a20701669c50479362fd50a823eccf2 Mon Sep 17 00:00:00 2001 From: zhanxingxin1998 Date: Wed, 5 Aug 2026 13:14:42 +0800 Subject: [PATCH] fix(discord): invalidate member cache on lifecycle events (#110414) Clear cached member and user entities when members join or leave, matching the existing update-event behavior and preventing stale role authorization. Co-authored-by: Claude Co-authored-by: Peter Steinberger --- .../discord/src/internal/entity-cache.test.ts | 22 +++++++++++++++++++ .../discord/src/internal/entity-cache.ts | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/extensions/discord/src/internal/entity-cache.test.ts b/extensions/discord/src/internal/entity-cache.test.ts index b914e9eb0323..3d1ed5f8494b 100644 --- a/extensions/discord/src/internal/entity-cache.test.ts +++ b/extensions/discord/src/internal/entity-cache.test.ts @@ -93,3 +93,25 @@ describe("DiscordEntityCache eviction", () => { expect(getCalls()).toBe(2); }); }); + +describe("DiscordEntityCache gateway invalidation", () => { + it.each([ + GatewayDispatchEvents.GuildMemberAdd, + GatewayDispatchEvents.GuildMemberRemove, + GatewayDispatchEvents.GuildMemberUpdate, + ])("invalidates member and user entries for %s", async (event) => { + const { cache, getCalls } = makeCache({ ttlMs: 60_000 }); + + await cache.fetchMember("g1", "u1"); + await cache.fetchUser("u1"); + await cache.fetchMember("g1", "u1"); + await cache.fetchUser("u1"); + expect(getCalls()).toBe(2); + + cache.invalidateForGatewayEvent(event, { guild_id: "g1", user: { id: "u1" } }); + + await cache.fetchMember("g1", "u1"); + await cache.fetchUser("u1"); + expect(getCalls()).toBe(4); + }); +}); diff --git a/extensions/discord/src/internal/entity-cache.ts b/extensions/discord/src/internal/entity-cache.ts index 263bf1149e05..d61ab7de96d2 100644 --- a/extensions/discord/src/internal/entity-cache.ts +++ b/extensions/discord/src/internal/entity-cache.ts @@ -70,6 +70,8 @@ export class DiscordEntityCache { const threadUpdate: string = GatewayDispatchEvents.ThreadUpdate; const threadDelete: string = GatewayDispatchEvents.ThreadDelete; const guildUpdate: string = GatewayDispatchEvents.GuildUpdate; + const guildMemberAdd: string = GatewayDispatchEvents.GuildMemberAdd; + const guildMemberRemove: string = GatewayDispatchEvents.GuildMemberRemove; const guildMemberUpdate: string = GatewayDispatchEvents.GuildMemberUpdate; if ( type === channelUpdate || @@ -82,7 +84,7 @@ export class DiscordEntityCache { if (type === guildUpdate) { this.deleteId("guild", raw.id); } - if (type === guildMemberUpdate) { + if (type === guildMemberAdd || type === guildMemberRemove || type === guildMemberUpdate) { const guildId = raw.guild_id; const user = raw.user && typeof raw.user === "object" ? (raw.user as { id?: unknown }) : {}; if (typeof guildId === "string" && typeof user.id === "string") {