Skip to content

Feat zoning alpha#1513

Draft
jxiwang wants to merge 29 commits intomainfrom
feat-zoning-alpha
Draft

Feat zoning alpha#1513
jxiwang wants to merge 29 commits intomainfrom
feat-zoning-alpha

Conversation

@jxiwang
Copy link
Collaborator

@jxiwang jxiwang commented Feb 3, 2026

Summary

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?:

@macroscopeapp
Copy link
Contributor

macroscopeapp bot commented Feb 3, 2026

Add zoning alpha by implementing exposure and scroll tracking with autocapturePlugin calling fireViewportContentUpdated and enriching events with [Amplitude] Element Path and [Amplitude] Page View ID (DEFAULT_EXPOSURE_DURATION=150ms)

Introduce exposure and scroll observables, add trackExposure (timer-based after 150ms) and trackScroll, and emit [Amplitude] Viewport Content Updated from autocapturePlugin with batching and page-end handling; enrich element events with [Amplitude] Element Path and [Amplitude] Page View ID, and persist page view IDs via sessionStorage.

📍Where to Start

Start with autocapturePlugin wiring and event emission in autocapture-plugin.ts, then review fireViewportContentUpdated in track-viewport-content-updated.ts and trackExposure/trackScroll in track-exposure.ts and track-scroll.ts.


Macroscope summarized 15118fc. (Automatic summaries will resume when PR exits draft mode or review begins).


/* istanbul ignore next */
amplitude?.track('[Amplitude] Viewport Content Updated', eventProperties);
lastScroll = { maxX: pageScrollMaxState.maxX, maxY: pageScrollMaxState.maxY };
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Medium

packages/plugin-autocapture-browser/src/autocapture/track-viewport-content-updated.ts:66

Reassigning lastScroll on line 66 only updates the local parameter, not the caller's object. Consider mutating the existing object instead (e.g., lastScroll.maxX = ...; lastScroll.maxY = ...) so the caller sees the updated values.

Suggested change
lastScroll = { maxX: pageScrollMaxState.maxX, maxY: pageScrollMaxState.maxY };
lastScroll.maxX = pageScrollMaxState.maxX;
lastScroll.maxY = pageScrollMaxState.maxY;

🚀 Want me to fix this? Reply ex: "fix it for me".

exposureDuration?: number;
}) {
// Track which elements have been marked as exposed (per-element state)
const exposureMap = new Map<Element, boolean>();
Copy link
Contributor

Choose a reason for hiding this comment

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

🟢 Low

packages/plugin-autocapture-browser/src/autocapture/track-exposure.ts:20

Using Map<Element, ...> holds strong references to DOM elements, preventing garbage collection even after elements are removed from the DOM. Consider using WeakMap for exposureMap and exposureTimerMap to allow automatic cleanup when elements are garbage collected.

🚀 Want me to fix this? Reply ex: "fix it for me".


const scrollObservable = createScrollObservable();

const exposureObservable = createExposureObservable(
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Medium

packages/plugin-autocapture-browser/src/autocapture-plugin.ts:211

Passing an empty cssSelectorAllowlist causes createExposureObservable to call querySelectorAll(""), which throws a DOMException. Consider adding a guard in createExposureObservable to skip observation when the selector list is empty.

🚀 Want me to fix this? Reply ex: "fix it for me".

.then(() => {
this.logger?.debug?.('Background capture script loaded (external)');
// eslint-disable-next-line
amplitudeBackgroundCaptureInstance = (window as any)?.amplitudeBackgroundCapture?.({
Copy link
Contributor

Choose a reason for hiding this comment

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

🟢 Low

packages/plugin-autocapture-browser/src/libs/messenger.ts:236

Consider closing the existing amplitudeBackgroundCaptureInstance before assigning a new one to prevent resource leaks if initialize-background-capture is called multiple times.

Suggested change
amplitudeBackgroundCaptureInstance = (window as any)?.amplitudeBackgroundCapture?.({
amplitudeBackgroundCaptureInstance?.close?.();
amplitudeBackgroundCaptureInstance = (window as any)?.amplitudeBackgroundCapture?.({

🚀 Want me to fix this? Reply ex: "fix it for me".

const exposedString = JSON.stringify(exposedArray);

if (exposedString.length >= constants.MAX_ELEMENT_EXPOSED_STR_LENGTH) {
fireViewportContentUpdatedCallback(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Medium

packages/plugin-autocapture-browser/src/autocapture/track-viewport-content-updated.ts:96

Calling fireViewportContentUpdatedCallback(false) here sets pageViewEndFired = true in the callback. If a real page-end event occurs within 100ms, it gets skipped, preventing elementExposedForPage.clear(). Consider either not setting the debounce flag for non-page-end flushes, or having onExposure batch without triggering the debounce mechanism.

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment on lines +59 to +61
unsubscribe: () => {
exposureSubscription.unsubscribe();
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🟢 Low

packages/plugin-autocapture-browser/src/autocapture/track-exposure.ts:59

The unsubscribe method doesn't clear pending timers, so onExposure callbacks may fire after teardown. Consider reusing the timer-clearing logic from reset in unsubscribe.

Suggested change
unsubscribe: () => {
exposureSubscription.unsubscribe();
},
unsubscribe: () => {
exposureTimerMap.forEach((timer) => {
if (timer) {
clearTimeout(timer);
}
});
exposureTimerMap.clear();
exposureSubscription.unsubscribe();
},

🚀 Want me to fix this? Reply ex: "fix it for me".

jxiwang and others added 3 commits February 3, 2026 11:55
 - @amplitude/analytics-browser@2.34.1-feat-zoning-alpha.0
 - @amplitude/analytics-client-common@2.4.25-feat-zoning-alpha.0
 - @amplitude/analytics-core@2.38.0-feat-zoning-alpha.0
 - @amplitude/analytics-node@1.5.35-feat-zoning-alpha.0
 - @amplitude/analytics-react-native@1.5.38-feat-zoning-alpha.0
 - @amplitude/analytics-types@2.12.0-feat-zoning-alpha.0
 - @amplitude/gtm-snippet@2.34.1-feat-zoning-alpha.0
 - @amplitude/plugin-autocapture-browser@1.20.0-feat-zoning-alpha.0
 - @amplitude/plugin-experiment-browser@1.0.0-feat-zoning-alpha.0
 - @amplitude/plugin-global-user-properties@1.2.116-feat-zoning-alpha.0
 - @amplitude/plugin-network-capture-browser@1.7.9-feat-zoning-alpha.0
 - @amplitude/plugin-page-url-enrichment-browser@0.5.15-feat-zoning-alpha.0
 - @amplitude/plugin-page-view-tracking-browser@2.7.0-feat-zoning-alpha.0
 - @amplitude/plugin-session-replay-browser@1.25.11-feat-zoning-alpha.0
 - @amplitude/plugin-session-replay-react-native@0.4.7-feat-zoning-alpha.0
 - @amplitude/plugin-web-attribution-browser@2.1.109-feat-zoning-alpha.0
 - @amplitude/plugin-web-vitals-browser@1.1.10-feat-zoning-alpha.0
 - @amplitude/segment-session-replay-plugin@0.0.0-feat-zoning-alpha.0
 - @amplitude/session-replay-browser@1.30.10-feat-zoning-alpha.0
 - @amplitude/unified@1.0.0-feat-zoning-alpha.0
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.

4 participants