Skip to content

fix(plugin-autocapture-browser): flush Viewport Content Updated on pagehide/visibilitychange - #1934

Draft
jxiwang wants to merge 1 commit into
mainfrom
cursor/vcu-exit-reliability-29a7
Draft

fix(plugin-autocapture-browser): flush Viewport Content Updated on pagehide/visibilitychange#1934
jxiwang wants to merge 1 commit into
mainfrom
cursor/vcu-exit-reliability-29a7

Conversation

@jxiwang

@jxiwang jxiwang commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

[Amplitude] Viewport Content Updated was systematically under-delivered relative to [Amplitude] Page Viewed, especially for land-and-leave sessions. Two root causes:

  1. Delivery race on exit. The event was fired inside a beforeunload handler, but track() only schedules a send on a timer (flushIntervalMillis, default 1s). The document is normally torn down before that timer runs, so the event was written to the AMP_unsent_* local storage queue and only replayed (with a stale timestamp) on the browser's next init — if the user ever returned. On MemoryStorage or for bounced users it was lost entirely.
  2. Missing exit signals. beforeunload does not fire on mobile app-switch, tab discard, or bfcache eviction, so those page views produced no exit event at all.

This PR makes the exit path reliable without changing the event's semantics (still at most one page-end event per page view; mid-page 18k-char buffer flushes are unchanged):

  • Prefer pagehide over beforeunload for the page-end trigger (feature-detected via a new getPageEndEventName helper; falls back to beforeunload when pagehide is unavailable). pagehide also fires on mobile teardown and bfcache eviction and, unlike beforeunload, doesn't make the page bfcache-ineligible. This mirrors the pattern already used by session-replay-browser.
  • Add a visibilitychangehidden checkpoint. This is the last signal reliably delivered before a tab is backgrounded and possibly discarded. It flushes as a mid-page checkpoint (isPageEnd === false) so page-level scroll/exposure state is preserved if the user returns; a real page end shortly after is collapsed by the existing 100ms dedupe window.
  • Flush on exit. Both exit handlers call amplitude.flush() after firing, so the request starts (with keepalive, already set by the fetch transport for small bodies) while the page is still alive, instead of waiting on the interval timer.
  • Scope the page-end dedupe to page-end triggers only. Previously any call (including mid-page buffer flushes) armed pageViewEndFired, which could suppress a genuine page-end event (and its state reset) that followed within 100ms. Now only isPageEnd === true triggers arm it.

Tests updated to dispatch pagehide and to cover the new visibilitychange (hidden and visible) paths and the flush-on-exit behavior; a unit test covers getPageEndEventName across all branches. Package tests pass at 100% coverage and lint is clean.

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?: No
Open in Web Open in Cursor 

@github-actions

Copy link
Copy Markdown

size-limit report 📦

Path Size
packages/analytics-browser/lib/scripts/amplitude-min.js.gz 61.63 KB (+0.2% 🔺)
packages/session-replay-browser/lib/scripts/session-replay-browser-min.js.gz 134.97 KB (0%)
packages/unified/lib/scripts/amplitude-min.umd.js.gz 215.71 KB (+0.06% 🔺)
@amplitude/element-selector (gzipped esm) 2.67 KB (0%)

@jxiwang

jxiwang commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 1aadef9. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants