fix(analytics-browser): rename Video events to align with new standards - #1971
fix(analytics-browser): rename Video events to align with new standards#1971daniel-graham-amplitude wants to merge 4 commits into
Conversation
size-limit report 📦
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Event names are inline string literals
- Moved [Amplitude] Content Started and Content Stopped into named constants in constants.ts and used those constants in video-capture.ts.
- ✅ Fixed: Delayed events can ignore EU zone
- getDelayedEventsServerUrl now uses the configured options.serverZone so EU projects with a custom serverUrl still get the EU delayed-events host.
Or push these changes by commenting:
@cursor push 91a0c16c02
Preview (91a0c16c02)
diff --git a/packages/analytics-browser/src/constants.ts b/packages/analytics-browser/src/constants.ts
--- a/packages/analytics-browser/src/constants.ts
+++ b/packages/analytics-browser/src/constants.ts
@@ -6,6 +6,8 @@
export const DEFAULT_FORM_START_EVENT = `${DEFAULT_EVENT_PREFIX} Form Started`;
export const DEFAULT_FORM_SUBMIT_EVENT = `${DEFAULT_EVENT_PREFIX} Form Submitted`;
export const DEFAULT_FILE_DOWNLOAD_EVENT = `${DEFAULT_EVENT_PREFIX} File Downloaded`;
+export const DEFAULT_CONTENT_STARTED_EVENT = `${DEFAULT_EVENT_PREFIX} Content Started`;
+export const DEFAULT_CONTENT_STOPPED_EVENT = `${DEFAULT_EVENT_PREFIX} Content Stopped`;
export const DEFAULT_SESSION_START_EVENT = 'session_start';
export const DEFAULT_SESSION_END_EVENT = 'session_end';
diff --git a/packages/analytics-browser/src/video-capture/video-capture.ts b/packages/analytics-browser/src/video-capture/video-capture.ts
--- a/packages/analytics-browser/src/video-capture/video-capture.ts
+++ b/packages/analytics-browser/src/video-capture/video-capture.ts
@@ -8,6 +8,7 @@
BaseEvent,
getHeartbeatInstance,
} from '@amplitude/analytics-core';
+import { DEFAULT_CONTENT_STARTED_EVENT, DEFAULT_CONTENT_STOPPED_EVENT } from '../constants';
/** Playback states where a view session is still in progress (e.g. buffering). */
const ACTIVE_PLAYBACK_STATES = new Set<VideoState['playbackState']>(['playing', 'waiting']);
@@ -81,7 +82,7 @@
const now = new Date().getTime();
const startEvent: BaseEvent = {
insert_id: UUID(),
- event_type: '[Amplitude] Content Started',
+ event_type: DEFAULT_CONTENT_STARTED_EVENT,
time: now,
event_properties: {
...nextState.lastEvent,
@@ -93,7 +94,7 @@
this.stopEvent = {
...startEvent,
insert_id: UUID(),
- event_type: '[Amplitude] Content Stopped',
+ event_type: DEFAULT_CONTENT_STOPPED_EVENT,
time: now + 1,
event_properties: {
...nextState.lastEvent,
diff --git a/packages/analytics-core/src/config.ts b/packages/analytics-core/src/config.ts
--- a/packages/analytics-core/src/config.ts
+++ b/packages/analytics-core/src/config.ts
@@ -82,7 +82,7 @@
const serverConfig = createServerConfig(options.serverUrl, options.serverZone, options.useBatch);
this.serverZone = serverConfig.serverZone;
this.serverUrl = serverConfig.serverUrl;
- this.delayedEventsServerUrl = getDelayedEventsServerUrl(options.delayedEventsServerUrl, this.serverZone);
+ this.delayedEventsServerUrl = getDelayedEventsServerUrl(options.delayedEventsServerUrl, options.serverZone);
}
}
diff --git a/packages/analytics-core/test/config.test.ts b/packages/analytics-core/test/config.test.ts
--- a/packages/analytics-core/test/config.test.ts
+++ b/packages/analytics-core/test/config.test.ts
@@ -68,6 +68,21 @@
);
});
+ test('should default delayedEventsServerUrl for EU when custom serverUrl is set', () => {
+ const defaultConfig = useDefaultConfig();
+ const config = new Config({
+ apiKey: API_KEY,
+ serverZone: 'EU',
+ serverUrl: 'https://proxy.example.com/2/httpapi',
+ storageProvider: defaultConfig.storageProvider,
+ transportProvider: defaultConfig.transportProvider,
+ });
+ expect(config.serverZone).toBeUndefined();
+ expect(config.delayedEventsServerUrl).toBe(
+ 'https://delayed-events.prod.eu-central-1.amplitude.com/2/httpapi/delayed',
+ );
+ });
+
test('should overwrite default config', () => {
const defaultConfig = useDefaultConfig();
const config = new Config({You can send follow-ups to the cloud agent here.
7e554da to
893f344
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Delayed events URL path is wrong
- getDelayedEventsServerUrl now prefers delayedEventsServerUrl and appends /delayed to a custom HTTP API serverUrl instead of duplicating /2/httpapi/delayed.
- ✅ Fixed: Browser config drops custom serverUrl
- BrowserConfig now forwards serverUrl to the parent Config constructor so delayed events follow a custom ingest URL or proxy.
Or push these changes by commenting:
@cursor push 0aeaf04cb3
Preview (0aeaf04cb3)
diff --git a/packages/analytics-browser/src/config.ts b/packages/analytics-browser/src/config.ts
--- a/packages/analytics-browser/src/config.ts
+++ b/packages/analytics-browser/src/config.ts
@@ -87,7 +87,7 @@
public partnerId?: string,
public plan?: Plan,
public serverUrl: string = '',
- public delayedEventsServerUrl?: string,
+ delayedEventsServerUrl?: string,
public serverZone: ServerZoneType = DEFAULT_SERVER_ZONE,
sessionId?: number,
deferredSessionId?: number,
@@ -114,7 +114,14 @@
public enableRequestBodyCompression: boolean = false,
public customEnrichment?: boolean | CustomEnrichmentOptions,
) {
- super({ apiKey, storageProvider, transportProvider: createTransport(transport) });
+ super({
+ apiKey,
+ storageProvider,
+ transportProvider: createTransport(transport),
+ delayedEventsServerUrl,
+ serverUrl,
+ serverZone,
+ });
this._cookieStorage = cookieStorage;
this.deviceId = deviceId;
this.lastEventId = lastEventId;
diff --git a/packages/analytics-browser/src/constants.ts b/packages/analytics-browser/src/constants.ts
--- a/packages/analytics-browser/src/constants.ts
+++ b/packages/analytics-browser/src/constants.ts
@@ -6,6 +6,8 @@
export const DEFAULT_FORM_START_EVENT = `${DEFAULT_EVENT_PREFIX} Form Started`;
export const DEFAULT_FORM_SUBMIT_EVENT = `${DEFAULT_EVENT_PREFIX} Form Submitted`;
export const DEFAULT_FILE_DOWNLOAD_EVENT = `${DEFAULT_EVENT_PREFIX} File Downloaded`;
+export const DEFAULT_CONTENT_STARTED_EVENT = `${DEFAULT_EVENT_PREFIX} Content Started`;
+export const DEFAULT_CONTENT_STOPPED_EVENT = `${DEFAULT_EVENT_PREFIX} Content Stopped`;
export const DEFAULT_SESSION_START_EVENT = 'session_start';
export const DEFAULT_SESSION_END_EVENT = 'session_end';
diff --git a/packages/analytics-browser/src/video-capture/video-capture.ts b/packages/analytics-browser/src/video-capture/video-capture.ts
--- a/packages/analytics-browser/src/video-capture/video-capture.ts
+++ b/packages/analytics-browser/src/video-capture/video-capture.ts
@@ -8,6 +8,7 @@
BaseEvent,
getHeartbeatInstance,
} from '@amplitude/analytics-core';
+import { DEFAULT_CONTENT_STARTED_EVENT, DEFAULT_CONTENT_STOPPED_EVENT } from '../constants';
/** Playback states where a view session is still in progress (e.g. buffering). */
const ACTIVE_PLAYBACK_STATES = new Set<VideoState['playbackState']>(['playing', 'waiting']);
@@ -81,7 +82,7 @@
const now = new Date().getTime();
const startEvent: BaseEvent = {
insert_id: UUID(),
- event_type: '[Amplitude] Content Started',
+ event_type: DEFAULT_CONTENT_STARTED_EVENT,
time: now,
event_properties: {
...nextState.lastEvent,
@@ -93,7 +94,7 @@
this.stopEvent = {
...startEvent,
insert_id: UUID(),
- event_type: '[Amplitude] Content Stopped',
+ event_type: DEFAULT_CONTENT_STOPPED_EVENT,
time: now + 1,
event_properties: {
...nextState.lastEvent,
diff --git a/packages/analytics-browser/test/config.test.ts b/packages/analytics-browser/test/config.test.ts
--- a/packages/analytics-browser/test/config.test.ts
+++ b/packages/analytics-browser/test/config.test.ts
@@ -157,7 +157,7 @@
},
topLevelDomain: '.amplitude.com',
enableRequestBodyCompression: false,
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
});
expect(getTopLevelDomain).toHaveBeenCalledTimes(1);
});
@@ -169,6 +169,21 @@
expect(config.delayedEventsServerUrl).toBe(delayedEventsServerUrl);
});
+ test('should derive delayedEventsServerUrl from custom serverUrl', async () => {
+ jest.spyOn(Config, 'getTopLevelDomain').mockResolvedValueOnce('.amplitude.com');
+ const serverUrl = 'https://proxy.example.com/2/httpapi';
+ const config = await Config.useBrowserConfig(apiKey, { serverUrl }, new AmplitudeBrowser());
+ expect(config.delayedEventsServerUrl).toBe(`${serverUrl}/delayed`);
+ });
+
+ test('should default delayedEventsServerUrl for EU', async () => {
+ jest.spyOn(Config, 'getTopLevelDomain').mockResolvedValueOnce('.amplitude.com');
+ const config = await Config.useBrowserConfig(apiKey, { serverZone: 'EU' }, new AmplitudeBrowser());
+ expect(config.delayedEventsServerUrl).toBe(
+ 'https://delayed-events.prod.eu-central-1.amplitude.com/2/httpapi/delayed',
+ );
+ });
+
test('should fall back to memoryStorage when storageProvider is not enabled', async () => {
const localStorageIsEnabledSpy = jest
.spyOn(LocalStorageModule.LocalStorage.prototype, 'isEnabled')
@@ -293,7 +308,7 @@
},
topLevelDomain: 'amplitude.com',
enableRequestBodyCompression: false,
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
});
});
});
diff --git a/packages/analytics-core/src/config.ts b/packages/analytics-core/src/config.ts
--- a/packages/analytics-core/src/config.ts
+++ b/packages/analytics-core/src/config.ts
@@ -73,7 +73,6 @@
this.offline = options.offline !== undefined ? options.offline : defaultConfig.offline;
this.optOut = options.optOut ?? defaultConfig.optOut;
this.serverUrl = options.serverUrl;
- this.delayedEventsServerUrl = options.delayedEventsServerUrl;
this.serverZone = options.serverZone || defaultConfig.serverZone;
this.storageProvider = options.storageProvider;
this.transportProvider = options.transportProvider;
@@ -83,6 +82,11 @@
const serverConfig = createServerConfig(options.serverUrl, options.serverZone, options.useBatch);
this.serverZone = serverConfig.serverZone;
this.serverUrl = serverConfig.serverUrl;
+ this.delayedEventsServerUrl = getDelayedEventsServerUrl(
+ options.serverUrl,
+ options.delayedEventsServerUrl,
+ this.serverZone,
+ );
}
}
@@ -108,6 +112,26 @@
};
};
+export const getDelayedEventsServerUrl = (
+ serverUrl: string | undefined,
+ delayedEventsServerUrl: string | undefined,
+ serverZone: ServerZoneType = getDefaultConfig().serverZone,
+) => {
+ if (delayedEventsServerUrl) {
+ return delayedEventsServerUrl;
+ }
+ if (serverUrl) {
+ return `${serverUrl}/delayed`;
+ }
+ switch (serverZone) {
+ case 'EU':
+ return 'https://delayed-events.prod.eu-central-1.amplitude.com/2/httpapi/delayed';
+ case 'US':
+ default:
+ return 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed';
+ }
+};
+
export class RequestMetadata implements IRequestMetadata {
sdk: {
metrics: {
diff --git a/packages/analytics-core/test/config.test.ts b/packages/analytics-core/test/config.test.ts
--- a/packages/analytics-core/test/config.test.ts
+++ b/packages/analytics-core/test/config.test.ts
@@ -33,7 +33,7 @@
plan: undefined,
ingestionMetadata: undefined,
serverUrl: 'https://api2.amplitude.com/2/httpapi',
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
serverZone: 'US',
storageProvider: defaultConfig.storageProvider,
transportProvider: defaultConfig.transportProvider,
@@ -55,6 +55,44 @@
expect(config.delayedEventsServerUrl).toBe(delayedEventsServerUrl);
});
+ test('should prefer delayedEventsServerUrl over custom serverUrl', () => {
+ const defaultConfig = useDefaultConfig();
+ const delayedEventsServerUrl = 'https://example.com/2/httpapi/delayed';
+ const config = new Config({
+ apiKey: API_KEY,
+ serverUrl: 'https://proxy.example.com/2/httpapi',
+ delayedEventsServerUrl,
+ storageProvider: defaultConfig.storageProvider,
+ transportProvider: defaultConfig.transportProvider,
+ });
+ expect(config.delayedEventsServerUrl).toBe(delayedEventsServerUrl);
+ });
+
+ test('should derive delayedEventsServerUrl from custom serverUrl', () => {
+ const defaultConfig = useDefaultConfig();
+ const serverUrl = 'https://proxy.example.com/2/httpapi';
+ const config = new Config({
+ apiKey: API_KEY,
+ serverUrl,
+ storageProvider: defaultConfig.storageProvider,
+ transportProvider: defaultConfig.transportProvider,
+ });
+ expect(config.delayedEventsServerUrl).toBe(`${serverUrl}/delayed`);
+ });
+
+ test('should default delayedEventsServerUrl for EU', () => {
+ const defaultConfig = useDefaultConfig();
+ const config = new Config({
+ apiKey: API_KEY,
+ serverZone: 'EU',
+ storageProvider: defaultConfig.storageProvider,
+ transportProvider: defaultConfig.transportProvider,
+ });
+ expect(config.delayedEventsServerUrl).toBe(
+ 'https://delayed-events.prod.eu-central-1.amplitude.com/2/httpapi/delayed',
+ );
+ });
+
test('should overwrite default config', () => {
const defaultConfig = useDefaultConfig();
const config = new Config({
@@ -90,7 +128,7 @@
sourceVersion: '2.0.0',
},
serverUrl: 'https://api2.amplitude.com/batch',
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
serverZone: 'US',
storageProvider: defaultConfig.storageProvider,
transportProvider: defaultConfig.transportProvider,
diff --git a/packages/analytics-node/test/config.test.ts b/packages/analytics-node/test/config.test.ts
--- a/packages/analytics-node/test/config.test.ts
+++ b/packages/analytics-node/test/config.test.ts
@@ -26,7 +26,7 @@
plan: undefined,
ingestionMetadata: undefined,
serverUrl: 'https://api2.amplitude.com/2/httpapi',
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
serverZone: 'US',
storageProvider: undefined,
transportProvider: new Http(),
@@ -61,7 +61,7 @@
plan: undefined,
ingestionMetadata: undefined,
serverUrl: 'https://api2.amplitude.com/2/httpapi',
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
serverZone: 'US',
storageProvider: undefined,
transportProvider: new Http(),
diff --git a/packages/analytics-react-native/test/config.test.ts b/packages/analytics-react-native/test/config.test.ts
--- a/packages/analytics-react-native/test/config.test.ts
+++ b/packages/analytics-react-native/test/config.test.ts
@@ -43,7 +43,7 @@
plan: undefined,
ingestionMetadata: undefined,
serverUrl: 'https://api2.amplitude.com/2/httpapi',
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
serverZone: 'US',
sessionTimeout: 300000,
trackingOptions: {
@@ -99,7 +99,7 @@
plan: undefined,
ingestionMetadata: undefined,
serverUrl: 'https://api2.amplitude.com/2/httpapi',
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
serverZone: 'US',
sessionTimeout: 300000,
storageProvider: new core.MemoryStorage(),
@@ -181,7 +181,7 @@
sourceVersion: '2.0.0',
},
serverUrl: 'https://api2.amplitude.com/2/httpapi',
- delayedEventsServerUrl: undefined,
+ delayedEventsServerUrl: 'https://delayed-events.prod.us-west-2.amplitude.com/2/httpapi/delayed',
serverZone: 'US',
_sessionId: -1,
sessionTimeout: 1,
diff --git a/test-server/video-analytics/track-html-video.html b/test-server/video-analytics/track-html-video.html
--- a/test-server/video-analytics/track-html-video.html
+++ b/test-server/video-analytics/track-html-video.html
@@ -56,7 +56,6 @@
// initialize Amplitude
amplitude.setUserId(userId);
amplitude.init(import.meta.env.VITE_AMPLITUDE_API_KEY, {
- delayedEventsServerUrl: `${location.origin}/2/httpapi/delayed`,
fetchRemoteConfig: false,
autocapture: false,
}).promise.then(() => {You can send follow-ups to the cloud agent here.
11d62e6 to
2964f6a
Compare
2964f6a to
cdf4d09
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Derived delayed URL path is wrong
- Changed getDelayedEventsServerUrl to append /delayed onto custom serverUrl so proxies that already include /2/httpapi no longer get a doubled path.
Or push these changes by commenting:
@cursor push 1c80bf5f37
Preview (1c80bf5f37)
diff --git a/packages/analytics-browser/src/config.ts b/packages/analytics-browser/src/config.ts
--- a/packages/analytics-browser/src/config.ts
+++ b/packages/analytics-browser/src/config.ts
@@ -50,7 +50,7 @@
serverZone: ServerZoneType = DEFAULT_SERVER_ZONE,
) => {
if (serverUrl) {
- return `${serverUrl}/2/httpapi/delayed`;
+ return `${serverUrl}/delayed`;
}
if (delayedEventsServerUrl) {
return delayedEventsServerUrl;
diff --git a/packages/analytics-browser/test/config.test.ts b/packages/analytics-browser/test/config.test.ts
--- a/packages/analytics-browser/test/config.test.ts
+++ b/packages/analytics-browser/test/config.test.ts
@@ -179,9 +179,9 @@
test('should derive delayedEventsServerUrl from custom serverUrl', async () => {
jest.spyOn(Config, 'getTopLevelDomain').mockResolvedValueOnce('.amplitude.com');
- const serverUrl = 'https://proxy.example.com';
+ const serverUrl = 'https://proxy.example.com/2/httpapi';
const config = await Config.useBrowserConfig(apiKey, { serverUrl }, new AmplitudeBrowser());
- expect(config.delayedEventsServerUrl).toBe(`${serverUrl}/2/httpapi/delayed`);
+ expect(config.delayedEventsServerUrl).toBe(`${serverUrl}/delayed`);
});
test('should prefer custom serverUrl over delayedEventsServerUrl', async () => {
@@ -192,7 +192,7 @@
{ serverUrl, delayedEventsServerUrl: 'https://example.com/2/httpapi/delayed' },
new AmplitudeBrowser(),
);
- expect(config.delayedEventsServerUrl).toBe(`${serverUrl}/2/httpapi/delayed`);
+ expect(config.delayedEventsServerUrl).toBe(`${serverUrl}/delayed`);
});
test('should fall back to memoryStorage when storageProvider is not enabled', async () => {You can send follow-ups to the cloud agent here.
This reverts commit a0d72dd.
a1ec66e to
0641843
Compare
This reverts commit a273f20.
0641843 to
b155a00
Compare
|
bugbot run |
There was a problem hiding this comment.
✅ 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 b155a00. Configure here.
Mercy811
left a comment
There was a problem hiding this comment.
The PR LGTM! Are the new [Amplitude] events registered in the backend?
|
@Mercy811 not yet no. I'll look into that next |

Summary
Checklist
Note
Medium Risk
Renamed content events can break existing dashboards and funnels; default delayed-events endpoints now apply in production unless overridden, changing where delayed heartbeat events are sent.
Overview
Media analytics renames playback events from
Video Content Started/Video Content Stoppedto[Amplitude] Content Startedand[Amplitude] Content Stopped(via new constants), and addsdelivery_mode(video|audio) on start/stop properties. Capture and core observers now acceptHTMLMediaElementsotrackVideoworks on<audio>as well as<video>.Browser config introduces
getDelayedEventsServerUrl, which setsdelayedEventsServerUrlon init: append/delayedwhenserverUrlis set (overriding an explicit delayed URL), otherwise usedelayedEventsServerUrlif provided, or zone defaults (US/EU prod delayed-events hosts). Previously the field often stayed undefined without manual config.Bundle budgets in
.size-limit.jsrise (analytics-browser 69kb, unified 235kb). The video test page loads HLS via hls.js for Mux streams and drops the localdelayedEventsServerUrloverride on init.Reviewed by Cursor Bugbot for commit b155a00. Bugbot is set up for automated code reviews on this repo. Configure here.