diff --git a/.changeset/curvy-owls-repair.md b/.changeset/curvy-owls-repair.md new file mode 100644 index 00000000..3cdcbab1 --- /dev/null +++ b/.changeset/curvy-owls-repair.md @@ -0,0 +1,5 @@ +--- +"@slashid/react": patch +--- + +Fixed: event buffer wasn't buffering events after all subscribers stopped listening for a particular event diff --git a/packages/react/src/components/form/event-buffer.test.ts b/packages/react/src/components/form/event-buffer.test.ts index 4863007b..c2d33e24 100644 --- a/packages/react/src/components/form/event-buffer.test.ts +++ b/packages/react/src/components/form/event-buffer.test.ts @@ -85,6 +85,44 @@ describe("createEventBuffer", () => { expect(callback).toHaveBeenCalledTimes(1); }); + it("should buffer events after all subscribers unsubscribe from an event and replay them to the first subscriber", () => { + const sdk = new MockSlashID({ analyticsEnabled: false, oid: "test" }); + const eventBuffer = createEventBuffer({ sdk }); + const callback = vi.fn(); + + // subscribe + eventBuffer.subscribe("authnContextUpdateChallengeReceivedEvent", callback); + + sdk.mockPublish("authnContextUpdateChallengeReceivedEvent", { + targetOrgId: "1", + }); + + // unsubscribe + eventBuffer.unsubscribe( + "authnContextUpdateChallengeReceivedEvent", + callback + ); + sdk.mockPublish("authnContextUpdateChallengeReceivedEvent", { + targetOrgId: "2", + }); + + expect(callback).toHaveBeenLastCalledWith({ + targetOrgId: "1", + }); + expect(callback).toHaveBeenCalledTimes(1); + + // subscribe again + // there was a bug where we incorrectly managed internal state and didn't buffer events after the last subscriber unsubscribed + eventBuffer.subscribe("authnContextUpdateChallengeReceivedEvent", callback); + + // this should replay the event that was missed + + expect(callback).toHaveBeenLastCalledWith({ + targetOrgId: "2", + }); + expect(callback).toHaveBeenCalledTimes(2); + }); + it("should only let the first subscriber consume the buffered events", () => { const sdk = new MockSlashID({ analyticsEnabled: false, oid: "test" }); const eventBuffer = createEventBuffer({ sdk }); diff --git a/packages/react/src/components/form/event-buffer.ts b/packages/react/src/components/form/event-buffer.ts index 3538fe31..5093c3d3 100644 --- a/packages/react/src/components/form/event-buffer.ts +++ b/packages/react/src/components/form/event-buffer.ts @@ -26,7 +26,10 @@ export function createEventBuffer({ sdk }: CreateEventBufferArgs): EventBuffer { Utils.PUBLIC_READ_EVENTS.forEach((publicReadEventName: EventNames) => { // create an event handler const handler = (event: Event) => { - if (subscribers.has(publicReadEventName)) { + if ( + subscribers.has(publicReadEventName) && + subscribers.get(publicReadEventName)?.length + ) { // if there is a subscriber for this event type just ignore return; } @@ -79,7 +82,7 @@ export function createEventBuffer({ sdk }: CreateEventBufferArgs): EventBuffer { function unsubscribe(eventType: EventNames, callback: EventCallback) { sdk.unsubscribe(eventType, callback); - if (!subscribers.has(eventType)) { + if (!subscribers.has(eventType) || !subscribers.get(eventType)?.length) { return; }