Skip to content

Commit f9761df

Browse files
fix(audience): guard shutdown() against double-call
Add destroyed flag so calling shutdown() twice (e.g. React strict mode useEffect cleanup) doesn't queue duplicate session_end or decrement liveInstances below the real count. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fdce15e commit f9761df

2 files changed

Lines changed: 22 additions & 0 deletions

File tree

packages/audience/sdk/src/sdk.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,24 @@ describe('Audience', () => {
655655
expect(msg.properties.duration).toBe(5);
656656
});
657657

658+
it('is safe to call twice (React strict mode)', async () => {
659+
const sdk = createSDK({ consent: 'full' });
660+
sdk.shutdown();
661+
662+
await Promise.resolve();
663+
await Promise.resolve();
664+
fetchCalls.length = 0;
665+
666+
sdk.shutdown();
667+
await Promise.resolve();
668+
await Promise.resolve();
669+
670+
const sessionEnds = sentMessages().filter(
671+
(m: any) => m.eventName === SESSION_END,
672+
);
673+
expect(sessionEnds).toHaveLength(0);
674+
});
675+
658676
it('does not emit session_end at none consent', async () => {
659677
const sdk = createSDK({ consent: 'none' });
660678
sdk.shutdown();

packages/audience/sdk/src/sdk.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ export class Audience {
6464

6565
private isFirstPage = true;
6666

67+
private destroyed = false;
68+
6769
private constructor(config: AudienceConfig) {
6870
const {
6971
cookieDomain,
@@ -419,6 +421,8 @@ export class Audience {
419421
* unmounts or the player leaves.
420422
*/
421423
shutdown(): void {
424+
if (this.destroyed) return;
425+
this.destroyed = true;
422426
if (!this.isTrackingDisabled()) this.trackSessionEnd();
423427
this.queue.destroy();
424428
Audience.liveInstances = Math.max(0, Audience.liveInstances - 1);

0 commit comments

Comments
 (0)